free-pi-cli 0.6.17 → 0.6.19

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
@@ -27,4 +27,4 @@ if (err) {
27
27
  console.error(err);
28
28
  process.exit(1);
29
29
  }
30
- await import("./main-9s59pfn5.js");
30
+ await import("./main-qn3gsj3n.js");
@@ -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.17",
518
+ version: "0.6.19",
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",
@@ -6614,7 +6614,7 @@ async function runFreePiMcp(args, deps) {
6614
6614
  }
6615
6615
 
6616
6616
  // src/gravity-pixel.ts
6617
- import { createHash } from "node:crypto";
6617
+ import { createHash, randomUUID } from "node:crypto";
6618
6618
  var GRAVITY_PIXEL_ID = "6cd74804-0541-4d9b-899d-d7fceb465b6f";
6619
6619
  function userIdFromJwt(jwt) {
6620
6620
  try {
@@ -6661,14 +6661,15 @@ function startGravityPixel(jwt, storageDir, env = process.env, load = () => impo
6661
6661
  pixel = undefined;
6662
6662
  }
6663
6663
  })();
6664
+ const fallbackSessionId = randomUUID();
6664
6665
  return () => {
6665
6666
  try {
6666
- const sessionId = pixel?.getSessionId();
6667
- if (!sdk || !pixel || !sessionId)
6667
+ if (!sdk)
6668
6668
  return;
6669
+ const sessionId = pixel?.getSessionId();
6669
6670
  const ctx = sdk.gravityContext({
6670
- sessionId,
6671
- 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 }
6672
6673
  });
6673
6674
  const encoded = Buffer.from(JSON.stringify(ctx)).toString("base64url");
6674
6675
  return encoded.length <= GRAVITY_CONTEXT_MAX_BYTES ? encoded : undefined;
@@ -6705,6 +6706,29 @@ function loadConfig(env = process.env) {
6705
6706
  };
6706
6707
  }
6707
6708
 
6709
+ // ../pi-ads/src/api.ts
6710
+ import { arch } from "node:os";
6711
+
6712
+ // ../pi-ads/src/turn.ts
6713
+ var TURN_HEADER = "x-turn-id";
6714
+ var current;
6715
+ function startTurn() {
6716
+ current = crypto.randomUUID();
6717
+ return current;
6718
+ }
6719
+ var currentTurnId = () => current;
6720
+ function createTurnHeaderExtension() {
6721
+ return {
6722
+ name: "free-pi-turn-header",
6723
+ factory(pi) {
6724
+ pi.on("before_provider_headers", (event) => {
6725
+ if (current)
6726
+ event.headers[TURN_HEADER] = current;
6727
+ });
6728
+ }
6729
+ };
6730
+ }
6731
+
6708
6732
  // ../pi-ads/src/api.ts
6709
6733
  var REQUEST_TIMEOUT_MS = 5000;
6710
6734
  async function authedFetch(deps, path, init) {
@@ -6719,10 +6743,18 @@ async function authedFetch(deps, path, init) {
6719
6743
  Authorization: `Bearer ${token}`,
6720
6744
  "Content-Type": "application/json",
6721
6745
  ...deps.sessionId && (path.startsWith("/ads/next") || path === "/ads/impression") ? { "x-session-id": deps.sessionId } : {},
6722
- ...path.startsWith("/ads/next") ? gravityHeader(deps) : {}
6746
+ ...path.startsWith("/ads/next") ? { ...currentTurnId() ? { "x-turn-id": currentTurnId() } : {}, ...deps.clientVersion ? { "x-client-version": deps.clientVersion } : {}, "x-client-os": process.platform, "x-client-arch": arch(), ...localeHeaders(), ...gravityHeader(deps) } : {}
6723
6747
  }
6724
6748
  });
6725
6749
  }
6750
+ function localeHeaders() {
6751
+ try {
6752
+ const o = Intl.DateTimeFormat().resolvedOptions();
6753
+ return { ...o.timeZone ? { "x-client-timezone": o.timeZone } : {}, ...o.locale ? { "x-client-locale": o.locale } : {} };
6754
+ } catch {
6755
+ return {};
6756
+ }
6757
+ }
6726
6758
  function gravityHeader(deps) {
6727
6759
  try {
6728
6760
  const ctx = deps.getGravityContext?.();
@@ -6750,9 +6782,9 @@ function fireGravityImpression(deps, ad) {
6750
6782
  } catch {}
6751
6783
  return true;
6752
6784
  }
6753
- async function fetchAdNext(deps, slot) {
6785
+ async function fetchAdNext(deps, slot, trigger) {
6754
6786
  try {
6755
- const res = await authedFetch(deps, `/ads/next?slot=${slot}`);
6787
+ const res = await authedFetch(deps, `/ads/next?slot=${slot}`, trigger ? { headers: { "x-ad-trigger": trigger } } : undefined);
6756
6788
  if (res.status === 204 || !res.ok)
6757
6789
  return;
6758
6790
  return await res.json();
@@ -6852,8 +6884,8 @@ function createBannerRenderer(opts = {}) {
6852
6884
  let lastPostedToken;
6853
6885
  const screen = opts.screen ?? {};
6854
6886
  return {
6855
- async render(deps, ctx, config) {
6856
- const ad = await fetchAdNext(deps, "banner");
6887
+ async render(deps, ctx, config, trigger) {
6888
+ const ad = await fetchAdNext(deps, "banner", trigger);
6857
6889
  if (opts.isActive && !opts.isActive())
6858
6890
  return;
6859
6891
  if (!ad) {
@@ -7119,13 +7151,14 @@ async function renderMeter(deps, ctx, opts = {}) {
7119
7151
  }
7120
7152
 
7121
7153
  // ../pi-ads/src/index.ts
7122
- var BANNER_REFRESH_MS = 10 * 60 * 1000;
7154
+ var REFRESH_ON_TURN_END = false;
7123
7155
  var BANNER_MIN_REFRESH_MS = 60 * 1000;
7124
7156
  function createAdsExtension(opts) {
7125
7157
  const deps = {
7126
7158
  baseUrl: opts.baseUrl,
7127
7159
  getToken: opts.getToken,
7128
7160
  sessionId: opts.sessionId,
7161
+ clientVersion: opts.clientVersion,
7129
7162
  fetchImpl: opts.fetchImpl,
7130
7163
  getGravityContext: opts.getGravityContext,
7131
7164
  onMe: opts.onMe
@@ -7138,44 +7171,72 @@ function createAdsExtension(opts) {
7138
7171
  const screen = {};
7139
7172
  const banner = createBannerRenderer({ onBannerAd: opts.onBannerAd, isActive: () => !stopped, screen });
7140
7173
  const inline = createInlineRenderer(screen);
7141
- let bannerInterval;
7174
+ let launchTurnUnused = false;
7175
+ let launchToken;
7176
+ let launchAt = 0;
7142
7177
  let lastBannerAt = 0;
7143
7178
  let bannerInFlight = false;
7144
- const renderBanner = async (ctx, force = false) => {
7179
+ const renderBanner = async (ctx, trigger, force = false) => {
7145
7180
  if (stopped)
7146
7181
  return;
7147
7182
  if (!force && (bannerInFlight || Date.now() - lastBannerAt < BANNER_MIN_REFRESH_MS))
7148
7183
  return;
7149
7184
  bannerInFlight = true;
7150
7185
  try {
7151
- await banner.render(deps, ctx, config);
7186
+ await banner.render(deps, ctx, config, trigger);
7152
7187
  } finally {
7153
7188
  bannerInFlight = false;
7154
7189
  lastBannerAt = Date.now();
7155
7190
  }
7156
7191
  };
7192
+ pi.on("before_provider_headers", (event) => {
7193
+ const id = currentTurnId();
7194
+ if (id)
7195
+ event.headers[TURN_HEADER] = id;
7196
+ });
7157
7197
  pi.on("session_start", async (_event, ctx) => {
7158
7198
  stopped = false;
7159
7199
  inline.reset(ctx);
7200
+ startTurn();
7201
+ launchTurnUnused = true;
7202
+ launchAt = Date.now();
7203
+ launchToken = await Promise.resolve(deps.getToken()).catch(() => {
7204
+ return;
7205
+ });
7160
7206
  opts.registerMeterRefresh?.(() => renderMeter(deps, ctx));
7161
- await Promise.all([renderBanner(ctx, true), renderMeter(deps, ctx)]);
7162
- if (bannerInterval)
7163
- clearInterval(bannerInterval);
7164
- bannerInterval = setInterval(() => {
7165
- renderBanner(ctx);
7166
- }, BANNER_REFRESH_MS);
7167
- bannerInterval.unref?.();
7207
+ await Promise.all([renderBanner(ctx, "launch", true), renderMeter(deps, ctx)]);
7208
+ });
7209
+ const LAUNCH_AD_MAX_AGE_MS = 60 * 60000;
7210
+ const newMessage = async (ctx) => {
7211
+ if (launchTurnUnused) {
7212
+ launchTurnUnused = false;
7213
+ const token = await Promise.resolve(deps.getToken()).catch(() => {
7214
+ return;
7215
+ });
7216
+ if (token === launchToken && Date.now() - launchAt < LAUNCH_AD_MAX_AGE_MS)
7217
+ return;
7218
+ }
7219
+ startTurn();
7220
+ renderBanner(ctx, "user_message", true);
7221
+ };
7222
+ pi.on("before_agent_start", async (event, ctx) => {
7223
+ if (event.messageType !== "user")
7224
+ return;
7225
+ await newMessage(ctx);
7226
+ });
7227
+ pi.on("input", (event, ctx) => {
7228
+ const e = event;
7229
+ if (e.streamingBehavior && e.source !== "extension")
7230
+ return newMessage(ctx).then(() => ({ action: "continue" }));
7231
+ return { action: "continue" };
7168
7232
  });
7169
7233
  pi.on("turn_end", async (_event, ctx) => {
7170
- renderBanner(ctx);
7171
- await Promise.all([inline.onTurnEnd(deps, ctx, config), renderMeter(deps, ctx)]);
7234
+ if (REFRESH_ON_TURN_END)
7235
+ renderBanner(ctx, "turn_end");
7236
+ await renderMeter(deps, ctx);
7172
7237
  });
7173
7238
  pi.on("session_shutdown", () => {
7174
7239
  stopped = true;
7175
- if (bannerInterval) {
7176
- clearInterval(bannerInterval);
7177
- bannerInterval = undefined;
7178
- }
7179
7240
  });
7180
7241
  }
7181
7242
  };
@@ -9848,6 +9909,14 @@ function mergeAttribution(a, b) {
9848
9909
 
9849
9910
  // src/changelog.ts
9850
9911
  var CHANGELOG_HIGHLIGHTS = [
9912
+ {
9913
+ version: "0.6.19",
9914
+ highlights: ["The ad banner now refreshes when you send a message, not on a timer."]
9915
+ },
9916
+ {
9917
+ version: "0.6.18",
9918
+ highlights: ["Ads load correctly from your first prompt."]
9919
+ },
9851
9920
  {
9852
9921
  version: "0.6.17",
9853
9922
  highlights: [
@@ -14271,6 +14340,7 @@ function buildRuntimeOptions(opts, sessionId) {
14271
14340
  baseUrl: opts.baseUrl,
14272
14341
  getToken: () => auth.getJwt(),
14273
14342
  sessionId,
14343
+ clientVersion: CLI_VERSION,
14274
14344
  getGravityContext: opts.getGravityContext,
14275
14345
  onBannerAd: (u) => {
14276
14346
  advertiserUrl = u;
@@ -14381,6 +14451,7 @@ function buildRuntimeOptions(opts, sessionId) {
14381
14451
  hasCredit: () => (getAllowanceCache()?.boughtCredits ?? 0) > 0,
14382
14452
  extensionsFor: (subSessionId) => [
14383
14453
  { name: "free-pi-subagent-provider", factory: (pi) => registerProviders(pi, subSessionId, { "x-freepi-subagent": "1" }) },
14454
+ createTurnHeaderExtension(),
14384
14455
  createToolGuardExtension({
14385
14456
  baseUrl: opts.baseUrl,
14386
14457
  getToken: () => auth.getJwt(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-pi-cli",
3
- "version": "0.6.17",
3
+ "version": "0.6.19",
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",