@thirdfy/agent-cli 0.1.5 → 0.1.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.
package/README.md CHANGED
@@ -16,6 +16,9 @@ If you want the full developer docs, start here:
16
16
  - [API Reference home](https://docs.thirdfy.com/api)
17
17
  - [LLM docs map (llms.txt)](https://docs.thirdfy.com/llms.txt)
18
18
  - [LLM full API snapshot (llms-full.txt)](https://docs.thirdfy.com/llms-full.txt)
19
+ - [DogeOS quickstart (repo)](./docs/dogeos-quickstart.md)
20
+ - [Action onboarding standard](./docs/action-onboarding-standard.md)
21
+ - [Skill integration RFC](./docs/skill-integration-rfc.md)
19
22
 
20
23
  ## Install
21
24
 
@@ -33,18 +36,19 @@ npx @thirdfy/agent-cli --help
33
36
  ## Quick start
34
37
 
35
38
  ```bash
36
- export THIRDFY_API_BASE="https://api.thirdfy.com"
37
39
  export AGENT_API_KEY="..."
38
40
  export THIRDFY_AUTH_TOKEN="..."
39
41
  export THIRDFY_OWNER_SESSION_TOKEN="..."
40
42
 
41
- thirdfy-agent actions --api-base "$THIRDFY_API_BASE" --agent-api-key "$AGENT_API_KEY" --json
43
+ thirdfy-agent actions --agent-api-key "$AGENT_API_KEY" --json
42
44
  thirdfy-agent profile init --profile personal --json
43
- thirdfy-agent preflight --api-base "$THIRDFY_API_BASE" --agent-api-key "$AGENT_API_KEY" --action swap --params '{"tokenIn":"0x...","tokenOut":"0x...","amountIn":"1000000"}' --estimated-amount-usd 25 --json
44
- thirdfy-agent run --api-base "$THIRDFY_API_BASE" --agent-api-key "$AGENT_API_KEY" --action swap --params '{"tokenIn":"0x...","tokenOut":"0x...","amountIn":"1000000"}' --estimated-amount-usd 25 --json
45
- thirdfy-agent intent-status --api-base "$THIRDFY_API_BASE" --intent-id "<intentId>" --json
45
+ thirdfy-agent preflight --agent-api-key "$AGENT_API_KEY" --action swap --params '{"tokenIn":"0x...","tokenOut":"0x...","amountIn":"1000000"}' --estimated-amount-usd 25 --json
46
+ thirdfy-agent run --agent-api-key "$AGENT_API_KEY" --action swap --params '{"tokenIn":"0x...","tokenOut":"0x...","amountIn":"1000000"}' --estimated-amount-usd 25 --json
47
+ thirdfy-agent intent-status --intent-id "<intentId>" --json
46
48
  ```
47
49
 
50
+ By default, the CLI targets production (`https://api.thirdfy.com`). Use `--api-base` (or `THIRDFY_API_BASE`) only for staging/dev/custom deployments.
51
+
48
52
  ## Auth model
49
53
 
50
54
  - `AGENT_API_KEY`: execution identity and policy-aware action access (`actions --agent-api-key`, `preflight`, `run`)
@@ -56,15 +60,15 @@ Execution-only workflows can run with only `AGENT_API_KEY` after onboarding is c
56
60
  ### Wallet-sign onboarding path
57
61
 
58
62
  ```bash
59
- thirdfy-agent agent auth challenge --api-base "$THIRDFY_API_BASE" --agent-key "0xYOUR_AGENT_KEY" --json
63
+ thirdfy-agent agent auth challenge --agent-key "0xYOUR_AGENT_KEY" --json
60
64
  # Sign challenge.message with your wallet, then:
61
- thirdfy-agent agent auth verify --api-base "$THIRDFY_API_BASE" --challenge-id "<challengeId>" --signature "0x..." --agent-key "0xYOUR_AGENT_KEY" --json
65
+ thirdfy-agent agent auth verify --challenge-id "<challengeId>" --signature "0x..." --agent-key "0xYOUR_AGENT_KEY" --json
62
66
  ```
63
67
 
64
68
  Use the returned session token:
65
69
 
66
70
  ```bash
67
- thirdfy-agent agent register --api-base "$THIRDFY_API_BASE" --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" --agent-key "0xYOUR_AGENT_KEY" --name "my-agent" --json
71
+ thirdfy-agent agent register --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" --agent-key "0xYOUR_AGENT_KEY" --name "my-agent" --json
68
72
  ```
69
73
 
70
74
  ### Idempotency behavior (`run`)
@@ -113,9 +117,40 @@ Jeff-friendly aliases map directly to core commands:
113
117
  - Execution: `preflight`, `run`, `intent-status`, `jeff preflight`, `jeff trade`, `jeff status`
114
118
  - Profiles: `profile init`, `profile use`, `profile show`, `whoami`
115
119
  - Onboarding: `agent auth challenge`, `agent auth verify`, `agent register`, `agent key rotate`, `agent key revoke`
116
- - Governance readiness: `delegation upsert`, `delegation status`, `credentials upsert`, `credentials status`
120
+ - Governance readiness: `delegation create`, `delegation activate`, `delegation status`, `credentials upsert`, `credentials status`
117
121
  - Account: `credits balance`
118
122
 
123
+ ## Delegation lifecycle (canonical)
124
+
125
+ Use `create` + `activate` as the default delegated execution lifecycle:
126
+
127
+ ```bash
128
+ thirdfy-agent delegation create \
129
+ --auth-token "$THIRDFY_AUTH_TOKEN" \
130
+ --agent-key "0xYOUR_AGENT_KEY" \
131
+ --owner-address "0xOWNER_WALLET" \
132
+ --token-address "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" \
133
+ --max-usd-per-day 250 \
134
+ --json
135
+
136
+ # Sign the returned `data.delegation` payload in your wallet, then:
137
+ thirdfy-agent delegation activate \
138
+ --auth-token "$THIRDFY_AUTH_TOKEN" \
139
+ --agent-key "0xYOUR_AGENT_KEY" \
140
+ --wallet-address "0xOWNER_WALLET" \
141
+ --session-account-address "0xSESSION_ACCOUNT" \
142
+ --delegation-manager "0xDELEGATION_MANAGER" \
143
+ --delegation '{"delegator":"0x...","delegate":"0x...","authority":"0x...","caveats":[],"salt":"0x...","signature":"0x"}' \
144
+ --signature "0xSIGNED_DELEGATION" \
145
+ --json
146
+
147
+ thirdfy-agent delegation status \
148
+ --auth-token "$THIRDFY_AUTH_TOKEN" \
149
+ --agent-key "0xYOUR_AGENT_KEY" \
150
+ --verify \
151
+ --json
152
+ ```
153
+
119
154
  ## More docs
120
155
 
121
156
  - OpenClaw CLI onboarding flow:
@@ -130,8 +130,11 @@ async function main() {
130
130
  case 'credits balance':
131
131
  await commandCreditsBalance(context, subFlags, capabilities);
132
132
  return;
133
- case 'delegation upsert':
134
- await commandDelegationUpsert(context, subFlags, capabilities);
133
+ case 'delegation create':
134
+ await commandDelegationCreate(context, subFlags, capabilities);
135
+ return;
136
+ case 'delegation activate':
137
+ await commandDelegationActivate(context, subFlags, capabilities);
135
138
  return;
136
139
  case 'delegation status':
137
140
  await commandDelegationStatus(context, subFlags, capabilities);
@@ -388,32 +391,76 @@ async function commandCreditsBalance(ctx, flags, capabilities) {
388
391
  });
389
392
  }
390
393
 
391
- async function commandDelegationUpsert(ctx, flags, capabilities) {
394
+ async function commandDelegationCreate(ctx, flags, capabilities) {
395
+ const authToken = getAuthToken(flags, 'Missing --auth-token (or THIRDFY_AUTH_TOKEN) for delegation create');
396
+ let sessionAccountAddress = String(flags.sessionAccountAddress || '').trim();
397
+ if (!sessionAccountAddress) {
398
+ try {
399
+ const executorAddressResponse = await apiGet(ctx, '/api/v1/agent/delegation/executor-address', {
400
+ Authorization: `Bearer ${authToken}`,
401
+ });
402
+ sessionAccountAddress = String(executorAddressResponse?.data?.executorAddress || '').trim();
403
+ } catch (error) {
404
+ if (ctx.verbose) {
405
+ process.stderr.write(
406
+ `delegation create: executor-address discovery failed, falling back to --session-account-address (${error?.message || 'unknown error'})\n`
407
+ );
408
+ }
409
+ }
410
+ }
411
+ const payload = {
412
+ agentKey: requireFlag(flags, 'agentKey', 'Missing --agent-key'),
413
+ ownerAddress: String(flags.ownerAddress || flags.walletAddress || '').trim() || requireFlag(flags, 'ownerAddress', 'Missing --owner-address'),
414
+ sessionAccountAddress: sessionAccountAddress || requireFlag(flags, 'sessionAccountAddress', 'Missing --session-account-address'),
415
+ chainId: Number(flags.chainId || 8453),
416
+ tokenAddress: requireFlag(flags, 'tokenAddress', 'Missing --token-address'),
417
+ maxUsdPerDay: Number(requireFlag(flags, 'maxUsdPerDay', 'Missing --max-usd-per-day')),
418
+ periodDuration: Number(flags.periodDuration || 86_400),
419
+ expirySeconds: Number(flags.expirySeconds || 86_400),
420
+ };
421
+ const response = await apiPost(ctx, '/api/v1/agent/delegation/create', payload, {
422
+ Authorization: `Bearer ${authToken}`,
423
+ });
424
+ const success = Boolean(response?.success !== false);
425
+ printEnvelope({
426
+ success,
427
+ code: success ? 'DELEGATION_CREATED' : 'DELEGATION_CREATE_FAILED',
428
+ message: response?.message || (success ? 'Delegation payload created' : 'Delegation create failed'),
429
+ data: response,
430
+ meta: {
431
+ apiBase: ctx.apiBase,
432
+ capabilitiesVersion: capabilities?.contractVersion || null,
433
+ },
434
+ });
435
+ }
436
+
437
+ async function commandDelegationActivate(ctx, flags, capabilities) {
438
+ const authToken = getAuthToken(flags, 'Missing --auth-token (or THIRDFY_AUTH_TOKEN) for delegation activate');
392
439
  const payload = {
393
- userDid: requireFlag(flags, 'userDid', 'Missing --user-did'),
394
440
  agentKey: requireFlag(flags, 'agentKey', 'Missing --agent-key'),
395
441
  walletAddress: requireFlag(flags, 'walletAddress', 'Missing --wallet-address'),
442
+ sessionAccountAddress: requireFlag(flags, 'sessionAccountAddress', 'Missing --session-account-address'),
443
+ sessionAccountType: String(flags.sessionAccountType || 'smart').trim(),
396
444
  chainId: Number(flags.chainId || 8453),
397
- executionMode: String(flags.executionMode || 'delegated_execution_user'),
445
+ delegationManager: requireFlag(flags, 'delegationManager', 'Missing --delegation-manager'),
446
+ signature: requireFlag(flags, 'signature', 'Missing --signature'),
398
447
  };
399
- if (flags.sessionAccountAddress) payload.sessionAccountAddress = String(flags.sessionAccountAddress).trim();
400
- if (flags.sessionAccountType) payload.sessionAccountType = String(flags.sessionAccountType).trim();
401
- if (flags.permissionsContext) payload.permissionsContext = String(flags.permissionsContext).trim();
402
- if (flags.delegationManager) payload.delegationManager = String(flags.delegationManager).trim();
448
+ const delegation = parseJsonFlag(flags, 'delegation');
449
+ payload.delegation = delegation;
403
450
  if (flags.permissionsExpiry) payload.permissionsExpiry = String(flags.permissionsExpiry).trim();
404
- if (flags.strategyManifestVersion) payload.strategyManifestVersion = String(flags.strategyManifestVersion).trim();
405
- if (flags.strategyManifestHash) payload.strategyManifestHash = String(flags.strategyManifestHash).trim();
451
+ if (flags.permissionsContext) payload.permissionsContext = String(flags.permissionsContext).trim();
452
+ if (flags.permission) payload.permission = parseJsonFlag(flags, 'permission');
453
+ if (flags.permissionType) payload.permissionType = String(flags.permissionType).trim();
454
+ if (flags.permissionData) payload.permissionData = parseJsonFlag(flags, 'permissionData');
406
455
  if (flags.modelAConstraints) payload.modelAConstraints = parseJsonFlag(flags, 'modelAConstraints');
407
-
408
- const response = await requestWithRouteFallback(ctx, {
409
- method: 'POST',
410
- routes: ['/api/v1/agent/delegation/upsert', '/api/v1/agent/agent/delegation/upsert'],
411
- body: payload,
456
+ const response = await apiPost(ctx, '/api/v1/agent/delegation/activate', payload, {
457
+ Authorization: `Bearer ${authToken}`,
412
458
  });
459
+ const success = Boolean(response?.success !== false);
413
460
  printEnvelope({
414
- success: Boolean(response?.success !== false),
415
- code: response?.updated ? 'DELEGATION_UPDATED' : 'DELEGATION_UPSERTED',
416
- message: response?.message || 'Delegation upsert completed',
461
+ success,
462
+ code: success ? 'DELEGATION_ACTIVATED' : 'DELEGATION_ACTIVATE_FAILED',
463
+ message: response?.message || (success ? 'Delegation activated' : 'Delegation activate failed'),
417
464
  data: response,
418
465
  meta: {
419
466
  apiBase: ctx.apiBase,
@@ -1264,7 +1311,8 @@ Core commands:
1264
1311
  thirdfy-agent whoami [--json]
1265
1312
  thirdfy-agent catalogs list [--json]
1266
1313
  thirdfy-agent actions [--catalog <id>] [--provider <id>] [--agent-api-key <key>] [--chain-id <id>] [--run-mode <mode>] [--json]
1267
- thirdfy-agent delegation upsert --user-did <did> --agent-key <key> --wallet-address <addr> [--json]
1314
+ thirdfy-agent delegation create --auth-token <token> --agent-key <key> --owner-address <addr> --token-address <addr> --max-usd-per-day <usd> [--session-account-address <addr>] [--json]
1315
+ thirdfy-agent delegation activate --auth-token <token> --agent-key <key> --wallet-address <addr> --session-account-address <addr> --delegation-manager <addr> --delegation <json> --signature <hex> [--json]
1268
1316
  thirdfy-agent delegation status --auth-token <token> --agent-key <key> [--verify] [--json]
1269
1317
  thirdfy-agent credentials upsert --auth-token <token> --venue <id> --api-key <key> --api-secret <secret> [--json]
1270
1318
  thirdfy-agent credentials status --auth-token <token> [--venue <id>] [--json]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,13 @@
18
18
  "test": "node --test \"test/**/*.test.cjs\"",
19
19
  "lint": "node --check ./bin/thirdfy-agent.mjs",
20
20
  "validate:cli-contract-schemas": "node ./scripts/validate-cli-contract-schemas.mjs",
21
+ "validate:actions": "node ./scripts/e2e/validate-actions.mjs",
22
+ "e2e:base:simulate": "node ./scripts/e2e/base-simulate.mjs",
23
+ "e2e:base:real": "node ./scripts/e2e/base-realtx.mjs",
24
+ "e2e:delegation:discovery": "node ./scripts/e2e/delegation-discovery.mjs",
25
+ "e2e:delegation:lifecycle": "node ./scripts/e2e/delegation-lifecycle.mjs",
26
+ "e2e:delegation:prod:certify": "node ./scripts/e2e/prod-delegation-certify.mjs",
27
+ "e2e:report:last": "node ./scripts/e2e/report-last.mjs",
21
28
  "smoke:cli": "node ./bin/thirdfy-agent.mjs --help && node ./bin/thirdfy-agent.mjs --version --json",
22
29
  "prepublishOnly": "npm run validate:cli-contract-schemas && npm run test && npm run smoke:cli"
23
30
  },
@@ -19,6 +19,20 @@ function normalizeIntentResponse(response) {
19
19
  const executed = Number(response?.executed || 0);
20
20
  const total = Number(response?.total || results.length || 0);
21
21
  const success = Boolean(response?.success);
22
+ const topLevelBlockedReason = String(response?.blockedReason || '').trim() || null;
23
+ const topLevelBlockedStage = String(response?.blockedStage || '').trim() || null;
24
+ const blockedByReasonFromResponse =
25
+ response?.blockedByReason && typeof response.blockedByReason === 'object' ? response.blockedByReason : null;
26
+ const groupedBlocked = summarizeBlockedReasons(results);
27
+ if (topLevelBlockedReason && !groupedBlocked[topLevelBlockedReason]) {
28
+ groupedBlocked[topLevelBlockedReason] = 1;
29
+ }
30
+ const mergedBlockedByReason = blockedByReasonFromResponse
31
+ ? { ...groupedBlocked, ...blockedByReasonFromResponse }
32
+ : groupedBlocked;
33
+ const topLevelGovernanceSignal = String(response?.governanceSignal || '').trim() || null;
34
+ const topLevelDelegationSignal = String(response?.delegationSignal || '').trim() || null;
35
+ const policyEvaluated = response?.policyEvaluated === true;
22
36
  return {
23
37
  success,
24
38
  status: response?.status || (success ? 'queued' : 'failed'),
@@ -28,7 +42,12 @@ function normalizeIntentResponse(response) {
28
42
  blocked,
29
43
  failed,
30
44
  results,
31
- blockedByReason: summarizeBlockedReasons(results),
45
+ blockedByReason: mergedBlockedByReason,
46
+ blockedReason: topLevelBlockedReason,
47
+ blockedStage: topLevelBlockedStage,
48
+ governanceSignal: topLevelGovernanceSignal,
49
+ delegationSignal: topLevelDelegationSignal,
50
+ policyEvaluated,
32
51
  error: response?.error || null,
33
52
  mode: response?.mode || null,
34
53
  unsignedTx: response?.unsignedTx || null,