codeharbor 0.1.18 → 0.1.19

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 CHANGED
@@ -93,6 +93,8 @@ Notes:
93
93
  - Service commands auto-elevate with `sudo` when root privileges are required.
94
94
  - `codeharbor service install --with-admin` and `install-linux-easy.sh --enable-admin-service` now install
95
95
  `/etc/sudoers.d/codeharbor-restart` for non-root service users, so Admin UI restart actions work out-of-box.
96
+ - `npm install -g codeharbor@latest` now performs best-effort restart for active `codeharbor(.service)` units on Linux
97
+ so upgrades take effect immediately (set `CODEHARBOR_SKIP_POSTINSTALL_RESTART=1` to disable).
96
98
  - If your environment blocks interactive `sudo`, use explicit fallback:
97
99
  - `sudo <node-bin> <codeharbor-cli-script> service ...`
98
100
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeharbor",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Instant-messaging bridge for Codex CLI sessions",
5
5
  "license": "MIT",
6
6
  "main": "dist/cli.js",
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "files": [
19
19
  "dist",
20
+ "scripts/postinstall-restart.cjs",
20
21
  ".env.example",
21
22
  "README.md",
22
23
  "LICENSE"
@@ -55,6 +56,7 @@
55
56
  "e2e:install": "playwright install chromium",
56
57
  "test:watch": "vitest test",
57
58
  "test:legacy": "bash ./scripts/run-legacy-tests.sh",
59
+ "postinstall": "node ./scripts/postinstall-restart.cjs",
58
60
  "prepare": "npm run build",
59
61
  "prepublishOnly": "npm run changelog:check && npm run typecheck && npm run lint && npm run test:coverage && npm run build",
60
62
  "changelog:check": "bash ./scripts/check-changelog.sh",
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { execFileSync } = require("node:child_process");
5
+
6
+ const LOG_PREFIX = "[codeharbor postinstall]";
7
+ const MAIN_SERVICE = "codeharbor.service";
8
+ const ADMIN_SERVICE = "codeharbor-admin.service";
9
+
10
+ function isTruthy(value) {
11
+ if (!value) {
12
+ return false;
13
+ }
14
+ const normalized = String(value).trim().toLowerCase();
15
+ return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
16
+ }
17
+
18
+ function isGlobalInstall() {
19
+ return isTruthy(process.env.npm_config_global);
20
+ }
21
+
22
+ function hasRootPrivileges() {
23
+ if (typeof process.getuid !== "function") {
24
+ return true;
25
+ }
26
+ return process.getuid() === 0;
27
+ }
28
+
29
+ function commandExists(file) {
30
+ try {
31
+ execFileSync(file, ["--version"], { stdio: "ignore" });
32
+ return true;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ function runCommand(file, args, options) {
39
+ return execFileSync(file, args, {
40
+ encoding: "utf8",
41
+ stdio: ["ignore", "pipe", "pipe"],
42
+ ...options,
43
+ });
44
+ }
45
+
46
+ function listUnitFiles(unitName) {
47
+ try {
48
+ return runCommand("systemctl", ["list-unit-files", unitName, "--no-legend", "--no-pager"]);
49
+ } catch {
50
+ return "";
51
+ }
52
+ }
53
+
54
+ function isUnitInstalled(unitName) {
55
+ const output = listUnitFiles(unitName).trim();
56
+ if (!output) {
57
+ return false;
58
+ }
59
+ return output.split(/\r?\n/).some((line) => line.trim().startsWith(unitName));
60
+ }
61
+
62
+ function isUnitActive(unitName) {
63
+ try {
64
+ const output = runCommand("systemctl", ["is-active", unitName], {}).trim();
65
+ return output === "active";
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ function restartUnit(unitName) {
72
+ if (hasRootPrivileges()) {
73
+ runCommand("systemctl", ["restart", unitName], {});
74
+ return;
75
+ }
76
+
77
+ runCommand("sudo", ["-n", "systemctl", "restart", unitName], {});
78
+ }
79
+
80
+ function main() {
81
+ if (isTruthy(process.env.CODEHARBOR_SKIP_POSTINSTALL_RESTART)) {
82
+ console.log(`${LOG_PREFIX} skip restart: CODEHARBOR_SKIP_POSTINSTALL_RESTART is set.`);
83
+ return;
84
+ }
85
+
86
+ const forceRestart = isTruthy(process.env.CODEHARBOR_FORCE_POSTINSTALL_RESTART);
87
+ if (!forceRestart && !isGlobalInstall()) {
88
+ return;
89
+ }
90
+
91
+ if (process.platform !== "linux") {
92
+ return;
93
+ }
94
+
95
+ if (!commandExists("systemctl")) {
96
+ return;
97
+ }
98
+
99
+ const candidates = [MAIN_SERVICE, ADMIN_SERVICE].filter((unitName) => isUnitInstalled(unitName));
100
+ const activeUnits = candidates.filter((unitName) => isUnitActive(unitName));
101
+ if (activeUnits.length === 0) {
102
+ return;
103
+ }
104
+
105
+ const restarted = [];
106
+ const failed = [];
107
+
108
+ for (const unitName of activeUnits) {
109
+ try {
110
+ restartUnit(unitName);
111
+ restarted.push(unitName);
112
+ } catch (error) {
113
+ failed.push({ unitName, error });
114
+ }
115
+ }
116
+
117
+ if (restarted.length > 0) {
118
+ console.log(`${LOG_PREFIX} restarted: ${restarted.join(", ")}`);
119
+ }
120
+ if (failed.length > 0) {
121
+ for (const failure of failed) {
122
+ const message = failure.error instanceof Error ? failure.error.message : String(failure.error);
123
+ console.warn(`${LOG_PREFIX} failed to restart ${failure.unitName}: ${message}`);
124
+ }
125
+ console.warn(`${LOG_PREFIX} run "codeharbor service restart --with-admin" manually if needed.`);
126
+ }
127
+ }
128
+
129
+ try {
130
+ main();
131
+ } catch (error) {
132
+ const message = error instanceof Error ? error.message : String(error);
133
+ console.warn(`${LOG_PREFIX} unexpected error: ${message}`);
134
+ }