@pushpalsdev/cli 1.0.17 → 1.0.18
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 +270 -16
- package/package.json +1 -1
package/dist/pushpals-cli.js
CHANGED
|
@@ -25,6 +25,41 @@ import { relative, resolve as resolve2 } from "path";
|
|
|
25
25
|
import { existsSync, readFileSync } from "fs";
|
|
26
26
|
import { join, resolve, isAbsolute } from "path";
|
|
27
27
|
|
|
28
|
+
// ../shared/src/autonomy_policy.ts
|
|
29
|
+
var DRIVE_RE = /^[A-Za-z]:\//;
|
|
30
|
+
var SLASH_RE = /\/+/g;
|
|
31
|
+
function normalizeAutonomyComponentArea(value) {
|
|
32
|
+
const normalized = normalizeRepoRelativePath(value);
|
|
33
|
+
if (!normalized)
|
|
34
|
+
return null;
|
|
35
|
+
return normalized;
|
|
36
|
+
}
|
|
37
|
+
function normalizeRepoRelativePath(value) {
|
|
38
|
+
if (typeof value !== "string")
|
|
39
|
+
return null;
|
|
40
|
+
let path = value.trim();
|
|
41
|
+
if (!path)
|
|
42
|
+
return null;
|
|
43
|
+
path = path.normalize("NFC").replace(/\\/g, "/");
|
|
44
|
+
if (path.startsWith("/"))
|
|
45
|
+
return null;
|
|
46
|
+
if (DRIVE_RE.test(path))
|
|
47
|
+
return null;
|
|
48
|
+
path = path.replace(SLASH_RE, "/");
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const rawSegment of path.split("/")) {
|
|
51
|
+
const segment = rawSegment.trim();
|
|
52
|
+
if (!segment || segment === ".")
|
|
53
|
+
continue;
|
|
54
|
+
if (segment === "..")
|
|
55
|
+
return null;
|
|
56
|
+
out.push(segment);
|
|
57
|
+
}
|
|
58
|
+
if (out.length === 0)
|
|
59
|
+
return null;
|
|
60
|
+
return out.join("/");
|
|
61
|
+
}
|
|
62
|
+
|
|
28
63
|
// ../shared/src/local_network.ts
|
|
29
64
|
var DEFAULT_LOCAL_LOOPBACK_HOST = "127.0.0.1";
|
|
30
65
|
function isLoopbackHost(hostname) {
|
|
@@ -398,14 +433,26 @@ function loadPushPalsConfig(options = {}) {
|
|
|
398
433
|
"tests/unit": 2
|
|
399
434
|
};
|
|
400
435
|
const remoteAutonomyDispatchByComponentRaw = asStringNumberRecord(remoteAutonomyNode.max_dispatch_per_hour_by_component);
|
|
401
|
-
const
|
|
402
|
-
|
|
436
|
+
const legacyAutonomyComponentAliasMap = new Map(Object.keys(remoteAutonomyDispatchByComponentCfg).flatMap((key) => {
|
|
437
|
+
const direct = normalizeAutonomyComponentArea(key);
|
|
438
|
+
const legacyUnderscore = normalizeAutonomyComponentArea(key.replace(/\//g, "_"));
|
|
439
|
+
const legacyHyphen = normalizeAutonomyComponentArea(key.replace(/\//g, "-"));
|
|
440
|
+
return [direct, legacyUnderscore, legacyHyphen].filter((value) => Boolean(value)).map((value) => [value, key]);
|
|
441
|
+
}));
|
|
442
|
+
const coerceAutonomyComponentConfigKey = (value) => {
|
|
443
|
+
const direct = normalizeAutonomyComponentArea(value);
|
|
444
|
+
const legacyAliasCandidate = normalizeAutonomyComponentArea(value.trim().toLowerCase().replace(/\\/g, "/").replace(/_+/g, "/").replace(/-+/g, "/").replace(/\/+/g, "/"));
|
|
445
|
+
if (legacyAliasCandidate && legacyAutonomyComponentAliasMap.has(legacyAliasCandidate)) {
|
|
446
|
+
return legacyAutonomyComponentAliasMap.get(legacyAliasCandidate) ?? legacyAliasCandidate;
|
|
447
|
+
}
|
|
448
|
+
return direct;
|
|
403
449
|
};
|
|
404
|
-
const
|
|
405
|
-
|
|
450
|
+
const remoteAutonomyDispatchByComponent = Object.fromEntries(Object.entries(remoteAutonomyDispatchByComponentCfg).map(([key, value]) => [
|
|
451
|
+
coerceAutonomyComponentConfigKey(key) ?? key,
|
|
452
|
+
value
|
|
453
|
+
]));
|
|
406
454
|
for (const [rawKey, rawValue] of Object.entries(remoteAutonomyDispatchByComponentRaw)) {
|
|
407
|
-
const
|
|
408
|
-
const canonical = canonicalComponentByNormalized.get(normalized);
|
|
455
|
+
const canonical = coerceAutonomyComponentConfigKey(rawKey);
|
|
409
456
|
if (!canonical)
|
|
410
457
|
continue;
|
|
411
458
|
const parsed = typeof rawValue === "number" ? rawValue : typeof rawValue === "string" ? Number.parseInt(rawValue.trim(), 10) : Number.NaN;
|
|
@@ -1369,9 +1416,9 @@ function parsePositiveInt(value, fallback) {
|
|
|
1369
1416
|
function jsonHtmlBootstrap(value) {
|
|
1370
1417
|
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1371
1418
|
}
|
|
1372
|
-
async function
|
|
1419
|
+
async function runCommandWithEnv(command, cwd, env) {
|
|
1373
1420
|
try {
|
|
1374
|
-
const proc = Bun.spawn(
|
|
1421
|
+
const proc = Bun.spawn(command, {
|
|
1375
1422
|
cwd,
|
|
1376
1423
|
env,
|
|
1377
1424
|
stdout: "pipe",
|
|
@@ -1392,6 +1439,9 @@ async function runGitWithEnv(args, cwd, env) {
|
|
|
1392
1439
|
};
|
|
1393
1440
|
}
|
|
1394
1441
|
}
|
|
1442
|
+
async function runGitWithEnv(args, cwd, env) {
|
|
1443
|
+
return await runCommandWithEnv(["git", ...args], cwd, env);
|
|
1444
|
+
}
|
|
1395
1445
|
async function runGit(args, cwd) {
|
|
1396
1446
|
return await runGitWithEnv(args, cwd, {
|
|
1397
1447
|
...process.env,
|
|
@@ -1744,7 +1794,9 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
|
|
|
1744
1794
|
PUSHPALS_PROTOCOL_SCHEMAS_DIR: join2(opts.runtimeRoot, "protocol", "schemas"),
|
|
1745
1795
|
...typeof opts.sessionId === "string" && opts.sessionId.trim() ? { PUSHPALS_SESSION_ID: opts.sessionId.trim() } : {},
|
|
1746
1796
|
...typeof env.PUSHPALS_GIT_BIN === "string" && env.PUSHPALS_GIT_BIN.trim() ? { PUSHPALS_GIT_BIN: env.PUSHPALS_GIT_BIN.trim() } : {},
|
|
1747
|
-
...typeof env.PUSHPALS_GIT_BIN_ABSOLUTE === "string" && env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() ? { PUSHPALS_GIT_BIN_ABSOLUTE: env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() } : {}
|
|
1797
|
+
...typeof env.PUSHPALS_GIT_BIN_ABSOLUTE === "string" && env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() ? { PUSHPALS_GIT_BIN_ABSOLUTE: env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() } : {},
|
|
1798
|
+
...typeof env.PUSHPALS_DOCKER_BIN === "string" && env.PUSHPALS_DOCKER_BIN.trim() ? { PUSHPALS_DOCKER_BIN: env.PUSHPALS_DOCKER_BIN.trim() } : {},
|
|
1799
|
+
...typeof env.PUSHPALS_DOCKER_BIN_ABSOLUTE === "string" && env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() ? { PUSHPALS_DOCKER_BIN_ABSOLUTE: env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() } : {}
|
|
1748
1800
|
};
|
|
1749
1801
|
}
|
|
1750
1802
|
function normalizeChildProcessEnv(baseEnv, platform = process.platform) {
|
|
@@ -2020,6 +2072,19 @@ function applyResolvedGitBinaryToRuntimeEnv(env, resolvedGitBinary, platform = p
|
|
|
2020
2072
|
}
|
|
2021
2073
|
return env;
|
|
2022
2074
|
}
|
|
2075
|
+
function applyResolvedDockerBinaryToRuntimeEnv(env, resolvedDockerBinary, platform = process.platform) {
|
|
2076
|
+
const resolvedPath = String(resolvedDockerBinary ?? "").trim();
|
|
2077
|
+
if (!resolvedPath)
|
|
2078
|
+
return env;
|
|
2079
|
+
prependExecutableDirToPath(env, resolvedPath, platform);
|
|
2080
|
+
env.PUSHPALS_DOCKER_BIN = basename(resolvedPath);
|
|
2081
|
+
if (resolvedPath.includes("/") || resolvedPath.includes("\\")) {
|
|
2082
|
+
env.PUSHPALS_DOCKER_BIN_ABSOLUTE = resolvedPath;
|
|
2083
|
+
} else {
|
|
2084
|
+
delete env.PUSHPALS_DOCKER_BIN_ABSOLUTE;
|
|
2085
|
+
}
|
|
2086
|
+
return env;
|
|
2087
|
+
}
|
|
2023
2088
|
function resolveRuntimeGitExecutableCandidates(env, platform = process.platform) {
|
|
2024
2089
|
const candidates = [];
|
|
2025
2090
|
const seen = new Set;
|
|
@@ -2039,6 +2104,25 @@ function resolveRuntimeGitExecutableCandidates(env, platform = process.platform)
|
|
|
2039
2104
|
pushCandidate("git");
|
|
2040
2105
|
return candidates;
|
|
2041
2106
|
}
|
|
2107
|
+
function resolveRuntimeDockerExecutableCandidates(env, platform = process.platform) {
|
|
2108
|
+
const candidates = [];
|
|
2109
|
+
const seen = new Set;
|
|
2110
|
+
const pushCandidate = (value) => {
|
|
2111
|
+
const trimmed = String(value ?? "").trim();
|
|
2112
|
+
if (!trimmed)
|
|
2113
|
+
return;
|
|
2114
|
+
const key = platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
2115
|
+
if (seen.has(key))
|
|
2116
|
+
return;
|
|
2117
|
+
seen.add(key);
|
|
2118
|
+
candidates.push(trimmed);
|
|
2119
|
+
};
|
|
2120
|
+
pushCandidate(env.PUSHPALS_DOCKER_BIN ?? "");
|
|
2121
|
+
pushCandidate(env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? "");
|
|
2122
|
+
pushCandidate(platform === "win32" ? "docker.exe" : "docker");
|
|
2123
|
+
pushCandidate("docker");
|
|
2124
|
+
return candidates;
|
|
2125
|
+
}
|
|
2042
2126
|
function resolveWindowsShellExecutableCandidatesForEnv(env, platform = process.platform) {
|
|
2043
2127
|
if (platform !== "win32")
|
|
2044
2128
|
return [];
|
|
@@ -2153,6 +2237,32 @@ async function resolveSourceControlManagerGitProbe(cwd, env, platform = process.
|
|
|
2153
2237
|
detail: candidates.join(", ") || "git"
|
|
2154
2238
|
};
|
|
2155
2239
|
}
|
|
2240
|
+
async function resolveWorkerpalDockerProbe(cwd, env, platform = process.platform) {
|
|
2241
|
+
const resolvedDockerBinary = await resolveCommandPath(platform === "win32" ? "docker.exe" : "docker", cwd, env);
|
|
2242
|
+
if (resolvedDockerBinary) {
|
|
2243
|
+
prependExecutableDirToPath(env, resolvedDockerBinary, platform);
|
|
2244
|
+
env.PUSHPALS_DOCKER_BIN = basename(resolvedDockerBinary);
|
|
2245
|
+
env.PUSHPALS_DOCKER_BIN_ABSOLUTE = resolvedDockerBinary;
|
|
2246
|
+
}
|
|
2247
|
+
const candidates = resolveRuntimeDockerExecutableCandidates(env, platform);
|
|
2248
|
+
const failures = [];
|
|
2249
|
+
for (const candidate of candidates) {
|
|
2250
|
+
const result = await runCommandWithEnv([candidate, "version", "--format", "{{.Server.Version}}"], cwd, env);
|
|
2251
|
+
if (result.ok) {
|
|
2252
|
+
const version = result.stdout.trim();
|
|
2253
|
+
return {
|
|
2254
|
+
ok: true,
|
|
2255
|
+
detail: version ? `${candidate} (${version})` : candidate
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
const detail = result.stderr || result.stdout || `exit ${result.exitCode}`;
|
|
2259
|
+
failures.push(`${candidate}: ${detail}`);
|
|
2260
|
+
}
|
|
2261
|
+
return {
|
|
2262
|
+
ok: false,
|
|
2263
|
+
detail: failures.join(" | ") || "docker"
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2156
2266
|
async function precheckSourceControlManagerGitAvailability(opts) {
|
|
2157
2267
|
const platform = opts.platform ?? process.platform;
|
|
2158
2268
|
const env = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
|
|
@@ -2161,8 +2271,9 @@ async function precheckSourceControlManagerGitAvailability(opts) {
|
|
|
2161
2271
|
useRuntimeConfig: opts.preflightUsesEmbeddedRuntime,
|
|
2162
2272
|
sessionId: opts.sessionId
|
|
2163
2273
|
});
|
|
2164
|
-
|
|
2165
|
-
|
|
2274
|
+
const preconfiguredGitBinary = env.PUSHPALS_GIT_BIN_ABSOLUTE ?? env.PUSHPALS_GIT_BIN;
|
|
2275
|
+
if (preconfiguredGitBinary) {
|
|
2276
|
+
applyResolvedGitBinaryToRuntimeEnv(env, preconfiguredGitBinary, platform);
|
|
2166
2277
|
}
|
|
2167
2278
|
const remoteStatus = opts.gitRemoteCheckFn ? await opts.gitRemoteCheckFn(opts.repoRoot, opts.remote, env) : opts.repoHasRemoteFn ? await opts.repoHasRemoteFn(opts.repoRoot, opts.remote) ? { status: "ok", remote: opts.remote } : { status: "missing_remote", remote: opts.remote } : await checkGitRemoteConfigured(opts.repoRoot, opts.remote, env);
|
|
2168
2279
|
if (remoteStatus.status === "missing_remote") {
|
|
@@ -2198,6 +2309,55 @@ async function precheckSourceControlManagerGitAvailability(opts) {
|
|
|
2198
2309
|
env
|
|
2199
2310
|
};
|
|
2200
2311
|
}
|
|
2312
|
+
async function precheckWorkerpalDockerAvailability(opts) {
|
|
2313
|
+
const env = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
|
|
2314
|
+
repoRoot: opts.repoRoot,
|
|
2315
|
+
runtimeRoot: opts.runtimeRoot,
|
|
2316
|
+
useRuntimeConfig: opts.preflightUsesEmbeddedRuntime,
|
|
2317
|
+
sessionId: opts.sessionId
|
|
2318
|
+
});
|
|
2319
|
+
const preconfiguredDockerBinary = env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? env.PUSHPALS_DOCKER_BIN;
|
|
2320
|
+
if (preconfiguredDockerBinary) {
|
|
2321
|
+
applyResolvedDockerBinaryToRuntimeEnv(env, preconfiguredDockerBinary, opts.platform ?? process.platform);
|
|
2322
|
+
}
|
|
2323
|
+
if (!opts.autoSpawnWorkerpals) {
|
|
2324
|
+
return {
|
|
2325
|
+
status: "skipped",
|
|
2326
|
+
detail: "WorkerPal auto-spawn is disabled",
|
|
2327
|
+
env
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
if (!opts.dockerEnabled) {
|
|
2331
|
+
return {
|
|
2332
|
+
status: "skipped",
|
|
2333
|
+
detail: "WorkerPal docker mode is disabled",
|
|
2334
|
+
env
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
if (!opts.requireDocker) {
|
|
2338
|
+
return {
|
|
2339
|
+
status: "skipped",
|
|
2340
|
+
detail: "WorkerPal docker mode is optional",
|
|
2341
|
+
env
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
const dockerProbe = await (opts.dockerProbeFn ?? resolveWorkerpalDockerProbe)(opts.repoRoot, env, opts.platform ?? process.platform);
|
|
2345
|
+
if (!dockerProbe.ok) {
|
|
2346
|
+
return {
|
|
2347
|
+
status: "failed",
|
|
2348
|
+
detail: dockerProbe.detail,
|
|
2349
|
+
env
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
return {
|
|
2353
|
+
status: "ok",
|
|
2354
|
+
detail: dockerProbe.detail,
|
|
2355
|
+
env
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
function resolveWorkerpalCapacityTimeoutMs(config) {
|
|
2359
|
+
return Math.max(config.remotebuddy.waitForWorkerpalMs, config.remotebuddy.workerpalStartupTimeoutMs, config.remotebuddy.workerpalDocker ? config.workerpals.dockerAgentStartupTimeoutMs + 15000 : 0, 1e4);
|
|
2360
|
+
}
|
|
2201
2361
|
async function checkGitRemoteConfigured(repoRoot, remote, env) {
|
|
2202
2362
|
const normalizedRemote = String(remote ?? "").trim();
|
|
2203
2363
|
if (!normalizedRemote) {
|
|
@@ -2539,6 +2699,42 @@ async function probeSourceControlManager(port) {
|
|
|
2539
2699
|
return false;
|
|
2540
2700
|
}
|
|
2541
2701
|
}
|
|
2702
|
+
async function fetchWorkerStatusRows(serverUrl, ttlMs) {
|
|
2703
|
+
const payload = await fetchJsonWithTimeout(`${serverUrl}/workers?ttlMs=${Math.max(1000, Math.floor(ttlMs))}`, {}, 1e4);
|
|
2704
|
+
if (!payload?.ok || !Array.isArray(payload.workers)) {
|
|
2705
|
+
return [];
|
|
2706
|
+
}
|
|
2707
|
+
return payload.workers;
|
|
2708
|
+
}
|
|
2709
|
+
async function waitForWorkerpalCapacity(opts) {
|
|
2710
|
+
const deadline = Date.now() + Math.max(1000, opts.timeoutMs);
|
|
2711
|
+
let lastObservedOnline = 0;
|
|
2712
|
+
while (Date.now() < deadline) {
|
|
2713
|
+
const workers = await (opts.fetchWorkersFn ?? fetchWorkerStatusRows)(opts.serverUrl, opts.ttlMs);
|
|
2714
|
+
const onlineWorkers = workers.filter((worker) => Boolean(worker?.isOnline) && String(worker?.status ?? "").trim().toLowerCase() !== "offline");
|
|
2715
|
+
const idleWorkers = onlineWorkers.filter((worker) => Number(worker?.activeJobCount ?? 0) <= 0);
|
|
2716
|
+
if (onlineWorkers.length > 0) {
|
|
2717
|
+
lastObservedOnline = Math.max(lastObservedOnline, onlineWorkers.length);
|
|
2718
|
+
}
|
|
2719
|
+
if (idleWorkers.length > 0) {
|
|
2720
|
+
return {
|
|
2721
|
+
ok: true,
|
|
2722
|
+
detail: `${idleWorkers.length} idle / ${onlineWorkers.length} online`
|
|
2723
|
+
};
|
|
2724
|
+
}
|
|
2725
|
+
await (opts.sleepFn ?? Bun.sleep)(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
2726
|
+
}
|
|
2727
|
+
if (lastObservedOnline > 0) {
|
|
2728
|
+
return {
|
|
2729
|
+
ok: false,
|
|
2730
|
+
detail: `${lastObservedOnline} online WorkerPal(s) reported but none became idle within ${Math.max(1000, opts.timeoutMs)}ms`
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
return {
|
|
2734
|
+
ok: false,
|
|
2735
|
+
detail: `no online WorkerPal reported within ${Math.max(1000, opts.timeoutMs)}ms`
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2542
2738
|
async function fetchWithTimeout(url, init = {}, timeoutMs = HTTP_TIMEOUT_MS) {
|
|
2543
2739
|
const controller = new AbortController;
|
|
2544
2740
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -2624,15 +2820,20 @@ async function autoStartRuntimeServices(opts) {
|
|
|
2624
2820
|
}
|
|
2625
2821
|
await ensureRuntimeAssets(runtimeRoot, runtimeTag);
|
|
2626
2822
|
const runtimeBinaries = await ensureRuntimeBinaries(runtimeRoot, runtimeTag);
|
|
2627
|
-
const runtimeEnv = buildEmbeddedRuntimeEnv(process.env, {
|
|
2823
|
+
const runtimeEnv = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
|
|
2628
2824
|
repoRoot: opts.repoRoot,
|
|
2629
2825
|
runtimeRoot,
|
|
2630
2826
|
useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
2631
2827
|
sessionId: opts.sessionId
|
|
2632
2828
|
});
|
|
2633
2829
|
runtimeEnv.PUSHPALS_WORKERPALS_BIN = runtimeBinaries.workerpals;
|
|
2634
|
-
|
|
2635
|
-
|
|
2830
|
+
const preconfiguredRuntimeGitBinary = runtimeEnv.PUSHPALS_GIT_BIN_ABSOLUTE ?? runtimeEnv.PUSHPALS_GIT_BIN;
|
|
2831
|
+
if (preconfiguredRuntimeGitBinary) {
|
|
2832
|
+
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, preconfiguredRuntimeGitBinary);
|
|
2833
|
+
}
|
|
2834
|
+
const preconfiguredRuntimeDockerBinary = runtimeEnv.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? runtimeEnv.PUSHPALS_DOCKER_BIN;
|
|
2835
|
+
if (preconfiguredRuntimeDockerBinary) {
|
|
2836
|
+
applyResolvedDockerBinaryToRuntimeEnv(runtimeEnv, preconfiguredRuntimeDockerBinary);
|
|
2636
2837
|
}
|
|
2637
2838
|
const gitLookupCommand = typeof runtimeEnv.PUSHPALS_GIT_BIN === "string" && runtimeEnv.PUSHPALS_GIT_BIN.trim() ? runtimeEnv.PUSHPALS_GIT_BIN.trim() : "git";
|
|
2638
2839
|
const resolvedGitBinary = await resolveCommandPath(gitLookupCommand, opts.repoRoot, runtimeEnv);
|
|
@@ -2720,6 +2921,24 @@ ${tail}` : ""}`);
|
|
|
2720
2921
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] embedded remotebuddy autonomous engine is disabled (remotebuddy.autonomy.enabled=false).");
|
|
2721
2922
|
};
|
|
2722
2923
|
reportRemoteBuddyAutonomousEngineState();
|
|
2924
|
+
if (runtimePreflight.config.remotebuddy.autoSpawnWorkerpals) {
|
|
2925
|
+
const workerpalReadyTimeoutMs = resolveWorkerpalCapacityTimeoutMs(runtimePreflight.config);
|
|
2926
|
+
const workerpalCapacity = await waitForWorkerpalCapacity({
|
|
2927
|
+
serverUrl: opts.serverUrl,
|
|
2928
|
+
timeoutMs: workerpalReadyTimeoutMs,
|
|
2929
|
+
ttlMs: runtimePreflight.config.remotebuddy.workerpalOnlineTtlMs
|
|
2930
|
+
});
|
|
2931
|
+
if (!workerpalCapacity.ok) {
|
|
2932
|
+
const tail = readLogTail(remotebuddyService.logPath);
|
|
2933
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded workerpal capacity did not become available within ${workerpalReadyTimeoutMs}ms.`);
|
|
2934
|
+
stopRuntimeServices(services);
|
|
2935
|
+
throw new Error(`Embedded WorkerPal capacity did not become available within ${workerpalReadyTimeoutMs}ms (${workerpalCapacity.detail}). ` + `See ${remotebuddyService.logPath}${tail ? `
|
|
2936
|
+
--- remotebuddy log tail ---
|
|
2937
|
+
${tail}` : ""}`);
|
|
2938
|
+
}
|
|
2939
|
+
console.log(`[pushpals] Embedded WorkerPal capacity is ready (${workerpalCapacity.detail}).`);
|
|
2940
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded workerpal capacity ready (${workerpalCapacity.detail}).`);
|
|
2941
|
+
}
|
|
2723
2942
|
const scmHealthy = await probeSourceControlManager(opts.sourceControlManagerPort);
|
|
2724
2943
|
const scmGitProbe = await resolveSourceControlManagerGitProbe(opts.repoRoot, runtimeEnv, process.platform);
|
|
2725
2944
|
const scmRemoteStatus = await checkGitRemoteConfigured(opts.repoRoot, opts.sourceControlManagerRemote, runtimeEnv);
|
|
@@ -3396,6 +3615,15 @@ async function main() {
|
|
|
3396
3615
|
console.error(`[pushpals] Precheck failed: embedded SourceControlManager git command is unavailable (${scmGitPrecheck.detail}).`);
|
|
3397
3616
|
process.exit(1);
|
|
3398
3617
|
}
|
|
3618
|
+
const workerpalDockerPrecheck = await precheckWorkerpalDockerAvailability({
|
|
3619
|
+
repoRoot,
|
|
3620
|
+
runtimeRoot: preparedRuntime.runtimeRoot,
|
|
3621
|
+
preflightUsesEmbeddedRuntime: preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
3622
|
+
autoSpawnWorkerpals: Boolean(config.remotebuddy.autoSpawnWorkerpals),
|
|
3623
|
+
dockerEnabled: Boolean(config.remotebuddy.workerpalDocker),
|
|
3624
|
+
requireDocker: Boolean(config.remotebuddy.workerpalRequireDocker),
|
|
3625
|
+
baseEnv: scmGitPrecheck.env
|
|
3626
|
+
});
|
|
3399
3627
|
const precheckPassed = await enforcePushpalsRemoteBranchPrecheck(repoRoot, config.sourceControlManager.remote, config.sourceControlManager.mainBranch);
|
|
3400
3628
|
if (!precheckPassed) {
|
|
3401
3629
|
process.exit(1);
|
|
@@ -3428,6 +3656,11 @@ async function main() {
|
|
|
3428
3656
|
};
|
|
3429
3657
|
if (!serverHealthy) {
|
|
3430
3658
|
if (!parsed.noAutoStart) {
|
|
3659
|
+
if (workerpalDockerPrecheck.status === "failed") {
|
|
3660
|
+
console.error(`[pushpals] Precheck failed: Docker-backed WorkerPal auto-spawn is required but Docker is unavailable (${workerpalDockerPrecheck.detail}).`);
|
|
3661
|
+
console.error("[pushpals] Precheck failed: start Docker Desktop or the Docker daemon, then retry pushpals.");
|
|
3662
|
+
process.exit(1);
|
|
3663
|
+
}
|
|
3431
3664
|
try {
|
|
3432
3665
|
const startedRuntime = await autoStartRuntimeServices({
|
|
3433
3666
|
repoRoot,
|
|
@@ -3438,7 +3671,8 @@ async function main() {
|
|
|
3438
3671
|
sourceControlManagerRemote: config.sourceControlManager.remote,
|
|
3439
3672
|
preparedRuntime,
|
|
3440
3673
|
requestedRuntimeTag: parsed.runtimeTag,
|
|
3441
|
-
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled))
|
|
3674
|
+
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled)),
|
|
3675
|
+
baseEnv: workerpalDockerPrecheck.env
|
|
3442
3676
|
});
|
|
3443
3677
|
autoStartedServices = startedRuntime.services;
|
|
3444
3678
|
pushpalsLogPath = startedRuntime.pushpalsLogPath;
|
|
@@ -3493,6 +3727,22 @@ async function main() {
|
|
|
3493
3727
|
}
|
|
3494
3728
|
process.exit(1);
|
|
3495
3729
|
}
|
|
3730
|
+
const workerpalCapacity = await waitForWorkerpalCapacity({
|
|
3731
|
+
serverUrl,
|
|
3732
|
+
timeoutMs: resolveWorkerpalCapacityTimeoutMs(config),
|
|
3733
|
+
ttlMs: config.remotebuddy.workerpalOnlineTtlMs
|
|
3734
|
+
});
|
|
3735
|
+
if (!workerpalCapacity.ok) {
|
|
3736
|
+
stopAutoStartedServices();
|
|
3737
|
+
console.error(`[pushpals] WorkerPal capacity is not ready for repo ${repoRoot}: ${workerpalCapacity.detail}.`);
|
|
3738
|
+
if (workerpalDockerPrecheck.status === "failed") {
|
|
3739
|
+
console.error(`[pushpals] Docker precheck detail: ${workerpalDockerPrecheck.detail}`);
|
|
3740
|
+
} else if (serverWasAlreadyHealthy) {
|
|
3741
|
+
console.error("[pushpals] A PushPals runtime is already serving this repo, but it does not currently have an idle WorkerPal available.");
|
|
3742
|
+
console.error("[pushpals] Wait for a worker to become idle or restart the runtime after fixing WorkerPal startup.");
|
|
3743
|
+
}
|
|
3744
|
+
process.exit(1);
|
|
3745
|
+
}
|
|
3496
3746
|
const saved = statePath ? readCliState(statePath) : {};
|
|
3497
3747
|
pushpalsLogPath = pushpalsLogPath || (typeof saved.pushpalsLogPath === "string" ? saved.pushpalsLogPath : undefined);
|
|
3498
3748
|
const preferredHubUrl = normalizeUrl(parsed.monitoringHubUrl ?? process.env.PUSHPALS_MONITOR_URL ?? saved.monitoringHubUrl ?? "");
|
|
@@ -3667,10 +3917,12 @@ if (import.meta.main) {
|
|
|
3667
3917
|
});
|
|
3668
3918
|
}
|
|
3669
3919
|
export {
|
|
3920
|
+
waitForWorkerpalCapacity,
|
|
3670
3921
|
startEmbeddedMonitoringHub,
|
|
3671
3922
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
3672
3923
|
resolveWindowsShellExecutableCandidatesForEnv,
|
|
3673
3924
|
resolveRuntimeGitExecutableCandidates,
|
|
3925
|
+
resolveRuntimeDockerExecutableCandidates,
|
|
3674
3926
|
resolvePreferredRuntimeReleaseTag,
|
|
3675
3927
|
resolveCommandPath,
|
|
3676
3928
|
resolveCliStatePath,
|
|
@@ -3678,6 +3930,7 @@ export {
|
|
|
3678
3930
|
resolveBundledRuntimeAssetSource,
|
|
3679
3931
|
resolveBundledMonitoringHubRoot,
|
|
3680
3932
|
prepareCliRuntime,
|
|
3933
|
+
precheckWorkerpalDockerAvailability,
|
|
3681
3934
|
precheckSourceControlManagerGitAvailability,
|
|
3682
3935
|
normalizeRepoPathForComparison,
|
|
3683
3936
|
normalizeCliInteractiveMessage,
|
|
@@ -3695,5 +3948,6 @@ export {
|
|
|
3695
3948
|
buildEmbeddedRuntimeEnv,
|
|
3696
3949
|
buildEmbeddedMonitoringHubHtml,
|
|
3697
3950
|
buildCliClearTargets,
|
|
3698
|
-
applyResolvedGitBinaryToRuntimeEnv
|
|
3951
|
+
applyResolvedGitBinaryToRuntimeEnv,
|
|
3952
|
+
applyResolvedDockerBinaryToRuntimeEnv
|
|
3699
3953
|
};
|