@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.
- package/compose/docker-compose.yml +1 -1
- package/dist/index.js +48 -57
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
2
2
|
# Horus — Production Docker Compose
|
|
3
|
-
# Managed by @
|
|
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 {
|
|
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
|
|
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 =
|
|
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("--
|
|
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
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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(
|
|
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
|
|
429
|
+
let selectedRuntime;
|
|
426
430
|
if (opts.yes) {
|
|
427
|
-
const
|
|
428
|
-
if (!
|
|
429
|
-
console.log(chalk.red(
|
|
430
|
-
console.log(chalk.dim(
|
|
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 === "
|
|
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
|
|
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
|
|
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
|
|
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) {
|