free-pi-cli 0.6.2 → 0.6.3

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/index.js CHANGED
@@ -30,4 +30,4 @@ if (err) {
30
30
  console.error(err);
31
31
  process.exit(1);
32
32
  }
33
- await import("./main-cwwgr8bh.js");
33
+ await import("./main-q0zat1nk.js");
@@ -5,8 +5,10 @@ import {
5
5
  } from "./index-n5qs9pq1.js";
6
6
 
7
7
  // src/main.ts
8
- import { realpathSync as realpathSync4, writeFileSync as writeFileSync3 } from "node:fs";
9
- import { dirname as dirname10, join as join12 } from "node:path";
8
+ import { spawn as spawn3 } from "node:child_process";
9
+ import { realpathSync as realpathSync5, writeFileSync as writeFileSync3 } from "node:fs";
10
+ import { homedir as homedir7 } from "node:os";
11
+ import { dirname as dirname11, join as join13 } from "node:path";
10
12
 
11
13
  // src/browser.ts
12
14
  import { spawn } from "node:child_process";
@@ -15057,7 +15059,7 @@ var HiveClassifyResponseSchema = exports_external.object({
15057
15059
  // package.json
15058
15060
  var package_default = {
15059
15061
  name: "free-pi-cli",
15060
- version: "0.6.2",
15062
+ version: "0.6.3",
15061
15063
  description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
15062
15064
  license: "MIT",
15063
15065
  type: "module",
@@ -21270,6 +21272,12 @@ function mergeAttribution(a, b) {
21270
21272
 
21271
21273
  // src/changelog.ts
21272
21274
  var CHANGELOG_HIGHLIGHTS = [
21275
+ {
21276
+ version: "0.6.3",
21277
+ highlights: [
21278
+ "When your version is older than the minimum, free-pi now updates itself and restarts in the same folder, instead of asking you to run a command."
21279
+ ]
21280
+ },
21273
21281
  {
21274
21282
  version: "0.6.2",
21275
21283
  highlights: [
@@ -24562,6 +24570,140 @@ function createRealSpawn(io = { platform: process.platform, spawnSyncImpl: spawn
24562
24570
  };
24563
24571
  }
24564
24572
 
24573
+ // src/relaunch.ts
24574
+ import { existsSync as existsSync5, mkdtempSync as mkdtempSync2, realpathSync as realpathSync3, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
24575
+ import { constants as osConstants, tmpdir as tmpdir2 } from "node:os";
24576
+ import { delimiter, dirname as dirname9, isAbsolute as isAbsolute2, join as join11, resolve as resolve4, sep as sep2, win32 } from "node:path";
24577
+ function applyRelaunchCwd(env, chdir = (d) => process.chdir(d), isDir = (p) => {
24578
+ try {
24579
+ return statSync3(p).isDirectory();
24580
+ } catch {
24581
+ return false;
24582
+ }
24583
+ }) {
24584
+ const dir = env.FREEPI_RELAUNCH_CWD;
24585
+ delete env.FREEPI_RELAUNCH_CWD;
24586
+ if (!dir)
24587
+ return;
24588
+ const fail2 = `free-pi: could not return to your project folder (${dir}) after updating. Run free-pi again from that folder.`;
24589
+ if (!isDir(dir))
24590
+ return fail2;
24591
+ try {
24592
+ chdir(dir);
24593
+ } catch {
24594
+ return fail2;
24595
+ }
24596
+ return;
24597
+ }
24598
+ var SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
24599
+ function canon(p, fold) {
24600
+ let r;
24601
+ try {
24602
+ r = realpathSync3.native(p);
24603
+ } catch {
24604
+ r = resolve4(p);
24605
+ }
24606
+ return fold ? r.toLowerCase() : r;
24607
+ }
24608
+ function isInside2(e, d) {
24609
+ return e === d || e.startsWith(d.endsWith(sep2) ? d : d + sep2);
24610
+ }
24611
+ function hasNodeModulesBin(entry) {
24612
+ const parts = entry.split(/[\\/]+/).map((x) => x.toLowerCase());
24613
+ return parts.some((x, i) => x === "node_modules" && parts[i + 1] === ".bin");
24614
+ }
24615
+ function sanitizedEnv(env, nodeDir, project, home, win) {
24616
+ const proj = canon(project, win);
24617
+ const broad = proj === canon(home, win) || dirname9(proj) === proj;
24618
+ const out = {};
24619
+ let pathKey = "PATH";
24620
+ let oldPath = "";
24621
+ for (const [k, v] of Object.entries(env)) {
24622
+ const lk = k.toLowerCase();
24623
+ if (lk.startsWith("npm_") || lk === "node_options" || lk === "node_path")
24624
+ continue;
24625
+ if (lk === "path") {
24626
+ pathKey = k;
24627
+ oldPath = v ?? "";
24628
+ continue;
24629
+ }
24630
+ if (win && lk === "comspec")
24631
+ continue;
24632
+ out[k] = v;
24633
+ }
24634
+ if (win) {
24635
+ const sysRoot = Object.entries(env).find(([k]) => k.toLowerCase() === "systemroot")?.[1];
24636
+ const root = sysRoot && win32.isAbsolute(sysRoot) ? sysRoot : "C:\\Windows";
24637
+ const comKey = Object.keys(env).find((k) => k.toLowerCase() === "comspec") ?? "ComSpec";
24638
+ out[comKey] = win32.join(root, "System32", "cmd.exe");
24639
+ }
24640
+ const kept = oldPath.split(delimiter).filter((e) => e && isAbsolute2(e) && (broad || !isInside2(canon(e, win), proj)) && !hasNodeModulesBin(e));
24641
+ out[pathKey] = [nodeDir, ...kept].join(delimiter);
24642
+ return out;
24643
+ }
24644
+ async function relaunchViaNpx(latest, deps) {
24645
+ const { env } = deps;
24646
+ if ("FREEPI_NO_AUTO_UPDATE" in env || !deps.autoUpdateEnabled())
24647
+ return;
24648
+ if (env.FREEPI_AUTO_UPDATED)
24649
+ return;
24650
+ const win = deps.platform === "win32";
24651
+ if (!latest || !SEMVER.test(latest))
24652
+ return;
24653
+ const target2 = latest;
24654
+ const nodeDir = dirname9(deps.execPath);
24655
+ const npxCli = win ? join11(nodeDir, "node_modules", "npm", "bin", "npx-cli.js") : join11(dirname9(nodeDir), "lib", "node_modules", "npm", "bin", "npx-cli.js");
24656
+ const exists = deps.exists ?? existsSync5;
24657
+ const distroNpxCli = "/usr/share/nodejs/npm/bin/npx-cli.js";
24658
+ let npxCliPath = npxCli;
24659
+ if (!exists(npxCli)) {
24660
+ if (!win && deps.execPath === "/usr/bin/node" && exists(distroNpxCli))
24661
+ npxCliPath = distroNpxCli;
24662
+ else
24663
+ return;
24664
+ }
24665
+ const childEnv = sanitizedEnv(env, nodeDir, deps.cwd, deps.homedir, win);
24666
+ const readyDir = mkdtempSync2(join11(tmpdir2(), "free-pi-relaunch-"));
24667
+ const readyFile = join11(readyDir, "started");
24668
+ try {
24669
+ deps.log(`Updating free-pi to ${target2}…`);
24670
+ return await new Promise((resolve5) => {
24671
+ let child;
24672
+ try {
24673
+ child = deps.spawn(deps.execPath, [npxCliPath, "--yes", `free-pi-cli@${target2}`, ...deps.argv], {
24674
+ stdio: "inherit",
24675
+ cwd: deps.homedir,
24676
+ env: { ...childEnv, FREEPI_AUTO_UPDATED: target2, FREEPI_RELAUNCH_READY: readyFile, FREEPI_RELAUNCH_CWD: deps.cwd },
24677
+ shell: false
24678
+ });
24679
+ } catch {
24680
+ resolve5(undefined);
24681
+ return;
24682
+ }
24683
+ const handlers = win ? [] : [
24684
+ ["SIGINT", () => {}],
24685
+ ["SIGTERM", () => void child.kill("SIGTERM")],
24686
+ ["SIGHUP", () => void child.kill("SIGTERM")]
24687
+ ];
24688
+ for (const [sig, cb] of handlers)
24689
+ deps.proc.on(sig, cb);
24690
+ const done3 = (code) => {
24691
+ for (const [sig, cb] of handlers)
24692
+ deps.proc.off(sig, cb);
24693
+ resolve5(code);
24694
+ };
24695
+ child.on("error", () => done3(undefined));
24696
+ child.on("exit", (code, signal) => {
24697
+ const exitCode = code ?? 128 + (signal ? osConstants.signals[signal] ?? 0 : 0);
24698
+ const cancelled = signal === "SIGINT" || signal === "SIGTERM" || signal === "SIGHUP" || exitCode === 130 || exitCode === 143 || exitCode === 129;
24699
+ done3(exitCode !== 0 && !cancelled && !existsSync5(readyFile) ? undefined : exitCode);
24700
+ });
24701
+ });
24702
+ } finally {
24703
+ rmSync2(readyDir, { recursive: true, force: true });
24704
+ }
24705
+ }
24706
+
24565
24707
  // src/run.ts
24566
24708
  import { SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
24567
24709
 
@@ -24692,13 +24834,13 @@ async function readOneKeypress(prompt, stdin = process.stdin, stdout = process.s
24692
24834
  if (typeof stdin.setRawMode === "function")
24693
24835
  stdin.setRawMode(true);
24694
24836
  stdin.resume();
24695
- return await new Promise((resolve4) => {
24837
+ return await new Promise((resolve5) => {
24696
24838
  const onData = (chunk) => {
24697
24839
  stdin.off("data", onData);
24698
24840
  if (typeof stdin.setRawMode === "function")
24699
24841
  stdin.setRawMode(wasRaw ?? false);
24700
24842
  stdin.pause();
24701
- resolve4(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
24843
+ resolve5(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
24702
24844
  };
24703
24845
  stdin.once("data", onData);
24704
24846
  });
@@ -24714,9 +24856,9 @@ async function promptResumeInteractive(session) {
24714
24856
  }
24715
24857
 
24716
24858
  // src/onboarding.ts
24717
- import { existsSync as existsSync5, readdirSync as readdirSync3, realpathSync as realpathSync3 } from "node:fs";
24859
+ import { existsSync as existsSync6, readdirSync as readdirSync3, realpathSync as realpathSync4 } from "node:fs";
24718
24860
  import { homedir as homedir6 } from "node:os";
24719
- import { join as join11 } from "node:path";
24861
+ import { join as join12 } from "node:path";
24720
24862
  var INTRO_ENV_SKIP = "FREEPI_NO_INTRO";
24721
24863
  var INTRO_TEXT = `
24722
24864
  Welcome to free-pi — a free coding agent in your terminal.
@@ -24745,9 +24887,9 @@ var FIRST_TASK_EXPERIMENT = "retain.first_task";
24745
24887
  var PROJECT_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "Gemfile", "composer.json"];
24746
24888
  function looksLikeProject(cwd, home = homedir6()) {
24747
24889
  try {
24748
- if (realpathSync3(cwd) === realpathSync3(home))
24890
+ if (realpathSync4(cwd) === realpathSync4(home))
24749
24891
  return false;
24750
- if (PROJECT_MARKERS.some((m) => existsSync5(join11(cwd, m))))
24892
+ if (PROJECT_MARKERS.some((m) => existsSync6(join12(cwd, m))))
24751
24893
  return true;
24752
24894
  return readdirSync3(cwd).some((f) => /\.(sln|csproj)$/.test(f));
24753
24895
  } catch {
@@ -24774,12 +24916,12 @@ ${text}
24774
24916
  }
24775
24917
 
24776
24918
  // src/whats-new.ts
24777
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
24778
- import { dirname as dirname9 } from "node:path";
24919
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
24920
+ import { dirname as dirname10 } from "node:path";
24779
24921
  function maybeWhatsNew(cliVersion, agentDir) {
24780
24922
  try {
24781
24923
  const path = getLastSeenVersionPath(agentDir);
24782
- const lastSeen = existsSync6(path) ? readFileSync4(path, "utf-8").trim() : undefined;
24924
+ const lastSeen = existsSync7(path) ? readFileSync4(path, "utf-8").trim() : undefined;
24783
24925
  writeLastSeen(path, cliVersion);
24784
24926
  if (lastSeen === undefined)
24785
24927
  return;
@@ -24795,7 +24937,7 @@ function maybeWhatsNew(cliVersion, agentDir) {
24795
24937
  }
24796
24938
  }
24797
24939
  function writeLastSeen(path, version3) {
24798
- mkdirSync2(dirname9(path), { recursive: true });
24940
+ mkdirSync2(dirname10(path), { recursive: true });
24799
24941
  writeFileSync2(path, version3, "utf-8");
24800
24942
  }
24801
24943
 
@@ -24950,6 +25092,9 @@ Guest trial is not available right now.
24950
25092
  if (outcome === "updated-exit")
24951
25093
  return 0;
24952
25094
  if (outcome === "blocked") {
25095
+ const relaunched = await deps.relaunchLatest?.(versionCheck.latest);
25096
+ if (relaunched !== undefined)
25097
+ return relaunched;
24953
25098
  deps.log("free-pi-cli needs an update. From a trusted folder (for example: cd ~), run: npx --yes free-pi-cli@latest (or: npm i -g free-pi-cli@latest)");
24954
25099
  return 1;
24955
25100
  }
@@ -25118,12 +25263,17 @@ async function checkClientVersion(baseUrl, cliVersion, fetchImpl = fetch, jwt2)
25118
25263
  // src/main.ts
25119
25264
  function resolveCliRealpath() {
25120
25265
  try {
25121
- return realpathSync4(process.argv[1] ?? "");
25266
+ return realpathSync5(process.argv[1] ?? "");
25122
25267
  } catch {
25123
25268
  return "";
25124
25269
  }
25125
25270
  }
25126
25271
  async function main() {
25272
+ const cwdError = applyRelaunchCwd(process.env);
25273
+ if (cwdError) {
25274
+ console.error(cwdError);
25275
+ process.exit(1);
25276
+ }
25127
25277
  if (process.env.FREEPI_RELAUNCH_READY) {
25128
25278
  try {
25129
25279
  writeFileSync3(process.env.FREEPI_RELAUNCH_READY, "");
@@ -25169,11 +25319,23 @@ ${HELP_TEXT}`);
25169
25319
  credentialsPath: getCredentialsPath(agentDir),
25170
25320
  promptConsent: promptConsentInteractive,
25171
25321
  openBrowser,
25172
- sleep: (ms) => new Promise((resolve4) => setTimeout(resolve4, ms)),
25322
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
25173
25323
  launchPi,
25174
25324
  log: (message) => console.log(message),
25175
25325
  checkClientVersion: (baseUrl, cliVersion, jwt2) => checkClientVersion(baseUrl, cliVersion, fetch, jwt2),
25176
- startGravityPixel: (jwt2) => startGravityPixel(jwt2, join12(dirname10(agentDir), "gravity")),
25326
+ startGravityPixel: (jwt2) => startGravityPixel(jwt2, join13(dirname11(agentDir), "gravity")),
25327
+ relaunchLatest: (latest) => relaunchViaNpx(latest, {
25328
+ env: process.env,
25329
+ argv: process.argv.slice(2),
25330
+ platform: process.platform,
25331
+ cwd: process.cwd(),
25332
+ homedir: homedir7(),
25333
+ execPath: process.execPath,
25334
+ autoUpdateEnabled: () => readAutoUpdate(getConfigPath()),
25335
+ spawn: (cmd, args, opts) => spawn3(cmd, args, opts),
25336
+ proc: process,
25337
+ log: (message) => console.log(message)
25338
+ }),
25177
25339
  maybeSelfUpdate: (action2, latest) => maybeSelfUpdate(action2, latest, {
25178
25340
  env: process.env,
25179
25341
  autoUpdateEnabled: () => readAutoUpdate(getConfigPath()),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-pi-cli",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
5
5
  "license": "MIT",
6
6
  "type": "module",