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 を確定させるためです。 |
- whoami.ts: target the credentials' own `arkorCloudApiUrl` for `/v1/me` instead of `defaultArkorCloudApiUrl()`. Without this a stale `ARKOR_CLOUD_API_URL` (or a token issued against a non-default endpoint) would route the call to the wrong cloud-api, and the dead-end formatter in `cli/main.ts` could surface single-device guidance for a token that's still valid on its real deployment. Auth0 creds don't pin a URL and fall through to the env default. (Copilot on PR #100) - whoami.ts: emit `ANON_SINGLE_DEVICE_NOTE` on stderr, not stdout. `arkor whoami` writes the user JSON + `Orgs:` line to stdout; mixing human-oriented prose into that stream broke wrappers grepping/jq-ing the output. stdout now stays a stable, machine-parseable shape. (Copilot on PR #100) - whoami.test.ts: split the existing assertion onto stdout/stderr to match the new contract, and add a case that pins the resolved baseUrl to the credentials' value over the env-derived default. - docs/cli/dev.mdx + ja: broaden the "rejected on the next refresh" wording. The `latest_jti` mismatch fires from `userAuth` on every authenticated request, not just refresh, so the original phrasing understated when failures land. The current SDK still doesn't rotate the jti on its own (auto-refresh is on the roadmap) so a copied file often appears to work for a while; the new text spells that out without implying refresh is the only trigger. - docs/cli/auth.mdx + ja: replace the stale "Other 4xx/5xx exit `0` with `Token may be expired`" exit-code line with the new contract (rethrow → `bin.ts` stack-trace → exit `1`), and split out the anonymous-auth dead-end exit `1` row so the section no longer contradicts the Token-expiry / Common-errors paragraphs below.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Probe deployment OAuth status only on the dead-end path so we | ||
| // don't add a network round-trip to every successful command. | ||
| // Failure collapses to "no OAuth", which steers the formatter at | ||
| // the universally-available `arkor login --anonymous` recovery. | ||
| const oauthAvailable = await probeOauthAvailability(); | ||
| const friendly = formatAnonymousAuthError(err, { oauthAvailable }); |
| // The note goes to stderr so wrapper scripts piping `arkor whoami` | ||
| // through `jq` (or grepping the JSON / `Orgs:` line on stdout) | ||
| // aren't broken by human-oriented prose appearing in their data | ||
| // stream. stdout stays a stable, machine-parseable shape. | ||
| process.stderr.write(`\n${ANON_SINGLE_DEVICE_NOTE}\n`); |
| // 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); |
…rror
- main.ts + auth0.ts: bound the OAuth-availability probe with
`AbortSignal.timeout(3000)`. The probe runs *after* a command has
already failed, so a degraded `/v1/auth/cli/config` would otherwise
leave the user staring at nothing while the recovery hint waits on a
hung HTTP call. `fetchCliConfig` now takes an `{ fetch, signal }`
options object; the previous positional `fetchImpl` shape (still used
by the older auth0.test.ts cases) was migrated.
- whoami.ts: gate `ANON_SINGLE_DEVICE_NOTE` on `process.stdout.isTTY &&
process.stderr.isTTY`. Pipelines (`arkor whoami | jq`) drop
`stdout.isTTY`; CI runners that treat stderr-on-success as a warning
marker drop `stderr.isTTY`. Both groups now see clean output. The
note still goes to stderr in the interactive case so stdout's
machine-parseable JSON / `Orgs:` shape is preserved.
- whoami.test.ts: split the existing assertion into TTY / non-TTY
cases — the first forces both streams to TTY before invoking
runWhoami, the second pins them to false to cover CI/script use.
- bin.ts: render `CloudApiError` as just `err.message`; keep
`err.stack ?? err.message` for unknown `Error`s. Routine HTTP
failures (expired OAuth session, transient 5xx, unmapped 4xx) no
longer dump a full stack frame, so wrappers and humans see one-line
output that matches the upstream cloud-api message; genuine SDK bugs
still surface a stack so they're filable.
- docs/cli/auth.mdx + ja: update the exit-codes paragraph, the
Token-expiry write-up, and the Common-errors row to describe the
new bin.ts rendering rule (message-only for CloudApiError) and the
TTY gate on whoami's anonymous note.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
packages/arkor/src/cli/commands/whoami.ts:40
- This new direct
createClient()call reintroduces the cloud-api client's knownonDeprecationbug: passingrecordDeprecationdirectly causes the client to log[@arkor/cloud-api-client] onDeprecation handler threw; ignoring:on deprecated responses because it treats the callback'svoidreturn as thenable-checkable.studio/server.tsalready wrapsrecordDeprecationand returnsnullto avoid that noise;whoamishould use the same workaround or deprecated/v1/meresponses will start emitting an unexpected stderr line.
const rpc = createClient({
baseUrl,
token: () =>
creds.mode === "anon" ? creds.token : creds.accessToken,
clientVersion: SDK_VERSION,
onDeprecation: recordDeprecation,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| `arkor dev` は **Studio** と呼ばれるローカル Web UI を `http://localhost:4000` で開きます。初回起動時に使い捨ての匿名ワークスペースをプロビジョニングするので、すぐに実際のトレーニング実行を開始できます。 | ||
|
|
||
| 後からアカウントに紐付けたい場合は `arkor login --oauth` を実行してください。 | ||
| 匿名ワークスペースは `arkor dev` を最初に実行したマシーン専用です。OAuth に切り替えても既存の匿名ワークスペースは引き継げません。`arkor login --oauth` は `~/.arkor/credentials.json` を新しい OAuth identity で上書きし、それ以降の作業は OAuth アカウントに紐付きますが、既存の匿名ジョブや org は発行元の credentials ファイルからしか辿れません。複数端末で作業したくなった時点で `arkor login --oauth` を実行し、アカウント付きの新しいワークスペースで再スタートしてください。 |
|
|
||
| Anonymous credentials let you try Arkor without an account: training runs, jobs, and any work you do are tied to the local machine via the anonymous token. The anonymous path always mints a brand-new token (and a new `anonymousId`) and overwrites `~/.arkor/credentials.json`, so re-running `arkor login --anonymous` does not refresh the existing identity. Switching to OAuth (`arkor login --oauth`, or selecting `OAuth (browser)` in the picker) overwrites the credentials file the same way and does not migrate prior anonymous workspaces or jobs into the account. Merging anonymous work into an OAuth account once you sign in is on the roadmap; until that lands, run `arkor login --oauth` **before** you start the runs you want associated with the account. | ||
|
|
||
| Anonymous accounts are intentionally **single-device**: the cloud-api binds the issued token to the machine that received it (via a `latest_jti` rotation each time the SDK refreshes the token), so copying `~/.arkor/credentials.json` to a second machine and using it from both will trip the server's single-device guard. The losing client receives an HTTP 401 / 409 with `code: "anonymous_token_single_device"`, which `cli/main.ts` surfaces as actionable guidance; deletion of the underlying anonymous row surfaces as `code: "anonymous_account_not_found"` the same way. The recovery hint is deployment-aware: on OAuth-supporting deployments the CLI points at `arkor login --oauth` so you can sign up for an account that supports multiple devices, while on anon-only deployments (where OAuth is not configured) it points at `arkor login --anonymous` instead — `--oauth` would fail there, and minting a fresh anonymous identity is the only recovery available. Note that **neither path migrates existing anonymous work** into the new identity; the previous workspace stays reachable only from the credentials file that issued it. |
|
|
||
| 匿名認証は、アカウントなしで Arkor を試すためのものです。学習、ジョブ、その他の作業は匿名トークンを介してローカルマシーンに紐づきます。あとで OAuth に切り替える(`arkor login --oauth`、またはピッカーから `OAuth (browser)` を選ぶ)と認証情報ファイルは差し替えられますが、作業は移行されません。匿名で学習したものを残したいなら、学習を始める前に `arkor login --oauth` を走らせてください。 | ||
|
|
||
| 匿名アカウントは設計上 **単一端末専用** です。クラウド API は発行したマシーンに対してトークンを `latest_jti` ローテーションで束ねるので、`~/.arkor/credentials.json` を別マシーンにコピーして両方から使うとサーバー側の単一端末ガードに引っかかります。負けた側のクライアントは HTTP 401 / 409 を `code: "anonymous_token_single_device"` で受け取り、`cli/main.ts` がそれを実行可能なガイダンスとして整形します。匿名行そのものが削除された場合は `code: "anonymous_account_not_found"` も同じ仕組みで surface されます。リカバリー提案は **デプロイ形態に応じて分岐** します。OAuth がサポートされているデプロイでは `arkor login --oauth` を案内して複数端末対応のアカウントへサインアップする経路を示し、匿名専用デプロイ(OAuth 未設定)では代わりに `arkor login --anonymous` を案内します。後者で `--oauth` を出すとそのコマンドはそのまま失敗するので、新規匿名 identity の発行が唯一のリカバリ手段になるためです。**いずれのパスも、過去の匿名作業は移行されません**。前のワークスペースには発行元の credentials ファイルからしか到達できません。 |
| Anonymous tokens are tied to this machine. Copying | ||
| \`~/.arkor/credentials.json\` to another device will be rejected as a | ||
| single-device policy violation. When you're ready for an account-backed | ||
| workspace that follows you across devices, run: | ||
|
|
||
| \`\`\` | ||
| npx arkor login | ||
| npx arkor login --oauth | ||
| \`\`\` |
| issues a throwaway token tied to a brand-new personal org. **It only | ||
| works on the machine where it was issued.** Copying | ||
| `~/.arkor/credentials.json` to a second machine and using it from both | ||
| will trip the server's single-device guard, and one of the two will be | ||
| locked out with `anonymous_token_single_device`. On OAuth-supporting | ||
| deployments the CLI directs the user at `arkor login --oauth` to start | ||
| a real account; on anon-only deployments it points at `arkor login | ||
| --anonymous` instead, since `--oauth` would fail there. Either path is |
| `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, and switching to OAuth does not migrate them: `arkor login --oauth` overwrites `~/.arkor/credentials.json` with a fresh OAuth identity that any *future* work will be associated with, but existing anonymous jobs and orgs stay reachable only from the credentials file that issued them. Run `arkor login --oauth` whenever you're ready to start an account-backed workspace that follows you across devices. |
…docs
The auth/dev docs already explain that single-device enforcement comes
from a `latest_jti` mismatch on every authenticated request, but the
summary copy in README.md, README.ja.md, packages/arkor/README.md,
packages/cli-internal/src/templates.ts, and the one-paragraph blurb in
docs/cli/auth.mdx (+ JA mirror) all overstated the timing — saying a
copied credentials.json "will be rejected" immediately or "only works
on the machine where it was issued". The current SDK doesn't
auto-refresh anonymous tokens, so client-side rotation never happens
today; a copy on a second machine often keeps working until either
the issuing user runs `arkor login --anonymous` again (overwriting
the stored jti) or auto-refresh ships and rotates server-side. Once
that happens, every other copy fails with `anonymous_token_single_device`
on its next call.
Updated wording across these surfaces to:
1. Describe the mechanism as "userAuth checks `latest_jti` on every
request" (matches the deployed reality), not "rotated each refresh"
(which contradicts the same-page note that auto-refresh is not
wired).
2. Surface the two timing regimes — "today: copies tend to keep
working until a manual relogin or admin rotation", "after
auto-refresh ships: any rotation invalidates other copies on the
next call" — so users aren't surprised when a copied file appears
to work for a while.
3. Soften "only works on this machine" / "will be rejected" to
"designed for one machine" / "isn't a supported workflow" while
keeping the policy clear.
The user-facing CLI message string `ANON_SINGLE_DEVICE_NOTE`
("Note: anonymous accounts work on this machine only.") is left
untouched: it's a short policy hint emitted at issuance, not a claim
about timing.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b64c560113
ℹ️ 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".
| } catch { | ||
| return false; |
There was a problem hiding this comment.
Distinguish probe failure from anon-only deployments
probeOauthAvailability() maps every /v1/auth/cli/config failure to false, and formatAnonymousAuthError treats false as “OAuth is not advertised.” In the common case where the config probe times out or transiently fails on an OAuth-enabled deployment, users are shown a definitive anon-only diagnosis and steered to arkor login --anonymous, which can hide the correct recovery path (--oauth) for that deployment. Return an “unknown” state (or separate error flag) so transient probe failures don't produce a false deployment claim.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| Both anonymous paths surface the new `anonymousId` and an explanation that the same id is how Arkor Cloud recognises this client across sessions, though the line shape differs by entry point. `arkor login` (`--anonymous` flag or picker → **Anonymous**) prints `Anonymous id: <id>` as the spinner stop and then a separate info line saying that keeping the credentials file (`credentialsPath()`, typically `~/.arkor/credentials.json` on Linux and macOS) is what preserves the identity. `arkor dev`'s auto-bootstrap skips the spinner and emits a single info line that already embeds the id and the same explanation. The picker → **Anonymous** path additionally surfaces a one-line warn alongside the success message — ``Anonymous sessions aren't guaranteed to persist — sign in with `arkor login --oauth` to tie future work to your Arkor Cloud account.`` — so the upgrade hint is visible at issuance time. The explicit `--anonymous` shortcut suppresses that warn because it skips the `/v1/auth/cli/config` fetch and so cannot tell whether `arkor login --oauth` would succeed on this deployment; pointing at it on a rare anon-only deployment would steer users at a command that fails. | ||
|
|
||
| Every anonymous issuance path also surfaces the single-device limitation as a separate info line. The exact wording is gated on `oauthAvailable` (same contract as the persistence nudge). On OAuth-supporting deployments callers see the upgrade-flavoured ``Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.``, while on anon-only deployments (and on `arkor login --anonymous` where the cfg fetch is skipped intentionally) callers see only the bare ``Note: anonymous accounts work on this machine only.``, so users on those deployments aren't pointed at a command that would fail. `arkor whoami` on an anonymous identity also emits the bare variant on stderr — and only when both stdout and stderr are TTYs, so a wrapper redirecting `arkor whoami | jq` (loses `stdout.isTTY`) or a CI runner that treats stderr-on-success as a warning marker (loses `stderr.isTTY`) sees clean output. The note is purely a UX hint; it doesn't gate any behaviour, and `whoami`'s machine-readable JSON shape on stdout never changes. |
|
|
||
| どちらの匿名パスでも、新しい `anonymousId` と「同じ id がセッションをまたいで Arkor Cloud がこのクライアントを認識するための識別子である」旨の説明を必ず surface します。ただし出力の形は入口によって異なります。`arkor login`(`--anonymous` フラグまたはピッカー → **Anonymous**)はスピナーの停止行として `Anonymous id: <id>` を出し、続けて「認証情報ファイル(`credentialsPath()`、Linux と macOS では通常 `~/.arkor/credentials.json`)を保持していれば同じ identity を維持できる」旨の info 行を別行で出します。`arkor dev` の自動ブートストラップはスピナーを使わず、id と同じ説明を 1 本の info 行にまとめて出します。ピッカー → **Anonymous** のパスでは、さらに成功メッセージと並んで 1 行の warn(``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` でサインインしてください)が出るので、発行時点でアップグレードのヒントが見えます。明示的な `--anonymous` ショートカットでは `/v1/auth/cli/config` の取得をスキップしているため `arkor login --oauth` がそのデプロイで成功するかわからず、稀に存在する匿名専用デプロイで失敗するコマンドへユーザーを誘導しないよう、warn は意図的に抑制されます。 | ||
|
|
||
| 匿名発行のあらゆる入口で、単一端末の制約も別行の info として surface します。実際の文言は永続性ナッジと同じ `oauthAvailable` のゲーティング契約に従って分岐します。OAuth サポートのデプロイでは ``Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.``(和訳: 注意: 匿名アカウントはこのマシーンでのみ動作します。複数端末で使うには `arkor login --oauth` でサインアップしてください)が出ますが、匿名専用デプロイ、および `/v1/auth/cli/config` の取得を意図的にスキップする `arkor login --anonymous` の経路では、bare な ``Note: anonymous accounts work on this machine only.`` だけが出ます。失敗確実なコマンドへユーザーを誘導しないためです。`arkor whoami` も匿名 identity に対しては bare 版のみを stderr に出します。さらに stdout / stderr の両方が TTY のときに限るので、`arkor whoami | jq`(stdout が TTY でなくなる)や、stderr 出力をすべて警告扱いする CI ランナー(stderr が TTY でなくなる)では note は出ません。あくまで UX のヒントであり、どの挙動もゲートしません。stdout 側の機械可読 JSON は常に同じ形です。 |
Codex P2 (PR #100): probeOauthAvailability collapsed every probe failure to false, and formatAnonymousAuthError treated false as "OAuth is not advertised". On a transient probe failure (timeout, network blip, malformed cfg) against an OAuth-supporting deployment, the dead-end formatter would confidently steer users at arkor login --anonymous and hide the real recovery (--oauth). - main.ts: probe now returns the tri-state OauthAvailability ("available" | "absent" | "unknown"), and the call site translates to oauthAvailable: true | false | undefined for the formatter so "absent" (cfg fetched, no Auth0) and "unknown" (cfg fetch failed) no longer collide. - anonymous-auth-error.ts: split the oauthAvailable === false branch from the new oauthAvailable === undefined branch. The latter hedges with both commands and an explicit "Couldn't reach the deployment to confirm whether OAuth is offered" header so users aren't told the deployment is anon-only when we couldn't actually verify. The OAuth-confirmed and absent branches keep their previous output. - main.test.ts + anonymous-auth-error.test.ts: rename the previous "treats undefined as anon-only" cases to "hedges with both commands when probe is inconclusive", add the same coverage for anonymous_account_not_found, and assert the new wording does NOT claim the deployment is anon-only. Copilot (PR #100, EN + JA): docs/cli/auth.mdx claimed arkor whoami's stdout stays "machine-readable JSON" and used arkor whoami | jq as the motivating example. The actual stdout is JSON.stringify(user, …) followed by an optional human-readable Orgs: <slug>, … line, which isn't a valid JSON stream end-to-end. - docs/cli/auth.mdx + ja: drop the jq example, describe the real shape (JSON head + optional Orgs tail), and warn against piping the full output through a strict JSON parser. - whoami.ts: the inline TTY-gate comment was inheriting the same framing. Updated it to match the doc and to spell out that stdout isn't a strict-JSON stream.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function isAnonymousAuthDeadEnd(err: unknown): err is CloudApiError { | ||
| return ( | ||
| err instanceof CloudApiError && | ||
| (err.code === ANONYMOUS_TOKEN_SINGLE_DEVICE || | ||
| err.code === ANONYMOUS_ACCOUNT_NOT_FOUND) | ||
| ); |
| if (err.code === ANONYMOUS_TOKEN_SINGLE_DEVICE) { | ||
| if (ctx.oauthAvailable === true) { | ||
| return [ | ||
| "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", | ||
| ].join("\n"); |
| if (err.code === ANONYMOUS_ACCOUNT_NOT_FOUND) { | ||
| if (ctx.oauthAvailable === true) { | ||
| return [ | ||
| "Your anonymous credentials are no longer valid.", | ||
| "Sign up to continue:", | ||
| "", | ||
| " arkor login --oauth", | ||
| ].join("\n"); |
| // The unknown-state branch surfaces both commands so users on an | ||
| // OAuth-supporting deployment aren't denied the correct recovery | ||
| // just because the config endpoint timed out. The order points at | ||
| // `--oauth` first because it covers the majority of deployments; | ||
| // anon-only users will get a clean "OAuth is not configured" error | ||
| // and can fall through to the second command. | ||
| const unknownTail = [ | ||
| "Couldn't reach the deployment to confirm whether OAuth is offered. Try the OAuth path first; if it fails with `OAuth is not configured`, fall through to the anonymous path:", | ||
| "", | ||
| " arkor login --oauth", | ||
| " arkor login --anonymous", |
…ints Copilot (PR #100): two findings about the anonymous-auth-error helpers. 1. `isAnonymousAuthDeadEnd` was exported but no retry loop consumed it. `createTrainer().wait()` happily reconnected on every failure, including dead-end auth errors where retrying never recovers (`anonymous_token_single_device`, `anonymous_account_not_found`). The reconnect budget would burn through and bury the actionable recovery hint that `cli/main.ts` formats from the same error. - trainer.ts: `handleFailure` now checks `isAnonymousAuthDeadEnd` up front and rethrows immediately on a hit. The error bubbles unchanged to `cli/main.ts`'s top-level handler. - trainer.test.ts: new "fails fast on anonymous-auth dead-end errors without retrying" case asserts exactly one stream-open attempt and that the original CloudApiError is what surfaces. 2. The formatter recommended `arkor login --oauth` even in CI, but `runLogin()` rejects `--oauth` outright when `process.env.CI` is set (PKCE needs a browser callback CI runners can't satisfy). A dead-end in a CI job on an OAuth-supporting deployment was being pointed at a guaranteed failure. - anonymous-auth-error.ts: ctx now carries `inCi?: boolean`, defaulting to `Boolean(process.env.CI)` for callers (cli/main.ts) that don't plumb it. In CI: * the OAuth-confirmed branch points at `arkor login --anonymous` and tells the user where `--oauth` would actually run (a developer machine); * the unknown-state hedge drops the OAuth half entirely, since suggesting it would just send the runner at a dead command. The OAuth-absent branch is unchanged (already `--anonymous`). - anonymous-auth-error.test.ts: beforeEach scrubs CI to keep existing assertions deterministic; new cases cover the OAuth-confirmed and unknown-state CI branches plus the env auto-read.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| `arkor dev` は **Studio** と呼ばれるローカル Web UI を `http://localhost:4000` で開きます。初回起動時に使い捨ての匿名ワークスペースをプロビジョニングするので、すぐに実際のトレーニング実行を開始できます。 | ||
|
|
||
| 後からアカウントに紐付けたい場合は `arkor login --oauth` を実行してください。 | ||
| 匿名ワークスペースはこのマシーンに紐づきます。クラウド API はサーバー側で単一端末ガードを強制しているので、`~/.arkor/credentials.json` を別端末にコピーする運用は想定外です。各匿名ワークスペースは発行されたマシーンで使う前提です。今のところ CLI は匿名トークンを自動 refresh しないので、コピーがしばらく動いてしまうことはあります。発行ユーザーがもう一度 `arkor login --anonymous` を実行する(または自動 refresh が入って保存中の jti が進む)と、そのタイミングで他の全コピーは次回呼び出しから単一端末エラーで失敗するようになります。OAuth に切り替えても既存の匿名ワークスペースは引き継げません。`arkor login --oauth` は `~/.arkor/credentials.json` を新しい OAuth identity で上書きし、それ以降の作業は OAuth アカウントに紐付きますが、既存の匿名ジョブや org は発行元の credentials ファイルからしか辿れません。複数端末で作業したくなった時点で `arkor login --oauth` を実行し、アカウント付きの新しいワークスペースで再スタートしてください。 |
| "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", |
| "Mint a new anonymous identity to continue (your previous workspace data cannot be recovered):", | ||
| "", | ||
| " arkor login --anonymous", |
| be migrated, so future work created after sign-in is what ends up under | ||
| the account. |
| `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. The cloud-api enforces a single-device guard server-side, so `~/.arkor/credentials.json` shouldn't be copied to a second machine — each anonymous workspace is meant to live where it was issued. Today the CLI doesn't auto-refresh the anonymous token, so a copy may keep working for a while and only start failing once the issuing user runs `arkor login --anonymous` again (or auto-refresh ships and rotates the stored jti); when that happens, every other copy starts failing on its next call with a single-device error. Switching to OAuth does not migrate prior anonymous work: `arkor login --oauth` overwrites `~/.arkor/credentials.json` with a fresh OAuth identity that any *future* work will be associated with, but existing anonymous jobs and orgs stay reachable only from the credentials file that issued them. Run `arkor login --oauth` whenever you're ready to start an account-backed workspace that follows you across devices. |
Copilot (PR #100, 5 hits): re-issuing credentials alone is not enough to resume work in an existing project directory. `ensureProjectState()` in `packages/arkor/src/core/projectState.ts` reads `.arkor/state.json` and reuses it unchanged when present, so after either path of recovery (`arkor login --anonymous` re-mint or `arkor login --oauth` switch) commands in the same directory keep targeting the previous identity's `(orgSlug, projectSlug)`. Without the state-reset step the recovery appears not to take effect: the user gets repeat 401s, or worse, silently writes into a workspace they cannot reach. - anonymous-auth-error.ts: introduce a STATE_RESET_NOTE constant and append it to every recovery branch (single-device + account-not-found, for `oauthAvailable` true / false / undefined / inCi). The note tells users to delete `.arkor/state.json` before resuming, or to run `arkor init` for an OAuth account. - anonymous-auth-error.test.ts: add `expect(out!).toMatch(/.arkor/state.json/)` assertions on the OAuth-confirmed and OAuth-absent branches so a future change cannot silently strip the note. - README.md: spell out that switching to OAuth does not auto-route subsequent runs in an existing project directory; users must delete `.arkor/state.json` (or run `arkor init` for the new account) before the new identity takes effect. Also drops the leftover em-dash on this paragraph. - README.ja.md: JP mirror of the same correction. - packages/arkor/README.md: add a separate paragraph describing the state-reset step so the package README is consistent with the root README. Replaces the em-dash on "Either path is a *new* identity" with a period. - packages/cli-internal/src/templates.ts: same fix in the scaffolded README, including a copy-pasteable `rm -rf .arkor/state.json && npx arkor init` recovery hint.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ecce1e684
ℹ️ 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".
| creds?.mode === "anon" && creds.arkorCloudApiUrl | ||
| ? creds.arkorCloudApiUrl | ||
| : defaultArkorCloudApiUrl(); |
There was a problem hiding this comment.
Probe OAuth on the endpoint that raised the auth error
probeOauthAvailability() now always prefers creds.arkorCloudApiUrl, but several CLI flows (e.g. arkor start via createTrainer) still call cloud APIs against defaultArkorCloudApiUrl() at runtime, so the dead-end error can come from a different deployment than the one being probed. In that case the formatter can recommend the wrong recovery command (--oauth vs --anonymous) for the endpoint that actually failed. Fresh evidence: createTrainer continues to bind baseUrl from defaultArkorCloudApiUrl() (not credential URL), so this mismatch is still reachable when ARKOR_CLOUD_API_URL differs from stored anonymous credentials.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| `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. The cloud-api enforces a single-device guard server-side, so `~/.arkor/credentials.json` shouldn't be copied to a second machine. Each anonymous workspace is meant to live where it was issued. Today the CLI doesn't auto-refresh the anonymous token, so a copy may keep working for a while and only start failing once the issuing user runs `arkor login --anonymous` again (or auto-refresh ships and rotates the stored jti); when that happens, every other copy starts failing on its next call with a single-device error. Switching to OAuth does not migrate prior anonymous work: `arkor login --oauth` overwrites `~/.arkor/credentials.json` with a fresh OAuth identity, but existing anonymous jobs and orgs stay reachable only from the credentials file that issued them. **The CLI also won't automatically route runs in an existing project directory to the new identity**: `.arkor/state.json` pins the directory to the old `(orgSlug, projectSlug)`, so commands here will keep targeting the previous workspace until you delete that file (then re-run, or run `arkor init` for the OAuth account). Run `arkor login --oauth` whenever you're ready to start an account-backed workspace that follows you across devices. |
| `arkor dev` は **Studio** と呼ばれるローカル Web UI を `http://localhost:4000` で開きます。初回起動時に使い捨ての匿名ワークスペースをプロビジョニングするので、すぐに実際のトレーニング実行を開始できます。 | ||
|
|
||
| 後からアカウントに紐付けたい場合は `arkor login --oauth` を実行してください。 | ||
| 匿名ワークスペースはこのマシーンに紐づきます。クラウド API はサーバー側で単一端末ガードを強制しているので、`~/.arkor/credentials.json` を別端末にコピーする運用は想定外です。各匿名ワークスペースは発行されたマシーンで使う前提です。今のところ CLI は匿名トークンを自動 refresh しないので、コピーがしばらく動いてしまうことはあります。発行ユーザーがもう一度 `arkor login --anonymous` を実行する(または自動 refresh が入って保存中の jti が進む)と、そのタイミングで他の全コピーは次回呼び出しから単一端末エラーで失敗するようになります。OAuth に切り替えても既存の匿名ワークスペースは引き継げません。`arkor login --oauth` は `~/.arkor/credentials.json` を新しい OAuth identity で上書きしますが、既存の匿名ジョブや org は発行元の credentials ファイルからしか辿れません。**さらに、既存のプロジェクトディレクトリでの実行は自動的に新しい identity 側へ切り替わりません**: `.arkor/state.json` が古い `(orgSlug, projectSlug)` をピンしているため、そのファイルを削除する(その後再実行するか、OAuth アカウントなら `arkor init` を走らせる)まで、このディレクトリのコマンドは前のワークスペースを参照し続けます。複数端末で作業したくなった時点で `arkor login --oauth` を実行し、アカウント付きの新しいワークスペースで再スタートしてください。 |
|
|
||
| Anonymous credentials let you try Arkor without an account: training runs, jobs, and any work you do are tied to the local machine via the anonymous token. The anonymous path always mints a brand-new token (and a new `anonymousId`) and overwrites `~/.arkor/credentials.json`, so re-running `arkor login --anonymous` does not refresh the existing identity. Switching to OAuth (`arkor login --oauth`, or selecting `OAuth (browser)` in the picker) overwrites the credentials file the same way and does not migrate prior anonymous workspaces or jobs into the account. Merging anonymous work into an OAuth account once you sign in is on the roadmap; until that lands, run `arkor login --oauth` **before** you start the runs you want associated with the account. | ||
|
|
||
| Anonymous accounts are intentionally **single-device**: the cloud-api stores a `latest_jti` per anonymous user and `userAuth` rejects every authenticated request whose JWT carries a different jti. The jti only changes when the token rotates, so the practical timing has two regimes. **Today** the CLI does not auto-refresh anonymous tokens (that wiring lives in `@arkor/cloud-api-client`'s `getToken()` and is on the [SDK roadmap](/roadmap)), so no rotation ever happens client-side and a copy of `~/.arkor/credentials.json` on a second machine usually keeps working — until the issuing user explicitly mints a new identity (`arkor login --anonymous` overwrites the file with a fresh jti) or an admin rotates server-side, at which point every other copy starts failing on its next call. **Once auto-refresh ships**, any refresh rotates `latest_jti` immediately, so the older-jti copy on any other device (or an older backup of the same file on this machine) starts failing on its next call without warning. Either way, the losing client receives an HTTP 401 / 409 with `code: "anonymous_token_single_device"`, which `cli/main.ts` surfaces as actionable guidance; deletion of the underlying anonymous row surfaces as `code: "anonymous_account_not_found"` the same way. The recovery hint is deployment-aware: on OAuth-supporting deployments the CLI points at `arkor login --oauth` so you can sign up for an account that supports multiple devices, while on anon-only deployments (where OAuth is not configured) it points at `arkor login --anonymous` instead — `--oauth` would fail there, and minting a fresh anonymous identity is the only recovery available. Note that **neither path migrates existing anonymous work** into the new identity; the previous workspace stays reachable only from the credentials file that issued it. |
|
|
||
| 匿名認証は、アカウントなしで Arkor を試すためのものです。学習、ジョブ、その他の作業は匿名トークンを介してローカルマシーンに紐づきます。あとで OAuth に切り替える(`arkor login --oauth`、またはピッカーから `OAuth (browser)` を選ぶ)と認証情報ファイルは差し替えられますが、作業は移行されません。匿名で学習したものを残したいなら、学習を始める前に `arkor login --oauth` を走らせてください。 | ||
|
|
||
| 匿名アカウントは設計上 **単一端末専用** です。クラウド API は匿名ユーザーごとに `latest_jti` を保持しており、`userAuth` は受信した JWT の jti が一致しないリクエストをすべて拒否します。jti はトークンのローテーションでしか変わらないので、実際の発火タイミングは大きく 2 つに分かれます。**今のところ** CLI は匿名トークンの自動 refresh を実装していません(`@arkor/cloud-api-client` の `getToken()` 側で配線予定で、[SDK ロードマップ](/ja/roadmap) に乗っています)。そのためクライアント側からのローテーションは起こらず、別マシーンにコピーした `~/.arkor/credentials.json` も大抵しばらく動いてしまいます。失効するのは、発行ユーザーが明示的に新 identity を発行したとき(`arkor login --anonymous` が新しい jti でファイルを上書き)か、管理者がサーバー側でローテーションしたとき。そのタイミングで他のコピーは次の呼び出しから失敗します。**自動 refresh が入ると**、refresh のたびに `latest_jti` が即座に進むので、別端末(または同じマシーン上の古いバックアップ)に残った旧 jti のコピーは予告なく次の呼び出しで失敗するようになります。いずれにせよ、負けた側のクライアントは HTTP 401 / 409 を `code: "anonymous_token_single_device"` で受け取り、`cli/main.ts` がそれを実行可能なガイダンスとして整形します。匿名行そのものが削除された場合は `code: "anonymous_account_not_found"` も同じ仕組みで surface されます。リカバリー提案は **デプロイ形態に応じて分岐** します。OAuth がサポートされているデプロイでは `arkor login --oauth` を案内して複数端末対応のアカウントへサインアップする経路を示し、匿名専用デプロイ(OAuth 未設定)では代わりに `arkor login --anonymous` を案内します。後者で `--oauth` を出すとそのコマンドはそのまま失敗するので、新規匿名 identity の発行が唯一のリカバリ手段になるためです。**いずれのパスも、過去の匿名作業は移行されません**。前のワークスペースには発行元の credentials ファイルからしか到達できません。 |
| second machine and using it from both will eventually trip the | ||
| server's single-device guard. The exact timing depends on whether the | ||
| SDK is currently auto-refreshing the token: today it isn't, so a | ||
| copy may keep working for a while; once one of the copies forces a | ||
| fresh login (or auto-refresh ships and rotates the stored jti), every | ||
| *other* copy starts failing on its next call with | ||
| `anonymous_token_single_device`. On OAuth-supporting deployments the | ||
| CLI directs the user at `arkor login --oauth` to start a real |
| workflow. The issuing machine and the copy share one identity, and | ||
| once either side forces a token rotation (today: a manual | ||
| \`arkor login --anonymous\`; future: any auto-refresh) the other side | ||
| starts failing with a single-device error on its next call. When | ||
| you're ready for an account-backed workspace that follows you across | ||
| devices, run: |
| * the state file (or run `arkor init` for OAuth) is what actually | ||
| * closes the loop. | ||
| */ | ||
| const STATE_RESET_NOTE = | ||
| "Local project state pins the working directory to the old workspace. Delete `.arkor/state.json` (or, for an OAuth account, re-run `arkor init`) before resuming work in this directory, otherwise commands here will keep targeting the previous identity's org/project."; |
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.