Skip to content

feat: add Altimate as a provider via /connect#162

Closed
suryaiyer95 wants to merge 1 commit intomainfrom
feat/login-auto-model-v2
Closed

feat: add Altimate as a provider via /connect#162
suryaiyer95 wants to merge 1 commit intomainfrom
feat/login-auto-model-v2

Conversation

@suryaiyer95
Copy link
Contributor

@suryaiyer95 suryaiyer95 commented Mar 15, 2026

Summary

Registers Altimate as an OpenAI-compatible provider accessible through the /connect flow.

  • Provider registration: altimate-backend auto-loads when ~/.altimate/altimate.json exists, configured with baseURL, apiKey, and x-tenant header
  • Connect flow: Selecting Altimate from /connect opens a 3-field form — Instance Name, API Key, URL — with tab navigation and credential validation against /auth_health
  • No model auto-select: After connecting, the dialog closes — user picks the model separately
  • Security: Filesystem.write() enforces file permissions via chmod() on every write. Credentials saved with 0o600
  • Input sanitization: Trims whitespace and strips trailing slashes from URL

Files changed

File Description
dialog-altimate-login.tsx New: credential form with validation, tab navigation, inline errors
dialog-provider.tsx Routes Altimate selection to credential form
provider.ts Registers altimate-backend with auto-loader using saved credentials
filesystem.ts Adds chmod() after writeFile to enforce permissions on existing files
getting-started.md Documents /connect provider options and Altimate credential setup

Test Plan

  • /connect → Altimate appears in Popular with (API key) label
  • Select Altimate → 3-field form appears (Instance Name, API Key, URL)
  • Tab cycles fields, Enter submits
  • Wrong credentials → inline error, form stays open
  • Valid credentials → saves to ~/.altimate/altimate.json, dialog closes
  • Restart → Altimate provider auto-loads, available in model list
  • Switch to another provider → stays on that provider after restart
  • Trailing spaces or slash in URL → handled correctly

Checklist

  • Tests added/updated
  • Documentation updated (if needed)
  • CHANGELOG updated (if user-facing)

suryaiyer95 added a commit that referenced this pull request Mar 15, 2026
`Filesystem.write()` passes `mode` to `writeFile()`, but Node.js only
applies the mode on file creation — not when overwriting an existing file.
Add explicit `chmod()` after write to ensure `0o600` permissions are
always enforced on `~/.altimate/altimate.json`, even on subsequent logins.

Addresses Sentry review finding on PR #162.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@suryaiyer95 suryaiyer95 changed the title feat: add /login command and auto-select Altimate model feat: add Altimate provider with /login command and credential validation Mar 15, 2026
@dev-punia-altimate
Copy link

🤖 Behavioral Analysis — 3 Finding(s)

🟡 Warnings (3)

  • F1 packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx:62 [Validation bypass: whitespace-only values pass required-field checks]
    The three required-field guards use bare if (!instance), if (!key), if (!url) with no trim. A value of ' ' (spaces only) is truthy, so it passes the check. The whitespace value then becomes an HTTP header value (e.g., 'x-tenant: " "'). The server rejects auth and the user sees 'Invalid credentials' instead of the specific 'Instance name is required' error. This is particularly likely for copy-pasted API keys that include surrounding whitespace. Evidence: diff lines ~62-83 show the bare !-checks. Compare question.tsx:148 which uses textarea?.plainText?.trim() ?? '' for the same kind of textarea — proving the codebase pattern is to trim.

  • F2 packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx:99 [Trailing slash in saved URL produces double-slash paths on every API call]
    The saved altimateUrl is used in two places with a hardcoded leading slash on the path: (1) AltimateApi.request in client.ts:67 builds ${creds.altimateUrl}${endpoint} where endpoints all start with '/' (e.g., '/datamates/'); (2) provider.ts new loader uses ${creds.altimateUrl}/agents/v1. If the user enters 'https://api.myaltimate.com/' (trailing slash), all resulting URLs get double slashes: 'https://api.myaltimate.com//datamates/', 'https://api.myaltimate.com//agents/v1'. The validation fetch to ${url}/auth_health also double-slashes, but most servers accept it — so validation passes and the bad URL is saved. Enterprise users directed to enter a custom URL are the most likely to include a trailing slash.

  • F3 packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx:113 [Silent failure: setValidating(false) called before save operations with no error handling]
    After auth validation succeeds, setValidating(false) is called and then the credential-save and reconnect steps are executed with no surrounding try/catch:

    setValidating(false) // <-- set here
    await Filesystem.writeJson(...) // can throw EACCES on chmod, ENOENT if mkdir fails
    await sdk.client.instance.dispose() // can throw network error
    await sync.bootstrap() // safe (handles errors internally via .catch)
    local.model.set(...)
    dialog.clear()

Because submit() is called fire-and-forget from the keyboard handler (no await), any exception from writeJson or dispose becomes an unhandled promise rejection. The dialog remains open (dialog.clear() never called), but validating() is already false so the form looks ready for another attempt. There is zero indication to the user that credentials were not saved. The PR itself introduces the new chmod call in writeFile that can now throw on restricted filesystems (EPERM), making this more likely to trigger than before.

Analysis run | Powered by QA Autopilot

@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from af70cf9 to dc75bc3 Compare March 17, 2026 18:24
Copilot AI review requested due to automatic review settings March 17, 2026 18:24
@github-actions
Copy link

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for an Altimate-hosted “OpenAI-compatible” backend provider, including a TUI login flow that persists credentials locally and prefers Altimate as the default model when configured.

Changes:

  • Add an altimate-backend provider loader (autoloads when Altimate credentials exist) and register a default Altimate model in the provider database.
  • Introduce a new TUI “Login to Altimate” dialog and wire it into /connect and a new /login command.
  • Ensure file writes that specify a mode also apply permissions via chmod.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/opencode/src/util/filesystem.ts Applies chmod after writes when mode is provided.
packages/opencode/src/provider/provider.ts Adds altimate-backend custom loader and registers an OpenAI-compatible Altimate model/provider entry.
packages/opencode/src/cli/cmd/tui/context/local.tsx Prefers Altimate provider/model as the fallback default when connected.
packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx Adds Altimate to provider list priority and routes selection to the Altimate login dialog.
packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx New dialog to validate and store Altimate credentials, then bootstrap and select Altimate model.
packages/opencode/src/cli/cmd/tui/app.tsx Adds a “Login to Altimate” command with /login slash entry.
docs/docs/getting-started.md Documents how to connect/login to Altimate and where credentials are stored.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from dc75bc3 to bdc5217 Compare March 17, 2026 18:33
@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from bdc5217 to bc2c8a7 Compare March 17, 2026 19:06
@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from bc2c8a7 to b6fb0e9 Compare March 17, 2026 19:10
@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from b6fb0e9 to abb6294 Compare March 17, 2026 19:17
@suryaiyer95 suryaiyer95 changed the title feat: add Altimate provider with /login command and credential validation feat: add Altimate provider with credential-based auto-loading Mar 17, 2026
@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from abb6294 to 4fc2c1a Compare March 17, 2026 19:25
@suryaiyer95 suryaiyer95 changed the title feat: add Altimate provider with credential-based auto-loading feat: add Altimate provider with credential validation via /connect Mar 17, 2026
@suryaiyer95 suryaiyer95 changed the title feat: add Altimate provider with credential validation via /connect feat: add Altimate as a provider via /connect Mar 17, 2026
@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from 4fc2c1a to 36b20c6 Compare March 17, 2026 19:47
@suryaiyer95 suryaiyer95 force-pushed the feat/login-auto-model-v2 branch from 36b20c6 to 8d22792 Compare March 17, 2026 19:52
@dev-punia-altimate
Copy link

✅ Tests — All Passed

TypeScript — passed

Python — passed

cc @suryaiyer95
Tested at 8d227929 | Run log | Powered by QA Autopilot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants