@hmharness/cli 0.6.2 → 0.6.4
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/dist/main.js +10 -0
- package/dist/web-daemon.d.ts +10 -0
- package/dist/web-daemon.js +56 -6
- package/package.json +7 -7
package/dist/main.js
CHANGED
|
@@ -658,6 +658,16 @@ flags:
|
|
|
658
658
|
if (sub === 'status') {
|
|
659
659
|
const up = await hmhWebUp(Number.isFinite(port) ? port : 7788);
|
|
660
660
|
stdout.write(up ? t.webRunning(readWebPid() || 0, port) + '\n' : t.webNotRunning + '\n');
|
|
661
|
+
// stale-daemon warning: the daemon is a code snapshot from spawn time;
|
|
662
|
+
// after an upgrade it keeps serving old code until restarted
|
|
663
|
+
const { webDaemonStale } = await import("./web-daemon.js");
|
|
664
|
+
const v = webDaemonStale();
|
|
665
|
+
if (up && v.stale) {
|
|
666
|
+
stdout.write(YELLOW(`⚠ daemon is running v${v.daemon} but CLI is v${v.cli} — stale code (new features missing). Run: hmh web stop && hmh web start\n`));
|
|
667
|
+
}
|
|
668
|
+
else if (up && v.daemon) {
|
|
669
|
+
stdout.write(DIM(`daemon v${v.daemon}\n`));
|
|
670
|
+
}
|
|
661
671
|
return;
|
|
662
672
|
}
|
|
663
673
|
if (sub === 'start') {
|
package/dist/web-daemon.d.ts
CHANGED
|
@@ -10,8 +10,18 @@ export declare function stopWebDaemon(): boolean;
|
|
|
10
10
|
* Idempotent: if our web UI is already up (pid file alive, or the port
|
|
11
11
|
* answers as hmh), keep it; otherwise spawn it and wait up to ~6s for
|
|
12
12
|
* readiness. Returns true when the web UI is usable.
|
|
13
|
+
*
|
|
14
|
+
* Version check: a daemon spawned by an older CLI keeps serving old code
|
|
15
|
+
* (the user sees missing features for days). We compare the recorded daemon
|
|
16
|
+
* version with the current CLI and restart on mismatch.
|
|
13
17
|
*/
|
|
14
18
|
export declare function ensureWebDaemon(port?: number, entry?: string): Promise<boolean>;
|
|
19
|
+
/** Report whether the running daemon matches the current CLI version. */
|
|
20
|
+
export declare function webDaemonStale(): {
|
|
21
|
+
stale: boolean;
|
|
22
|
+
daemon: string;
|
|
23
|
+
cli: string;
|
|
24
|
+
};
|
|
15
25
|
export declare function startWebDaemon(port?: number, entry?: string): {
|
|
16
26
|
already: boolean;
|
|
17
27
|
pid: number;
|
package/dist/web-daemon.js
CHANGED
|
@@ -3,9 +3,16 @@
|
|
|
3
3
|
* Shared by `hmh web start|stop|status` and the TUI auto-link (hmh tui
|
|
4
4
|
* brings the web UI up unless --no-web). One pid file + one log file under
|
|
5
5
|
* HMH_HOME; the daemon runs detached with no window and survives terminals.
|
|
6
|
+
*
|
|
7
|
+
* VERSION-AWARE (this bit me twice): a daemon is a code snapshot frozen at
|
|
8
|
+
* spawn time. After `npm i -g @hmharness/cli@X` the running daemon still
|
|
9
|
+
* serves the OLD code — new features silently missing, and the user sees
|
|
10
|
+
* stale behavior forever. `ensureWebDaemon` now compares the daemon's
|
|
11
|
+
* recorded version against the running CLI and auto-restarts on mismatch.
|
|
6
12
|
*/
|
|
7
13
|
import { spawn } from 'node:child_process';
|
|
8
14
|
import { execSync } from 'node:child_process';
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
9
16
|
import { openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
10
17
|
import { join } from 'node:path';
|
|
11
18
|
import { homeDir } from '@hmharness/kernel';
|
|
@@ -90,7 +97,8 @@ export function stopWebDaemon() {
|
|
|
90
97
|
}
|
|
91
98
|
return killed;
|
|
92
99
|
}
|
|
93
|
-
/** Spawn the daemon (no window, detached). Returns the pid.
|
|
100
|
+
/** Spawn the daemon (no window, detached). Returns the pid. Records the
|
|
101
|
+
* spawning CLI's version so a later upgrade can detect the stale snapshot. */
|
|
94
102
|
function spawnWebDaemon(port, entry = process.argv[1]) {
|
|
95
103
|
const home = homeDir();
|
|
96
104
|
const log = openSync(join(home, 'web.log'), 'a');
|
|
@@ -102,21 +110,57 @@ function spawnWebDaemon(port, entry = process.argv[1]) {
|
|
|
102
110
|
});
|
|
103
111
|
child.unref();
|
|
104
112
|
const pid = child.pid ?? 0;
|
|
105
|
-
if (pid > 0)
|
|
113
|
+
if (pid > 0) {
|
|
106
114
|
writeFileSync(join(home, 'web.pid'), String(pid));
|
|
115
|
+
try {
|
|
116
|
+
const v = createRequire(import.meta.url)('../package.json').version;
|
|
117
|
+
writeFileSync(join(home, 'web.version'), v);
|
|
118
|
+
}
|
|
119
|
+
catch { /* best effort */ }
|
|
120
|
+
}
|
|
107
121
|
return pid;
|
|
108
122
|
}
|
|
123
|
+
function daemonVersion() {
|
|
124
|
+
try {
|
|
125
|
+
return readFileSync(join(homeDir(), 'web.version'), 'utf8').trim();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function cliVersion() {
|
|
132
|
+
try {
|
|
133
|
+
return createRequire(import.meta.url)('../package.json').version;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return '';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
109
139
|
/**
|
|
110
140
|
* Idempotent: if our web UI is already up (pid file alive, or the port
|
|
111
141
|
* answers as hmh), keep it; otherwise spawn it and wait up to ~6s for
|
|
112
142
|
* readiness. Returns true when the web UI is usable.
|
|
143
|
+
*
|
|
144
|
+
* Version check: a daemon spawned by an older CLI keeps serving old code
|
|
145
|
+
* (the user sees missing features for days). We compare the recorded daemon
|
|
146
|
+
* version with the current CLI and restart on mismatch.
|
|
113
147
|
*/
|
|
114
148
|
export async function ensureWebDaemon(port = DEFAULT_WEB_PORT, entry = process.argv[1]) {
|
|
115
149
|
const pid = readWebPid();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
150
|
+
const daemonV = daemonVersion();
|
|
151
|
+
const myV = cliVersion();
|
|
152
|
+
const stale = daemonV !== '' && myV !== '' && daemonV !== myV;
|
|
153
|
+
if (!stale) {
|
|
154
|
+
if (pid && alive(pid))
|
|
155
|
+
return true;
|
|
156
|
+
if (await hmhWebUp(port))
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// stale daemon: kill it (it may hold the pid OR just the port)
|
|
161
|
+
stopWebDaemon();
|
|
162
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
163
|
+
}
|
|
120
164
|
spawnWebDaemon(port, entry);
|
|
121
165
|
for (let i = 0; i < 12; i++) {
|
|
122
166
|
await new Promise((r) => setTimeout(r, 500));
|
|
@@ -125,6 +169,12 @@ export async function ensureWebDaemon(port = DEFAULT_WEB_PORT, entry = process.a
|
|
|
125
169
|
}
|
|
126
170
|
return false;
|
|
127
171
|
}
|
|
172
|
+
/** Report whether the running daemon matches the current CLI version. */
|
|
173
|
+
export function webDaemonStale() {
|
|
174
|
+
const d = daemonVersion();
|
|
175
|
+
const c = cliVersion();
|
|
176
|
+
return { stale: d !== '' && c !== '' && d !== c, daemon: d, cli: c };
|
|
177
|
+
}
|
|
128
178
|
export function startWebDaemon(port = DEFAULT_WEB_PORT, entry = process.argv[1]) {
|
|
129
179
|
const pid = readWebPid();
|
|
130
180
|
if (pid && alive(pid))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "hmharness command line: one-shot tasks, an interactive REPL, a fullscreen TUI, the web frontend, and direct tool invocation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"build": "tsc -p tsconfig.build.json"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@hmharness/kernel": "0.6.
|
|
47
|
-
"@hmharness/evolution": "0.6.
|
|
48
|
-
"@hmharness/domain-harmony": "0.6.
|
|
49
|
-
"@hmharness/domain-ops": "0.6.
|
|
50
|
-
"@hmharness/agent": "0.6.
|
|
51
|
-
"@hmharness/web": "0.6.
|
|
46
|
+
"@hmharness/kernel": "0.6.4",
|
|
47
|
+
"@hmharness/evolution": "0.6.4",
|
|
48
|
+
"@hmharness/domain-harmony": "0.6.4",
|
|
49
|
+
"@hmharness/domain-ops": "0.6.4",
|
|
50
|
+
"@hmharness/agent": "0.6.4",
|
|
51
|
+
"@hmharness/web": "0.6.4"
|
|
52
52
|
}
|
|
53
53
|
}
|