feat(workspace): browser-based workspace creation handoff - #1100
feat(workspace): browser-based workspace creation handoff#1100sahrizvi wants to merge 3 commits into
Conversation
Adds the CLI half of the Workspaces pilot: after the first-run scan
completes and the CLI is authenticated with Altimate, prompt the user
once to create a new workspace or attach the project to an existing one.
The link is a direct authenticated call — no device flow — and the
browser opens after create so the user can configure integrations /
knowledge in the SaaS.
Fork-owned TuiPlugin per docs/internal/2026-06-23-tui-fork-features-
as-plugins-adr.md: single file at
`packages/opencode/src/plugin/tui/altimate/workspace.tsx`, added to
the existing `altimateTuiPlugins()` aggregator. Upstream
`packages/tui/**` stays byte-for-byte upstream. Uses the real
`api.ui.*` / `api.keymap.registerLayer` / `api.state.path.directory`
/ `api.kv` (persistent) surface.
Shared modules under `packages/opencode/src/altimate/workspace/` so
the plugin and the `altimate link` subcommand can't drift on request
shape or error handling:
- `api-client.ts` — typed errors (Conflict/Precondition/NotFound/
Forbidden/NotConfigured/Api), FastAPI `{"detail": {...}}` parsing,
15s abort timeout, credentials re-read on every call so an account
switch is picked up without restart.
- `detect.ts` — `detectProjectRemote` + `projectNameFromRemote`;
reuses `stripGitRemoteCredentials` (now exported from
`project-scan.ts` so the two callers can't drift).
- `state.ts` — local binding cache scoped to (tenant, apiUrl) with
atomic write + post-write `chmod 0o600` + corruption recovery.
Trigger: `onboarding-telemetry.ts` `tool.execute.after` hook publishes
`TuiEvent.CommandExecute` with `"altimate.workspace.postScan"` when
`project_scan` completes, gated on the new `Flag.ALTIMATE_WORKSPACE`
and `AltimateApi.isConfigured()` (BYOK users are silently skipped —
no place to send them). Never blocks onboarding on a publish failure.
Server-authoritative pre-check via `GET /datamate-project-bindings/
by-remote`; local cache used only as an offline fallback, and the
fallback path renders a mandatory "unverified" banner rather than
silently trusting stale data. Browser-open failure surfaces a
copyable-URL toast rather than swallowing silently.
7-day Skip latch lives in `api.kv` keyed by SHA-1(remote) — UTC
rolling window; `altimate link` (user-initiated) deliberately
bypasses the latch.
New `altimate-code link` subcommand runs the same three-way flow
outside a TUI session via `@clack/prompts` for scripting / catch-up
after a Skip. Bails early with helpful messages when credentials
are missing or no git remote is set.
Tests: 17 unit tests covering project-name parsing, git detection
graceful failure, cache read/write + chmod + tenant-scoping (account-
switch invalidation), and Skip latch TTL semantics with UTC boundary.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8FGy89Qpr39k8nCSpCcK2
…prompt Two user-flagged issues on the Workspaces post-scan prompt landed in 7c7e17f: 1. Post-scan dialog raced the LLM's onboarding-menu streaming — the dialog painted while text was still generating, and Enter didn't register until streaming finished. Fix: arm a one-shot `session.idle` listener via `EventV2Bridge` from `onboarding-telemetry.ts` and publish `TuiEvent.CommandExecute` only after the session settles. Costs a few seconds of latency; kills the race. 2. `resolveProjectRemote` returned undefined for projects without a git remote (materialized sample dbt scaffolds, fresh scratch dirs), so the post-scan prompt and `altimate-code link` both bailed silently. Fix: new `resolveProjectIdentifier` in `workspace/detect.ts` always returns a `{repoRemote?, projectPath}` pair (path is symlink-resolved `realpath`). `ProjectIdentifier` type threads through `WorkspaceApi`, the TuiPlugin dialogs, and the `link` subcommand — remote is preferred when available (stronger identity, survives directory moves); path is the fallback the backend indexes symmetrically. Also: `projectNameFromPath` fallback for auto-naming (derives from directory basename when no remote); Skip-latch key hashes remote-or-path so path-only projects also get the 7-day suppression; `runFlow` and `runOnDemandPicker` reworked to use `WorkspaceApi.getBindingForProject` (tries remote first, then path); `CachedBinding` in state.ts extended with `projectPath: string | null`. Tests updated + one new latch test covers the path-only case. `bun test test/altimate/plugin/workspace.test.ts` → 18/18.
Adds a browser handoff for creating and linking a Workspace: CLI opens the SaaS approval modal on `<tenant>.ws.myaltimate.com/create-and-link` with the current project's context (git remote or path + auto-derived name), user approves, the SaaS creates a workspace and delivers its ID back to the CLI via a loopback callback (same pattern as gateway sign-in). CLI then binds the current project to that workspace via the existing `POST /bind`. Additive to `feat/agent-workspaces` — every pre-existing option in the post-scan dialog and `altimate-code link` picker (Create quick workspace, Link to existing, Skip, workspace-picker rows) continues to work unchanged. The new "Set up in browser" option auto-hides when the deployment isn't supported (localhost, enterprise, custom domain) — freemium only for pilot. - New `packages/opencode/src/altimate/workspace/browser-handoff.ts`: loopback listener (own instance per flow, port walk 7317..7325 with natural fallback past a live OAuth listener), tenant-mismatch guard, typed failure reasons. Duplicates the loopback pattern from `altimate.ts` deliberately — shared-helper refactor is a follow-up ticket once both flows have prod experience. - Post-scan `OfferDialog`: adds "Set up in browser (recommended)" as the default when available, sitting alongside the existing options. - `altimate-code link` picker: adds "+ Set up in browser" as the first row when available. - Handles browser-open failures with a copy-URL fallback; 15-min timeout; explicit cancel via SaaS-delivered `?error=cancelled`. Tests: 14 new unit tests for browser-handoff (URL resolution, pre-flight failures, end-to-end via dependency-injected browser opener, port walk past a squatting listener). 32/32 workspace + plugin tests pass.
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 33124345 | Triggered | Basic Auth String | 7c7e17f | packages/opencode/src/altimate/workspace/detect.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
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. |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Summary
Adds a browser-based workspace creation handoff to the CLI: post-scan /
altimate-code linkopens the Altimate SaaS on<tenant>.ws.myaltimate.com/create-and-linkwith the current project's context, the user approves in a single modal, and the SaaS delivers the newly-created workspace's id back to a CLI-local loopback listener (same pattern as gateway sign-in). CLI then binds the current project via the existingPOST /datamate-project-bindings/bind.Stacked on the Workspaces draft PR (#1099 /
feat/agent-workspaces).What's added
packages/opencode/src/altimate/workspace/browser-handoff.ts(new) — per-flow loopback listener (own instance, walks 7317..7325 past a live OAuth listener), tenant-mismatch guard, typed failure reasons. Duplicates the loopback pattern fromaltimate.tsdeliberately — shared-helper refactor is a follow-up once both flows have prod experience.OfferDialog(post-scan) — adds "Set up in browser (recommended)" as the default option when the deployment supports it (freemium only for pilot;resolveWorkspaceWebUrlreturnsnullotherwise and the option auto-hides).altimate-code linkpicker — adds "+ Set up in browser" as the first row under the same condition.ALTIMATE_WORKSPACE_WEB_URLenv var overrides the deployment map lookup (used for local integration testing; never set in production).What's unchanged
Every pre-existing option in the post-scan dialog and
altimate-code linkpicker (Create quick workspace here, Link to an existing workspace, Skip for now, existing workspace-picker rows) continues to work exactly as it does today. The browser-handoff option is strictly additive. Rolling back is a single-commit revert with no schema, no cache format, and no backend-contract implications.A user whose deployment doesn't support the browser flow (localhost, enterprise) sees zero behavior change — the new option auto-hides.
Tests
browser-handoff.tscovering URL resolution edge cases (freemium / localhost / enterprise / malformed), pre-flight failures (unavailable / not-configured), end-to-end via dependency-injected browser opener (happy path, tenant mismatch, cancel via?error=cancelled, missing workspace_id, invalid workspace_id, browser-open failure withauthorizeUrlcopyable), and port walk past a squatting listener on 7317.E2E verified against the live backend
Ran the CLI-side round-trip against a live
altimate-backendon localhost:client,redirect,state,project_path,project_name,#cli_context)POST /datamates/creates workspacePOST /bindlinks withproject_path-based binding (no git remote)GET /by-pathreturns the binding afterFull SaaS-side E2E (real browser clicking Approve) is a manual smoke once the paired SaaS PR is up.
Paired SaaS PR
AltimateAI/altimate-frontend PR (
feat/AI-8510-workspace-browser-handoff) — stacked on Ralph'sfeature/AI-8496-ws-list-createbranch.Follow-ups (deliberately out of scope)
altimate.tswith a TODO comment).ws.UX (session gate returns user to default post-auth destination instead of/create-and-link; password login works correctly).🤖 Generated with Claude Code
https://claude.ai/code/session_016H42Vt4pt5dcD7opRqckeM
Summary by cubic
Adds a browser-based workspace creation handoff and completes the Workspaces pilot in the CLI/TUI. Previously, create/link ran only in-CLI; now post-scan and
altimate-code linkoffer “Set up in browser” (default when supported), which creates in the SaaS and returns the workspace id to the CLI for binding. Unsupported deployments auto-hide the option; existing flows are unchanged.packages/opencode/src/altimate/workspace/browser-handoff.ts(binds 127.0.0.1 on ports 7317..7325, independent of OAuth), tenant/state validation, cancel handling, 15‑min timeout, and copyable-URL fallback ifopenfails.OfferDialogandaltimate-code linkpicker add “Set up in browser” whenresolveWorkspaceWebUrlreturns a URL; otherwise they fall back to existing options.workspace/api-client.ts(typed errors, 15s request timeout),workspace/state.ts(per-tenant local cache, 0o600),workspace/detect.ts(git or path identity; exportsstripGitRemoteCredentials), newaltimate linkinpackages/opencode/src/cli/cmd/link.ts.onboarding-telemetry.ts) to avoid the onboarding-stream race.Flag.ALTIMATE_WORKSPACE; pilot is freemium-only. Dev override viaALTIMATE_WORKSPACE_WEB_URL. Rollback is a revert; no schema or backend contract changes.feat/AI-8510-workspace-browser-handoff.Rollout
Flag.ALTIMATE_WORKSPACE.ALTIMATE_WORKSPACE_WEB_URLto aws.host under your environment.Written for commit 6210bc6. Summary will update on new commits.