Conversation
Updated the documentation to emphasize that anonymous workspaces are single-device only, and provided clear instructions for users on how to transition to an OAuth account for multi-device access. This includes updates to the README and CLI documentation, ensuring users are aware of the constraints and the necessary steps to retain their work across devices.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 80adb17491
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ui.log.info( | ||
| "Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.", |
There was a problem hiding this comment.
Gate OAuth signup hint when OAuth availability is unknown
This message is emitted even when runAnonymousLogin is called with oauthAvailable: false or undefined (e.g. explicit arkor login --anonymous or anon-only deployments), so users are told to run arkor login --oauth in environments where that command is expected to fail. That regresses the existing gating behavior documented in cli/anonymous.ts and produces a dead-end recovery path for a real deployment mode.
Useful? React with 👍 / 👎.
| ui.log.info( | ||
| "Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.", |
There was a problem hiding this comment.
Suppress OAuth-only guidance on anonymous-only deployments
In ensureCredentialsForStudio, oauthAvailable can be false (deployment explicitly does not advertise Auth0), but this unconditional note still instructs the user to run arkor login --oauth. On anon-only deployments this points users to a command that cannot succeed, which is especially confusing during first-run arkor dev onboarding where this path is common.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR updates Arkor’s CLI and documentation around anonymous authentication, aiming to make the single-device limitation explicit and to improve how anonymous auth failures are surfaced to users. It touches both runtime messaging and English/Japanese docs so the guidance is more consistent across the product surface.
Changes:
- Adds structured anonymous-auth error handling in the CLI via
CloudApiError.codeand a formatter for known anonymous failure modes. - Surfaces new single-device guidance in anonymous login/dev/whoami flows and updates template/readme/docs copy accordingly.
- Expands English and Japanese auth/dev documentation to describe new error codes, anonymous-workspace constraints, and recovery guidance.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
packages/cli-internal/src/templates.ts |
Updates scaffolded quickstart text about anonymous-account limitations and upgrade path. |
packages/arkor/src/core/client.ts |
Extends CloudApiError with structured code support and exports the error builder. |
packages/arkor/src/core/anonymous-auth-error.ts |
Adds formatter/helpers for anonymous auth dead-end errors. |
packages/arkor/src/core/anonymous-auth-error.test.ts |
Adds unit tests for anonymous auth error formatting helpers. |
packages/arkor/src/cli/main.ts |
Intercepts known anonymous auth errors at top level and prints friendly guidance. |
packages/arkor/src/cli/commands/whoami.ts |
Reworks non-2xx handling and adds anonymous-account guidance on success. |
packages/arkor/src/cli/commands/whoami.test.ts |
Updates tests for thrown CloudApiError behavior from whoami. |
packages/arkor/src/cli/commands/login.ts |
Adds post-anonymous-login note about the single-device limitation. |
packages/arkor/src/cli/commands/dev.ts |
Adds matching anonymous-account guidance during Studio credential bootstrap. |
packages/arkor/README.md |
Documents anonymous accounts as single-device and explains limitations. |
docs/ja/cli/dev.mdx |
Japanese dev docs updated with new anonymous-account messaging. |
docs/ja/cli/auth.mdx |
Japanese auth docs updated for new error codes and anonymous-account behavior. |
docs/cli/dev.mdx |
English dev docs updated with single-device and bootstrap guidance. |
docs/cli/auth.mdx |
English auth docs updated with structured error-code behavior and expiry guidance. |
README.md |
Root README quickstart and feature bullets now mention single-device anonymous workspaces. |
README.ja.md |
Japanese root README mirrors the new anonymous-workspace guidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - `code: "anonymous_token_single_device"` → ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. Sign up for an account that supports multiple devices: arkor login --oauth`` (exit `1`). | ||
| - `code: "anonymous_account_not_found"` → ``Your anonymous credentials are no longer valid. Sign up to continue: arkor login --oauth`` (exit `1`). | ||
|
|
||
| Errors without a known `code` propagate as commander's default error message (the upstream `error` body), so the user sees the cloud-api's reason verbatim. |
| // second machine. `arkor login --oauth` is the explicit upgrade | ||
| // path; phrasing matches the auth-error formatter so users see the | ||
| // same advice from both surfaces. | ||
| process.stdout.write( | ||
| "\nNote: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.\n", |
| | `No credentials on file — requesting an anonymous token.` | Same as above on anon-only deployments (no Auth0 advertised in `/v1/auth/cli/config`). The CLI omits the `arkor login --oauth` hint because that command would fail there. | Nothing required. | | ||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.`` | Informational follow-up after the anonymous bootstrap completes — surfaces the cloud-side identifier and where it lives (the path is the resolved `credentialsPath()`, typically `~/.arkor/credentials.json` on Linux and macOS). | Nothing required. Back up the credentials file if you want to keep using the same anonymous identity from another machine. | | ||
| | ``Anonymous sessions aren't guaranteed to persist — sign in with `arkor login --oauth` to tie future work to your Arkor Cloud account.`` | Persistence nudge fired alongside the success message when the deployment is known to support OAuth. Anonymous work has no SLA on the cloud-api side, so the CLI surfaces the upgrade path before you invest real work. Suppressed on anon-only deployments. | Optional: run `arkor login --oauth` to tie future work to your account. Existing anonymous work stays under its current id; there is no migration path today. | | ||
| | ``Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.`` | Informational note fired alongside the bootstrap success line on every deployment. Anonymous accounts are bound to the issuing machine via the cloud-api's single-device guard, so the CLI surfaces the limitation up front rather than waiting for the first 401 on a second machine. | Optional. If you need multi-device access, run `arkor login --oauth` for an OAuth account. Existing anonymous work cannot be migrated; the OAuth account starts fresh. | |
| | `No credentials on file — requesting an anonymous token.`(認証情報ファイルがありません。匿名トークンを要求します) | 同上だが匿名専用デプロイの場合(`/v1/auth/cli/config` で Auth0 がアドバタイズされていない)。`arkor login --oauth` は失敗するので OAuth ヒントは省かれる。 | 何もしなくてよい。 | | ||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.``(匿名 id: `<id>`。Arkor Cloud はこの id でセッション間でこのクライアントを識別します。同じ匿名 identity を維持するには認証情報ファイルを保持してください。パスは `credentialsPath()` の解決結果で、Linux と macOS では通常 `~/.arkor/credentials.json`) | 匿名ブートストラップ完了後の情報行。クラウド側の識別子と、それを保持しているファイルの場所を明示する。 | 何もしなくてよい。別マシーンから同じ匿名 identity を使いたいなら認証情報ファイルをバックアップ。 | | ||
| | ``Anonymous sessions aren't guaranteed to persist — sign in with `arkor login --oauth` to tie future work to your Arkor Cloud account.``(匿名セッションは永続性が保証されないので、今後の作業を Arkor Cloud アカウントに紐付けたいなら `arkor login --oauth` でサインインしてください) | デプロイが OAuth をサポートすると分かっているときに、成功メッセージと並んで出る永続性ナッジ。匿名作業はクラウド API 側で SLA がないので、本格的に作業する前にアップグレード経路を提示している。匿名専用デプロイでは抑制される。 | 任意。今後の作業をアカウントに紐付けたいなら `arkor login --oauth`。既存の匿名作業はその id に残り、現状マイグレーションパスはない。 | | ||
| | ``Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.``(注意: 匿名アカウントはこのマシーンでのみ動作します。複数端末で使うには `arkor login --oauth` でサインアップしてください) | 匿名ブートストラップ成功行と並んで全デプロイで出る情報行。匿名アカウントは発行マシーンに対して単一端末ガードでバインドされるので、別端末での 401 を待たず最初に制約を提示する。 | 任意。複数端末でのアクセスが必要なら OAuth アカウント用に `arkor login --oauth`。既存の匿名作業はマイグレーションできず、OAuth アカウントは新規スタート。 | |
| `arkor dev` opens **Studio**, a local web UI at `http://localhost:4000`. On first launch it provisions a throwaway anonymous workspace so you can fire off a real training run right away. | ||
|
|
||
| Run `arkor login --oauth` later if you want to claim your work under an account. | ||
| Anonymous workspaces are tied to this machine. They only work where you ran `arkor dev` first. Run `arkor login --oauth` later to attach your work to an account that follows you across devices. |
| if (isAnonymous) { | ||
| // Anonymous accounts are single-device on purpose, so surface the | ||
| // limitation here so users discover it before hitting a 401 on a | ||
| // second machine. `arkor login --oauth` is the explicit upgrade | ||
| // path; phrasing matches the auth-error formatter so users see the | ||
| // same advice from both surfaces. | ||
| process.stdout.write( | ||
| "\nNote: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.\n", |
| - `code: "anonymous_token_single_device"` → ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. Sign up for an account that supports multiple devices: arkor login --oauth``(和訳: 匿名認証情報が単一端末ポリシー違反として拒否されました。複数端末で使うにはアカウントへサインアップしてください)。終了コード `1`。 | ||
| - `code: "anonymous_account_not_found"` → ``Your anonymous credentials are no longer valid. Sign up to continue: arkor login --oauth``(和訳: 匿名認証情報はもう有効ではありません。続けるにはサインアップしてください)。終了コード `1`。 | ||
|
|
||
| 既知の `code` を持たないエラーは commander のデフォルトとして上流の `error` 本文をそのまま表示するので、ユーザーはクラウド API の理由を一字一句見ます。 |
| // machine. The wording aligns with `formatAnonymousAuthError` so the | ||
| // hint they see now matches the error they'd see later. | ||
| ui.log.info( | ||
| "Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.", |
| ui.log.info( | ||
| "Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.", | ||
| ); |
| } catch (err) { | ||
| // Intercept the structured anonymous-auth-state errors before | ||
| // commander's default handler converts them into a noisy stack | ||
| // trace. The helper returns a CLI-shaped string for the two known | ||
| // dead-end codes (`anonymous_token_single_device`, | ||
| // `anonymous_account_not_found`); everything else rethrows so | ||
| // commander still surfaces it. Setting `process.exitCode` (rather | ||
| // than calling `process.exit` directly) keeps the deprecation + | ||
| // telemetry-shutdown step in the `finally` block reachable. | ||
| const friendly = formatAnonymousAuthError(err); | ||
| if (friendly !== null) { | ||
| process.stderr.write(`${friendly}\n`); | ||
| process.exitCode = 1; |
…ansition Updated documentation across multiple files to clarify that anonymous workspaces are strictly single-device and that switching to OAuth does not migrate existing anonymous work. Clear guidance is provided for users on how to transition to an OAuth account for multi-device access, ensuring they understand the implications of their workspace choices.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| typeof body.user === "object" && | ||
| body.user !== null && | ||
| (body.user as { kind?: unknown }).kind === "anonymous"; |
| @@ -60,6 +60,7 @@ This means `arkor dev` is safe on a shared dev machine: another tab cannot read | |||
| | `No credentials on file — requesting an anonymous token.` | Same as above on anon-only deployments (no Auth0 advertised in `/v1/auth/cli/config`). The CLI omits the `arkor login --oauth` hint because that command would fail there. | Nothing required. | | |||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.`` | Informational follow-up after the anonymous bootstrap completes — surfaces the cloud-side identifier and where it lives (the path is the resolved `credentialsPath()`, typically `~/.arkor/credentials.json` on Linux and macOS). | Nothing required. Back up the credentials file if you want to keep using the same anonymous identity from another machine. | | |||
| | `No credentials on file — requesting an anonymous token.`(認証情報ファイルがありません。匿名トークンを要求します) | 同上だが匿名専用デプロイの場合(`/v1/auth/cli/config` で Auth0 がアドバタイズされていない)。`arkor login --oauth` は失敗するので OAuth ヒントは省かれる。 | 何もしなくてよい。 | | ||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.``(匿名 id: `<id>`。Arkor Cloud はこの id でセッション間でこのクライアントを識別します。同じ匿名 identity を維持するには認証情報ファイルを保持してください。パスは `credentialsPath()` の解決結果で、Linux と macOS では通常 `~/.arkor/credentials.json`) | 匿名ブートストラップ完了後の情報行。クラウド側の識別子と、それを保持しているファイルの場所を明示する。 | 何もしなくてよい。別マシーンから同じ匿名 identity を使いたいなら認証情報ファイルをバックアップ。 | | ||
| | ``Anonymous sessions aren't guaranteed to persist — sign in with `arkor login --oauth` to tie future work to your Arkor Cloud account.``(匿名セッションは永続性が保証されないので、今後の作業を Arkor Cloud アカウントに紐付けたいなら `arkor login --oauth` でサインインしてください) | デプロイが OAuth をサポートすると分かっているときに、成功メッセージと並んで出る永続性ナッジ。匿名作業はクラウド API 側で SLA がないので、本格的に作業する前にアップグレード経路を提示している。匿名専用デプロイでは抑制される。 | 任意。今後の作業をアカウントに紐付けたいなら `arkor login --oauth`。既存の匿名作業はその id に残り、現状マイグレーションパスはない。 | | ||
| | ``Note: anonymous accounts work on this machine only.``(OAuth サポート時には ``Run `arkor login --oauth` to sign up for multi-device access.`` が後に続く / 和訳: 注意: 匿名アカウントはこのマシーンでのみ動作します。複数端末で使うには `arkor login --oauth` でサインアップしてください) | 匿名ブートストラップ成功行と並んで出る情報行。匿名アカウントは発行マシーンに対して単一端末ガードでバインドされるので、別端末での 401 を待たず最初に制約を提示する。OAuth フレーバーのアップグレードヒントは永続性ナッジと同じ `oauthAvailable` でゲートされるので、匿名専用デプロイでは bare な fact のみが出る(`arkor login --oauth` は失敗するため)。 | 任意。OAuth サポートのデプロイでは、複数端末でアカウント付きの作業を始めたいタイミングで `arkor login --oauth` を実行。既存の匿名作業はマイグレーションできず、OAuth アカウントは新規スタート。 | |
| - on anon-only deployments: ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. This deployment does not advertise OAuth, so the only recovery is to mint a new anonymous identity (your previous workspace data cannot be recovered): arkor login --anonymous`` (exit `1`). | ||
| - `code: "anonymous_account_not_found"` → analogous OAuth-vs-anon-only split, ending in `arkor login --oauth` or `arkor login --anonymous`. | ||
|
|
||
| Errors without a known `code` (and any non-`CloudApiError` exceptions) are rethrown out of `main()`. They reach Node's top-level rejection handler via `bin.ts`, which logs them with the standard error formatting (class name + stack trace) and exits non-zero — so you'll see something like `CloudApiError: cloud-api 503` followed by a stack frame, not just the upstream `error` body. |
| - 匿名専用デプロイ: ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. This deployment does not advertise OAuth, so the only recovery is to mint a new anonymous identity (your previous workspace data cannot be recovered): arkor login --anonymous``(終了コード `1`)。 | ||
| - `code: "anonymous_account_not_found"` も同様に分岐し、末尾は `arkor login --oauth` または `arkor login --anonymous` のいずれか。 | ||
|
|
||
| 既知の `code` を持たないエラー(および `CloudApiError` 以外の例外)は `main()` から再 throw され、`bin.ts` には catch がないので Node のデフォルトのトップレベル拒否ハンドラが標準のエラーフォーマット(クラス名 + スタックトレース)で表示し、非ゼロで終了します。つまり `CloudApiError: cloud-api 503` の後にスタックフレームが見え、上流の `error` 本文だけが見えるわけではありません。 |
| | `Failed to fetch /v1/me (<status>). Token may be expired.` | `arkor whoami` | The cloud-api rejected the request with a non-200, non-426 status. Most often expired access tokens. | Re-run `arkor login` matching the current `mode`: `arkor login --oauth` for `mode: "auth0"`, `arkor login --anonymous` for `mode: "anon"` (the latter mints a new `anonymousId`, so it lands in a new workspace). The exit code stays `0` so wrapper scripts can inspect the message. | | ||
| | ``Anonymous credentials were rejected as single-device. …`` (followed by either `arkor login --oauth` or `arkor login --anonymous` depending on deployment) | `arkor whoami` and any other authenticated command | The cloud-api rejected the request with `code: "anonymous_token_single_device"` (HTTP 401 from the userAuth jti check or HTTP 409 from the rotate-jti CAS). Either the credentials file was copied to a second device, or another local process refreshed past this token within the recovery window. | Follow the command in the message: `arkor login --oauth` to sign up for an OAuth account on supporting deployments, or `arkor login --anonymous` to mint a new anonymous identity on anon-only deployments. Either path is a *new* identity; existing anonymous work cannot be migrated. The CLI exits `1`. | | ||
| | ``Your anonymous credentials are no longer valid. …`` (followed by either `arkor login --oauth` or `arkor login --anonymous`) | `arkor whoami` and any other authenticated command | The cloud-api rejected the request with `code: "anonymous_account_not_found"` (HTTP 401). The underlying `anonymous_users` row was deleted (admin / cascade / explicit revocation). | Same as above — follow the deployment-aware command in the message. The previous anonymous workspace cannot be recovered. The CLI exits `1`. | | ||
| | `CloudApiError: cloud-api <status>` (and a stack trace) | `arkor whoami` and any other authenticated command | A non-200 / non-426 cloud-api response without a structured auth-state `code` (transient 5xx, an unmapped 4xx, etc.). `cli/main.ts` rethrows it; bin.ts has no catch, so Node's default top-level rejection handler renders it. | Inspect the upstream message at the top of the trace; if it looks like a transport/server failure, retry. For an expired OAuth access token, re-run `arkor login --oauth`. For an expired anonymous token, re-run `arkor login --anonymous` (mints a new `anonymousId`). | |
| | `Failed to fetch /v1/me (<status>). Token may be expired.`(`/v1/me` の取得に失敗しました(`<status>`)。トークンが期限切れの可能性があります) | `arkor whoami` | クラウド API が 200 と 426 以外のステータスで拒否した。多くは期限切れアクセストークン。 | 現在のモードに合わせて再ログイン: OAuth セッション(`mode: "auth0"`)なら `arkor login --oauth`、匿名セッション(`mode: "anon"`)なら `arkor login --anonymous`(新しい `anonymousId` で別ワークスペースになる点に注意)。終了コードは `0` のままで、ラッパースクリプトはメッセージを検査できる。 | | ||
| | ``Anonymous credentials were rejected as single-device. …``(末尾は `arkor login --oauth` または `arkor login --anonymous`、デプロイ形態次第) | `arkor whoami` および認証付きの全コマンド | クラウド API が `code: "anonymous_token_single_device"` で拒否した(userAuth の jti チェックからの HTTP 401、または rotate-jti の CAS からの HTTP 409)。認証情報ファイルが別端末にコピーされたか、別のローカルプロセスがこのトークンを recovery window 内でローテートし越えた、のいずれか。 | メッセージ末尾のコマンドに従う: OAuth サポートのデプロイなら `arkor login --oauth`、匿名専用デプロイなら `arkor login --anonymous`。どちらも *新規* identity で、過去の匿名作業は移行できない。CLI は `1` で終了。 | | ||
| | ``Your anonymous credentials are no longer valid. …``(末尾は `arkor login --oauth` または `arkor login --anonymous`) | `arkor whoami` および認証付きの全コマンド | クラウド API が `code: "anonymous_account_not_found"` で拒否した(HTTP 401)。背後の `anonymous_users` 行が削除された(admin / cascade / 明示的な revoke)。 | 上と同じく、メッセージ末尾のデプロイ依存コマンドに従う。前の匿名ワークスペースは取り戻せない。CLI は `1` で終了。 | | ||
| | `CloudApiError: cloud-api <status>`(とスタックトレース) | `arkor whoami` および認証付きの全コマンド | 構造化された認証状態 `code` を持たない非 200 / 非 426 のクラウド API レスポンス(一過性の 5xx、未マップの 4xx など)。`cli/main.ts` がそのまま再 throw し、`bin.ts` に catch がないので Node のデフォルトのトップレベル拒否ハンドラがレンダリングする。 | スタック先頭の上流メッセージを確認し、トランスポート / サーバー障害なら再試行。OAuth アクセストークンの期限切れなら `arkor login --oauth` を再実行。匿名トークンの期限切れなら `arkor login --anonymous` を再実行(新しい `anonymousId` で別ワークスペースになる)。 | |
…dency PR #100 round 3 (Copilot, 2026-05-02 15:26 UTC). Three concrete issues to chase: 1. `whoami.ts` keyed the single-device note on `body.user.kind === "anonymous"`, but the cloud-api `/v1/me` schema doesn't guarantee a `kind` field on the response — and the existing test fixtures (and E2E response shapes) don't surface one. That meant the note silently never fired for anonymous users in practice. Switch to `creds.mode === "anon"` (already in scope from `readCredentials()`) so the discriminator is local-only and can't drift with the server schema. Update the unit test fixture to a plain user object so a regression that re-introduces a body-shape dependency would catch it. 2. The previous commit's docs claimed unmapped `CloudApiError`s reach "Node's default top-level rejection handler because `bin.ts` has no catch". That was wrong: `bin.ts:54-58` actually wraps the top-level `await main()` in a try/catch that logs `err.stack ?? err.message` to stderr and sets `process.exitCode = 1`. The explicit catch is there to dodge the bundled minified code-frame Node's default would surface, and to keep stderr flush deterministic across the supported Node range. Update both the prose paragraph and the troubleshooting table row in `docs/cli/auth.mdx` + `docs/ja/cli/auth.mdx`. 3. `docs/cli/dev.mdx` (and Japanese counterpart) had a pre-existing table row telling readers to "back up the credentials file if you want to keep using the same anonymous identity from another machine" directly above the new single-device note that says cross-device use is rejected. Reword the action text so the backup advice is scoped to local recovery on the *same* machine and points at the single-device row for cross-device guidance. 344 unit tests pass; new fixture exercises the post-regression path.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ui.log.info( | ||
| opts.oauthAvailable === true | ||
| ? ANON_SINGLE_DEVICE_NOTE_WITH_OAUTH | ||
| : ANON_SINGLE_DEVICE_NOTE, |
| ui.log.info( | ||
| oauthAvailable | ||
| ? ANON_SINGLE_DEVICE_NOTE_WITH_OAUTH | ||
| : ANON_SINGLE_DEVICE_NOTE, |
| const message = fields.error || text || `cloud-api ${res.status}`; | ||
| return new CloudApiError(res.status, message); | ||
| return new CloudApiError(res.status, message, fields.code); |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1869df3b43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| */ | ||
| async function probeOauthAvailability(): Promise<boolean> { | ||
| try { | ||
| const cfg = await fetchCliConfig(defaultArkorCloudApiUrl()); |
There was a problem hiding this comment.
Probe OAuth config against the failing deployment URL
The anonymous-auth formatter now decides between --oauth and --anonymous by always calling fetchCliConfig(defaultArkorCloudApiUrl()), but the CloudApiError being handled may come from a different base URL (for example, commands that use a non-default API endpoint). In that case this probe can inspect the wrong deployment and emit the opposite recovery path, sending users to a command that fails on their actual target deployment. The probe should use the same base URL that produced the auth error (or the active credentials’ API URL) instead of the global default.
Useful? React with 👍 / 👎.
…ating - main.ts: probe `/v1/auth/cli/config` against the credentials' own `arkorCloudApiUrl` instead of the global default, so users on a non-default deployment (or with a stale `ARKOR_CLOUD_API_URL`) get the recovery hint that matches the deployment that actually rejected them. (Codex P2 on PR #100) - main.test.ts: partial-mock `core/credentials` so `readCredentials` becomes controllable per-test, and add a case that pins the probe URL to the credentials' value rather than the env-derived default. - client.test.ts: cover `buildCloudApiError` preserving `code` (the field cli/main.ts pivots on for the friendly anon-auth-error path) and the `code === undefined` fall-through on bodies without it. - login.test.ts / dev.test.ts: add gating tests for `ANON_SINGLE_DEVICE_NOTE` mirroring the persistence-nudge tests, so the OAuth-flavoured variant only fires when `oauthAvailable === true`.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Throw a `CloudApiError` carrying the structured `code` so the | ||
| // top-level handler in `cli/main.ts` can format anonymous auth-state | ||
| // failures (`anonymous_token_single_device`, | ||
| // `anonymous_account_not_found`) into actionable guidance instead of | ||
| // a generic "Token may be expired" line. | ||
| throw await buildCloudApiError(res); |
| // them at a command that fails immediately. The matching login/dev | ||
| // surfaces, which already know `oauthAvailable`, do append the | ||
| // upgrade hint when warranted. | ||
| process.stdout.write(`\n${ANON_SINGLE_DEVICE_NOTE}\n`); |
| | ``No credentials on file — bootstrapping an anonymous session. Run `arkor login --oauth` to sign in to your account instead.`` | First `arkor dev` on this machine when the deployment advertises OAuth. The CLI bootstraps anonymous so Studio can start immediately; the message is informational, not an error. | Nothing required. To upgrade to a real account, run `arkor login --oauth` separately (it overwrites `~/.arkor/credentials.json`) and refresh Studio. | | ||
| | `No credentials on file — requesting an anonymous token.` | Same as above on anon-only deployments (no Auth0 advertised in `/v1/auth/cli/config`). The CLI omits the `arkor login --oauth` hint because that command would fail there. | Nothing required. | | ||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.`` | Informational follow-up after the anonymous bootstrap completes — surfaces the cloud-side identifier and where it lives (the path is the resolved `credentialsPath()`, typically `~/.arkor/credentials.json` on Linux and macOS). | Nothing required. Back up the credentials file if you want to keep using the same anonymous identity from another machine. | | ||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.`` | Informational follow-up after the anonymous bootstrap completes — surfaces the cloud-side identifier and where it lives (the path is the resolved `credentialsPath()`, typically `~/.arkor/credentials.json` on Linux and macOS). | Nothing required. The "stay signed in as the same anonymous identity" guidance just means: don't delete the file. The cloud-api enforces single-device on the server side, so the file is not portable. Copying it to another machine will be rejected on the next refresh, and once a refresh lands (the current SDK doesn't auto-refresh, but it's on the roadmap), even an older backup of the file on this machine becomes stale because the server's `latest_jti` has moved on. Treat the file as live state, not as something to back up and restore. | |
| | ``No credentials on file — bootstrapping an anonymous session. Run `arkor login --oauth` to sign in to your account instead.``(認証情報ファイルがありません。匿名セッションをブートストラップします。アカウントでサインインしたい場合は `arkor login --oauth` を実行してください) | OAuth をアドバタイズしているデプロイでこのマシーン初回の `arkor dev`。Studio をすぐ起動できるよう CLI が匿名でブートストラップしている旨の案内で、エラーではない。 | 何もしなくてよい。本物のアカウントにアップグレードしたいなら、別途 `arkor login --oauth` を実行(`~/.arkor/credentials.json` を上書き)して Studio をリロード。 | | ||
| | `No credentials on file — requesting an anonymous token.`(認証情報ファイルがありません。匿名トークンを要求します) | 同上だが匿名専用デプロイの場合(`/v1/auth/cli/config` で Auth0 がアドバタイズされていない)。`arkor login --oauth` は失敗するので OAuth ヒントは省かれる。 | 何もしなくてよい。 | | ||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.``(匿名 id: `<id>`。Arkor Cloud はこの id でセッション間でこのクライアントを識別します。同じ匿名 identity を維持するには認証情報ファイルを保持してください。パスは `credentialsPath()` の解決結果で、Linux と macOS では通常 `~/.arkor/credentials.json`) | 匿名ブートストラップ完了後の情報行。クラウド側の識別子と、それを保持しているファイルの場所を明示する。 | 何もしなくてよい。別マシーンから同じ匿名 identity を使いたいなら認証情報ファイルをバックアップ。 | | ||
| | ``Anonymous id: <id> — Arkor Cloud uses this id to recognise this client across sessions. Keep `<home>/.arkor/credentials.json` to stay signed in as the same anonymous identity.``(匿名 id: `<id>`。Arkor Cloud はこの id でセッション間でこのクライアントを識別します。同じ匿名 identity を維持するには認証情報ファイルを保持してください。パスは `credentialsPath()` の解決結果で、Linux と macOS では通常 `~/.arkor/credentials.json`) | 匿名ブートストラップ完了後の情報行。クラウド側の識別子と、それを保持しているファイルの場所を明示する。 | 何もしなくてよい。「同じ匿名 identity を維持」の案内は「ファイルを削除しない」という意味です。サーバー側の単一端末ガードのため、このファイルは可搬性がありません: 別マシーンへコピーすると次回 refresh で拒否され、一度 refresh が走ると(現在の SDK は自動 refresh しませんがロードマップに乗っています)同じマシーン上の古いバックアップですらサーバーの `latest_jti` が進んでしまうため stale になります。バックアップして復旧する対象ではなく、live state として扱ってください。 | |
| - on OAuth-supporting deployments: ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. Sign up for an account that supports multiple devices: arkor login --oauth`` (exit `1`). | ||
| - on anon-only deployments: ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. This deployment does not advertise OAuth, so the only recovery is to mint a new anonymous identity (your previous workspace data cannot be recovered): arkor login --anonymous`` (exit `1`). | ||
| - `code: "anonymous_account_not_found"` → analogous OAuth-vs-anon-only split, ending in `arkor login --oauth` or `arkor login --anonymous`. | ||
|
|
||
| Errors without a known `code` (and any non-`CloudApiError` exceptions) are rethrown out of `main()`. `bin.ts` wraps the top-level `await main(...)` in a try/catch that logs `err.stack ?? err.message` to stderr and sets `process.exitCode = 1` — so you'll see something like `CloudApiError: cloud-api 503` followed by a stack frame, not just the upstream `error` body. (The explicit catch is there to avoid the bundled minified frame Node's default unhandled-rejection handler would surface, and to keep the stderr flush deterministic across the supported Node range.) |
| - 期限切れまたは無効化されたトークンはクラウド API からの非 2xx として現れます。`arkor whoami` は汎用的な「Token may be expired」を出さなくなり、代わりに上流の `code`(あれば)を持った `CloudApiError` を投げ、`cli/main.ts` のトップレベルハンドラが既知 2 種の認証状態 `code` を実行可能なガイダンスへ整形します。整形前に `main()` は `/v1/auth/cli/config` を best-effort で取得し、リカバリーヒントをデプロイ形態に揃えます。 | ||
| - `code: "anonymous_token_single_device"`: | ||
| - OAuth サポートのデプロイ: ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. Sign up for an account that supports multiple devices: arkor login --oauth``(終了コード `1`)。 | ||
| - 匿名専用デプロイ: ``Anonymous credentials were rejected as single-device. Anonymous accounts only work on one machine. This deployment does not advertise OAuth, so the only recovery is to mint a new anonymous identity (your previous workspace data cannot be recovered): arkor login --anonymous``(終了コード `1`)。 | ||
| - `code: "anonymous_account_not_found"` も同様に分岐し、末尾は `arkor login --oauth` または `arkor login --anonymous` のいずれか。 | ||
|
|
||
| 既知の `code` を持たないエラー(および `CloudApiError` 以外の例外)は `main()` から再 throw されます。`bin.ts` のトップレベル `await main(...)` は try/catch で囲まれており、`console.error(err.stack ?? err.message)` で stderr に出力した上で `process.exitCode = 1` をセットします。つまり `CloudApiError: cloud-api 503` の後にスタックフレームが見え、上流の `error` 本文だけが見えるわけではありません。明示的に catch しているのは、Node のデフォルトの unhandled rejection ハンドラがバンドル後の minified なコードフレームを出すのを避け、サポート対象の Node バージョン全体で stderr の flush を確定させるためです。 |
This pull request updates both English and Japanese documentation to clarify the single-device limitation of anonymous accounts and improves the error handling and user guidance around authentication failures. The changes ensure users are explicitly informed that anonymous workspaces are tied to the originating machine, and provide actionable messages when authentication issues occur, especially around multi-device usage and anonymous account expiration.
Anonymous account limitations and user guidance:
arkor login --oauthfor multi-device access. [1] [2] [3] [4] [5] [6] [7]Authentication error handling improvements:
anonymous_token_single_device,anonymous_account_not_found) returned by the cloud API, and how the CLI turns these into actionable, user-facing guidance (with exit code 1), both in English and Japanese. [1] [2] [3] [4] [5]Other clarifications:
These changes improve user understanding of anonymous workspace constraints and make authentication failures easier to diagnose and resolve.Updated the documentation to emphasize that anonymous workspaces are single-device only, and provided clear instructions for users on how to transition to an OAuth account for multi-device access. This includes updates to the README and CLI documentation, ensuring users are aware of the constraints and the necessary steps to retain their work across devices.