@indexnetwork/cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -0
- package/dist/index.js +651 -30
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -73,6 +73,9 @@ View user profiles and trigger profile regeneration.
|
|
|
73
73
|
index profile # Show your own profile
|
|
74
74
|
index profile show <user-id> # Show another user's profile
|
|
75
75
|
index profile sync # Regenerate your profile
|
|
76
|
+
index profile search <query> # Search profiles by name or keyword
|
|
77
|
+
index profile create # Generate profile from social links
|
|
78
|
+
index profile update <details> # Update profile details
|
|
76
79
|
```
|
|
77
80
|
|
|
78
81
|
### `index intent`
|
|
@@ -85,7 +88,11 @@ index intent list --archived # Include archived signals
|
|
|
85
88
|
index intent list --limit 5 # Limit to 5 results
|
|
86
89
|
index intent show <id> # Show full signal details
|
|
87
90
|
index intent create "Looking for a CTO" # Create from natural language
|
|
91
|
+
index intent update <id> "revised text" # Update a signal (runs full pipeline)
|
|
88
92
|
index intent archive <id> # Archive a signal
|
|
93
|
+
index intent link <id> <network-id> # Link a signal to a network
|
|
94
|
+
index intent unlink <id> <network-id> # Unlink a signal from a network
|
|
95
|
+
index intent links <id> # List networks linked to a signal
|
|
89
96
|
```
|
|
90
97
|
|
|
91
98
|
### `index opportunity`
|
|
@@ -99,6 +106,9 @@ index opportunity list --limit 5 # Limit results
|
|
|
99
106
|
index opportunity show <id> # Show full details
|
|
100
107
|
index opportunity accept <id> # Accept an opportunity
|
|
101
108
|
index opportunity reject <id> # Reject an opportunity
|
|
109
|
+
index opportunity discover "query" # Discover opportunities by search
|
|
110
|
+
index opportunity discover --target <id> # Discover with a specific user
|
|
111
|
+
index opportunity discover --introduce <a> <b> # Introduce two users
|
|
102
112
|
```
|
|
103
113
|
|
|
104
114
|
Status values: `pending`, `accepted`, `rejected`, `expired`.
|
|
@@ -112,11 +122,98 @@ index network list # List your networks
|
|
|
112
122
|
index network create "My Network" # Create a network
|
|
113
123
|
index network create "AI" --prompt "AI researchers" # Create with description
|
|
114
124
|
index network show <id> # Show details and members
|
|
125
|
+
index network update <id> --title "New Name" # Update a network
|
|
126
|
+
index network delete <id> # Delete a network
|
|
115
127
|
index network join <id> # Join a public network
|
|
116
128
|
index network leave <id> # Leave a network
|
|
117
129
|
index network invite <id> user@email # Invite a user by email
|
|
118
130
|
```
|
|
119
131
|
|
|
132
|
+
### `index contact`
|
|
133
|
+
|
|
134
|
+
Manage your contacts. Add, list, remove, or import contacts.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
index contact list # List your contacts
|
|
138
|
+
index contact add user@email # Add a contact by email
|
|
139
|
+
index contact add user@email --name "Name" # Add with display name
|
|
140
|
+
index contact remove user@email # Remove a contact
|
|
141
|
+
index contact import --gmail # Import contacts from Gmail
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `index scrape`
|
|
145
|
+
|
|
146
|
+
Scrape and extract content from a URL.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
index scrape https://example.com # Scrape a URL
|
|
150
|
+
index scrape https://example.com --objective "..." # Scrape with focus
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `index sync`
|
|
154
|
+
|
|
155
|
+
Sync all user context (profile, networks, intents, contacts) to a local file.
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
index sync # Sync to ~/.index/context.json
|
|
159
|
+
index sync --json # Output to stdout as JSON
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Examples: Opportunity Discovery
|
|
163
|
+
|
|
164
|
+
The `opportunity discover` command supports multiple modes for creating connections.
|
|
165
|
+
|
|
166
|
+
### Scenario 1: Search-based discovery
|
|
167
|
+
|
|
168
|
+
Find people whose intents match a search query. The protocol runs HyDE-powered semantic search across your networks.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Find collaborators for a project
|
|
172
|
+
index opportunity discover "looking for an AI engineer with privacy expertise"
|
|
173
|
+
|
|
174
|
+
# Scope discovery to a specific network
|
|
175
|
+
index opportunity discover "need a React developer" --target <network-id>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Scenario 2: Direct connection
|
|
179
|
+
|
|
180
|
+
Propose an opportunity with a specific person. Use when you already know who you want to connect with.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# First, find the user
|
|
184
|
+
index profile search "Jane Smith"
|
|
185
|
+
|
|
186
|
+
# Then create a direct opportunity with them
|
|
187
|
+
index opportunity discover "collaborate on open-source LLM tooling" --target <user-id>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Scenario 3: Introduction
|
|
191
|
+
|
|
192
|
+
Introduce two people you think should connect. You become the introducer — both parties see you as the connector.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Introduce two users to each other
|
|
196
|
+
index opportunity discover --introduce <user-id-a> <user-id-b>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Scenario 4: Review and act
|
|
200
|
+
|
|
201
|
+
After discovery creates draft opportunities, review and accept/reject them.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# List pending opportunities
|
|
205
|
+
index opportunity list --status pending
|
|
206
|
+
|
|
207
|
+
# See full details (reasoning, scores, mutual intents)
|
|
208
|
+
index opportunity show <id>
|
|
209
|
+
|
|
210
|
+
# Accept — starts a conversation thread
|
|
211
|
+
index opportunity accept <id>
|
|
212
|
+
|
|
213
|
+
# Or reject
|
|
214
|
+
index opportunity reject <id>
|
|
215
|
+
```
|
|
216
|
+
|
|
120
217
|
## Options
|
|
121
218
|
|
|
122
219
|
| Flag | Short | Description |
|
|
@@ -129,6 +226,13 @@ index network invite <id> user@email # Invite a user by email
|
|
|
129
226
|
| `--status <status>` | | Filter opportunities by status |
|
|
130
227
|
| `--limit <n>` | | Limit number of results |
|
|
131
228
|
| `--prompt <text>` | `-p` | Network description (for `network create`) |
|
|
229
|
+
| `--title <text>` | | Network title (for `network update`) |
|
|
230
|
+
| `--name <name>` | | Display name (for `contact add`) |
|
|
231
|
+
| `--gmail` | | Import from Gmail (for `contact import`) |
|
|
232
|
+
| `--target <id>` | | Target user ID (for `opportunity discover`) |
|
|
233
|
+
| `--introduce <id>` | | Introduce two users (for `opportunity discover`) |
|
|
234
|
+
| `--objective <text>` | | Focus objective (for `scrape`) |
|
|
235
|
+
| `--json` | | Output raw JSON to stdout |
|
|
132
236
|
| `--help` | `-h` | Show help |
|
|
133
237
|
| `--version` | `-v` | Show version |
|
|
134
238
|
|
|
@@ -170,6 +274,9 @@ cli/
|
|
|
170
274
|
intent.command.ts Intent (signal) subcommand handlers
|
|
171
275
|
opportunity.command.ts Opportunity subcommand handlers
|
|
172
276
|
network.command.ts Network subcommand handlers
|
|
277
|
+
contact.command.ts Contact subcommand handlers
|
|
278
|
+
scrape.command.ts URL scraping command handler
|
|
279
|
+
sync.command.ts Context sync command handler
|
|
173
280
|
output/ Terminal formatting, colors, markdown renderer
|
|
174
281
|
api.client.ts Typed HTTP client for the protocol API
|
|
175
282
|
auth.store.ts Credential persistence (~/.index/credentials.json)
|
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
5
|
|
|
6
6
|
// src/args.parser.ts
|
|
7
|
-
var KNOWN_COMMANDS = new Set(["login", "logout", "profile", "intent", "opportunity", "network", "conversation", "help", "version"]);
|
|
8
|
-
var OPPORTUNITY_SUBCOMMANDS = new Set(["list", "show", "accept", "reject"]);
|
|
9
|
-
var NETWORK_SUBCOMMANDS = new Set(["list", "create", "show", "join", "leave", "invite"]);
|
|
7
|
+
var KNOWN_COMMANDS = new Set(["login", "logout", "profile", "intent", "opportunity", "network", "conversation", "contact", "scrape", "onboarding", "sync", "help", "version"]);
|
|
8
|
+
var OPPORTUNITY_SUBCOMMANDS = new Set(["list", "show", "accept", "reject", "discover"]);
|
|
9
|
+
var NETWORK_SUBCOMMANDS = new Set(["list", "create", "show", "join", "leave", "invite", "update", "delete"]);
|
|
10
10
|
var CONVERSATION_SUBCOMMANDS = new Set(["list", "with", "show", "send", "stream", "sessions", "help"]);
|
|
11
11
|
function parseArgs(args) {
|
|
12
12
|
const result = {
|
|
@@ -62,6 +62,39 @@ function parseArgs(args) {
|
|
|
62
62
|
} else if (arg === "--prompt" || arg === "-p") {
|
|
63
63
|
result.prompt = args[i + 1];
|
|
64
64
|
i += 2;
|
|
65
|
+
} else if (arg === "--json") {
|
|
66
|
+
result.json = true;
|
|
67
|
+
i++;
|
|
68
|
+
} else if (arg === "--name") {
|
|
69
|
+
result.name = args[i + 1];
|
|
70
|
+
i += 2;
|
|
71
|
+
} else if (arg === "--gmail") {
|
|
72
|
+
result.gmail = true;
|
|
73
|
+
i++;
|
|
74
|
+
} else if (arg === "--objective") {
|
|
75
|
+
result.objective = args[i + 1];
|
|
76
|
+
i += 2;
|
|
77
|
+
} else if (arg === "--target") {
|
|
78
|
+
result.target = args[i + 1];
|
|
79
|
+
i += 2;
|
|
80
|
+
} else if (arg === "--introduce") {
|
|
81
|
+
result.introduce = args[i + 1];
|
|
82
|
+
i += 2;
|
|
83
|
+
} else if (arg === "--linkedin") {
|
|
84
|
+
result.linkedin = args[i + 1];
|
|
85
|
+
i += 2;
|
|
86
|
+
} else if (arg === "--github") {
|
|
87
|
+
result.github = args[i + 1];
|
|
88
|
+
i += 2;
|
|
89
|
+
} else if (arg === "--twitter") {
|
|
90
|
+
result.twitter = args[i + 1];
|
|
91
|
+
i += 2;
|
|
92
|
+
} else if (arg === "--title") {
|
|
93
|
+
result.title = args[i + 1];
|
|
94
|
+
i += 2;
|
|
95
|
+
} else if (arg === "--details") {
|
|
96
|
+
result.details = args[i + 1];
|
|
97
|
+
i += 2;
|
|
65
98
|
} else if (arg.startsWith("--")) {
|
|
66
99
|
i++;
|
|
67
100
|
} else {
|
|
@@ -73,7 +106,9 @@ function parseArgs(args) {
|
|
|
73
106
|
const sub = positionals[0];
|
|
74
107
|
if (sub && OPPORTUNITY_SUBCOMMANDS.has(sub)) {
|
|
75
108
|
result.subcommand = sub;
|
|
76
|
-
if (
|
|
109
|
+
if (sub === "discover") {
|
|
110
|
+
result.positionals = positionals.slice(1);
|
|
111
|
+
} else if (positionals[1]) {
|
|
77
112
|
result.targetId = positionals[1];
|
|
78
113
|
}
|
|
79
114
|
}
|
|
@@ -88,8 +123,34 @@ function parseArgs(args) {
|
|
|
88
123
|
}
|
|
89
124
|
} else if (sub === "sync") {
|
|
90
125
|
result.subcommand = "sync";
|
|
126
|
+
} else if (sub === "search") {
|
|
127
|
+
result.subcommand = "search";
|
|
128
|
+
result.positionals = positionals.slice(1);
|
|
129
|
+
} else if (sub === "create") {
|
|
130
|
+
result.subcommand = "create";
|
|
131
|
+
} else if (sub === "update") {
|
|
132
|
+
result.subcommand = "update";
|
|
133
|
+
result.positionals = positionals.slice(1);
|
|
91
134
|
}
|
|
92
135
|
}
|
|
136
|
+
if (result.command === "onboarding" && positionals.length > 0) {
|
|
137
|
+
const sub = positionals[0];
|
|
138
|
+
if (sub === "complete") {
|
|
139
|
+
result.subcommand = "complete";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (result.command === "contact") {
|
|
143
|
+
if (positionals.length > 0) {
|
|
144
|
+
const sub = positionals[0];
|
|
145
|
+
if (["list", "add", "remove", "import"].includes(sub)) {
|
|
146
|
+
result.subcommand = sub;
|
|
147
|
+
result.positionals = positionals.slice(1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (result.command === "scrape") {
|
|
152
|
+
result.positionals = positionals;
|
|
153
|
+
}
|
|
93
154
|
if (result.command === "intent") {
|
|
94
155
|
parseIntentArgs(positionals, result);
|
|
95
156
|
}
|
|
@@ -111,7 +172,7 @@ function parseArgs(args) {
|
|
|
111
172
|
}
|
|
112
173
|
return result;
|
|
113
174
|
}
|
|
114
|
-
var INTENT_SUBCOMMANDS = new Set(["list", "show", "create", "archive"]);
|
|
175
|
+
var INTENT_SUBCOMMANDS = new Set(["list", "show", "create", "archive", "update", "link", "unlink", "links"]);
|
|
115
176
|
function parseIntentArgs(positionals, result) {
|
|
116
177
|
if (positionals.length === 0)
|
|
117
178
|
return;
|
|
@@ -123,6 +184,7 @@ function parseIntentArgs(positionals, result) {
|
|
|
123
184
|
switch (result.subcommand) {
|
|
124
185
|
case "show":
|
|
125
186
|
case "archive":
|
|
187
|
+
case "links":
|
|
126
188
|
result.intentId = rest[0];
|
|
127
189
|
break;
|
|
128
190
|
case "create":
|
|
@@ -130,6 +192,17 @@ function parseIntentArgs(positionals, result) {
|
|
|
130
192
|
result.intentContent = rest.join(" ");
|
|
131
193
|
}
|
|
132
194
|
break;
|
|
195
|
+
case "update":
|
|
196
|
+
result.intentId = rest[0];
|
|
197
|
+
if (rest.length > 1) {
|
|
198
|
+
result.intentContent = rest.slice(1).join(" ");
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
case "link":
|
|
202
|
+
case "unlink":
|
|
203
|
+
result.intentId = rest[0];
|
|
204
|
+
result.targetId = rest[1];
|
|
205
|
+
break;
|
|
133
206
|
}
|
|
134
207
|
}
|
|
135
208
|
|
|
@@ -265,7 +338,10 @@ class ApiClient {
|
|
|
265
338
|
async listNetworks() {
|
|
266
339
|
const res = await this.get("/api/indexes");
|
|
267
340
|
const body = await res.json();
|
|
268
|
-
return body.indexes
|
|
341
|
+
return body.indexes.map((n) => ({
|
|
342
|
+
...n,
|
|
343
|
+
joinPolicy: n.joinPolicy ?? n.permissions?.joinPolicy
|
|
344
|
+
}));
|
|
269
345
|
}
|
|
270
346
|
async createNetwork(title, prompt) {
|
|
271
347
|
const res = await this.post("/api/indexes", {
|
|
@@ -278,7 +354,8 @@ class ApiClient {
|
|
|
278
354
|
async getNetwork(id) {
|
|
279
355
|
const res = await this.get(`/api/indexes/${id}`);
|
|
280
356
|
const body = await res.json();
|
|
281
|
-
|
|
357
|
+
const n = body.index;
|
|
358
|
+
return { ...n, joinPolicy: n.joinPolicy ?? n.permissions?.joinPolicy };
|
|
282
359
|
}
|
|
283
360
|
async getNetworkMembers(id) {
|
|
284
361
|
const res = await this.get(`/api/indexes/${id}/members`);
|
|
@@ -350,6 +427,10 @@ class ApiClient {
|
|
|
350
427
|
}
|
|
351
428
|
return res;
|
|
352
429
|
}
|
|
430
|
+
async callTool(toolName, query = {}) {
|
|
431
|
+
const res = await this.post(`/api/tools/${toolName}`, { query });
|
|
432
|
+
return await res.json();
|
|
433
|
+
}
|
|
353
434
|
async get(path) {
|
|
354
435
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
355
436
|
headers: {
|
|
@@ -910,6 +991,19 @@ function profileCard(data) {
|
|
|
910
991
|
console.log(output);
|
|
911
992
|
return output;
|
|
912
993
|
}
|
|
994
|
+
function contactTable(contacts) {
|
|
995
|
+
if (contacts.length === 0) {
|
|
996
|
+
console.log(" No contacts yet.");
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
const nameWidth = Math.max(6, ...contacts.map((c) => c.name.length));
|
|
1000
|
+
const header = ` ${"Name".padEnd(nameWidth)} Email`;
|
|
1001
|
+
console.log(`${BOLD}${header}${RESET}`);
|
|
1002
|
+
for (const c of contacts) {
|
|
1003
|
+
const ghost = c.isGhost ? ` ${DIM}(ghost)${RESET}` : "";
|
|
1004
|
+
console.log(` ${c.name.padEnd(nameWidth)} ${c.email}${ghost}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
913
1007
|
function sessionTable(sessions) {
|
|
914
1008
|
if (sessions.length === 0) {
|
|
915
1009
|
dim(" No chat sessions found.");
|
|
@@ -1034,10 +1128,12 @@ function opportunityTable(opportunities) {
|
|
|
1034
1128
|
`);
|
|
1035
1129
|
for (const opp of opportunities) {
|
|
1036
1130
|
const shortId = opp.id.slice(0, 8);
|
|
1037
|
-
const
|
|
1131
|
+
const fallbackName = opp.actors?.[1]?.name ?? opp.actors?.find((a) => a.name)?.name;
|
|
1132
|
+
const name = (opp.counterpartName ?? fallbackName ?? "Unknown").slice(0, nameW);
|
|
1038
1133
|
const category = (opp.interpretation?.category ?? "-").slice(0, catW);
|
|
1039
1134
|
const st = opp.status.slice(0, statusW);
|
|
1040
|
-
const
|
|
1135
|
+
const rawConf = opp.interpretation?.confidence;
|
|
1136
|
+
const conf = rawConf != null ? `${Math.round(rawConf <= 1 ? rawConf * 100 : rawConf)}%` : "-";
|
|
1041
1137
|
const date = opp.createdAt ? new Date(opp.createdAt).toLocaleDateString("en-US", {
|
|
1042
1138
|
year: "numeric",
|
|
1043
1139
|
month: "short",
|
|
@@ -1256,7 +1352,20 @@ function messageList(messages) {
|
|
|
1256
1352
|
}
|
|
1257
1353
|
}
|
|
1258
1354
|
// src/profile.command.ts
|
|
1259
|
-
async function handleProfile(client, subcommand, positionals) {
|
|
1355
|
+
async function handleProfile(client, subcommand, positionals, options = {}) {
|
|
1356
|
+
if (subcommand === "create") {
|
|
1357
|
+
await profileCreate(client, options);
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
if (subcommand === "update") {
|
|
1361
|
+
const action = positionals.join(" ");
|
|
1362
|
+
if (!action) {
|
|
1363
|
+
error("Usage: index profile update <action> [--details <text>]", 1);
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
await profileUpdate(client, action, options.details);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1260
1369
|
if (subcommand === "sync") {
|
|
1261
1370
|
await profileSync(client);
|
|
1262
1371
|
return;
|
|
@@ -1270,6 +1379,35 @@ async function handleProfile(client, subcommand, positionals) {
|
|
|
1270
1379
|
await profileShow(client, userId);
|
|
1271
1380
|
return;
|
|
1272
1381
|
}
|
|
1382
|
+
if (subcommand === "search") {
|
|
1383
|
+
const query = positionals.join(" ");
|
|
1384
|
+
if (!query) {
|
|
1385
|
+
error("Usage: index profile search <query>", 1);
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
const result = await client.callTool("read_user_profiles", { query });
|
|
1389
|
+
if (options.json) {
|
|
1390
|
+
console.log(JSON.stringify(result));
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
if (!result.success) {
|
|
1394
|
+
error(result.error ?? "Search failed", 1);
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const data = result.data;
|
|
1398
|
+
heading("Search Results");
|
|
1399
|
+
if (!data.profiles?.length) {
|
|
1400
|
+
dim(" No profiles found.");
|
|
1401
|
+
} else {
|
|
1402
|
+
for (const p of data.profiles) {
|
|
1403
|
+
console.log(` ${p.name} (${p.userId.slice(0, 8)})`);
|
|
1404
|
+
if (p.profile?.bio)
|
|
1405
|
+
dim(` ${p.profile.bio.slice(0, 100)}`);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
console.log();
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1273
1411
|
await profileMe(client);
|
|
1274
1412
|
}
|
|
1275
1413
|
async function profileMe(client) {
|
|
@@ -1288,6 +1426,34 @@ async function profileSync(client) {
|
|
|
1288
1426
|
await client.syncProfile();
|
|
1289
1427
|
success("Profile regeneration triggered. It may take a moment to complete.");
|
|
1290
1428
|
}
|
|
1429
|
+
async function profileCreate(client, options) {
|
|
1430
|
+
info("Creating profile...");
|
|
1431
|
+
const query = { confirm: true };
|
|
1432
|
+
if (options.linkedin)
|
|
1433
|
+
query.linkedinUrl = options.linkedin;
|
|
1434
|
+
if (options.github)
|
|
1435
|
+
query.githubUrl = options.github;
|
|
1436
|
+
if (options.twitter)
|
|
1437
|
+
query.twitterUrl = options.twitter;
|
|
1438
|
+
const result = await client.callTool("create_user_profile", query);
|
|
1439
|
+
if (!result.success) {
|
|
1440
|
+
error(result.error ?? "Profile creation failed", 1);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
success("Profile created.");
|
|
1444
|
+
}
|
|
1445
|
+
async function profileUpdate(client, action, details) {
|
|
1446
|
+
info("Updating profile...");
|
|
1447
|
+
const query = { action };
|
|
1448
|
+
if (details)
|
|
1449
|
+
query.details = details;
|
|
1450
|
+
const result = await client.callTool("update_user_profile", query);
|
|
1451
|
+
if (!result.success) {
|
|
1452
|
+
error(result.error ?? "Profile update failed", 1);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
success("Profile updated.");
|
|
1456
|
+
}
|
|
1291
1457
|
|
|
1292
1458
|
// src/intent.command.ts
|
|
1293
1459
|
var INTENT_HELP = `
|
|
@@ -1295,11 +1461,19 @@ Usage:
|
|
|
1295
1461
|
index intent list [--archived] [--limit <n>] List your signals
|
|
1296
1462
|
index intent show <id> Show signal details (accepts short ID)
|
|
1297
1463
|
index intent create <content> Create a signal from text
|
|
1464
|
+
index intent update <id> <content> Update a signal's description
|
|
1298
1465
|
index intent archive <id> Archive a signal (accepts short ID)
|
|
1466
|
+
index intent link <id> <network-id> Link a signal to a network
|
|
1467
|
+
index intent unlink <id> <network-id> Unlink a signal from a network
|
|
1468
|
+
index intent links <id> Show linked networks for a signal
|
|
1299
1469
|
`;
|
|
1300
1470
|
async function handleIntent(client, subcommand, options) {
|
|
1301
1471
|
if (!subcommand) {
|
|
1302
|
-
|
|
1472
|
+
if (options.json) {
|
|
1473
|
+
console.log(JSON.stringify({ error: "No subcommand provided" }));
|
|
1474
|
+
} else {
|
|
1475
|
+
console.log(INTENT_HELP);
|
|
1476
|
+
}
|
|
1303
1477
|
return;
|
|
1304
1478
|
}
|
|
1305
1479
|
switch (subcommand) {
|
|
@@ -1308,6 +1482,10 @@ async function handleIntent(client, subcommand, options) {
|
|
|
1308
1482
|
archived: options.archived,
|
|
1309
1483
|
limit: options.limit
|
|
1310
1484
|
});
|
|
1485
|
+
if (options.json) {
|
|
1486
|
+
console.log(JSON.stringify(result));
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1311
1489
|
heading("Signals");
|
|
1312
1490
|
intentTable(result.intents);
|
|
1313
1491
|
if (result.pagination.total > 0) {
|
|
@@ -1323,6 +1501,10 @@ async function handleIntent(client, subcommand, options) {
|
|
|
1323
1501
|
return;
|
|
1324
1502
|
}
|
|
1325
1503
|
const intent = await client.getIntent(options.intentId);
|
|
1504
|
+
if (options.json) {
|
|
1505
|
+
console.log(JSON.stringify(intent));
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1326
1508
|
intentCard(intent);
|
|
1327
1509
|
return;
|
|
1328
1510
|
}
|
|
@@ -1333,81 +1515,236 @@ async function handleIntent(client, subcommand, options) {
|
|
|
1333
1515
|
}
|
|
1334
1516
|
info("Processing signal...");
|
|
1335
1517
|
const result = await client.processIntent(options.intentContent);
|
|
1518
|
+
if (options.json) {
|
|
1519
|
+
console.log(JSON.stringify(result));
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1336
1522
|
success("Signal processed successfully.");
|
|
1337
1523
|
if (result.message) {
|
|
1338
1524
|
dim(` ${result.message}`);
|
|
1339
1525
|
}
|
|
1340
1526
|
return;
|
|
1341
1527
|
}
|
|
1528
|
+
case "update": {
|
|
1529
|
+
if (!options.intentId) {
|
|
1530
|
+
error("Missing signal ID. Usage: index intent update <id> <content>", 1);
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
if (!options.intentContent) {
|
|
1534
|
+
error("Missing content. Usage: index intent update <id> <content>", 1);
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
info("Updating signal...");
|
|
1538
|
+
const result = await client.callTool("update_intent", {
|
|
1539
|
+
intentId: options.intentId,
|
|
1540
|
+
newDescription: options.intentContent
|
|
1541
|
+
});
|
|
1542
|
+
if (options.json) {
|
|
1543
|
+
console.log(JSON.stringify(result));
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
if (!result.success) {
|
|
1547
|
+
error(result.error ?? "Failed to update signal", 1);
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
success("Signal updated.");
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1342
1553
|
case "archive": {
|
|
1343
1554
|
if (!options.intentId) {
|
|
1344
1555
|
error("Missing signal ID. Usage: index intent archive <id>", 1);
|
|
1345
1556
|
return;
|
|
1346
1557
|
}
|
|
1347
1558
|
await client.archiveIntent(options.intentId);
|
|
1559
|
+
if (options.json) {
|
|
1560
|
+
console.log(JSON.stringify({ success: true }));
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1348
1563
|
success(`Signal ${options.intentId} archived.`);
|
|
1349
1564
|
return;
|
|
1350
1565
|
}
|
|
1566
|
+
case "link": {
|
|
1567
|
+
if (!options.intentId || !options.targetId) {
|
|
1568
|
+
error("Usage: index intent link <intent-id> <network-id>", 1);
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
const result = await client.callTool("create_intent_index", {
|
|
1572
|
+
intentId: options.intentId,
|
|
1573
|
+
indexId: options.targetId
|
|
1574
|
+
});
|
|
1575
|
+
if (options.json) {
|
|
1576
|
+
console.log(JSON.stringify(result));
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
if (!result.success) {
|
|
1580
|
+
error(result.error ?? "Failed to link signal", 1);
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
success("Signal linked to network.");
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
case "unlink": {
|
|
1587
|
+
if (!options.intentId || !options.targetId) {
|
|
1588
|
+
error("Usage: index intent unlink <intent-id> <network-id>", 1);
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
const result = await client.callTool("delete_intent_index", {
|
|
1592
|
+
intentId: options.intentId,
|
|
1593
|
+
indexId: options.targetId
|
|
1594
|
+
});
|
|
1595
|
+
if (options.json) {
|
|
1596
|
+
console.log(JSON.stringify(result));
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
if (!result.success) {
|
|
1600
|
+
error(result.error ?? "Failed to unlink signal", 1);
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
success("Signal unlinked from network.");
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
case "links": {
|
|
1607
|
+
if (!options.intentId) {
|
|
1608
|
+
error("Missing signal ID. Usage: index intent links <id>", 1);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
const result = await client.callTool("read_intent_indexes", {
|
|
1612
|
+
intentId: options.intentId
|
|
1613
|
+
});
|
|
1614
|
+
if (options.json) {
|
|
1615
|
+
console.log(JSON.stringify(result));
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
if (!result.success) {
|
|
1619
|
+
error(result.error ?? "Failed to read linked networks", 1);
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
const data = result.data;
|
|
1623
|
+
heading("Linked Networks");
|
|
1624
|
+
if (!data.indexes?.length) {
|
|
1625
|
+
dim(" No linked networks.");
|
|
1626
|
+
} else {
|
|
1627
|
+
for (const idx of data.indexes) {
|
|
1628
|
+
const score = idx.relevancyScore !== undefined ? ` (${idx.relevancyScore.toFixed(2)})` : "";
|
|
1629
|
+
console.log(` ${idx.title} ${DIM}${idx.indexId.slice(0, 8)}${score}${RESET}`);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
console.log();
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1351
1635
|
}
|
|
1352
1636
|
}
|
|
1353
1637
|
|
|
1354
1638
|
// src/opportunity.command.ts
|
|
1355
1639
|
var OPPORTUNITY_HELP = `
|
|
1356
1640
|
Usage:
|
|
1357
|
-
index opportunity list
|
|
1358
|
-
index opportunity list --status <s>
|
|
1359
|
-
index opportunity list --limit <n>
|
|
1360
|
-
index opportunity show <id>
|
|
1361
|
-
index opportunity accept <id>
|
|
1362
|
-
index opportunity reject <id>
|
|
1641
|
+
index opportunity list List your opportunities
|
|
1642
|
+
index opportunity list --status <s> Filter by status (pending|accepted|rejected|expired)
|
|
1643
|
+
index opportunity list --limit <n> Limit results
|
|
1644
|
+
index opportunity show <id> Show full opportunity details (accepts short ID)
|
|
1645
|
+
index opportunity accept <id> Accept an opportunity (accepts short ID)
|
|
1646
|
+
index opportunity reject <id> Reject an opportunity (accepts short ID)
|
|
1647
|
+
index opportunity discover <query> Discover opportunities by search query
|
|
1648
|
+
index opportunity discover --target <uid> <q> Discover for a specific user
|
|
1649
|
+
index opportunity discover --introduce <a> <b> Introduce two users
|
|
1363
1650
|
`;
|
|
1364
1651
|
async function handleOpportunity(client, subcommand, options) {
|
|
1365
1652
|
if (!subcommand) {
|
|
1366
|
-
|
|
1653
|
+
if (options.json) {
|
|
1654
|
+
console.log(JSON.stringify({ error: "No subcommand provided" }));
|
|
1655
|
+
} else {
|
|
1656
|
+
console.log(OPPORTUNITY_HELP);
|
|
1657
|
+
}
|
|
1367
1658
|
return;
|
|
1368
1659
|
}
|
|
1369
1660
|
switch (subcommand) {
|
|
1370
1661
|
case "list":
|
|
1371
|
-
await opportunityList(client, options.status, options.limit);
|
|
1662
|
+
await opportunityList(client, options.status, options.limit, options.json);
|
|
1372
1663
|
return;
|
|
1373
1664
|
case "show":
|
|
1374
1665
|
if (!options.targetId) {
|
|
1375
1666
|
error("Missing opportunity ID. Usage: index opportunity show <id>", 1);
|
|
1376
1667
|
return;
|
|
1377
1668
|
}
|
|
1378
|
-
await opportunityShow(client, options.targetId);
|
|
1669
|
+
await opportunityShow(client, options.targetId, options.json);
|
|
1379
1670
|
return;
|
|
1380
1671
|
case "accept":
|
|
1381
1672
|
if (!options.targetId) {
|
|
1382
1673
|
error("Missing opportunity ID. Usage: index opportunity accept <id>", 1);
|
|
1383
1674
|
return;
|
|
1384
1675
|
}
|
|
1385
|
-
await opportunityStatusUpdate(client, options.targetId, "accepted");
|
|
1676
|
+
await opportunityStatusUpdate(client, options.targetId, "accepted", options.json);
|
|
1386
1677
|
return;
|
|
1387
1678
|
case "reject":
|
|
1388
1679
|
if (!options.targetId) {
|
|
1389
1680
|
error("Missing opportunity ID. Usage: index opportunity reject <id>", 1);
|
|
1390
1681
|
return;
|
|
1391
1682
|
}
|
|
1392
|
-
await opportunityStatusUpdate(client, options.targetId, "rejected");
|
|
1683
|
+
await opportunityStatusUpdate(client, options.targetId, "rejected", options.json);
|
|
1684
|
+
return;
|
|
1685
|
+
case "discover": {
|
|
1686
|
+
const query = options.positionals?.join(" ");
|
|
1687
|
+
if (!query && !options.introduce) {
|
|
1688
|
+
error("Usage: index opportunity discover <query>", 1);
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
info("Discovering opportunities...");
|
|
1692
|
+
const toolQuery = {};
|
|
1693
|
+
if (options.introduce) {
|
|
1694
|
+
toolQuery.mode = "introduction";
|
|
1695
|
+
toolQuery.sourceUserId = options.introduce;
|
|
1696
|
+
toolQuery.targetUserId = options.positionals?.[0];
|
|
1697
|
+
} else if (options.target) {
|
|
1698
|
+
toolQuery.mode = "direct";
|
|
1699
|
+
toolQuery.targetUserId = options.target;
|
|
1700
|
+
toolQuery.searchQuery = query;
|
|
1701
|
+
} else {
|
|
1702
|
+
toolQuery.searchQuery = query;
|
|
1703
|
+
}
|
|
1704
|
+
const result = await client.callTool("create_opportunities", toolQuery);
|
|
1705
|
+
if (options.json) {
|
|
1706
|
+
console.log(JSON.stringify(result));
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
if (!result.success) {
|
|
1710
|
+
error(result.error ?? "Discovery failed", 1);
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
success("Discovery complete.");
|
|
1714
|
+
const data = result.data;
|
|
1715
|
+
if (data?.message)
|
|
1716
|
+
dim(` ${data.message}`);
|
|
1393
1717
|
return;
|
|
1718
|
+
}
|
|
1394
1719
|
default:
|
|
1395
1720
|
error(`Unknown subcommand: ${subcommand}`, 1);
|
|
1396
1721
|
return;
|
|
1397
1722
|
}
|
|
1398
1723
|
}
|
|
1399
|
-
async function opportunityList(client, status2, limit) {
|
|
1724
|
+
async function opportunityList(client, status2, limit, json) {
|
|
1400
1725
|
const opportunities = await client.listOpportunities({ status: status2, limit });
|
|
1726
|
+
if (json) {
|
|
1727
|
+
console.log(JSON.stringify(opportunities));
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1401
1730
|
heading("Opportunities");
|
|
1402
1731
|
opportunityTable(opportunities);
|
|
1403
1732
|
console.log();
|
|
1404
1733
|
}
|
|
1405
|
-
async function opportunityShow(client, id) {
|
|
1734
|
+
async function opportunityShow(client, id, json) {
|
|
1406
1735
|
const opportunity = await client.getOpportunity(id);
|
|
1736
|
+
if (json) {
|
|
1737
|
+
console.log(JSON.stringify(opportunity));
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1407
1740
|
opportunityCard(opportunity);
|
|
1408
1741
|
}
|
|
1409
|
-
async function opportunityStatusUpdate(client, id, status2) {
|
|
1410
|
-
await client.updateOpportunityStatus(id, status2);
|
|
1742
|
+
async function opportunityStatusUpdate(client, id, status2, json) {
|
|
1743
|
+
const result = await client.updateOpportunityStatus(id, status2);
|
|
1744
|
+
if (json) {
|
|
1745
|
+
console.log(JSON.stringify(result));
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1411
1748
|
const label = status2 === "accepted" ? "accepted" : "rejected";
|
|
1412
1749
|
success(`Opportunity ${label}.`);
|
|
1413
1750
|
}
|
|
@@ -1419,6 +1756,9 @@ Network Commands:
|
|
|
1419
1756
|
index network create <name> Create a new network
|
|
1420
1757
|
index network create <name> --prompt Create with a description
|
|
1421
1758
|
index network show <id|key> Show network details and members
|
|
1759
|
+
index network update <id> [--title <t>] [--prompt <p>]
|
|
1760
|
+
Update a network
|
|
1761
|
+
index network delete <id> Delete a network
|
|
1422
1762
|
index network join <id|key> Join a public network
|
|
1423
1763
|
index network leave <id|key> Leave a network
|
|
1424
1764
|
index network invite <id|key> <email> Invite a user by email
|
|
@@ -1444,6 +1784,12 @@ async function handleNetwork(client, subcommand, positionals, options) {
|
|
|
1444
1784
|
case "leave":
|
|
1445
1785
|
await networkLeave(client, positionals[0]);
|
|
1446
1786
|
return;
|
|
1787
|
+
case "update":
|
|
1788
|
+
await networkUpdate(client, positionals[0], options);
|
|
1789
|
+
return;
|
|
1790
|
+
case "delete":
|
|
1791
|
+
await networkDelete(client, positionals[0]);
|
|
1792
|
+
return;
|
|
1447
1793
|
case "invite":
|
|
1448
1794
|
await networkInvite(client, positionals[0], positionals[1]);
|
|
1449
1795
|
return;
|
|
@@ -1499,6 +1845,39 @@ async function networkLeave(client, id) {
|
|
|
1499
1845
|
await client.leaveNetwork(id);
|
|
1500
1846
|
success("Left network.");
|
|
1501
1847
|
}
|
|
1848
|
+
async function networkUpdate(client, id, options) {
|
|
1849
|
+
if (!id) {
|
|
1850
|
+
error("Usage: index network update <id> [--title <t>] [--prompt <p>]", 1);
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
const settings = {};
|
|
1854
|
+
if (options.title)
|
|
1855
|
+
settings.title = options.title;
|
|
1856
|
+
if (options.prompt)
|
|
1857
|
+
settings.prompt = options.prompt;
|
|
1858
|
+
if (Object.keys(settings).length === 0) {
|
|
1859
|
+
error("Provide at least --title or --prompt. Usage: index network update <id> [--title <t>] [--prompt <p>]", 1);
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
const result = await client.callTool("update_index", { indexId: id, settings });
|
|
1863
|
+
if (!result.success) {
|
|
1864
|
+
error(result.error ?? "Network update failed", 1);
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
success("Network updated.");
|
|
1868
|
+
}
|
|
1869
|
+
async function networkDelete(client, id) {
|
|
1870
|
+
if (!id) {
|
|
1871
|
+
error("Usage: index network delete <id>", 1);
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
const result = await client.callTool("delete_index", { indexId: id });
|
|
1875
|
+
if (!result.success) {
|
|
1876
|
+
error(result.error ?? "Network deletion failed", 1);
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
success("Network deleted.");
|
|
1880
|
+
}
|
|
1502
1881
|
async function networkInvite(client, id, email) {
|
|
1503
1882
|
if (!id || !email) {
|
|
1504
1883
|
error("Usage: index network invite <id> <email>", 1);
|
|
@@ -1885,6 +2264,175 @@ async function handleStreamError(response) {
|
|
|
1885
2264
|
error(body.error ?? `HTTP ${response.status}`, 1);
|
|
1886
2265
|
}
|
|
1887
2266
|
|
|
2267
|
+
// src/contact.command.ts
|
|
2268
|
+
var CONTACT_HELP = `
|
|
2269
|
+
Usage:
|
|
2270
|
+
index contact list List your contacts
|
|
2271
|
+
index contact add <email> [--name <n>] Add a contact by email
|
|
2272
|
+
index contact remove <email> Remove a contact
|
|
2273
|
+
index contact import --gmail Import contacts from Gmail
|
|
2274
|
+
`;
|
|
2275
|
+
async function handleContact(client, subcommand, positionals, options) {
|
|
2276
|
+
if (!subcommand) {
|
|
2277
|
+
if (options.json) {
|
|
2278
|
+
console.log(JSON.stringify({ error: "No subcommand provided" }));
|
|
2279
|
+
} else {
|
|
2280
|
+
console.log(CONTACT_HELP);
|
|
2281
|
+
}
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
switch (subcommand) {
|
|
2285
|
+
case "list": {
|
|
2286
|
+
const result = await client.callTool("list_contacts", {});
|
|
2287
|
+
if (options.json) {
|
|
2288
|
+
console.log(JSON.stringify(result));
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2291
|
+
if (!result.success) {
|
|
2292
|
+
error(result.error ?? "Failed to list contacts", 1);
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
const data = result.data;
|
|
2296
|
+
heading("Contacts");
|
|
2297
|
+
contactTable(data.contacts);
|
|
2298
|
+
dim(`
|
|
2299
|
+
${data.count} contact${data.count !== 1 ? "s" : ""}`);
|
|
2300
|
+
console.log();
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
case "add": {
|
|
2304
|
+
const email = positionals[0];
|
|
2305
|
+
if (!email) {
|
|
2306
|
+
error("Missing email. Usage: index contact add <email>", 1);
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
const result = await client.callTool("add_contact", { email, name: options.name });
|
|
2310
|
+
if (options.json) {
|
|
2311
|
+
console.log(JSON.stringify(result));
|
|
2312
|
+
return;
|
|
2313
|
+
}
|
|
2314
|
+
if (!result.success) {
|
|
2315
|
+
error(result.error ?? "Failed to add contact", 1);
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
const data = result.data;
|
|
2319
|
+
success(data.message);
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
case "remove": {
|
|
2323
|
+
const email = positionals[0];
|
|
2324
|
+
if (!email) {
|
|
2325
|
+
error("Missing email. Usage: index contact remove <email>", 1);
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
const listResult = await client.callTool("list_contacts", {});
|
|
2329
|
+
if (!listResult.success) {
|
|
2330
|
+
error("Failed to resolve contact", 1);
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
const contacts = listResult.data.contacts;
|
|
2334
|
+
const match = contacts.find((c) => c.email.toLowerCase() === email.toLowerCase());
|
|
2335
|
+
if (!match) {
|
|
2336
|
+
error(`No contact found with email: ${email}`, 1);
|
|
2337
|
+
return;
|
|
2338
|
+
}
|
|
2339
|
+
const result = await client.callTool("remove_contact", { contactUserId: match.userId });
|
|
2340
|
+
if (options.json) {
|
|
2341
|
+
console.log(JSON.stringify(result));
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
if (!result.success) {
|
|
2345
|
+
error(result.error ?? "Failed to remove contact", 1);
|
|
2346
|
+
return;
|
|
2347
|
+
}
|
|
2348
|
+
success(`Removed ${email} from contacts.`);
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
case "import": {
|
|
2352
|
+
if (options.gmail) {
|
|
2353
|
+
const result = await client.callTool("import_gmail_contacts", {});
|
|
2354
|
+
if (options.json) {
|
|
2355
|
+
console.log(JSON.stringify(result));
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
2358
|
+
if (!result.success) {
|
|
2359
|
+
error(result.error ?? "Failed to import Gmail contacts", 1);
|
|
2360
|
+
return;
|
|
2361
|
+
}
|
|
2362
|
+
const data = result.data;
|
|
2363
|
+
success(data.message);
|
|
2364
|
+
} else {
|
|
2365
|
+
error("Specify import source: --gmail", 1);
|
|
2366
|
+
}
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
default: {
|
|
2370
|
+
if (options.json) {
|
|
2371
|
+
console.log(JSON.stringify({ error: `Unknown subcommand: ${subcommand}` }));
|
|
2372
|
+
} else {
|
|
2373
|
+
error(`Unknown subcommand: ${subcommand}`);
|
|
2374
|
+
console.log(CONTACT_HELP);
|
|
2375
|
+
}
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// src/scrape.command.ts
|
|
2382
|
+
async function handleScrape(client, positionals, options) {
|
|
2383
|
+
const url = positionals[0];
|
|
2384
|
+
if (!url) {
|
|
2385
|
+
error("Usage: index scrape <url> [--objective <text>]", 1);
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
info(`Scraping ${url}...`);
|
|
2389
|
+
const result = await client.callTool("scrape_url", { url, objective: options.objective });
|
|
2390
|
+
if (options.json) {
|
|
2391
|
+
console.log(JSON.stringify(result));
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
if (!result.success) {
|
|
2395
|
+
error(result.error ?? "Scrape failed", 1);
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
const data = result.data;
|
|
2399
|
+
heading(`Content from ${data.url}`);
|
|
2400
|
+
console.log(data.content);
|
|
2401
|
+
dim(`
|
|
2402
|
+
${data.contentLength} characters extracted`);
|
|
2403
|
+
console.log();
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
// src/sync.command.ts
|
|
2407
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2408
|
+
import { homedir as homedir2 } from "node:os";
|
|
2409
|
+
import { join as join2 } from "node:path";
|
|
2410
|
+
async function handleSync(client, options) {
|
|
2411
|
+
if (!options.json)
|
|
2412
|
+
info("Syncing context...");
|
|
2413
|
+
const [profile, networks, intents, contacts] = await Promise.all([
|
|
2414
|
+
client.callTool("read_user_profiles", {}),
|
|
2415
|
+
client.callTool("read_indexes", {}),
|
|
2416
|
+
client.callTool("read_intents", {}),
|
|
2417
|
+
client.callTool("list_contacts", {})
|
|
2418
|
+
]);
|
|
2419
|
+
const context = {
|
|
2420
|
+
syncedAt: new Date().toISOString(),
|
|
2421
|
+
profile: profile.success ? profile.data : null,
|
|
2422
|
+
networks: networks.success ? networks.data : null,
|
|
2423
|
+
intents: intents.success ? intents.data : null,
|
|
2424
|
+
contacts: contacts.success ? contacts.data : null
|
|
2425
|
+
};
|
|
2426
|
+
if (options.json) {
|
|
2427
|
+
console.log(JSON.stringify(context));
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
const dir = join2(homedir2(), ".index");
|
|
2431
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2432
|
+
writeFileSync(join2(dir, "context.json"), JSON.stringify(context, null, 2), { mode: 384 });
|
|
2433
|
+
success("Context synced to ~/.index/context.json");
|
|
2434
|
+
}
|
|
2435
|
+
|
|
1888
2436
|
// src/main.ts
|
|
1889
2437
|
var DEFAULT_API_URL = "https://protocol.index.network";
|
|
1890
2438
|
var DEFAULT_APP_URL = "https://index.network";
|
|
@@ -1908,22 +2456,43 @@ Usage:
|
|
|
1908
2456
|
index profile Show your profile
|
|
1909
2457
|
index profile show <user-id> Show another user's profile
|
|
1910
2458
|
index profile sync Regenerate your profile
|
|
2459
|
+
index profile search <query> Search user profiles
|
|
2460
|
+
index profile create [--linkedin <url>] [--github <url>] [--twitter <url>]
|
|
2461
|
+
Create your profile from social URLs
|
|
2462
|
+
index profile update <action> [--details <text>]
|
|
2463
|
+
Update your profile
|
|
1911
2464
|
index intent list [--archived] [--limit <n>] List your signals
|
|
1912
2465
|
index intent show <id> Show signal details
|
|
1913
2466
|
index intent create <content> Create a signal from natural language
|
|
2467
|
+
index intent update <id> <content> Update a signal's description
|
|
1914
2468
|
index intent archive <id> Archive a signal
|
|
2469
|
+
index intent link <id> <network-id> Link a signal to a network
|
|
2470
|
+
index intent unlink <id> <network-id> Unlink a signal from a network
|
|
2471
|
+
index intent links <id> Show linked networks for a signal
|
|
1915
2472
|
index opportunity list List your opportunities
|
|
1916
2473
|
index opportunity list --status <s> Filter by status (pending|accepted|rejected|expired)
|
|
1917
2474
|
index opportunity list --limit <n> Limit results
|
|
1918
2475
|
index opportunity show <id> Show full opportunity details
|
|
1919
2476
|
index opportunity accept <id> Accept an opportunity
|
|
1920
2477
|
index opportunity reject <id> Reject an opportunity
|
|
2478
|
+
index opportunity discover <query> Discover opportunities by search query
|
|
1921
2479
|
index network list List your networks
|
|
1922
2480
|
index network create <name> Create a new network
|
|
1923
2481
|
index network show <id> Show network details and members
|
|
2482
|
+
index network update <id> [--title <t>] [--prompt <p>]
|
|
2483
|
+
Update a network
|
|
2484
|
+
index network delete <id> Delete a network
|
|
1924
2485
|
index network join <id> Join a public network
|
|
1925
2486
|
index network leave <id> Leave a network
|
|
1926
2487
|
index network invite <id> <email> Invite a user by email
|
|
2488
|
+
index contact list List your contacts
|
|
2489
|
+
index contact add <email> Add a contact by email
|
|
2490
|
+
index contact remove <email> Remove a contact
|
|
2491
|
+
index contact import --gmail Import contacts from Gmail
|
|
2492
|
+
index scrape <url> Extract content from a URL
|
|
2493
|
+
index onboarding complete Mark onboarding as complete
|
|
2494
|
+
index sync Sync context to ~/.index/context.json
|
|
2495
|
+
index sync --json Output synced context to stdout
|
|
1927
2496
|
index --help Show this help message
|
|
1928
2497
|
index --version Show version
|
|
1929
2498
|
|
|
@@ -1935,6 +2504,17 @@ Options:
|
|
|
1935
2504
|
--archived Include archived signals (intent list)
|
|
1936
2505
|
--status <status> Filter opportunities by status
|
|
1937
2506
|
--limit <n> Limit number of results
|
|
2507
|
+
--json Output raw JSON instead of formatted text
|
|
2508
|
+
--name <name> Name for contact add
|
|
2509
|
+
--gmail Import source flag for contact import
|
|
2510
|
+
--objective <text> Objective for scrape command
|
|
2511
|
+
--target <uid> Target user for opportunity discover
|
|
2512
|
+
--introduce <uid> Source user for introduction discovery
|
|
2513
|
+
--linkedin <url> LinkedIn URL for profile create
|
|
2514
|
+
--github <url> GitHub URL for profile create
|
|
2515
|
+
--twitter <url> Twitter URL for profile create
|
|
2516
|
+
--title <text> Title for network update
|
|
2517
|
+
--details <text> Details for profile update
|
|
1938
2518
|
`;
|
|
1939
2519
|
async function requireAuth(apiUrlOverride) {
|
|
1940
2520
|
const store = new CredentialStore;
|
|
@@ -2017,26 +2597,39 @@ async function main() {
|
|
|
2017
2597
|
const client = await requireAuth(args.apiUrl);
|
|
2018
2598
|
switch (args.command) {
|
|
2019
2599
|
case "profile":
|
|
2020
|
-
await handleProfile(client, args.subcommand, args.userId ? [args.userId] : []
|
|
2600
|
+
await handleProfile(client, args.subcommand, args.subcommand === "search" || args.subcommand === "update" ? args.positionals ?? [] : args.userId ? [args.userId] : [], {
|
|
2601
|
+
json: args.json,
|
|
2602
|
+
linkedin: args.linkedin,
|
|
2603
|
+
github: args.github,
|
|
2604
|
+
twitter: args.twitter,
|
|
2605
|
+
details: args.details
|
|
2606
|
+
});
|
|
2021
2607
|
return;
|
|
2022
2608
|
case "intent":
|
|
2023
2609
|
await handleIntent(client, args.subcommand, {
|
|
2024
2610
|
intentId: args.intentId,
|
|
2025
2611
|
intentContent: args.intentContent,
|
|
2026
2612
|
archived: args.archived,
|
|
2027
|
-
limit: args.limit
|
|
2613
|
+
limit: args.limit,
|
|
2614
|
+
json: args.json,
|
|
2615
|
+
targetId: args.targetId
|
|
2028
2616
|
});
|
|
2029
2617
|
return;
|
|
2030
2618
|
case "opportunity":
|
|
2031
2619
|
await handleOpportunity(client, args.subcommand, {
|
|
2032
2620
|
targetId: args.targetId,
|
|
2033
2621
|
status: args.status,
|
|
2034
|
-
limit: args.limit
|
|
2622
|
+
limit: args.limit,
|
|
2623
|
+
json: args.json,
|
|
2624
|
+
positionals: args.positionals,
|
|
2625
|
+
target: args.target,
|
|
2626
|
+
introduce: args.introduce
|
|
2035
2627
|
});
|
|
2036
2628
|
return;
|
|
2037
2629
|
case "network":
|
|
2038
2630
|
await handleNetwork(client, args.subcommand, args.positionals ?? [], {
|
|
2039
|
-
prompt: args.prompt
|
|
2631
|
+
prompt: args.prompt,
|
|
2632
|
+
title: args.title
|
|
2040
2633
|
});
|
|
2041
2634
|
return;
|
|
2042
2635
|
case "conversation":
|
|
@@ -2046,6 +2639,34 @@ async function main() {
|
|
|
2046
2639
|
message: args.message
|
|
2047
2640
|
});
|
|
2048
2641
|
return;
|
|
2642
|
+
case "contact":
|
|
2643
|
+
await handleContact(client, args.subcommand, args.positionals ?? [], {
|
|
2644
|
+
json: args.json,
|
|
2645
|
+
name: args.name,
|
|
2646
|
+
gmail: args.gmail
|
|
2647
|
+
});
|
|
2648
|
+
return;
|
|
2649
|
+
case "scrape":
|
|
2650
|
+
await handleScrape(client, args.positionals ?? [], {
|
|
2651
|
+
json: args.json,
|
|
2652
|
+
objective: args.objective
|
|
2653
|
+
});
|
|
2654
|
+
return;
|
|
2655
|
+
case "onboarding":
|
|
2656
|
+
if (args.subcommand === "complete") {
|
|
2657
|
+
const result = await client.callTool("complete_onboarding", {});
|
|
2658
|
+
if (!result.success) {
|
|
2659
|
+
error(result.error ?? "Onboarding completion failed", 1);
|
|
2660
|
+
} else {
|
|
2661
|
+
success("Onboarding completed.");
|
|
2662
|
+
}
|
|
2663
|
+
} else {
|
|
2664
|
+
error("Usage: index onboarding complete", 1);
|
|
2665
|
+
}
|
|
2666
|
+
return;
|
|
2667
|
+
case "sync":
|
|
2668
|
+
await handleSync(client, { json: args.json });
|
|
2669
|
+
return;
|
|
2049
2670
|
}
|
|
2050
2671
|
}
|
|
2051
2672
|
main().catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indexnetwork/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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.
|
|
24
|
-
"@indexnetwork/cli-linux-arm64": "0.
|
|
25
|
-
"@indexnetwork/cli-darwin-x64": "0.
|
|
26
|
-
"@indexnetwork/cli-darwin-arm64": "0.
|
|
23
|
+
"@indexnetwork/cli-linux-x64": "0.9.0",
|
|
24
|
+
"@indexnetwork/cli-linux-arm64": "0.9.0",
|
|
25
|
+
"@indexnetwork/cli-darwin-x64": "0.9.0",
|
|
26
|
+
"@indexnetwork/cli-darwin-arm64": "0.9.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/bun": "latest"
|