@cortex-context/cli 0.0.12 → 0.0.14

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 CHANGED
@@ -5617,7 +5617,7 @@ var require_dist = __commonJS({
5617
5617
  });
5618
5618
  };
5619
5619
  }
5620
- var prompts6 = require_prompts();
5620
+ var prompts7 = require_prompts();
5621
5621
  var passOn = ["suggest", "format", "onState", "validate", "onRender", "type"];
5622
5622
  var noop = () => {
5623
5623
  };
@@ -5668,7 +5668,7 @@ var require_dist = __commonJS({
5668
5668
  var _question2 = question;
5669
5669
  name = _question2.name;
5670
5670
  type = _question2.type;
5671
- if (prompts6[type] === void 0) {
5671
+ if (prompts7[type] === void 0) {
5672
5672
  throw new Error(`prompt type (${type}) is not defined`);
5673
5673
  }
5674
5674
  if (override2[question.name] !== void 0) {
@@ -5679,7 +5679,7 @@ var require_dist = __commonJS({
5679
5679
  }
5680
5680
  }
5681
5681
  try {
5682
- answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : yield prompts6[type](question);
5682
+ answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : yield prompts7[type](question);
5683
5683
  answers[name] = answer = yield getFormattedAnswer(question, answer, true);
5684
5684
  quit = yield onSubmit(question, answer, answers);
5685
5685
  } catch (err) {
@@ -5711,7 +5711,7 @@ var require_dist = __commonJS({
5711
5711
  }
5712
5712
  module2.exports = Object.assign(prompt, {
5713
5713
  prompt,
5714
- prompts: prompts6,
5714
+ prompts: prompts7,
5715
5715
  inject,
5716
5716
  override
5717
5717
  });
@@ -7798,7 +7798,7 @@ var require_prompts2 = __commonJS({
7798
7798
  var require_lib = __commonJS({
7799
7799
  "node_modules/prompts/lib/index.js"(exports2, module2) {
7800
7800
  "use strict";
7801
- var prompts6 = require_prompts2();
7801
+ var prompts7 = require_prompts2();
7802
7802
  var passOn = ["suggest", "format", "onState", "validate", "onRender", "type"];
7803
7803
  var noop = () => {
7804
7804
  };
@@ -7830,7 +7830,7 @@ var require_lib = __commonJS({
7830
7830
  throw new Error("prompt message is required");
7831
7831
  }
7832
7832
  ({ name, type } = question);
7833
- if (prompts6[type] === void 0) {
7833
+ if (prompts7[type] === void 0) {
7834
7834
  throw new Error(`prompt type (${type}) is not defined`);
7835
7835
  }
7836
7836
  if (override2[question.name] !== void 0) {
@@ -7841,7 +7841,7 @@ var require_lib = __commonJS({
7841
7841
  }
7842
7842
  }
7843
7843
  try {
7844
- answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : await prompts6[type](question);
7844
+ answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : await prompts7[type](question);
7845
7845
  answers[name] = answer = await getFormattedAnswer(question, answer, true);
7846
7846
  quit = await onSubmit(question, answer, answers);
7847
7847
  } catch (err) {
@@ -7864,7 +7864,7 @@ var require_lib = __commonJS({
7864
7864
  function override(answers) {
7865
7865
  prompt._override = Object.assign({}, answers);
7866
7866
  }
7867
- module2.exports = Object.assign(prompt, { prompt, prompts: prompts6, inject, override });
7867
+ module2.exports = Object.assign(prompt, { prompt, prompts: prompts7, inject, override });
7868
7868
  }
7869
7869
  });
7870
7870
 
@@ -18008,16 +18008,33 @@ function writeMcpConfig(mcpPath, config2) {
18008
18008
  (0, import_fs3.mkdirSync)((0, import_path3.dirname)(mcpPath), { recursive: true });
18009
18009
  (0, import_fs3.writeFileSync)(mcpPath, JSON.stringify(config2, null, 2) + "\n");
18010
18010
  }
18011
- function injectCortexMcpServer(mcpPath, cortexUrl, cortexToken) {
18011
+ function injectCortexMcpServer(mcpPath, cortexUrl, cortexToken, target) {
18012
18012
  const config2 = readMcpConfig(mcpPath);
18013
18013
  const existed = "cortex" in config2.servers;
18014
+ let cortexTokenEnv;
18015
+ if (cortexToken) {
18016
+ if (target === "vscode" || target === "claude") {
18017
+ if (!config2.inputs) config2.inputs = [];
18018
+ if (!config2.inputs.some((i) => i.id === "cortex_token")) {
18019
+ config2.inputs.push({
18020
+ id: "cortex_token",
18021
+ type: "promptString",
18022
+ description: "Cortex API Token (set during cortex-context server setup)",
18023
+ password: true
18024
+ });
18025
+ }
18026
+ cortexTokenEnv = "${input:cortex_token}";
18027
+ } else {
18028
+ cortexTokenEnv = "${env:CORTEX_API_TOKEN}";
18029
+ }
18030
+ }
18014
18031
  config2.servers["cortex"] = {
18015
18032
  type: "stdio",
18016
18033
  command: "npx",
18017
18034
  args: ["-y", "@cortex-context/cli", "mcp-serve"],
18018
18035
  env: {
18019
18036
  CORTEX_URL: cortexUrl,
18020
- ...cortexToken ? { CORTEX_API_TOKEN: cortexToken } : {}
18037
+ ...cortexTokenEnv ? { CORTEX_API_TOKEN: cortexTokenEnv } : {}
18021
18038
  }
18022
18039
  };
18023
18040
  writeMcpConfig(mcpPath, config2);
@@ -18133,7 +18150,7 @@ function configureClaudeDesktop(cortexUrl, cortexToken) {
18133
18150
  args: ["-y", "@cortex-context/cli", "mcp-serve"],
18134
18151
  env: {
18135
18152
  CORTEX_URL: cortexUrl,
18136
- ...cortexToken ? { CORTEX_API_TOKEN: cortexToken } : {}
18153
+ ...cortexToken ? { CORTEX_API_TOKEN: "${env:CORTEX_API_TOKEN}" } : {}
18137
18154
  }
18138
18155
  };
18139
18156
  (0, import_fs3.writeFileSync)(configPath, JSON.stringify(config2, null, 2) + "\n");
@@ -18295,6 +18312,10 @@ async function deployLocalStack(workspacePath, opts = {}) {
18295
18312
  if (!(0, import_fs5.existsSync)(destPath)) {
18296
18313
  (0, import_fs5.cpSync)(templatePath, destPath);
18297
18314
  }
18315
+ const envPath = (0, import_path5.join)(workspacePath, ".env");
18316
+ const envContent = `CORTEX_API_TOKEN=${opts.token ?? ""}
18317
+ `;
18318
+ (0, import_fs5.writeFileSync)(envPath, envContent, { encoding: "utf-8" });
18298
18319
  const imageTag = opts.embeddings ? "latest-embeddings" : "latest";
18299
18320
  const cortexImage = `ghcr.io/rodrigoroldan/cortex-context:${imageTag}`;
18300
18321
  try {
@@ -18321,6 +18342,66 @@ ${stderr.trim()}` : ""}`
18321
18342
  };
18322
18343
  }
18323
18344
  }
18345
+ async function upgradeLocalStack(workspacePath, opts = {}) {
18346
+ const composePath = (0, import_path5.join)(workspacePath, "docker-compose.local.yml");
18347
+ if (!(0, import_fs5.existsSync)(composePath)) {
18348
+ return {
18349
+ upgraded: false,
18350
+ error: `docker-compose.local.yml not found in ${workspacePath}. Run: cortex-context server`
18351
+ };
18352
+ }
18353
+ const imageTag = opts.embeddings ? "latest-embeddings" : "latest";
18354
+ const cortexImage = `ghcr.io/rodrigoroldan/cortex-context:${imageTag}`;
18355
+ const composeCmd = `docker compose --project-name cortex-local -f "${composePath}"`;
18356
+ try {
18357
+ (0, import_child_process.execSync)(`${composeCmd} pull cortex-api`, {
18358
+ cwd: workspacePath,
18359
+ stdio: ["inherit", "inherit", "pipe"],
18360
+ env: { ...process.env, CORTEX_IMAGE: cortexImage }
18361
+ });
18362
+ } catch (err) {
18363
+ const stderr = err instanceof Error && "stderr" in err ? err.stderr?.toString() ?? "" : "";
18364
+ const msg = err instanceof Error ? err.message : String(err);
18365
+ return {
18366
+ upgraded: false,
18367
+ error: `docker compose pull failed: ${msg}${stderr ? `
18368
+ ${stderr.trim()}` : ""}`
18369
+ };
18370
+ }
18371
+ try {
18372
+ (0, import_child_process.execSync)(`${composeCmd} up -d --force-recreate cortex-api`, {
18373
+ cwd: workspacePath,
18374
+ stdio: ["inherit", "inherit", "pipe"],
18375
+ env: { ...process.env, CORTEX_IMAGE: cortexImage }
18376
+ });
18377
+ } catch (err) {
18378
+ const stderr = err instanceof Error && "stderr" in err ? err.stderr?.toString() ?? "" : "";
18379
+ const msg = err instanceof Error ? err.message : String(err);
18380
+ return {
18381
+ upgraded: false,
18382
+ error: `docker compose up failed: ${msg}${stderr ? `
18383
+ ${stderr.trim()}` : ""}`
18384
+ };
18385
+ }
18386
+ return { upgraded: true };
18387
+ }
18388
+ function restartCortexApi(workspacePath) {
18389
+ const composePath = (0, import_path5.join)(workspacePath, "docker-compose.local.yml");
18390
+ if (!(0, import_fs5.existsSync)(composePath))
18391
+ return { restarted: false, error: "docker-compose.local.yml not found" };
18392
+ try {
18393
+ (0, import_child_process.execSync)(
18394
+ `docker compose --project-name cortex-local -f "${composePath}" restart cortex-api`,
18395
+ { cwd: workspacePath, stdio: "ignore" }
18396
+ );
18397
+ return { restarted: true };
18398
+ } catch (err) {
18399
+ return {
18400
+ restarted: false,
18401
+ error: err instanceof Error ? err.message : String(err)
18402
+ };
18403
+ }
18404
+ }
18324
18405
  function isDockerAvailable() {
18325
18406
  try {
18326
18407
  (0, import_child_process.execSync)("docker --version", { stdio: "ignore" });
@@ -18711,7 +18792,8 @@ async function initCommand(options) {
18711
18792
  const mcpStatus = injectCortexMcpServer(
18712
18793
  detected.mcpPath,
18713
18794
  cortexUrl,
18714
- cortexToken
18795
+ cortexToken,
18796
+ detected.target
18715
18797
  );
18716
18798
  if (mcpStatus.added) {
18717
18799
  console.log(source_default.green(` \u2713 ${label} \u2014 Cortex server added`));
@@ -28113,6 +28195,7 @@ async function serveCommand() {
28113
28195
  var import_os4 = require("os");
28114
28196
  var import_fs12 = require("fs");
28115
28197
  var import_path13 = require("path");
28198
+ var import_crypto = require("crypto");
28116
28199
  var import_prompts3 = __toESM(require_prompts3());
28117
28200
  var CORTEX_URL2 = "http://localhost:8082";
28118
28201
  var NEO4J_URL = "http://localhost:7474";
@@ -28233,7 +28316,73 @@ async function serverCommand(options) {
28233
28316
  process.exit(0);
28234
28317
  }
28235
28318
  console.log("");
28236
- console.log(source_default.bold(" Step 3: Starting Cortex stack"));
28319
+ console.log(source_default.bold(" Step 3: API Token"));
28320
+ console.log(
28321
+ source_default.dim(" Optionally secure write endpoints with a Bearer token.")
28322
+ );
28323
+ console.log(
28324
+ source_default.dim(" Leave open (no token) for purely local/private use.")
28325
+ );
28326
+ console.log("");
28327
+ const generatedToken = (0, import_crypto.randomBytes)(24).toString("hex");
28328
+ const { tokenChoice } = await (0, import_prompts3.default)(
28329
+ {
28330
+ type: "select",
28331
+ name: "tokenChoice",
28332
+ message: "API token:",
28333
+ choices: [
28334
+ {
28335
+ title: "Generate random token " + source_default.dim("(recommended)"),
28336
+ value: "generate"
28337
+ },
28338
+ { title: "Enter my own token", value: "custom" },
28339
+ {
28340
+ title: "No token " + source_default.dim("(open access \u2014 local only)"),
28341
+ value: "none"
28342
+ }
28343
+ ],
28344
+ initial: 0
28345
+ },
28346
+ {
28347
+ onCancel: () => {
28348
+ console.log(source_default.yellow("\n Aborted."));
28349
+ process.exit(0);
28350
+ }
28351
+ }
28352
+ );
28353
+ let serverToken = "";
28354
+ if (!tokenChoice || tokenChoice === "none") {
28355
+ console.log(
28356
+ source_default.dim(" \u2139 No token \u2014 API is open (suitable for local use only)")
28357
+ );
28358
+ } else if (tokenChoice === "generate") {
28359
+ serverToken = generatedToken;
28360
+ console.log(source_default.green(" \u2713 Random token generated"));
28361
+ } else {
28362
+ const { customToken } = await (0, import_prompts3.default)(
28363
+ {
28364
+ type: "password",
28365
+ name: "customToken",
28366
+ message: "Enter your token:",
28367
+ validate: (v) => v.trim().length >= 8 || "Token must be at least 8 characters"
28368
+ },
28369
+ {
28370
+ onCancel: () => {
28371
+ console.log(source_default.yellow("\n Aborted."));
28372
+ process.exit(0);
28373
+ }
28374
+ }
28375
+ );
28376
+ serverToken = customToken?.trim() ?? "";
28377
+ if (!serverToken) {
28378
+ console.log(source_default.dim(" \u2139 Empty \u2014 API is open"));
28379
+ } else {
28380
+ console.log(source_default.green(" \u2713 Token saved"));
28381
+ }
28382
+ }
28383
+ writeConfig({ url: CORTEX_URL2, token: serverToken });
28384
+ console.log("");
28385
+ console.log(source_default.bold(" Step 4: Starting Cortex stack"));
28237
28386
  const sysctlFix = tryFixLxcSysctl();
28238
28387
  if (sysctlFix.applied) {
28239
28388
  console.log(
@@ -28257,7 +28406,8 @@ async function serverCommand(options) {
28257
28406
  console.log(source_default.dim(" $ docker compose up -d"));
28258
28407
  console.log("");
28259
28408
  const startResult = await deployLocalStack(workDir, {
28260
- embeddings: options.embeddings ?? false
28409
+ embeddings: options.embeddings ?? false,
28410
+ token: serverToken
28261
28411
  });
28262
28412
  console.log("");
28263
28413
  if (!startResult.started) {
@@ -28306,7 +28456,7 @@ async function serverCommand(options) {
28306
28456
  }
28307
28457
  console.log(source_default.green(" \u2713 Containers started"));
28308
28458
  console.log("");
28309
- console.log(source_default.bold(" Step 4: Waiting for Cortex API..."));
28459
+ console.log(source_default.bold(" Step 5: Waiting for Cortex API..."));
28310
28460
  const healthSpinner = ora(
28311
28461
  " Polling http://localhost:8082/health (up to 2 min)..."
28312
28462
  ).start();
@@ -28332,9 +28482,22 @@ async function serverCommand(options) {
28332
28482
  console.log(source_default.cyan(` Neo4j UI \u2192 ${NEO4J_URL}`));
28333
28483
  console.log(source_default.dim(" Neo4j login \u2192 neo4j / cortex-local"));
28334
28484
  console.log("");
28335
- console.log(source_default.bold(" Next steps:"));
28336
- console.log(source_default.dim(" 1. Initialize your workspace:"));
28337
- console.log(source_default.cyan(` cortex-context init --url ${CORTEX_URL2}`));
28485
+ if (serverToken) {
28486
+ console.log(source_default.bold(" \u{1F511} API Token (save this!):"));
28487
+ console.log(source_default.bold.yellow(` ${serverToken}`));
28488
+ console.log("");
28489
+ console.log(source_default.bold(" Next steps:"));
28490
+ console.log(source_default.dim(" 1. Initialize your workspace:"));
28491
+ console.log(
28492
+ source_default.cyan(
28493
+ ` cortex-context init --url ${CORTEX_URL2} --token ${serverToken}`
28494
+ )
28495
+ );
28496
+ } else {
28497
+ console.log(source_default.bold(" Next steps:"));
28498
+ console.log(source_default.dim(" 1. Initialize your workspace:"));
28499
+ console.log(source_default.cyan(` cortex-context init --url ${CORTEX_URL2}`));
28500
+ }
28338
28501
  console.log(source_default.dim(" 2. Check connectivity:"));
28339
28502
  console.log(source_default.cyan(" cortex-context doctor"));
28340
28503
  console.log("");
@@ -28488,8 +28651,8 @@ async function uninstallCommand(options) {
28488
28651
  if (!options.keepHook && hasHook) {
28489
28652
  const hookPath = (0, import_path14.join)(workspacePath, ".git", "hooks", "post-commit");
28490
28653
  try {
28491
- const { readFileSync: readFileSync8 } = await import("fs");
28492
- const hookContent = readFileSync8(hookPath, "utf-8");
28654
+ const { readFileSync: readFileSync9 } = await import("fs");
28655
+ const hookContent = readFileSync9(hookPath, "utf-8");
28493
28656
  if (hookContent.includes("cortex-context")) {
28494
28657
  (0, import_fs13.unlinkSync)(hookPath);
28495
28658
  console.log(source_default.green(" \u2713 .git/hooks/post-commit \u2014 removed"));
@@ -28556,7 +28719,11 @@ async function resetCommand(options) {
28556
28719
  console.log("");
28557
28720
  console.log(source_default.bold(" \u25C6 Cortex Context \u2014 Reset Graph"));
28558
28721
  console.log("");
28559
- console.log(source_default.yellow(" \u26A0 This will delete ALL nodes and relationships from the graph."));
28722
+ console.log(
28723
+ source_default.yellow(
28724
+ " \u26A0 This will delete ALL nodes and relationships from the graph."
28725
+ )
28726
+ );
28560
28727
  console.log(source_default.dim(` Server: ${url}`));
28561
28728
  console.log("");
28562
28729
  const confirmed = options.yes || await (0, import_prompts5.default)({
@@ -28584,7 +28751,9 @@ async function resetCommand(options) {
28584
28751
  } catch (err) {
28585
28752
  process.stdout.write("\n");
28586
28753
  console.error(
28587
- source_default.red(` \u2717 Network error: ${err instanceof Error ? err.message : String(err)}`)
28754
+ source_default.red(
28755
+ ` \u2717 Network error: ${err instanceof Error ? err.message : String(err)}`
28756
+ )
28588
28757
  );
28589
28758
  process.exit(1);
28590
28759
  }
@@ -28603,11 +28772,258 @@ async function resetCommand(options) {
28603
28772
  console.log(source_default.dim(" Run `cortex-context sync` to re-ingest the graph."));
28604
28773
  }
28605
28774
 
28606
- // src/cli.ts
28775
+ // src/commands/token.ts
28776
+ var import_crypto2 = require("crypto");
28607
28777
  var import_fs14 = require("fs");
28608
28778
  var import_path15 = require("path");
28779
+ var import_os5 = require("os");
28780
+ var import_prompts6 = __toESM(require_prompts3());
28781
+ var CORTEX_DIR = (0, import_path15.join)((0, import_os5.homedir)(), ".cortex-context");
28782
+ var ENV_FILE = (0, import_path15.join)(CORTEX_DIR, ".env");
28783
+ async function applyTokenRestart() {
28784
+ if (!isDockerAvailable()) return;
28785
+ console.log(
28786
+ source_default.dim(" Restarting cortex-api container to apply new token...")
28787
+ );
28788
+ const result = restartCortexApi(CORTEX_DIR);
28789
+ if (!result.restarted) {
28790
+ console.log(
28791
+ source_default.yellow(
28792
+ ` \u26A0 Could not restart container: ${result.error ?? "unknown error"}`
28793
+ )
28794
+ );
28795
+ console.log(
28796
+ source_default.dim(
28797
+ " docker compose -f ~/.cortex-context/docker-compose.local.yml restart cortex-api"
28798
+ )
28799
+ );
28800
+ return;
28801
+ }
28802
+ const config2 = readConfig();
28803
+ const url = config2.url ?? "http://localhost:8082";
28804
+ console.log(source_default.dim(` Waiting for Cortex API at ${url}...`));
28805
+ const { healthy } = await waitForCortexHealth(url, 6e4);
28806
+ if (healthy) {
28807
+ console.log(source_default.green(" \u2713 Container restarted and healthy"));
28808
+ } else {
28809
+ console.log(
28810
+ source_default.yellow(
28811
+ " \u26A0 Container restarted but did not become healthy within 60s"
28812
+ )
28813
+ );
28814
+ }
28815
+ }
28816
+ function readEnvToken() {
28817
+ if (!(0, import_fs14.existsSync)(ENV_FILE)) return "";
28818
+ const content = (0, import_fs14.readFileSync)(ENV_FILE, "utf-8");
28819
+ const match = content.match(/^CORTEX_API_TOKEN=(.*)$/m);
28820
+ return match?.[1]?.trim() ?? "";
28821
+ }
28822
+ function writeEnvToken(token) {
28823
+ (0, import_fs14.mkdirSync)(CORTEX_DIR, { recursive: true });
28824
+ if (!(0, import_fs14.existsSync)(ENV_FILE)) {
28825
+ (0, import_fs14.writeFileSync)(ENV_FILE, `CORTEX_API_TOKEN=${token}
28826
+ `, "utf-8");
28827
+ return;
28828
+ }
28829
+ const content = (0, import_fs14.readFileSync)(ENV_FILE, "utf-8");
28830
+ const updated = content.includes("CORTEX_API_TOKEN=") ? content.replace(/^CORTEX_API_TOKEN=.*$/m, `CORTEX_API_TOKEN=${token}`) : content + `CORTEX_API_TOKEN=${token}
28831
+ `;
28832
+ (0, import_fs14.writeFileSync)(ENV_FILE, updated, "utf-8");
28833
+ }
28834
+ async function tokenCommand(options) {
28835
+ console.log("");
28836
+ console.log(source_default.bold(" \u25C6 Cortex Context \u2014 API Token"));
28837
+ console.log("");
28838
+ if (options.set !== void 0) {
28839
+ const newToken = options.set.trim();
28840
+ if (!newToken) {
28841
+ console.log(
28842
+ source_default.yellow(
28843
+ " Clearing token \u2014 API will accept all requests (open access)."
28844
+ )
28845
+ );
28846
+ }
28847
+ writeConfig({ token: newToken });
28848
+ writeEnvToken(newToken);
28849
+ console.log(
28850
+ source_default.green(newToken ? " \u2713 Token updated" : " \u2713 Token cleared")
28851
+ );
28852
+ console.log("");
28853
+ await applyTokenRestart();
28854
+ console.log("");
28855
+ return;
28856
+ }
28857
+ if (options.rotate) {
28858
+ const { confirm } = await (0, import_prompts6.default)(
28859
+ {
28860
+ type: "confirm",
28861
+ name: "confirm",
28862
+ message: "Generate a new random token? (requires container restart to apply)",
28863
+ initial: true
28864
+ },
28865
+ {
28866
+ onCancel: () => {
28867
+ console.log(source_default.yellow("\n Aborted."));
28868
+ process.exit(0);
28869
+ }
28870
+ }
28871
+ );
28872
+ if (!confirm) {
28873
+ console.log(source_default.dim(" Aborted."));
28874
+ console.log("");
28875
+ return;
28876
+ }
28877
+ const newToken = (0, import_crypto2.randomBytes)(24).toString("hex");
28878
+ writeConfig({ token: newToken });
28879
+ writeEnvToken(newToken);
28880
+ console.log(source_default.green(" \u2713 New token generated and saved"));
28881
+ console.log("");
28882
+ console.log(source_default.bold(" \u{1F511} New Token:"));
28883
+ console.log(source_default.bold.yellow(` ${newToken}`));
28884
+ console.log("");
28885
+ const config3 = readConfig();
28886
+ console.log(
28887
+ source_default.dim(" Re-initialize your workspace to update MCP config:")
28888
+ );
28889
+ console.log(
28890
+ source_default.cyan(
28891
+ ` cortex-context init --url ${config3.url ?? "http://localhost:8082"} --token ${newToken}`
28892
+ )
28893
+ );
28894
+ console.log("");
28895
+ await applyTokenRestart();
28896
+ console.log("");
28897
+ return;
28898
+ }
28899
+ const config2 = readConfig();
28900
+ const currentToken = config2.token ?? readEnvToken();
28901
+ if (!currentToken) {
28902
+ console.log(
28903
+ source_default.yellow(" \u26A0 No token configured \u2014 API is in open access mode")
28904
+ );
28905
+ console.log(source_default.dim(" Generate one: cortex-context token --rotate"));
28906
+ console.log(
28907
+ source_default.dim(" Set one: cortex-context token --set <value>")
28908
+ );
28909
+ } else if (options.show) {
28910
+ console.log(source_default.bold(` Token: ${source_default.yellow(currentToken)}`));
28911
+ } else {
28912
+ const masked = currentToken.length > 8 ? currentToken.slice(0, 4) + "\u2022".repeat(currentToken.length - 8) + currentToken.slice(-4) : "\u2022".repeat(currentToken.length);
28913
+ console.log(source_default.dim(` Token: ${masked}`));
28914
+ console.log(source_default.dim(" Use --show to reveal the full token"));
28915
+ }
28916
+ console.log("");
28917
+ }
28918
+
28919
+ // src/commands/upgrade.ts
28920
+ var import_path16 = require("path");
28921
+ var import_os6 = require("os");
28922
+ var import_fs15 = require("fs");
28923
+ async function upgradeCommand(options) {
28924
+ console.log("");
28925
+ console.log(source_default.bold.cyan(" \u25C6 Cortex Context \u2014 Upgrade"));
28926
+ console.log(
28927
+ source_default.dim(
28928
+ " Pulls the latest backend image and recreates the cortex-api container."
28929
+ )
28930
+ );
28931
+ console.log("");
28932
+ if (!isDockerAvailable()) {
28933
+ console.error(source_default.red(" \u2717 Docker is not available on $PATH."));
28934
+ console.log(
28935
+ source_default.dim(" Install Docker or ensure it is running, then retry.")
28936
+ );
28937
+ process.exit(1);
28938
+ }
28939
+ const workDir = options.dir ?? (0, import_path16.join)((0, import_os6.homedir)(), ".cortex-context");
28940
+ const composePath = (0, import_path16.join)(workDir, "docker-compose.local.yml");
28941
+ if (!(0, import_fs15.existsSync)(composePath)) {
28942
+ console.error(
28943
+ source_default.red(` \u2717 docker-compose.local.yml not found in ${workDir}`)
28944
+ );
28945
+ console.log(
28946
+ source_default.dim(" The local Cortex stack has not been set up yet. Run:")
28947
+ );
28948
+ console.log(source_default.cyan(" cortex-context server"));
28949
+ console.log("");
28950
+ process.exit(1);
28951
+ }
28952
+ const imageTag = options.embeddings ? "latest-embeddings" : "latest";
28953
+ const cortexImage = `ghcr.io/rodrigoroldan/cortex-context:${imageTag}`;
28954
+ console.log(source_default.dim(` Target image: ${cortexImage}`));
28955
+ console.log(source_default.dim(` Compose dir: ${workDir}`));
28956
+ console.log("");
28957
+ console.log(source_default.bold(" Step 1 \u2014 Pulling latest image..."));
28958
+ if (options.pullOnly) {
28959
+ const { execSync: execSync3 } = await import("child_process");
28960
+ try {
28961
+ execSync3(
28962
+ `docker compose --project-name cortex-local -f "${composePath}" pull cortex-api`,
28963
+ {
28964
+ cwd: workDir,
28965
+ stdio: ["inherit", "inherit", "pipe"],
28966
+ env: { ...process.env, CORTEX_IMAGE: cortexImage }
28967
+ }
28968
+ );
28969
+ console.log("");
28970
+ console.log(
28971
+ source_default.green(" \u2713 Image pulled successfully (--pull-only mode)")
28972
+ );
28973
+ console.log(
28974
+ source_default.dim(
28975
+ " Run without --pull-only to recreate the container with the new image."
28976
+ )
28977
+ );
28978
+ console.log("");
28979
+ } catch (err) {
28980
+ const msg = err instanceof Error ? err.message : String(err);
28981
+ console.error(source_default.red(` \u2717 Pull failed: ${msg}`));
28982
+ process.exit(1);
28983
+ }
28984
+ return;
28985
+ }
28986
+ const result = await upgradeLocalStack(workDir, {
28987
+ embeddings: options.embeddings
28988
+ });
28989
+ if (!result.upgraded) {
28990
+ console.error(source_default.red(` \u2717 Upgrade failed: ${result.error}`));
28991
+ process.exit(1);
28992
+ }
28993
+ console.log("");
28994
+ console.log(source_default.bold(" Step 2 \u2014 Waiting for Cortex API to be healthy..."));
28995
+ const config2 = readConfig();
28996
+ const cortexUrl = config2.url ?? "http://localhost:8082";
28997
+ const { healthy } = await waitForCortexHealth(cortexUrl, 12e4);
28998
+ console.log("");
28999
+ if (healthy) {
29000
+ console.log(source_default.green(" \u2713 Cortex API is healthy"));
29001
+ console.log("");
29002
+ console.log(source_default.bold(" \u25C6 Upgrade complete!"));
29003
+ console.log(source_default.dim(` API running at ${cortexUrl}`));
29004
+ console.log("");
29005
+ } else {
29006
+ console.log(
29007
+ source_default.yellow(
29008
+ " \u26A0 Container is up but did not become healthy within 120s"
29009
+ )
29010
+ );
29011
+ console.log(source_default.dim(" Check container logs:"));
29012
+ console.log(
29013
+ source_default.cyan(
29014
+ ` docker compose -f ${workDir}/docker-compose.local.yml logs cortex-api`
29015
+ )
29016
+ );
29017
+ console.log("");
29018
+ process.exit(1);
29019
+ }
29020
+ }
29021
+
29022
+ // src/cli.ts
29023
+ var import_fs16 = require("fs");
29024
+ var import_path17 = require("path");
28609
29025
  var pkg = JSON.parse(
28610
- (0, import_fs14.readFileSync)((0, import_path15.join)(__dirname, "..", "package.json"), "utf-8")
29026
+ (0, import_fs16.readFileSync)((0, import_path17.join)(__dirname, "..", "package.json"), "utf-8")
28611
29027
  );
28612
29028
  function createCli() {
28613
29029
  const program3 = new Command();
@@ -28655,6 +29071,21 @@ function createCli() {
28655
29071
  program3.command("reset").description(
28656
29072
  "Wipe all nodes and relationships from the Cortex Knowledge Graph (irreversible)"
28657
29073
  ).option("-y, --yes", "Skip confirmation prompt").action(resetCommand);
29074
+ program3.command("token").description(
29075
+ "Show or manage the Cortex API token for the local stack (~/.cortex-context/.env)"
29076
+ ).option("--show", "Reveal the full token (default shows masked)").option("--rotate", "Generate a new random token and save it").option(
29077
+ "--set <value>",
29078
+ "Set a specific token value (empty string clears auth)"
29079
+ ).action(tokenCommand);
29080
+ program3.command("upgrade").description(
29081
+ "Pull the latest Cortex backend image and restart the local stack"
29082
+ ).option(
29083
+ "--dir <path>",
29084
+ "Directory with docker-compose.local.yml (defaults to ~/.cortex-context)"
29085
+ ).option("--embeddings", "Use the embeddings-enabled image variant").option(
29086
+ "--pull-only",
29087
+ "Pull the latest image without recreating the container"
29088
+ ).action(upgradeCommand);
28658
29089
  return program3;
28659
29090
  }
28660
29091