dominds 1.25.19 → 1.26.1

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.
Files changed (47) hide show
  1. package/README.md +17 -0
  2. package/README.zh.md +17 -0
  3. package/dist/apps/runtime.d.ts +2 -4
  4. package/dist/apps-host/client.d.ts +2 -5
  5. package/dist/apps-host/ipc-types.d.ts +2 -5
  6. package/dist/apps-host/ipc-types.js +5 -1
  7. package/dist/bootstrap/rtws-cli.d.ts +1 -0
  8. package/dist/bootstrap/rtws-cli.js +8 -8
  9. package/dist/cli/cert.d.ts +4 -0
  10. package/dist/cli/cert.js +247 -0
  11. package/dist/cli/create.d.ts +1 -1
  12. package/dist/cli/create.js +1 -1
  13. package/dist/cli/read.js +1 -1
  14. package/dist/cli/tui.js +1 -1
  15. package/dist/cli/webui.d.ts +1 -1
  16. package/dist/cli/webui.js +35 -5
  17. package/dist/cli-runner.d.ts +31 -0
  18. package/dist/cli-runner.js +349 -0
  19. package/dist/cli.d.ts +0 -30
  20. package/dist/cli.js +540 -292
  21. package/dist/docs/cli-usage.md +40 -3
  22. package/dist/docs/cli-usage.zh.md +40 -3
  23. package/dist/docs/dialog-persistence.md +1 -1
  24. package/dist/docs/dominds-terminology.md +2 -2
  25. package/dist/llm/kernel-driver/drive.js +19 -14
  26. package/dist/runtime/driver-messages.d.ts +2 -0
  27. package/dist/runtime/driver-messages.js +24 -4
  28. package/dist/server/auth.d.ts +7 -0
  29. package/dist/server/auth.js +15 -4
  30. package/dist/server/certificates.d.ts +61 -0
  31. package/dist/server/certificates.js +418 -0
  32. package/dist/server/dominds-self-update.js +33 -121
  33. package/dist/server/network-hosts.d.ts +5 -0
  34. package/dist/server/network-hosts.js +277 -0
  35. package/dist/server/server-core.d.ts +12 -1
  36. package/dist/server/server-core.js +26 -4
  37. package/dist/server/websocket-handler.d.ts +3 -2
  38. package/dist/server/websocket-handler.js +15 -8
  39. package/dist/server-debug.d.ts +2 -0
  40. package/dist/server-debug.js +79 -0
  41. package/dist/server.d.ts +5 -0
  42. package/dist/server.js +101 -126
  43. package/dist/supervisor-protocol.d.ts +15 -0
  44. package/dist/supervisor-protocol.js +4 -0
  45. package/package.json +6 -5
  46. package/dist/server/dominds-self-update-restart-helper.d.ts +0 -15
  47. package/dist/server/dominds-self-update-restart-helper.js +0 -305
@@ -0,0 +1,418 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_SELF_SIGNED_CERT_DAYS = void 0;
37
+ exports.getDefaultDomindsCertsDir = getDefaultDomindsCertsDir;
38
+ exports.findAutoHttpsCertificateForHost = findAutoHttpsCertificateForHost;
39
+ exports.createSelfSignedCertificate = createSelfSignedCertificate;
40
+ const node_child_process_1 = require("node:child_process");
41
+ const node_crypto_1 = require("node:crypto");
42
+ const fs = __importStar(require("node:fs/promises"));
43
+ const net = __importStar(require("node:net"));
44
+ const os = __importStar(require("node:os"));
45
+ const path = __importStar(require("node:path"));
46
+ const tls = __importStar(require("node:tls"));
47
+ const node_util_1 = require("node:util");
48
+ const network_hosts_1 = require("./network-hosts");
49
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
50
+ const CERT_EXTENSIONS = ['.crt', '.cert', '.pem'];
51
+ const KEY_SUFFIXES = ['.key.pem', '.key', '-key.pem'];
52
+ exports.DEFAULT_SELF_SIGNED_CERT_DAYS = 3650;
53
+ function getDefaultDomindsCertsDir() {
54
+ return path.join(os.homedir(), '.dominds', 'certs');
55
+ }
56
+ async function findAutoHttpsCertificateForHost(params) {
57
+ const host = (0, network_hosts_1.normalizeNetworkHost)(params.host);
58
+ const matchHosts = await (0, network_hosts_1.resolveLanHttpsHostsForBindHost)(host);
59
+ const endpoint = { bindHost: host, urlHost: matchHosts[0] ?? host };
60
+ const certsDirAbs = params.certsDirAbs ?? getDefaultDomindsCertsDir();
61
+ const now = params.now ?? new Date();
62
+ const diagnostics = [];
63
+ if (matchHosts.length === 0) {
64
+ return { kind: 'not_found', certsDirAbs, endpoint, diagnostics };
65
+ }
66
+ let entries;
67
+ try {
68
+ entries = await fs.readdir(certsDirAbs);
69
+ }
70
+ catch (error) {
71
+ if (isNodeErrorWithCode(error, 'ENOENT')) {
72
+ return { kind: 'not_found', certsDirAbs, endpoint, diagnostics };
73
+ }
74
+ throw error;
75
+ }
76
+ const matches = [];
77
+ for (const entry of entries) {
78
+ if (!isCertificateFilename(entry))
79
+ continue;
80
+ const certPath = path.join(certsDirAbs, entry);
81
+ const keyPath = await findKeyPathForCert({ certsDirAbs, certEntry: entry });
82
+ if (keyPath === null) {
83
+ diagnostics.push({
84
+ level: 'warn',
85
+ code: 'missing_key_file',
86
+ message: `Certificate has no matching private key: ${certPath}`,
87
+ certPath,
88
+ });
89
+ continue;
90
+ }
91
+ const loaded = await loadAndValidateCandidate({
92
+ matchHosts,
93
+ now,
94
+ certPath,
95
+ keyPath,
96
+ });
97
+ if (loaded.kind === 'match') {
98
+ matches.push(loaded.certificate);
99
+ continue;
100
+ }
101
+ if (loaded.kind === 'diagnostic') {
102
+ diagnostics.push(loaded.diagnostic);
103
+ }
104
+ }
105
+ if (matches.length === 0) {
106
+ return { kind: 'not_found', certsDirAbs, endpoint, diagnostics };
107
+ }
108
+ matches.sort((a, b) => a.matchedHostPriority - b.matchedHostPriority || b.validTo.getTime() - a.validTo.getTime());
109
+ if (matches.length > 1) {
110
+ diagnostics.push({
111
+ level: 'warn',
112
+ code: 'multiple_matching_certs',
113
+ message: `Multiple valid certificates match host '${host}'; using the best-priority match with the latest expiry.`,
114
+ host,
115
+ certPaths: matches.map((m) => m.certPath),
116
+ });
117
+ }
118
+ return {
119
+ kind: 'found',
120
+ certsDirAbs,
121
+ endpoint: { bindHost: host, urlHost: matches[0].matchedHost },
122
+ certificate: matches[0],
123
+ diagnostics,
124
+ };
125
+ }
126
+ async function createSelfSignedCertificate(params) {
127
+ const hosts = normalizeCertificateHostsForCreate(params.host === undefined
128
+ ? await (0, network_hosts_1.resolveDefaultLanHttpsHosts)()
129
+ : [params.host, ...(params.altHosts ?? [])], { skipInvalidHosts: params.host === undefined });
130
+ const host = hosts[0];
131
+ const days = normalizeDays(params.days ?? exports.DEFAULT_SELF_SIGNED_CERT_DAYS);
132
+ const certsDirAbs = params.certsDirAbs ?? getDefaultDomindsCertsDir();
133
+ const basename = certificateBasenameForHosts(hosts);
134
+ const certPath = path.join(certsDirAbs, `${basename}.crt.pem`);
135
+ const keyPath = path.join(certsDirAbs, `${basename}.key.pem`);
136
+ const metadataPath = path.join(certsDirAbs, `${basename}.json`);
137
+ await fs.mkdir(certsDirAbs, { recursive: true, mode: 0o700 });
138
+ if (params.force !== true) {
139
+ await assertPathDoesNotExist(certPath);
140
+ await assertPathDoesNotExist(keyPath);
141
+ await assertPathDoesNotExist(metadataPath);
142
+ }
143
+ const san = buildSubjectAltName(hosts);
144
+ await execFileAsync('openssl', [
145
+ 'req',
146
+ '-x509',
147
+ '-newkey',
148
+ 'rsa:2048',
149
+ '-sha256',
150
+ '-nodes',
151
+ '-days',
152
+ String(days),
153
+ '-keyout',
154
+ keyPath,
155
+ '-out',
156
+ certPath,
157
+ '-subj',
158
+ `/CN=${escapeOpenSslSubjectValue(host)}`,
159
+ '-addext',
160
+ `subjectAltName=${san}`,
161
+ ], { maxBuffer: 1024 * 1024 });
162
+ await fs.chmod(keyPath, 0o600);
163
+ await fs.chmod(certPath, 0o644);
164
+ const metadata = {
165
+ kind: 'dominds_self_signed_cert',
166
+ host,
167
+ hosts,
168
+ days,
169
+ certPath,
170
+ keyPath,
171
+ createdAt: new Date().toISOString(),
172
+ subjectAltName: san,
173
+ };
174
+ await fs.writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, 'utf8');
175
+ await fs.chmod(metadataPath, 0o644);
176
+ return { host, hosts, days, certsDirAbs, certPath, keyPath, metadataPath };
177
+ }
178
+ function normalizeCertificateHostsForCreate(rawHosts, options) {
179
+ const hosts = [];
180
+ const seen = new Set();
181
+ for (const rawHost of rawHosts) {
182
+ const hostCandidates = getCertificateMatchHostsForCreateHost(rawHost);
183
+ for (const host of hostCandidates) {
184
+ const validation = validateCertificateHost(host);
185
+ if (validation.kind === 'invalid') {
186
+ if (options.skipInvalidHosts)
187
+ continue;
188
+ throw new Error(validation.message);
189
+ }
190
+ const key = host.toLowerCase();
191
+ if (seen.has(key))
192
+ continue;
193
+ seen.add(key);
194
+ hosts.push(host);
195
+ }
196
+ }
197
+ if (hosts.length === 0) {
198
+ throw new Error(options.skipInvalidHosts
199
+ ? 'No certificate-valid non-loopback LAN host was detected; provide --host explicitly'
200
+ : '--host must be provided at least once');
201
+ }
202
+ return hosts;
203
+ }
204
+ function getCertificateMatchHostsForCreateHost(rawHost) {
205
+ const host = (0, network_hosts_1.normalizeNetworkHost)(rawHost);
206
+ return [host];
207
+ }
208
+ function normalizeDays(days) {
209
+ if (!Number.isInteger(days) || days < 1 || days > 36500) {
210
+ throw new Error('--days must be an integer in [1, 36500]');
211
+ }
212
+ return days;
213
+ }
214
+ function validateCertificateHost(host) {
215
+ if (host === '') {
216
+ return { kind: 'invalid', message: '--host must not be empty' };
217
+ }
218
+ if (host.includes('/')) {
219
+ return {
220
+ kind: 'invalid',
221
+ message: '--host must be an exact DNS name or IP address; CIDR/IP ranges are not valid',
222
+ };
223
+ }
224
+ if (!(0, network_hosts_1.isLanHttpsHost)(host)) {
225
+ return {
226
+ kind: 'invalid',
227
+ message: '--host must not be localhost, loopback, 127.0.0.0/8, 169.254.0.0/16, ::1, fe80::/10, 0.0.0.0, or ::',
228
+ };
229
+ }
230
+ if (net.isIP(host) !== 0)
231
+ return { kind: 'valid' };
232
+ if (!isValidDnsHost(host)) {
233
+ return {
234
+ kind: 'invalid',
235
+ message: '--host must be a DNS name, an IPv4 address, or an IPv6 address',
236
+ };
237
+ }
238
+ return { kind: 'valid' };
239
+ }
240
+ function isValidDnsHost(host) {
241
+ if (host.length > 253)
242
+ return false;
243
+ const trimmed = host.endsWith('.') ? host.slice(0, -1) : host;
244
+ if (trimmed === '')
245
+ return false;
246
+ const labels = trimmed.split('.');
247
+ for (const label of labels) {
248
+ if (label.length < 1 || label.length > 63)
249
+ return false;
250
+ if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(label))
251
+ return false;
252
+ }
253
+ return true;
254
+ }
255
+ async function assertPathDoesNotExist(filePathAbs) {
256
+ try {
257
+ await fs.access(filePathAbs);
258
+ }
259
+ catch (error) {
260
+ if (isNodeErrorWithCode(error, 'ENOENT'))
261
+ return;
262
+ throw error;
263
+ }
264
+ throw new Error(`Refusing to overwrite existing certificate file: ${filePathAbs}`);
265
+ }
266
+ function certificateBasenameForHosts(hosts) {
267
+ const host = hosts[0];
268
+ const safeHost = host
269
+ .toLowerCase()
270
+ .replace(/[^a-z0-9.-]+/g, '_')
271
+ .replace(/^_+|_+$/g, '');
272
+ const digest = (0, node_crypto_1.createHash)('sha256').update(hosts.join('\0')).digest('hex').slice(0, 8);
273
+ return `dominds-${safeHost || 'host'}-${digest}`;
274
+ }
275
+ function buildSubjectAltName(hosts) {
276
+ return hosts
277
+ .map((host) => {
278
+ const ipVersion = net.isIP(host);
279
+ return ipVersion !== 0 ? `IP:${host}` : `DNS:${host}`;
280
+ })
281
+ .join(',');
282
+ }
283
+ function escapeOpenSslSubjectValue(value) {
284
+ return value.replace(/\\/g, '\\\\').replace(/\//g, '\\/');
285
+ }
286
+ function isCertificateFilename(filename) {
287
+ return CERT_EXTENSIONS.some((ext) => filename.endsWith(ext)) && !isKeyFilename(filename);
288
+ }
289
+ function isKeyFilename(filename) {
290
+ return KEY_SUFFIXES.some((suffix) => filename.endsWith(suffix));
291
+ }
292
+ async function findKeyPathForCert(params) {
293
+ const baseNames = certBaseNameVariants(params.certEntry);
294
+ for (const baseName of baseNames) {
295
+ for (const suffix of KEY_SUFFIXES) {
296
+ const keyPath = path.join(params.certsDirAbs, `${baseName}${suffix}`);
297
+ try {
298
+ await fs.access(keyPath);
299
+ return keyPath;
300
+ }
301
+ catch (error) {
302
+ if (isNodeErrorWithCode(error, 'ENOENT'))
303
+ continue;
304
+ throw error;
305
+ }
306
+ }
307
+ }
308
+ return null;
309
+ }
310
+ function certBaseNameVariants(certEntry) {
311
+ const variants = [];
312
+ for (const ext of CERT_EXTENSIONS) {
313
+ if (certEntry.endsWith(ext)) {
314
+ variants.push(certEntry.slice(0, -ext.length));
315
+ }
316
+ }
317
+ if (certEntry.endsWith('.crt.pem')) {
318
+ variants.push(certEntry.slice(0, -'.crt.pem'.length));
319
+ }
320
+ return Array.from(new Set(variants));
321
+ }
322
+ async function loadAndValidateCandidate(params) {
323
+ let certPem;
324
+ let keyPem;
325
+ let cert;
326
+ try {
327
+ certPem = await fs.readFile(params.certPath, 'utf8');
328
+ keyPem = await fs.readFile(params.keyPath, 'utf8');
329
+ cert = new node_crypto_1.X509Certificate(certPem);
330
+ }
331
+ catch (error) {
332
+ return {
333
+ kind: 'diagnostic',
334
+ diagnostic: {
335
+ level: 'warn',
336
+ code: 'invalid_cert_file',
337
+ message: `Invalid certificate file '${params.certPath}': ${formatUnknownError(error)}`,
338
+ certPath: params.certPath,
339
+ keyPath: params.keyPath,
340
+ },
341
+ };
342
+ }
343
+ const matchedHost = findCertificateMatchedHost(cert, params.matchHosts);
344
+ if (matchedHost === null) {
345
+ return { kind: 'no_match' };
346
+ }
347
+ if (!certificateIsCurrentlyValid(cert, params.now)) {
348
+ return {
349
+ kind: 'diagnostic',
350
+ diagnostic: {
351
+ level: 'warn',
352
+ code: 'expired_or_not_yet_valid',
353
+ message: `Certificate is not currently valid: ${params.certPath}`,
354
+ certPath: params.certPath,
355
+ keyPath: params.keyPath,
356
+ },
357
+ };
358
+ }
359
+ try {
360
+ tls.createSecureContext({ cert: certPem, key: keyPem });
361
+ }
362
+ catch (error) {
363
+ return {
364
+ kind: 'diagnostic',
365
+ diagnostic: {
366
+ level: 'warn',
367
+ code: 'invalid_key_pair',
368
+ message: `Certificate and private key do not form a valid TLS pair: ${formatUnknownError(error)}`,
369
+ certPath: params.certPath,
370
+ keyPath: params.keyPath,
371
+ },
372
+ };
373
+ }
374
+ return {
375
+ kind: 'match',
376
+ certificate: {
377
+ cert: certPem,
378
+ key: keyPem,
379
+ certPath: params.certPath,
380
+ keyPath: params.keyPath,
381
+ matchedHost: matchedHost.host,
382
+ matchedHostPriority: matchedHost.priority,
383
+ validTo: new Date(Date.parse(cert.validTo)),
384
+ },
385
+ };
386
+ }
387
+ function certificateIsCurrentlyValid(cert, now) {
388
+ const validFromMs = Date.parse(cert.validFrom);
389
+ const validToMs = Date.parse(cert.validTo);
390
+ if (!Number.isFinite(validFromMs) || !Number.isFinite(validToMs)) {
391
+ return false;
392
+ }
393
+ const nowMs = now.getTime();
394
+ return validFromMs <= nowMs && nowMs <= validToMs;
395
+ }
396
+ function findCertificateMatchedHost(cert, matchHosts) {
397
+ for (let i = 0; i < matchHosts.length; i += 1) {
398
+ const host = matchHosts[i];
399
+ if (certificateMatchesHost(cert, host)) {
400
+ return { host, priority: i };
401
+ }
402
+ }
403
+ return null;
404
+ }
405
+ function certificateMatchesHost(cert, host) {
406
+ const normalizedHost = (0, network_hosts_1.normalizeNetworkHost)(host);
407
+ const ipVersion = net.isIP(normalizedHost);
408
+ if (ipVersion !== 0) {
409
+ return cert.checkIP(normalizedHost) !== undefined;
410
+ }
411
+ return cert.checkHost(normalizedHost) !== undefined;
412
+ }
413
+ function isNodeErrorWithCode(error, code) {
414
+ return error instanceof Error && error.code === code;
415
+ }
416
+ function formatUnknownError(error) {
417
+ return error instanceof Error ? error.message : String(error);
418
+ }
@@ -15,14 +15,11 @@ const promises_1 = __importDefault(require("fs/promises"));
15
15
  const path_1 = __importDefault(require("path"));
16
16
  const time_1 = require("@longrun-ai/kernel/utils/time");
17
17
  const log_1 = require("../log");
18
+ const supervisor_protocol_1 = require("../supervisor-protocol");
18
19
  const dominds_running_version_1 = require("./dominds-running-version");
19
20
  const log = (0, log_1.createLogger)('dominds-self-update');
20
21
  const BACKGROUND_CHECK_INTERVAL_MS = 30 * 60 * 1000;
21
22
  const LATEST_VERSION_CHECK_TIMEOUT_MS = 60000;
22
- const RESTART_PORT_RELEASE_TIMEOUT_MS = 15000;
23
- const RESTART_PORT_PROBE_INTERVAL_MS = 150;
24
- const RESTART_EXIT_GRACE_MS = 1000;
25
- const RESTART_FORCE_KILL_AFTER_MS = 30000;
26
23
  const COMMAND_OUTPUT_LOG_LIMIT = 2000;
27
24
  const PROXY_URL_ENV_KEYS = new Set([
28
25
  'HTTP_PROXY',
@@ -34,7 +31,6 @@ const PROXY_URL_ENV_KEYS = new Set([
34
31
  ]);
35
32
  const IDLE_RESTART_STATE = { kind: 'idle' };
36
33
  const PROCESS_START_ARGV = [...process.argv];
37
- const PROCESS_START_EXEC_ARGV = [...process.execArgv];
38
34
  const PROCESS_START_CWD = process.cwd();
39
35
  let runtimeConfig = null;
40
36
  let latestObservation = { kind: 'unknown' };
@@ -86,19 +82,6 @@ function detectRunKind(mode) {
86
82
  }
87
83
  return 'global';
88
84
  }
89
- function hasInteractiveConsole() {
90
- return Boolean(process.stdin.isTTY || process.stdout.isTTY || process.stderr.isTTY);
91
- }
92
- function getRestartHelperStdio() {
93
- return hasInteractiveConsole() ? 'inherit' : 'ignore';
94
- }
95
- function getRestartPortProbeHost(host) {
96
- if (host === '0.0.0.0')
97
- return '127.0.0.1';
98
- if (host === '::')
99
- return '::1';
100
- return host;
101
- }
102
85
  function normalizeComparablePath(value) {
103
86
  const normalized = path_1.default.normalize(value);
104
87
  return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
@@ -118,9 +101,6 @@ function buildRestartTraceContext() {
118
101
  ].join('-'));
119
102
  return { debugDir, traceFile };
120
103
  }
121
- function getRestartHelperEntrypoint() {
122
- return path_1.default.resolve(__dirname, 'dominds-self-update-restart-helper.js');
123
- }
124
104
  async function appendRestartTrace(trace, event, details = {}) {
125
105
  const payload = {
126
106
  ...details,
@@ -170,13 +150,6 @@ function getCurrentProcessEntrypoint() {
170
150
  }
171
151
  return entrypoint;
172
152
  }
173
- function buildCurrentProcessRestartArgs() {
174
- getCurrentProcessEntrypoint();
175
- return [...PROCESS_START_EXEC_ARGV, ...PROCESS_START_ARGV.slice(1)];
176
- }
177
- function buildCurrentDomindsCliArgs() {
178
- return PROCESS_START_ARGV.slice(2);
179
- }
180
153
  function getNpxWorkspaceDirAbs() {
181
154
  const entrypoint = getCurrentProcessEntrypoint().replace(/\\/g, '/');
182
155
  const marker = '/node_modules/dominds/';
@@ -223,28 +196,12 @@ async function readNpxDomindsSpec() {
223
196
  }
224
197
  return null;
225
198
  }
226
- async function buildNpxLatestRestartLaunchSpec() {
227
- const npm = await resolveNpmCommandSpec();
228
- return {
229
- command: npm.command,
230
- args: [...npm.args, 'exec', '-y', '--', 'dominds@latest', ...buildCurrentDomindsCliArgs()],
231
- };
232
- }
233
- function buildCurrentProcessRestartLaunchSpec() {
234
- return {
235
- command: process.execPath,
236
- args: buildCurrentProcessRestartArgs(),
237
- };
238
- }
239
199
  function truncateCommandOutput(value) {
240
200
  const raw = typeof value === 'string' ? value.trim() : '';
241
201
  if (raw.length <= COMMAND_OUTPUT_LOG_LIMIT)
242
202
  return raw;
243
203
  return `${raw.slice(0, COMMAND_OUTPUT_LOG_LIMIT)}...[truncated ${raw.length - COMMAND_OUTPUT_LOG_LIMIT} chars]`;
244
204
  }
245
- function delayMs(ms) {
246
- return new Promise((resolve) => setTimeout(resolve, ms));
247
- }
248
205
  function formatPathEnvExcerpt(pathEnv) {
249
206
  if (pathEnv === null || pathEnv.trim() === '')
250
207
  return null;
@@ -1382,57 +1339,31 @@ async function installLatestDominds() {
1382
1339
  publishStatusUpdateSoon();
1383
1340
  return await getDomindsSelfUpdateStatus();
1384
1341
  }
1385
- async function spawnDetachedRestartHelper(params) {
1386
- const stdioMode = getRestartHelperStdio();
1387
- const helperEntrypoint = getRestartHelperEntrypoint();
1388
- await promises_1.default.access(helperEntrypoint);
1389
- const helperPayload = {
1390
- command: params.command,
1391
- args: [...params.args],
1392
- cwd: params.cwd,
1393
- host: getRestartPortProbeHost(params.host),
1394
- port: params.port,
1395
- retiringPid: process.pid,
1396
- forceKillAfterMs: RESTART_FORCE_KILL_AFTER_MS,
1397
- probeIntervalMs: RESTART_PORT_PROBE_INTERVAL_MS,
1398
- portReleaseTimeoutMs: RESTART_PORT_RELEASE_TIMEOUT_MS,
1399
- stdioMode,
1342
+ async function requestSupervisorRestart(params) {
1343
+ if (typeof process.send !== 'function') {
1344
+ throw new Error('Dominds restart requires the dominds supervisor process; this runner has no supervisor IPC channel');
1345
+ }
1346
+ const cfg = assertRuntimeConfig();
1347
+ const message = {
1348
+ type: supervisor_protocol_1.DOMINDS_SUPERVISOR_RESTART_WEBUI,
1349
+ cwd: process.cwd(),
1350
+ host: cfg.host,
1351
+ port: cfg.port,
1400
1352
  traceFile: params.trace.traceFile,
1401
1353
  debugDir: params.trace.debugDir,
1354
+ currentVersion: params.currentVersion,
1355
+ targetVersion: params.targetVersion,
1356
+ runKind: params.runKind,
1402
1357
  };
1403
- const helper = (0, child_process_1.spawn)(process.execPath, [helperEntrypoint, JSON.stringify(helperPayload)], {
1404
- cwd: params.cwd,
1405
- env: process.env,
1406
- detached: stdioMode !== 'inherit',
1407
- stdio: stdioMode,
1408
- windowsHide: stdioMode !== 'inherit',
1409
- });
1410
- const helperPid = typeof helper.pid === 'number' ? helper.pid : null;
1411
- const helperSpawned = new Promise((resolve, reject) => {
1412
- helper.once('spawn', () => {
1413
- appendRestartTraceSoon(params.trace, 'parent.helper_spawn_event', {
1414
- helperPid,
1415
- });
1416
- resolve(helperPid);
1417
- });
1418
- helper.once('error', (error) => {
1419
- appendRestartTraceSoon(params.trace, 'parent.helper_error_event', {
1420
- message: error.message,
1421
- stack: error.stack ?? null,
1422
- });
1423
- reject(error);
1424
- });
1425
- });
1426
- helper.once('exit', (code, signal) => {
1427
- appendRestartTraceSoon(params.trace, 'parent.helper_exit_event', {
1428
- code,
1429
- signal,
1358
+ await new Promise((resolve, reject) => {
1359
+ process.send?.(message, (error) => {
1360
+ if (error !== null) {
1361
+ reject(error);
1362
+ return;
1363
+ }
1364
+ resolve();
1430
1365
  });
1431
1366
  });
1432
- if (stdioMode !== 'inherit') {
1433
- helper.unref();
1434
- }
1435
- return await helperSpawned;
1436
1367
  }
1437
1368
  async function stopAndExitForRestart() {
1438
1369
  const cfg = assertRuntimeConfig();
@@ -1444,7 +1375,6 @@ async function stopAndExitForRestart() {
1444
1375
  await appendRestartTraceBestEffort(trace, 'parent.stop_and_exit.start', {
1445
1376
  host: cfg.host,
1446
1377
  port: cfg.port,
1447
- exitGraceMs: RESTART_EXIT_GRACE_MS,
1448
1378
  });
1449
1379
  }
1450
1380
  const stopPromise = cfg
@@ -1481,18 +1411,8 @@ async function stopAndExitForRestart() {
1481
1411
  return undefined;
1482
1412
  });
1483
1413
  cfg.closeWebSocketClients();
1484
- await delayMs(RESTART_EXIT_GRACE_MS);
1485
- if (!stopSettled) {
1486
- log.warn('Exiting Dominds process before graceful HTTP server stop completed during restart', undefined, {
1487
- host: cfg.host,
1488
- port: cfg.port,
1489
- graceMs: RESTART_EXIT_GRACE_MS,
1490
- });
1491
- }
1414
+ await stopPromise;
1492
1415
  if (trace !== null) {
1493
- if (stopSettled) {
1494
- await stopPromise;
1495
- }
1496
1416
  await appendRestartTraceBestEffort(trace, 'parent.process_exit', {
1497
1417
  host: cfg.host,
1498
1418
  port: cfg.port,
@@ -1519,19 +1439,17 @@ async function restartDomindsIntoLatest() {
1519
1439
  throw new Error('No restartable Dominds update is currently available');
1520
1440
  }
1521
1441
  const runKind = detectRunKind(cfg.mode);
1522
- let launchSpec;
1523
- const previousRestartRequiredState = restartState.kind === 'restart_required' ? restartState : null;
1524
- if (previousRestartRequiredState !== null) {
1525
- launchSpec = buildCurrentProcessRestartLaunchSpec();
1442
+ if (runKind === 'disabled') {
1443
+ throw new Error('Dominds restart requires a production run kind');
1526
1444
  }
1527
- else if (runKind === 'npx') {
1445
+ const previousRestartRequiredState = restartState.kind === 'restart_required' ? restartState : null;
1446
+ if (previousRestartRequiredState === null && runKind === 'npx') {
1528
1447
  const npxSpec = await readNpxDomindsSpec();
1529
1448
  if (npxSpec?.kind !== 'latest') {
1530
1449
  throw new Error('Dominds npx restart requires launching Dominds with dominds@latest');
1531
1450
  }
1532
- launchSpec = await buildNpxLatestRestartLaunchSpec();
1533
1451
  }
1534
- else {
1452
+ else if (previousRestartRequiredState === null) {
1535
1453
  throw new Error('Dominds restart requires a completed install or a restartable session');
1536
1454
  }
1537
1455
  restartState = { kind: 'restarting', targetVersion: status.targetVersion };
@@ -1544,29 +1462,23 @@ async function restartDomindsIntoLatest() {
1544
1462
  runKind,
1545
1463
  currentVersion: status.currentVersion,
1546
1464
  targetVersion: status.targetVersion,
1547
- launchCommand: launchSpec.command,
1548
- launchArgs: launchSpec.args,
1549
1465
  restartCwd,
1550
1466
  host: cfg.host,
1551
1467
  port: cfg.port,
1552
1468
  traceFile: trace.traceFile,
1553
1469
  });
1554
- const helperPid = await spawnDetachedRestartHelper({
1555
- command: launchSpec.command,
1556
- args: launchSpec.args,
1557
- cwd: restartCwd,
1558
- host: cfg.host,
1559
- port: cfg.port,
1470
+ await requestSupervisorRestart({
1560
1471
  trace,
1472
+ runKind,
1473
+ currentVersion: status.currentVersion,
1474
+ targetVersion: status.targetVersion,
1561
1475
  });
1562
- await appendRestartTraceBestEffort(trace, 'parent.helper_spawned', {
1563
- helperPid,
1476
+ await appendRestartTraceBestEffort(trace, 'parent.supervisor_restart_requested', {
1564
1477
  retiringPid: process.pid,
1565
1478
  host: cfg.host,
1566
1479
  port: cfg.port,
1567
1480
  });
1568
- log.info('Dominds restart helper spawned', undefined, {
1569
- helperPid,
1481
+ log.info('Dominds supervisor restart requested', undefined, {
1570
1482
  traceFile: trace.traceFile,
1571
1483
  });
1572
1484
  setImmediate(() => {
@@ -0,0 +1,5 @@
1
+ export declare function normalizeNetworkHost(host: string): string;
2
+ export declare function isLanHttpsHost(host: string): boolean;
3
+ export declare function resolveHttpUrlHostForBindHost(bindHost: string): string;
4
+ export declare function resolveLanHttpsHostsForBindHost(bindHost: string): Promise<readonly string[]>;
5
+ export declare function resolveDefaultLanHttpsHosts(): Promise<readonly string[]>;