@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 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 --idempotency-key "swap-001" --json
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`: user-scoped onboarding and account/governance operations (`agent register`, `agent key *`, `delegation *`, `credentials *`, `credits balance`)
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
 
@@ -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
- const isEntrypoint = (() => {
43
- try {
44
- if (!process.argv[1]) return false;
45
- const resolvedArgvPath = fs.realpathSync(process.argv[1]);
46
- return import.meta.url === pathToFileURL(resolvedArgvPath).href;
47
- } catch {
48
- return false;
49
- }
50
- })();
51
-
52
- if (isEntrypoint) {
53
- main().catch((error) => {
54
- const statusCode = Number(error?.statusCode || 0) || undefined;
55
- const payload = error?.payload && typeof error.payload === 'object' ? error.payload : {};
56
- const blockedReason = String(payload.blockedReason || payload.error || '').trim() || undefined;
57
- const blockedStage = String(payload.blockedStage || '').trim() || undefined;
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 authToken = getAuthToken(flags, 'Missing --auth-token (or THIRDFY_AUTH_TOKEN) for agent register');
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 response = await apiPost(ctx, '/api/v1/agent/onboarding/register-with-privy', payload, {
518
- Authorization: `Bearer ${authToken}`,
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 authToken = getAuthToken(flags, 'Missing --auth-token (or THIRDFY_AUTH_TOKEN) for agent key rotate');
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 authToken = getAuthToken(flags, 'Missing --auth-token (or THIRDFY_AUTH_TOKEN) for agent key revoke');
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 register --auth-token <token> --agent-key <key> --name <name> [--json]
810
- thirdfy-agent agent key rotate --auth-token <token> [--agent-key <key>] [--json]
811
- thirdfy-agent agent key revoke --auth-token <token> [--agent-key <key>] [--json]
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {