free-pi-cli 0.6.16 → 0.6.18

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/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ <p align="center"><img src="https://freepi.ai/brand/free-pi-cli-lockup.png" alt="free-pi CLI" width="360"></p>
2
+
1
3
  # free-pi-cli
2
4
 
3
5
  **A free AI coding agent for your terminal.**
package/dist/index.js CHANGED
@@ -27,4 +27,4 @@ if (err) {
27
27
  console.error(err);
28
28
  process.exit(1);
29
29
  }
30
- await import("./main-gckhcg3t.js");
30
+ await import("./main-m86v4egg.js");
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/main.ts
4
- import { spawn as spawn3 } from "node:child_process";
4
+ import { spawn as spawn4 } from "node:child_process";
5
5
  import { realpathSync as realpathSync8, writeFileSync as writeFileSync6 } from "node:fs";
6
6
  import { homedir as homedir10 } from "node:os";
7
7
  import { dirname as dirname14, join as join15 } from "node:path";
@@ -515,7 +515,7 @@ function folderMatches(cwd, sessionCwd) {
515
515
  // package.json
516
516
  var package_default = {
517
517
  name: "free-pi-cli",
518
- version: "0.6.16",
518
+ version: "0.6.18",
519
519
  description: "Free AI coding agent for your terminal. Ads and a daily free allowance pay for the models; sign in with GitHub and run npx free-pi-cli.",
520
520
  keywords: [
521
521
  "coding agent",
@@ -6001,6 +6001,11 @@ var AdCreativeSchema = object({
6001
6001
  cta: string2(),
6002
6002
  accent: string2()
6003
6003
  });
6004
+ var AdNextCreativeSchema = AdCreativeSchema.extend({
6005
+ icon_url: string2().optional(),
6006
+ image_url: string2().optional(),
6007
+ brand_name: string2().optional()
6008
+ });
6004
6009
  var AdNextQuerySchema = object({
6005
6010
  slot: AdSlotSchema,
6006
6011
  own_only: _enum(["1"]).optional()
@@ -6008,7 +6013,7 @@ var AdNextQuerySchema = object({
6008
6013
  var AdNextResponseSchema = object({
6009
6014
  ad_id: string2(),
6010
6015
  click_token: string2(),
6011
- creative: AdCreativeSchema,
6016
+ creative: AdNextCreativeSchema,
6012
6017
  click_url: string2(),
6013
6018
  source: _enum(["gravity", "own"]).optional(),
6014
6019
  imp_url: string2().optional()
@@ -6609,7 +6614,7 @@ async function runFreePiMcp(args, deps) {
6609
6614
  }
6610
6615
 
6611
6616
  // src/gravity-pixel.ts
6612
- import { createHash } from "node:crypto";
6617
+ import { createHash, randomUUID } from "node:crypto";
6613
6618
  var GRAVITY_PIXEL_ID = "6cd74804-0541-4d9b-899d-d7fceb465b6f";
6614
6619
  function userIdFromJwt(jwt) {
6615
6620
  try {
@@ -6656,14 +6661,15 @@ function startGravityPixel(jwt, storageDir, env = process.env, load = () => impo
6656
6661
  pixel = undefined;
6657
6662
  }
6658
6663
  })();
6664
+ const fallbackSessionId = randomUUID();
6659
6665
  return () => {
6660
6666
  try {
6661
- const sessionId = pixel?.getSessionId();
6662
- if (!sdk || !pixel || !sessionId)
6667
+ if (!sdk)
6663
6668
  return;
6669
+ const sessionId = pixel?.getSessionId();
6664
6670
  const ctx = sdk.gravityContext({
6665
- sessionId,
6666
- user: { userId: userHash, gruid: pixel.getVisitorId(), grclid: pixel.getClickId(), graid: pixel.getGraid() }
6671
+ sessionId: sessionId || fallbackSessionId,
6672
+ user: sessionId && pixel ? { userId: userHash, gruid: pixel.getVisitorId(), grclid: pixel.getClickId(), graid: pixel.getGraid() } : { userId: userHash }
6667
6673
  });
6668
6674
  const encoded = Buffer.from(JSON.stringify(ctx)).toString("base64url");
6669
6675
  return encoded.length <= GRAVITY_CONTEXT_MAX_BYTES ? encoded : undefined;
@@ -6701,6 +6707,7 @@ function loadConfig(env = process.env) {
6701
6707
  }
6702
6708
 
6703
6709
  // ../pi-ads/src/api.ts
6710
+ import { arch } from "node:os";
6704
6711
  var REQUEST_TIMEOUT_MS = 5000;
6705
6712
  async function authedFetch(deps, path, init) {
6706
6713
  const token = await deps.getToken();
@@ -6714,10 +6721,18 @@ async function authedFetch(deps, path, init) {
6714
6721
  Authorization: `Bearer ${token}`,
6715
6722
  "Content-Type": "application/json",
6716
6723
  ...deps.sessionId && (path.startsWith("/ads/next") || path === "/ads/impression") ? { "x-session-id": deps.sessionId } : {},
6717
- ...path.startsWith("/ads/next") ? gravityHeader(deps) : {}
6724
+ ...path.startsWith("/ads/next") ? { ...deps.clientVersion ? { "x-client-version": deps.clientVersion } : {}, "x-client-os": process.platform, "x-client-arch": arch(), ...localeHeaders(), ...gravityHeader(deps) } : {}
6718
6725
  }
6719
6726
  });
6720
6727
  }
6728
+ function localeHeaders() {
6729
+ try {
6730
+ const o = Intl.DateTimeFormat().resolvedOptions();
6731
+ return { ...o.timeZone ? { "x-client-timezone": o.timeZone } : {}, ...o.locale ? { "x-client-locale": o.locale } : {} };
6732
+ } catch {
6733
+ return {};
6734
+ }
6735
+ }
6721
6736
  function gravityHeader(deps) {
6722
6737
  try {
6723
6738
  const ctx = deps.getGravityContext?.();
@@ -7121,6 +7136,7 @@ function createAdsExtension(opts) {
7121
7136
  baseUrl: opts.baseUrl,
7122
7137
  getToken: opts.getToken,
7123
7138
  sessionId: opts.sessionId,
7139
+ clientVersion: opts.clientVersion,
7124
7140
  fetchImpl: opts.fetchImpl,
7125
7141
  getGravityContext: opts.getGravityContext,
7126
7142
  onMe: opts.onMe
@@ -9843,6 +9859,17 @@ function mergeAttribution(a, b) {
9843
9859
 
9844
9860
  // src/changelog.ts
9845
9861
  var CHANGELOG_HIGHLIGHTS = [
9862
+ {
9863
+ version: "0.6.18",
9864
+ highlights: ["Ads load correctly from your first prompt."]
9865
+ },
9866
+ {
9867
+ version: "0.6.17",
9868
+ highlights: [
9869
+ "free-pi now runs full screen. Click an ad to open it; selecting text still copies it, and the mouse wheel scrolls.",
9870
+ "The questions before start (terms, how you found free-pi, folder trust) now use color."
9871
+ ]
9872
+ },
9846
9873
  {
9847
9874
  version: "0.6.16",
9848
9875
  highlights: [
@@ -14035,10 +14062,83 @@ function patchResumeLine(stream) {
14035
14062
  };
14036
14063
  }
14037
14064
 
14065
+ // src/safe-url-open.ts
14066
+ import { spawn as spawn3 } from "node:child_process";
14067
+ function openLink(url) {
14068
+ if (process.platform !== "win32")
14069
+ return void openBrowser(url);
14070
+ spawn3("rundll32", ["url.dll,FileProtocolHandler", url], { stdio: "ignore", detached: true }).on("error", () => {}).unref();
14071
+ }
14072
+ function safeOpenUrl(url, open = openLink) {
14073
+ const safe = safeBrowserUrl(url);
14074
+ if (safe)
14075
+ open(safe);
14076
+ }
14077
+ function installSafeUrlOpen(mode, open = openLink) {
14078
+ const m = mode;
14079
+ const patch = (r) => {
14080
+ if (r && "openUrl" in r)
14081
+ r.openUrl = (url) => safeOpenUrl(url, open);
14082
+ };
14083
+ let current = m.renderer;
14084
+ patch(current);
14085
+ Object.defineProperty(m, "renderer", {
14086
+ configurable: true,
14087
+ enumerable: true,
14088
+ get: () => current,
14089
+ set: (next) => {
14090
+ current = next;
14091
+ patch(next);
14092
+ }
14093
+ });
14094
+ }
14095
+
14038
14096
  // src/project-trust.ts
14039
14097
  import { createInterface } from "node:readline/promises";
14040
14098
  import { resolve as resolve3 } from "node:path";
14041
14099
  import { hasTrustRequiringProjectResources, ProjectTrustStore } from "@earendil-works/pi-coding-agent";
14100
+
14101
+ // src/prelaunch-style.ts
14102
+ function colorOn() {
14103
+ return !process.env.NO_COLOR && process.stdout.isTTY === true;
14104
+ }
14105
+ var ACCENT = "96";
14106
+ var sgr2 = (code, s, on) => on ? `\x1B[${code}m${s}\x1B[0m` : s;
14107
+ function styleHeader(on = colorOn()) {
14108
+ return sgr2(`1;${ACCENT}`, "›π free-pi", on);
14109
+ }
14110
+ function styleYesNo(prompt, on = colorOn()) {
14111
+ return prompt.replace(/^(.*?)(\[y\/N\])(\s*)$/s, (_m, q, yn, sp) => `${sgr2("1", q, on)}${sgr2(ACCENT, yn, on)}${sp}`);
14112
+ }
14113
+ function styleConsent(text, on = colorOn()) {
14114
+ if (!on)
14115
+ return text;
14116
+ const body = text.split(`
14117
+ `).map((line, i) => {
14118
+ if (i === 0)
14119
+ return sgr2("1", line, on);
14120
+ if (line.startsWith("Terms:"))
14121
+ return line.replace(/https:\/\/\S+/g, (u) => sgr2(`4;${ACCENT}`, u, on));
14122
+ return sgr2("2", line, on);
14123
+ }).join(`
14124
+ `);
14125
+ return `${styleHeader(on)}
14126
+ ${body}`;
14127
+ }
14128
+ function styleSourceQuestion(q, on = colorOn()) {
14129
+ if (!on)
14130
+ return q;
14131
+ return q.replace(/How did you find free-pi\? \(optional\)/, (h) => sgr2("1", h, on)).replace(/\d\)/g, (n) => sgr2(ACCENT, n, on)).replace(/Type a number, or press Enter to skip: $/, (h) => sgr2("2", h, on));
14132
+ }
14133
+ function styleIntro(text, on = colorOn()) {
14134
+ const i = text.indexOf("npx ");
14135
+ if (i < 0)
14136
+ return text;
14137
+ return `${sgr2("2", text.slice(0, i).trimEnd(), on)}
14138
+ ${sgr2(ACCENT, text.slice(i), on)}`;
14139
+ }
14140
+
14141
+ // src/project-trust.ts
14042
14142
  var PROJECT_TRUST_PROMPT = "This folder has project settings for free-pi (.pi/ or .agents/skills), which can run code such as MCP servers and extensions. Trust this folder? [y/N] ";
14043
14143
  async function decideProjectTrust(cwd, agentDir, deps) {
14044
14144
  if (!(deps.hasTrustRequiringResources ?? hasTrustRequiringProjectResources)(cwd))
@@ -14051,7 +14151,7 @@ async function decideProjectTrust(cwd, agentDir, deps) {
14051
14151
  deps.log("This folder has project settings (.pi/ or .agents/skills); they are ignored because it is not trusted.");
14052
14152
  return false;
14053
14153
  }
14054
- const decision = /^y(es)?$/i.test((await deps.askLine(PROJECT_TRUST_PROMPT)).trim());
14154
+ const decision = /^y(es)?$/i.test((await deps.askLine(styleYesNo(PROJECT_TRUST_PROMPT))).trim());
14055
14155
  store.set(cwd, decision);
14056
14156
  return decision;
14057
14157
  }
@@ -14152,7 +14252,7 @@ function buildSettingsManager(opts, models, projectTrusted) {
14152
14252
  quietStartup: true,
14153
14253
  defaultThinkingLevel: "off",
14154
14254
  hideThinkingBlock: true,
14155
- tuiMode: "regular",
14255
+ tuiMode: "fullscreen",
14156
14256
  theme: "light/dark",
14157
14257
  enableInstallTelemetry: false,
14158
14258
  ...compactionSettingsFor(opts.costArm) ? { compaction: compactionSettingsFor(opts.costArm) } : {}
@@ -14186,6 +14286,7 @@ function buildRuntimeOptions(opts, sessionId) {
14186
14286
  baseUrl: opts.baseUrl,
14187
14287
  getToken: () => auth.getJwt(),
14188
14288
  sessionId,
14289
+ clientVersion: CLI_VERSION,
14189
14290
  getGravityContext: opts.getGravityContext,
14190
14291
  onBannerAd: (u) => {
14191
14292
  advertiserUrl = u;
@@ -14451,6 +14552,7 @@ async function launchPi(opts) {
14451
14552
  });
14452
14553
  installLogoutHandler(mode, { credentialsPath: opts.credentialsPath, isGuest: () => auth.isGuest() });
14453
14554
  installModelAllowanceHandler(mode);
14555
+ installSafeUrlOpen(mode);
14454
14556
  installFooterRightAlign(mode, () => {
14455
14557
  const id = runtime.session?.model?.id;
14456
14558
  return freeModelBase(pickerModelName(opts, id));
@@ -14471,8 +14573,8 @@ async function promptConsent(deps, text = CONSENT_TEXT) {
14471
14573
  deps.log(NON_INTERACTIVE_CONSENT_TEXT);
14472
14574
  return false;
14473
14575
  }
14474
- deps.log(text);
14475
- const answer = await deps.askLine(CONSENT_PROMPT);
14576
+ deps.log(styleConsent(text));
14577
+ const answer = await deps.askLine(styleYesNo(CONSENT_PROMPT));
14476
14578
  return parseConsentAnswer(answer);
14477
14579
  }
14478
14580
  async function promptConsentInteractive(text) {
@@ -14664,7 +14766,7 @@ function showIntro(log, env = process.env) {
14664
14766
  if (introSkipped(env))
14665
14767
  return;
14666
14768
  log(`
14667
- ${INTRO_TEXT}
14769
+ ${styleIntro(INTRO_TEXT)}
14668
14770
  `);
14669
14771
  }
14670
14772
  var FIRST_TASK_EXPERIMENT = "retain.first_task";
@@ -14839,7 +14941,7 @@ async function promptSourceInteractive() {
14839
14941
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
14840
14942
  try {
14841
14943
  return await Promise.race([
14842
- rl.question(SOURCE_QUESTION),
14944
+ rl.question(styleSourceQuestion(SOURCE_QUESTION)),
14843
14945
  new Promise((resolve) => rl.once("close", () => resolve(undefined))),
14844
14946
  new Promise((resolve) => rl.once("SIGINT", () => resolve("aborted")))
14845
14947
  ]);
@@ -15460,7 +15562,7 @@ ${HELP_TEXT}`);
15460
15562
  homedir: homedir10(),
15461
15563
  execPath: process.execPath,
15462
15564
  autoUpdateEnabled: () => readAutoUpdate(getConfigPath()),
15463
- spawn: (cmd, args, opts) => spawn3(cmd, args, opts),
15565
+ spawn: (cmd, args, opts) => spawn4(cmd, args, opts),
15464
15566
  proc: process,
15465
15567
  log: (message) => console.log(message)
15466
15568
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-pi-cli",
3
- "version": "0.6.16",
3
+ "version": "0.6.18",
4
4
  "description": "Free AI coding agent for your terminal. Ads and a daily free allowance pay for the models; sign in with GitHub and run npx free-pi-cli.",
5
5
  "keywords": [
6
6
  "coding agent",