@supacortex/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/dist/index.js +345 -0
- package/dist/index.mjs +192 -0
- package/package.json +35 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __spreadValues = (a, b) => {
|
|
8
|
+
for (var prop in b || (b = {}))
|
|
9
|
+
if (__hasOwnProp.call(b, prop))
|
|
10
|
+
__defNormalProp(a, prop, b[prop]);
|
|
11
|
+
if (__getOwnPropSymbols)
|
|
12
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
13
|
+
if (__propIsEnum.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
}
|
|
16
|
+
return a;
|
|
17
|
+
};
|
|
18
|
+
var __objRest = (source, exclude) => {
|
|
19
|
+
var target = {};
|
|
20
|
+
for (var prop in source)
|
|
21
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
22
|
+
target[prop] = source[prop];
|
|
23
|
+
if (source != null && __getOwnPropSymbols)
|
|
24
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
25
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
26
|
+
target[prop] = source[prop];
|
|
27
|
+
}
|
|
28
|
+
return target;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
import { Command } from "commander";
|
|
33
|
+
|
|
34
|
+
// src/lib/config.ts
|
|
35
|
+
import path from "path";
|
|
36
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
37
|
+
import os from "os";
|
|
38
|
+
var homeDir = os.homedir();
|
|
39
|
+
var configDir = path.join(homeDir, ".supacortex");
|
|
40
|
+
var configPath = path.join(configDir, "config.json");
|
|
41
|
+
var getConfig = () => {
|
|
42
|
+
try {
|
|
43
|
+
const content = readFileSync(configPath, "utf-8");
|
|
44
|
+
const data = JSON.parse(content);
|
|
45
|
+
return data;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var setConfig = (updates) => {
|
|
51
|
+
mkdirSync(configDir, { recursive: true });
|
|
52
|
+
const existing = getConfig();
|
|
53
|
+
const data = __spreadValues(__spreadValues({}, existing), updates);
|
|
54
|
+
writeFileSync(configPath, JSON.stringify(data, null, 2));
|
|
55
|
+
};
|
|
56
|
+
var writeConfig = (config) => {
|
|
57
|
+
mkdirSync(configDir, { recursive: true });
|
|
58
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/lib/api.ts
|
|
62
|
+
var DEFAULT_ENDPOINT = "https://api.supacortex.ai";
|
|
63
|
+
var DEFAULT_APP_URL = "https://supacortex.ai";
|
|
64
|
+
var getEndpoint = () => {
|
|
65
|
+
const config = getConfig();
|
|
66
|
+
return config.endpoint || DEFAULT_ENDPOINT;
|
|
67
|
+
};
|
|
68
|
+
var getAppUrl = () => {
|
|
69
|
+
return process.env.SCX_APP_URL || getConfig().appUrl || DEFAULT_APP_URL;
|
|
70
|
+
};
|
|
71
|
+
var apiRequest = async (path2, method, body) => {
|
|
72
|
+
const config = getConfig();
|
|
73
|
+
if (!config.apiKey) {
|
|
74
|
+
console.error("Missing API key. Run: scx token <apikey>");
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const endpoint = getEndpoint();
|
|
78
|
+
const url = `${endpoint}/v1/${path2}`;
|
|
79
|
+
let res;
|
|
80
|
+
try {
|
|
81
|
+
res = await fetch(url, {
|
|
82
|
+
method,
|
|
83
|
+
headers: {
|
|
84
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
85
|
+
"Content-Type": "application/json"
|
|
86
|
+
},
|
|
87
|
+
body: body ? JSON.stringify(body) : void 0
|
|
88
|
+
});
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.error(`Failed to connect to ${endpoint}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
const errorBody = await res.json().catch(() => null);
|
|
95
|
+
const message = (errorBody == null ? void 0 : errorBody.error) || `${res.status} ${res.statusText}`;
|
|
96
|
+
console.error(`Error: ${message}`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const data = await res.json();
|
|
100
|
+
return data;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/commands/login.ts
|
|
104
|
+
import { exec } from "child_process";
|
|
105
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
106
|
+
var registerLoginCommand = (program2) => {
|
|
107
|
+
program2.command("login").description("Authenticate with Supacortex via browser").action(async () => {
|
|
108
|
+
const appUrl = getAppUrl();
|
|
109
|
+
let deviceCode;
|
|
110
|
+
let userCode;
|
|
111
|
+
let verifyUrl;
|
|
112
|
+
try {
|
|
113
|
+
const res = await fetch(`${appUrl}/api/cli/device`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: { "Content-Type": "application/json" }
|
|
116
|
+
});
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
console.error("Failed to start login flow");
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const data = await res.json();
|
|
122
|
+
deviceCode = data.deviceCode;
|
|
123
|
+
userCode = data.userCode;
|
|
124
|
+
verifyUrl = data.verifyUrl;
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error(`Failed to connect to ${appUrl}`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const verifyWithCode = `${verifyUrl}?code=${userCode}`;
|
|
130
|
+
console.log();
|
|
131
|
+
console.log(` Opening: ${verifyWithCode}`);
|
|
132
|
+
console.log();
|
|
133
|
+
console.log(" If the browser didn't open, copy and paste the URL above.");
|
|
134
|
+
console.log();
|
|
135
|
+
console.log("Waiting for authorization...");
|
|
136
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
137
|
+
exec(`${openCmd} "${verifyWithCode}"`);
|
|
138
|
+
const maxAttempts = 180;
|
|
139
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
140
|
+
await sleep(5e3);
|
|
141
|
+
try {
|
|
142
|
+
const res = await fetch(`${appUrl}/api/cli/token`, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: { "Content-Type": "application/json" },
|
|
145
|
+
body: JSON.stringify({ deviceCode })
|
|
146
|
+
});
|
|
147
|
+
if (!res.ok) continue;
|
|
148
|
+
const data = await res.json();
|
|
149
|
+
if (data.status === "approved" && data.apiKey) {
|
|
150
|
+
setConfig({ apiKey: data.apiKey });
|
|
151
|
+
console.log("Logged in!");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (data.status === "expired") {
|
|
155
|
+
console.error("Code expired. Run `scx login` again.");
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
} catch (e) {
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
console.error("Code expired. Run `scx login` again.");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/commands/logout.ts
|
|
167
|
+
var registerLogoutCommand = (program2) => {
|
|
168
|
+
program2.command("logout").description("Log out and remove saved API key").action(() => {
|
|
169
|
+
const _a = getConfig(), { apiKey } = _a, rest = __objRest(_a, ["apiKey"]);
|
|
170
|
+
if (!apiKey) {
|
|
171
|
+
console.log("Not logged in.");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
writeConfig(rest);
|
|
175
|
+
console.log("Logged out.");
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// src/commands/token.ts
|
|
180
|
+
var registerTokenCommand = (program2) => {
|
|
181
|
+
program2.command("token").argument("<apikey>", "API key to authenticate with").action((key) => {
|
|
182
|
+
setConfig({ apiKey: key.trim() });
|
|
183
|
+
console.log("API key saved.");
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// src/commands/endpoint.ts
|
|
188
|
+
var registerEndpointCommand = (program2) => {
|
|
189
|
+
program2.command("endpoint").argument("[url]", "API endpoint URL").description("Show, set, or reset the API endpoint").action((url) => {
|
|
190
|
+
if (!url) {
|
|
191
|
+
const config = getConfig();
|
|
192
|
+
console.log(config.endpoint || "Using default endpoint.");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (url === "reset") {
|
|
196
|
+
const _a = getConfig(), { endpoint } = _a, rest = __objRest(_a, ["endpoint"]);
|
|
197
|
+
writeConfig(rest);
|
|
198
|
+
console.log("Endpoint reset to default.");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
setConfig({ endpoint: url.trim() });
|
|
202
|
+
console.log(`Endpoint updated to ${url.trim()}`);
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/commands/whoami.ts
|
|
207
|
+
var registerWhoamiCommand = (program2) => {
|
|
208
|
+
program2.command("whoami").action(() => {
|
|
209
|
+
const { endpoint, apiKey } = getConfig();
|
|
210
|
+
console.log(`Endpoint: ${endpoint != null ? endpoint : "not set"}`);
|
|
211
|
+
console.log(`API Key: ${apiKey != null ? apiKey : "not set"}`);
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/commands/bookmarks.ts
|
|
216
|
+
var registerBookmarksCommand = (program2) => {
|
|
217
|
+
const bookmarks = program2.command("bookmarks");
|
|
218
|
+
bookmarks.command("list").option("-l, --limit <number>", "Max results", "20").option("-o, --offset <number>", "Skip results").option("-s, --search <string>", "Search bookmarks").option("-j, --json", "Output raw JSON").description("List all bookmarks").action(async (option) => {
|
|
219
|
+
var _a, _b;
|
|
220
|
+
const searchParams = new URLSearchParams();
|
|
221
|
+
for (const [key, value] of Object.entries(option)) {
|
|
222
|
+
if (value && key !== "json") searchParams.append(key, value);
|
|
223
|
+
}
|
|
224
|
+
const result = await apiRequest(
|
|
225
|
+
`bookmarks?${searchParams.toString()}`,
|
|
226
|
+
"GET"
|
|
227
|
+
);
|
|
228
|
+
if (option.json) {
|
|
229
|
+
console.log(JSON.stringify(result, null, 2));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const { data } = result;
|
|
233
|
+
for (const b of data) {
|
|
234
|
+
const label = b.type === "tweet" ? b.content : b.title;
|
|
235
|
+
const truncated = label && label.length > 80 ? label.slice(0, 80) + "..." : label;
|
|
236
|
+
console.log(` ${truncated || "Untitled"}`);
|
|
237
|
+
console.log(` ${b.url}`);
|
|
238
|
+
console.log(` ${b.author ? `@${b.author} \xB7 ` : ""}${b.type}`);
|
|
239
|
+
console.log();
|
|
240
|
+
}
|
|
241
|
+
console.log(`Showing ${data.length} of ${(_b = (_a = result.meta) == null ? void 0 : _a.total) != null ? _b : data.length} bookmarks`);
|
|
242
|
+
});
|
|
243
|
+
bookmarks.command("add").description("Add a new bookmark").argument("<url>", "URL to bookmark").option("-j, --json", "Output raw JSON").action(async (url, option) => {
|
|
244
|
+
const result = await apiRequest("bookmarks", "POST", { url });
|
|
245
|
+
if (option.json) {
|
|
246
|
+
console.log(JSON.stringify(result, null, 2));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
console.log(`Bookmark added: ${result.title || result.url}`);
|
|
250
|
+
});
|
|
251
|
+
bookmarks.command("delete").description("Delete a bookmark").argument("<id>", "Bookmark ID to delete").option("-j, --json", "Output raw JSON").action(async (id, option) => {
|
|
252
|
+
const result = await apiRequest(`bookmarks/${id}`, "DELETE");
|
|
253
|
+
if (option.json) {
|
|
254
|
+
console.log(JSON.stringify(result, null, 2));
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
console.log("Bookmark deleted.");
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/commands/groups.ts
|
|
262
|
+
var registerGroupsCommand = (program2) => {
|
|
263
|
+
const groups = program2.command("groups");
|
|
264
|
+
groups.command("list").description("List all groups").option("-j, --json", "Output raw JSON").action(async (option) => {
|
|
265
|
+
const result = await apiRequest("groups", "GET");
|
|
266
|
+
if (option.json) {
|
|
267
|
+
console.log(JSON.stringify(result, null, 2));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
for (const g of result) {
|
|
271
|
+
console.log(` ${g.name}`);
|
|
272
|
+
console.log(` ${g.id}`);
|
|
273
|
+
console.log();
|
|
274
|
+
}
|
|
275
|
+
console.log(`${result.length} groups`);
|
|
276
|
+
});
|
|
277
|
+
groups.command("create").description("Create a new group").argument("<name>", "Group name").option("-j, --json", "Output raw JSON").action(async (name, option) => {
|
|
278
|
+
const result = await apiRequest("groups", "POST", { name });
|
|
279
|
+
if (option.json) {
|
|
280
|
+
console.log(JSON.stringify(result, null, 2));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
console.log(`Group created: ${result.name}`);
|
|
284
|
+
});
|
|
285
|
+
groups.command("delete").description("Delete a group").argument("<id>", "Group ID to delete").option("-j, --json", "Output raw JSON").action(async (id, option) => {
|
|
286
|
+
const result = await apiRequest(`groups/${id}`, "DELETE");
|
|
287
|
+
if (option.json) {
|
|
288
|
+
console.log(JSON.stringify(result, null, 2));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
console.log("Group deleted.");
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// src/commands/sync.ts
|
|
296
|
+
var registerSyncCommand = (program2) => {
|
|
297
|
+
const sync = program2.command("sync").description("Sync bookmarks from connected platforms");
|
|
298
|
+
sync.command("twitter", { isDefault: true }).description("Sync bookmarks from X (Twitter)").option("--since <year>", "Only fetch bookmarks from this year onwards").action(async (opts) => {
|
|
299
|
+
let sinceYear;
|
|
300
|
+
if (opts.since) {
|
|
301
|
+
sinceYear = parseInt(opts.since, 10);
|
|
302
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
303
|
+
if (isNaN(sinceYear) || sinceYear < 2010 || sinceYear > currentYear) {
|
|
304
|
+
console.error(`Invalid year. Must be between 2010 and ${currentYear}.`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
console.log(sinceYear ? `Syncing X bookmarks from ${sinceYear} onwards...` : "Syncing X bookmarks...");
|
|
309
|
+
const result = await apiRequest("sync", "POST", sinceYear ? { sinceYear } : void 0);
|
|
310
|
+
if (result.synced === 0) {
|
|
311
|
+
console.log("Already up to date.");
|
|
312
|
+
} else {
|
|
313
|
+
console.log(`Synced ${result.synced} new bookmarks.`);
|
|
314
|
+
}
|
|
315
|
+
if (result.status === "interrupted") {
|
|
316
|
+
console.log(`Rate limited. Resets at ${new Date(result.rateLimitResetsAt).toLocaleString()}`);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
sync.command("status").description("Check latest sync status").action(async () => {
|
|
320
|
+
const result = await apiRequest("sync/status", "GET");
|
|
321
|
+
if (result.status === "none") {
|
|
322
|
+
console.log("No syncs yet.");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
console.log(`Status: ${result.status}`);
|
|
326
|
+
console.log(`Synced: ${result.tweetsSynced} / ${result.tweetsTotal}`);
|
|
327
|
+
console.log(`Mode: ${result.mode}`);
|
|
328
|
+
if (result.createdAt) {
|
|
329
|
+
console.log(`Last: ${new Date(result.createdAt).toLocaleString()}`);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// src/index.ts
|
|
335
|
+
var program = new Command();
|
|
336
|
+
program.name("scx").description("Supacortex CLI \u2014 your second brain from the terminal").version("0.1.0");
|
|
337
|
+
registerLoginCommand(program);
|
|
338
|
+
registerLogoutCommand(program);
|
|
339
|
+
registerTokenCommand(program);
|
|
340
|
+
registerEndpointCommand(program);
|
|
341
|
+
registerWhoamiCommand(program);
|
|
342
|
+
registerBookmarksCommand(program);
|
|
343
|
+
registerGroupsCommand(program);
|
|
344
|
+
registerSyncCommand(program);
|
|
345
|
+
program.parse();
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __spreadValues = (a, b) => {
|
|
8
|
+
for (var prop in b || (b = {}))
|
|
9
|
+
if (__hasOwnProp.call(b, prop))
|
|
10
|
+
__defNormalProp(a, prop, b[prop]);
|
|
11
|
+
if (__getOwnPropSymbols)
|
|
12
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
13
|
+
if (__propIsEnum.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
}
|
|
16
|
+
return a;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
import { Command } from "commander";
|
|
21
|
+
|
|
22
|
+
// src/lib/config.ts
|
|
23
|
+
import path from "path";
|
|
24
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
25
|
+
import os from "os";
|
|
26
|
+
var homeDir = os.homedir();
|
|
27
|
+
var configDir = path.join(homeDir, ".supacortex");
|
|
28
|
+
var configPath = path.join(configDir, "config.json");
|
|
29
|
+
var getConfig = () => {
|
|
30
|
+
try {
|
|
31
|
+
const content = readFileSync(configPath, "utf-8");
|
|
32
|
+
const data = JSON.parse(content);
|
|
33
|
+
return data;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var setConfig = (updates) => {
|
|
39
|
+
mkdirSync(configDir, { recursive: true });
|
|
40
|
+
const existing = getConfig();
|
|
41
|
+
const data = __spreadValues(__spreadValues({}, existing), updates);
|
|
42
|
+
writeFileSync(configPath, JSON.stringify(data, null, 2));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/commands/token.ts
|
|
46
|
+
var registerTokenCommand = (program2) => {
|
|
47
|
+
program2.command("token").argument("<apikey>", "API key to authenticate with").action((key) => {
|
|
48
|
+
setConfig({ apiKey: key.trim() });
|
|
49
|
+
console.log("API key saved.");
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/commands/endpoint.ts
|
|
54
|
+
var registerEndpointCommand = (program2) => {
|
|
55
|
+
program2.command("endpoint").argument("<url>", "API endpoint URL").action((url) => {
|
|
56
|
+
setConfig({ endpoint: url.trim() });
|
|
57
|
+
console.log(`Endpoint updated to ${url.trim()}`);
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/commands/whoami.ts
|
|
62
|
+
var registerWhoamiCommand = (program2) => {
|
|
63
|
+
program2.command("whoami").action(() => {
|
|
64
|
+
const { endpoint, apiKey } = getConfig();
|
|
65
|
+
console.log(`Endpoint: ${endpoint != null ? endpoint : "not set"}`);
|
|
66
|
+
console.log(`API Key: ${apiKey != null ? apiKey : "not set"}`);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/lib/api.ts
|
|
71
|
+
var DEFAULT_ENDPOINT = "https://api.supacortex.ai";
|
|
72
|
+
var apiRequest = async (path2, method, body) => {
|
|
73
|
+
const config = getConfig();
|
|
74
|
+
if (!config.apiKey) {
|
|
75
|
+
console.error("Missing API key. Run: scx token <apikey>");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const endpoint = config.endpoint || DEFAULT_ENDPOINT;
|
|
79
|
+
const url = `${endpoint}/v1/${path2}`;
|
|
80
|
+
let res;
|
|
81
|
+
try {
|
|
82
|
+
res = await fetch(url, {
|
|
83
|
+
method,
|
|
84
|
+
headers: {
|
|
85
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
86
|
+
"Content-Type": "application/json"
|
|
87
|
+
},
|
|
88
|
+
body: body ? JSON.stringify(body) : void 0
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(`Failed to connect to ${endpoint}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
if (!res.ok) {
|
|
95
|
+
const errorBody = await res.json().catch(() => null);
|
|
96
|
+
const message = (errorBody == null ? void 0 : errorBody.error) || `${res.status} ${res.statusText}`;
|
|
97
|
+
console.error(`Error: ${message}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const data = await res.json();
|
|
101
|
+
return data;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/commands/bookmarks.ts
|
|
105
|
+
var registerBookmarksCommand = (program2) => {
|
|
106
|
+
const bookmarks = program2.command("bookmarks");
|
|
107
|
+
bookmarks.command("list").option("--limit <number>", "Max results", "20").option("--offset <number>", "Skip results").option("--search <string>", "Search bookmarks").option("--json", "Output raw JSON").description("List all bookmarks").action(async (option) => {
|
|
108
|
+
var _a, _b;
|
|
109
|
+
const searchParams = new URLSearchParams();
|
|
110
|
+
for (const [key, value] of Object.entries(option)) {
|
|
111
|
+
if (value && key !== "json") searchParams.append(key, value);
|
|
112
|
+
}
|
|
113
|
+
const result = await apiRequest(
|
|
114
|
+
`bookmarks?${searchParams.toString()}`,
|
|
115
|
+
"GET"
|
|
116
|
+
);
|
|
117
|
+
if (option.json) {
|
|
118
|
+
console.log(JSON.stringify(result, null, 2));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const { data } = result;
|
|
122
|
+
for (const b of data) {
|
|
123
|
+
const label = b.type === "tweet" ? b.content : b.title;
|
|
124
|
+
const truncated = label && label.length > 80 ? label.slice(0, 80) + "..." : label;
|
|
125
|
+
console.log(` ${truncated || "Untitled"}`);
|
|
126
|
+
console.log(` ${b.url}`);
|
|
127
|
+
console.log(` ${b.author ? `@${b.author} \xB7 ` : ""}${b.type}`);
|
|
128
|
+
console.log();
|
|
129
|
+
}
|
|
130
|
+
console.log(`Showing ${data.length} of ${(_b = (_a = result.meta) == null ? void 0 : _a.total) != null ? _b : data.length} bookmarks`);
|
|
131
|
+
});
|
|
132
|
+
bookmarks.command("add").description("Add a new bookmark").argument("<url>", "URL to bookmark").option("--json", "Output raw JSON").action(async (url, option) => {
|
|
133
|
+
const result = await apiRequest("bookmarks", "POST", { url });
|
|
134
|
+
if (option.json) {
|
|
135
|
+
console.log(JSON.stringify(result, null, 2));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
console.log(`Bookmark added: ${result.title || result.url}`);
|
|
139
|
+
});
|
|
140
|
+
bookmarks.command("delete").description("Delete a bookmark").argument("<id>", "Bookmark ID to delete").option("--json", "Output raw JSON").action(async (id, option) => {
|
|
141
|
+
const result = await apiRequest(`bookmarks/${id}`, "DELETE");
|
|
142
|
+
if (option.json) {
|
|
143
|
+
console.log(JSON.stringify(result, null, 2));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
console.log("Bookmark deleted.");
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/commands/groups.ts
|
|
151
|
+
var registerGroupsCommand = (program2) => {
|
|
152
|
+
const groups = program2.command("groups");
|
|
153
|
+
groups.command("list").description("List all groups").option("--json", "Output raw JSON").action(async (option) => {
|
|
154
|
+
const result = await apiRequest("groups", "GET");
|
|
155
|
+
if (option.json) {
|
|
156
|
+
console.log(JSON.stringify(result, null, 2));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
for (const g of result) {
|
|
160
|
+
console.log(` ${g.name}`);
|
|
161
|
+
console.log(` ${g.id}`);
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
164
|
+
console.log(`${result.length} groups`);
|
|
165
|
+
});
|
|
166
|
+
groups.command("create").description("Create a new group").argument("<name>", "Group name").option("--json", "Output raw JSON").action(async (name, option) => {
|
|
167
|
+
const result = await apiRequest("groups", "POST", { name });
|
|
168
|
+
if (option.json) {
|
|
169
|
+
console.log(JSON.stringify(result, null, 2));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
console.log(`Group created: ${result.name}`);
|
|
173
|
+
});
|
|
174
|
+
groups.command("delete").description("Delete a group").argument("<id>", "Group ID to delete").option("--json", "Output raw JSON").action(async (id, option) => {
|
|
175
|
+
const result = await apiRequest(`groups/${id}`, "DELETE");
|
|
176
|
+
if (option.json) {
|
|
177
|
+
console.log(JSON.stringify(result, null, 2));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
console.log("Group deleted.");
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/index.ts
|
|
185
|
+
var program = new Command();
|
|
186
|
+
program.name("scx").description("Supacortex CLI \u2014 your second brain from the terminal").version("0.1.0");
|
|
187
|
+
registerTokenCommand(program);
|
|
188
|
+
registerEndpointCommand(program);
|
|
189
|
+
registerWhoamiCommand(program);
|
|
190
|
+
registerBookmarksCommand(program);
|
|
191
|
+
registerGroupsCommand(program);
|
|
192
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@supacortex/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Supacortex CLI — your second brain from the terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"scx": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "tsx src/index.ts",
|
|
14
|
+
"build": "tsup src/index.ts --format esm"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"supacortex",
|
|
18
|
+
"bookmarks",
|
|
19
|
+
"cli",
|
|
20
|
+
"second-brain"
|
|
21
|
+
],
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/monorepo-labs/supercortex"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"commander": "^13.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsx": "^4.19.0",
|
|
32
|
+
"tsup": "^8.0.0",
|
|
33
|
+
"typescript": "^5"
|
|
34
|
+
}
|
|
35
|
+
}
|