@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 +44 -9
- package/bin/thirdfy-agent.mjs +68 -20
- package/package.json +8 -1
- package/src/utils/cli/intentNormalization.cjs +20 -1
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 --
|
|
43
|
+
thirdfy-agent actions --agent-api-key "$AGENT_API_KEY" --json
|
|
42
44
|
thirdfy-agent profile init --profile personal --json
|
|
43
|
-
thirdfy-agent preflight --
|
|
44
|
-
thirdfy-agent run --
|
|
45
|
-
thirdfy-agent intent-status --
|
|
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 --
|
|
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 --
|
|
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 --
|
|
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
|
|
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:
|
package/bin/thirdfy-agent.mjs
CHANGED
|
@@ -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
|
|
134
|
-
await
|
|
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
|
|
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
|
-
|
|
445
|
+
delegationManager: requireFlag(flags, 'delegationManager', 'Missing --delegation-manager'),
|
|
446
|
+
signature: requireFlag(flags, 'signature', 'Missing --signature'),
|
|
398
447
|
};
|
|
399
|
-
|
|
400
|
-
|
|
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.
|
|
405
|
-
if (flags.
|
|
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
|
-
|
|
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
|
|
415
|
-
code:
|
|
416
|
-
message: response?.message || 'Delegation
|
|
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
|
|
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.
|
|
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:
|
|
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,
|