4runr-os 2.10.69 → 2.10.71
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/apps/gateway/dist/apps/gateway/src/db/docker-manager.d.ts +4 -0
- package/apps/gateway/dist/apps/gateway/src/db/docker-manager.d.ts.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/db/docker-manager.js +55 -45
- package/apps/gateway/dist/apps/gateway/src/db/docker-manager.js.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/db/init.d.ts.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/db/init.js +4 -11
- package/apps/gateway/dist/apps/gateway/src/db/init.js.map +1 -1
- package/apps/gateway/docker-compose.local.yml +2 -0
- package/apps/gateway/src/db/docker-manager.ts +98 -62
- package/apps/gateway/src/db/init.ts +9 -15
- package/dist/boot-orchestrator.d.ts.map +1 -1
- package/dist/boot-orchestrator.js +5 -0
- package/dist/boot-orchestrator.js.map +1 -1
- package/dist/deferred-npm-install-lock.d.ts +10 -1
- package/dist/deferred-npm-install-lock.d.ts.map +1 -1
- package/dist/deferred-npm-install-lock.js +69 -2
- package/dist/deferred-npm-install-lock.js.map +1 -1
- package/dist/security/package-integrity.d.ts.map +1 -1
- package/dist/security/package-integrity.js +22 -6
- package/dist/security/package-integrity.js.map +1 -1
- package/dist/tui-handlers.js +129 -23
- package/dist/tui-handlers.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +26 -19
- package/dist/version-check.js.map +1 -1
- package/dist/watchdog.js +2 -2
- package/dist/watchdog.js.map +1 -1
- package/package.json +2 -2
- package/src/security/package-integrity.ts +24 -7
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
export declare const FOURRUNR_DB_CONTAINERS: readonly ["4runr-postgres", "4runr-redis"];
|
|
1
2
|
export declare function isDockerAvailable(): Promise<boolean>;
|
|
2
3
|
export declare function isDockerRunning(): Promise<boolean>;
|
|
4
|
+
export declare function isContainerRunning(containerName: string): boolean;
|
|
3
5
|
export declare function areContainersRunning(): Promise<boolean>;
|
|
6
|
+
export declare function startStoppedContainers(): Promise<void>;
|
|
4
7
|
export declare function startDockerCompose(composeFilePath: string): Promise<void>;
|
|
8
|
+
export declare function ensure4RunrStackRunning(composeFilePath: string): Promise<void>;
|
|
5
9
|
export declare function stopDockerCompose(composeFilePath: string): Promise<void>;
|
|
6
10
|
export declare function stop4RunrContainers(): Promise<void>;
|
|
7
11
|
export declare function waitForHealthy(containerName: string, timeoutMs?: number): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker-manager.d.ts","sourceRoot":"","sources":["../../../../../src/db/docker-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"docker-manager.d.ts","sourceRoot":"","sources":["../../../../../src/db/docker-manager.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,4CAA6C,CAAC;AAqBjF,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO1D;AAKD,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAOxD;AAKD,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAUjE;AAKD,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE7D;AAKD,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAW5D;AAMD,wBAAsB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/E;AAKD,wBAAsB,uBAAuB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF;AAMD,wBAAsB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9E;AAKD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBzD;AAOD,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,GAAE,MAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCpG;AAMD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOhE;AAOD,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,MAAM,CAMlF;AAYD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,wBAAsB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CA0B7D"}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
+
export const FOURRUNR_DB_CONTAINERS = ['4runr-postgres', '4runr-redis'];
|
|
3
|
+
const COMPOSE_PROJECT = '4runr';
|
|
4
|
+
function dockerShell() {
|
|
5
|
+
return process.platform === 'win32' ? process.env['COMSPEC'] || 'cmd.exe' : undefined;
|
|
6
|
+
}
|
|
7
|
+
function dockerExec(command, timeoutMs = 30000) {
|
|
8
|
+
return execSync(command, {
|
|
9
|
+
encoding: 'utf-8',
|
|
10
|
+
stdio: 'pipe',
|
|
11
|
+
timeout: timeoutMs,
|
|
12
|
+
...(dockerShell() ? { shell: dockerShell() } : {}),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
2
15
|
export async function isDockerAvailable() {
|
|
3
16
|
try {
|
|
4
|
-
const result =
|
|
5
|
-
encoding: 'utf-8',
|
|
6
|
-
stdio: 'pipe',
|
|
7
|
-
timeout: 5000
|
|
8
|
-
});
|
|
17
|
+
const result = dockerExec('docker --version', 5000);
|
|
9
18
|
return result.includes('Docker version');
|
|
10
19
|
}
|
|
11
20
|
catch {
|
|
@@ -14,40 +23,56 @@ export async function isDockerAvailable() {
|
|
|
14
23
|
}
|
|
15
24
|
export async function isDockerRunning() {
|
|
16
25
|
try {
|
|
17
|
-
|
|
18
|
-
encoding: 'utf-8',
|
|
19
|
-
stdio: 'pipe',
|
|
20
|
-
timeout: 5000
|
|
21
|
-
});
|
|
26
|
+
dockerExec('docker info', 5000);
|
|
22
27
|
return true;
|
|
23
28
|
}
|
|
24
29
|
catch {
|
|
25
30
|
return false;
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
|
-
export
|
|
33
|
+
export function isContainerRunning(containerName) {
|
|
29
34
|
try {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
stdio: 'pipe',
|
|
33
|
-
timeout: 5000
|
|
34
|
-
});
|
|
35
|
-
return result.trim().includes('4runr-postgres');
|
|
35
|
+
const state = dockerExec(`docker inspect --format="{{.State.Running}}" ${containerName}`, 5000).trim();
|
|
36
|
+
return state === 'true';
|
|
36
37
|
}
|
|
37
38
|
catch {
|
|
38
39
|
return false;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
export async function areContainersRunning() {
|
|
43
|
+
return FOURRUNR_DB_CONTAINERS.every((name) => isContainerRunning(name));
|
|
44
|
+
}
|
|
45
|
+
export async function startStoppedContainers() {
|
|
46
|
+
const stopped = FOURRUNR_DB_CONTAINERS.filter((name) => !isContainerRunning(name));
|
|
47
|
+
if (stopped.length === 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
dockerExec(`docker start ${stopped.join(' ')}`, 60000);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
41
56
|
export async function startDockerCompose(composeFilePath) {
|
|
42
|
-
|
|
57
|
+
const cmd = `docker compose -p ${COMPOSE_PROJECT} -f "${composeFilePath}" up -d`;
|
|
58
|
+
execSync(cmd, {
|
|
43
59
|
stdio: 'inherit',
|
|
44
|
-
timeout:
|
|
60
|
+
timeout: 120000,
|
|
61
|
+
...(dockerShell() ? { shell: dockerShell() } : {}),
|
|
45
62
|
});
|
|
46
63
|
}
|
|
64
|
+
export async function ensure4RunrStackRunning(composeFilePath) {
|
|
65
|
+
await startStoppedContainers();
|
|
66
|
+
if (!(await areContainersRunning())) {
|
|
67
|
+
await startDockerCompose(composeFilePath);
|
|
68
|
+
}
|
|
69
|
+
await startStoppedContainers();
|
|
70
|
+
}
|
|
47
71
|
export async function stopDockerCompose(composeFilePath) {
|
|
48
|
-
execSync(`docker compose -f "${composeFilePath}" stop`, {
|
|
72
|
+
execSync(`docker compose -p ${COMPOSE_PROJECT} -f "${composeFilePath}" stop`, {
|
|
49
73
|
stdio: 'pipe',
|
|
50
|
-
timeout: 30000
|
|
74
|
+
timeout: 30000,
|
|
75
|
+
...(dockerShell() ? { shell: dockerShell() } : {}),
|
|
51
76
|
});
|
|
52
77
|
}
|
|
53
78
|
export async function stop4RunrContainers() {
|
|
@@ -74,39 +99,28 @@ export async function waitForHealthy(containerName, timeoutMs = 30000) {
|
|
|
74
99
|
const start = Date.now();
|
|
75
100
|
while (Date.now() - start < timeoutMs) {
|
|
76
101
|
try {
|
|
77
|
-
const health =
|
|
78
|
-
encoding: 'utf-8',
|
|
79
|
-
stdio: 'pipe',
|
|
80
|
-
timeout: 5000
|
|
81
|
-
}).trim();
|
|
102
|
+
const health = dockerExec(`docker inspect --format="{{.State.Health.Status}}" ${containerName}`, 5000).trim();
|
|
82
103
|
if (health === 'healthy') {
|
|
83
104
|
return;
|
|
84
105
|
}
|
|
85
106
|
if (health === '' || health === '<no value>') {
|
|
86
|
-
const running =
|
|
87
|
-
encoding: 'utf-8',
|
|
88
|
-
stdio: 'pipe',
|
|
89
|
-
timeout: 5000
|
|
90
|
-
}).trim();
|
|
107
|
+
const running = dockerExec(`docker inspect --format="{{.State.Running}}" ${containerName}`, 5000).trim();
|
|
91
108
|
if (running === 'true') {
|
|
92
109
|
await sleep(2000);
|
|
93
110
|
return;
|
|
94
111
|
}
|
|
95
112
|
}
|
|
96
113
|
}
|
|
97
|
-
catch
|
|
114
|
+
catch {
|
|
98
115
|
}
|
|
99
116
|
await sleep(1000);
|
|
100
117
|
}
|
|
101
|
-
|
|
118
|
+
const logs = getContainerLogs(containerName, 20);
|
|
119
|
+
throw new Error(`Container ${containerName} did not become healthy within ${timeoutMs}ms. Recent logs:\n${logs}`);
|
|
102
120
|
}
|
|
103
121
|
export async function isPortInUse(port) {
|
|
104
122
|
try {
|
|
105
|
-
const result =
|
|
106
|
-
encoding: 'utf-8',
|
|
107
|
-
stdio: 'pipe',
|
|
108
|
-
timeout: 5000
|
|
109
|
-
});
|
|
123
|
+
const result = dockerExec(`docker ps --filter "publish=${port}" --format "{{.Names}}"`, 5000);
|
|
110
124
|
return result.trim().length > 0;
|
|
111
125
|
}
|
|
112
126
|
catch {
|
|
@@ -115,24 +129,20 @@ export async function isPortInUse(port) {
|
|
|
115
129
|
}
|
|
116
130
|
export function getContainerLogs(containerName, lines = 50) {
|
|
117
131
|
try {
|
|
118
|
-
return
|
|
119
|
-
encoding: 'utf-8',
|
|
120
|
-
stdio: 'pipe',
|
|
121
|
-
timeout: 10000
|
|
122
|
-
});
|
|
132
|
+
return dockerExec(`docker logs --tail ${lines} ${containerName}`, 10000);
|
|
123
133
|
}
|
|
124
134
|
catch (err) {
|
|
125
135
|
return `Failed to retrieve logs: ${err instanceof Error ? err.message : String(err)}`;
|
|
126
136
|
}
|
|
127
137
|
}
|
|
128
138
|
function sleep(ms) {
|
|
129
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
139
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
130
140
|
}
|
|
131
141
|
export async function getDockerStatus() {
|
|
132
142
|
const status = {
|
|
133
143
|
available: false,
|
|
134
144
|
running: false,
|
|
135
|
-
containersRunning: false
|
|
145
|
+
containersRunning: false,
|
|
136
146
|
};
|
|
137
147
|
try {
|
|
138
148
|
status.available = await isDockerAvailable();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker-manager.js","sourceRoot":"","sources":["../../../../../src/db/docker-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"docker-manager.js","sourceRoot":"","sources":["../../../../../src/db/docker-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,gBAAgB,EAAE,aAAa,CAAU,CAAC;AAEjF,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC,SAAS,WAAW;IAClB,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AACxF,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,YAAoB,KAAK;IAC5D,OAAO,QAAQ,CAAC,OAAO,EAAE;QACvB,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,SAAS;QAClB,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QACpD,OAAO,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAKD,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,UAAU,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAKD,MAAM,UAAU,kBAAkB,CAAC,aAAqB;IACtD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,UAAU,CACtB,gDAAgD,aAAa,EAAE,EAC/D,IAAI,CACL,CAAC,IAAI,EAAE,CAAC;QACT,OAAO,KAAK,KAAK,MAAM,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAKD,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,OAAO,sBAAsB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1E,CAAC;AAKD,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC1C,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,UAAU,CAAC,gBAAgB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;IAET,CAAC;AACH,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,eAAuB;IAC9D,MAAM,GAAG,GAAG,qBAAqB,eAAe,QAAQ,eAAe,SAAS,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE;QACZ,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,MAAM;QACf,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAKD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,eAAuB;IACnE,MAAM,sBAAsB,EAAE,CAAC;IAE/B,IAAI,CAAC,CAAC,MAAM,oBAAoB,EAAE,CAAC,EAAE,CAAC;QACpC,MAAM,kBAAkB,CAAC,eAAe,CAAC,CAAC;IAC5C,CAAC;IAGD,MAAM,sBAAsB,EAAE,CAAC;AACjC,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,eAAuB;IAC7D,QAAQ,CAAC,qBAAqB,eAAe,QAAQ,eAAe,QAAQ,EAAE;QAC5E,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,KAAK;QACd,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAKD,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,QAAQ,CAAC,8CAA8C,EAAE;gBACvD,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS;aAC3C,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,4DAA4D,EAAE;gBACrE,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,SAAS;aACjB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;IAET,CAAC;AACH,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,aAAqB,EAAE,YAAoB,KAAK;IACnF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,CACvB,sDAAsD,aAAa,EAAE,EACrE,IAAI,CACL,CAAC,IAAI,EAAE,CAAC;YAET,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YAGD,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;gBAC7C,MAAM,OAAO,GAAG,UAAU,CACxB,gDAAgD,aAAa,EAAE,EAC/D,IAAI,CACL,CAAC,IAAI,EAAE,CAAC;gBAET,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;oBACvB,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;oBAClB,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;QAET,CAAC;QAED,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACjD,MAAM,IAAI,KAAK,CACb,aAAa,aAAa,kCAAkC,SAAS,qBAAqB,IAAI,EAAE,CACjG,CAAC;AACJ,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,CAAC,+BAA+B,IAAI,yBAAyB,EAAE,IAAI,CAAC,CAAC;QAC9F,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAOD,MAAM,UAAU,gBAAgB,CAAC,aAAqB,EAAE,QAAgB,EAAE;IACxE,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,sBAAsB,KAAK,IAAI,aAAa,EAAE,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACxF,CAAC;AACH,CAAC;AAKD,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAeD,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,MAAM,GAAiB;QAC3B,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,KAAK;QACd,iBAAiB,EAAE,KAAK;KACzB,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,CAAC,SAAS,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,CAAC,KAAK,GAAG,sBAAsB,CAAC;YACtC,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,CAAC,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,GAAG,2BAA2B,CAAC;YAC3C,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,CAAC,iBAAiB,GAAG,MAAM,oBAAoB,EAAE,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/db/init.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/db/init.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAkB9C,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAQD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgGjF;AA2ID,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9F"}
|
|
@@ -7,7 +7,7 @@ import { PrismaClient } from '@prisma/client';
|
|
|
7
7
|
import { get4RunrDataDir } from '@4runr/shared';
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
|
-
import {
|
|
10
|
+
import { ensure4RunrStackRunning, waitForHealthy, getDockerStatus } from './docker-manager.js';
|
|
11
11
|
export async function initializeDatabase(logger) {
|
|
12
12
|
const warnings = [];
|
|
13
13
|
const persistenceMode = process.env['GATEWAY_PERSISTENCE'] || 'auto';
|
|
@@ -32,15 +32,8 @@ export async function initializeDatabase(logger) {
|
|
|
32
32
|
return { mode: 'memory', client: null, warnings };
|
|
33
33
|
}
|
|
34
34
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
logger.info('Starting 4Runr Docker containers...');
|
|
38
|
-
await startDockerContainers(logger);
|
|
39
|
-
logger.info('✓ Docker containers started');
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
logger.info('✓ Docker containers already running');
|
|
43
|
-
}
|
|
35
|
+
logger.info('Ensuring 4Runr Docker containers (Postgres + Redis) are running...');
|
|
36
|
+
await startDockerContainers(logger);
|
|
44
37
|
logger.info('Waiting for database to be ready...');
|
|
45
38
|
await waitForHealthy('4runr-postgres', 60000);
|
|
46
39
|
await waitForHealthy('4runr-redis', 15000);
|
|
@@ -145,7 +138,7 @@ async function startDockerContainers(logger) {
|
|
|
145
138
|
else {
|
|
146
139
|
logger.warn(`Docker Compose template missing; using existing ${composeFilePath}`);
|
|
147
140
|
}
|
|
148
|
-
await
|
|
141
|
+
await ensure4RunrStackRunning(composeFilePath);
|
|
149
142
|
}
|
|
150
143
|
async function runMigrations(databaseUrl, logger) {
|
|
151
144
|
const candidates = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../../../src/db/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,OAAO,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../../../src/db/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
|
|
3
|
+
/** Must match `container_name` in docker-compose.local.yml */
|
|
4
|
+
export const FOURRUNR_DB_CONTAINERS = ['4runr-postgres', '4runr-redis'] as const;
|
|
5
|
+
|
|
6
|
+
const COMPOSE_PROJECT = '4runr';
|
|
7
|
+
|
|
8
|
+
function dockerShell(): string | undefined {
|
|
9
|
+
return process.platform === 'win32' ? process.env['COMSPEC'] || 'cmd.exe' : undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function dockerExec(command: string, timeoutMs: number = 30000): string {
|
|
13
|
+
return execSync(command, {
|
|
14
|
+
encoding: 'utf-8',
|
|
15
|
+
stdio: 'pipe',
|
|
16
|
+
timeout: timeoutMs,
|
|
17
|
+
...(dockerShell() ? { shell: dockerShell() } : {}),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
3
21
|
/**
|
|
4
22
|
* Check if Docker is installed and available
|
|
5
23
|
* Works with both Docker Desktop and Docker Engine
|
|
6
24
|
*/
|
|
7
25
|
export async function isDockerAvailable(): Promise<boolean> {
|
|
8
26
|
try {
|
|
9
|
-
const result =
|
|
10
|
-
encoding: 'utf-8',
|
|
11
|
-
stdio: 'pipe',
|
|
12
|
-
timeout: 5000
|
|
13
|
-
});
|
|
27
|
+
const result = dockerExec('docker --version', 5000);
|
|
14
28
|
return result.includes('Docker version');
|
|
15
29
|
} catch {
|
|
16
30
|
return false;
|
|
@@ -22,11 +36,7 @@ export async function isDockerAvailable(): Promise<boolean> {
|
|
|
22
36
|
*/
|
|
23
37
|
export async function isDockerRunning(): Promise<boolean> {
|
|
24
38
|
try {
|
|
25
|
-
|
|
26
|
-
encoding: 'utf-8',
|
|
27
|
-
stdio: 'pipe',
|
|
28
|
-
timeout: 5000
|
|
29
|
-
});
|
|
39
|
+
dockerExec('docker info', 5000);
|
|
30
40
|
return true;
|
|
31
41
|
} catch {
|
|
32
42
|
return false;
|
|
@@ -34,40 +44,79 @@ export async function isDockerRunning(): Promise<boolean> {
|
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
/**
|
|
37
|
-
* Check if
|
|
47
|
+
* Check if a named container is running
|
|
38
48
|
*/
|
|
39
|
-
export
|
|
49
|
+
export function isContainerRunning(containerName: string): boolean {
|
|
40
50
|
try {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return result.trim().includes('4runr-postgres');
|
|
51
|
+
const state = dockerExec(
|
|
52
|
+
`docker inspect --format="{{.State.Running}}" ${containerName}`,
|
|
53
|
+
5000,
|
|
54
|
+
).trim();
|
|
55
|
+
return state === 'true';
|
|
47
56
|
} catch {
|
|
48
57
|
return false;
|
|
49
58
|
}
|
|
50
59
|
}
|
|
51
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Check if both 4Runr Postgres and Redis containers are running
|
|
63
|
+
*/
|
|
64
|
+
export async function areContainersRunning(): Promise<boolean> {
|
|
65
|
+
return FOURRUNR_DB_CONTAINERS.every((name) => isContainerRunning(name));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Start stopped 4Runr containers by name (watchdog stops them on 4r exit)
|
|
70
|
+
*/
|
|
71
|
+
export async function startStoppedContainers(): Promise<void> {
|
|
72
|
+
const stopped = FOURRUNR_DB_CONTAINERS.filter((name) => !isContainerRunning(name));
|
|
73
|
+
if (stopped.length === 0) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
dockerExec(`docker start ${stopped.join(' ')}`, 60000);
|
|
79
|
+
} catch {
|
|
80
|
+
// Container may not exist yet — compose up will create it
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
52
84
|
/**
|
|
53
85
|
* Start Docker Compose services
|
|
54
86
|
* @param composeFilePath - Absolute path to docker-compose.yml
|
|
55
87
|
*/
|
|
56
88
|
export async function startDockerCompose(composeFilePath: string): Promise<void> {
|
|
57
|
-
|
|
89
|
+
const cmd = `docker compose -p ${COMPOSE_PROJECT} -f "${composeFilePath}" up -d`;
|
|
90
|
+
execSync(cmd, {
|
|
58
91
|
stdio: 'inherit',
|
|
59
|
-
timeout:
|
|
92
|
+
timeout: 120000,
|
|
93
|
+
...(dockerShell() ? { shell: dockerShell() } : {}),
|
|
60
94
|
});
|
|
61
95
|
}
|
|
62
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Ensure Postgres + Redis are up: start stopped containers, then compose up if needed.
|
|
99
|
+
*/
|
|
100
|
+
export async function ensure4RunrStackRunning(composeFilePath: string): Promise<void> {
|
|
101
|
+
await startStoppedContainers();
|
|
102
|
+
|
|
103
|
+
if (!(await areContainersRunning())) {
|
|
104
|
+
await startDockerCompose(composeFilePath);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Watchdog may have stopped only one container — verify both after compose/start
|
|
108
|
+
await startStoppedContainers();
|
|
109
|
+
}
|
|
110
|
+
|
|
63
111
|
/**
|
|
64
112
|
* Stop Docker Compose services (preserves data volumes)
|
|
65
113
|
* @param composeFilePath - Absolute path to docker-compose.yml
|
|
66
114
|
*/
|
|
67
115
|
export async function stopDockerCompose(composeFilePath: string): Promise<void> {
|
|
68
|
-
execSync(`docker compose -f "${composeFilePath}" stop`, {
|
|
116
|
+
execSync(`docker compose -p ${COMPOSE_PROJECT} -f "${composeFilePath}" stop`, {
|
|
69
117
|
stdio: 'pipe',
|
|
70
|
-
timeout: 30000
|
|
118
|
+
timeout: 30000,
|
|
119
|
+
...(dockerShell() ? { shell: dockerShell() } : {}),
|
|
71
120
|
});
|
|
72
121
|
}
|
|
73
122
|
|
|
@@ -101,43 +150,41 @@ export async function stop4RunrContainers(): Promise<void> {
|
|
|
101
150
|
*/
|
|
102
151
|
export async function waitForHealthy(containerName: string, timeoutMs: number = 30000): Promise<void> {
|
|
103
152
|
const start = Date.now();
|
|
104
|
-
|
|
153
|
+
|
|
105
154
|
while (Date.now() - start < timeoutMs) {
|
|
106
155
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}).trim();
|
|
113
|
-
|
|
156
|
+
const health = dockerExec(
|
|
157
|
+
`docker inspect --format="{{.State.Health.Status}}" ${containerName}`,
|
|
158
|
+
5000,
|
|
159
|
+
).trim();
|
|
160
|
+
|
|
114
161
|
if (health === 'healthy') {
|
|
115
162
|
return;
|
|
116
163
|
}
|
|
117
|
-
|
|
164
|
+
|
|
118
165
|
// Also accept containers without health checks that are running
|
|
119
166
|
if (health === '' || health === '<no value>') {
|
|
120
|
-
const running =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
167
|
+
const running = dockerExec(
|
|
168
|
+
`docker inspect --format="{{.State.Running}}" ${containerName}`,
|
|
169
|
+
5000,
|
|
170
|
+
).trim();
|
|
171
|
+
|
|
126
172
|
if (running === 'true') {
|
|
127
|
-
// Give it a moment to stabilize
|
|
128
173
|
await sleep(2000);
|
|
129
174
|
return;
|
|
130
175
|
}
|
|
131
176
|
}
|
|
132
|
-
} catch
|
|
133
|
-
// Container might not exist yet or Docker command failed
|
|
134
|
-
// Continue waiting
|
|
177
|
+
} catch {
|
|
178
|
+
// Container might not exist yet or Docker command failed — keep waiting
|
|
135
179
|
}
|
|
136
|
-
|
|
180
|
+
|
|
137
181
|
await sleep(1000);
|
|
138
182
|
}
|
|
139
|
-
|
|
140
|
-
|
|
183
|
+
|
|
184
|
+
const logs = getContainerLogs(containerName, 20);
|
|
185
|
+
throw new Error(
|
|
186
|
+
`Container ${containerName} did not become healthy within ${timeoutMs}ms. Recent logs:\n${logs}`,
|
|
187
|
+
);
|
|
141
188
|
}
|
|
142
189
|
|
|
143
190
|
/**
|
|
@@ -146,16 +193,9 @@ export async function waitForHealthy(containerName: string, timeoutMs: number =
|
|
|
146
193
|
*/
|
|
147
194
|
export async function isPortInUse(port: number): Promise<boolean> {
|
|
148
195
|
try {
|
|
149
|
-
|
|
150
|
-
const result = execSync(`docker ps --filter "publish=${port}" --format "{{.Names}}"`, {
|
|
151
|
-
encoding: 'utf-8',
|
|
152
|
-
stdio: 'pipe',
|
|
153
|
-
timeout: 5000
|
|
154
|
-
});
|
|
196
|
+
const result = dockerExec(`docker ps --filter "publish=${port}" --format "{{.Names}}"`, 5000);
|
|
155
197
|
return result.trim().length > 0;
|
|
156
198
|
} catch {
|
|
157
|
-
// If command fails, assume port might be in use by non-Docker process
|
|
158
|
-
// Let Docker compose handle the conflict
|
|
159
199
|
return false;
|
|
160
200
|
}
|
|
161
201
|
}
|
|
@@ -167,11 +207,7 @@ export async function isPortInUse(port: number): Promise<boolean> {
|
|
|
167
207
|
*/
|
|
168
208
|
export function getContainerLogs(containerName: string, lines: number = 50): string {
|
|
169
209
|
try {
|
|
170
|
-
return
|
|
171
|
-
encoding: 'utf-8',
|
|
172
|
-
stdio: 'pipe',
|
|
173
|
-
timeout: 10000
|
|
174
|
-
});
|
|
210
|
+
return dockerExec(`docker logs --tail ${lines} ${containerName}`, 10000);
|
|
175
211
|
} catch (err) {
|
|
176
212
|
return `Failed to retrieve logs: ${err instanceof Error ? err.message : String(err)}`;
|
|
177
213
|
}
|
|
@@ -181,7 +217,7 @@ export function getContainerLogs(containerName: string, lines: number = 50): str
|
|
|
181
217
|
* Sleep helper
|
|
182
218
|
*/
|
|
183
219
|
function sleep(ms: number): Promise<void> {
|
|
184
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
220
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
185
221
|
}
|
|
186
222
|
|
|
187
223
|
/**
|
|
@@ -201,22 +237,22 @@ export async function getDockerStatus(): Promise<DockerStatus> {
|
|
|
201
237
|
const status: DockerStatus = {
|
|
202
238
|
available: false,
|
|
203
239
|
running: false,
|
|
204
|
-
containersRunning: false
|
|
240
|
+
containersRunning: false,
|
|
205
241
|
};
|
|
206
|
-
|
|
242
|
+
|
|
207
243
|
try {
|
|
208
244
|
status.available = await isDockerAvailable();
|
|
209
245
|
if (!status.available) {
|
|
210
246
|
status.error = 'Docker not installed';
|
|
211
247
|
return status;
|
|
212
248
|
}
|
|
213
|
-
|
|
249
|
+
|
|
214
250
|
status.running = await isDockerRunning();
|
|
215
251
|
if (!status.running) {
|
|
216
252
|
status.error = 'Docker daemon not running';
|
|
217
253
|
return status;
|
|
218
254
|
}
|
|
219
|
-
|
|
255
|
+
|
|
220
256
|
status.containersRunning = await areContainersRunning();
|
|
221
257
|
return status;
|
|
222
258
|
} catch (err) {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
isDockerRunning,
|
|
14
14
|
areContainersRunning,
|
|
15
15
|
startDockerCompose,
|
|
16
|
+
ensure4RunrStackRunning,
|
|
16
17
|
waitForHealthy,
|
|
17
18
|
getDockerStatus
|
|
18
19
|
} from './docker-manager.js';
|
|
@@ -67,21 +68,14 @@ export async function initializeDatabase(logger: any): Promise<DatabaseInitResul
|
|
|
67
68
|
return { mode: 'memory', client: null, warnings };
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
// Docker is available -
|
|
71
|
+
// Docker is available - ensure both Postgres and Redis are running
|
|
71
72
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
await startDockerContainers(logger);
|
|
77
|
-
logger.info('✓ Docker containers started');
|
|
78
|
-
} else {
|
|
79
|
-
logger.info('✓ Docker containers already running');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Wait for containers to be healthy
|
|
73
|
+
logger.info('Ensuring 4Runr Docker containers (Postgres + Redis) are running...');
|
|
74
|
+
await startDockerContainers(logger);
|
|
75
|
+
|
|
76
|
+
// Wait for containers to be healthy (Postgres first — slower cold start)
|
|
83
77
|
logger.info('Waiting for database to be ready...');
|
|
84
|
-
await waitForHealthy('4runr-postgres', 60000);
|
|
78
|
+
await waitForHealthy('4runr-postgres', 60000);
|
|
85
79
|
await waitForHealthy('4runr-redis', 15000);
|
|
86
80
|
logger.info('✓ Database containers healthy');
|
|
87
81
|
|
|
@@ -219,8 +213,8 @@ async function startDockerContainers(logger: any): Promise<void> {
|
|
|
219
213
|
logger.warn(`Docker Compose template missing; using existing ${composeFilePath}`);
|
|
220
214
|
}
|
|
221
215
|
|
|
222
|
-
// Start containers
|
|
223
|
-
await
|
|
216
|
+
// Start or restart containers (watchdog stops both when 4r exits)
|
|
217
|
+
await ensure4RunrStackRunning(composeFilePath);
|
|
224
218
|
}
|
|
225
219
|
|
|
226
220
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boot-orchestrator.d.ts","sourceRoot":"","sources":["../src/boot-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,SAAS,CAAkB;;IAOnC,OAAO,CAAC,eAAe;IAwCvB;;;OAGG;IACH,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,UAAU;IAsBlB;;OAEG;YACW,YAAY;
|
|
1
|
+
{"version":3,"file":"boot-orchestrator.d.ts","sourceRoot":"","sources":["../src/boot-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,SAAS,CAAkB;;IAOnC,OAAO,CAAC,eAAe;IAwCvB;;;OAGG;IACH,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,UAAU;IAsBlB;;OAEG;YACW,YAAY;IAmC1B;;OAEG;YACW,qBAAqB;IAiBnC;;;;;OAKG;YACW,WAAW;IA4EzB;;OAEG;YACW,WAAW;IAezB;;OAEG;YACW,kBAAkB;IAqBhC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;CAiClC;AAED,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,UAAU,CAAC,CAGnE"}
|
|
@@ -117,6 +117,11 @@ export class BootOrchestrator {
|
|
|
117
117
|
return { needsUpdate: false };
|
|
118
118
|
}
|
|
119
119
|
try {
|
|
120
|
+
const { isGlobalUpdateLockActive } = await import('./deferred-npm-install-lock.js');
|
|
121
|
+
if (isGlobalUpdateLockActive()) {
|
|
122
|
+
this.updateStep('version', 'warning', 'Update in progress — wait for updater window');
|
|
123
|
+
return { needsUpdate: false };
|
|
124
|
+
}
|
|
120
125
|
const { checkForUpdates } = await import('./version-check.js');
|
|
121
126
|
const versionInfo = await checkForUpdates('4runr-os');
|
|
122
127
|
if (versionInfo.needsUpdate) {
|