From ea13532be34b99fd0200fd6661e8918c4ee3edf4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 07:41:50 +0000 Subject: [PATCH 1/5] feat(auth): add agent-friendly split auth flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent harnesses (Perplexity, Claude, Grok, etc.) often refuse to run `acp configure` because the name and behavior read as "interactive human setup" — they punt to the user instead of executing it and relaying the URL. Add a sibling `acp auth` namespace that decouples URL emission from polling: - `acp auth url` returns {url, requestId} and exits immediately. No browser opens, no polling — safe for agents to call directly. - `acp auth complete --request-id ` finalizes after the human signs in. Polls (up to 5 min) and saves tokens. `acp configure` is untouched for humans on a workstation and for the non-interactive --token / env-var flow. Rewrite the SKILL.md Setup section to make the agent recipe explicit ("you, the agent, run these — do not punt to the human") and update the NOT_AUTHENTICATED recovery hint and file map accordingly. --- SKILL.md | 21 +++++++-- bin/acp.ts | 2 + src/commands/auth.ts | 101 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/commands/auth.ts diff --git a/SKILL.md b/SKILL.md index 5060cb0..f54297a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -26,11 +26,23 @@ acp configure # one-time browser OAuth; token saved to OS keychain acp agent create # creates the agent identity + EVM wallet ``` -`acp configure` **opens a browser and needs an interactive human session** — it won't work for fully headless agents. Run it once on a workstation; the saved token is reusable. +### Authenticating from an agent (don't punt this to the human) -After these two commands you can immediately use email, card, wallet view-only/topup, and read-only marketplace browse. Anything that signs on-chain (wallet sign/send, tokenization, compute top-up, marketplace job actions) additionally needs `acp agent add-signer` — covered in the recipe that needs it. +**You — the agent — run these commands yourself.** Do not tell the human to "run `acp configure`." All you need from the human is one click on a URL you give them. -`ACP_CONFIG_DIR` overrides where `acp configure` saves config (default `~/.config/acp`). Other environment knobs (`IS_TESTNET`, `PARTNER_ID`) are in [Reference](#environment-variables). +Use the split `acp auth` flow so nothing blocks for 5 minutes on your end: + +1. Run `acp auth url --json` → returns `{url, requestId}` and exits immediately. No browser opens, no polling. +2. Show the `url` to the human and ask them to sign in (one click in their browser). +3. When the human confirms they've signed in, run `acp auth complete --request-id --json` to finalize. This polls (up to 5 min) and saves tokens to the OS keychain. + +`acp configure` still exists for humans on a workstation — it opens a browser and blocks until sign-in completes. Agents should prefer `acp auth url` + `acp auth complete`. + +For fully non-interactive contexts (CI, scripts), pass `--token`, `--refresh-token`, and `--wallet` to `acp configure` (or set `ACP_ACCESS_TOKEN` / `ACP_REFRESH_TOKEN` / `ACP_OWNER_WALLET`). + +After auth + `acp agent create` you can immediately use email, card, wallet view-only/topup, and read-only marketplace browse. Anything that signs on-chain (wallet sign/send, tokenization, compute top-up, marketplace job actions) additionally needs `acp agent add-signer` — covered in the recipe that needs it. + +`ACP_CONFIG_DIR` overrides where the saved config lives (default `~/.config/acp`). Other environment knobs (`IS_TESTNET`, `PARTNER_ID`) are in [Reference](#environment-variables). ## Recipes @@ -410,7 +422,7 @@ Most commands print structured JSON errors to stderr on `--json`: | Code | Meaning | Recovery | |---|---|---| -| `NOT_AUTHENTICATED` | No token or session expired | `acp configure` | +| `NOT_AUTHENTICATED` | No token or session expired | `acp auth url` → hand URL to human → `acp auth complete --request-id ` (agents); `acp configure` (humans on a workstation) | | `NO_ACTIVE_AGENT` | No active agent set | `acp agent use` or `acp agent list` | | `NO_SIGNER` | No signing key, or key missing from keychain | `acp agent add-signer` | | `SESSION_NOT_FOUND` | Job ID doesn't exist or wallet isn't a participant | `acp job list` to verify | @@ -448,6 +460,7 @@ bin/acp-cli-signer-* Platform signer binaries (linux/macos/windows) src/ commands/ configure.ts Browser-based auth flow; saves token to OS keychain + auth.ts Split auth flow for agents: `acp auth url` + `acp auth complete` agent.ts Agent management (create, list, use, whoami, add-signer, update, tokenize, migrate, register-erc8004) offering.ts Offering management (list, create, update, delete; subscription attachments) subscription.ts Subscription management diff --git a/bin/acp.ts b/bin/acp.ts index 686968b..36d814a 100755 --- a/bin/acp.ts +++ b/bin/acp.ts @@ -9,6 +9,7 @@ import { registerEventsCommand } from "../src/commands/events"; import { registerMessageCommands } from "../src/commands/message"; import { registerWalletCommands } from "../src/commands/wallet"; import { registerConfigureCommand } from "../src/commands/configure"; +import { registerAuthCommands } from "../src/commands/auth"; import { registerAgentCommands } from "../src/commands/agent"; import { registerBrowseCommand } from "../src/commands/browse"; import { registerOfferingCommands } from "../src/commands/offering"; @@ -50,6 +51,7 @@ registerEventsCommand(program); registerMessageCommands(program); registerWalletCommands(program); registerConfigureCommand(program); +registerAuthCommands(program); registerAgentCommands(program); registerBrowseCommand(program); registerOfferingCommands(program); diff --git a/src/commands/auth.ts b/src/commands/auth.ts new file mode 100644 index 0000000..ca746ee --- /dev/null +++ b/src/commands/auth.ts @@ -0,0 +1,101 @@ +import type { Command } from "commander"; +import { isJson, outputResult, outputError } from "../lib/output"; +import { CliError } from "../lib/errors"; +import { AuthApi } from "../lib/api/auth"; +import { getClient } from "../lib/api/client"; +import { setCurrentOwnerWallet, setTokens } from "../lib/config"; + +const POLL_INTERVAL_MS = 2000; +const POLL_TIMEOUT_MS = 5 * 60 * 1000; + +async function waitForToken( + authApi: AuthApi, + requestId: string +): Promise<{ + token: string; + refreshToken: string; + walletAddress: string; +} | null> { + const deadline = Date.now() + POLL_TIMEOUT_MS; + while (Date.now() < deadline) { + await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS)); + const result = await authApi.pollCliToken(requestId); + if (result) return result; + } + return null; +} + +export function registerAuthCommands(program: Command): void { + const auth = program + .command("auth") + .description( + "Agent-friendly authentication. `acp auth url` returns a URL and exits; `acp auth complete` finalizes after the human signs in." + ); + + // URL — emits {url, requestId} and exits immediately. Designed for agent + // harnesses that can't (or won't) sit on a blocking browser flow: the + // agent runs this, relays the URL to its human, then calls `complete`. + auth + .command("url") + .description( + "Print a sign-in URL and requestId, then exit. Does not open a browser or poll." + ) + .action(async (_opts, cmd) => { + const json = isJson(cmd); + try { + const { authApi } = await getClient(true); + const { url, requestId } = await authApi.getCliUrl(); + if (json) { + outputResult(json, { url, requestId }); + } else { + console.log(`\nSign in here:\n\n ${url}\n`); + console.log(`Then run:\n acp auth complete --request-id ${requestId}\n`); + } + } catch (err) { + outputError( + json, + `Failed to get auth URL: ${err instanceof Error ? err.message : String(err)}` + ); + } + }); + + // COMPLETE — blocks polling for a previously-issued requestId. Run this + // *after* the human has clicked through the URL from `acp auth url`. + auth + .command("complete") + .description( + "Poll until the human finishes the sign-in for a requestId from `acp auth url`, then save tokens." + ) + .requiredOption("--request-id ", "Request ID returned by `acp auth url`") + .action(async (opts, cmd) => { + const json = isJson(cmd); + const requestId: string = opts.requestId; + try { + const { authApi } = await getClient(true); + const result = await waitForToken(authApi, requestId); + if (!result) { + outputError( + json, + new CliError( + "Authentication timed out.", + "TIMEOUT", + "Run `acp auth url` again and complete the browser sign-in." + ) + ); + return; + } + setCurrentOwnerWallet(result.walletAddress); + await setTokens(result.token, result.refreshToken, result.walletAddress); + if (json) { + outputResult(json, { + message: "Successfully authenticated to ACP CLI", + walletAddress: result.walletAddress, + }); + } else { + console.log("Successfully authenticated to ACP CLI"); + } + } catch (err) { + outputError(json, err instanceof Error ? err : String(err)); + } + }); +} From 1620c9c99c2936bb8059fffa7dbcec4befed5c20 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 09:16:39 +0000 Subject: [PATCH 2/5] refactor(auth): make `acp auth complete` non-blocking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously `complete` polled internally for up to 5 minutes, which reintroduces the same problem `acp auth` was meant to solve — long blocking calls trip agent-harness timeouts. Make `acp auth complete` a single non-blocking probe instead. Returns `{done: false}` when the human hasn't signed in yet, or `{done: true, walletAddress}` once tokens are saved. Agents poll it every few seconds (same pattern as `acp card signup-poll` and the OTP recipe). Cap retries at ~5 min on the caller side; re-run `acp auth url` if it expires. Drop the internal `waitForToken` helper since we no longer block. Update SKILL.md recipe and the `NOT_AUTHENTICATED` recovery hint. --- SKILL.md | 6 ++--- src/commands/auth.ts | 59 +++++++++++++++----------------------------- 2 files changed, 23 insertions(+), 42 deletions(-) diff --git a/SKILL.md b/SKILL.md index f54297a..53e91cf 100644 --- a/SKILL.md +++ b/SKILL.md @@ -34,7 +34,7 @@ Use the split `acp auth` flow so nothing blocks for 5 minutes on your end: 1. Run `acp auth url --json` → returns `{url, requestId}` and exits immediately. No browser opens, no polling. 2. Show the `url` to the human and ask them to sign in (one click in their browser). -3. When the human confirms they've signed in, run `acp auth complete --request-id --json` to finalize. This polls (up to 5 min) and saves tokens to the OS keychain. +3. Poll `acp auth complete --request-id --json` every ~2–3s. Each call is a single non-blocking probe that returns `{done: false}` (try again) or `{done: true, walletAddress}` (tokens saved to OS keychain). Cap retries at ~5 min; if still not done, re-run `acp auth url` and start over. `acp configure` still exists for humans on a workstation — it opens a browser and blocks until sign-in completes. Agents should prefer `acp auth url` + `acp auth complete`. @@ -422,7 +422,7 @@ Most commands print structured JSON errors to stderr on `--json`: | Code | Meaning | Recovery | |---|---|---| -| `NOT_AUTHENTICATED` | No token or session expired | `acp auth url` → hand URL to human → `acp auth complete --request-id ` (agents); `acp configure` (humans on a workstation) | +| `NOT_AUTHENTICATED` | No token or session expired | `acp auth url` → hand URL to human → poll `acp auth complete --request-id ` every ~2–3s until `done: true` (agents); `acp configure` (humans on a workstation) | | `NO_ACTIVE_AGENT` | No active agent set | `acp agent use` or `acp agent list` | | `NO_SIGNER` | No signing key, or key missing from keychain | `acp agent add-signer` | | `SESSION_NOT_FOUND` | Job ID doesn't exist or wallet isn't a participant | `acp job list` to verify | @@ -460,7 +460,7 @@ bin/acp-cli-signer-* Platform signer binaries (linux/macos/windows) src/ commands/ configure.ts Browser-based auth flow; saves token to OS keychain - auth.ts Split auth flow for agents: `acp auth url` + `acp auth complete` + auth.ts Split auth flow for agents: `acp auth url` + non-blocking poll `acp auth complete` agent.ts Agent management (create, list, use, whoami, add-signer, update, tokenize, migrate, register-erc8004) offering.ts Offering management (list, create, update, delete; subscription attachments) subscription.ts Subscription management diff --git a/src/commands/auth.ts b/src/commands/auth.ts index ca746ee..bd24118 100644 --- a/src/commands/auth.ts +++ b/src/commands/auth.ts @@ -1,40 +1,18 @@ import type { Command } from "commander"; import { isJson, outputResult, outputError } from "../lib/output"; -import { CliError } from "../lib/errors"; -import { AuthApi } from "../lib/api/auth"; import { getClient } from "../lib/api/client"; import { setCurrentOwnerWallet, setTokens } from "../lib/config"; -const POLL_INTERVAL_MS = 2000; -const POLL_TIMEOUT_MS = 5 * 60 * 1000; - -async function waitForToken( - authApi: AuthApi, - requestId: string -): Promise<{ - token: string; - refreshToken: string; - walletAddress: string; -} | null> { - const deadline = Date.now() + POLL_TIMEOUT_MS; - while (Date.now() < deadline) { - await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS)); - const result = await authApi.pollCliToken(requestId); - if (result) return result; - } - return null; -} - export function registerAuthCommands(program: Command): void { const auth = program .command("auth") .description( - "Agent-friendly authentication. `acp auth url` returns a URL and exits; `acp auth complete` finalizes after the human signs in." + "Agent-friendly authentication. `acp auth url` returns a URL and exits; `acp auth complete` is a single non-blocking probe — call repeatedly until done." ); // URL — emits {url, requestId} and exits immediately. Designed for agent // harnesses that can't (or won't) sit on a blocking browser flow: the - // agent runs this, relays the URL to its human, then calls `complete`. + // agent runs this, relays the URL to its human, then polls `complete`. auth .command("url") .description( @@ -49,7 +27,9 @@ export function registerAuthCommands(program: Command): void { outputResult(json, { url, requestId }); } else { console.log(`\nSign in here:\n\n ${url}\n`); - console.log(`Then run:\n acp auth complete --request-id ${requestId}\n`); + console.log( + `Then poll:\n acp auth complete --request-id ${requestId}\n` + ); } } catch (err) { outputError( @@ -59,12 +39,14 @@ export function registerAuthCommands(program: Command): void { } }); - // COMPLETE — blocks polling for a previously-issued requestId. Run this - // *after* the human has clicked through the URL from `acp auth url`. + // COMPLETE — single non-blocking probe. The caller (typically an agent) + // is expected to poll this every few seconds until `done: true`. We + // don't block here so harnesses that timeout long calls don't kill the + // flow. auth .command("complete") .description( - "Poll until the human finishes the sign-in for a requestId from `acp auth url`, then save tokens." + "Probe once whether the human has finished signing in. Returns {done, walletAddress?} and saves tokens when done. Non-blocking — call repeatedly." ) .requiredOption("--request-id ", "Request ID returned by `acp auth url`") .action(async (opts, cmd) => { @@ -72,27 +54,26 @@ export function registerAuthCommands(program: Command): void { const requestId: string = opts.requestId; try { const { authApi } = await getClient(true); - const result = await waitForToken(authApi, requestId); + const result = await authApi.pollCliToken(requestId); if (!result) { - outputError( - json, - new CliError( - "Authentication timed out.", - "TIMEOUT", - "Run `acp auth url` again and complete the browser sign-in." - ) - ); + if (json) { + outputResult(json, { done: false }); + } else { + console.log("Not signed in yet — try again in a few seconds."); + } return; } setCurrentOwnerWallet(result.walletAddress); await setTokens(result.token, result.refreshToken, result.walletAddress); if (json) { outputResult(json, { - message: "Successfully authenticated to ACP CLI", + done: true, walletAddress: result.walletAddress, }); } else { - console.log("Successfully authenticated to ACP CLI"); + console.log( + `Successfully authenticated to ACP CLI as ${result.walletAddress}` + ); } } catch (err) { outputError(json, err instanceof Error ? err : String(err)); From 990f3d1ec76164f1ef9be13f6c5af3df4f4a35c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 09:28:37 +0000 Subject: [PATCH 3/5] revert: drop `acp auth` namespace; keep docs-only fix `acp auth url` + `acp auth complete` duplicated the auth surface that `acp configure` already covers. The real problem is behavioral, not a missing command: agent harnesses were pushing `acp configure` back to the human instead of running it themselves. Drop the auth.ts file and its registration. Reshape the SKILL.md Setup section around `acp configure --json` directly: the URL prints on stdout almost immediately, the command then waits up to 5 min, and runtimes that can't tolerate long-running commands can stream stdout to relay the URL the moment it lands. Update the NOT_AUTHENTICATED recovery hint and file map. --- SKILL.md | 16 ++++----- bin/acp.ts | 2 -- src/commands/auth.ts | 82 -------------------------------------------- 3 files changed, 7 insertions(+), 93 deletions(-) delete mode 100644 src/commands/auth.ts diff --git a/SKILL.md b/SKILL.md index 53e91cf..4a3d973 100644 --- a/SKILL.md +++ b/SKILL.md @@ -28,17 +28,16 @@ acp agent create # creates the agent identity + EVM wallet ### Authenticating from an agent (don't punt this to the human) -**You — the agent — run these commands yourself.** Do not tell the human to "run `acp configure`." All you need from the human is one click on a URL you give them. +**You — the agent — run `acp configure` yourself.** Do not tell the human to "run `acp configure`." All you need from the human is one click on a URL you give them. -Use the split `acp auth` flow so nothing blocks for 5 minutes on your end: +How `acp configure --json` behaves: -1. Run `acp auth url --json` → returns `{url, requestId}` and exits immediately. No browser opens, no polling. -2. Show the `url` to the human and ask them to sign in (one click in their browser). -3. Poll `acp auth complete --request-id --json` every ~2–3s. Each call is a single non-blocking probe that returns `{done: false}` (try again) or `{done: true, walletAddress}` (tokens saved to OS keychain). Cap retries at ~5 min; if still not done, re-run `acp auth url` and start over. +1. It prints `{"url":"..."}` to stdout almost immediately. **Relay this URL to the human** — that's the one thing you need them to do (one click, sign in). +2. The command then waits (up to ~5 min) until the human finishes the sign-in, then prints the final `{"message":"...","walletAddress":"..."}` and exits 0. Tokens are saved to the OS keychain. -`acp configure` still exists for humans on a workstation — it opens a browser and blocks until sign-in completes. Agents should prefer `acp auth url` + `acp auth complete`. +If your runtime can't tolerate a long-running command, stream stdout line-by-line: the URL appears on the first JSON line so you can relay it the moment it's printed, even though the process is still alive. -For fully non-interactive contexts (CI, scripts), pass `--token`, `--refresh-token`, and `--wallet` to `acp configure` (or set `ACP_ACCESS_TOKEN` / `ACP_REFRESH_TOKEN` / `ACP_OWNER_WALLET`). +For fully non-interactive contexts (CI, prebuilt environments), pass `--token`, `--refresh-token`, and `--wallet` to `acp configure` (or set `ACP_ACCESS_TOKEN` / `ACP_REFRESH_TOKEN` / `ACP_OWNER_WALLET`) — no browser flow at all. After auth + `acp agent create` you can immediately use email, card, wallet view-only/topup, and read-only marketplace browse. Anything that signs on-chain (wallet sign/send, tokenization, compute top-up, marketplace job actions) additionally needs `acp agent add-signer` — covered in the recipe that needs it. @@ -422,7 +421,7 @@ Most commands print structured JSON errors to stderr on `--json`: | Code | Meaning | Recovery | |---|---|---| -| `NOT_AUTHENTICATED` | No token or session expired | `acp auth url` → hand URL to human → poll `acp auth complete --request-id ` every ~2–3s until `done: true` (agents); `acp configure` (humans on a workstation) | +| `NOT_AUTHENTICATED` | No token or session expired | Run `acp configure` yourself (don't push to the human); relay the printed URL to the human for the sign-in click | | `NO_ACTIVE_AGENT` | No active agent set | `acp agent use` or `acp agent list` | | `NO_SIGNER` | No signing key, or key missing from keychain | `acp agent add-signer` | | `SESSION_NOT_FOUND` | Job ID doesn't exist or wallet isn't a participant | `acp job list` to verify | @@ -460,7 +459,6 @@ bin/acp-cli-signer-* Platform signer binaries (linux/macos/windows) src/ commands/ configure.ts Browser-based auth flow; saves token to OS keychain - auth.ts Split auth flow for agents: `acp auth url` + non-blocking poll `acp auth complete` agent.ts Agent management (create, list, use, whoami, add-signer, update, tokenize, migrate, register-erc8004) offering.ts Offering management (list, create, update, delete; subscription attachments) subscription.ts Subscription management diff --git a/bin/acp.ts b/bin/acp.ts index 36d814a..686968b 100755 --- a/bin/acp.ts +++ b/bin/acp.ts @@ -9,7 +9,6 @@ import { registerEventsCommand } from "../src/commands/events"; import { registerMessageCommands } from "../src/commands/message"; import { registerWalletCommands } from "../src/commands/wallet"; import { registerConfigureCommand } from "../src/commands/configure"; -import { registerAuthCommands } from "../src/commands/auth"; import { registerAgentCommands } from "../src/commands/agent"; import { registerBrowseCommand } from "../src/commands/browse"; import { registerOfferingCommands } from "../src/commands/offering"; @@ -51,7 +50,6 @@ registerEventsCommand(program); registerMessageCommands(program); registerWalletCommands(program); registerConfigureCommand(program); -registerAuthCommands(program); registerAgentCommands(program); registerBrowseCommand(program); registerOfferingCommands(program); diff --git a/src/commands/auth.ts b/src/commands/auth.ts deleted file mode 100644 index bd24118..0000000 --- a/src/commands/auth.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { Command } from "commander"; -import { isJson, outputResult, outputError } from "../lib/output"; -import { getClient } from "../lib/api/client"; -import { setCurrentOwnerWallet, setTokens } from "../lib/config"; - -export function registerAuthCommands(program: Command): void { - const auth = program - .command("auth") - .description( - "Agent-friendly authentication. `acp auth url` returns a URL and exits; `acp auth complete` is a single non-blocking probe — call repeatedly until done." - ); - - // URL — emits {url, requestId} and exits immediately. Designed for agent - // harnesses that can't (or won't) sit on a blocking browser flow: the - // agent runs this, relays the URL to its human, then polls `complete`. - auth - .command("url") - .description( - "Print a sign-in URL and requestId, then exit. Does not open a browser or poll." - ) - .action(async (_opts, cmd) => { - const json = isJson(cmd); - try { - const { authApi } = await getClient(true); - const { url, requestId } = await authApi.getCliUrl(); - if (json) { - outputResult(json, { url, requestId }); - } else { - console.log(`\nSign in here:\n\n ${url}\n`); - console.log( - `Then poll:\n acp auth complete --request-id ${requestId}\n` - ); - } - } catch (err) { - outputError( - json, - `Failed to get auth URL: ${err instanceof Error ? err.message : String(err)}` - ); - } - }); - - // COMPLETE — single non-blocking probe. The caller (typically an agent) - // is expected to poll this every few seconds until `done: true`. We - // don't block here so harnesses that timeout long calls don't kill the - // flow. - auth - .command("complete") - .description( - "Probe once whether the human has finished signing in. Returns {done, walletAddress?} and saves tokens when done. Non-blocking — call repeatedly." - ) - .requiredOption("--request-id ", "Request ID returned by `acp auth url`") - .action(async (opts, cmd) => { - const json = isJson(cmd); - const requestId: string = opts.requestId; - try { - const { authApi } = await getClient(true); - const result = await authApi.pollCliToken(requestId); - if (!result) { - if (json) { - outputResult(json, { done: false }); - } else { - console.log("Not signed in yet — try again in a few seconds."); - } - return; - } - setCurrentOwnerWallet(result.walletAddress); - await setTokens(result.token, result.refreshToken, result.walletAddress); - if (json) { - outputResult(json, { - done: true, - walletAddress: result.walletAddress, - }); - } else { - console.log( - `Successfully authenticated to ACP CLI as ${result.walletAddress}` - ); - } - } catch (err) { - outputError(json, err instanceof Error ? err : String(err)); - } - }); -} From d65bdd5fb08aa2a933c1a9621b15048b15a42e3f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 09:33:50 +0000 Subject: [PATCH 4/5] docs: clarify --token flags on configure are partner-only, not an agent escape hatch --- SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SKILL.md b/SKILL.md index 4a3d973..82b836a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -37,7 +37,7 @@ How `acp configure --json` behaves: If your runtime can't tolerate a long-running command, stream stdout line-by-line: the URL appears on the first JSON line so you can relay it the moment it's printed, even though the process is still alive. -For fully non-interactive contexts (CI, prebuilt environments), pass `--token`, `--refresh-token`, and `--wallet` to `acp configure` (or set `ACP_ACCESS_TOKEN` / `ACP_REFRESH_TOKEN` / `ACP_OWNER_WALLET`) — no browser flow at all. +> The `--token / --refresh-token / --wallet` flags (and matching `ACP_*` env vars) on `acp configure` are **not** an escape hatch for agents — they only work with tokens minted by a Virtuals partner backend, used to pre-bake the CLI onto end-user machines. See [docs/headless-deployment.md](docs/headless-deployment.md) if that's what you're doing. After auth + `acp agent create` you can immediately use email, card, wallet view-only/topup, and read-only marketplace browse. Anything that signs on-chain (wallet sign/send, tokenization, compute top-up, marketplace job actions) additionally needs `acp agent add-signer` — covered in the recipe that needs it. From 76f0320c3bbc197fda5e5634d88dd1ffc911e8a9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 09:34:34 +0000 Subject: [PATCH 5/5] docs: drop partner-flag callout; agents don't need to know about it --- SKILL.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/SKILL.md b/SKILL.md index 82b836a..91a3673 100644 --- a/SKILL.md +++ b/SKILL.md @@ -37,8 +37,6 @@ How `acp configure --json` behaves: If your runtime can't tolerate a long-running command, stream stdout line-by-line: the URL appears on the first JSON line so you can relay it the moment it's printed, even though the process is still alive. -> The `--token / --refresh-token / --wallet` flags (and matching `ACP_*` env vars) on `acp configure` are **not** an escape hatch for agents — they only work with tokens minted by a Virtuals partner backend, used to pre-bake the CLI onto end-user machines. See [docs/headless-deployment.md](docs/headless-deployment.md) if that's what you're doing. - After auth + `acp agent create` you can immediately use email, card, wallet view-only/topup, and read-only marketplace browse. Anything that signs on-chain (wallet sign/send, tokenization, compute top-up, marketplace job actions) additionally needs `acp agent add-signer` — covered in the recipe that needs it. `ACP_CONFIG_DIR` overrides where the saved config lives (default `~/.config/acp`). Other environment knobs (`IS_TESTNET`, `PARTNER_ID`) are in [Reference](#environment-variables).