dominds 1.26.0 → 1.26.2
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 +3 -3
- package/README.zh.md +3 -3
- package/dist/bootstrap/rtws-cli.d.ts +1 -0
- package/dist/bootstrap/rtws-cli.js +8 -8
- package/dist/cli/cert.d.ts +3 -1
- package/dist/cli/cert.js +69 -20
- package/dist/cli/create.d.ts +1 -1
- package/dist/cli/create.js +1 -1
- package/dist/cli/read.js +1 -1
- package/dist/cli/tui.js +1 -1
- package/dist/cli/webui.js +15 -5
- package/dist/cli-runner.d.ts +31 -0
- package/dist/cli-runner.js +349 -0
- package/dist/cli.d.ts +0 -31
- package/dist/cli.js +540 -304
- package/dist/docs/cli-usage.md +11 -10
- package/dist/docs/cli-usage.zh.md +11 -10
- package/dist/docs/dialog-persistence.md +1 -1
- package/dist/docs/dominds-terminology.md +2 -2
- package/dist/llm/kernel-driver/drive.js +19 -14
- package/dist/runtime/driver-messages.d.ts +2 -0
- package/dist/runtime/driver-messages.js +24 -4
- package/dist/server/certificates.js +1 -1
- package/dist/server/dominds-self-update.js +33 -121
- package/dist/server/network-hosts.js +140 -38
- package/dist/server-debug.d.ts +2 -0
- package/dist/server-debug.js +79 -0
- package/dist/server.js +4 -123
- package/dist/supervisor-protocol.d.ts +15 -0
- package/dist/supervisor-protocol.js +4 -0
- package/package.json +6 -5
- package/dist/server/dominds-self-update-restart-helper.d.ts +0 -15
- package/dist/server/dominds-self-update-restart-helper.js +0 -305
package/dist/cli.js
CHANGED
|
@@ -1,34 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
/**
|
|
4
|
-
* Main CLI entry point for dominds
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* dominds [subcommand] [options]
|
|
8
|
-
*
|
|
9
|
-
* Subcommands:
|
|
10
|
-
* webui - Start WebUI server (default)
|
|
11
|
-
* tui - Start Text User Interface
|
|
12
|
-
* run - Run task dialog (alias for tui)
|
|
13
|
-
* read - Read team configuration
|
|
14
|
-
* man - Render toolset manual to stdout
|
|
15
|
-
* manual - Alias for man
|
|
16
|
-
* validate_team_def - Validate explicit team toolset declarations
|
|
17
|
-
* cert - Create and inspect local HTTPS certificates
|
|
18
|
-
* create - Create a new runtime workspace (rtws) from a template
|
|
19
|
-
* install - Install a Dominds App into this rtws
|
|
20
|
-
* doctor - Diagnose Dominds App state in this rtws
|
|
21
|
-
* enable - Enable an installed Dominds App in this rtws
|
|
22
|
-
* disable - Disable an installed Dominds App in this rtws
|
|
23
|
-
* uninstall- Uninstall a Dominds App from this rtws
|
|
24
|
-
* update - Update installed Dominds App(s)
|
|
25
|
-
* new - Alias for create
|
|
26
|
-
* help - Show help
|
|
27
|
-
*
|
|
28
|
-
* Global installation:
|
|
29
|
-
* pnpm add -g dominds
|
|
30
|
-
* dominds webui
|
|
31
|
-
*/
|
|
32
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
33
4
|
if (k2 === undefined) k2 = k;
|
|
34
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -62,315 +33,580 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
62
33
|
return result;
|
|
63
34
|
};
|
|
64
35
|
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
65
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
|
-
exports.configureEnvProxySupport = configureEnvProxySupport;
|
|
67
40
|
exports.main = main;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
43
|
+
const net_1 = __importDefault(require("net"));
|
|
44
|
+
const path_1 = __importDefault(require("path"));
|
|
45
|
+
const time_1 = require("@longrun-ai/kernel/utils/time");
|
|
73
46
|
const rtws_cli_1 = require("./bootstrap/rtws-cli");
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
require(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
47
|
+
const supervisor_protocol_1 = require("./supervisor-protocol");
|
|
48
|
+
const RUNNER_JS_FILENAME = 'cli-runner.js';
|
|
49
|
+
const RUNNER_TS_FILENAME = 'cli-runner.ts';
|
|
50
|
+
const INITIAL_RESTART_BACKOFF_MS = 1000;
|
|
51
|
+
const MAX_RESTART_BACKOFF_MS = 30 * 60 * 1000;
|
|
52
|
+
const PORT_RELEASE_TIMEOUT_MS = 15000;
|
|
53
|
+
const PORT_PROBE_INTERVAL_MS = 150;
|
|
54
|
+
const RESTART_RUNNER_EXIT_GRACE_MS = 30000;
|
|
55
|
+
const RESTART_RUNNER_KILL_GRACE_MS = 5000;
|
|
56
|
+
function getDefaultRunnerCommandSpec() {
|
|
57
|
+
if (__filename.endsWith('.ts')) {
|
|
58
|
+
return {
|
|
59
|
+
command: process.execPath,
|
|
60
|
+
args: [
|
|
61
|
+
require.resolve('tsx/cli'),
|
|
62
|
+
'--tsconfig',
|
|
63
|
+
path_1.default.resolve(__dirname, 'tsconfig.dev.json'),
|
|
64
|
+
path_1.default.resolve(__dirname, RUNNER_TS_FILENAME),
|
|
65
|
+
],
|
|
66
|
+
};
|
|
93
67
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
68
|
+
return { command: process.execPath, args: [path_1.default.resolve(__dirname, RUNNER_JS_FILENAME)] };
|
|
69
|
+
}
|
|
70
|
+
function isLongRunningCommand(argv) {
|
|
71
|
+
return argv.length === 0 || argv[0] === 'webui';
|
|
72
|
+
}
|
|
73
|
+
function isDevelopmentMode(argv) {
|
|
74
|
+
if (process.env.NODE_ENV === 'dev')
|
|
75
|
+
return true;
|
|
76
|
+
for (let i = 0; i < argv.length; i++) {
|
|
77
|
+
const arg = argv[i];
|
|
78
|
+
if (arg === '--mode')
|
|
79
|
+
return argv[i + 1] === 'dev';
|
|
80
|
+
if (arg === '--mode=dev')
|
|
81
|
+
return true;
|
|
101
82
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
function parseSupervisorArgs(argv) {
|
|
86
|
+
const launchCwd = process.cwd();
|
|
87
|
+
const parsed = (0, rtws_cli_1.extractGlobalRtwsChdir)({ argv, baseCwd: launchCwd });
|
|
88
|
+
return {
|
|
89
|
+
cwd: parsed.chdir ?? launchCwd,
|
|
90
|
+
runnerArgv: parsed.argv,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function isRecord(value) {
|
|
94
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
95
|
+
}
|
|
96
|
+
function isRestartRunKind(value) {
|
|
97
|
+
return value === 'global' || value === 'npx';
|
|
98
|
+
}
|
|
99
|
+
function parseRunnerMessage(message) {
|
|
100
|
+
if (!isRecord(message))
|
|
101
|
+
return { kind: 'ignore' };
|
|
102
|
+
if (message['type'] !== supervisor_protocol_1.DOMINDS_SUPERVISOR_RESTART_WEBUI)
|
|
103
|
+
return { kind: 'ignore' };
|
|
104
|
+
const cwd = message['cwd'];
|
|
105
|
+
const host = message['host'];
|
|
106
|
+
const port = message['port'];
|
|
107
|
+
const traceFile = message['traceFile'];
|
|
108
|
+
const debugDir = message['debugDir'];
|
|
109
|
+
const currentVersion = message['currentVersion'];
|
|
110
|
+
const targetVersion = message['targetVersion'];
|
|
111
|
+
const runKind = message['runKind'];
|
|
112
|
+
const invalid = (reason) => ({
|
|
113
|
+
kind: 'invalid',
|
|
114
|
+
reason,
|
|
115
|
+
traceFile: typeof traceFile === 'string' ? traceFile : null,
|
|
116
|
+
debugDir: typeof debugDir === 'string' ? debugDir : null,
|
|
117
|
+
});
|
|
118
|
+
if (typeof cwd !== 'string')
|
|
119
|
+
return invalid('cwd must be a string');
|
|
120
|
+
if (typeof host !== 'string')
|
|
121
|
+
return invalid('host must be a string');
|
|
122
|
+
if (typeof port !== 'number' || !Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
123
|
+
return invalid('port must be an integer in 1..65535');
|
|
124
|
+
}
|
|
125
|
+
if (typeof traceFile !== 'string')
|
|
126
|
+
return invalid('traceFile must be a string');
|
|
127
|
+
if (typeof debugDir !== 'string')
|
|
128
|
+
return invalid('debugDir must be a string');
|
|
129
|
+
if (typeof currentVersion !== 'string')
|
|
130
|
+
return invalid('currentVersion must be a string');
|
|
131
|
+
if (targetVersion !== null && typeof targetVersion !== 'string') {
|
|
132
|
+
return invalid('targetVersion must be a string or null');
|
|
105
133
|
}
|
|
134
|
+
if (!isRestartRunKind(runKind))
|
|
135
|
+
return invalid('runKind must be global or npx');
|
|
136
|
+
return {
|
|
137
|
+
kind: 'restart',
|
|
138
|
+
message: {
|
|
139
|
+
type: supervisor_protocol_1.DOMINDS_SUPERVISOR_RESTART_WEBUI,
|
|
140
|
+
cwd,
|
|
141
|
+
host,
|
|
142
|
+
port,
|
|
143
|
+
traceFile,
|
|
144
|
+
debugDir,
|
|
145
|
+
currentVersion,
|
|
146
|
+
targetVersion,
|
|
147
|
+
runKind,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
106
150
|
}
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
manual [options] Alias for man
|
|
124
|
-
validate_team_def [options] Validate explicit team toolset declarations
|
|
125
|
-
cert [options] Create and inspect local HTTPS certificates
|
|
126
|
-
create [options] Create a new runtime workspace (rtws) from a template
|
|
127
|
-
install [options] Install a Dominds App into this rtws
|
|
128
|
-
doctor [options] Read-only diagnosis across manifest/lock/configuration/resolution/handshake
|
|
129
|
-
enable [options] Enable an installed Dominds App in this rtws
|
|
130
|
-
disable [options] Disable an installed Dominds App in this rtws
|
|
131
|
-
uninstall [options] Uninstall a Dominds App from this rtws
|
|
132
|
-
update [options] Update installed Dominds App(s)
|
|
133
|
-
new [options] Alias for create
|
|
134
|
-
help Show this help message
|
|
135
|
-
|
|
136
|
-
Examples:
|
|
137
|
-
dominds # Start WebUI server (default)
|
|
138
|
-
dominds webui # Start WebUI server
|
|
139
|
-
dominds -C /path/to/my-ws webui # Start in specific rtws
|
|
140
|
-
dominds tui --help # Show TUI help
|
|
141
|
-
dominds run task.tsk # Run task dialog
|
|
142
|
-
dominds read # Read team configuration
|
|
143
|
-
dominds man ws_read --lang zh --all
|
|
144
|
-
dominds validate_team_def # Validate toolset references in .minds/team.yaml
|
|
145
|
-
dominds cert create --host 192.168.1.10
|
|
146
|
-
dominds create web-scaffold my-project # Create rtws from a template
|
|
147
|
-
dominds doctor @longrun-ai/web-dev # Diagnose a single app across all app-state layers
|
|
148
|
-
|
|
149
|
-
Installation:
|
|
150
|
-
pnpm add -g dominds
|
|
151
|
-
|
|
152
|
-
For detailed help on a specific subcommand:
|
|
153
|
-
dominds <subcommand> --help
|
|
154
|
-
`);
|
|
151
|
+
async function appendSupervisorTrace(message, event, details = {}) {
|
|
152
|
+
const record = {
|
|
153
|
+
...details,
|
|
154
|
+
event,
|
|
155
|
+
capturedAt: (0, time_1.formatUnifiedTimestamp)(new Date()),
|
|
156
|
+
supervisorPid: process.pid,
|
|
157
|
+
platform: process.platform,
|
|
158
|
+
};
|
|
159
|
+
await promises_1.default.mkdir(message.debugDir, { recursive: true });
|
|
160
|
+
await promises_1.default.appendFile(message.traceFile, `${JSON.stringify(record)}\n`, 'utf8');
|
|
161
|
+
}
|
|
162
|
+
function appendSupervisorTraceSoon(message, event, details = {}) {
|
|
163
|
+
void appendSupervisorTrace(message, event, details).catch((error) => {
|
|
164
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
165
|
+
console.error(`Failed to write Dominds supervisor trace: ${text}`);
|
|
166
|
+
});
|
|
155
167
|
}
|
|
156
|
-
function
|
|
168
|
+
async function appendSupervisorTraceBestEffort(message, event, details = {}) {
|
|
157
169
|
try {
|
|
158
|
-
|
|
159
|
-
console.log(`dominds v${packageJson.version}`);
|
|
170
|
+
await appendSupervisorTrace(message, event, details);
|
|
160
171
|
}
|
|
161
|
-
catch {
|
|
162
|
-
|
|
172
|
+
catch (error) {
|
|
173
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
174
|
+
console.error(`Failed to write Dominds supervisor trace: ${text}`);
|
|
163
175
|
}
|
|
164
176
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
177
|
+
function appendInvalidSupervisorTraceSoon(parsed, event = 'supervisor.invalid_restart_message', details = {}) {
|
|
178
|
+
if (parsed.traceFile === null || parsed.debugDir === null)
|
|
179
|
+
return;
|
|
180
|
+
const message = {
|
|
181
|
+
type: supervisor_protocol_1.DOMINDS_SUPERVISOR_RESTART_WEBUI,
|
|
182
|
+
cwd: process.cwd(),
|
|
183
|
+
host: 'unknown',
|
|
184
|
+
port: 1,
|
|
185
|
+
traceFile: parsed.traceFile,
|
|
186
|
+
debugDir: parsed.debugDir,
|
|
187
|
+
currentVersion: 'unknown',
|
|
188
|
+
targetVersion: null,
|
|
189
|
+
runKind: 'global',
|
|
190
|
+
};
|
|
191
|
+
appendSupervisorTraceSoon(message, event, {
|
|
192
|
+
...details,
|
|
193
|
+
reason: parsed.reason,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
async function delayUnlessStopping(ms, stopState) {
|
|
197
|
+
if (stopState.stopping)
|
|
198
|
+
return;
|
|
199
|
+
await new Promise((resolve) => {
|
|
200
|
+
let settled = false;
|
|
201
|
+
const finish = () => {
|
|
202
|
+
if (settled)
|
|
203
|
+
return;
|
|
204
|
+
settled = true;
|
|
205
|
+
clearTimeout(timer);
|
|
206
|
+
clearInterval(poll);
|
|
207
|
+
resolve();
|
|
208
|
+
};
|
|
209
|
+
const timer = setTimeout(finish, ms);
|
|
210
|
+
const poll = setInterval(() => {
|
|
211
|
+
if (!stopState.stopping)
|
|
212
|
+
return;
|
|
213
|
+
finish();
|
|
214
|
+
}, 100);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function getPortProbeHost(host) {
|
|
218
|
+
if (host === '0.0.0.0')
|
|
219
|
+
return '127.0.0.1';
|
|
220
|
+
if (host === '::')
|
|
221
|
+
return '::1';
|
|
222
|
+
return host;
|
|
223
|
+
}
|
|
224
|
+
function isPortBusy(host, port) {
|
|
225
|
+
return new Promise((resolve) => {
|
|
226
|
+
const socket = net_1.default.createConnection({ host, port });
|
|
227
|
+
let settled = false;
|
|
228
|
+
const finish = (busy) => {
|
|
229
|
+
if (settled)
|
|
230
|
+
return;
|
|
231
|
+
settled = true;
|
|
232
|
+
socket.destroy();
|
|
233
|
+
resolve(busy);
|
|
234
|
+
};
|
|
235
|
+
socket.once('connect', () => finish(true));
|
|
236
|
+
socket.once('error', () => finish(false));
|
|
237
|
+
socket.setTimeout(1000, () => finish(true));
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async function waitForPortRelease(params) {
|
|
241
|
+
const probeHost = getPortProbeHost(params.host);
|
|
242
|
+
const deadline = Date.now() + PORT_RELEASE_TIMEOUT_MS;
|
|
243
|
+
let consecutiveReady = 0;
|
|
244
|
+
while (!params.stopState.stopping && Date.now() < deadline) {
|
|
245
|
+
if (!(await isPortBusy(probeHost, params.port))) {
|
|
246
|
+
consecutiveReady += 1;
|
|
247
|
+
if (consecutiveReady >= 2)
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
consecutiveReady = 0;
|
|
252
|
+
}
|
|
253
|
+
await delayUnlessStopping(PORT_PROBE_INTERVAL_MS, params.stopState);
|
|
169
254
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
async function resolveNpmCommandSpec() {
|
|
258
|
+
const nodeDir = path_1.default.dirname(process.execPath);
|
|
259
|
+
const candidates = process.platform === 'win32'
|
|
260
|
+
? [path_1.default.join(nodeDir, 'node_modules', 'npm', 'bin', 'npm-cli.js')]
|
|
261
|
+
: [
|
|
262
|
+
path_1.default.join(nodeDir, '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js'),
|
|
263
|
+
path_1.default.join(nodeDir, '..', 'share', 'nodejs', 'npm', 'bin', 'npm-cli.js'),
|
|
264
|
+
];
|
|
265
|
+
for (const candidate of candidates) {
|
|
266
|
+
try {
|
|
267
|
+
await promises_1.default.access(candidate);
|
|
268
|
+
return { command: process.execPath, args: [candidate] };
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (process.platform === 'win32') {
|
|
275
|
+
throw new Error(`Cannot find bundled npm CLI at ${candidates.join(', ')}`);
|
|
173
276
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
277
|
+
return { command: 'npm', args: [] };
|
|
278
|
+
}
|
|
279
|
+
async function captureCommandStdout(command, args) {
|
|
280
|
+
return await new Promise((resolve, reject) => {
|
|
281
|
+
let stdout = '';
|
|
282
|
+
let stderr = '';
|
|
283
|
+
const child = (0, child_process_1.spawn)(command, [...args], {
|
|
284
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
285
|
+
windowsHide: true,
|
|
286
|
+
});
|
|
287
|
+
child.stdout?.setEncoding('utf8');
|
|
288
|
+
child.stderr?.setEncoding('utf8');
|
|
289
|
+
child.stdout?.on('data', (chunk) => {
|
|
290
|
+
stdout += chunk;
|
|
291
|
+
});
|
|
292
|
+
child.stderr?.on('data', (chunk) => {
|
|
293
|
+
stderr += chunk;
|
|
294
|
+
});
|
|
295
|
+
child.once('error', reject);
|
|
296
|
+
child.once('exit', (code, signal) => {
|
|
297
|
+
if (code === 0 && signal === null) {
|
|
298
|
+
resolve(stdout);
|
|
299
|
+
return;
|
|
180
300
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
301
|
+
reject(new Error(`Command failed while resolving dominds@latest runner (code=${String(code)}, signal=${String(signal)}): ${stderr.trim()}`));
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async function resolveNpxLatestRunnerEntrypoint() {
|
|
306
|
+
const npm = await resolveNpmCommandSpec();
|
|
307
|
+
const packageJsonPath = (await captureCommandStdout(npm.command, [
|
|
308
|
+
...npm.args,
|
|
309
|
+
'exec',
|
|
310
|
+
'-y',
|
|
311
|
+
'--package',
|
|
312
|
+
'dominds@latest',
|
|
313
|
+
'--',
|
|
314
|
+
process.execPath,
|
|
315
|
+
'-e',
|
|
316
|
+
[
|
|
317
|
+
"const fs=require('fs');",
|
|
318
|
+
"const path=require('path');",
|
|
319
|
+
"for (const dir of (process.env.PATH||'').split(path.delimiter)) {",
|
|
320
|
+
" if (path.basename(dir) !== '.bin') continue;",
|
|
321
|
+
" const candidate=path.join(path.dirname(dir),'dominds','package.json');",
|
|
322
|
+
' if (fs.existsSync(candidate)) {',
|
|
323
|
+
' process.stdout.write(fs.realpathSync(candidate));',
|
|
324
|
+
' process.exit(0);',
|
|
325
|
+
' }',
|
|
326
|
+
'}',
|
|
327
|
+
"process.stderr.write('Cannot find dominds package in npm exec PATH');",
|
|
328
|
+
'process.exit(1);',
|
|
329
|
+
].join(' '),
|
|
330
|
+
])).trim();
|
|
331
|
+
if (packageJsonPath === '') {
|
|
332
|
+
throw new Error('npm exec dominds@latest did not return a package path');
|
|
333
|
+
}
|
|
334
|
+
const packageRoot = path_1.default.dirname(packageJsonPath);
|
|
335
|
+
const runnerEntrypoint = path_1.default.join(packageRoot, 'dist', RUNNER_JS_FILENAME);
|
|
336
|
+
await promises_1.default.access(runnerEntrypoint);
|
|
337
|
+
return runnerEntrypoint;
|
|
338
|
+
}
|
|
339
|
+
function spawnRunner(params) {
|
|
340
|
+
return (0, child_process_1.spawn)(params.commandSpec.command, [...params.commandSpec.args, ...params.argv], {
|
|
341
|
+
cwd: params.cwd,
|
|
342
|
+
env: {
|
|
343
|
+
...process.env,
|
|
344
|
+
DOMINDS_SUPERVISOR_PID: String(process.pid),
|
|
345
|
+
},
|
|
346
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
347
|
+
windowsHide: false,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function isRunningChild(child) {
|
|
351
|
+
return child.exitCode === null && child.signalCode === null;
|
|
352
|
+
}
|
|
353
|
+
async function runOneShot(params) {
|
|
354
|
+
const commandSpec = getDefaultRunnerCommandSpec();
|
|
355
|
+
const child = (0, child_process_1.spawn)(commandSpec.command, [...commandSpec.args, ...params.runnerArgv], {
|
|
356
|
+
cwd: params.cwd,
|
|
357
|
+
env: process.env,
|
|
358
|
+
stdio: 'inherit',
|
|
359
|
+
windowsHide: false,
|
|
360
|
+
});
|
|
361
|
+
return await new Promise((resolve) => {
|
|
362
|
+
child.once('error', (error) => {
|
|
363
|
+
console.error(`Failed to start dominds-runner: ${error.message}`);
|
|
364
|
+
resolve(1);
|
|
365
|
+
});
|
|
366
|
+
child.once('exit', (code, signal) => {
|
|
367
|
+
if (signal !== null) {
|
|
368
|
+
console.error(`dominds-runner terminated by signal: ${signal}`);
|
|
369
|
+
resolve(1);
|
|
370
|
+
return;
|
|
184
371
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
372
|
+
resolve(code ?? 1);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
async function runDevelopmentRunner(params) {
|
|
377
|
+
try {
|
|
378
|
+
process.chdir(params.cwd);
|
|
379
|
+
const { main: runnerMain } = await Promise.resolve().then(() => __importStar(require('./cli-runner')));
|
|
380
|
+
await runnerMain(params.runnerArgv);
|
|
381
|
+
return 0;
|
|
190
382
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
printHelp();
|
|
196
|
-
process.exit(0);
|
|
383
|
+
catch (error) {
|
|
384
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
385
|
+
console.error(`dominds development runner failed: ${message}`);
|
|
386
|
+
return 1;
|
|
197
387
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
388
|
+
}
|
|
389
|
+
async function runSupervised(params) {
|
|
390
|
+
let backoffMs = INITIAL_RESTART_BACKOFF_MS;
|
|
391
|
+
const stopState = { stopping: false };
|
|
392
|
+
let activeChild = null;
|
|
393
|
+
const restartState = {
|
|
394
|
+
pending: null,
|
|
395
|
+
};
|
|
396
|
+
const invalidRestartState = { pending: null };
|
|
397
|
+
let runnerCommandSpec = getDefaultRunnerCommandSpec();
|
|
398
|
+
const stopFromSignal = (signal) => {
|
|
399
|
+
if (stopState.stopping)
|
|
400
|
+
return;
|
|
401
|
+
stopState.stopping = true;
|
|
402
|
+
const child = activeChild;
|
|
403
|
+
if (child !== null && child.exitCode === null && child.signalCode === null) {
|
|
404
|
+
child.kill(signal);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
process.once('SIGINT', stopFromSignal);
|
|
408
|
+
process.once('SIGTERM', stopFromSignal);
|
|
409
|
+
if (process.platform === 'win32') {
|
|
410
|
+
process.once('SIGBREAK', stopFromSignal);
|
|
201
411
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
(subcommand === 'manual' && subcommandArgs.includes('-h')) ||
|
|
210
|
-
((subcommand === 'create' || subcommand === 'new') && subcommandArgs.includes('-h')) ||
|
|
211
|
-
(subcommand === 'install' &&
|
|
212
|
-
(subcommandArgs.includes('--help') || subcommandArgs.includes('-h'))) ||
|
|
213
|
-
(subcommand === 'doctor' &&
|
|
214
|
-
(subcommandArgs.includes('--help') || subcommandArgs.includes('-h'))) ||
|
|
215
|
-
(subcommand === 'enable' &&
|
|
216
|
-
(subcommandArgs.includes('--help') || subcommandArgs.includes('-h'))) ||
|
|
217
|
-
(subcommand === 'disable' &&
|
|
218
|
-
(subcommandArgs.includes('--help') || subcommandArgs.includes('-h'))) ||
|
|
219
|
-
(subcommand === 'uninstall' &&
|
|
220
|
-
(subcommandArgs.includes('--help') || subcommandArgs.includes('-h'))) ||
|
|
221
|
-
(subcommand === 'update' &&
|
|
222
|
-
(subcommandArgs.includes('--help') || subcommandArgs.includes('-h')));
|
|
223
|
-
if (!shouldSkipRtwsSetup) {
|
|
224
|
-
if (parsed.chdir) {
|
|
225
|
-
try {
|
|
226
|
-
process.chdir(parsed.chdir);
|
|
412
|
+
while (true) {
|
|
413
|
+
let restartExitTimer = null;
|
|
414
|
+
let restartKillTimer = null;
|
|
415
|
+
const clearRestartExitEnforcement = () => {
|
|
416
|
+
if (restartExitTimer !== null) {
|
|
417
|
+
clearTimeout(restartExitTimer);
|
|
418
|
+
restartExitTimer = null;
|
|
227
419
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
420
|
+
if (restartKillTimer !== null) {
|
|
421
|
+
clearTimeout(restartKillTimer);
|
|
422
|
+
restartKillTimer = null;
|
|
231
423
|
}
|
|
424
|
+
};
|
|
425
|
+
const scheduleRestartExitEnforcement = (child, message) => {
|
|
426
|
+
clearRestartExitEnforcement();
|
|
427
|
+
restartExitTimer = setTimeout(() => {
|
|
428
|
+
if (!isRunningChild(child))
|
|
429
|
+
return;
|
|
430
|
+
appendSupervisorTraceSoon(message, 'supervisor.restart_runner_exit_timeout', {
|
|
431
|
+
runnerPid: child.pid ?? null,
|
|
432
|
+
graceMs: RESTART_RUNNER_EXIT_GRACE_MS,
|
|
433
|
+
});
|
|
434
|
+
console.error(`dominds-runner did not exit within ${String(RESTART_RUNNER_EXIT_GRACE_MS)}ms after restart request; sending SIGTERM`);
|
|
435
|
+
child.kill('SIGTERM');
|
|
436
|
+
restartKillTimer = setTimeout(() => {
|
|
437
|
+
if (!isRunningChild(child))
|
|
438
|
+
return;
|
|
439
|
+
appendSupervisorTraceSoon(message, 'supervisor.restart_runner_kill_timeout', {
|
|
440
|
+
runnerPid: child.pid ?? null,
|
|
441
|
+
graceMs: RESTART_RUNNER_KILL_GRACE_MS,
|
|
442
|
+
});
|
|
443
|
+
console.error(`dominds-runner still did not exit after SIGTERM; sending SIGKILL before restart`);
|
|
444
|
+
child.kill('SIGKILL');
|
|
445
|
+
}, RESTART_RUNNER_KILL_GRACE_MS);
|
|
446
|
+
}, RESTART_RUNNER_EXIT_GRACE_MS);
|
|
447
|
+
};
|
|
448
|
+
const scheduleInvalidRestartExitEnforcement = (child, parsed) => {
|
|
449
|
+
clearRestartExitEnforcement();
|
|
450
|
+
restartKillTimer = setTimeout(() => {
|
|
451
|
+
if (!isRunningChild(child))
|
|
452
|
+
return;
|
|
453
|
+
appendInvalidSupervisorTraceSoon(parsed, 'supervisor.invalid_restart_runner_kill_timeout', {
|
|
454
|
+
runnerPid: child.pid ?? null,
|
|
455
|
+
graceMs: RESTART_RUNNER_KILL_GRACE_MS,
|
|
456
|
+
});
|
|
457
|
+
console.error(`dominds-runner did not exit after invalid restart message; sending SIGKILL`);
|
|
458
|
+
child.kill('SIGKILL');
|
|
459
|
+
}, RESTART_RUNNER_KILL_GRACE_MS);
|
|
460
|
+
};
|
|
461
|
+
const child = spawnRunner({
|
|
462
|
+
cwd: params.cwd,
|
|
463
|
+
argv: params.runnerArgv,
|
|
464
|
+
commandSpec: runnerCommandSpec,
|
|
465
|
+
});
|
|
466
|
+
activeChild = child;
|
|
467
|
+
const exit = await new Promise((resolve) => {
|
|
468
|
+
child.once('error', (error) => {
|
|
469
|
+
console.error(`Failed to start dominds-runner: ${error.message}`);
|
|
470
|
+
resolve({ code: 1, signal: null });
|
|
471
|
+
});
|
|
472
|
+
child.on('message', (raw) => {
|
|
473
|
+
const parsed = parseRunnerMessage(raw);
|
|
474
|
+
if (parsed.kind === 'ignore')
|
|
475
|
+
return;
|
|
476
|
+
if (parsed.kind === 'invalid') {
|
|
477
|
+
if (invalidRestartState.pending !== null)
|
|
478
|
+
return;
|
|
479
|
+
console.error(`Invalid dominds-runner restart message: ${parsed.reason}`);
|
|
480
|
+
invalidRestartState.pending = parsed.reason;
|
|
481
|
+
appendInvalidSupervisorTraceSoon(parsed);
|
|
482
|
+
child.kill('SIGTERM');
|
|
483
|
+
scheduleInvalidRestartExitEnforcement(child, parsed);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const { message } = parsed;
|
|
487
|
+
restartState.pending = message;
|
|
488
|
+
appendSupervisorTraceSoon(message, 'supervisor.restart_requested', {
|
|
489
|
+
runnerPid: child.pid ?? null,
|
|
490
|
+
cwd: message.cwd,
|
|
491
|
+
host: message.host,
|
|
492
|
+
port: message.port,
|
|
493
|
+
});
|
|
494
|
+
scheduleRestartExitEnforcement(child, message);
|
|
495
|
+
});
|
|
496
|
+
child.once('exit', (code, signal) => {
|
|
497
|
+
clearRestartExitEnforcement();
|
|
498
|
+
resolve({ code, signal });
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
activeChild = null;
|
|
502
|
+
if (stopState.stopping) {
|
|
503
|
+
return exit.signal === null ? (exit.code ?? 0) : 1;
|
|
232
504
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
505
|
+
if (invalidRestartState.pending !== null) {
|
|
506
|
+
console.error(`dominds supervisor stopped after invalid restart message: ${invalidRestartState.pending}`);
|
|
507
|
+
return 1;
|
|
508
|
+
}
|
|
509
|
+
if (restartState.pending !== null) {
|
|
510
|
+
const restart = restartState.pending;
|
|
511
|
+
restartState.pending = null;
|
|
512
|
+
while (true) {
|
|
513
|
+
await appendSupervisorTraceBestEffort(restart, 'supervisor.wait_port_release.start', {
|
|
514
|
+
host: restart.host,
|
|
515
|
+
port: restart.port,
|
|
516
|
+
timeoutMs: PORT_RELEASE_TIMEOUT_MS,
|
|
517
|
+
});
|
|
518
|
+
const released = await waitForPortRelease({
|
|
519
|
+
host: restart.host,
|
|
520
|
+
port: restart.port,
|
|
521
|
+
stopState,
|
|
522
|
+
});
|
|
523
|
+
await appendSupervisorTraceBestEffort(restart, 'supervisor.wait_port_release.finish', {
|
|
524
|
+
host: restart.host,
|
|
525
|
+
port: restart.port,
|
|
526
|
+
released,
|
|
527
|
+
});
|
|
528
|
+
if (stopState.stopping)
|
|
529
|
+
return 0;
|
|
530
|
+
if (released)
|
|
531
|
+
break;
|
|
532
|
+
await appendSupervisorTraceBestEffort(restart, 'supervisor.restart_blocked_port_busy', {
|
|
533
|
+
host: restart.host,
|
|
534
|
+
port: restart.port,
|
|
535
|
+
retryDelayMs: backoffMs,
|
|
259
536
|
});
|
|
537
|
+
console.error(`Dominds restart is waiting because port ${restart.host}:${String(restart.port)} is still busy; retrying in ${String(backoffMs)}ms`);
|
|
538
|
+
await delayUnlessStopping(backoffMs, stopState);
|
|
539
|
+
if (stopState.stopping)
|
|
540
|
+
return 0;
|
|
541
|
+
backoffMs = Math.min(backoffMs * 2, MAX_RESTART_BACKOFF_MS);
|
|
260
542
|
}
|
|
543
|
+
if (restart.runKind === 'npx') {
|
|
544
|
+
while (true) {
|
|
545
|
+
try {
|
|
546
|
+
const runnerEntrypoint = await resolveNpxLatestRunnerEntrypoint();
|
|
547
|
+
runnerCommandSpec = { command: process.execPath, args: [runnerEntrypoint] };
|
|
548
|
+
await appendSupervisorTraceBestEffort(restart, 'supervisor.npx_latest_runner_resolved', {
|
|
549
|
+
runnerEntrypoint,
|
|
550
|
+
});
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
555
|
+
await appendSupervisorTraceBestEffort(restart, 'supervisor.npx_latest_runner_error', {
|
|
556
|
+
message,
|
|
557
|
+
retryDelayMs: backoffMs,
|
|
558
|
+
});
|
|
559
|
+
console.error(`Dominds restart could not resolve dominds@latest runner: ${message}; retrying in ${String(backoffMs)}ms`);
|
|
560
|
+
await delayUnlessStopping(backoffMs, stopState);
|
|
561
|
+
if (stopState.stopping)
|
|
562
|
+
return 0;
|
|
563
|
+
backoffMs = Math.min(backoffMs * 2, MAX_RESTART_BACKOFF_MS);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
runnerCommandSpec = getDefaultRunnerCommandSpec();
|
|
569
|
+
}
|
|
570
|
+
backoffMs = INITIAL_RESTART_BACKOFF_MS;
|
|
571
|
+
continue;
|
|
261
572
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// Route to appropriate subcommand
|
|
268
|
-
switch (subcommand) {
|
|
269
|
-
case 'webui':
|
|
270
|
-
await runSubcommand('webui', subcommandArgs);
|
|
271
|
-
break;
|
|
272
|
-
case 'tui':
|
|
273
|
-
case 'run':
|
|
274
|
-
await runSubcommand('tui', subcommandArgs);
|
|
275
|
-
break;
|
|
276
|
-
case 'read':
|
|
277
|
-
await runSubcommand('read', subcommandArgs);
|
|
278
|
-
break;
|
|
279
|
-
case 'man':
|
|
280
|
-
await runSubcommand('manual', subcommandArgs);
|
|
281
|
-
break;
|
|
282
|
-
case 'manual':
|
|
283
|
-
await runSubcommand('manual', subcommandArgs);
|
|
284
|
-
break;
|
|
285
|
-
case 'validate_team_def':
|
|
286
|
-
await runSubcommand('validate_team_def', subcommandArgs);
|
|
287
|
-
break;
|
|
288
|
-
case 'cert':
|
|
289
|
-
await runSubcommand('cert', subcommandArgs);
|
|
290
|
-
break;
|
|
291
|
-
case 'create':
|
|
292
|
-
case 'new':
|
|
293
|
-
await runSubcommand('create', subcommandArgs);
|
|
294
|
-
break;
|
|
295
|
-
case 'install':
|
|
296
|
-
await runSubcommand('install', subcommandArgs);
|
|
297
|
-
break;
|
|
298
|
-
case 'doctor':
|
|
299
|
-
await runSubcommand('doctor', subcommandArgs);
|
|
300
|
-
break;
|
|
301
|
-
case 'enable':
|
|
302
|
-
await runSubcommand('enable', subcommandArgs);
|
|
303
|
-
break;
|
|
304
|
-
case 'disable':
|
|
305
|
-
await runSubcommand('disable', subcommandArgs);
|
|
306
|
-
break;
|
|
307
|
-
case 'uninstall':
|
|
308
|
-
await runSubcommand('uninstall', subcommandArgs);
|
|
309
|
-
break;
|
|
310
|
-
case 'update':
|
|
311
|
-
await runSubcommand('update', subcommandArgs);
|
|
312
|
-
break;
|
|
313
|
-
default:
|
|
314
|
-
console.error(`Error: Unknown subcommand '${subcommand}'`);
|
|
315
|
-
console.error(`Run 'dominds help' for usage information.`);
|
|
316
|
-
process.exit(1);
|
|
573
|
+
console.error(`dominds-runner exited unexpectedly (code=${String(exit.code)}, signal=${String(exit.signal)}); restarting in ${String(backoffMs)}ms`);
|
|
574
|
+
await delayUnlessStopping(backoffMs, stopState);
|
|
575
|
+
if (stopState.stopping)
|
|
576
|
+
return 0;
|
|
577
|
+
backoffMs = Math.min(backoffMs * 2, MAX_RESTART_BACKOFF_MS);
|
|
317
578
|
}
|
|
318
579
|
}
|
|
319
|
-
async function
|
|
580
|
+
async function main(argv = process.argv.slice(2)) {
|
|
581
|
+
let parsed;
|
|
320
582
|
try {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
else if (subcommand === 'tui') {
|
|
326
|
-
await (0, tui_1.main)(args);
|
|
327
|
-
}
|
|
328
|
-
else if (subcommand === 'read') {
|
|
329
|
-
await (0, read_1.main)(args);
|
|
330
|
-
}
|
|
331
|
-
else if (subcommand === 'manual') {
|
|
332
|
-
await (0, manual_1.main)(args);
|
|
333
|
-
}
|
|
334
|
-
else if (subcommand === 'validate_team_def') {
|
|
335
|
-
await (0, validate_team_def_1.main)(args);
|
|
336
|
-
}
|
|
337
|
-
else if (subcommand === 'cert') {
|
|
338
|
-
await (0, cert_1.main)(args);
|
|
339
|
-
}
|
|
340
|
-
else if (subcommand === 'create') {
|
|
341
|
-
await (0, create_1.main)(args);
|
|
342
|
-
}
|
|
343
|
-
else if (subcommand === 'install') {
|
|
344
|
-
await (0, install_1.main)(args);
|
|
345
|
-
}
|
|
346
|
-
else if (subcommand === 'doctor') {
|
|
347
|
-
await (0, doctor_1.main)(args);
|
|
348
|
-
}
|
|
349
|
-
else if (subcommand === 'enable') {
|
|
350
|
-
await (0, enable_1.main)(args);
|
|
351
|
-
}
|
|
352
|
-
else if (subcommand === 'disable') {
|
|
353
|
-
await (0, disable_1.main)(args);
|
|
354
|
-
}
|
|
355
|
-
else if (subcommand === 'uninstall') {
|
|
356
|
-
await (0, uninstall_1.main)(args);
|
|
357
|
-
}
|
|
358
|
-
else if (subcommand === 'update') {
|
|
359
|
-
await (0, update_1.main)(args);
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
console.error(`Error: Subcommand '${subcommand}' not implemented`);
|
|
363
|
-
process.exit(1);
|
|
583
|
+
parsed = parseSupervisorArgs(argv);
|
|
584
|
+
const stat = await promises_1.default.stat(parsed.cwd);
|
|
585
|
+
if (!stat.isDirectory()) {
|
|
586
|
+
throw new Error(`rtws path is not a directory: ${parsed.cwd}`);
|
|
364
587
|
}
|
|
365
588
|
}
|
|
366
|
-
catch (
|
|
367
|
-
|
|
589
|
+
catch (error) {
|
|
590
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
591
|
+
console.error(`Error: ${message}`);
|
|
368
592
|
process.exit(1);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (isLongRunningCommand(parsed.runnerArgv) && isDevelopmentMode(parsed.runnerArgv)) {
|
|
596
|
+
const exitCode = await runDevelopmentRunner(parsed);
|
|
597
|
+
if (exitCode !== 0)
|
|
598
|
+
process.exit(exitCode);
|
|
599
|
+
return;
|
|
369
600
|
}
|
|
601
|
+
const exitCode = isLongRunningCommand(parsed.runnerArgv)
|
|
602
|
+
? await runSupervised(parsed)
|
|
603
|
+
: await runOneShot(parsed);
|
|
604
|
+
process.exit(exitCode);
|
|
370
605
|
}
|
|
371
606
|
if (require.main === module) {
|
|
372
|
-
main().catch((
|
|
373
|
-
|
|
607
|
+
main().catch((error) => {
|
|
608
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
609
|
+
console.error(`Unhandled dominds supervisor error: ${message}`);
|
|
374
610
|
process.exit(1);
|
|
375
611
|
});
|
|
376
612
|
}
|