@pushpalsdev/cli 1.0.17 → 1.0.19
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 +542 -23
- package/package.json +1 -1
- package/runtime/sandbox/apps/workerpals/.python-version +1 -0
- package/runtime/sandbox/apps/workerpals/Dockerfile.sandbox +71 -0
- package/runtime/sandbox/apps/workerpals/package.json +25 -0
- package/runtime/sandbox/apps/workerpals/pyproject.toml +8 -0
- package/runtime/sandbox/apps/workerpals/src/backends/backend_config.ts +111 -0
- package/runtime/sandbox/apps/workerpals/src/backends/miniswe/miniswe_executor.py +2029 -0
- package/runtime/sandbox/apps/workerpals/src/backends/miniswe_backend.ts +48 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/openai_codex_executor.py +1259 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/test_openai_codex_runtime_config.py +110 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex_backend.ts +67 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openhands/openhands_executor.py +563 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openhands_backend.ts +161 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openhands_task_execute.ts +536 -0
- package/runtime/sandbox/apps/workerpals/src/backends/shared/executor_base.py +746 -0
- package/runtime/sandbox/apps/workerpals/src/backends/shared/test_settings_resolver.py +60 -0
- package/runtime/sandbox/apps/workerpals/src/backends/task_execute_registry.ts +21 -0
- package/runtime/sandbox/apps/workerpals/src/backends/types.ts +52 -0
- package/runtime/sandbox/apps/workerpals/src/common/execution_utils.ts +149 -0
- package/runtime/sandbox/apps/workerpals/src/common/executor_backend.ts +15 -0
- package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +210 -0
- package/runtime/sandbox/apps/workerpals/src/common/logger.ts +65 -0
- package/runtime/sandbox/apps/workerpals/src/common/types.ts +9 -0
- package/runtime/sandbox/apps/workerpals/src/common/worktree_cleanup.ts +66 -0
- package/runtime/sandbox/apps/workerpals/src/context_manager.ts +45 -0
- package/runtime/sandbox/apps/workerpals/src/docker_executor.ts +1842 -0
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +3063 -0
- package/runtime/sandbox/apps/workerpals/src/job_runner.ts +194 -0
- package/runtime/sandbox/apps/workerpals/src/shell_manager.ts +210 -0
- package/runtime/sandbox/apps/workerpals/src/timeout_policy.ts +24 -0
- package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +1436 -0
- package/runtime/sandbox/apps/workerpals/tsconfig.json +15 -0
- package/runtime/sandbox/apps/workerpals/uv.lock +2014 -0
- package/runtime/sandbox/bun.lock +2591 -0
- package/runtime/sandbox/configs/backend.toml +79 -0
- package/runtime/sandbox/configs/default.toml +260 -0
- package/runtime/sandbox/configs/dev.toml +2 -0
- package/runtime/sandbox/configs/local.example.toml +129 -0
- package/runtime/sandbox/package.json +65 -0
- package/runtime/sandbox/packages/protocol/README.md +168 -0
- package/runtime/sandbox/packages/protocol/package.json +37 -0
- package/runtime/sandbox/packages/protocol/scripts/copy-schemas.js +17 -0
- package/runtime/sandbox/packages/protocol/src/a2a/README.md +52 -0
- package/runtime/sandbox/packages/protocol/src/a2a/mapping.ts +55 -0
- package/runtime/sandbox/packages/protocol/src/index.browser.ts +25 -0
- package/runtime/sandbox/packages/protocol/src/index.ts +25 -0
- package/runtime/sandbox/packages/protocol/src/schemas/approvals.schema.json +6 -0
- package/runtime/sandbox/packages/protocol/src/schemas/envelope.schema.json +96 -0
- package/runtime/sandbox/packages/protocol/src/schemas/events.schema.json +679 -0
- package/runtime/sandbox/packages/protocol/src/schemas/http.schema.json +50 -0
- package/runtime/sandbox/packages/protocol/src/types.ts +267 -0
- package/runtime/sandbox/packages/protocol/src/validate.browser.ts +154 -0
- package/runtime/sandbox/packages/protocol/src/validate.ts +233 -0
- package/runtime/sandbox/packages/protocol/src/version.ts +1 -0
- package/runtime/sandbox/packages/protocol/tsconfig.json +20 -0
- package/runtime/sandbox/packages/shared/package.json +19 -0
- package/runtime/sandbox/packages/shared/src/autonomy_policy.ts +400 -0
- package/runtime/sandbox/packages/shared/src/client_preflight.ts +297 -0
- package/runtime/sandbox/packages/shared/src/communication.ts +313 -0
- package/runtime/sandbox/packages/shared/src/config.ts +2201 -0
- package/runtime/sandbox/packages/shared/src/config_template_parity.ts +70 -0
- package/runtime/sandbox/packages/shared/src/git_backend.ts +205 -0
- package/runtime/sandbox/packages/shared/src/index.ts +100 -0
- package/runtime/sandbox/packages/shared/src/local_network.ts +101 -0
- package/runtime/sandbox/packages/shared/src/localbuddy_runtime.ts +329 -0
- package/runtime/sandbox/packages/shared/src/prompts.ts +64 -0
- package/runtime/sandbox/packages/shared/src/repo.ts +134 -0
- package/runtime/sandbox/packages/shared/src/session_event_visibility.ts +25 -0
- package/runtime/sandbox/packages/shared/src/vision.ts +247 -0
- package/runtime/sandbox/packages/shared/tsconfig.json +16 -0
- package/runtime/sandbox/prompts/workerpals/codex_quality_critic_instruction_prompt.md +14 -0
- package/runtime/sandbox/prompts/workerpals/commit_message_prompt.md +36 -0
- package/runtime/sandbox/prompts/workerpals/commit_message_user_prompt.md +7 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_broker_system_prompt.md +33 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_broker_task_prompt.md +5 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_completion_requirement.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_context_compaction_retry_prompt.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_explicit_targets_block.md +2 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_recovery_guidance_base.md +4 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_recovery_guidance_blocker_line.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_strict_tool_use_guidance.md +6 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_supplemental_guidance_section.md +2 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_timeout_note.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_toolcall_retry_guidance.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_default_system_prompt.md +4 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_instruction_wrapper.md +5 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_runtime_policy_appendix.md +5 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_supplemental_guidance_section.md +2 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_task_execute_system_prompt.md +12 -0
- package/runtime/sandbox/prompts/workerpals/openhands_minimal_security_policy.j2 +8 -0
- package/runtime/sandbox/prompts/workerpals/openhands_minimal_system_prompt.j2 +20 -0
- package/runtime/sandbox/prompts/workerpals/openhands_strict_tool_use_message.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openhands_supplemental_guidance_message.md +2 -0
- package/runtime/sandbox/prompts/workerpals/openhands_task_execute_fallback_system_prompt.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openhands_task_execute_system_prompt.md +21 -0
- package/runtime/sandbox/prompts/workerpals/openhands_task_user_prompt.md +6 -0
- package/runtime/sandbox/prompts/workerpals/openhands_timeout_note.md +1 -0
- package/runtime/sandbox/prompts/workerpals/pr_description.md +42 -0
- package/runtime/sandbox/prompts/workerpals/task_quality_critic_system_prompt.md +9 -0
- package/runtime/sandbox/prompts/workerpals/task_quality_critic_user_prompt.md +17 -0
- package/runtime/sandbox/prompts/workerpals/workerpals_system_prompt.md +115 -0
- package/runtime/sandbox/protocol/schemas/approvals.schema.json +6 -0
- package/runtime/sandbox/protocol/schemas/envelope.schema.json +96 -0
- package/runtime/sandbox/protocol/schemas/events.schema.json +679 -0
- package/runtime/sandbox/protocol/schemas/http.schema.json +50 -0
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,
|
|
@@ -1422,6 +1472,130 @@ function buildRuntimeAssetSource(root, protocolSchemasDir) {
|
|
|
1422
1472
|
protocolSchemasDir
|
|
1423
1473
|
};
|
|
1424
1474
|
}
|
|
1475
|
+
function buildWorkerpalSandboxPaths(runtimeRoot) {
|
|
1476
|
+
const root = join2(runtimeRoot, "sandbox");
|
|
1477
|
+
return {
|
|
1478
|
+
root,
|
|
1479
|
+
dockerfilePath: join2(root, "apps", "workerpals", "Dockerfile.sandbox"),
|
|
1480
|
+
packageJsonPath: join2(root, "package.json"),
|
|
1481
|
+
workerpalsDir: join2(root, "apps", "workerpals"),
|
|
1482
|
+
sharedDir: join2(root, "packages", "shared"),
|
|
1483
|
+
protocolDir: join2(root, "packages", "protocol"),
|
|
1484
|
+
configsDir: join2(root, "configs"),
|
|
1485
|
+
workerpalsPromptsDir: join2(root, "prompts", "workerpals"),
|
|
1486
|
+
protocolSchemasDir: join2(root, "protocol", "schemas")
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
function normalizeGitTrackedPath(pathValue) {
|
|
1490
|
+
return String(pathValue ?? "").trim().replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
1491
|
+
}
|
|
1492
|
+
function listTrackedRepoFilesForPath(repoRoot, sourcePath) {
|
|
1493
|
+
const normalizedSource = normalizeGitTrackedPath(sourcePath);
|
|
1494
|
+
if (!normalizedSource)
|
|
1495
|
+
return [];
|
|
1496
|
+
const proc = Bun.spawnSync(["git", "ls-files", "-z", "--", normalizedSource], {
|
|
1497
|
+
cwd: repoRoot,
|
|
1498
|
+
stdout: "pipe",
|
|
1499
|
+
stderr: "pipe",
|
|
1500
|
+
env: {
|
|
1501
|
+
...process.env,
|
|
1502
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1503
|
+
GCM_INTERACTIVE: "Never"
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
if (proc.exitCode !== 0) {
|
|
1507
|
+
const stderr = Buffer.from(proc.stderr ?? []).toString("utf8").trim();
|
|
1508
|
+
throw new Error(`git ls-files failed for ${normalizedSource}${stderr ? `: ${stderr}` : ""}`);
|
|
1509
|
+
}
|
|
1510
|
+
return Buffer.from(proc.stdout ?? []).toString("utf8").split("\x00").map(normalizeGitTrackedPath).filter(Boolean);
|
|
1511
|
+
}
|
|
1512
|
+
function copyTrackedRepoPath(repoRoot, sourcePath, destinationPath, force = true) {
|
|
1513
|
+
const normalizedSource = normalizeGitTrackedPath(sourcePath);
|
|
1514
|
+
if (!normalizedSource) {
|
|
1515
|
+
throw new Error("sourcePath is required");
|
|
1516
|
+
}
|
|
1517
|
+
const absoluteSource = resolve4(repoRoot, normalizedSource);
|
|
1518
|
+
if (!existsSync4(absoluteSource)) {
|
|
1519
|
+
throw new Error(`tracked repo source is missing: ${absoluteSource}`);
|
|
1520
|
+
}
|
|
1521
|
+
const trackedFiles = listTrackedRepoFilesForPath(repoRoot, normalizedSource);
|
|
1522
|
+
const sourceStat = lstatSync(absoluteSource);
|
|
1523
|
+
if (!sourceStat.isDirectory()) {
|
|
1524
|
+
if (!trackedFiles.includes(normalizedSource)) {
|
|
1525
|
+
throw new Error(`tracked repo file is not tracked by git: ${normalizedSource}`);
|
|
1526
|
+
}
|
|
1527
|
+
mkdirSync(dirname(destinationPath), { recursive: true });
|
|
1528
|
+
cpSync(absoluteSource, destinationPath, {
|
|
1529
|
+
recursive: false,
|
|
1530
|
+
force,
|
|
1531
|
+
errorOnExist: false
|
|
1532
|
+
});
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
if (trackedFiles.length === 0) {
|
|
1536
|
+
throw new Error(`tracked repo directory has no tracked files: ${normalizedSource}`);
|
|
1537
|
+
}
|
|
1538
|
+
for (const trackedFile of trackedFiles) {
|
|
1539
|
+
const relativePath = trackedFile === normalizedSource ? basename(trackedFile) : trackedFile.slice(normalizedSource.length + 1);
|
|
1540
|
+
const sourceFile = resolve4(repoRoot, trackedFile);
|
|
1541
|
+
const targetFile = join2(destinationPath, relativePath);
|
|
1542
|
+
mkdirSync(dirname(targetFile), { recursive: true });
|
|
1543
|
+
cpSync(sourceFile, targetFile, {
|
|
1544
|
+
recursive: false,
|
|
1545
|
+
force,
|
|
1546
|
+
errorOnExist: false
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
function isCompleteWorkerpalSandboxRoot(root) {
|
|
1551
|
+
return existsSync4(join2(root, "package.json")) && existsSync4(join2(root, "apps", "workerpals", "Dockerfile.sandbox")) && existsSync4(join2(root, "packages", "shared", "package.json")) && existsSync4(join2(root, "packages", "protocol", "package.json")) && existsSync4(join2(root, "configs", "default.toml")) && existsSync4(join2(root, "prompts", "workerpals")) && existsSync4(join2(root, "protocol", "schemas", "envelope.schema.json")) && existsSync4(join2(root, "protocol", "schemas", "events.schema.json"));
|
|
1552
|
+
}
|
|
1553
|
+
function populateWorkerpalSandboxRuntimeAssets(runtimeRoot, force) {
|
|
1554
|
+
const sandbox = buildWorkerpalSandboxPaths(runtimeRoot);
|
|
1555
|
+
cpSync(join2(runtimeRoot, "configs"), sandbox.configsDir, {
|
|
1556
|
+
recursive: true,
|
|
1557
|
+
force,
|
|
1558
|
+
errorOnExist: false
|
|
1559
|
+
});
|
|
1560
|
+
cpSync(join2(runtimeRoot, "prompts", "workerpals"), sandbox.workerpalsPromptsDir, {
|
|
1561
|
+
recursive: true,
|
|
1562
|
+
force,
|
|
1563
|
+
errorOnExist: false
|
|
1564
|
+
});
|
|
1565
|
+
cpSync(join2(runtimeRoot, "protocol", "schemas"), sandbox.protocolSchemasDir, {
|
|
1566
|
+
recursive: true,
|
|
1567
|
+
force,
|
|
1568
|
+
errorOnExist: false
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
function copySourceCheckoutWorkerpalSandboxBuildContext(sourceRoot, runtimeRoot, force) {
|
|
1572
|
+
const sandbox = buildWorkerpalSandboxPaths(runtimeRoot);
|
|
1573
|
+
const copyPairs = [
|
|
1574
|
+
["package.json", sandbox.packageJsonPath],
|
|
1575
|
+
["apps/workerpals", sandbox.workerpalsDir],
|
|
1576
|
+
["packages/shared", sandbox.sharedDir],
|
|
1577
|
+
["packages/protocol", sandbox.protocolDir]
|
|
1578
|
+
];
|
|
1579
|
+
for (const [fromPath, toPath] of copyPairs) {
|
|
1580
|
+
copyTrackedRepoPath(sourceRoot, fromPath, toPath, force);
|
|
1581
|
+
}
|
|
1582
|
+
if (existsSync4(join2(sourceRoot, "bun.lock"))) {
|
|
1583
|
+
copyTrackedRepoPath(sourceRoot, "bun.lock", join2(sandbox.root, "bun.lock"), force);
|
|
1584
|
+
}
|
|
1585
|
+
populateWorkerpalSandboxRuntimeAssets(runtimeRoot, force);
|
|
1586
|
+
}
|
|
1587
|
+
function copyWorkerpalSandboxBuildContext(source, runtimeRoot, force) {
|
|
1588
|
+
const packagedSandboxRoot = join2(source.root, "sandbox");
|
|
1589
|
+
if (isCompleteWorkerpalSandboxRoot(packagedSandboxRoot)) {
|
|
1590
|
+
cpSync(packagedSandboxRoot, join2(runtimeRoot, "sandbox"), {
|
|
1591
|
+
recursive: true,
|
|
1592
|
+
force,
|
|
1593
|
+
errorOnExist: false
|
|
1594
|
+
});
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
copySourceCheckoutWorkerpalSandboxBuildContext(source.root, runtimeRoot, force);
|
|
1598
|
+
}
|
|
1425
1599
|
function isCompleteRuntimeAssetSource(source) {
|
|
1426
1600
|
return existsSync4(source.envExamplePath) && existsSync4(source.visionExamplePath) && existsSync4(join2(source.configsDir, "default.toml")) && existsSync4(source.promptsDir) && existsSync4(join2(source.protocolSchemasDir, "envelope.schema.json")) && existsSync4(join2(source.protocolSchemasDir, "events.schema.json"));
|
|
1427
1601
|
}
|
|
@@ -1608,6 +1782,7 @@ function copyRuntimeAssetBundle(source, runtimeRoot, force) {
|
|
|
1608
1782
|
force,
|
|
1609
1783
|
errorOnExist: false
|
|
1610
1784
|
});
|
|
1785
|
+
copyWorkerpalSandboxBuildContext(source, runtimeRoot, force);
|
|
1611
1786
|
}
|
|
1612
1787
|
function copyBundledRuntimeAssets(runtimeRoot, force = true) {
|
|
1613
1788
|
const bundledSource = resolveBundledRuntimeAssetSource();
|
|
@@ -1643,7 +1818,7 @@ async function downloadRuntimeAssetsFromSourceTag(runtimeRoot, tag) {
|
|
|
1643
1818
|
throw new Error(`Failed to fetch runtime source tree for ${tag} (HTTP ${treeResponse.status})`);
|
|
1644
1819
|
}
|
|
1645
1820
|
const treePayload = await treeResponse.json();
|
|
1646
|
-
const paths = (treePayload.tree ?? []).filter((entry) => entry.type === "blob" && typeof entry.path === "string").map((entry) => String(entry.path)).filter((pathValue) => pathValue === ".env.example" || pathValue === "vision.example.md" || pathValue.startsWith("configs/") || pathValue.startsWith("prompts/") || pathValue.startsWith("packages/protocol/src/schemas/"));
|
|
1821
|
+
const paths = (treePayload.tree ?? []).filter((entry) => entry.type === "blob" && typeof entry.path === "string").map((entry) => String(entry.path)).filter((pathValue) => pathValue === ".env.example" || pathValue === "vision.example.md" || pathValue === "package.json" || pathValue === "bun.lock" || pathValue.startsWith("configs/") || pathValue.startsWith("prompts/workerpals/") || pathValue.startsWith("prompts/") || pathValue.startsWith("apps/workerpals/") || pathValue.startsWith("packages/shared/") || pathValue.startsWith("packages/protocol/") || pathValue.startsWith("packages/protocol/src/schemas/"));
|
|
1647
1822
|
if (paths.length === 0) {
|
|
1648
1823
|
throw new Error(`Runtime source tree for ${tag} did not include prompts/config assets`);
|
|
1649
1824
|
}
|
|
@@ -1651,10 +1826,16 @@ async function downloadRuntimeAssetsFromSourceTag(runtimeRoot, tag) {
|
|
|
1651
1826
|
for (const pathValue of sorted) {
|
|
1652
1827
|
const rawUrl = `https://raw.githubusercontent.com/${GITHUB_OWNER}/${GITHUB_REPO}/${encodeURIComponent(tag)}/${pathValue}`;
|
|
1653
1828
|
const body = await fetchTextFromUrl(rawUrl, 20000);
|
|
1654
|
-
const outPath = pathValue.
|
|
1829
|
+
const outPath = pathValue === "package.json" || pathValue === "bun.lock" ? join2(runtimeRoot, "sandbox", pathValue) : pathValue.startsWith("apps/workerpals/") || pathValue.startsWith("packages/shared/") || pathValue.startsWith("packages/protocol/") ? join2(runtimeRoot, "sandbox", pathValue) : join2(runtimeRoot, pathValue);
|
|
1655
1830
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
1656
1831
|
writeFileSync(outPath, body, "utf8");
|
|
1832
|
+
if (pathValue.startsWith("packages/protocol/src/schemas/")) {
|
|
1833
|
+
const runtimeSchemaPath = join2(runtimeRoot, "protocol", "schemas", pathValue.slice("packages/protocol/src/schemas/".length));
|
|
1834
|
+
mkdirSync(dirname(runtimeSchemaPath), { recursive: true });
|
|
1835
|
+
writeFileSync(runtimeSchemaPath, body, "utf8");
|
|
1836
|
+
}
|
|
1657
1837
|
}
|
|
1838
|
+
populateWorkerpalSandboxRuntimeAssets(runtimeRoot, true);
|
|
1658
1839
|
}
|
|
1659
1840
|
async function ensureRuntimeAssets(runtimeRoot, runtimeTag) {
|
|
1660
1841
|
console.log(`[pushpals] Preparing embedded runtime assets for ${runtimeTag}...`);
|
|
@@ -1662,12 +1843,12 @@ async function ensureRuntimeAssets(runtimeRoot, runtimeTag) {
|
|
|
1662
1843
|
const currentTag = existsSync4(markerPath) ? readFileSync4(markerPath, "utf8").trim() : "";
|
|
1663
1844
|
const protocolSchemasDir = join2(runtimeRoot, "protocol", "schemas");
|
|
1664
1845
|
const hasProtocolSchemas = existsSync4(join2(protocolSchemasDir, "envelope.schema.json")) && existsSync4(join2(protocolSchemasDir, "events.schema.json"));
|
|
1665
|
-
const hasAssets = existsSync4(join2(runtimeRoot, ".env.example")) && existsSync4(join2(runtimeRoot, "vision.example.md")) && existsSync4(join2(runtimeRoot, "configs", "default.toml")) && existsSync4(join2(runtimeRoot, "prompts")) && hasProtocolSchemas;
|
|
1846
|
+
const hasAssets = existsSync4(join2(runtimeRoot, ".env.example")) && existsSync4(join2(runtimeRoot, "vision.example.md")) && existsSync4(join2(runtimeRoot, "configs", "default.toml")) && existsSync4(join2(runtimeRoot, "prompts")) && hasProtocolSchemas && isCompleteWorkerpalSandboxRoot(join2(runtimeRoot, "sandbox"));
|
|
1666
1847
|
if (!hasAssets || currentTag !== runtimeTag) {
|
|
1667
1848
|
console.log(`[pushpals] Embedded runtime assets ${hasAssets ? "are stale" : "are missing"}; refreshing bundle...`);
|
|
1668
1849
|
copyBundledRuntimeAssets(runtimeRoot);
|
|
1669
1850
|
const hasProtocolSchemasAfterCopy = existsSync4(join2(protocolSchemasDir, "envelope.schema.json")) && existsSync4(join2(protocolSchemasDir, "events.schema.json"));
|
|
1670
|
-
const hasAssetsAfterCopy = existsSync4(join2(runtimeRoot, ".env.example")) && existsSync4(join2(runtimeRoot, "vision.example.md")) && existsSync4(join2(runtimeRoot, "configs", "default.toml")) && existsSync4(join2(runtimeRoot, "prompts")) && hasProtocolSchemasAfterCopy;
|
|
1851
|
+
const hasAssetsAfterCopy = existsSync4(join2(runtimeRoot, ".env.example")) && existsSync4(join2(runtimeRoot, "vision.example.md")) && existsSync4(join2(runtimeRoot, "configs", "default.toml")) && existsSync4(join2(runtimeRoot, "prompts")) && hasProtocolSchemasAfterCopy && isCompleteWorkerpalSandboxRoot(join2(runtimeRoot, "sandbox"));
|
|
1671
1852
|
if (!hasAssetsAfterCopy) {
|
|
1672
1853
|
console.log("[pushpals] Bundled runtime assets are incomplete; falling back to release source downloads...");
|
|
1673
1854
|
await downloadRuntimeAssetsFromSourceTag(runtimeRoot, runtimeTag);
|
|
@@ -1737,14 +1918,18 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
|
|
|
1737
1918
|
PUSHPALS_PROJECT_ROOT_OVERRIDE: opts.repoRoot,
|
|
1738
1919
|
...useRuntimeConfig ? {
|
|
1739
1920
|
PUSHPALS_CONFIG_DIR_OVERRIDE: join2(opts.runtimeRoot, "configs"),
|
|
1740
|
-
PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.runtimeRoot
|
|
1921
|
+
PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.runtimeRoot,
|
|
1922
|
+
PUSHPALS_WORKERPALS_SANDBOX_ROOT: join2(opts.runtimeRoot, "sandbox"),
|
|
1923
|
+
...typeof opts.runtimeTag === "string" && opts.runtimeTag.trim() ? { PUSHPALS_RUNTIME_TAG: opts.runtimeTag.trim() } : {}
|
|
1741
1924
|
} : {
|
|
1742
1925
|
PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.repoRoot
|
|
1743
1926
|
},
|
|
1744
1927
|
PUSHPALS_PROTOCOL_SCHEMAS_DIR: join2(opts.runtimeRoot, "protocol", "schemas"),
|
|
1745
1928
|
...typeof opts.sessionId === "string" && opts.sessionId.trim() ? { PUSHPALS_SESSION_ID: opts.sessionId.trim() } : {},
|
|
1746
1929
|
...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() } : {}
|
|
1930
|
+
...typeof env.PUSHPALS_GIT_BIN_ABSOLUTE === "string" && env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() ? { PUSHPALS_GIT_BIN_ABSOLUTE: env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() } : {},
|
|
1931
|
+
...typeof env.PUSHPALS_DOCKER_BIN === "string" && env.PUSHPALS_DOCKER_BIN.trim() ? { PUSHPALS_DOCKER_BIN: env.PUSHPALS_DOCKER_BIN.trim() } : {},
|
|
1932
|
+
...typeof env.PUSHPALS_DOCKER_BIN_ABSOLUTE === "string" && env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() ? { PUSHPALS_DOCKER_BIN_ABSOLUTE: env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() } : {}
|
|
1748
1933
|
};
|
|
1749
1934
|
}
|
|
1750
1935
|
function normalizeChildProcessEnv(baseEnv, platform = process.platform) {
|
|
@@ -2020,6 +2205,19 @@ function applyResolvedGitBinaryToRuntimeEnv(env, resolvedGitBinary, platform = p
|
|
|
2020
2205
|
}
|
|
2021
2206
|
return env;
|
|
2022
2207
|
}
|
|
2208
|
+
function applyResolvedDockerBinaryToRuntimeEnv(env, resolvedDockerBinary, platform = process.platform) {
|
|
2209
|
+
const resolvedPath = String(resolvedDockerBinary ?? "").trim();
|
|
2210
|
+
if (!resolvedPath)
|
|
2211
|
+
return env;
|
|
2212
|
+
prependExecutableDirToPath(env, resolvedPath, platform);
|
|
2213
|
+
env.PUSHPALS_DOCKER_BIN = basename(resolvedPath);
|
|
2214
|
+
if (resolvedPath.includes("/") || resolvedPath.includes("\\")) {
|
|
2215
|
+
env.PUSHPALS_DOCKER_BIN_ABSOLUTE = resolvedPath;
|
|
2216
|
+
} else {
|
|
2217
|
+
delete env.PUSHPALS_DOCKER_BIN_ABSOLUTE;
|
|
2218
|
+
}
|
|
2219
|
+
return env;
|
|
2220
|
+
}
|
|
2023
2221
|
function resolveRuntimeGitExecutableCandidates(env, platform = process.platform) {
|
|
2024
2222
|
const candidates = [];
|
|
2025
2223
|
const seen = new Set;
|
|
@@ -2039,6 +2237,25 @@ function resolveRuntimeGitExecutableCandidates(env, platform = process.platform)
|
|
|
2039
2237
|
pushCandidate("git");
|
|
2040
2238
|
return candidates;
|
|
2041
2239
|
}
|
|
2240
|
+
function resolveRuntimeDockerExecutableCandidates(env, platform = process.platform) {
|
|
2241
|
+
const candidates = [];
|
|
2242
|
+
const seen = new Set;
|
|
2243
|
+
const pushCandidate = (value) => {
|
|
2244
|
+
const trimmed = String(value ?? "").trim();
|
|
2245
|
+
if (!trimmed)
|
|
2246
|
+
return;
|
|
2247
|
+
const key = platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
2248
|
+
if (seen.has(key))
|
|
2249
|
+
return;
|
|
2250
|
+
seen.add(key);
|
|
2251
|
+
candidates.push(trimmed);
|
|
2252
|
+
};
|
|
2253
|
+
pushCandidate(env.PUSHPALS_DOCKER_BIN ?? "");
|
|
2254
|
+
pushCandidate(env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? "");
|
|
2255
|
+
pushCandidate(platform === "win32" ? "docker.exe" : "docker");
|
|
2256
|
+
pushCandidate("docker");
|
|
2257
|
+
return candidates;
|
|
2258
|
+
}
|
|
2042
2259
|
function resolveWindowsShellExecutableCandidatesForEnv(env, platform = process.platform) {
|
|
2043
2260
|
if (platform !== "win32")
|
|
2044
2261
|
return [];
|
|
@@ -2153,6 +2370,142 @@ async function resolveSourceControlManagerGitProbe(cwd, env, platform = process.
|
|
|
2153
2370
|
detail: candidates.join(", ") || "git"
|
|
2154
2371
|
};
|
|
2155
2372
|
}
|
|
2373
|
+
async function resolveWorkerpalDockerProbe(cwd, env, platform = process.platform) {
|
|
2374
|
+
const resolvedDockerBinary = await resolveCommandPath(platform === "win32" ? "docker.exe" : "docker", cwd, env);
|
|
2375
|
+
if (resolvedDockerBinary) {
|
|
2376
|
+
prependExecutableDirToPath(env, resolvedDockerBinary, platform);
|
|
2377
|
+
env.PUSHPALS_DOCKER_BIN = basename(resolvedDockerBinary);
|
|
2378
|
+
env.PUSHPALS_DOCKER_BIN_ABSOLUTE = resolvedDockerBinary;
|
|
2379
|
+
}
|
|
2380
|
+
const candidates = resolveRuntimeDockerExecutableCandidates(env, platform);
|
|
2381
|
+
const failures = [];
|
|
2382
|
+
for (const candidate of candidates) {
|
|
2383
|
+
const result = await runCommandWithEnv([candidate, "version", "--format", "{{.Server.Version}}"], cwd, env);
|
|
2384
|
+
if (result.ok) {
|
|
2385
|
+
const version = result.stdout.trim();
|
|
2386
|
+
return {
|
|
2387
|
+
ok: true,
|
|
2388
|
+
detail: version ? `${candidate} (${version})` : candidate
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
const detail = result.stderr || result.stdout || `exit ${result.exitCode}`;
|
|
2392
|
+
failures.push(`${candidate}: ${detail}`);
|
|
2393
|
+
}
|
|
2394
|
+
return {
|
|
2395
|
+
ok: false,
|
|
2396
|
+
detail: failures.join(" | ") || "docker"
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
var WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL = "pushpals.runtime_tag";
|
|
2400
|
+
var WORKERPAL_SANDBOX_COMPONENT_LABEL = "pushpals.component=workerpals-sandbox";
|
|
2401
|
+
function resolveConfiguredDockerExecutable(env, platform = process.platform) {
|
|
2402
|
+
const configured = String(env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? env.PUSHPALS_DOCKER_BIN ?? (platform === "win32" ? "docker.exe" : "docker")).trim();
|
|
2403
|
+
return configured || (platform === "win32" ? "docker.exe" : "docker");
|
|
2404
|
+
}
|
|
2405
|
+
async function inspectDockerImageRuntimeTag(dockerExecutable, imageName, cwd, env) {
|
|
2406
|
+
const inspect = await runCommandWithEnv([
|
|
2407
|
+
dockerExecutable,
|
|
2408
|
+
"image",
|
|
2409
|
+
"inspect",
|
|
2410
|
+
"--format",
|
|
2411
|
+
`{{ index .Config.Labels "${WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL}" }}`,
|
|
2412
|
+
imageName
|
|
2413
|
+
], cwd, env);
|
|
2414
|
+
if (!inspect.ok)
|
|
2415
|
+
return "";
|
|
2416
|
+
const value = inspect.stdout.trim();
|
|
2417
|
+
return value === "<no value>" ? "" : value;
|
|
2418
|
+
}
|
|
2419
|
+
async function ensureWorkerpalDockerImageReady(opts) {
|
|
2420
|
+
const runtimeTag = String(opts.runtimeTag ?? "").trim();
|
|
2421
|
+
if (!runtimeTag) {
|
|
2422
|
+
return {
|
|
2423
|
+
ok: false,
|
|
2424
|
+
detail: "embedded runtime tag is required to prepare the WorkerPal sandbox image"
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
await (opts.ensureRuntimeAssetsFn ?? ensureRuntimeAssets)(opts.runtimeRoot, runtimeTag);
|
|
2428
|
+
const sandbox = buildWorkerpalSandboxPaths(opts.runtimeRoot);
|
|
2429
|
+
if (!isCompleteWorkerpalSandboxRoot(sandbox.root)) {
|
|
2430
|
+
return {
|
|
2431
|
+
ok: false,
|
|
2432
|
+
detail: `embedded WorkerPal sandbox assets are incomplete at ${sandbox.root}`
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
const dockerExecutable = resolveConfiguredDockerExecutable(opts.env, opts.platform ?? process.platform);
|
|
2436
|
+
const inspectImageRuntimeTagFn = opts.inspectImageRuntimeTagFn ?? inspectDockerImageRuntimeTag;
|
|
2437
|
+
const runCommandWithEnvFn = opts.runCommandWithEnvFn ?? runCommandWithEnv;
|
|
2438
|
+
const existingRuntimeTag = await inspectImageRuntimeTagFn(dockerExecutable, opts.dockerImage, sandbox.root, opts.env);
|
|
2439
|
+
if (existingRuntimeTag === runtimeTag) {
|
|
2440
|
+
return {
|
|
2441
|
+
ok: true,
|
|
2442
|
+
detail: `WorkerPal sandbox image is ready locally (${opts.dockerImage}, runtimeTag=${runtimeTag})`
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
console.log(existingRuntimeTag ? `[pushpals] WorkerPal sandbox image ${opts.dockerImage} is stale (runtimeTag=${existingRuntimeTag}); rebuilding locally...` : `[pushpals] WorkerPal sandbox image ${opts.dockerImage} is missing; building locally...`);
|
|
2446
|
+
const build = await runCommandWithEnvFn([
|
|
2447
|
+
dockerExecutable,
|
|
2448
|
+
"build",
|
|
2449
|
+
"-f",
|
|
2450
|
+
"apps/workerpals/Dockerfile.sandbox",
|
|
2451
|
+
"--label",
|
|
2452
|
+
`${WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL}=${runtimeTag}`,
|
|
2453
|
+
"--label",
|
|
2454
|
+
WORKERPAL_SANDBOX_COMPONENT_LABEL,
|
|
2455
|
+
"-t",
|
|
2456
|
+
opts.dockerImage,
|
|
2457
|
+
"."
|
|
2458
|
+
], sandbox.root, opts.env);
|
|
2459
|
+
if (!build.ok) {
|
|
2460
|
+
const detail = build.stderr || build.stdout || `docker build exited ${build.exitCode}`;
|
|
2461
|
+
return {
|
|
2462
|
+
ok: false,
|
|
2463
|
+
detail: `failed to build local WorkerPal sandbox image ${opts.dockerImage}: ${detail}`
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
return {
|
|
2467
|
+
ok: true,
|
|
2468
|
+
detail: `built local WorkerPal sandbox image ${opts.dockerImage} for runtimeTag=${runtimeTag}`
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2471
|
+
async function prepareEmbeddedWorkerpalDockerImageIfNeeded(opts) {
|
|
2472
|
+
if (!opts.preparedRuntime.preflightUsesEmbeddedRuntime) {
|
|
2473
|
+
return {
|
|
2474
|
+
status: "skipped",
|
|
2475
|
+
detail: "repo is using source-checkout runtime assets",
|
|
2476
|
+
runtimeTag: ""
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
if (!opts.config.remotebuddy.autoSpawnWorkerpals || !opts.config.remotebuddy.workerpalDocker || !opts.config.remotebuddy.workerpalRequireDocker) {
|
|
2480
|
+
return {
|
|
2481
|
+
status: "skipped",
|
|
2482
|
+
detail: "embedded docker-backed WorkerPal auto-spawn is not required",
|
|
2483
|
+
runtimeTag: ""
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
if (opts.dockerPrecheck.status === "failed") {
|
|
2487
|
+
return {
|
|
2488
|
+
status: "failed",
|
|
2489
|
+
detail: opts.dockerPrecheck.detail,
|
|
2490
|
+
runtimeTag: ""
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
const runtimeTag = opts.preparedRuntime.runtimeTag || String(opts.runtimeTagHint ?? "").trim() || await (opts.resolveRuntimeReleaseTagFn ?? resolveRuntimeReleaseTag)(opts.runtimeTagHint);
|
|
2494
|
+
if (!runtimeTag) {
|
|
2495
|
+
return {
|
|
2496
|
+
status: "failed",
|
|
2497
|
+
detail: "embedded runtime tag is required to prepare the WorkerPal sandbox image",
|
|
2498
|
+
runtimeTag: ""
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
const ensureResult = await (opts.ensureWorkerpalDockerImageReadyFn ?? ensureWorkerpalDockerImageReady)({
|
|
2502
|
+
runtimeRoot: opts.preparedRuntime.runtimeRoot,
|
|
2503
|
+
runtimeTag,
|
|
2504
|
+
dockerImage: opts.config.remotebuddy.workerpalImage ?? opts.config.workerpals.dockerImage,
|
|
2505
|
+
env: opts.dockerPrecheck.env
|
|
2506
|
+
});
|
|
2507
|
+
return ensureResult.ok ? { status: "ok", detail: ensureResult.detail, runtimeTag } : { status: "failed", detail: ensureResult.detail, runtimeTag };
|
|
2508
|
+
}
|
|
2156
2509
|
async function precheckSourceControlManagerGitAvailability(opts) {
|
|
2157
2510
|
const platform = opts.platform ?? process.platform;
|
|
2158
2511
|
const env = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
|
|
@@ -2161,8 +2514,9 @@ async function precheckSourceControlManagerGitAvailability(opts) {
|
|
|
2161
2514
|
useRuntimeConfig: opts.preflightUsesEmbeddedRuntime,
|
|
2162
2515
|
sessionId: opts.sessionId
|
|
2163
2516
|
});
|
|
2164
|
-
|
|
2165
|
-
|
|
2517
|
+
const preconfiguredGitBinary = env.PUSHPALS_GIT_BIN_ABSOLUTE ?? env.PUSHPALS_GIT_BIN;
|
|
2518
|
+
if (preconfiguredGitBinary) {
|
|
2519
|
+
applyResolvedGitBinaryToRuntimeEnv(env, preconfiguredGitBinary, platform);
|
|
2166
2520
|
}
|
|
2167
2521
|
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
2522
|
if (remoteStatus.status === "missing_remote") {
|
|
@@ -2198,6 +2552,55 @@ async function precheckSourceControlManagerGitAvailability(opts) {
|
|
|
2198
2552
|
env
|
|
2199
2553
|
};
|
|
2200
2554
|
}
|
|
2555
|
+
async function precheckWorkerpalDockerAvailability(opts) {
|
|
2556
|
+
const env = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
|
|
2557
|
+
repoRoot: opts.repoRoot,
|
|
2558
|
+
runtimeRoot: opts.runtimeRoot,
|
|
2559
|
+
useRuntimeConfig: opts.preflightUsesEmbeddedRuntime,
|
|
2560
|
+
sessionId: opts.sessionId
|
|
2561
|
+
});
|
|
2562
|
+
const preconfiguredDockerBinary = env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? env.PUSHPALS_DOCKER_BIN;
|
|
2563
|
+
if (preconfiguredDockerBinary) {
|
|
2564
|
+
applyResolvedDockerBinaryToRuntimeEnv(env, preconfiguredDockerBinary, opts.platform ?? process.platform);
|
|
2565
|
+
}
|
|
2566
|
+
if (!opts.autoSpawnWorkerpals) {
|
|
2567
|
+
return {
|
|
2568
|
+
status: "skipped",
|
|
2569
|
+
detail: "WorkerPal auto-spawn is disabled",
|
|
2570
|
+
env
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
if (!opts.dockerEnabled) {
|
|
2574
|
+
return {
|
|
2575
|
+
status: "skipped",
|
|
2576
|
+
detail: "WorkerPal docker mode is disabled",
|
|
2577
|
+
env
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
if (!opts.requireDocker) {
|
|
2581
|
+
return {
|
|
2582
|
+
status: "skipped",
|
|
2583
|
+
detail: "WorkerPal docker mode is optional",
|
|
2584
|
+
env
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
const dockerProbe = await (opts.dockerProbeFn ?? resolveWorkerpalDockerProbe)(opts.repoRoot, env, opts.platform ?? process.platform);
|
|
2588
|
+
if (!dockerProbe.ok) {
|
|
2589
|
+
return {
|
|
2590
|
+
status: "failed",
|
|
2591
|
+
detail: dockerProbe.detail,
|
|
2592
|
+
env
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
return {
|
|
2596
|
+
status: "ok",
|
|
2597
|
+
detail: dockerProbe.detail,
|
|
2598
|
+
env
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
function resolveWorkerpalCapacityTimeoutMs(config) {
|
|
2602
|
+
return Math.max(config.remotebuddy.waitForWorkerpalMs, config.remotebuddy.workerpalStartupTimeoutMs, config.remotebuddy.workerpalDocker ? config.workerpals.dockerAgentStartupTimeoutMs + 15000 : 0, 1e4);
|
|
2603
|
+
}
|
|
2201
2604
|
async function checkGitRemoteConfigured(repoRoot, remote, env) {
|
|
2202
2605
|
const normalizedRemote = String(remote ?? "").trim();
|
|
2203
2606
|
if (!normalizedRemote) {
|
|
@@ -2539,6 +2942,42 @@ async function probeSourceControlManager(port) {
|
|
|
2539
2942
|
return false;
|
|
2540
2943
|
}
|
|
2541
2944
|
}
|
|
2945
|
+
async function fetchWorkerStatusRows(serverUrl, ttlMs) {
|
|
2946
|
+
const payload = await fetchJsonWithTimeout(`${serverUrl}/workers?ttlMs=${Math.max(1000, Math.floor(ttlMs))}`, {}, 1e4);
|
|
2947
|
+
if (!payload?.ok || !Array.isArray(payload.workers)) {
|
|
2948
|
+
return [];
|
|
2949
|
+
}
|
|
2950
|
+
return payload.workers;
|
|
2951
|
+
}
|
|
2952
|
+
async function waitForWorkerpalCapacity(opts) {
|
|
2953
|
+
const deadline = Date.now() + Math.max(1000, opts.timeoutMs);
|
|
2954
|
+
let lastObservedOnline = 0;
|
|
2955
|
+
while (Date.now() < deadline) {
|
|
2956
|
+
const workers = await (opts.fetchWorkersFn ?? fetchWorkerStatusRows)(opts.serverUrl, opts.ttlMs);
|
|
2957
|
+
const onlineWorkers = workers.filter((worker) => Boolean(worker?.isOnline) && String(worker?.status ?? "").trim().toLowerCase() !== "offline");
|
|
2958
|
+
const idleWorkers = onlineWorkers.filter((worker) => Number(worker?.activeJobCount ?? 0) <= 0);
|
|
2959
|
+
if (onlineWorkers.length > 0) {
|
|
2960
|
+
lastObservedOnline = Math.max(lastObservedOnline, onlineWorkers.length);
|
|
2961
|
+
}
|
|
2962
|
+
if (idleWorkers.length > 0) {
|
|
2963
|
+
return {
|
|
2964
|
+
ok: true,
|
|
2965
|
+
detail: `${idleWorkers.length} idle / ${onlineWorkers.length} online`
|
|
2966
|
+
};
|
|
2967
|
+
}
|
|
2968
|
+
await (opts.sleepFn ?? Bun.sleep)(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
2969
|
+
}
|
|
2970
|
+
if (lastObservedOnline > 0) {
|
|
2971
|
+
return {
|
|
2972
|
+
ok: false,
|
|
2973
|
+
detail: `${lastObservedOnline} online WorkerPal(s) reported but none became idle within ${Math.max(1000, opts.timeoutMs)}ms`
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
2976
|
+
return {
|
|
2977
|
+
ok: false,
|
|
2978
|
+
detail: `no online WorkerPal reported within ${Math.max(1000, opts.timeoutMs)}ms`
|
|
2979
|
+
};
|
|
2980
|
+
}
|
|
2542
2981
|
async function fetchWithTimeout(url, init = {}, timeoutMs = HTTP_TIMEOUT_MS) {
|
|
2543
2982
|
const controller = new AbortController;
|
|
2544
2983
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -2624,15 +3063,21 @@ async function autoStartRuntimeServices(opts) {
|
|
|
2624
3063
|
}
|
|
2625
3064
|
await ensureRuntimeAssets(runtimeRoot, runtimeTag);
|
|
2626
3065
|
const runtimeBinaries = await ensureRuntimeBinaries(runtimeRoot, runtimeTag);
|
|
2627
|
-
const runtimeEnv = buildEmbeddedRuntimeEnv(process.env, {
|
|
3066
|
+
const runtimeEnv = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
|
|
2628
3067
|
repoRoot: opts.repoRoot,
|
|
2629
3068
|
runtimeRoot,
|
|
2630
3069
|
useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
2631
|
-
sessionId: opts.sessionId
|
|
3070
|
+
sessionId: opts.sessionId,
|
|
3071
|
+
runtimeTag
|
|
2632
3072
|
});
|
|
2633
3073
|
runtimeEnv.PUSHPALS_WORKERPALS_BIN = runtimeBinaries.workerpals;
|
|
2634
|
-
|
|
2635
|
-
|
|
3074
|
+
const preconfiguredRuntimeGitBinary = runtimeEnv.PUSHPALS_GIT_BIN_ABSOLUTE ?? runtimeEnv.PUSHPALS_GIT_BIN;
|
|
3075
|
+
if (preconfiguredRuntimeGitBinary) {
|
|
3076
|
+
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, preconfiguredRuntimeGitBinary);
|
|
3077
|
+
}
|
|
3078
|
+
const preconfiguredRuntimeDockerBinary = runtimeEnv.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? runtimeEnv.PUSHPALS_DOCKER_BIN;
|
|
3079
|
+
if (preconfiguredRuntimeDockerBinary) {
|
|
3080
|
+
applyResolvedDockerBinaryToRuntimeEnv(runtimeEnv, preconfiguredRuntimeDockerBinary);
|
|
2636
3081
|
}
|
|
2637
3082
|
const gitLookupCommand = typeof runtimeEnv.PUSHPALS_GIT_BIN === "string" && runtimeEnv.PUSHPALS_GIT_BIN.trim() ? runtimeEnv.PUSHPALS_GIT_BIN.trim() : "git";
|
|
2638
3083
|
const resolvedGitBinary = await resolveCommandPath(gitLookupCommand, opts.repoRoot, runtimeEnv);
|
|
@@ -2720,6 +3165,24 @@ ${tail}` : ""}`);
|
|
|
2720
3165
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] embedded remotebuddy autonomous engine is disabled (remotebuddy.autonomy.enabled=false).");
|
|
2721
3166
|
};
|
|
2722
3167
|
reportRemoteBuddyAutonomousEngineState();
|
|
3168
|
+
if (runtimePreflight.config.remotebuddy.autoSpawnWorkerpals) {
|
|
3169
|
+
const workerpalReadyTimeoutMs = resolveWorkerpalCapacityTimeoutMs(runtimePreflight.config);
|
|
3170
|
+
const workerpalCapacity = await waitForWorkerpalCapacity({
|
|
3171
|
+
serverUrl: opts.serverUrl,
|
|
3172
|
+
timeoutMs: workerpalReadyTimeoutMs,
|
|
3173
|
+
ttlMs: runtimePreflight.config.remotebuddy.workerpalOnlineTtlMs
|
|
3174
|
+
});
|
|
3175
|
+
if (!workerpalCapacity.ok) {
|
|
3176
|
+
const tail = readLogTail(remotebuddyService.logPath);
|
|
3177
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded workerpal capacity did not become available within ${workerpalReadyTimeoutMs}ms.`);
|
|
3178
|
+
stopRuntimeServices(services);
|
|
3179
|
+
throw new Error(`Embedded WorkerPal capacity did not become available within ${workerpalReadyTimeoutMs}ms (${workerpalCapacity.detail}). ` + `See ${remotebuddyService.logPath}${tail ? `
|
|
3180
|
+
--- remotebuddy log tail ---
|
|
3181
|
+
${tail}` : ""}`);
|
|
3182
|
+
}
|
|
3183
|
+
console.log(`[pushpals] Embedded WorkerPal capacity is ready (${workerpalCapacity.detail}).`);
|
|
3184
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded workerpal capacity ready (${workerpalCapacity.detail}).`);
|
|
3185
|
+
}
|
|
2723
3186
|
const scmHealthy = await probeSourceControlManager(opts.sourceControlManagerPort);
|
|
2724
3187
|
const scmGitProbe = await resolveSourceControlManagerGitProbe(opts.repoRoot, runtimeEnv, process.platform);
|
|
2725
3188
|
const scmRemoteStatus = await checkGitRemoteConfigured(opts.repoRoot, opts.sourceControlManagerRemote, runtimeEnv);
|
|
@@ -3396,6 +3859,15 @@ async function main() {
|
|
|
3396
3859
|
console.error(`[pushpals] Precheck failed: embedded SourceControlManager git command is unavailable (${scmGitPrecheck.detail}).`);
|
|
3397
3860
|
process.exit(1);
|
|
3398
3861
|
}
|
|
3862
|
+
const workerpalDockerPrecheck = await precheckWorkerpalDockerAvailability({
|
|
3863
|
+
repoRoot,
|
|
3864
|
+
runtimeRoot: preparedRuntime.runtimeRoot,
|
|
3865
|
+
preflightUsesEmbeddedRuntime: preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
3866
|
+
autoSpawnWorkerpals: Boolean(config.remotebuddy.autoSpawnWorkerpals),
|
|
3867
|
+
dockerEnabled: Boolean(config.remotebuddy.workerpalDocker),
|
|
3868
|
+
requireDocker: Boolean(config.remotebuddy.workerpalRequireDocker),
|
|
3869
|
+
baseEnv: scmGitPrecheck.env
|
|
3870
|
+
});
|
|
3399
3871
|
const precheckPassed = await enforcePushpalsRemoteBranchPrecheck(repoRoot, config.sourceControlManager.remote, config.sourceControlManager.mainBranch);
|
|
3400
3872
|
if (!precheckPassed) {
|
|
3401
3873
|
process.exit(1);
|
|
@@ -3414,6 +3886,7 @@ async function main() {
|
|
|
3414
3886
|
};
|
|
3415
3887
|
let autoStartedServices = [];
|
|
3416
3888
|
let pushpalsLogPath;
|
|
3889
|
+
let resolvedRuntimeTagForAutoStart = preparedRuntime.runtimeTag || parsed.runtimeTag || "";
|
|
3417
3890
|
const stopAutoStartedServices = () => {
|
|
3418
3891
|
if (autoStartedServices.length === 0)
|
|
3419
3892
|
return;
|
|
@@ -3422,6 +3895,26 @@ async function main() {
|
|
|
3422
3895
|
};
|
|
3423
3896
|
let serverHealthy = await probeServer(serverUrl);
|
|
3424
3897
|
const serverWasAlreadyHealthy = serverHealthy;
|
|
3898
|
+
if (!serverHealthy && workerpalDockerPrecheck.status === "failed") {
|
|
3899
|
+
console.error(`[pushpals] Precheck failed: Docker-backed WorkerPal auto-spawn is required but Docker is unavailable (${workerpalDockerPrecheck.detail}).`);
|
|
3900
|
+
console.error("[pushpals] Precheck failed: start Docker Desktop or the Docker daemon, then retry pushpals.");
|
|
3901
|
+
process.exit(1);
|
|
3902
|
+
}
|
|
3903
|
+
if (workerpalDockerPrecheck.status !== "failed") {
|
|
3904
|
+
const workerpalImagePrecheck = await prepareEmbeddedWorkerpalDockerImageIfNeeded({
|
|
3905
|
+
preparedRuntime,
|
|
3906
|
+
config,
|
|
3907
|
+
dockerPrecheck: workerpalDockerPrecheck,
|
|
3908
|
+
runtimeTagHint: resolvedRuntimeTagForAutoStart || parsed.runtimeTag
|
|
3909
|
+
});
|
|
3910
|
+
if (workerpalImagePrecheck.status === "failed") {
|
|
3911
|
+
console.error(`[pushpals] Precheck failed: ${workerpalImagePrecheck.detail}.`);
|
|
3912
|
+
process.exit(1);
|
|
3913
|
+
}
|
|
3914
|
+
if (workerpalImagePrecheck.runtimeTag) {
|
|
3915
|
+
resolvedRuntimeTagForAutoStart = workerpalImagePrecheck.runtimeTag;
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3425
3918
|
let remoteBuddyConsumerHealth = {
|
|
3426
3919
|
ok: false,
|
|
3427
3920
|
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
|
|
@@ -3437,8 +3930,9 @@ async function main() {
|
|
|
3437
3930
|
sourceControlManagerPort: config.sourceControlManager.port,
|
|
3438
3931
|
sourceControlManagerRemote: config.sourceControlManager.remote,
|
|
3439
3932
|
preparedRuntime,
|
|
3440
|
-
requestedRuntimeTag: parsed.runtimeTag,
|
|
3441
|
-
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled))
|
|
3933
|
+
requestedRuntimeTag: resolvedRuntimeTagForAutoStart || parsed.runtimeTag,
|
|
3934
|
+
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled)),
|
|
3935
|
+
baseEnv: workerpalDockerPrecheck.env
|
|
3442
3936
|
});
|
|
3443
3937
|
autoStartedServices = startedRuntime.services;
|
|
3444
3938
|
pushpalsLogPath = startedRuntime.pushpalsLogPath;
|
|
@@ -3493,6 +3987,22 @@ async function main() {
|
|
|
3493
3987
|
}
|
|
3494
3988
|
process.exit(1);
|
|
3495
3989
|
}
|
|
3990
|
+
const workerpalCapacity = await waitForWorkerpalCapacity({
|
|
3991
|
+
serverUrl,
|
|
3992
|
+
timeoutMs: resolveWorkerpalCapacityTimeoutMs(config),
|
|
3993
|
+
ttlMs: config.remotebuddy.workerpalOnlineTtlMs
|
|
3994
|
+
});
|
|
3995
|
+
if (!workerpalCapacity.ok) {
|
|
3996
|
+
stopAutoStartedServices();
|
|
3997
|
+
console.error(`[pushpals] WorkerPal capacity is not ready for repo ${repoRoot}: ${workerpalCapacity.detail}.`);
|
|
3998
|
+
if (workerpalDockerPrecheck.status === "failed") {
|
|
3999
|
+
console.error(`[pushpals] Docker precheck detail: ${workerpalDockerPrecheck.detail}`);
|
|
4000
|
+
} else if (serverWasAlreadyHealthy) {
|
|
4001
|
+
console.error("[pushpals] A PushPals runtime is already serving this repo, but it does not currently have an idle WorkerPal available.");
|
|
4002
|
+
console.error("[pushpals] Wait for a worker to become idle or restart the runtime after fixing WorkerPal startup.");
|
|
4003
|
+
}
|
|
4004
|
+
process.exit(1);
|
|
4005
|
+
}
|
|
3496
4006
|
const saved = statePath ? readCliState(statePath) : {};
|
|
3497
4007
|
pushpalsLogPath = pushpalsLogPath || (typeof saved.pushpalsLogPath === "string" ? saved.pushpalsLogPath : undefined);
|
|
3498
4008
|
const preferredHubUrl = normalizeUrl(parsed.monitoringHubUrl ?? process.env.PUSHPALS_MONITOR_URL ?? saved.monitoringHubUrl ?? "");
|
|
@@ -3667,17 +4177,21 @@ if (import.meta.main) {
|
|
|
3667
4177
|
});
|
|
3668
4178
|
}
|
|
3669
4179
|
export {
|
|
4180
|
+
waitForWorkerpalCapacity,
|
|
3670
4181
|
startEmbeddedMonitoringHub,
|
|
3671
4182
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
3672
4183
|
resolveWindowsShellExecutableCandidatesForEnv,
|
|
3673
4184
|
resolveRuntimeGitExecutableCandidates,
|
|
4185
|
+
resolveRuntimeDockerExecutableCandidates,
|
|
3674
4186
|
resolvePreferredRuntimeReleaseTag,
|
|
3675
4187
|
resolveCommandPath,
|
|
3676
4188
|
resolveCliStatePath,
|
|
3677
4189
|
resolveCliLocalBuddyAutostart,
|
|
3678
4190
|
resolveBundledRuntimeAssetSource,
|
|
3679
4191
|
resolveBundledMonitoringHubRoot,
|
|
4192
|
+
prepareEmbeddedWorkerpalDockerImageIfNeeded,
|
|
3680
4193
|
prepareCliRuntime,
|
|
4194
|
+
precheckWorkerpalDockerAvailability,
|
|
3681
4195
|
precheckSourceControlManagerGitAvailability,
|
|
3682
4196
|
normalizeRepoPathForComparison,
|
|
3683
4197
|
normalizeCliInteractiveMessage,
|
|
@@ -3688,12 +4202,17 @@ export {
|
|
|
3688
4202
|
formatSessionEventLine,
|
|
3689
4203
|
extractRemoteBuddySessionConsumerHealth,
|
|
3690
4204
|
extractRemoteBuddyAutonomousEngineState,
|
|
4205
|
+
ensureWorkerpalDockerImageReady,
|
|
4206
|
+
downloadRuntimeAssetsFromSourceTag,
|
|
4207
|
+
copyTrackedRepoPath,
|
|
3691
4208
|
bundledMonitoringHubNeedsRefresh,
|
|
4209
|
+
buildWorkerpalSandboxPaths,
|
|
3692
4210
|
buildServiceStopCommand,
|
|
3693
4211
|
buildRuntimeServiceLogPaths,
|
|
3694
4212
|
buildOpenMonitoringHubCommand,
|
|
3695
4213
|
buildEmbeddedRuntimeEnv,
|
|
3696
4214
|
buildEmbeddedMonitoringHubHtml,
|
|
3697
4215
|
buildCliClearTargets,
|
|
3698
|
-
applyResolvedGitBinaryToRuntimeEnv
|
|
4216
|
+
applyResolvedGitBinaryToRuntimeEnv,
|
|
4217
|
+
applyResolvedDockerBinaryToRuntimeEnv
|
|
3699
4218
|
};
|