@finchtech/cli 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +128 -120
  2. package/dist/finch.js +154 -53
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -1,120 +1,128 @@
1
- # Finch Agent CLI
2
-
3
- Install the public npm package with `npm install -g @finchtech/cli`, then run `finch --help`. The primary executable is `finch`; `finchtech` is an equivalent fallback for machines where another application already owns the `finch` command.
4
-
5
- The Finch Agent CLI stores wallet keys, AgentCLI Session bearers, and MCP grant bearers only in an owner-only `FINCHTECH_HOME`, which defaults to `~/.finchtech`; secrets are never accepted as command arguments or emitted to stdout.
6
-
7
- For an existing private key, run `finch wallet use --file <PATH>` on a fresh installation, then `finch login --help` to choose an authentication chain. The file must contain a single 0x-prefixed 32-byte hex private key and have owner-only permissions. First import is offline and makes that wallet current. `finch wallet create` generates a new identity instead. An existing Account switch requires login and remote revocation before changing the current wallet. Incomplete wallet files require recovery from backup; residual local authorization requires restoring the original wallet or explicitly clearing local authorization with `finch logout --local`.
8
-
9
- `finch logout` revokes Account-bound remote authority before removing local Session and grant state. If invalid, corrupt, or legacy Session metadata prevents that flow, `finch logout --local` performs an explicit offline reset of only `session/` and `grants/`. It makes no network request and reports `remoteRevoked: false`; it remains available while an irreversible recovery is pending and preserves profiles, registered wallets, transaction journals, and recovery records so the user can log in again and run the recorded recovery command. Ordinary logout and wallet switching remain blocked until that recovery completes.
10
-
11
- CLI-authored EVM addresses accept canonical lowercase or valid EIP-55 input and are normalized to lowercase before request validation and transport. Invalid mixed-case and uppercase forms are rejected. Remote responses and Remote MCP plans remain strictly lowercase so normalization cannot change signed or hashed plan bytes.
12
-
13
- Valid remote Identity, Agent, Task, Skill, and OAuth errors are emitted in their original domain envelope with the remote trace ID. Local transport failures use `CLI_REMOTE_UNAVAILABLE`; malformed or contract-invalid responses use `CLI_RESPONSE_INVALID`. Locally synthesized failures use a null trace ID rather than impersonating a server trace.
14
-
15
- Skill upload failures also retain the final `uploadBytes`, `uploadObjectKind`, HTTP `remoteStatus` (null when no response was obtained), and available allowlisted `remoteCode`, `remoteRequestId`, and `remoteTraceId` in the JSON-encoded `state`. Locally synthesized errors retain the existing null `reasonCode` and `traceId` contract; downstream evidence is labelled explicitly in `state`. These fields never include raw response text, upload filenames, credentials, or transport causes. `not_attempted` refers only to chain submission: storage may have accepted an object before a response was lost. Follow `contact_support` until the individual attempt is reconciled; another publication's status is not evidence about this attempt.
16
-
17
- ZIP import failures return `AGENT_MARKET_REQUEST_INVALID`, exit code `2`, and `fix_request` before upload. The JSON-encoded `state` identifies `SKILL_SOURCE_ZIP_INVALID` and a `zipReason`: `archive_too_large`, `expanded_content_too_large`, `utf8_flag_missing`, `filename_encoding_invalid`, or `invalid_archive`. Size failures include `actualBytes`, `maxBytes` (10,485,760), and `sizeBasis`: `archive` measures the compressed input; `declared_expanded` is the cumulative uncompressed size declared by entries at the first limit violation, before decompression. A safely decoded root-relative entry `path` is included when available. Non-ASCII filenames require valid UTF-8 bytes and bit 11 in the ZIP headers; rebuilding with those flags preserves file contents. These import limits are independent of final upload admission.
18
-
19
- Intent HTTP failures include `remotePath: "/api/skills/intents"`, `remoteStatus`, and available safe correlation IDs. `responseIssue` distinguishes `transport_failed`, `body_read_failed`, `empty_body`, `invalid_content_length`, `response_too_large`, `invalid_json`, and `schema_mismatch`. Schema failures include at most eight `schemaIssues` with known field paths and validation keywords; an empty path means the response root or an unrecognized field, whose name is withheld. No response values, arbitrary property names, URLs, or raw causes are printed. Malformed success responses are rejected during `intent`, before signing or writing a pending-send marker. Use the returned recovery command for the same publication; the CLI never automatically retries intent requests or resubmits transactions. The fingerprint identifies the local recovery journal, not a server trace; use returned remote IDs to correlate server logs.
20
-
21
- The CLI measures the exact final multipart request, including metadata and framing. Requests above 4,000,000 bytes emit a structured SKILL_UPLOAD_NEAR_LIMIT warning on stderr and continue; requests above 4,500,000 bytes fail before upload. Exactly 4,000,000 bytes do not warn; exactly 4,500,000 bytes warn and continue. These MB thresholds are decimal. The manifest allowance is separately 3 MiB (3,145,728 bytes) on upload and reads. Repacking a compressed source ZIP may increase its size. Passing admission does not certify production upload success or authorize replaying a previously failed publication.
22
-
23
- The command groups are `wallet`, `login/status/logout`, `site`, `mcp doctor`, `mcp authorize`, `mcp grant`, `creator`, `credentials`, `intent`, `operation show`, `purchase`, `task`, and `skill`. Run `finch --help` for the exact command list as one JSON value.
24
-
25
- `finch site open` creates a sixty-second one-use handoff from the active AgentCLI Session and opens the exact configured Finch origin in the local default Browser. `finch site login <REQUEST_URL>` binds a website-created five-minute request once, then opens the same confirmation page. Both commands require the wallet, CLI, and graphical Browser on the user's machine. A launch failure returns `BROWSER_OPEN_FAILED` without printing the secret completion URL. The Browser receives only a normal seven-day Finch Web Session; wallet signing, broadcast, and market confirmation remain local CLI actions.
26
-
27
- `finch mcp doctor` is read-only. It returns a `checks` object for endpoint, wallet, local Session, remote Session authority, and MCP discovery. Each check reports `ok`, `missing`, `invalid`, `unavailable`, or `skipped`, with guidance for unhealthy checks. Missing login or wallet state does not prevent independent discovery checks; an invalid endpoint prevents network checks. The command emits one JSON report with exit code 0 when ready, or 2 when any required check is unhealthy. It verifies that the active CLI authority, AgentCLI Session, local wallet, MCP challenge, OAuth metadata, and source-controlled persistent-Harness manifest agree. The Agent Harness owns Remote MCP configuration, OAuth credentials, and refresh; the CLI does not configure or launch Codex and never reads Harness OAuth material. During the Harness's native OAuth flow, the authorization page supplies an exact `finch mcp authorize <REQUEST_TOKEN>` command. The versioned base64url token carries the original OAuth request without shell metacharacters so Windows package-manager and Volta launchers cannot split it; the CLI decodes it before applying the existing origin, callback, state, and scope checks. That command uses only the active local AgentCLI Session and opens the verified callback without returning its one-use code. It never uses a Browser Session, Social account, or extension wallet as Agent authority. Configure `https://www.finchtech.ai/mcp` once through the Harness, then use MCP `identity_actor_get` together with `finch status` to verify exact Account and wallet alignment before custody. The server-reported environment is diagnostic and never selects the CLI endpoint or a transaction chain.
28
-
29
- Doctor's `ready: true` is scoped by `readinessScope: "cli_session_and_mcp_discovery"`. Its `harnessOAuth.status` is always `not_checked`: CLI login and valid discovery metadata do not establish that the connected Harness has usable OAuth credentials, a saved issuer, or working refresh. The result includes identity-verification and native reauthorization guidance. If the Harness reports an OAuth failure, complete its native MCP authorization flow and the authorization page's fresh `finch mcp authorize` command, then verify MCP identity again.
30
-
31
- Run `finch login --help` to list supported production authentication chain IDs (1, 10, 56, 8453, 42161). For example, `finch login --chain-id 56` selects BNB Smart Chain for authentication only.
32
-
33
- Ordinary installs require no profile file or environment selection. Network commands default to the PROD endpoint at `https://www.finchtech.ai`. `finch login --chain-id <CHAIN_ID>` explicitly selects only the authentication chain, which the selected server must support. Market transaction chains come from Remote MCP/API plans and are independent of the login chain. `intent approve` signs only the shared publication-v2 or Local-purchase-v1 EIP-712 authorization; it cannot sign arbitrary transaction calldata.
34
-
35
- `operation show` and signing-decision recovery require an active, unexpired MCP grant for the current account and endpoint with `agent_market:operations:read`. The CLI selects that grant from protected local state and never substitutes the AgentCLI Session bearer.
36
-
37
- Creator commands are thin projections of the shared Agent Market merchant operations. Reads require `agent_market:merchant:read`; mutations require `agent_market:merchant:write`, a stable caller-chosen `--request-id` UUID for local idempotency recovery, and `--expected-revision` wherever the shared operation requires If-Match. Structured request bodies come from bounded JSON `--from-file` documents and are validated against the same shared schemas before HTTP.
38
-
39
- Local AgentOn HMAC generation is not a product command. `credentials material-download <setupId> --out-file <absolute-path>` claims the AgentOn setup, downloads only its exact live lease as raw FCR1, and creates a new owner-only file; stdout contains only `keyId` and `path`. `credentials fulfill` accepts only Direct credential uploads. It accepts either an existing protected FCR1 with `--from-file <path>`, or a protected raw Bearer token with `--bearer-token-file <path> --key-id <keyId>`; the latter validates one whitespace-free token and adds the `Bearer ` prefix only in CLI memory. Secret bytes never enter argv or stdout.
40
-
41
- `purchase transaction <operationId>` is read-only. It requires an active `agent_market:buyer:purchase` MCP grant and returns the two server-composed transaction requests plus their chain authority; it does not sign or broadcast arbitrary calldata.
42
-
43
- `purchase submit <operationId>` requires the same grant and uses only the source-controlled public RPCs for the plan's chain. It uses the protected wallet to re-encode and submit the exact server-composed USDC approval and AgentService Vault purchase. Before signing it verifies `eth_chainId`, the reviewed chain contract authority, every transaction field, and a successful `eth_call` simulation. Every returned broadcast hash is atomically checkpointed before receipt waiting; a restart re-verifies the exact wallet, call, receipt, block, and current server authority and never re-broadcasts a known phase. Finch then verifies the canonical confirmation event and creates Call Rights. If the purchase receipt conclusively reverts, the paired zero-allowance cleanup follows the same checkpoint discipline; unknown receipts are never guessed. The RPC owns nonce selection. User RPC and environment variables are ignored, and neither RPC URLs nor wallet keys are emitted.
44
-
45
- `purchase recover-approval <operationId> --transaction-hash <hash>` is the narrow adoption path for an approval broadcast before its durable checkpoint was written. It re-fetches the current server authority and active wallet, then verifies the exact successful approval transaction and sufficient live token allowance on the plan chain before writing one owner-only recovery journal. It never broadcasts, confirms, scans chain history, or accepts a nonce. A subsequent `purchase submit` continues with the purchase phase without approving again.
46
-
47
- If the process exits after broadcasting the Vault purchase but before confirmation returns, recover with `purchase confirm <operationId> --transaction-hash <hash>`. This command never trusts the supplied hash: Finch fetches and decodes the canonical chain receipt before any Call Rights mutation.
48
-
49
- Task and Skill business discovery and preparation stay in the Remote MCP server. The `task` commands only fund an exact prepared Task pool, sign exact prepared awards, or submit an exact prepared reclaim. Skill purchase, conversion, price, and supply commands submit strict MCP preparations. `skill publish-submit --path <file-or-directory> --from-file <plan>` and `skill version-submit` accept either one file or one directory, then apply the plan's independent `plaintext`, `finchip_v2`, or `lit` content mode; filtering, deterministic ZIP construction, encryption, upload, and key custody remain local. `skill asset-upload --path <cover> --from-file <plan>` uploads a separately prepared public cover, and `skill presentation-submit --from-file <plan>` publishes bounded public presentation metadata without changing protected package bytes. `skill download --from-file <delivery> --out-file <new-file>` downloads IPFS content directly or signs with the entitled local wallet for protected content, verifies the SkillRoot and every file (or the plaintext ZIP hash for historical Skills), and creates an owner-only file. Every transaction action uses its plan's chain, source-controlled public RPCs, and reviewed chain-level contract authority. The CLI recomputes transaction bytes locally and rejects mismatched chains, destinations, calldata, values, source commits, or envelope tags.
50
-
51
- ## ZIP import and SkillRoot publication
52
-
53
- Version 0.3.1 fixes Skill search continuation: pass the returned `nextCursor` unchanged as
54
- `--cursor`. Search uses the server rules; the CLI does not implement a matching algorithm.
55
- Older clients receive an upgrade message from the Skill search API.
56
-
57
- Version 0.3.0 upgrades Skill publication and encrypted delivery together. Upgrade with
58
- `pnpm add -g @finchtech/cli@0.3.4` and verify `finch --version` before publishing new Skills
59
- or downloading encrypted Skills from the upgraded gateway. Older CLI versions cannot use
60
- the new encrypted delivery, including for historical Skills; 0.3.0 retains historical package support.
61
-
62
- A single `.zip` passed to `skill publish-submit --path` or `skill version-submit --path` is expanded locally before filtering and primary-document hashing; request a `file` source plan for this input. Directory inputs keep ZIP assets as ordinary files. Imports support stored/DEFLATE ZIPs up to 10 MiB compressed and expanded, with at most 1024 entries. Unsafe paths, symbolic links, encrypted ZIPs, ZIP64, conflicting entries, and corrupt content are refused. Include `SKILL.md`, `README.md`, or one unambiguous root Markdown document.
63
-
64
- New Skills publish a DAG-CBOR SkillRoot hash in one graph bundle. Existing Skill identities keep ZIP-hash updates. New Skills require this updated CLI; it verifies every object and decrypts protected files locally into an ordinary ZIP. Historical ZIP downloads and whole-package decryption remain supported. A pending publication reuses its uploaded bytes and saved root commitment on resume.
65
-
66
- The [publication ADR](../../docs/adr/ADR-0064-20260908-skillroot-publication.md) specifies the pinned draft, bundle/profile format, compatibility trust boundary, and rollout order.
67
-
68
- Finch-managed and Lit downloads use Oracle V2 device-sealed grants and a non-extractable local CryptoKey.
69
- The updated gateway also supports historical Finch-managed and Lit ZIPs through this delivery. It refuses
70
- old bare-key buyer requests; use the updated CLI. Both providers use the same sealed delivery. See
71
- [ADR-0065](../../docs/adr/ADR-0065-20260908-skill-oracle-v2-delivery.md) for deployment requirements.
72
-
73
- Publication preparation accepts an optional `summary` (up to 500 characters). It is committed in
74
- SkillRoot exactly as supplied, matching Browser publication metadata; omission means an empty summary.
75
- Equal public file bytes, paths, name, summary, version and history produce equal roots. Encrypted
76
- publications use random keys/IVs and need not produce the same root. Updates send the proposed
77
- manifest for server validation; URI intents are prepared after the new chain binding is confirmed.
78
-
79
- Finch-managed publication key requests use a signed timestamp and a one-time nonce. Each request is
80
- valid for five minutes; retrying after expiry or a lost response requires a fresh signature. The
81
- gateway rejects older CLI publication requests without timestamps. Historical downloads are unchanged.
82
-
83
- ### Local publication failures
84
-
85
- Directory publication rejects symbolic links, including internal relative links and broken links.
86
- It skips excluded paths (for example `.git`, `node_modules`, `.aws`, and credential files) before reading
87
- or descending into them. `excluded` lists a skipped directory once instead of enumerating its children.
88
- Keep required resource paths functional when preparing a link-free source; deleting required links is
89
- not a valid repair. ZIP imports also reject symbolic links.
90
-
91
- Source errors keep exit code 2 and `AGENT_MARKET_REQUEST_INVALID`. Parse the JSON string in `error.state`
92
- for `diagnosticCode`: `SKILL_SOURCE_SYMLINK_UNSUPPORTED`, `SKILL_SOURCE_UNREADABLE`, or
93
- `SKILL_UPLOAD_SIZE_INVALID`, `SKILL_SOURCE_ZIP_INVALID`, `SKILL_SOURCE_PRIMARY_DOCUMENT_INVALID`, `SKILL_UPLOAD_REQUEST_TOO_LARGE`, or `SKILL_MANIFEST_SIZE_INVALID`. Source diagnostics include a root-relative `path` (`.` for the selected
94
- root); size diagnostics include `actualBytes` and `maxBytes`. The 10 MiB internal packaging bound is independent of the stricter HTTP limit and applies to the final
95
- upload object, including SkillRoot metadata and encryption overhead. STORE ZIP construction does not
96
- compress files. A size rejection occurs before that upload and before transaction submission.
97
-
98
- Publish, version, and publication-recovery failures also include `phase`, `fingerprint`,
99
- `transactionHashes` (keyed by `primary`, `uri`, or `lit`), `submissionStatus`, and `recoveryCommand` in
100
- `error.state`. `packaging` includes local hashing/encryption; `transaction` covers signing, broadcast,
101
- and receipt handling. A validated remote error keeps its code, message, trace and HTTP status
102
- (`remoteStatus`), with its original state retained as `remoteState`; publication retry guidance takes
103
- precedence over a remote service's generic retry flag. Private causes, RPC URLs, credentials and
104
- journal contents are never printed.
105
-
106
- Publication failures set `retryable: false`: this is not permission to replay a paid operation.
107
- `not_attempted` means no send was attempted in this invocation and no hashes were found in the
108
- validated journal; it is not proof about other invocations. `hash_known` includes known transaction
109
- hashes. `uncertain` means sending or durable checkpointing failed; contact support before retrying or
110
- recovering, even when a hash is shown. A non-null `recoveryCommand` is provided only after a journal
111
- is validated or saved and no submission/checkpoint uncertainty was observed. Recheck CLI/MCP identity
112
- and use that command rather than repeating publish-submit. Cleanup failures do not claim a journal
113
- still exists. If source is unavailable or its excluded-path inventory changed across CLI versions,
114
- the fingerprint-only recovery command can use the already stored publication bytes.
115
-
116
- These fixes require CLI 0.3.3 or newer; npm 0.3.2 does not include them. Updating a Skill does not upgrade the installed CLI. Journal v6 saves a pending stage before signing/submitting. Missing hashes in a pending stage or a legacy v4/v5 journal block automatic resubmission; contact support. Known hashes are verified without rebroadcast. The pending marker also blocks destructive local authorization/wallet cleanup through the recovery inventory.
117
-
118
- ## Bootstrap and health reports (CLI 0.3.4+)
119
-
120
- First-wallet offline import, installed Account-switch revocation, partial doctor checks and exit code 2, contextual wallet recovery guidance, login authentication-chain help, and strict OAuth UUID response validation require CLI 0.3.4 or newer. Updating the website or Skill does not upgrade an installed CLI.
1
+ # Finch Agent CLI
2
+
3
+ Install the public npm package with `npm install -g @finchtech/cli`, then run `finch --help`. The primary executable is `finch`; `finchtech` is an equivalent fallback for machines where another application already owns the `finch` command.
4
+
5
+ The Finch Agent CLI stores wallet keys, AgentCLI Session bearers, and MCP grant bearers only in an owner-only `FINCHTECH_HOME`, which defaults to `~/.finchtech`; secrets are never accepted as command arguments or emitted to stdout.
6
+
7
+ For an existing private key, run `finch wallet use --file <PATH>` on a fresh installation, then `finch login --help` to choose an authentication chain. The file must contain a single 0x-prefixed 32-byte hex private key and have owner-only permissions. First import is offline and makes that wallet current. `finch wallet create` generates a new identity instead. An existing Account switch requires login and remote revocation before changing the current wallet. Incomplete wallet files require recovery from backup; residual local authorization requires restoring the original wallet or explicitly clearing local authorization with `finch logout --local`.
8
+
9
+ `finch logout` revokes Account-bound remote authority before removing local Session and grant state. If invalid, corrupt, or legacy Session metadata prevents that flow, `finch logout --local` performs an explicit offline reset of only `session/` and `grants/`. It makes no network request and reports `remoteRevoked: false`; it remains available while an irreversible recovery is pending and preserves profiles, registered wallets, transaction journals, and recovery records so the user can log in again and run the recorded recovery command. Ordinary logout and wallet switching remain blocked until that recovery completes.
10
+
11
+ CLI-authored EVM addresses accept canonical lowercase or valid EIP-55 input and are normalized to lowercase before request validation and transport. Invalid mixed-case and uppercase forms are rejected. Remote responses and Remote MCP plans remain strictly lowercase so normalization cannot change signed or hashed plan bytes.
12
+
13
+ Valid remote Identity, Agent, Task, Skill, and OAuth errors are emitted in their original domain envelope with the remote trace ID. Local transport failures use `CLI_REMOTE_UNAVAILABLE`; malformed or contract-invalid responses use `CLI_RESPONSE_INVALID`. Locally synthesized failures use a null trace ID rather than impersonating a server trace.
14
+
15
+ Skill upload failures also retain the final `uploadBytes`, `uploadObjectKind`, HTTP `remoteStatus` (null when no response was obtained), and available allowlisted `remoteCode`, `remoteRequestId`, and `remoteTraceId` in the JSON-encoded `state`. Locally synthesized errors retain the existing null `reasonCode` and `traceId` contract; downstream evidence is labelled explicitly in `state`. These fields never include raw response text, upload filenames, credentials, or transport causes. `not_attempted` refers only to chain submission: storage may have accepted an object before a response was lost. Follow `contact_support` until the individual attempt is reconciled; another publication's status is not evidence about this attempt.
16
+
17
+ ZIP import failures return `AGENT_MARKET_REQUEST_INVALID`, exit code `2`, and `fix_request` before upload. The JSON-encoded `state` identifies `SKILL_SOURCE_ZIP_INVALID` and a `zipReason`: `archive_too_large`, `expanded_content_too_large`, `utf8_flag_missing`, `filename_encoding_invalid`, or `invalid_archive`. Size failures include `actualBytes`, `maxBytes` (10,485,760), and `sizeBasis`: `archive` measures the compressed input; `declared_expanded` is the cumulative uncompressed size declared by entries at the first limit violation, before decompression. A safely decoded root-relative entry `path` is included when available. Non-ASCII filenames require valid UTF-8 bytes and bit 11 in the ZIP headers; rebuilding with those flags preserves file contents. These import limits are independent of final upload admission.
18
+
19
+ Intent HTTP failures include `remotePath: "/api/skills/intents"`, `remoteStatus`, and available safe correlation IDs. `responseIssue` distinguishes `transport_failed`, `body_read_failed`, `empty_body`, `invalid_content_length`, `response_too_large`, `invalid_json`, and `schema_mismatch`. Schema failures include at most eight `schemaIssues` with known field paths and validation keywords; an empty path means the response root or an unrecognized field, whose name is withheld. No response values, arbitrary property names, URLs, or raw causes are printed. Malformed success responses are rejected during `intent`, before signing or writing a pending-send marker. Use the returned recovery command for the same publication; the CLI never automatically retries intent requests or resubmits transactions. The fingerprint identifies the local recovery journal, not a server trace; use returned remote IDs to correlate server logs.
20
+
21
+ The CLI measures the exact final multipart request, including metadata and framing. Requests above 4,000,000 bytes emit a structured SKILL_UPLOAD_NEAR_LIMIT warning on stderr and continue; requests above 4,500,000 bytes fail before upload. Exactly 4,000,000 bytes do not warn; exactly 4,500,000 bytes warn and continue. These MB thresholds are decimal. The manifest allowance is separately 3 MiB (3,145,728 bytes) on upload and reads. Repacking a compressed source ZIP may increase its size. Passing admission does not certify production upload success or authorize replaying a previously failed publication.
22
+
23
+ The command groups are `wallet`, `login/status/logout`, `site`, `mcp doctor`, `mcp authorize`, `mcp grant`, `creator`, `credentials`, `intent`, `operation show`, `purchase`, `task`, and `skill`. Run `finch --help` for the exact command list as one JSON value.
24
+
25
+ `finch site open` creates a sixty-second one-use handoff from the active AgentCLI Session and opens the exact configured Finch origin in the local default Browser. `finch site login <REQUEST_URL>` binds a website-created five-minute request once, then opens the same confirmation page. Both commands require the wallet, CLI, and graphical Browser on the user's machine. A launch failure returns `BROWSER_OPEN_FAILED` without printing the secret completion URL. The Browser receives only a normal seven-day Finch Web Session; wallet signing, broadcast, and market confirmation remain local CLI actions.
26
+
27
+ `finch mcp doctor` is read-only. It returns a `checks` object for endpoint, wallet, local Session, remote Session authority, and MCP discovery. Each check reports `ok`, `missing`, `invalid`, `unavailable`, or `skipped`, with guidance for unhealthy checks. Missing login or wallet state does not prevent independent discovery checks; an invalid endpoint prevents network checks. The command emits one JSON report with exit code 0 when ready, or 2 when any required check is unhealthy. It verifies that the active CLI authority, AgentCLI Session, local wallet, MCP challenge, OAuth metadata, and source-controlled persistent-Harness manifest agree. The Agent Harness owns Remote MCP configuration, OAuth credentials, and refresh; the CLI does not configure or launch Codex and never reads Harness OAuth material. During the Harness's native OAuth flow, the authorization page supplies an exact `finch mcp authorize <REQUEST_TOKEN>` command. The versioned base64url token carries the original OAuth request without shell metacharacters so Windows package-manager and Volta launchers cannot split it; the CLI decodes it before applying the existing origin, callback, state, and scope checks. That command uses only the active local AgentCLI Session and opens the verified callback without returning its one-use code. It never uses a Browser Session, Social account, or extension wallet as Agent authority. Configure `https://www.finchtech.ai/mcp` once through the Harness, then use MCP `identity_actor_get` together with `finch status` to verify exact Account and wallet alignment before custody. The server-reported environment is diagnostic and never selects the CLI endpoint or a transaction chain.
28
+
29
+ Doctor's `ready: true` is scoped by `readinessScope: "cli_session_and_mcp_discovery"`. Its `harnessOAuth.status` is always `not_checked`: CLI login and valid discovery metadata do not establish that the connected Harness has usable OAuth credentials, a saved issuer, or working refresh. The result includes identity-verification and native reauthorization guidance. If the Harness reports an OAuth failure, complete its native MCP authorization flow and the authorization page's fresh `finch mcp authorize` command, then verify MCP identity again.
30
+
31
+ Run `finch login --help` to list supported production authentication chain IDs (1, 10, 56, 8453, 42161). For example, `finch login --chain-id 56` selects BNB Smart Chain for authentication only.
32
+
33
+ Ordinary installs require no profile file or environment selection. Network commands default to the PROD endpoint at `https://www.finchtech.ai`. `finch login --chain-id <CHAIN_ID>` explicitly selects only the authentication chain, which the selected server must support. Market transaction chains come from Remote MCP/API plans and are independent of the login chain. `intent approve` signs only the shared publication-v2 or Local-purchase-v1 EIP-712 authorization; it cannot sign arbitrary transaction calldata.
34
+
35
+ `operation show` and signing-decision recovery require an active, unexpired MCP grant for the current account and endpoint with `agent_market:operations:read`. The CLI selects that grant from protected local state and never substitutes the AgentCLI Session bearer.
36
+
37
+ Creator commands are thin projections of the shared Agent Market merchant operations. Reads require `agent_market:merchant:read`; mutations require `agent_market:merchant:write`, a stable caller-chosen `--request-id` UUID for local idempotency recovery, and `--expected-revision` wherever the shared operation requires If-Match. Structured request bodies come from bounded JSON `--from-file` documents and are validated against the same shared schemas before HTTP.
38
+
39
+ Local AgentOn HMAC generation is not a product command. `credentials material-download <setupId> --out-file <absolute-path>` claims the AgentOn setup, downloads only its exact live lease as raw FCR1, and creates a new owner-only file; stdout contains only `keyId` and `path`. `credentials fulfill` accepts only Direct credential uploads. It accepts either an existing protected FCR1 with `--from-file <path>`, or a protected raw Bearer token with `--bearer-token-file <path> --key-id <keyId>`; the latter validates one whitespace-free token and adds the `Bearer ` prefix only in CLI memory. Secret bytes never enter argv or stdout.
40
+
41
+ `purchase transaction <operationId>` is read-only. It requires an active `agent_market:buyer:purchase` MCP grant and returns the two server-composed transaction requests plus their chain authority; it does not sign or broadcast arbitrary calldata.
42
+
43
+ `purchase submit <operationId>` requires the same grant and uses only the source-controlled public RPCs for the plan's chain. It uses the protected wallet to re-encode and submit the exact server-composed USDC approval and AgentService Vault purchase. Before signing it verifies `eth_chainId`, the reviewed chain contract authority, every transaction field, and a successful `eth_call` simulation. Every returned broadcast hash is atomically checkpointed before receipt waiting; a restart re-verifies the exact wallet, call, receipt, block, and current server authority and never re-broadcasts a known phase. Finch then verifies the canonical confirmation event and creates Call Rights. If the purchase receipt conclusively reverts, the paired zero-allowance cleanup follows the same checkpoint discipline; unknown receipts are never guessed. The RPC owns nonce selection. User RPC and environment variables are ignored, and neither RPC URLs nor wallet keys are emitted.
44
+
45
+ `purchase recover-approval <operationId> --transaction-hash <hash>` is the narrow adoption path for an approval broadcast before its durable checkpoint was written. It re-fetches the current server authority and active wallet, then verifies the exact successful approval transaction and sufficient live token allowance on the plan chain before writing one owner-only recovery journal. It never broadcasts, confirms, scans chain history, or accepts a nonce. A subsequent `purchase submit` continues with the purchase phase without approving again.
46
+
47
+ If the process exits after broadcasting the Vault purchase but before confirmation returns, recover with `purchase confirm <operationId> --transaction-hash <hash>`. This command never trusts the supplied hash: Finch fetches and decodes the canonical chain receipt before any Call Rights mutation.
48
+
49
+ Task and Skill business discovery and preparation stay in the Remote MCP server. The `task` commands only fund an exact prepared Task pool, sign exact prepared awards, or submit an exact prepared reclaim. Skill purchase, conversion, price, and supply commands submit strict MCP preparations. `skill publish-submit --path <file-or-directory> --from-file <plan>` and `skill version-submit` accept either one file or one directory, then apply the plan's independent `plaintext`, `finchip_v2`, or `lit` content mode; filtering, deterministic ZIP construction, encryption, upload, and key custody remain local. `skill asset-upload --path <cover> --from-file <plan>` uploads a separately prepared public cover, and `skill presentation-submit --from-file <plan>` publishes bounded public presentation metadata without changing protected package bytes. `skill download --from-file <delivery> --out-file <new-file>` downloads IPFS content directly or signs with the entitled local wallet for protected content, verifies the SkillRoot and every file (or the plaintext ZIP hash for historical Skills), and creates an owner-only file. Every transaction action uses its plan's chain, source-controlled public RPCs, and reviewed chain-level contract authority. The CLI recomputes transaction bytes locally and rejects mismatched chains, destinations, calldata, values, source commits, or envelope tags.
50
+
51
+ ## ZIP import and SkillRoot publication
52
+
53
+ Version 0.3.1 fixes Skill search continuation: pass the returned `nextCursor` unchanged as
54
+ `--cursor`. Search uses the server rules; the CLI does not implement a matching algorithm.
55
+ Older clients receive an upgrade message from the Skill search API.
56
+
57
+ Version 0.3.0 upgrades Skill publication and encrypted delivery together. Upgrade with
58
+ `pnpm add -g @finchtech/cli@0.3.6` and verify `finch --version` before publishing new Skills
59
+ or downloading encrypted Skills from the upgraded gateway. Older CLI versions cannot use
60
+ the new encrypted delivery, including for historical Skills; 0.3.0 retains historical package support.
61
+
62
+ A single `.zip` passed to `skill publish-submit --path` or `skill version-submit --path` is expanded locally before filtering and primary-document hashing; request a `file` source plan for this input. Directory inputs keep ZIP assets as ordinary files. Imports support stored/DEFLATE ZIPs up to 10 MiB compressed and expanded, with at most 1024 entries. Unsafe paths, symbolic links, encrypted ZIPs, ZIP64, conflicting entries, and corrupt content are refused. Include `SKILL.md`, `README.md`, or one unambiguous root Markdown document.
63
+
64
+ New Skills publish a DAG-CBOR SkillRoot hash in one graph bundle. Existing Skill identities keep ZIP-hash updates. New Skills require this updated CLI; it verifies every object and decrypts protected files locally into an ordinary ZIP. Historical ZIP downloads and whole-package decryption remain supported. A pending publication reuses its uploaded bytes and saved root commitment on resume.
65
+
66
+ The [publication ADR](../../docs/adr/ADR-0064-20260908-skillroot-publication.md) specifies the pinned draft, bundle/profile format, compatibility trust boundary, and rollout order.
67
+
68
+ Finch-managed and Lit downloads use Oracle V2 device-sealed grants and a non-extractable local CryptoKey.
69
+ The updated gateway also supports historical Finch-managed and Lit ZIPs through this delivery. It refuses
70
+ old bare-key buyer requests; use the updated CLI. Both providers use the same sealed delivery. See
71
+ [ADR-0065](../../docs/adr/ADR-0065-20260908-skill-oracle-v2-delivery.md) for deployment requirements.
72
+
73
+ Publication preparation accepts an optional `summary` (up to 500 characters). It is committed in
74
+ SkillRoot exactly as supplied, matching Browser publication metadata; omission means an empty summary.
75
+ Equal public file bytes, paths, name, summary, version and history produce equal roots. Encrypted
76
+ publications use random keys/IVs and need not produce the same root. Updates send the proposed
77
+ manifest for server validation; URI intents are prepared after the new chain binding is confirmed.
78
+
79
+ Finch-managed publication key requests use a signed timestamp and a one-time nonce. Each request is
80
+ valid for five minutes; retrying after expiry or a lost response requires a fresh signature. The
81
+ gateway rejects older CLI publication requests without timestamps. Historical downloads are unchanged.
82
+
83
+ ### Local publication failures
84
+
85
+ Directory publication rejects symbolic links, including internal relative links and broken links.
86
+ It skips excluded paths (for example `.git`, `node_modules`, `.aws`, and credential files) before reading
87
+ or descending into them. `excluded` lists a skipped directory once instead of enumerating its children.
88
+ Keep required resource paths functional when preparing a link-free source; deleting required links is
89
+ not a valid repair. ZIP imports also reject symbolic links.
90
+
91
+ Source errors keep exit code 2 and `AGENT_MARKET_REQUEST_INVALID`. Parse the JSON string in `error.state`
92
+ for `diagnosticCode`: `SKILL_SOURCE_SYMLINK_UNSUPPORTED`, `SKILL_SOURCE_UNREADABLE`, or
93
+ `SKILL_UPLOAD_SIZE_INVALID`, `SKILL_SOURCE_ZIP_INVALID`, `SKILL_SOURCE_PRIMARY_DOCUMENT_INVALID`, `SKILL_UPLOAD_REQUEST_TOO_LARGE`, or `SKILL_MANIFEST_SIZE_INVALID`. Source diagnostics include a root-relative `path` (`.` for the selected
94
+ root); size diagnostics include `actualBytes` and `maxBytes`. The 10 MiB internal packaging bound is independent of the stricter HTTP limit and applies to the final
95
+ upload object, including SkillRoot metadata and encryption overhead. STORE ZIP construction does not
96
+ compress files. A size rejection occurs before that upload and before transaction submission.
97
+
98
+ Publish, version, and publication-recovery failures also include `phase`, `fingerprint`,
99
+ `transactionHashes` (keyed by `primary`, `uri`, or `lit`), `submissionStatus`, and `recoveryCommand` in
100
+ `error.state`. `packaging` includes local hashing/encryption; `transaction` covers signing, broadcast,
101
+ and receipt handling. A validated remote error keeps its code, message, trace and HTTP status
102
+ (`remoteStatus`), with its original state retained as `remoteState`; publication retry guidance takes
103
+ precedence over a remote service's generic retry flag. Private causes, RPC URLs, credentials and
104
+ journal contents are never printed.
105
+
106
+ Publication failures set `retryable: false`: this is not permission to replay a paid operation.
107
+ `not_attempted` means no send was attempted in this invocation and no hashes were found in the
108
+ validated journal; it is not proof about other invocations. `hash_known` includes known transaction
109
+ hashes. `uncertain` means sending or durable checkpointing failed; contact support before retrying or
110
+ recovering, even when a hash is shown. A non-null `recoveryCommand` is provided only after a journal
111
+ is validated or saved and no submission/checkpoint uncertainty was observed. Recheck CLI/MCP identity
112
+ and use that command rather than repeating publish-submit. Cleanup failures do not claim a journal
113
+ still exists. If source is unavailable or its excluded-path inventory changed across CLI versions,
114
+ the fingerprint-only recovery command can use the already stored publication bytes.
115
+
116
+ These fixes require CLI 0.3.3 or newer; npm 0.3.2 does not include them. Updating a Skill does not upgrade the installed CLI. Journal v6 saves a pending stage before signing/submitting. Missing hashes in a pending stage or a legacy v4/v5 journal block automatic resubmission; contact support. Known hashes are verified without rebroadcast. The pending marker also blocks destructive local authorization/wallet cleanup through the recovery inventory.
117
+
118
+ ## Bootstrap and health reports (CLI 0.3.4+)
119
+
120
+ First-wallet offline import, installed Account-switch revocation, partial doctor checks and exit code 2, contextual wallet recovery guidance, login authentication-chain help, and strict OAuth UUID response validation require CLI 0.3.4 or newer. Updating the website or Skill does not upgrade an installed CLI.
121
+
122
+ ## WebP image detection (CLI 0.3.5+)
123
+
124
+ CLI 0.3.5 fixes valid WebP covers and packaged detail images being rejected when binary file-size bytes were decoded as UTF-8. Upgrade the installed CLI to receive the local detection fix; website or Skill updates alone do not update the executable. Image types, size limits, and publication recovery rules are unchanged.
125
+
126
+ ## Market category compatibility (CLI 0.3.6+)
127
+
128
+ Use CLI 0.3.6 or newer for the expanded market categories. Skill response display categories now accept bounded text, so future taxonomy additions do not require another CLI upgrade. Updating the website or this Skill does not upgrade an installed CLI.
package/dist/finch.js CHANGED
@@ -4349,7 +4349,7 @@ var require_core = __commonJS({
4349
4349
  errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
4350
4350
  if (!errors || errors.length === 0)
4351
4351
  return "No errors";
4352
- return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text2, msg) => text2 + separator + msg);
4352
+ return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text3, msg) => text3 + separator + msg);
4353
4353
  }
4354
4354
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
4355
4355
  const rules = this.RULES.all;
@@ -7224,11 +7224,32 @@ var init_delivery_message = __esm({
7224
7224
  }
7225
7225
  });
7226
7226
 
7227
+ // ../../packages/skill-content/src/image-signature.ts
7228
+ function matchesAscii(bytes, offset, tag) {
7229
+ if (bytes.length < offset + tag.length) return false;
7230
+ return [...tag].every((character, index2) => bytes[offset + index2] === character.charCodeAt(0));
7231
+ }
7232
+ function detectedImageMediaType(bytes) {
7233
+ if (bytes.length >= PNG_SIGNATURE.length && PNG_SIGNATURE.every((byte, index2) => bytes[index2] === byte)) return "image/png";
7234
+ if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
7235
+ if (matchesAscii(bytes, 0, "GIF87a") || matchesAscii(bytes, 0, "GIF89a")) return "image/gif";
7236
+ if (matchesAscii(bytes, 0, "RIFF") && matchesAscii(bytes, 8, "WEBP")) return "image/webp";
7237
+ return null;
7238
+ }
7239
+ var PNG_SIGNATURE;
7240
+ var init_image_signature = __esm({
7241
+ "../../packages/skill-content/src/image-signature.ts"() {
7242
+ "use strict";
7243
+ PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
7244
+ }
7245
+ });
7246
+
7227
7247
  // ../../packages/skill-content/src/static-detail.ts
7228
7248
  var MAX_SKILL_DETAIL_OBJECT_BYTES, MAX_SKILL_DETAIL_DOCUMENT_BYTES, NAMED_REFERENCES;
7229
7249
  var init_static_detail = __esm({
7230
7250
  "../../packages/skill-content/src/static-detail.ts"() {
7231
7251
  "use strict";
7252
+ init_image_signature();
7232
7253
  MAX_SKILL_DETAIL_OBJECT_BYTES = 4 * 1024 * 1024;
7233
7254
  MAX_SKILL_DETAIL_DOCUMENT_BYTES = MAX_SKILL_DETAIL_OBJECT_BYTES;
7234
7255
  NAMED_REFERENCES = Object.freeze({
@@ -9558,7 +9579,7 @@ function baseX({ name, prefix, alphabet, caseInsensitive = false }) {
9558
9579
  prefix,
9559
9580
  name,
9560
9581
  encode: encode10,
9561
- decode: (text2) => coerce(decode10(text2))
9582
+ decode: (text3) => coerce(decode10(text3))
9562
9583
  });
9563
9584
  }
9564
9585
  function decode2(string5, alphabetIdx, bitsPerChar, name) {
@@ -9678,12 +9699,12 @@ var init_base = __esm({
9678
9699
  this.prefixCodePoint = prefixCodePoint;
9679
9700
  this.baseDecode = baseDecode;
9680
9701
  }
9681
- decode(text2) {
9682
- if (typeof text2 === "string") {
9683
- if (text2.codePointAt(0) !== this.prefixCodePoint) {
9684
- throw Error(`Unable to decode multibase string ${JSON.stringify(text2)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`);
9702
+ decode(text3) {
9703
+ if (typeof text3 === "string") {
9704
+ if (text3.codePointAt(0) !== this.prefixCodePoint) {
9705
+ throw Error(`Unable to decode multibase string ${JSON.stringify(text3)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`);
9685
9706
  }
9686
- return this.baseDecode(text2.slice(this.prefix.length));
9707
+ return this.baseDecode(text3.slice(this.prefix.length));
9687
9708
  } else {
9688
9709
  throw Error("Can only multibase decode strings");
9689
9710
  }
@@ -11737,6 +11758,7 @@ var init_src2 = __esm({
11737
11758
  init_crypto();
11738
11759
  init_delivery_message();
11739
11760
  init_detail_documents();
11761
+ init_image_signature();
11740
11762
  init_manifest();
11741
11763
  init_packaging();
11742
11764
  init_static_detail();
@@ -14645,19 +14667,35 @@ var init_display = __esm({
14645
14667
  "U+3000",
14646
14668
  "U+FEFF",
14647
14669
  "U+D800..U+DFFF"
14670
+ ]),
14671
+ descriptionForbiddenCodePoints: Object.freeze([
14672
+ "U+0000..U+0009",
14673
+ "U+000B..U+000C",
14674
+ "U+000E..U+001F",
14675
+ "U+007F..U+009F",
14676
+ "U+00A0",
14677
+ "U+1680",
14678
+ "U+2000..U+200A",
14679
+ "U+2028",
14680
+ "U+2029",
14681
+ "U+202F",
14682
+ "U+205F",
14683
+ "U+3000",
14684
+ "U+FEFF",
14685
+ "U+D800..U+DFFF"
14648
14686
  ])
14649
14687
  });
14650
- displayTextSchema = (minBytes, maxBytes) => Object.freeze({
14688
+ displayTextSchema = (minBytes, maxBytes, allowStandardLineBreaks = false) => Object.freeze({
14651
14689
  type: "string",
14652
14690
  description: `NFC display text containing ${minBytes}..${maxBytes} UTF-8 bytes and no forbidden code points.`,
14653
14691
  "x-finch-nfc": true,
14654
14692
  "x-finch-min-utf8-bytes": minBytes,
14655
14693
  "x-finch-max-utf8-bytes": maxBytes,
14656
14694
  "x-finch-no-edge-ascii-space": true,
14657
- "x-finch-forbidden-code-points": DISPLAY_TEXT_CODEC_CONTRACT.forbiddenCodePoints
14695
+ "x-finch-forbidden-code-points": allowStandardLineBreaks ? DISPLAY_TEXT_CODEC_CONTRACT.descriptionForbiddenCodePoints : DISPLAY_TEXT_CODEC_CONTRACT.forbiddenCodePoints
14658
14696
  });
14659
14697
  agentMarketDisplayNameSchema = displayTextSchema(1, 120);
14660
- agentMarketDescriptionSchema = displayTextSchema(0, 2e3);
14698
+ agentMarketDescriptionSchema = displayTextSchema(0, 2e3, true);
14661
14699
  agentMarketSupportContactSchema = displayTextSchema(1, 500);
14662
14700
  agentMarketRevisionDisplaySchema = strictObjectSchema({
14663
14701
  displayName: agentMarketDisplayNameSchema,
@@ -15100,6 +15138,52 @@ var init_identity = __esm({
15100
15138
  }
15101
15139
  });
15102
15140
 
15141
+ // ../../packages/agent-market-contracts/src/v1/localization.ts
15142
+ var text2, listingInputRequestSchema, saveListingRequestSchema, listingSaveResponseSchema;
15143
+ var init_localization = __esm({
15144
+ "../../packages/agent-market-contracts/src/v1/localization.ts"() {
15145
+ "use strict";
15146
+ init_common2();
15147
+ init_display();
15148
+ text2 = strictObjectSchema({
15149
+ name: { type: "string", minLength: 1, maxLength: 120 },
15150
+ description: { type: "string", maxLength: 5e3 },
15151
+ deliverable: { type: "string", maxLength: 2e3 },
15152
+ serviceBoundary: { type: "string", maxLength: 2e3 }
15153
+ }, ["name", "description"]);
15154
+ listingInputRequestSchema = strictObjectSchema({
15155
+ inputLocales: {
15156
+ type: "array",
15157
+ minItems: 1,
15158
+ maxItems: 3,
15159
+ uniqueItems: true,
15160
+ items: { enum: ["en", "zh", "zh-TW"] },
15161
+ description: "Explicit authored languages. One queues the other two; multiple languages disable automatic translation."
15162
+ },
15163
+ translations: strictObjectSchema({ en: text2, zh: text2, "zh-TW": text2 }, [])
15164
+ });
15165
+ saveListingRequestSchema = strictObjectSchema({
15166
+ versionRevision: positiveRevisionSchema,
15167
+ integrationFamily: { enum: ["agenton_v2", "direct_api_v1"] },
15168
+ displayName: agentMarketDisplayNameSchema,
15169
+ description: agentMarketDescriptionSchema,
15170
+ supportContact: { type: "string", minLength: 3, maxLength: 320 },
15171
+ avatarUri: { type: "string", maxLength: 2048 },
15172
+ avatarMediaType: { enum: [null, "image/jpeg", "image/png", "image/webp", "video/mp4", "video/webm"] },
15173
+ category: { type: "string", maxLength: 80 },
15174
+ tags: { type: "array", maxItems: 16, items: { type: "string", minLength: 1, maxLength: 80 } },
15175
+ deliverable: { type: "string", maxLength: 1e3 },
15176
+ serviceBoundary: { type: "string", maxLength: 1e3 },
15177
+ localization: listingInputRequestSchema
15178
+ }, ["versionRevision", "integrationFamily", "displayName", "description", "supportContact", "avatarUri", "avatarMediaType", "category", "tags", "deliverable", "serviceBoundary"]);
15179
+ listingSaveResponseSchema = strictObjectSchema({
15180
+ serviceVersionId: { type: "string", format: "uuid" },
15181
+ versionRevision: positiveRevisionSchema,
15182
+ presentationRevision: positiveRevisionSchema
15183
+ });
15184
+ }
15185
+ });
15186
+
15103
15187
  // ../../packages/agent-market-contracts/src/v1/merchant.ts
15104
15188
  function signedStatusVariants(kind, denialCode, executionFailureCode, successResult, extra = {}) {
15105
15189
  const base3 = (status) => ({ ...signedBase(kind, status), ...extra });
@@ -15119,6 +15203,7 @@ var init_merchant = __esm({
15119
15203
  "../../packages/agent-market-contracts/src/v1/merchant.ts"() {
15120
15204
  "use strict";
15121
15205
  init_common2();
15206
+ init_localization();
15122
15207
  init_validation_input();
15123
15208
  init_display();
15124
15209
  init_actions();
@@ -15194,8 +15279,9 @@ var init_merchant = __esm({
15194
15279
  displayName: agentMarketDisplayNameSchema,
15195
15280
  description: agentMarketDescriptionSchema,
15196
15281
  supportContact: agentMarketSupportContactSchema,
15197
- avatarUri: nullableIpfsUriSchema
15198
- });
15282
+ avatarUri: nullableIpfsUriSchema,
15283
+ localization: listingInputRequestSchema
15284
+ }, ["integrationFamily", "displayName", "description", "supportContact", "avatarUri"]);
15199
15285
  agentResponseSchema = strictObjectSchema({
15200
15286
  agentId: canonicalUuidSchema,
15201
15287
  supportContact: agentMarketSupportContactSchema,
@@ -15283,8 +15369,9 @@ var init_merchant = __esm({
15283
15369
  createAdditionalVersionRequestSchema = strictObjectSchema({
15284
15370
  integrationFamily: { type: "string", enum: integrationFamilyValues },
15285
15371
  displayName: agentMarketDisplayNameSchema,
15286
- description: agentMarketDescriptionSchema
15287
- });
15372
+ description: agentMarketDescriptionSchema,
15373
+ localization: listingInputRequestSchema
15374
+ }, ["integrationFamily", "displayName", "description"]);
15288
15375
  beginVersionDraftRequestSchema = emptyStrictObjectSchema;
15289
15376
  beginVersionDraftResponseSchema = strictObjectSchema({
15290
15377
  serviceVersionId: canonicalUuidSchema,
@@ -15371,7 +15458,7 @@ var init_merchant = __esm({
15371
15458
  artifactContract: artifactBatchContractSchema,
15372
15459
  validationInput: validationInputSchema
15373
15460
  };
15374
- createOfferRequestSchema = strictObjectSchema(offerConfigurationProperties);
15461
+ createOfferRequestSchema = strictObjectSchema({ ...offerConfigurationProperties, localization: listingInputRequestSchema }, Object.keys(offerConfigurationProperties));
15375
15462
  replaceOfferRequestSchema = createOfferRequestSchema;
15376
15463
  offerResponseSchema = strictObjectSchema({
15377
15464
  offerId: canonicalUuidSchema,
@@ -15991,6 +16078,7 @@ var families, AGENT_MARKET_V1_OPERATIONS, CLI_IDEMPOTENCY_RETRY_CONTRACT, REQUES
15991
16078
  var init_operations = __esm({
15992
16079
  "../../packages/agent-market-contracts/src/v1/operations.ts"() {
15993
16080
  "use strict";
16081
+ init_localization();
15994
16082
  init_artifacts();
15995
16083
  init_agent_cli();
15996
16084
  init_buyer();
@@ -16106,6 +16194,19 @@ var init_operations = __esm({
16106
16194
  sessionWrites: false,
16107
16195
  writes: true
16108
16196
  }),
16197
+ "merchant_listings.replace": operation({
16198
+ id: "merchant_listings.replace",
16199
+ requiredScope: "agent_market:merchant:write",
16200
+ http: http("put", "/api/v1/agent-market/merchant/versions/{versionId}/listing", 200, {
16201
+ requestSchema: saveListingRequestSchema,
16202
+ responseSchema: listingSaveResponseSchema,
16203
+ idempotencyRequired: true
16204
+ }),
16205
+ mcp: { name: "agent_market_merchant_listing_replace" },
16206
+ cli: null,
16207
+ integrationFamilies: families.active,
16208
+ writes: true
16209
+ }),
16109
16210
  "merchant_versions.get": operation({
16110
16211
  id: "merchant_versions.get",
16111
16212
  requiredScope: "agent_market:merchant:read",
@@ -17329,6 +17430,7 @@ var init_v1 = __esm({
17329
17430
  init_snapshot();
17330
17431
  init_testnet_purchase_v1();
17331
17432
  init_validation_input();
17433
+ init_localization();
17332
17434
  }
17333
17435
  });
17334
17436
 
@@ -24898,8 +25000,8 @@ var capitalizeFirstCharacter, error28;
24898
25000
  var init_lt = __esm({
24899
25001
  "../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/locales/lt.js"() {
24900
25002
  init_util();
24901
- capitalizeFirstCharacter = (text2) => {
24902
- return text2.charAt(0).toUpperCase() + text2.slice(1);
25003
+ capitalizeFirstCharacter = (text3) => {
25004
+ return text3.charAt(0).toUpperCase() + text3.slice(1);
24903
25005
  };
24904
25006
  error28 = () => {
24905
25007
  const Sizable = {
@@ -32940,12 +33042,12 @@ var init_skill_protocol = __esm({
32940
33042
  name: "BNB Smart Chain",
32941
33043
  explorerUrl: "https://bscscan.com",
32942
33044
  defaultRpcUrls: [
33045
+ "https://bsc.rpc.blxrbdn.com",
32943
33046
  "https://0.48.club",
32944
- "https://bsc-mainnet.public.blastapi.io",
32945
33047
  "https://bsc-dataseed.binance.org",
32946
33048
  "https://bsc-dataseed1.defibit.io"
32947
33049
  ],
32948
- maxLogBlockRange: 500
33050
+ maxLogBlockRange: 5e3
32949
33051
  },
32950
33052
  sourceRepository: "FinchipAI/finchip-protocol",
32951
33053
  sourceCommit: "89e54d6ff46dbfba61122e90c5e77f409f02fe5e",
@@ -32971,10 +33073,10 @@ var init_skill_protocol = __esm({
32971
33073
  name: "Base",
32972
33074
  explorerUrl: "https://basescan.org",
32973
33075
  defaultRpcUrls: [
32974
- "https://mainnet.base.org",
32975
- "https://base.drpc.org"
33076
+ "https://mainnet.base.org"
32976
33077
  ],
32977
- maxLogBlockRange: 2e3
33078
+ maxLogBlockRange: 2e3,
33079
+ maxConcurrentRequests: 3
32978
33080
  },
32979
33081
  sourceRepository: "FinchipAI/finchip-protocol",
32980
33082
  sourceCommit: "89e54d6ff46dbfba61122e90c5e77f409f02fe5e",
@@ -33261,7 +33363,8 @@ function getSkillProtocolDeployment(environment3, chainId) {
33261
33363
  factoryDeploymentBlock: value.deployment.factoryFirstBlock,
33262
33364
  confirmations: value.deployment.confirmations,
33263
33365
  defaultRpcUrls: Object.freeze([...value.network.defaultRpcUrls]),
33264
- maxLogBlockRange: value.network.maxLogBlockRange ?? DEFAULT_LOG_BLOCK_RANGE
33366
+ maxLogBlockRange: value.network.maxLogBlockRange ?? DEFAULT_LOG_BLOCK_RANGE,
33367
+ maxConcurrentRequests: value.network.maxConcurrentRequests ?? DEFAULT_CONCURRENT_REQUESTS
33265
33368
  });
33266
33369
  }
33267
33370
  function getSkillProtocolDeploymentByChainId(chainId) {
@@ -33272,7 +33375,7 @@ function getSkillProtocolDeploymentByChainId(chainId) {
33272
33375
  function getAllSkillProtocolChainIds() {
33273
33376
  return Object.freeze(Object.keys(SKILL_CLIENT_DEPLOYMENT_BY_CHAIN).map(Number).sort((left, right) => left - right));
33274
33377
  }
33275
- var canonicalAddressSchema, hashSchema, commitSchema, positiveDecimalSchema, DEFAULT_LOG_BLOCK_RANGE, networkSchema, publicRpcUrlsSchema, legacySkillIndexerChainSchema, legacySkillIndexerRegistrySchema, sourceSchema, disabledDeploymentSchema, deploymentsShape, agentActiveSchema, taskActiveSchema, skillActiveSchema, agentRegistrySchema, taskRegistrySchema, skillEnvironmentSchema, skillRegistrySchema, registrySchema, agentRegistry, taskRegistry, skillRegistry, legacySkillIndexerRegistry, AGENT_CLIENT_DEPLOYMENT_BY_CHAIN, TASK_CLIENT_DEPLOYMENT_BY_CHAIN, SKILL_CLIENT_DEPLOYMENT_BY_CHAIN;
33378
+ var canonicalAddressSchema, hashSchema, commitSchema, positiveDecimalSchema, DEFAULT_LOG_BLOCK_RANGE, DEFAULT_CONCURRENT_REQUESTS, networkSchema, publicRpcUrlsSchema, legacySkillIndexerChainSchema, legacySkillIndexerRegistrySchema, sourceSchema, disabledDeploymentSchema, deploymentsShape, agentActiveSchema, taskActiveSchema, skillActiveSchema, agentRegistrySchema, taskRegistrySchema, skillEnvironmentSchema, skillRegistrySchema, registrySchema, agentRegistry, taskRegistry, skillRegistry, legacySkillIndexerRegistry, AGENT_CLIENT_DEPLOYMENT_BY_CHAIN, TASK_CLIENT_DEPLOYMENT_BY_CHAIN, SKILL_CLIENT_DEPLOYMENT_BY_CHAIN;
33276
33379
  var init_chains = __esm({
33277
33380
  "../../packages/deployment-manifest/src/chains/index.ts"() {
33278
33381
  "use strict";
@@ -33288,13 +33391,24 @@ var init_chains = __esm({
33288
33391
  commitSchema = external_exports.string().regex(/^[0-9a-f]{40}$/u);
33289
33392
  positiveDecimalSchema = external_exports.string().regex(/^[1-9][0-9]*$/u);
33290
33393
  DEFAULT_LOG_BLOCK_RANGE = 9999;
33394
+ DEFAULT_CONCURRENT_REQUESTS = 12;
33291
33395
  networkSchema = external_exports.strictObject({
33292
33396
  chainId: external_exports.number().int().positive(),
33293
33397
  name: external_exports.string().min(1).max(100),
33294
33398
  explorerUrl: external_exports.url().refine((value) => new URL(value).protocol === "https:"),
33295
33399
  defaultRpcUrls: external_exports.array(external_exports.url().refine((value) => new URL(value).protocol === "https:")).min(1).max(4),
33296
33400
  /** Omit where every listed endpoint serves the default window. */
33297
- maxLogBlockRange: external_exports.number().int().min(1).max(DEFAULT_LOG_BLOCK_RANGE).optional()
33401
+ maxLogBlockRange: external_exports.number().int().min(1).max(DEFAULT_LOG_BLOCK_RANGE).optional(),
33402
+ /**
33403
+ * Shared by scheduled and receipt readers for this environment/chain within one process.
33404
+ * Omit where every listed endpoint serves the default number of concurrent requests.
33405
+ *
33406
+ * Narrow it for an endpoint that meters compute rather than requests: Base answers four
33407
+ * concurrent `eth_call`s and refuses most of eight with `-32016 over rate limit`, while serving
33408
+ * twelve concurrent balance reads without complaint. The number that matters is the one measured
33409
+ * with the call the indexer actually makes.
33410
+ */
33411
+ maxConcurrentRequests: external_exports.number().int().min(1).max(DEFAULT_CONCURRENT_REQUESTS).optional()
33298
33412
  });
33299
33413
  publicRpcUrlsSchema = external_exports.array(
33300
33414
  external_exports.url().refine((value) => new URL(value).protocol === "https:")
@@ -33858,12 +33972,12 @@ var init_merchant_avatar_video = __esm({
33858
33972
  test: {
33859
33973
  enabled: true,
33860
33974
  offerId: "e72aeda7-da44-5685-b5a0-4d90cfa63c2d",
33861
- dailyLimit: 10
33975
+ dailyLimit: 3
33862
33976
  },
33863
33977
  production: {
33864
33978
  enabled: true,
33865
33979
  offerId: "1cf22f74-a451-54f0-894a-baff6221693d",
33866
- dailyLimit: 10
33980
+ dailyLimit: 3
33867
33981
  }
33868
33982
  }
33869
33983
  };
@@ -34102,21 +34216,18 @@ var init_common4 = __esm({
34102
34216
  }
34103
34217
  });
34104
34218
 
34219
+ // ../../packages/market-categories/src/index.ts
34220
+ var init_src4 = __esm({
34221
+ "../../packages/market-categories/src/index.ts"() {
34222
+ }
34223
+ });
34224
+
34105
34225
  // ../../packages/skill-market-contracts/src/public.ts
34106
- var SKILL_CATEGORIES, SKILL_V3_ENVELOPE_SCHEME, SKILL_V3_LIT_ENVELOPE_SCHEME;
34226
+ var SKILL_V3_ENVELOPE_SCHEME, SKILL_V3_LIT_ENVELOPE_SCHEME;
34107
34227
  var init_public = __esm({
34108
34228
  "../../packages/skill-market-contracts/src/public.ts"() {
34109
34229
  "use strict";
34110
- SKILL_CATEGORIES = [
34111
- "Dev Environment",
34112
- "Documents & Legal",
34113
- "Security Audit",
34114
- "Finance & Tax",
34115
- "Education & Research",
34116
- "Media & Entertainment",
34117
- "Health & Medicine",
34118
- "Other"
34119
- ];
34230
+ init_src4();
34120
34231
  SKILL_V3_ENVELOPE_SCHEME = "FINCHIP_V2";
34121
34232
  SKILL_V3_LIT_ENVELOPE_SCHEME = "LIT_V1";
34122
34233
  }
@@ -34150,7 +34261,6 @@ var init_client = __esm({
34150
34261
  init_platform();
34151
34262
  init_common4();
34152
34263
  init_public();
34153
- init_public();
34154
34264
  SKILL_V3_CHAIN_IDS = getAllSkillProtocolChainIds();
34155
34265
  SKILL_V3_DEPLOYMENTS = Object.freeze(SKILL_V3_CHAIN_IDS.map((chainId) => {
34156
34266
  const deployment = getSkillProtocolDeploymentByChainId(chainId);
@@ -34158,7 +34268,7 @@ var init_client = __esm({
34158
34268
  }));
34159
34269
  versionSchema = { type: "integer", minimum: 1, maximum: 4294967295 };
34160
34270
  chainIdSchema2 = { type: "integer", enum: SKILL_V3_CHAIN_IDS };
34161
- nullableCategorySchema = { type: ["string", "null"], enum: [...SKILL_CATEGORIES, null] };
34271
+ nullableCategorySchema = { type: ["string", "null"], minLength: 1, maxLength: 100 };
34162
34272
  signingAuthoritySchema = strictObjectSchema3({
34163
34273
  accountId: canonicalUuidSchema3,
34164
34274
  walletAddress: addressSchema2,
@@ -56304,15 +56414,6 @@ function assetPlan(value, walletAddress2) {
56304
56414
  }
56305
56415
  return { chainId, skillId, acceptedMediaTypes: accepted, maxBytes: plan.maxBytes };
56306
56416
  }
56307
- function coverMediaType(bytes) {
56308
- const png = [137, 80, 78, 71, 13, 10, 26, 10];
56309
- if (bytes.length >= 8 && png.every((byte, index2) => bytes[index2] === byte)) return "image/png";
56310
- if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
56311
- const head = new TextDecoder().decode(bytes.slice(0, 12));
56312
- if (head.startsWith("GIF87a") || head.startsWith("GIF89a")) return "image/gif";
56313
- if (head.startsWith("RIFF") && head.slice(8, 12) === "WEBP") return "image/webp";
56314
- return null;
56315
- }
56316
56417
  function isPlaintextSkillDelivery(value) {
56317
56418
  return value !== null && typeof value === "object" && !Array.isArray(value) && value.plaintext === true;
56318
56419
  }
@@ -57189,7 +57290,7 @@ var init_skill_local_flow = __esm({
57189
57290
  if (!stat.isFile() || stat.isSymbolicLink()) throw new TypeError("Skill cover path must be one regular file.");
57190
57291
  if (stat.size < 1 || stat.size > plan.maxBytes) throw new TypeError("Skill cover size is outside the prepared bound.");
57191
57292
  const bytes = new Uint8Array(await readFile2(absolute));
57192
- const mediaType = coverMediaType(bytes);
57293
+ const mediaType = detectedImageMediaType(bytes);
57193
57294
  if (mediaType === null || !plan.acceptedMediaTypes.includes(mediaType)) {
57194
57295
  throw new TypeError("Skill cover bytes do not match an accepted image type.");
57195
57296
  }
@@ -57431,8 +57532,8 @@ async function readBoundedJsonFile(path2) {
57431
57532
  if (after.size !== before.size || bytes.byteLength !== before.size) {
57432
57533
  throw new TypeError("Request file changed while it was read.");
57433
57534
  }
57434
- const text2 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
57435
- return asObject(JSON.parse(text2), "Request file");
57535
+ const text3 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
57536
+ return asObject(JSON.parse(text3), "Request file");
57436
57537
  } catch (cause) {
57437
57538
  if (cause instanceof TypeError) throw cause;
57438
57539
  throw new TypeError("Request file is invalid.", { cause });
@@ -59045,7 +59146,7 @@ var init_package = __esm({
59045
59146
  "package.json"() {
59046
59147
  package_default = {
59047
59148
  name: "@finchtech/cli",
59048
- version: "0.3.4",
59149
+ version: "0.3.6",
59049
59150
  description: "Finch Agent CLI for wallet-owned marketplace and MCP workflows.",
59050
59151
  type: "module",
59051
59152
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finchtech/cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Finch Agent CLI for wallet-owned marketplace and MCP workflows.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,11 +34,11 @@
34
34
  "typescript-eslint": "8.67.0",
35
35
  "viem": "2.55.15",
36
36
  "vitest": "4.1.10",
37
- "@finch/deployment-manifest": "0.1.0",
38
37
  "@finch/agent-market-contracts": "0.1.0",
39
- "@finch/skill-market-contracts": "0.1.0",
38
+ "@finch/task-market-contracts": "0.1.0",
40
39
  "@finch/skill-content": "0.1.0",
41
- "@finch/task-market-contracts": "0.1.0"
40
+ "@finch/deployment-manifest": "0.1.0",
41
+ "@finch/skill-market-contracts": "0.1.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "esbuild src/bootstrap.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/finch.js --banner:js=\"#!/usr/bin/env node\"",