@pushpalsdev/cli 1.0.18 → 1.0.20

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.
Files changed (108) hide show
  1. package/dist/pushpals-cli.js +291 -44
  2. package/package.json +1 -1
  3. package/runtime/configs/backend.toml +1 -1
  4. package/runtime/configs/default.toml +1 -1
  5. package/runtime/sandbox/apps/workerpals/.python-version +1 -0
  6. package/runtime/sandbox/apps/workerpals/Dockerfile.sandbox +71 -0
  7. package/runtime/sandbox/apps/workerpals/package.json +25 -0
  8. package/runtime/sandbox/apps/workerpals/pyproject.toml +8 -0
  9. package/runtime/sandbox/apps/workerpals/src/backends/backend_config.ts +119 -0
  10. package/runtime/sandbox/apps/workerpals/src/backends/miniswe/miniswe_executor.py +2029 -0
  11. package/runtime/sandbox/apps/workerpals/src/backends/miniswe_backend.ts +48 -0
  12. package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/openai_codex_executor.py +1259 -0
  13. package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/test_openai_codex_runtime_config.py +110 -0
  14. package/runtime/sandbox/apps/workerpals/src/backends/openai_codex_backend.ts +67 -0
  15. package/runtime/sandbox/apps/workerpals/src/backends/openhands/openhands_executor.py +563 -0
  16. package/runtime/sandbox/apps/workerpals/src/backends/openhands_backend.ts +161 -0
  17. package/runtime/sandbox/apps/workerpals/src/backends/openhands_task_execute.ts +536 -0
  18. package/runtime/sandbox/apps/workerpals/src/backends/shared/executor_base.py +746 -0
  19. package/runtime/sandbox/apps/workerpals/src/backends/shared/test_settings_resolver.py +60 -0
  20. package/runtime/sandbox/apps/workerpals/src/backends/task_execute_registry.ts +21 -0
  21. package/runtime/sandbox/apps/workerpals/src/backends/types.ts +52 -0
  22. package/runtime/sandbox/apps/workerpals/src/common/execution_utils.ts +149 -0
  23. package/runtime/sandbox/apps/workerpals/src/common/executor_backend.ts +15 -0
  24. package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +210 -0
  25. package/runtime/sandbox/apps/workerpals/src/common/logger.ts +65 -0
  26. package/runtime/sandbox/apps/workerpals/src/common/types.ts +9 -0
  27. package/runtime/sandbox/apps/workerpals/src/common/worktree_cleanup.ts +66 -0
  28. package/runtime/sandbox/apps/workerpals/src/context_manager.ts +45 -0
  29. package/runtime/sandbox/apps/workerpals/src/docker_executor.ts +1842 -0
  30. package/runtime/sandbox/apps/workerpals/src/execute_job.ts +3063 -0
  31. package/runtime/sandbox/apps/workerpals/src/job_runner.ts +194 -0
  32. package/runtime/sandbox/apps/workerpals/src/shell_manager.ts +210 -0
  33. package/runtime/sandbox/apps/workerpals/src/timeout_policy.ts +24 -0
  34. package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +1436 -0
  35. package/runtime/sandbox/apps/workerpals/tsconfig.json +15 -0
  36. package/runtime/sandbox/apps/workerpals/uv.lock +2014 -0
  37. package/runtime/sandbox/bun.lock +2591 -0
  38. package/runtime/sandbox/configs/backend.toml +79 -0
  39. package/runtime/sandbox/configs/default.toml +260 -0
  40. package/runtime/sandbox/configs/dev.toml +2 -0
  41. package/runtime/sandbox/configs/local.example.toml +129 -0
  42. package/runtime/sandbox/package.json +65 -0
  43. package/runtime/sandbox/packages/protocol/README.md +168 -0
  44. package/runtime/sandbox/packages/protocol/package.json +37 -0
  45. package/runtime/sandbox/packages/protocol/scripts/copy-schemas.js +17 -0
  46. package/runtime/sandbox/packages/protocol/src/a2a/README.md +52 -0
  47. package/runtime/sandbox/packages/protocol/src/a2a/mapping.ts +55 -0
  48. package/runtime/sandbox/packages/protocol/src/index.browser.ts +25 -0
  49. package/runtime/sandbox/packages/protocol/src/index.ts +25 -0
  50. package/runtime/sandbox/packages/protocol/src/schemas/approvals.schema.json +6 -0
  51. package/runtime/sandbox/packages/protocol/src/schemas/envelope.schema.json +96 -0
  52. package/runtime/sandbox/packages/protocol/src/schemas/events.schema.json +679 -0
  53. package/runtime/sandbox/packages/protocol/src/schemas/http.schema.json +50 -0
  54. package/runtime/sandbox/packages/protocol/src/types.ts +267 -0
  55. package/runtime/sandbox/packages/protocol/src/validate.browser.ts +154 -0
  56. package/runtime/sandbox/packages/protocol/src/validate.ts +233 -0
  57. package/runtime/sandbox/packages/protocol/src/version.ts +1 -0
  58. package/runtime/sandbox/packages/protocol/tsconfig.json +20 -0
  59. package/runtime/sandbox/packages/shared/package.json +19 -0
  60. package/runtime/sandbox/packages/shared/src/autonomy_policy.ts +400 -0
  61. package/runtime/sandbox/packages/shared/src/client_preflight.ts +286 -0
  62. package/runtime/sandbox/packages/shared/src/communication.ts +313 -0
  63. package/runtime/sandbox/packages/shared/src/config.ts +2180 -0
  64. package/runtime/sandbox/packages/shared/src/config_template_parity.ts +70 -0
  65. package/runtime/sandbox/packages/shared/src/git_backend.ts +205 -0
  66. package/runtime/sandbox/packages/shared/src/index.ts +101 -0
  67. package/runtime/sandbox/packages/shared/src/local_network.ts +101 -0
  68. package/runtime/sandbox/packages/shared/src/localbuddy_runtime.ts +314 -0
  69. package/runtime/sandbox/packages/shared/src/prompts.ts +64 -0
  70. package/runtime/sandbox/packages/shared/src/repo.ts +134 -0
  71. package/runtime/sandbox/packages/shared/src/session_event_visibility.ts +25 -0
  72. package/runtime/sandbox/packages/shared/src/vision.ts +247 -0
  73. package/runtime/sandbox/packages/shared/tsconfig.json +16 -0
  74. package/runtime/sandbox/prompts/workerpals/codex_quality_critic_instruction_prompt.md +14 -0
  75. package/runtime/sandbox/prompts/workerpals/commit_message_prompt.md +36 -0
  76. package/runtime/sandbox/prompts/workerpals/commit_message_user_prompt.md +7 -0
  77. package/runtime/sandbox/prompts/workerpals/miniswe_broker_system_prompt.md +33 -0
  78. package/runtime/sandbox/prompts/workerpals/miniswe_broker_task_prompt.md +5 -0
  79. package/runtime/sandbox/prompts/workerpals/miniswe_completion_requirement.md +1 -0
  80. package/runtime/sandbox/prompts/workerpals/miniswe_context_compaction_retry_prompt.md +1 -0
  81. package/runtime/sandbox/prompts/workerpals/miniswe_explicit_targets_block.md +2 -0
  82. package/runtime/sandbox/prompts/workerpals/miniswe_recovery_guidance_base.md +4 -0
  83. package/runtime/sandbox/prompts/workerpals/miniswe_recovery_guidance_blocker_line.md +1 -0
  84. package/runtime/sandbox/prompts/workerpals/miniswe_strict_tool_use_guidance.md +6 -0
  85. package/runtime/sandbox/prompts/workerpals/miniswe_supplemental_guidance_section.md +2 -0
  86. package/runtime/sandbox/prompts/workerpals/miniswe_timeout_note.md +1 -0
  87. package/runtime/sandbox/prompts/workerpals/miniswe_toolcall_retry_guidance.md +1 -0
  88. package/runtime/sandbox/prompts/workerpals/openai_codex_default_system_prompt.md +4 -0
  89. package/runtime/sandbox/prompts/workerpals/openai_codex_instruction_wrapper.md +5 -0
  90. package/runtime/sandbox/prompts/workerpals/openai_codex_runtime_policy_appendix.md +5 -0
  91. package/runtime/sandbox/prompts/workerpals/openai_codex_supplemental_guidance_section.md +2 -0
  92. package/runtime/sandbox/prompts/workerpals/openai_codex_task_execute_system_prompt.md +12 -0
  93. package/runtime/sandbox/prompts/workerpals/openhands_minimal_security_policy.j2 +8 -0
  94. package/runtime/sandbox/prompts/workerpals/openhands_minimal_system_prompt.j2 +20 -0
  95. package/runtime/sandbox/prompts/workerpals/openhands_strict_tool_use_message.md +1 -0
  96. package/runtime/sandbox/prompts/workerpals/openhands_supplemental_guidance_message.md +2 -0
  97. package/runtime/sandbox/prompts/workerpals/openhands_task_execute_fallback_system_prompt.md +1 -0
  98. package/runtime/sandbox/prompts/workerpals/openhands_task_execute_system_prompt.md +21 -0
  99. package/runtime/sandbox/prompts/workerpals/openhands_task_user_prompt.md +6 -0
  100. package/runtime/sandbox/prompts/workerpals/openhands_timeout_note.md +1 -0
  101. package/runtime/sandbox/prompts/workerpals/pr_description.md +42 -0
  102. package/runtime/sandbox/prompts/workerpals/task_quality_critic_system_prompt.md +9 -0
  103. package/runtime/sandbox/prompts/workerpals/task_quality_critic_user_prompt.md +17 -0
  104. package/runtime/sandbox/prompts/workerpals/workerpals_system_prompt.md +115 -0
  105. package/runtime/sandbox/protocol/schemas/approvals.schema.json +6 -0
  106. package/runtime/sandbox/protocol/schemas/envelope.schema.json +96 -0
  107. package/runtime/sandbox/protocol/schemas/events.schema.json +679 -0
  108. package/runtime/sandbox/protocol/schemas/http.schema.json +50 -0
@@ -101,7 +101,6 @@ function normalizeLoopbackHttpUrl(value, fallbackPort) {
101
101
  // ../shared/src/config.ts
102
102
  var PROJECT_ROOT = resolve(import.meta.dir, "..", "..", "..");
103
103
  var DEFAULT_CONFIG_DIR = "configs";
104
- var LEGACY_CONFIG_DIR = "config";
105
104
  var TRUTHY = new Set(["1", "true", "yes", "on"]);
106
105
  var FALSY = new Set(["0", "false", "no", "off"]);
107
106
  var DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE = 8;
@@ -114,6 +113,7 @@ var DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS = 180000;
114
113
  var DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS = 45000;
115
114
  var DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS = 16000;
116
115
  var DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS = 8000;
116
+ var DEFAULT_WORKERPALS_EXECUTOR = "openai_codex";
117
117
  var DEFAULT_WORKERPALS_EXECUTOR_RESULT_PREFIX = "__PUSHPALS_OH_RESULT__ ";
118
118
  var DEFAULT_REMOTEBUDDY_MEMORY_MAX_RECALL_ITEMS = 12;
119
119
  var DEFAULT_REMOTEBUDDY_MEMORY_MAX_RECALL_CHARS = 2400;
@@ -155,6 +155,12 @@ function parseTomlFile(path) {
155
155
  return {};
156
156
  return parsed;
157
157
  }
158
+ function parseRequiredTomlFile(path) {
159
+ if (!existsSync(path)) {
160
+ throw new Error(`Missing required runtime config file: ${path}`);
161
+ }
162
+ return parseTomlFile(path);
163
+ }
158
164
  function isObject(value) {
159
165
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
160
166
  }
@@ -260,20 +266,7 @@ function resolveRuntimeConfigDir(projectRoot, configuredDir) {
260
266
  if (configuredDir && configuredDir.trim()) {
261
267
  return resolvePathFromRoot(projectRoot, configuredDir);
262
268
  }
263
- const canonicalDir = resolvePathFromRoot(projectRoot, DEFAULT_CONFIG_DIR);
264
- const legacyDir = resolvePathFromRoot(projectRoot, LEGACY_CONFIG_DIR);
265
- if (existsSync(join(canonicalDir, "default.toml")))
266
- return canonicalDir;
267
- if (existsSync(join(legacyDir, "default.toml")))
268
- return legacyDir;
269
- return canonicalDir;
270
- }
271
- function parseTomlWithLegacyFallback(primaryPath, fallbackPath) {
272
- if (existsSync(primaryPath))
273
- return parseTomlFile(primaryPath);
274
- if (fallbackPath && existsSync(fallbackPath))
275
- return parseTomlFile(fallbackPath);
276
- return {};
269
+ return resolvePathFromRoot(projectRoot, DEFAULT_CONFIG_DIR);
277
270
  }
278
271
  function normalizeBackend(value) {
279
272
  const text = value.trim().toLowerCase();
@@ -345,17 +338,15 @@ function loadPushPalsConfig(options = {}) {
345
338
  const projectRoot = resolve(projectRootOverride);
346
339
  const configDirOverride = firstNonEmpty(options.configDir, process.env.PUSHPALS_CONFIG_DIR_OVERRIDE, "");
347
340
  const configDir = resolveRuntimeConfigDir(projectRoot, configDirOverride);
348
- const legacyConfigDir = resolvePathFromRoot(projectRoot, LEGACY_CONFIG_DIR);
349
- const fallbackConfigDir = !configDirOverride && configDir !== legacyConfigDir ? legacyConfigDir : "";
350
341
  const cacheKey = `${projectRoot}::${configDir}::${process.env.PUSHPALS_PROFILE ?? ""}`;
351
342
  if (!options.reload && cachedConfig && cachedConfigKey === cacheKey) {
352
343
  return cachedConfig;
353
344
  }
354
- const defaultToml = parseTomlWithLegacyFallback(join(configDir, "default.toml"), fallbackConfigDir ? join(fallbackConfigDir, "default.toml") : undefined);
345
+ const defaultToml = parseRequiredTomlFile(join(configDir, "default.toml"));
355
346
  const preferredProfile = firstNonEmpty(process.env.PUSHPALS_PROFILE, asString(defaultToml.profile, "dev"), "dev");
356
- const profileToml = parseTomlWithLegacyFallback(join(configDir, `${preferredProfile}.toml`), fallbackConfigDir ? join(fallbackConfigDir, `${preferredProfile}.toml`) : undefined);
357
- const localExampleToml = parseTomlWithLegacyFallback(join(configDir, "local.example.toml"), fallbackConfigDir ? join(fallbackConfigDir, "local.example.toml") : undefined);
358
- const localToml = parseTomlWithLegacyFallback(join(configDir, "local.toml"), fallbackConfigDir ? join(fallbackConfigDir, "local.toml") : undefined);
347
+ const profileToml = parseTomlFile(join(configDir, `${preferredProfile}.toml`));
348
+ const localExampleToml = parseTomlFile(join(configDir, "local.example.toml"));
349
+ const localToml = parseTomlFile(join(configDir, "local.toml"));
359
350
  const merged = mergeDeep(mergeDeep(mergeDeep(defaultToml, profileToml), localExampleToml), localToml);
360
351
  const profile = firstNonEmpty(process.env.PUSHPALS_PROFILE, asString(merged.profile, preferredProfile), preferredProfile);
361
352
  const sessionId = firstNonEmpty(process.env.PUSHPALS_SESSION_ID, asString(merged.session_id, "dev"), "dev");
@@ -462,7 +453,7 @@ function loadPushPalsConfig(options = {}) {
462
453
  const workerOpenHandsNode = getObject(workerNode, "openhands");
463
454
  const workerPollMs = Math.max(200, asInt(parseIntEnv("WORKERPALS_POLL_MS") ?? workerNode.poll_ms, 2000));
464
455
  const workerHeartbeatMs = Math.max(200, asInt(parseIntEnv("WORKERPALS_HEARTBEAT_MS") ?? workerNode.heartbeat_ms, 5000));
465
- const workerExecutor = firstNonEmpty(process.env.WORKERPALS_EXECUTOR, asString(workerNode.executor, "openhands"), "openhands").toLowerCase();
456
+ const workerExecutor = firstNonEmpty(process.env.WORKERPALS_EXECUTOR, asString(workerNode.executor, DEFAULT_WORKERPALS_EXECUTOR), DEFAULT_WORKERPALS_EXECUTOR).toLowerCase();
466
457
  const workerOpenHandsPython = firstNonEmpty(process.env.WORKERPALS_OPENHANDS_PYTHON, asString(workerNode.openhands_python, "python"), "python");
467
458
  const workerOpenHandsTimeoutMs = Math.max(1e4, asInt(parseIntEnv("WORKERPALS_OPENHANDS_TIMEOUT_MS") ?? workerNode.openhands_timeout_ms, 1800000));
468
459
  const workerMiniswePython = firstNonEmpty(process.env.WORKERPALS_MINISWE_PYTHON, asString(workerNode.miniswe_python, "python"), "python");
@@ -964,18 +955,10 @@ function resolveClientConfigDir(projectRoot, runtimeRoot, explicitConfigDir) {
964
955
  if (runtimeHasConfigDir(runtimeRoot, "configs")) {
965
956
  return runtimeCanonical;
966
957
  }
967
- const runtimeLegacy = resolve2(runtimeRoot, "config");
968
- if (runtimeHasConfigDir(runtimeRoot, "config")) {
969
- return runtimeLegacy;
970
- }
971
958
  const projectCanonical = resolve2(projectRoot, "configs");
972
959
  if (runtimeHasConfigDir(projectRoot, "configs")) {
973
960
  return projectCanonical;
974
961
  }
975
- const projectLegacy = resolve2(projectRoot, "config");
976
- if (runtimeHasConfigDir(projectRoot, "config")) {
977
- return projectLegacy;
978
- }
979
962
  return runtimeCanonical;
980
963
  }
981
964
  function toDisplayPath(currentRoot, pathValue) {
@@ -1025,8 +1008,7 @@ function evaluateClientRuntimePreflight(options) {
1025
1008
  });
1026
1009
  }
1027
1010
  const localTomlPath = resolve2(runtimeRoot, "configs", "local.toml");
1028
- const legacyLocalTomlPath = resolve2(runtimeRoot, "config", "local.toml");
1029
- if (!existsSync2(localTomlPath) && !existsSync2(legacyLocalTomlPath)) {
1011
+ if (!existsSync2(localTomlPath)) {
1030
1012
  const localExamplePath = resolve2(runtimeRoot, "configs", "local.example.toml");
1031
1013
  issues.push({
1032
1014
  code: "missing_local_toml",
@@ -1472,6 +1454,130 @@ function buildRuntimeAssetSource(root, protocolSchemasDir) {
1472
1454
  protocolSchemasDir
1473
1455
  };
1474
1456
  }
1457
+ function buildWorkerpalSandboxPaths(runtimeRoot) {
1458
+ const root = join2(runtimeRoot, "sandbox");
1459
+ return {
1460
+ root,
1461
+ dockerfilePath: join2(root, "apps", "workerpals", "Dockerfile.sandbox"),
1462
+ packageJsonPath: join2(root, "package.json"),
1463
+ workerpalsDir: join2(root, "apps", "workerpals"),
1464
+ sharedDir: join2(root, "packages", "shared"),
1465
+ protocolDir: join2(root, "packages", "protocol"),
1466
+ configsDir: join2(root, "configs"),
1467
+ workerpalsPromptsDir: join2(root, "prompts", "workerpals"),
1468
+ protocolSchemasDir: join2(root, "protocol", "schemas")
1469
+ };
1470
+ }
1471
+ function normalizeGitTrackedPath(pathValue) {
1472
+ return String(pathValue ?? "").trim().replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
1473
+ }
1474
+ function listTrackedRepoFilesForPath(repoRoot, sourcePath) {
1475
+ const normalizedSource = normalizeGitTrackedPath(sourcePath);
1476
+ if (!normalizedSource)
1477
+ return [];
1478
+ const proc = Bun.spawnSync(["git", "ls-files", "-z", "--", normalizedSource], {
1479
+ cwd: repoRoot,
1480
+ stdout: "pipe",
1481
+ stderr: "pipe",
1482
+ env: {
1483
+ ...process.env,
1484
+ GIT_TERMINAL_PROMPT: "0",
1485
+ GCM_INTERACTIVE: "Never"
1486
+ }
1487
+ });
1488
+ if (proc.exitCode !== 0) {
1489
+ const stderr = Buffer.from(proc.stderr ?? []).toString("utf8").trim();
1490
+ throw new Error(`git ls-files failed for ${normalizedSource}${stderr ? `: ${stderr}` : ""}`);
1491
+ }
1492
+ return Buffer.from(proc.stdout ?? []).toString("utf8").split("\x00").map(normalizeGitTrackedPath).filter(Boolean);
1493
+ }
1494
+ function copyTrackedRepoPath(repoRoot, sourcePath, destinationPath, force = true) {
1495
+ const normalizedSource = normalizeGitTrackedPath(sourcePath);
1496
+ if (!normalizedSource) {
1497
+ throw new Error("sourcePath is required");
1498
+ }
1499
+ const absoluteSource = resolve4(repoRoot, normalizedSource);
1500
+ if (!existsSync4(absoluteSource)) {
1501
+ throw new Error(`tracked repo source is missing: ${absoluteSource}`);
1502
+ }
1503
+ const trackedFiles = listTrackedRepoFilesForPath(repoRoot, normalizedSource);
1504
+ const sourceStat = lstatSync(absoluteSource);
1505
+ if (!sourceStat.isDirectory()) {
1506
+ if (!trackedFiles.includes(normalizedSource)) {
1507
+ throw new Error(`tracked repo file is not tracked by git: ${normalizedSource}`);
1508
+ }
1509
+ mkdirSync(dirname(destinationPath), { recursive: true });
1510
+ cpSync(absoluteSource, destinationPath, {
1511
+ recursive: false,
1512
+ force,
1513
+ errorOnExist: false
1514
+ });
1515
+ return;
1516
+ }
1517
+ if (trackedFiles.length === 0) {
1518
+ throw new Error(`tracked repo directory has no tracked files: ${normalizedSource}`);
1519
+ }
1520
+ for (const trackedFile of trackedFiles) {
1521
+ const relativePath = trackedFile === normalizedSource ? basename(trackedFile) : trackedFile.slice(normalizedSource.length + 1);
1522
+ const sourceFile = resolve4(repoRoot, trackedFile);
1523
+ const targetFile = join2(destinationPath, relativePath);
1524
+ mkdirSync(dirname(targetFile), { recursive: true });
1525
+ cpSync(sourceFile, targetFile, {
1526
+ recursive: false,
1527
+ force,
1528
+ errorOnExist: false
1529
+ });
1530
+ }
1531
+ }
1532
+ function isCompleteWorkerpalSandboxRoot(root) {
1533
+ 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"));
1534
+ }
1535
+ function populateWorkerpalSandboxRuntimeAssets(runtimeRoot, force) {
1536
+ const sandbox = buildWorkerpalSandboxPaths(runtimeRoot);
1537
+ cpSync(join2(runtimeRoot, "configs"), sandbox.configsDir, {
1538
+ recursive: true,
1539
+ force,
1540
+ errorOnExist: false
1541
+ });
1542
+ cpSync(join2(runtimeRoot, "prompts", "workerpals"), sandbox.workerpalsPromptsDir, {
1543
+ recursive: true,
1544
+ force,
1545
+ errorOnExist: false
1546
+ });
1547
+ cpSync(join2(runtimeRoot, "protocol", "schemas"), sandbox.protocolSchemasDir, {
1548
+ recursive: true,
1549
+ force,
1550
+ errorOnExist: false
1551
+ });
1552
+ }
1553
+ function copySourceCheckoutWorkerpalSandboxBuildContext(sourceRoot, runtimeRoot, force) {
1554
+ const sandbox = buildWorkerpalSandboxPaths(runtimeRoot);
1555
+ const copyPairs = [
1556
+ ["package.json", sandbox.packageJsonPath],
1557
+ ["apps/workerpals", sandbox.workerpalsDir],
1558
+ ["packages/shared", sandbox.sharedDir],
1559
+ ["packages/protocol", sandbox.protocolDir]
1560
+ ];
1561
+ for (const [fromPath, toPath] of copyPairs) {
1562
+ copyTrackedRepoPath(sourceRoot, fromPath, toPath, force);
1563
+ }
1564
+ if (existsSync4(join2(sourceRoot, "bun.lock"))) {
1565
+ copyTrackedRepoPath(sourceRoot, "bun.lock", join2(sandbox.root, "bun.lock"), force);
1566
+ }
1567
+ populateWorkerpalSandboxRuntimeAssets(runtimeRoot, force);
1568
+ }
1569
+ function copyWorkerpalSandboxBuildContext(source, runtimeRoot, force) {
1570
+ const packagedSandboxRoot = join2(source.root, "sandbox");
1571
+ if (isCompleteWorkerpalSandboxRoot(packagedSandboxRoot)) {
1572
+ cpSync(packagedSandboxRoot, join2(runtimeRoot, "sandbox"), {
1573
+ recursive: true,
1574
+ force,
1575
+ errorOnExist: false
1576
+ });
1577
+ return;
1578
+ }
1579
+ copySourceCheckoutWorkerpalSandboxBuildContext(source.root, runtimeRoot, force);
1580
+ }
1475
1581
  function isCompleteRuntimeAssetSource(source) {
1476
1582
  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"));
1477
1583
  }
@@ -1658,6 +1764,7 @@ function copyRuntimeAssetBundle(source, runtimeRoot, force) {
1658
1764
  force,
1659
1765
  errorOnExist: false
1660
1766
  });
1767
+ copyWorkerpalSandboxBuildContext(source, runtimeRoot, force);
1661
1768
  }
1662
1769
  function copyBundledRuntimeAssets(runtimeRoot, force = true) {
1663
1770
  const bundledSource = resolveBundledRuntimeAssetSource();
@@ -1693,7 +1800,7 @@ async function downloadRuntimeAssetsFromSourceTag(runtimeRoot, tag) {
1693
1800
  throw new Error(`Failed to fetch runtime source tree for ${tag} (HTTP ${treeResponse.status})`);
1694
1801
  }
1695
1802
  const treePayload = await treeResponse.json();
1696
- 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/"));
1803
+ 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/"));
1697
1804
  if (paths.length === 0) {
1698
1805
  throw new Error(`Runtime source tree for ${tag} did not include prompts/config assets`);
1699
1806
  }
@@ -1701,10 +1808,16 @@ async function downloadRuntimeAssetsFromSourceTag(runtimeRoot, tag) {
1701
1808
  for (const pathValue of sorted) {
1702
1809
  const rawUrl = `https://raw.githubusercontent.com/${GITHUB_OWNER}/${GITHUB_REPO}/${encodeURIComponent(tag)}/${pathValue}`;
1703
1810
  const body = await fetchTextFromUrl(rawUrl, 20000);
1704
- const outPath = pathValue.startsWith("packages/protocol/src/schemas/") ? join2(runtimeRoot, "protocol", "schemas", pathValue.slice("packages/protocol/src/schemas/".length)) : join2(runtimeRoot, pathValue);
1811
+ 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);
1705
1812
  mkdirSync(dirname(outPath), { recursive: true });
1706
1813
  writeFileSync(outPath, body, "utf8");
1814
+ if (pathValue.startsWith("packages/protocol/src/schemas/")) {
1815
+ const runtimeSchemaPath = join2(runtimeRoot, "protocol", "schemas", pathValue.slice("packages/protocol/src/schemas/".length));
1816
+ mkdirSync(dirname(runtimeSchemaPath), { recursive: true });
1817
+ writeFileSync(runtimeSchemaPath, body, "utf8");
1818
+ }
1707
1819
  }
1820
+ populateWorkerpalSandboxRuntimeAssets(runtimeRoot, true);
1708
1821
  }
1709
1822
  async function ensureRuntimeAssets(runtimeRoot, runtimeTag) {
1710
1823
  console.log(`[pushpals] Preparing embedded runtime assets for ${runtimeTag}...`);
@@ -1712,12 +1825,12 @@ async function ensureRuntimeAssets(runtimeRoot, runtimeTag) {
1712
1825
  const currentTag = existsSync4(markerPath) ? readFileSync4(markerPath, "utf8").trim() : "";
1713
1826
  const protocolSchemasDir = join2(runtimeRoot, "protocol", "schemas");
1714
1827
  const hasProtocolSchemas = existsSync4(join2(protocolSchemasDir, "envelope.schema.json")) && existsSync4(join2(protocolSchemasDir, "events.schema.json"));
1715
- const hasAssets = existsSync4(join2(runtimeRoot, ".env.example")) && existsSync4(join2(runtimeRoot, "vision.example.md")) && existsSync4(join2(runtimeRoot, "configs", "default.toml")) && existsSync4(join2(runtimeRoot, "prompts")) && hasProtocolSchemas;
1828
+ 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"));
1716
1829
  if (!hasAssets || currentTag !== runtimeTag) {
1717
1830
  console.log(`[pushpals] Embedded runtime assets ${hasAssets ? "are stale" : "are missing"}; refreshing bundle...`);
1718
1831
  copyBundledRuntimeAssets(runtimeRoot);
1719
1832
  const hasProtocolSchemasAfterCopy = existsSync4(join2(protocolSchemasDir, "envelope.schema.json")) && existsSync4(join2(protocolSchemasDir, "events.schema.json"));
1720
- const hasAssetsAfterCopy = existsSync4(join2(runtimeRoot, ".env.example")) && existsSync4(join2(runtimeRoot, "vision.example.md")) && existsSync4(join2(runtimeRoot, "configs", "default.toml")) && existsSync4(join2(runtimeRoot, "prompts")) && hasProtocolSchemasAfterCopy;
1833
+ 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"));
1721
1834
  if (!hasAssetsAfterCopy) {
1722
1835
  console.log("[pushpals] Bundled runtime assets are incomplete; falling back to release source downloads...");
1723
1836
  await downloadRuntimeAssetsFromSourceTag(runtimeRoot, runtimeTag);
@@ -1787,7 +1900,9 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
1787
1900
  PUSHPALS_PROJECT_ROOT_OVERRIDE: opts.repoRoot,
1788
1901
  ...useRuntimeConfig ? {
1789
1902
  PUSHPALS_CONFIG_DIR_OVERRIDE: join2(opts.runtimeRoot, "configs"),
1790
- PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.runtimeRoot
1903
+ PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.runtimeRoot,
1904
+ PUSHPALS_WORKERPALS_SANDBOX_ROOT: join2(opts.runtimeRoot, "sandbox"),
1905
+ ...typeof opts.runtimeTag === "string" && opts.runtimeTag.trim() ? { PUSHPALS_RUNTIME_TAG: opts.runtimeTag.trim() } : {}
1791
1906
  } : {
1792
1907
  PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.repoRoot
1793
1908
  },
@@ -2263,6 +2378,116 @@ async function resolveWorkerpalDockerProbe(cwd, env, platform = process.platform
2263
2378
  detail: failures.join(" | ") || "docker"
2264
2379
  };
2265
2380
  }
2381
+ var WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL = "pushpals.runtime_tag";
2382
+ var WORKERPAL_SANDBOX_COMPONENT_LABEL = "pushpals.component=workerpals-sandbox";
2383
+ function resolveConfiguredDockerExecutable(env, platform = process.platform) {
2384
+ const configured = String(env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? env.PUSHPALS_DOCKER_BIN ?? (platform === "win32" ? "docker.exe" : "docker")).trim();
2385
+ return configured || (platform === "win32" ? "docker.exe" : "docker");
2386
+ }
2387
+ async function inspectDockerImageRuntimeTag(dockerExecutable, imageName, cwd, env) {
2388
+ const inspect = await runCommandWithEnv([
2389
+ dockerExecutable,
2390
+ "image",
2391
+ "inspect",
2392
+ "--format",
2393
+ `{{ index .Config.Labels "${WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL}" }}`,
2394
+ imageName
2395
+ ], cwd, env);
2396
+ if (!inspect.ok)
2397
+ return "";
2398
+ const value = inspect.stdout.trim();
2399
+ return value === "<no value>" ? "" : value;
2400
+ }
2401
+ async function ensureWorkerpalDockerImageReady(opts) {
2402
+ const runtimeTag = String(opts.runtimeTag ?? "").trim();
2403
+ if (!runtimeTag) {
2404
+ return {
2405
+ ok: false,
2406
+ detail: "embedded runtime tag is required to prepare the WorkerPal sandbox image"
2407
+ };
2408
+ }
2409
+ await (opts.ensureRuntimeAssetsFn ?? ensureRuntimeAssets)(opts.runtimeRoot, runtimeTag);
2410
+ const sandbox = buildWorkerpalSandboxPaths(opts.runtimeRoot);
2411
+ if (!isCompleteWorkerpalSandboxRoot(sandbox.root)) {
2412
+ return {
2413
+ ok: false,
2414
+ detail: `embedded WorkerPal sandbox assets are incomplete at ${sandbox.root}`
2415
+ };
2416
+ }
2417
+ const dockerExecutable = resolveConfiguredDockerExecutable(opts.env, opts.platform ?? process.platform);
2418
+ const inspectImageRuntimeTagFn = opts.inspectImageRuntimeTagFn ?? inspectDockerImageRuntimeTag;
2419
+ const runCommandWithEnvFn = opts.runCommandWithEnvFn ?? runCommandWithEnv;
2420
+ const existingRuntimeTag = await inspectImageRuntimeTagFn(dockerExecutable, opts.dockerImage, sandbox.root, opts.env);
2421
+ if (existingRuntimeTag === runtimeTag) {
2422
+ return {
2423
+ ok: true,
2424
+ detail: `WorkerPal sandbox image is ready locally (${opts.dockerImage}, runtimeTag=${runtimeTag})`
2425
+ };
2426
+ }
2427
+ 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...`);
2428
+ const build = await runCommandWithEnvFn([
2429
+ dockerExecutable,
2430
+ "build",
2431
+ "-f",
2432
+ "apps/workerpals/Dockerfile.sandbox",
2433
+ "--label",
2434
+ `${WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL}=${runtimeTag}`,
2435
+ "--label",
2436
+ WORKERPAL_SANDBOX_COMPONENT_LABEL,
2437
+ "-t",
2438
+ opts.dockerImage,
2439
+ "."
2440
+ ], sandbox.root, opts.env);
2441
+ if (!build.ok) {
2442
+ const detail = build.stderr || build.stdout || `docker build exited ${build.exitCode}`;
2443
+ return {
2444
+ ok: false,
2445
+ detail: `failed to build local WorkerPal sandbox image ${opts.dockerImage}: ${detail}`
2446
+ };
2447
+ }
2448
+ return {
2449
+ ok: true,
2450
+ detail: `built local WorkerPal sandbox image ${opts.dockerImage} for runtimeTag=${runtimeTag}`
2451
+ };
2452
+ }
2453
+ async function prepareEmbeddedWorkerpalDockerImageIfNeeded(opts) {
2454
+ if (!opts.preparedRuntime.preflightUsesEmbeddedRuntime) {
2455
+ return {
2456
+ status: "skipped",
2457
+ detail: "repo is using source-checkout runtime assets",
2458
+ runtimeTag: ""
2459
+ };
2460
+ }
2461
+ if (!opts.config.remotebuddy.autoSpawnWorkerpals || !opts.config.remotebuddy.workerpalDocker || !opts.config.remotebuddy.workerpalRequireDocker) {
2462
+ return {
2463
+ status: "skipped",
2464
+ detail: "embedded docker-backed WorkerPal auto-spawn is not required",
2465
+ runtimeTag: ""
2466
+ };
2467
+ }
2468
+ if (opts.dockerPrecheck.status === "failed") {
2469
+ return {
2470
+ status: "failed",
2471
+ detail: opts.dockerPrecheck.detail,
2472
+ runtimeTag: ""
2473
+ };
2474
+ }
2475
+ const runtimeTag = opts.preparedRuntime.runtimeTag || String(opts.runtimeTagHint ?? "").trim() || await (opts.resolveRuntimeReleaseTagFn ?? resolveRuntimeReleaseTag)(opts.runtimeTagHint);
2476
+ if (!runtimeTag) {
2477
+ return {
2478
+ status: "failed",
2479
+ detail: "embedded runtime tag is required to prepare the WorkerPal sandbox image",
2480
+ runtimeTag: ""
2481
+ };
2482
+ }
2483
+ const ensureResult = await (opts.ensureWorkerpalDockerImageReadyFn ?? ensureWorkerpalDockerImageReady)({
2484
+ runtimeRoot: opts.preparedRuntime.runtimeRoot,
2485
+ runtimeTag,
2486
+ dockerImage: opts.config.remotebuddy.workerpalImage ?? opts.config.workerpals.dockerImage,
2487
+ env: opts.dockerPrecheck.env
2488
+ });
2489
+ return ensureResult.ok ? { status: "ok", detail: ensureResult.detail, runtimeTag } : { status: "failed", detail: ensureResult.detail, runtimeTag };
2490
+ }
2266
2491
  async function precheckSourceControlManagerGitAvailability(opts) {
2267
2492
  const platform = opts.platform ?? process.platform;
2268
2493
  const env = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
@@ -2824,7 +3049,8 @@ async function autoStartRuntimeServices(opts) {
2824
3049
  repoRoot: opts.repoRoot,
2825
3050
  runtimeRoot,
2826
3051
  useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime,
2827
- sessionId: opts.sessionId
3052
+ sessionId: opts.sessionId,
3053
+ runtimeTag
2828
3054
  });
2829
3055
  runtimeEnv.PUSHPALS_WORKERPALS_BIN = runtimeBinaries.workerpals;
2830
3056
  const preconfiguredRuntimeGitBinary = runtimeEnv.PUSHPALS_GIT_BIN_ABSOLUTE ?? runtimeEnv.PUSHPALS_GIT_BIN;
@@ -3642,6 +3868,7 @@ async function main() {
3642
3868
  };
3643
3869
  let autoStartedServices = [];
3644
3870
  let pushpalsLogPath;
3871
+ let resolvedRuntimeTagForAutoStart = preparedRuntime.runtimeTag || parsed.runtimeTag || "";
3645
3872
  const stopAutoStartedServices = () => {
3646
3873
  if (autoStartedServices.length === 0)
3647
3874
  return;
@@ -3650,17 +3877,32 @@ async function main() {
3650
3877
  };
3651
3878
  let serverHealthy = await probeServer(serverUrl);
3652
3879
  const serverWasAlreadyHealthy = serverHealthy;
3880
+ if (!serverHealthy && workerpalDockerPrecheck.status === "failed") {
3881
+ console.error(`[pushpals] Precheck failed: Docker-backed WorkerPal auto-spawn is required but Docker is unavailable (${workerpalDockerPrecheck.detail}).`);
3882
+ console.error("[pushpals] Precheck failed: start Docker Desktop or the Docker daemon, then retry pushpals.");
3883
+ process.exit(1);
3884
+ }
3885
+ if (workerpalDockerPrecheck.status !== "failed") {
3886
+ const workerpalImagePrecheck = await prepareEmbeddedWorkerpalDockerImageIfNeeded({
3887
+ preparedRuntime,
3888
+ config,
3889
+ dockerPrecheck: workerpalDockerPrecheck,
3890
+ runtimeTagHint: resolvedRuntimeTagForAutoStart || parsed.runtimeTag
3891
+ });
3892
+ if (workerpalImagePrecheck.status === "failed") {
3893
+ console.error(`[pushpals] Precheck failed: ${workerpalImagePrecheck.detail}.`);
3894
+ process.exit(1);
3895
+ }
3896
+ if (workerpalImagePrecheck.runtimeTag) {
3897
+ resolvedRuntimeTagForAutoStart = workerpalImagePrecheck.runtimeTag;
3898
+ }
3899
+ }
3653
3900
  let remoteBuddyConsumerHealth = {
3654
3901
  ok: false,
3655
3902
  detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
3656
3903
  };
3657
3904
  if (!serverHealthy) {
3658
3905
  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
- }
3664
3906
  try {
3665
3907
  const startedRuntime = await autoStartRuntimeServices({
3666
3908
  repoRoot,
@@ -3670,7 +3912,7 @@ async function main() {
3670
3912
  sourceControlManagerPort: config.sourceControlManager.port,
3671
3913
  sourceControlManagerRemote: config.sourceControlManager.remote,
3672
3914
  preparedRuntime,
3673
- requestedRuntimeTag: parsed.runtimeTag,
3915
+ requestedRuntimeTag: resolvedRuntimeTagForAutoStart || parsed.runtimeTag,
3674
3916
  startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled)),
3675
3917
  baseEnv: workerpalDockerPrecheck.env
3676
3918
  });
@@ -3929,6 +4171,7 @@ export {
3929
4171
  resolveCliLocalBuddyAutostart,
3930
4172
  resolveBundledRuntimeAssetSource,
3931
4173
  resolveBundledMonitoringHubRoot,
4174
+ prepareEmbeddedWorkerpalDockerImageIfNeeded,
3932
4175
  prepareCliRuntime,
3933
4176
  precheckWorkerpalDockerAvailability,
3934
4177
  precheckSourceControlManagerGitAvailability,
@@ -3941,7 +4184,11 @@ export {
3941
4184
  formatSessionEventLine,
3942
4185
  extractRemoteBuddySessionConsumerHealth,
3943
4186
  extractRemoteBuddyAutonomousEngineState,
4187
+ ensureWorkerpalDockerImageReady,
4188
+ downloadRuntimeAssetsFromSourceTag,
4189
+ copyTrackedRepoPath,
3944
4190
  bundledMonitoringHubNeedsRefresh,
4191
+ buildWorkerpalSandboxPaths,
3945
4192
  buildServiceStopCommand,
3946
4193
  buildRuntimeServiceLogPaths,
3947
4194
  buildOpenMonitoringHubCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,4 +1,4 @@
1
- default_backend = "miniswe"
1
+ default_backend = "openai_codex"
2
2
 
3
3
  [env]
4
4
  shared_passthrough = [
@@ -118,7 +118,7 @@ max_payload_bytes = 262144
118
118
  [workerpals]
119
119
  poll_ms = 2000
120
120
  heartbeat_ms = 5000
121
- executor = "miniswe"
121
+ executor = "openai_codex"
122
122
  openhands_python = "python"
123
123
  openhands_timeout_ms = 1800000
124
124
  miniswe_python = "python"
@@ -0,0 +1,71 @@
1
+ # PushPals Worker Sandbox Dockerfile
2
+ # This image provides an isolated environment for job execution
3
+ # The entire monorepo is available in the container for job execution
4
+
5
+ FROM oven/bun:1-debian AS base
6
+
7
+ # Install git and other essential tools
8
+ RUN apt-get update && apt-get install -y --no-install-recommends \
9
+ git \
10
+ curl \
11
+ ca-certificates \
12
+ openssh-client \
13
+ grep \
14
+ jq \
15
+ ripgrep \
16
+ fd-find \
17
+ less \
18
+ unzip \
19
+ zip \
20
+ procps \
21
+ dnsutils \
22
+ iputils-ping \
23
+ python3 \
24
+ python3-venv \
25
+ python3-pip \
26
+ && rm -rf /var/lib/apt/lists/*
27
+
28
+ # Worker runtime in one shared venv (uv default layout).
29
+ # Keep the venv at /workspace/.venv so all backends use the same Python runtime.
30
+ RUN mkdir -p /workspace \
31
+ && python3 -m venv /workspace/.venv \
32
+ && /workspace/.venv/bin/pip install --no-cache-dir --upgrade pip \
33
+ && /workspace/.venv/bin/pip install --no-cache-dir \
34
+ openhands-sdk \
35
+ openhands-agent-server \
36
+ openhands-workspace \
37
+ openhands-tools \
38
+ mini-swe-agent \
39
+ playwright \
40
+ && bun add -g @openai/codex \
41
+ && /workspace/.venv/bin/playwright install --with-deps chromium
42
+
43
+ ENV PATH="/workspace/.venv/bin:/root/.bun/bin:${PATH}"
44
+ ENV WORKERPALS_OPENHANDS_PYTHON="/workspace/.venv/bin/python"
45
+ ENV WORKERPALS_OPENHANDS_WORKSPACE_PYTHON="/workspace/.venv/bin/python"
46
+ ENV WORKERPALS_MINISWE_PYTHON="/workspace/.venv/bin/python"
47
+ ENV PUSHPALS_OPENAI_CODEX_PYTHON="/workspace/.venv/bin/python"
48
+
49
+ # Configure git for the worker
50
+ RUN git config --global user.name "PushPals Worker" \
51
+ && git config --global user.email "worker@pushpals.local" \
52
+ && git config --global safe.directory '*' \
53
+ && git config --global core.autocrlf input
54
+
55
+ # Set up the full monorepo structure
56
+ WORKDIR /workspace
57
+
58
+ # Copy entire repository (build context should be repo root)
59
+ COPY . .
60
+
61
+ # Install workspace dependencies (including devDeps needed for protocol build)
62
+ RUN bun install
63
+
64
+ # Build protocol package (needed by worker)
65
+ RUN cd packages/protocol && bun run build
66
+
67
+ # Set working directory for job execution
68
+ WORKDIR /workspace
69
+
70
+ # Default entrypoint runs the job runner with base64-encoded job spec
71
+ ENTRYPOINT ["bun", "run", "/workspace/apps/workerpals/src/job_runner.ts"]
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "workerpals",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bun --watch --no-clear-screen src/workerpals_main.ts",
7
+ "start": "bun run src/workerpals_main.ts",
8
+ "build:sandbox": "docker build -f Dockerfile.sandbox -t pushpals-worker-sandbox:latest ../..",
9
+ "docker:build": "docker build -f Dockerfile.sandbox -t pushpals-worker-sandbox:latest ../..",
10
+ "docker:build:local": "docker build -f Dockerfile.sandbox -t pushpals-worker-sandbox:local ../..",
11
+ "docker:run": "docker run --rm -it pushpals-worker-sandbox:latest",
12
+ "dev:docker": "bun run src/workerpals_main.ts --docker",
13
+ "workerpals:docker": "bun run src/workerpals_main.ts --docker",
14
+ "workerpals:docker:local": "bun run src/workerpals_main.ts --docker --docker-image pushpals-worker-sandbox:local"
15
+ },
16
+ "dependencies": {
17
+ "protocol": "workspace:*",
18
+ "shared": "workspace:*"
19
+ },
20
+ "devDependencies": {
21
+ "typescript": "~5.9.2",
22
+ "@types/bun": "latest"
23
+ },
24
+ "private": true
25
+ }
@@ -0,0 +1,8 @@
1
+ [project]
2
+ name = "workerpals"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ requires-python = ">=3.12"
6
+ dependencies = [
7
+ "mini-swe-agent>=2.1.0",
8
+ ]