@cortex-context/cli 0.0.8 → 0.0.10

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
@@ -18220,7 +18220,68 @@ function tryFixLxcSysctl() {
18220
18220
  return { applied: false };
18221
18221
  }
18222
18222
  }
18223
- async function deployLocalStack(workspacePath) {
18223
+ function tryConfigureCrunRuntime() {
18224
+ const result = {
18225
+ attempted: false,
18226
+ crunInstalled: false,
18227
+ daemonUpdated: false,
18228
+ dockerRestarted: false
18229
+ };
18230
+ if ((0, import_os3.platform)() !== "linux") return result;
18231
+ result.attempted = true;
18232
+ const hasCrun = (() => {
18233
+ try {
18234
+ (0, import_child_process.execSync)("which crun", { stdio: "ignore" });
18235
+ return true;
18236
+ } catch {
18237
+ return false;
18238
+ }
18239
+ })();
18240
+ if (hasCrun) {
18241
+ result.crunInstalled = true;
18242
+ } else {
18243
+ try {
18244
+ (0, import_child_process.execSync)("apt-get install -y crun", { stdio: "ignore" });
18245
+ result.crunInstalled = true;
18246
+ } catch (e) {
18247
+ result.error = `crun install: ${e instanceof Error ? e.message : String(e)}`;
18248
+ return result;
18249
+ }
18250
+ }
18251
+ const daemonPath = "/etc/docker/daemon.json";
18252
+ let cfg = {};
18253
+ try {
18254
+ if ((0, import_fs5.existsSync)(daemonPath)) {
18255
+ cfg = JSON.parse((0, import_fs5.readFileSync)(daemonPath, "utf-8"));
18256
+ }
18257
+ } catch {
18258
+ cfg = {};
18259
+ }
18260
+ if (cfg["default-runtime"] === "crun") {
18261
+ result.daemonUpdated = true;
18262
+ result.dockerRestarted = true;
18263
+ return result;
18264
+ }
18265
+ const runtimes = cfg["runtimes"] ?? {};
18266
+ runtimes["crun"] = { path: "/usr/bin/crun" };
18267
+ cfg["runtimes"] = runtimes;
18268
+ cfg["default-runtime"] = "crun";
18269
+ try {
18270
+ (0, import_fs5.writeFileSync)(daemonPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
18271
+ result.daemonUpdated = true;
18272
+ } catch (e) {
18273
+ result.error = `daemon.json: ${e instanceof Error ? e.message : String(e)}`;
18274
+ return result;
18275
+ }
18276
+ try {
18277
+ (0, import_child_process.execSync)("systemctl restart docker", { stdio: "ignore" });
18278
+ result.dockerRestarted = true;
18279
+ } catch (e) {
18280
+ result.error = `docker restart: ${e instanceof Error ? e.message : String(e)}`;
18281
+ }
18282
+ return result;
18283
+ }
18284
+ async function deployLocalStack(workspacePath, opts = {}) {
18224
18285
  const templatePath = (0, import_path5.join)(
18225
18286
  __dirname,
18226
18287
  "templates",
@@ -18234,22 +18295,29 @@ async function deployLocalStack(workspacePath) {
18234
18295
  if (!(0, import_fs5.existsSync)(destPath)) {
18235
18296
  (0, import_fs5.cpSync)(templatePath, destPath);
18236
18297
  }
18298
+ const imageTag = opts.embeddings ? "latest-embeddings" : "latest";
18299
+ const cortexImage = `ghcr.io/rodrigoroldan/cortex-context:${imageTag}`;
18237
18300
  try {
18238
18301
  (0, import_child_process.execSync)(
18239
18302
  `docker compose --project-name cortex-local -f "${destPath}" up -d`,
18240
18303
  {
18241
18304
  cwd: workspacePath,
18242
- stdio: "inherit"
18305
+ // stdout → terminal (user sees progress), stderr → captured for error detection
18306
+ stdio: ["inherit", "inherit", "pipe"],
18307
+ env: { ...process.env, CORTEX_IMAGE: cortexImage }
18243
18308
  }
18244
18309
  );
18245
18310
  return { started: true };
18246
18311
  } catch (err) {
18312
+ const stderr = err instanceof Error && "stderr" in err ? err.stderr?.toString() ?? "" : "";
18247
18313
  const msg = err instanceof Error ? err.message : String(err);
18248
- const isLxcSysctlError = msg.includes("ip_unprivileged_port_start") || msg.includes("sysctl");
18314
+ const combined = stderr + msg;
18315
+ const isLxcSysctlError = combined.includes("ip_unprivileged_port_start") || combined.includes("sysctl net.ipv4");
18249
18316
  return {
18250
18317
  started: false,
18251
18318
  isLxcSysctlError,
18252
- error: `docker compose failed: ${msg}`
18319
+ error: `docker compose failed: ${msg}${stderr ? `
18320
+ ${stderr.trim()}` : ""}`
18253
18321
  };
18254
18322
  }
18255
18323
  }
@@ -28056,6 +28124,13 @@ async function serverCommand(options) {
28056
28124
  " Installs and starts the Cortex stack (Neo4j + Cortex API) via Docker."
28057
28125
  )
28058
28126
  );
28127
+ if (options.embeddings) {
28128
+ console.log(
28129
+ source_default.dim(
28130
+ " Mode: embeddings (sentence-transformers + all-MiniLM-L6-v2 baked in, ~1.5 GB image)"
28131
+ )
28132
+ );
28133
+ }
28059
28134
  console.log("");
28060
28135
  const defaultDir = (0, import_path13.join)((0, import_os4.homedir)(), ".cortex-context");
28061
28136
  const workDir = options.dir ?? defaultDir;
@@ -28161,28 +28236,69 @@ async function serverCommand(options) {
28161
28236
  console.log(source_default.bold(" Step 3: Starting Cortex stack"));
28162
28237
  const sysctlFix = tryFixLxcSysctl();
28163
28238
  if (sysctlFix.applied) {
28164
- console.log(source_default.dim(" (applied net.ipv4.ip_unprivileged_port_start=0 for LXC compatibility)"));
28239
+ console.log(
28240
+ source_default.dim(
28241
+ " (applied net.ipv4.ip_unprivileged_port_start=0 for LXC compatibility)"
28242
+ )
28243
+ );
28244
+ }
28245
+ const crunSetup = tryConfigureCrunRuntime();
28246
+ if (crunSetup.attempted) {
28247
+ if (crunSetup.dockerRestarted) {
28248
+ console.log(
28249
+ source_default.dim(
28250
+ " (configured crun as Docker runtime for LXC compatibility \u2014 daemon restarted)"
28251
+ )
28252
+ );
28253
+ } else if (crunSetup.error) {
28254
+ console.log(
28255
+ source_default.dim(` (crun setup skipped: ${crunSetup.error})`)
28256
+ );
28257
+ }
28165
28258
  }
28166
28259
  const startSpinner = ora(" Running docker compose up -d...").start();
28167
- const startResult = await deployLocalStack(workDir);
28260
+ const startResult = await deployLocalStack(workDir, {
28261
+ embeddings: options.embeddings ?? false
28262
+ });
28168
28263
  if (!startResult.started) {
28169
28264
  startSpinner.fail(source_default.red(" \u2717 Docker Compose failed"));
28170
28265
  if (startResult.isLxcSysctlError) {
28171
28266
  console.log("");
28172
- console.log(source_default.bold.yellow(" \u26A0 Proxmox LXC sysctl restriction detected"));
28267
+ console.log(
28268
+ source_default.bold.yellow(" \u26A0 Proxmox LXC sysctl restriction detected")
28269
+ );
28173
28270
  console.log("");
28174
- console.log(source_default.dim(" runc >= 1.1 tries to configure net.ipv4.ip_unprivileged_port_start"));
28175
- console.log(source_default.dim(" inside each container's network namespace. Proxmox LXC containers"));
28176
- console.log(source_default.dim(" block this unless the sysctl is explicitly allowed in the LXC config."));
28271
+ console.log(
28272
+ source_default.dim(
28273
+ " runc >= 1.1 writes net.ipv4.ip_unprivileged_port_start in each new"
28274
+ )
28275
+ );
28276
+ console.log(
28277
+ source_default.dim(
28278
+ " Docker network namespace. Unprivileged Proxmox LXC blocks this write."
28279
+ )
28280
+ );
28177
28281
  console.log("");
28178
- console.log(source_default.bold(" Fix (run on the Proxmox host):"));
28282
+ console.log(
28283
+ source_default.dim(
28284
+ " Attempted automatic fix (crun) \u2014 but it was not enough."
28285
+ )
28286
+ );
28287
+ console.log(
28288
+ source_default.dim(
28289
+ " Manual fix: install crun and configure Docker daemon on this LXC:"
28290
+ )
28291
+ );
28179
28292
  console.log("");
28180
- console.log(source_default.cyan(" CTID=<your-container-id>"));
28181
- console.log(source_default.cyan(' echo "lxc.sysctl.net.ipv4.ip_unprivileged_port_start = 0" \\'));
28182
- console.log(source_default.cyan(" >> /etc/pve/lxc/${CTID}.conf"));
28183
- console.log(source_default.cyan(" pct restart ${CTID}"));
28293
+ console.log(source_default.cyan(" apt-get install -y crun"));
28294
+ console.log(source_default.cyan(" cat > /etc/docker/daemon.json << 'EOF'"));
28295
+ console.log(source_default.cyan(' {"default-runtime":"crun","runtimes":{"crun":{"path":"/usr/bin/crun"}}}'));
28296
+ console.log(source_default.cyan(" EOF"));
28297
+ console.log(source_default.cyan(" systemctl restart docker"));
28184
28298
  console.log("");
28185
- console.log(source_default.dim(" Then run cortex-context server again inside the LXC."));
28299
+ console.log(
28300
+ source_default.dim(" Then run cortex-context server again.")
28301
+ );
28186
28302
  } else {
28187
28303
  console.log(source_default.dim(` ${startResult.error}`));
28188
28304
  }
@@ -28372,8 +28488,8 @@ async function uninstallCommand(options) {
28372
28488
  if (!options.keepHook && hasHook) {
28373
28489
  const hookPath = (0, import_path14.join)(workspacePath, ".git", "hooks", "post-commit");
28374
28490
  try {
28375
- const { readFileSync: readFileSync7 } = await import("fs");
28376
- const hookContent = readFileSync7(hookPath, "utf-8");
28491
+ const { readFileSync: readFileSync8 } = await import("fs");
28492
+ const hookContent = readFileSync8(hookPath, "utf-8");
28377
28493
  if (hookContent.includes("cortex-context")) {
28378
28494
  (0, import_fs13.unlinkSync)(hookPath);
28379
28495
  console.log(source_default.green(" \u2713 .git/hooks/post-commit \u2014 removed"));
@@ -28454,6 +28570,9 @@ function createCli() {
28454
28570
  ).option(
28455
28571
  "--skip-docker-install",
28456
28572
  "Fail instead of auto-installing Docker when it is not found"
28573
+ ).option(
28574
+ "--embeddings",
28575
+ "Use the embeddings-enabled image (includes sentence-transformers + all-MiniLM-L6-v2, ~1.5 GB)"
28457
28576
  ).action(serverCommand);
28458
28577
  program3.command("sync").description("Ingest latest git diff into the Cortex Knowledge Graph").option("--repo <path>", "Repository path (defaults to current directory)").option("--dry-run", "Show diff without sending to Cortex API").action(syncCommand);
28459
28578
  program3.command("update").description(