@pushpalsdev/cli 1.0.94 → 1.0.95

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 (2) hide show
  1. package/dist/pushpals-cli.js +120 -15
  2. package/package.json +1 -1
@@ -11,6 +11,7 @@ import {
11
11
  mkdirSync,
12
12
  readdirSync,
13
13
  readFileSync as readFileSync4,
14
+ renameSync,
14
15
  rmSync as rmSync2,
15
16
  writeFileSync
16
17
  } from "fs";
@@ -1978,8 +1979,29 @@ async function runCommandWithEnv(command, cwd, env, timeoutMs) {
1978
1979
  };
1979
1980
  }
1980
1981
  }
1982
+ function appendGitConfigEnv(env, key, value) {
1983
+ const existingCount = Number.parseInt(String(env.GIT_CONFIG_COUNT ?? "0").trim(), 10);
1984
+ const count = Number.isFinite(existingCount) && existingCount >= 0 ? existingCount : 0;
1985
+ for (let index = 0;index < count; index++) {
1986
+ if (String(env[`GIT_CONFIG_KEY_${index}`] ?? "") === key)
1987
+ return env;
1988
+ }
1989
+ return {
1990
+ ...env,
1991
+ GIT_CONFIG_COUNT: String(count + 1),
1992
+ [`GIT_CONFIG_KEY_${count}`]: key,
1993
+ [`GIT_CONFIG_VALUE_${count}`]: value
1994
+ };
1995
+ }
1996
+ function withWindowsGitSchannelEnv(env, platform = process.platform) {
1997
+ if (platform !== "win32")
1998
+ return env;
1999
+ if (parseBooleanFlag(env.PUSHPALS_DISABLE_WINDOWS_GIT_SCHANNEL) === true)
2000
+ return env;
2001
+ return appendGitConfigEnv(env, "http.sslBackend", "schannel");
2002
+ }
1981
2003
  async function runGitWithEnv(args, cwd, env) {
1982
- return await runCommandWithEnv(["git", ...args], cwd, env);
2004
+ return await runCommandWithEnv(["git", ...args], cwd, withWindowsGitSchannelEnv(env));
1983
2005
  }
1984
2006
  async function runGit(args, cwd) {
1985
2007
  return await runGitWithEnv(args, cwd, {
@@ -2264,15 +2286,59 @@ function resolveRuntimePlatformKey() {
2264
2286
  throw new Error(`Unsupported platform for embedded runtime binaries: ${process.platform}/${process.arch}`);
2265
2287
  }
2266
2288
  async function fetchLatestReleaseTag() {
2267
- const response = await fetchWithTimeout(`${GITHUB_API_URL}/releases/latest`, { headers: GITHUB_HEADERS }, 20000);
2268
- if (!response.ok) {
2269
- throw new Error(`Failed to resolve latest release tag (HTTP ${response.status})`);
2289
+ try {
2290
+ const response = await fetchWithTimeout(`${GITHUB_API_URL}/releases/latest`, { headers: GITHUB_HEADERS }, 20000);
2291
+ if (!response.ok) {
2292
+ throw new Error(`Failed to resolve latest release tag (HTTP ${response.status})`);
2293
+ }
2294
+ const payload = await response.json();
2295
+ const tagName = String(payload.tag_name ?? "").trim();
2296
+ if (!tagName)
2297
+ throw new Error("Latest release payload did not include tag_name");
2298
+ return tagName;
2299
+ } catch (err) {
2300
+ const fallback = await fetchLatestReleaseTagWithGitFallback(err);
2301
+ if (fallback)
2302
+ return fallback;
2303
+ throw err;
2270
2304
  }
2271
- const payload = await response.json();
2272
- const tagName = String(payload.tag_name ?? "").trim();
2273
- if (!tagName)
2274
- throw new Error("Latest release payload did not include tag_name");
2275
- return tagName;
2305
+ }
2306
+ async function fetchLatestReleaseTagWithGitFallback(cause) {
2307
+ const message = String(cause instanceof Error ? cause.message : cause ?? "");
2308
+ if (process.platform !== "win32" || !/certificate|cert_|unable to verify|self[- ]signed|tls|ssl/i.test(message) && !/fetch failed/i.test(message)) {
2309
+ return null;
2310
+ }
2311
+ console.warn("[pushpals] Bun could not verify the GitHub API certificate; resolving latest release tag with Git instead.");
2312
+ const result = await runGitWithEnv([
2313
+ "ls-remote",
2314
+ "--tags",
2315
+ "--refs",
2316
+ `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}.git`,
2317
+ "refs/tags/v*"
2318
+ ], process.cwd(), {
2319
+ ...process.env,
2320
+ GIT_TERMINAL_PROMPT: "0",
2321
+ GCM_INTERACTIVE: "Never"
2322
+ });
2323
+ if (!result.ok)
2324
+ return null;
2325
+ const tags = result.stdout.split(/\r?\n/g).map((line) => line.trim().match(/refs\/tags\/(v\d+\.\d+\.\d+(?:[-.][0-9A-Za-z.-]+)?)$/)?.[1]).filter((tag) => Boolean(tag));
2326
+ return sortReleaseTagsDescending(tags)[0] ?? null;
2327
+ }
2328
+ function sortReleaseTagsDescending(tags) {
2329
+ return [...new Set(tags)].sort((a, b) => compareReleaseTags(b, a));
2330
+ }
2331
+ function compareReleaseTags(a, b) {
2332
+ const parse = (value) => String(value).replace(/^v/i, "").split(/[.-]/g).map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
2333
+ const left = parse(a);
2334
+ const right = parse(b);
2335
+ const max = Math.max(left.length, right.length, 3);
2336
+ for (let index = 0;index < max; index++) {
2337
+ const delta = (left[index] ?? 0) - (right[index] ?? 0);
2338
+ if (delta !== 0)
2339
+ return delta;
2340
+ }
2341
+ return a.localeCompare(b);
2276
2342
  }
2277
2343
  function resolvePreferredRuntimeReleaseTag(explicitTag, env = process.env) {
2278
2344
  const fromArg = String(explicitTag ?? "").trim();
@@ -2588,7 +2654,7 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
2588
2654
  const existing = env[key];
2589
2655
  return typeof existing !== "string" || existing.trim().length === 0;
2590
2656
  })) : {};
2591
- return {
2657
+ const runtimeEnv = {
2592
2658
  ...inherited,
2593
2659
  PUSHPALS_REPO_ROOT_OVERRIDE: opts.repoRoot,
2594
2660
  PUSHPALS_PROJECT_ROOT_OVERRIDE: opts.repoRoot,
@@ -2609,6 +2675,7 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
2609
2675
  ...typeof env.PUSHPALS_DOCKER_BIN === "string" && env.PUSHPALS_DOCKER_BIN.trim() ? { PUSHPALS_DOCKER_BIN: env.PUSHPALS_DOCKER_BIN.trim() } : {},
2610
2676
  ...typeof env.PUSHPALS_DOCKER_BIN_ABSOLUTE === "string" && env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() ? { PUSHPALS_DOCKER_BIN_ABSOLUTE: env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() } : {}
2611
2677
  };
2678
+ return withWindowsGitSchannelEnv(runtimeEnv, platform);
2612
2679
  }
2613
2680
  function parseBooleanFlag(raw) {
2614
2681
  const normalized = String(raw ?? "").trim().toLowerCase();
@@ -2752,13 +2819,51 @@ function readRemoteBuddyAutonomousEngineState(logPath) {
2752
2819
  async function downloadBinaryAsset(tag, assetName, outPath) {
2753
2820
  console.log(`[pushpals] Downloading embedded runtime binary ${assetName} from ${tag}...`);
2754
2821
  const url = `${GITHUB_RELEASE_URL}/${encodeURIComponent(tag)}/${assetName}`;
2755
- const response = await fetchWithTimeout(url, { headers: GITHUB_HEADERS }, 60000);
2756
- if (!response.ok) {
2757
- throw new Error(`Failed to download ${assetName} from ${tag} (HTTP ${response.status})`);
2822
+ try {
2823
+ const response = await fetchWithTimeout(url, { headers: GITHUB_HEADERS }, 60000);
2824
+ if (!response.ok) {
2825
+ throw new Error(`Failed to download ${assetName} from ${tag} (HTTP ${response.status})`);
2826
+ }
2827
+ const bytes = new Uint8Array(await response.arrayBuffer());
2828
+ mkdirSync(dirname(outPath), { recursive: true });
2829
+ await Bun.write(outPath, bytes);
2830
+ return;
2831
+ } catch (err) {
2832
+ const fallback = await downloadBinaryAssetWithWindowsCurlFallback(url, outPath, err);
2833
+ if (fallback)
2834
+ return;
2835
+ throw err;
2836
+ }
2837
+ }
2838
+ async function downloadBinaryAssetWithWindowsCurlFallback(url, outPath, cause) {
2839
+ if (process.platform !== "win32")
2840
+ return false;
2841
+ const message = String(cause instanceof Error ? cause.message : cause ?? "");
2842
+ if (!/certificate|cert_|unable to verify|self[- ]signed|tls|ssl/i.test(message) && !/fetch failed/i.test(message)) {
2843
+ return false;
2758
2844
  }
2759
- const bytes = new Uint8Array(await response.arrayBuffer());
2845
+ const tmpPath = `${outPath}.download-${process.pid}-${Date.now()}.tmp`;
2760
2846
  mkdirSync(dirname(outPath), { recursive: true });
2761
- await Bun.write(outPath, bytes);
2847
+ rmSync2(tmpPath, { force: true });
2848
+ console.warn("[pushpals] Bun could not verify the GitHub release certificate; retrying download with Windows curl certificate handling.");
2849
+ const result = await runCommandWithEnv([
2850
+ "curl.exe",
2851
+ "--fail",
2852
+ "--location",
2853
+ "--silent",
2854
+ "--show-error",
2855
+ "--ssl-no-revoke",
2856
+ "--output",
2857
+ tmpPath,
2858
+ url
2859
+ ], process.cwd(), process.env, 120000);
2860
+ if (!result.ok || !existsSync5(tmpPath)) {
2861
+ rmSync2(tmpPath, { force: true });
2862
+ const detail = result.stderr || result.stdout || `exit ${result.exitCode}`;
2863
+ throw new Error(`Windows curl fallback failed while downloading runtime binary: ${detail}`);
2864
+ }
2865
+ renameSync(tmpPath, outPath);
2866
+ return true;
2762
2867
  }
2763
2868
  async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
2764
2869
  const platformKey = resolveRuntimePlatformKey();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.0.94",
3
+ "version": "1.0.95",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {