feat: add Altimate provider with /login command and credential valida…#606
feat: add Altimate provider with /login command and credential valida…#606anandgupta42 merged 4 commits intomainfrom
Conversation
There was a problem hiding this comment.
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.
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 inresolveEnvVarsinclient.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.mdaround 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
📒 Files selected for processing (7)
docs/docs/getting-started.mdpackages/opencode/src/altimate/api/client.tspackages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsxpackages/opencode/src/cli/cmd/tui/component/dialog-provider.tsxpackages/opencode/src/provider/provider.tspackages/opencode/src/util/filesystem.tspackages/opencode/test/altimate/datamate.test.ts
| await sdk.client.instance.dispose() | ||
| await sync.bootstrap() |
There was a problem hiding this comment.
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.
| 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.
57dfe15 to
660054f
Compare
- 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
660054f to
22c693b
Compare
There was a problem hiding this comment.
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.,
textorplaintext) 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.mdaround 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 fromtotext (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 fromtotext (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 -->
e9734f9 to
d27f30d
Compare
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
docs/docs/getting-started.mdpackages/opencode/src/altimate/api/client.tspackages/opencode/src/altimate/plugin/altimate.tspackages/opencode/src/cli/cmd/tui/component/dialog-provider.tsxpackages/opencode/src/plugin/index.tspackages/opencode/src/provider/provider.tspackages/opencode/src/util/filesystem.tspackages/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
- `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]>
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
…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
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:
/logindialog for Altimate (dialog-altimate-login.tsx)https://api.myaltimate.com)GET /auth_healthbefore saving~/.altimate/altimate.jsonwith mode0o600on success, then disposes the SDK session and re-bootstraps sync so the new provider loads immediately/connectprovider listProvider registration (
provider.ts)altimate-backendin the provider database as an OpenAI-compatible provider with a singlealtimate-defaultmodel (200k context, 128k output, tool-call capable)~/.altimate/altimate.jsonand builds the SDK options:baseURL = altimateUrl/agents/v1,apiKey, andx-tenant: altimateInstanceNameheader — mirrors the header contract used by the MCP engineProvider list integration (
dialog-provider.tsx)"altimate-backend"toPROVIDER_PRIORITY(shown in Popular section)onSelecttoDialogAltimateLogininstead of the generic API-key flowEnvironment variable substitution (
client.ts)getCredentials()now resolves${env:VARIABLE_NAME}references in~/.altimate/altimate.jsonbefore zod validation, matching the behaviour already present inaltimate-mcp-engine"prefix-${env:KEY}-suffix"), recursive object/array traversal, and throws with the variable name when a referenced env var is missingFilesystem fix (
filesystem.ts)chmod(p, mode)after eachwriteFile(..., { mode })call — Node.js does not reliably apply themodeoption on all platforms; this ensures the0o600permission on the credentials file is always enforcedDocs (
getting-started.md)~/.altimate/altimate.jsonformat and the three required fieldsTest Plan
packages/opencode/test/altimate/datamate.test.tscovering env var substitution ingetCredentials():${env:VAR}in the same stringmcpServerUrlfield also resolvedbun test test/altimate/datamate.test.ts)bun script/upstream/analyze.ts --markers --base mainpasses — all new code in upstream-shared files is wrapped withaltimate_changemarkersChecklist
docs/docs/getting-started.md— Connecting to Altimate section)Summary by CodeRabbit
New Features
Behavior
Documentation
Tests