dominds 1.25.13 → 1.25.15
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/dialog-display-state.d.ts +4 -2
- package/dist/dialog-display-state.js +26 -6
- package/dist/llm/kernel-driver/drive.js +15 -0
- package/dist/llm/kernel-driver/flow.js +3 -0
- package/dist/llm/kernel-driver/sideDialog.js +1 -9
- package/dist/mcp/supervisor.js +2 -4
- package/dist/minds/system-prompt.js +4 -4
- package/dist/runtime/driver-messages.d.ts +5 -0
- package/dist/runtime/driver-messages.js +103 -59
- package/dist/server/api-routes.js +5 -8
- package/dist/server/dominds-self-update.js +584 -106
- package/dist/server/websocket-handler.js +5 -15
- package/dist/tools/app-reminders.js +6 -6
- package/dist/tools/cmd-runner-protocol.d.ts +1 -0
- package/dist/tools/cmd-runner-protocol.js +8 -1
- package/dist/tools/cmd-runner.js +33 -6
- package/dist/tools/mcp.js +2 -8
- package/dist/tools/os.js +27 -22
- package/dist/tools/pending-tellask-reminder.js +10 -10
- package/dist/tools/process-kill.d.ts +4 -0
- package/dist/tools/process-kill.js +48 -0
- package/dist/tools/prompts/os/en/tools.md +2 -2
- package/dist/tools/prompts/os/zh/tools.md +2 -2
- package/package.json +4 -4
- package/webapp/dist/assets/{_basePickBy-B1-brAPU.js → _basePickBy-bB-pfQPi.js} +3 -3
- package/webapp/dist/assets/{_basePickBy-B1-brAPU.js.map → _basePickBy-bB-pfQPi.js.map} +1 -1
- package/webapp/dist/assets/{_baseUniq-BuB0jmKD.js → _baseUniq-CGrWBZre.js} +2 -2
- package/webapp/dist/assets/{_baseUniq-BuB0jmKD.js.map → _baseUniq-CGrWBZre.js.map} +1 -1
- package/webapp/dist/assets/{arc-CvsBvjnB.js → arc-I9vbIaKk.js} +2 -2
- package/webapp/dist/assets/{arc-CvsBvjnB.js.map → arc-I9vbIaKk.js.map} +1 -1
- package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-B-xGuLbb.js → architectureDiagram-2XIMDMQ5-CrwjTxLz.js} +7 -7
- package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-B-xGuLbb.js.map → architectureDiagram-2XIMDMQ5-CrwjTxLz.js.map} +1 -1
- package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-Bu53fwsa.js → blockDiagram-WCTKOSBZ-DTm0qfvP.js} +7 -7
- package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-Bu53fwsa.js.map → blockDiagram-WCTKOSBZ-DTm0qfvP.js.map} +1 -1
- package/webapp/dist/assets/{c4Diagram-IC4MRINW-D9-FJ4LB.js → c4Diagram-IC4MRINW-DwRQtzZF.js} +3 -3
- package/webapp/dist/assets/{c4Diagram-IC4MRINW-D9-FJ4LB.js.map → c4Diagram-IC4MRINW-DwRQtzZF.js.map} +1 -1
- package/webapp/dist/assets/{channel-D1VPurpu.js → channel-BHLOwBnI.js} +2 -2
- package/webapp/dist/assets/{channel-D1VPurpu.js.map → channel-BHLOwBnI.js.map} +1 -1
- package/webapp/dist/assets/{chunk-4BX2VUAB-DB14wcWS.js → chunk-4BX2VUAB-C-uXnDVV.js} +2 -2
- package/webapp/dist/assets/{chunk-4BX2VUAB-DB14wcWS.js.map → chunk-4BX2VUAB-C-uXnDVV.js.map} +1 -1
- package/webapp/dist/assets/{chunk-55IACEB6-C5KCE85A.js → chunk-55IACEB6-Dq9BI2N6.js} +2 -2
- package/webapp/dist/assets/{chunk-55IACEB6-C5KCE85A.js.map → chunk-55IACEB6-Dq9BI2N6.js.map} +1 -1
- package/webapp/dist/assets/{chunk-FMBD7UC4-Bb6zm0iH.js → chunk-FMBD7UC4-DR9kFZOV.js} +2 -2
- package/webapp/dist/assets/{chunk-FMBD7UC4-Bb6zm0iH.js.map → chunk-FMBD7UC4-DR9kFZOV.js.map} +1 -1
- package/webapp/dist/assets/{chunk-JSJVCQXG-CJPrv6fM.js → chunk-JSJVCQXG-xnc6SVE5.js} +2 -2
- package/webapp/dist/assets/{chunk-JSJVCQXG-CJPrv6fM.js.map → chunk-JSJVCQXG-xnc6SVE5.js.map} +1 -1
- package/webapp/dist/assets/{chunk-KX2RTZJC-8J1Swk7E.js → chunk-KX2RTZJC-rUxlh4aS.js} +2 -2
- package/webapp/dist/assets/{chunk-KX2RTZJC-8J1Swk7E.js.map → chunk-KX2RTZJC-rUxlh4aS.js.map} +1 -1
- package/webapp/dist/assets/{chunk-NQ4KR5QH-DySHY4x9.js → chunk-NQ4KR5QH-BGU4PhTQ.js} +4 -4
- package/webapp/dist/assets/{chunk-NQ4KR5QH-DySHY4x9.js.map → chunk-NQ4KR5QH-BGU4PhTQ.js.map} +1 -1
- package/webapp/dist/assets/{chunk-QZHKN3VN-CTO5Z-4P.js → chunk-QZHKN3VN-CAlu4xE7.js} +2 -2
- package/webapp/dist/assets/{chunk-QZHKN3VN-CTO5Z-4P.js.map → chunk-QZHKN3VN-CAlu4xE7.js.map} +1 -1
- package/webapp/dist/assets/{chunk-WL4C6EOR-Ci_Ec8Ax.js → chunk-WL4C6EOR-zZXRkBBY.js} +6 -6
- package/webapp/dist/assets/{chunk-WL4C6EOR-Ci_Ec8Ax.js.map → chunk-WL4C6EOR-zZXRkBBY.js.map} +1 -1
- package/webapp/dist/assets/{classDiagram-VBA2DB6C-CxAxBvv7.js → classDiagram-VBA2DB6C-DI1CfOtL.js} +7 -7
- package/webapp/dist/assets/{classDiagram-VBA2DB6C-CxAxBvv7.js.map → classDiagram-VBA2DB6C-DI1CfOtL.js.map} +1 -1
- package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-CxAxBvv7.js → classDiagram-v2-RAHNMMFH-DI1CfOtL.js} +7 -7
- package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-CxAxBvv7.js.map → classDiagram-v2-RAHNMMFH-DI1CfOtL.js.map} +1 -1
- package/webapp/dist/assets/{clone-ePiNaiNY.js → clone-quTqCNPA.js} +2 -2
- package/webapp/dist/assets/{clone-ePiNaiNY.js.map → clone-quTqCNPA.js.map} +1 -1
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-j-Ex-Sef.js → cose-bilkent-S5V4N54A-BY28yO7Y.js} +2 -2
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-j-Ex-Sef.js.map → cose-bilkent-S5V4N54A-BY28yO7Y.js.map} +1 -1
- package/webapp/dist/assets/{dagre-KLK3FWXG-ihZ2wOCM.js → dagre-KLK3FWXG-DLyZ54D-.js} +7 -7
- package/webapp/dist/assets/{dagre-KLK3FWXG-ihZ2wOCM.js.map → dagre-KLK3FWXG-DLyZ54D-.js.map} +1 -1
- package/webapp/dist/assets/{diagram-E7M64L7V-Cp4GQGS7.js → diagram-E7M64L7V-CD-HWZAJ.js} +8 -8
- package/webapp/dist/assets/{diagram-E7M64L7V-Cp4GQGS7.js.map → diagram-E7M64L7V-CD-HWZAJ.js.map} +1 -1
- package/webapp/dist/assets/{diagram-IFDJBPK2-B70cgyS5.js → diagram-IFDJBPK2-BHOQ9HH2.js} +7 -7
- package/webapp/dist/assets/{diagram-IFDJBPK2-B70cgyS5.js.map → diagram-IFDJBPK2-BHOQ9HH2.js.map} +1 -1
- package/webapp/dist/assets/{diagram-P4PSJMXO-DMOv7eKE.js → diagram-P4PSJMXO-OEPgBDcD.js} +7 -7
- package/webapp/dist/assets/{diagram-P4PSJMXO-DMOv7eKE.js.map → diagram-P4PSJMXO-OEPgBDcD.js.map} +1 -1
- package/webapp/dist/assets/{erDiagram-INFDFZHY-BKpXWjIc.js → erDiagram-INFDFZHY-Dcgo204J.js} +5 -5
- package/webapp/dist/assets/{erDiagram-INFDFZHY-BKpXWjIc.js.map → erDiagram-INFDFZHY-Dcgo204J.js.map} +1 -1
- package/webapp/dist/assets/{flowDiagram-PKNHOUZH-DgrItj0h.js → flowDiagram-PKNHOUZH-CWLo3Lkf.js} +7 -7
- package/webapp/dist/assets/{flowDiagram-PKNHOUZH-DgrItj0h.js.map → flowDiagram-PKNHOUZH-CWLo3Lkf.js.map} +1 -1
- package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-7-8hlYsT.js → ganttDiagram-A5KZAMGK-DWXL4ZD6.js} +3 -3
- package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-7-8hlYsT.js.map → ganttDiagram-A5KZAMGK-DWXL4ZD6.js.map} +1 -1
- package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-cPSaCUUk.js → gitGraphDiagram-K3NZZRJ6-7iLQbTrH.js} +8 -8
- package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-cPSaCUUk.js.map → gitGraphDiagram-K3NZZRJ6-7iLQbTrH.js.map} +1 -1
- package/webapp/dist/assets/{graph-CAlg3tEk.js → graph-Bq1MLNWN.js} +3 -3
- package/webapp/dist/assets/{graph-CAlg3tEk.js.map → graph-Bq1MLNWN.js.map} +1 -1
- package/webapp/dist/assets/{index-DLTS_eOh.js → index-r4An5YoX.js} +245 -60
- package/webapp/dist/assets/{index-DLTS_eOh.js.map → index-r4An5YoX.js.map} +1 -1
- package/webapp/dist/assets/{infoDiagram-LFFYTUFH-CHJHvxMC.js → infoDiagram-LFFYTUFH-CoF5OE3_.js} +6 -6
- package/webapp/dist/assets/{infoDiagram-LFFYTUFH-CHJHvxMC.js.map → infoDiagram-LFFYTUFH-CoF5OE3_.js.map} +1 -1
- package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-S8N-XZ8E.js → ishikawaDiagram-PHBUUO56-iKggfQhA.js} +2 -2
- package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-S8N-XZ8E.js.map → ishikawaDiagram-PHBUUO56-iKggfQhA.js.map} +1 -1
- package/webapp/dist/assets/{journeyDiagram-4ABVD52K-ChHNpMtH.js → journeyDiagram-4ABVD52K-NYDQ7_xv.js} +5 -5
- package/webapp/dist/assets/{journeyDiagram-4ABVD52K-ChHNpMtH.js.map → journeyDiagram-4ABVD52K-NYDQ7_xv.js.map} +1 -1
- package/webapp/dist/assets/{kanban-definition-K7BYSVSG-Cqxd99wZ.js → kanban-definition-K7BYSVSG-YWCnJwk0.js} +3 -3
- package/webapp/dist/assets/{kanban-definition-K7BYSVSG-Cqxd99wZ.js.map → kanban-definition-K7BYSVSG-YWCnJwk0.js.map} +1 -1
- package/webapp/dist/assets/{layout-uOLcVthp.js → layout-CE6gccWy.js} +5 -5
- package/webapp/dist/assets/{layout-uOLcVthp.js.map → layout-CE6gccWy.js.map} +1 -1
- package/webapp/dist/assets/{linear-Ga_f4H_w.js → linear-12arIJK-.js} +2 -2
- package/webapp/dist/assets/{linear-Ga_f4H_w.js.map → linear-12arIJK-.js.map} +1 -1
- package/webapp/dist/assets/{mindmap-definition-YRQLILUH-TSH7wOlZ.js → mindmap-definition-YRQLILUH-D_5iNhfP.js} +4 -4
- package/webapp/dist/assets/{mindmap-definition-YRQLILUH-TSH7wOlZ.js.map → mindmap-definition-YRQLILUH-D_5iNhfP.js.map} +1 -1
- package/webapp/dist/assets/{pieDiagram-SKSYHLDU-DPXszqns.js → pieDiagram-SKSYHLDU-DCbLQaXM.js} +8 -8
- package/webapp/dist/assets/{pieDiagram-SKSYHLDU-DPXszqns.js.map → pieDiagram-SKSYHLDU-DCbLQaXM.js.map} +1 -1
- package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BgA_GVhR.js → quadrantDiagram-337W2JSQ-CVRDOyty.js} +3 -3
- package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BgA_GVhR.js.map → quadrantDiagram-337W2JSQ-CVRDOyty.js.map} +1 -1
- package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-CM-47daj.js → requirementDiagram-Z7DCOOCP-iBpmayOS.js} +4 -4
- package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-CM-47daj.js.map → requirementDiagram-Z7DCOOCP-iBpmayOS.js.map} +1 -1
- package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-CxDCwHAj.js → sankeyDiagram-WA2Y5GQK-DqzWCtib.js} +2 -2
- package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-CxDCwHAj.js.map → sankeyDiagram-WA2Y5GQK-DqzWCtib.js.map} +1 -1
- package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-1UJP6Fff.js → sequenceDiagram-2WXFIKYE-DHzt-u2j.js} +4 -4
- package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-1UJP6Fff.js.map → sequenceDiagram-2WXFIKYE-DHzt-u2j.js.map} +1 -1
- package/webapp/dist/assets/{stateDiagram-RAJIS63D-B2aqr0KQ.js → stateDiagram-RAJIS63D-D14FagfU.js} +9 -9
- package/webapp/dist/assets/{stateDiagram-RAJIS63D-B2aqr0KQ.js.map → stateDiagram-RAJIS63D-D14FagfU.js.map} +1 -1
- package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-DvmkevVb.js → stateDiagram-v2-FVOUBMTO-CZRolF-A.js} +5 -5
- package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-DvmkevVb.js.map → stateDiagram-v2-FVOUBMTO-CZRolF-A.js.map} +1 -1
- package/webapp/dist/assets/{timeline-definition-YZTLITO2-CaOrqzT1.js → timeline-definition-YZTLITO2-L4O4kc8p.js} +3 -3
- package/webapp/dist/assets/{timeline-definition-YZTLITO2-CaOrqzT1.js.map → timeline-definition-YZTLITO2-L4O4kc8p.js.map} +1 -1
- package/webapp/dist/assets/{treemap-KZPCXAKY-CWs_8GJm.js → treemap-KZPCXAKY-CvHKWhtl.js} +5 -5
- package/webapp/dist/assets/{treemap-KZPCXAKY-CWs_8GJm.js.map → treemap-KZPCXAKY-CvHKWhtl.js.map} +1 -1
- package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BuBxFDz6.js → vennDiagram-LZ73GAT5-8sPBS5jq.js} +2 -2
- package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BuBxFDz6.js.map → vennDiagram-LZ73GAT5-8sPBS5jq.js.map} +1 -1
- package/webapp/dist/assets/{xychartDiagram-JWTSCODW-ChjL-_2b.js → xychartDiagram-JWTSCODW-CqLmOgCw.js} +3 -3
- package/webapp/dist/assets/{xychartDiagram-JWTSCODW-ChjL-_2b.js.map → xychartDiagram-JWTSCODW-CqLmOgCw.js.map} +1 -1
- package/webapp/dist/index.html +1 -1
|
@@ -21,7 +21,7 @@ const LATEST_VERSION_CHECK_TIMEOUT_MS = 60000;
|
|
|
21
21
|
const RESTART_PORT_RELEASE_TIMEOUT_MS = 15000;
|
|
22
22
|
const RESTART_PORT_PROBE_INTERVAL_MS = 150;
|
|
23
23
|
const RESTART_EXIT_GRACE_MS = 1000;
|
|
24
|
-
const RESTART_FORCE_KILL_AFTER_MS =
|
|
24
|
+
const RESTART_FORCE_KILL_AFTER_MS = 30000;
|
|
25
25
|
const COMMAND_OUTPUT_LOG_LIMIT = 2000;
|
|
26
26
|
const PROXY_URL_ENV_KEYS = new Set([
|
|
27
27
|
'HTTP_PROXY',
|
|
@@ -32,12 +32,14 @@ const PROXY_URL_ENV_KEYS = new Set([
|
|
|
32
32
|
'npm_config_https_proxy',
|
|
33
33
|
]);
|
|
34
34
|
const IDLE_RESTART_STATE = { kind: 'idle' };
|
|
35
|
+
const PROCESS_START_CWD = process.cwd();
|
|
35
36
|
let runtimeConfig = null;
|
|
36
37
|
let latestObservation = { kind: 'unknown' };
|
|
37
38
|
let backgroundCheckTimer = null;
|
|
38
39
|
let installPromise = null;
|
|
39
40
|
let restartPromise = null;
|
|
40
41
|
let restartState = IDLE_RESTART_STATE;
|
|
42
|
+
let installFailureObservation = null;
|
|
41
43
|
let broadcastStatusUpdate = null;
|
|
42
44
|
function normalizeVersionString(value) {
|
|
43
45
|
return value.trim().replace(/^v/i, '');
|
|
@@ -76,9 +78,9 @@ function detectRunKind(mode) {
|
|
|
76
78
|
return 'disabled';
|
|
77
79
|
const scriptPath = (process.argv[1] ?? '').replace(/\\/g, '/');
|
|
78
80
|
if (scriptPath.includes('/_npx/') && scriptPath.includes('/node_modules/dominds/')) {
|
|
79
|
-
return '
|
|
81
|
+
return 'npx';
|
|
80
82
|
}
|
|
81
|
-
return '
|
|
83
|
+
return 'global';
|
|
82
84
|
}
|
|
83
85
|
function hasInteractiveConsole() {
|
|
84
86
|
return Boolean(process.stdin.isTTY || process.stdout.isTTY || process.stderr.isTTY);
|
|
@@ -93,6 +95,94 @@ function getRestartPortProbeHost(host) {
|
|
|
93
95
|
return '::1';
|
|
94
96
|
return host;
|
|
95
97
|
}
|
|
98
|
+
function normalizeComparablePath(value) {
|
|
99
|
+
const normalized = path_1.default.normalize(value);
|
|
100
|
+
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
|
|
101
|
+
}
|
|
102
|
+
async function getComparableRealPath(absPath) {
|
|
103
|
+
try {
|
|
104
|
+
return normalizeComparablePath(await promises_1.default.realpath(absPath));
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const code = getStringErrorProp(error, 'code');
|
|
108
|
+
if (code === 'ENOENT')
|
|
109
|
+
return normalizeComparablePath(path_1.default.resolve(absPath));
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function getCurrentProcessEntrypoint() {
|
|
114
|
+
const entrypoint = process.argv[1];
|
|
115
|
+
if (typeof entrypoint !== 'string' || entrypoint.trim() === '') {
|
|
116
|
+
throw new Error('Cannot restart Dominds because the current process entrypoint is unavailable');
|
|
117
|
+
}
|
|
118
|
+
return entrypoint;
|
|
119
|
+
}
|
|
120
|
+
function buildCurrentProcessRestartArgs() {
|
|
121
|
+
getCurrentProcessEntrypoint();
|
|
122
|
+
return [...process.execArgv, ...process.argv.slice(1)];
|
|
123
|
+
}
|
|
124
|
+
function buildCurrentDomindsCliArgs() {
|
|
125
|
+
return process.argv.slice(2);
|
|
126
|
+
}
|
|
127
|
+
function getNpxWorkspaceDirAbs() {
|
|
128
|
+
const entrypoint = getCurrentProcessEntrypoint().replace(/\\/g, '/');
|
|
129
|
+
const marker = '/node_modules/dominds/';
|
|
130
|
+
const markerIndex = entrypoint.indexOf(marker);
|
|
131
|
+
if (markerIndex < 0)
|
|
132
|
+
return null;
|
|
133
|
+
const nodeModulesPrefix = entrypoint.slice(0, markerIndex);
|
|
134
|
+
const nodeModulesSuffix = '/node_modules';
|
|
135
|
+
if (!nodeModulesPrefix.endsWith(nodeModulesSuffix))
|
|
136
|
+
return null;
|
|
137
|
+
const workspaceDir = nodeModulesPrefix.slice(0, -nodeModulesSuffix.length);
|
|
138
|
+
if (workspaceDir.trim() === '')
|
|
139
|
+
return null;
|
|
140
|
+
return path_1.default.resolve(PROCESS_START_CWD, workspaceDir);
|
|
141
|
+
}
|
|
142
|
+
function parseNpxDomindsPackageSpec(spec) {
|
|
143
|
+
if (spec === 'dominds@latest')
|
|
144
|
+
return { kind: 'latest' };
|
|
145
|
+
if (spec === 'dominds')
|
|
146
|
+
return { kind: 'versionless' };
|
|
147
|
+
if (spec.startsWith('dominds@'))
|
|
148
|
+
return { kind: 'fixed', spec };
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
async function readNpxDomindsSpec() {
|
|
152
|
+
const workspaceDir = getNpxWorkspaceDirAbs();
|
|
153
|
+
if (workspaceDir === null)
|
|
154
|
+
return null;
|
|
155
|
+
const packageJsonPath = path_1.default.join(workspaceDir, 'package.json');
|
|
156
|
+
const raw = await promises_1.default.readFile(packageJsonPath, 'utf8');
|
|
157
|
+
const parsed = JSON.parse(raw);
|
|
158
|
+
if (!isRecord(parsed))
|
|
159
|
+
throw new Error(`Invalid npx package metadata at ${packageJsonPath}`);
|
|
160
|
+
const npxValue = parsed['_npx'];
|
|
161
|
+
const packagesValue = isRecord(npxValue) ? npxValue['packages'] : undefined;
|
|
162
|
+
if (!Array.isArray(packagesValue))
|
|
163
|
+
return null;
|
|
164
|
+
for (const packageSpec of packagesValue) {
|
|
165
|
+
if (typeof packageSpec !== 'string')
|
|
166
|
+
continue;
|
|
167
|
+
const parsedSpec = parseNpxDomindsPackageSpec(packageSpec.trim());
|
|
168
|
+
if (parsedSpec !== null)
|
|
169
|
+
return parsedSpec;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
async function buildNpxLatestRestartLaunchSpec() {
|
|
174
|
+
const npm = await resolveNpmCommandSpec();
|
|
175
|
+
return {
|
|
176
|
+
command: npm.command,
|
|
177
|
+
args: [...npm.args, 'exec', '-y', '--', 'dominds@latest', ...buildCurrentDomindsCliArgs()],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function buildCurrentProcessRestartLaunchSpec() {
|
|
181
|
+
return {
|
|
182
|
+
command: process.execPath,
|
|
183
|
+
args: buildCurrentProcessRestartArgs(),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
96
186
|
function truncateCommandOutput(value) {
|
|
97
187
|
const raw = typeof value === 'string' ? value.trim() : '';
|
|
98
188
|
if (raw.length <= COMMAND_OUTPUT_LOG_LIMIT)
|
|
@@ -136,7 +226,7 @@ function redactProxyUrl(proxyUrl) {
|
|
|
136
226
|
return proxyUrl.replace(/\/\/[^@/]+@/g, '//***@');
|
|
137
227
|
}
|
|
138
228
|
}
|
|
139
|
-
function
|
|
229
|
+
function buildPackageManagerCommandEnv() {
|
|
140
230
|
const env = { ...process.env };
|
|
141
231
|
const httpProxy = getEnvValue(env, 'HTTP_PROXY', 'http_proxy');
|
|
142
232
|
const httpsProxy = getEnvValue(env, 'HTTPS_PROXY', 'https_proxy');
|
|
@@ -195,7 +285,7 @@ async function resolveNpmCommandSpec() {
|
|
|
195
285
|
return {
|
|
196
286
|
command: process.execPath,
|
|
197
287
|
args: [candidate],
|
|
198
|
-
display:
|
|
288
|
+
display: process.execPath + ' ' + candidate,
|
|
199
289
|
};
|
|
200
290
|
}
|
|
201
291
|
catch {
|
|
@@ -203,10 +293,55 @@ async function resolveNpmCommandSpec() {
|
|
|
203
293
|
}
|
|
204
294
|
}
|
|
205
295
|
if (process.platform === 'win32') {
|
|
206
|
-
throw new Error(
|
|
296
|
+
throw new Error('Cannot find bundled npm CLI at ' +
|
|
297
|
+
candidates.join(', ') +
|
|
298
|
+
'. Dominds self-update avoids npm.cmd so the Windows console title is not hijacked.');
|
|
207
299
|
}
|
|
208
300
|
return { command: 'npm', args: [], display: 'npm' };
|
|
209
301
|
}
|
|
302
|
+
async function resolveCorepackPnpmCommandSpec() {
|
|
303
|
+
const nodeDir = path_1.default.dirname(process.execPath);
|
|
304
|
+
const candidates = process.platform === 'win32'
|
|
305
|
+
? [path_1.default.join(nodeDir, 'node_modules', 'corepack', 'dist', 'corepack.js')]
|
|
306
|
+
: [
|
|
307
|
+
path_1.default.join(nodeDir, '..', 'lib', 'node_modules', 'corepack', 'dist', 'corepack.js'),
|
|
308
|
+
path_1.default.join(nodeDir, '..', 'share', 'nodejs', 'corepack', 'dist', 'corepack.js'),
|
|
309
|
+
];
|
|
310
|
+
for (const candidate of candidates) {
|
|
311
|
+
try {
|
|
312
|
+
await promises_1.default.access(candidate);
|
|
313
|
+
return {
|
|
314
|
+
command: process.execPath,
|
|
315
|
+
args: [candidate, 'pnpm'],
|
|
316
|
+
display: process.execPath + ' ' + candidate + ' pnpm',
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
async function resolvePnpmCommandSpec() {
|
|
326
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
327
|
+
if (typeof userAgent === 'string' && userAgent.startsWith('pnpm/')) {
|
|
328
|
+
const npmExecpath = process.env.npm_execpath;
|
|
329
|
+
if (typeof npmExecpath === 'string' && npmExecpath.trim() !== '') {
|
|
330
|
+
return {
|
|
331
|
+
command: process.execPath,
|
|
332
|
+
args: [npmExecpath],
|
|
333
|
+
display: process.execPath + ' ' + npmExecpath,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const corepackPnpm = await resolveCorepackPnpmCommandSpec();
|
|
338
|
+
if (corepackPnpm !== null)
|
|
339
|
+
return corepackPnpm;
|
|
340
|
+
if (process.platform === 'win32') {
|
|
341
|
+
throw new Error('Cannot resolve pnpm CLI without pnpm npm_execpath or bundled Corepack on Windows');
|
|
342
|
+
}
|
|
343
|
+
return { command: 'pnpm', args: [], display: 'pnpm' };
|
|
344
|
+
}
|
|
210
345
|
function getStringOrNumberErrorProp(error, key) {
|
|
211
346
|
const value = getErrorProp(error, key);
|
|
212
347
|
return typeof value === 'string' || typeof value === 'number' ? value : null;
|
|
@@ -244,36 +379,13 @@ function createCommandFailureError(params) {
|
|
|
244
379
|
error.domindsProxyEnvSummary = params.proxyEnvSummary;
|
|
245
380
|
return error;
|
|
246
381
|
}
|
|
247
|
-
async function
|
|
248
|
-
const env =
|
|
382
|
+
async function runPackageManagerCommand(commandSpec, args, params) {
|
|
383
|
+
const env = buildPackageManagerCommandEnv();
|
|
249
384
|
const startedAtMs = Date.now();
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
npm = await resolveNpmCommandSpec();
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
const fallbackCmd = ['npm', ...args].join(' ');
|
|
256
|
-
throw createCommandFailureError({
|
|
257
|
-
cmd: fallbackCmd,
|
|
258
|
-
durationMs: Date.now() - startedAtMs,
|
|
259
|
-
timeoutMs: params.timeoutMs,
|
|
260
|
-
timedOut: false,
|
|
261
|
-
outputExceeded: false,
|
|
262
|
-
capturedOutputChars: 0,
|
|
263
|
-
maxBuffer: params.maxBuffer,
|
|
264
|
-
code: null,
|
|
265
|
-
signal: null,
|
|
266
|
-
killed: null,
|
|
267
|
-
stdout: '',
|
|
268
|
-
stderr: '',
|
|
269
|
-
proxyEnvSummary: summarizeNpmProxyEnv(env),
|
|
270
|
-
cause: error,
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
const cmd = [npm.display, ...args].join(' ');
|
|
385
|
+
const cmd = [commandSpec.display, ...args].join(' ');
|
|
274
386
|
return await new Promise((resolve, reject) => {
|
|
275
|
-
const child = (0, child_process_1.spawn)(
|
|
276
|
-
cwd:
|
|
387
|
+
const child = (0, child_process_1.spawn)(commandSpec.command, [...commandSpec.args, ...args], {
|
|
388
|
+
cwd: PROCESS_START_CWD,
|
|
277
389
|
env,
|
|
278
390
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
279
391
|
windowsHide: true,
|
|
@@ -361,6 +473,56 @@ async function runNpmCommand(args, params) {
|
|
|
361
473
|
});
|
|
362
474
|
});
|
|
363
475
|
}
|
|
476
|
+
async function runCurrentDomindsVersionProbe() {
|
|
477
|
+
const startedAtMs = Date.now();
|
|
478
|
+
const timeoutMs = 15000;
|
|
479
|
+
return await new Promise((resolve, reject) => {
|
|
480
|
+
const child = (0, child_process_1.spawn)(process.execPath, [getCurrentProcessEntrypoint(), '--version'], {
|
|
481
|
+
cwd: PROCESS_START_CWD,
|
|
482
|
+
env: process.env,
|
|
483
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
484
|
+
windowsHide: true,
|
|
485
|
+
shell: false,
|
|
486
|
+
});
|
|
487
|
+
let stdout = '';
|
|
488
|
+
let stderr = '';
|
|
489
|
+
let settled = false;
|
|
490
|
+
let timedOut = false;
|
|
491
|
+
const finish = (fn) => {
|
|
492
|
+
if (settled)
|
|
493
|
+
return;
|
|
494
|
+
settled = true;
|
|
495
|
+
fn();
|
|
496
|
+
};
|
|
497
|
+
const timer = setTimeout(() => {
|
|
498
|
+
timedOut = true;
|
|
499
|
+
child.kill('SIGTERM');
|
|
500
|
+
}, timeoutMs);
|
|
501
|
+
child.stdout?.on('data', (chunk) => {
|
|
502
|
+
stdout += chunk.toString('utf8');
|
|
503
|
+
});
|
|
504
|
+
child.stderr?.on('data', (chunk) => {
|
|
505
|
+
stderr += chunk.toString('utf8');
|
|
506
|
+
});
|
|
507
|
+
child.once('error', (error) => {
|
|
508
|
+
clearTimeout(timer);
|
|
509
|
+
finish(() => reject(error));
|
|
510
|
+
});
|
|
511
|
+
child.once('close', (code, signal) => {
|
|
512
|
+
clearTimeout(timer);
|
|
513
|
+
finish(() => {
|
|
514
|
+
if (code === 0) {
|
|
515
|
+
resolve({ stdout, stderr });
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const detail = timedOut
|
|
519
|
+
? `timed out after ${String(Date.now() - startedAtMs)}ms`
|
|
520
|
+
: `exited with code=${String(code)} signal=${String(signal)}`;
|
|
521
|
+
reject(new Error(`current Dominds entrypoint --version probe ${detail}; stdout=${truncateCommandOutput(stdout)} stderr=${truncateCommandOutput(stderr)}`));
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
}
|
|
364
526
|
function getErrorProp(error, key) {
|
|
365
527
|
if (typeof error !== 'object' || error === null)
|
|
366
528
|
return undefined;
|
|
@@ -475,7 +637,7 @@ function formatCommandFailureForUi(diagnostics) {
|
|
|
475
637
|
async function queryLatestVersion() {
|
|
476
638
|
const checkedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
|
|
477
639
|
try {
|
|
478
|
-
const { stdout } = await
|
|
640
|
+
const { stdout } = await runPackageManagerCommand(await resolveNpmCommandSpec(), ['view', 'dominds', 'version', '--json'], {
|
|
479
641
|
maxBuffer: 1024 * 1024,
|
|
480
642
|
timeoutMs: LATEST_VERSION_CHECK_TIMEOUT_MS,
|
|
481
643
|
});
|
|
@@ -520,20 +682,225 @@ async function queryLatestVersion() {
|
|
|
520
682
|
};
|
|
521
683
|
}
|
|
522
684
|
}
|
|
523
|
-
async function
|
|
524
|
-
const
|
|
685
|
+
async function findRunningDomindsPackageRootAbs() {
|
|
686
|
+
const scriptPath = process.argv[1];
|
|
687
|
+
if (typeof scriptPath !== 'string' || scriptPath.trim() === '')
|
|
688
|
+
return null;
|
|
689
|
+
const scriptPathAbs = path_1.default.isAbsolute(scriptPath)
|
|
690
|
+
? scriptPath
|
|
691
|
+
: path_1.default.resolve(PROCESS_START_CWD, scriptPath);
|
|
692
|
+
let currentDir = path_1.default.dirname(scriptPathAbs);
|
|
693
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
694
|
+
const packageJsonPath = path_1.default.join(currentDir, 'package.json');
|
|
695
|
+
try {
|
|
696
|
+
const raw = await promises_1.default.readFile(packageJsonPath, 'utf8');
|
|
697
|
+
const parsed = JSON.parse(raw);
|
|
698
|
+
if (isRecord(parsed) && parsed.name === 'dominds')
|
|
699
|
+
return currentDir;
|
|
700
|
+
}
|
|
701
|
+
catch (error) {
|
|
702
|
+
const code = getStringErrorProp(error, 'code');
|
|
703
|
+
if (code !== 'ENOENT')
|
|
704
|
+
throw error;
|
|
705
|
+
}
|
|
706
|
+
const parentDir = path_1.default.dirname(currentDir);
|
|
707
|
+
if (parentDir === currentDir)
|
|
708
|
+
return null;
|
|
709
|
+
currentDir = parentDir;
|
|
710
|
+
}
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
async function readPackageVersionFromPackageJson(packageJsonPath) {
|
|
714
|
+
try {
|
|
715
|
+
const raw = await promises_1.default.readFile(packageJsonPath, 'utf8');
|
|
716
|
+
const parsed = JSON.parse(raw);
|
|
717
|
+
if (!isRecord(parsed) || typeof parsed.version !== 'string' || parsed.version.trim() === '') {
|
|
718
|
+
throw new Error(`Invalid Dominds package metadata at ${packageJsonPath}`);
|
|
719
|
+
}
|
|
720
|
+
return parsed.version.trim();
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
const code = getStringErrorProp(error, 'code');
|
|
724
|
+
if (code === 'ENOENT')
|
|
725
|
+
return null;
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
async function readCurrentDomindsPackageRootAbs() {
|
|
730
|
+
const runningPackageRoot = await findRunningDomindsPackageRootAbs();
|
|
731
|
+
if (runningPackageRoot === null) {
|
|
732
|
+
throw new Error('Cannot find package.json for the current Dominds process entrypoint');
|
|
733
|
+
}
|
|
734
|
+
return runningPackageRoot;
|
|
735
|
+
}
|
|
736
|
+
async function readCurrentDomindsPackageVersion() {
|
|
737
|
+
const runningPackageRoot = await readCurrentDomindsPackageRootAbs();
|
|
738
|
+
const packageJsonPath = path_1.default.join(runningPackageRoot, 'package.json');
|
|
739
|
+
const version = await readPackageVersionFromPackageJson(packageJsonPath);
|
|
740
|
+
if (version === null) {
|
|
741
|
+
throw new Error(`Cannot read current Dominds package metadata at ${packageJsonPath}`);
|
|
742
|
+
}
|
|
743
|
+
return version;
|
|
744
|
+
}
|
|
745
|
+
function parsePackageManagerRoot(stdout) {
|
|
746
|
+
const lines = stdout
|
|
747
|
+
.split(/\r?\n/u)
|
|
748
|
+
.map((line) => line.trim())
|
|
749
|
+
.filter((line) => line !== '');
|
|
750
|
+
const npmRoot = lines[lines.length - 1];
|
|
751
|
+
if (npmRoot === undefined) {
|
|
752
|
+
throw new Error('package manager root -g returned empty stdout');
|
|
753
|
+
}
|
|
754
|
+
return npmRoot;
|
|
755
|
+
}
|
|
756
|
+
async function readSymlinkTargetAbs(absPath) {
|
|
757
|
+
try {
|
|
758
|
+
const stat = await promises_1.default.lstat(absPath);
|
|
759
|
+
if (!stat.isSymbolicLink())
|
|
760
|
+
return null;
|
|
761
|
+
const target = await promises_1.default.readlink(absPath);
|
|
762
|
+
return path_1.default.isAbsolute(target) ? target : path_1.default.resolve(path_1.default.dirname(absPath), target);
|
|
763
|
+
}
|
|
764
|
+
catch (error) {
|
|
765
|
+
const code = getStringErrorProp(error, 'code');
|
|
766
|
+
if (code === 'ENOENT')
|
|
767
|
+
return null;
|
|
768
|
+
throw error;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
function isSameOrInsidePath(childAbs, parentAbs) {
|
|
772
|
+
const childComparable = normalizeComparablePath(path_1.default.resolve(childAbs));
|
|
773
|
+
const parentComparable = normalizeComparablePath(path_1.default.resolve(parentAbs));
|
|
774
|
+
if (childComparable === parentComparable)
|
|
775
|
+
return true;
|
|
776
|
+
const relative = path_1.default.relative(parentComparable, childComparable);
|
|
777
|
+
return relative !== '' && !relative.startsWith('..') && !path_1.default.isAbsolute(relative);
|
|
778
|
+
}
|
|
779
|
+
async function readGlobalRootAbs(commandSpec) {
|
|
780
|
+
const rootResult = await runPackageManagerCommand(commandSpec, ['root', '-g'], {
|
|
525
781
|
maxBuffer: 1024 * 1024,
|
|
526
782
|
timeoutMs: 15000,
|
|
527
783
|
});
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
784
|
+
return path_1.default.resolve(PROCESS_START_CWD, parsePackageManagerRoot(rootResult.stdout));
|
|
785
|
+
}
|
|
786
|
+
async function verifyGlobalInstallTargetsCurrentPackage() {
|
|
787
|
+
let currentPackageRootAbs = null;
|
|
788
|
+
const checkedTargets = [];
|
|
789
|
+
try {
|
|
790
|
+
currentPackageRootAbs = await readCurrentDomindsPackageRootAbs();
|
|
791
|
+
const currentComparable = await getComparableRealPath(currentPackageRootAbs);
|
|
792
|
+
const managers = ['npm', 'pnpm'];
|
|
793
|
+
for (const manager of managers) {
|
|
794
|
+
try {
|
|
795
|
+
const commandSpec = manager === 'pnpm' ? await resolvePnpmCommandSpec() : await resolveNpmCommandSpec();
|
|
796
|
+
const managerGlobalRootAbs = await readGlobalRootAbs(commandSpec);
|
|
797
|
+
const managerPackageRootAbs = path_1.default.join(managerGlobalRootAbs, 'dominds');
|
|
798
|
+
checkedTargets.push(manager + ':' + managerPackageRootAbs);
|
|
799
|
+
const symlinkTargetAbs = await readSymlinkTargetAbs(managerPackageRootAbs);
|
|
800
|
+
if (symlinkTargetAbs !== null &&
|
|
801
|
+
!isSameOrInsidePath(symlinkTargetAbs, managerGlobalRootAbs)) {
|
|
802
|
+
checkedTargets.push(manager + ':linked:' + symlinkTargetAbs);
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
const managerComparable = await getComparableRealPath(managerPackageRootAbs);
|
|
806
|
+
if (currentComparable === managerComparable) {
|
|
807
|
+
return {
|
|
808
|
+
kind: 'ok',
|
|
809
|
+
manager,
|
|
810
|
+
currentPackageRootAbs,
|
|
811
|
+
managerPackageRootAbs,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
catch (error) {
|
|
816
|
+
checkedTargets.push(manager + ':error:' + formatErrorForUi(error));
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
kind: 'unsupported',
|
|
821
|
+
currentPackageRootAbs,
|
|
822
|
+
checkedTargets,
|
|
823
|
+
message: 'Automatic global update is disabled because neither npm nor pnpm global install target safely matches the current Dominds process at ' +
|
|
824
|
+
currentPackageRootAbs +
|
|
825
|
+
'. Checked: ' +
|
|
826
|
+
checkedTargets.join('; ') +
|
|
827
|
+
'.',
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
catch (error) {
|
|
831
|
+
return {
|
|
832
|
+
kind: 'unsupported',
|
|
833
|
+
currentPackageRootAbs,
|
|
834
|
+
checkedTargets,
|
|
835
|
+
message: 'Automatic global update is disabled because the global install target could not be verified: ' +
|
|
836
|
+
formatErrorForUi(error),
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
function buildGlobalInstallArgs(manager) {
|
|
841
|
+
return manager === 'pnpm' ? ['add', '-g', 'dominds@latest'] : ['i', '-g', 'dominds@latest'];
|
|
842
|
+
}
|
|
843
|
+
async function buildGlobalInstallCommandSpec(manager) {
|
|
844
|
+
return manager === 'pnpm' ? await resolvePnpmCommandSpec() : await resolveNpmCommandSpec();
|
|
845
|
+
}
|
|
846
|
+
function parseDomindsVersionProbe(stdout, stderr) {
|
|
847
|
+
const combined = `${stdout}\n${stderr}`;
|
|
848
|
+
const match = /dominds\s+v([^\s]+)/i.exec(combined) ??
|
|
849
|
+
/\bv(\d+(?:\.\d+){1,3}(?:[-+][^\s]+)?)\b/i.exec(combined);
|
|
850
|
+
if (match === null)
|
|
851
|
+
return null;
|
|
852
|
+
return match[1].trim();
|
|
853
|
+
}
|
|
854
|
+
function formatErrorForUi(error) {
|
|
855
|
+
const diagnostics = extractCommandFailureDiagnostics(error);
|
|
856
|
+
if (diagnostics.cmd !== null || diagnostics.stdout !== '' || diagnostics.stderr !== '') {
|
|
857
|
+
return formatCommandFailureForUi(diagnostics);
|
|
858
|
+
}
|
|
859
|
+
return error instanceof Error ? error.message : String(error);
|
|
860
|
+
}
|
|
861
|
+
async function verifyInstalledDominds(targetVersion) {
|
|
862
|
+
let packageVersion = null;
|
|
863
|
+
let probeVersion = null;
|
|
864
|
+
try {
|
|
865
|
+
packageVersion = await readCurrentDomindsPackageVersion();
|
|
866
|
+
const probe = await runCurrentDomindsVersionProbe();
|
|
867
|
+
probeVersion = parseDomindsVersionProbe(probe.stdout, probe.stderr);
|
|
868
|
+
if (probeVersion === null) {
|
|
869
|
+
return {
|
|
870
|
+
kind: 'failed',
|
|
871
|
+
message: `Current Dominds entrypoint did not report a parseable version; stdout=${truncateCommandOutput(probe.stdout)} stderr=${truncateCommandOutput(probe.stderr)}`,
|
|
872
|
+
packageVersion,
|
|
873
|
+
probeVersion,
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
const packageProbeComparison = compareVersions(packageVersion, probeVersion);
|
|
877
|
+
const versionsMatch = packageProbeComparison !== null
|
|
878
|
+
? packageProbeComparison === 0
|
|
879
|
+
: packageVersion === probeVersion;
|
|
880
|
+
const packageTargetComparison = compareVersions(packageVersion, targetVersion);
|
|
881
|
+
const probeTargetComparison = compareVersions(probeVersion, targetVersion);
|
|
882
|
+
const packageAtLeastTarget = packageTargetComparison !== null
|
|
883
|
+
? packageTargetComparison >= 0
|
|
884
|
+
: packageVersion === targetVersion;
|
|
885
|
+
const probeAtLeastTarget = probeTargetComparison !== null ? probeTargetComparison >= 0 : probeVersion === targetVersion;
|
|
886
|
+
if (!versionsMatch || !packageAtLeastTarget || !probeAtLeastTarget) {
|
|
887
|
+
return {
|
|
888
|
+
kind: 'failed',
|
|
889
|
+
message: `Current Dominds entrypoint version verification failed; target=${targetVersion} package=${packageVersion} probe=${probeVersion}`,
|
|
890
|
+
packageVersion,
|
|
891
|
+
probeVersion,
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
return { kind: 'ok', packageVersion, probeVersion };
|
|
895
|
+
}
|
|
896
|
+
catch (error) {
|
|
897
|
+
return {
|
|
898
|
+
kind: 'failed',
|
|
899
|
+
message: error instanceof Error ? error.message : String(error),
|
|
900
|
+
packageVersion,
|
|
901
|
+
probeVersion,
|
|
902
|
+
};
|
|
531
903
|
}
|
|
532
|
-
const commandAbs = process.platform === 'win32'
|
|
533
|
-
? path_1.default.join(prefix, 'dominds.cmd')
|
|
534
|
-
: path_1.default.join(prefix, 'bin', 'dominds');
|
|
535
|
-
await promises_1.default.access(commandAbs);
|
|
536
|
-
return commandAbs;
|
|
537
904
|
}
|
|
538
905
|
function clearBackgroundCheckTimer() {
|
|
539
906
|
if (backgroundCheckTimer === null)
|
|
@@ -593,6 +960,7 @@ async function checkLatestDomindsVersionNow() {
|
|
|
593
960
|
if (restartState.kind === 'restart_required' || restartState.kind === 'restarting') {
|
|
594
961
|
return await getDomindsSelfUpdateStatus();
|
|
595
962
|
}
|
|
963
|
+
installFailureObservation = null;
|
|
596
964
|
latestObservation = await queryLatestVersion();
|
|
597
965
|
publishStatusUpdateSoon();
|
|
598
966
|
scheduleBackgroundCheck(BACKGROUND_CHECK_INTERVAL_MS);
|
|
@@ -630,6 +998,7 @@ function configureDomindsSelfUpdate(params) {
|
|
|
630
998
|
};
|
|
631
999
|
latestObservation = { kind: 'unknown' };
|
|
632
1000
|
restartState = IDLE_RESTART_STATE;
|
|
1001
|
+
installFailureObservation = null;
|
|
633
1002
|
clearBackgroundCheckTimer();
|
|
634
1003
|
if (params.mode === 'production') {
|
|
635
1004
|
scheduleBackgroundCheck(BACKGROUND_CHECK_INTERVAL_MS);
|
|
@@ -669,8 +1038,8 @@ async function getDomindsSelfUpdateStatus() {
|
|
|
669
1038
|
runKind,
|
|
670
1039
|
action: 'restart',
|
|
671
1040
|
busy: 'idle',
|
|
672
|
-
reason:
|
|
673
|
-
message: 'Latest Dominds is installed and waiting for restart',
|
|
1041
|
+
reason: restartState.reason,
|
|
1042
|
+
message: restartState.installReport ?? 'Latest Dominds is installed and waiting for restart',
|
|
674
1043
|
targetVersion: restartState.installedVersion,
|
|
675
1044
|
});
|
|
676
1045
|
}
|
|
@@ -704,6 +1073,21 @@ async function getDomindsSelfUpdateStatus() {
|
|
|
704
1073
|
targetVersion: latestObservation.kind === 'ok' ? latestObservation.latestVersion : null,
|
|
705
1074
|
});
|
|
706
1075
|
}
|
|
1076
|
+
if (installFailureObservation !== null) {
|
|
1077
|
+
return buildStatus({
|
|
1078
|
+
currentVersion: runningVersion,
|
|
1079
|
+
installedVersion: runningVersion,
|
|
1080
|
+
latestVersion: latestObservation.kind === 'ok' ? latestObservation.latestVersion : null,
|
|
1081
|
+
checkedAt: installFailureObservation.failedAt,
|
|
1082
|
+
mode: cfg.mode,
|
|
1083
|
+
runKind,
|
|
1084
|
+
action: latestObservation.kind === 'ok' ? 'install' : 'none',
|
|
1085
|
+
busy: 'idle',
|
|
1086
|
+
reason: 'install_failed',
|
|
1087
|
+
message: installFailureObservation.errorText,
|
|
1088
|
+
targetVersion: latestObservation.kind === 'ok' ? latestObservation.latestVersion : null,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
707
1091
|
if (restartPromise !== null) {
|
|
708
1092
|
return buildStatus({
|
|
709
1093
|
currentVersion: runningVersion,
|
|
@@ -766,7 +1150,27 @@ async function getDomindsSelfUpdateStatus() {
|
|
|
766
1150
|
targetVersion: null,
|
|
767
1151
|
});
|
|
768
1152
|
}
|
|
769
|
-
if (runKind === '
|
|
1153
|
+
if (runKind === 'npx') {
|
|
1154
|
+
const npxSpec = await readNpxDomindsSpec();
|
|
1155
|
+
if (npxSpec?.kind === 'latest') {
|
|
1156
|
+
return buildStatus({
|
|
1157
|
+
currentVersion: runningVersion,
|
|
1158
|
+
installedVersion: runningVersion,
|
|
1159
|
+
latestVersion: latestObservation.latestVersion,
|
|
1160
|
+
checkedAt: latestObservation.checkedAt,
|
|
1161
|
+
mode: cfg.mode,
|
|
1162
|
+
runKind,
|
|
1163
|
+
action: 'restart',
|
|
1164
|
+
busy: 'idle',
|
|
1165
|
+
reason: 'restart_required',
|
|
1166
|
+
message: 'Restart to relaunch Dominds via npx dominds@latest',
|
|
1167
|
+
targetVersion: latestObservation.latestVersion,
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
const reason = npxSpec?.kind === 'fixed' ? 'npx_fixed_version_unsupported' : 'npx_versionless_unsupported';
|
|
1171
|
+
const message = npxSpec?.kind === 'fixed'
|
|
1172
|
+
? `A newer Dominds version is available, but this npx session was launched with ${npxSpec.spec}; use dominds@latest to enable automatic restart updates.`
|
|
1173
|
+
: 'A newer Dominds version is available, but this npx session was launched without a verified dominds@latest spec; use dominds@latest to enable automatic restart updates.';
|
|
770
1174
|
return buildStatus({
|
|
771
1175
|
currentVersion: runningVersion,
|
|
772
1176
|
installedVersion: runningVersion,
|
|
@@ -774,10 +1178,26 @@ async function getDomindsSelfUpdateStatus() {
|
|
|
774
1178
|
checkedAt: latestObservation.checkedAt,
|
|
775
1179
|
mode: cfg.mode,
|
|
776
1180
|
runKind,
|
|
777
|
-
action: '
|
|
1181
|
+
action: 'none',
|
|
778
1182
|
busy: 'idle',
|
|
779
|
-
reason
|
|
780
|
-
message
|
|
1183
|
+
reason,
|
|
1184
|
+
message,
|
|
1185
|
+
targetVersion: latestObservation.latestVersion,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
const globalTargetVerification = await verifyGlobalInstallTargetsCurrentPackage();
|
|
1189
|
+
if (globalTargetVerification.kind === 'unsupported') {
|
|
1190
|
+
return buildStatus({
|
|
1191
|
+
currentVersion: runningVersion,
|
|
1192
|
+
installedVersion: runningVersion,
|
|
1193
|
+
latestVersion: latestObservation.latestVersion,
|
|
1194
|
+
checkedAt: latestObservation.checkedAt,
|
|
1195
|
+
mode: cfg.mode,
|
|
1196
|
+
runKind,
|
|
1197
|
+
action: 'none',
|
|
1198
|
+
busy: 'idle',
|
|
1199
|
+
reason: 'global_install_target_unsupported',
|
|
1200
|
+
message: globalTargetVerification.message,
|
|
781
1201
|
targetVersion: latestObservation.latestVersion,
|
|
782
1202
|
});
|
|
783
1203
|
}
|
|
@@ -801,7 +1221,7 @@ async function installLatestDominds() {
|
|
|
801
1221
|
throw new Error('Dominds self-update install is disabled in development mode');
|
|
802
1222
|
}
|
|
803
1223
|
const runKind = detectRunKind(cfg.mode);
|
|
804
|
-
if (runKind !== '
|
|
1224
|
+
if (runKind !== 'global') {
|
|
805
1225
|
throw new Error(`Install action is not supported for runKind=${runKind}`);
|
|
806
1226
|
}
|
|
807
1227
|
if (restartState.kind === 'restart_required') {
|
|
@@ -814,49 +1234,100 @@ async function installLatestDominds() {
|
|
|
814
1234
|
return await getDomindsSelfUpdateStatus();
|
|
815
1235
|
}
|
|
816
1236
|
installPromise = (async () => {
|
|
1237
|
+
installFailureObservation = null;
|
|
817
1238
|
const runningVersion = getRunningVersion();
|
|
1239
|
+
const previouslyKnownLatest = latestObservation.kind === 'ok' ? latestObservation : null;
|
|
818
1240
|
const latest = await queryLatestVersion();
|
|
819
|
-
|
|
820
|
-
if (latest.kind === '
|
|
1241
|
+
let targetVersion;
|
|
1242
|
+
if (latest.kind === 'ok') {
|
|
1243
|
+
latestObservation = latest;
|
|
1244
|
+
targetVersion = latest.latestVersion;
|
|
1245
|
+
}
|
|
1246
|
+
else if (previouslyKnownLatest === null) {
|
|
1247
|
+
latestObservation = latest;
|
|
821
1248
|
throw new Error(latest.errorText);
|
|
822
1249
|
}
|
|
823
|
-
|
|
824
|
-
|
|
1250
|
+
else {
|
|
1251
|
+
targetVersion = previouslyKnownLatest.latestVersion;
|
|
1252
|
+
log.warn('Dominds install latest-version refresh failed; continuing with previously known update target', undefined, {
|
|
1253
|
+
previousLatestVersion: previouslyKnownLatest.latestVersion,
|
|
1254
|
+
previousCheckedAt: previouslyKnownLatest.checkedAt,
|
|
1255
|
+
refreshErrorText: latest.errorText,
|
|
1256
|
+
refreshCheckedAt: latest.checkedAt,
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
const comparison = compareVersions(runningVersion, targetVersion);
|
|
1260
|
+
const hasUpdate = comparison !== null ? comparison < 0 : runningVersion !== targetVersion;
|
|
825
1261
|
if (!hasUpdate) {
|
|
826
1262
|
throw new Error('No installable Dominds update is currently available');
|
|
827
1263
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
}
|
|
832
|
-
|
|
1264
|
+
const globalTargetVerification = await verifyGlobalInstallTargetsCurrentPackage();
|
|
1265
|
+
if (globalTargetVerification.kind === 'unsupported') {
|
|
1266
|
+
throw new Error(globalTargetVerification.message);
|
|
1267
|
+
}
|
|
1268
|
+
let installCommandError = null;
|
|
1269
|
+
try {
|
|
1270
|
+
await runPackageManagerCommand(await buildGlobalInstallCommandSpec(globalTargetVerification.manager), buildGlobalInstallArgs(globalTargetVerification.manager), {
|
|
1271
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
1272
|
+
timeoutMs: 10 * 60 * 1000,
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
catch (error) {
|
|
1276
|
+
installCommandError = error;
|
|
1277
|
+
log.warn('Dominds install command failed; verifying installed files before reporting failure', error, {
|
|
1278
|
+
currentVersion: runningVersion,
|
|
1279
|
+
targetVersion,
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
const installedVerification = await verifyInstalledDominds(targetVersion);
|
|
1283
|
+
if (installedVerification.kind === 'failed') {
|
|
1284
|
+
if (installCommandError !== null) {
|
|
1285
|
+
throw new Error(`npm install failed and installed Dominds verification failed. npm: ${formatErrorForUi(installCommandError)} verification: ${installedVerification.message}`);
|
|
1286
|
+
}
|
|
1287
|
+
throw new Error(installedVerification.message);
|
|
1288
|
+
}
|
|
1289
|
+
if (installCommandError !== null) {
|
|
1290
|
+
log.warn('Dominds install command failed but installed files passed verification', installCommandError, {
|
|
1291
|
+
targetVersion,
|
|
1292
|
+
packageVersion: installedVerification.packageVersion,
|
|
1293
|
+
probeVersion: installedVerification.probeVersion,
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
const installReport = installCommandError === null
|
|
1297
|
+
? null
|
|
1298
|
+
: `Dominds install command reported failure, but the current entrypoint version probe confirmed v${installedVerification.probeVersion}. Restart is available.`;
|
|
833
1299
|
restartState = {
|
|
834
1300
|
kind: 'restart_required',
|
|
835
|
-
installedVersion:
|
|
836
|
-
|
|
1301
|
+
installedVersion: installedVerification.probeVersion,
|
|
1302
|
+
installReport,
|
|
1303
|
+
reason: installCommandError === null
|
|
1304
|
+
? 'restart_required'
|
|
1305
|
+
: 'install_verified_after_command_failure',
|
|
837
1306
|
};
|
|
1307
|
+
installFailureObservation = null;
|
|
838
1308
|
return await getDomindsSelfUpdateStatus();
|
|
839
1309
|
})()
|
|
840
1310
|
.catch((error) => {
|
|
841
1311
|
const latestVersion = latestObservation.kind === 'ok' ? latestObservation.latestVersion : null;
|
|
842
1312
|
const checkedAt = latestObservation.kind === 'unknown' ? null : latestObservation.checkedAt;
|
|
1313
|
+
installFailureObservation = {
|
|
1314
|
+
errorText: formatErrorForUi(error),
|
|
1315
|
+
failedAt: (0, time_1.formatUnifiedTimestamp)(new Date()),
|
|
1316
|
+
};
|
|
843
1317
|
log.error('Dominds version install failed', error, {
|
|
844
1318
|
runKind,
|
|
845
1319
|
currentVersion: getRunningVersion(),
|
|
846
1320
|
latestVersion,
|
|
847
1321
|
checkedAt,
|
|
848
1322
|
});
|
|
849
|
-
|
|
1323
|
+
return getDomindsSelfUpdateStatus();
|
|
850
1324
|
})
|
|
851
1325
|
.finally(() => {
|
|
852
1326
|
installPromise = null;
|
|
853
1327
|
publishStatusUpdateSoon();
|
|
854
1328
|
});
|
|
855
1329
|
publishStatusUpdateSoon();
|
|
856
|
-
return await
|
|
857
|
-
}
|
|
858
|
-
function buildRestartArgs(cfg) {
|
|
859
|
-
return ['webui', '-p', String(cfg.port), '-h', cfg.host, '--mode', 'prod', '--nobrowser'];
|
|
1330
|
+
return await getDomindsSelfUpdateStatus();
|
|
860
1331
|
}
|
|
861
1332
|
function spawnDetachedRestartHelper(params) {
|
|
862
1333
|
const stdioMode = getRestartHelperStdio();
|
|
@@ -876,7 +1347,7 @@ function spawnDetachedRestartHelper(params) {
|
|
|
876
1347
|
"const net = require('net');",
|
|
877
1348
|
"const { spawn } = require('child_process');",
|
|
878
1349
|
'const payload = JSON.parse(process.argv[1]);',
|
|
879
|
-
'const detached = payload.stdioMode !== "inherit"
|
|
1350
|
+
'const detached = payload.stdioMode !== "inherit";',
|
|
880
1351
|
'function isPortBusy() {',
|
|
881
1352
|
' return new Promise((resolve) => {',
|
|
882
1353
|
' const socket = net.createConnection({ host: payload.host, port: payload.port });',
|
|
@@ -915,13 +1386,18 @@ function spawnDetachedRestartHelper(params) {
|
|
|
915
1386
|
' throw error;',
|
|
916
1387
|
' }',
|
|
917
1388
|
'}',
|
|
918
|
-
'async function
|
|
1389
|
+
'async function waitForRetiringProcessExit(timeoutMs) {',
|
|
919
1390
|
' const deadline = Date.now() + timeoutMs;',
|
|
1391
|
+
' while (Date.now() < deadline) {',
|
|
1392
|
+
' if (!isRetiringProcessAlive()) return true;',
|
|
1393
|
+
' await new Promise((resolve) => setTimeout(resolve, payload.probeIntervalMs));',
|
|
1394
|
+
' }',
|
|
1395
|
+
' return false;',
|
|
1396
|
+
'}',
|
|
1397
|
+
'async function waitForPortReleaseUntil(deadline) {',
|
|
920
1398
|
' let consecutiveReady = 0;',
|
|
921
1399
|
' while (Date.now() < deadline) {',
|
|
922
|
-
'
|
|
923
|
-
' const portBusy = await isPortBusy();',
|
|
924
|
-
' if (!processAlive && !portBusy) {',
|
|
1400
|
+
' if (!(await isPortBusy())) {',
|
|
925
1401
|
' consecutiveReady += 1;',
|
|
926
1402
|
' if (consecutiveReady >= 2) return true;',
|
|
927
1403
|
' } else {',
|
|
@@ -933,7 +1409,7 @@ function spawnDetachedRestartHelper(params) {
|
|
|
933
1409
|
'}',
|
|
934
1410
|
'function runBestEffortKiller(command, args) {',
|
|
935
1411
|
' return new Promise((resolve) => {',
|
|
936
|
-
' const killer = spawn(command, args, { stdio: payload.stdioMode });',
|
|
1412
|
+
' const killer = spawn(command, args, { stdio: payload.stdioMode, windowsHide: payload.stdioMode !== "inherit" });',
|
|
937
1413
|
" killer.once('error', () => resolve());",
|
|
938
1414
|
" killer.once('exit', () => resolve());",
|
|
939
1415
|
' });',
|
|
@@ -953,16 +1429,26 @@ function spawnDetachedRestartHelper(params) {
|
|
|
953
1429
|
'}',
|
|
954
1430
|
'(async () => {',
|
|
955
1431
|
' try {',
|
|
956
|
-
' const
|
|
957
|
-
'
|
|
1432
|
+
' const forceKillDeadline = Date.now() + payload.forceKillAfterMs;',
|
|
1433
|
+
' const exitedGracefully = await waitForRetiringProcessExit(payload.forceKillAfterMs);',
|
|
1434
|
+
' if (!exitedGracefully) {',
|
|
958
1435
|
' await forceKillRetiringProcess();',
|
|
959
|
-
' const
|
|
960
|
-
' if (!
|
|
961
|
-
' throw new Error(`Dominds
|
|
1436
|
+
' const exitedAfterKill = await waitForRetiringProcessExit(payload.portReleaseTimeoutMs);',
|
|
1437
|
+
' if (!exitedAfterKill) {',
|
|
1438
|
+
' throw new Error(`Dominds retiring process is still alive after force-killing pid ${String(payload.retiringPid)}`);',
|
|
962
1439
|
' }',
|
|
963
1440
|
' }',
|
|
964
|
-
|
|
1441
|
+
' const portReleaseDeadline = Math.max(forceKillDeadline, Date.now() + payload.portReleaseTimeoutMs);',
|
|
1442
|
+
' const portReleased = await waitForPortReleaseUntil(portReleaseDeadline);',
|
|
1443
|
+
' if (!portReleased) {',
|
|
1444
|
+
' throw new Error(`Dominds restart port is still busy after retiring pid ${String(payload.retiringPid)} exited; port=${String(payload.host)}:${String(payload.port)}`);',
|
|
1445
|
+
' }',
|
|
1446
|
+
' const child = spawn(payload.command, payload.args, { cwd: payload.cwd, env: process.env, detached, stdio: payload.stdioMode, shell: false, windowsHide: payload.stdioMode !== "inherit" });',
|
|
965
1447
|
' if (detached) child.unref();',
|
|
1448
|
+
' await new Promise((resolve, reject) => {',
|
|
1449
|
+
" child.once('error', reject);",
|
|
1450
|
+
" child.once('spawn', resolve);",
|
|
1451
|
+
' });',
|
|
966
1452
|
' process.exit(0);',
|
|
967
1453
|
' } catch (error) {',
|
|
968
1454
|
' console.error(error instanceof Error ? error.message : String(error));',
|
|
@@ -973,10 +1459,11 @@ function spawnDetachedRestartHelper(params) {
|
|
|
973
1459
|
const helper = (0, child_process_1.spawn)(process.execPath, ['-e', helperScript, helperPayload], {
|
|
974
1460
|
cwd: params.cwd,
|
|
975
1461
|
env: process.env,
|
|
976
|
-
detached: stdioMode !== 'inherit'
|
|
1462
|
+
detached: stdioMode !== 'inherit',
|
|
977
1463
|
stdio: stdioMode,
|
|
1464
|
+
windowsHide: stdioMode !== 'inherit',
|
|
978
1465
|
});
|
|
979
|
-
if (stdioMode !== 'inherit'
|
|
1466
|
+
if (stdioMode !== 'inherit') {
|
|
980
1467
|
helper.unref();
|
|
981
1468
|
}
|
|
982
1469
|
}
|
|
@@ -1023,50 +1510,41 @@ async function restartDomindsIntoLatest() {
|
|
|
1023
1510
|
throw new Error('No restartable Dominds update is currently available');
|
|
1024
1511
|
}
|
|
1025
1512
|
const runKind = detectRunKind(cfg.mode);
|
|
1026
|
-
|
|
1027
|
-
let command;
|
|
1513
|
+
let launchSpec;
|
|
1028
1514
|
const previousRestartRequiredState = restartState.kind === 'restart_required' ? restartState : null;
|
|
1029
|
-
if (
|
|
1030
|
-
|
|
1031
|
-
args.unshift('dominds@latest');
|
|
1032
|
-
args.unshift('-y');
|
|
1515
|
+
if (previousRestartRequiredState !== null) {
|
|
1516
|
+
launchSpec = buildCurrentProcessRestartLaunchSpec();
|
|
1033
1517
|
}
|
|
1034
|
-
else if (
|
|
1035
|
-
|
|
1518
|
+
else if (runKind === 'npx') {
|
|
1519
|
+
const npxSpec = await readNpxDomindsSpec();
|
|
1520
|
+
if (npxSpec?.kind !== 'latest') {
|
|
1521
|
+
throw new Error('Dominds npx restart requires launching Dominds with dominds@latest');
|
|
1522
|
+
}
|
|
1523
|
+
launchSpec = await buildNpxLatestRestartLaunchSpec();
|
|
1036
1524
|
}
|
|
1037
1525
|
else {
|
|
1038
|
-
throw new Error('Dominds restart requires a completed install or
|
|
1526
|
+
throw new Error('Dominds restart requires a completed install or a restartable session');
|
|
1039
1527
|
}
|
|
1040
1528
|
restartState = { kind: 'restarting', targetVersion: status.targetVersion };
|
|
1041
1529
|
publishStatusUpdateSoon();
|
|
1042
1530
|
try {
|
|
1043
1531
|
spawnDetachedRestartHelper({
|
|
1044
|
-
command,
|
|
1045
|
-
args,
|
|
1046
|
-
cwd:
|
|
1532
|
+
command: launchSpec.command,
|
|
1533
|
+
args: launchSpec.args,
|
|
1534
|
+
cwd: PROCESS_START_CWD,
|
|
1047
1535
|
host: cfg.host,
|
|
1048
1536
|
port: cfg.port,
|
|
1049
1537
|
});
|
|
1050
1538
|
setImmediate(() => {
|
|
1051
1539
|
void stopAndExitForRestart().catch((error) => {
|
|
1052
1540
|
log.error('Failed to stop Dominds server during restart', error);
|
|
1053
|
-
|
|
1054
|
-
restartState = previousRestartRequiredState;
|
|
1055
|
-
publishStatusUpdateSoon();
|
|
1056
|
-
return;
|
|
1057
|
-
}
|
|
1058
|
-
restartState = IDLE_RESTART_STATE;
|
|
1541
|
+
restartState = previousRestartRequiredState ?? IDLE_RESTART_STATE;
|
|
1059
1542
|
publishStatusUpdateSoon();
|
|
1060
1543
|
});
|
|
1061
1544
|
});
|
|
1062
1545
|
}
|
|
1063
1546
|
catch (error) {
|
|
1064
|
-
|
|
1065
|
-
restartState = previousRestartRequiredState;
|
|
1066
|
-
}
|
|
1067
|
-
else {
|
|
1068
|
-
restartState = IDLE_RESTART_STATE;
|
|
1069
|
-
}
|
|
1547
|
+
restartState = previousRestartRequiredState ?? IDLE_RESTART_STATE;
|
|
1070
1548
|
publishStatusUpdateSoon();
|
|
1071
1549
|
throw error;
|
|
1072
1550
|
}
|