File size: 3,332 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { rmSync } from "node:fs";
import { t } from "../i18n.mjs";
import { emit } from "../output.mjs";
import { withSpinner } from "../spinner.mjs";
import {
  getRuntimeNodeModules,
  hasModule,
  isBetterSqliteBinaryValid,
  ensureBetterSqliteRuntime,
} from "../runtime/nativeDeps.mjs";

async function confirm(msg) {
  const readline = await import("node:readline");
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
  const answer = await new Promise((r) => rl.question(`${msg} [y/N] `, r));
  rl.close();
  return /^y(es)?$/i.test(answer);
}

/**
 * Shared repair action used by both `omniroute runtime repair` and the
 * top-level `omniroute repair` alias. Reinstalls better-sqlite3 into the
 * user-writable runtime directory via the existing engine — no hand-rolled
 * npm-rebuild spawn. Exits with code 1 on failure.
 */
async function runRepairAction(opts, cmd) {
  const globalOpts = cmd.optsWithGlobals();
  await withSpinner(
    "Repairing native deps",
    async () => ensureBetterSqliteRuntime({ silent: true, force: opts.force }),
    globalOpts
  );
  const ok = hasModule("better-sqlite3") && isBetterSqliteBinaryValid();
  if (ok) {
    process.stdout.write("✓ better-sqlite3 repaired OK\n");
  } else {
    process.stderr.write("✗ Repair failed — check npm availability\n");
    process.exit(1);
  }
}

export function registerRuntime(program) {
  const runtime = program
    .command("runtime")
    .description(t("runtime.description") || "Manage native runtime dependencies");

  runtime
    .command("check")
    .description("Check status of native deps in runtime directory")
    .action(async (opts, cmd) => {
      const globalOpts = cmd.optsWithGlobals();
      const status = {
        runtimeDir: getRuntimeNodeModules(),
        betterSqlite3: {
          installed: hasModule("better-sqlite3"),
          valid: isBetterSqliteBinaryValid(),
        },
      };
      emit(status, globalOpts);
    });

  runtime
    .command("repair")
    .description("Reinstall native deps in runtime directory")
    .option("--force", "Force reinstall even if valid")
    .action(runRepairAction);

  // Top-level discoverability alias: `omniroute repair` invokes the SAME action
  // as `omniroute runtime repair` (no duplicated logic). Surfaced in the
  // native-error / startup hints so users with a broken better-sqlite3 binding
  // have a single self-heal command that works without a C++ toolchain.
  program
    .command("repair")
    .description("Repair native deps (alias for `runtime repair`)")
    .option("--force", "Force reinstall even if valid")
    .action(runRepairAction);

  runtime
    .command("clean")
    .description("Remove runtime directory (frees disk space)")
    .option("--yes", "Skip confirmation")
    .action(async (opts) => {
      if (!opts.yes) {
        const ok = await confirm(`Remove ${getRuntimeNodeModules()}?`);
        if (!ok) {
          process.stdout.write("Cancelled.\n");
          return;
        }
      }
      try {
        rmSync(getRuntimeNodeModules(), { recursive: true, force: true });
        process.stdout.write("Cleaned runtime directory.\n");
      } catch (e) {
        process.stderr.write(`Failed: ${e instanceof Error ? e.message : String(e)}\n`);
        process.exit(1);
      }
    });
}