@indexnetwork/cli 0.16.0-rc.65.1 → 0.28.0

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.
Files changed (3) hide show
  1. package/README.md +181 -57
  2. package/dist/index.js +719 -1218
  3. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1,25 +1,11 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __returnValue = (v) => v;
4
- function __exportSetter(name, newValue) {
5
- this[name] = __returnValue.bind(null, newValue);
6
- }
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true,
12
- configurable: true,
13
- set: __exportSetter.bind(all, name)
14
- });
15
- };
16
2
 
17
3
  // src/main.ts
18
4
  import { spawn } from "node:child_process";
19
5
  // package.json
20
6
  var package_default = {
21
7
  name: "@indexnetwork/cli",
22
- version: "0.16.0",
8
+ version: "0.28.0",
23
9
  description: "Command-line interface for Index Network",
24
10
  license: "MIT",
25
11
  type: "module",
@@ -39,10 +25,10 @@ var package_default = {
39
25
  "publish:all": "bun scripts/publish.ts"
40
26
  },
41
27
  optionalDependencies: {
42
- "@indexnetwork/cli-linux-x64": "0.16.0",
43
- "@indexnetwork/cli-linux-arm64": "0.16.0",
44
- "@indexnetwork/cli-darwin-x64": "0.16.0",
45
- "@indexnetwork/cli-darwin-arm64": "0.16.0"
28
+ "@indexnetwork/cli-linux-x64": "0.28.0",
29
+ "@indexnetwork/cli-linux-arm64": "0.28.0",
30
+ "@indexnetwork/cli-darwin-x64": "0.28.0",
31
+ "@indexnetwork/cli-darwin-arm64": "0.28.0"
46
32
  },
47
33
  devDependencies: {
48
34
  "@types/bun": "1.3.14"
@@ -50,15 +36,15 @@ var package_default = {
50
36
  };
51
37
 
52
38
  // src/args.parser.ts
53
- var KNOWN_COMMANDS = new Set(["login", "logout", "profile", "intent", "opportunity", "negotiation", "network", "conversation", "contact", "scrape", "onboarding", "sync", "help", "version"]);
54
- var OPPORTUNITY_SUBCOMMANDS = new Set(["list", "show", "accept", "reject"]);
55
- var NEGOTIATION_SUBCOMMANDS = new Set(["list", "show"]);
56
- var NETWORK_SUBCOMMANDS = new Set(["list", "create", "show", "join", "leave", "invite", "update", "delete"]);
57
- var CONVERSATION_SUBCOMMANDS = new Set(["list", "with", "show", "send", "stream", "help"]);
39
+ var KNOWN_COMMANDS = new Set(["docs", "agent", "login", "logout", "profile", "intent", "opportunity", "negotiation", "network", "conversation", "scrape", "onboarding", "sync", "help", "version"]);
40
+ var OPPORTUNITY_SUBCOMMANDS = new Set(["list", "show", "accept", "reject", "start-chat"]);
41
+ var NEGOTIATION_SUBCOMMANDS = new Set(["list", "show", "turn"]);
42
+ var NETWORK_SUBCOMMANDS = new Set(["list", "create", "show", "join", "leave", "invite", "update", "delete", "discover", "requests", "request-update", "request-dismiss"]);
43
+ var CONVERSATION_SUBCOMMANDS = new Set(["list", "with", "show", "send", "answer", "stream", "help"]);
58
44
  function parseArgs(args) {
59
45
  const result = {
60
46
  command: "help",
61
- list: false
47
+ json: args.includes("--json")
62
48
  };
63
49
  if (args.length === 0) {
64
50
  return result;
@@ -67,6 +53,8 @@ function parseArgs(args) {
67
53
  for (let j = 0;j < args.length; j++) {
68
54
  const a = args[j];
69
55
  if (a === "--api-url" || a === "--app-url") {
56
+ if (!args[j + 1] || args[j + 1].startsWith("--"))
57
+ throw new Error(`Missing value for ${a}`);
70
58
  if (a === "--api-url")
71
59
  result.apiUrl = args[j + 1];
72
60
  else
@@ -78,7 +66,11 @@ function parseArgs(args) {
78
66
  } else if (a === "--version" || a === "-v") {
79
67
  result.command = "version";
80
68
  return result;
81
- } else if (!a.startsWith("--")) {
69
+ } else if (a === "--json") {
70
+ continue;
71
+ } else if (a.startsWith("-")) {
72
+ throw new Error(`Unknown option: ${a}`);
73
+ } else {
82
74
  commandIndex = j;
83
75
  break;
84
76
  }
@@ -97,10 +89,66 @@ function parseArgs(args) {
97
89
  const positionals = [];
98
90
  while (i < args.length) {
99
91
  const arg = args[i];
100
- if (arg === "--list" || arg === "-l") {
101
- result.list = true;
102
- i++;
103
- } else if (arg === "--session" || arg === "-s") {
92
+ if (arg === "--help" || arg === "-h")
93
+ return { ...result, command: "help" };
94
+ if (arg === "--version" || arg === "-v")
95
+ return { ...result, command: "version" };
96
+ if (["--api-url", "--app-url", "--status", "--limit", "--prompt", "-p", "--objective", "--title"].includes(arg) && (!args[i + 1] || args[i + 1].startsWith("--")))
97
+ throw new Error(`Missing value for ${arg}`);
98
+ if (["--query", "--state", "--action", "--message", "--intent-id", "--question-id", "--expected-turn-count", "--receipt", "--name", "--intro", "--location", "--statuses", "--answer", "--social"].includes(arg)) {
99
+ const value = args[i + 1];
100
+ if (value === undefined || value.startsWith("--"))
101
+ throw new Error(`Missing value for ${arg}`);
102
+ switch (arg) {
103
+ case "--query":
104
+ result.query = value;
105
+ break;
106
+ case "--state":
107
+ result.state = value;
108
+ break;
109
+ case "--action":
110
+ result.action = value;
111
+ break;
112
+ case "--message":
113
+ result.message = value;
114
+ break;
115
+ case "--intent-id":
116
+ result.intentId = value;
117
+ break;
118
+ case "--question-id":
119
+ result.questionId = value;
120
+ break;
121
+ case "--expected-turn-count":
122
+ result.expectedTurnCount = Number(value);
123
+ break;
124
+ case "--receipt":
125
+ result.receipt = value;
126
+ break;
127
+ case "--name":
128
+ result.name = value;
129
+ break;
130
+ case "--intro":
131
+ result.intro = value;
132
+ break;
133
+ case "--location":
134
+ result.location = value;
135
+ break;
136
+ case "--statuses":
137
+ result.statuses = value;
138
+ break;
139
+ case "--answer":
140
+ case "--social": {
141
+ const at = value.indexOf("=");
142
+ if (at < 1 || !value.slice(at + 1).trim())
143
+ throw new Error(`${arg} needs key=value`);
144
+ const pair = { key: value.slice(0, at), value: value.slice(at + 1) };
145
+ if (arg === "--answer")
146
+ (result.answers ??= []).push(pair);
147
+ else
148
+ (result.socials ??= []).push({ label: pair.key, value: pair.value });
149
+ break;
150
+ }
151
+ }
104
152
  i += 2;
105
153
  } else if (arg === "--api-url") {
106
154
  result.apiUrl = args[i + 1];
@@ -115,7 +163,9 @@ function parseArgs(args) {
115
163
  result.status = args[i + 1];
116
164
  i += 2;
117
165
  } else if (arg === "--limit") {
118
- result.limit = parseInt(args[i + 1], 10);
166
+ result.limit = Number(args[i + 1]);
167
+ if (!Number.isSafeInteger(result.limit) || result.limit < 1)
168
+ throw new Error("--limit must be a positive integer");
119
169
  i += 2;
120
170
  } else if (arg === "--prompt" || arg === "-p") {
121
171
  result.prompt = args[i + 1];
@@ -126,34 +176,45 @@ function parseArgs(args) {
126
176
  } else if (arg === "--objective") {
127
177
  result.objective = args[i + 1];
128
178
  i += 2;
129
- } else if (arg === "--linkedin") {
130
- result.linkedin = args[i + 1];
131
- i += 2;
132
- } else if (arg === "--github") {
133
- result.github = args[i + 1];
134
- i += 2;
135
- } else if (arg === "--twitter") {
136
- result.twitter = args[i + 1];
137
- i += 2;
138
179
  } else if (arg === "--title") {
139
180
  result.title = args[i + 1];
140
181
  i += 2;
141
- } else if (arg === "--details") {
142
- result.details = args[i + 1];
143
- i += 2;
144
- } else if (arg === "--since") {
145
- result.since = args[i + 1];
146
- i += 2;
147
- } else if (arg === "--acknowledge-uptake") {
148
- result.acknowledgeUptake = (args[i + 1] ?? "").split(",").map((id) => id.trim()).filter(Boolean);
149
- i += 2;
150
- } else if (arg.startsWith("--")) {
151
- i++;
182
+ } else if (arg.startsWith("-")) {
183
+ throw new Error(`Unknown option: ${arg}`);
152
184
  } else {
153
185
  positionals.push(arg);
154
186
  i++;
155
187
  }
156
188
  }
189
+ const subcommands = {
190
+ agent: new Set(["me"]),
191
+ opportunity: OPPORTUNITY_SUBCOMMANDS,
192
+ negotiation: NEGOTIATION_SUBCOMMANDS,
193
+ network: NETWORK_SUBCOMMANDS,
194
+ conversation: CONVERSATION_SUBCOMMANDS,
195
+ intent: INTENT_SUBCOMMANDS,
196
+ profile: new Set(["show", "sync", "update"]),
197
+ onboarding: new Set(["confirm-profile", "complete"])
198
+ };
199
+ const allowed = subcommands[result.command];
200
+ if (allowed && result.command !== "profile" && !positionals[0]) {
201
+ throw new Error(`Missing subcommand for index ${result.command}. Run index --help.`);
202
+ }
203
+ if (allowed && positionals[0] && !allowed.has(positionals[0])) {
204
+ throw new Error(`Unknown command: ${result.command} ${positionals[0]}`);
205
+ }
206
+ if (result.command === "agent") {
207
+ result.subcommand = positionals[0];
208
+ result.positionals = positionals.slice(1);
209
+ return result;
210
+ }
211
+ if (result.command === "login") {
212
+ if (positionals.length > 1 || positionals[0] && !["local", "dev", "prod"].includes(positionals[0])) {
213
+ throw new Error("Usage: index login [local|dev|prod] [--api-url <origin>] [--app-url <origin>]");
214
+ }
215
+ result.loginTarget = positionals[0];
216
+ return result;
217
+ }
157
218
  if (result.command === "opportunity") {
158
219
  const sub = positionals[0];
159
220
  if (sub && OPPORTUNITY_SUBCOMMANDS.has(sub)) {
@@ -168,6 +229,9 @@ function parseArgs(args) {
168
229
  return result;
169
230
  }
170
231
  if (result.command === "negotiation") {
232
+ if (result.limit !== undefined || result.status !== undefined) {
233
+ throw new Error("Negotiation filters are --intent-id and --state only");
234
+ }
171
235
  const sub = positionals[0];
172
236
  if (sub && NEGOTIATION_SUBCOMMANDS.has(sub)) {
173
237
  result.subcommand = sub;
@@ -198,20 +262,11 @@ function parseArgs(args) {
198
262
  }
199
263
  if (result.command === "onboarding" && positionals.length > 0) {
200
264
  const sub = positionals[0];
201
- if (sub === "complete") {
202
- result.subcommand = "complete";
203
- }
204
- }
205
- if (result.command === "contact") {
206
- if (positionals.length > 0) {
207
- const sub = positionals[0];
208
- if (["list", "add", "remove", "import"].includes(sub)) {
209
- result.subcommand = sub;
210
- result.positionals = positionals.slice(1);
211
- }
265
+ if (sub === "complete" || sub === "confirm-profile") {
266
+ result.subcommand = sub;
212
267
  }
213
268
  }
214
- if (result.command === "scrape") {
269
+ if (result.command === "scrape" || result.command === "docs") {
215
270
  result.positionals = positionals;
216
271
  }
217
272
  if (result.command === "intent") {
@@ -233,7 +288,7 @@ function parseArgs(args) {
233
288
  }
234
289
  return result;
235
290
  }
236
- var INTENT_SUBCOMMANDS = new Set(["list", "show", "create", "archive", "update", "link", "unlink"]);
291
+ var INTENT_SUBCOMMANDS = new Set(["list", "show", "create", "prepare", "archive", "pause", "resume", "update", "networks", "add-to-network", "remove-from-network"]);
237
292
  function parseIntentArgs(positionals, result) {
238
293
  if (positionals.length === 0)
239
294
  return;
@@ -245,9 +300,13 @@ function parseIntentArgs(positionals, result) {
245
300
  switch (result.subcommand) {
246
301
  case "show":
247
302
  case "archive":
303
+ case "pause":
304
+ case "resume":
305
+ case "networks":
248
306
  result.intentId = rest[0];
249
307
  break;
250
308
  case "create":
309
+ case "prepare":
251
310
  if (rest.length > 0) {
252
311
  result.intentContent = rest.join(" ");
253
312
  }
@@ -258,8 +317,8 @@ function parseIntentArgs(positionals, result) {
258
317
  result.intentContent = rest.slice(1).join(" ");
259
318
  }
260
319
  break;
261
- case "link":
262
- case "unlink":
320
+ case "add-to-network":
321
+ case "remove-from-network":
263
322
  result.intentId = rest[0];
264
323
  result.targetId = rest[1];
265
324
  break;
@@ -283,7 +342,7 @@ class CredentialStore {
283
342
  try {
284
343
  const raw = await readFile(this.filePath, "utf-8");
285
344
  const parsed = JSON.parse(raw);
286
- if (parsed.token && parsed.apiUrl && parsed.authKind === "api_key" && parsed.keyId) {
345
+ if (parsed.token && parsed.apiUrl && parsed.authKind === "session") {
287
346
  return parsed;
288
347
  }
289
348
  return null;
@@ -327,21 +386,16 @@ function isEarlyAccessNetworkCreationError(error) {
327
386
  }
328
387
 
329
388
  class ApiClient {
389
+ credentialKind;
330
390
  baseUrl;
331
391
  token;
332
- constructor(baseUrl, token) {
392
+ constructor(baseUrl, token, credentialKind = "session") {
393
+ this.credentialKind = credentialKind;
333
394
  this.baseUrl = baseUrl.replace(/\/$/, "");
334
395
  this.token = token;
335
396
  }
336
397
  authHeaders() {
337
- return { "x-api-key": this.token };
338
- }
339
- async revokeApiKey(keyId, targetKey = this.token) {
340
- const res = await this.post("/api/auth/cli-credential/revoke", { keyId, targetKey });
341
- const body = await res.json();
342
- if (body.success !== true) {
343
- throw new Error("API-key revocation was not confirmed");
344
- }
398
+ return this.credentialKind === "apiKey" ? { "x-api-key": this.token } : { Authorization: `Bearer ${this.token}` };
345
399
  }
346
400
  async getMe() {
347
401
  const res = await this.get("/api/auth/me");
@@ -357,10 +411,13 @@ class ApiClient {
357
411
  const params = new URLSearchParams;
358
412
  if (opts?.status)
359
413
  params.set("status", opts.status);
414
+ if (opts?.statuses)
415
+ params.set("statuses", opts.statuses);
360
416
  if (opts?.limit)
361
417
  params.set("limit", String(opts.limit));
362
418
  const qs = params.toString();
363
- const path = qs ? `/api/opportunities?${qs}` : "/api/opportunities";
419
+ const base = opts?.intentId ? `/api/intents/${encodeURIComponent(opts.intentId)}/opportunities` : "/api/opportunities";
420
+ const path = qs ? `${base}?${qs}` : base;
364
421
  const res = await this.get(path);
365
422
  const body = await res.json();
366
423
  return body.opportunities;
@@ -369,11 +426,12 @@ class ApiClient {
369
426
  const res = await this.get(`/api/opportunities/${id}`);
370
427
  return await res.json();
371
428
  }
372
- async updateOpportunityStatus(id, status, acknowledgedUptakeQuestionIds) {
373
- const res = await this.patch(`/api/opportunities/${id}/status`, {
374
- status,
375
- ...status === "accepted" && acknowledgedUptakeQuestionIds ? { acknowledgedUptakeQuestionIds } : {}
376
- });
429
+ async updateOpportunityStatus(id, status) {
430
+ const res = await this.patch(`/api/opportunities/${encodeURIComponent(id)}/status`, { status });
431
+ return await res.json();
432
+ }
433
+ async startOpportunityChat(id) {
434
+ const res = await this.post(`/api/opportunities/${encodeURIComponent(id)}/start-chat`, {});
377
435
  return await res.json();
378
436
  }
379
437
  async listIntents(options = {}) {
@@ -386,6 +444,8 @@ class ApiClient {
386
444
  body.sourceType = options.sourceType;
387
445
  if (options.page !== undefined)
388
446
  body.page = options.page;
447
+ if (options.query !== undefined)
448
+ body.q = options.query;
389
449
  const res = await this.post("/api/intents/list", body);
390
450
  return await res.json();
391
451
  }
@@ -394,17 +454,38 @@ class ApiClient {
394
454
  const body = await res.json();
395
455
  return body.intent;
396
456
  }
397
- async confirmIntent(proposalId, description, networkId) {
398
- const res = await this.post("/api/intents/confirm", {
399
- proposalId,
457
+ async createIntent(description, networkIds, preparationReceipt) {
458
+ const res = await this.post("/api/intents", {
400
459
  description,
401
- ...networkId ? { networkId } : {}
460
+ ...networkIds?.length ? { networkIds } : {},
461
+ ...preparationReceipt ? { preparationReceipt } : {}
402
462
  });
403
- const body = await res.json();
404
- return body;
463
+ return await res.json();
464
+ }
465
+ async prepareIntent(payload, answers = []) {
466
+ const res = await this.post("/api/intents/prepare", { payload, answers });
467
+ return await res.json();
468
+ }
469
+ async updateIntentStatus(id, status) {
470
+ await this.patch(`/api/intents/${encodeURIComponent(id)}/status`, { status });
405
471
  }
406
472
  async updateIntent(intentId, description) {
407
- return this.callTool("update_intent", { intentId, description });
473
+ const res = await this.patch(`/api/intents/${encodeURIComponent(intentId)}`, { description });
474
+ return await res.json();
475
+ }
476
+ async archiveIntent(intentId) {
477
+ await this.patch(`/api/intents/${encodeURIComponent(intentId)}/archive`);
478
+ }
479
+ async listIntentNetworks(intentId) {
480
+ const res = await this.get(`/api/intents/${encodeURIComponent(intentId)}/networks`);
481
+ const body = await res.json();
482
+ return body.networkIds;
483
+ }
484
+ async addIntentToNetwork(intentId, networkId) {
485
+ await this.post(`/api/intents/${encodeURIComponent(intentId)}/networks`, { networkId });
486
+ }
487
+ async removeIntentFromNetwork(intentId, networkId) {
488
+ await this.del(`/api/intents/${encodeURIComponent(intentId)}/networks/${encodeURIComponent(networkId)}`);
408
489
  }
409
490
  async listNetworks() {
410
491
  const res = await this.get("/api/networks");
@@ -414,14 +495,26 @@ class ApiClient {
414
495
  joinPolicy: n.joinPolicy ?? n.permissions?.joinPolicy
415
496
  }));
416
497
  }
498
+ async discoverNetworks() {
499
+ return await (await this.get("/api/networks/discovery/public?page=1&limit=50")).json();
500
+ }
501
+ async listNetworkRequests() {
502
+ return await (await this.get("/api/network-requests")).json();
503
+ }
504
+ async updateNetworkRequest(id, name, purpose) {
505
+ return await (await this.patch(`/api/network-requests/${encodeURIComponent(id)}`, { name, ...purpose ? { purpose } : {} })).json();
506
+ }
507
+ async dismissNetworkRequest(id) {
508
+ await this.del(`/api/network-requests/${encodeURIComponent(id)}`);
509
+ }
417
510
  async createNetworkOrRequest(title, prompt) {
418
511
  try {
419
- const res2 = await this.post("/api/networks", {
512
+ const res = await this.post("/api/networks", {
420
513
  title,
421
514
  ...prompt ? { prompt } : {}
422
515
  });
423
- const body2 = await res2.json();
424
- return { kind: "created", network: body2.network };
516
+ const body = await res.json();
517
+ return { kind: "created", network: body.network };
425
518
  } catch (error) {
426
519
  if (!isEarlyAccessNetworkCreationError(error))
427
520
  throw error;
@@ -452,6 +545,14 @@ class ApiClient {
452
545
  async leaveNetwork(id) {
453
546
  await this.post(`/api/networks/${id}/leave`, {});
454
547
  }
548
+ async updateNetwork(id, settings) {
549
+ const res = await this.put(`/api/networks/${id}`, settings);
550
+ const body = await res.json();
551
+ return body.network;
552
+ }
553
+ async deleteNetwork(id) {
554
+ await this.del(`/api/networks/${id}`);
555
+ }
455
556
  async inviteNetworkMember(networkId, email, name) {
456
557
  const res = await this.post(`/api/networks/${networkId}/members/invite`, {
457
558
  email,
@@ -481,18 +582,23 @@ class ApiClient {
481
582
  const body = await res.json();
482
583
  return body.messages;
483
584
  }
484
- async sendMessage(conversationId, text) {
585
+ async sendMessage(conversationId, text, intentId, questionId) {
485
586
  const res = await this.post(`/api/conversations/${conversationId}/messages`, {
486
- parts: [{ type: "text", text }]
587
+ parts: [{ kind: "text", text }],
588
+ ...intentId ? { metadata: { intentId } } : {},
589
+ ...questionId ? { questionId } : {}
487
590
  });
488
591
  const body = await res.json();
489
592
  return body.message;
490
593
  }
594
+ async answerAgentQuestions(intentId, answers) {
595
+ return await (await this.post("/api/conversations/agent/answers", { intentId, answers })).json();
596
+ }
491
597
  async hideConversation(conversationId) {
492
598
  await this.del(`/api/conversations/${conversationId}`);
493
599
  }
494
- async streamConversationEvents() {
495
- const res = await fetch(`${this.baseUrl}/api/conversations/stream`, {
600
+ async streamEvents() {
601
+ const res = await fetch(`${this.baseUrl}/api/events`, {
496
602
  headers: {
497
603
  ...this.authHeaders(),
498
604
  Accept: "text/event-stream"
@@ -504,16 +610,13 @@ class ApiClient {
504
610
  return res;
505
611
  }
506
612
  async listNegotiations(opts) {
507
- const me = await this.getMe();
508
613
  const params = new URLSearchParams;
509
- if (opts?.limit)
510
- params.set("limit", String(opts.limit));
511
- if (opts?.offset)
512
- params.set("offset", String(opts.offset));
513
- if (opts?.since)
514
- params.set("since", opts.since);
614
+ if (opts?.intentId)
615
+ params.set("intentId", opts.intentId);
616
+ if (opts?.state)
617
+ params.set("state", opts.state);
515
618
  const qs = params.toString();
516
- const path = qs ? `/api/users/${me.id}/negotiations?${qs}` : `/api/users/${me.id}/negotiations`;
619
+ const path = `/api/negotiations${qs ? `?${qs}` : ""}`;
517
620
  const res = await this.get(path);
518
621
  const body = await res.json();
519
622
  return body.negotiations;
@@ -522,18 +625,37 @@ class ApiClient {
522
625
  const res = await this.post("/api/enrichment/enrich", {});
523
626
  return await res.json();
524
627
  }
525
- async readUserContexts(query = {}) {
526
- return this.callTool("read_user_contexts", query);
628
+ async updateProfile(fields) {
629
+ const body = await (await this.patch("/api/auth/profile/update", fields)).json();
630
+ return body.user;
527
631
  }
528
- async createUserContext(query) {
529
- return this.callTool("create_user_context", query);
632
+ async scrapeUrl(url, objective) {
633
+ const res = await this.post("/api/scrape", { url, ...objective ? { objective } : {} });
634
+ return await res.json();
530
635
  }
531
- async updateUserContext(query) {
532
- return this.callTool("update_user_context", query);
636
+ async readDocs(topic) {
637
+ const path = topic ? `/api/docs?${new URLSearchParams({ topic })}` : "/api/docs";
638
+ return (await this.get(path)).json();
533
639
  }
534
- async callTool(toolName, query = {}) {
535
- const res = await this.post(`/api/tools/${toolName}`, { query });
536
- return await res.json();
640
+ async getAgent() {
641
+ return (await this.get("/api/agents/me")).json();
642
+ }
643
+ async getNegotiation(opportunityId) {
644
+ const body = await (await this.get(`/api/opportunities/${encodeURIComponent(opportunityId)}/negotiation`)).json();
645
+ return body.negotiation;
646
+ }
647
+ async submitNegotiationTurn(opportunityId, turn) {
648
+ const body = await (await this.post(`/api/opportunities/${encodeURIComponent(opportunityId)}/negotiation/turns`, turn)).json();
649
+ return body.negotiation;
650
+ }
651
+ async getAgentConversation(intentId) {
652
+ return (await this.get(`/api/conversations/agent/messages?${new URLSearchParams({ intentId })}`)).json();
653
+ }
654
+ async confirmProfile() {
655
+ return (await this.post("/api/auth/onboarding/confirm-profile", {})).json();
656
+ }
657
+ async completeOnboarding(intentId) {
658
+ return (await this.post("/api/auth/onboarding/complete", { intentId })).json();
537
659
  }
538
660
  async get(path) {
539
661
  const res = await fetch(`${this.baseUrl}${path}`, {
@@ -558,6 +680,19 @@ class ApiClient {
558
680
  }
559
681
  return res;
560
682
  }
683
+ async put(path, body) {
684
+ const res = await fetch(`${this.baseUrl}${path}`, {
685
+ method: "PUT",
686
+ headers: {
687
+ "Content-Type": "application/json",
688
+ ...this.authHeaders()
689
+ },
690
+ ...body !== undefined ? { body: JSON.stringify(body) } : {}
691
+ });
692
+ if (!res.ok)
693
+ await this.handleError(res);
694
+ return res;
695
+ }
561
696
  async patch(path, body) {
562
697
  const res = await fetch(`${this.baseUrl}${path}`, {
563
698
  method: "PATCH",
@@ -582,9 +717,6 @@ class ApiClient {
582
717
  return res;
583
718
  }
584
719
  async handleError(res) {
585
- if (res.status === 401) {
586
- throw new ApiError("Session expired or invalid. Run `index login` to re-authenticate.", res.status);
587
- }
588
720
  let message = `HTTP ${res.status}`;
589
721
  let response;
590
722
  try {
@@ -603,23 +735,33 @@ class ApiClient {
603
735
  import { randomBytes } from "node:crypto";
604
736
  import { createServer } from "node:http";
605
737
 
606
- // src/auth.lifecycle.ts
607
- async function storeReplacementCredentials(store, previous, replacement, options = {}) {
608
- await store.save(replacement);
609
- if (!previous)
610
- return {};
611
- if (replacement.keyId === previous.keyId) {
612
- return {};
613
- }
614
- const createClient = options.clientFactory ?? ((apiUrl, token) => new ApiClient(apiUrl, token));
738
+ // src/auth.session.ts
739
+ var DEVICE_CLIENT_ID = "index-device";
740
+ async function redeemDeviceCode(baseUrl, deviceCode) {
741
+ const response = await fetch(`${baseUrl}/api/auth/device/token`, {
742
+ method: "POST",
743
+ headers: { "Content-Type": "application/json", "User-Agent": "index-cli" },
744
+ body: JSON.stringify({
745
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
746
+ device_code: deviceCode,
747
+ client_id: DEVICE_CLIENT_ID
748
+ })
749
+ });
750
+ const body = await response.json().catch(() => null);
751
+ if (!response.ok || !body?.access_token) {
752
+ throw new Error(body?.error_description ?? "Could not complete device sign-in.");
753
+ }
754
+ return body.access_token;
755
+ }
756
+ async function revokeSession(baseUrl, token) {
615
757
  try {
616
- await createClient(replacement.apiUrl, replacement.token).revokeApiKey(previous.keyId, previous.token);
617
- return {};
618
- } catch (error) {
619
- const detail = error instanceof Error ? ` ${error.message}` : "";
620
- return {
621
- warning: `Login succeeded, but the previous CLI API key remains active because it could not be revoked.${detail} It should be removed in Index web settings.`
622
- };
758
+ const response = await fetch(`${baseUrl}/api/auth/sign-out`, {
759
+ method: "POST",
760
+ headers: { Authorization: `Bearer ${token}` }
761
+ });
762
+ return response.ok;
763
+ } catch {
764
+ return false;
623
765
  }
624
766
  }
625
767
 
@@ -676,33 +818,28 @@ async function handleLogin(apiUrl, appUrl, store, options = {}) {
676
818
  return;
677
819
  }
678
820
  stateConsumed = true;
679
- const apiKey = url.searchParams.get("api_key");
680
- const keyId = url.searchParams.get("key_id");
681
- if (apiKey && keyId) {
821
+ const deviceCode = url.searchParams.get("device_code");
822
+ if (deviceCode) {
682
823
  try {
683
- const cleanup = await storeReplacementCredentials(store, previousCredentials, {
684
- token: apiKey,
685
- apiUrl: baseUrl,
686
- authKind: "api_key",
687
- keyId
688
- }, { clientFactory: options.credentialClientFactory });
689
- resolveCallback({ success: true, ...cleanup });
690
- } catch {
691
- resolveCallback({ success: false, error: "Failed to save CLI credentials." });
824
+ const token = await redeemDeviceCode(baseUrl, deviceCode);
825
+ await store.save({ token, apiUrl: baseUrl, authKind: "session" });
826
+ if (previousCredentials)
827
+ await revokeSession(previousCredentials.apiUrl, previousCredentials.token);
828
+ resolveCallback({ success: true });
829
+ } catch (error) {
830
+ const message = error instanceof Error ? error.message : "Failed to save CLI credentials.";
831
+ resolveCallback({ success: false, error: message });
692
832
  res.writeHead(500, { "Content-Type": "text/html" });
693
- res.end(callbackHtml("Authorization failed", "CLI credentials could not be saved. Return to the terminal and try again."));
833
+ res.end(callbackHtml("Authorization failed", "CLI sign-in could not be completed. Return to the terminal and try again."));
694
834
  return;
695
835
  }
696
836
  res.writeHead(200, { "Content-Type": "text/html" });
697
- res.end(callbackHtml("CLI authorized", "You can close this window and return to the terminal.", true));
837
+ res.end(callbackHtml("Authentication complete", "You can close this window and return to the terminal.", true));
698
838
  return;
699
839
  }
700
- resolveCallback({
701
- success: false,
702
- error: apiKey ? "CLI API-key callback did not include its key ID." : "No CLI credential received in callback."
703
- });
840
+ resolveCallback({ success: false, error: "No device code received in callback." });
704
841
  res.writeHead(400, { "Content-Type": "text/html" });
705
- res.end(callbackHtml("Authorization failed", "Incomplete CLI credentials received. Please try again."));
842
+ res.end(callbackHtml("Authorization failed", "Incomplete sign-in received. Please try again."));
706
843
  });
707
844
  let listenSettled = false;
708
845
  let rejectListen = null;
@@ -813,168 +950,64 @@ p{font-size:14px;font-weight:500;margin:0;color:rgba(244,251,246,0.78)}
813
950
  }
814
951
 
815
952
  // src/logout.command.ts
816
- async function handleLogout(store, options = {}) {
953
+ async function handleLogout(store) {
817
954
  const credentials = await store.load();
818
955
  if (!credentials) {
819
956
  return { success: true, message: "Already logged out." };
820
957
  }
821
- const createClient = options.clientFactory ?? ((apiUrl, token) => new ApiClient(apiUrl, token));
822
- try {
823
- await createClient(credentials.apiUrl, credentials.token).revokeApiKey(credentials.keyId, credentials.token);
824
- } catch (error) {
825
- const detail = error instanceof Error ? ` ${error.message}` : "";
826
- return {
827
- success: false,
828
- warning: `Logout could not revoke the stored CLI API key, so credentials were retained.${detail}`
829
- };
830
- }
958
+ const revoked = await revokeSession(credentials.apiUrl, credentials.token);
831
959
  try {
832
960
  await store.clear();
833
961
  } catch {
834
962
  return {
835
963
  success: false,
836
- warning: "The server API key was revoked, but local credential cleanup failed. Remove the local credentials file manually before signing in again."
964
+ warning: "Local credential cleanup failed. Remove the local credentials file manually before signing in again."
837
965
  };
838
966
  }
839
- return { success: true, message: "Logged out. CLI API key revoked." };
967
+ return {
968
+ success: true,
969
+ message: revoked ? "Logged out." : "Logged out locally. The session could not be reached — revoke it in Index web settings if this machine is not yours."
970
+ };
840
971
  }
841
972
 
842
- // src/output/index.ts
843
- var exports_output = {};
844
- __export(exports_output, {
845
- wordWrap: () => wordWrap,
846
- warn: () => warn,
847
- toolActivity: () => toolActivity,
848
- success: () => success,
849
- stripAnsi: () => stripAnsi,
850
- status: () => status,
851
- raw: () => raw,
852
- profileCard: () => profileCard,
853
- padTo: () => padTo,
854
- opportunityTable: () => opportunityTable,
855
- opportunityCard: () => opportunityCard,
856
- networkTable: () => networkTable,
857
- networkCard: () => networkCard,
858
- messageList: () => messageList,
859
- memberTable: () => memberTable,
860
- intentTable: () => intentTable,
861
- intentCard: () => intentCard,
862
- info: () => info,
863
- humanizeToolName: () => humanizeToolName,
864
- heading: () => heading,
865
- error: () => error,
866
- dim: () => dim,
867
- conversationTable: () => conversationTable,
868
- conversationCard: () => conversationCard,
869
- contactTable: () => contactTable,
870
- confidenceBar: () => confidenceBar,
871
- clearStatus: () => clearStatus,
872
- chatHeader: () => chatHeader,
873
- YELLOW: () => YELLOW,
874
- WHITE: () => WHITE,
875
- USER_PROMPT: () => USER_PROMPT,
876
- RESET: () => RESET,
877
- RED: () => RED,
878
- PROMPT_STR: () => PROMPT_STR,
879
- ORANGE: () => ORANGE,
880
- MarkdownRenderer: () => MarkdownRenderer,
881
- MAGENTA: () => MAGENTA,
882
- ITALIC: () => ITALIC,
883
- GREEN: () => GREEN,
884
- GRAY: () => GRAY,
885
- DIM: () => DIM,
886
- CYAN: () => CYAN,
887
- BOLD: () => BOLD,
888
- BLUE: () => BLUE,
889
- AGENT_TEXT: () => AGENT_TEXT
890
- });
891
-
892
973
  // src/output/base.ts
893
974
  var RESET = "\x1B[0m";
894
975
  var BOLD = "\x1B[1m";
895
976
  var DIM = "\x1B[2m";
896
- var ITALIC = "\x1B[3m";
897
977
  var RED = "\x1B[31m";
898
978
  var GREEN = "\x1B[32m";
899
979
  var YELLOW = "\x1B[33m";
900
980
  var BLUE = "\x1B[34m";
901
- var MAGENTA = "\x1B[35m";
902
981
  var CYAN = "\x1B[36m";
903
982
  var WHITE = "\x1B[37m";
904
983
  var GRAY = "\x1B[90m";
905
- var ORANGE = "\x1B[38;5;208m";
906
984
  var AGENT_TEXT = "\x1B[38;5;252m";
907
985
  var USER_PROMPT = CYAN;
908
986
  function error(message, exitCode) {
987
+ if (process.argv.includes("--json"))
988
+ throw new Error(message);
909
989
  console.error(`${RED}${BOLD}error${RESET}${RED}: ${message}${RESET}`);
910
- if (exitCode !== undefined) {
990
+ process.exitCode = exitCode ?? 1;
991
+ if (exitCode !== undefined)
911
992
  process.exit(exitCode);
912
- }
913
993
  }
914
994
  function success(message) {
915
995
  console.log(`${GREEN}${BOLD}+${RESET} ${message}`);
916
996
  }
917
997
  function info(message) {
918
- console.log(`${CYAN}${message}${RESET}`);
998
+ console.error(`${CYAN}${message}${RESET}`);
919
999
  }
920
1000
  function warn(message) {
921
1001
  console.warn(`${YELLOW}${message}${RESET}`);
922
1002
  }
923
1003
  function dim(message) {
924
- console.log(`${GRAY}${message}${RESET}`);
1004
+ console.error(`${GRAY}${message}${RESET}`);
925
1005
  }
926
1006
  function heading(message) {
927
1007
  console.log(`
928
1008
  ${BOLD}${message}${RESET}`);
929
1009
  }
930
- function chatHeader() {
931
- console.log();
932
- console.log(` ${BOLD}${CYAN}I N D E X${RESET} ${GRAY}chat${RESET}`);
933
- console.log(` ${GRAY}─────────────────────────────────────${RESET}`);
934
- console.log(` ${GRAY}Type your message. "exit" or Ctrl+C to quit.${RESET}`);
935
- console.log();
936
- }
937
1010
  var PROMPT_STR = `${USER_PROMPT}${BOLD}> ${RESET}`;
938
- function raw(text) {
939
- process.stdout.write(text);
940
- }
941
- function status(message) {
942
- process.stderr.write(`\r ${GRAY}${message}${RESET}\x1B[K`);
943
- }
944
- function clearStatus() {
945
- process.stderr.write("\r\x1B[K");
946
- }
947
- function toolActivity(description) {
948
- process.stderr.write(`\r\x1B[K${ORANGE}> ${description}${RESET}
949
- `);
950
- }
951
- var TOOL_DESCRIPTIONS = {
952
- read_user_contexts: "Reading your profile...",
953
- create_user_context: "Creating your profile...",
954
- update_user_context: "Updating your profile...",
955
- read_intents: "Fetching your active signals...",
956
- create_intent: "Creating a new signal...",
957
- update_intent: "Updating signal...",
958
- delete_intent: "Removing signal...",
959
- create_intent_index: "Saving signal to index...",
960
- read_intent_indexes: "Fetching signals in index...",
961
- delete_intent_index: "Removing signal from index...",
962
- read_networks: "Checking your communities...",
963
- create_network: "Creating a new community...",
964
- update_network: "Updating community...",
965
- delete_network: "Deleting community...",
966
- create_network_membership: "Adding member to community...",
967
- read_network_memberships: "Fetching community memberships...",
968
- list_opportunities: "Listing your opportunities...",
969
- update_opportunity: "Updating opportunity status...",
970
- scrape_url: "Reading content from URL...",
971
- read_docs: "Looking up documentation...",
972
- list_contacts: "Listing your contacts...",
973
- remove_contact: "Removing contact..."
974
- };
975
- function humanizeToolName(name) {
976
- return TOOL_DESCRIPTIONS[name] ?? name.replace(/_/g, " ") + "...";
977
- }
978
1011
  function wordWrap(text, maxWidth) {
979
1012
  const lines = [];
980
1013
  const words = text.split(/\s+/);
@@ -1005,220 +1038,6 @@ function padTo(cellWidth, plainText) {
1005
1038
  function stripAnsi(str) {
1006
1039
  return str.replace(/\x1b\[[0-9;]*m/g, "");
1007
1040
  }
1008
- // src/output/markdown.ts
1009
- class MarkdownRenderer {
1010
- buffer = "";
1011
- inCodeBlock = false;
1012
- codeBlockLang = "";
1013
- codeBlockContent = "";
1014
- pristine = true;
1015
- write(text) {
1016
- this.buffer += text;
1017
- this.flush();
1018
- }
1019
- flush() {
1020
- while (this.buffer.length > 0) {
1021
- if (this.inCodeBlock) {
1022
- const endIdx = this.buffer.indexOf("```");
1023
- if (endIdx !== -1) {
1024
- this.codeBlockContent += this.buffer.slice(0, endIdx);
1025
- this.buffer = this.buffer.slice(endIdx + 3);
1026
- if (this.buffer.startsWith(`
1027
- `)) {
1028
- this.buffer = this.buffer.slice(1);
1029
- }
1030
- this.renderCodeBlock();
1031
- this.inCodeBlock = false;
1032
- this.codeBlockLang = "";
1033
- this.codeBlockContent = "";
1034
- continue;
1035
- }
1036
- this.codeBlockContent += this.buffer;
1037
- this.buffer = "";
1038
- return;
1039
- }
1040
- const codeBlockMatch = this.buffer.match(/^```([^\n]*)\n/);
1041
- if (codeBlockMatch) {
1042
- this.codeBlockLang = codeBlockMatch[1].trim();
1043
- this.buffer = this.buffer.slice(codeBlockMatch[0].length);
1044
- this.inCodeBlock = true;
1045
- this.codeBlockContent = "";
1046
- continue;
1047
- }
1048
- if (this.buffer.startsWith("```")) {
1049
- return;
1050
- }
1051
- const nlIdx = this.buffer.indexOf(`
1052
- `);
1053
- if (nlIdx === -1) {
1054
- return;
1055
- }
1056
- const line = this.buffer.slice(0, nlIdx);
1057
- this.buffer = this.buffer.slice(nlIdx + 1);
1058
- this.emitLine(line);
1059
- }
1060
- }
1061
- emitLine(line) {
1062
- if (this.pristine) {
1063
- process.stdout.write(`
1064
- `);
1065
- this.pristine = false;
1066
- }
1067
- const rendered = this.renderLine(line);
1068
- process.stdout.write(rendered + `
1069
- `);
1070
- }
1071
- renderLine(line) {
1072
- const bulletMatch = line.match(/^(\s*)([-*])\s+(.*)$/);
1073
- if (bulletMatch) {
1074
- const [, indent, , content] = bulletMatch;
1075
- return `${indent} ${CYAN}*${RESET} ${this.renderInline(content)}`;
1076
- }
1077
- const numMatch = line.match(/^(\s*)(\d+)\.\s+(.*)$/);
1078
- if (numMatch) {
1079
- const [, indent, num, content] = numMatch;
1080
- return `${indent} ${CYAN}${num}.${RESET} ${this.renderInline(content)}`;
1081
- }
1082
- if (line.startsWith("### ")) {
1083
- return `${BOLD}${WHITE}${line.slice(4)}${RESET}`;
1084
- }
1085
- if (line.startsWith("## ")) {
1086
- return `${BOLD}${WHITE}${line.slice(3)}${RESET}`;
1087
- }
1088
- if (line.startsWith("# ")) {
1089
- return `${BOLD}${WHITE}${line.slice(2)}${RESET}`;
1090
- }
1091
- return this.renderInline(line);
1092
- }
1093
- renderInline(text) {
1094
- text = text.replace(/\*\*\*(.+?)\*\*\*/g, `${BOLD}${ITALIC}${WHITE}$1${RESET}${AGENT_TEXT}`);
1095
- text = text.replace(/\*\*(.+?)\*\*/g, `${BOLD}${WHITE}$1${RESET}${AGENT_TEXT}`);
1096
- text = text.replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g, `${ITALIC}$1${RESET}${AGENT_TEXT}`);
1097
- text = text.replace(/`([^`]+)`/g, `${CYAN}$1${RESET}${AGENT_TEXT}`);
1098
- return `${AGENT_TEXT}${text}${RESET}`;
1099
- }
1100
- renderCodeBlock() {
1101
- const lang = this.codeBlockLang;
1102
- const content = this.codeBlockContent.trimEnd();
1103
- if (!this.pristine) {
1104
- process.stdout.write(`
1105
- `);
1106
- }
1107
- this.pristine = false;
1108
- if (lang === "intent_proposal") {
1109
- this.renderIntentProposal(content);
1110
- return;
1111
- }
1112
- if (lang === "opportunity") {
1113
- this.renderOpportunity(content);
1114
- return;
1115
- }
1116
- const border = `${GRAY}|${RESET}`;
1117
- if (lang) {
1118
- process.stdout.write(` ${GRAY}--- ${lang} ${"─".repeat(Math.max(0, 40 - lang.length))}${RESET}
1119
- `);
1120
- } else {
1121
- process.stdout.write(` ${GRAY}${"─".repeat(46)}${RESET}
1122
- `);
1123
- }
1124
- for (const line of content.split(`
1125
- `)) {
1126
- process.stdout.write(` ${border} ${CYAN}${line}${RESET}
1127
- `);
1128
- }
1129
- process.stdout.write(` ${GRAY}${"─".repeat(46)}${RESET}
1130
-
1131
- `);
1132
- }
1133
- renderIntentProposal(content) {
1134
- try {
1135
- const data = JSON.parse(content);
1136
- const desc = data.description ?? content;
1137
- const confidence = data.confidence;
1138
- process.stdout.write(` ${MAGENTA}+${"─".repeat(56)}+${RESET}
1139
- `);
1140
- process.stdout.write(` ${MAGENTA}|${RESET} ${BOLD}${MAGENTA}Signal Proposal${RESET}${" ".repeat(40)}${MAGENTA}|${RESET}
1141
- `);
1142
- process.stdout.write(` ${MAGENTA}|${RESET}${" ".repeat(56)}${MAGENTA}|${RESET}
1143
- `);
1144
- const wrapped = wordWrap(desc, 52);
1145
- for (const line of wrapped) {
1146
- const pad = Math.max(0, 54 - line.length);
1147
- process.stdout.write(` ${MAGENTA}|${RESET} ${AGENT_TEXT}${line}${RESET}${" ".repeat(pad)}${MAGENTA}|${RESET}
1148
- `);
1149
- }
1150
- if (confidence !== undefined) {
1151
- process.stdout.write(` ${MAGENTA}|${RESET}${" ".repeat(56)}${MAGENTA}|${RESET}
1152
- `);
1153
- const bar = confidenceBar(confidence);
1154
- process.stdout.write(` ${MAGENTA}|${RESET} ${GRAY}Confidence: ${bar}${RESET}${" ".repeat(24)}${MAGENTA}|${RESET}
1155
- `);
1156
- }
1157
- process.stdout.write(` ${MAGENTA}+${"─".repeat(56)}+${RESET}
1158
-
1159
- `);
1160
- } catch {
1161
- process.stdout.write(` ${GRAY}${content}${RESET}
1162
-
1163
- `);
1164
- }
1165
- }
1166
- renderOpportunity(content) {
1167
- try {
1168
- const data = JSON.parse(content);
1169
- process.stdout.write(` ${BLUE}+${"─".repeat(56)}+${RESET}
1170
- `);
1171
- process.stdout.write(` ${BLUE}|${RESET} ${BOLD}${BLUE}Opportunity${RESET}${" ".repeat(44)}${BLUE}|${RESET}
1172
- `);
1173
- if (data.title) {
1174
- const pad = Math.max(0, 54 - data.title.length);
1175
- process.stdout.write(` ${BLUE}|${RESET} ${BOLD}${data.title}${RESET}${" ".repeat(pad)}${BLUE}|${RESET}
1176
- `);
1177
- }
1178
- if (data.description) {
1179
- process.stdout.write(` ${BLUE}|${RESET}${" ".repeat(56)}${BLUE}|${RESET}
1180
- `);
1181
- const wrapped = wordWrap(data.description, 52);
1182
- for (const line of wrapped) {
1183
- const pad = Math.max(0, 54 - line.length);
1184
- process.stdout.write(` ${BLUE}|${RESET} ${AGENT_TEXT}${line}${RESET}${" ".repeat(pad)}${BLUE}|${RESET}
1185
- `);
1186
- }
1187
- }
1188
- process.stdout.write(` ${BLUE}+${"─".repeat(56)}+${RESET}
1189
-
1190
- `);
1191
- } catch {
1192
- process.stdout.write(` ${GRAY}${content}${RESET}
1193
-
1194
- `);
1195
- }
1196
- }
1197
- reset(reason) {
1198
- if (this.buffer.length > 0 || this.inCodeBlock) {
1199
- this.buffer = "";
1200
- this.inCodeBlock = false;
1201
- this.codeBlockContent = "";
1202
- this.codeBlockLang = "";
1203
- }
1204
- if (!this.pristine) {
1205
- process.stdout.write(`
1206
- ${GRAY}--- retrying${reason ? `: ${reason}` : ""} ---${RESET}
1207
- `);
1208
- }
1209
- this.pristine = true;
1210
- }
1211
- finalize() {
1212
- if (this.inCodeBlock) {
1213
- this.renderCodeBlock();
1214
- this.inCodeBlock = false;
1215
- }
1216
- if (this.buffer.length > 0) {
1217
- this.emitLine(this.buffer);
1218
- this.buffer = "";
1219
- }
1220
- }
1221
- }
1222
1041
  // src/output/formatters.ts
1223
1042
  function profileCard(data) {
1224
1043
  const lines = [];
@@ -1228,9 +1047,8 @@ function profileCard(data) {
1228
1047
  lines.push("");
1229
1048
  lines.push(hline);
1230
1049
  const displayName = data.name ?? "(unnamed)";
1231
- const ghostTag = data.isGhost ? ` ${YELLOW}[ghost]${RESET}` : "";
1232
- const nameContent = `${BOLD}${WHITE}${displayName}${RESET}${ghostTag}`;
1233
- lines.push(` ${border("|")} ${nameContent}${padTo(W - 2, stripAnsi(displayName + (data.isGhost ? " [ghost]" : "")))}${border("|")}`);
1050
+ const nameContent = `${BOLD}${WHITE}${displayName}${RESET}`;
1051
+ lines.push(` ${border("|")} ${nameContent}${padTo(W - 2, stripAnsi(displayName))}${border("|")}`);
1234
1052
  if (data.intro) {
1235
1053
  lines.push(` ${border("|")}${" ".repeat(W)}${border("|")}`);
1236
1054
  const wrapped = wordWrap(data.intro, W - 4);
@@ -1265,19 +1083,6 @@ function profileCard(data) {
1265
1083
  console.log(output);
1266
1084
  return output;
1267
1085
  }
1268
- function contactTable(contacts) {
1269
- if (contacts.length === 0) {
1270
- console.log(" No contacts yet.");
1271
- return;
1272
- }
1273
- const nameWidth = Math.max(6, ...contacts.map((c) => c.name.length));
1274
- const header = ` ${"Name".padEnd(nameWidth)} Email`;
1275
- console.log(`${BOLD}${header}${RESET}`);
1276
- for (const c of contacts) {
1277
- const ghost = c.isGhost ? ` ${DIM}(ghost)${RESET}` : "";
1278
- console.log(` ${c.name.padEnd(nameWidth)} ${c.email}${ghost}`);
1279
- }
1280
- }
1281
1086
  function intentTable(intents) {
1282
1087
  if (intents.length === 0) {
1283
1088
  dim(" No signals found.");
@@ -1293,7 +1098,7 @@ function intentTable(intents) {
1293
1098
  for (const intent of intents) {
1294
1099
  const shortId = intent.id.slice(0, 8);
1295
1100
  const desc = (intent.summary ?? intent.payload).slice(0, descWidth);
1296
- const status2 = (intent.status ?? "").padEnd(statusWidth);
1101
+ const status = (intent.status ?? "").padEnd(statusWidth);
1297
1102
  const source = (intent.sourceType ?? "-").padEnd(sourceWidth);
1298
1103
  const date = new Date(intent.createdAt).toLocaleDateString("en-US", {
1299
1104
  year: "numeric",
@@ -1301,7 +1106,7 @@ function intentTable(intents) {
1301
1106
  day: "numeric"
1302
1107
  });
1303
1108
  const sColor = intent.status === "ACTIVE" ? GREEN : GRAY;
1304
- console.log(` ${CYAN}${shortId}${RESET} ${desc.padEnd(descWidth)} ${sColor}${status2}${RESET} ${GRAY}${source}${RESET} ${GRAY}${date}${RESET}`);
1109
+ console.log(` ${CYAN}${shortId}${RESET} ${desc.padEnd(descWidth)} ${sColor}${status}${RESET} ${GRAY}${source}${RESET} ${GRAY}${date}${RESET}`);
1305
1110
  }
1306
1111
  }
1307
1112
  function intentCard(intent) {
@@ -1341,10 +1146,10 @@ function intentCard(intent) {
1341
1146
  if (intent.archivedAt) {
1342
1147
  console.log(` ${BOLD}Archived${RESET} ${GRAY}${new Date(intent.archivedAt).toLocaleString()}${RESET}`);
1343
1148
  }
1344
- if (intent.indexes && intent.indexes.length > 0) {
1149
+ if (intent.networks && intent.networks.length > 0) {
1345
1150
  console.log();
1346
- console.log(` ${BOLD}Index Assignments${RESET}`);
1347
- for (const idx of intent.indexes) {
1151
+ console.log(` ${BOLD}Networks${RESET}`);
1152
+ for (const idx of intent.networks) {
1348
1153
  const score = idx.relevancyScore !== undefined ? ` (${idx.relevancyScore.toFixed(2)})` : "";
1349
1154
  console.log(` ${CYAN}*${RESET} ${idx.title}${GRAY}${score}${RESET}`);
1350
1155
  }
@@ -1370,35 +1175,25 @@ function opportunityTable(opportunities) {
1370
1175
  }
1371
1176
  const idW = 8;
1372
1177
  const nameW = 20;
1373
- const catW = 18;
1374
- const statusW = 10;
1375
- const confW = 8;
1376
- const dateW = 20;
1377
- process.stdout.write(` ${BOLD}${"ID".padEnd(idW)} ${"Counterparty".padEnd(nameW)} ${"Category".padEnd(catW)} ${"Status".padEnd(statusW)} ${"Conf".padEnd(confW)} ${"Created".padEnd(dateW)}${RESET}
1178
+ const statusW = 12;
1179
+ const headlineW = 48;
1180
+ process.stdout.write(` ${BOLD}${"ID".padEnd(idW)} ${"Counterparty".padEnd(nameW)} ${"Status".padEnd(statusW)} Headline${RESET}
1378
1181
  `);
1379
- process.stdout.write(` ${GRAY}${"-".repeat(idW)} ${"-".repeat(nameW)} ${"-".repeat(catW)} ${"-".repeat(statusW)} ${"-".repeat(confW)} ${"-".repeat(dateW)}${RESET}
1182
+ process.stdout.write(` ${GRAY}${"-".repeat(idW)} ${"-".repeat(nameW)} ${"-".repeat(statusW)} ${"-".repeat(headlineW)}${RESET}
1380
1183
  `);
1381
1184
  for (const opp of opportunities) {
1382
- const shortId = opp.id.slice(0, 8);
1383
- const fallbackName = opp.actors?.[1]?.name ?? opp.actors?.find((a) => a.name)?.name;
1384
- const name = (opp.counterpartName ?? fallbackName ?? "Unknown").slice(0, nameW);
1385
- const category = (opp.interpretation?.category ?? "-").slice(0, catW);
1185
+ const shortId = opp.opportunityId.slice(0, idW);
1186
+ const name = opp.peer.name.slice(0, nameW);
1386
1187
  const st = opp.status.slice(0, statusW);
1387
- const rawConf = opp.interpretation?.confidence;
1388
- const conf = rawConf != null ? `${Math.round(rawConf <= 1 ? rawConf * 100 : rawConf)}%` : "-";
1389
- const date = opp.createdAt ? new Date(opp.createdAt).toLocaleDateString("en-US", {
1390
- year: "numeric",
1391
- month: "short",
1392
- day: "numeric"
1393
- }) : "-";
1394
- process.stdout.write(` ${CYAN}${shortId}${RESET} ${name.padEnd(nameW)} ${GRAY}${category.padEnd(catW)}${RESET} ${statusColor(st)}${st.padEnd(statusW)}${RESET} ${GRAY}${conf.padEnd(confW)}${RESET} ${GRAY}${date.padEnd(dateW)}${RESET}
1188
+ const headline = (opp.headline ?? opp.mainText).slice(0, headlineW);
1189
+ process.stdout.write(` ${CYAN}${shortId}${RESET} ${name.padEnd(nameW)} ${statusColor(st)}${st.padEnd(statusW)}${RESET} ${headline}
1395
1190
  `);
1396
1191
  }
1397
1192
  }
1398
1193
  function opportunityCard(opp) {
1399
1194
  const width = 58;
1400
1195
  const innerWidth = width - 2;
1401
- const title = opp.presentation?.title ?? "Opportunity";
1196
+ const title = opp.headline ?? "Opportunity";
1402
1197
  process.stdout.write(`
1403
1198
  ${BLUE}+${"─".repeat(width)}+${RESET}
1404
1199
  `);
@@ -1409,13 +1204,15 @@ function opportunityCard(opp) {
1409
1204
  cardLine(`${BOLD}Status:${RESET} ${statusColor(st)}${st}${RESET}`);
1410
1205
  if (opp.category)
1411
1206
  cardLine(`${BOLD}Category:${RESET} ${opp.category}`);
1412
- if (opp.index?.title)
1413
- cardLine(`${BOLD}Network:${RESET} ${opp.index.title}`);
1207
+ if (opp.network?.title)
1208
+ cardLine(`${BOLD}Network:${RESET} ${opp.network.title}`);
1414
1209
  if (opp.confidence != null)
1415
1210
  cardLine(`${BOLD}Confidence:${RESET} ${confidenceBar(opp.confidence)}`);
1416
1211
  if (opp.myRole)
1417
1212
  cardLine(`${BOLD}Your role:${RESET} ${roleLabel(opp.myRole)}`);
1418
- if (opp.otherParties && opp.otherParties.length > 0) {
1213
+ if (opp.peer?.name)
1214
+ cardLine(`${BOLD}With:${RESET} ${opp.peer.name}`);
1215
+ else if (opp.otherParties && opp.otherParties.length > 0) {
1419
1216
  process.stdout.write(` ${BLUE}|${RESET}
1420
1217
  `);
1421
1218
  cardLine(`${BOLD}With:${RESET}`);
@@ -1423,11 +1220,11 @@ function opportunityCard(opp) {
1423
1220
  cardLine(` ${p.name ?? p.id} ${roleLabel(p.role)}`);
1424
1221
  }
1425
1222
  }
1426
- if (opp.presentation?.description) {
1223
+ if (opp.mainText) {
1427
1224
  process.stdout.write(` ${BLUE}|${RESET}
1428
1225
  `);
1429
1226
  cardLine(`${BOLD}Details:${RESET}`);
1430
- for (const line of wordWrap(opp.presentation.description, innerWidth - 4)) {
1227
+ for (const line of wordWrap(opp.mainText, innerWidth - 4)) {
1431
1228
  cardLine(` ${AGENT_TEXT}${line}${RESET}`);
1432
1229
  }
1433
1230
  }
@@ -1590,7 +1387,7 @@ function messageList(messages) {
1590
1387
  month: "short",
1591
1388
  day: "numeric"
1592
1389
  });
1593
- const textParts = msg.parts.filter((p) => p.type === "text" && p.text).map((p) => p.text).join("");
1390
+ const textParts = msg.parts.filter((p) => p.kind === "text" && p.text).map((p) => p.text).join("");
1594
1391
  console.log(` ${CYAN}${sender}${RESET} ${GRAY}${date} ${time}${RESET}`);
1595
1392
  console.log(` ${textParts}`);
1596
1393
  console.log();
@@ -1598,17 +1395,20 @@ function messageList(messages) {
1598
1395
  }
1599
1396
  // src/profile.command.ts
1600
1397
  async function handleProfile(client, subcommand, positionals, options = {}) {
1601
- if (subcommand === "create") {
1602
- await profileCreate(client, options, options?.json);
1603
- return;
1604
- }
1605
1398
  if (subcommand === "update") {
1606
- const action = positionals.join(" ");
1607
- if (!action) {
1608
- error("Usage: index profile update <action> [--details <text>]", 1);
1609
- return;
1399
+ const { name, intro, location, socials } = options;
1400
+ if (name === undefined && intro === undefined && location === undefined && socials === undefined) {
1401
+ throw new Error("Usage: index profile update [--name <text>] [--intro <text>] [--location <text>] [--social label=value]");
1610
1402
  }
1611
- await profileUpdate(client, action, options.details, options?.json);
1403
+ const user = await client.updateProfile({ name, intro, location, socials });
1404
+ if (options.json)
1405
+ console.log(JSON.stringify(user));
1406
+ else
1407
+ success("Profile updated.");
1408
+ return;
1409
+ }
1410
+ if (subcommand === "create" || subcommand === "search") {
1411
+ error(`profile ${subcommand} is not available; use "index profile sync" for public research prefill`, 1);
1612
1412
  return;
1613
1413
  }
1614
1414
  if (subcommand === "sync") {
@@ -1624,35 +1424,6 @@ async function handleProfile(client, subcommand, positionals, options = {}) {
1624
1424
  await profileShow(client, userId, options?.json);
1625
1425
  return;
1626
1426
  }
1627
- if (subcommand === "search") {
1628
- const query = positionals.join(" ");
1629
- if (!query) {
1630
- error("Usage: index profile search <query>", 1);
1631
- return;
1632
- }
1633
- const result = await client.readUserContexts({ query });
1634
- if (options.json) {
1635
- console.log(JSON.stringify(result));
1636
- return;
1637
- }
1638
- if (!result.success) {
1639
- error(result.error ?? "Search failed", 1);
1640
- return;
1641
- }
1642
- const data = result.data;
1643
- heading("Search Results");
1644
- if (!data.profiles?.length) {
1645
- dim(" No profiles found.");
1646
- } else {
1647
- for (const p of data.profiles) {
1648
- console.log(` ${p.name} (${p.userId.slice(0, 8)})`);
1649
- if (p.bio)
1650
- dim(` ${p.bio.slice(0, 100)}`);
1651
- }
1652
- }
1653
- console.log();
1654
- return;
1655
- }
1656
1427
  await profileMe(client, options?.json);
1657
1428
  }
1658
1429
  async function profileMe(client, json) {
@@ -1687,61 +1458,26 @@ async function profileSync(client, json) {
1687
1458
  return;
1688
1459
  }
1689
1460
  success("Profile enriched.");
1690
- if (result.profile.name)
1461
+ if (result.profile?.name)
1691
1462
  dim(` Name: ${result.profile.name}`);
1692
- if (result.profile.location)
1463
+ if (result.profile?.location)
1693
1464
  dim(` Location: ${result.profile.location}`);
1694
- dim(` Social links: ${result.profile.socials.length}`);
1695
- }
1696
- async function profileCreate(client, options, json) {
1697
- if (!json)
1698
- info("Creating profile...");
1699
- const query = { confirm: true };
1700
- if (options.linkedin)
1701
- query.linkedinUrl = options.linkedin;
1702
- if (options.github)
1703
- query.githubUrl = options.github;
1704
- if (options.twitter)
1705
- query.twitterUrl = options.twitter;
1706
- const result = await client.createUserContext(query);
1707
- if (json) {
1708
- console.log(JSON.stringify(result));
1709
- return;
1710
- }
1711
- if (!result.success) {
1712
- error(result.error ?? "Profile creation failed", 1);
1713
- return;
1714
- }
1715
- success("Profile created.");
1716
- }
1717
- async function profileUpdate(client, action, details, json) {
1718
- if (!json)
1719
- info("Updating profile...");
1720
- const query = { action };
1721
- if (details)
1722
- query.details = details;
1723
- const result = await client.updateUserContext(query);
1724
- if (json) {
1725
- console.log(JSON.stringify(result));
1726
- return;
1727
- }
1728
- if (!result.success) {
1729
- error(result.error ?? "Profile update failed", 1);
1730
- return;
1731
- }
1732
- success("Profile updated.");
1465
+ dim(` Social links: ${result.profile?.socials?.length ?? 0}`);
1733
1466
  }
1734
1467
 
1735
1468
  // src/intent.command.ts
1736
1469
  var INTENT_HELP = `
1737
1470
  Usage:
1738
- index intent list [--archived] [--limit <n>] List your signals
1471
+ index intent list [--archived] [--limit <n>] [--query <text>] List your signals
1739
1472
  index intent show <id> Show signal details (accepts short ID)
1740
- index intent create <content> Create a signal from text
1473
+ index intent prepare <content> [--answer 'prompt=reply'] Review and repair a draft
1474
+ index intent create <content> [--receipt <token>] Create an admitted signal
1475
+ index intent pause <id> | resume <id> Hold or restart its agent
1741
1476
  index intent update <id> <content> Update a signal's description
1742
1477
  index intent archive <id> Archive a signal (accepts short ID)
1743
- index intent link <id> <network-id> Link a signal to a network
1744
- index intent unlink <id> <network-id> Unlink a signal from a network
1478
+ index intent networks <id> List the networks a signal is shared in
1479
+ index intent add-to-network <id> <network-id> Add a signal to a network
1480
+ index intent remove-from-network <id> <network-id> Remove a signal from a network
1745
1481
  `;
1746
1482
  async function handleIntent(client, subcommand, options) {
1747
1483
  if (!subcommand) {
@@ -1756,7 +1492,8 @@ async function handleIntent(client, subcommand, options) {
1756
1492
  case "list": {
1757
1493
  const result = await client.listIntents({
1758
1494
  archived: options.archived,
1759
- limit: options.limit
1495
+ limit: options.limit,
1496
+ query: options.query
1760
1497
  });
1761
1498
  if (options.json) {
1762
1499
  console.log(JSON.stringify(result));
@@ -1784,6 +1521,13 @@ async function handleIntent(client, subcommand, options) {
1784
1521
  intentCard(intent);
1785
1522
  return;
1786
1523
  }
1524
+ case "prepare": {
1525
+ if (!options.intentContent)
1526
+ throw new Error("Usage: index intent prepare <content> [--answer 'prompt=reply']");
1527
+ const prepared = await client.prepareIntent(options.intentContent, (options.answers ?? []).map(({ key, value }) => ({ prompt: key, answer: value })));
1528
+ console.log(JSON.stringify(prepared, null, options.json ? undefined : 2));
1529
+ return;
1530
+ }
1787
1531
  case "create": {
1788
1532
  if (!options.intentContent) {
1789
1533
  error("Missing content. Usage: index intent create <content>", 1);
@@ -1791,27 +1535,13 @@ async function handleIntent(client, subcommand, options) {
1791
1535
  }
1792
1536
  if (!options.json)
1793
1537
  info("Processing signal...");
1794
- const result = await client.callTool("create_intent", {
1795
- description: options.intentContent
1796
- });
1538
+ const result = await client.createIntent(options.intentContent, options.targetId ? [options.targetId] : undefined, options.receipt);
1797
1539
  if (options.json) {
1798
1540
  console.log(JSON.stringify(result));
1799
1541
  return;
1800
1542
  }
1801
- if (!result.success) {
1802
- error(result.error ?? "Failed to create signal", 1);
1803
- return;
1804
- }
1805
- const proposals = parseIntentProposals(result.data?.message);
1806
- if (proposals.length === 0) {
1807
- error("Signal proposal could not be parsed from the response.", 1);
1808
- return;
1809
- }
1810
- for (const proposal of proposals) {
1811
- await client.confirmIntent(proposal.proposalId, proposal.description);
1812
- success("Signal created.");
1813
- dim(` ${proposal.description}`);
1814
- }
1543
+ success("Signal created.");
1544
+ dim(` shared in ${result.networkIds.length} network${result.networkIds.length === 1 ? "" : "s"}`);
1815
1545
  return;
1816
1546
  }
1817
1547
  case "update": {
@@ -1830,102 +1560,90 @@ async function handleIntent(client, subcommand, options) {
1830
1560
  console.log(JSON.stringify(result));
1831
1561
  return;
1832
1562
  }
1833
- if (!result.success) {
1834
- error(result.error ?? "Failed to update signal", 1);
1835
- return;
1836
- }
1837
1563
  success("Signal updated.");
1838
1564
  return;
1839
1565
  }
1566
+ case "pause":
1567
+ case "resume": {
1568
+ if (!options.intentId)
1569
+ throw new Error(`Usage: index intent ${subcommand} <id>`);
1570
+ const status = subcommand === "pause" ? "PAUSED" : "ACTIVE";
1571
+ await client.updateIntentStatus(options.intentId, status);
1572
+ if (options.json)
1573
+ console.log(JSON.stringify({ intentId: options.intentId, status }));
1574
+ else
1575
+ success(`Signal ${status.toLowerCase()}.`);
1576
+ return;
1577
+ }
1840
1578
  case "archive": {
1841
1579
  if (!options.intentId) {
1842
1580
  error("Missing signal ID. Usage: index intent archive <id>", 1);
1843
1581
  return;
1844
1582
  }
1845
- const intent = await client.getIntent(options.intentId);
1846
- const result = await client.callTool("delete_intent", { intentId: intent.id });
1583
+ await client.archiveIntent(options.intentId);
1847
1584
  if (options.json) {
1848
- console.log(JSON.stringify(result));
1849
- return;
1850
- }
1851
- if (!result.success) {
1852
- error(result.error ?? "Failed to archive signal", 1);
1585
+ console.log(JSON.stringify({ success: true }));
1853
1586
  return;
1854
1587
  }
1855
1588
  success(`Signal ${options.intentId} archived.`);
1856
1589
  return;
1857
1590
  }
1858
- case "link": {
1859
- if (!options.intentId || !options.targetId) {
1860
- error("Usage: index intent link <intent-id> <network-id>", 1);
1591
+ case "networks": {
1592
+ if (!options.intentId) {
1593
+ error("Usage: index intent networks <intent-id>", 1);
1861
1594
  return;
1862
1595
  }
1863
- const intent = await client.getIntent(options.intentId);
1864
- const result = await client.callTool("create_intent_index", {
1865
- intentId: intent.id,
1866
- networkId: options.targetId
1867
- });
1596
+ const networkIds = await client.listIntentNetworks(options.intentId);
1868
1597
  if (options.json) {
1869
- console.log(JSON.stringify(result));
1870
- return;
1871
- }
1872
- if (!result.success) {
1873
- error(result.error ?? "Failed to link signal", 1);
1598
+ console.log(JSON.stringify({ networkIds }));
1874
1599
  return;
1875
1600
  }
1876
- success("Signal linked to network.");
1601
+ heading("Networks");
1602
+ for (const networkId of networkIds)
1603
+ dim(` ${networkId}`);
1604
+ console.log();
1877
1605
  return;
1878
1606
  }
1879
- case "unlink": {
1607
+ case "add-to-network": {
1880
1608
  if (!options.intentId || !options.targetId) {
1881
- error("Usage: index intent unlink <intent-id> <network-id>", 1);
1609
+ error("Usage: index intent add-to-network <intent-id> <network-id>", 1);
1882
1610
  return;
1883
1611
  }
1884
- const intent = await client.getIntent(options.intentId);
1885
- const result = await client.callTool("delete_intent_index", {
1886
- intentId: intent.id,
1887
- networkId: options.targetId
1888
- });
1612
+ await client.addIntentToNetwork(options.intentId, options.targetId);
1889
1613
  if (options.json) {
1890
- console.log(JSON.stringify(result));
1614
+ console.log(JSON.stringify({ success: true }));
1615
+ return;
1616
+ }
1617
+ success("Signal added to network.");
1618
+ return;
1619
+ }
1620
+ case "remove-from-network": {
1621
+ if (!options.intentId || !options.targetId) {
1622
+ error("Usage: index intent remove-from-network <intent-id> <network-id>", 1);
1891
1623
  return;
1892
1624
  }
1893
- if (!result.success) {
1894
- error(result.error ?? "Failed to unlink signal", 1);
1625
+ await client.removeIntentFromNetwork(options.intentId, options.targetId);
1626
+ if (options.json) {
1627
+ console.log(JSON.stringify({ success: true }));
1895
1628
  return;
1896
1629
  }
1897
- success("Signal unlinked from network.");
1630
+ success("Signal removed from network.");
1898
1631
  return;
1899
1632
  }
1900
1633
  }
1901
1634
  }
1902
- function parseIntentProposals(message) {
1903
- if (!message)
1904
- return [];
1905
- const proposals = [];
1906
- const blockRegex = /```intent_proposal\s*\n([\s\S]*?)\n```/g;
1907
- let match;
1908
- while ((match = blockRegex.exec(message)) !== null) {
1909
- try {
1910
- const parsed = JSON.parse(match[1]);
1911
- if (parsed.proposalId && parsed.description) {
1912
- proposals.push({ proposalId: parsed.proposalId, description: parsed.description });
1913
- }
1914
- } catch {}
1915
- }
1916
- return proposals;
1917
- }
1918
1635
 
1919
1636
  // src/opportunity.command.ts
1920
1637
  var OPPORTUNITY_HELP = `
1921
1638
  Usage:
1922
1639
  index opportunity list List your opportunities
1923
- index opportunity list --status <s> Filter by status (pending|accepted|rejected|expired)
1640
+ index opportunity list --intent-id <id> --statuses negotiating,pending,accepted,expired
1641
+ index opportunity list --status <s> Filter by one settled or actionable status
1924
1642
  index opportunity list --limit <n> Limit results
1925
1643
  index opportunity show <id> Show full opportunity details (accepts short ID)
1926
1644
  index opportunity accept <id> Accept an opportunity (accepts short ID)
1927
- index opportunity accept <id> --acknowledge-uptake <id,id> Continue after an uptake advisory
1928
1645
  index opportunity reject <id> Reject an opportunity (accepts short ID)
1646
+ index opportunity start-chat <id> Open the chat through this opportunity
1929
1647
  `;
1930
1648
  async function handleOpportunity(client, subcommand, options) {
1931
1649
  if (!subcommand) {
@@ -1938,7 +1656,7 @@ async function handleOpportunity(client, subcommand, options) {
1938
1656
  }
1939
1657
  switch (subcommand) {
1940
1658
  case "list":
1941
- await opportunityList(client, options.status, options.limit, options.json);
1659
+ await opportunityList(client, options.status, options.statuses, options.intentId, options.limit, options.json);
1942
1660
  return;
1943
1661
  case "show":
1944
1662
  if (!options.targetId) {
@@ -1952,7 +1670,7 @@ async function handleOpportunity(client, subcommand, options) {
1952
1670
  error("Missing opportunity ID. Usage: index opportunity accept <id>", 1);
1953
1671
  return;
1954
1672
  }
1955
- await opportunityStatusUpdate(client, options.targetId, "accepted", options.json, options.acknowledgeUptake);
1673
+ await opportunityStatusUpdate(client, options.targetId, "accepted", options.json);
1956
1674
  return;
1957
1675
  case "reject":
1958
1676
  if (!options.targetId) {
@@ -1961,13 +1679,23 @@ async function handleOpportunity(client, subcommand, options) {
1961
1679
  }
1962
1680
  await opportunityStatusUpdate(client, options.targetId, "rejected", options.json);
1963
1681
  return;
1682
+ case "start-chat": {
1683
+ if (!options.targetId)
1684
+ throw new Error("Usage: index opportunity start-chat <id>");
1685
+ const result = await client.startOpportunityChat(options.targetId);
1686
+ if (options.json)
1687
+ console.log(JSON.stringify(result));
1688
+ else
1689
+ success(`Chat opened: ${result.conversationId}`);
1690
+ return;
1691
+ }
1964
1692
  default:
1965
1693
  error(`Unknown subcommand: ${subcommand}`, 1);
1966
1694
  return;
1967
1695
  }
1968
1696
  }
1969
- async function opportunityList(client, status2, limit, json) {
1970
- const opportunities = await client.listOpportunities({ status: status2, limit });
1697
+ async function opportunityList(client, status, statuses, intentId, limit, json) {
1698
+ const opportunities = await client.listOpportunities({ status, statuses, intentId, limit });
1971
1699
  if (json) {
1972
1700
  console.log(JSON.stringify(opportunities));
1973
1701
  return;
@@ -1984,225 +1712,55 @@ async function opportunityShow(client, id, json) {
1984
1712
  }
1985
1713
  opportunityCard(opportunity);
1986
1714
  }
1987
- async function opportunityStatusUpdate(client, id, status2, json, acknowledgedUptakeQuestionIds) {
1715
+ async function opportunityStatusUpdate(client, id, status, json) {
1988
1716
  const opportunity = await client.getOpportunity(id);
1989
- if (status2 === "accepted") {
1990
- try {
1991
- const result2 = await client.updateOpportunityStatus(opportunity.id, "accepted", acknowledgedUptakeQuestionIds);
1992
- if (json) {
1993
- console.log(JSON.stringify(result2));
1994
- return;
1995
- }
1996
- success("Opportunity accepted.");
1997
- } catch (error2) {
1998
- const advisory = uptakeAdvisoryFromError(error2);
1999
- if (json && error2 instanceof ApiError) {
2000
- console.log(JSON.stringify(error2.response ?? { error: error2.message }));
2001
- return;
2002
- }
2003
- if (!advisory)
2004
- throw error2;
2005
- heading("Questions before accepting");
2006
- for (const question of advisory.advisory.questions) {
2007
- console.log(`
2008
- ${question.title}`);
2009
- console.log(question.prompt);
2010
- for (const option of question.options) {
2011
- console.log(` - ${option.label}${option.description ? ` — ${option.description}` : ""}`);
2012
- }
2013
- dim(` Question ID: ${question.id}`);
2014
- }
2015
- const ids = advisory.advisory.questions.map((question) => question.id).join(",");
2016
- info(`Answer or dismiss these questions, then retry. To continue anyway, run:
2017
- index opportunity accept ${opportunity.id} --acknowledge-uptake ${ids}`);
2018
- }
2019
- return;
2020
- }
2021
- const result = await client.updateOpportunityStatus(opportunity.id, "rejected");
1717
+ const result = await client.updateOpportunityStatus(opportunity.id, status);
2022
1718
  if (json) {
2023
1719
  console.log(JSON.stringify(result));
2024
1720
  return;
2025
1721
  }
2026
- success("Opportunity rejected.");
2027
- }
2028
- function uptakeAdvisoryFromError(error2) {
2029
- if (!(error2 instanceof ApiError) || error2.status !== 409)
2030
- return null;
2031
- const body = error2.response;
2032
- return body?.advisory?.code === "unresolved_uptake_questions" ? body : null;
1722
+ success(`Opportunity ${status}.`);
2033
1723
  }
2034
1724
 
2035
1725
  // src/negotiation.command.ts
2036
- var NEGOTIATION_HELP = `
2037
- Usage:
2038
- index negotiation list List your agent's negotiations
2039
- index negotiation list --limit <n> Limit results
2040
- index negotiation list --since <date|duration> Filter by time (ISO date or duration like 1h, 2d, 1w)
2041
- index negotiation show <id> Show negotiation turn-by-turn details
2042
- `;
2043
1726
  async function handleNegotiation(client, subcommand, options) {
2044
- if (!subcommand) {
2045
- if (options.json) {
2046
- console.log(JSON.stringify({ error: "No subcommand provided" }));
1727
+ let result;
1728
+ if (subcommand === "list") {
1729
+ if (options.state !== undefined && options.state !== "open" && options.state !== "settled")
1730
+ throw new Error("--state must be open or settled");
1731
+ result = await client.listNegotiations({ intentId: options.intentId, state: options.state });
1732
+ } else if (subcommand === "show" || subcommand === "turn") {
1733
+ if (!options.targetId)
1734
+ throw new Error("An opportunity ID is required");
1735
+ if (subcommand === "show") {
1736
+ result = await client.getNegotiation(options.targetId);
2047
1737
  } else {
2048
- console.log(NEGOTIATION_HELP);
2049
- }
2050
- return;
2051
- }
2052
- switch (subcommand) {
2053
- case "list":
2054
- await negotiationList(client, options.limit, options.since, options.json);
2055
- return;
2056
- case "show":
2057
- if (!options.targetId) {
2058
- error("Missing negotiation ID. Usage: index negotiation show <id>", 1);
2059
- return;
2060
- }
2061
- await negotiationShow(client, options.targetId, options.json);
2062
- return;
2063
- default:
2064
- error(`Unknown subcommand: ${subcommand}`, 1);
2065
- return;
2066
- }
2067
- }
2068
- function resolveSince(input) {
2069
- const match = input.match(/^(\d+)([smhdw])$/);
2070
- if (match) {
2071
- const n = parseInt(match[1], 10);
2072
- const unit = match[2];
2073
- const ms = { s: 1000, m: 60000, h: 3600000, d: 86400000, w: 604800000 }[unit];
2074
- return new Date(Date.now() - n * ms).toISOString();
2075
- }
2076
- const d = new Date(input);
2077
- if (isNaN(d.getTime()))
2078
- throw new Error(`Invalid --since value: "${input}". Use ISO date or duration like 1h, 2d, 1w.`);
2079
- return d.toISOString();
2080
- }
2081
- async function negotiationList(client, limit, since, json) {
2082
- const sinceIso = since ? resolveSince(since) : undefined;
2083
- const negotiations = await client.listNegotiations({ limit, since: sinceIso });
2084
- if (json) {
2085
- console.log(JSON.stringify(negotiations));
2086
- return;
2087
- }
2088
- heading("Negotiations");
2089
- negotiationTable(negotiations);
2090
- console.log();
2091
- }
2092
- async function negotiationShow(client, id, json) {
2093
- const negotiations = await client.listNegotiations({ limit: 50 });
2094
- const match = negotiations.find((n) => n.id === id || n.id.startsWith(id));
2095
- if (!match) {
2096
- error(`Negotiation not found: ${id}`, 1);
2097
- return;
2098
- }
2099
- if (json) {
2100
- console.log(JSON.stringify(match));
2101
- return;
2102
- }
2103
- negotiationCard(match);
2104
- }
2105
- var { BOLD: BOLD2, RESET: RESET2, CYAN: CYAN2, GRAY: GRAY2, GREEN: GREEN2, YELLOW: YELLOW2, BLUE: BLUE2, DIM: DIM2 } = exports_output;
2106
- function outcomeLabel(outcome) {
2107
- if (!outcome)
2108
- return `${GRAY2}unknown${RESET2}`;
2109
- return outcome.hasOpportunity ? `${GREEN2}opportunity${RESET2}` : `${GRAY2}no match${RESET2}`;
2110
- }
2111
- function roleLabel2(role) {
2112
- switch (role) {
2113
- case "agent":
2114
- return `${GREEN2}helper${RESET2}`;
2115
- case "patient":
2116
- return `${YELLOW2}seeker${RESET2}`;
2117
- case "peer":
2118
- return `${CYAN2}peer${RESET2}`;
2119
- default:
2120
- return `${GRAY2}-${RESET2}`;
2121
- }
2122
- }
2123
- function actionColorFor(action) {
2124
- switch (action) {
2125
- case "accept":
2126
- return GREEN2;
2127
- case "reject":
2128
- return RED;
2129
- default:
2130
- return BLUE2;
2131
- }
2132
- }
2133
- function negotiationTable(negotiations) {
2134
- if (negotiations.length === 0) {
2135
- dim(" No negotiations found.");
2136
- return;
2137
- }
2138
- const idW = 8;
2139
- const nameW = 22;
2140
- const outcomeW = 14;
2141
- const roleW = 8;
2142
- const turnsW = 6;
2143
- const dateW = 20;
2144
- process.stdout.write(` ${BOLD2}${"ID".padEnd(idW)} ${"Counterparty".padEnd(nameW)} ${"Outcome".padEnd(outcomeW)} ${"Role".padEnd(roleW)} ${"Turns".padEnd(turnsW)} ${"Created".padEnd(dateW)}${RESET2}
2145
- `);
2146
- process.stdout.write(` ${GRAY2}${"-".repeat(idW)} ${"-".repeat(nameW)} ${"-".repeat(outcomeW)} ${"-".repeat(roleW)} ${"-".repeat(turnsW)} ${"-".repeat(dateW)}${RESET2}
2147
- `);
2148
- for (const n of negotiations) {
2149
- const shortId = n.id.slice(0, 8);
2150
- const name = (n.counterparty?.name ?? "Unknown").slice(0, nameW);
2151
- const turns = String(n.outcome?.turnCount ?? n.turns?.length ?? "-");
2152
- const date = n.createdAt ? new Date(n.createdAt).toLocaleDateString("en-US", {
2153
- year: "numeric",
2154
- month: "short",
2155
- day: "numeric"
2156
- }) : "-";
2157
- const outcomeStr = outcomeLabel(n.outcome);
2158
- const roleStr = roleLabel2(n.outcome?.role);
2159
- process.stdout.write(` ${CYAN2}${shortId}${RESET2} ${name.padEnd(nameW)} ${outcomeStr}${padTo(outcomeW, stripAnsi(outcomeStr))} ${roleStr}${padTo(roleW, stripAnsi(roleStr))} ${turns.padEnd(turnsW)} ${GRAY2}${date}${RESET2}
2160
- `);
2161
- }
2162
- }
2163
- function negotiationCard(n) {
2164
- console.log();
2165
- console.log(` ${BOLD2}${CYAN2}Negotiation Details${RESET2}`);
2166
- console.log(` ${GRAY2}${"─".repeat(60)}${RESET2}`);
2167
- console.log(` ${BOLD2}ID${RESET2} ${GRAY2}${n.id}${RESET2}`);
2168
- console.log(` ${BOLD2}Counterparty${RESET2} ${n.counterparty?.name ?? "Unknown"}`);
2169
- console.log(` ${BOLD2}Outcome${RESET2} ${outcomeLabel(n.outcome)}`);
2170
- console.log(` ${BOLD2}Your Role${RESET2} ${roleLabel2(n.outcome?.role)}`);
2171
- console.log(` ${BOLD2}Turns${RESET2} ${n.outcome?.turnCount ?? n.turns?.length ?? "-"}`);
2172
- console.log(` ${BOLD2}Created${RESET2} ${GRAY2}${new Date(n.createdAt).toLocaleString()}${RESET2}`);
2173
- if (n.turns && n.turns.length > 0) {
2174
- console.log();
2175
- console.log(` ${BOLD2}Turn-by-Turn${RESET2}`);
2176
- console.log(` ${GRAY2}${"─".repeat(60)}${RESET2}`);
2177
- for (let i = 0;i < n.turns.length; i++) {
2178
- const t = n.turns[i];
2179
- const time = new Date(t.createdAt).toLocaleTimeString("en-US", {
2180
- hour: "2-digit",
2181
- minute: "2-digit",
2182
- second: "2-digit"
1738
+ if (!options.action || !["propose", "counter", "accept", "decline"].includes(options.action))
1739
+ throw new Error("--action must be propose, counter, accept, or decline");
1740
+ if (!options.message?.trim())
1741
+ throw new Error("--message is required");
1742
+ if (options.expectedTurnCount === undefined || !Number.isSafeInteger(options.expectedTurnCount) || options.expectedTurnCount < 0)
1743
+ throw new Error("--expected-turn-count must be the observed nonnegative integer turn count");
1744
+ result = await client.submitNegotiationTurn(options.targetId, {
1745
+ action: options.action,
1746
+ message: options.message,
1747
+ expectedTurnCount: options.expectedTurnCount
2183
1748
  });
2184
- const actionColor = actionColorFor(t.action);
2185
- console.log(` ${DIM2}Turn ${i + 1}${RESET2} ${CYAN2}${t.speaker?.name ?? "?"}${RESET2} ${actionColor}${t.action}${RESET2} ${GRAY2}${time}${RESET2}`);
2186
- if (t.suggestedRoles) {
2187
- console.log(` ${DIM2} roles: own=${t.suggestedRoles.ownUser ?? "?"} other=${t.suggestedRoles.otherUser ?? "?"}${RESET2}`);
2188
- }
2189
- if (t.reasoning) {
2190
- const lines = wordWrap(t.reasoning, 72);
2191
- for (const line of lines) {
2192
- console.log(` ${DIM2} ${line}${RESET2}`);
2193
- }
2194
- }
2195
- console.log();
2196
1749
  }
1750
+ } else {
1751
+ throw new Error("Usage: index negotiation list [--intent-id <id>] [--state open|settled], show <opportunity-id>, or turn <opportunity-id> --action <action> --message <text> --expected-turn-count <n>");
2197
1752
  }
2198
- console.log(` ${GRAY2}${"─".repeat(60)}${RESET2}`);
2199
- console.log();
1753
+ console.log(JSON.stringify(result, null, options.json ? undefined : 2));
2200
1754
  }
2201
1755
 
2202
1756
  // src/network.command.ts
2203
1757
  var NETWORK_HELP = `
2204
1758
  Network Commands:
2205
1759
  index network list List your networks
1760
+ index network discover Find public networks to join
1761
+ index network requests See your network creation requests
1762
+ index network request-update <id> --title <name> [--prompt <purpose>]
1763
+ index network request-dismiss <id> Dismiss your request
2206
1764
  index network create <name> Create directly or submit an early-access request
2207
1765
  index network create <name> --prompt Create directly or request early access with a description
2208
1766
  index network show <id|key> Show network details and members
@@ -2222,6 +1780,47 @@ async function handleNetwork(client, subcommand, positionals, options) {
2222
1780
  case "list":
2223
1781
  await networkList(client, options.json);
2224
1782
  return;
1783
+ case "discover": {
1784
+ const result = await client.discoverNetworks();
1785
+ if (options.json)
1786
+ console.log(JSON.stringify(result));
1787
+ else {
1788
+ heading("Public networks");
1789
+ networkTable(result.networks);
1790
+ }
1791
+ return;
1792
+ }
1793
+ case "requests": {
1794
+ const result = await client.listNetworkRequests();
1795
+ if (options.json)
1796
+ console.log(JSON.stringify(result));
1797
+ else {
1798
+ heading("Your network requests");
1799
+ for (const request of result.requests)
1800
+ dim(` ${request.id} · ${request.title} · ${request.status}`);
1801
+ }
1802
+ return;
1803
+ }
1804
+ case "request-update": {
1805
+ if (!positionals[0] || !options.title)
1806
+ throw new Error("Usage: index network request-update <id> --title <name> [--prompt <purpose>]");
1807
+ const result = await client.updateNetworkRequest(positionals[0], options.title, options.prompt);
1808
+ if (options.json)
1809
+ console.log(JSON.stringify(result));
1810
+ else
1811
+ success("Network request resubmitted.");
1812
+ return;
1813
+ }
1814
+ case "request-dismiss": {
1815
+ if (!positionals[0])
1816
+ throw new Error("Usage: index network request-dismiss <id>");
1817
+ await client.dismissNetworkRequest(positionals[0]);
1818
+ if (options.json)
1819
+ console.log(JSON.stringify({ success: true }));
1820
+ else
1821
+ success("Network request dismissed.");
1822
+ return;
1823
+ }
2225
1824
  case "create":
2226
1825
  await networkCreate(client, positionals[0], options.prompt, options.json);
2227
1826
  return;
@@ -2249,13 +1848,12 @@ async function handleNetwork(client, subcommand, positionals, options) {
2249
1848
  }
2250
1849
  async function networkList(client, json) {
2251
1850
  const networks = await client.listNetworks();
2252
- const filtered = networks.filter((n) => !n.isPersonal);
2253
1851
  if (json) {
2254
- console.log(JSON.stringify(filtered));
1852
+ console.log(JSON.stringify(networks));
2255
1853
  return;
2256
1854
  }
2257
1855
  heading("Networks");
2258
- networkTable(filtered);
1856
+ networkTable(networks);
2259
1857
  console.log();
2260
1858
  }
2261
1859
  async function networkCreate(client, name, prompt, json) {
@@ -2336,13 +1934,9 @@ async function networkUpdate(client, id, options, json) {
2336
1934
  error("Provide at least --title or --prompt. Usage: index network update <id> [--title <t>] [--prompt <p>]", 1);
2337
1935
  return;
2338
1936
  }
2339
- const result = await client.callTool("update_network", { networkId: id, settings });
1937
+ const network = await client.updateNetwork(id, settings);
2340
1938
  if (json) {
2341
- console.log(JSON.stringify(result));
2342
- return;
2343
- }
2344
- if (!result.success) {
2345
- error(result.error ?? "Network update failed", 1);
1939
+ console.log(JSON.stringify({ network }));
2346
1940
  return;
2347
1941
  }
2348
1942
  success("Network updated.");
@@ -2352,13 +1946,9 @@ async function networkDelete(client, id, json) {
2352
1946
  error("Usage: index network delete <id>", 1);
2353
1947
  return;
2354
1948
  }
2355
- const result = await client.callTool("delete_network", { networkId: id });
1949
+ await client.deleteNetwork(id);
2356
1950
  if (json) {
2357
- console.log(JSON.stringify(result));
2358
- return;
2359
- }
2360
- if (!result.success) {
2361
- error(result.error ?? "Network deletion failed", 1);
1951
+ console.log(JSON.stringify({ success: true }));
2362
1952
  return;
2363
1953
  }
2364
1954
  success("Network deleted.");
@@ -2382,6 +1972,47 @@ async function networkInvite(client, id, email, json) {
2382
1972
  dim(" Created a pending account for this invitee.");
2383
1973
  }
2384
1974
 
1975
+ // src/sse.parser.ts
1976
+ function parseSSEEvents(chunk) {
1977
+ if (!chunk)
1978
+ return [];
1979
+ const events = [];
1980
+ const rawEvents = chunk.split(`
1981
+
1982
+ `);
1983
+ for (let i = 0;i < rawEvents.length; i++) {
1984
+ const raw = rawEvents[i].trim();
1985
+ if (!raw)
1986
+ continue;
1987
+ if (i === rawEvents.length - 1 && !chunk.endsWith(`
1988
+
1989
+ `)) {
1990
+ continue;
1991
+ }
1992
+ let eventType;
1993
+ const dataLines = [];
1994
+ for (const line of raw.split(`
1995
+ `)) {
1996
+ if (line.startsWith(":")) {
1997
+ continue;
1998
+ }
1999
+ if (line.startsWith("event:")) {
2000
+ eventType = line.slice("event:".length).trim();
2001
+ } else if (line.startsWith("data:")) {
2002
+ dataLines.push(line.slice("data:".length).trim());
2003
+ }
2004
+ }
2005
+ if (dataLines.length > 0) {
2006
+ events.push({
2007
+ event: eventType,
2008
+ data: dataLines.join(`
2009
+ `)
2010
+ });
2011
+ }
2012
+ }
2013
+ return events;
2014
+ }
2015
+
2385
2016
  // src/conversation.command.ts
2386
2017
  var CONVERSATION_HELP = `
2387
2018
  Conversation Commands:
@@ -2390,13 +2021,37 @@ Conversation Commands:
2390
2021
  index conversation show <id> Show messages (accepts short ID)
2391
2022
  index conversation show <id> --limit <n> Limit number of messages
2392
2023
  index conversation send <id> <message> Send a message (accepts short ID)
2024
+ index conversation show agent --intent-id <id> Read scoped agent state
2025
+ index conversation send agent <text> --intent-id <id> [--question-id <id>]
2026
+ index conversation answer agent --intent-id <id> --answer 'question-id=text' [--answer ...]
2393
2027
  index conversation stream Listen for real-time events (SSE)
2394
2028
  `;
2395
2029
  async function handleConversation(client, subcommand, positionals, options) {
2396
- if (!subcommand) {
2397
- error("Agent chat is no longer available from the CLI. Use `index conversation list` " + "for your conversations, or chat in the Index app.", 1);
2030
+ if (positionals[0] === "agent" && (subcommand === "show" || subcommand === "send")) {
2031
+ if (!options?.intentId)
2032
+ throw new Error("Personal-agent conversations require --intent-id <id>");
2033
+ let result;
2034
+ if (subcommand === "show")
2035
+ result = await client.getAgentConversation(options.intentId);
2036
+ else {
2037
+ const text = positionals.slice(1).join(" ");
2038
+ if (!text.trim())
2039
+ throw new Error("A message is required");
2040
+ result = options.questionId ? await client.answerAgentQuestions(options.intentId, [{ questionId: options.questionId, text }]) : await client.sendMessage("agent", text, options.intentId);
2041
+ }
2042
+ console.log(JSON.stringify(result, null, options.json ? undefined : 2));
2043
+ return;
2044
+ }
2045
+ if (subcommand === "answer") {
2046
+ if (positionals[0] !== "agent" || !options?.intentId || !options.answers?.length) {
2047
+ throw new Error("Usage: index conversation answer agent --intent-id <id> --answer 'question-id=text' [--answer ...]");
2048
+ }
2049
+ const result = await client.answerAgentQuestions(options.intentId, options.answers.map(({ key, value }) => ({ questionId: key, text: value })));
2050
+ console.log(JSON.stringify(result, null, options.json ? undefined : 2));
2398
2051
  return;
2399
2052
  }
2053
+ if (!subcommand)
2054
+ throw new Error(CONVERSATION_HELP.trim());
2400
2055
  switch (subcommand) {
2401
2056
  case "list":
2402
2057
  await conversationList(client, options?.json);
@@ -2411,10 +2066,10 @@ async function handleConversation(client, subcommand, positionals, options) {
2411
2066
  await conversationSend(client, positionals[0], positionals.slice(1), options?.json);
2412
2067
  return;
2413
2068
  case "stream":
2414
- await conversationStream(client);
2069
+ await conversationStream(client, options?.json);
2415
2070
  return;
2416
2071
  case "help":
2417
- console.log(CONVERSATION_HELP);
2072
+ console.log(options?.json ? JSON.stringify({ help: CONVERSATION_HELP.trim() }) : CONVERSATION_HELP);
2418
2073
  return;
2419
2074
  default:
2420
2075
  error(`Unknown conversation subcommand: ${subcommand}`, 1);
@@ -2472,11 +2127,11 @@ async function conversationSend(client, id, messageParts, json) {
2472
2127
  }
2473
2128
  success(`Message sent (${msg.id})`);
2474
2129
  }
2475
- async function conversationStream(client) {
2130
+ async function conversationStream(client, json) {
2476
2131
  info("Connecting to conversation stream...");
2477
2132
  dim(`Press Ctrl+C to stop.
2478
2133
  `);
2479
- const response = await client.streamConversationEvents();
2134
+ const response = await client.streamEvents();
2480
2135
  if (!response.body) {
2481
2136
  error("No response body from stream endpoint.", 1);
2482
2137
  return;
@@ -2490,33 +2145,21 @@ async function conversationStream(client) {
2490
2145
  if (done)
2491
2146
  break;
2492
2147
  buffer += decoder.decode(value, { stream: true });
2493
- while (buffer.includes(`
2494
-
2495
- `)) {
2496
- const idx = buffer.indexOf(`
2148
+ buffer = buffer.replace(/\r\n/g, `
2149
+ `);
2150
+ const boundary = buffer.lastIndexOf(`
2497
2151
 
2498
2152
  `);
2499
- const raw2 = buffer.slice(0, idx);
2500
- buffer = buffer.slice(idx + 2);
2501
- for (const line of raw2.split(`
2502
- `)) {
2503
- if (line.startsWith(":"))
2504
- continue;
2505
- if (!line.startsWith("data:"))
2506
- continue;
2507
- const jsonStr = line.slice("data:".length).trim();
2508
- if (!jsonStr)
2509
- continue;
2510
- try {
2511
- const event = JSON.parse(jsonStr);
2512
- const type = event.type;
2513
- if (type === "connected") {
2514
- success("Connected to conversation stream.");
2515
- } else {
2516
- dim(`[${type}] ${JSON.stringify(event)}`);
2517
- }
2518
- } catch {}
2519
- }
2153
+ if (boundary < 0)
2154
+ continue;
2155
+ const complete = buffer.slice(0, boundary + 2);
2156
+ buffer = buffer.slice(boundary + 2);
2157
+ for (const event of parseSSEEvents(complete)) {
2158
+ const data = JSON.parse(event.data);
2159
+ if (json)
2160
+ console.log(JSON.stringify({ event: event.event, data }));
2161
+ else
2162
+ dim(JSON.stringify(data));
2520
2163
  }
2521
2164
  }
2522
2165
  } finally {
@@ -2524,81 +2167,6 @@ async function conversationStream(client) {
2524
2167
  }
2525
2168
  }
2526
2169
 
2527
- // src/contact.command.ts
2528
- var CONTACT_HELP = `
2529
- Usage:
2530
- index contact list List your contacts
2531
- index contact remove <email> Remove a contact
2532
- `;
2533
- async function handleContact(client, subcommand, positionals, options) {
2534
- if (!subcommand) {
2535
- if (options.json) {
2536
- console.log(JSON.stringify({ error: "No subcommand provided" }));
2537
- } else {
2538
- console.log(CONTACT_HELP);
2539
- }
2540
- return;
2541
- }
2542
- switch (subcommand) {
2543
- case "list": {
2544
- const result = await client.callTool("list_contacts", {});
2545
- if (options.json) {
2546
- console.log(JSON.stringify(result));
2547
- return;
2548
- }
2549
- if (!result.success) {
2550
- error(result.error ?? "Failed to list contacts", 1);
2551
- return;
2552
- }
2553
- const data = result.data;
2554
- heading("Contacts");
2555
- contactTable(data.contacts);
2556
- dim(`
2557
- ${data.count} contact${data.count !== 1 ? "s" : ""}`);
2558
- console.log();
2559
- return;
2560
- }
2561
- case "remove": {
2562
- const email = positionals[0];
2563
- if (!email) {
2564
- error("Missing email. Usage: index contact remove <email>", 1);
2565
- return;
2566
- }
2567
- const listResult = await client.callTool("list_contacts", {});
2568
- if (!listResult.success) {
2569
- error("Failed to resolve contact", 1);
2570
- return;
2571
- }
2572
- const contacts = listResult.data.contacts;
2573
- const match = contacts.find((c) => c.email.toLowerCase() === email.toLowerCase());
2574
- if (!match) {
2575
- error(`No contact found with email: ${email}`, 1);
2576
- return;
2577
- }
2578
- const result = await client.callTool("remove_contact", { contactUserId: match.userId });
2579
- if (options.json) {
2580
- console.log(JSON.stringify(result));
2581
- return;
2582
- }
2583
- if (!result.success) {
2584
- error(result.error ?? "Failed to remove contact", 1);
2585
- return;
2586
- }
2587
- success(`Removed ${email} from contacts.`);
2588
- return;
2589
- }
2590
- default: {
2591
- if (options.json) {
2592
- console.log(JSON.stringify({ error: `Unknown subcommand: ${subcommand}` }));
2593
- } else {
2594
- error(`Unknown subcommand: ${subcommand}`);
2595
- console.log(CONTACT_HELP);
2596
- }
2597
- return;
2598
- }
2599
- }
2600
- }
2601
-
2602
2170
  // src/scrape.command.ts
2603
2171
  async function handleScrape(client, positionals, options) {
2604
2172
  const url = positionals[0];
@@ -2608,20 +2176,15 @@ async function handleScrape(client, positionals, options) {
2608
2176
  }
2609
2177
  if (!options.json)
2610
2178
  info(`Scraping ${url}...`);
2611
- const result = await client.callTool("scrape_url", { url, objective: options.objective });
2179
+ const result = await client.scrapeUrl(url, options.objective);
2612
2180
  if (options.json) {
2613
2181
  console.log(JSON.stringify(result));
2614
2182
  return;
2615
2183
  }
2616
- if (!result.success) {
2617
- error(result.error ?? "Scrape failed", 1);
2618
- return;
2619
- }
2620
- const data = result.data;
2621
- heading(`Content from ${data.url}`);
2622
- console.log(data.content);
2184
+ heading(`Content from ${result.url}`);
2185
+ console.log(result.content);
2623
2186
  dim(`
2624
- ${data.contentLength} characters extracted`);
2187
+ ${result.contentLength} characters extracted`);
2625
2188
  console.log();
2626
2189
  }
2627
2190
 
@@ -2632,18 +2195,17 @@ import { join as join2 } from "node:path";
2632
2195
  async function handleSync(client, options) {
2633
2196
  if (!options.json)
2634
2197
  info("Syncing context...");
2635
- const [profile, networks, intents, contacts] = await Promise.all([
2636
- client.readUserContexts(),
2637
- client.callTool("read_networks", {}),
2638
- client.callTool("read_intents", {}),
2639
- client.callTool("list_contacts", {})
2198
+ const me = await client.getMe();
2199
+ const [profile, networks, intents] = await Promise.all([
2200
+ client.getUser(me.id),
2201
+ client.listNetworks(),
2202
+ client.listIntents({ limit: 100 })
2640
2203
  ]);
2641
2204
  const context = {
2642
2205
  syncedAt: new Date().toISOString(),
2643
- profile: profile.success ? profile.data : null,
2644
- networks: networks.success ? networks.data : null,
2645
- intents: intents.success ? intents.data : null,
2646
- contacts: contacts.success ? contacts.data : null
2206
+ profile,
2207
+ networks,
2208
+ intents: intents.intents
2647
2209
  };
2648
2210
  if (options.json) {
2649
2211
  console.log(JSON.stringify(context));
@@ -2655,190 +2217,100 @@ async function handleSync(client, options) {
2655
2217
  success("Context synced to ~/.index/context.json");
2656
2218
  }
2657
2219
 
2658
- // src/onboarding.command.ts
2659
- var ONBOARDING_HELP = `
2660
- Onboarding Commands:
2661
- index onboarding complete Mark onboarding as complete
2662
- `;
2663
- async function handleOnboarding(client, subcommand, options) {
2664
- if (subcommand === "complete") {
2665
- await onboardingComplete(client, options?.json);
2666
- return;
2667
- }
2668
- console.log(ONBOARDING_HELP);
2669
- }
2670
- async function onboardingComplete(client, json) {
2671
- if (!json) {
2672
- info("Completing onboarding...");
2673
- }
2674
- const result = await client.callTool("complete_onboarding", {});
2220
+ // src/docs.command.ts
2221
+ async function handleDocs(client, topic, json) {
2222
+ const result = await client.readDocs(topic);
2675
2223
  if (json) {
2676
2224
  console.log(JSON.stringify(result));
2677
2225
  return;
2678
2226
  }
2679
- if (result.success) {
2680
- success("Onboarding marked as complete.");
2681
- } else {
2682
- error(result.error ?? "Failed to complete onboarding.");
2683
- }
2227
+ console.log(result.content);
2228
+ if (result.topics)
2229
+ dim(`
2230
+ Topics: ${result.topics.join(", ")}`);
2231
+ }
2232
+
2233
+ // src/onboarding.command.ts
2234
+ async function handleOnboarding(client, subcommand, options) {
2235
+ let result;
2236
+ if (subcommand === "confirm-profile")
2237
+ result = await client.confirmProfile();
2238
+ else if (subcommand === "complete")
2239
+ result = await client.completeOnboarding(options?.intentId);
2240
+ else
2241
+ throw new Error("Usage: index onboarding confirm-profile | complete [--intent-id <id>]");
2242
+ console.log(JSON.stringify(result, null, options?.json ? undefined : 2));
2684
2243
  }
2685
2244
 
2686
2245
  // src/main.ts
2687
2246
  var DEFAULT_API_URL = "https://protocol.index.network";
2688
- var DEFAULT_APP_URL = "https://index.network";
2689
- var VERSION = package_default.version;
2690
- var BOX = { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" };
2691
- function visualLen(s) {
2692
- return stripAnsi(s).length;
2693
- }
2694
- function padVisual(s, width) {
2695
- return s + " ".repeat(Math.max(0, width - visualLen(s)));
2696
- }
2697
- function panel(title, rows) {
2698
- const innerW = Math.max(52, title.length + 4, ...rows.map((r) => visualLen(r)));
2699
- const dashRun = Math.max(1, innerW - title.length - 3);
2700
- const innerTop = `${BOX.h} ${title} ${BOX.h.repeat(dashRun)}`;
2701
- console.log(`${GRAY}${BOX.tl}${innerTop}${BOX.tr}${RESET}`);
2702
- for (const row of rows) {
2703
- console.log(`${GRAY}${BOX.v}${RESET}${padVisual(row, innerW)}${GRAY}${BOX.v}${RESET}`);
2704
- }
2705
- console.log(`${GRAY}${BOX.bl}${BOX.h.repeat(innerW)}${BOX.br}${RESET}
2706
- `);
2247
+ var LOGIN_API_URLS = {
2248
+ local: "http://localhost:3001",
2249
+ dev: "https://protocol.dev.index.network",
2250
+ prod: DEFAULT_API_URL
2251
+ };
2252
+ function webOriginForApi(apiUrl) {
2253
+ const url = new URL(apiUrl);
2254
+ if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
2255
+ return `${url.protocol}//${url.hostname}:3000`;
2256
+ }
2257
+ const hostname = url.hostname.startsWith("protocol.") ? url.hostname.slice("protocol.".length) : url.hostname;
2258
+ return `https://${hostname}`;
2707
2259
  }
2708
- function helpRowDim(leftW, left, right) {
2709
- const pad = Math.max(0, leftW - left.length);
2710
- return ` ${GRAY}${left}${" ".repeat(pad)} ${right}${RESET}`;
2711
- }
2712
- function helpRowCmd(leftW, left, right) {
2713
- const pad = Math.max(0, leftW - left.length);
2714
- return ` ${BOLD}${CYAN}${left}${RESET}${" ".repeat(pad)} ${right}`;
2715
- }
2716
- function helpRowCont(leftW, right) {
2717
- return ` ${" ".repeat(leftW)} ${right}`;
2718
- }
2719
- function renderHelp() {
2720
- const gsLefts = [
2721
- "index login",
2722
- "index logout",
2723
- 'index intent create "..."',
2724
- "index negotiation list",
2725
- "index opportunity list"
2726
- ];
2727
- const gsLW = Math.max(...gsLefts.map((s) => s.length));
2728
- const formsLefts = [
2729
- "index conversation list",
2730
- "index sync --json",
2731
- "index profile create",
2732
- "index profile update",
2733
- "index intent list",
2734
- "index negotiation list",
2735
- "index opportunity list"
2736
- ];
2737
- const formsLW = Math.max(...formsLefts.map((s) => s.length));
2738
- const cmdLefts = [
2739
- "pattern",
2740
- "example",
2741
- "intent",
2742
- "negotiation",
2743
- "opportunity",
2744
- "profile",
2745
- "conversation",
2746
- "network",
2747
- "contact",
2748
- "scrape",
2749
- "sync",
2750
- "onboarding"
2751
- ];
2752
- const cmdLW = Math.max(...cmdLefts.map((s) => s.length));
2753
- const optLefts = [
2754
- "--api-url <url>",
2755
- "--app-url <url>",
2756
- "--archived",
2757
- "--status <status>",
2758
- "--limit <n>",
2759
- "--since <date>",
2760
- "--json",
2761
- "--objective <text>",
2762
- "--linkedin <url>",
2763
- "--github <url>",
2764
- "--twitter <url>",
2765
- "--title <text>",
2766
- "--details <text>",
2767
- "--help",
2768
- "--version"
2769
- ];
2770
- const optLW = Math.max(...optLefts.map((s) => s.length));
2771
- console.log();
2772
- console.log(` ${BOLD}${CYAN}I N D E X${RESET} ${GRAY}cli${RESET}`);
2773
- console.log(` ${GRAY}v${VERSION}${RESET}
2774
- `);
2775
- panel("getting started", [
2776
- helpRowCmd(gsLW, "index login", "authenticate via browser"),
2777
- helpRowCmd(gsLW, "index logout", "clear stored session"),
2778
- helpRowCmd(gsLW, 'index intent create "..."', "describe what you're looking for"),
2779
- helpRowCmd(gsLW, "index negotiation list", "see agent debates in progress"),
2780
- helpRowCmd(gsLW, "index opportunity list", "see what was found for you")
2781
- ]);
2782
- panel("common forms", [
2783
- helpRowCmd(formsLW, "index conversation list", "list your conversations"),
2784
- helpRowCmd(formsLW, "index sync --json", "print synced context as JSON"),
2785
- "",
2786
- helpRowCmd(formsLW, "index profile create", "use social URL flags"),
2787
- helpRowCmd(formsLW, "index profile update", "use action text or --details"),
2788
- helpRowCmd(formsLW, "index intent list", "supports --archived and --limit"),
2789
- helpRowCmd(formsLW, "index negotiation list", "supports --since and --limit"),
2790
- helpRowCmd(formsLW, "index opportunity list", "supports --status and --limit")
2791
- ]);
2792
- panel("commands", [
2793
- helpRowDim(cmdLW, "pattern", "index <command> [args] [options]"),
2794
- helpRowDim(cmdLW, "example", 'index intent create "looking for a CTO"'),
2795
- "",
2796
- helpRowCmd(cmdLW, "intent", "list · show · create · update · archive"),
2797
- helpRowCont(cmdLW, "link · unlink · links"),
2798
- helpRowCmd(cmdLW, "negotiation", "list · show"),
2799
- "",
2800
- helpRowCmd(cmdLW, "profile", "show · sync · search · create · update"),
2801
- helpRowCmd(cmdLW, "conversation", "list · with · show · send · stream"),
2802
- helpRowCmd(cmdLW, "network", "list · create · show · update · delete"),
2803
- helpRowCont(cmdLW, "join · leave · invite"),
2804
- helpRowCmd(cmdLW, "contact", "list · remove"),
2805
- "",
2806
- helpRowCmd(cmdLW, "scrape", "extract content from a URL"),
2807
- helpRowCmd(cmdLW, "sync", "download your context locally"),
2808
- helpRowCmd(cmdLW, "onboarding", "finish account setup")
2809
- ]);
2810
- panel("options", [
2811
- helpRowDim(optLW, "--api-url <url>", "override API server URL"),
2812
- helpRowDim(optLW, "--app-url <url>", "override app URL for login"),
2813
- helpRowDim(optLW, "--archived", "include archived signals"),
2814
- helpRowDim(optLW, "--status <status>", "filter opportunities by status"),
2815
- helpRowDim(optLW, "--limit <n>", "limit number of results"),
2816
- helpRowDim(optLW, "--since <date>", "filter by ISO date or duration"),
2817
- helpRowDim(optLW, "--json", "output raw JSON"),
2818
- helpRowDim(optLW, "--objective <text>", "objective for scrape command"),
2819
- helpRowDim(optLW, "--linkedin <url>", "LinkedIn URL for profile create"),
2820
- helpRowDim(optLW, "--github <url>", "GitHub URL for profile create"),
2821
- helpRowDim(optLW, "--twitter <url>", "Twitter URL for profile create"),
2822
- helpRowDim(optLW, "--title <text>", "title for network update"),
2823
- helpRowDim(optLW, "--details <text>", "details for profile update"),
2824
- helpRowDim(optLW, "--help", "show help for any command"),
2825
- helpRowDim(optLW, "--version", "show version")
2826
- ]);
2260
+ var VERSION = package_default.version;
2261
+ function renderHelp(json) {
2262
+ const help = `Usage: index <command> [args] [options]
2263
+
2264
+ login [local|dev|prod] [--app-url <url>] Authenticate through the matching web app
2265
+ logout Revoke and clear the stored session
2266
+ docs [topic] Read the protocol's canonical guidance
2267
+ agent me Read your selected personal agent
2268
+ profile [show <user-id>|sync|update] Read, research, or edit your profile
2269
+ intent list|show|prepare|create|update|pause|resume|archive|networks|add-to-network|remove-from-network
2270
+ network list|discover|requests|request-update|request-dismiss|show|create|update|delete|join|leave|invite
2271
+ opportunity list|show|accept|reject|start-chat
2272
+ negotiation list [--intent-id <id>] [--state open|settled]
2273
+ negotiation show <opportunity-id>
2274
+ negotiation turn <opportunity-id> --action propose|counter|accept|decline
2275
+ --message <text> --expected-turn-count <n>
2276
+ conversation list|with|show|send|answer|stream
2277
+ conversation show agent --intent-id <id>
2278
+ conversation send agent <text> --intent-id <id> [--question-id <id>]
2279
+ onboarding confirm-profile
2280
+ onboarding complete [--intent-id <id>]
2281
+ scrape <url> [--objective <text>]
2282
+ sync Download profile, networks, and intents
2283
+
2284
+ Global options: --api-url <origin>, --json, --help, --version
2285
+ List options: --archived (intents), --status (opportunities), --limit <n>
2286
+ Intent list options: --query <text> (matches description and summary)
2287
+ Network options: --prompt <text> (create), --title <text> (update)
2288
+
2289
+ Auth: INDEX_SESSION_TOKEN or INDEX_API_KEY, otherwise stored browser login.
2290
+ API origin: --api-url, INDEX_API_URL, stored login URL, production default.
2291
+ Login target: local (localhost:3001), dev (protocol.dev.index.network), prod (default).
2292
+ --json emits one result/error; conversation stream emits NDJSON events.`;
2293
+ console.log(json ? JSON.stringify({ version: VERSION, help }) : `Index CLI ${VERSION}
2294
+
2295
+ ${help}`);
2827
2296
  }
2828
2297
  async function requireAuth(apiUrlOverride) {
2829
2298
  const store = new CredentialStore;
2830
2299
  const creds = await store.load();
2831
- if (!creds) {
2832
- error("Not logged in. Run `index login` first.", 1);
2833
- process.exit(1);
2834
- }
2835
- const apiUrl = apiUrlOverride ?? creds.apiUrl;
2836
- return new ApiClient(apiUrl, creds.token);
2837
- }
2838
- async function runLogin(apiUrlOverride, appUrlOverride) {
2300
+ const session = process.env.INDEX_SESSION_TOKEN || undefined;
2301
+ const apiKey = process.env.INDEX_API_KEY || undefined;
2302
+ if (session && apiKey)
2303
+ throw new Error("Set only one of INDEX_SESSION_TOKEN and INDEX_API_KEY");
2304
+ const token = session ?? apiKey ?? creds?.token;
2305
+ if (!token)
2306
+ throw new Error("Not logged in. Run `index login` or set INDEX_SESSION_TOKEN or INDEX_API_KEY.");
2307
+ const apiUrl = apiUrlOverride ?? process.env.INDEX_API_URL ?? creds?.apiUrl ?? DEFAULT_API_URL;
2308
+ return new ApiClient(apiUrl, token, apiKey ? "apiKey" : "session");
2309
+ }
2310
+ async function runLogin(target, apiUrlOverride, appUrlOverride, json) {
2839
2311
  const store = new CredentialStore;
2840
- const apiUrl = apiUrlOverride ?? DEFAULT_API_URL;
2841
- const appUrl = appUrlOverride ?? DEFAULT_APP_URL;
2312
+ const apiUrl = apiUrlOverride ?? (target ? LOGIN_API_URLS[target] : undefined) ?? process.env.INDEX_API_URL ?? (await store.load())?.apiUrl ?? DEFAULT_API_URL;
2313
+ const appUrl = appUrlOverride ?? webOriginForApi(apiUrl);
2842
2314
  info(`Authenticating with ${apiUrl}...`);
2843
2315
  const { authUrl, callbackPromise } = await handleLogin(apiUrl, appUrl, store);
2844
2316
  info("Opening browser for authentication...");
@@ -2865,6 +2337,12 @@ async function runLogin(apiUrlOverride, appUrlOverride) {
2865
2337
  } catch {}
2866
2338
  dim("Waiting for authentication callback...");
2867
2339
  const result = await callbackPromise;
2340
+ if (json) {
2341
+ if (!result.success)
2342
+ throw new Error(result.error ?? "Login failed.");
2343
+ console.log(JSON.stringify(result));
2344
+ return;
2345
+ }
2868
2346
  if (result.success) {
2869
2347
  try {
2870
2348
  const creds = await store.load();
@@ -2882,8 +2360,14 @@ async function runLogin(apiUrlOverride, appUrlOverride) {
2882
2360
  error(result.error ?? "Login failed.", 1);
2883
2361
  }
2884
2362
  }
2885
- async function runLogout() {
2363
+ async function runLogout(json) {
2886
2364
  const result = await handleLogout(new CredentialStore);
2365
+ if (json) {
2366
+ console.log(JSON.stringify(result));
2367
+ if (!result.success)
2368
+ process.exitCode = 1;
2369
+ return;
2370
+ }
2887
2371
  if (result.success) {
2888
2372
  success(result.message);
2889
2373
  return;
@@ -2895,30 +2379,38 @@ async function main() {
2895
2379
  const args = parseArgs(process.argv.slice(2));
2896
2380
  switch (args.command) {
2897
2381
  case "help":
2898
- renderHelp();
2382
+ renderHelp(args.json);
2899
2383
  return;
2900
2384
  case "version":
2901
- console.log(VERSION);
2385
+ console.log(args.json ? JSON.stringify({ version: VERSION }) : VERSION);
2902
2386
  return;
2903
2387
  case "unknown":
2904
2388
  error(`Unknown command: ${args.unknown}`, 1);
2905
2389
  return;
2906
2390
  case "login":
2907
- await runLogin(args.apiUrl, args.appUrl);
2391
+ await runLogin(args.loginTarget, args.apiUrl, args.appUrl, args.json);
2908
2392
  return;
2909
2393
  case "logout":
2910
- await runLogout();
2394
+ await runLogout(args.json);
2911
2395
  return;
2912
2396
  }
2913
2397
  const client = await requireAuth(args.apiUrl);
2914
2398
  switch (args.command) {
2399
+ case "docs":
2400
+ await handleDocs(client, args.positionals?.[0], args.json);
2401
+ return;
2402
+ case "agent":
2403
+ if (args.subcommand !== "me")
2404
+ throw new Error("Usage: index agent me");
2405
+ console.log(JSON.stringify(await client.getAgent(), null, args.json ? undefined : 2));
2406
+ return;
2915
2407
  case "profile":
2916
2408
  await handleProfile(client, args.subcommand, args.subcommand === "search" || args.subcommand === "update" ? args.positionals ?? [] : args.userId ? [args.userId] : [], {
2917
2409
  json: args.json,
2918
- linkedin: args.linkedin,
2919
- github: args.github,
2920
- twitter: args.twitter,
2921
- details: args.details
2410
+ name: args.name,
2411
+ intro: args.intro,
2412
+ location: args.location,
2413
+ socials: args.socials
2922
2414
  });
2923
2415
  return;
2924
2416
  case "intent":
@@ -2928,24 +2420,31 @@ async function main() {
2928
2420
  archived: args.archived,
2929
2421
  limit: args.limit,
2930
2422
  json: args.json,
2931
- targetId: args.targetId
2423
+ targetId: args.targetId,
2424
+ query: args.query,
2425
+ receipt: args.receipt,
2426
+ answers: args.answers
2932
2427
  });
2933
2428
  return;
2934
2429
  case "opportunity":
2935
2430
  await handleOpportunity(client, args.subcommand, {
2936
2431
  targetId: args.targetId,
2937
2432
  status: args.status,
2433
+ statuses: args.statuses,
2434
+ intentId: args.intentId,
2938
2435
  limit: args.limit,
2939
2436
  json: args.json,
2940
- positionals: args.positionals,
2941
- acknowledgeUptake: args.acknowledgeUptake
2437
+ positionals: args.positionals
2942
2438
  });
2943
2439
  return;
2944
2440
  case "negotiation":
2945
2441
  await handleNegotiation(client, args.subcommand, {
2946
2442
  targetId: args.targetId,
2947
- limit: args.limit,
2948
- since: args.since,
2443
+ intentId: args.intentId,
2444
+ state: args.state,
2445
+ action: args.action,
2446
+ message: args.message,
2447
+ expectedTurnCount: args.expectedTurnCount,
2949
2448
  json: args.json
2950
2449
  });
2951
2450
  return;
@@ -2959,12 +2458,10 @@ async function main() {
2959
2458
  case "conversation":
2960
2459
  await handleConversation(client, args.subcommand, args.positionals ?? [], {
2961
2460
  limit: args.limit,
2962
- json: args.json
2963
- });
2964
- return;
2965
- case "contact":
2966
- await handleContact(client, args.subcommand, args.positionals ?? [], {
2967
- json: args.json
2461
+ json: args.json,
2462
+ intentId: args.intentId,
2463
+ questionId: args.questionId,
2464
+ answers: args.answers
2968
2465
  });
2969
2466
  return;
2970
2467
  case "scrape":
@@ -2974,7 +2471,7 @@ async function main() {
2974
2471
  });
2975
2472
  return;
2976
2473
  case "onboarding":
2977
- await handleOnboarding(client, args.subcommand, { json: args.json });
2474
+ await handleOnboarding(client, args.subcommand, { json: args.json, intentId: args.intentId });
2978
2475
  return;
2979
2476
  case "sync":
2980
2477
  await handleSync(client, { json: args.json });
@@ -2982,5 +2479,9 @@ async function main() {
2982
2479
  }
2983
2480
  }
2984
2481
  main().catch((err) => {
2985
- error(err instanceof Error ? err.message : String(err), 1);
2482
+ if (process.argv.includes("--json")) {
2483
+ console.log(JSON.stringify(err instanceof ApiError ? { error: err.message, status: err.status, response: err.response } : { error: err instanceof Error ? err.message : String(err) }));
2484
+ process.exitCode = 1;
2485
+ } else
2486
+ error(err instanceof Error ? err.message : String(err), 1);
2986
2487
  });