@arkhera30/cli 0.1.0 → 0.1.2

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.
@@ -1,6 +1,6 @@
1
1
  # ─────────────────────────────────────────────────────────────────────────────
2
2
  # Horus — Production Docker Compose
3
- # Managed by @horus/cli. Do not edit manually.
3
+ # Managed by @arkhera30/cli. Do not edit manually.
4
4
  #
5
5
  # This file uses pre-built images from ghcr.io. Once CI pipelines are set up,
6
6
  # images will be published on every release. Until then, these are placeholder
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 { password, 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";
@@ -48,7 +48,6 @@ var CONFIG_VERSION = "1.0";
48
48
  function defaultConfig() {
49
49
  return {
50
50
  version: CONFIG_VERSION,
51
- api_key: "",
52
51
  data_dir: DEFAULT_DATA_DIR,
53
52
  runtime: "docker",
54
53
  ports: { ...DEFAULT_PORTS },
@@ -72,7 +71,6 @@ function loadConfig() {
72
71
  const defaults = defaultConfig();
73
72
  return {
74
73
  version: parsed.version ?? defaults.version,
75
- api_key: parsed.api_key ?? defaults.api_key,
76
74
  data_dir: parsed.data_dir ?? defaults.data_dir,
77
75
  runtime: parsed.runtime ?? defaults.runtime,
78
76
  ports: {
@@ -136,7 +134,6 @@ function writeEnvFile(config) {
136
134
  writeFileSync(ENV_PATH, content, "utf-8");
137
135
  }
138
136
  var CONFIG_KEYS = [
139
- "api-key",
140
137
  "data-dir",
141
138
  "host-repos-path",
142
139
  "runtime",
@@ -148,8 +145,6 @@ var CONFIG_KEYS = [
148
145
  ];
149
146
  function getConfigValue(config, key) {
150
147
  switch (key) {
151
- case "api-key":
152
- return config.api_key;
153
148
  case "data-dir":
154
149
  return config.data_dir;
155
150
  case "host-repos-path":
@@ -171,9 +166,6 @@ function getConfigValue(config, key) {
171
166
  function setConfigValue(config, key, value) {
172
167
  const updated = { ...config };
173
168
  switch (key) {
174
- case "api-key":
175
- updated.api_key = value;
176
- break;
177
169
  case "data-dir":
178
170
  updated.data_dir = value;
179
171
  break;
@@ -269,6 +261,9 @@ function createRuntime(name) {
269
261
  }
270
262
  };
271
263
  }
264
+ async function checkRuntime(name) {
265
+ return tryCommand(name, ["compose", "version"]);
266
+ }
272
267
  async function detectRuntime(preferred) {
273
268
  if (preferred) {
274
269
  const hasPreferred = await tryCommand(preferred, ["compose", "version"]);
@@ -362,10 +357,10 @@ Run 'docker compose logs' from ~/.horus/ to investigate.`
362
357
 
363
358
  // src/lib/compose.ts
364
359
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
365
- import { join as join2, dirname as dirname2 } from "path";
360
+ import { join as join2, dirname } from "path";
366
361
  import { fileURLToPath } from "url";
367
362
  var __filename = fileURLToPath(import.meta.url);
368
- var __dirname = dirname2(__filename);
363
+ var __dirname = dirname(__filename);
369
364
  function getBundledComposePath() {
370
365
  const candidates = [
371
366
  join2(__dirname, "..", "..", "compose", "docker-compose.yml"),
@@ -392,7 +387,7 @@ function installComposeFile() {
392
387
  }
393
388
 
394
389
  // src/commands/setup.ts
395
- var setupCommand = new Command("setup").description("Interactive first-run setup for Horus").option("-y, --yes", "Non-interactive mode (use defaults + env vars)").option("--api-key <key>", "Anthropic API key").option("--data-dir <path>", "Data directory path").option("--repos-path <path>", "Host repos path for Forge scanning").action(async (opts) => {
390
+ 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) => {
396
391
  console.log("");
397
392
  console.log(chalk.bold("Horus Setup"));
398
393
  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"));
@@ -411,42 +406,55 @@ var setupCommand = new Command("setup").description("Interactive first-run setup
411
406
  }
412
407
  }
413
408
  }
414
- const runtimeSpinner = ora("Detecting container runtime...").start();
415
- let runtime;
416
- try {
417
- runtime = await detectRuntime();
418
- runtimeSpinner.succeed(`Detected ${chalk.cyan(runtime.name)}`);
419
- } catch (error) {
420
- runtimeSpinner.fail("No container runtime found");
409
+ const checkSpinner = ora("Checking for container runtimes...").start();
410
+ const [hasDocker, hasPodman] = await Promise.all([
411
+ checkRuntime("docker"),
412
+ checkRuntime("podman")
413
+ ]);
414
+ checkSpinner.stop();
415
+ const available = [
416
+ ...hasDocker ? ["docker"] : [],
417
+ ...hasPodman ? ["podman"] : []
418
+ ];
419
+ if (available.length === 0) {
420
+ console.log(chalk.red("No container runtime found."));
421
421
  console.log("");
422
- console.log(error.message);
422
+ console.log("Horus requires Docker or Podman with the Compose plugin.");
423
+ console.log("");
424
+ console.log("Install one of:");
425
+ console.log(" Docker Desktop: https://www.docker.com/products/docker-desktop/");
426
+ console.log(" Podman Desktop: https://podman-desktop.io/");
423
427
  process.exit(1);
424
428
  }
425
- let config;
429
+ let selectedRuntime;
426
430
  if (opts.yes) {
427
- const apiKey = opts.apiKey || process.env.HORUS_API_KEY || "";
428
- if (!apiKey) {
429
- console.log(chalk.red("Error: API key is required."));
430
- console.log(chalk.dim("Set HORUS_API_KEY env var or use --api-key flag."));
431
+ const requested = opts.runtime;
432
+ if (requested && !available.includes(requested)) {
433
+ console.log(chalk.red(`Requested runtime "${requested}" is not installed.`));
434
+ console.log(chalk.dim(`Available: ${available.join(", ")}`));
431
435
  process.exit(1);
432
436
  }
437
+ selectedRuntime = requested ?? available[0];
438
+ console.log(`Using ${chalk.cyan(selectedRuntime)}`);
439
+ } else {
440
+ selectedRuntime = await select({
441
+ message: "Which container runtime would you like to use?",
442
+ choices: available.map((r) => ({
443
+ value: r,
444
+ name: r === "docker" ? "Docker" : "Podman"
445
+ }))
446
+ });
447
+ }
448
+ const runtime = await detectRuntime(selectedRuntime);
449
+ let config;
450
+ if (opts.yes) {
433
451
  config = {
434
452
  ...defaultConfig(),
435
- api_key: apiKey,
436
453
  runtime: runtime.name,
437
454
  data_dir: opts.dataDir || DEFAULT_DATA_DIR,
438
455
  host_repos_path: opts.reposPath || ""
439
456
  };
440
457
  } else {
441
- const api_key = await password({
442
- message: "Anthropic API key:",
443
- mask: "*",
444
- validate: (val) => {
445
- if (!val) return "API key is required";
446
- if (!val.startsWith("sk-ant-")) return 'API key must start with "sk-ant-"';
447
- return true;
448
- }
449
- });
450
458
  const data_dir = await input({
451
459
  message: "Data directory:",
452
460
  default: DEFAULT_DATA_DIR
@@ -486,7 +494,6 @@ var setupCommand = new Command("setup").description("Interactive first-run setup
486
494
  }
487
495
  config = {
488
496
  ...defaultConfig(),
489
- api_key,
490
497
  data_dir,
491
498
  host_repos_path,
492
499
  runtime: runtime.name,
@@ -771,7 +778,6 @@ var configCommand = new Command5("config").description("View or modify Horus con
771
778
  console.log(chalk5.bold("Horus Configuration"));
772
779
  console.log(chalk5.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"));
773
780
  console.log(` ${chalk5.bold("version:")} ${config.version}`);
774
- console.log(` ${chalk5.bold("api-key:")} ${maskApiKey(config.api_key)}`);
775
781
  console.log(` ${chalk5.bold("data-dir:")} ${config.data_dir}`);
776
782
  console.log(` ${chalk5.bold("runtime:")} ${config.runtime}`);
777
783
  console.log(` ${chalk5.bold("host-repos-path:")} ${config.host_repos_path || chalk5.dim("(not set)")}`);
@@ -805,7 +811,7 @@ configCommand.command("get <key>").description("Get a configuration value").acti
805
811
  }
806
812
  const config = loadConfig();
807
813
  const value = getConfigValue(config, key);
808
- if (key === "api-key" || key === "github-token") {
814
+ if (key === "github-token") {
809
815
  console.log(maskApiKey(value));
810
816
  } else {
811
817
  console.log(value || "");
@@ -833,7 +839,6 @@ configCommand.command("set <key> <value>").description("Set a configuration valu
833
839
  writeEnvFile(config);
834
840
  console.log(chalk5.green(`Set ${key} and regenerated .env file.`));
835
841
  const needsRestart = [
836
- "api-key",
837
842
  "data-dir",
838
843
  "host-repos-path",
839
844
  "runtime",
@@ -1040,7 +1045,7 @@ var connectCommand = new Command6("connect").description("Configure Claude/Curso
1040
1045
  import { Command as Command7 } from "commander";
1041
1046
  import chalk7 from "chalk";
1042
1047
  import ora6 from "ora";
1043
- import { select, confirm as confirm3 } from "@inquirer/prompts";
1048
+ import { select as select2, confirm as confirm3 } from "@inquirer/prompts";
1044
1049
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, readdirSync, existsSync as existsSync4 } from "fs";
1045
1050
  import { join as join4 } from "path";
1046
1051
  import { createHash } from "crypto";
@@ -1137,7 +1142,7 @@ var updateCommand = new Command7("update").description("Update Horus to the late
1137
1142
  name: `${snapshot.timestamp} (images: ${Object.keys(snapshot.images).length})`,
1138
1143
  value: i
1139
1144
  }));
1140
- const idx = await select({
1145
+ const idx = await select2({
1141
1146
  message: "Select snapshot to restore:",
1142
1147
  choices
1143
1148
  });
@@ -1316,7 +1321,7 @@ function colorMessage(status, msg) {
1316
1321
  return chalk8.red(msg);
1317
1322
  }
1318
1323
  }
1319
- async function checkRuntime() {
1324
+ async function checkRuntime2() {
1320
1325
  try {
1321
1326
  execSync("docker info", { stdio: "ignore" });
1322
1327
  return { status: "pass", label: "Runtime", message: "Docker is running" };
@@ -1499,23 +1504,12 @@ async function checkServices(runtime) {
1499
1504
  }
1500
1505
  return results;
1501
1506
  }
1502
- function checkApiKey(config) {
1503
- if (config.api_key && config.api_key.length > 0) {
1504
- return { status: "pass", label: "API key", message: "API key is configured" };
1505
- }
1506
- return {
1507
- status: "warn",
1508
- label: "API key",
1509
- message: "API key is not set",
1510
- hint: "Run: horus config set api-key <your-key>"
1511
- };
1512
- }
1513
1507
  var doctorCommand = new Command8("doctor").description("Diagnose common Horus issues").action(async () => {
1514
1508
  console.log("");
1515
1509
  console.log(chalk8.bold("Horus Doctor"));
1516
1510
  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"));
1517
1511
  const allResults = [];
1518
- allResults.push(await checkRuntime());
1512
+ allResults.push(await checkRuntime2());
1519
1513
  allResults.push(await checkCompose());
1520
1514
  allResults.push(checkConfig());
1521
1515
  allResults.push(checkComposeFile());
@@ -1528,9 +1522,6 @@ var doctorCommand = new Command8("doctor").description("Diagnose common Horus is
1528
1522
  allResults.push(checkPort(ports.forge, "Forge"));
1529
1523
  allResults.push(checkDataDir(dataDir));
1530
1524
  allResults.push(checkDiskSpace(dataDir));
1531
- if (config) {
1532
- allResults.push(checkApiKey(config));
1533
- }
1534
1525
  const runtimeOk = allResults[0].status !== "fail";
1535
1526
  const composeOk = allResults[1].status !== "fail";
1536
1527
  if (runtimeOk && composeOk) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkhera30/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "CLI for managing the Horus AI development stack",
5
5
  "type": "module",
6
6
  "bin": {