geneseed 3.1.3 → 3.3.0
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/CHANGELOG.md +180 -0
- package/README.md +24 -14
- package/SETUP.md +38 -7
- package/SHIPPED.md +6 -5
- package/adapters/bob/README.md +40 -10
- package/adapters/claude-code/README.md +5 -0
- package/adapters/claude-code/settings.json +8 -1
- package/adapters/copilot/README.md +50 -24
- package/adapters/opencode/README.md +5 -1
- package/adapters/opencode/plugins/geneseed-guard.js +40 -6
- package/bin/build-driver.mjs +143 -89
- package/bin/geneseed-cli.mjs +63 -106
- package/bin/geneseed-hook.mjs +26 -16
- package/docs/candidate-hosts.md +74 -0
- package/docs/extending.md +6 -5
- package/docs/token-footprint.md +32 -21
- package/docs/web-ui.md +5 -13
- package/harness.config.json +1 -1
- package/js/README.md +5 -15
- package/js/build/emit-claude.mjs +26 -12
- package/js/build/emit-common.mjs +63 -114
- package/js/build/emit-opencode.mjs +74 -66
- package/js/build/generate.mjs +4 -6
- package/js/build/render.mjs +50 -32
- package/js/build/source.mjs +29 -8
- package/js/build/stubs.mjs +25 -33
- package/js/build/themes.mjs +1 -1
- package/js/build/version.mjs +1 -3
- package/js/cli-table.json +84 -0
- package/js/hosts/hooks.mjs +243 -81
- package/js/hosts/hosts.mjs +57 -5
- package/js/hosts/installs.mjs +35 -28
- package/js/hosts/link.mjs +2 -2
- package/js/hosts/mcp.mjs +3 -1
- package/js/hosts/native.mjs +54 -19
- package/js/hosts/opencode.mjs +39 -28
- package/js/hosts/settings.mjs +206 -115
- package/js/inspect/checks-authoring.mjs +5 -4
- package/js/inspect/checks-build.mjs +1 -1
- package/js/inspect/diff.mjs +1 -3
- package/js/inspect/doctor.mjs +59 -44
- package/js/inspect/excludes.mjs +5 -11
- package/js/inspect/inventory.mjs +38 -56
- package/js/inspect/registry.mjs +4 -7
- package/js/inspect/scan.mjs +8 -15
- package/js/inspect/status.mjs +51 -3
- package/js/lib/fs.mjs +34 -0
- package/js/lib/json.mjs +28 -1
- package/js/lib/paths.mjs +0 -10
- package/js/lib/udiff.mjs +11 -16
- package/js/maintain/memory.mjs +2 -4
- package/js/maintain/migrate.mjs +3 -3
- package/js/maintain/uninstall.mjs +127 -88
- package/js/maintain/update.mjs +4 -4
- package/js/ui/anim.mjs +1 -1
- package/js/ui/menu.mjs +1 -1
- package/js/ui/tui.mjs +3 -20
- package/js/web/actions.mjs +230 -217
- package/js/web/activity.mjs +71 -89
- package/js/web/api.mjs +130 -226
- package/js/web/daemon.mjs +92 -110
- package/js/web/docs.mjs +59 -126
- package/js/web/handler.mjs +94 -123
- package/js/web/jobs.mjs +116 -178
- package/js/web/routes.mjs +47 -192
- package/js/web/server.mjs +47 -82
- package/package.json +1 -1
- package/registry.json +14 -0
- package/src/AGENT.md.tmpl +55 -16
- package/src/agents/explorer.md +2 -1
- package/src/doctrines/README.md +3 -3
- package/src/doctrines/process.md +4 -2
- package/src/doctrines/rigor.md +13 -0
- package/src/laws/universal.md +133 -43
- package/src/ontology/universal.md +38 -13
- package/src/skills/_template.md +5 -1
- package/src/skills/bruno-collection-generator.md +63 -0
- package/src/skills/bruno-test-writer.md +62 -0
- package/src/skills/clarify.md +1 -1
- package/src/skills/crash-course.md +1 -0
- package/src/skills/decode.md +1 -0
- package/src/skills/drill.md +1 -0
- package/src/skills/feynman.md +1 -0
- package/src/skills/forge-mcp.md +2 -1
- package/src/skills/fresh-eyes.md +1 -1
- package/src/skills/herdr.md +1 -0
- package/src/skills/ingest.md +2 -1
- package/src/skills/learning-path.md +1 -0
- package/src/skills/opencode-theme.md +1 -0
- package/src/skills/parallel-agents.md +1 -1
- package/src/skills/ponytail.md +2 -2
- package/src/skills/research.md +4 -2
- package/themes/_TEMPLATE.json +5 -0
- package/themes/biker.json +5 -0
- package/themes/commentator.json +5 -0
- package/themes/cyberpunk.json +5 -0
- package/themes/gamer.json +5 -0
- package/themes/imperial.json +5 -0
- package/themes/joker.json +5 -0
- package/themes/marvin.json +5 -0
- package/themes/mean.json +5 -0
- package/themes/military.json +5 -0
- package/themes/neutral.json +5 -0
- package/themes/pirate.json +5 -0
- package/themes/sports.json +5 -0
- package/themes/verstappen.json +5 -0
- package/themes/wizard.json +5 -0
- package/web/dist/assets/Activity-BfVcLrda.js +1 -0
- package/web/dist/assets/ActivityDetail-CQXccGPl.js +1 -0
- package/web/dist/assets/CatalogRow-DALEQImZ.js +1 -0
- package/web/dist/assets/Diff-BIjxXx46.js +5 -0
- package/web/dist/assets/Doctor-CJLbgsrw.js +1 -0
- package/web/dist/assets/FilterInput-CkUCk_lY.js +1 -0
- package/web/dist/assets/GreenhouseView-DfC5IILJ.js +2 -0
- package/web/dist/assets/Harness-Dm5CCU-v.js +1 -0
- package/web/dist/assets/JournalView-Cpu2eI4s.js +1 -0
- package/web/dist/assets/Laws-DDDVFuQz.js +3 -0
- package/web/dist/assets/Library-Dao3T9na.js +1 -0
- package/web/dist/assets/{marked.esm-JKvyS0Pp.js → Markdown-C__nuWX5.js} +15 -15
- package/web/dist/assets/OperatorHudView-DEpCtAjP.js +2 -0
- package/web/dist/assets/Profile-BlXtUlh2.js +1 -0
- package/web/dist/assets/{Rules-CfsV1EVy.js → Rules-DIre6gxR.js} +1 -1
- package/web/dist/assets/Skills-rY13ZPhM.js +1 -0
- package/web/dist/assets/{StatusBadge-C9bZOEZk.js → StatusBadge-wW_RS_my.js} +1 -1
- package/web/dist/assets/{activity-Cc0n2jwx.js → activity-DpYPKNrA.js} +1 -1
- package/web/dist/assets/index-BEZvPvpF.js +1 -0
- package/web/dist/assets/index-DNQE5ydy.js +10 -0
- package/web/dist/assets/index-DzdQjvM1.css +1 -0
- package/web/dist/assets/index-PmQGRS1X.js +2 -0
- package/web/dist/assets/jobBuckets-DfTUJUxR.js +1 -0
- package/web/dist/assets/lawCats-DUS9UTH4.js +1 -0
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +119 -130
- package/js/web/graph.mjs +0 -0
- package/web/dist/assets/Activity-B49fE_ug.js +0 -1
- package/web/dist/assets/ActivityDetail-abt7hzD2.js +0 -1
- package/web/dist/assets/Diff-D3_bVkih.js +0 -5
- package/web/dist/assets/Doctor-hXp7srsx.js +0 -1
- package/web/dist/assets/Graph-DvYMtlvl.js +0 -1
- package/web/dist/assets/Harness-BIWei7N5.js +0 -1
- package/web/dist/assets/Laws-CxmnRdwt.js +0 -3
- package/web/dist/assets/Library-MjbOkHIW.js +0 -1
- package/web/dist/assets/Markdown-Dx_w0faR.js +0 -1
- package/web/dist/assets/Profile-BRFvv2gr.js +0 -1
- package/web/dist/assets/Skills-DArA6IHS.js +0 -1
- package/web/dist/assets/index-Bx3oxZ1o.css +0 -1
- package/web/dist/assets/index-DC3-GlCY.js +0 -1
- package/web/dist/assets/index-DP035wPa.js +0 -12
- package/web/dist/assets/index-dHi-7M-F.js +0 -2
- package/web/dist/assets/roman-DkYN0LX9.js +0 -1
package/js/web/daemon.mjs
CHANGED
|
@@ -14,11 +14,9 @@ import { isTruthy, jsonDumpsCompact } from '../lib/json.mjs';
|
|
|
14
14
|
import { webState } from './api.mjs';
|
|
15
15
|
import { spawn } from 'node:child_process';
|
|
16
16
|
import { chmodSync, mkdirSync, openSync, readFileSync, unlinkSync } from 'node:fs';
|
|
17
|
-
import { request as httpRequest } from 'node:http';
|
|
18
17
|
import { join, resolve as pathResolve } from 'node:path';
|
|
19
|
-
import { fileURLToPath } from 'node:url';
|
|
20
18
|
|
|
21
|
-
const ROOT = pathResolve(
|
|
19
|
+
const ROOT = pathResolve(import.meta.dirname, '..', '..');
|
|
22
20
|
|
|
23
21
|
// ---- the daemon record -----------------------------------------------------
|
|
24
22
|
|
|
@@ -38,9 +36,9 @@ export function writeDaemon(target, data) {
|
|
|
38
36
|
const p = statePath(target);
|
|
39
37
|
writeText(p, jsonDumpsCompact(data, { bareInts: true }));
|
|
40
38
|
// Owner-only: the record carries the API token. A no-op on Windows, where the
|
|
41
|
-
// mode only maps to the read-only attribute
|
|
39
|
+
// mode only maps to the read-only attribute.
|
|
42
40
|
chmodSync(p, 0o600);
|
|
43
|
-
} catch { /*
|
|
41
|
+
} catch { /* best-effort: a failed write here just means no daemon record */ }
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
export function clearDaemon(target) {
|
|
@@ -51,75 +49,62 @@ export function clearDaemon(target) {
|
|
|
51
49
|
|
|
52
50
|
// ---- daemon mode -----------------------------------------------------------
|
|
53
51
|
//
|
|
54
|
-
// `geneseed web start|stop|status|restart`. The
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// token. Two things about the TWIN, neither of which is in that banner:
|
|
52
|
+
// `geneseed web start|stop|status|restart`. The running server records pid/port/token/url
|
|
53
|
+
// in a small JSON file and control is over HTTP, so nothing here needs OS process-kill
|
|
54
|
+
// semantics, only a localhost request with the token.
|
|
58
55
|
//
|
|
59
|
-
// *
|
|
60
|
-
// synchronous
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// `web status` that answered correctly would then hang for the keep-alive timeout
|
|
66
|
-
// instead of exiting. Python's `urlopen` opens and closes a connection per call.
|
|
56
|
+
// * Every function below that touches the network is `async` (`fetch` has no
|
|
57
|
+
// synchronous equivalent), and `cmdWeb` awaits it.
|
|
58
|
+
// * Both requests are drained (`res.body?.cancel()`) whether or not the caller wants the
|
|
59
|
+
// body: an undrained `fetch` response can keep a keep-alive socket referenced, which is
|
|
60
|
+
// the same hang a `web status` that answered correctly would otherwise suffer while
|
|
61
|
+
// waiting to exit.
|
|
67
62
|
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
63
|
+
/**
|
|
64
|
+
* `${url}/api/ping` answered 200 — the cheap liveness probe both `status` and the launcher's
|
|
65
|
+
* wait loop are built on. Every failure mode (network error, timeout, malformed url) is
|
|
66
|
+
* `false`.
|
|
67
|
+
*/
|
|
68
|
+
export async function probe(url, timeout = 1.5) {
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetch(`${url}/api/ping`, {
|
|
71
|
+
method: 'GET',
|
|
72
|
+
signal: AbortSignal.timeout(Math.round(timeout * 1000)),
|
|
73
|
+
});
|
|
74
|
+
const ok = res.status === 200;
|
|
75
|
+
// Drained after the verdict is already read: a failure canceling the body must not
|
|
76
|
+
// flip an already-successful ping to `false`.
|
|
77
|
+
await res.body?.cancel().catch(() => {});
|
|
78
|
+
return ok;
|
|
79
|
+
} catch {
|
|
80
|
+
// network error, timeout abort, or a malformed url — all become `false`.
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
89
83
|
}
|
|
90
84
|
|
|
91
85
|
/** The token-carrying POST `geneseed web stop` and the in-page Stop button both send. */
|
|
92
|
-
export function postShutdown(url, token, timeout = 3.0) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} catch {
|
|
107
|
-
return finish(false);
|
|
108
|
-
}
|
|
109
|
-
req.setTimeout(Math.round(timeout * 1000), () => { req.destroy(); finish(false); });
|
|
110
|
-
req.on('error', () => finish(false));
|
|
111
|
-
req.end('{}');
|
|
112
|
-
return undefined;
|
|
113
|
-
});
|
|
86
|
+
export async function postShutdown(url, token, timeout = 3.0) {
|
|
87
|
+
try {
|
|
88
|
+
const res = await fetch(`${url}/api/shutdown`, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: { 'X-Geneseed-Token': token, 'Content-Type': 'application/json' },
|
|
91
|
+
body: '{}',
|
|
92
|
+
signal: AbortSignal.timeout(Math.round(timeout * 1000)),
|
|
93
|
+
});
|
|
94
|
+
const ok = res.status === 200;
|
|
95
|
+
await res.body?.cancel().catch(() => {});
|
|
96
|
+
return ok;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
114
100
|
}
|
|
115
101
|
|
|
116
102
|
/**
|
|
117
|
-
* The record ONLY if a server is answering; otherwise the stale file is deleted
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* carrying no url at all, which skips the probe and still clears.
|
|
103
|
+
* The record ONLY if a server is answering; otherwise the stale file is deleted — which is
|
|
104
|
+
* what stops a machine that crashed with a daemon running from re-probing a dead URL on
|
|
105
|
+
* every `status` forever. Both arms are gated by a `harness_golden` cell — a record whose
|
|
106
|
+
* url nothing answers, and a record carrying no url at all, which skips the probe and still
|
|
107
|
+
* clears.
|
|
123
108
|
*/
|
|
124
109
|
export async function liveDaemon(target) {
|
|
125
110
|
const st = readDaemon(target);
|
|
@@ -130,76 +115,76 @@ export async function liveDaemon(target) {
|
|
|
130
115
|
|
|
131
116
|
const sleep = (ms) => new Promise((r) => { setTimeout(r, ms); });
|
|
132
117
|
|
|
133
|
-
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
118
|
+
/**
|
|
119
|
+
* A missing key prints `None`, not `undefined` — only reachable through a hand-edited
|
|
120
|
+
* record, kept for consistency with how every other missing value is reported here.
|
|
121
|
+
*/
|
|
136
122
|
export const none = (v) => (v === undefined || v === null ? 'None' : String(v));
|
|
137
123
|
|
|
138
124
|
/**
|
|
139
|
-
*
|
|
140
|
-
* module owns.
|
|
125
|
+
* The daemon launcher, and the second `ALLOWED_SPAWNS` entry this module owns.
|
|
141
126
|
*
|
|
142
127
|
* WHY THERE IS NO IN-PROCESS EQUIVALENT: the whole contract is that the server OUTLIVES
|
|
143
128
|
* the process that started it. `geneseed web start` prints a URL and returns to the shell;
|
|
144
129
|
* the server it started must still be there. A thread, a `worker_thread` or an in-process
|
|
145
130
|
* `serve()` all die with the launcher.
|
|
146
131
|
*
|
|
147
|
-
* WHAT IT STARTS IS THIS PROGRAM
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* and no `build.py` at all.
|
|
132
|
+
* WHAT IT STARTS IS THIS PROGRAM: `process.execPath` + `bin/geneseed-cli.mjs` — the running
|
|
133
|
+
* interpreter by absolute path and this repo's own CLI, re-executing itself rather than
|
|
134
|
+
* shelling out to anything else. `tests/unit/web_daemon.test.mjs:199-223` scans this
|
|
135
|
+
* function's own source for `python`/`harness.py`/`build.py`/`sys.executable` and asserts
|
|
136
|
+
* none of them appear — this daemon may re-execute only itself.
|
|
153
137
|
*/
|
|
154
138
|
function spawnDetached(webArgs, log) {
|
|
155
139
|
const cmd = [process.execPath, join(ROOT, 'bin', 'geneseed-cli.mjs'), 'web', ...webArgs];
|
|
156
140
|
let out = 'ignore';
|
|
157
141
|
try {
|
|
158
|
-
out = openSync(log, 'a');
|
|
159
|
-
} catch { /*
|
|
142
|
+
out = openSync(log, 'a');
|
|
143
|
+
} catch { /* falls back to 'ignore' if the log can't be opened */ }
|
|
160
144
|
const child = spawn(cmd[0], cmd.slice(1), {
|
|
161
|
-
// `DETACHED_PROCESS | NEW_PROCESS_GROUP` on Windows and
|
|
162
|
-
//
|
|
163
|
-
// the child
|
|
145
|
+
// `detached: true` covers `DETACHED_PROCESS | NEW_PROCESS_GROUP` on Windows and
|
|
146
|
+
// `start_new_session` on POSIX; `unref()` is what then lets the launcher's own event
|
|
147
|
+
// loop end while the child keeps running — Node does not do this by default.
|
|
164
148
|
detached: true, windowsHide: true, stdio: ['ignore', out, out],
|
|
165
149
|
});
|
|
166
150
|
child.unref();
|
|
167
151
|
}
|
|
168
152
|
|
|
169
|
-
/** The `web` argv the launcher passes. Pure
|
|
170
|
-
* corpus: the binary differs per side by design, the FLAGS may not. */
|
|
153
|
+
/** The `web` argv the launcher passes. Pure and side-effect free. */
|
|
171
154
|
export function daemonArgs(port, theme) {
|
|
172
155
|
const args = ['--daemon-internal', '--port', String(port), '--no-browser'];
|
|
173
156
|
if (isTruthy(theme)) args.push('--theme', theme);
|
|
174
157
|
return args;
|
|
175
158
|
}
|
|
176
159
|
|
|
177
|
-
/** The same for the out-of-band restart
|
|
160
|
+
/** The same for the out-of-band restart. */
|
|
178
161
|
export function restartArgs(theme) {
|
|
179
162
|
const args = ['restart', '--no-browser'];
|
|
180
163
|
if (isTruthy(theme)) args.push('--theme', theme);
|
|
181
164
|
return args;
|
|
182
165
|
}
|
|
183
166
|
|
|
184
|
-
/**
|
|
185
|
-
*
|
|
167
|
+
/**
|
|
168
|
+
* A detached `web restart`, so the new server survives the exit of the very process asking
|
|
169
|
+
* for it. Called by POST `/api/restart`.
|
|
170
|
+
*/
|
|
186
171
|
export function requestRestart(theme) {
|
|
187
172
|
const target = webState(theme).target;
|
|
188
173
|
spawnDetached(restartArgs(theme), join(target, '.geneseed-web.log'));
|
|
189
174
|
}
|
|
190
175
|
|
|
191
176
|
/**
|
|
192
|
-
*
|
|
193
|
-
*
|
|
177
|
+
* Opens a URL in the desktop's default browser. No Node stdlib API knows what that is, so
|
|
178
|
+
* this shells out to an OS-native opener (`start`/`open`/`xdg-open`).
|
|
194
179
|
*
|
|
195
|
-
* IT IS A MACHINE PRIMITIVE, the same class as `taskkill` and `java -version
|
|
180
|
+
* IT IS A MACHINE PRIMITIVE, the same class as `taskkill` and `java -version`, and the
|
|
196
181
|
* opposite of a passthrough: it asks the OS to open a URL and hands it nothing of this
|
|
197
182
|
* program's work. Declining it instead would have been a silent regression in the one verb
|
|
198
183
|
* whose job is to put a UI on screen — `geneseed web` would print an address and do
|
|
199
|
-
* nothing, and no
|
|
184
|
+
* nothing, and no automated test would catch it, because every test passes `--no-browser`.
|
|
200
185
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
186
|
+
* Every failure here is swallowed — a machine with no browser opener configured must not
|
|
187
|
+
* crash a server that is otherwise working fine.
|
|
203
188
|
*/
|
|
204
189
|
export function openUrl(url) {
|
|
205
190
|
const [file, args, extra] = process.platform === 'win32'
|
|
@@ -214,19 +199,17 @@ export function openUrl(url) {
|
|
|
214
199
|
detached: true, stdio: 'ignore', windowsHide: true, ...extra,
|
|
215
200
|
});
|
|
216
201
|
// ⚠ ENOENT ARRIVES AS AN EVENT, NOT AS A THROW, AND THE `try` ABOVE CANNOT SEE IT.
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
// exception. So the port's `try/catch` read as equivalent and suppressed nothing.
|
|
202
|
+
// Node's `spawn` reports a missing executable on the child's `error` event; an 'error'
|
|
203
|
+
// with no listener is re-thrown as an uncaught exception that would crash the daemon
|
|
204
|
+
// over a browser it doesn't strictly need to open. The listener below is what actually
|
|
205
|
+
// suppresses it — the `try`/`catch` around this call catches nothing here.
|
|
222
206
|
//
|
|
223
|
-
// MEASURED
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
// passes `--no-browser`, so no recorded cell has ever executed this line.
|
|
207
|
+
// MEASURED: `geneseed web` on a headless Linux box with no browser opener printed
|
|
208
|
+
// `Error: spawn xdg-open ENOENT` before this listener existed. No automated test catches
|
|
209
|
+
// this, because every test passes `--no-browser`.
|
|
227
210
|
child.on('error', () => { /* no opener on this machine; the daemon is still serving */ });
|
|
228
211
|
child.unref();
|
|
229
|
-
} catch { /*
|
|
212
|
+
} catch { /* spawn itself can still throw synchronously on some platforms; same rule */ }
|
|
230
213
|
}
|
|
231
214
|
|
|
232
215
|
export async function startDaemon(theme, port, openBrowser = true) {
|
|
@@ -240,8 +223,8 @@ export async function startDaemon(theme, port, openBrowser = true) {
|
|
|
240
223
|
clearDaemon(target);
|
|
241
224
|
const log = join(target, '.geneseed-web.log');
|
|
242
225
|
spawnDetached(daemonArgs(port, theme), log);
|
|
243
|
-
//
|
|
244
|
-
//
|
|
226
|
+
// Twelve seconds for the child to bind and write its record (60 * 200ms). The probe
|
|
227
|
+
// timeout here is the shorter 0.5s, not the default 1.5s.
|
|
245
228
|
for (let i = 0; i < 60; i += 1) {
|
|
246
229
|
const rec = readDaemon(target);
|
|
247
230
|
|
|
@@ -264,10 +247,9 @@ export async function stopDaemon(theme = null) {
|
|
|
264
247
|
const { target } = webState(theme);
|
|
265
248
|
const st = readDaemon(target);
|
|
266
249
|
if (!isTruthy(st) || !isTruthy(st.url)) {
|
|
267
|
-
// NO `clearDaemon` HERE
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
// a file the reference keeps and stdout would look identical.
|
|
250
|
+
// NO `clearDaemon` HERE — deliberately asymmetric with `liveDaemon`/`statusDaemon`,
|
|
251
|
+
// which DO delete a record naming no url. Unifying the two would delete a file this
|
|
252
|
+
// function is not supposed to touch.
|
|
271
253
|
printOut('[web] no running server recorded.\n');
|
|
272
254
|
return 0;
|
|
273
255
|
}
|
|
@@ -304,7 +286,7 @@ export async function restartDaemon(theme = null, port = 4747, openBrowser = tru
|
|
|
304
286
|
let open = openBrowser;
|
|
305
287
|
if (live) {
|
|
306
288
|
// A live daemon means a tab is already open on this (preserved) port and will
|
|
307
|
-
// reconnect on its own —
|
|
289
|
+
// reconnect on its own — no need to open a fresh browser window here.
|
|
308
290
|
open = false;
|
|
309
291
|
await stopDaemon(theme);
|
|
310
292
|
for (let i = 0; i < 50; i += 1) {
|