@caphub/cli 0.1.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 +41 -0
- package/bin/caphub.js +261 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# `@caphub/cli`
|
|
2
|
+
|
|
3
|
+
Root CLI for Caphub capabilities.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @caphub/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run without install:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @caphub/cli help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Auth
|
|
18
|
+
|
|
19
|
+
Store your API key once:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
caphub auth login --api-key csk_live_...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Discovery
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
caphub help
|
|
29
|
+
caphub capabilities
|
|
30
|
+
caphub help search
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Run capabilities
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
caphub search '{"queries":["site:github.com awesome ai agents"]}'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
caphub search-ideas '{"queries":["best robot vacuum"]}'
|
|
41
|
+
```
|
package/bin/caphub.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const pkg = JSON.parse(readFileSync(resolve(__dirname, "..", "package.json"), "utf8"));
|
|
10
|
+
const DEFAULT_API_URL = "https://api.caphub.io";
|
|
11
|
+
const CONFIG_DIR = resolve(os.homedir(), ".config", "caphub");
|
|
12
|
+
const CONFIG_PATH = resolve(CONFIG_DIR, "config.json");
|
|
13
|
+
|
|
14
|
+
const ROOT_HELP = `caphub
|
|
15
|
+
|
|
16
|
+
purpose: root CLI for Caphub agent capabilities
|
|
17
|
+
auth: CAPHUB_API_KEY env or ${CONFIG_PATH}
|
|
18
|
+
api: ${DEFAULT_API_URL}
|
|
19
|
+
|
|
20
|
+
commands:
|
|
21
|
+
help explain the platform and command layout
|
|
22
|
+
help <capability> show capability-specific help from the API
|
|
23
|
+
capabilities list live capabilities with short descriptions
|
|
24
|
+
auth login save api key locally for future runs
|
|
25
|
+
auth whoami verify the current api key against the API
|
|
26
|
+
auth logout remove stored api key from local config
|
|
27
|
+
<capability> <json> run a capability directly, e.g. search or search-ideas
|
|
28
|
+
|
|
29
|
+
examples:
|
|
30
|
+
caphub capabilities
|
|
31
|
+
caphub auth login --api-key csk_live_...
|
|
32
|
+
caphub help search
|
|
33
|
+
caphub search '{"queries":["site:github.com awesome ai agents"]}'
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
function readConfig() {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
39
|
+
} catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function writeConfig(config) {
|
|
45
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
46
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getApiUrl() {
|
|
50
|
+
const config = readConfig();
|
|
51
|
+
return (
|
|
52
|
+
process.env.CAPHUB_API_URL ||
|
|
53
|
+
config.api_url ||
|
|
54
|
+
DEFAULT_API_URL
|
|
55
|
+
).replace(/\/+$/, "");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getApiKey() {
|
|
59
|
+
const config = readConfig();
|
|
60
|
+
return (
|
|
61
|
+
process.env.CAPHUB_API_KEY ||
|
|
62
|
+
config.api_key ||
|
|
63
|
+
""
|
|
64
|
+
).trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function fail(message) {
|
|
68
|
+
process.stderr.write(`${message}\n`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readStdin() {
|
|
73
|
+
return new Promise((resolveInput) => {
|
|
74
|
+
let data = "";
|
|
75
|
+
process.stdin.setEncoding("utf8");
|
|
76
|
+
process.stdin.on("data", (chunk) => (data += chunk));
|
|
77
|
+
process.stdin.on("end", () => resolveInput(data));
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function fetchJson(url, { method = "GET", body, apiKey = "" } = {}) {
|
|
82
|
+
const headers = { "Content-Type": "application/json" };
|
|
83
|
+
if (apiKey) headers["X-API-Key"] = apiKey;
|
|
84
|
+
|
|
85
|
+
const resp = await fetch(url, {
|
|
86
|
+
method,
|
|
87
|
+
headers,
|
|
88
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const text = await resp.text();
|
|
92
|
+
let data;
|
|
93
|
+
try {
|
|
94
|
+
data = text ? JSON.parse(text) : {};
|
|
95
|
+
} catch {
|
|
96
|
+
throw new Error(`non-JSON response from ${url} (HTTP ${resp.status})`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!resp.ok) {
|
|
100
|
+
throw new Error(data?.error || `HTTP ${resp.status}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function parseFlag(args, name) {
|
|
107
|
+
const idx = args.indexOf(name);
|
|
108
|
+
if (idx === -1) return "";
|
|
109
|
+
return args[idx + 1] || "";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function printCapabilities(payload) {
|
|
113
|
+
const lines = ["caphub capabilities", ""];
|
|
114
|
+
for (const capability of payload.capabilities || []) {
|
|
115
|
+
lines.push(`/${capability.capability} - ${capability.purpose}`);
|
|
116
|
+
if (capability.credits) lines.push(` credits: ${capability.credits}`);
|
|
117
|
+
if (capability.limits?.max_queries_per_request) lines.push(` max queries: ${capability.limits.max_queries_per_request}`);
|
|
118
|
+
if (capability.endpoint) lines.push(` endpoint: ${capability.endpoint}`);
|
|
119
|
+
lines.push("");
|
|
120
|
+
}
|
|
121
|
+
process.stdout.write(lines.join("\n"));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function printCapabilityHelp(payload) {
|
|
125
|
+
const lines = [
|
|
126
|
+
payload.capability,
|
|
127
|
+
"",
|
|
128
|
+
`purpose: ${payload.purpose}`,
|
|
129
|
+
`endpoint: ${payload.endpoint}`,
|
|
130
|
+
`method: ${payload.method}`,
|
|
131
|
+
payload.credits ? `credits: ${payload.credits}` : null,
|
|
132
|
+
payload.limits?.max_queries_per_request ? `limits: max ${payload.limits.max_queries_per_request} queries per request` : null,
|
|
133
|
+
`auth: ${payload.auth?.env || "CAPHUB_API_KEY"} or ${CONFIG_PATH}`,
|
|
134
|
+
"",
|
|
135
|
+
"input:",
|
|
136
|
+
JSON.stringify(payload.input_contract, null, 2),
|
|
137
|
+
"",
|
|
138
|
+
"output:",
|
|
139
|
+
JSON.stringify(payload.output_contract, null, 2),
|
|
140
|
+
].filter(Boolean);
|
|
141
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function commandHelp(args) {
|
|
145
|
+
const apiUrl = getApiUrl();
|
|
146
|
+
const capability = args[0];
|
|
147
|
+
if (!capability) {
|
|
148
|
+
process.stdout.write(ROOT_HELP);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const payload = await fetchJson(`${apiUrl}/v1/${capability}/help`);
|
|
153
|
+
printCapabilityHelp(payload);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function commandCapabilities(args) {
|
|
157
|
+
const apiUrl = getApiUrl();
|
|
158
|
+
const payload = await fetchJson(`${apiUrl}/v1/help`);
|
|
159
|
+
if (args.includes("--json")) {
|
|
160
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
printCapabilities(payload);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function commandAuth(args) {
|
|
167
|
+
const sub = args[0];
|
|
168
|
+
const config = readConfig();
|
|
169
|
+
|
|
170
|
+
if (sub === "login") {
|
|
171
|
+
const apiKey = parseFlag(args, "--api-key") || getApiKey();
|
|
172
|
+
const apiUrl = parseFlag(args, "--api-url") || getApiUrl();
|
|
173
|
+
if (!apiKey) fail("Error: auth login requires --api-key or CAPHUB_API_KEY.");
|
|
174
|
+
writeConfig({
|
|
175
|
+
...config,
|
|
176
|
+
api_key: apiKey,
|
|
177
|
+
api_url: apiUrl,
|
|
178
|
+
});
|
|
179
|
+
process.stdout.write(`${JSON.stringify({ ok: true, config_path: CONFIG_PATH, api_url: apiUrl }, null, 2)}\n`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (sub === "whoami") {
|
|
184
|
+
const apiKey = getApiKey();
|
|
185
|
+
if (!apiKey) fail(`Error: no api key configured. Use 'caphub auth login --api-key ...' or set ${CONFIG_PATH}.`);
|
|
186
|
+
const payload = await fetchJson(`${getApiUrl()}/v1/me`, { apiKey });
|
|
187
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (sub === "logout") {
|
|
192
|
+
const next = { ...config };
|
|
193
|
+
delete next.api_key;
|
|
194
|
+
writeConfig(next);
|
|
195
|
+
process.stdout.write(`${JSON.stringify({ ok: true, config_path: CONFIG_PATH }, null, 2)}\n`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fail("Error: auth commands are: login, whoami, logout.");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function commandCapability(capability, args) {
|
|
203
|
+
const apiKey = getApiKey();
|
|
204
|
+
if (!apiKey) fail(`Error: CAPHUB_API_KEY is required, or set api_key in ${CONFIG_PATH}.`);
|
|
205
|
+
|
|
206
|
+
if (args[0] === "--help" || args[0] === "help") {
|
|
207
|
+
await commandHelp([capability]);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const arg = args[0];
|
|
212
|
+
const rawInput = arg ?? (process.stdin.isTTY ? "" : await readStdin());
|
|
213
|
+
if (!rawInput.trim()) {
|
|
214
|
+
fail(`Error: input JSON is required. Run 'caphub help ${capability}' for the contract.`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let body;
|
|
218
|
+
try {
|
|
219
|
+
body = JSON.parse(rawInput);
|
|
220
|
+
} catch {
|
|
221
|
+
fail("Error: input must be valid JSON.");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!("function" in body)) body.function = capability;
|
|
225
|
+
|
|
226
|
+
const payload = await fetchJson(`${getApiUrl()}/v1/${capability}`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
apiKey,
|
|
229
|
+
body,
|
|
230
|
+
});
|
|
231
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function main() {
|
|
235
|
+
const args = process.argv.slice(2);
|
|
236
|
+
const cmd = args[0];
|
|
237
|
+
|
|
238
|
+
if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
239
|
+
await commandHelp(args.slice(1));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
244
|
+
process.stdout.write(`${pkg.version}\n`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (cmd === "capabilities") {
|
|
249
|
+
await commandCapabilities(args.slice(1));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (cmd === "auth") {
|
|
254
|
+
await commandAuth(args.slice(1));
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await commandCapability(cmd, args.slice(1));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
await main().catch((error) => fail(`Error: ${error.message}`));
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caphub/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Root CLI for Caphub agent capabilities",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"caphub": "bin/caphub.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"agents",
|
|
15
|
+
"cli",
|
|
16
|
+
"caphub",
|
|
17
|
+
"search",
|
|
18
|
+
"automation"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"license": "UNLICENSED",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
}
|
|
27
|
+
}
|