@indexnetwork/cli 0.28.0 → 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 +35 -35
  2. package/dist/index.js +336 -59
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Index CLI
2
2
 
3
- Command-line interface for [Index Network](https://index.network). Message your personal agent, manage signals, and discover opportunities — all from your terminal.
3
+ Command-line interface for [Index Network](https://index.network). Message your personal agent, manage intents, and discover opportunities — all from your terminal.
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,17 +12,17 @@ npm install -g @indexnetwork/cli
12
12
 
13
13
  Index helps you find the right people—and helps the right people find you—based on what you are actually trying to do, not just a profile headline. The value is grounded intros: suggestions come from communities you share (syndicates, founder groups, firm networks), not from spraying the open web.
14
14
 
15
- The flow below is one complete story—shape a room, invite people, publish an approved signal, let broker agents evaluate in the background, then review a persisted match.
15
+ The flow below is one complete story—create an intent, let agents negotiate in the background, then review a persisted match.
16
16
 
17
17
  ```bash
18
18
  # login + setup
19
19
  index login
20
20
  index profile
21
21
 
22
- # 1. express intent (signals)
22
+ # 1. express an intent
23
23
  index intent create "federated learning collaboration"
24
24
 
25
- # 2. broker agents evaluate approved signals in the background
25
+ # 2. agents negotiate on your intents in the background
26
26
  index negotiation list
27
27
 
28
28
  # 3. wait for persisted opportunities, then review outcomes
@@ -53,16 +53,16 @@ flags are listed in [Options](#options), and examples follow this reference.
53
53
  | `index profile show <user-id>` | Show another user's accessible profile. |
54
54
  | `index profile sync` | Research public profile information and return a suggested profile without persisting it. |
55
55
  | `index profile update` | Save reviewed name, bio, location, or social links. |
56
- | `index intent list` | List signals, with optional archived, text-query, and result-limit filters. |
57
- | `index intent show <id>` | Show one signal. |
56
+ | `index intent list` | List intents, with optional archived, text-query, and result-limit filters. |
57
+ | `index intent show <id>` | Show one intent. |
58
58
  | `index intent prepare <text>` | Prepare a draft, optionally answering recovery prompts; returns an admission receipt when ready. |
59
- | `index intent create <text>` | Create a signal, optionally using the preparation receipt. |
60
- | `index intent update <id> <text>` | Update and reprocess a signal's description. |
61
- | `index intent archive <id>` | Archive a signal so it stops participating in discovery. |
59
+ | `index intent create <text>` | Create an intent, optionally using the preparation receipt. |
60
+ | `index intent update <id> <text>` | Update and reprocess an intent's description. |
61
+ | `index intent archive <id>` | Archive an intent so it stops participating in discovery. |
62
62
  | `index intent pause <id>` / `resume <id>` | Pause or resume its agent without archiving. |
63
- | `index intent networks <id>` | List the networks a signal is shared in. |
64
- | `index intent add-to-network <id> <network-id>` | Share a signal in a network. |
65
- | `index intent remove-from-network <id> <network-id>` | Stop sharing a signal in a network. |
63
+ | `index intent networks <id>` | List the networks an intent is shared in. |
64
+ | `index intent add-to-network <id> <network-id>` | Share an intent in a network. |
65
+ | `index intent remove-from-network <id> <network-id>` | Stop sharing an intent in a network. |
66
66
  | `index negotiation list` | List negotiations, optionally filtered by intent or open/settled state. |
67
67
  | `index negotiation show <opportunity-id>` | Read the negotiation's turns, current state, and available actions. |
68
68
  | `index negotiation turn <opportunity-id> --action <action> --message <text> --expected-turn-count <n>` | Submit one `propose`, `counter`, `accept`, or `decline` turn against the observed turn count. |
@@ -76,7 +76,7 @@ flags are listed in [Options](#options), and examples follow this reference.
76
76
  | `index network requests` / `request-update` / `request-dismiss` | Review or revise your early-access creation requests. |
77
77
  | `index network create <title>` | Create a network when eligible, or submit an early-access creation request. |
78
78
  | `index network show <id>` | Show a network and its members. |
79
- | `index network update <id> --title <text>` | Change a network's title. |
79
+ | `index network update <id> [--title <text>] [--prompt <text>]` | Change a network's title or description. |
80
80
  | `index network delete <id>` | Delete a network you own. |
81
81
  | `index network join <id>` | Join an open network. |
82
82
  | `index network leave <id>` | Leave a network. |
@@ -89,9 +89,9 @@ flags are listed in [Options](#options), and examples follow this reference.
89
89
  | `index conversation stream` | Subscribe to conversation, negotiation, opportunity, and intent events over SSE. |
90
90
  | `index conversation help` | Show conversation-specific help. |
91
91
  | `index onboarding confirm-profile` | Confirm that the owner has reviewed their profile. |
92
- | `index onboarding complete` | Complete onboarding once profile confirmation and first-signal prerequisites are met. |
92
+ | `index onboarding complete` | Complete onboarding once profile confirmation and first-intent prerequisites are met. |
93
93
  | `index scrape <url>` | Extract text from a URL, optionally guided by an objective. |
94
- | `index sync` | Read your profile, networks, and signals into `~/.index/context.json`, or stdout with `--json`. |
94
+ | `index sync` | Read your profile, networks, and intents into `~/.index/context.json`, or stdout with `--json`. |
95
95
 
96
96
  ### `index login`
97
97
 
@@ -142,31 +142,31 @@ index logout
142
142
 
143
143
  ### `index intent`
144
144
 
145
- Manage your signals (intents). Create signals from natural language, list active signals, view details, and archive signals you no longer need.
145
+ Manage your intents. Create intents from natural language, list active intents, view details, and archive intents you no longer need.
146
146
 
147
147
  ```bash
148
- index intent list # List active signals
149
- index intent list --archived # Include archived signals
148
+ index intent list # List active intents
149
+ index intent list --archived # Include archived intents
150
150
  index intent list --limit 5 # Limit to 5 results
151
151
  index intent list --query "CTO" # Match description and summary text
152
- index intent show <id> # Show full signal details
152
+ index intent show <id> # Show full intent details
153
153
  index intent prepare "Looking for a CTO" # Read feedback / recovery prompts and receipt
154
154
  index intent prepare "Looking for a CTO" --answer 'Prompt label=My answer'
155
155
  index intent create "<returned payload>" --receipt '<returned preparationReceipt>'
156
156
  index intent pause <id> # Hold the agent without archiving
157
157
  index intent resume <id> # Start it again
158
- index intent update <id> "revised text" # Update a signal (runs full pipeline)
159
- index intent archive <id> # Archive a signal
160
- index intent networks <id> # List the networks a signal is shared in
161
- index intent add-to-network <id> <network-id> # Add a signal to a network
162
- index intent remove-from-network <id> <network-id> # Remove a signal from a network
158
+ index intent update <id> "revised text" # Update an intent (runs full pipeline)
159
+ index intent archive <id> # Archive an intent
160
+ index intent networks <id> # List the networks an intent is shared in
161
+ index intent add-to-network <id> <network-id> # Add an intent to a network
162
+ index intent remove-from-network <id> <network-id> # Remove an intent from a network
163
163
  ```
164
164
 
165
165
  Preparation returns `needs_revision` with recovery field labels or `ready` with a
166
166
  `payload` and `preparationReceipt`. Use the exact returned payload with its receipt
167
167
  in `create`; `--answer` pairs the displayed recovery **label** with your response.
168
168
  You can also create directly: the server prepares the description and rejects an
169
- unadmitted draft. A new signal is shared in every network you belong to. Narrow it afterwards with
169
+ unadmitted draft. A new intent is shared in every network you belong to. Narrow it afterwards with
170
170
  `remove-from-network`, or widen an existing one with `add-to-network`.
171
171
 
172
172
  ### `index negotiation`
@@ -203,7 +203,7 @@ index opportunity start-chat <id> # Open its human chat (pending/accept
203
203
 
204
204
  `--status` accepts one of `pending`, `accepted`, `rejected`, `expired`;
205
205
  `--statuses` accepts a comma-separated radar selection including `negotiating`.
206
- The signal-scoped radar uses `--intent-id <id>`.
206
+ The intent-scoped radar uses `--intent-id <id>`.
207
207
 
208
208
  ### `index network`
209
209
 
@@ -227,7 +227,7 @@ index network invite <id> user@email # Invite directly by email
227
227
 
228
228
  ### `index conversation`
229
229
 
230
- Read and message the personal agent within a signal, or use human DMs.
230
+ Read and message the personal agent within an intent, or use human DMs.
231
231
 
232
232
  ```bash
233
233
  index conversation show agent --intent-id <id> --json
@@ -254,7 +254,7 @@ index onboarding complete [--intent-id <id>] --json
254
254
  ```
255
255
 
256
256
  Confirm a profile only after the owner reviews it. Completion uses the server's
257
- profile-confirmation and first-signal prerequisites; it does not bypass them.
257
+ profile-confirmation and first-intent prerequisites; it does not bypass them.
258
258
  For a new account: `profile sync` to see suggested facts, `profile update` to save
259
259
  what you approve, `onboarding confirm-profile`, `intent prepare` and `intent create`
260
260
  with the returned payload and receipt, then `onboarding complete --intent-id <id>`.
@@ -298,7 +298,7 @@ fails, without saving partial context.
298
298
 
299
299
  ## Examples: Reviewing Opportunities
300
300
 
301
- Approved signals are evaluated in the background. `opportunity list` only reviews persisted results; it does not start evaluation.
301
+ Approved intents are evaluated in the background. `opportunity list` only reviews persisted results; it does not start evaluation.
302
302
 
303
303
  ### Review and act
304
304
 
@@ -323,23 +323,23 @@ index opportunity reject <id>
323
323
  | Flag | Short | Description |
324
324
  | -------------------- | ----- | --------------------------------------------------------------- |
325
325
  | `--api-url <url>` | | Override API server (default: `https://protocol.index.network`) |
326
- | `--app-url <url>` | | Override app URL for login (default: `https://index.network`) |
327
- | `--archived` | | Include archived signals (intent list) |
326
+ | `--app-url <url>` | | Override the web app URL for login (default: inferred from the API origin) |
327
+ | `--archived` | | Include archived intents (intent list) |
328
328
  | `--status <status>` | | Filter opportunities by one pending/accepted/rejected/expired status |
329
329
  | `--statuses <list>` | | Comma-separated radar stages, including negotiating |
330
330
  | `--limit <n>` | | Positive result limit for intent/opportunity lists or conversation messages |
331
- | `--prompt <text>` | `-p` | Network description (for `network create`) |
331
+ | `--prompt <text>` | `-p` | Network description (for `network create` and `network update`) |
332
332
  | `--title <text>` | | Network title (for `network update`) |
333
333
  | `--objective <text>` | | Focus objective (for `scrape`) |
334
- | `--query <text>` | | Match signal description and summary text (intent list) |
335
- | `--intent-id <id>` | | Filter negotiations, scope agent conversations, or select onboarding's first signal |
334
+ | `--query <text>` | | Match intent description and summary text (intent list) |
335
+ | `--intent-id <id>` | | Filter negotiations, scope agent conversations, or select onboarding's first intent |
336
336
  | `--state <state>` | | Filter negotiations by `open` or `settled` |
337
337
  | `--action <action>` | | Required negotiation turn action: `propose`, `counter`, `accept`, or `decline` |
338
338
  | `--message <text>` | | Required message for a negotiation turn |
339
339
  | `--expected-turn-count <n>` | | Required observed nonnegative integer turn count for a negotiation turn |
340
340
  | `--question-id <id>` | | Identify the displayed question when answering the personal agent |
341
341
  | `--answer <key=value>` | | Repeat for recovery prompt labels or agent question IDs |
342
- | `--receipt <token>` | | Preparation receipt for signal creation |
342
+ | `--receipt <token>` | | Preparation receipt for intent creation |
343
343
  | `--name`, `--intro`, `--location` | | Profile fields to update |
344
344
  | `--social <label=value>` | | Repeat to replace profile social links |
345
345
  | `--json` | | Output raw JSON to stdout |
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { spawn } from "node:child_process";
5
5
  // package.json
6
6
  var package_default = {
7
7
  name: "@indexnetwork/cli",
8
- version: "0.28.0",
8
+ version: "0.29.0",
9
9
  description: "Command-line interface for Index Network",
10
10
  license: "MIT",
11
11
  type: "module",
@@ -25,10 +25,10 @@ var package_default = {
25
25
  "publish:all": "bun scripts/publish.ts"
26
26
  },
27
27
  optionalDependencies: {
28
- "@indexnetwork/cli-linux-x64": "0.28.0",
29
- "@indexnetwork/cli-linux-arm64": "0.28.0",
30
- "@indexnetwork/cli-darwin-x64": "0.28.0",
31
- "@indexnetwork/cli-darwin-arm64": "0.28.0"
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"
32
32
  },
33
33
  devDependencies: {
34
34
  "@types/bun": "1.3.14"
@@ -638,7 +638,7 @@ class ApiClient {
638
638
  return (await this.get(path)).json();
639
639
  }
640
640
  async getAgent() {
641
- return (await this.get("/api/agents/me")).json();
641
+ return await (await this.get("/api/agents/me")).json();
642
642
  }
643
643
  async getNegotiation(opportunityId) {
644
644
  const body = await (await this.get(`/api/opportunities/${encodeURIComponent(opportunityId)}/negotiation`)).json();
@@ -649,13 +649,13 @@ class ApiClient {
649
649
  return body.negotiation;
650
650
  }
651
651
  async getAgentConversation(intentId) {
652
- return (await this.get(`/api/conversations/agent/messages?${new URLSearchParams({ intentId })}`)).json();
652
+ return await (await this.get(`/api/conversations/agent/messages?${new URLSearchParams({ intentId })}`)).json();
653
653
  }
654
654
  async confirmProfile() {
655
- return (await this.post("/api/auth/onboarding/confirm-profile", {})).json();
655
+ return await (await this.post("/api/auth/onboarding/confirm-profile", {})).json();
656
656
  }
657
657
  async completeOnboarding(intentId) {
658
- return (await this.post("/api/auth/onboarding/complete", { intentId })).json();
658
+ return await (await this.post("/api/auth/onboarding/complete", { intentId })).json();
659
659
  }
660
660
  async get(path) {
661
661
  const res = await fetch(`${this.baseUrl}${path}`, {
@@ -1085,7 +1085,7 @@ function profileCard(data) {
1085
1085
  }
1086
1086
  function intentTable(intents) {
1087
1087
  if (intents.length === 0) {
1088
- dim(" No signals found.");
1088
+ dim(" No intents found.");
1089
1089
  return;
1090
1090
  }
1091
1091
  const idWidth = 8;
@@ -1093,7 +1093,7 @@ function intentTable(intents) {
1093
1093
  const statusWidth = 10;
1094
1094
  const sourceWidth = 16;
1095
1095
  const dateWidth = 20;
1096
- 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}`);
1097
1097
  console.log(` ${GRAY}${"-".repeat(idWidth)} ${"-".repeat(descWidth)} ${"-".repeat(statusWidth)} ${"-".repeat(sourceWidth)} ${"-".repeat(dateWidth)}${RESET}`);
1098
1098
  for (const intent of intents) {
1099
1099
  const shortId = intent.id.slice(0, 8);
@@ -1109,9 +1109,45 @@ function intentTable(intents) {
1109
1109
  console.log(` ${CYAN}${shortId}${RESET} ${desc.padEnd(descWidth)} ${sColor}${status}${RESET} ${GRAY}${source}${RESET} ${GRAY}${date}${RESET}`);
1110
1110
  }
1111
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
+ }
1112
1148
  function intentCard(intent) {
1113
1149
  console.log();
1114
- console.log(` ${BOLD}${CYAN}Signal Details${RESET}`);
1150
+ console.log(` ${BOLD}${CYAN}Intent Details${RESET}`);
1115
1151
  console.log(` ${GRAY}${"─".repeat(50)}${RESET}`);
1116
1152
  console.log(` ${BOLD}ID${RESET} ${GRAY}${intent.id}${RESET}`);
1117
1153
  console.log(` ${BOLD}Status${RESET} ${intent.status === "ACTIVE" ? GREEN : GRAY}${intent.status}${RESET}`);
@@ -1262,6 +1298,173 @@ function statusColor(st) {
1262
1298
  return "";
1263
1299
  }
1264
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
+ }
1265
1468
  function networkTable(networks) {
1266
1469
  if (networks.length === 0) {
1267
1470
  dim(" No networks found.");
@@ -1468,16 +1671,16 @@ async function profileSync(client, json) {
1468
1671
  // src/intent.command.ts
1469
1672
  var INTENT_HELP = `
1470
1673
  Usage:
1471
- index intent list [--archived] [--limit <n>] [--query <text>] List your signals
1472
- index intent show <id> Show signal details (accepts short ID)
1674
+ index intent list [--archived] [--limit <n>] [--query <text>] List your intents
1675
+ index intent show <id> Show intent details (accepts short ID)
1473
1676
  index intent prepare <content> [--answer 'prompt=reply'] Review and repair a draft
1474
- index intent create <content> [--receipt <token>] Create an admitted signal
1677
+ index intent create <content> [--receipt <token>] Create an admitted intent
1475
1678
  index intent pause <id> | resume <id> Hold or restart its agent
1476
- index intent update <id> <content> Update a signal's description
1477
- index intent archive <id> Archive a signal (accepts short ID)
1478
- index intent networks <id> List the networks a signal is shared in
1479
- index intent add-to-network <id> <network-id> Add a signal to a network
1480
- index intent remove-from-network <id> <network-id> Remove a signal from a network
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
1481
1684
  `;
1482
1685
  async function handleIntent(client, subcommand, options) {
1483
1686
  if (!subcommand) {
@@ -1499,7 +1702,7 @@ async function handleIntent(client, subcommand, options) {
1499
1702
  console.log(JSON.stringify(result));
1500
1703
  return;
1501
1704
  }
1502
- heading("Signals");
1705
+ heading("Intents");
1503
1706
  intentTable(result.intents);
1504
1707
  if (result.pagination.totalCount > 0) {
1505
1708
  dim(`
@@ -1510,7 +1713,7 @@ async function handleIntent(client, subcommand, options) {
1510
1713
  }
1511
1714
  case "show": {
1512
1715
  if (!options.intentId) {
1513
- error("Missing signal ID. Usage: index intent show <id>", 1);
1716
+ error("Missing intent ID. Usage: index intent show <id>", 1);
1514
1717
  return;
1515
1718
  }
1516
1719
  const intent = await client.getIntent(options.intentId);
@@ -1525,7 +1728,11 @@ async function handleIntent(client, subcommand, options) {
1525
1728
  if (!options.intentContent)
1526
1729
  throw new Error("Usage: index intent prepare <content> [--answer 'prompt=reply']");
1527
1730
  const prepared = await client.prepareIntent(options.intentContent, (options.answers ?? []).map(({ key, value }) => ({ prompt: key, answer: value })));
1528
- console.log(JSON.stringify(prepared, null, options.json ? undefined : 2));
1731
+ if (options.json) {
1732
+ console.log(JSON.stringify(prepared));
1733
+ return;
1734
+ }
1735
+ intentPreparation(prepared);
1529
1736
  return;
1530
1737
  }
1531
1738
  case "create": {
@@ -1534,19 +1741,19 @@ async function handleIntent(client, subcommand, options) {
1534
1741
  return;
1535
1742
  }
1536
1743
  if (!options.json)
1537
- info("Processing signal...");
1744
+ info("Processing intent...");
1538
1745
  const result = await client.createIntent(options.intentContent, options.targetId ? [options.targetId] : undefined, options.receipt);
1539
1746
  if (options.json) {
1540
1747
  console.log(JSON.stringify(result));
1541
1748
  return;
1542
1749
  }
1543
- success("Signal created.");
1750
+ success("Intent created.");
1544
1751
  dim(` shared in ${result.networkIds.length} network${result.networkIds.length === 1 ? "" : "s"}`);
1545
1752
  return;
1546
1753
  }
1547
1754
  case "update": {
1548
1755
  if (!options.intentId) {
1549
- error("Missing signal ID. Usage: index intent update <id> <content>", 1);
1756
+ error("Missing intent ID. Usage: index intent update <id> <content>", 1);
1550
1757
  return;
1551
1758
  }
1552
1759
  if (!options.intentContent) {
@@ -1554,13 +1761,13 @@ async function handleIntent(client, subcommand, options) {
1554
1761
  return;
1555
1762
  }
1556
1763
  if (!options.json)
1557
- info("Updating signal...");
1764
+ info("Updating intent...");
1558
1765
  const result = await client.updateIntent(options.intentId, options.intentContent);
1559
1766
  if (options.json) {
1560
1767
  console.log(JSON.stringify(result));
1561
1768
  return;
1562
1769
  }
1563
- success("Signal updated.");
1770
+ success("Intent updated.");
1564
1771
  return;
1565
1772
  }
1566
1773
  case "pause":
@@ -1572,12 +1779,12 @@ async function handleIntent(client, subcommand, options) {
1572
1779
  if (options.json)
1573
1780
  console.log(JSON.stringify({ intentId: options.intentId, status }));
1574
1781
  else
1575
- success(`Signal ${status.toLowerCase()}.`);
1782
+ success(`Intent ${status.toLowerCase()}.`);
1576
1783
  return;
1577
1784
  }
1578
1785
  case "archive": {
1579
1786
  if (!options.intentId) {
1580
- error("Missing signal ID. Usage: index intent archive <id>", 1);
1787
+ error("Missing intent ID. Usage: index intent archive <id>", 1);
1581
1788
  return;
1582
1789
  }
1583
1790
  await client.archiveIntent(options.intentId);
@@ -1585,7 +1792,7 @@ async function handleIntent(client, subcommand, options) {
1585
1792
  console.log(JSON.stringify({ success: true }));
1586
1793
  return;
1587
1794
  }
1588
- success(`Signal ${options.intentId} archived.`);
1795
+ success(`Intent ${options.intentId} archived.`);
1589
1796
  return;
1590
1797
  }
1591
1798
  case "networks": {
@@ -1614,7 +1821,7 @@ async function handleIntent(client, subcommand, options) {
1614
1821
  console.log(JSON.stringify({ success: true }));
1615
1822
  return;
1616
1823
  }
1617
- success("Signal added to network.");
1824
+ success("Intent added to network.");
1618
1825
  return;
1619
1826
  }
1620
1827
  case "remove-from-network": {
@@ -1627,7 +1834,7 @@ async function handleIntent(client, subcommand, options) {
1627
1834
  console.log(JSON.stringify({ success: true }));
1628
1835
  return;
1629
1836
  }
1630
- success("Signal removed from network.");
1837
+ success("Intent removed from network.");
1631
1838
  return;
1632
1839
  }
1633
1840
  }
@@ -1724,16 +1931,30 @@ async function opportunityStatusUpdate(client, id, status, json) {
1724
1931
 
1725
1932
  // src/negotiation.command.ts
1726
1933
  async function handleNegotiation(client, subcommand, options) {
1727
- let result;
1728
1934
  if (subcommand === "list") {
1729
1935
  if (options.state !== undefined && options.state !== "open" && options.state !== "settled")
1730
1936
  throw new Error("--state must be open or settled");
1731
- result = await client.listNegotiations({ intentId: options.intentId, state: options.state });
1937
+ const negotiations = await client.listNegotiations({ intentId: options.intentId, state: options.state });
1938
+ if (options.json) {
1939
+ console.log(JSON.stringify(negotiations));
1940
+ return;
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();
1732
1948
  } else if (subcommand === "show" || subcommand === "turn") {
1733
1949
  if (!options.targetId)
1734
1950
  throw new Error("An opportunity ID is required");
1735
1951
  if (subcommand === "show") {
1736
- result = await client.getNegotiation(options.targetId);
1952
+ const negotiation = await client.getNegotiation(options.targetId);
1953
+ if (options.json) {
1954
+ console.log(JSON.stringify(negotiation));
1955
+ return;
1956
+ }
1957
+ negotiationCard(negotiation);
1737
1958
  } else {
1738
1959
  if (!options.action || !["propose", "counter", "accept", "decline"].includes(options.action))
1739
1960
  throw new Error("--action must be propose, counter, accept, or decline");
@@ -1741,16 +1962,21 @@ async function handleNegotiation(client, subcommand, options) {
1741
1962
  throw new Error("--message is required");
1742
1963
  if (options.expectedTurnCount === undefined || !Number.isSafeInteger(options.expectedTurnCount) || options.expectedTurnCount < 0)
1743
1964
  throw new Error("--expected-turn-count must be the observed nonnegative integer turn count");
1744
- result = await client.submitNegotiationTurn(options.targetId, {
1965
+ const negotiation = await client.submitNegotiationTurn(options.targetId, {
1745
1966
  action: options.action,
1746
1967
  message: options.message,
1747
1968
  expectedTurnCount: options.expectedTurnCount
1748
1969
  });
1970
+ if (options.json) {
1971
+ console.log(JSON.stringify(negotiation));
1972
+ return;
1973
+ }
1974
+ success(`Turn submitted: ${options.action}`);
1975
+ negotiationCard(negotiation);
1749
1976
  }
1750
1977
  } else {
1751
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>");
1752
1979
  }
1753
- console.log(JSON.stringify(result, null, options.json ? undefined : 2));
1754
1980
  }
1755
1981
 
1756
1982
  // src/network.command.ts
@@ -2030,24 +2256,50 @@ async function handleConversation(client, subcommand, positionals, options) {
2030
2256
  if (positionals[0] === "agent" && (subcommand === "show" || subcommand === "send")) {
2031
2257
  if (!options?.intentId)
2032
2258
  throw new Error("Personal-agent conversations require --intent-id <id>");
2033
- let result;
2034
- if (subcommand === "show")
2035
- result = await client.getAgentConversation(options.intentId);
2036
- else {
2037
- const text = positionals.slice(1).join(" ");
2038
- if (!text.trim())
2039
- throw new Error("A message is required");
2040
- result = options.questionId ? await client.answerAgentQuestions(options.intentId, [{ questionId: options.questionId, text }]) : await client.sendMessage("agent", text, options.intentId);
2041
- }
2042
- console.log(JSON.stringify(result, null, options.json ? undefined : 2));
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})`);
2043
2290
  return;
2044
2291
  }
2045
2292
  if (subcommand === "answer") {
2046
2293
  if (positionals[0] !== "agent" || !options?.intentId || !options.answers?.length) {
2047
2294
  throw new Error("Usage: index conversation answer agent --intent-id <id> --answer 'question-id=text' [--answer ...]");
2048
2295
  }
2049
- const result = await client.answerAgentQuestions(options.intentId, options.answers.map(({ key, value }) => ({ questionId: key, text: value })));
2050
- console.log(JSON.stringify(result, null, options.json ? undefined : 2));
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);
2051
2303
  return;
2052
2304
  }
2053
2305
  if (!subcommand)
@@ -2075,6 +2327,16 @@ async function handleConversation(client, subcommand, positionals, options) {
2075
2327
  error(`Unknown conversation subcommand: ${subcommand}`, 1);
2076
2328
  }
2077
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
+ }
2078
2340
  async function conversationList(client, json) {
2079
2341
  const conversations = await client.listConversations();
2080
2342
  if (json) {
@@ -2232,14 +2494,23 @@ async function handleDocs(client, topic, json) {
2232
2494
 
2233
2495
  // src/onboarding.command.ts
2234
2496
  async function handleOnboarding(client, subcommand, options) {
2235
- let result;
2236
- if (subcommand === "confirm-profile")
2237
- result = await client.confirmProfile();
2238
- else if (subcommand === "complete")
2239
- result = await client.completeOnboarding(options?.intentId);
2240
- else
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}).`);
2511
+ } else {
2241
2512
  throw new Error("Usage: index onboarding confirm-profile | complete [--intent-id <id>]");
2242
- console.log(JSON.stringify(result, null, options?.json ? undefined : 2));
2513
+ }
2243
2514
  }
2244
2515
 
2245
2516
  // src/main.ts
@@ -2399,11 +2670,17 @@ async function main() {
2399
2670
  case "docs":
2400
2671
  await handleDocs(client, args.positionals?.[0], args.json);
2401
2672
  return;
2402
- case "agent":
2673
+ case "agent": {
2403
2674
  if (args.subcommand !== "me")
2404
2675
  throw new Error("Usage: index agent me");
2405
- console.log(JSON.stringify(await client.getAgent(), null, args.json ? undefined : 2));
2676
+ const selected = await client.getAgent();
2677
+ if (args.json) {
2678
+ console.log(JSON.stringify(selected));
2679
+ return;
2680
+ }
2681
+ agentCard(selected);
2406
2682
  return;
2683
+ }
2407
2684
  case "profile":
2408
2685
  await handleProfile(client, args.subcommand, args.subcommand === "search" || args.subcommand === "update" ? args.positionals ?? [] : args.userId ? [args.userId] : [], {
2409
2686
  json: args.json,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indexnetwork/cli",
3
- "version": "0.28.0",
3
+ "version": "0.29.0-rc.88.2",
4
4
  "description": "Command-line interface for Index Network",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,10 +20,10 @@
20
20
  "publish:all": "bun scripts/publish.ts"
21
21
  },
22
22
  "optionalDependencies": {
23
- "@indexnetwork/cli-linux-x64": "0.28.0",
24
- "@indexnetwork/cli-linux-arm64": "0.28.0",
25
- "@indexnetwork/cli-darwin-x64": "0.28.0",
26
- "@indexnetwork/cli-darwin-arm64": "0.28.0"
23
+ "@indexnetwork/cli-linux-x64": "0.29.0-rc.88.2",
24
+ "@indexnetwork/cli-linux-arm64": "0.29.0-rc.88.2",
25
+ "@indexnetwork/cli-darwin-x64": "0.29.0-rc.88.2",
26
+ "@indexnetwork/cli-darwin-arm64": "0.29.0-rc.88.2"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/bun": "1.3.14"