@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.
Files changed (2) hide show
  1. package/dist/cli.js +197 -52
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,27 +1,17 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
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
- var SUPPORTED = ["claude-code", "cursor", "codex"];
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
- const apiBase = await resolveApiBase();
247
+ return creds;
248
+ }
249
+ async function installToolByName(tool, token, apiBase) {
261
250
  switch (tool) {
262
251
  case "claude-code":
263
- await installClaudeCode(creds.token, apiBase);
252
+ await installClaudeCode(token, apiBase);
264
253
  await migrateLegacyClaudeSettings();
265
254
  break;
266
255
  case "cursor":
267
- await installCursor(creds.token, apiBase);
256
+ await installCursor(token, apiBase);
268
257
  break;
269
258
  case "codex":
270
- await installCodex(creds.token, apiBase);
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 (!SUPPORTED.includes(v)) {
372
+ if (!SUPPORTED_TOOLS.includes(v)) {
386
373
  throw new Error(
387
- `unsupported tool: ${v}. Supported: ${SUPPORTED.join(", ")}`
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 logout Forget the saved token.
481
- imprint whoami Print where the credentials are stored.
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
- (await Promise.resolve().then(() => (init_config(), config_exports))).removeCredentials();
499
- console.log("\u2713 logged out");
644
+ await logout();
500
645
  break;
501
646
  case "whoami":
502
- console.log((await Promise.resolve().then(() => (init_config(), config_exports))).credentialsPath());
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",
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",