@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.
Files changed (3) hide show
  1. package/README.md +8 -2
  2. package/bin/caphub.js +121 -6
  3. 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
- Store your API key once:
19
+ Open the website approval flow:
20
20
 
21
21
  ```bash
22
- caphub auth login --api-key csk_live_...
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 save api key locally for future runs
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 --api-key csk_live_...
36
+ 3. caphub auth login
35
37
  4. caphub <capability> '<json>'
36
38
 
37
39
  recovery:
38
- no api key caphub auth login --api-key csk_live_...
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 --api-key csk_live_...");
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 --api-key csk_live_...",
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 apiKey = parseFlag(args, "--api-key") || getApiKey();
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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caphub/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Root CLI for Caphub agent capabilities",
5
5
  "type": "module",
6
6
  "bin": {