@caphub/cli 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/bin/caphub.js +121 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,10 +16,16 @@ npx @caphub/cli help
|
|
|
16
16
|
|
|
17
17
|
## Auth
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Open the website approval flow:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
caphub auth login
|
|
22
|
+
caphub auth login
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For headless/cloud agents, put the key in the platform secret manager and expose it as:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
CAPHUB_API_KEY=csk_live_...
|
|
23
29
|
```
|
|
24
30
|
|
|
25
31
|
## Discovery
|
package/bin/caphub.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
7
8
|
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -23,7 +24,8 @@ commands:
|
|
|
23
24
|
help explain the platform and command layout
|
|
24
25
|
help <capability> show capability-specific help from the API
|
|
25
26
|
capabilities list live capabilities with short descriptions
|
|
26
|
-
auth login
|
|
27
|
+
auth show current login state
|
|
28
|
+
auth login open website login flow; stores api key locally after approval
|
|
27
29
|
auth whoami verify the current api key against the API
|
|
28
30
|
auth logout remove stored api key from local config
|
|
29
31
|
<capability> <json> run a capability directly, e.g. search or search-ideas
|
|
@@ -31,18 +33,20 @@ commands:
|
|
|
31
33
|
agent workflow:
|
|
32
34
|
1. caphub capabilities
|
|
33
35
|
2. caphub help <capability>
|
|
34
|
-
3. caphub auth login
|
|
36
|
+
3. caphub auth login
|
|
35
37
|
4. caphub <capability> '<json>'
|
|
36
38
|
|
|
37
39
|
recovery:
|
|
38
|
-
no api key caphub auth login
|
|
40
|
+
no api key caphub auth login
|
|
39
41
|
invalid api key generate a new key in https://caphub.io/dashboard/
|
|
40
42
|
insufficient credits top up in https://caphub.io/dashboard/
|
|
41
43
|
bad json caphub help <capability>
|
|
42
44
|
capability unknown caphub capabilities
|
|
43
45
|
|
|
44
46
|
examples:
|
|
47
|
+
caphub auth
|
|
45
48
|
caphub capabilities
|
|
49
|
+
caphub auth login
|
|
46
50
|
caphub auth login --api-key csk_live_...
|
|
47
51
|
caphub help search
|
|
48
52
|
caphub search '{"queries":["site:github.com awesome ai agents"]}'
|
|
@@ -93,6 +97,27 @@ function fail(message) {
|
|
|
93
97
|
process.exit(1);
|
|
94
98
|
}
|
|
95
99
|
|
|
100
|
+
function openUrl(url) {
|
|
101
|
+
if (process.env.CAPHUB_NO_OPEN === "1") return false;
|
|
102
|
+
|
|
103
|
+
const command = process.platform === "darwin"
|
|
104
|
+
? "open"
|
|
105
|
+
: process.platform === "win32"
|
|
106
|
+
? "cmd"
|
|
107
|
+
: "xdg-open";
|
|
108
|
+
const args = process.platform === "win32"
|
|
109
|
+
? ["/c", "start", "", url]
|
|
110
|
+
: [url];
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore" });
|
|
114
|
+
child.unref();
|
|
115
|
+
return true;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
96
121
|
function readStdin() {
|
|
97
122
|
return new Promise((resolveInput) => {
|
|
98
123
|
let data = "";
|
|
@@ -145,7 +170,7 @@ function buildRecoveryHints(error, context = {}) {
|
|
|
145
170
|
const status = Number(error?.status || 0);
|
|
146
171
|
|
|
147
172
|
if (status === 401 || message.includes("missing x-api-key header")) {
|
|
148
|
-
hints.push("login: caphub auth login
|
|
173
|
+
hints.push("login: caphub auth login");
|
|
149
174
|
}
|
|
150
175
|
|
|
151
176
|
if (status === 403 || message.includes("invalid api key")) {
|
|
@@ -191,6 +216,10 @@ function failWithHints(message, error, context = {}) {
|
|
|
191
216
|
fail(lines.join("\n"));
|
|
192
217
|
}
|
|
193
218
|
|
|
219
|
+
function sleep(ms) {
|
|
220
|
+
return new Promise((resolveWait) => setTimeout(resolveWait, ms));
|
|
221
|
+
}
|
|
222
|
+
|
|
194
223
|
function printCapabilities(payload) {
|
|
195
224
|
const lines = [
|
|
196
225
|
"caphub capabilities",
|
|
@@ -230,7 +259,7 @@ function printCapabilityHelp(payload) {
|
|
|
230
259
|
...(payload.notes_for_agents || []).map((note) => `- ${note}`),
|
|
231
260
|
"",
|
|
232
261
|
"common recovery:",
|
|
233
|
-
"- auth missing: caphub auth login
|
|
262
|
+
"- auth missing: caphub auth login",
|
|
234
263
|
"- auth invalid: generate a new key in https://caphub.io/dashboard/",
|
|
235
264
|
"- low credits: top up in https://caphub.io/dashboard/",
|
|
236
265
|
`- bad payload: caphub help ${payload.capability}`,
|
|
@@ -264,8 +293,94 @@ async function commandAuth(args) {
|
|
|
264
293
|
const sub = args[0];
|
|
265
294
|
const config = readConfig();
|
|
266
295
|
|
|
296
|
+
if (!sub) {
|
|
297
|
+
const apiKey = getApiKey();
|
|
298
|
+
if (!apiKey) {
|
|
299
|
+
process.stdout.write([
|
|
300
|
+
"caphub auth",
|
|
301
|
+
"",
|
|
302
|
+
"status: not logged in",
|
|
303
|
+
"",
|
|
304
|
+
"next:",
|
|
305
|
+
" - caphub auth login",
|
|
306
|
+
" - or for headless/cloud agents: caphub auth login --api-key csk_live_...",
|
|
307
|
+
"",
|
|
308
|
+
].join("\n"));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
const payload = await fetchJson(`${getApiUrl()}/v1/me`, { apiKey });
|
|
314
|
+
process.stdout.write([
|
|
315
|
+
"caphub auth",
|
|
316
|
+
"",
|
|
317
|
+
"status: logged in",
|
|
318
|
+
`email: ${payload.user.email}`,
|
|
319
|
+
`user_id: ${payload.user.id}`,
|
|
320
|
+
`credits_remaining: ${payload.total_usage.credits_remaining}`,
|
|
321
|
+
`total_credits_used: ${payload.total_usage.total_credits_used}`,
|
|
322
|
+
"",
|
|
323
|
+
"next:",
|
|
324
|
+
" - caphub capabilities",
|
|
325
|
+
" - caphub help search",
|
|
326
|
+
"",
|
|
327
|
+
].join("\n"));
|
|
328
|
+
return;
|
|
329
|
+
} catch (error) {
|
|
330
|
+
failWithHints("stored credentials are not valid", error, { capability: "search" });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
267
334
|
if (sub === "login") {
|
|
268
|
-
const
|
|
335
|
+
const explicitApiKey = parseFlag(args, "--api-key");
|
|
336
|
+
if (!explicitApiKey && !args.includes("--api-key")) {
|
|
337
|
+
const apiUrl = getApiUrl();
|
|
338
|
+
const started = await fetchJson(`${apiUrl}/v1/auth/cli/start`, { method: "POST", body: {} });
|
|
339
|
+
const opened = openUrl(started.approval_url);
|
|
340
|
+
process.stdout.write([
|
|
341
|
+
"caphub auth login",
|
|
342
|
+
"",
|
|
343
|
+
opened
|
|
344
|
+
? "Opened the browser approval page. If it did not open, use this URL:"
|
|
345
|
+
: "Open this URL in your browser and finish sign-in:",
|
|
346
|
+
started.approval_url,
|
|
347
|
+
"",
|
|
348
|
+
`code: ${started.code}`,
|
|
349
|
+
`expires_in_seconds: ${started.expires_in_seconds}`,
|
|
350
|
+
process.env.CAPHUB_NO_OPEN === "1" ? "browser_open: disabled by CAPHUB_NO_OPEN=1" : `browser_open: ${opened ? "attempted" : "failed"}`,
|
|
351
|
+
"",
|
|
352
|
+
"Waiting for website approval...",
|
|
353
|
+
"",
|
|
354
|
+
].join("\n"));
|
|
355
|
+
|
|
356
|
+
const deadline = Date.now() + Number(started.expires_in_seconds || 600) * 1000;
|
|
357
|
+
const intervalMs = Number(started.poll_interval_seconds || 2) * 1000;
|
|
358
|
+
while (Date.now() < deadline) {
|
|
359
|
+
const polled = await fetchJson(`${apiUrl}/v1/auth/cli/poll`, {
|
|
360
|
+
method: "POST",
|
|
361
|
+
body: {
|
|
362
|
+
session_id: started.session_id,
|
|
363
|
+
poll_token: started.poll_token,
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
if (polled.status === "approved" && polled.api_key) {
|
|
368
|
+
writeConfig({
|
|
369
|
+
...config,
|
|
370
|
+
api_key: polled.api_key,
|
|
371
|
+
api_url: apiUrl,
|
|
372
|
+
});
|
|
373
|
+
process.stdout.write(`${JSON.stringify({ ok: true, config_path: CONFIG_PATH, api_url: apiUrl }, null, 2)}\n`);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
await sleep(intervalMs);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
fail("Error: login approval timed out.\n\nnext:\n - rerun: caphub auth login\n - or open https://caphub.io/dashboard/");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const apiKey = explicitApiKey || getApiKey();
|
|
269
384
|
const apiUrl = parseFlag(args, "--api-url") || getApiUrl();
|
|
270
385
|
if (!apiKey) fail("Error: auth login requires --api-key or CAPHUB_API_KEY.\n\nnext:\n - caphub auth login --api-key csk_live_...");
|
|
271
386
|
writeConfig({
|