codeharbor 0.1.4 → 0.1.6
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/README.md +23 -2
- package/dist/cli.js +323 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,24 @@ curl -fsSL https://raw.githubusercontent.com/biglone/CodeHarbor/main/scripts/ins
|
|
|
64
64
|
--matrix-access-token 'your-token'
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
Install first, then enable systemd service with one command:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
sudo codeharbor service install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Install + enable main and admin services:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
sudo codeharbor service install --with-admin
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Remove installed services:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
sudo codeharbor service uninstall --with-admin
|
|
83
|
+
```
|
|
84
|
+
|
|
67
85
|
Enable Admin service at install time:
|
|
68
86
|
|
|
69
87
|
```bash
|
|
@@ -83,8 +101,9 @@ Run local script with custom options:
|
|
|
83
101
|
|
|
84
102
|
Runtime home behavior:
|
|
85
103
|
|
|
86
|
-
- By default, all `codeharbor` commands use
|
|
87
|
-
-
|
|
104
|
+
- By default, all `codeharbor` commands use `~/.codeharbor` for `.env` and relative data paths.
|
|
105
|
+
- Backward compatibility: if `/opt/codeharbor/.env` already exists, it continues to be used automatically.
|
|
106
|
+
- No manual `cd` is required after installation.
|
|
88
107
|
- To use a custom runtime directory, set `CODEHARBOR_HOME` (for example `export CODEHARBOR_HOME=/srv/codeharbor`).
|
|
89
108
|
|
|
90
109
|
Install directly from GitHub:
|
|
@@ -228,6 +247,8 @@ It documents:
|
|
|
228
247
|
- `codeharbor start`: start service
|
|
229
248
|
- `codeharbor doctor`: check `codex` and Matrix connectivity
|
|
230
249
|
- `codeharbor admin serve`: start admin UI + config API server
|
|
250
|
+
- `codeharbor service install`: install/enable systemd unit(s) after npm install (supports `--with-admin`)
|
|
251
|
+
- `codeharbor service uninstall`: remove installed systemd unit(s) (supports `--with-admin`)
|
|
231
252
|
- `codeharbor config export`: export current config snapshot as JSON
|
|
232
253
|
- `codeharbor config import <file>`: import config snapshot JSON (supports `--dry-run`)
|
|
233
254
|
- `npm run changelog:check`: validate `CHANGELOG.md` has notes for current package version
|
package/dist/cli.js
CHANGED
|
@@ -24,8 +24,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var import_node_fs11 = __toESM(require("fs"));
|
|
28
|
+
var import_node_path12 = __toESM(require("path"));
|
|
29
29
|
var import_commander = require("commander");
|
|
30
30
|
|
|
31
31
|
// src/app.ts
|
|
@@ -4911,21 +4911,288 @@ function readEnv(env, key) {
|
|
|
4911
4911
|
}
|
|
4912
4912
|
|
|
4913
4913
|
// src/runtime-home.ts
|
|
4914
|
+
var import_node_fs9 = __toESM(require("fs"));
|
|
4915
|
+
var import_node_os2 = __toESM(require("os"));
|
|
4914
4916
|
var import_node_path10 = __toESM(require("path"));
|
|
4915
|
-
var
|
|
4917
|
+
var LEGACY_RUNTIME_HOME = "/opt/codeharbor";
|
|
4918
|
+
var USER_RUNTIME_HOME_DIR = ".codeharbor";
|
|
4919
|
+
var DEFAULT_RUNTIME_HOME = import_node_path10.default.resolve(import_node_os2.default.homedir(), USER_RUNTIME_HOME_DIR);
|
|
4916
4920
|
var RUNTIME_HOME_ENV_KEY = "CODEHARBOR_HOME";
|
|
4917
4921
|
function resolveRuntimeHome(env = process.env) {
|
|
4918
4922
|
const configured = env[RUNTIME_HOME_ENV_KEY]?.trim();
|
|
4919
4923
|
if (configured) {
|
|
4920
4924
|
return import_node_path10.default.resolve(configured);
|
|
4921
4925
|
}
|
|
4922
|
-
|
|
4926
|
+
const legacyEnvPath = import_node_path10.default.resolve(LEGACY_RUNTIME_HOME, ".env");
|
|
4927
|
+
if (import_node_fs9.default.existsSync(legacyEnvPath)) {
|
|
4928
|
+
return LEGACY_RUNTIME_HOME;
|
|
4929
|
+
}
|
|
4930
|
+
return resolveUserRuntimeHome(env);
|
|
4931
|
+
}
|
|
4932
|
+
function resolveUserRuntimeHome(env = process.env) {
|
|
4933
|
+
const home = env.HOME?.trim() || import_node_os2.default.homedir();
|
|
4934
|
+
return import_node_path10.default.resolve(home, USER_RUNTIME_HOME_DIR);
|
|
4935
|
+
}
|
|
4936
|
+
|
|
4937
|
+
// src/service-manager.ts
|
|
4938
|
+
var import_node_child_process5 = require("child_process");
|
|
4939
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
4940
|
+
var import_node_os3 = __toESM(require("os"));
|
|
4941
|
+
var import_node_path11 = __toESM(require("path"));
|
|
4942
|
+
var SYSTEMD_DIR = "/etc/systemd/system";
|
|
4943
|
+
var MAIN_SERVICE_NAME = "codeharbor.service";
|
|
4944
|
+
var ADMIN_SERVICE_NAME = "codeharbor-admin.service";
|
|
4945
|
+
function resolveDefaultRunUser(env = process.env) {
|
|
4946
|
+
const sudoUser = env.SUDO_USER?.trim();
|
|
4947
|
+
if (sudoUser) {
|
|
4948
|
+
return sudoUser;
|
|
4949
|
+
}
|
|
4950
|
+
const user = env.USER?.trim();
|
|
4951
|
+
if (user) {
|
|
4952
|
+
return user;
|
|
4953
|
+
}
|
|
4954
|
+
try {
|
|
4955
|
+
return import_node_os3.default.userInfo().username;
|
|
4956
|
+
} catch {
|
|
4957
|
+
return "root";
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
function resolveRuntimeHomeForUser(runUser, env = process.env, explicitRuntimeHome) {
|
|
4961
|
+
const configuredRuntimeHome = explicitRuntimeHome?.trim() || env[RUNTIME_HOME_ENV_KEY]?.trim();
|
|
4962
|
+
if (configuredRuntimeHome) {
|
|
4963
|
+
return import_node_path11.default.resolve(configuredRuntimeHome);
|
|
4964
|
+
}
|
|
4965
|
+
const userHome = resolveUserHome(runUser);
|
|
4966
|
+
if (userHome) {
|
|
4967
|
+
return import_node_path11.default.resolve(userHome, USER_RUNTIME_HOME_DIR);
|
|
4968
|
+
}
|
|
4969
|
+
return import_node_path11.default.resolve(import_node_os3.default.homedir(), USER_RUNTIME_HOME_DIR);
|
|
4970
|
+
}
|
|
4971
|
+
function buildMainServiceUnit(options) {
|
|
4972
|
+
validateUnitOptions(options);
|
|
4973
|
+
const runtimeHome2 = import_node_path11.default.resolve(options.runtimeHome);
|
|
4974
|
+
return [
|
|
4975
|
+
"[Unit]",
|
|
4976
|
+
"Description=CodeHarbor main service",
|
|
4977
|
+
"After=network-online.target",
|
|
4978
|
+
"Wants=network-online.target",
|
|
4979
|
+
"",
|
|
4980
|
+
"[Service]",
|
|
4981
|
+
"Type=simple",
|
|
4982
|
+
`User=${options.runUser}`,
|
|
4983
|
+
`WorkingDirectory=${runtimeHome2}`,
|
|
4984
|
+
`Environment=CODEHARBOR_HOME=${runtimeHome2}`,
|
|
4985
|
+
`ExecStart=${import_node_path11.default.resolve(options.nodeBinPath)} ${import_node_path11.default.resolve(options.cliScriptPath)} start`,
|
|
4986
|
+
"Restart=always",
|
|
4987
|
+
"RestartSec=3",
|
|
4988
|
+
"NoNewPrivileges=true",
|
|
4989
|
+
"PrivateTmp=true",
|
|
4990
|
+
"ProtectSystem=full",
|
|
4991
|
+
"ProtectHome=true",
|
|
4992
|
+
`ReadWritePaths=${runtimeHome2}`,
|
|
4993
|
+
"",
|
|
4994
|
+
"[Install]",
|
|
4995
|
+
"WantedBy=multi-user.target",
|
|
4996
|
+
""
|
|
4997
|
+
].join("\n");
|
|
4998
|
+
}
|
|
4999
|
+
function buildAdminServiceUnit(options) {
|
|
5000
|
+
validateUnitOptions(options);
|
|
5001
|
+
const runtimeHome2 = import_node_path11.default.resolve(options.runtimeHome);
|
|
5002
|
+
return [
|
|
5003
|
+
"[Unit]",
|
|
5004
|
+
"Description=CodeHarbor admin service",
|
|
5005
|
+
"After=network-online.target",
|
|
5006
|
+
"Wants=network-online.target",
|
|
5007
|
+
"",
|
|
5008
|
+
"[Service]",
|
|
5009
|
+
"Type=simple",
|
|
5010
|
+
`User=${options.runUser}`,
|
|
5011
|
+
`WorkingDirectory=${runtimeHome2}`,
|
|
5012
|
+
`Environment=CODEHARBOR_HOME=${runtimeHome2}`,
|
|
5013
|
+
`ExecStart=${import_node_path11.default.resolve(options.nodeBinPath)} ${import_node_path11.default.resolve(options.cliScriptPath)} admin serve`,
|
|
5014
|
+
"Restart=always",
|
|
5015
|
+
"RestartSec=3",
|
|
5016
|
+
"NoNewPrivileges=true",
|
|
5017
|
+
"PrivateTmp=true",
|
|
5018
|
+
"ProtectSystem=full",
|
|
5019
|
+
"ProtectHome=true",
|
|
5020
|
+
`ReadWritePaths=${runtimeHome2}`,
|
|
5021
|
+
"",
|
|
5022
|
+
"[Install]",
|
|
5023
|
+
"WantedBy=multi-user.target",
|
|
5024
|
+
""
|
|
5025
|
+
].join("\n");
|
|
5026
|
+
}
|
|
5027
|
+
function installSystemdServices(options) {
|
|
5028
|
+
assertLinuxWithSystemd();
|
|
5029
|
+
assertRootPrivileges();
|
|
5030
|
+
const output = options.output ?? process.stdout;
|
|
5031
|
+
const runUser = options.runUser.trim();
|
|
5032
|
+
const runtimeHome2 = import_node_path11.default.resolve(options.runtimeHome);
|
|
5033
|
+
validateSimpleValue(runUser, "runUser");
|
|
5034
|
+
validateSimpleValue(runtimeHome2, "runtimeHome");
|
|
5035
|
+
validateSimpleValue(options.nodeBinPath, "nodeBinPath");
|
|
5036
|
+
validateSimpleValue(options.cliScriptPath, "cliScriptPath");
|
|
5037
|
+
ensureUserExists(runUser);
|
|
5038
|
+
const runGroup = resolveUserGroup(runUser);
|
|
5039
|
+
import_node_fs10.default.mkdirSync(runtimeHome2, { recursive: true });
|
|
5040
|
+
runCommand("chown", ["-R", `${runUser}:${runGroup}`, runtimeHome2]);
|
|
5041
|
+
const mainPath = import_node_path11.default.join(SYSTEMD_DIR, MAIN_SERVICE_NAME);
|
|
5042
|
+
const adminPath = import_node_path11.default.join(SYSTEMD_DIR, ADMIN_SERVICE_NAME);
|
|
5043
|
+
const unitOptions = {
|
|
5044
|
+
runUser,
|
|
5045
|
+
runtimeHome: runtimeHome2,
|
|
5046
|
+
nodeBinPath: options.nodeBinPath,
|
|
5047
|
+
cliScriptPath: options.cliScriptPath
|
|
5048
|
+
};
|
|
5049
|
+
import_node_fs10.default.writeFileSync(mainPath, buildMainServiceUnit(unitOptions), "utf8");
|
|
5050
|
+
if (options.installAdmin) {
|
|
5051
|
+
import_node_fs10.default.writeFileSync(adminPath, buildAdminServiceUnit(unitOptions), "utf8");
|
|
5052
|
+
}
|
|
5053
|
+
runSystemctl(["daemon-reload"]);
|
|
5054
|
+
if (options.startNow) {
|
|
5055
|
+
runSystemctl(["enable", "--now", MAIN_SERVICE_NAME]);
|
|
5056
|
+
if (options.installAdmin) {
|
|
5057
|
+
runSystemctl(["enable", "--now", ADMIN_SERVICE_NAME]);
|
|
5058
|
+
}
|
|
5059
|
+
} else {
|
|
5060
|
+
runSystemctl(["enable", MAIN_SERVICE_NAME]);
|
|
5061
|
+
if (options.installAdmin) {
|
|
5062
|
+
runSystemctl(["enable", ADMIN_SERVICE_NAME]);
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
output.write(`Installed systemd unit: ${mainPath}
|
|
5066
|
+
`);
|
|
5067
|
+
if (options.installAdmin) {
|
|
5068
|
+
output.write(`Installed systemd unit: ${adminPath}
|
|
5069
|
+
`);
|
|
5070
|
+
}
|
|
5071
|
+
output.write("Done. Check status with: systemctl status codeharbor --no-pager\n");
|
|
5072
|
+
}
|
|
5073
|
+
function uninstallSystemdServices(options) {
|
|
5074
|
+
assertLinuxWithSystemd();
|
|
5075
|
+
assertRootPrivileges();
|
|
5076
|
+
const output = options.output ?? process.stdout;
|
|
5077
|
+
const mainPath = import_node_path11.default.join(SYSTEMD_DIR, MAIN_SERVICE_NAME);
|
|
5078
|
+
const adminPath = import_node_path11.default.join(SYSTEMD_DIR, ADMIN_SERVICE_NAME);
|
|
5079
|
+
stopAndDisableIfPresent(MAIN_SERVICE_NAME);
|
|
5080
|
+
if (import_node_fs10.default.existsSync(mainPath)) {
|
|
5081
|
+
import_node_fs10.default.unlinkSync(mainPath);
|
|
5082
|
+
}
|
|
5083
|
+
if (options.removeAdmin) {
|
|
5084
|
+
stopAndDisableIfPresent(ADMIN_SERVICE_NAME);
|
|
5085
|
+
if (import_node_fs10.default.existsSync(adminPath)) {
|
|
5086
|
+
import_node_fs10.default.unlinkSync(adminPath);
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
runSystemctl(["daemon-reload"]);
|
|
5090
|
+
runSystemctlIgnoreFailure(["reset-failed"]);
|
|
5091
|
+
output.write(`Removed systemd unit: ${mainPath}
|
|
5092
|
+
`);
|
|
5093
|
+
if (options.removeAdmin) {
|
|
5094
|
+
output.write(`Removed systemd unit: ${adminPath}
|
|
5095
|
+
`);
|
|
5096
|
+
}
|
|
5097
|
+
output.write("Done.\n");
|
|
5098
|
+
}
|
|
5099
|
+
function resolveUserHome(runUser) {
|
|
5100
|
+
try {
|
|
5101
|
+
const passwdRaw = import_node_fs10.default.readFileSync("/etc/passwd", "utf8");
|
|
5102
|
+
const line = passwdRaw.split(/\r?\n/).find((item) => item.startsWith(`${runUser}:`));
|
|
5103
|
+
if (!line) {
|
|
5104
|
+
return null;
|
|
5105
|
+
}
|
|
5106
|
+
const fields = line.split(":");
|
|
5107
|
+
return fields[5] ? fields[5].trim() : null;
|
|
5108
|
+
} catch {
|
|
5109
|
+
return null;
|
|
5110
|
+
}
|
|
5111
|
+
}
|
|
5112
|
+
function validateUnitOptions(options) {
|
|
5113
|
+
validateSimpleValue(options.runUser, "runUser");
|
|
5114
|
+
validateSimpleValue(options.runtimeHome, "runtimeHome");
|
|
5115
|
+
validateSimpleValue(options.nodeBinPath, "nodeBinPath");
|
|
5116
|
+
validateSimpleValue(options.cliScriptPath, "cliScriptPath");
|
|
5117
|
+
}
|
|
5118
|
+
function validateSimpleValue(value, key) {
|
|
5119
|
+
if (!value.trim()) {
|
|
5120
|
+
throw new Error(`${key} cannot be empty.`);
|
|
5121
|
+
}
|
|
5122
|
+
if (/[\r\n]/.test(value)) {
|
|
5123
|
+
throw new Error(`${key} contains invalid newline characters.`);
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
function assertLinuxWithSystemd() {
|
|
5127
|
+
if (process.platform !== "linux") {
|
|
5128
|
+
throw new Error("Systemd service install only supports Linux.");
|
|
5129
|
+
}
|
|
5130
|
+
try {
|
|
5131
|
+
(0, import_node_child_process5.execFileSync)("systemctl", ["--version"], { stdio: "ignore" });
|
|
5132
|
+
} catch {
|
|
5133
|
+
throw new Error("systemctl is required but not found.");
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
5136
|
+
function assertRootPrivileges() {
|
|
5137
|
+
if (typeof process.getuid !== "function") {
|
|
5138
|
+
return;
|
|
5139
|
+
}
|
|
5140
|
+
if (process.getuid() !== 0) {
|
|
5141
|
+
throw new Error("Root privileges are required. Run with sudo.");
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
function ensureUserExists(runUser) {
|
|
5145
|
+
runCommand("id", ["-u", runUser]);
|
|
5146
|
+
}
|
|
5147
|
+
function resolveUserGroup(runUser) {
|
|
5148
|
+
return runCommand("id", ["-gn", runUser]).trim();
|
|
5149
|
+
}
|
|
5150
|
+
function runSystemctl(args) {
|
|
5151
|
+
runCommand("systemctl", args);
|
|
5152
|
+
}
|
|
5153
|
+
function stopAndDisableIfPresent(unitName) {
|
|
5154
|
+
runSystemctlIgnoreFailure(["disable", "--now", unitName]);
|
|
5155
|
+
}
|
|
5156
|
+
function runSystemctlIgnoreFailure(args) {
|
|
5157
|
+
try {
|
|
5158
|
+
runCommand("systemctl", args);
|
|
5159
|
+
} catch {
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
function runCommand(file, args) {
|
|
5163
|
+
try {
|
|
5164
|
+
return (0, import_node_child_process5.execFileSync)(file, args, {
|
|
5165
|
+
encoding: "utf8",
|
|
5166
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
5167
|
+
});
|
|
5168
|
+
} catch (error) {
|
|
5169
|
+
throw new Error(formatCommandError(file, args, error), { cause: error });
|
|
5170
|
+
}
|
|
5171
|
+
}
|
|
5172
|
+
function formatCommandError(file, args, error) {
|
|
5173
|
+
const command = `${file} ${args.join(" ")}`.trim();
|
|
5174
|
+
if (error && typeof error === "object") {
|
|
5175
|
+
const maybeError = error;
|
|
5176
|
+
const stderr = bufferToTrimmedString(maybeError.stderr);
|
|
5177
|
+
const stdout = bufferToTrimmedString(maybeError.stdout);
|
|
5178
|
+
const details = stderr || stdout || maybeError.message || "command failed";
|
|
5179
|
+
return `Command failed: ${command}. ${details}`;
|
|
5180
|
+
}
|
|
5181
|
+
return `Command failed: ${command}. ${String(error)}`;
|
|
5182
|
+
}
|
|
5183
|
+
function bufferToTrimmedString(value) {
|
|
5184
|
+
if (!value) {
|
|
5185
|
+
return "";
|
|
5186
|
+
}
|
|
5187
|
+
const text = typeof value === "string" ? value : value.toString("utf8");
|
|
5188
|
+
return text.trim();
|
|
4923
5189
|
}
|
|
4924
5190
|
|
|
4925
5191
|
// src/cli.ts
|
|
4926
5192
|
var runtimeHome = null;
|
|
5193
|
+
var cliVersion = resolveCliVersion();
|
|
4927
5194
|
var program = new import_commander.Command();
|
|
4928
|
-
program.name("codeharbor").description("Instant-messaging bridge for Codex CLI sessions").version(
|
|
5195
|
+
program.name("codeharbor").description("Instant-messaging bridge for Codex CLI sessions").version(cliVersion);
|
|
4929
5196
|
program.command("init").description("Create or update .env via guided prompts").option("-f, --force", "overwrite existing .env without confirmation").action(async (options) => {
|
|
4930
5197
|
const home = ensureRuntimeHomeOrExit();
|
|
4931
5198
|
await runInitCommand({ force: options.force ?? false, cwd: home });
|
|
@@ -4961,6 +5228,7 @@ program.command("doctor").description("Check codex and matrix connectivity").act
|
|
|
4961
5228
|
});
|
|
4962
5229
|
var admin = program.command("admin").description("Admin utilities");
|
|
4963
5230
|
var configCommand = program.command("config").description("Config snapshot utilities");
|
|
5231
|
+
var serviceCommand = program.command("service").description("Systemd service management");
|
|
4964
5232
|
admin.command("serve").description("Start admin config API server").option("--host <host>", "override admin bind host").option("--port <port>", "override admin bind port").option(
|
|
4965
5233
|
"--allow-insecure-no-token",
|
|
4966
5234
|
"allow serving admin API without ADMIN_TOKEN on non-loopback host (not recommended)"
|
|
@@ -5018,6 +5286,37 @@ configCommand.command("import").description("Import config snapshot from JSON").
|
|
|
5018
5286
|
process.exitCode = 1;
|
|
5019
5287
|
}
|
|
5020
5288
|
});
|
|
5289
|
+
serviceCommand.command("install").description("Install and enable codeharbor systemd service (requires root)").option("--run-user <user>", "service user (default: sudo user or current user)").option("--runtime-home <path>", "runtime home used as CODEHARBOR_HOME").option("--with-admin", "also install codeharbor-admin.service").option("--no-start", "enable service without starting immediately").action((options) => {
|
|
5290
|
+
try {
|
|
5291
|
+
const runUser = options.runUser?.trim() || resolveDefaultRunUser();
|
|
5292
|
+
const runtimeHomePath = resolveRuntimeHomeForUser(runUser, process.env, options.runtimeHome);
|
|
5293
|
+
installSystemdServices({
|
|
5294
|
+
runUser,
|
|
5295
|
+
runtimeHome: runtimeHomePath,
|
|
5296
|
+
nodeBinPath: process.execPath,
|
|
5297
|
+
cliScriptPath: resolveCliScriptPath(),
|
|
5298
|
+
installAdmin: options.withAdmin ?? false,
|
|
5299
|
+
startNow: options.start ?? true
|
|
5300
|
+
});
|
|
5301
|
+
} catch (error) {
|
|
5302
|
+
process.stderr.write(`Service install failed: ${formatError3(error)}
|
|
5303
|
+
`);
|
|
5304
|
+
process.stderr.write("Hint: run with sudo, for example: sudo codeharbor service install\n");
|
|
5305
|
+
process.exitCode = 1;
|
|
5306
|
+
}
|
|
5307
|
+
});
|
|
5308
|
+
serviceCommand.command("uninstall").description("Remove codeharbor systemd service (requires root)").option("--with-admin", "also remove codeharbor-admin.service").action((options) => {
|
|
5309
|
+
try {
|
|
5310
|
+
uninstallSystemdServices({
|
|
5311
|
+
removeAdmin: options.withAdmin ?? false
|
|
5312
|
+
});
|
|
5313
|
+
} catch (error) {
|
|
5314
|
+
process.stderr.write(`Service uninstall failed: ${formatError3(error)}
|
|
5315
|
+
`);
|
|
5316
|
+
process.stderr.write("Hint: run with sudo, for example: sudo codeharbor service uninstall\n");
|
|
5317
|
+
process.exitCode = 1;
|
|
5318
|
+
}
|
|
5319
|
+
});
|
|
5021
5320
|
if (process.argv.length <= 2) {
|
|
5022
5321
|
process.argv.push("start");
|
|
5023
5322
|
}
|
|
@@ -5049,7 +5348,7 @@ function ensureRuntimeHomeOrExit() {
|
|
|
5049
5348
|
}
|
|
5050
5349
|
const home = resolveRuntimeHome();
|
|
5051
5350
|
try {
|
|
5052
|
-
|
|
5351
|
+
import_node_fs11.default.mkdirSync(home, { recursive: true });
|
|
5053
5352
|
} catch (error) {
|
|
5054
5353
|
const message = error instanceof Error ? error.message : String(error);
|
|
5055
5354
|
process.stderr.write(`Runtime setup failed: cannot create ${home}. ${message}
|
|
@@ -5064,7 +5363,7 @@ function ensureRuntimeHomeOrExit() {
|
|
|
5064
5363
|
`);
|
|
5065
5364
|
process.exit(1);
|
|
5066
5365
|
}
|
|
5067
|
-
loadEnvFromFile(
|
|
5366
|
+
loadEnvFromFile(import_node_path12.default.resolve(home, ".env"));
|
|
5068
5367
|
runtimeHome = home;
|
|
5069
5368
|
return runtimeHome;
|
|
5070
5369
|
}
|
|
@@ -5077,6 +5376,23 @@ function parsePortOption(raw, fallback) {
|
|
|
5077
5376
|
}
|
|
5078
5377
|
return parsed;
|
|
5079
5378
|
}
|
|
5379
|
+
function resolveCliVersion() {
|
|
5380
|
+
try {
|
|
5381
|
+
const packagePath = import_node_path12.default.resolve(__dirname, "..", "package.json");
|
|
5382
|
+
const content = import_node_fs11.default.readFileSync(packagePath, "utf8");
|
|
5383
|
+
const parsed = JSON.parse(content);
|
|
5384
|
+
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version : "0.0.0";
|
|
5385
|
+
} catch {
|
|
5386
|
+
return "0.0.0";
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
5389
|
+
function resolveCliScriptPath() {
|
|
5390
|
+
const argvPath = process.argv[1];
|
|
5391
|
+
if (argvPath && argvPath.trim()) {
|
|
5392
|
+
return import_node_path12.default.resolve(argvPath);
|
|
5393
|
+
}
|
|
5394
|
+
return import_node_path12.default.resolve(__dirname, "cli.js");
|
|
5395
|
+
}
|
|
5080
5396
|
function formatError3(error) {
|
|
5081
5397
|
if (error instanceof Error) {
|
|
5082
5398
|
return error.message;
|