@thirdfy/agent-cli 0.1.1 → 0.1.3
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 +25 -3
- package/bin/thirdfy-agent.mjs +104 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,25 +34,47 @@ npx @thirdfy/agent-cli --help
|
|
|
34
34
|
export THIRDFY_API_BASE="https://api.thirdfy.com"
|
|
35
35
|
export AGENT_API_KEY="..."
|
|
36
36
|
export THIRDFY_AUTH_TOKEN="..."
|
|
37
|
+
export THIRDFY_OWNER_SESSION_TOKEN="..."
|
|
37
38
|
|
|
38
39
|
thirdfy-agent actions --api-base "$THIRDFY_API_BASE" --agent-api-key "$AGENT_API_KEY" --json
|
|
39
40
|
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
|
|
40
|
-
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 --
|
|
41
|
+
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
|
|
41
42
|
thirdfy-agent intent-status --api-base "$THIRDFY_API_BASE" --intent-id "<intentId>" --json
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
## Auth model
|
|
45
46
|
|
|
46
47
|
- `AGENT_API_KEY`: execution identity and policy-aware action access (`actions --agent-api-key`, `preflight`, `run`)
|
|
47
|
-
- `THIRDFY_AUTH_TOKEN`:
|
|
48
|
+
- `THIRDFY_AUTH_TOKEN`: Privy-native owner/account auth (`agent register`, `agent key *`, `delegation *`, `credentials *`, `credits balance`)
|
|
49
|
+
- `THIRDFY_OWNER_SESSION_TOKEN`: wallet-sign owner auth session (`agent register`, `agent key *`)
|
|
48
50
|
|
|
49
51
|
Execution-only workflows can run with only `AGENT_API_KEY` after onboarding is complete.
|
|
50
52
|
|
|
53
|
+
### Wallet-sign onboarding path
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
thirdfy-agent agent auth challenge --api-base "$THIRDFY_API_BASE" --agent-key "0xYOUR_AGENT_KEY" --json
|
|
57
|
+
# Sign challenge.message with your wallet, then:
|
|
58
|
+
thirdfy-agent agent auth verify --api-base "$THIRDFY_API_BASE" --challenge-id "<challengeId>" --signature "0x..." --agent-key "0xYOUR_AGENT_KEY" --json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Use the returned session token:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
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
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Idempotency behavior (`run`)
|
|
68
|
+
|
|
69
|
+
- `run` auto-generates `idempotencyKey` by default when not provided.
|
|
70
|
+
- For deterministic retries across workers/restarts, pass your own stable `--idempotency-key`.
|
|
71
|
+
- `preflight` does not force idempotency keys.
|
|
72
|
+
|
|
51
73
|
## Command groups
|
|
52
74
|
|
|
53
75
|
- Discovery: `catalogs list`, `actions`
|
|
54
76
|
- Execution: `preflight`, `run`, `intent-status`
|
|
55
|
-
- Onboarding: `agent register`, `agent key rotate`, `agent key revoke`
|
|
77
|
+
- Onboarding: `agent auth challenge`, `agent auth verify`, `agent register`, `agent key rotate`, `agent key revoke`
|
|
56
78
|
- Governance readiness: `delegation upsert`, `delegation status`, `credentials upsert`, `credentials status`
|
|
57
79
|
- Account: `credits balance`
|
|
58
80
|
|
package/bin/thirdfy-agent.mjs
CHANGED
|
@@ -4,16 +4,15 @@ import { randomUUID } from 'crypto';
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
|
-
import { pathToFileURL } from 'url';
|
|
8
7
|
|
|
9
8
|
const require = createRequire(import.meta.url);
|
|
10
9
|
const { normalizeIntentResponse } = require('../src/utils/cli/intentNormalization.cjs');
|
|
10
|
+
const { version: CLI_VERSION } = require('../package.json');
|
|
11
11
|
const {
|
|
12
12
|
normalizeDelegationStatus,
|
|
13
13
|
normalizeCredentialStatus,
|
|
14
14
|
} = require('../src/utils/cli/delegationNormalization.cjs');
|
|
15
15
|
|
|
16
|
-
const CLI_VERSION = '0.1.0';
|
|
17
16
|
const DEFAULT_API_BASE = 'https://api.thirdfy.com';
|
|
18
17
|
const DEFAULT_TIMEOUT_MS = 30000;
|
|
19
18
|
const DEFAULT_CATALOG_CACHE_TTL_MS = 60_000;
|
|
@@ -39,37 +38,25 @@ const context = {
|
|
|
39
38
|
verbose,
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
printEnvelope({
|
|
59
|
-
success: false,
|
|
60
|
-
code: statusCode ? 'API_REQUEST_FAILED' : 'CLI_UNHANDLED_ERROR',
|
|
61
|
-
message: error?.message || 'Unhandled CLI error',
|
|
62
|
-
data: {
|
|
63
|
-
statusCode,
|
|
64
|
-
blockedReason,
|
|
65
|
-
blockedStage,
|
|
66
|
-
payload,
|
|
67
|
-
},
|
|
68
|
-
meta: { version: CLI_VERSION, apiBase: context.apiBase },
|
|
69
|
-
});
|
|
70
|
-
process.exit(1);
|
|
41
|
+
main().catch((error) => {
|
|
42
|
+
const statusCode = Number(error?.statusCode || 0) || undefined;
|
|
43
|
+
const payload = error?.payload && typeof error.payload === 'object' ? error.payload : {};
|
|
44
|
+
const blockedReason = String(payload.blockedReason || payload.error || '').trim() || undefined;
|
|
45
|
+
const blockedStage = String(payload.blockedStage || '').trim() || undefined;
|
|
46
|
+
printEnvelope({
|
|
47
|
+
success: false,
|
|
48
|
+
code: statusCode ? 'API_REQUEST_FAILED' : 'CLI_UNHANDLED_ERROR',
|
|
49
|
+
message: error?.message || 'Unhandled CLI error',
|
|
50
|
+
data: {
|
|
51
|
+
statusCode,
|
|
52
|
+
blockedReason,
|
|
53
|
+
blockedStage,
|
|
54
|
+
payload,
|
|
55
|
+
},
|
|
56
|
+
meta: { version: CLI_VERSION, apiBase: context.apiBase },
|
|
71
57
|
});
|
|
72
|
-
|
|
58
|
+
process.exit(1);
|
|
59
|
+
});
|
|
73
60
|
|
|
74
61
|
async function main() {
|
|
75
62
|
if (globalFlags.version) {
|
|
@@ -126,6 +113,14 @@ async function main() {
|
|
|
126
113
|
await commandAgentKeyRevoke(context, subFlags, capabilities);
|
|
127
114
|
return;
|
|
128
115
|
}
|
|
116
|
+
if (commandKey3 === 'agent auth challenge') {
|
|
117
|
+
await commandAgentAuthChallenge(context, subFlags, capabilities);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (commandKey3 === 'agent auth verify') {
|
|
121
|
+
await commandAgentAuthVerify(context, subFlags, capabilities);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
129
124
|
|
|
130
125
|
const rootCommand = parsed.positionals[0];
|
|
131
126
|
switch (rootCommand) {
|
|
@@ -496,7 +491,10 @@ async function commandCredentialsStatus(ctx, flags, capabilities) {
|
|
|
496
491
|
}
|
|
497
492
|
|
|
498
493
|
async function commandAgentRegister(ctx, flags, capabilities) {
|
|
499
|
-
const
|
|
494
|
+
const ownerHeaders = buildOwnerAuthHeaders(
|
|
495
|
+
flags,
|
|
496
|
+
'Missing owner auth for agent register: provide --auth-token (THIRDFY_AUTH_TOKEN) or --owner-session-token'
|
|
497
|
+
);
|
|
500
498
|
const payload = {
|
|
501
499
|
agentKey: requireFlag(flags, 'agentKey', 'Missing --agent-key'),
|
|
502
500
|
name: requireFlag(flags, 'name', 'Missing --name'),
|
|
@@ -514,9 +512,10 @@ async function commandAgentRegister(ctx, flags, capabilities) {
|
|
|
514
512
|
if (flags.strategy) payload.strategy = parseJsonFlag(flags, 'strategy');
|
|
515
513
|
if (flags.apiKeyEnvelopePublicKey) payload.apiKeyEnvelopePublicKey = String(flags.apiKeyEnvelopePublicKey);
|
|
516
514
|
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
515
|
+
const route = ownerHeaders['x-owner-session-token']
|
|
516
|
+
? '/api/v1/agent/onboarding/register-with-wallet'
|
|
517
|
+
: '/api/v1/agent/onboarding/register-with-privy';
|
|
518
|
+
const response = await apiPost(ctx, route, payload, ownerHeaders);
|
|
520
519
|
printEnvelope({
|
|
521
520
|
success: true,
|
|
522
521
|
code: 'AGENT_REGISTERED',
|
|
@@ -530,12 +529,13 @@ async function commandAgentRegister(ctx, flags, capabilities) {
|
|
|
530
529
|
}
|
|
531
530
|
|
|
532
531
|
async function commandAgentKeyRotate(ctx, flags, capabilities) {
|
|
533
|
-
const
|
|
532
|
+
const ownerHeaders = buildOwnerAuthHeaders(
|
|
533
|
+
flags,
|
|
534
|
+
'Missing owner auth for agent key rotate: provide --auth-token (THIRDFY_AUTH_TOKEN) or --owner-session-token'
|
|
535
|
+
);
|
|
534
536
|
const payload = {};
|
|
535
537
|
if (flags.agentKey) payload.agentKey = String(flags.agentKey).trim();
|
|
536
|
-
const response = await apiPost(ctx, '/api/v1/agent/onboarding/me/api-key/rotate', payload,
|
|
537
|
-
Authorization: `Bearer ${authToken}`,
|
|
538
|
-
});
|
|
538
|
+
const response = await apiPost(ctx, '/api/v1/agent/onboarding/me/api-key/rotate', payload, ownerHeaders);
|
|
539
539
|
printEnvelope({
|
|
540
540
|
success: true,
|
|
541
541
|
code: 'AGENT_KEY_ROTATED',
|
|
@@ -549,12 +549,13 @@ async function commandAgentKeyRotate(ctx, flags, capabilities) {
|
|
|
549
549
|
}
|
|
550
550
|
|
|
551
551
|
async function commandAgentKeyRevoke(ctx, flags, capabilities) {
|
|
552
|
-
const
|
|
552
|
+
const ownerHeaders = buildOwnerAuthHeaders(
|
|
553
|
+
flags,
|
|
554
|
+
'Missing owner auth for agent key revoke: provide --auth-token (THIRDFY_AUTH_TOKEN) or --owner-session-token'
|
|
555
|
+
);
|
|
553
556
|
const payload = {};
|
|
554
557
|
if (flags.agentKey) payload.agentKey = String(flags.agentKey).trim();
|
|
555
|
-
const response = await apiPost(ctx, '/api/v1/agent/onboarding/me/api-key/revoke', payload,
|
|
556
|
-
Authorization: `Bearer ${authToken}`,
|
|
557
|
-
});
|
|
558
|
+
const response = await apiPost(ctx, '/api/v1/agent/onboarding/me/api-key/revoke', payload, ownerHeaders);
|
|
558
559
|
printEnvelope({
|
|
559
560
|
success: true,
|
|
560
561
|
code: 'AGENT_KEY_REVOKED',
|
|
@@ -567,6 +568,47 @@ async function commandAgentKeyRevoke(ctx, flags, capabilities) {
|
|
|
567
568
|
});
|
|
568
569
|
}
|
|
569
570
|
|
|
571
|
+
async function commandAgentAuthChallenge(ctx, flags, capabilities) {
|
|
572
|
+
const payload = {
|
|
573
|
+
agentKey: requireFlag(flags, 'agentKey', 'Missing --agent-key'),
|
|
574
|
+
chainId: Number(flags.chainId || 8453),
|
|
575
|
+
};
|
|
576
|
+
if (flags.walletAddress) payload.walletAddress = String(flags.walletAddress).trim();
|
|
577
|
+
|
|
578
|
+
const response = await apiPost(ctx, '/api/v1/agent/onboarding/wallet/challenge', payload);
|
|
579
|
+
printEnvelope({
|
|
580
|
+
success: true,
|
|
581
|
+
code: 'OWNER_CHALLENGE_CREATED',
|
|
582
|
+
message: 'Wallet challenge created',
|
|
583
|
+
data: response,
|
|
584
|
+
meta: {
|
|
585
|
+
apiBase: ctx.apiBase,
|
|
586
|
+
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
587
|
+
},
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async function commandAgentAuthVerify(ctx, flags, capabilities) {
|
|
592
|
+
const payload = {
|
|
593
|
+
challengeId: requireFlag(flags, 'challengeId', 'Missing --challenge-id'),
|
|
594
|
+
signature: requireFlag(flags, 'signature', 'Missing --signature'),
|
|
595
|
+
agentKey: requireFlag(flags, 'agentKey', 'Missing --agent-key'),
|
|
596
|
+
};
|
|
597
|
+
if (flags.walletAddress) payload.walletAddress = String(flags.walletAddress).trim();
|
|
598
|
+
|
|
599
|
+
const response = await apiPost(ctx, '/api/v1/agent/onboarding/wallet/verify', payload);
|
|
600
|
+
printEnvelope({
|
|
601
|
+
success: true,
|
|
602
|
+
code: 'OWNER_VERIFIED',
|
|
603
|
+
message: 'Wallet challenge verified',
|
|
604
|
+
data: response,
|
|
605
|
+
meta: {
|
|
606
|
+
apiBase: ctx.apiBase,
|
|
607
|
+
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
570
612
|
function buildIntentPayload(flags, options) {
|
|
571
613
|
const payload = {
|
|
572
614
|
agentApiKey: requireFlag(flags, 'agentApiKey', 'Missing --agent-api-key'),
|
|
@@ -700,6 +742,18 @@ function getAuthToken(flags, missingMessage) {
|
|
|
700
742
|
return authToken;
|
|
701
743
|
}
|
|
702
744
|
|
|
745
|
+
function buildOwnerAuthHeaders(flags, missingMessage) {
|
|
746
|
+
const authToken = String(flags.authToken || process.env.THIRDFY_AUTH_TOKEN || '').trim();
|
|
747
|
+
const ownerSessionToken = String(flags.ownerSessionToken || process.env.THIRDFY_OWNER_SESSION_TOKEN || '').trim();
|
|
748
|
+
if (!authToken && !ownerSessionToken) {
|
|
749
|
+
throw new Error(missingMessage);
|
|
750
|
+
}
|
|
751
|
+
if (authToken) {
|
|
752
|
+
return { Authorization: `Bearer ${authToken}` };
|
|
753
|
+
}
|
|
754
|
+
return { 'x-owner-session-token': ownerSessionToken };
|
|
755
|
+
}
|
|
756
|
+
|
|
703
757
|
function extractActions(response) {
|
|
704
758
|
if (Array.isArray(response)) return response;
|
|
705
759
|
if (Array.isArray(response.actions)) return response.actions;
|
|
@@ -806,9 +860,11 @@ Core commands:
|
|
|
806
860
|
thirdfy-agent delegation status --auth-token <token> --agent-key <key> [--verify] [--json]
|
|
807
861
|
thirdfy-agent credentials upsert --auth-token <token> --venue <id> --api-key <key> --api-secret <secret> [--json]
|
|
808
862
|
thirdfy-agent credentials status --auth-token <token> [--venue <id>] [--json]
|
|
809
|
-
thirdfy-agent agent
|
|
810
|
-
thirdfy-agent agent
|
|
811
|
-
thirdfy-agent agent key
|
|
863
|
+
thirdfy-agent agent auth challenge --agent-key <key> [--wallet-address <addr>] [--chain-id <id>] [--json]
|
|
864
|
+
thirdfy-agent agent auth verify --challenge-id <id> --signature <hex> --agent-key <key> [--wallet-address <addr>] [--json]
|
|
865
|
+
thirdfy-agent agent register --agent-key <key> --name <name> [--auth-token <token> | --owner-session-token <token>] [--json]
|
|
866
|
+
thirdfy-agent agent key rotate [--agent-key <key>] [--auth-token <token> | --owner-session-token <token>] [--json]
|
|
867
|
+
thirdfy-agent agent key revoke [--agent-key <key>] [--auth-token <token> | --owner-session-token <token>] [--json]
|
|
812
868
|
thirdfy-agent preflight --agent-api-key <key> --action <id> --params <json> [--json]
|
|
813
869
|
thirdfy-agent run --agent-api-key <key> --action <id> --params <json> [--idempotency-key <key>] [--json]
|
|
814
870
|
thirdfy-agent intent-status --intent-id <id> [--json]
|
|
@@ -818,6 +874,7 @@ Global flags:
|
|
|
818
874
|
--api-base <url> default: https://api.thirdfy.com
|
|
819
875
|
--env <file> load env vars from file
|
|
820
876
|
--timeout <ms> request timeout
|
|
877
|
+
--owner-session-token <token> owner session token (or THIRDFY_OWNER_SESSION_TOKEN)
|
|
821
878
|
--json stable machine-readable output
|
|
822
879
|
--verbose print adapter diagnostics
|
|
823
880
|
--version print version
|