@finchtech/cli 0.5.1 → 0.5.2

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 +10 -6
  2. package/dist/finch.js +199 -166
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -4,7 +4,7 @@ Install the public npm package with `npm install -g @finchtech/cli`, then run `f
4
4
 
5
5
  The Finch Agent CLI stores wallet keys and AgentCLI Session bearers only in an owner-only `FINCHTECH_HOME`, which defaults to `~/.finchtech`; secrets are never accepted as command arguments or emitted to stdout.
6
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 32-byte hex private key (64 hexadecimal digits, optionally prefixed with `0x`) 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 with a stored Session revokes that Account before changing the current wallet. With no stored Session, switching is offline and does not claim remote revocation. A pending login must be recovered before switching; other partial or corrupt Session state requires explicit repair. Incomplete wallet files require recovery from backup; residual local authorization requires restoring the original wallet or explicitly clearing local authorization with `finch logout --local`.
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 32-byte hex private key (64 hexadecimal digits, optionally prefixed with `0x`) and have owner-only permissions. First import is offline and makes that wallet current. `finch wallet create` generates a new identity instead. `finch wallet list` shows offline the current wallet and every registered address; `finch wallet use <ADDRESS>` accepts only one of those addresses. An existing Account switch with a stored Session revokes that Account before changing the current wallet. With no stored Session, switching is offline and does not claim remote revocation. A pending login must be recovered before switching; other partial or corrupt Session state requires explicit repair. 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
8
 
9
9
  `finch logout` revokes Account-bound CLI/MCP remote authority before removing local Session and grant state. `finch logout --local` clears only local `session/` and `grants/`, makes no network request and reports `remoteRevoked: false`. Both preserve profiles, registered wallets and transaction/publication journals. Ordinary logout and wallet switching proceed independently of unfinished journals; resume a journal later with its original wallet and matching Account/environment.
10
10
 
@@ -20,11 +20,11 @@ Publication intent HTTP failures identify the Session-owned `/api/v1/agent-cli/s
20
20
 
21
21
  The CLI measures the exact final multipart request, including metadata and framing. At or below 4,000,000 bytes it posts that request to the inline upload route. Above it the object goes through the staged transport instead: the CLI asks for a signed URL, writes the bytes straight to storage, and has the server read them back, verify them and pin them, so the platform's 4,500,000-byte request-body limit does not apply. These MB thresholds are decimal. The per-object ceilings are enforced against the final object either way: 30 MiB (31,457,280 bytes) for a package, 5 MiB for a cover, and 3 MiB (3,145,728 bytes) for a manifest. The package ceiling is one shared constant, applied by upload admission, by the retrieval route, by this client before it uploads, and by this client when it downloads, so a package that publishes is a package that reads back. 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
22
 
23
- The command groups are `wallet`, `login/status/logout`, `site`, `mcp doctor`, `mcp connect`, `mcp authorize`, `credentials`, `intent`, `purchase`, `task`, and `skill`. Run `finch --help` for the exact command list as one JSON value.
23
+ The command groups are `wallet`, `login/status/logout`, `site`, `mcp doctor`, `mcp connect`, `mcp authorize`, `credentials`, `intent`, `purchase`, `task`, and `skill`. Run `finch --help` for the exact command list as one JSON value. Every command prints exactly one JSON value, so there is no `--json` option. Invalid input returns `AGENT_MARKET_REQUEST_INVALID` with exit code 2 and a message that names the rejected option, missing argument or unknown subcommand together with the command's usage. Argument values are never echoed.
24
24
 
25
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
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 the endpoint, wallet/key binding, Account, Session identity, environment, audience and expiry, plus the MCP challenge and required OAuth capabilities. Each remote read has a 15-second timeout. Wallet inspection does not migrate legacy files. Client guide availability, client names, guide versions and repository paths do not affect readiness; `compatibilityManifestUrl` is only an informational link. The Agent Harness owns Remote MCP configuration, OAuth credentials, and refresh; the CLI never configures the Harness and never reads Harness OAuth material.
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. A failed network check adds `httpStatus` when a response arrived, or the Node.js `networkErrorCode` (for example `ENOTFOUND`, `TIMEOUT`, or a TLS code such as `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`) when none did; TLS certificate failures also name the HTTPS-intercepting proxy case. 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 the endpoint, wallet/key binding, Account, Session identity, environment, audience and expiry, plus the MCP challenge and required OAuth capabilities. Each remote read has a 15-second timeout. Wallet inspection does not migrate legacy files. Client guide availability, client names, guide versions and repository paths do not affect readiness; `compatibilityManifestUrl` is only an informational link. The Agent Harness owns Remote MCP configuration, OAuth credentials, and refresh; the CLI never configures the Harness and never reads Harness OAuth material.
28
28
 
29
29
  The CLI approves the Harness's native OAuth request with the active local AgentCLI Session through one of three entry points, tried in this order:
30
30
 
@@ -36,7 +36,7 @@ Each entry point applies the same origin, redirect, state, scope, and RFC 9207 i
36
36
 
37
37
  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, start its native MCP authorization flow again, approve the fresh request through the entry points above, then verify MCP identity again.
38
38
 
39
- Run `finch login --help` to list supported production authentication chain IDs (1, 10, 56, 8453, 42161, 43114). For example, `finch login --chain-id 56` selects BNB Smart Chain for authentication only.
39
+ Run `finch login --help` to list supported production authentication chain IDs (1, 10, 56, 8453, 42161, 43114, 1776). For example, `finch login --chain-id 56` selects BNB Smart Chain for authentication only.
40
40
 
41
41
  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.
42
42
 
@@ -70,7 +70,7 @@ Recover an interrupted action with `finch task recover <taskId> <publish|award|r
70
70
 
71
71
  Skill preparations return `walletIntent.intentId`. With the updated CLI, run `skill purchase-submit`, `convert-submit`, `price-submit`, or `supply-submit` with `--intent <intentId>`. The commands fetch server-bound transaction bytes and use only the CLI Session for execution and confirmation. `--from-file` reads only the handoff reference; changing price, chain or calldata in the file cannot change the signed call. Prepare unexecuted old plan files again through MCP. CLI 0.5.0 supports this handoff; npm publication is a separate release step.
72
72
 
73
- `skill publish-submit --path <file-or-directory> --intent <intentId>` and `skill version-submit` retain local filtering, ZIP construction, packaging, encryption, upload and key custody. The server fixes the prepared business metadata and records each transaction stage once. `skill asset-upload --path <cover> --intent <intentId>` uploads a prepared public cover. `skill presentation-submit --intent <intentId>` publishes only the prepared public presentation and checkpoints its wallet result. Repeating an intent with a recorded transaction resumes confirmation. An unfinished publication requires its original protected material journal; restoring it preserves uploaded bytes and keys.
73
+ `skill publish-submit --path <file-or-directory> --intent <intentId>` and `skill version-submit` retain local filtering, ZIP construction, packaging, encryption, upload and key custody. The server fixes the prepared business metadata and records each transaction stage once. `skill asset-upload --path <cover> --intent <intentId>` uploads a prepared public cover. `skill presentation-submit` is retired and refuses before wallet or network access. Use Remote MCP `skill_market_presentation_update` to save listing text, cover, demo video and detail tabs as database overrides without a transaction. Repeating an intent with a recorded transaction resumes confirmation. An unfinished publication requires its original protected material journal; restoring it preserves uploaded bytes and keys.
74
74
 
75
75
  Recover wallet results with `skill recover intent <fingerprint>` and publication material with `skill recover publication <fingerprint>`. Recovery preserves broadcast hashes and the publication pending-send protections. Old buyer journals are admitted only as receipt-only recovery after the server reconstructs and verifies their confirmed canonical transaction. Existing publication journals retain their conservative typed recovery path. A confirmed receipt or projected stage can replay without another signature/broadcast; preserve journals during projection lag.
76
76
 
@@ -83,7 +83,7 @@ Version 0.3.1 fixes Skill search continuation: pass the returned `nextCursor` un
83
83
  Older clients receive an upgrade message from the Skill search API.
84
84
 
85
85
  Version 0.3.0 upgrades Skill publication and encrypted delivery together. Upgrade with
86
- `npm install -g @finchtech/cli@0.5.1` and verify `finch --version` before publishing new Skills
86
+ `npm install -g @finchtech/cli@0.5.2` and verify `finch --version` before publishing new Skills
87
87
  or downloading encrypted Skills from the upgraded gateway. Older CLI versions cannot use
88
88
  the new encrypted delivery, including for historical Skills; 0.3.0 retains historical package support.
89
89
 
@@ -180,3 +180,7 @@ Keep existing journals when upgrading. Agent recovery uses the owned operation/i
180
180
  ## Recovery-preserving Account transitions (CLI 0.5.1)
181
181
 
182
182
  CLI 0.5.1 allows ordinary logout and wallet switching with unfinished transaction/publication journals, preserving Account-wide CLI/MCP revocation and the original recovery evidence. It removes the redundant local recovery index. Invalid or expired Sessions receive direct same-authority login guidance. The bundle also includes the shared Avalanche PROD authentication allowlist already deployed on Finch.
183
+
184
+ ## Actionable usage errors, wallet list and network diagnostics (CLI 0.5.2)
185
+
186
+ CLI 0.5.2 names the rejected option, missing argument or unknown subcommand together with the command's usage instead of a generic schema message; every command already prints JSON, so there is no `--json` option. `finch wallet list` shows the current wallet and registered addresses offline. Failed `finch mcp doctor` network checks report `httpStatus` or `networkErrorCode`, so a TLS-intercepting proxy, DNS failure or timeout is distinguishable from a server outage. Its PROD authentication-chain help also lists Injective (`1776`). Upgrade the installed executable to receive these changes; updating the Skill or website alone does not apply them.
package/dist/finch.js CHANGED
@@ -6619,7 +6619,7 @@ var init_package = __esm({
6619
6619
  "package.json"() {
6620
6620
  package_default = {
6621
6621
  name: "@finchtech/cli",
6622
- version: "0.5.1",
6622
+ version: "0.5.2",
6623
6623
  description: "Finch Agent CLI for wallet custody, Session-owned execution, and MCP OAuth approval.",
6624
6624
  type: "module",
6625
6625
  bin: {
@@ -21941,6 +21941,45 @@ var init_agent_service = __esm({
21941
21941
  paymentTokenSymbol: "USDC",
21942
21942
  paymentTokenDecimals: 6
21943
21943
  }
21944
+ },
21945
+ "1776": {
21946
+ enabled: true,
21947
+ network: {
21948
+ chainId: 1776,
21949
+ name: "Injective",
21950
+ explorerUrl: "https://blockscout.injective.network",
21951
+ defaultRpcUrls: [
21952
+ "https://sentry.evm-rpc.injective.network"
21953
+ ]
21954
+ },
21955
+ sourceRepository: "FinchipAI/Finch-Agent-Service-Protocol",
21956
+ sourceCommit: "97f6f0810932b282de482238ec0f9460cdc177b3",
21957
+ contracts: {
21958
+ protocol: "0xd0f6f5be7581ad76f0b6cf10e468c017c9aad902",
21959
+ protocolImplementation: "0xeb426199277c10cd12ade7858bff24eb67d3342e",
21960
+ protocolProxyAdmin: "0xa6cb95ee7262b198c7727344cae7e95afd2922ee",
21961
+ vault: "0xf5bdf5f74c5597151bd3fd978cdd741d9f7f7103",
21962
+ vaultImplementation: "0xe8e53bca9ada0dfc77d977db5d929973db79d2df",
21963
+ vaultProxyAdmin: "0x45869f4af762ff7adee9a8a1a3264c5518320b98",
21964
+ paymentToken: "0xa00c59ff5a080d2b954d0c75e46e22a0c371235a"
21965
+ },
21966
+ authority: {
21967
+ admin: "0x4fcb985a020dc1dd8a5dfc4adf4606eac4f68075",
21968
+ treasury: "0xd5ca4d96a17723fd008b2c3f1a413cb0736a2848",
21969
+ settlementAttestor: "0x1dc1ccc1ebdcdd6c57a8c22776883b45c6824cf6",
21970
+ refundAttestor: "0x1dc1ccc1ebdcdd6c57a8c22776883b45c6824cf6",
21971
+ freezeKeeper: "0x1dc1ccc1ebdcdd6c57a8c22776883b45c6824cf6"
21972
+ },
21973
+ deployment: {
21974
+ firstBlock: "185194836",
21975
+ completedBlock: "185194908",
21976
+ confirmations: 2,
21977
+ deploymentHash: "0x3b6a22dfb42ecc1d4800a60971ccf932bd39a005cec796a9bcef9a31702e0b33"
21978
+ },
21979
+ policy: {
21980
+ paymentTokenSymbol: "USDC",
21981
+ paymentTokenDecimals: 6
21982
+ }
21944
21983
  }
21945
21984
  }
21946
21985
  }
@@ -22328,6 +22367,44 @@ var init_task_protocol = __esm({
22328
22367
  paymentTokenSymbol: "USDC",
22329
22368
  paymentTokenDecimals: 6
22330
22369
  }
22370
+ },
22371
+ "1776": {
22372
+ enabled: true,
22373
+ network: {
22374
+ chainId: 1776,
22375
+ name: "Injective",
22376
+ explorerUrl: "https://blockscout.injective.network",
22377
+ defaultRpcUrls: [
22378
+ "https://sentry.evm-rpc.injective.network"
22379
+ ]
22380
+ },
22381
+ sourceRepository: "FinchipAI/Finch-Task-Protocol",
22382
+ sourceCommit: "b3331c2488e9b5cc7159beb58be0ba3f60430d7e",
22383
+ abiSourceCommit: "c4491b8fe85a09840c5873bc6ac142c580febd32",
22384
+ contracts: {
22385
+ protocol: "0x20ab3a991f987a6f7ccb4e8f21a9c6e1097d7a19",
22386
+ protocolImplementation: "0xe520cd670add2f8813e3c76bd69a033c5cfa5b92",
22387
+ protocolProxyAdmin: "0x2ae16beefb2a74efceb32379a54052871c86aeb8",
22388
+ vault: "0xcb19ea4fa56c3fc0c03892ec4b76e546572ba2b8",
22389
+ vaultImplementation: "0x43fe3e0afb3ed8c2851b6326522e031599a44a30",
22390
+ vaultProxyAdmin: "0xa19b2318184e1f826ccf4d6d7eaeb5356efe3dce",
22391
+ paymentToken: "0xa00c59ff5a080d2b954d0c75e46e22a0c371235a"
22392
+ },
22393
+ authority: {
22394
+ admin: "0x4fcb985a020dc1dd8a5dfc4adf4606eac4f68075",
22395
+ treasury: "0xd5ca4d96a17723fd008b2c3f1a413cb0736a2848",
22396
+ awardAttestor: "0x1dc1ccc1ebdcdd6c57a8c22776883b45c6824cf6",
22397
+ freezeKeeper: "0x1dc1ccc1ebdcdd6c57a8c22776883b45c6824cf6"
22398
+ },
22399
+ deployment: {
22400
+ firstBlock: "185193254",
22401
+ completedBlock: "185193311",
22402
+ confirmations: 2
22403
+ },
22404
+ policy: {
22405
+ paymentTokenSymbol: "USDC",
22406
+ paymentTokenDecimals: 6
22407
+ }
22331
22408
  }
22332
22409
  }
22333
22410
  }
@@ -22991,7 +23068,7 @@ var init_internal_apps = __esm({
22991
23068
  sessionTtlSeconds: 28800,
22992
23069
  queryAgentEnabled: true,
22993
23070
  aiBaseUrl: "https://api.tokenrouter.com/v1",
22994
- aiModel: "xiaomi/mimo-v2.6-flash"
23071
+ aiModel: "xiaomi/mimo-v2.6-pro"
22995
23072
  },
22996
23073
  ops: {
22997
23074
  sessionTtlSeconds: 28800,
@@ -23003,7 +23080,7 @@ var init_internal_apps = __esm({
23003
23080
  sessionTtlSeconds: 28800,
23004
23081
  queryAgentEnabled: true,
23005
23082
  aiBaseUrl: "https://api.tokenrouter.com/v1",
23006
- aiModel: "xiaomi/mimo-v2.6-flash"
23083
+ aiModel: "xiaomi/mimo-v2.6-pro"
23007
23084
  },
23008
23085
  ops: {
23009
23086
  sessionTtlSeconds: 28800,
@@ -23013,9 +23090,9 @@ var init_internal_apps = __esm({
23013
23090
  production: {
23014
23091
  data: {
23015
23092
  sessionTtlSeconds: 28800,
23016
- queryAgentEnabled: false,
23093
+ queryAgentEnabled: true,
23017
23094
  aiBaseUrl: "https://api.tokenrouter.com/v1",
23018
- aiModel: "xiaomi/mimo-v2.6-flash"
23095
+ aiModel: "xiaomi/mimo-v2.6-pro"
23019
23096
  },
23020
23097
  ops: {
23021
23098
  sessionTtlSeconds: 28800,
@@ -23492,13 +23569,15 @@ var init_src = __esm({
23492
23569
  });
23493
23570
 
23494
23571
  // ../../packages/skill-market-contracts/src/public.ts
23495
- var SKILL_V3_ENVELOPE_SCHEME, SKILL_V3_LIT_ENVELOPE_SCHEME;
23572
+ var SKILL_V3_ENVELOPE_SCHEME, SKILL_V3_LIT_ENVELOPE_SCHEME, SKILL_PUBLICATION_ROYALTY_BPS, SKILL_PUBLICATION_USAGE_LIMIT;
23496
23573
  var init_public = __esm({
23497
23574
  "../../packages/skill-market-contracts/src/public.ts"() {
23498
23575
  "use strict";
23499
23576
  init_src();
23500
23577
  SKILL_V3_ENVELOPE_SCHEME = "FINCHIP_V2";
23501
23578
  SKILL_V3_LIT_ENVELOPE_SCHEME = "LIT_V1";
23579
+ SKILL_PUBLICATION_ROYALTY_BPS = 250;
23580
+ SKILL_PUBLICATION_USAGE_LIMIT = 0;
23502
23581
  }
23503
23582
  });
23504
23583
 
@@ -24150,6 +24229,7 @@ function localFailureMessage(error51) {
24150
24229
  }
24151
24230
  return "Protected local state is corrupt. Recreate the affected profile, login, or recovery state.";
24152
24231
  }
24232
+ if (error51 instanceof CliUsageError) return error51.message;
24153
24233
  if (error51.message === "FINCHTECH_HOME is required.") {
24154
24234
  return "FINCHTECH_HOME is required. Set it to the protected Finch Agent CLI state directory.";
24155
24235
  }
@@ -24163,7 +24243,7 @@ function localFailureMessage(error51) {
24163
24243
  if (publicInputMessages.includes(error51.message)) return error51.message;
24164
24244
  return "Command input does not match the expected schema. Check finch help for this command.";
24165
24245
  }
24166
- var CliAuthorityError, CliProductionUnavailableError, BrowserOpenError, MCP_AUTHORIZATION_FLOW_GUIDANCE, McpAuthorizationFlowError, CLI_SESSION_RENEWAL_GUIDANCE, CliSkillSourceError, CliSkillPresentationStaleError, CliSkillRecoveryUncertainError, CliSkillPublicationError;
24246
+ var CliAuthorityError, CliProductionUnavailableError, BrowserOpenError, MCP_AUTHORIZATION_FLOW_GUIDANCE, McpAuthorizationFlowError, CliUsageError, CLI_SESSION_RENEWAL_GUIDANCE, CliSkillSourceError, CliSkillPresentationStaleError, CliSkillRecoveryUncertainError, CliSkillPublicationError;
24167
24247
  var init_cli_error = __esm({
24168
24248
  "src/cli-error.ts"() {
24169
24249
  "use strict";
@@ -24213,6 +24293,12 @@ var init_cli_error = __esm({
24213
24293
  code = "MCP_AUTHORIZATION_FLOW_FAILED";
24214
24294
  guidance;
24215
24295
  };
24296
+ CliUsageError = class extends TypeError {
24297
+ constructor(message) {
24298
+ super(message);
24299
+ this.name = "CliUsageError";
24300
+ }
24301
+ };
24216
24302
  CLI_SESSION_RENEWAL_GUIDANCE = "The CLI Session is invalid or expired. Run finch login --chain-id <CHAIN_ID> using the same wallet, endpoint and authentication chain. Use finch login --help to list supported authentication chains. Existing recovery journals are preserved.";
24217
24303
  CliSkillSourceError = class extends TypeError {
24218
24304
  constructor(diagnosticCode, details, message) {
@@ -28808,9 +28894,6 @@ function packageForVersion(manifest6, version6) {
28808
28894
  function withVersion(manifest6, entry2) {
28809
28895
  return buildManifest([...manifest6?.versions ?? [], entry2], manifest6?.presentation, manifest6?.documents);
28810
28896
  }
28811
- function withPresentation(manifest6, presentation) {
28812
- return buildManifest(manifest6.versions, presentation, manifest6.documents);
28813
- }
28814
28897
  var MAX_SKILL_MANIFEST_BYTES, SKILL_MANIFEST_SCHEME, SKILL_DETAIL_KINDS, DETAIL_MEDIA_TYPES, DETAIL_HASH, DETAIL_ASSET_PATH, DETAIL_IPFS_URI, DETAIL_STORAGE_URI, DETAIL_BUCKET_BY_KIND, DETAIL_MEDIA_HOST;
28815
28898
  var init_manifest = __esm({
28816
28899
  "../../packages/skill-content/src/manifest.ts"() {
@@ -32439,7 +32522,7 @@ var init_identity = __esm({
32439
32522
  "../../packages/agent-market-contracts/src/v1/identity.ts"() {
32440
32523
  "use strict";
32441
32524
  init_common3();
32442
- PRODUCTION_AUTH_CHAIN_IDS = Object.freeze([1, 10, 56, 8453, 42161, 43114]);
32525
+ PRODUCTION_AUTH_CHAIN_IDS = Object.freeze([1, 10, 56, 8453, 42161, 43114, 1776]);
32443
32526
  TEST_AUTH_CHAIN_IDS = Object.freeze([84532]);
32444
32527
  IDENTITY_SCOPE_ORDER_CONTRACT = Object.freeze({
32445
32528
  order: "ascii_lexicographic",
@@ -57569,7 +57652,7 @@ function publishPlan(value, context) {
57569
57652
  const chainId = assertAuthority(plan, "publish", context.walletAddress);
57570
57653
  assertLocalAction(plan.localAction, "skill publish-submit");
57571
57654
  const request2 = object2(plan.request, "Skill publish request");
57572
- exact2(request2, ["chainId", "slug", "name", "category", "licenseTypeName", "imageURI", "sourceKind", "contentMode", "unitPrice", "maxSupply", "royaltyBPS", "usageLimit", ...Object.hasOwn(request2, "summary") ? ["summary"] : []], "Skill publish request");
57655
+ exact2(request2, ["chainId", "slug", "name", "category", "licenseTypeName", "imageURI", "sourceKind", "contentMode", "unitPrice", "maxSupply", ...Object.hasOwn(request2, "summary") ? ["summary"] : []], "Skill publish request");
57573
57656
  if (request2.chainId !== chainId) throw new TypeError("Skill publish request chain is invalid.");
57574
57657
  string4(request2.slug, "Skill slug");
57575
57658
  string4(request2.name, "Skill name");
@@ -57583,8 +57666,6 @@ function publishPlan(value, context) {
57583
57666
  contentMode(request2.contentMode);
57584
57667
  string4(request2.unitPrice, "Skill unitPrice", DECIMAL);
57585
57668
  string4(request2.maxSupply, "Skill maxSupply", DECIMAL);
57586
- string4(request2.usageLimit, "Skill usageLimit", DECIMAL);
57587
- if (!Number.isInteger(request2.royaltyBPS) || Number(request2.royaltyBPS) < 0 || Number(request2.royaltyBPS) > 1e3) throw new TypeError("Skill royaltyBPS is invalid.");
57588
57669
  return {
57589
57670
  chainId,
57590
57671
  request: request2,
@@ -57631,32 +57712,6 @@ function managePlan(value, walletAddress2, kind) {
57631
57712
  signingAuthority: plan.signingAuthority
57632
57713
  }, walletAddress2) };
57633
57714
  }
57634
- function presentationPlan(value, walletAddress2) {
57635
- const plan = object2(value, "Skill presentation plan");
57636
- exact2(plan, [
57637
- "kind",
57638
- "sourceCommit",
57639
- "chainId",
57640
- "walletAddress",
57641
- "signingAuthority",
57642
- "skillId",
57643
- "currentVersion",
57644
- "currentManifestURI",
57645
- "presentation",
57646
- "localAction"
57647
- ], "Skill presentation plan");
57648
- const chainId = assertAuthority(plan, "presentation", walletAddress2);
57649
- const skillId = string4(plan.skillId, "Skill id", /^[1-9][0-9]*$/u);
57650
- assertLocalAction(plan.localAction, "skill presentation-submit", skillId);
57651
- return {
57652
- chainId,
57653
- skillId,
57654
- currentVersion: integer2(plan.currentVersion, "Current version"),
57655
- currentManifestURI: string4(plan.currentManifestURI, "Current manifest URI", IPFS),
57656
- presentation: buildPresentation(plan.presentation),
57657
- signingAuthority: signingAuthority(plan.signingAuthority, walletAddress2)
57658
- };
57659
- }
57660
57715
  function assetPlan(value, walletAddress2) {
57661
57716
  const plan = object2(value, "Skill asset plan");
57662
57717
  exact2(plan, [
@@ -58045,10 +58100,10 @@ var init_skill_local_flow = __esm({
58045
58100
  async save(fingerprint, journal) {
58046
58101
  await required2(this.recovery, "Skill publication recovery").saveSkillPublicationRecovery(fingerprint, journal);
58047
58102
  }
58048
- async project(chainId, transactionHash3, bearer, intentId, stage) {
58103
+ async project(chainId, transactionHash3, bearer, intentId, stage, requireReady = true) {
58049
58104
  if (intentId === void 0 && this.api.confirmSkillProjection === void 0) return;
58050
58105
  const status = await required2(this.api.confirmSkillProjection, "Skill projection confirmation").call(this.api, chainId, transactionHash3, bearer, intentId, stage);
58051
- if (intentId !== void 0 && status !== "projected") throw new TypeError("Skill projection is pending. Resume the saved Skill recovery.");
58106
+ if (requireReady && intentId !== void 0 && status !== "projected") throw new TypeError("The Skill transaction is confirmed; presentation projection is pending. Resume the saved Skill recovery to retry projection without sending another transaction.");
58052
58107
  }
58053
58108
  async stage(progress, context, fingerprint, journal, stage, action, request2, prepare) {
58054
58109
  progress.journal = journal;
@@ -58062,7 +58117,7 @@ var init_skill_local_flow = __esm({
58062
58117
  await this.save(fingerprint, journal);
58063
58118
  progress.uncertain = false;
58064
58119
  progress.phase = "projection";
58065
- await this.project(journal.chainId, journal.receipts[stage].transactionHash, context.bearer, context.intentId, stage);
58120
+ await this.project(journal.chainId, journal.receipts[stage].transactionHash, context.bearer, context.intentId, stage, journal.kind !== "version" || stage !== "primary");
58066
58121
  return;
58067
58122
  }
58068
58123
  if ((stage !== "uri" || context.intentId !== void 0) && (journal.pendingStage != null || journal.version !== 6)) {
@@ -58104,7 +58159,7 @@ var init_skill_local_flow = __esm({
58104
58159
  await this.save(fingerprint, journal);
58105
58160
  progress.uncertain = false;
58106
58161
  progress.phase = "projection";
58107
- await this.project(journal.chainId, journal.receipts[stage].transactionHash, context.bearer, context.intentId, stage);
58162
+ await this.project(journal.chainId, journal.receipts[stage].transactionHash, context.bearer, context.intentId, stage, journal.kind !== "version" || stage !== "primary");
58108
58163
  }
58109
58164
  async recoverWithProgress(fingerprint, context, progress) {
58110
58165
  progress.phase = "recovery";
@@ -58570,68 +58625,11 @@ var init_skill_local_flow = __esm({
58570
58625
  await this.project(plan.chainId, submission.transactionHash, context.bearer, context.intentId);
58571
58626
  return { ...submission, chainId: plan.chainId, skillId: plan.request.skillId, kind };
58572
58627
  }
58573
- /**
58574
- * Binds a cover and an introduction to a Skill by replacing its manifest URI.
58575
- *
58576
- * Reported through the same publication progress as a publish, so a failure states the phase it
58577
- * reached and whether a transaction was broadcast. Session-owned execution checkpoints its hash
58578
- * through the caller before receipt waiting; legacy callers retain the convergent URI retry.
58579
- */
58580
- async publishPresentation(planValue, context) {
58581
- return this.publication((progress) => this.publishPresentationWithProgress(planValue, context, progress));
58582
- }
58583
- async publishPresentationWithProgress(planValue, context, progress) {
58584
- const plan = presentationPlan(planValue, context.walletAddress);
58585
- progress.resubmitSafe = true;
58586
- const liveVersion = await required2(this.api.readSkillCurrentVersion, "Skill version API").call(this.api, plan.chainId, plan.skillId, context.bearer, context.intentId);
58587
- if (liveVersion !== plan.currentVersion) {
58588
- throw new CliSkillPresentationStaleError("version_moved", plan.currentVersion, liveVersion);
58589
- }
58590
- const currentBytes = await required2(this.api.fetchSkillStoredContent, "Skill content relay").call(this.api, plan.currentManifestURI, context.bearer);
58591
- const currentManifest = parseManifest2(currentBytes);
58592
- if (currentManifest === null) {
58593
- throw new CliSkillPresentationStaleError("manifest_unreadable", plan.currentVersion, liveVersion);
58594
- }
58595
- progress.phase = "upload";
58596
- const manifestURI = await this.upload({
58597
- bytes: serializeManifest(withPresentation(currentManifest, plan.presentation)),
58598
- filename: `skill-${plan.skillId}.versions.json`,
58599
- bearer: context.bearer,
58600
- objectKind: "manifest"
58601
- });
58602
- const request2 = { action: "setSkillURI", chainId: plan.chainId, skillId: plan.skillId, packageURI: manifestURI };
58603
- progress.phase = "intent";
58604
- const prepared = await required2(this.api.requestSkillIntent, "Skill intent API").call(this.api, request2, context.bearer, context.intentId);
58605
- const resumed = await context.resumePrepared?.(request2, object2(prepared, "Skill presentation intent"));
58606
- if (resumed) {
58607
- await this.project(plan.chainId, resumed.transactionHash, context.bearer, context.intentId);
58608
- return { ...resumed, chainId: plan.chainId, skillId: plan.skillId, currentVersion: plan.currentVersion, manifestURI };
58609
- }
58610
- progress.phase = "transaction";
58611
- progress.uncertain = true;
58612
- const submission = await this.send(
58613
- context,
58614
- "setSkillURI",
58615
- request2,
58616
- prepared,
58617
- plan.signingAuthority,
58618
- async ({ transactionHash: transactionHash3 }) => {
58619
- progress.broadcast = { setSkillURI: transactionHash3 };
58620
- const fingerprint = await context.onBroadcast?.({ request: request2, intent: object2(prepared, "Skill presentation intent"), transactionHash: transactionHash3 });
58621
- if (fingerprint) {
58622
- progress.walletFingerprint = fingerprint;
58623
- progress.journalSaved = true;
58624
- progress.resubmitSafe = false;
58625
- }
58626
- progress.uncertain = false;
58627
- }
58628
- );
58629
- await context.onReceipt?.(submission.blockNumber);
58630
- progress.broadcast = { setSkillURI: submission.transactionHash };
58631
- progress.uncertain = false;
58632
- progress.phase = "projection";
58633
- await this.project(plan.chainId, submission.transactionHash, context.bearer, context.intentId);
58634
- return { ...submission, chainId: plan.chainId, skillId: plan.skillId, currentVersion: plan.currentVersion, manifestURI };
58628
+ /** Retained only to give installed callers an explicit upgrade instruction before any I/O. */
58629
+ async publishPresentation(_planValue, _context) {
58630
+ void _planValue;
58631
+ void _context;
58632
+ throw new TypeError("SKILL_PRESENTATION_FLOW_REMOVED: Use skill_market_presentation_update through Remote MCP.");
58635
58633
  }
58636
58634
  async uploadCover(path2, planValue, context) {
58637
58635
  const plan = assetPlan(planValue, context.walletAddress);
@@ -58936,6 +58934,26 @@ var init_loopback_callback = __esm({
58936
58934
  import { createHash as createHash3, randomUUID } from "node:crypto";
58937
58935
  import { isDeepStrictEqual } from "node:util";
58938
58936
  import { open as open2 } from "node:fs/promises";
58937
+ function transportErrorCode(error51) {
58938
+ let current = error51.cause;
58939
+ for (let depth = 0; depth < 4 && current instanceof Error; depth += 1) {
58940
+ if (current.name === "TimeoutError") return "TIMEOUT";
58941
+ const code = current.code;
58942
+ if (typeof code === "string" && /^[A-Z][A-Z0-9_]{1,63}$/u.test(code)) return code;
58943
+ current = current.cause;
58944
+ }
58945
+ return null;
58946
+ }
58947
+ function httpFailureCheck(cause, status, guidance) {
58948
+ if (cause.status !== null) return { status, message: guidance, httpStatus: cause.status };
58949
+ const networkErrorCode = transportErrorCode(cause);
58950
+ if (networkErrorCode === null) return { status, message: `No HTTP response was received. ${guidance}` };
58951
+ return {
58952
+ status,
58953
+ message: TLS_ERROR_CODE.test(networkErrorCode) ? `TLS certificate verification failed. If a proxy, VPN or security software intercepts HTTPS on this machine, make Node.js trust its certificate authority, for example with NODE_EXTRA_CA_CERTS. ${guidance}` : `No HTTP response was received. ${guidance}`,
58954
+ networkErrorCode
58955
+ };
58956
+ }
58939
58957
  function required3(value, name) {
58940
58958
  if (value === void 0) throw new TypeError(`CLI dependency ${name} is unavailable.`);
58941
58959
  return value;
@@ -59203,7 +59221,7 @@ function parseTaskRecoveryJournal(value, expected) {
59203
59221
  idempotencyKey: journal.idempotencyKey
59204
59222
  });
59205
59223
  }
59206
- var MAXIMUM_STDIN_AUTHORIZATION_BYTES, FinchCliApplication;
59224
+ var MAXIMUM_STDIN_AUTHORIZATION_BYTES, TLS_ERROR_CODE, FinchCliApplication;
59207
59225
  var init_application = __esm({
59208
59226
  "src/application.ts"() {
59209
59227
  "use strict";
@@ -59220,6 +59238,7 @@ var init_application = __esm({
59220
59238
  init_cli_error();
59221
59239
  init_loopback_callback();
59222
59240
  MAXIMUM_STDIN_AUTHORIZATION_BYTES = 4098;
59241
+ TLS_ERROR_CODE = /CERT|SSL|TLS|SELF_SIGNED/u;
59223
59242
  FinchCliApplication = class {
59224
59243
  api;
59225
59244
  state;
@@ -59519,6 +59538,12 @@ var init_application = __esm({
59519
59538
  await required3(this.state.saveWallet, "state.saveWallet").call(this.state, { walletAddress: walletAddress2, privateKey: privateKey3 });
59520
59539
  return Object.freeze({ walletAddress: walletAddress2 });
59521
59540
  }
59541
+ async listWallets() {
59542
+ const walletAddresses = await required3(this.state.listWalletAddresses, "state.listWalletAddresses").call(this.state);
59543
+ if (walletAddresses.length === 0) return Object.freeze({ currentWalletAddress: null, walletAddresses });
59544
+ const current = await required3(this.state.readWalletAddress, "state.readWalletAddress").call(this.state);
59545
+ return Object.freeze({ currentWalletAddress: current.walletAddress, walletAddresses });
59546
+ }
59522
59547
  async showWallet() {
59523
59548
  const wallet = await required3(this.state.readWalletAddress, "state.readWalletAddress").call(this.state);
59524
59549
  return Object.freeze({ walletAddress: wallet.walletAddress });
@@ -59600,7 +59625,7 @@ var init_application = __esm({
59600
59625
  }];
59601
59626
  } else if (cause instanceof CliHttpError) {
59602
59627
  const unavailable = cause.status === 429 || cause.status !== null && cause.status >= 500;
59603
- return [null, { status: !unavailable && (cause.status === 401 || cause.status === 403 || cause.code === "CLI_RESPONSE_INVALID") ? "invalid" : "unavailable", message: guidance }];
59628
+ return [null, httpFailureCheck(cause, !unavailable && (cause.status === 401 || cause.status === 403 || cause.code === "CLI_RESPONSE_INVALID") ? "invalid" : "unavailable", guidance)];
59604
59629
  } else if (cause instanceof TypeError || cause instanceof CliAuthorityError) {
59605
59630
  return [null, { status: "invalid", message: guidance }];
59606
59631
  } else {
@@ -59737,7 +59762,9 @@ var init_application = __esm({
59737
59762
  if (!/^0x[0-9a-f]{40}$/u.test(target.walletAddress)) throw new TypeError("Wallet address is invalid.");
59738
59763
  if (input.file === void 0) {
59739
59764
  const wallets = await required3(this.state.listWalletAddresses, "state.listWalletAddresses").call(this.state);
59740
- if (!wallets.includes(target.walletAddress)) throw new TypeError("Wallet is not registered in FINCHTECH_HOME.");
59765
+ if (!wallets.includes(target.walletAddress)) {
59766
+ throw new CliUsageError("Wallet is not registered in FINCHTECH_HOME. Run finch wallet list to see registered wallets, or finch wallet use --file <PATH> to import a private key.");
59767
+ }
59741
59768
  }
59742
59769
  if (target.walletAddress === current.walletAddress.toLowerCase()) return Object.freeze(target);
59743
59770
  if (this.state.readOptionalSession !== void 0 && await this.state.readOptionalSession.call(this.state) === null) {
@@ -60057,45 +60084,9 @@ var init_application = __esm({
60057
60084
  const managed = await this.submitSkillIntent(kind === "price" ? "set_unit_price" : "set_max_supply", planFile);
60058
60085
  return { ...managed, nextAction: { operationId: "skill_market.skills.get", request: { chainId: managed.chainId, skillId: managed.skillId } } };
60059
60086
  }
60060
- async submitSkillPresentation(planFile) {
60061
- const authority = await this.walletAuthority();
60062
- const owned = await this.skillWalletIntent(planFile, "presentation", authority.session);
60063
- const plan = owned.preparation;
60064
- assertSigningAuthority(plan, authority.session);
60065
- if (owned.stages.primary?.transactionHash) return this.confirmSkillWalletResult(owned.intentId, owned.stages.primary.transactionHash, authority.session);
60066
- let journal;
60067
- const walletPlan = (request2, intent2) => ({ kind: "presentation", sourceCommit: plan.sourceCommit, walletAddress: plan.walletAddress, signingAuthority: plan.signingAuthority, request: request2, ...intent2, walletIntent: { intentId: owned.intentId, kind: owned.kind } });
60068
- const context = Object.freeze({
60069
- privateKey: authority.wallet.privateKey,
60070
- walletAddress: authority.wallet.walletAddress,
60071
- bearer: authority.session.rawBearer,
60072
- intentId: owned.intentId,
60073
- origin: authority.session.origin,
60074
- resumePrepared: async (request2, intent2) => {
60075
- const fingerprint = skillBuyerIntentFingerprint(walletPlan(request2, intent2));
60076
- const stored = await this.state.readSkillBuyerIntentRecovery?.(fingerprint);
60077
- if (stored == null) return null;
60078
- const recovered = await this.recoverSkill("intent", fingerprint);
60079
- if (recovered.status !== "projected") throw new TypeError("Skill projection is pending. Resume the recorded wallet result.");
60080
- return { transactionHash: String(recovered.transactionHash), blockNumber: String(recovered.blockNumber) };
60081
- },
60082
- onBroadcast: async (broadcast) => {
60083
- journal = createSkillBuyerIntentJournal({ intent: walletPlan(broadcast.request, broadcast.intent), intentId: owned.intentId, origin: authority.session.origin, accountId: authority.session.accountId, walletAddress: authority.wallet.walletAddress, environment: authority.session.environment, transactionHash: broadcast.transactionHash, idempotencyKey: skillIntentConfirmationIdempotencyKey(Number(plan.chainId), broadcast.transactionHash) });
60084
- await required3(this.state.saveSkillBuyerIntentRecovery, "state.saveSkillBuyerIntentRecovery").call(this.state, journal.fingerprint, journal);
60085
- return journal.fingerprint;
60086
- },
60087
- onReceipt: async (blockNumber2) => {
60088
- if (!journal) throw new TypeError("Skill presentation broadcast was not checkpointed.");
60089
- journal = withSkillBuyerIntentReceipt(journal, blockNumber2);
60090
- await required3(this.state.saveSkillBuyerIntentRecovery, "state.saveSkillBuyerIntentRecovery").call(this.state, journal.fingerprint, journal);
60091
- }
60092
- });
60093
- const published = await required3(this.skillLocalFlow, "skill local flow").publishPresentation(plan, context);
60094
- if (journal) await required3(this.state.clearSkillBuyerIntentRecovery, "state.clearSkillBuyerIntentRecovery").call(this.state, journal.fingerprint);
60095
- return { ...published, nextAction: { operationId: "skill_market.manage.get", request: {
60096
- chainId: published.chainId,
60097
- skillId: published.skillId
60098
- } } };
60087
+ async submitSkillPresentation(_planFile) {
60088
+ void _planFile;
60089
+ throw new TypeError("SKILL_PRESENTATION_FLOW_REMOVED: Use skill_market_presentation_update through Remote MCP to save presentation, documents and video directly without a wallet transaction.");
60099
60090
  }
60100
60091
  async uploadSkillAsset(path2, planFile) {
60101
60092
  const authority = await this.walletAuthority();
@@ -60499,6 +60490,23 @@ function helpTopic(allArguments) {
60499
60490
  }
60500
60491
  return null;
60501
60492
  }
60493
+ function usageError(allArguments, grammar) {
60494
+ const arguments_ = finchOwnArguments(allArguments);
60495
+ const command = FINCH_COMMANDS.filter((candidate) => candidate.split(" ").every((word, index2) => arguments_[index2] === word)).sort((left, right) => right.length - left.length)[0];
60496
+ if (command === void 0) {
60497
+ const group = FINCH_COMMANDS.filter((candidate) => candidate.startsWith(`${arguments_[0]} `));
60498
+ if (group.length > 0) {
60499
+ return new CliUsageError(`Unknown finch ${arguments_[0]} command. Supported: ${group.map((candidate) => `finch ${candidate}`).join(", ")}.`);
60500
+ }
60501
+ return new CliUsageError(`${grammar.detail ?? "Unknown finch command."} Run finch help to list supported commands.`);
60502
+ }
60503
+ const usage = FINCH_COMMAND_USAGE[command];
60504
+ if (grammar.detail !== null) return new CliUsageError(`${grammar.detail} Usage: ${usage}`);
60505
+ const accepted = new Set(usage.match(/--[a-z][a-z0-9-]*/gu) ?? []);
60506
+ if (accepted.has("--intent")) accepted.add("--from-file");
60507
+ const unsupported = [...new Set(arguments_.filter((argument) => OPTION_NAME.test(argument) && argument !== "--help" && !accepted.has(argument)))];
60508
+ return new CliUsageError(unsupported.length > 0 ? `finch ${command} does not accept ${unsupported.join(", ")}. Usage: ${usage}` : `Unexpected arguments for finch ${command}. Usage: ${usage}`);
60509
+ }
60502
60510
  function method(value, name) {
60503
60511
  if (value === void 0) throw new TypeError(`Command ${name} is unavailable.`);
60504
60512
  return value;
@@ -60506,31 +60514,31 @@ function method(value, name) {
60506
60514
  function exactOption(arguments_, name) {
60507
60515
  const index2 = arguments_.indexOf(name);
60508
60516
  if (index2 < 0 || index2 !== arguments_.lastIndexOf(name) || index2 + 1 >= arguments_.length) {
60509
- throw new TypeError(`${name} is required exactly once.`);
60517
+ throw new CommandGrammarError(`${name} is required exactly once.`);
60510
60518
  }
60511
60519
  const value = arguments_[index2 + 1];
60512
- if (value === void 0 || value.startsWith("--")) throw new TypeError(`${name} requires a value.`);
60520
+ if (value === void 0 || value.startsWith("--")) throw new CommandGrammarError(`${name} requires a value.`);
60513
60521
  return value;
60514
60522
  }
60515
60523
  function assertExactOptions(arguments_, start, names) {
60516
60524
  const options = arguments_.slice(start);
60517
60525
  if (options.length !== names.length * 2) {
60518
- throw new TypeError("Command arguments do not match the frozen finch command tree.");
60526
+ throw new CommandGrammarError();
60519
60527
  }
60520
60528
  const actualNames = options.filter((_value, index2) => index2 % 2 === 0);
60521
60529
  if (actualNames.some((name) => !names.includes(name)) || new Set(actualNames).size !== names.length) {
60522
- throw new TypeError("Command arguments do not match the frozen finch command tree.");
60530
+ throw new CommandGrammarError();
60523
60531
  }
60524
60532
  }
60525
60533
  function optionalOptions(arguments_, start, names) {
60526
60534
  const options = arguments_.slice(start);
60527
- if (options.length % 2 !== 0) throw new TypeError("Command arguments do not match the frozen finch command tree.");
60535
+ if (options.length % 2 !== 0) throw new CommandGrammarError();
60528
60536
  const result2 = {};
60529
60537
  for (let index2 = 0; index2 < options.length; index2 += 2) {
60530
60538
  const name = options[index2];
60531
60539
  const value = options[index2 + 1];
60532
60540
  if (name === void 0 || value === void 0 || !names.includes(name) || Object.hasOwn(result2, name) || value.startsWith("--")) {
60533
- throw new TypeError("Command arguments do not match the frozen finch command tree.");
60541
+ throw new CommandGrammarError();
60534
60542
  }
60535
60543
  result2[name] = value;
60536
60544
  }
@@ -60542,10 +60550,18 @@ function result(command, data) {
60542
60550
  function walletIntentInput(arguments_) {
60543
60551
  const file2 = arguments_.includes("--from-file") ? exactOption(arguments_, "--from-file") : void 0;
60544
60552
  const intent2 = arguments_.includes("--intent") ? exactOption(arguments_, "--intent") : void 0;
60545
- if (file2 === void 0 === (intent2 === void 0)) throw new TypeError("Provide exactly one of --intent or --from-file.");
60553
+ if (file2 === void 0 === (intent2 === void 0)) throw new CommandGrammarError("Provide exactly one of --intent or --from-file.");
60546
60554
  return intent2 ?? file2;
60547
60555
  }
60548
60556
  async function routeCommand(arguments_, application) {
60557
+ try {
60558
+ return await dispatchCommand(arguments_, application);
60559
+ } catch (cause) {
60560
+ if (cause instanceof CommandGrammarError) throw usageError(arguments_, cause);
60561
+ throw cause;
60562
+ }
60563
+ }
60564
+ async function dispatchCommand(arguments_, application) {
60549
60565
  if (arguments_.length === 0 || arguments_.length === 1 && ["--help", "help"].includes(arguments_[0])) {
60550
60566
  return Object.freeze({ command: "help", commands: FINCH_COMMANDS });
60551
60567
  }
@@ -60573,12 +60589,15 @@ async function routeCommand(arguments_, application) {
60573
60589
  }
60574
60590
  const commands = FINCH_COMMANDS.filter((command) => command.startsWith(`${topic} `));
60575
60591
  if (commands.length > 0) return Object.freeze({ command: "help", topic, commands });
60576
- throw new TypeError(`Unknown finch help topic: ${topic}.`);
60592
+ throw new CommandGrammarError("Unknown finch help topic.");
60577
60593
  }
60578
60594
  const [first, second, third] = arguments_;
60579
60595
  if (first === "wallet" && second === "create" && arguments_.length === 2) {
60580
60596
  return result("wallet.create", await method(application.createWallet, "wallet create").call(application));
60581
60597
  }
60598
+ if (first === "wallet" && second === "list" && arguments_.length === 2) {
60599
+ return result("wallet.list", await method(application.listWallets, "wallet list").call(application));
60600
+ }
60582
60601
  if (first === "wallet" && second === "show" && arguments_.length === 2) {
60583
60602
  return result("wallet.show", await method(application.showWallet, "wallet show").call(application));
60584
60603
  }
@@ -60589,13 +60608,13 @@ async function routeCommand(arguments_, application) {
60589
60608
  if (arguments_.length === 4 && third === "--file" && arguments_[3] !== void 0 && !arguments_[3].startsWith("--")) {
60590
60609
  return result("wallet.use", await method(application.useWallet, "wallet use").call(application, Object.freeze({ file: arguments_[3] })));
60591
60610
  }
60592
- throw new TypeError("Command arguments do not match the frozen finch command tree.");
60611
+ throw new CommandGrammarError();
60593
60612
  }
60594
60613
  if (first === "login") {
60595
60614
  const rawChainId = exactOption(arguments_, "--chain-id");
60596
60615
  assertExactOptions(arguments_, 1, ["--chain-id"]);
60597
60616
  if (!/^[1-9][0-9]*$/u.test(rawChainId) || !Number.isSafeInteger(Number(rawChainId))) {
60598
- throw new TypeError("--chain-id must be a positive safe integer.");
60617
+ throw new CommandGrammarError("--chain-id must be a positive safe integer.");
60599
60618
  }
60600
60619
  return result("login", await method(application.login, "login").call(application, Number(rawChainId)));
60601
60620
  }
@@ -60632,7 +60651,7 @@ async function routeCommand(arguments_, application) {
60632
60651
  const framed = arguments_.includes("--from-file");
60633
60652
  const bearer = arguments_.includes("--bearer-token-file");
60634
60653
  if (framed === bearer) {
60635
- throw new TypeError("Choose exactly one of --from-file or --bearer-token-file.");
60654
+ throw new CommandGrammarError("Choose exactly one of --from-file or --bearer-token-file.");
60636
60655
  }
60637
60656
  const input = framed ? Object.freeze({ kind: "framed", path: exactOption(arguments_, "--from-file") }) : Object.freeze({
60638
60657
  kind: "bearer_token",
@@ -60697,7 +60716,7 @@ async function routeCommand(arguments_, application) {
60697
60716
  if (first === "task" && second === "recover" && third !== void 0 && arguments_.length === 4) {
60698
60717
  const kind = arguments_[3];
60699
60718
  if (!["publish", "award", "reclaim"].includes(String(kind))) {
60700
- throw new TypeError("Task recovery kind must be publish, award, or reclaim.");
60719
+ throw new CommandGrammarError("Task recovery kind must be publish, award, or reclaim.");
60701
60720
  }
60702
60721
  return result("task.recover", await method(application.recoverTask, "task recover").call(application, third, kind));
60703
60722
  }
@@ -60708,7 +60727,7 @@ async function routeCommand(arguments_, application) {
60708
60727
  }
60709
60728
  if (first === "skill" && second === "recover" && arguments_.length === 4) {
60710
60729
  if (!["publication", "intent"].includes(String(third)) || arguments_[3] === void 0) {
60711
- throw new TypeError("Skill recovery kind must be publication or intent.");
60730
+ throw new CommandGrammarError("Skill recovery kind must be publication or intent.");
60712
60731
  }
60713
60732
  return result("skill.recover", await method(application.recoverSkill, "skill recover").call(application, third, arguments_[3]));
60714
60733
  }
@@ -60739,16 +60758,18 @@ async function routeCommand(arguments_, application) {
60739
60758
  optionalOptions(arguments_, 2, ["--from-file", "--intent", "--out-file"]);
60740
60759
  return result("skill.download", await method(application.downloadSkill, "skill download").call(application, walletIntentInput(arguments_), exactOption(arguments_, "--out-file")));
60741
60760
  }
60742
- throw new TypeError("Command arguments do not match the frozen finch command tree.");
60761
+ throw new CommandGrammarError();
60743
60762
  }
60744
- var FINCH_COMMANDS, FINCH_CLI_IDENTITY, FINCH_CLI_PACKAGE, FINCH_CLI_VERSION, FINCH_COMMAND_USAGE;
60763
+ var FINCH_COMMANDS, FINCH_CLI_IDENTITY, FINCH_CLI_PACKAGE, FINCH_CLI_VERSION, FINCH_COMMAND_USAGE, CommandGrammarError, OPTION_NAME;
60745
60764
  var init_command_router = __esm({
60746
60765
  "src/command-router.ts"() {
60747
60766
  "use strict";
60748
60767
  init_package();
60749
60768
  init_v1();
60769
+ init_cli_error();
60750
60770
  FINCH_COMMANDS = Object.freeze([
60751
60771
  "wallet create",
60772
+ "wallet list",
60752
60773
  "wallet show",
60753
60774
  "wallet use",
60754
60775
  "login",
@@ -60790,6 +60811,7 @@ var init_command_router = __esm({
60790
60811
  FINCH_CLI_VERSION = package_default.version;
60791
60812
  FINCH_COMMAND_USAGE = Object.freeze({
60792
60813
  "wallet create": "finch wallet create",
60814
+ "wallet list": "finch wallet list",
60793
60815
  "wallet show": "finch wallet show",
60794
60816
  "wallet use": "finch wallet use <ADDRESS> | finch wallet use --file <PATH>",
60795
60817
  login: "finch login --chain-id <CHAIN_ID>",
@@ -60826,6 +60848,15 @@ var init_command_router = __esm({
60826
60848
  "skill recover publication": "finch skill recover publication <FINGERPRINT>",
60827
60849
  "skill recover intent": "finch skill recover intent <FINGERPRINT>"
60828
60850
  });
60851
+ CommandGrammarError = class extends Error {
60852
+ constructor(detail = null) {
60853
+ super(detail ?? "Command arguments do not match the finch command tree.");
60854
+ this.detail = detail;
60855
+ this.name = "CommandGrammarError";
60856
+ }
60857
+ detail;
60858
+ };
60859
+ OPTION_NAME = /^--[a-z][a-z0-9-]{0,39}$/u;
60829
60860
  }
60830
60861
  });
60831
60862
 
@@ -63487,7 +63518,7 @@ function expectedCall(kind, request2, deployment) {
63487
63518
  function expectedPreparedCall(action, request2, deployment) {
63488
63519
  const skillId = () => unsigned2(request2.skillId, "Skill id", true);
63489
63520
  if (action === "createSkill") {
63490
- if (typeof request2.plaintext !== "boolean" || !Number.isInteger(request2.royaltyBPS) || Number(request2.royaltyBPS) < 0 || Number(request2.royaltyBPS) > 1e3) throw new TypeError("Skill create request is invalid.");
63521
+ if (typeof request2.plaintext !== "boolean") throw new TypeError("Skill create request is invalid.");
63491
63522
  return {
63492
63523
  to: getAddress(deployment.skillFactoryAddress),
63493
63524
  value: BigInt(0),
@@ -63503,8 +63534,10 @@ function expectedPreparedCall(action, request2, deployment) {
63503
63534
  plaintext: request2.plaintext,
63504
63535
  unitPrice: unsigned2(request2.unitPrice, "Skill unit price"),
63505
63536
  maxSupply: unsigned2(request2.maxSupply, "Skill max supply"),
63506
- royaltyBPS: BigInt(Number(request2.royaltyBPS)),
63507
- usageLimit: unsigned2(request2.usageLimit, "Skill usage limit")
63537
+ // Immutable and Finch-enforced nowhere, so every entrance writes the same fixed
63538
+ // values (Finch-Site issue #1240) instead of trusting caller-supplied ones.
63539
+ royaltyBPS: BigInt(SKILL_PUBLICATION_ROYALTY_BPS),
63540
+ usageLimit: BigInt(SKILL_PUBLICATION_USAGE_LIMIT)
63508
63541
  }] })
63509
63542
  };
63510
63543
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finchtech/cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Finch Agent CLI for wallet custody, Session-owned execution, and MCP OAuth approval.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,10 +37,10 @@
37
37
  "viem": "2.55.15",
38
38
  "vitest": "4.1.10",
39
39
  "@finch/agent-market-contracts": "0.1.0",
40
- "@finch/deployment-manifest": "0.1.0",
41
- "@finch/skill-market-contracts": "0.1.0",
42
40
  "@finch/skill-content": "0.1.0",
43
- "@finch/task-market-contracts": "0.1.0"
41
+ "@finch/deployment-manifest": "0.1.0",
42
+ "@finch/task-market-contracts": "0.1.0",
43
+ "@finch/skill-market-contracts": "0.1.0"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "esbuild src/bootstrap.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/finch.js --banner:js=\"import { createRequire as createBundleRequire } from 'node:module'; const require = createBundleRequire(import.meta.url);\"",