@arkhera30/cli 0.1.1 → 0.1.3

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/index.js +61 -15
  2. package/package.json +9 -7
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import chalk10 from "chalk";
8
8
  import { Command } from "commander";
9
9
  import chalk from "chalk";
10
10
  import ora from "ora";
11
- import { input, confirm, number } from "@inquirer/prompts";
11
+ import { input, confirm, number, select } from "@inquirer/prompts";
12
12
 
13
13
  // src/lib/config.ts
14
14
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
@@ -212,7 +212,15 @@ function toResult(result) {
212
212
  }
213
213
  async function tryCommand(command, args) {
214
214
  try {
215
- await execa(command, args, { reject: false });
215
+ const result = await execa(command, args, { reject: false });
216
+ return result.exitCode === 0;
217
+ } catch {
218
+ return false;
219
+ }
220
+ }
221
+ async function commandExists(command) {
222
+ try {
223
+ await execa(command, ["--version"], { reject: false });
216
224
  return true;
217
225
  } catch {
218
226
  return false;
@@ -261,6 +269,9 @@ function createRuntime(name) {
261
269
  }
262
270
  };
263
271
  }
272
+ async function checkRuntime(name) {
273
+ return tryCommand(name, ["compose", "version"]);
274
+ }
264
275
  async function detectRuntime(preferred) {
265
276
  if (preferred) {
266
277
  const hasPreferred = await tryCommand(preferred, ["compose", "version"]);
@@ -276,6 +287,12 @@ async function detectRuntime(preferred) {
276
287
  if (hasPodman) {
277
288
  return createRuntime("podman");
278
289
  }
290
+ const podmanInstalled = await commandExists("podman");
291
+ if (podmanInstalled) {
292
+ throw new Error(
293
+ "Podman is installed but `podman compose` is not working.\n\nFix options:\n 1. Ensure your Podman machine is running: podman machine start\n 2. Install podman-compose: pip3 install podman-compose\n 3. Upgrade Podman to v5+: brew upgrade podman\n"
294
+ );
295
+ }
279
296
  throw new Error(
280
297
  "No container runtime found.\n\nHorus requires Docker or Podman with the Compose plugin.\n\nInstall one of:\n - Docker Desktop: https://www.docker.com/products/docker-desktop/\n - Podman Desktop: https://podman-desktop.io/\n"
281
298
  );
@@ -384,7 +401,7 @@ function installComposeFile() {
384
401
  }
385
402
 
386
403
  // src/commands/setup.ts
387
- var setupCommand = new Command("setup").description("Interactive first-run setup for Horus").option("-y, --yes", "Non-interactive mode (use defaults + env vars)").option("--data-dir <path>", "Data directory path").option("--repos-path <path>", "Host repos path for Forge scanning").action(async (opts) => {
404
+ var setupCommand = new Command("setup").description("Interactive first-run setup for Horus").option("-y, --yes", "Non-interactive mode (use defaults + env vars)").option("--runtime <runtime>", "Container runtime to use: docker or podman (non-interactive only)").option("--data-dir <path>", "Data directory path").option("--repos-path <path>", "Host repos path for Forge scanning").action(async (opts) => {
388
405
  console.log("");
389
406
  console.log(chalk.bold("Horus Setup"));
390
407
  console.log(chalk.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
@@ -403,17 +420,46 @@ var setupCommand = new Command("setup").description("Interactive first-run setup
403
420
  }
404
421
  }
405
422
  }
406
- const runtimeSpinner = ora("Detecting container runtime...").start();
407
- let runtime;
408
- try {
409
- runtime = await detectRuntime();
410
- runtimeSpinner.succeed(`Detected ${chalk.cyan(runtime.name)}`);
411
- } catch (error) {
412
- runtimeSpinner.fail("No container runtime found");
423
+ const checkSpinner = ora("Checking for container runtimes...").start();
424
+ const [hasDocker, hasPodman] = await Promise.all([
425
+ checkRuntime("docker"),
426
+ checkRuntime("podman")
427
+ ]);
428
+ checkSpinner.stop();
429
+ const available = [
430
+ ...hasDocker ? ["docker"] : [],
431
+ ...hasPodman ? ["podman"] : []
432
+ ];
433
+ if (available.length === 0) {
434
+ console.log(chalk.red("No container runtime found."));
413
435
  console.log("");
414
- console.log(error.message);
436
+ console.log("Horus requires Docker or Podman with the Compose plugin.");
437
+ console.log("");
438
+ console.log("Install one of:");
439
+ console.log(" Docker Desktop: https://www.docker.com/products/docker-desktop/");
440
+ console.log(" Podman Desktop: https://podman-desktop.io/");
415
441
  process.exit(1);
416
442
  }
443
+ let selectedRuntime;
444
+ if (opts.yes) {
445
+ const requested = opts.runtime;
446
+ if (requested && !available.includes(requested)) {
447
+ console.log(chalk.red(`Requested runtime "${requested}" is not installed.`));
448
+ console.log(chalk.dim(`Available: ${available.join(", ")}`));
449
+ process.exit(1);
450
+ }
451
+ selectedRuntime = requested ?? available[0];
452
+ console.log(`Using ${chalk.cyan(selectedRuntime)}`);
453
+ } else {
454
+ selectedRuntime = await select({
455
+ message: "Which container runtime would you like to use?",
456
+ choices: available.map((r) => ({
457
+ value: r,
458
+ name: r === "docker" ? "Docker" : "Podman"
459
+ }))
460
+ });
461
+ }
462
+ const runtime = await detectRuntime(selectedRuntime);
417
463
  let config;
418
464
  if (opts.yes) {
419
465
  config = {
@@ -1013,7 +1059,7 @@ var connectCommand = new Command6("connect").description("Configure Claude/Curso
1013
1059
  import { Command as Command7 } from "commander";
1014
1060
  import chalk7 from "chalk";
1015
1061
  import ora6 from "ora";
1016
- import { select, confirm as confirm3 } from "@inquirer/prompts";
1062
+ import { select as select2, confirm as confirm3 } from "@inquirer/prompts";
1017
1063
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, readdirSync, existsSync as existsSync4 } from "fs";
1018
1064
  import { join as join4 } from "path";
1019
1065
  import { createHash } from "crypto";
@@ -1110,7 +1156,7 @@ var updateCommand = new Command7("update").description("Update Horus to the late
1110
1156
  name: `${snapshot.timestamp} (images: ${Object.keys(snapshot.images).length})`,
1111
1157
  value: i
1112
1158
  }));
1113
- const idx = await select({
1159
+ const idx = await select2({
1114
1160
  message: "Select snapshot to restore:",
1115
1161
  choices
1116
1162
  });
@@ -1289,7 +1335,7 @@ function colorMessage(status, msg) {
1289
1335
  return chalk8.red(msg);
1290
1336
  }
1291
1337
  }
1292
- async function checkRuntime() {
1338
+ async function checkRuntime2() {
1293
1339
  try {
1294
1340
  execSync("docker info", { stdio: "ignore" });
1295
1341
  return { status: "pass", label: "Runtime", message: "Docker is running" };
@@ -1477,7 +1523,7 @@ var doctorCommand = new Command8("doctor").description("Diagnose common Horus is
1477
1523
  console.log(chalk8.bold("Horus Doctor"));
1478
1524
  console.log(chalk8.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1479
1525
  const allResults = [];
1480
- allResults.push(await checkRuntime());
1526
+ allResults.push(await checkRuntime2());
1481
1527
  allResults.push(await checkCompose());
1482
1528
  allResults.push(checkConfig());
1483
1529
  allResults.push(checkComposeFile());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkhera30/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "CLI for managing the Horus AI development stack",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,20 +9,22 @@
9
9
  "scripts": {
10
10
  "build": "tsup src/index.ts --format esm --dts",
11
11
  "dev": "tsup src/index.ts --format esm --watch",
12
- "typecheck": "tsc --noEmit"
12
+ "typecheck": "tsc --noEmit",
13
+ "test": "vitest run"
13
14
  },
14
15
  "dependencies": {
15
16
  "@inquirer/prompts": "^7.0.0",
17
+ "chalk": "^5.0.0",
16
18
  "commander": "^12.0.0",
17
19
  "execa": "^8.0.0",
18
- "chalk": "^5.0.0",
19
- "yaml": "^2.0.0",
20
- "ora": "^8.0.0"
20
+ "ora": "^8.0.0",
21
+ "yaml": "^2.0.0"
21
22
  },
22
23
  "devDependencies": {
23
- "typescript": "^5.4.0",
24
+ "@types/node": "^20.0.0",
24
25
  "tsup": "^8.0.0",
25
- "@types/node": "^20.0.0"
26
+ "typescript": "^5.4.0",
27
+ "vitest": "^4.0.18"
26
28
  },
27
29
  "engines": {
28
30
  "node": ">=18"