Skip to content

Clarify limitations of anonymous workspaces and transition guidance#100

Open
k-taro56 wants to merge 4 commits intomainfrom
eng-353
Open

Clarify limitations of anonymous workspaces and transition guidance#100
k-taro56 wants to merge 4 commits intomainfrom
eng-353

Conversation

@k-taro56
Copy link
Copy Markdown
Contributor

@k-taro56 k-taro56 commented May 2, 2026

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:

  • All docs now clearly state that anonymous accounts/workspaces are strictly single-device; copying credentials to another machine will result in authentication errors, and users are advised to use arkor login --oauth for multi-device access. [1] [2] [3] [4] [5] [6] [7]
  • Both English and Japanese docs surface this limitation at the point of anonymous workspace creation and in relevant CLI output, ensuring consistent messaging before users encounter errors. [1] [2]

Authentication error handling improvements:

  • Documentation now describes new structured error codes (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]
  • The old generic "Token may be expired" message is removed; unknown errors now show the upstream error message verbatim, reducing confusion and improving transparency. [1] [2] [3]

Other clarifications:

  • Documentation for anonymous token expiration and refresh behavior is updated, noting the 90-day TTL and that auto-refresh is not yet implemented. [1] [2]
  • CLI guidance for attaching anonymous workspaces to an account and the implications for multi-device workflows is made more explicit in both languages. [1] [2] [3] [4]

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.

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.
@k-taro56 k-taro56 self-assigned this May 2, 2026
Copilot AI review requested due to automatic review settings May 2, 2026 14:33
@mintlify
Copy link
Copy Markdown

mintlify Bot commented May 2, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
arkor-92aeef0e 🟢 Ready View Preview May 2, 2026, 2:33 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 2, 2026

Codecov Report

❌ Patch coverage is 95.12195% with 2 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/arkor/src/cli/main.ts 85.71% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +160 to +161
ui.log.info(
"Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Comment thread packages/arkor/src/cli/commands/dev.ts Outdated
Comment on lines +159 to +160
ui.log.info(
"Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.code and 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.

Comment thread docs/cli/auth.mdx Outdated
- `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.
Comment on lines +71 to +75
// 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",
Comment thread docs/cli/dev.mdx Outdated
| `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. |
Comment thread docs/ja/cli/dev.mdx Outdated
| `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 アカウントは新規スタート。 |
Comment thread README.md Outdated
`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.
Comment on lines +68 to +75
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",
Comment thread docs/ja/cli/auth.mdx Outdated
- `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 の理由を一字一句見ます。
Comment on lines +158 to +161
// 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.",
Comment on lines +159 to +161
ui.log.info(
"Note: anonymous accounts work on this machine only. Run `arkor login --oauth` to sign up for multi-device access.",
);
Comment thread packages/arkor/src/cli/main.ts Outdated
Comment on lines +153 to +165
} 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.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +60 to +62
typeof body.user === "object" &&
body.user !== null &&
(body.user as { kind?: unknown }).kind === "anonymous";
Comment thread docs/cli/dev.mdx Outdated
@@ -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. |
Comment thread docs/ja/cli/dev.mdx
| `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 アカウントは新規スタート。 |
Comment thread docs/cli/auth.mdx Outdated
- 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.
Comment thread docs/ja/cli/auth.mdx Outdated
- 匿名専用デプロイ: ``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` 本文だけが見えるわけではありません。
Comment thread docs/cli/auth.mdx Outdated
| `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`). |
Comment thread docs/ja/cli/auth.mdx Outdated
| `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.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +164 to +167
ui.log.info(
opts.oauthAvailable === true
? ANON_SINGLE_DEVICE_NOTE_WITH_OAUTH
: ANON_SINGLE_DEVICE_NOTE,
Comment on lines +167 to +170
ui.log.info(
oauthAvailable
? ANON_SINGLE_DEVICE_NOTE_WITH_OAUTH
: ANON_SINGLE_DEVICE_NOTE,
Comment on lines 72 to +73
const message = fields.error || text || `cloud-api ${res.status}`;
return new CloudApiError(res.status, message);
return new CloudApiError(res.status, message, fields.code);
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment thread packages/arkor/src/cli/main.ts Outdated
*/
async function probeOauthAvailability(): Promise<boolean> {
try {
const cfg = await fetchCliConfig(defaultArkorCloudApiUrl());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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`.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +48 to +53
// 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`);
Comment thread docs/cli/dev.mdx
| ``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. |
Comment thread docs/ja/cli/dev.mdx
| ``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 として扱ってください。 |
Comment thread docs/cli/auth.mdx
Comment on lines +119 to +123
- 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.)
Comment thread docs/ja/cli/auth.mdx
Comment on lines +118 to +124
- 期限切れまたは無効化されたトークンはクラウド 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 を確定させるためです。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants