Skip to content

feat: add Altimate provider with /login command and credential valida…#606

Merged
anandgupta42 merged 4 commits intomainfrom
feat/login-auto-model-v2
Apr 2, 2026
Merged

feat: add Altimate provider with /login command and credential valida…#606
anandgupta42 merged 4 commits intomainfrom
feat/login-auto-model-v2

Conversation

@mdesmet
Copy link
Copy Markdown
Contributor

@mdesmet mdesmet commented Apr 1, 2026

Summary

Adds an Altimate platform LLM provider so users with an Altimate account can use it as their model backend directly from the TUI.

What changed

New: /login dialog for Altimate (dialog-altimate-login.tsx)

  • 3-field TUI form: Instance Name, API Key, URL (pre-filled with https://api.myaltimate.com)
  • Tab-key navigation between fields, Enter to submit
  • Validates credentials live against GET /auth_health before saving
  • Writes ~/.altimate/altimate.json with mode 0o600 on success, then disposes the SDK session and re-bootstraps sync so the new provider loads immediately
  • Shown automatically when user picks "Altimate" from the /connect provider list

Provider registration (provider.ts)

  • Registers altimate-backend in the provider database as an OpenAI-compatible provider with a single altimate-default model (200k context, 128k output, tool-call capable)
  • Autoload handler reads ~/.altimate/altimate.json and builds the SDK options: baseURL = altimateUrl/agents/v1, apiKey, and x-tenant: altimateInstanceName header — mirrors the header contract used by the MCP engine

Provider list integration (dialog-provider.tsx)

  • Adds "altimate-backend" to PROVIDER_PRIORITY (shown in Popular section)
  • Routes onSelect to DialogAltimateLogin instead of the generic API-key flow

Environment variable substitution (client.ts)

  • getCredentials() now resolves ${env:VARIABLE_NAME} references in ~/.altimate/altimate.json before zod validation, matching the behaviour already present in altimate-mcp-engine
  • Supports partial substitution (e.g. "prefix-${env:KEY}-suffix"), recursive object/array traversal, and throws with the variable name when a referenced env var is missing

Filesystem fix (filesystem.ts)

  • Added explicit chmod(p, mode) after each writeFile(..., { mode }) call — Node.js does not reliably apply the mode option on all platforms; this ensures the 0o600 permission on the credentials file is always enforced

Docs (getting-started.md)

  • Added "Connecting to Altimate" subsection under Step 3 explaining the ~/.altimate/altimate.json format and the three required fields

Test Plan

  • 6 new unit tests in packages/opencode/test/altimate/datamate.test.ts covering env var substitution in getCredentials():
    • All 3 required fields resolved from env vars
    • Mixed literal + ${env:VAR} in the same string
    • Error thrown when referenced env var is not set (error message matches MCP engine)
    • Literal values left unchanged when no substitution syntax is present
    • Optional mcpServerUrl field also resolved
    • All 19 tests in the file pass (bun test test/altimate/datamate.test.ts)
  • bun script/upstream/analyze.ts --markers --base main passes — all new code in upstream-shared files is wrapped with altimate_change markers

Checklist

  • Tests added/updated
  • Documentation updated (docs/docs/getting-started.md — Connecting to Altimate section)
  • CHANGELOG updated (if user-facing)

Summary by CodeRabbit

  • New Features

    • Altimate integration: new authentication provider, TUI "/connect" flow with credential parsing/validation, and automatic provider autoload when configured.
    • New internal plugin registers the Altimate auth option.
  • Behavior

    • Credentials file (~/.altimate/altimate.json) takes precedence over TUI; supports ${env:VAR} placeholders resolved at read time (unset vars error).
    • Credential writes enforce secure file permissions and normalize URLs.
  • Documentation

    • Added "Connecting to Altimate" guide with examples and setup steps.
  • Tests

    • Expanded coverage for parsing, env substitution, saving, and validation.

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Altimate integration: credential parsing, env-var resolution, validation, secure saving, a TUI connect flow, a custom provider loader and in-memory provider registration, an internal auth plugin, explicit filesystem permission enforcement on writes, new tests, and docs updates.

Changes

Cohort / File(s) Summary
Documentation
docs/docs/getting-started.md
New "Connecting to Altimate" section documenting TUI /connect flow, instance-url::instance-name::api-key format, example, validation via Altimate API, and optional ~/.altimate/altimate.json (overrides TUI).
Altimate API Core
packages/opencode/src/altimate/api/client.ts
Added recursive ${env:VAR} resolver, parseAltimateKey, saveCredentials (writes altimate.json with trailing-slash stripping and mode 0o600), and validateCredentials (tenant-name check, POST to /dbt/v3/validate-credentials, maps 401/403 and network errors). getCredentials() now resolves env placeholders before parsing and normalizes altimateUrl.
TUI / Dialog
packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
Dialog ApiMethod now handles altimate-backend input: parses input, validates via AltimateApi.validateCredentials, surfaces errors, saves creds via AltimateApi.saveCredentials, disposes SDK and bootstraps sync on success; unchanged flow for other providers.
Provider Loader & Registration
packages/opencode/src/provider/provider.ts
Added CUSTOM_LOADERS["altimate-backend"] that autoloads when configured (via AltimateApi) or falls back to Auth parsing; registers in-memory altimate-backend provider and altimate-default model when missing; removes stale/invalid auth entries.
Auth Plugin
packages/opencode/src/altimate/plugin/altimate.ts, packages/opencode/src/plugin/index.ts
Added AltimateAuthPlugin exporting auth hooks for altimate-backend and registered it in INTERNAL_PLUGINS.
Filesystem
packages/opencode/src/util/filesystem.ts
Filesystem.write() now explicitly calls chmod(p, mode) after writing (including ENOENT recovery) to ensure requested file permissions are applied.
Tests
packages/opencode/test/altimate/datamate.test.ts
Extensive tests for env-var substitution in credentials, parseAltimateKey edge cases, saveCredentials behavior and permissions, parse→save→get round-trip (trailing-slash stripping), and validateCredentials success/error/network paths with mocked fetch.

Sequence Diagram

sequenceDiagram
    participant User
    participant TUI as "TUI Dialog"
    participant Client as "AltimateApi Client"
    participant Remote as "Altimate Remote API"
    participant Provider as "Provider System"
    participant Auth as "Auth System"

    User->>TUI: Enter credentials (url::instance::key)
    TUI->>Client: parseAltimateKey(input)
    Client-->>TUI: parsed creds or null

    TUI->>Client: validateCredentials(creds)
    Client->>Remote: POST /dbt/v3/validate-credentials\nHeaders: Authorization, x-tenant
    Remote-->>Client: 200 / 401 / 403 / error
    Client-->>TUI: { ok: true } or { ok: false, error }

    alt Validation Success
        TUI->>Client: saveCredentials(creds)
        Client->>Client: resolveEnvVars and write ~/.altimate/altimate.json (mode 0o600)
        Client-->>TUI: saved
        TUI->>Provider: bootstrap/register provider
        Provider->>Auth: register provider (altimate-backend)
    else Validation Failed
        TUI-->>User: show validationError
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related issues

Poem

🐰
I nibbled at strings and chased env-var light,
Parsed keys, trimmed slashes, and validated at night,
Saved secrets snug with permissions just right,
Registered a provider to wake models bright,
A carrot-hop commit — everything feels light 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title summarizes the main feature (Altimate provider with /login command and credential validation), which aligns with the primary changes in the changeset.
Description check ✅ Passed The description is comprehensive and follows the template structure with Summary, Test Plan, and Checklist sections, covering all major changes, testing evidence, and documentation updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/login-auto-model-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
docs/docs/getting-started.md (1)

84-100: Missing documentation for environment variable substitution.

The ${env:VAR_NAME} syntax for credential values is supported (as implemented in resolveEnvVars in client.ts) but not documented here. Users may want to avoid hardcoding API keys in the JSON file.

Consider adding:

You can use environment variable substitution for sensitive values:

```json
{
  "altimateInstanceName": "your-instance",
  "altimateApiKey": "${env:ALTIMATE_API_KEY}",
  "altimateUrl": "https://api.myaltimate.com"
}

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/docs/getting-started.md around lines 84 - 100, The docs are missing
mention of the environment-variable substitution supported by resolveEnvVars in
client.ts; update the "Connecting to Altimate" section to document the
${env:VAR_NAME} syntax for sensitive fields (e.g., altimateApiKey) with a short
example JSON showing "altimateApiKey": "${env:ALTIMATE_API_KEY}" and a brief
note that resolveEnvVars will substitute environment variables so users can
avoid hardcoding secrets.


</details>

</blockquote></details>
<details>
<summary>packages/opencode/src/altimate/api/client.ts (1)</summary><blockquote>

`57-69`: **Empty environment variable values will throw an error.**

The check `if (!value)` on line 61 will throw for environment variables that are set to an empty string. This may be intentional for credentials (empty API keys are invalid), but could be surprising for other use cases.

If empty values should be allowed, change to:
```diff
-        if (!value) throw new Error(`Environment variable ${envVar} not found`)
+        if (value === undefined) throw new Error(`Environment variable ${envVar} not found`)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/altimate/api/client.ts` around lines 57 - 69, The
resolver currently throws whenever process.env[envVar] is falsy which rejects
legitimate empty-string env vars; in resolveEnvVars change the missing-variable
check to only treat undefined as missing (e.g., test value === undefined or
value == null) instead of using if (!value) so empty strings are allowed; update
the error throw in the same block that reads process.env[envVar] inside
resolveEnvVars to only trigger when the env var is truly absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx`:
- Around line 113-114: The explicit await sync.bootstrap() right after await
sdk.client.instance.dispose() can trigger a duplicate bootstrap because
server.instance.disposed already calls sync.bootstrap(); remove the explicit
await sync.bootstrap() in dialog-altimate-login.tsx (or alternatively add a
guard/flag in sync.bootstrap() or unsubscribe/guard the server.instance.disposed
listener) so only one bootstrap runs; look for sdk.client.instance.dispose() and
sync.bootstrap() and remove the redundant call or implement a one-time/guarded
bootstrap to prevent concurrent double execution.

---

Nitpick comments:
In `@docs/docs/getting-started.md`:
- Around line 84-100: The docs are missing mention of the environment-variable
substitution supported by resolveEnvVars in client.ts; update the "Connecting to
Altimate" section to document the ${env:VAR_NAME} syntax for sensitive fields
(e.g., altimateApiKey) with a short example JSON showing "altimateApiKey":
"${env:ALTIMATE_API_KEY}" and a brief note that resolveEnvVars will substitute
environment variables so users can avoid hardcoding secrets.

In `@packages/opencode/src/altimate/api/client.ts`:
- Around line 57-69: The resolver currently throws whenever process.env[envVar]
is falsy which rejects legitimate empty-string env vars; in resolveEnvVars
change the missing-variable check to only treat undefined as missing (e.g., test
value === undefined or value == null) instead of using if (!value) so empty
strings are allowed; update the error throw in the same block that reads
process.env[envVar] inside resolveEnvVars to only trigger when the env var is
truly absent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9bcb1601-8168-44bf-bd12-5676bd0ef61a

📥 Commits

Reviewing files that changed from the base of the PR and between 5d0ada3 and 3466dbc.

📒 Files selected for processing (7)
  • docs/docs/getting-started.md
  • packages/opencode/src/altimate/api/client.ts
  • packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx
  • packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/src/util/filesystem.ts
  • packages/opencode/test/altimate/datamate.test.ts

Comment on lines +113 to +114
await sdk.client.instance.dispose()
await sync.bootstrap()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential duplicate bootstrap() call.

Based on the context snippet from sync.tsx (lines 156-161), the server.instance.disposed event triggers bootstrap() automatically. The explicit await sync.bootstrap() on line 114 may result in two concurrent bootstrap() calls, since dispose() on line 113 fires the event.

Consider removing the explicit call and relying on the event-driven bootstrap:

      await Filesystem.writeJson(AltimateApi.credentialsPath(), creds, 0o600)
      await sdk.client.instance.dispose()
-     await sync.bootstrap()
      dialog.clear()

Or, if explicit control is preferred, ensure the event listener doesn't fire for this case.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await sdk.client.instance.dispose()
await sync.bootstrap()
await sdk.client.instance.dispose()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx` around
lines 113 - 114, The explicit await sync.bootstrap() right after await
sdk.client.instance.dispose() can trigger a duplicate bootstrap because
server.instance.disposed already calls sync.bootstrap(); remove the explicit
await sync.bootstrap() in dialog-altimate-login.tsx (or alternatively add a
guard/flag in sync.bootstrap() or unsubscribe/guard the server.instance.disposed
listener) so only one bootstrap runs; look for sdk.client.instance.dispose() and
sync.bootstrap() and remove the redundant call or implement a one-time/guarded
bootstrap to prevent concurrent double execution.

@mdesmet mdesmet force-pushed the feat/login-auto-model-v2 branch 2 times, most recently from 57dfe15 to 660054f Compare April 1, 2026 23:45
- Parse url::instance::key from ApiMethod dialog and write ~/.altimate/altimate.json directly
- Validate credentials against /dbt/v3/validate-credentials before saving (mirrors altimate-mcp-engine pattern)
- Show inline error in dialog on invalid format, bad API key (401), or bad instance name (403)
- Register AltimateAuthPlugin to surface "Connect to Altimate" method in /connect dialog
- Add altimate-backend CUSTOM_LOADER: file-first, auth-store fallback
- Add Filesystem.writeJson helper with mkdir-on-ENOENT and explicit chmod
- 47 tests covering parseAltimateKey, saveCredentials, validateCredentials, and TUI round-trip
@mdesmet mdesmet force-pushed the feat/login-auto-model-v2 branch from 660054f to 22c693b Compare April 2, 2026 00:01
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
docs/docs/getting-started.md (1)

88-90: Add a language identifier to the fenced code block.

The static analysis tool flagged this code block as missing a language specifier. While the content is plain text, adding a language identifier (e.g., text or plaintext) improves accessibility and silences the linter.

📝 Proposed fix
-```
+```text
 instance-url::instance-name::api-key
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/docs/getting-started.md around lines 88 - 90, The fenced code block
containing the literal line "instance-url::instance-name::api-key" is missing a
language identifier; update that block by changing the opening fence from totext (or ```plaintext) so the block becomes a plaintext code fence, which
satisfies the linter and improves accessibility.


</details>

</blockquote></details>
<details>
<summary>packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx (1)</summary><blockquote>

`270-288`: **Consider adding error handling for `saveCredentials` and subsequent operations.**

If `saveCredentials`, `sdk.client.instance.dispose()`, or `sync.bootstrap()` throws, the error is unhandled and the user sees no feedback. Other auth flows in this file (e.g., `CodeMethod`) don't handle this either, but since these are new I/O operations (filesystem write, SDK disposal), wrapping them in a try-catch and surfacing errors via `setValidationError` would improve UX.



<details>
<summary>🛡️ Proposed fix</summary>

```diff
         if (!validation.ok) {
           setValidationError(validation.error)
           return
         }
-        await AltimateApi.saveCredentials(parsed)
-        await sdk.client.instance.dispose()
-        await sync.bootstrap()
-        dialog.replace(() => <DialogModel providerID={props.providerID} />)
+        try {
+          await AltimateApi.saveCredentials(parsed)
+          await sdk.client.instance.dispose()
+          await sync.bootstrap()
+          dialog.replace(() => <DialogModel providerID={props.providerID} />)
+        } catch (err) {
+          setValidationError(err instanceof Error ? err.message : "Failed to save credentials")
+        }
         return
       }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx` around lines
270 - 288, The altimate-backend branch calls AltimateApi.saveCredentials,
sdk.client.instance.dispose(), and sync.bootstrap() without error handling; wrap
these calls in a try-catch around the block after validation (the code that
currently awaits AltimateApi.saveCredentials, sdk.client.instance.dispose,
sync.bootstrap and then dialog.replace) and on catch call setValidationError
with a user-friendly message including the caught error.message (or toString()),
ensuring you still avoid leaving the UI in a broken state; reference
AltimateApi.saveCredentials, sdk.client.instance.dispose, sync.bootstrap,
setValidationError, dialog.replace(() => <DialogModel
providerID={props.providerID} />) and props.providerID when making the changes.
```

</details>

</blockquote></details>
<details>
<summary>packages/opencode/src/altimate/api/client.ts (1)</summary><blockquote>

`120-145`: **Consider logging or preserving the error cause in `validateCredentials`.**

The catch block on line 142-144 discards the underlying error. For debugging connectivity issues, it may be helpful to at least log the error or include more context (e.g., hostname resolution failure vs. timeout).



<details>
<summary>🔍 Example improvement</summary>

```diff
     } catch (err) {
-      return { ok: false, error: "Could not reach Altimate API" }
+      const detail = err instanceof Error ? err.message : String(err)
+      return { ok: false, error: `Could not reach Altimate API: ${detail}` }
     }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/altimate/api/client.ts` around lines 120 - 145, The
catch in validateCredentials currently swallows the error; update the try/catch
to catch the error (e.g., catch (err)) and include the error message/stack in
the response or a log call so callers can see the root cause. Specifically,
inside validateCredentials replace the bare catch with catch (err) {
processLogger.error?.("validateCredentials failed", err); return { ok: false,
error: `Could not reach Altimate API: ${err?.message ?? String(err)}` } } (or
use console.error if processLogger is unavailable) so the original error details
are preserved.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Inline comments:
In @packages/opencode/src/provider/provider.ts:

  • Line 207: Replace the unsafe cast by constructing the proper ProviderID when
    calling Auth.get: instead of passing the string literal with "as any", call
    Auth.get with ProviderID.make("altimate-backend") so it uses the same ProviderID
    type used when registering the provider; update the call site of Auth.get
    accordingly (search for Auth.get invocation near the current diff) to use
    ProviderID.make("altimate-backend").

Nitpick comments:
In @docs/docs/getting-started.md:

  • Around line 88-90: The fenced code block containing the literal line
    "instance-url::instance-name::api-key" is missing a language identifier; update
    that block by changing the opening fence from totext (or ```plaintext)
    so the block becomes a plaintext code fence, which satisfies the linter and
    improves accessibility.

In @packages/opencode/src/altimate/api/client.ts:

  • Around line 120-145: The catch in validateCredentials currently swallows the
    error; update the try/catch to catch the error (e.g., catch (err)) and include
    the error message/stack in the response or a log call so callers can see the
    root cause. Specifically, inside validateCredentials replace the bare catch with
    catch (err) { processLogger.error?.("validateCredentials failed", err); return {
    ok: false, error: Could not reach Altimate API: ${err?.message ?? String(err)}
    } } (or use console.error if processLogger is unavailable) so the original error
    details are preserved.

In @packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx:

  • Around line 270-288: The altimate-backend branch calls
    AltimateApi.saveCredentials, sdk.client.instance.dispose(), and sync.bootstrap()
    without error handling; wrap these calls in a try-catch around the block after
    validation (the code that currently awaits AltimateApi.saveCredentials,
    sdk.client.instance.dispose, sync.bootstrap and then dialog.replace) and on
    catch call setValidationError with a user-friendly message including the caught
    error.message (or toString()), ensuring you still avoid leaving the UI in a
    broken state; reference AltimateApi.saveCredentials,
    sdk.client.instance.dispose, sync.bootstrap, setValidationError,
    dialog.replace(() => ) and
    props.providerID when making the changes.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: Repository UI

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `5375bed7-fe03-47ef-ae06-45d47783aa52`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 660054f77ad2708b137e13b2fb3e09b143e5f63e and e9734f9cf798810779a1613a50df8cd415a20966.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `docs/docs/getting-started.md`
* `packages/opencode/src/altimate/api/client.ts`
* `packages/opencode/src/altimate/plugin/altimate.ts`
* `packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx`
* `packages/opencode/src/plugin/index.ts`
* `packages/opencode/src/provider/provider.ts`
* `packages/opencode/src/util/filesystem.ts`
* `packages/opencode/test/altimate/datamate.test.ts`

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (3)</summary>

* packages/opencode/src/util/filesystem.ts
* packages/opencode/src/altimate/plugin/altimate.ts
* packages/opencode/test/altimate/datamate.test.ts

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@mdesmet mdesmet force-pushed the feat/login-auto-model-v2 branch from e9734f9 to d27f30d Compare April 2, 2026 00:51
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/docs/getting-started.md`:
- Around line 98-99: Add a minimal schema and example for the altimate.json
configuration immediately after the paragraph that mentions creating
~/.altimate/altimate.json: show a small JSON example containing the keys
altimateUrl, altimateInstanceName, and altimateApiKey and briefly describe each
key’s purpose and required/optional status (e.g., altimateUrl: full base URL of
the Altimate API, altimateInstanceName: human-friendly instance identifier,
altimateApiKey: API key/token used for authentication); ensure the docs state
that this file takes priority over TUI credentials and note any optional fields
or formatting requirements (JSON string values, no trailing commas).

In `@packages/opencode/src/altimate/api/client.ts`:
- Around line 76-77: getCredentials() currently returns the stored altimateUrl
verbatim which can contain a trailing slash and later causes request() to
produce malformed URLs; ensure altimateUrl is normalized (strip trailing
slashes) both when reading and when writing credentials. Update the code path
that calls AltimateCredentials.parse(raw) in getCredentials() to trim any
trailing '/' on raw.altimateUrl before parsing (or augment
AltimateCredentials.parse to normalize), and likewise normalize altimateUrl in
the credential-save path around the code at the 95-101 block so saved values
never include a trailing slash; keep validateCredentials() behavior but ensure
getCredentials()/save logic always returns/stores the normalized URL to prevent
"//" concatenation in request().

In `@packages/opencode/src/provider/provider.ts`:
- Around line 188-223: This loader must actively suppress a stale
"altimate-backend" auth when it cannot construct valid options: update the
failure branches in the AltimateApi.isConfigured path (catch after
AltimateApi.getCredentials()) and the auth-store path (when
AltimateApi.parseAltimateKey(auth.key) returns null) to explicitly remove or
override the merged auth for ProviderID.make("altimate-backend") instead of just
returning { autoload: false }; call the existing Auth API (e.g., Auth.remove or
Auth.set with an empty/disabled entry) or otherwise mark that provider as
disabled so the earlier generic Auth.all() merge cannot leave a live, but
invalid, altimate-backend entry. Ensure you reference AltimateApi.isConfigured,
AltimateApi.getCredentials, AltimateApi.parseAltimateKey, and
ProviderID.make("altimate-backend") when locating the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 53f1e274-7bf0-42ad-8574-4f2fc5ef74ef

📥 Commits

Reviewing files that changed from the base of the PR and between e9734f9 and d27f30d.

📒 Files selected for processing (8)
  • docs/docs/getting-started.md
  • packages/opencode/src/altimate/api/client.ts
  • packages/opencode/src/altimate/plugin/altimate.ts
  • packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
  • packages/opencode/src/plugin/index.ts
  • packages/opencode/src/provider/provider.ts
  • packages/opencode/src/util/filesystem.ts
  • packages/opencode/test/altimate/datamate.test.ts
✅ Files skipped from review due to trivial changes (3)
  • packages/opencode/src/util/filesystem.ts
  • packages/opencode/src/altimate/plugin/altimate.ts
  • packages/opencode/test/altimate/datamate.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/opencode/src/plugin/index.ts

anandgupta42 and others added 2 commits April 2, 2026 09:57
- `resolveEnvVars`: check `=== undefined` instead of `!value` to allow empty-string env vars
- Normalize `altimateUrl` (strip trailing slashes) in both `getCredentials()` and `saveCredentials()` to prevent malformed `//` URLs in `request()`
- Include underlying error details in `validateCredentials` catch for easier debugging
- Suppress stale `altimate-backend` auth via `Auth.remove()` when the loader cannot build valid options
- Wrap `saveCredentials`/`dispose`/`bootstrap` in try-catch in `dialog-provider.tsx`
- Document `altimate.json` schema and `${env:VAR_NAME}` substitution in `getting-started.md`
- Add `text` language identifier to credential format code block
- Update tests for URL normalization and error message changes

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Add `.catch(() => {})` to `Auth.remove()` in provider.ts to prevent
  a storage error from crashing all provider initialization
- Add test verifying that `${env:VAR}` resolves to `""` when the env
  var is set to an empty string (covers the `!value` → `=== undefined` change)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@anandgupta42 anandgupta42 merged commit 0d34855 into main Apr 2, 2026
10 checks passed
anandgupta42 pushed a commit that referenced this pull request Apr 3, 2026
Cover two untested code paths added this week: ClickHouse warehouse auto-detection
via env vars (PR #574) and the altimate-backend provider credential resolver (PR #606).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

https://claude.ai/code/session_01JpvxVa5gFNv3Y7hQrtjo8U
anandgupta42 pushed a commit that referenced this pull request Apr 3, 2026
…allback

Cover the new altimate-backend provider loader (PR #606) which had zero test
coverage. Tests exercise both credential paths (config file, auth store),
trailing-slash normalization, stale-auth cleanup, and model registration.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

https://claude.ai/code/session_01RdRKyZcQMBTikMoz8TJcDk
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