@pushpalsdev/cli 1.0.35 → 1.0.36
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/pushpals-cli.js +395 -126
- package/package.json +1 -1
- package/runtime/sandbox/package.json +1 -0
package/dist/pushpals-cli.js
CHANGED
|
@@ -17,6 +17,258 @@ import {
|
|
|
17
17
|
import { basename, delimiter, dirname, extname, join as join2, resolve as resolve4, win32 as pathWin32 } from "path";
|
|
18
18
|
import { createInterface } from "readline";
|
|
19
19
|
|
|
20
|
+
// ../shared/src/localbuddy_runtime.ts
|
|
21
|
+
var TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
22
|
+
var FALSY = new Set(["0", "false", "no", "off"]);
|
|
23
|
+
|
|
24
|
+
// ../../scripts/start_runtime_services.ts
|
|
25
|
+
var DEFAULT_SERVICE_MANAGER_POLL_MS = 1000;
|
|
26
|
+
var DEFAULT_SERVICE_MANAGER_MAX_RESTART_ATTEMPTS = 4;
|
|
27
|
+
var DEFAULT_SERVICE_MANAGER_STABLE_WINDOW_MS = 60000;
|
|
28
|
+
var DEFAULT_SERVICE_MANAGER_BASE_BACKOFF_MS = 2000;
|
|
29
|
+
var DEFAULT_SERVICE_MANAGER_MAX_BACKOFF_MS = 30000;
|
|
30
|
+
function formatEmbeddedRuntimeHealthLines(health) {
|
|
31
|
+
if (!health)
|
|
32
|
+
return [];
|
|
33
|
+
const lines = [`[pushpals] embeddedRuntime=${health.state} detail=${health.detail}`];
|
|
34
|
+
if (health.action) {
|
|
35
|
+
lines.push(`[pushpals] embeddedRuntimeAction=${health.action}`);
|
|
36
|
+
}
|
|
37
|
+
return lines;
|
|
38
|
+
}
|
|
39
|
+
function computeServiceRestartBackoffMs(attempt) {
|
|
40
|
+
const boundedAttempt = Math.max(1, Math.floor(attempt));
|
|
41
|
+
const exponential = DEFAULT_SERVICE_MANAGER_BASE_BACKOFF_MS * Math.pow(2, boundedAttempt - 1);
|
|
42
|
+
return Math.max(DEFAULT_SERVICE_MANAGER_BASE_BACKOFF_MS, Math.min(DEFAULT_SERVICE_MANAGER_MAX_BACKOFF_MS, Math.floor(exponential)));
|
|
43
|
+
}
|
|
44
|
+
function shouldRestartService(attempts, maxAttempts = DEFAULT_SERVICE_MANAGER_MAX_RESTART_ATTEMPTS) {
|
|
45
|
+
const normalizedAttempts = Math.max(0, Math.floor(attempts));
|
|
46
|
+
const normalizedMax = Math.max(1, Math.floor(maxAttempts));
|
|
47
|
+
return normalizedAttempts < normalizedMax;
|
|
48
|
+
}
|
|
49
|
+
function pipeProcessStreamToLines(stream, onLine) {
|
|
50
|
+
if (!stream || typeof stream === "number" || typeof stream.getReader !== "function")
|
|
51
|
+
return;
|
|
52
|
+
const reader = stream.getReader();
|
|
53
|
+
const decoder = new TextDecoder;
|
|
54
|
+
let pending = "";
|
|
55
|
+
(async () => {
|
|
56
|
+
try {
|
|
57
|
+
while (true) {
|
|
58
|
+
const { done, value } = await reader.read();
|
|
59
|
+
if (done)
|
|
60
|
+
break;
|
|
61
|
+
pending += decoder.decode(value, { stream: true });
|
|
62
|
+
const lines = pending.split(/\r?\n/);
|
|
63
|
+
pending = lines.pop() ?? "";
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
const trimmed = line.trimEnd();
|
|
66
|
+
if (!trimmed)
|
|
67
|
+
continue;
|
|
68
|
+
onLine?.(trimmed);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const rest = decoder.decode();
|
|
72
|
+
if (rest)
|
|
73
|
+
pending += rest;
|
|
74
|
+
const tail = pending.trimEnd();
|
|
75
|
+
if (tail)
|
|
76
|
+
onLine?.(tail);
|
|
77
|
+
} catch {} finally {
|
|
78
|
+
reader.releaseLock();
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
}
|
|
82
|
+
function spawnManagedService(spec) {
|
|
83
|
+
const env = { ...spec.env ?? {} };
|
|
84
|
+
const proc = Bun.spawn(spec.command, {
|
|
85
|
+
cwd: spec.cwd,
|
|
86
|
+
env,
|
|
87
|
+
stdout: "pipe",
|
|
88
|
+
stderr: "pipe"
|
|
89
|
+
});
|
|
90
|
+
pipeProcessStreamToLines(proc.stdout, spec.onStdoutLine);
|
|
91
|
+
pipeProcessStreamToLines(proc.stderr, spec.onStderrLine);
|
|
92
|
+
const service = {
|
|
93
|
+
name: spec.name,
|
|
94
|
+
proc,
|
|
95
|
+
command: [...spec.command],
|
|
96
|
+
cwd: spec.cwd,
|
|
97
|
+
env,
|
|
98
|
+
exited: false,
|
|
99
|
+
exitCode: null,
|
|
100
|
+
launchedAtMs: Date.now(),
|
|
101
|
+
logPath: spec.logPath
|
|
102
|
+
};
|
|
103
|
+
proc.exited.then((code) => {
|
|
104
|
+
service.exited = true;
|
|
105
|
+
service.exitCode = code;
|
|
106
|
+
});
|
|
107
|
+
return service;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class ServiceManager {
|
|
111
|
+
services = new Map;
|
|
112
|
+
launchSpecs = new Map;
|
|
113
|
+
stateByService = new Map;
|
|
114
|
+
degradedServiceReasons = new Map;
|
|
115
|
+
pollMs;
|
|
116
|
+
maxRestartAttempts;
|
|
117
|
+
stableWindowMs;
|
|
118
|
+
computeRestartBackoffMs;
|
|
119
|
+
degradedAction;
|
|
120
|
+
spawnService;
|
|
121
|
+
onHealthChange;
|
|
122
|
+
onServiceDegraded;
|
|
123
|
+
onEvent;
|
|
124
|
+
timer;
|
|
125
|
+
stopped = false;
|
|
126
|
+
constructor(options = {}) {
|
|
127
|
+
this.pollMs = Math.max(50, Math.floor(options.pollMs ?? DEFAULT_SERVICE_MANAGER_POLL_MS));
|
|
128
|
+
this.maxRestartAttempts = Math.max(1, Math.floor(options.maxRestartAttempts ?? DEFAULT_SERVICE_MANAGER_MAX_RESTART_ATTEMPTS));
|
|
129
|
+
this.stableWindowMs = Math.max(1000, Math.floor(options.stableWindowMs ?? DEFAULT_SERVICE_MANAGER_STABLE_WINDOW_MS));
|
|
130
|
+
this.computeRestartBackoffMs = options.computeRestartBackoffMs ?? computeServiceRestartBackoffMs;
|
|
131
|
+
this.degradedAction = options.degradedAction ?? "Inspect the affected service logs or restart the runtime after fixing the failure.";
|
|
132
|
+
this.spawnService = options.spawnService ?? spawnManagedService;
|
|
133
|
+
this.onHealthChange = options.onHealthChange;
|
|
134
|
+
this.onServiceDegraded = options.onServiceDegraded;
|
|
135
|
+
this.onEvent = options.onEvent;
|
|
136
|
+
this.timer = setInterval(() => this.tick(), this.pollMs);
|
|
137
|
+
}
|
|
138
|
+
startService(spec) {
|
|
139
|
+
this.launchSpecs.set(spec.name, {
|
|
140
|
+
...spec,
|
|
141
|
+
command: [...spec.command],
|
|
142
|
+
env: { ...spec.env ?? {} }
|
|
143
|
+
});
|
|
144
|
+
const service = this.spawnService(spec);
|
|
145
|
+
this.services.set(spec.name, service);
|
|
146
|
+
return service;
|
|
147
|
+
}
|
|
148
|
+
getServices() {
|
|
149
|
+
return Array.from(this.services.values());
|
|
150
|
+
}
|
|
151
|
+
getService(name) {
|
|
152
|
+
return this.services.get(name) ?? null;
|
|
153
|
+
}
|
|
154
|
+
getHealth() {
|
|
155
|
+
if (this.degradedServiceReasons.size === 0)
|
|
156
|
+
return null;
|
|
157
|
+
const detail = Array.from(this.degradedServiceReasons.entries()).map(([name, reason]) => `${name}: ${reason}`).join(" | ");
|
|
158
|
+
return {
|
|
159
|
+
state: "degraded",
|
|
160
|
+
detail,
|
|
161
|
+
action: this.degradedAction
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
stop() {
|
|
165
|
+
if (this.stopped)
|
|
166
|
+
return;
|
|
167
|
+
this.stopped = true;
|
|
168
|
+
clearInterval(this.timer);
|
|
169
|
+
for (const state of this.stateByService.values()) {
|
|
170
|
+
if (!state.pendingRestartTimer)
|
|
171
|
+
continue;
|
|
172
|
+
clearTimeout(state.pendingRestartTimer);
|
|
173
|
+
state.pendingRestartTimer = null;
|
|
174
|
+
}
|
|
175
|
+
for (const service of this.services.values()) {
|
|
176
|
+
try {
|
|
177
|
+
const pid = service.proc.pid;
|
|
178
|
+
if (process.platform === "win32" && typeof pid === "number" && pid > 0) {
|
|
179
|
+
Bun.spawnSync(["taskkill", "/PID", String(pid), "/T", "/F"], {
|
|
180
|
+
stdin: "ignore",
|
|
181
|
+
stdout: "ignore",
|
|
182
|
+
stderr: "ignore"
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
service.proc.kill();
|
|
186
|
+
}
|
|
187
|
+
} catch {}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
ensureState(name) {
|
|
191
|
+
const existing = this.stateByService.get(name);
|
|
192
|
+
if (existing)
|
|
193
|
+
return existing;
|
|
194
|
+
const created = {
|
|
195
|
+
attempts: 0,
|
|
196
|
+
nextRestartAtMs: 0,
|
|
197
|
+
lastRestartReason: "",
|
|
198
|
+
pendingRestartTimer: null
|
|
199
|
+
};
|
|
200
|
+
this.stateByService.set(name, created);
|
|
201
|
+
return created;
|
|
202
|
+
}
|
|
203
|
+
emitHealthChange() {
|
|
204
|
+
this.onHealthChange?.(this.getHealth());
|
|
205
|
+
}
|
|
206
|
+
emitEvent(level, line) {
|
|
207
|
+
this.onEvent?.(level, line);
|
|
208
|
+
}
|
|
209
|
+
tick() {
|
|
210
|
+
if (this.stopped)
|
|
211
|
+
return;
|
|
212
|
+
const now = Date.now();
|
|
213
|
+
for (const [name, service] of this.services.entries()) {
|
|
214
|
+
const launchSpec = this.launchSpecs.get(name);
|
|
215
|
+
if (!launchSpec)
|
|
216
|
+
continue;
|
|
217
|
+
const state = this.ensureState(name);
|
|
218
|
+
if (!service.exited) {
|
|
219
|
+
if (state.attempts > 0 && now - service.launchedAtMs >= this.stableWindowMs) {
|
|
220
|
+
state.attempts = 0;
|
|
221
|
+
state.nextRestartAtMs = 0;
|
|
222
|
+
state.lastRestartReason = "";
|
|
223
|
+
}
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (state.pendingRestartTimer)
|
|
227
|
+
continue;
|
|
228
|
+
if (state.nextRestartAtMs > now)
|
|
229
|
+
continue;
|
|
230
|
+
const reason = `exit code ${service.exitCode ?? "unknown"}`;
|
|
231
|
+
if (!shouldRestartService(state.attempts, this.maxRestartAttempts)) {
|
|
232
|
+
this.emitEvent("error", `Managed ${name} exited (${reason}) and reached restart limit (${state.attempts}/${this.maxRestartAttempts}).`);
|
|
233
|
+
this.launchSpecs.delete(name);
|
|
234
|
+
if (!this.degradedServiceReasons.has(name)) {
|
|
235
|
+
const degradationReason = `reached restart limit after ${reason} (${state.attempts}/${this.maxRestartAttempts})`;
|
|
236
|
+
this.degradedServiceReasons.set(name, degradationReason);
|
|
237
|
+
const health = this.getHealth();
|
|
238
|
+
if (health) {
|
|
239
|
+
this.onHealthChange?.(health);
|
|
240
|
+
this.onServiceDegraded?.(name, degradationReason, health);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const nextAttempt = state.attempts + 1;
|
|
246
|
+
state.lastRestartReason = reason;
|
|
247
|
+
const backoffMs = Math.max(1, Math.floor(this.computeRestartBackoffMs(nextAttempt)));
|
|
248
|
+
this.emitEvent("warn", `Managed ${name} exited (${reason}); restarting attempt ${nextAttempt}/${this.maxRestartAttempts} in ${backoffMs}ms.`);
|
|
249
|
+
state.nextRestartAtMs = now + backoffMs;
|
|
250
|
+
state.pendingRestartTimer = setTimeout(() => {
|
|
251
|
+
state.pendingRestartTimer = null;
|
|
252
|
+
state.nextRestartAtMs = 0;
|
|
253
|
+
if (this.stopped)
|
|
254
|
+
return;
|
|
255
|
+
const current = this.services.get(name);
|
|
256
|
+
if (!current || !current.exited)
|
|
257
|
+
return;
|
|
258
|
+
const spec = this.launchSpecs.get(name);
|
|
259
|
+
if (!spec)
|
|
260
|
+
return;
|
|
261
|
+
if (!shouldRestartService(state.attempts, this.maxRestartAttempts))
|
|
262
|
+
return;
|
|
263
|
+
state.attempts += 1;
|
|
264
|
+
const restarted = this.spawnService(spec);
|
|
265
|
+
this.services.set(name, restarted);
|
|
266
|
+
this.emitEvent("log", `Restarted managed ${name}.`);
|
|
267
|
+
}, backoffMs);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
20
272
|
// ../shared/src/client_preflight.ts
|
|
21
273
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
22
274
|
import { relative, resolve as resolve2 } from "path";
|
|
@@ -101,8 +353,8 @@ function normalizeLoopbackHttpUrl(value, fallbackPort) {
|
|
|
101
353
|
// ../shared/src/config.ts
|
|
102
354
|
var PROJECT_ROOT = resolve(import.meta.dir, "..", "..", "..");
|
|
103
355
|
var DEFAULT_CONFIG_DIR = "configs";
|
|
104
|
-
var
|
|
105
|
-
var
|
|
356
|
+
var TRUTHY2 = new Set(["1", "true", "yes", "on"]);
|
|
357
|
+
var FALSY2 = new Set(["0", "false", "no", "off"]);
|
|
106
358
|
var DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE = 8;
|
|
107
359
|
var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS = 1;
|
|
108
360
|
var DEFAULT_WORKERPALS_FILE_MODIFYING_JOBS = ["task.execute"];
|
|
@@ -133,9 +385,9 @@ function parseBoolEnv(name) {
|
|
|
133
385
|
const raw = (process.env[name] ?? "").trim().toLowerCase();
|
|
134
386
|
if (!raw)
|
|
135
387
|
return null;
|
|
136
|
-
if (
|
|
388
|
+
if (TRUTHY2.has(raw))
|
|
137
389
|
return true;
|
|
138
|
-
if (
|
|
390
|
+
if (FALSY2.has(raw))
|
|
139
391
|
return false;
|
|
140
392
|
return null;
|
|
141
393
|
}
|
|
@@ -192,9 +444,9 @@ function asBoolean(value, fallback) {
|
|
|
192
444
|
return value;
|
|
193
445
|
if (typeof value === "string") {
|
|
194
446
|
const lowered = value.trim().toLowerCase();
|
|
195
|
-
if (
|
|
447
|
+
if (TRUTHY2.has(lowered))
|
|
196
448
|
return true;
|
|
197
|
-
if (
|
|
449
|
+
if (FALSY2.has(lowered))
|
|
198
450
|
return false;
|
|
199
451
|
}
|
|
200
452
|
return fallback;
|
|
@@ -1217,6 +1469,7 @@ var DEFAULT_RUNTIME_BOOT_TIMEOUT_MS = 90000;
|
|
|
1217
1469
|
var DEFAULT_RUNTIME_BOOT_POLL_MS = 1000;
|
|
1218
1470
|
var DEFAULT_SERVER_BOOT_TIMEOUT_MS = 20000;
|
|
1219
1471
|
var DEFAULT_SERVICE_STABILITY_GRACE_MS = 4000;
|
|
1472
|
+
var EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS = 4;
|
|
1220
1473
|
var WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS = 15000;
|
|
1221
1474
|
var EMBEDDED_RUNTIME_SAFETY_CAP_DISABLE_ENV = "PUSHPALS_DISABLE_EMBEDDED_SAFETY_CAPS";
|
|
1222
1475
|
var EMBEDDED_RUNTIME_WINDOWS_SAFETY_CAPS = {
|
|
@@ -1292,6 +1545,9 @@ function formatWorkerExecutionReadinessLines(readiness) {
|
|
|
1292
1545
|
}
|
|
1293
1546
|
return lines;
|
|
1294
1547
|
}
|
|
1548
|
+
function formatEmbeddedRuntimeHealthLines2(health) {
|
|
1549
|
+
return formatEmbeddedRuntimeHealthLines(health);
|
|
1550
|
+
}
|
|
1295
1551
|
function summarizeWorkerStatusRows(workers) {
|
|
1296
1552
|
const onlineWorkers = workers.filter((worker) => Boolean(worker?.isOnline) && String(worker?.status ?? "").trim().toLowerCase() !== "offline");
|
|
1297
1553
|
const idleWorkers = onlineWorkers.filter((worker) => Number(worker?.activeJobCount ?? 0) <= 0);
|
|
@@ -2282,71 +2538,6 @@ async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
|
|
|
2282
2538
|
console.log("[pushpals] Embedded runtime binaries are ready.");
|
|
2283
2539
|
return runtimeBinaries;
|
|
2284
2540
|
}
|
|
2285
|
-
function spawnRuntimeService(name, command, cwd, env, logPath, runtimeServicesLogPath) {
|
|
2286
|
-
const header = `[pushpals] service=${name} command=${command.join(" ")} cwd=${cwd}`;
|
|
2287
|
-
writeFileSync(logPath, `${header}
|
|
2288
|
-
`, "utf8");
|
|
2289
|
-
if (runtimeServicesLogPath) {
|
|
2290
|
-
appendRuntimeServicesLogLine(runtimeServicesLogPath, header);
|
|
2291
|
-
}
|
|
2292
|
-
const proc = Bun.spawn(command, {
|
|
2293
|
-
cwd,
|
|
2294
|
-
env,
|
|
2295
|
-
stdout: "pipe",
|
|
2296
|
-
stderr: "pipe"
|
|
2297
|
-
});
|
|
2298
|
-
const pipeToLog = async (stream, channel) => {
|
|
2299
|
-
if (!stream)
|
|
2300
|
-
return;
|
|
2301
|
-
const reader = stream.getReader();
|
|
2302
|
-
const decoder = new TextDecoder;
|
|
2303
|
-
let pending = "";
|
|
2304
|
-
while (true) {
|
|
2305
|
-
const { done, value } = await reader.read();
|
|
2306
|
-
if (done)
|
|
2307
|
-
break;
|
|
2308
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
2309
|
-
if (!chunk)
|
|
2310
|
-
continue;
|
|
2311
|
-
pending += chunk;
|
|
2312
|
-
const lines = pending.split(/\r?\n/);
|
|
2313
|
-
pending = lines.pop() ?? "";
|
|
2314
|
-
for (const line of lines) {
|
|
2315
|
-
const serviceLine = `[${channel}] ${line}`;
|
|
2316
|
-
appendFileSync(logPath, `${serviceLine}
|
|
2317
|
-
`, "utf8");
|
|
2318
|
-
if (runtimeServicesLogPath) {
|
|
2319
|
-
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[${name}] ${serviceLine}`);
|
|
2320
|
-
}
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
const rest = decoder.decode();
|
|
2324
|
-
if (rest)
|
|
2325
|
-
pending += rest;
|
|
2326
|
-
if (pending.trim().length > 0) {
|
|
2327
|
-
const serviceLine = `[${channel}] ${pending.trimEnd()}`;
|
|
2328
|
-
appendFileSync(logPath, `${serviceLine}
|
|
2329
|
-
`, "utf8");
|
|
2330
|
-
if (runtimeServicesLogPath) {
|
|
2331
|
-
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[${name}] ${serviceLine}`);
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
};
|
|
2335
|
-
pipeToLog(proc.stdout, "stdout");
|
|
2336
|
-
pipeToLog(proc.stderr, "stderr");
|
|
2337
|
-
const service = {
|
|
2338
|
-
name,
|
|
2339
|
-
proc,
|
|
2340
|
-
logPath,
|
|
2341
|
-
exited: false,
|
|
2342
|
-
exitCode: null
|
|
2343
|
-
};
|
|
2344
|
-
proc.exited.then((code) => {
|
|
2345
|
-
service.exited = true;
|
|
2346
|
-
service.exitCode = code;
|
|
2347
|
-
});
|
|
2348
|
-
return service;
|
|
2349
|
-
}
|
|
2350
2541
|
function buildServiceStopCommand(pid, platform = process.platform) {
|
|
2351
2542
|
if (platform === "win32" && typeof pid === "number" && pid > 0) {
|
|
2352
2543
|
return ["taskkill", "/PID", String(pid), "/T", "/F"];
|
|
@@ -2560,6 +2751,12 @@ function quoteWindowsCmdArg(value) {
|
|
|
2560
2751
|
function isOptionalEmbeddedService(name) {
|
|
2561
2752
|
return name === "source_control_manager";
|
|
2562
2753
|
}
|
|
2754
|
+
function computeEmbeddedServiceRestartBackoffMs(attempt) {
|
|
2755
|
+
return computeServiceRestartBackoffMs(attempt);
|
|
2756
|
+
}
|
|
2757
|
+
function shouldRestartEmbeddedService(attempts, maxAttempts = EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS) {
|
|
2758
|
+
return shouldRestartService(attempts, maxAttempts);
|
|
2759
|
+
}
|
|
2563
2760
|
async function canSpawnCommand(command, cwd, env) {
|
|
2564
2761
|
try {
|
|
2565
2762
|
const proc = Bun.spawn(command, {
|
|
@@ -3482,7 +3679,6 @@ async function autoStartRuntimeServices(opts) {
|
|
|
3482
3679
|
if (resolvedGitBinary) {
|
|
3483
3680
|
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, resolvedGitBinary);
|
|
3484
3681
|
}
|
|
3485
|
-
const services = [];
|
|
3486
3682
|
const startupStartedAt = Date.now();
|
|
3487
3683
|
const startupPhases = [];
|
|
3488
3684
|
const recordStartupPhase = (name, startedAt, status) => {
|
|
@@ -3521,23 +3717,70 @@ async function autoStartRuntimeServices(opts) {
|
|
|
3521
3717
|
console.log(`[pushpals] service log (localbuddy)=${serviceLogPaths.localbuddy}`);
|
|
3522
3718
|
console.log(`[pushpals] service log (remotebuddy)=${serviceLogPaths.remotebuddy}`);
|
|
3523
3719
|
console.log(`[pushpals] service log (source_control_manager)=${serviceLogPaths.source_control_manager}`);
|
|
3720
|
+
const serviceManager = new ServiceManager({
|
|
3721
|
+
degradedAction: "Inspect the embedded service log or restart pushpals after fixing the runtime failure.",
|
|
3722
|
+
onEvent: (level, line) => {
|
|
3723
|
+
const cliLine = `[pushpals] ${line.replace(/^Managed /, "Embedded ").replace(/^Restarted managed /, "Restarted embedded ")}`;
|
|
3724
|
+
if (level === "error") {
|
|
3725
|
+
console.error(cliLine);
|
|
3726
|
+
} else if (level === "warn") {
|
|
3727
|
+
console.warn(cliLine);
|
|
3728
|
+
} else {
|
|
3729
|
+
console.log(cliLine);
|
|
3730
|
+
}
|
|
3731
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, cliLine);
|
|
3732
|
+
},
|
|
3733
|
+
onHealthChange: (health) => {
|
|
3734
|
+
for (const line of formatEmbeddedRuntimeHealthLines2(health)) {
|
|
3735
|
+
console.error(line);
|
|
3736
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, line);
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
});
|
|
3740
|
+
const launchService = (name, command) => {
|
|
3741
|
+
const logPath = serviceLogPaths[name];
|
|
3742
|
+
const header = `[pushpals] service=${name} command=${command.join(" ")} cwd=${opts.repoRoot}`;
|
|
3743
|
+
writeFileSync(logPath, `${header}
|
|
3744
|
+
`, "utf8");
|
|
3745
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, header);
|
|
3746
|
+
return serviceManager.startService({
|
|
3747
|
+
name,
|
|
3748
|
+
color: "",
|
|
3749
|
+
command,
|
|
3750
|
+
cwd: opts.repoRoot,
|
|
3751
|
+
env: runtimeEnv,
|
|
3752
|
+
logPath,
|
|
3753
|
+
onStdoutLine: (line) => {
|
|
3754
|
+
const serviceLine = `[stdout] ${line}`;
|
|
3755
|
+
appendFileSync(logPath, `${serviceLine}
|
|
3756
|
+
`, "utf8");
|
|
3757
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[${name}] ${serviceLine}`);
|
|
3758
|
+
},
|
|
3759
|
+
onStderrLine: (line) => {
|
|
3760
|
+
const serviceLine = `[stderr] ${line}`;
|
|
3761
|
+
appendFileSync(logPath, `${serviceLine}
|
|
3762
|
+
`, "utf8");
|
|
3763
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[${name}] ${serviceLine}`);
|
|
3764
|
+
}
|
|
3765
|
+
});
|
|
3766
|
+
};
|
|
3524
3767
|
const serverHealthy = await probeServer(opts.serverUrl);
|
|
3525
3768
|
if (!serverHealthy) {
|
|
3526
3769
|
const serverPhaseStartedAt = Date.now();
|
|
3527
3770
|
console.log("[pushpals] Starting embedded server...");
|
|
3528
|
-
const serverService =
|
|
3529
|
-
|
|
3530
|
-
console.log(`[pushpals] server log: ${
|
|
3771
|
+
const serverService = launchService("server", [runtimeBinaries.server]);
|
|
3772
|
+
const serverLogPath = serverService.logPath ?? serviceLogPaths.server;
|
|
3773
|
+
console.log(`[pushpals] server log: ${serverLogPath}`);
|
|
3531
3774
|
const serverDeadline = Date.now() + DEFAULT_SERVER_BOOT_TIMEOUT_MS;
|
|
3532
3775
|
let serverIsReady = false;
|
|
3533
3776
|
while (Date.now() < serverDeadline) {
|
|
3534
3777
|
if (serverService.exited) {
|
|
3535
|
-
const tail = readLogTail(
|
|
3778
|
+
const tail = readLogTail(serverLogPath);
|
|
3536
3779
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded server exited during bootstrap (code=${serverService.exitCode ?? "unknown"}).`);
|
|
3537
3780
|
recordStartupPhase("server", serverPhaseStartedAt, "exited");
|
|
3538
3781
|
emitStartupTimingSummary("failed", "server exited during bootstrap");
|
|
3539
|
-
stopRuntimeServices(
|
|
3540
|
-
throw new Error(`Embedded server exited during bootstrap (code=${serverService.exitCode ?? "unknown"}). ` + `See ${
|
|
3782
|
+
stopRuntimeServices(serviceManager.getServices());
|
|
3783
|
+
throw new Error(`Embedded server exited during bootstrap (code=${serverService.exitCode ?? "unknown"}). ` + `See ${serverLogPath}${tail ? `
|
|
3541
3784
|
--- server log tail ---
|
|
3542
3785
|
${tail}` : ""}`);
|
|
3543
3786
|
}
|
|
@@ -3548,12 +3791,12 @@ ${tail}` : ""}`);
|
|
|
3548
3791
|
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
3549
3792
|
}
|
|
3550
3793
|
if (!serverIsReady) {
|
|
3551
|
-
const tail = readLogTail(
|
|
3794
|
+
const tail = readLogTail(serverLogPath);
|
|
3552
3795
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded server did not become healthy within ${DEFAULT_SERVER_BOOT_TIMEOUT_MS}ms.`);
|
|
3553
3796
|
recordStartupPhase("server", serverPhaseStartedAt, "timeout");
|
|
3554
3797
|
emitStartupTimingSummary("failed", "server health timeout");
|
|
3555
|
-
stopRuntimeServices(
|
|
3556
|
-
throw new Error(`Embedded server did not become healthy within ${DEFAULT_SERVER_BOOT_TIMEOUT_MS}ms. ` + `See ${
|
|
3798
|
+
stopRuntimeServices(serviceManager.getServices());
|
|
3799
|
+
throw new Error(`Embedded server did not become healthy within ${DEFAULT_SERVER_BOOT_TIMEOUT_MS}ms. ` + `See ${serverLogPath}${tail ? `
|
|
3557
3800
|
--- server log tail ---
|
|
3558
3801
|
${tail}` : ""}`);
|
|
3559
3802
|
}
|
|
@@ -3567,9 +3810,8 @@ ${tail}` : ""}`);
|
|
|
3567
3810
|
if (localBuddyEnabled) {
|
|
3568
3811
|
const localBuddyPhaseStartedAt = Date.now();
|
|
3569
3812
|
console.log("[pushpals] Starting embedded LocalBuddy...");
|
|
3570
|
-
const localbuddyService =
|
|
3571
|
-
|
|
3572
|
-
console.log(`[pushpals] localbuddy log: ${localbuddyService.logPath}`);
|
|
3813
|
+
const localbuddyService = launchService("localbuddy", [runtimeBinaries.localbuddy]);
|
|
3814
|
+
console.log(`[pushpals] localbuddy log: ${localbuddyService.logPath ?? serviceLogPaths.localbuddy}`);
|
|
3573
3815
|
recordStartupPhase("localbuddy", localBuddyPhaseStartedAt, "started");
|
|
3574
3816
|
} else {
|
|
3575
3817
|
recordStartupPhase("localbuddy", Date.now(), "skipped");
|
|
@@ -3578,13 +3820,13 @@ ${tail}` : ""}`);
|
|
|
3578
3820
|
}
|
|
3579
3821
|
const remoteBuddyPhaseStartedAt = Date.now();
|
|
3580
3822
|
console.log("[pushpals] Starting embedded RemoteBuddy...");
|
|
3581
|
-
const remotebuddyService =
|
|
3582
|
-
|
|
3583
|
-
console.log(`[pushpals] remotebuddy log: ${
|
|
3823
|
+
const remotebuddyService = launchService("remotebuddy", [runtimeBinaries.remotebuddy]);
|
|
3824
|
+
const remotebuddyLogPath = remotebuddyService.logPath ?? serviceLogPaths.remotebuddy;
|
|
3825
|
+
console.log(`[pushpals] remotebuddy log: ${remotebuddyLogPath}`);
|
|
3584
3826
|
recordStartupPhase("remotebuddy", remoteBuddyPhaseStartedAt, "started");
|
|
3585
3827
|
let lastReportedRemoteBuddyAutonomyState = "unknown";
|
|
3586
3828
|
const reportRemoteBuddyAutonomousEngineState = () => {
|
|
3587
|
-
const autonomyState = readRemoteBuddyAutonomousEngineState(
|
|
3829
|
+
const autonomyState = readRemoteBuddyAutonomousEngineState(remotebuddyLogPath);
|
|
3588
3830
|
if (autonomyState === "unknown" || autonomyState === lastReportedRemoteBuddyAutonomyState) {
|
|
3589
3831
|
return;
|
|
3590
3832
|
}
|
|
@@ -3635,9 +3877,11 @@ ${tail}` : ""}`);
|
|
|
3635
3877
|
} else if (scmRemoteStatus.status === "ok") {
|
|
3636
3878
|
console.log(`[pushpals] Embedded SourceControlManager git=${scmGitProbe.detail}`);
|
|
3637
3879
|
console.log("[pushpals] Starting embedded SourceControlManager...");
|
|
3638
|
-
const sourceControlManagerService =
|
|
3639
|
-
|
|
3640
|
-
|
|
3880
|
+
const sourceControlManagerService = launchService("source_control_manager", [
|
|
3881
|
+
runtimeBinaries.sourceControlManager,
|
|
3882
|
+
"--skip-clean-check"
|
|
3883
|
+
]);
|
|
3884
|
+
console.log(`[pushpals] source_control_manager log: ${sourceControlManagerService.logPath ?? serviceLogPaths.source_control_manager}`);
|
|
3641
3885
|
recordStartupPhase("source_control_manager", scmPhaseStartedAt, "started");
|
|
3642
3886
|
} else {
|
|
3643
3887
|
console.log(`[pushpals] Repo has no git remote "${opts.sourceControlManagerRemote}"; skipping embedded SourceControlManager.`);
|
|
@@ -3651,30 +3895,36 @@ ${tail}` : ""}`);
|
|
|
3651
3895
|
}
|
|
3652
3896
|
const deadline = Date.now() + DEFAULT_RUNTIME_BOOT_TIMEOUT_MS;
|
|
3653
3897
|
const readinessPhaseStartedAt = Date.now();
|
|
3898
|
+
const optionalServiceExitWarned = new Set;
|
|
3654
3899
|
while (Date.now() < deadline) {
|
|
3655
3900
|
reportRemoteBuddyAutonomousEngineState();
|
|
3656
|
-
for (
|
|
3657
|
-
const service = services[i];
|
|
3901
|
+
for (const service of serviceManager.getServices()) {
|
|
3658
3902
|
if (service.exited) {
|
|
3659
3903
|
if (isOptionalEmbeddedService(service.name)) {
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3904
|
+
const runtimeServiceName2 = service.name;
|
|
3905
|
+
const serviceLogPath2 = service.logPath ?? serviceLogPaths[runtimeServiceName2];
|
|
3906
|
+
if (!optionalServiceExitWarned.has(runtimeServiceName2)) {
|
|
3907
|
+
console.warn(`[pushpals] Embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}); startup will continue and host supervisor will attempt recovery.`);
|
|
3908
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}); startup will continue and host supervisor will attempt recovery.`);
|
|
3909
|
+
const tail2 = readLogTail(serviceLogPath2);
|
|
3910
|
+
if (tail2) {
|
|
3911
|
+
console.warn(`[pushpals] ${service.name} log tail:
|
|
3665
3912
|
${tail2}`);
|
|
3666
|
-
|
|
3913
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] ${service.name} log tail:
|
|
3667
3914
|
${tail2}`);
|
|
3915
|
+
}
|
|
3916
|
+
optionalServiceExitWarned.add(runtimeServiceName2);
|
|
3668
3917
|
}
|
|
3669
|
-
services.splice(i, 1);
|
|
3670
3918
|
continue;
|
|
3671
3919
|
}
|
|
3672
|
-
const
|
|
3920
|
+
const runtimeServiceName = service.name;
|
|
3921
|
+
const serviceLogPath = service.logPath ?? serviceLogPaths[runtimeServiceName];
|
|
3922
|
+
const tail = readLogTail(serviceLogPath);
|
|
3673
3923
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}).`);
|
|
3674
3924
|
recordStartupPhase("readiness", readinessPhaseStartedAt, "failed");
|
|
3675
3925
|
emitStartupTimingSummary("failed", `${service.name} exited during startup`);
|
|
3676
|
-
stopRuntimeServices(
|
|
3677
|
-
throw new Error(`Embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}). ` + `See ${
|
|
3926
|
+
stopRuntimeServices(serviceManager.getServices());
|
|
3927
|
+
throw new Error(`Embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}). ` + `See ${serviceLogPath}${tail ? `
|
|
3678
3928
|
--- ${service.name} log tail ---
|
|
3679
3929
|
${tail}` : ""}`);
|
|
3680
3930
|
}
|
|
@@ -3686,29 +3936,34 @@ ${tail}` : ""}`);
|
|
|
3686
3936
|
const stabilityDeadline = Date.now() + DEFAULT_SERVICE_STABILITY_GRACE_MS;
|
|
3687
3937
|
while (Date.now() < stabilityDeadline) {
|
|
3688
3938
|
reportRemoteBuddyAutonomousEngineState();
|
|
3689
|
-
for (
|
|
3690
|
-
const service = services[i];
|
|
3939
|
+
for (const service of serviceManager.getServices()) {
|
|
3691
3940
|
if (!service.exited)
|
|
3692
3941
|
continue;
|
|
3693
3942
|
if (isOptionalEmbeddedService(service.name)) {
|
|
3694
|
-
const
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
console.warn(`[pushpals] ${service.name}
|
|
3943
|
+
const runtimeServiceName2 = service.name;
|
|
3944
|
+
const serviceLogPath2 = service.logPath ?? serviceLogPaths[runtimeServiceName2];
|
|
3945
|
+
if (!optionalServiceExitWarned.has(runtimeServiceName2)) {
|
|
3946
|
+
const tail2 = readLogTail(serviceLogPath2);
|
|
3947
|
+
console.warn(`[pushpals] Embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}); startup will continue and host supervisor will attempt recovery.`);
|
|
3948
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}); startup will continue and host supervisor will attempt recovery.`);
|
|
3949
|
+
if (tail2) {
|
|
3950
|
+
console.warn(`[pushpals] ${service.name} log tail:
|
|
3699
3951
|
${tail2}`);
|
|
3700
|
-
|
|
3952
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] ${service.name} log tail:
|
|
3701
3953
|
${tail2}`);
|
|
3954
|
+
}
|
|
3955
|
+
optionalServiceExitWarned.add(runtimeServiceName2);
|
|
3702
3956
|
}
|
|
3703
|
-
services.splice(i, 1);
|
|
3704
3957
|
continue;
|
|
3705
3958
|
}
|
|
3706
|
-
const
|
|
3959
|
+
const runtimeServiceName = service.name;
|
|
3960
|
+
const serviceLogPath = service.logPath ?? serviceLogPaths[runtimeServiceName];
|
|
3961
|
+
const tail = readLogTail(serviceLogPath);
|
|
3707
3962
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}).`);
|
|
3708
3963
|
recordStartupPhase("readiness", readinessPhaseStartedAt, "failed");
|
|
3709
3964
|
emitStartupTimingSummary("failed", `${service.name} exited immediately after bootstrap`);
|
|
3710
|
-
stopRuntimeServices(
|
|
3711
|
-
throw new Error(`Embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}). ` + `See ${
|
|
3965
|
+
stopRuntimeServices(serviceManager.getServices());
|
|
3966
|
+
throw new Error(`Embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}). ` + `See ${serviceLogPath}${tail ? `
|
|
3712
3967
|
--- ${service.name} log tail ---
|
|
3713
3968
|
${tail}` : ""}`);
|
|
3714
3969
|
}
|
|
@@ -3719,13 +3974,13 @@ ${tail}` : ""}`);
|
|
|
3719
3974
|
emitStartupTimingSummary("ready");
|
|
3720
3975
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] embedded runtime is ready.");
|
|
3721
3976
|
return {
|
|
3722
|
-
|
|
3977
|
+
serviceManager,
|
|
3723
3978
|
pushpalsLogPath: runtimeServicesLogPath
|
|
3724
3979
|
};
|
|
3725
3980
|
}
|
|
3726
3981
|
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
3727
3982
|
}
|
|
3728
|
-
stopRuntimeServices(
|
|
3983
|
+
stopRuntimeServices(serviceManager.getServices());
|
|
3729
3984
|
const remoteBuddyHealth = await probeRemoteBuddySessionConsumer(opts.serverUrl, opts.sessionId);
|
|
3730
3985
|
if (!localBuddyEnabled && !remoteBuddyHealth.ok) {
|
|
3731
3986
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] timed out waiting for RemoteBuddy session consumer readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms (${remoteBuddyHealth.detail}).`);
|
|
@@ -4405,7 +4660,7 @@ async function main() {
|
|
|
4405
4660
|
platform: `${process.platform}/${process.arch}`,
|
|
4406
4661
|
repoRoot
|
|
4407
4662
|
};
|
|
4408
|
-
let
|
|
4663
|
+
let autoStartedServiceManager = null;
|
|
4409
4664
|
let pushpalsLogPath;
|
|
4410
4665
|
let resolvedRuntimeTagForAutoStart = preparedRuntime.runtimeTag || parsed.runtimeTag || "";
|
|
4411
4666
|
const cleanupWorkerpalWarmContainersIfNeeded = async (phase) => {
|
|
@@ -4464,17 +4719,24 @@ async function main() {
|
|
|
4464
4719
|
}
|
|
4465
4720
|
return readiness;
|
|
4466
4721
|
};
|
|
4722
|
+
const reportEmbeddedRuntimeHealth = () => {
|
|
4723
|
+
const health = autoStartedServiceManager?.getHealth() ?? null;
|
|
4724
|
+
for (const line of formatEmbeddedRuntimeHealthLines2(health)) {
|
|
4725
|
+
console.log(line);
|
|
4726
|
+
}
|
|
4727
|
+
return health;
|
|
4728
|
+
};
|
|
4467
4729
|
const stopAutoStartedServices = () => {
|
|
4468
|
-
if (
|
|
4730
|
+
if (!autoStartedServiceManager)
|
|
4469
4731
|
return;
|
|
4470
|
-
|
|
4471
|
-
|
|
4732
|
+
autoStartedServiceManager.stop();
|
|
4733
|
+
autoStartedServiceManager = null;
|
|
4472
4734
|
};
|
|
4473
4735
|
const stopAutoStartedServicesGracefully = async (reason) => {
|
|
4474
|
-
if (
|
|
4736
|
+
if (!autoStartedServiceManager)
|
|
4475
4737
|
return;
|
|
4476
|
-
const
|
|
4477
|
-
|
|
4738
|
+
const serviceManager = autoStartedServiceManager;
|
|
4739
|
+
autoStartedServiceManager = null;
|
|
4478
4740
|
const shutdown = await requestLocalRuntimeShutdown(serverUrl, repoRoot, reason);
|
|
4479
4741
|
if (shutdown.attempted && shutdown.accepted) {
|
|
4480
4742
|
console.log("[pushpals] Local runtime shutdown accepted; waiting for services to exit...");
|
|
@@ -4484,6 +4746,8 @@ async function main() {
|
|
|
4484
4746
|
} else if (shutdown.detail) {
|
|
4485
4747
|
console.warn(`[pushpals] ${shutdown.detail}`);
|
|
4486
4748
|
}
|
|
4749
|
+
serviceManager.stop();
|
|
4750
|
+
const services = serviceManager.getServices();
|
|
4487
4751
|
await stopRuntimeServicesGracefully(services);
|
|
4488
4752
|
await cleanupWorkerpalWarmContainersIfNeeded("cli shutdown");
|
|
4489
4753
|
await cleanupPushPalsGitWorktreesIfNeeded("cli shutdown");
|
|
@@ -4531,7 +4795,7 @@ async function main() {
|
|
|
4531
4795
|
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled)),
|
|
4532
4796
|
baseEnv: workerpalDockerPrecheck.env
|
|
4533
4797
|
});
|
|
4534
|
-
|
|
4798
|
+
autoStartedServiceManager = startedRuntime.serviceManager;
|
|
4535
4799
|
pushpalsLogPath = startedRuntime.pushpalsLogPath;
|
|
4536
4800
|
serverHealthy = await probeServer(serverUrl);
|
|
4537
4801
|
} catch (err) {
|
|
@@ -4655,6 +4919,7 @@ async function main() {
|
|
|
4655
4919
|
console.log(`[pushpals] pushpalsLog=${pushpalsLogPath ?? "unavailable"}`);
|
|
4656
4920
|
console.log(`[pushpals] cliStateFile=${statePath ?? "unavailable"}`);
|
|
4657
4921
|
reportWorkerExecutionReadinessFromSnapshot(startupWorkerExecutionReadiness);
|
|
4922
|
+
reportEmbeddedRuntimeHealth();
|
|
4658
4923
|
if (parsed.runtimeOnly) {
|
|
4659
4924
|
console.log("[pushpals] runtimeOnly=true");
|
|
4660
4925
|
} else {
|
|
@@ -4689,7 +4954,7 @@ ${line}
|
|
|
4689
4954
|
try {
|
|
4690
4955
|
monitoringHub?.stop();
|
|
4691
4956
|
} catch {}
|
|
4692
|
-
if (
|
|
4957
|
+
if (autoStartedServiceManager) {
|
|
4693
4958
|
console.log("[pushpals] Stopping embedded runtime services...");
|
|
4694
4959
|
}
|
|
4695
4960
|
await stopAutoStartedServicesGracefully("pushpals CLI exit");
|
|
@@ -4767,6 +5032,7 @@ ${line}
|
|
|
4767
5032
|
console.log(`[pushpals] pushpalsLog=${pushpalsLogPath ?? "unavailable"}`);
|
|
4768
5033
|
console.log(monitoringHubUrl ? `[pushpals] monitoringHubUrl=${monitoringHubUrl}` : "[pushpals] monitoringHubUrl=unavailable");
|
|
4769
5034
|
await reportWorkerExecutionReadiness();
|
|
5035
|
+
reportEmbeddedRuntimeHealth();
|
|
4770
5036
|
rl.prompt();
|
|
4771
5037
|
continue;
|
|
4772
5038
|
}
|
|
@@ -4806,6 +5072,7 @@ export {
|
|
|
4806
5072
|
waitForWorkerpalCapacity,
|
|
4807
5073
|
startEmbeddedMonitoringHub,
|
|
4808
5074
|
shouldRunEmbeddedRuntimeStartupPrechecks,
|
|
5075
|
+
shouldRestartEmbeddedService,
|
|
4809
5076
|
resolveWorkerExecutionReadiness,
|
|
4810
5077
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
4811
5078
|
resolveWindowsShellExecutableCandidatesForEnv,
|
|
@@ -4832,6 +5099,7 @@ export {
|
|
|
4832
5099
|
formatTimestampedCliLine,
|
|
4833
5100
|
formatSessionEventLine,
|
|
4834
5101
|
formatRuntimeStartupTimingSummary,
|
|
5102
|
+
formatEmbeddedRuntimeHealthLines2 as formatEmbeddedRuntimeHealthLines,
|
|
4835
5103
|
extractRemoteBuddySessionConsumerHealth,
|
|
4836
5104
|
extractRemoteBuddyAutonomousEngineState,
|
|
4837
5105
|
ensureWorkerpalDockerImageReady,
|
|
@@ -4840,6 +5108,7 @@ export {
|
|
|
4840
5108
|
describeWorkerExecutionReadiness,
|
|
4841
5109
|
createSessionEventReplayFilter,
|
|
4842
5110
|
copyTrackedRepoPath,
|
|
5111
|
+
computeEmbeddedServiceRestartBackoffMs,
|
|
4843
5112
|
cleanupLingeringWorkerpalWarmContainers,
|
|
4844
5113
|
cleanupLingeringPushPalsGitWorktrees,
|
|
4845
5114
|
bundledMonitoringHubNeedsRefresh,
|
package/package.json
CHANGED
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"test:prompt-policy": "bun test tests/prompt-policy.enforcement.test.ts",
|
|
46
46
|
"test:cli:integration": "bun test tests/cli.invocation-logging.test.ts tests/cli.runtime-bootstrap.test.ts tests/client.runtime-bootstrap.test.ts tests/shared.client-preflight.test.ts",
|
|
47
47
|
"test:cli:e2e": "bun test ./tests/integration/cli.e2e.ts",
|
|
48
|
+
"test:start:e2e": "bun test ./tests/integration/start.e2e.ts",
|
|
48
49
|
"test:root": "bun test tests",
|
|
49
50
|
"test:protocol": "bun run tests/protocol.integration.ts",
|
|
50
51
|
"test:integration": "python -u tests/integration/integration_controller.py --mode integration",
|