@pushpalsdev/cli 1.0.27 → 1.0.29

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.
@@ -1946,8 +1946,14 @@ function cleanupLegacyRuntimeBinaryLayouts(runtimeRoot, platformKey, activeBinDi
1946
1946
  function buildEmbeddedRuntimeEnv(baseEnv, opts) {
1947
1947
  const env = normalizeChildProcessEnv(baseEnv);
1948
1948
  const useRuntimeConfig = opts.useRuntimeConfig !== false;
1949
+ const inherited = { ...env };
1950
+ if (!useRuntimeConfig) {
1951
+ delete inherited.PUSHPALS_CONFIG_DIR_OVERRIDE;
1952
+ delete inherited.PUSHPALS_WORKERPALS_SANDBOX_ROOT;
1953
+ delete inherited.PUSHPALS_RUNTIME_TAG;
1954
+ }
1949
1955
  return {
1950
- ...env,
1956
+ ...inherited,
1951
1957
  PUSHPALS_REPO_ROOT_OVERRIDE: opts.repoRoot,
1952
1958
  PUSHPALS_PROJECT_ROOT_OVERRIDE: opts.repoRoot,
1953
1959
  ...useRuntimeConfig ? {
@@ -2484,6 +2490,36 @@ async function resolveWorkerpalDockerProbe(cwd, env, platform = process.platform
2484
2490
  var WORKERPAL_SANDBOX_RUNTIME_TAG_LABEL = "pushpals.runtime_tag";
2485
2491
  var WORKERPAL_SANDBOX_COMPONENT_LABEL = "pushpals.component=workerpals-sandbox";
2486
2492
  var WORKERPAL_WARM_COMPONENT_LABEL = "pushpals.component=workerpals-warm";
2493
+ var SOURCE_CONTROL_MANAGER_TEMP_BRANCH_PREFIX = "_source_control_manager/";
2494
+ function normalizeFsPathForComparison(value) {
2495
+ const resolved = resolve4(String(value ?? "").trim()).replace(/\\/g, "/").replace(/\/+$/, "");
2496
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
2497
+ }
2498
+ function parseGitWorktreeListPorcelain(stdout) {
2499
+ const entries = [];
2500
+ const blocks = String(stdout ?? "").split(/\r?\n\r?\n/g).map((block) => block.trim()).filter(Boolean);
2501
+ for (const block of blocks) {
2502
+ const lines = block.split(/\r?\n/g);
2503
+ const pathLine = lines.find((line) => line.startsWith("worktree "));
2504
+ if (!pathLine)
2505
+ continue;
2506
+ const branchLine = lines.find((line) => line.startsWith("branch "));
2507
+ entries.push({
2508
+ path: pathLine.slice("worktree ".length).trim(),
2509
+ branch: branchLine ? branchLine.slice("branch ".length).trim() : null,
2510
+ detached: lines.includes("detached")
2511
+ });
2512
+ }
2513
+ return entries;
2514
+ }
2515
+ function isWorkerpalEphemeralWorktreePath(repoRoot, worktreePath) {
2516
+ const expectedPrefix = `${normalizeFsPathForComparison(join2(repoRoot, ".worktrees"))}/`;
2517
+ const normalizedPath = normalizeFsPathForComparison(worktreePath);
2518
+ if (!normalizedPath.startsWith(expectedPrefix))
2519
+ return false;
2520
+ const leaf = basename(normalizedPath);
2521
+ return /^(job|selfcheck)-.*-workerpal-[a-z0-9._-]+/i.test(leaf);
2522
+ }
2487
2523
  function resolveConfiguredDockerExecutable(env, platform = process.platform) {
2488
2524
  const configured = String(env.PUSHPALS_DOCKER_BIN_ABSOLUTE ?? env.PUSHPALS_DOCKER_BIN ?? (platform === "win32" ? "docker.exe" : "docker")).trim();
2489
2525
  return configured || (platform === "win32" ? "docker.exe" : "docker");
@@ -2531,6 +2567,80 @@ async function cleanupLingeringWorkerpalWarmContainers(opts) {
2531
2567
  removed: containerIds.length
2532
2568
  };
2533
2569
  }
2570
+ async function cleanupLingeringPushPalsGitWorktrees(opts) {
2571
+ const runCommandWithEnvFn = opts.runCommandWithEnvFn ?? runCommandWithEnv;
2572
+ const list = await runCommandWithEnvFn(["git", "worktree", "list", "--porcelain"], opts.repoRoot, opts.env);
2573
+ if (!list.ok) {
2574
+ const detail = list.stderr || list.stdout || `exit ${list.exitCode}`;
2575
+ return {
2576
+ ok: false,
2577
+ detail: `failed to inspect lingering PushPals git artifacts: ${detail}`,
2578
+ removed: 0
2579
+ };
2580
+ }
2581
+ const currentRepoPath = normalizeFsPathForComparison(opts.repoRoot);
2582
+ const removable = parseGitWorktreeListPorcelain(list.stdout).filter((entry) => {
2583
+ const normalizedPath = normalizeFsPathForComparison(entry.path);
2584
+ if (normalizedPath === currentRepoPath)
2585
+ return false;
2586
+ if (entry.branch?.startsWith(`refs/heads/${SOURCE_CONTROL_MANAGER_TEMP_BRANCH_PREFIX}`)) {
2587
+ return true;
2588
+ }
2589
+ return isWorkerpalEphemeralWorktreePath(opts.repoRoot, entry.path);
2590
+ });
2591
+ let removed = 0;
2592
+ const failures = [];
2593
+ for (const entry of removable) {
2594
+ const remove = await runCommandWithEnvFn(["git", "worktree", "remove", "--force", "--force", entry.path], opts.repoRoot, opts.env);
2595
+ if (remove.ok) {
2596
+ removed += 1;
2597
+ continue;
2598
+ }
2599
+ failures.push(`${entry.path}: ${remove.stderr || remove.stdout || `exit ${remove.exitCode}`}`);
2600
+ }
2601
+ const prune = await runCommandWithEnvFn(["git", "worktree", "prune"], opts.repoRoot, opts.env);
2602
+ if (!prune.ok) {
2603
+ failures.push(`prune: ${prune.stderr || prune.stdout || `exit ${prune.exitCode}`}`);
2604
+ }
2605
+ const deleteTempBranches = await runCommandWithEnvFn([
2606
+ "git",
2607
+ "for-each-ref",
2608
+ "--format=%(refname:short)",
2609
+ `refs/heads/${SOURCE_CONTROL_MANAGER_TEMP_BRANCH_PREFIX}`
2610
+ ], opts.repoRoot, opts.env);
2611
+ if (!deleteTempBranches.ok) {
2612
+ failures.push(`list temp branches: ${deleteTempBranches.stderr || deleteTempBranches.stdout || `exit ${deleteTempBranches.exitCode}`}`);
2613
+ } else {
2614
+ const branches = deleteTempBranches.stdout.split(/\r?\n/g).map((value) => value.trim()).filter(Boolean);
2615
+ for (const branch of branches) {
2616
+ const deleteResult = await runCommandWithEnvFn(["git", "branch", "-D", branch], opts.repoRoot, opts.env);
2617
+ if (!deleteResult.ok) {
2618
+ failures.push(`${branch}: ${deleteResult.stderr || deleteResult.stdout || `exit ${deleteResult.exitCode}`}`);
2619
+ } else {
2620
+ removed += 1;
2621
+ }
2622
+ }
2623
+ }
2624
+ if (removed === 0 && failures.length === 0) {
2625
+ return {
2626
+ ok: true,
2627
+ detail: "no lingering PushPals git artifacts found",
2628
+ removed: 0
2629
+ };
2630
+ }
2631
+ if (failures.length > 0) {
2632
+ return {
2633
+ ok: false,
2634
+ detail: `removed ${removed} lingering PushPals git artifact(s), but cleanup was incomplete: ${failures.join(" | ")}`,
2635
+ removed
2636
+ };
2637
+ }
2638
+ return {
2639
+ ok: true,
2640
+ detail: `removed ${removed} lingering PushPals git artifact(s)`,
2641
+ removed
2642
+ };
2643
+ }
2534
2644
  async function inspectDockerImageRuntimeTag(dockerExecutable, imageName, cwd, env) {
2535
2645
  const inspect = await runCommandWithEnv([
2536
2646
  dockerExecutable,
@@ -4034,6 +4144,19 @@ async function main() {
4034
4144
  console.log(`[pushpals] ${cleanup.detail} (${phase}).`);
4035
4145
  }
4036
4146
  };
4147
+ const cleanupPushPalsGitWorktreesIfNeeded = async (phase) => {
4148
+ const cleanup = await cleanupLingeringPushPalsGitWorktrees({
4149
+ repoRoot,
4150
+ env: workerpalDockerPrecheck.env
4151
+ });
4152
+ if (!cleanup.ok) {
4153
+ console.warn(`[pushpals] PushPals worktree cleanup warning (${phase}): ${cleanup.detail}`);
4154
+ return;
4155
+ }
4156
+ if (cleanup.removed > 0) {
4157
+ console.log(`[pushpals] ${cleanup.detail} (${phase}).`);
4158
+ }
4159
+ };
4037
4160
  const stopAutoStartedServices = () => {
4038
4161
  if (autoStartedServices.length === 0)
4039
4162
  return;
@@ -4056,6 +4179,7 @@ async function main() {
4056
4179
  }
4057
4180
  await stopRuntimeServicesGracefully(services);
4058
4181
  await cleanupWorkerpalWarmContainersIfNeeded("cli shutdown");
4182
+ await cleanupPushPalsGitWorktreesIfNeeded("cli shutdown");
4059
4183
  };
4060
4184
  let serverHealthy = await probeServer(serverUrl);
4061
4185
  const serverWasAlreadyHealthy = serverHealthy;
@@ -4085,6 +4209,7 @@ async function main() {
4085
4209
  };
4086
4210
  if (!serverHealthy) {
4087
4211
  await cleanupWorkerpalWarmContainersIfNeeded("startup preflight");
4212
+ await cleanupPushPalsGitWorktreesIfNeeded("startup preflight");
4088
4213
  if (!parsed.noAutoStart) {
4089
4214
  try {
4090
4215
  const startedRuntime = await autoStartRuntimeServices({
@@ -4383,6 +4508,7 @@ export {
4383
4508
  downloadRuntimeAssetsFromSourceTag,
4384
4509
  copyTrackedRepoPath,
4385
4510
  cleanupLingeringWorkerpalWarmContainers,
4511
+ cleanupLingeringPushPalsGitWorktrees,
4386
4512
  bundledMonitoringHubNeedsRefresh,
4387
4513
  buildWorkerpalSandboxPaths,
4388
4514
  buildServiceStopCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {