@indexnetwork/cli 0.17.0-rc.66.1 → 0.29.0-rc.88.2

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 +194 -70
  2. package/dist/index.js +1001 -1221
  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.17.0",
8
+ version: "0.29.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.17.0",
43
- "@indexnetwork/cli-linux-arm64": "0.17.0",
44
- "@indexnetwork/cli-darwin-x64": "0.17.0",
45
- "@indexnetwork/cli-darwin-arm64": "0.17.0"
28
+ "@indexnetwork/cli-linux-x64": "0.29.0",
29
+ "@indexnetwork/cli-linux-arm64": "0.29.0",
30
+ "@indexnetwork/cli-darwin-x64": "0.29.0",
31
+ "@indexnetwork/cli-darwin-arm64": "0.29.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;
631
+ }
632
+ async scrapeUrl(url, objective) {
633
+ const res = await this.post("/api/scrape", { url, ...objective ? { objective } : {} });
634
+ return await res.json();
635
+ }
636
+ async readDocs(topic) {
637
+ const path = topic ? `/api/docs?${new URLSearchParams({ topic })}` : "/api/docs";
638
+ return (await this.get(path)).json();
527
639
  }
528
- async createUserContext(query) {
529
- return this.callTool("create_user_context", query);
640
+ async getAgent() {
641
+ return await (await this.get("/api/agents/me")).json();
530
642
  }
531
- async updateUserContext(query) {
532
- return this.callTool("update_user_context", query);
643
+ async getNegotiation(opportunityId) {
644
+ const body = await (await this.get(`/api/opportunities/${encodeURIComponent(opportunityId)}/negotiation`)).json();
645
+ return body.negotiation;
533
646
  }
534
- async callTool(toolName, query = {}) {
535
- const res = await this.post(`/api/tools/${toolName}`, { query });
536
- return await res.json();
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 (await this.get(`/api/conversations/agent/messages?${new URLSearchParams({ intentId })}`)).json();
653
+ }
654
+ async confirmProfile() {
655
+ return await (await this.post("/api/auth/onboarding/confirm-profile", {})).json();
656
+ }
657
+ async completeOnboarding(intentId) {
658
+ return await (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 = [];
@@ -1264,21 +1083,9 @@ function profileCard(data) {
1264
1083
  console.log(output);
1265
1084
  return output;
1266
1085
  }
1267
- function contactTable(contacts) {
1268
- if (contacts.length === 0) {
1269
- console.log(" No contacts yet.");
1270
- return;
1271
- }
1272
- const nameWidth = Math.max(6, ...contacts.map((c) => c.name.length));
1273
- const header = ` ${"Name".padEnd(nameWidth)} Email`;
1274
- console.log(`${BOLD}${header}${RESET}`);
1275
- for (const c of contacts) {
1276
- console.log(` ${c.name.padEnd(nameWidth)} ${c.email}`);
1277
- }
1278
- }
1279
1086
  function intentTable(intents) {
1280
1087
  if (intents.length === 0) {
1281
- dim(" No signals found.");
1088
+ dim(" No intents found.");
1282
1089
  return;
1283
1090
  }
1284
1091
  const idWidth = 8;
@@ -1286,12 +1093,12 @@ function intentTable(intents) {
1286
1093
  const statusWidth = 10;
1287
1094
  const sourceWidth = 16;
1288
1095
  const dateWidth = 20;
1289
- console.log(` ${BOLD}${"ID".padEnd(idWidth)} ${"Signal".padEnd(descWidth)} ${"Status".padEnd(statusWidth)} ${"Source".padEnd(sourceWidth)} ${"Created".padEnd(dateWidth)}${RESET}`);
1096
+ console.log(` ${BOLD}${"ID".padEnd(idWidth)} ${"Intent".padEnd(descWidth)} ${"Status".padEnd(statusWidth)} ${"Source".padEnd(sourceWidth)} ${"Created".padEnd(dateWidth)}${RESET}`);
1290
1097
  console.log(` ${GRAY}${"-".repeat(idWidth)} ${"-".repeat(descWidth)} ${"-".repeat(statusWidth)} ${"-".repeat(sourceWidth)} ${"-".repeat(dateWidth)}${RESET}`);
1291
1098
  for (const intent of intents) {
1292
1099
  const shortId = intent.id.slice(0, 8);
1293
1100
  const desc = (intent.summary ?? intent.payload).slice(0, descWidth);
1294
- const status2 = (intent.status ?? "").padEnd(statusWidth);
1101
+ const status = (intent.status ?? "").padEnd(statusWidth);
1295
1102
  const source = (intent.sourceType ?? "-").padEnd(sourceWidth);
1296
1103
  const date = new Date(intent.createdAt).toLocaleDateString("en-US", {
1297
1104
  year: "numeric",
@@ -1299,12 +1106,48 @@ function intentTable(intents) {
1299
1106
  day: "numeric"
1300
1107
  });
1301
1108
  const sColor = intent.status === "ACTIVE" ? GREEN : GRAY;
1302
- 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}`);
1303
1110
  }
1304
1111
  }
1112
+ function intentPreparation(prepared) {
1113
+ if (prepared.status === "ready") {
1114
+ success("Intent is ready to create.");
1115
+ console.log();
1116
+ for (const line of wordWrap(prepared.payload, 72))
1117
+ console.log(` ${line}`);
1118
+ console.log();
1119
+ console.log(` ${BOLD}Create it:${RESET}`);
1120
+ console.log(` index intent create ${shellQuote(prepared.payload)} --receipt ${shellQuote(prepared.preparationReceipt)}`);
1121
+ console.log();
1122
+ return;
1123
+ }
1124
+ console.log();
1125
+ console.log(` ${BOLD}${YELLOW}Needs revision${RESET}`);
1126
+ for (const line of wordWrap(prepared.feedback, 72))
1127
+ console.log(` ${line}`);
1128
+ console.log();
1129
+ console.log(` ${BOLD}Answer these:${RESET}`);
1130
+ for (const field of prepared.recovery) {
1131
+ console.log(` ${CYAN}*${RESET} ${field.label}`);
1132
+ for (const option of field.options ?? []) {
1133
+ const description = option.description ? `${GRAY} — ${option.description}${RESET}` : "";
1134
+ console.log(` ${option.label}${description}`);
1135
+ }
1136
+ if (field.placeholder)
1137
+ console.log(` ${GRAY}${field.placeholder}${RESET}`);
1138
+ }
1139
+ console.log();
1140
+ const answerFlags = prepared.recovery.map((field) => `--answer ${shellQuote(`${field.label}=<reply>`)}`);
1141
+ console.log(` ${BOLD}Then run:${RESET}`);
1142
+ console.log(` index intent prepare ${shellQuote(prepared.payload)} ${answerFlags.join(" ")}`);
1143
+ console.log();
1144
+ }
1145
+ function shellQuote(value) {
1146
+ return `'${value.replace(/'/g, "'\\''")}'`;
1147
+ }
1305
1148
  function intentCard(intent) {
1306
1149
  console.log();
1307
- console.log(` ${BOLD}${CYAN}Signal Details${RESET}`);
1150
+ console.log(` ${BOLD}${CYAN}Intent Details${RESET}`);
1308
1151
  console.log(` ${GRAY}${"─".repeat(50)}${RESET}`);
1309
1152
  console.log(` ${BOLD}ID${RESET} ${GRAY}${intent.id}${RESET}`);
1310
1153
  console.log(` ${BOLD}Status${RESET} ${intent.status === "ACTIVE" ? GREEN : GRAY}${intent.status}${RESET}`);
@@ -1339,10 +1182,10 @@ function intentCard(intent) {
1339
1182
  if (intent.archivedAt) {
1340
1183
  console.log(` ${BOLD}Archived${RESET} ${GRAY}${new Date(intent.archivedAt).toLocaleString()}${RESET}`);
1341
1184
  }
1342
- if (intent.indexes && intent.indexes.length > 0) {
1185
+ if (intent.networks && intent.networks.length > 0) {
1343
1186
  console.log();
1344
- console.log(` ${BOLD}Index Assignments${RESET}`);
1345
- for (const idx of intent.indexes) {
1187
+ console.log(` ${BOLD}Networks${RESET}`);
1188
+ for (const idx of intent.networks) {
1346
1189
  const score = idx.relevancyScore !== undefined ? ` (${idx.relevancyScore.toFixed(2)})` : "";
1347
1190
  console.log(` ${CYAN}*${RESET} ${idx.title}${GRAY}${score}${RESET}`);
1348
1191
  }
@@ -1368,35 +1211,25 @@ function opportunityTable(opportunities) {
1368
1211
  }
1369
1212
  const idW = 8;
1370
1213
  const nameW = 20;
1371
- const catW = 18;
1372
- const statusW = 10;
1373
- const confW = 8;
1374
- const dateW = 20;
1375
- process.stdout.write(` ${BOLD}${"ID".padEnd(idW)} ${"Counterparty".padEnd(nameW)} ${"Category".padEnd(catW)} ${"Status".padEnd(statusW)} ${"Conf".padEnd(confW)} ${"Created".padEnd(dateW)}${RESET}
1214
+ const statusW = 12;
1215
+ const headlineW = 48;
1216
+ process.stdout.write(` ${BOLD}${"ID".padEnd(idW)} ${"Counterparty".padEnd(nameW)} ${"Status".padEnd(statusW)} Headline${RESET}
1376
1217
  `);
1377
- process.stdout.write(` ${GRAY}${"-".repeat(idW)} ${"-".repeat(nameW)} ${"-".repeat(catW)} ${"-".repeat(statusW)} ${"-".repeat(confW)} ${"-".repeat(dateW)}${RESET}
1218
+ process.stdout.write(` ${GRAY}${"-".repeat(idW)} ${"-".repeat(nameW)} ${"-".repeat(statusW)} ${"-".repeat(headlineW)}${RESET}
1378
1219
  `);
1379
1220
  for (const opp of opportunities) {
1380
- const shortId = opp.id.slice(0, 8);
1381
- const fallbackName = opp.actors?.[1]?.name ?? opp.actors?.find((a) => a.name)?.name;
1382
- const name = (opp.counterpartName ?? fallbackName ?? "Unknown").slice(0, nameW);
1383
- const category = (opp.interpretation?.category ?? "-").slice(0, catW);
1221
+ const shortId = opp.opportunityId.slice(0, idW);
1222
+ const name = opp.peer.name.slice(0, nameW);
1384
1223
  const st = opp.status.slice(0, statusW);
1385
- const rawConf = opp.interpretation?.confidence;
1386
- const conf = rawConf != null ? `${Math.round(rawConf <= 1 ? rawConf * 100 : rawConf)}%` : "-";
1387
- const date = opp.createdAt ? new Date(opp.createdAt).toLocaleDateString("en-US", {
1388
- year: "numeric",
1389
- month: "short",
1390
- day: "numeric"
1391
- }) : "-";
1392
- 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}
1224
+ const headline = (opp.headline ?? opp.mainText).slice(0, headlineW);
1225
+ process.stdout.write(` ${CYAN}${shortId}${RESET} ${name.padEnd(nameW)} ${statusColor(st)}${st.padEnd(statusW)}${RESET} ${headline}
1393
1226
  `);
1394
1227
  }
1395
1228
  }
1396
1229
  function opportunityCard(opp) {
1397
1230
  const width = 58;
1398
1231
  const innerWidth = width - 2;
1399
- const title = opp.presentation?.title ?? "Opportunity";
1232
+ const title = opp.headline ?? "Opportunity";
1400
1233
  process.stdout.write(`
1401
1234
  ${BLUE}+${"─".repeat(width)}+${RESET}
1402
1235
  `);
@@ -1407,13 +1240,15 @@ function opportunityCard(opp) {
1407
1240
  cardLine(`${BOLD}Status:${RESET} ${statusColor(st)}${st}${RESET}`);
1408
1241
  if (opp.category)
1409
1242
  cardLine(`${BOLD}Category:${RESET} ${opp.category}`);
1410
- if (opp.index?.title)
1411
- cardLine(`${BOLD}Network:${RESET} ${opp.index.title}`);
1243
+ if (opp.network?.title)
1244
+ cardLine(`${BOLD}Network:${RESET} ${opp.network.title}`);
1412
1245
  if (opp.confidence != null)
1413
1246
  cardLine(`${BOLD}Confidence:${RESET} ${confidenceBar(opp.confidence)}`);
1414
1247
  if (opp.myRole)
1415
1248
  cardLine(`${BOLD}Your role:${RESET} ${roleLabel(opp.myRole)}`);
1416
- if (opp.otherParties && opp.otherParties.length > 0) {
1249
+ if (opp.peer?.name)
1250
+ cardLine(`${BOLD}With:${RESET} ${opp.peer.name}`);
1251
+ else if (opp.otherParties && opp.otherParties.length > 0) {
1417
1252
  process.stdout.write(` ${BLUE}|${RESET}
1418
1253
  `);
1419
1254
  cardLine(`${BOLD}With:${RESET}`);
@@ -1421,11 +1256,11 @@ function opportunityCard(opp) {
1421
1256
  cardLine(` ${p.name ?? p.id} ${roleLabel(p.role)}`);
1422
1257
  }
1423
1258
  }
1424
- if (opp.presentation?.description) {
1259
+ if (opp.mainText) {
1425
1260
  process.stdout.write(` ${BLUE}|${RESET}
1426
1261
  `);
1427
1262
  cardLine(`${BOLD}Details:${RESET}`);
1428
- for (const line of wordWrap(opp.presentation.description, innerWidth - 4)) {
1263
+ for (const line of wordWrap(opp.mainText, innerWidth - 4)) {
1429
1264
  cardLine(` ${AGENT_TEXT}${line}${RESET}`);
1430
1265
  }
1431
1266
  }
@@ -1463,6 +1298,173 @@ function statusColor(st) {
1463
1298
  return "";
1464
1299
  }
1465
1300
  }
1301
+ var ACTION_COLORS = {
1302
+ propose: CYAN,
1303
+ counter: BLUE,
1304
+ accept: GREEN,
1305
+ decline: RED
1306
+ };
1307
+ function shortDateTime(iso) {
1308
+ return new Date(iso).toLocaleString("en-US", {
1309
+ month: "short",
1310
+ day: "numeric",
1311
+ hour: "2-digit",
1312
+ minute: "2-digit"
1313
+ });
1314
+ }
1315
+ function outcomeLabel(outcome) {
1316
+ switch (outcome) {
1317
+ case "agreed":
1318
+ return `${GREEN}agreed${RESET}`;
1319
+ case "declined":
1320
+ return `${RED}declined${RESET}`;
1321
+ case "closed":
1322
+ return `${GRAY}closed${RESET}`;
1323
+ case null:
1324
+ return `${YELLOW}open${RESET}`;
1325
+ }
1326
+ }
1327
+ function whoseTurn(negotiation) {
1328
+ if (negotiation.awaitingUserId === null)
1329
+ return "nobody";
1330
+ return negotiation.awaitingUserId === negotiation.counterparty.userId ? "them" : "you";
1331
+ }
1332
+ function counterpartyName(negotiation) {
1333
+ return negotiation.counterparty.name ?? "(unnamed)";
1334
+ }
1335
+ function negotiationTable(negotiations) {
1336
+ if (negotiations.length === 0) {
1337
+ dim(" No negotiations found.");
1338
+ return;
1339
+ }
1340
+ const idW = 8;
1341
+ const nameW = 22;
1342
+ const turnsW = 5;
1343
+ const turnW = 10;
1344
+ const outcomeW = 8;
1345
+ const dateW = 12;
1346
+ console.log(` ${BOLD}${"ID".padEnd(idW)} ${"Counterparty".padEnd(nameW)} ${"Turns".padEnd(turnsW)} ${"Whose turn".padEnd(turnW)} ${"Outcome".padEnd(outcomeW)} ${"Updated".padEnd(dateW)}${RESET}`);
1347
+ console.log(` ${GRAY}${"-".repeat(idW)} ${"-".repeat(nameW)} ${"-".repeat(turnsW)} ${"-".repeat(turnW)} ${"-".repeat(outcomeW)} ${"-".repeat(dateW)}${RESET}`);
1348
+ for (const negotiation of negotiations) {
1349
+ const shortId = negotiation.opportunityId.slice(0, idW);
1350
+ const name = counterpartyName(negotiation).slice(0, nameW);
1351
+ const turns = String(negotiation.turnCount);
1352
+ const turn = whoseTurn(negotiation);
1353
+ let turnCell = `${GRAY}${"—".padEnd(turnW)}${RESET}`;
1354
+ if (turn === "you")
1355
+ turnCell = `${YELLOW}${BOLD}${"you".padEnd(turnW)}${RESET}`;
1356
+ if (turn === "them")
1357
+ turnCell = `${GRAY}${"them".padEnd(turnW)}${RESET}`;
1358
+ const outcome = outcomeLabel(negotiation.outcome);
1359
+ const date = new Date(negotiation.updatedAt).toLocaleDateString("en-US", {
1360
+ year: "numeric",
1361
+ month: "short",
1362
+ day: "numeric"
1363
+ });
1364
+ console.log(` ${CYAN}${shortId}${RESET} ${name.padEnd(nameW)} ${turns.padEnd(turnsW)} ${turnCell} ${outcome}${padTo(outcomeW, stripAnsi(outcome))} ${GRAY}${date}${RESET}`);
1365
+ }
1366
+ }
1367
+ function negotiationCard(negotiation) {
1368
+ const name = counterpartyName(negotiation);
1369
+ const rule = ` ${GRAY}${"─".repeat(60)}${RESET}`;
1370
+ let turnLabel = `${GRAY}— settled${RESET}`;
1371
+ if (whoseTurn(negotiation) === "you")
1372
+ turnLabel = `${YELLOW}${BOLD}you${RESET}`;
1373
+ if (whoseTurn(negotiation) === "them")
1374
+ turnLabel = name;
1375
+ console.log();
1376
+ console.log(` ${BOLD}${CYAN}Negotiation with ${name}${RESET}`);
1377
+ console.log(rule);
1378
+ console.log(` ${BOLD}Opportunity${RESET} ${GRAY}${negotiation.opportunityId}${RESET}`);
1379
+ console.log(` ${BOLD}Outcome${RESET} ${outcomeLabel(negotiation.outcome)}`);
1380
+ console.log(` ${BOLD}Turn${RESET} ${negotiation.turnCount} of ${negotiation.protocol.maxTurns}`);
1381
+ console.log(` ${BOLD}Whose turn${RESET} ${turnLabel}`);
1382
+ const [firstLine, ...moreLines] = wordWrap(negotiation.counterparty.statement, 58);
1383
+ console.log(` ${BOLD}Their intent${RESET} ${firstLine}`);
1384
+ for (const line of moreLines)
1385
+ console.log(` ${line}`);
1386
+ console.log();
1387
+ console.log(` ${BOLD}Transcript${RESET}`);
1388
+ if (negotiation.turns.length === 0)
1389
+ console.log(` ${GRAY}No turns yet.${RESET}`);
1390
+ for (const turn of negotiation.turns) {
1391
+ const seat = turn.seatUserId === negotiation.counterparty.userId ? name : "You";
1392
+ console.log(` ${GRAY}${turn.turnIndex + 1}.${RESET} ${BOLD}${seat}${RESET} ${ACTION_COLORS[turn.action]}${turn.action}${RESET} ${GRAY}${shortDateTime(turn.createdAt)}${RESET}`);
1393
+ for (const line of wordWrap(turn.message, 66))
1394
+ console.log(` ${line}`);
1395
+ }
1396
+ const { availableActions, blockedReason, messageLimit } = negotiation.protocol;
1397
+ console.log();
1398
+ if (availableActions.length > 0) {
1399
+ const actions = availableActions.map((action) => `${ACTION_COLORS[action]}${action}${RESET}`).join(", ");
1400
+ console.log(` ${BOLD}Available actions:${RESET} ${actions} ${GRAY}(message up to ${messageLimit} characters)${RESET}`);
1401
+ } else {
1402
+ console.log(` ${BOLD}Available actions:${RESET} ${GRAY}none${RESET}`);
1403
+ }
1404
+ if (blockedReason)
1405
+ console.log(` ${BOLD}Blocked:${RESET} ${YELLOW}${blockedReason}${RESET}`);
1406
+ console.log(rule);
1407
+ if (availableActions.length > 0) {
1408
+ dim(` Reply: index negotiation turn ${negotiation.opportunityId.slice(0, 8)} --action <action> --message <text> --expected-turn-count ${negotiation.turnCount}`);
1409
+ }
1410
+ console.log();
1411
+ }
1412
+ function agentCard(selected) {
1413
+ const { agent, onboardingCompletedAt } = selected;
1414
+ const rule = ` ${GRAY}${"─".repeat(50)}${RESET}`;
1415
+ console.log();
1416
+ console.log(` ${BOLD}${CYAN}${agent.name}${RESET}`);
1417
+ console.log(rule);
1418
+ console.log(` ${BOLD}Seat${RESET} External negotiator (negotiates instead of Index's hosted agent)`);
1419
+ console.log(` ${BOLD}Status${RESET} ${agent.status === "active" ? GREEN : GRAY}${agent.status}${RESET}`);
1420
+ if (agent.description)
1421
+ console.log(` ${BOLD}Description${RESET} ${agent.description}`);
1422
+ console.log(` ${BOLD}Last seen${RESET} ${GRAY}${agent.lastSeenAt ? shortDateTime(agent.lastSeenAt) : "never"}${RESET}`);
1423
+ const onboarding = onboardingCompletedAt ? `${GREEN}complete${RESET} ${GRAY}(${shortDateTime(onboardingCompletedAt)})${RESET}` : `${YELLOW}not complete${RESET}`;
1424
+ console.log(` ${BOLD}Onboarding${RESET} ${onboarding}`);
1425
+ console.log(` ${BOLD}ID${RESET} ${GRAY}${agent.id}${RESET}`);
1426
+ console.log(rule);
1427
+ console.log();
1428
+ }
1429
+ function agentConversation(conversation) {
1430
+ const seat = conversation.agent.status === "hosted" ? "Index's hosted agent" : "your external negotiator";
1431
+ const rule = ` ${GRAY}${"─".repeat(60)}${RESET}`;
1432
+ console.log();
1433
+ console.log(` ${BOLD}${CYAN}Personal agent${RESET} ${GRAY}answered by ${seat}${RESET}`);
1434
+ console.log(rule);
1435
+ if (conversation.messages.length === 0)
1436
+ console.log(` ${GRAY}No messages yet.${RESET}`);
1437
+ for (const message of conversation.messages) {
1438
+ const author = message.role === "user" ? `${BOLD}${CYAN}you${RESET}` : `${BOLD}${BLUE}agent${RESET}`;
1439
+ const text = message.parts.filter((part) => part.kind === "text" && part.text).map((part) => part.text).join(`
1440
+ `);
1441
+ console.log(` ${author} ${GRAY}${shortDateTime(message.createdAt)}${RESET}`);
1442
+ for (const line of wordWrap(text, 70))
1443
+ console.log(` ${line}`);
1444
+ console.log();
1445
+ }
1446
+ const { questions } = conversation.agent;
1447
+ if (questions.length === 0) {
1448
+ console.log(` ${GRAY}No questions waiting for you.${RESET}`);
1449
+ console.log();
1450
+ return;
1451
+ }
1452
+ console.log(` ${BOLD}${YELLOW}Waiting for your answer (${questions.length})${RESET}`);
1453
+ for (const question of questions) {
1454
+ const [firstLine, ...moreLines] = wordWrap(question.question, 66);
1455
+ console.log(` ${YELLOW}?${RESET} ${BOLD}${firstLine}${RESET}`);
1456
+ for (const line of moreLines)
1457
+ console.log(` ${BOLD}${line}${RESET}`);
1458
+ console.log(` ${GRAY}Question ID${RESET} ${CYAN}${question.id}${RESET}`);
1459
+ if (question.options?.length)
1460
+ console.log(` ${GRAY}Options${RESET} ${question.options.join(" / ")}`);
1461
+ if (question.matches.length > 0) {
1462
+ const about = question.matches.map((match) => `${match.counterparty.name ?? "(unnamed)"} ${GRAY}(${match.opportunityId.slice(0, 8)})${RESET}`).join(", ");
1463
+ console.log(` ${GRAY}About${RESET} ${about}`);
1464
+ }
1465
+ console.log();
1466
+ }
1467
+ }
1466
1468
  function networkTable(networks) {
1467
1469
  if (networks.length === 0) {
1468
1470
  dim(" No networks found.");
@@ -1588,7 +1590,7 @@ function messageList(messages) {
1588
1590
  month: "short",
1589
1591
  day: "numeric"
1590
1592
  });
1591
- const textParts = msg.parts.filter((p) => p.type === "text" && p.text).map((p) => p.text).join("");
1593
+ const textParts = msg.parts.filter((p) => p.kind === "text" && p.text).map((p) => p.text).join("");
1592
1594
  console.log(` ${CYAN}${sender}${RESET} ${GRAY}${date} ${time}${RESET}`);
1593
1595
  console.log(` ${textParts}`);
1594
1596
  console.log();
@@ -1596,17 +1598,20 @@ function messageList(messages) {
1596
1598
  }
1597
1599
  // src/profile.command.ts
1598
1600
  async function handleProfile(client, subcommand, positionals, options = {}) {
1599
- if (subcommand === "create") {
1600
- await profileCreate(client, options, options?.json);
1601
- return;
1602
- }
1603
1601
  if (subcommand === "update") {
1604
- const action = positionals.join(" ");
1605
- if (!action) {
1606
- error("Usage: index profile update <action> [--details <text>]", 1);
1607
- return;
1602
+ const { name, intro, location, socials } = options;
1603
+ if (name === undefined && intro === undefined && location === undefined && socials === undefined) {
1604
+ throw new Error("Usage: index profile update [--name <text>] [--intro <text>] [--location <text>] [--social label=value]");
1608
1605
  }
1609
- await profileUpdate(client, action, options.details, options?.json);
1606
+ const user = await client.updateProfile({ name, intro, location, socials });
1607
+ if (options.json)
1608
+ console.log(JSON.stringify(user));
1609
+ else
1610
+ success("Profile updated.");
1611
+ return;
1612
+ }
1613
+ if (subcommand === "create" || subcommand === "search") {
1614
+ error(`profile ${subcommand} is not available; use "index profile sync" for public research prefill`, 1);
1610
1615
  return;
1611
1616
  }
1612
1617
  if (subcommand === "sync") {
@@ -1622,35 +1627,6 @@ async function handleProfile(client, subcommand, positionals, options = {}) {
1622
1627
  await profileShow(client, userId, options?.json);
1623
1628
  return;
1624
1629
  }
1625
- if (subcommand === "search") {
1626
- const query = positionals.join(" ");
1627
- if (!query) {
1628
- error("Usage: index profile search <query>", 1);
1629
- return;
1630
- }
1631
- const result = await client.readUserContexts({ query });
1632
- if (options.json) {
1633
- console.log(JSON.stringify(result));
1634
- return;
1635
- }
1636
- if (!result.success) {
1637
- error(result.error ?? "Search failed", 1);
1638
- return;
1639
- }
1640
- const data = result.data;
1641
- heading("Search Results");
1642
- if (!data.profiles?.length) {
1643
- dim(" No profiles found.");
1644
- } else {
1645
- for (const p of data.profiles) {
1646
- console.log(` ${p.name} (${p.userId.slice(0, 8)})`);
1647
- if (p.bio)
1648
- dim(` ${p.bio.slice(0, 100)}`);
1649
- }
1650
- }
1651
- console.log();
1652
- return;
1653
- }
1654
1630
  await profileMe(client, options?.json);
1655
1631
  }
1656
1632
  async function profileMe(client, json) {
@@ -1685,61 +1661,26 @@ async function profileSync(client, json) {
1685
1661
  return;
1686
1662
  }
1687
1663
  success("Profile enriched.");
1688
- if (result.profile.name)
1664
+ if (result.profile?.name)
1689
1665
  dim(` Name: ${result.profile.name}`);
1690
- if (result.profile.location)
1666
+ if (result.profile?.location)
1691
1667
  dim(` Location: ${result.profile.location}`);
1692
- dim(` Social links: ${result.profile.socials.length}`);
1693
- }
1694
- async function profileCreate(client, options, json) {
1695
- if (!json)
1696
- info("Creating profile...");
1697
- const query = { confirm: true };
1698
- if (options.linkedin)
1699
- query.linkedinUrl = options.linkedin;
1700
- if (options.github)
1701
- query.githubUrl = options.github;
1702
- if (options.twitter)
1703
- query.twitterUrl = options.twitter;
1704
- const result = await client.createUserContext(query);
1705
- if (json) {
1706
- console.log(JSON.stringify(result));
1707
- return;
1708
- }
1709
- if (!result.success) {
1710
- error(result.error ?? "Profile creation failed", 1);
1711
- return;
1712
- }
1713
- success("Profile created.");
1714
- }
1715
- async function profileUpdate(client, action, details, json) {
1716
- if (!json)
1717
- info("Updating profile...");
1718
- const query = { action };
1719
- if (details)
1720
- query.details = details;
1721
- const result = await client.updateUserContext(query);
1722
- if (json) {
1723
- console.log(JSON.stringify(result));
1724
- return;
1725
- }
1726
- if (!result.success) {
1727
- error(result.error ?? "Profile update failed", 1);
1728
- return;
1729
- }
1730
- success("Profile updated.");
1668
+ dim(` Social links: ${result.profile?.socials?.length ?? 0}`);
1731
1669
  }
1732
1670
 
1733
1671
  // src/intent.command.ts
1734
1672
  var INTENT_HELP = `
1735
1673
  Usage:
1736
- index intent list [--archived] [--limit <n>] List your signals
1737
- index intent show <id> Show signal details (accepts short ID)
1738
- index intent create <content> Create a signal from text
1739
- index intent update <id> <content> Update a signal's description
1740
- index intent archive <id> Archive a signal (accepts short ID)
1741
- index intent link <id> <network-id> Link a signal to a network
1742
- index intent unlink <id> <network-id> Unlink a signal from a network
1674
+ index intent list [--archived] [--limit <n>] [--query <text>] List your intents
1675
+ index intent show <id> Show intent details (accepts short ID)
1676
+ index intent prepare <content> [--answer 'prompt=reply'] Review and repair a draft
1677
+ index intent create <content> [--receipt <token>] Create an admitted intent
1678
+ index intent pause <id> | resume <id> Hold or restart its agent
1679
+ index intent update <id> <content> Update an intent's description
1680
+ index intent archive <id> Archive an intent (accepts short ID)
1681
+ index intent networks <id> List the networks an intent is shared in
1682
+ index intent add-to-network <id> <network-id> Add an intent to a network
1683
+ index intent remove-from-network <id> <network-id> Remove an intent from a network
1743
1684
  `;
1744
1685
  async function handleIntent(client, subcommand, options) {
1745
1686
  if (!subcommand) {
@@ -1754,13 +1695,14 @@ async function handleIntent(client, subcommand, options) {
1754
1695
  case "list": {
1755
1696
  const result = await client.listIntents({
1756
1697
  archived: options.archived,
1757
- limit: options.limit
1698
+ limit: options.limit,
1699
+ query: options.query
1758
1700
  });
1759
1701
  if (options.json) {
1760
1702
  console.log(JSON.stringify(result));
1761
1703
  return;
1762
1704
  }
1763
- heading("Signals");
1705
+ heading("Intents");
1764
1706
  intentTable(result.intents);
1765
1707
  if (result.pagination.totalCount > 0) {
1766
1708
  dim(`
@@ -1771,7 +1713,7 @@ async function handleIntent(client, subcommand, options) {
1771
1713
  }
1772
1714
  case "show": {
1773
1715
  if (!options.intentId) {
1774
- error("Missing signal ID. Usage: index intent show <id>", 1);
1716
+ error("Missing intent ID. Usage: index intent show <id>", 1);
1775
1717
  return;
1776
1718
  }
1777
1719
  const intent = await client.getIntent(options.intentId);
@@ -1782,39 +1724,36 @@ async function handleIntent(client, subcommand, options) {
1782
1724
  intentCard(intent);
1783
1725
  return;
1784
1726
  }
1727
+ case "prepare": {
1728
+ if (!options.intentContent)
1729
+ throw new Error("Usage: index intent prepare <content> [--answer 'prompt=reply']");
1730
+ const prepared = await client.prepareIntent(options.intentContent, (options.answers ?? []).map(({ key, value }) => ({ prompt: key, answer: value })));
1731
+ if (options.json) {
1732
+ console.log(JSON.stringify(prepared));
1733
+ return;
1734
+ }
1735
+ intentPreparation(prepared);
1736
+ return;
1737
+ }
1785
1738
  case "create": {
1786
1739
  if (!options.intentContent) {
1787
1740
  error("Missing content. Usage: index intent create <content>", 1);
1788
1741
  return;
1789
1742
  }
1790
1743
  if (!options.json)
1791
- info("Processing signal...");
1792
- const result = await client.callTool("create_intent", {
1793
- description: options.intentContent
1794
- });
1744
+ info("Processing intent...");
1745
+ const result = await client.createIntent(options.intentContent, options.targetId ? [options.targetId] : undefined, options.receipt);
1795
1746
  if (options.json) {
1796
1747
  console.log(JSON.stringify(result));
1797
1748
  return;
1798
1749
  }
1799
- if (!result.success) {
1800
- error(result.error ?? "Failed to create signal", 1);
1801
- return;
1802
- }
1803
- const proposals = parseIntentProposals(result.data?.message);
1804
- if (proposals.length === 0) {
1805
- error("Signal proposal could not be parsed from the response.", 1);
1806
- return;
1807
- }
1808
- for (const proposal of proposals) {
1809
- await client.confirmIntent(proposal.proposalId, proposal.description);
1810
- success("Signal created.");
1811
- dim(` ${proposal.description}`);
1812
- }
1750
+ success("Intent created.");
1751
+ dim(` shared in ${result.networkIds.length} network${result.networkIds.length === 1 ? "" : "s"}`);
1813
1752
  return;
1814
1753
  }
1815
1754
  case "update": {
1816
1755
  if (!options.intentId) {
1817
- error("Missing signal ID. Usage: index intent update <id> <content>", 1);
1756
+ error("Missing intent ID. Usage: index intent update <id> <content>", 1);
1818
1757
  return;
1819
1758
  }
1820
1759
  if (!options.intentContent) {
@@ -1822,108 +1761,96 @@ async function handleIntent(client, subcommand, options) {
1822
1761
  return;
1823
1762
  }
1824
1763
  if (!options.json)
1825
- info("Updating signal...");
1764
+ info("Updating intent...");
1826
1765
  const result = await client.updateIntent(options.intentId, options.intentContent);
1827
1766
  if (options.json) {
1828
1767
  console.log(JSON.stringify(result));
1829
1768
  return;
1830
1769
  }
1831
- if (!result.success) {
1832
- error(result.error ?? "Failed to update signal", 1);
1833
- return;
1834
- }
1835
- success("Signal updated.");
1770
+ success("Intent updated.");
1771
+ return;
1772
+ }
1773
+ case "pause":
1774
+ case "resume": {
1775
+ if (!options.intentId)
1776
+ throw new Error(`Usage: index intent ${subcommand} <id>`);
1777
+ const status = subcommand === "pause" ? "PAUSED" : "ACTIVE";
1778
+ await client.updateIntentStatus(options.intentId, status);
1779
+ if (options.json)
1780
+ console.log(JSON.stringify({ intentId: options.intentId, status }));
1781
+ else
1782
+ success(`Intent ${status.toLowerCase()}.`);
1836
1783
  return;
1837
1784
  }
1838
1785
  case "archive": {
1839
1786
  if (!options.intentId) {
1840
- error("Missing signal ID. Usage: index intent archive <id>", 1);
1787
+ error("Missing intent ID. Usage: index intent archive <id>", 1);
1841
1788
  return;
1842
1789
  }
1843
- const intent = await client.getIntent(options.intentId);
1844
- const result = await client.callTool("delete_intent", { intentId: intent.id });
1790
+ await client.archiveIntent(options.intentId);
1845
1791
  if (options.json) {
1846
- console.log(JSON.stringify(result));
1792
+ console.log(JSON.stringify({ success: true }));
1847
1793
  return;
1848
1794
  }
1849
- if (!result.success) {
1850
- error(result.error ?? "Failed to archive signal", 1);
1795
+ success(`Intent ${options.intentId} archived.`);
1796
+ return;
1797
+ }
1798
+ case "networks": {
1799
+ if (!options.intentId) {
1800
+ error("Usage: index intent networks <intent-id>", 1);
1851
1801
  return;
1852
1802
  }
1853
- success(`Signal ${options.intentId} archived.`);
1803
+ const networkIds = await client.listIntentNetworks(options.intentId);
1804
+ if (options.json) {
1805
+ console.log(JSON.stringify({ networkIds }));
1806
+ return;
1807
+ }
1808
+ heading("Networks");
1809
+ for (const networkId of networkIds)
1810
+ dim(` ${networkId}`);
1811
+ console.log();
1854
1812
  return;
1855
1813
  }
1856
- case "link": {
1814
+ case "add-to-network": {
1857
1815
  if (!options.intentId || !options.targetId) {
1858
- error("Usage: index intent link <intent-id> <network-id>", 1);
1816
+ error("Usage: index intent add-to-network <intent-id> <network-id>", 1);
1859
1817
  return;
1860
1818
  }
1861
- const intent = await client.getIntent(options.intentId);
1862
- const result = await client.callTool("create_intent_index", {
1863
- intentId: intent.id,
1864
- networkId: options.targetId
1865
- });
1819
+ await client.addIntentToNetwork(options.intentId, options.targetId);
1866
1820
  if (options.json) {
1867
- console.log(JSON.stringify(result));
1821
+ console.log(JSON.stringify({ success: true }));
1868
1822
  return;
1869
1823
  }
1870
- if (!result.success) {
1871
- error(result.error ?? "Failed to link signal", 1);
1872
- return;
1873
- }
1874
- success("Signal linked to network.");
1824
+ success("Intent added to network.");
1875
1825
  return;
1876
1826
  }
1877
- case "unlink": {
1827
+ case "remove-from-network": {
1878
1828
  if (!options.intentId || !options.targetId) {
1879
- error("Usage: index intent unlink <intent-id> <network-id>", 1);
1829
+ error("Usage: index intent remove-from-network <intent-id> <network-id>", 1);
1880
1830
  return;
1881
1831
  }
1882
- const intent = await client.getIntent(options.intentId);
1883
- const result = await client.callTool("delete_intent_index", {
1884
- intentId: intent.id,
1885
- networkId: options.targetId
1886
- });
1832
+ await client.removeIntentFromNetwork(options.intentId, options.targetId);
1887
1833
  if (options.json) {
1888
- console.log(JSON.stringify(result));
1889
- return;
1890
- }
1891
- if (!result.success) {
1892
- error(result.error ?? "Failed to unlink signal", 1);
1834
+ console.log(JSON.stringify({ success: true }));
1893
1835
  return;
1894
1836
  }
1895
- success("Signal unlinked from network.");
1837
+ success("Intent removed from network.");
1896
1838
  return;
1897
1839
  }
1898
1840
  }
1899
1841
  }
1900
- function parseIntentProposals(message) {
1901
- if (!message)
1902
- return [];
1903
- const proposals = [];
1904
- const blockRegex = /```intent_proposal\s*\n([\s\S]*?)\n```/g;
1905
- let match;
1906
- while ((match = blockRegex.exec(message)) !== null) {
1907
- try {
1908
- const parsed = JSON.parse(match[1]);
1909
- if (parsed.proposalId && parsed.description) {
1910
- proposals.push({ proposalId: parsed.proposalId, description: parsed.description });
1911
- }
1912
- } catch {}
1913
- }
1914
- return proposals;
1915
- }
1916
1842
 
1917
1843
  // src/opportunity.command.ts
1918
1844
  var OPPORTUNITY_HELP = `
1919
1845
  Usage:
1920
1846
  index opportunity list List your opportunities
1921
- index opportunity list --status <s> Filter by status (pending|accepted|rejected|expired)
1847
+ index opportunity list --intent-id <id> --statuses negotiating,pending,accepted,expired
1848
+ index opportunity list --status <s> Filter by one settled or actionable status
1922
1849
  index opportunity list --limit <n> Limit results
1923
1850
  index opportunity show <id> Show full opportunity details (accepts short ID)
1924
1851
  index opportunity accept <id> Accept an opportunity (accepts short ID)
1925
- index opportunity accept <id> --acknowledge-uptake <id,id> Continue after an uptake advisory
1926
1852
  index opportunity reject <id> Reject an opportunity (accepts short ID)
1853
+ index opportunity start-chat <id> Open the chat through this opportunity
1927
1854
  `;
1928
1855
  async function handleOpportunity(client, subcommand, options) {
1929
1856
  if (!subcommand) {
@@ -1936,7 +1863,7 @@ async function handleOpportunity(client, subcommand, options) {
1936
1863
  }
1937
1864
  switch (subcommand) {
1938
1865
  case "list":
1939
- await opportunityList(client, options.status, options.limit, options.json);
1866
+ await opportunityList(client, options.status, options.statuses, options.intentId, options.limit, options.json);
1940
1867
  return;
1941
1868
  case "show":
1942
1869
  if (!options.targetId) {
@@ -1950,7 +1877,7 @@ async function handleOpportunity(client, subcommand, options) {
1950
1877
  error("Missing opportunity ID. Usage: index opportunity accept <id>", 1);
1951
1878
  return;
1952
1879
  }
1953
- await opportunityStatusUpdate(client, options.targetId, "accepted", options.json, options.acknowledgeUptake);
1880
+ await opportunityStatusUpdate(client, options.targetId, "accepted", options.json);
1954
1881
  return;
1955
1882
  case "reject":
1956
1883
  if (!options.targetId) {
@@ -1959,13 +1886,23 @@ async function handleOpportunity(client, subcommand, options) {
1959
1886
  }
1960
1887
  await opportunityStatusUpdate(client, options.targetId, "rejected", options.json);
1961
1888
  return;
1889
+ case "start-chat": {
1890
+ if (!options.targetId)
1891
+ throw new Error("Usage: index opportunity start-chat <id>");
1892
+ const result = await client.startOpportunityChat(options.targetId);
1893
+ if (options.json)
1894
+ console.log(JSON.stringify(result));
1895
+ else
1896
+ success(`Chat opened: ${result.conversationId}`);
1897
+ return;
1898
+ }
1962
1899
  default:
1963
1900
  error(`Unknown subcommand: ${subcommand}`, 1);
1964
1901
  return;
1965
1902
  }
1966
1903
  }
1967
- async function opportunityList(client, status2, limit, json) {
1968
- const opportunities = await client.listOpportunities({ status: status2, limit });
1904
+ async function opportunityList(client, status, statuses, intentId, limit, json) {
1905
+ const opportunities = await client.listOpportunities({ status, statuses, intentId, limit });
1969
1906
  if (json) {
1970
1907
  console.log(JSON.stringify(opportunities));
1971
1908
  return;
@@ -1982,225 +1919,74 @@ async function opportunityShow(client, id, json) {
1982
1919
  }
1983
1920
  opportunityCard(opportunity);
1984
1921
  }
1985
- async function opportunityStatusUpdate(client, id, status2, json, acknowledgedUptakeQuestionIds) {
1922
+ async function opportunityStatusUpdate(client, id, status, json) {
1986
1923
  const opportunity = await client.getOpportunity(id);
1987
- if (status2 === "accepted") {
1988
- try {
1989
- const result2 = await client.updateOpportunityStatus(opportunity.id, "accepted", acknowledgedUptakeQuestionIds);
1990
- if (json) {
1991
- console.log(JSON.stringify(result2));
1992
- return;
1993
- }
1994
- success("Opportunity accepted.");
1995
- } catch (error2) {
1996
- const advisory = uptakeAdvisoryFromError(error2);
1997
- if (json && error2 instanceof ApiError) {
1998
- console.log(JSON.stringify(error2.response ?? { error: error2.message }));
1999
- return;
2000
- }
2001
- if (!advisory)
2002
- throw error2;
2003
- heading("Questions before accepting");
2004
- for (const question of advisory.advisory.questions) {
2005
- console.log(`
2006
- ${question.title}`);
2007
- console.log(question.prompt);
2008
- for (const option of question.options) {
2009
- console.log(` - ${option.label}${option.description ? ` — ${option.description}` : ""}`);
2010
- }
2011
- dim(` Question ID: ${question.id}`);
2012
- }
2013
- const ids = advisory.advisory.questions.map((question) => question.id).join(",");
2014
- info(`Answer or dismiss these questions, then retry. To continue anyway, run:
2015
- index opportunity accept ${opportunity.id} --acknowledge-uptake ${ids}`);
2016
- }
2017
- return;
2018
- }
2019
- const result = await client.updateOpportunityStatus(opportunity.id, "rejected");
1924
+ const result = await client.updateOpportunityStatus(opportunity.id, status);
2020
1925
  if (json) {
2021
1926
  console.log(JSON.stringify(result));
2022
1927
  return;
2023
1928
  }
2024
- success("Opportunity rejected.");
2025
- }
2026
- function uptakeAdvisoryFromError(error2) {
2027
- if (!(error2 instanceof ApiError) || error2.status !== 409)
2028
- return null;
2029
- const body = error2.response;
2030
- return body?.advisory?.code === "unresolved_uptake_questions" ? body : null;
1929
+ success(`Opportunity ${status}.`);
2031
1930
  }
2032
1931
 
2033
1932
  // src/negotiation.command.ts
2034
- var NEGOTIATION_HELP = `
2035
- Usage:
2036
- index negotiation list List your agent's negotiations
2037
- index negotiation list --limit <n> Limit results
2038
- index negotiation list --since <date|duration> Filter by time (ISO date or duration like 1h, 2d, 1w)
2039
- index negotiation show <id> Show negotiation turn-by-turn details
2040
- `;
2041
1933
  async function handleNegotiation(client, subcommand, options) {
2042
- if (!subcommand) {
1934
+ if (subcommand === "list") {
1935
+ if (options.state !== undefined && options.state !== "open" && options.state !== "settled")
1936
+ throw new Error("--state must be open or settled");
1937
+ const negotiations = await client.listNegotiations({ intentId: options.intentId, state: options.state });
2043
1938
  if (options.json) {
2044
- console.log(JSON.stringify({ error: "No subcommand provided" }));
2045
- } else {
2046
- console.log(NEGOTIATION_HELP);
2047
- }
2048
- return;
2049
- }
2050
- switch (subcommand) {
2051
- case "list":
2052
- await negotiationList(client, options.limit, options.since, options.json);
1939
+ console.log(JSON.stringify(negotiations));
2053
1940
  return;
2054
- case "show":
2055
- if (!options.targetId) {
2056
- error("Missing negotiation ID. Usage: index negotiation show <id>", 1);
1941
+ }
1942
+ heading("Negotiations");
1943
+ negotiationTable(negotiations);
1944
+ if (negotiations.length > 0)
1945
+ dim(`
1946
+ Details: index negotiation show <id> (the short ID works)`);
1947
+ console.log();
1948
+ } else if (subcommand === "show" || subcommand === "turn") {
1949
+ if (!options.targetId)
1950
+ throw new Error("An opportunity ID is required");
1951
+ if (subcommand === "show") {
1952
+ const negotiation = await client.getNegotiation(options.targetId);
1953
+ if (options.json) {
1954
+ console.log(JSON.stringify(negotiation));
2057
1955
  return;
2058
1956
  }
2059
- await negotiationShow(client, options.targetId, options.json);
2060
- return;
2061
- default:
2062
- error(`Unknown subcommand: ${subcommand}`, 1);
2063
- return;
2064
- }
2065
- }
2066
- function resolveSince(input) {
2067
- const match = input.match(/^(\d+)([smhdw])$/);
2068
- if (match) {
2069
- const n = parseInt(match[1], 10);
2070
- const unit = match[2];
2071
- const ms = { s: 1000, m: 60000, h: 3600000, d: 86400000, w: 604800000 }[unit];
2072
- return new Date(Date.now() - n * ms).toISOString();
2073
- }
2074
- const d = new Date(input);
2075
- if (isNaN(d.getTime()))
2076
- throw new Error(`Invalid --since value: "${input}". Use ISO date or duration like 1h, 2d, 1w.`);
2077
- return d.toISOString();
2078
- }
2079
- async function negotiationList(client, limit, since, json) {
2080
- const sinceIso = since ? resolveSince(since) : undefined;
2081
- const negotiations = await client.listNegotiations({ limit, since: sinceIso });
2082
- if (json) {
2083
- console.log(JSON.stringify(negotiations));
2084
- return;
2085
- }
2086
- heading("Negotiations");
2087
- negotiationTable(negotiations);
2088
- console.log();
2089
- }
2090
- async function negotiationShow(client, id, json) {
2091
- const negotiations = await client.listNegotiations({ limit: 50 });
2092
- const match = negotiations.find((n) => n.id === id || n.id.startsWith(id));
2093
- if (!match) {
2094
- error(`Negotiation not found: ${id}`, 1);
2095
- return;
2096
- }
2097
- if (json) {
2098
- console.log(JSON.stringify(match));
2099
- return;
2100
- }
2101
- negotiationCard(match);
2102
- }
2103
- var { BOLD: BOLD2, RESET: RESET2, CYAN: CYAN2, GRAY: GRAY2, GREEN: GREEN2, YELLOW: YELLOW2, BLUE: BLUE2, DIM: DIM2 } = exports_output;
2104
- function outcomeLabel(outcome) {
2105
- if (!outcome)
2106
- return `${GRAY2}unknown${RESET2}`;
2107
- return outcome.hasOpportunity ? `${GREEN2}opportunity${RESET2}` : `${GRAY2}no match${RESET2}`;
2108
- }
2109
- function roleLabel2(role) {
2110
- switch (role) {
2111
- case "agent":
2112
- return `${GREEN2}helper${RESET2}`;
2113
- case "patient":
2114
- return `${YELLOW2}seeker${RESET2}`;
2115
- case "peer":
2116
- return `${CYAN2}peer${RESET2}`;
2117
- default:
2118
- return `${GRAY2}-${RESET2}`;
2119
- }
2120
- }
2121
- function actionColorFor(action) {
2122
- switch (action) {
2123
- case "accept":
2124
- return GREEN2;
2125
- case "reject":
2126
- return RED;
2127
- default:
2128
- return BLUE2;
2129
- }
2130
- }
2131
- function negotiationTable(negotiations) {
2132
- if (negotiations.length === 0) {
2133
- dim(" No negotiations found.");
2134
- return;
2135
- }
2136
- const idW = 8;
2137
- const nameW = 22;
2138
- const outcomeW = 14;
2139
- const roleW = 8;
2140
- const turnsW = 6;
2141
- const dateW = 20;
2142
- process.stdout.write(` ${BOLD2}${"ID".padEnd(idW)} ${"Counterparty".padEnd(nameW)} ${"Outcome".padEnd(outcomeW)} ${"Role".padEnd(roleW)} ${"Turns".padEnd(turnsW)} ${"Created".padEnd(dateW)}${RESET2}
2143
- `);
2144
- process.stdout.write(` ${GRAY2}${"-".repeat(idW)} ${"-".repeat(nameW)} ${"-".repeat(outcomeW)} ${"-".repeat(roleW)} ${"-".repeat(turnsW)} ${"-".repeat(dateW)}${RESET2}
2145
- `);
2146
- for (const n of negotiations) {
2147
- const shortId = n.id.slice(0, 8);
2148
- const name = (n.counterparty?.name ?? "Unknown").slice(0, nameW);
2149
- const turns = String(n.outcome?.turnCount ?? n.turns?.length ?? "-");
2150
- const date = n.createdAt ? new Date(n.createdAt).toLocaleDateString("en-US", {
2151
- year: "numeric",
2152
- month: "short",
2153
- day: "numeric"
2154
- }) : "-";
2155
- const outcomeStr = outcomeLabel(n.outcome);
2156
- const roleStr = roleLabel2(n.outcome?.role);
2157
- 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}
2158
- `);
2159
- }
2160
- }
2161
- function negotiationCard(n) {
2162
- console.log();
2163
- console.log(` ${BOLD2}${CYAN2}Negotiation Details${RESET2}`);
2164
- console.log(` ${GRAY2}${"─".repeat(60)}${RESET2}`);
2165
- console.log(` ${BOLD2}ID${RESET2} ${GRAY2}${n.id}${RESET2}`);
2166
- console.log(` ${BOLD2}Counterparty${RESET2} ${n.counterparty?.name ?? "Unknown"}`);
2167
- console.log(` ${BOLD2}Outcome${RESET2} ${outcomeLabel(n.outcome)}`);
2168
- console.log(` ${BOLD2}Your Role${RESET2} ${roleLabel2(n.outcome?.role)}`);
2169
- console.log(` ${BOLD2}Turns${RESET2} ${n.outcome?.turnCount ?? n.turns?.length ?? "-"}`);
2170
- console.log(` ${BOLD2}Created${RESET2} ${GRAY2}${new Date(n.createdAt).toLocaleString()}${RESET2}`);
2171
- if (n.turns && n.turns.length > 0) {
2172
- console.log();
2173
- console.log(` ${BOLD2}Turn-by-Turn${RESET2}`);
2174
- console.log(` ${GRAY2}${"─".repeat(60)}${RESET2}`);
2175
- for (let i = 0;i < n.turns.length; i++) {
2176
- const t = n.turns[i];
2177
- const time = new Date(t.createdAt).toLocaleTimeString("en-US", {
2178
- hour: "2-digit",
2179
- minute: "2-digit",
2180
- second: "2-digit"
1957
+ negotiationCard(negotiation);
1958
+ } else {
1959
+ if (!options.action || !["propose", "counter", "accept", "decline"].includes(options.action))
1960
+ throw new Error("--action must be propose, counter, accept, or decline");
1961
+ if (!options.message?.trim())
1962
+ throw new Error("--message is required");
1963
+ if (options.expectedTurnCount === undefined || !Number.isSafeInteger(options.expectedTurnCount) || options.expectedTurnCount < 0)
1964
+ throw new Error("--expected-turn-count must be the observed nonnegative integer turn count");
1965
+ const negotiation = await client.submitNegotiationTurn(options.targetId, {
1966
+ action: options.action,
1967
+ message: options.message,
1968
+ expectedTurnCount: options.expectedTurnCount
2181
1969
  });
2182
- const actionColor = actionColorFor(t.action);
2183
- console.log(` ${DIM2}Turn ${i + 1}${RESET2} ${CYAN2}${t.speaker?.name ?? "?"}${RESET2} ${actionColor}${t.action}${RESET2} ${GRAY2}${time}${RESET2}`);
2184
- if (t.suggestedRoles) {
2185
- console.log(` ${DIM2} roles: own=${t.suggestedRoles.ownUser ?? "?"} other=${t.suggestedRoles.otherUser ?? "?"}${RESET2}`);
2186
- }
2187
- if (t.reasoning) {
2188
- const lines = wordWrap(t.reasoning, 72);
2189
- for (const line of lines) {
2190
- console.log(` ${DIM2} ${line}${RESET2}`);
2191
- }
1970
+ if (options.json) {
1971
+ console.log(JSON.stringify(negotiation));
1972
+ return;
2192
1973
  }
2193
- console.log();
1974
+ success(`Turn submitted: ${options.action}`);
1975
+ negotiationCard(negotiation);
2194
1976
  }
1977
+ } else {
1978
+ 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>");
2195
1979
  }
2196
- console.log(` ${GRAY2}${"─".repeat(60)}${RESET2}`);
2197
- console.log();
2198
1980
  }
2199
1981
 
2200
1982
  // src/network.command.ts
2201
1983
  var NETWORK_HELP = `
2202
1984
  Network Commands:
2203
1985
  index network list List your networks
1986
+ index network discover Find public networks to join
1987
+ index network requests See your network creation requests
1988
+ index network request-update <id> --title <name> [--prompt <purpose>]
1989
+ index network request-dismiss <id> Dismiss your request
2204
1990
  index network create <name> Create directly or submit an early-access request
2205
1991
  index network create <name> --prompt Create directly or request early access with a description
2206
1992
  index network show <id|key> Show network details and members
@@ -2220,6 +2006,47 @@ async function handleNetwork(client, subcommand, positionals, options) {
2220
2006
  case "list":
2221
2007
  await networkList(client, options.json);
2222
2008
  return;
2009
+ case "discover": {
2010
+ const result = await client.discoverNetworks();
2011
+ if (options.json)
2012
+ console.log(JSON.stringify(result));
2013
+ else {
2014
+ heading("Public networks");
2015
+ networkTable(result.networks);
2016
+ }
2017
+ return;
2018
+ }
2019
+ case "requests": {
2020
+ const result = await client.listNetworkRequests();
2021
+ if (options.json)
2022
+ console.log(JSON.stringify(result));
2023
+ else {
2024
+ heading("Your network requests");
2025
+ for (const request of result.requests)
2026
+ dim(` ${request.id} · ${request.title} · ${request.status}`);
2027
+ }
2028
+ return;
2029
+ }
2030
+ case "request-update": {
2031
+ if (!positionals[0] || !options.title)
2032
+ throw new Error("Usage: index network request-update <id> --title <name> [--prompt <purpose>]");
2033
+ const result = await client.updateNetworkRequest(positionals[0], options.title, options.prompt);
2034
+ if (options.json)
2035
+ console.log(JSON.stringify(result));
2036
+ else
2037
+ success("Network request resubmitted.");
2038
+ return;
2039
+ }
2040
+ case "request-dismiss": {
2041
+ if (!positionals[0])
2042
+ throw new Error("Usage: index network request-dismiss <id>");
2043
+ await client.dismissNetworkRequest(positionals[0]);
2044
+ if (options.json)
2045
+ console.log(JSON.stringify({ success: true }));
2046
+ else
2047
+ success("Network request dismissed.");
2048
+ return;
2049
+ }
2223
2050
  case "create":
2224
2051
  await networkCreate(client, positionals[0], options.prompt, options.json);
2225
2052
  return;
@@ -2247,13 +2074,12 @@ async function handleNetwork(client, subcommand, positionals, options) {
2247
2074
  }
2248
2075
  async function networkList(client, json) {
2249
2076
  const networks = await client.listNetworks();
2250
- const filtered = networks.filter((n) => !n.isPersonal);
2251
2077
  if (json) {
2252
- console.log(JSON.stringify(filtered));
2078
+ console.log(JSON.stringify(networks));
2253
2079
  return;
2254
2080
  }
2255
2081
  heading("Networks");
2256
- networkTable(filtered);
2082
+ networkTable(networks);
2257
2083
  console.log();
2258
2084
  }
2259
2085
  async function networkCreate(client, name, prompt, json) {
@@ -2334,13 +2160,9 @@ async function networkUpdate(client, id, options, json) {
2334
2160
  error("Provide at least --title or --prompt. Usage: index network update <id> [--title <t>] [--prompt <p>]", 1);
2335
2161
  return;
2336
2162
  }
2337
- const result = await client.callTool("update_network", { networkId: id, settings });
2163
+ const network = await client.updateNetwork(id, settings);
2338
2164
  if (json) {
2339
- console.log(JSON.stringify(result));
2340
- return;
2341
- }
2342
- if (!result.success) {
2343
- error(result.error ?? "Network update failed", 1);
2165
+ console.log(JSON.stringify({ network }));
2344
2166
  return;
2345
2167
  }
2346
2168
  success("Network updated.");
@@ -2350,13 +2172,9 @@ async function networkDelete(client, id, json) {
2350
2172
  error("Usage: index network delete <id>", 1);
2351
2173
  return;
2352
2174
  }
2353
- const result = await client.callTool("delete_network", { networkId: id });
2175
+ await client.deleteNetwork(id);
2354
2176
  if (json) {
2355
- console.log(JSON.stringify(result));
2356
- return;
2357
- }
2358
- if (!result.success) {
2359
- error(result.error ?? "Network deletion failed", 1);
2177
+ console.log(JSON.stringify({ success: true }));
2360
2178
  return;
2361
2179
  }
2362
2180
  success("Network deleted.");
@@ -2380,6 +2198,47 @@ async function networkInvite(client, id, email, json) {
2380
2198
  dim(" Created a pending account for this invitee.");
2381
2199
  }
2382
2200
 
2201
+ // src/sse.parser.ts
2202
+ function parseSSEEvents(chunk) {
2203
+ if (!chunk)
2204
+ return [];
2205
+ const events = [];
2206
+ const rawEvents = chunk.split(`
2207
+
2208
+ `);
2209
+ for (let i = 0;i < rawEvents.length; i++) {
2210
+ const raw = rawEvents[i].trim();
2211
+ if (!raw)
2212
+ continue;
2213
+ if (i === rawEvents.length - 1 && !chunk.endsWith(`
2214
+
2215
+ `)) {
2216
+ continue;
2217
+ }
2218
+ let eventType;
2219
+ const dataLines = [];
2220
+ for (const line of raw.split(`
2221
+ `)) {
2222
+ if (line.startsWith(":")) {
2223
+ continue;
2224
+ }
2225
+ if (line.startsWith("event:")) {
2226
+ eventType = line.slice("event:".length).trim();
2227
+ } else if (line.startsWith("data:")) {
2228
+ dataLines.push(line.slice("data:".length).trim());
2229
+ }
2230
+ }
2231
+ if (dataLines.length > 0) {
2232
+ events.push({
2233
+ event: eventType,
2234
+ data: dataLines.join(`
2235
+ `)
2236
+ });
2237
+ }
2238
+ }
2239
+ return events;
2240
+ }
2241
+
2383
2242
  // src/conversation.command.ts
2384
2243
  var CONVERSATION_HELP = `
2385
2244
  Conversation Commands:
@@ -2388,13 +2247,63 @@ Conversation Commands:
2388
2247
  index conversation show <id> Show messages (accepts short ID)
2389
2248
  index conversation show <id> --limit <n> Limit number of messages
2390
2249
  index conversation send <id> <message> Send a message (accepts short ID)
2250
+ index conversation show agent --intent-id <id> Read scoped agent state
2251
+ index conversation send agent <text> --intent-id <id> [--question-id <id>]
2252
+ index conversation answer agent --intent-id <id> --answer 'question-id=text' [--answer ...]
2391
2253
  index conversation stream Listen for real-time events (SSE)
2392
2254
  `;
2393
2255
  async function handleConversation(client, subcommand, positionals, options) {
2394
- if (!subcommand) {
2395
- error("Agent chat is no longer available from the CLI. Use `index conversation list` " + "for your conversations, or chat in the Index app.", 1);
2256
+ if (positionals[0] === "agent" && (subcommand === "show" || subcommand === "send")) {
2257
+ if (!options?.intentId)
2258
+ throw new Error("Personal-agent conversations require --intent-id <id>");
2259
+ if (subcommand === "show") {
2260
+ const conversation = await client.getAgentConversation(options.intentId);
2261
+ if (options.json) {
2262
+ console.log(JSON.stringify(conversation));
2263
+ return;
2264
+ }
2265
+ agentConversation(conversation);
2266
+ if (conversation.agent.questions.length > 0) {
2267
+ dim(` Answer: index conversation answer agent --intent-id ${options.intentId} --answer '<question-id>=<reply>'`);
2268
+ }
2269
+ return;
2270
+ }
2271
+ const text = positionals.slice(1).join(" ");
2272
+ if (!text.trim())
2273
+ throw new Error("A message is required");
2274
+ if (options.questionId) {
2275
+ const answers = [{ questionId: options.questionId, text }];
2276
+ const result = await client.answerAgentQuestions(options.intentId, answers);
2277
+ if (options.json) {
2278
+ console.log(JSON.stringify(result));
2279
+ return;
2280
+ }
2281
+ printAgentAnswers(answers, result.messages);
2282
+ return;
2283
+ }
2284
+ const message = await client.sendMessage("agent", text, options.intentId);
2285
+ if (options.json) {
2286
+ console.log(JSON.stringify(message));
2287
+ return;
2288
+ }
2289
+ success(`Message sent to your agent (${message.id})`);
2290
+ return;
2291
+ }
2292
+ if (subcommand === "answer") {
2293
+ if (positionals[0] !== "agent" || !options?.intentId || !options.answers?.length) {
2294
+ throw new Error("Usage: index conversation answer agent --intent-id <id> --answer 'question-id=text' [--answer ...]");
2295
+ }
2296
+ const answers = options.answers.map(({ key, value }) => ({ questionId: key, text: value }));
2297
+ const result = await client.answerAgentQuestions(options.intentId, answers);
2298
+ if (options.json) {
2299
+ console.log(JSON.stringify(result));
2300
+ return;
2301
+ }
2302
+ printAgentAnswers(answers, result.messages);
2396
2303
  return;
2397
2304
  }
2305
+ if (!subcommand)
2306
+ throw new Error(CONVERSATION_HELP.trim());
2398
2307
  switch (subcommand) {
2399
2308
  case "list":
2400
2309
  await conversationList(client, options?.json);
@@ -2409,15 +2318,25 @@ async function handleConversation(client, subcommand, positionals, options) {
2409
2318
  await conversationSend(client, positionals[0], positionals.slice(1), options?.json);
2410
2319
  return;
2411
2320
  case "stream":
2412
- await conversationStream(client);
2321
+ await conversationStream(client, options?.json);
2413
2322
  return;
2414
2323
  case "help":
2415
- console.log(CONVERSATION_HELP);
2324
+ console.log(options?.json ? JSON.stringify({ help: CONVERSATION_HELP.trim() }) : CONVERSATION_HELP);
2416
2325
  return;
2417
2326
  default:
2418
2327
  error(`Unknown conversation subcommand: ${subcommand}`, 1);
2419
2328
  }
2420
2329
  }
2330
+ function printAgentAnswers(answers, messages) {
2331
+ for (const [index, message] of messages.entries()) {
2332
+ const { questionId } = answers[index];
2333
+ if (message.metadata.principalMessage.kind === "answer") {
2334
+ success(`Answered question ${questionId}`);
2335
+ } else {
2336
+ success(`Question ${questionId} is no longer waiting; your reply was sent to your agent as a message`);
2337
+ }
2338
+ }
2339
+ }
2421
2340
  async function conversationList(client, json) {
2422
2341
  const conversations = await client.listConversations();
2423
2342
  if (json) {
@@ -2470,11 +2389,11 @@ async function conversationSend(client, id, messageParts, json) {
2470
2389
  }
2471
2390
  success(`Message sent (${msg.id})`);
2472
2391
  }
2473
- async function conversationStream(client) {
2392
+ async function conversationStream(client, json) {
2474
2393
  info("Connecting to conversation stream...");
2475
2394
  dim(`Press Ctrl+C to stop.
2476
2395
  `);
2477
- const response = await client.streamConversationEvents();
2396
+ const response = await client.streamEvents();
2478
2397
  if (!response.body) {
2479
2398
  error("No response body from stream endpoint.", 1);
2480
2399
  return;
@@ -2488,33 +2407,21 @@ async function conversationStream(client) {
2488
2407
  if (done)
2489
2408
  break;
2490
2409
  buffer += decoder.decode(value, { stream: true });
2491
- while (buffer.includes(`
2492
-
2493
- `)) {
2494
- const idx = buffer.indexOf(`
2410
+ buffer = buffer.replace(/\r\n/g, `
2411
+ `);
2412
+ const boundary = buffer.lastIndexOf(`
2495
2413
 
2496
2414
  `);
2497
- const raw2 = buffer.slice(0, idx);
2498
- buffer = buffer.slice(idx + 2);
2499
- for (const line of raw2.split(`
2500
- `)) {
2501
- if (line.startsWith(":"))
2502
- continue;
2503
- if (!line.startsWith("data:"))
2504
- continue;
2505
- const jsonStr = line.slice("data:".length).trim();
2506
- if (!jsonStr)
2507
- continue;
2508
- try {
2509
- const event = JSON.parse(jsonStr);
2510
- const type = event.type;
2511
- if (type === "connected") {
2512
- success("Connected to conversation stream.");
2513
- } else {
2514
- dim(`[${type}] ${JSON.stringify(event)}`);
2515
- }
2516
- } catch {}
2517
- }
2415
+ if (boundary < 0)
2416
+ continue;
2417
+ const complete = buffer.slice(0, boundary + 2);
2418
+ buffer = buffer.slice(boundary + 2);
2419
+ for (const event of parseSSEEvents(complete)) {
2420
+ const data = JSON.parse(event.data);
2421
+ if (json)
2422
+ console.log(JSON.stringify({ event: event.event, data }));
2423
+ else
2424
+ dim(JSON.stringify(data));
2518
2425
  }
2519
2426
  }
2520
2427
  } finally {
@@ -2522,81 +2429,6 @@ async function conversationStream(client) {
2522
2429
  }
2523
2430
  }
2524
2431
 
2525
- // src/contact.command.ts
2526
- var CONTACT_HELP = `
2527
- Usage:
2528
- index contact list List your contacts
2529
- index contact remove <email> Remove a contact
2530
- `;
2531
- async function handleContact(client, subcommand, positionals, options) {
2532
- if (!subcommand) {
2533
- if (options.json) {
2534
- console.log(JSON.stringify({ error: "No subcommand provided" }));
2535
- } else {
2536
- console.log(CONTACT_HELP);
2537
- }
2538
- return;
2539
- }
2540
- switch (subcommand) {
2541
- case "list": {
2542
- const result = await client.callTool("list_contacts", {});
2543
- if (options.json) {
2544
- console.log(JSON.stringify(result));
2545
- return;
2546
- }
2547
- if (!result.success) {
2548
- error(result.error ?? "Failed to list contacts", 1);
2549
- return;
2550
- }
2551
- const data = result.data;
2552
- heading("Contacts");
2553
- contactTable(data.contacts);
2554
- dim(`
2555
- ${data.count} contact${data.count !== 1 ? "s" : ""}`);
2556
- console.log();
2557
- return;
2558
- }
2559
- case "remove": {
2560
- const email = positionals[0];
2561
- if (!email) {
2562
- error("Missing email. Usage: index contact remove <email>", 1);
2563
- return;
2564
- }
2565
- const listResult = await client.callTool("list_contacts", {});
2566
- if (!listResult.success) {
2567
- error("Failed to resolve contact", 1);
2568
- return;
2569
- }
2570
- const contacts = listResult.data.contacts;
2571
- const match = contacts.find((c) => c.email.toLowerCase() === email.toLowerCase());
2572
- if (!match) {
2573
- error(`No contact found with email: ${email}`, 1);
2574
- return;
2575
- }
2576
- const result = await client.callTool("remove_contact", { contactUserId: match.userId });
2577
- if (options.json) {
2578
- console.log(JSON.stringify(result));
2579
- return;
2580
- }
2581
- if (!result.success) {
2582
- error(result.error ?? "Failed to remove contact", 1);
2583
- return;
2584
- }
2585
- success(`Removed ${email} from contacts.`);
2586
- return;
2587
- }
2588
- default: {
2589
- if (options.json) {
2590
- console.log(JSON.stringify({ error: `Unknown subcommand: ${subcommand}` }));
2591
- } else {
2592
- error(`Unknown subcommand: ${subcommand}`);
2593
- console.log(CONTACT_HELP);
2594
- }
2595
- return;
2596
- }
2597
- }
2598
- }
2599
-
2600
2432
  // src/scrape.command.ts
2601
2433
  async function handleScrape(client, positionals, options) {
2602
2434
  const url = positionals[0];
@@ -2606,20 +2438,15 @@ async function handleScrape(client, positionals, options) {
2606
2438
  }
2607
2439
  if (!options.json)
2608
2440
  info(`Scraping ${url}...`);
2609
- const result = await client.callTool("scrape_url", { url, objective: options.objective });
2441
+ const result = await client.scrapeUrl(url, options.objective);
2610
2442
  if (options.json) {
2611
2443
  console.log(JSON.stringify(result));
2612
2444
  return;
2613
2445
  }
2614
- if (!result.success) {
2615
- error(result.error ?? "Scrape failed", 1);
2616
- return;
2617
- }
2618
- const data = result.data;
2619
- heading(`Content from ${data.url}`);
2620
- console.log(data.content);
2446
+ heading(`Content from ${result.url}`);
2447
+ console.log(result.content);
2621
2448
  dim(`
2622
- ${data.contentLength} characters extracted`);
2449
+ ${result.contentLength} characters extracted`);
2623
2450
  console.log();
2624
2451
  }
2625
2452
 
@@ -2630,18 +2457,17 @@ import { join as join2 } from "node:path";
2630
2457
  async function handleSync(client, options) {
2631
2458
  if (!options.json)
2632
2459
  info("Syncing context...");
2633
- const [profile, networks, intents, contacts] = await Promise.all([
2634
- client.readUserContexts(),
2635
- client.callTool("read_networks", {}),
2636
- client.callTool("read_intents", {}),
2637
- client.callTool("list_contacts", {})
2460
+ const me = await client.getMe();
2461
+ const [profile, networks, intents] = await Promise.all([
2462
+ client.getUser(me.id),
2463
+ client.listNetworks(),
2464
+ client.listIntents({ limit: 100 })
2638
2465
  ]);
2639
2466
  const context = {
2640
2467
  syncedAt: new Date().toISOString(),
2641
- profile: profile.success ? profile.data : null,
2642
- networks: networks.success ? networks.data : null,
2643
- intents: intents.success ? intents.data : null,
2644
- contacts: contacts.success ? contacts.data : null
2468
+ profile,
2469
+ networks,
2470
+ intents: intents.intents
2645
2471
  };
2646
2472
  if (options.json) {
2647
2473
  console.log(JSON.stringify(context));
@@ -2653,190 +2479,109 @@ async function handleSync(client, options) {
2653
2479
  success("Context synced to ~/.index/context.json");
2654
2480
  }
2655
2481
 
2656
- // src/onboarding.command.ts
2657
- var ONBOARDING_HELP = `
2658
- Onboarding Commands:
2659
- index onboarding complete Mark onboarding as complete
2660
- `;
2661
- async function handleOnboarding(client, subcommand, options) {
2662
- if (subcommand === "complete") {
2663
- await onboardingComplete(client, options?.json);
2664
- return;
2665
- }
2666
- console.log(ONBOARDING_HELP);
2667
- }
2668
- async function onboardingComplete(client, json) {
2669
- if (!json) {
2670
- info("Completing onboarding...");
2671
- }
2672
- const result = await client.callTool("complete_onboarding", {});
2482
+ // src/docs.command.ts
2483
+ async function handleDocs(client, topic, json) {
2484
+ const result = await client.readDocs(topic);
2673
2485
  if (json) {
2674
2486
  console.log(JSON.stringify(result));
2675
2487
  return;
2676
2488
  }
2677
- if (result.success) {
2678
- success("Onboarding marked as complete.");
2489
+ console.log(result.content);
2490
+ if (result.topics)
2491
+ dim(`
2492
+ Topics: ${result.topics.join(", ")}`);
2493
+ }
2494
+
2495
+ // src/onboarding.command.ts
2496
+ async function handleOnboarding(client, subcommand, options) {
2497
+ if (subcommand === "confirm-profile") {
2498
+ const result = await client.confirmProfile();
2499
+ if (options?.json) {
2500
+ console.log(JSON.stringify(result));
2501
+ return;
2502
+ }
2503
+ success("Profile confirmed.");
2504
+ } else if (subcommand === "complete") {
2505
+ const result = await client.completeOnboarding(options?.intentId);
2506
+ if (options?.json) {
2507
+ console.log(JSON.stringify(result));
2508
+ return;
2509
+ }
2510
+ success(`Onboarding complete (first intent ${result.intentId}).`);
2679
2511
  } else {
2680
- error(result.error ?? "Failed to complete onboarding.");
2512
+ throw new Error("Usage: index onboarding confirm-profile | complete [--intent-id <id>]");
2681
2513
  }
2682
2514
  }
2683
2515
 
2684
2516
  // src/main.ts
2685
2517
  var DEFAULT_API_URL = "https://protocol.index.network";
2686
- var DEFAULT_APP_URL = "https://index.network";
2687
- var VERSION = package_default.version;
2688
- var BOX = { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" };
2689
- function visualLen(s) {
2690
- return stripAnsi(s).length;
2691
- }
2692
- function padVisual(s, width) {
2693
- return s + " ".repeat(Math.max(0, width - visualLen(s)));
2694
- }
2695
- function panel(title, rows) {
2696
- const innerW = Math.max(52, title.length + 4, ...rows.map((r) => visualLen(r)));
2697
- const dashRun = Math.max(1, innerW - title.length - 3);
2698
- const innerTop = `${BOX.h} ${title} ${BOX.h.repeat(dashRun)}`;
2699
- console.log(`${GRAY}${BOX.tl}${innerTop}${BOX.tr}${RESET}`);
2700
- for (const row of rows) {
2701
- console.log(`${GRAY}${BOX.v}${RESET}${padVisual(row, innerW)}${GRAY}${BOX.v}${RESET}`);
2702
- }
2703
- console.log(`${GRAY}${BOX.bl}${BOX.h.repeat(innerW)}${BOX.br}${RESET}
2704
- `);
2518
+ var LOGIN_API_URLS = {
2519
+ local: "http://localhost:3001",
2520
+ dev: "https://protocol.dev.index.network",
2521
+ prod: DEFAULT_API_URL
2522
+ };
2523
+ function webOriginForApi(apiUrl) {
2524
+ const url = new URL(apiUrl);
2525
+ if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
2526
+ return `${url.protocol}//${url.hostname}:3000`;
2527
+ }
2528
+ const hostname = url.hostname.startsWith("protocol.") ? url.hostname.slice("protocol.".length) : url.hostname;
2529
+ return `https://${hostname}`;
2705
2530
  }
2706
- function helpRowDim(leftW, left, right) {
2707
- const pad = Math.max(0, leftW - left.length);
2708
- return ` ${GRAY}${left}${" ".repeat(pad)} ${right}${RESET}`;
2709
- }
2710
- function helpRowCmd(leftW, left, right) {
2711
- const pad = Math.max(0, leftW - left.length);
2712
- return ` ${BOLD}${CYAN}${left}${RESET}${" ".repeat(pad)} ${right}`;
2713
- }
2714
- function helpRowCont(leftW, right) {
2715
- return ` ${" ".repeat(leftW)} ${right}`;
2716
- }
2717
- function renderHelp() {
2718
- const gsLefts = [
2719
- "index login",
2720
- "index logout",
2721
- 'index intent create "..."',
2722
- "index negotiation list",
2723
- "index opportunity list"
2724
- ];
2725
- const gsLW = Math.max(...gsLefts.map((s) => s.length));
2726
- const formsLefts = [
2727
- "index conversation list",
2728
- "index sync --json",
2729
- "index profile create",
2730
- "index profile update",
2731
- "index intent list",
2732
- "index negotiation list",
2733
- "index opportunity list"
2734
- ];
2735
- const formsLW = Math.max(...formsLefts.map((s) => s.length));
2736
- const cmdLefts = [
2737
- "pattern",
2738
- "example",
2739
- "intent",
2740
- "negotiation",
2741
- "opportunity",
2742
- "profile",
2743
- "conversation",
2744
- "network",
2745
- "contact",
2746
- "scrape",
2747
- "sync",
2748
- "onboarding"
2749
- ];
2750
- const cmdLW = Math.max(...cmdLefts.map((s) => s.length));
2751
- const optLefts = [
2752
- "--api-url <url>",
2753
- "--app-url <url>",
2754
- "--archived",
2755
- "--status <status>",
2756
- "--limit <n>",
2757
- "--since <date>",
2758
- "--json",
2759
- "--objective <text>",
2760
- "--linkedin <url>",
2761
- "--github <url>",
2762
- "--twitter <url>",
2763
- "--title <text>",
2764
- "--details <text>",
2765
- "--help",
2766
- "--version"
2767
- ];
2768
- const optLW = Math.max(...optLefts.map((s) => s.length));
2769
- console.log();
2770
- console.log(` ${BOLD}${CYAN}I N D E X${RESET} ${GRAY}cli${RESET}`);
2771
- console.log(` ${GRAY}v${VERSION}${RESET}
2772
- `);
2773
- panel("getting started", [
2774
- helpRowCmd(gsLW, "index login", "authenticate via browser"),
2775
- helpRowCmd(gsLW, "index logout", "clear stored session"),
2776
- helpRowCmd(gsLW, 'index intent create "..."', "describe what you're looking for"),
2777
- helpRowCmd(gsLW, "index negotiation list", "see agent debates in progress"),
2778
- helpRowCmd(gsLW, "index opportunity list", "see what was found for you")
2779
- ]);
2780
- panel("common forms", [
2781
- helpRowCmd(formsLW, "index conversation list", "list your conversations"),
2782
- helpRowCmd(formsLW, "index sync --json", "print synced context as JSON"),
2783
- "",
2784
- helpRowCmd(formsLW, "index profile create", "use social URL flags"),
2785
- helpRowCmd(formsLW, "index profile update", "use action text or --details"),
2786
- helpRowCmd(formsLW, "index intent list", "supports --archived and --limit"),
2787
- helpRowCmd(formsLW, "index negotiation list", "supports --since and --limit"),
2788
- helpRowCmd(formsLW, "index opportunity list", "supports --status and --limit")
2789
- ]);
2790
- panel("commands", [
2791
- helpRowDim(cmdLW, "pattern", "index <command> [args] [options]"),
2792
- helpRowDim(cmdLW, "example", 'index intent create "looking for a CTO"'),
2793
- "",
2794
- helpRowCmd(cmdLW, "intent", "list · show · create · update · archive"),
2795
- helpRowCont(cmdLW, "link · unlink · links"),
2796
- helpRowCmd(cmdLW, "negotiation", "list · show"),
2797
- "",
2798
- helpRowCmd(cmdLW, "profile", "show · sync · search · create · update"),
2799
- helpRowCmd(cmdLW, "conversation", "list · with · show · send · stream"),
2800
- helpRowCmd(cmdLW, "network", "list · create · show · update · delete"),
2801
- helpRowCont(cmdLW, "join · leave · invite"),
2802
- helpRowCmd(cmdLW, "contact", "list · remove"),
2803
- "",
2804
- helpRowCmd(cmdLW, "scrape", "extract content from a URL"),
2805
- helpRowCmd(cmdLW, "sync", "download your context locally"),
2806
- helpRowCmd(cmdLW, "onboarding", "finish account setup")
2807
- ]);
2808
- panel("options", [
2809
- helpRowDim(optLW, "--api-url <url>", "override API server URL"),
2810
- helpRowDim(optLW, "--app-url <url>", "override app URL for login"),
2811
- helpRowDim(optLW, "--archived", "include archived signals"),
2812
- helpRowDim(optLW, "--status <status>", "filter opportunities by status"),
2813
- helpRowDim(optLW, "--limit <n>", "limit number of results"),
2814
- helpRowDim(optLW, "--since <date>", "filter by ISO date or duration"),
2815
- helpRowDim(optLW, "--json", "output raw JSON"),
2816
- helpRowDim(optLW, "--objective <text>", "objective for scrape command"),
2817
- helpRowDim(optLW, "--linkedin <url>", "LinkedIn URL for profile create"),
2818
- helpRowDim(optLW, "--github <url>", "GitHub URL for profile create"),
2819
- helpRowDim(optLW, "--twitter <url>", "Twitter URL for profile create"),
2820
- helpRowDim(optLW, "--title <text>", "title for network update"),
2821
- helpRowDim(optLW, "--details <text>", "details for profile update"),
2822
- helpRowDim(optLW, "--help", "show help for any command"),
2823
- helpRowDim(optLW, "--version", "show version")
2824
- ]);
2531
+ var VERSION = package_default.version;
2532
+ function renderHelp(json) {
2533
+ const help = `Usage: index <command> [args] [options]
2534
+
2535
+ login [local|dev|prod] [--app-url <url>] Authenticate through the matching web app
2536
+ logout Revoke and clear the stored session
2537
+ docs [topic] Read the protocol's canonical guidance
2538
+ agent me Read your selected personal agent
2539
+ profile [show <user-id>|sync|update] Read, research, or edit your profile
2540
+ intent list|show|prepare|create|update|pause|resume|archive|networks|add-to-network|remove-from-network
2541
+ network list|discover|requests|request-update|request-dismiss|show|create|update|delete|join|leave|invite
2542
+ opportunity list|show|accept|reject|start-chat
2543
+ negotiation list [--intent-id <id>] [--state open|settled]
2544
+ negotiation show <opportunity-id>
2545
+ negotiation turn <opportunity-id> --action propose|counter|accept|decline
2546
+ --message <text> --expected-turn-count <n>
2547
+ conversation list|with|show|send|answer|stream
2548
+ conversation show agent --intent-id <id>
2549
+ conversation send agent <text> --intent-id <id> [--question-id <id>]
2550
+ onboarding confirm-profile
2551
+ onboarding complete [--intent-id <id>]
2552
+ scrape <url> [--objective <text>]
2553
+ sync Download profile, networks, and intents
2554
+
2555
+ Global options: --api-url <origin>, --json, --help, --version
2556
+ List options: --archived (intents), --status (opportunities), --limit <n>
2557
+ Intent list options: --query <text> (matches description and summary)
2558
+ Network options: --prompt <text> (create), --title <text> (update)
2559
+
2560
+ Auth: INDEX_SESSION_TOKEN or INDEX_API_KEY, otherwise stored browser login.
2561
+ API origin: --api-url, INDEX_API_URL, stored login URL, production default.
2562
+ Login target: local (localhost:3001), dev (protocol.dev.index.network), prod (default).
2563
+ --json emits one result/error; conversation stream emits NDJSON events.`;
2564
+ console.log(json ? JSON.stringify({ version: VERSION, help }) : `Index CLI ${VERSION}
2565
+
2566
+ ${help}`);
2825
2567
  }
2826
2568
  async function requireAuth(apiUrlOverride) {
2827
2569
  const store = new CredentialStore;
2828
2570
  const creds = await store.load();
2829
- if (!creds) {
2830
- error("Not logged in. Run `index login` first.", 1);
2831
- process.exit(1);
2832
- }
2833
- const apiUrl = apiUrlOverride ?? creds.apiUrl;
2834
- return new ApiClient(apiUrl, creds.token);
2835
- }
2836
- async function runLogin(apiUrlOverride, appUrlOverride) {
2571
+ const session = process.env.INDEX_SESSION_TOKEN || undefined;
2572
+ const apiKey = process.env.INDEX_API_KEY || undefined;
2573
+ if (session && apiKey)
2574
+ throw new Error("Set only one of INDEX_SESSION_TOKEN and INDEX_API_KEY");
2575
+ const token = session ?? apiKey ?? creds?.token;
2576
+ if (!token)
2577
+ throw new Error("Not logged in. Run `index login` or set INDEX_SESSION_TOKEN or INDEX_API_KEY.");
2578
+ const apiUrl = apiUrlOverride ?? process.env.INDEX_API_URL ?? creds?.apiUrl ?? DEFAULT_API_URL;
2579
+ return new ApiClient(apiUrl, token, apiKey ? "apiKey" : "session");
2580
+ }
2581
+ async function runLogin(target, apiUrlOverride, appUrlOverride, json) {
2837
2582
  const store = new CredentialStore;
2838
- const apiUrl = apiUrlOverride ?? DEFAULT_API_URL;
2839
- const appUrl = appUrlOverride ?? DEFAULT_APP_URL;
2583
+ const apiUrl = apiUrlOverride ?? (target ? LOGIN_API_URLS[target] : undefined) ?? process.env.INDEX_API_URL ?? (await store.load())?.apiUrl ?? DEFAULT_API_URL;
2584
+ const appUrl = appUrlOverride ?? webOriginForApi(apiUrl);
2840
2585
  info(`Authenticating with ${apiUrl}...`);
2841
2586
  const { authUrl, callbackPromise } = await handleLogin(apiUrl, appUrl, store);
2842
2587
  info("Opening browser for authentication...");
@@ -2863,6 +2608,12 @@ async function runLogin(apiUrlOverride, appUrlOverride) {
2863
2608
  } catch {}
2864
2609
  dim("Waiting for authentication callback...");
2865
2610
  const result = await callbackPromise;
2611
+ if (json) {
2612
+ if (!result.success)
2613
+ throw new Error(result.error ?? "Login failed.");
2614
+ console.log(JSON.stringify(result));
2615
+ return;
2616
+ }
2866
2617
  if (result.success) {
2867
2618
  try {
2868
2619
  const creds = await store.load();
@@ -2880,8 +2631,14 @@ async function runLogin(apiUrlOverride, appUrlOverride) {
2880
2631
  error(result.error ?? "Login failed.", 1);
2881
2632
  }
2882
2633
  }
2883
- async function runLogout() {
2634
+ async function runLogout(json) {
2884
2635
  const result = await handleLogout(new CredentialStore);
2636
+ if (json) {
2637
+ console.log(JSON.stringify(result));
2638
+ if (!result.success)
2639
+ process.exitCode = 1;
2640
+ return;
2641
+ }
2885
2642
  if (result.success) {
2886
2643
  success(result.message);
2887
2644
  return;
@@ -2893,30 +2650,44 @@ async function main() {
2893
2650
  const args = parseArgs(process.argv.slice(2));
2894
2651
  switch (args.command) {
2895
2652
  case "help":
2896
- renderHelp();
2653
+ renderHelp(args.json);
2897
2654
  return;
2898
2655
  case "version":
2899
- console.log(VERSION);
2656
+ console.log(args.json ? JSON.stringify({ version: VERSION }) : VERSION);
2900
2657
  return;
2901
2658
  case "unknown":
2902
2659
  error(`Unknown command: ${args.unknown}`, 1);
2903
2660
  return;
2904
2661
  case "login":
2905
- await runLogin(args.apiUrl, args.appUrl);
2662
+ await runLogin(args.loginTarget, args.apiUrl, args.appUrl, args.json);
2906
2663
  return;
2907
2664
  case "logout":
2908
- await runLogout();
2665
+ await runLogout(args.json);
2909
2666
  return;
2910
2667
  }
2911
2668
  const client = await requireAuth(args.apiUrl);
2912
2669
  switch (args.command) {
2670
+ case "docs":
2671
+ await handleDocs(client, args.positionals?.[0], args.json);
2672
+ return;
2673
+ case "agent": {
2674
+ if (args.subcommand !== "me")
2675
+ throw new Error("Usage: index agent me");
2676
+ const selected = await client.getAgent();
2677
+ if (args.json) {
2678
+ console.log(JSON.stringify(selected));
2679
+ return;
2680
+ }
2681
+ agentCard(selected);
2682
+ return;
2683
+ }
2913
2684
  case "profile":
2914
2685
  await handleProfile(client, args.subcommand, args.subcommand === "search" || args.subcommand === "update" ? args.positionals ?? [] : args.userId ? [args.userId] : [], {
2915
2686
  json: args.json,
2916
- linkedin: args.linkedin,
2917
- github: args.github,
2918
- twitter: args.twitter,
2919
- details: args.details
2687
+ name: args.name,
2688
+ intro: args.intro,
2689
+ location: args.location,
2690
+ socials: args.socials
2920
2691
  });
2921
2692
  return;
2922
2693
  case "intent":
@@ -2926,24 +2697,31 @@ async function main() {
2926
2697
  archived: args.archived,
2927
2698
  limit: args.limit,
2928
2699
  json: args.json,
2929
- targetId: args.targetId
2700
+ targetId: args.targetId,
2701
+ query: args.query,
2702
+ receipt: args.receipt,
2703
+ answers: args.answers
2930
2704
  });
2931
2705
  return;
2932
2706
  case "opportunity":
2933
2707
  await handleOpportunity(client, args.subcommand, {
2934
2708
  targetId: args.targetId,
2935
2709
  status: args.status,
2710
+ statuses: args.statuses,
2711
+ intentId: args.intentId,
2936
2712
  limit: args.limit,
2937
2713
  json: args.json,
2938
- positionals: args.positionals,
2939
- acknowledgeUptake: args.acknowledgeUptake
2714
+ positionals: args.positionals
2940
2715
  });
2941
2716
  return;
2942
2717
  case "negotiation":
2943
2718
  await handleNegotiation(client, args.subcommand, {
2944
2719
  targetId: args.targetId,
2945
- limit: args.limit,
2946
- since: args.since,
2720
+ intentId: args.intentId,
2721
+ state: args.state,
2722
+ action: args.action,
2723
+ message: args.message,
2724
+ expectedTurnCount: args.expectedTurnCount,
2947
2725
  json: args.json
2948
2726
  });
2949
2727
  return;
@@ -2957,12 +2735,10 @@ async function main() {
2957
2735
  case "conversation":
2958
2736
  await handleConversation(client, args.subcommand, args.positionals ?? [], {
2959
2737
  limit: args.limit,
2960
- json: args.json
2961
- });
2962
- return;
2963
- case "contact":
2964
- await handleContact(client, args.subcommand, args.positionals ?? [], {
2965
- json: args.json
2738
+ json: args.json,
2739
+ intentId: args.intentId,
2740
+ questionId: args.questionId,
2741
+ answers: args.answers
2966
2742
  });
2967
2743
  return;
2968
2744
  case "scrape":
@@ -2972,7 +2748,7 @@ async function main() {
2972
2748
  });
2973
2749
  return;
2974
2750
  case "onboarding":
2975
- await handleOnboarding(client, args.subcommand, { json: args.json });
2751
+ await handleOnboarding(client, args.subcommand, { json: args.json, intentId: args.intentId });
2976
2752
  return;
2977
2753
  case "sync":
2978
2754
  await handleSync(client, { json: args.json });
@@ -2980,5 +2756,9 @@ async function main() {
2980
2756
  }
2981
2757
  }
2982
2758
  main().catch((err) => {
2983
- error(err instanceof Error ? err.message : String(err), 1);
2759
+ if (process.argv.includes("--json")) {
2760
+ console.log(JSON.stringify(err instanceof ApiError ? { error: err.message, status: err.status, response: err.response } : { error: err instanceof Error ? err.message : String(err) }));
2761
+ process.exitCode = 1;
2762
+ } else
2763
+ error(err instanceof Error ? err.message : String(err), 1);
2984
2764
  });