@hawkeye-xb.com/imprint-cli 0.2.3 → 0.2.4
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/cli.js +197 -52
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,27 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
2
|
+
|
|
3
|
+
// src/login.ts
|
|
4
|
+
import http from "http";
|
|
5
|
+
import os2 from "os";
|
|
6
|
+
import crypto from "crypto";
|
|
11
7
|
|
|
12
8
|
// src/config.ts
|
|
13
|
-
var config_exports = {};
|
|
14
|
-
__export(config_exports, {
|
|
15
|
-
DASHBOARD_URL: () => DASHBOARD_URL,
|
|
16
|
-
credentialsPath: () => credentialsPath,
|
|
17
|
-
readCredentials: () => readCredentials,
|
|
18
|
-
removeCredentials: () => removeCredentials,
|
|
19
|
-
resolveApiBase: () => resolveApiBase,
|
|
20
|
-
writeCredentials: () => writeCredentials
|
|
21
|
-
});
|
|
22
9
|
import os from "os";
|
|
23
10
|
import path from "path";
|
|
24
11
|
import fs from "fs/promises";
|
|
12
|
+
var DASHBOARD_URL = process.env.IMPRINT_DASHBOARD_URL?.replace(/\/+$/, "") ?? "https://imprint.hawkeye-xb.com";
|
|
13
|
+
var HARDCODED_API_BASE_FALLBACK = "https://imprint-api.hawkeye-xb.com";
|
|
14
|
+
var _resolvedApiBase = null;
|
|
25
15
|
async function resolveApiBase() {
|
|
26
16
|
if (_resolvedApiBase) return _resolvedApiBase;
|
|
27
17
|
const envOverride = process.env.IMPRINT_API_BASE?.replace(/\/+$/, "");
|
|
@@ -46,6 +36,8 @@ async function resolveApiBase() {
|
|
|
46
36
|
_resolvedApiBase = HARDCODED_API_BASE_FALLBACK;
|
|
47
37
|
return _resolvedApiBase;
|
|
48
38
|
}
|
|
39
|
+
var CRED_DIR = path.join(os.homedir(), ".imprint");
|
|
40
|
+
var CRED_FILE = path.join(CRED_DIR, "credentials.json");
|
|
49
41
|
function credentialsPath() {
|
|
50
42
|
return CRED_FILE;
|
|
51
43
|
}
|
|
@@ -73,23 +65,6 @@ async function writeCredentials(token, apiBase) {
|
|
|
73
65
|
async function removeCredentials() {
|
|
74
66
|
await fs.rm(CRED_FILE, { force: true });
|
|
75
67
|
}
|
|
76
|
-
var DASHBOARD_URL, HARDCODED_API_BASE_FALLBACK, _resolvedApiBase, CRED_DIR, CRED_FILE;
|
|
77
|
-
var init_config = __esm({
|
|
78
|
-
"src/config.ts"() {
|
|
79
|
-
"use strict";
|
|
80
|
-
DASHBOARD_URL = process.env.IMPRINT_DASHBOARD_URL?.replace(/\/+$/, "") ?? "https://imprint.hawkeye-xb.com";
|
|
81
|
-
HARDCODED_API_BASE_FALLBACK = "https://imprint-api.hawkeye-xb.com";
|
|
82
|
-
_resolvedApiBase = null;
|
|
83
|
-
CRED_DIR = path.join(os.homedir(), ".imprint");
|
|
84
|
-
CRED_FILE = path.join(CRED_DIR, "credentials.json");
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// src/login.ts
|
|
89
|
-
init_config();
|
|
90
|
-
import http from "http";
|
|
91
|
-
import os2 from "os";
|
|
92
|
-
import crypto from "crypto";
|
|
93
68
|
|
|
94
69
|
// src/utils.ts
|
|
95
70
|
import { exec } from "child_process";
|
|
@@ -243,13 +218,25 @@ function waitForCallback(port, expectedState) {
|
|
|
243
218
|
}
|
|
244
219
|
|
|
245
220
|
// src/install.ts
|
|
246
|
-
init_config();
|
|
247
221
|
import os3 from "os";
|
|
248
222
|
import path2 from "path";
|
|
249
223
|
import fs2 from "fs/promises";
|
|
250
|
-
|
|
224
|
+
|
|
225
|
+
// src/codex-markers.ts
|
|
226
|
+
var CODEX_MARK_BEGIN = "# === imprint:begin (managed \u2014 do not edit between markers) ===";
|
|
227
|
+
var CODEX_MARK_END = "# === imprint:end ===";
|
|
228
|
+
|
|
229
|
+
// src/install.ts
|
|
230
|
+
var SUPPORTED_TOOLS = ["claude-code", "cursor", "codex"];
|
|
251
231
|
async function install(args) {
|
|
252
232
|
const tool = parseTool(args);
|
|
233
|
+
const creds = await ensureLoggedIn();
|
|
234
|
+
const apiBase = await resolveApiBase();
|
|
235
|
+
await installToolByName(tool, creds.token, apiBase);
|
|
236
|
+
await bootstrapUsageDoc(creds.token, apiBase);
|
|
237
|
+
await printIdentity(creds.token, apiBase);
|
|
238
|
+
}
|
|
239
|
+
async function ensureLoggedIn() {
|
|
253
240
|
let creds = await readCredentials();
|
|
254
241
|
if (!creds) {
|
|
255
242
|
console.log("No credentials found \u2014 running login first.\n");
|
|
@@ -257,21 +244,21 @@ async function install(args) {
|
|
|
257
244
|
creds = await readCredentials();
|
|
258
245
|
if (!creds) throw new Error("login completed but credentials still missing");
|
|
259
246
|
}
|
|
260
|
-
|
|
247
|
+
return creds;
|
|
248
|
+
}
|
|
249
|
+
async function installToolByName(tool, token, apiBase) {
|
|
261
250
|
switch (tool) {
|
|
262
251
|
case "claude-code":
|
|
263
|
-
await installClaudeCode(
|
|
252
|
+
await installClaudeCode(token, apiBase);
|
|
264
253
|
await migrateLegacyClaudeSettings();
|
|
265
254
|
break;
|
|
266
255
|
case "cursor":
|
|
267
|
-
await installCursor(
|
|
256
|
+
await installCursor(token, apiBase);
|
|
268
257
|
break;
|
|
269
258
|
case "codex":
|
|
270
|
-
await installCodex(
|
|
259
|
+
await installCodex(token, apiBase);
|
|
271
260
|
break;
|
|
272
261
|
}
|
|
273
|
-
await bootstrapUsageDoc(creds.token, apiBase);
|
|
274
|
-
await printIdentity(creds.token, apiBase);
|
|
275
262
|
}
|
|
276
263
|
async function printIdentity(token, apiBase) {
|
|
277
264
|
try {
|
|
@@ -382,9 +369,9 @@ function parseTool(args) {
|
|
|
382
369
|
if (idx >= 0) {
|
|
383
370
|
const v = args[idx + 1];
|
|
384
371
|
if (!v) throw new Error("--tool requires a value");
|
|
385
|
-
if (!
|
|
372
|
+
if (!SUPPORTED_TOOLS.includes(v)) {
|
|
386
373
|
throw new Error(
|
|
387
|
-
`unsupported tool: ${v}. Supported: ${
|
|
374
|
+
`unsupported tool: ${v}. Supported: ${SUPPORTED_TOOLS.join(", ")}`
|
|
388
375
|
);
|
|
389
376
|
}
|
|
390
377
|
return v;
|
|
@@ -428,8 +415,6 @@ async function installCursor(token, apiBase) {
|
|
|
428
415
|
console.log(`\u2713 Cursor: wrote mcpServers.imprint to ${settingsPath}`);
|
|
429
416
|
console.log(` Reload Cursor (or its MCP server list in Settings) to pick it up.`);
|
|
430
417
|
}
|
|
431
|
-
var CODEX_MARK_BEGIN = "# === imprint:begin (managed \u2014 do not edit between markers) ===";
|
|
432
|
-
var CODEX_MARK_END = "# === imprint:end ===";
|
|
433
418
|
async function installCodex(token, apiBase) {
|
|
434
419
|
const configPath = path2.join(os3.homedir(), ".codex", "config.toml");
|
|
435
420
|
await fs2.mkdir(path2.dirname(configPath), { recursive: true });
|
|
@@ -469,6 +454,158 @@ async function installCodex(token, apiBase) {
|
|
|
469
454
|
);
|
|
470
455
|
}
|
|
471
456
|
|
|
457
|
+
// src/logout.ts
|
|
458
|
+
import os4 from "os";
|
|
459
|
+
import path3 from "path";
|
|
460
|
+
import fs3 from "fs/promises";
|
|
461
|
+
async function logout() {
|
|
462
|
+
await removeCredentials();
|
|
463
|
+
console.log(`\u2713 Removed credentials file`);
|
|
464
|
+
await cleanupJson(
|
|
465
|
+
path3.join(os4.homedir(), ".claude.json"),
|
|
466
|
+
"Claude Code (~/.claude.json)"
|
|
467
|
+
);
|
|
468
|
+
await cleanupJson(
|
|
469
|
+
path3.join(os4.homedir(), ".claude", "settings.json"),
|
|
470
|
+
"Claude legacy (~/.claude/settings.json)"
|
|
471
|
+
);
|
|
472
|
+
await cleanupJson(
|
|
473
|
+
path3.join(os4.homedir(), ".cursor", "mcp.json"),
|
|
474
|
+
"Cursor (~/.cursor/mcp.json)"
|
|
475
|
+
);
|
|
476
|
+
await cleanupCodex(path3.join(os4.homedir(), ".codex", "config.toml"));
|
|
477
|
+
}
|
|
478
|
+
async function cleanupJson(filePath, label) {
|
|
479
|
+
let raw;
|
|
480
|
+
try {
|
|
481
|
+
raw = await fs3.readFile(filePath, "utf8");
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (err.code === "ENOENT") return;
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (raw.trim().length === 0) return;
|
|
487
|
+
let parsed;
|
|
488
|
+
try {
|
|
489
|
+
parsed = JSON.parse(raw);
|
|
490
|
+
} catch {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return;
|
|
494
|
+
const obj = parsed;
|
|
495
|
+
if (!obj.mcpServers || typeof obj.mcpServers !== "object") return;
|
|
496
|
+
if (!("imprint" in obj.mcpServers)) return;
|
|
497
|
+
delete obj.mcpServers.imprint;
|
|
498
|
+
if (Object.keys(obj.mcpServers).length === 0) delete obj.mcpServers;
|
|
499
|
+
await fs3.writeFile(filePath, JSON.stringify(obj, null, 2) + "\n");
|
|
500
|
+
console.log(`\u2713 Removed imprint from ${label}`);
|
|
501
|
+
}
|
|
502
|
+
async function cleanupCodex(filePath) {
|
|
503
|
+
let raw;
|
|
504
|
+
try {
|
|
505
|
+
raw = await fs3.readFile(filePath, "utf8");
|
|
506
|
+
} catch {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const startIdx = raw.indexOf(CODEX_MARK_BEGIN);
|
|
510
|
+
const endIdx = raw.indexOf(CODEX_MARK_END);
|
|
511
|
+
if (startIdx < 0 || endIdx <= startIdx) return;
|
|
512
|
+
const before = raw.slice(0, startIdx).replace(/\s+$/, "");
|
|
513
|
+
const after = raw.slice(endIdx + CODEX_MARK_END.length).replace(/^\s+/, "");
|
|
514
|
+
const next = [before, after].filter((s) => s.length > 0).join("\n\n");
|
|
515
|
+
const final = next.length > 0 ? next + "\n" : "";
|
|
516
|
+
await fs3.writeFile(filePath, final);
|
|
517
|
+
console.log(`\u2713 Removed imprint from Codex (~/.codex/config.toml)`);
|
|
518
|
+
}
|
|
519
|
+
async function detectInstalledTools() {
|
|
520
|
+
const tools = [];
|
|
521
|
+
if (await jsonHasImprint(path3.join(os4.homedir(), ".claude.json"))) {
|
|
522
|
+
tools.push("claude-code");
|
|
523
|
+
}
|
|
524
|
+
if (await jsonHasImprint(path3.join(os4.homedir(), ".cursor", "mcp.json"))) {
|
|
525
|
+
tools.push("cursor");
|
|
526
|
+
}
|
|
527
|
+
if (await fileContains(
|
|
528
|
+
path3.join(os4.homedir(), ".codex", "config.toml"),
|
|
529
|
+
CODEX_MARK_BEGIN
|
|
530
|
+
)) {
|
|
531
|
+
tools.push("codex");
|
|
532
|
+
}
|
|
533
|
+
return tools;
|
|
534
|
+
}
|
|
535
|
+
async function jsonHasImprint(filePath) {
|
|
536
|
+
try {
|
|
537
|
+
const raw = await fs3.readFile(filePath, "utf8");
|
|
538
|
+
const parsed = JSON.parse(raw);
|
|
539
|
+
return typeof parsed === "object" && parsed !== null && typeof parsed.mcpServers === "object" && parsed.mcpServers !== null && "imprint" in parsed.mcpServers;
|
|
540
|
+
} catch {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async function fileContains(filePath, needle) {
|
|
545
|
+
try {
|
|
546
|
+
const raw = await fs3.readFile(filePath, "utf8");
|
|
547
|
+
return raw.includes(needle);
|
|
548
|
+
} catch {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/switch.ts
|
|
554
|
+
async function switchAccount(_args) {
|
|
555
|
+
const detected = await detectInstalledTools();
|
|
556
|
+
if (detected.length === 0) {
|
|
557
|
+
console.log(
|
|
558
|
+
"No installed tools detected. Run 'imprint install --tool <claude-code|cursor|codex>' instead."
|
|
559
|
+
);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
console.log(`Will re-install for: ${detected.join(", ")}
|
|
563
|
+
`);
|
|
564
|
+
await logout();
|
|
565
|
+
console.log("\nLog in with the account you want to switch to.\n");
|
|
566
|
+
const creds = await ensureLoggedIn();
|
|
567
|
+
const apiBase = await resolveApiBase();
|
|
568
|
+
for (const tool of detected) {
|
|
569
|
+
await installToolByName(tool, creds.token, apiBase);
|
|
570
|
+
}
|
|
571
|
+
await bootstrapUsageDoc(creds.token, apiBase);
|
|
572
|
+
await printIdentity(creds.token, apiBase);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// src/whoami.ts
|
|
576
|
+
async function whoami(args) {
|
|
577
|
+
if (args.includes("--path")) {
|
|
578
|
+
console.log(credentialsPath());
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const creds = await readCredentials();
|
|
582
|
+
if (!creds) {
|
|
583
|
+
console.log("(not logged in \u2014 run 'imprint login')");
|
|
584
|
+
console.log(` Would store at: ${credentialsPath()}`);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
const apiBase = await resolveApiBase();
|
|
588
|
+
try {
|
|
589
|
+
const res = await fetch(`${apiBase}/api/v1/whoami`, {
|
|
590
|
+
headers: { Authorization: `Bearer ${creds.token}` }
|
|
591
|
+
});
|
|
592
|
+
if (!res.ok) {
|
|
593
|
+
console.log(`(token rejected by ${apiBase}: HTTP ${res.status})`);
|
|
594
|
+
console.log(` Stored at: ${credentialsPath()}`);
|
|
595
|
+
console.log(` Try 'imprint login' to re-authenticate.`);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const body = await res.json();
|
|
599
|
+
const label = body.email || body.userId || "(unknown)";
|
|
600
|
+
console.log(`Logged in as: ${label}`);
|
|
601
|
+
console.log(` Token stored at: ${credentialsPath()}`);
|
|
602
|
+
console.log(` API: ${apiBase}`);
|
|
603
|
+
} catch (err) {
|
|
604
|
+
console.log(`(could not reach ${apiBase}: ${err.message})`);
|
|
605
|
+
console.log(` Stored at: ${credentialsPath()}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
472
609
|
// src/cli.ts
|
|
473
610
|
var HELP = `imprint \u2014 memory layer for AI coding tools
|
|
474
611
|
|
|
@@ -477,8 +614,14 @@ Usage:
|
|
|
477
614
|
imprint login Authenticate via browser; saves token to ~/.imprint/credentials.json.
|
|
478
615
|
imprint install [--tool ID] Log in (if needed) and write MCP config for the target tool.
|
|
479
616
|
Tools: claude-code (default), cursor, codex.
|
|
480
|
-
imprint
|
|
481
|
-
|
|
617
|
+
imprint switch Switch the locally-bound account. Detects which tools you
|
|
618
|
+
installed, clears all imprint configs and credentials, then
|
|
619
|
+
walks you through fresh OAuth and re-installs the same tools
|
|
620
|
+
under the new account.
|
|
621
|
+
imprint logout Remove credentials AND clean imprint entries out of
|
|
622
|
+
Claude Code / Cursor / Codex config files.
|
|
623
|
+
imprint whoami Show which account the saved token belongs to.
|
|
624
|
+
Use --path to print the credentials file location instead.
|
|
482
625
|
|
|
483
626
|
Environment overrides (for development):
|
|
484
627
|
IMPRINT_DASHBOARD_URL Dashboard origin (default: https://imprint.hawkeye-xb.com).
|
|
@@ -494,12 +637,14 @@ async function main() {
|
|
|
494
637
|
case "install":
|
|
495
638
|
await install(rest);
|
|
496
639
|
break;
|
|
640
|
+
case "switch":
|
|
641
|
+
await switchAccount(rest);
|
|
642
|
+
break;
|
|
497
643
|
case "logout":
|
|
498
|
-
|
|
499
|
-
console.log("\u2713 logged out");
|
|
644
|
+
await logout();
|
|
500
645
|
break;
|
|
501
646
|
case "whoami":
|
|
502
|
-
|
|
647
|
+
await whoami(rest);
|
|
503
648
|
break;
|
|
504
649
|
case "-h":
|
|
505
650
|
case "--help":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hawkeye-xb.com/imprint-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Imprint CLI — long-term memory for AI coding tools (Claude Code, Cursor, Codex). Installs the imprint MCP server into your tool's settings file via a browser OAuth flow.",
|
|
6
6
|
"license": "Elastic-2.0",
|