free-pi-cli 0.4.3 → 0.4.4

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-xn8n5cb2.js");
30
+ await import("./main-4wwh354d.js");
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/main.ts
4
4
  import { realpathSync } from "node:fs";
5
+ import { dirname as dirname5, join as join5 } from "node:path";
5
6
 
6
7
  // src/browser.ts
7
8
  import { spawn } from "node:child_process";
@@ -44,7 +45,7 @@ function openBrowser(url, platform = process.platform, spawnImpl = spawn) {
44
45
  // package.json
45
46
  var package_default = {
46
47
  name: "free-pi-cli",
47
- version: "0.4.3",
48
+ version: "0.4.4",
48
49
  description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
49
50
  license: "MIT",
50
51
  type: "module",
@@ -64,7 +65,8 @@ var package_default = {
64
65
  prepublishOnly: "bun run build"
65
66
  },
66
67
  dependencies: {
67
- "@earendil-works/pi-coding-agent": "1.2.0"
68
+ "@earendil-works/pi-coding-agent": "1.2.0",
69
+ "@gravity-ai/api": "1.2.1"
68
70
  },
69
71
  devDependencies: {
70
72
  "@freepi/pi-ads": "workspace:*",
@@ -92,6 +94,7 @@ Usage:
92
94
  Environment:
93
95
  FREEPI_BASE_URL override the backend base URL (default https://api.freepi.ai)
94
96
  FREEPI_NO_AUTO_UPDATE disable in-place self-update for this run
97
+ FREEPI_NO_GRAVITY_PIXEL=1 do not run Gravity's ad-measurement pixel
95
98
  `.trim();
96
99
  function versionLine() {
97
100
  return `free-pi-cli ${CLI_VERSION}`;
@@ -222,18 +225,8 @@ function getConfigPath() {
222
225
  return join2(homedir(), FREE_PI_DIR_NAME, "config.json");
223
226
  }
224
227
 
225
- // src/pi-launch.ts
226
- import {
227
- createAgentSessionFromServices,
228
- createAgentSessionRuntime,
229
- createAgentSessionServices,
230
- createCodemodeExtension,
231
- createMcpExtension,
232
- createToolSearchExtension,
233
- InteractiveMode,
234
- SessionManager,
235
- SettingsManager
236
- } from "@earendil-works/pi-coding-agent";
228
+ // src/gravity-pixel.ts
229
+ import { createHash } from "node:crypto";
237
230
 
238
231
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/core/core.js
239
232
  var _a;
@@ -4876,11 +4869,16 @@ var AdNextResponseSchema = object({
4876
4869
  ad_id: string2(),
4877
4870
  click_token: string2(),
4878
4871
  creative: AdCreativeSchema,
4879
- click_url: string2()
4872
+ click_url: string2(),
4873
+ source: _enum(["gravity", "own"]).optional(),
4874
+ imp_url: string2().optional()
4880
4875
  });
4876
+ var GRAVITY_CONTEXT_HEADER = "x-gravity-context";
4877
+ var GRAVITY_CONTEXT_MAX_BYTES = 8192;
4881
4878
  var AdImpressionRequestSchema = object({
4882
4879
  ad_id: string2(),
4883
- click_token: string2()
4880
+ click_token: string2(),
4881
+ imp_fired: boolean2().optional()
4884
4882
  });
4885
4883
  var AdminAdRequestSchema = object({
4886
4884
  slot: AdSlotSchema,
@@ -5087,6 +5085,83 @@ function formatCreditsLeftLine(remaining, allowance, refillAt) {
5087
5085
  return `${left} / ${total} credits left today · refills ${formatRefillEt(refillAt)}`;
5088
5086
  }
5089
5087
  var SLOW_LANE_METER_LINE = "Slow lane: cheapest model until refill";
5088
+ // src/gravity-pixel.ts
5089
+ var GRAVITY_PIXEL_ID = "6cd74804-0541-4d9b-899d-d7fceb465b6f";
5090
+ function userIdFromJwt(jwt) {
5091
+ try {
5092
+ const payload = JSON.parse(Buffer.from(jwt.split(".")[1] ?? "", "base64url").toString("utf8"));
5093
+ return typeof payload.sub === "string" ? payload.sub : undefined;
5094
+ } catch {
5095
+ return;
5096
+ }
5097
+ }
5098
+ function dropPixelSignalHandlers() {
5099
+ const onNew = (event, fn) => {
5100
+ if (event !== "SIGINT" && event !== "SIGTERM" || typeof fn !== "function")
5101
+ return;
5102
+ if (!/shutdownSync/.test(Function.prototype.toString.call(fn)))
5103
+ return;
5104
+ queueMicrotask(() => process.removeListener(event, fn));
5105
+ };
5106
+ process.on("newListener", onNew);
5107
+ return () => {
5108
+ process.removeListener("newListener", onNew);
5109
+ };
5110
+ }
5111
+ function startGravityPixel(jwt, storageDir, env = process.env, load = () => import("./index-xkn61g24.js")) {
5112
+ if (env.FREEPI_NO_GRAVITY_PIXEL === "1")
5113
+ return;
5114
+ const userId = userIdFromJwt(jwt);
5115
+ if (!userId)
5116
+ return;
5117
+ const userHash = createHash("sha256").update(userId).digest("hex");
5118
+ let sdk;
5119
+ let pixel;
5120
+ (async () => {
5121
+ try {
5122
+ sdk = await load();
5123
+ const p = sdk.gravityPixel({ pixelId: GRAVITY_PIXEL_ID, appName: "free-pi", storageDir });
5124
+ pixel = p;
5125
+ const stopWatching = dropPixelSignalHandlers();
5126
+ try {
5127
+ await p.start();
5128
+ } finally {
5129
+ stopWatching();
5130
+ }
5131
+ } catch {
5132
+ pixel = undefined;
5133
+ }
5134
+ })();
5135
+ return () => {
5136
+ try {
5137
+ const sessionId = pixel?.getSessionId();
5138
+ if (!sdk || !pixel || !sessionId)
5139
+ return;
5140
+ const ctx = sdk.gravityContext({
5141
+ sessionId,
5142
+ user: { userId: userHash, gruid: pixel.getVisitorId(), grclid: pixel.getClickId(), graid: pixel.getGraid() }
5143
+ });
5144
+ const encoded = Buffer.from(JSON.stringify(ctx)).toString("base64url");
5145
+ return encoded.length <= GRAVITY_CONTEXT_MAX_BYTES ? encoded : undefined;
5146
+ } catch {
5147
+ return;
5148
+ }
5149
+ };
5150
+ }
5151
+
5152
+ // src/pi-launch.ts
5153
+ import {
5154
+ createAgentSessionFromServices,
5155
+ createAgentSessionRuntime,
5156
+ createAgentSessionServices,
5157
+ createCodemodeExtension,
5158
+ createMcpExtension,
5159
+ createToolSearchExtension,
5160
+ InteractiveMode,
5161
+ SessionManager,
5162
+ SettingsManager
5163
+ } from "@earendil-works/pi-coding-agent";
5164
+
5090
5165
  // ../pi-ads/src/api.ts
5091
5166
  var REQUEST_TIMEOUT_MS = 5000;
5092
5167
  async function authedFetch(deps, path, init) {
@@ -5100,10 +5175,38 @@ async function authedFetch(deps, path, init) {
5100
5175
  ...init?.headers ?? {},
5101
5176
  Authorization: `Bearer ${token}`,
5102
5177
  "Content-Type": "application/json",
5103
- ...deps.sessionId && (path.startsWith("/ads/next") || path === "/ads/impression") ? { "x-session-id": deps.sessionId } : {}
5178
+ ...deps.sessionId && (path.startsWith("/ads/next") || path === "/ads/impression") ? { "x-session-id": deps.sessionId } : {},
5179
+ ...path.startsWith("/ads/next") ? gravityHeader(deps) : {}
5104
5180
  }
5105
5181
  });
5106
5182
  }
5183
+ function gravityHeader(deps) {
5184
+ try {
5185
+ const ctx = deps.getGravityContext?.();
5186
+ return ctx ? { [GRAVITY_CONTEXT_HEADER]: ctx } : {};
5187
+ } catch {
5188
+ return {};
5189
+ }
5190
+ }
5191
+ var IMP_TIMEOUT_MS = 3000;
5192
+ function isGravityImpUrl(raw) {
5193
+ if (!raw)
5194
+ return false;
5195
+ try {
5196
+ const u = new URL(raw);
5197
+ return u.protocol === "https:" && /(^|\.)trygravity\.(ai|com)$/.test(u.hostname);
5198
+ } catch {
5199
+ return false;
5200
+ }
5201
+ }
5202
+ function fireGravityImpression(deps, ad) {
5203
+ if (ad.source !== "gravity" || !isGravityImpUrl(ad.imp_url))
5204
+ return false;
5205
+ try {
5206
+ Promise.resolve((deps.fetchImpl ?? fetch)(ad.imp_url, { method: "GET", signal: AbortSignal.timeout(IMP_TIMEOUT_MS) })).catch(() => {});
5207
+ } catch {}
5208
+ return true;
5209
+ }
5107
5210
  async function fetchAdNext(deps, slot) {
5108
5211
  try {
5109
5212
  const res = await authedFetch(deps, `/ads/next?slot=${slot}`);
@@ -5114,11 +5217,11 @@ async function fetchAdNext(deps, slot) {
5114
5217
  return;
5115
5218
  }
5116
5219
  }
5117
- async function postImpression(deps, adId, clickToken) {
5220
+ async function postImpression(deps, adId, clickToken, impFired = false) {
5118
5221
  try {
5119
5222
  await authedFetch(deps, "/ads/impression", {
5120
5223
  method: "POST",
5121
- body: JSON.stringify({ ad_id: adId, click_token: clickToken })
5224
+ body: JSON.stringify({ ad_id: adId, click_token: clickToken, ...impFired ? { imp_fired: true } : {} })
5122
5225
  });
5123
5226
  } catch {}
5124
5227
  }
@@ -5134,7 +5237,7 @@ async function fetchMe(deps) {
5134
5237
  }
5135
5238
 
5136
5239
  // ../pi-ads/src/style.ts
5137
- import { createHash } from "node:crypto";
5240
+ import { createHash as createHash2 } from "node:crypto";
5138
5241
  var OSC_RE = /\x1b\][\s\S]*?(?:\x07|\x1b\\)/g;
5139
5242
  var CSI_RE = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
5140
5243
  var C1_RE = /[\x80-\x9f]/g;
@@ -5167,7 +5270,7 @@ function renderAdCard(creative, clickUrl, theme, columns) {
5167
5270
  const label = " AD ░▒▓ ";
5168
5271
  const top = `┌${label}${"─".repeat(Math.max(0, innerWidth + 2 - label.length))}┐`;
5169
5272
  const bottom = `└${"─".repeat(innerWidth + 2)}┘`;
5170
- const linkId = createHash("sha256").update(url).digest("hex").slice(0, 12);
5273
+ const linkId = createHash2("sha256").update(url).digest("hex").slice(0, 12);
5171
5274
  const row = (text, opts) => {
5172
5275
  const plain = pad(truncate(text, innerWidth), innerWidth);
5173
5276
  let styled = theme.fg("accent", plain);
@@ -5201,16 +5304,33 @@ function renderWarningLine(message, theme) {
5201
5304
  var BANNER_WIDGET_ID = "freepi-ad";
5202
5305
  function createBannerRenderer(opts = {}) {
5203
5306
  let lastPostedToken;
5307
+ const screen = opts.screen ?? {};
5204
5308
  return {
5205
5309
  async render(deps, ctx, config, columnsOverride) {
5206
5310
  const ad = await fetchAdNext(deps, "banner");
5207
5311
  if (opts.isActive && !opts.isActive())
5208
5312
  return;
5209
5313
  if (!ad) {
5314
+ screen.banner = undefined;
5315
+ ctx.ui.setWidget(BANNER_WIDGET_ID, undefined);
5316
+ opts.onBannerAd?.(undefined);
5317
+ return;
5318
+ }
5319
+ const isGravity = ad.source === "gravity";
5320
+ if (screen.inline === "gravity") {
5321
+ screen.banner = undefined;
5210
5322
  ctx.ui.setWidget(BANNER_WIDGET_ID, undefined);
5211
5323
  opts.onBannerAd?.(undefined);
5212
5324
  return;
5213
5325
  }
5326
+ if (isGravity && screen.inline)
5327
+ screen.clearInline?.();
5328
+ screen.banner = isGravity ? "gravity" : "own";
5329
+ screen.clearBanner = () => {
5330
+ screen.banner = undefined;
5331
+ ctx.ui.setWidget(BANNER_WIDGET_ID, undefined);
5332
+ opts.onBannerAd?.(undefined);
5333
+ };
5214
5334
  const theme = ctx.ui.theme;
5215
5335
  const columns = columnsOverride ?? process.stdout.columns ?? 80;
5216
5336
  const lines = columns >= config.adMinColumns ? renderAdCard(ad.creative, ad.click_url, theme, columns) : [renderPlainAdLine(ad.creative, ad.click_url, theme, columns)];
@@ -5218,7 +5338,8 @@ function createBannerRenderer(opts = {}) {
5218
5338
  opts.onBannerAd?.(ad.click_url);
5219
5339
  if (lastPostedToken !== ad.click_token) {
5220
5340
  lastPostedToken = ad.click_token;
5221
- await postImpression(deps, ad.ad_id, ad.click_token);
5341
+ const impFired = fireGravityImpression(deps, ad);
5342
+ await postImpression(deps, ad.ad_id, ad.click_token, impFired);
5222
5343
  }
5223
5344
  }
5224
5345
  };
@@ -5226,37 +5347,48 @@ function createBannerRenderer(opts = {}) {
5226
5347
 
5227
5348
  // ../pi-ads/src/inline.ts
5228
5349
  var INLINE_WIDGET_ID = "freepi-ad-inline";
5229
- function createInlineRenderer() {
5350
+ function createInlineRenderer(screen = {}) {
5230
5351
  let turnCount = 0;
5231
5352
  let inlineVisible = false;
5232
5353
  let lastPostedToken;
5354
+ const clearInline = (ctx) => {
5355
+ if (inlineVisible)
5356
+ ctx.ui.setWidget(INLINE_WIDGET_ID, undefined);
5357
+ inlineVisible = false;
5358
+ screen.inline = undefined;
5359
+ };
5233
5360
  return {
5234
5361
  async onTurnEnd(deps, ctx, config, columnsOverride) {
5235
5362
  turnCount += 1;
5236
- if (inlineVisible) {
5237
- ctx.ui.setWidget(INLINE_WIDGET_ID, undefined);
5238
- inlineVisible = false;
5239
- }
5363
+ clearInline(ctx);
5240
5364
  if (turnCount % config.adInlineTurnFrequency !== 0)
5241
5365
  return;
5366
+ if (screen.banner === "gravity")
5367
+ return;
5242
5368
  const ad = await fetchAdNext(deps, "inline");
5243
5369
  if (!ad)
5244
5370
  return;
5371
+ const isGravity = ad.source === "gravity";
5372
+ if (screen.banner === "gravity")
5373
+ return;
5374
+ if (isGravity && screen.banner)
5375
+ screen.clearBanner?.();
5245
5376
  const theme = ctx.ui.theme;
5246
5377
  const columns = columnsOverride ?? process.stdout.columns ?? 80;
5247
5378
  const lines = columns >= config.adMinColumns ? renderAdCard(ad.creative, ad.click_url, theme, columns) : [renderPlainAdLine(ad.creative, ad.click_url, theme, columns)];
5248
5379
  ctx.ui.setWidget(INLINE_WIDGET_ID, lines, { placement: "belowEditor" });
5249
5380
  inlineVisible = true;
5381
+ screen.inline = isGravity ? "gravity" : "own";
5382
+ screen.clearInline = () => clearInline(ctx);
5250
5383
  if (lastPostedToken !== ad.click_token) {
5251
5384
  lastPostedToken = ad.click_token;
5252
- await postImpression(deps, ad.ad_id, ad.click_token);
5385
+ const impFired = fireGravityImpression(deps, ad);
5386
+ await postImpression(deps, ad.ad_id, ad.click_token, impFired);
5253
5387
  }
5254
5388
  },
5255
5389
  reset(ctx) {
5256
5390
  turnCount = 0;
5257
- if (inlineVisible)
5258
- ctx.ui.setWidget(INLINE_WIDGET_ID, undefined);
5259
- inlineVisible = false;
5391
+ clearInline(ctx);
5260
5392
  }
5261
5393
  };
5262
5394
  }
@@ -5371,15 +5503,17 @@ function createAdsExtension(opts) {
5371
5503
  baseUrl: opts.baseUrl,
5372
5504
  getToken: opts.getToken,
5373
5505
  sessionId: opts.sessionId,
5374
- fetchImpl: opts.fetchImpl
5506
+ fetchImpl: opts.fetchImpl,
5507
+ getGravityContext: opts.getGravityContext
5375
5508
  };
5376
5509
  const config = loadConfig();
5377
5510
  return {
5378
5511
  name: "free-pi-ads",
5379
5512
  factory(pi) {
5380
5513
  let stopped = false;
5381
- const banner = createBannerRenderer({ onBannerAd: opts.onBannerAd, isActive: () => !stopped });
5382
- const inline = createInlineRenderer();
5514
+ const screen = {};
5515
+ const banner = createBannerRenderer({ onBannerAd: opts.onBannerAd, isActive: () => !stopped, screen });
5516
+ const inline = createInlineRenderer(screen);
5383
5517
  let bannerInterval;
5384
5518
  let lastBannerAt = 0;
5385
5519
  let bannerInFlight = false;
@@ -5453,7 +5587,7 @@ function buildProviderConfig(baseUrl, jwt, sessionId, models) {
5453
5587
  };
5454
5588
  }
5455
5589
 
5456
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/memory/metrics.mjs
5590
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/metrics.mjs
5457
5591
  var Metrics = {
5458
5592
  assign: 0,
5459
5593
  create: 0,
@@ -5461,7 +5595,8 @@ var Metrics = {
5461
5595
  discard: 0,
5462
5596
  update: 0
5463
5597
  };
5464
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/guard/guard.mjs
5598
+
5599
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/guard/guard.mjs
5465
5600
  function IsArray(value) {
5466
5601
  return Array.isArray(value);
5467
5602
  }
@@ -5506,7 +5641,7 @@ function Keys(value) {
5506
5641
  function Symbols(value) {
5507
5642
  return Object.getOwnPropertySymbols(value);
5508
5643
  }
5509
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/guard/globals.mjs
5644
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/guard/globals.mjs
5510
5645
  function IsTypeArray(value) {
5511
5646
  return globalThis.ArrayBuffer.isView(value);
5512
5647
  }
@@ -5519,7 +5654,25 @@ function IsSet(value) {
5519
5654
  function IsMap(value) {
5520
5655
  return value instanceof globalThis.Map;
5521
5656
  }
5522
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/memory/clone.mjs
5657
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/settings/settings.mjs
5658
+ var settings = {
5659
+ immutableTypes: false,
5660
+ maxErrors: 8,
5661
+ maxInstantiationCount: 128,
5662
+ useAcceleration: true,
5663
+ exactOptionalPropertyTypes: false,
5664
+ enumerableKind: false,
5665
+ correctiveParse: false,
5666
+ unionPrioritySort: true
5667
+ };
5668
+ function Get() {
5669
+ return settings;
5670
+ }
5671
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/freeze.mjs
5672
+ function Freeze(value) {
5673
+ return Get().immutableTypes ? Object.freeze(value) : value;
5674
+ }
5675
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/clone.mjs
5523
5676
  function FromClassInstance(value) {
5524
5677
  return value;
5525
5678
  }
@@ -5578,21 +5731,7 @@ function Clone(value) {
5578
5731
  Metrics.clone += 1;
5579
5732
  return FromValue(value);
5580
5733
  }
5581
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/settings/settings.mjs
5582
- var settings = {
5583
- immutableTypes: false,
5584
- maxErrors: 8,
5585
- maxInstantiationCount: 128,
5586
- useAcceleration: true,
5587
- exactOptionalPropertyTypes: false,
5588
- enumerableKind: false,
5589
- correctiveParse: false,
5590
- unionPrioritySort: true
5591
- };
5592
- function Get() {
5593
- return settings;
5594
- }
5595
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/memory/create.mjs
5734
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/create.mjs
5596
5735
  function MergeHidden(left, right) {
5597
5736
  for (const key of Object.keys(right)) {
5598
5737
  Object.defineProperty(left, key, {
@@ -5609,12 +5748,11 @@ function Merge(left, right) {
5609
5748
  }
5610
5749
  function Create(hidden, enumerable, options = {}) {
5611
5750
  Metrics.create += 1;
5612
- const settings = Get();
5613
5751
  const withOptions = Merge(enumerable, options);
5614
- const withHidden = settings.enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
5615
- return settings.immutableTypes ? Object.freeze(withHidden) : withHidden;
5752
+ const withHidden = Get().enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
5753
+ return Freeze(withHidden);
5616
5754
  }
5617
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/memory/update.mjs
5755
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/update.mjs
5618
5756
  function Update(current, hidden, enumerable) {
5619
5757
  Metrics.update += 1;
5620
5758
  const settings = Get();
@@ -5635,14 +5773,14 @@ function Update(current, hidden, enumerable) {
5635
5773
  value: enumerable[key]
5636
5774
  });
5637
5775
  }
5638
- return result;
5776
+ return Freeze(result);
5639
5777
  }
5640
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/schema.mjs
5778
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/schema.mjs
5641
5779
  function IsSchema(value) {
5642
5780
  return IsObject(value);
5643
5781
  }
5644
5782
 
5645
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs
5783
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs
5646
5784
  function AddOptionalOperation(type) {
5647
5785
  return Update(type, { "~optional": true }, {});
5648
5786
  }
@@ -5651,12 +5789,12 @@ function AddOptionalAction(type, options) {
5651
5789
  return result;
5652
5790
  }
5653
5791
 
5654
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/action/_add_optional.mjs
5792
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/action/_add_optional.mjs
5655
5793
  function AddOptional(type, options = {}) {
5656
5794
  return AddOptionalAction(type, options);
5657
5795
  }
5658
5796
 
5659
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/_optional.mjs
5797
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/_optional.mjs
5660
5798
  function Optional(type) {
5661
5799
  return AddOptional(type);
5662
5800
  }
@@ -5664,18 +5802,18 @@ function IsOptional(value) {
5664
5802
  return IsSchema(value) && HasPropertyKey(value, "~optional");
5665
5803
  }
5666
5804
 
5667
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/properties.mjs
5805
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/properties.mjs
5668
5806
  function RequiredArray(properties) {
5669
5807
  return Keys(properties).filter((key) => !IsOptional(properties[key]));
5670
5808
  }
5671
5809
 
5672
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/object.mjs
5810
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/object.mjs
5673
5811
  function _Object_(properties, options = {}) {
5674
5812
  const requiredKeys = RequiredArray(properties);
5675
5813
  const required = requiredKeys.length > 0 ? { required: requiredKeys } : {};
5676
5814
  return Create({ "~kind": "Object" }, { type: "object", ...required, properties }, options);
5677
5815
  }
5678
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/hashing/hash.mjs
5816
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/hashing/hash.mjs
5679
5817
  var ByteMarker;
5680
5818
  (function(ByteMarker) {
5681
5819
  ByteMarker[ByteMarker["Array"] = 0] = "Array";
@@ -5700,9 +5838,9 @@ var F64 = new Float64Array(1);
5700
5838
  var F64In = new DataView(F64.buffer);
5701
5839
  var F64Out = new Uint8Array(F64.buffer);
5702
5840
  var encoder = new TextEncoder;
5703
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/integer.mjs
5841
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/integer.mjs
5704
5842
  var IntegerPattern = "-?(?:0|[1-9][0-9]*)";
5705
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/literal.mjs
5843
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/literal.mjs
5706
5844
  class InvalidLiteralValue extends Error {
5707
5845
  constructor(value) {
5708
5846
  super(`Invalid Literal value`);
@@ -5722,27 +5860,27 @@ function LiteralTypeName(value) {
5722
5860
  function Literal(value, options) {
5723
5861
  return Create({ "~kind": "Literal" }, { type: LiteralTypeName(value), const: value }, options);
5724
5862
  }
5725
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/number.mjs
5863
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/number.mjs
5726
5864
  var NumberPattern = "-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?";
5727
5865
  function Number2(options) {
5728
5866
  return Create({ "~kind": "Number" }, { type: "number" }, options);
5729
5867
  }
5730
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/string.mjs
5868
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/string.mjs
5731
5869
  var StringPattern = ".*";
5732
5870
  function String2(options) {
5733
5871
  return Create({ "~kind": "String" }, { type: "string" }, options);
5734
5872
  }
5735
5873
 
5736
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/union.mjs
5874
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/union.mjs
5737
5875
  function Union(anyOf, options = {}) {
5738
5876
  return Create({ "~kind": "Union" }, { anyOf }, options);
5739
5877
  }
5740
5878
 
5741
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/types/record.mjs
5879
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/record.mjs
5742
5880
  var IntegerKey = `^${IntegerPattern}$`;
5743
5881
  var NumberKey = `^${NumberPattern}$`;
5744
5882
  var StringKey = `^${StringPattern}$`;
5745
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/script/token/internal/char.mjs
5883
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/internal/char.mjs
5746
5884
  function Range(start, end) {
5747
5885
  return Array.from({ length: end - start + 1 }, (_, i) => String.fromCharCode(start + i));
5748
5886
  }
@@ -5756,17 +5894,17 @@ var Digit = [Zero, ...NonZero];
5756
5894
  var UnderScore = "_";
5757
5895
  var DollarSign = "$";
5758
5896
 
5759
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/script/token/unsigned_integer.mjs
5897
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/unsigned_integer.mjs
5760
5898
  var AllowedDigits = [...Digit, UnderScore];
5761
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/script/token/ident.mjs
5899
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/ident.mjs
5762
5900
  var Initial = [...Alpha, UnderScore, DollarSign];
5763
5901
  var Remaining = [...Initial, ...Digit];
5764
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/script/token/unsigned_number.mjs
5902
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/unsigned_number.mjs
5765
5903
  var AllowedDigits2 = [...Digit, UnderScore];
5766
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/engine/helpers/keys.mjs
5904
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/engine/helpers/keys.mjs
5767
5905
  var integerKeyPattern = new RegExp("^(?:0|[1-9][0-9]*)$");
5768
5906
 
5769
- // ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/type/engine/indexed/from_object.mjs
5907
+ // ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/engine/indexed/from_object.mjs
5770
5908
  var NumericKeyPattern = new RegExp(IntegerKey);
5771
5909
  // src/usage-tool.ts
5772
5910
  var USAGE_TOOL_NAME = "free_pi_usage";
@@ -6464,6 +6602,13 @@ function installFooterRightAlign(mode) {
6464
6602
 
6465
6603
  // src/changelog.ts
6466
6604
  var CHANGELOG_HIGHLIGHTS = [
6605
+ {
6606
+ version: "0.4.4",
6607
+ highlights: [
6608
+ "Updated terms: you'll be asked to accept them once.",
6609
+ "Some ads now come from Gravity, measured with a small tracker. Turn it off with FREEPI_NO_GRAVITY_PIXEL=1."
6610
+ ]
6611
+ },
6467
6612
  {
6468
6613
  version: "0.4.3",
6469
6614
  highlights: [
@@ -7044,6 +7189,7 @@ function buildRuntimeOptions(opts, sessionId) {
7044
7189
  baseUrl: opts.baseUrl,
7045
7190
  getToken: () => opts.jwt,
7046
7191
  sessionId,
7192
+ getGravityContext: opts.getGravityContext,
7047
7193
  onBannerAd: (u) => {
7048
7194
  advertiserUrl = u;
7049
7195
  }
@@ -7184,6 +7330,9 @@ free-pi — before you start
7184
7330
  * Ad cards render in the terminal UI. Some ads come from a third-party ad
7185
7331
  provider (Gravity), which receives excerpts of your recent conversation
7186
7332
  to pick a relevant ad.
7333
+ * Gravity also keeps a session and visitor ID on your machine (in
7334
+ ~/.free-pi) and receives usage events from free-pi to measure and
7335
+ attribute ads.
7187
7336
  * Your coding sessions (prompts, code, files, and model responses) are saved
7188
7337
  as traces and used to train models. We may share or sell that data to
7189
7338
  third parties. Once you accept, there is no opt-out, except where the law
@@ -7202,7 +7351,7 @@ and the Privacy Policy (https://freepi.ai/privacy).
7202
7351
 
7203
7352
  Decline and nothing is sent to the free-pi server.
7204
7353
  `.trim();
7205
- var CONSENT_VERSION = "3";
7354
+ var CONSENT_VERSION = "4";
7206
7355
  var NON_INTERACTIVE_CONSENT_TEXT = "free-pi first-run consent needs an interactive terminal (stdin is not a TTY). " + "Run `npx free-pi-cli` in a terminal once to review and accept; " + "after that, non-interactive runs work.";
7207
7356
  var CONSENT_PROMPT = "Accept? [y/N] ";
7208
7357
  function parseConsentAnswer(answer) {
@@ -7437,14 +7586,17 @@ async function run(deps, argv = process.argv) {
7437
7586
  return 1;
7438
7587
  }
7439
7588
  let jwt = await loadJwt(deps.credentialsPath);
7589
+ let consentCurrent = false;
7440
7590
  if (jwt) {
7441
7591
  try {
7442
7592
  const serverConsent = await checkToken(deps.baseUrl, jwt);
7593
+ consentCurrent = serverConsent === CONSENT_VERSION;
7443
7594
  if (serverConsent !== undefined && serverConsent !== CONSENT_VERSION) {
7444
7595
  if (!await deps.promptConsent(REPROMPT_CONSENT_TEXT)) {
7445
7596
  deps.log("Declined. free-pi needs you to accept the updated terms to continue; you will be asked again next time.");
7446
7597
  return 1;
7447
7598
  }
7599
+ consentCurrent = true;
7448
7600
  try {
7449
7601
  await submitUserConsent(deps.baseUrl, jwt, CONSENT_VERSION);
7450
7602
  } catch (err) {
@@ -7467,6 +7619,7 @@ async function run(deps, argv = process.argv) {
7467
7619
  }
7468
7620
  showIntro(deps.log);
7469
7621
  jwt = await loginFlow(deps);
7622
+ consentCurrent = true;
7470
7623
  }
7471
7624
  const versionCheck = await deps.checkClientVersion(deps.baseUrl, CLI_VERSION);
7472
7625
  let updateLatest;
@@ -7487,6 +7640,12 @@ async function run(deps, argv = process.argv) {
7487
7640
  deps.log(whatsNew);
7488
7641
  if (versionCheck.notice)
7489
7642
  deps.log(versionCheck.notice);
7643
+ let getGravityContext;
7644
+ if (consentCurrent) {
7645
+ try {
7646
+ getGravityContext = deps.startGravityPixel?.(jwt);
7647
+ } catch {}
7648
+ }
7490
7649
  const finalSessionId = await deps.launchPi({
7491
7650
  baseUrl: deps.baseUrl,
7492
7651
  jwt,
@@ -7494,7 +7653,8 @@ async function run(deps, argv = process.argv) {
7494
7653
  session: sessionArg.session,
7495
7654
  model: versionCheck.model,
7496
7655
  models: versionCheck.models,
7497
- updateLatest
7656
+ updateLatest,
7657
+ getGravityContext
7498
7658
  });
7499
7659
  deps.log(finalSessionId ? `
7500
7660
  free-pi session ended. Start it again anytime: npx free-pi-cli --session ${finalSessionId}` : `
@@ -7620,6 +7780,7 @@ ${HELP_TEXT}`);
7620
7780
  launchPi,
7621
7781
  log: (message) => console.log(message),
7622
7782
  checkClientVersion,
7783
+ startGravityPixel: (jwt) => startGravityPixel(jwt, join5(dirname5(agentDir), "gravity")),
7623
7784
  maybeSelfUpdate: (action, latest) => maybeSelfUpdate(action, latest, {
7624
7785
  env: process.env,
7625
7786
  autoUpdateEnabled: () => readAutoUpdate(getConfigPath()),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-pi-cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
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",
@@ -20,7 +20,8 @@
20
20
  "prepublishOnly": "bun run build"
21
21
  },
22
22
  "dependencies": {
23
- "@earendil-works/pi-coding-agent": "1.2.0"
23
+ "@earendil-works/pi-coding-agent": "1.2.0",
24
+ "@gravity-ai/api": "1.2.1"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@freepi/pi-ads": "workspace:*",