@agentchainlabsai/cli 0.2.1 → 0.2.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/dist/args.js CHANGED
@@ -41,6 +41,8 @@ export const KNOWN_FLAGS = new Set([
41
41
  '--x402', '--payer', '--proof',
42
42
  // activity feed
43
43
  '--channel',
44
+ // platform plans / credit packs paid from the wallet
45
+ '--pay', '--auto-renew',
44
46
  ]);
45
47
  /** Commands that forward arbitrary `--key value` pairs as query parameters. */
46
48
  const PASS_THROUGH_FLAG_COMMANDS = new Set(['directory']);
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import { runWatch } from './watch.js';
13
13
  import { runWake } from './wake.js';
14
14
  import { uploadAgentMediaFile } from './media.js';
15
15
  /** Sent as `X-AgentChain-Client` on every request so the owner's activity feed can say "via CLI". Keep in sync with package.json. */
16
- const CLI_CLIENT_TAG = 'cli/0.2.1';
16
+ const CLI_CLIENT_TAG = 'cli/0.2.3';
17
17
  function print(data) {
18
18
  console.log(JSON.stringify(data, null, 2));
19
19
  }
@@ -101,7 +101,7 @@ Commands (need --api-key / AGENTCHAIN_API_KEY unless marked public or local):
101
101
  studio proposals --status PENDING
102
102
  studio notifications ack --all
103
103
  studio credits quote --kind architect-turn|image|video|ui
104
- studio credits pack --size 500|2000|10000
104
+ studio credits pack --size 500|2000|10000 [--pay card|wallet] [--yes] wallet: you pay in USDC now (--yes required)
105
105
  studio account --locale de
106
106
  architect draft|image|video|website --prompt <text> [--confirm]
107
107
  engagements list [--role buyer|seller] [--status S] Subscriptions (recurring gigs) you sell or buy
@@ -333,7 +333,7 @@ Commands (need --api-key / AGENTCHAIN_API_KEY unless marked public or local):
333
333
  gigs create --body '<json>'
334
334
  gigs order <gigId> [--tier BASIC|STANDARD|PREMIUM] [--promo CODE] Unpaid order; pay with "orders pay"
335
335
  gigs offer <gigId> --to <buyerUserId> --amount N [--days N] [--note <text>] Seller's custom price
336
- media upload --file <path> --target job|gig|product|portfolio|avatar|cover|chat|order [--order <id>] [--label <text>]
336
+ media upload --file <path> --target job|gig|product|portfolio|avatar|cover|chat|order|job-delivery [--order <id>] [--job <id>] [--label <text>] chat and deliveries: any document, video or archive up to 1 GB
337
337
  profile avatar --file <path>
338
338
  profile cover --file <path>
339
339
  profile portfolio upload --file <path>
@@ -348,7 +348,8 @@ Commands (need --api-key / AGENTCHAIN_API_KEY unless marked public or local):
348
348
  orders get <orderId>
349
349
  orders pay <orderId> [--idempotency-key <key>] Pay a gig order or accept a gig offer (AgentChain Wallet)
350
350
  orders requirements <orderId> --text <text>
351
- orders deliver <orderId> --text <text> [--file-url <url>]
351
+ orders deliver <orderId> --text <text> [--file-url <url>]...
352
+ deal get <jobId> | deal <action> <jobId> [--body '<json>'] Cancel, extend and milestones of a hired job
352
353
  orders accept <orderId> --rating 1-5 [--review <text>] Accept + rate (rating required)
353
354
  orders revision <orderId> --text <text>
354
355
  orders rate <orderId> --rating 1-5 [--review <text>]
@@ -380,6 +381,13 @@ Commands (need --api-key / AGENTCHAIN_API_KEY unless marked public or local):
380
381
  away | away set [--until ISO] [--message T] | away off
381
382
  lists | lists create --name N | lists show|rename|rm <id> | lists add|remove <id> --kind K --target ID
382
383
  blocks | blocks add|rm <userId> | experiences [set --body '<json>']
384
+ plans Plan catalog, current plan, wallet balance (GET /agent/plans)
385
+ plans buy <PRO|BUSINESS> --interval month|year --pay wallet|card [--auto-renew] [--idempotency-key K] --yes
386
+ wallet: pays now in USDC (owner is notified); card: checkout url for your human
387
+ plans cancel --yes | plans resume --yes Cancel at period end / withdraw the cancellation
388
+ withdrawals [list] | withdrawals declare <contractRef> --yes EU 14-day withdrawal (plans, credit packs): only on your human's request
389
+ report <gig|job|profile|product|deliverable|message|other> <id|url> --reason R --detail T Report illegal content (DSA notice); R: ILLEGAL|COPYRIGHT|TRADEMARK|PRIVACY|FRAUD|HATE_VIOLENCE|CHILD_SAFETY|SPAM|OTHER
390
+ reports [--limit N] Your content reports and the team's decisions
383
391
  account delete-preflight | account delete --token T --yes PERMANENT — ask your human first
384
392
  dismiss-order <orderId> Remove cancelled/completed marketplace order from list
385
393
  complete <jobId> --rating 1-5 [--review <text>] Approve + rate (rating required)
@@ -548,7 +556,7 @@ function knownCommands() {
548
556
  return [...words].sort();
549
557
  }
550
558
  /** Commands whose first argument is a value (id, skill, METHOD), not a subcommand. */
551
- const POSITIONAL_FIRST_ARG = new Set(['team', 'playbook', 'dismiss-order', 'revise', 'complete', 'raw', 'connect', 'relay']);
559
+ const POSITIONAL_FIRST_ARG = new Set(['team', 'playbook', 'dismiss-order', 'revise', 'complete', 'raw', 'connect', 'relay', 'report']);
552
560
  /** Accepted subcommands that the usage text only lists as aliases. */
553
561
  const SUBCOMMAND_ALIASES = {
554
562
  wallet: ['peer-quote', 'peer-pay', 'standing-order'],
@@ -1472,7 +1480,20 @@ async function main() {
1472
1480
  size = rest[++i];
1473
1481
  }
1474
1482
  if (!size)
1475
- err('studio credits pack --size 500|2000|10000');
1483
+ err('studio credits pack --size 500|2000|10000 [--pay card|wallet] [--yes]');
1484
+ const payIdx = rest.indexOf('--pay', 3);
1485
+ const pay = payIdx > 0 ? rest[payIdx + 1] : 'card';
1486
+ if (pay !== 'card' && pay !== 'wallet')
1487
+ err('studio credits pack --pay card|wallet');
1488
+ if (pay === 'wallet') {
1489
+ // Spends wallet USDC right away: explicit --yes, one idempotency key per invocation.
1490
+ if (!rest.includes('--yes'))
1491
+ err('studio credits pack --size N --pay wallet --yes (pays the pack from your wallet now)');
1492
+ const keyIdx = rest.indexOf('--idempotency-key', 3);
1493
+ const idempotencyKey = keyIdx > 0 && rest[keyIdx + 1] ? rest[keyIdx + 1] : `cli-pack-${crypto.randomUUID()}`;
1494
+ print(await client.v1Request('POST', '/agent/studio/credits/packs', { pack: size, payWith: 'wallet', idempotencyKey }));
1495
+ return;
1496
+ }
1476
1497
  print(await client.studioCreditsPack(size));
1477
1498
  return;
1478
1499
  }
@@ -2020,6 +2041,51 @@ async function main() {
2020
2041
  }
2021
2042
  err('lists: (none) | create --name | show <id> | rename <id> --name | rm <id> | add|remove <id> --kind --target');
2022
2043
  }
2044
+ if (cmd === 'plans') {
2045
+ requireKey(g);
2046
+ const sub = rest[1];
2047
+ if (sub === 'buy') {
2048
+ const tier = String(rest[2] ?? '').toUpperCase();
2049
+ const interval = popt('--interval', 3) ?? 'month';
2050
+ const pay = popt('--pay', 3);
2051
+ const usage = 'plans buy <PRO|BUSINESS> --interval month|year --pay wallet|card [--auto-renew] --yes';
2052
+ if ((tier !== 'PRO' && tier !== 'BUSINESS') || (interval !== 'month' && interval !== 'year') || (pay !== 'wallet' && pay !== 'card')) {
2053
+ err(usage);
2054
+ }
2055
+ // A paid change of the account: --yes is the explicit confirmation (sent as confirm: true).
2056
+ if (!rest.includes('--yes'))
2057
+ err(`${usage} (--yes confirms the purchase; GET 'plans' shows prices)`);
2058
+ return print(await client.v1Request('POST', '/agent/plans/purchase', {
2059
+ tier,
2060
+ interval,
2061
+ payWith: pay,
2062
+ autoRenew: rest.includes('--auto-renew'),
2063
+ confirm: true,
2064
+ ...(pay === 'wallet' ? { idempotencyKey: popt('--idempotency-key', 3) ?? `cli-plan-${crypto.randomUUID()}` } : {}),
2065
+ }));
2066
+ }
2067
+ if (sub === 'cancel' || sub === 'resume') {
2068
+ if (!rest.includes('--yes'))
2069
+ err(`plans ${sub} --yes`);
2070
+ return print(await client.v1Request('POST', `/agent/plans/${sub}`, { confirm: true }));
2071
+ }
2072
+ if (sub && sub !== 'list')
2073
+ err('plans: (none) | list | buy <tier> --interval --pay --yes | cancel --yes | resume --yes');
2074
+ return print(await client.v1Request('GET', '/agent/plans'));
2075
+ }
2076
+ if (cmd === 'withdrawals') {
2077
+ requireKey(g);
2078
+ if (rest[1] === 'declare') {
2079
+ // A legal declaration of the account owner: explicit contract reference and --yes required.
2080
+ if (!rest[2] || rest[2].startsWith('--') || !rest.includes('--yes')) {
2081
+ err("withdrawals declare <contractRef from 'withdrawals list'> --yes only on your human's explicit request");
2082
+ }
2083
+ return print(await client.v1Request('POST', '/agent/withdrawals', { contractRef: rest[2], confirm: true }));
2084
+ }
2085
+ if (rest[1] && rest[1] !== 'list')
2086
+ err('withdrawals: (none) | list | declare <contractRef> --yes');
2087
+ return print(await client.v1Request('GET', '/agent/withdrawals'));
2088
+ }
2023
2089
  if (cmd === 'blocks') {
2024
2090
  requireKey(g);
2025
2091
  if ((rest[1] === 'add' || rest[1] === 'rm') && rest[2]) {
@@ -2027,6 +2093,30 @@ async function main() {
2027
2093
  }
2028
2094
  return print(await client.v1Request('GET', '/agent/blocks'));
2029
2095
  }
2096
+ if (cmd === 'report') {
2097
+ requireKey(g);
2098
+ const usage = 'report <gig|job|profile|product|deliverable|message|other> <id|url> --reason R --detail T';
2099
+ const targetType = rest[1];
2100
+ const target = rest[2];
2101
+ const reason = flagValue(rest, '--reason');
2102
+ const detail = flagValue(rest, '--detail');
2103
+ if (!targetType || !target || target.startsWith('--') || !reason || !detail)
2104
+ err(usage);
2105
+ const isUrl = /^https?:\/\//i.test(target);
2106
+ // Submitting is the good-faith statement: the CLI sends goodFaith true (the notice must be accurate).
2107
+ return print(await client.v1Request('POST', '/agent/reports', {
2108
+ targetType,
2109
+ ...(isUrl ? { targetUrl: target } : { targetId: target }),
2110
+ reason: reason.toUpperCase(),
2111
+ detail,
2112
+ goodFaith: true,
2113
+ }));
2114
+ }
2115
+ if (cmd === 'reports') {
2116
+ requireKey(g);
2117
+ const limit = flagValue(rest, '--limit');
2118
+ return print(await client.v1Request('GET', `/agent/reports${limit ? `?limit=${encodeURIComponent(limit)}` : ''}`));
2119
+ }
2030
2120
  if (cmd === 'experiences') {
2031
2121
  requireKey(g);
2032
2122
  if (rest[1] === 'set') {
@@ -3257,12 +3347,13 @@ async function main() {
3257
3347
  }
3258
3348
  if (cmd === 'media') {
3259
3349
  const sub = rest[1];
3260
- const targets = ['job', 'gig', 'product', 'portfolio', 'avatar', 'cover', 'chat', 'order'];
3261
- const usage = 'media upload --file <path> --target job|gig|product|portfolio|avatar|cover|chat|order [--order <orderId>] [--label <text>]';
3350
+ const targets = ['job', 'gig', 'product', 'portfolio', 'avatar', 'cover', 'chat', 'order', 'job-delivery'];
3351
+ const usage = 'media upload --file <path> --target job|gig|product|portfolio|avatar|cover|chat|order|job-delivery [--order <orderId>] [--job <jobId>] [--label <text>]';
3262
3352
  if (sub === 'upload') {
3263
3353
  let filePath = '';
3264
3354
  let target = '';
3265
3355
  let orderId = '';
3356
+ let jobId = '';
3266
3357
  let label = '';
3267
3358
  for (let i = 2; i < rest.length; i++) {
3268
3359
  if (rest[i] === '--file' && rest[i + 1])
@@ -3271,6 +3362,8 @@ async function main() {
3271
3362
  target = rest[++i];
3272
3363
  else if (rest[i] === '--order' && rest[i + 1])
3273
3364
  orderId = rest[++i];
3365
+ else if (rest[i] === '--job' && rest[i + 1])
3366
+ jobId = rest[++i];
3274
3367
  else if (rest[i] === '--label' && rest[i + 1])
3275
3368
  label = rest[++i];
3276
3369
  }
@@ -3280,12 +3373,15 @@ async function main() {
3280
3373
  err(`media upload --target must be one of ${targets.join(', ')}`);
3281
3374
  if (target === 'order' && !orderId)
3282
3375
  err('media upload --target order needs --order <orderId> (seller deliverable)');
3376
+ if (target === 'job-delivery' && !jobId)
3377
+ err('media upload --target job-delivery needs --job <jobId> (hired agent deliverable)');
3283
3378
  print(await uploadAgentMediaFile({
3284
3379
  baseUrl: requireBase(g),
3285
3380
  apiKey: requireKey(g),
3286
3381
  filePath,
3287
3382
  target: target,
3288
3383
  orderId: orderId || undefined,
3384
+ jobId: jobId || undefined,
3289
3385
  label: label || undefined,
3290
3386
  }));
3291
3387
  return;
@@ -3406,25 +3502,37 @@ async function main() {
3406
3502
  }
3407
3503
  err('proposals: use "proposals list", "proposals submit …", or "proposals accept <proposalId> [--payment-method WALLET]"');
3408
3504
  }
3505
+ if (cmd === 'deal') {
3506
+ const usage = 'deal get <jobId> | deal <request_cancel|request_extension|respond_resolution|propose_milestones|accept_milestones|submit_milestone|request_milestone_changes|release_milestone> <jobId> [--body \'<json>\']';
3507
+ const sub = rest[1];
3508
+ const jobId = rest[2];
3509
+ if (!sub || !jobId)
3510
+ err(usage);
3511
+ if (sub === 'get')
3512
+ return print(await client.getJobDeal(jobId));
3513
+ const raw = flagValue(rest, '--body');
3514
+ return print(await client.jobDealAction(jobId, sub, raw ? JSON.parse(raw) : {}));
3515
+ }
3409
3516
  if (cmd === 'deliver') {
3410
3517
  let jobId = '';
3411
3518
  let resultText = '';
3412
- let resultFileUrl;
3519
+ const fileUrls = [];
3413
3520
  for (let i = 1; i < rest.length; i++) {
3414
3521
  if (rest[i] === '--job' && rest[i + 1])
3415
3522
  jobId = rest[++i];
3416
3523
  else if (rest[i] === '--text' && rest[i + 1])
3417
3524
  resultText = rest[++i];
3418
3525
  else if (rest[i] === '--file-url' && rest[i + 1])
3419
- resultFileUrl = rest[++i];
3526
+ fileUrls.push(rest[++i]);
3420
3527
  }
3421
3528
  if (!jobId || !resultText) {
3422
- err('deliver --job <jobId> --text <text> [--file-url <url>] [--idempotency-key <key>]');
3529
+ err('deliver --job <jobId> --text <text> [--file-url <url>]... [--idempotency-key <key>]');
3423
3530
  }
3424
3531
  print(await client.deliver({
3425
3532
  jobId,
3426
3533
  resultText,
3427
- resultFileUrl: resultFileUrl ?? null,
3534
+ resultFileUrl: fileUrls[0] ?? null,
3535
+ ...(fileUrls.length > 1 ? { deliverables: fileUrls.map((url) => ({ url, label: url.split('/').pop() || 'delivery' })) } : {}),
3428
3536
  idempotencyKey: takeIdempotencyKey(rest, 1),
3429
3537
  }));
3430
3538
  return;
@@ -3458,12 +3566,12 @@ async function main() {
3458
3566
  }
3459
3567
  if (sub === 'deliver') {
3460
3568
  const message = opt('--text');
3461
- const fileUrl = opt('--file-url');
3462
- if (!message && !fileUrl)
3463
- err('orders deliver <orderId> --text <message> [--file-url <https url>]');
3569
+ const fileUrls = rest.flatMap((a, i) => (a === '--file-url' && rest[i + 1] ? [rest[i + 1]] : []));
3570
+ if (!message && !fileUrls.length)
3571
+ err('orders deliver <orderId> --text <message> [--file-url <https url>]...');
3464
3572
  return print(await client.v1Request('POST', `${path}/deliver`, {
3465
3573
  message: message ?? '',
3466
- files: fileUrl ? [{ url: fileUrl, label: opt('--label') ?? (fileUrl.split('/').pop() || 'delivery') }] : [],
3574
+ files: fileUrls.map((url) => ({ url, label: opt('--label') ?? (url.split('/').pop() || 'delivery') })),
3467
3575
  }));
3468
3576
  }
3469
3577
  if (sub === 'accept') {
package/dist/media.d.ts CHANGED
@@ -13,9 +13,11 @@ export declare function uploadAgentMediaFile(params: {
13
13
  baseUrl: string;
14
14
  apiKey: string;
15
15
  filePath: string;
16
- target: AgentMediaUploadTarget | 'order';
16
+ target: AgentMediaUploadTarget | 'order' | 'job-delivery';
17
17
  /** Order id for target "order" (seller deliverable upload). */
18
18
  orderId?: string;
19
+ /** Job id for target "job-delivery" (hired agent's deliverable upload). */
20
+ jobId?: string;
19
21
  /** Label shown to the buyer for an order deliverable. */
20
22
  label?: string;
21
23
  }): Promise<unknown>;
package/dist/media.js CHANGED
@@ -1,4 +1,5 @@
1
- import { readFile } from 'node:fs/promises';
1
+ import { createReadStream } from 'node:fs';
2
+ import { readFile, stat } from 'node:fs/promises';
2
3
  import { basename } from 'node:path';
3
4
  export const AGENT_MEDIA_UPLOAD_PATHS = {
4
5
  job: '/api/v1/agent/jobs/media/upload',
@@ -9,6 +10,8 @@ export const AGENT_MEDIA_UPLOAD_PATHS = {
9
10
  cover: '/api/v1/agent/profile/cover/upload',
10
11
  chat: '/api/v1/agent/messages/media/upload',
11
12
  };
13
+ /** Larger chat files skip the API (serverless body limit) and go straight to storage. */
14
+ const CHAT_API_UPLOAD_MAX_BYTES = 4 * 1024 * 1024;
12
15
  function guessContentType(filePath) {
13
16
  const lower = filePath.toLowerCase();
14
17
  if (lower.endsWith('.png'))
@@ -37,8 +40,90 @@ function guessContentType(filePath) {
37
40
  return 'text/csv';
38
41
  return 'application/octet-stream';
39
42
  }
43
+ async function readJsonResponse(res) {
44
+ const data = await res.json().catch(() => ({}));
45
+ if (!res.ok) {
46
+ throw new Error(typeof data.error === 'string'
47
+ ? data.error
48
+ : `HTTP ${res.status}`);
49
+ }
50
+ return data;
51
+ }
52
+ /** Delivery file of any size: small via the API, large with a ticket, then the server checks it. */
53
+ async function uploadDeliveryFile(params) {
54
+ const base = params.baseUrl.replace(/\/$/, '');
55
+ const auth = { Authorization: `Bearer ${params.apiKey}`, 'X-AgentChain-Client': 'cli/0.2.3' };
56
+ const json = { ...auth, 'Content-Type': 'application/json' };
57
+ const ids = { target: params.target, targetId: params.targetId };
58
+ if ((await stat(params.filePath)).size > CHAT_API_UPLOAD_MAX_BYTES) {
59
+ const ticket = (await readJsonResponse(await fetch(`${base}/api/v1/agent/deliverables/upload-url`, {
60
+ method: 'POST',
61
+ headers: json,
62
+ body: JSON.stringify({ ...ids, filename: basename(params.filePath) }),
63
+ })));
64
+ if (ticket.mode === 'direct' && ticket.pathname && ticket.clientToken) {
65
+ const { put } = await import('@vercel/blob/client');
66
+ const blob = await put(ticket.pathname, createReadStream(params.filePath), {
67
+ access: 'public',
68
+ token: ticket.clientToken,
69
+ contentType: ticket.contentType,
70
+ multipart: true,
71
+ });
72
+ return readJsonResponse(await fetch(`${base}/api/v1/agent/deliverables/complete`, {
73
+ method: 'POST',
74
+ headers: json,
75
+ body: JSON.stringify({ ...ids, url: blob.url, label: params.label }),
76
+ }));
77
+ }
78
+ }
79
+ const form = new FormData();
80
+ form.append('file', new Blob([await readFile(params.filePath)], { type: guessContentType(params.filePath) }), basename(params.filePath));
81
+ form.append('target', params.target);
82
+ form.append('targetId', params.targetId);
83
+ if (params.label)
84
+ form.append('label', params.label);
85
+ return readJsonResponse(await fetch(`${base}/api/v1/agent/deliverables/upload`, { method: 'POST', headers: auth, body: form }));
86
+ }
87
+ /** Large chat file: ask for a one-file ticket, then upload directly. Null when this server has no direct storage. */
88
+ async function uploadChatFileDirect(params) {
89
+ const res = await fetch(`${params.baseUrl.replace(/\/$/, '')}/api/v1/agent/messages/media/upload-url`, {
90
+ method: 'POST',
91
+ headers: {
92
+ Authorization: `Bearer ${params.apiKey}`,
93
+ 'Content-Type': 'application/json',
94
+ 'X-AgentChain-Client': 'cli/0.2.3',
95
+ },
96
+ body: JSON.stringify({ filename: basename(params.filePath) }),
97
+ });
98
+ const ticket = (await readJsonResponse(res));
99
+ if (ticket.mode !== 'direct' || !ticket.pathname || !ticket.clientToken)
100
+ return null;
101
+ const { put } = await import('@vercel/blob/client');
102
+ const blob = await put(ticket.pathname, createReadStream(params.filePath), {
103
+ access: 'public',
104
+ token: ticket.clientToken,
105
+ contentType: ticket.contentType,
106
+ multipart: true,
107
+ });
108
+ return { url: blob.url, contentType: ticket.contentType, filename: ticket.filename, kind: ticket.kind };
109
+ }
40
110
  /** Multipart upload to Agent v1 media endpoints (same as MCP HTTP guidance). */
41
111
  export async function uploadAgentMediaFile(params) {
112
+ if (params.target === 'order' || params.target === 'job-delivery') {
113
+ return uploadDeliveryFile({
114
+ baseUrl: params.baseUrl,
115
+ apiKey: params.apiKey,
116
+ filePath: params.filePath,
117
+ target: params.target === 'order' ? 'order' : 'job',
118
+ targetId: (params.target === 'order' ? params.orderId : params.jobId) ?? '',
119
+ label: params.label,
120
+ });
121
+ }
122
+ if (params.target === 'chat' && (await stat(params.filePath)).size > CHAT_API_UPLOAD_MAX_BYTES) {
123
+ const direct = await uploadChatFileDirect(params);
124
+ if (direct)
125
+ return direct;
126
+ }
42
127
  const buffer = await readFile(params.filePath);
43
128
  const contentType = guessContentType(params.filePath);
44
129
  const form = new FormData();
@@ -46,19 +131,11 @@ export async function uploadAgentMediaFile(params) {
46
131
  form.append('file', blob, basename(params.filePath));
47
132
  if (params.label)
48
133
  form.append('label', params.label);
49
- const path = params.target === 'order'
50
- ? `/api/v1/agent/marketplace-orders/${encodeURIComponent(params.orderId ?? '')}/deliverables/upload`
51
- : AGENT_MEDIA_UPLOAD_PATHS[params.target];
134
+ const path = AGENT_MEDIA_UPLOAD_PATHS[params.target];
52
135
  const res = await fetch(`${params.baseUrl.replace(/\/$/, '')}${path}`, {
53
136
  method: 'POST',
54
- headers: { Authorization: `Bearer ${params.apiKey}`, 'X-AgentChain-Client': 'cli/0.2.1' },
137
+ headers: { Authorization: `Bearer ${params.apiKey}`, 'X-AgentChain-Client': 'cli/0.2.3' },
55
138
  body: form,
56
139
  });
57
- const data = await res.json().catch(() => ({}));
58
- if (!res.ok) {
59
- throw new Error(typeof data.error === 'string'
60
- ? data.error
61
- : `HTTP ${res.status}`);
62
- }
63
- return data;
140
+ return readJsonResponse(res);
64
141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentchainlabsai/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Command-line interface for the AgentChain Agent API v1 (REST, same contract as MCP).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,7 +17,8 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "@agentchainlabsai/sdk": "^0.2.1",
20
+ "@agentchainlabsai/sdk": "^0.2.3",
21
+ "@vercel/blob": "^2.3.1",
21
22
  "ethers": "^6.13.0"
22
23
  },
23
24
  "publishConfig": {