free-pi-cli 0.4.0 → 0.4.2

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-nyekh9dy.js");
30
+ await import("./main-37kszx0y.js");
@@ -44,7 +44,7 @@ function openBrowser(url, platform = process.platform, spawnImpl = spawn) {
44
44
  // package.json
45
45
  var package_default = {
46
46
  name: "free-pi-cli",
47
- version: "0.4.0",
47
+ version: "0.4.2",
48
48
  description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
49
49
  license: "MIT",
50
50
  type: "module",
@@ -4820,6 +4820,7 @@ var AuthConsentRequestSchema = object({
4820
4820
  consent_version: string2()
4821
4821
  });
4822
4822
  var AccountTierSchema = _enum(["new", "young", "established"]);
4823
+ var UsageLaneSchema = _enum(["standard", "fallback", "wait"]);
4823
4824
  var MeResponseSchema = object({
4824
4825
  user_id: string2(),
4825
4826
  handle: string2(),
@@ -4828,7 +4829,12 @@ var MeResponseSchema = object({
4828
4829
  tier: AccountTierSchema,
4829
4830
  credit_usd: number2(),
4830
4831
  notice: string2().optional(),
4831
- buy_url: string2().optional()
4832
+ buy_url: string2().optional(),
4833
+ allowance_credits: number2().optional(),
4834
+ remaining_credits_today: number2().optional(),
4835
+ refill_at: string2().optional(),
4836
+ lane: UsageLaneSchema.optional(),
4837
+ bonus_credits: number2().optional()
4832
4838
  });
4833
4839
  var MeStatsResponseSchema = object({
4834
4840
  user_id: string2(),
@@ -4839,6 +4845,11 @@ var MeStatsResponseSchema = object({
4839
4845
  remaining_usd_today: number2(),
4840
4846
  credit_usd: number2(),
4841
4847
  credit_credits: number2(),
4848
+ allowance_credits: number2(),
4849
+ remaining_credits_today: number2(),
4850
+ refill_at: string2(),
4851
+ lane: UsageLaneSchema,
4852
+ bonus_credits: number2(),
4842
4853
  request_count_today: number2(),
4843
4854
  prompt_tokens_today: number2(),
4844
4855
  completion_tokens_today: number2(),
@@ -4912,7 +4923,7 @@ function assertExhaustiveErrorCode(code) {
4912
4923
  }
4913
4924
  // ../shared/src/config.ts
4914
4925
  var ConfigSchema = object({
4915
- perUserDailyCapUsd: number2().default(2.5),
4926
+ perUserDailyCapUsd: number2().default(1.125),
4916
4927
  perUserRollingCapUsd: number2().default(0),
4917
4928
  rollingCapWindowHours: number2().int().default(5),
4918
4929
  globalDailyCapUsd: number2().default(50),
@@ -4936,8 +4947,12 @@ var ConfigSchema = object({
4936
4947
  providerOrder: array(string2()).optional(),
4937
4948
  tierNewMaxDays: number2().int().default(30),
4938
4949
  tierYoungMaxDays: number2().int().default(365),
4939
- tierCapNewUsd: number2().default(0.5),
4940
- tierCapYoungUsd: number2().default(2),
4950
+ tierCapNewUsd: number2().default(0.45),
4951
+ tierCapYoungUsd: number2().default(1.125),
4952
+ fallbackLaneUsd: number2().default(0.225),
4953
+ fallbackHourlyCap: number2().int().default(10),
4954
+ fallbackLaneMinHeadroomBps: number2().int().default(1000),
4955
+ queueWaitRetryAfterSeconds: number2().int().default(0),
4941
4956
  sessionLeaseTtlMinutes: number2().int().default(30),
4942
4957
  minCliVersion: string2().default("0.0.0"),
4943
4958
  latestCliVersion: string2().default("0.2.0"),
@@ -5006,6 +5021,10 @@ function loadConfig(env = process.env) {
5006
5021
  tierYoungMaxDays: envNumber(env.TIER_YOUNG_MAX_DAYS),
5007
5022
  tierCapNewUsd: envNumber(env.TIER_CAP_NEW_USD),
5008
5023
  tierCapYoungUsd: envNumber(env.TIER_CAP_YOUNG_USD),
5024
+ fallbackLaneUsd: envNumber(env.FALLBACK_LANE_USD),
5025
+ fallbackHourlyCap: envNumber(env.FALLBACK_HOURLY_CAP),
5026
+ fallbackLaneMinHeadroomBps: envNumber(env.FALLBACK_LANE_MIN_HEADROOM_BPS),
5027
+ queueWaitRetryAfterSeconds: envNumber(env.QUEUE_WAIT_RETRY_AFTER_SECONDS),
5009
5028
  sessionLeaseTtlMinutes: envNumber(env.SESSION_LEASE_TTL_MINUTES),
5010
5029
  minCliVersion: env.MIN_CLI_VERSION || undefined,
5011
5030
  latestCliVersion: env.LATEST_CLI_VERSION || undefined,
@@ -5051,6 +5070,21 @@ var CREDITS_PER_USD = 40000 / 3;
5051
5070
  function usdToCredits(usd) {
5052
5071
  return Math.round(Number(usd) * CREDITS_PER_USD);
5053
5072
  }
5073
+ function formatRefillEt(date, timeZone = "America/New_York") {
5074
+ const formatted = new Intl.DateTimeFormat("en-US", {
5075
+ timeZone,
5076
+ hour: "numeric",
5077
+ minute: "2-digit",
5078
+ hour12: true
5079
+ }).format(date);
5080
+ return `${formatted} ET`;
5081
+ }
5082
+ function formatCreditsLeftLine(remaining, allowance, refillAt) {
5083
+ const left = remaining.toLocaleString("en-US");
5084
+ const total = allowance.toLocaleString("en-US");
5085
+ return `${left} / ${total} credits left today · refills ${formatRefillEt(refillAt)}`;
5086
+ }
5087
+ var SLOW_LANE_METER_LINE = "Slow lane: cheapest model until refill";
5054
5088
  // ../pi-ads/src/api.ts
5055
5089
  var REQUEST_TIMEOUT_MS = 3000;
5056
5090
  async function authedFetch(deps, path, init) {
@@ -5157,6 +5191,9 @@ function renderPlainAdLine(creative, clickUrl, theme, columns) {
5157
5191
  function renderErrorLine(message, theme) {
5158
5192
  return theme.fg("error", message);
5159
5193
  }
5194
+ function renderWarningLine(message, theme) {
5195
+ return theme.fg("warning", message);
5196
+ }
5160
5197
 
5161
5198
  // ../pi-ads/src/banner.ts
5162
5199
  var BANNER_WIDGET_ID = "freepi-ad";
@@ -5224,7 +5261,9 @@ function createInlineRenderer() {
5224
5261
  function mapErrorCode(code) {
5225
5262
  switch (code) {
5226
5263
  case "daily_cap":
5227
- return "out of free tokens today, resets at 00:00 UTC";
5264
+ return "out of free credits today — refills at UTC midnight";
5265
+ case "queue_wait":
5266
+ return "free-pi is near its daily budget — wait and retry";
5228
5267
  case "rolling_cap":
5229
5268
  return "over the rolling spend cap on the free tier — wait for the Retry-After window";
5230
5269
  case "concurrent":
@@ -5257,6 +5296,13 @@ function inferErrorFromRemainingBudget(remainingUsdToday) {
5257
5296
 
5258
5297
  // ../pi-ads/src/meter.ts
5259
5298
  var METER_WIDGET_ID = "freepi-meter";
5299
+ var METER_STATUS_KEY = "freepi-credits";
5300
+ var LOW_CREDIT_FRACTION = 0.1;
5301
+ var BUY_LINK_TEXT = "freepi.ai/buy";
5302
+ function buyMore(buyUrl) {
5303
+ const url = typeof buyUrl === "string" ? sanitizeText(buyUrl) : "";
5304
+ return url !== "" ? ` · Buy more: ${osc8(BUY_LINK_TEXT, url)}` : "";
5305
+ }
5260
5306
  var CONFIRM_TURNS = 3;
5261
5307
  var lastCreditUsd;
5262
5308
  var confirm;
@@ -5265,6 +5311,7 @@ function creditAddedMessage(addedUsd) {
5265
5311
  }
5266
5312
  async function renderMeter(deps, ctx) {
5267
5313
  const me = await fetchMe(deps);
5314
+ ctx.ui.setStatus(METER_STATUS_KEY, undefined);
5268
5315
  if (!me) {
5269
5316
  ctx.ui.setWidget(METER_WIDGET_ID, undefined);
5270
5317
  return;
@@ -5286,6 +5333,24 @@ async function renderMeter(deps, ctx) {
5286
5333
  ctx.ui.setWidget(METER_WIDGET_ID, [renderErrorLine(notice, theme)]);
5287
5334
  return;
5288
5335
  }
5336
+ if (me.lane === "fallback") {
5337
+ ctx.ui.setStatus(METER_STATUS_KEY, theme.fg("muted", "0 credits · slow lane"));
5338
+ ctx.ui.setWidget(METER_WIDGET_ID, [
5339
+ renderWarningLine(`Out of free credits — slow lane until refill${buyMore(me.buy_url)}`, theme)
5340
+ ]);
5341
+ return;
5342
+ }
5343
+ if (typeof me.remaining_credits_today === "number" && typeof me.allowance_credits === "number" && typeof me.refill_at === "string") {
5344
+ const refill = new Date(me.refill_at);
5345
+ if (!Number.isNaN(refill.getTime()) && me.lane !== "wait") {
5346
+ const left = me.remaining_credits_today;
5347
+ const bought = typeof me.bonus_credits === "number" && me.bonus_credits > 0 ? ` + ${me.bonus_credits.toLocaleString("en-US")}` : "";
5348
+ ctx.ui.setStatus(METER_STATUS_KEY, theme.fg("muted", `${left.toLocaleString("en-US")}${bought} credits`));
5349
+ const low = left > 0 && left <= me.allowance_credits * LOW_CREDIT_FRACTION;
5350
+ ctx.ui.setWidget(METER_WIDGET_ID, low ? [renderWarningLine(`Low on credits: ${left.toLocaleString("en-US")} left today${buyMore(me.buy_url)}`, theme)] : undefined);
5351
+ return;
5352
+ }
5353
+ }
5289
5354
  const errorCode = inferErrorFromRemainingBudget(me.remaining_usd_today);
5290
5355
  if (errorCode === undefined) {
5291
5356
  ctx.ui.setWidget(METER_WIDGET_ID, undefined);
@@ -5688,12 +5753,19 @@ function percentUsed(spentUsd, capUsd) {
5688
5753
  }
5689
5754
  function formatStats(stats) {
5690
5755
  const credits = stats.credit_credits;
5756
+ const remaining = stats.remaining_credits_today;
5757
+ const allowance = stats.allowance_credits;
5758
+ const refillAt = stats.refill_at;
5759
+ const lane = stats.lane;
5760
+ const todayLine = typeof remaining === "number" && typeof allowance === "number" && typeof refillAt === "string" ? formatCreditsLeftLine(remaining, allowance, new Date(refillAt)) : `today: ${percentUsed(stats.spent_usd_today, stats.cap_usd_today)}% of your free daily allowance used`;
5691
5761
  return [
5692
5762
  `tier: ${stats.tier}`,
5693
- `today: ${percentUsed(stats.spent_usd_today, stats.cap_usd_today)}% of your free daily allowance used`,
5763
+ todayLine,
5764
+ ...lane === "fallback" ? [SLOW_LANE_METER_LINE] : [],
5694
5765
  ...typeof credits === "number" ? [`credit: ${credits.toLocaleString("en-US")} credits remaining`] : [],
5695
5766
  `today tokens: ${stats.prompt_tokens_today} prompt / ${stats.completion_tokens_today} completion`,
5696
- `lifetime: ${stats.lifetime.prompt_tokens} prompt / ${stats.lifetime.completion_tokens} completion tokens`
5767
+ `lifetime: ${stats.lifetime.prompt_tokens} prompt / ${stats.lifetime.completion_tokens} completion tokens`,
5768
+ "buy more credits: /buy-credits"
5697
5769
  ].join(`
5698
5770
  `);
5699
5771
  }
@@ -6279,8 +6351,37 @@ function registerBugAutocomplete(pi) {
6279
6351
  });
6280
6352
  }
6281
6353
 
6354
+ // src/footer-align.ts
6355
+ var ANSI = /\x1b\[[0-9;]*m/g;
6356
+ var SIMPLE = /^[\x20-\x7e\xa0-\xff]*$/;
6357
+ function installFooterRightAlign(mode) {
6358
+ const footer = mode.footer;
6359
+ const render = footer.render.bind(footer);
6360
+ footer.render = (w) => {
6361
+ const lines = render(w);
6362
+ if (lines.length <= 2)
6363
+ return lines;
6364
+ const last = lines[lines.length - 1];
6365
+ const text = last.replace(ANSI, "");
6366
+ if (!SIMPLE.test(text))
6367
+ return lines;
6368
+ return [...lines.slice(0, -1), " ".repeat(Math.max(0, w - text.length)) + last];
6369
+ };
6370
+ }
6371
+
6282
6372
  // src/changelog.ts
6283
6373
  var CHANGELOG_HIGHLIGHTS = [
6374
+ {
6375
+ version: "0.4.2",
6376
+ highlights: ["Credits now sit on the right of the footer, under the model name."]
6377
+ },
6378
+ {
6379
+ version: "0.4.1",
6380
+ highlights: [
6381
+ "The footer now shows your credits left, including any you bought. A warning appears when you are low or in the slow lane.",
6382
+ "/usage shows credits left today and when they refill, with /buy-credits to get more."
6383
+ ]
6384
+ },
6284
6385
  {
6285
6386
  version: "0.4.0",
6286
6387
  highlights: [
@@ -6520,7 +6621,7 @@ function createFreePiCommandsExtension(opts) {
6520
6621
  handler: (_args, ctx) => runUpdate(ctx)
6521
6622
  });
6522
6623
  pi.registerCommand("usage", {
6523
- description: "Show how much of today's free allowance you have used, and your purchased credit.",
6624
+ description: "Show credits left today, refill time, and purchased credit.",
6524
6625
  handler: (_args, ctx) => showUsage(opts, ctx)
6525
6626
  });
6526
6627
  pi.registerCommand("buy-credits", {
@@ -6545,7 +6646,7 @@ function createFreePiCommandsExtension(opts) {
6545
6646
 
6546
6647
  // src/header.ts
6547
6648
  var COMMANDS = [
6548
- { name: "/usage", description: "spend and remaining budget today" },
6649
+ { name: "/usage", description: "credits left today and refill time" },
6549
6650
  { name: "/support", description: "visit today's advertiser" },
6550
6651
  { name: "/tos", description: "terms of service" },
6551
6652
  { name: "/privacy-policy", description: "privacy policy" },
@@ -6882,6 +6983,7 @@ async function launchPi(opts) {
6882
6983
  initialMessages: []
6883
6984
  });
6884
6985
  installBugHandler(mode, { baseUrl: opts.baseUrl, getToken: () => opts.jwt });
6986
+ installFooterRightAlign(mode);
6885
6987
  await mode.run();
6886
6988
  return sm.getSessionId();
6887
6989
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-pi-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
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",