free-pi-cli 0.4.7 → 0.4.8

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-ep5298x9.js");
30
+ await import("./main-fka3s7gp.js");
@@ -3,7 +3,7 @@
3
3
  // src/main.ts
4
4
  import { spawn as spawn2 } from "node:child_process";
5
5
  import { realpathSync, writeFileSync as writeFileSync2 } from "node:fs";
6
- import { dirname as dirname5, join as join6 } from "node:path";
6
+ import { dirname as dirname5, join as join7 } from "node:path";
7
7
 
8
8
  // src/browser.ts
9
9
  import { spawn } from "node:child_process";
@@ -46,7 +46,7 @@ function openBrowser(url, platform = process.platform, spawnImpl = spawn) {
46
46
  // package.json
47
47
  var package_default = {
48
48
  name: "free-pi-cli",
49
- version: "0.4.7",
49
+ version: "0.4.8",
50
50
  description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
51
51
  license: "MIT",
52
52
  type: "module",
@@ -4970,6 +4970,7 @@ var ConfigSchema = object({
4970
4970
  abuseRestrictedCapUsd: number2().default(0.5),
4971
4971
  abuseRestrictedHourlyCap: number2().int().default(5),
4972
4972
  abuseNormalHourlyCap: number2().int().default(30),
4973
+ abuseSignedInNormalHourlyCap: number2().int().default(0),
4973
4974
  abuseFlipAlertThreshold: number2().int().default(10),
4974
4975
  abuseAdBypassMinRequests: number2().int().default(20),
4975
4976
  abuseAdBypassRatio: number2().int().default(10),
@@ -5042,6 +5043,7 @@ function loadConfig(env = process.env) {
5042
5043
  abuseRestrictedCapUsd: envNumber(env.ABUSE_RESTRICTED_CAP_USD),
5043
5044
  abuseRestrictedHourlyCap: envNumber(env.ABUSE_RESTRICTED_HOURLY_CAP),
5044
5045
  abuseNormalHourlyCap: envNumber(env.ABUSE_NORMAL_HOURLY_CAP),
5046
+ abuseSignedInNormalHourlyCap: envNumber(env.ABUSE_SIGNED_IN_NORMAL_HOURLY_CAP),
5045
5047
  abuseFlipAlertThreshold: envNumber(env.ABUSE_FLIP_ALERT_THRESHOLD),
5046
5048
  abuseAdBypassMinRequests: envNumber(env.ABUSE_AD_BYPASS_MIN_REQUESTS),
5047
5049
  abuseAdBypassRatio: envNumber(env.ABUSE_AD_BYPASS_RATIO),
@@ -5086,6 +5088,42 @@ function formatCreditsLeftLine(remaining, allowance, refillAt) {
5086
5088
  return `${left} / ${total} credits left today · refills ${formatRefillEt(refillAt)}`;
5087
5089
  }
5088
5090
  var SLOW_LANE_METER_LINE = "Slow lane: cheapest model until refill";
5091
+ var LIMIT_NOTICE_LABELS = {
5092
+ hourly_ceiling: "hourly limit",
5093
+ daily_cap: "daily limit",
5094
+ rolling_cap: "rolling limit",
5095
+ global_cap: "service daily limit",
5096
+ queue_wait: "queue wait",
5097
+ concurrent: "one request at a time",
5098
+ concurrent_session: "another session is active"
5099
+ };
5100
+ var EXTRA_CREDITS_URL = "freepi.ai/extra-credits";
5101
+ var LIMIT_NOTICE_LOCAL_TIME_AFTER_SECONDS = 90 * 60;
5102
+ function formatLimitNotice(fields, opts = {}) {
5103
+ const slow = fields.lane === "slow" && fields.limit === "hourly_ceiling" && fields.hourly_ceiling != null;
5104
+ const label = slow ? `You've used today's free allowance, so you're on the slow lane (${fields.hourly_ceiling.toLocaleString("en-US")} requests an hour).` : LIMIT_NOTICE_LABELS[fields.limit] ?? fields.limit.replace(/_/g, " ");
5105
+ const parts = [label];
5106
+ const retry = fields.retry_after_seconds;
5107
+ if (retry != null && retry > 0) {
5108
+ const useClock = Boolean(opts.localTime) && Boolean(fields.clears_at) && retry >= LIMIT_NOTICE_LOCAL_TIME_AFTER_SECONDS;
5109
+ if (useClock) {
5110
+ const formatted = new Intl.DateTimeFormat("en-US", {
5111
+ timeZone: opts.timeZone,
5112
+ hour: "numeric",
5113
+ minute: "2-digit"
5114
+ }).format(new Date(fields.clears_at));
5115
+ parts.push(`back at ${formatted}`);
5116
+ } else {
5117
+ parts.push(`back in ${Math.max(1, Math.ceil(retry / 60))} min`);
5118
+ }
5119
+ }
5120
+ if (slow) {
5121
+ const wait = parts.length > 1 ? ` ${parts[1][0].toUpperCase()}${parts[1].slice(1)} — or get` : " Get";
5122
+ return `${label}${wait} credits at ${EXTRA_CREDITS_URL} to keep going at full speed.`;
5123
+ }
5124
+ parts.push(`${fields.remaining_credits_today.toLocaleString("en-US")} credits left today`);
5125
+ return parts.join(", ");
5126
+ }
5089
5127
  // src/gravity-pixel.ts
5090
5128
  var GRAVITY_PIXEL_ID = "6cd74804-0541-4d9b-899d-d7fceb465b6f";
5091
5129
  function userIdFromJwt(jwt) {
@@ -8129,7 +8167,7 @@ function createAdsExtension(opts) {
8129
8167
  }
8130
8168
 
8131
8169
  // src/pi-launch.ts
8132
- import { dirname as dirname3, join as join4, resolve as resolve2 } from "node:path";
8170
+ import { dirname as dirname3, join as join5, resolve as resolve2 } from "node:path";
8133
8171
  import { fileURLToPath as fileURLToPath3 } from "node:url";
8134
8172
 
8135
8173
  // src/provider.ts
@@ -8545,6 +8583,104 @@ function createUsageToolExtension(opts) {
8545
8583
  };
8546
8584
  }
8547
8585
 
8586
+ // src/image-tool.ts
8587
+ import { randomBytes } from "node:crypto";
8588
+ import { mkdir as mkdir2, open } from "node:fs/promises";
8589
+ import { join as join3 } from "node:path";
8590
+ var IMAGE_TOOL_NAME = "generate_image";
8591
+ var MAX_BYTES = 8 * 1024 * 1024;
8592
+ var TIMEOUT_MS = 120000;
8593
+ var EXT = { "image/png": "png", "image/jpeg": "jpg", "image/webp": "webp" };
8594
+ var IMAGE_GUIDELINES = [
8595
+ "Use generate_image only when the user asks for an image, mockup, icon, illustration or other visual asset. Do not call it for diagrams you can write as code, SVG or ASCII.",
8596
+ "Make one call per asset. Put the style, any exact text to render, and the composition in the prompt, and pick aspect_ratio to suit the use (16:9 for banners, 1:1 for icons).",
8597
+ "You cannot see the generated image. The tool saves it to a file and returns the relative path; refer to that path in code, docs or your answer, and tell the user you could not check how it looks.",
8598
+ "Each image uses part of the user's daily allowance, so do not regenerate unless the user asks."
8599
+ ];
8600
+ var ERROR_TEXT = {
8601
+ daily_cap: "The user's free allowance for today is used up. Do not retry. Tell the user they can get credits at freepi.ai/extra-credits, and continue without this tool.",
8602
+ rolling_cap: "The user's short-term usage limit is reached. Do not retry; continue without this tool.",
8603
+ rate_limited: "Too many image requests right now. Do not call generate_image again in this turn unless the user asks.",
8604
+ not_configured: "Image generation is not available on this server. Do not call generate_image again in this session.",
8605
+ upstream_error: "The image provider failed. You may retry once.",
8606
+ account_review: "This account is under review, so image generation is disabled. Do not retry; tell the user and continue without it.",
8607
+ content_rejected: "The image provider rejected this prompt. Do not retry the same prompt; tell the user and offer to reword it."
8608
+ };
8609
+ async function post(opts, body, signal) {
8610
+ try {
8611
+ const timeout = AbortSignal.timeout(TIMEOUT_MS);
8612
+ const res = await (opts.fetchImpl ?? fetch)(`${opts.baseUrl}/tools/image`, {
8613
+ method: "POST",
8614
+ headers: { Authorization: `Bearer ${await opts.getToken()}`, "Content-Type": "application/json" },
8615
+ body: JSON.stringify(body),
8616
+ signal: signal ? AbortSignal.any([signal, timeout]) : timeout
8617
+ });
8618
+ if (res.ok)
8619
+ return { ok: true, data: await res.json() };
8620
+ const j = await res.json().catch(() => ({}));
8621
+ const code = j.error?.code ?? j.code ?? "";
8622
+ const mapped = ERROR_TEXT[code];
8623
+ if (mapped)
8624
+ return { ok: false, text: mapped };
8625
+ return { ok: false, text: `Request failed (HTTP ${res.status}): ${j.error?.message ?? j.message ?? "no message"}` };
8626
+ } catch (e) {
8627
+ if (e instanceof Error && e.name === "TimeoutError")
8628
+ return { ok: false, text: "Image generation timed out after 120 seconds." };
8629
+ return { ok: false, text: `Request failed: ${String(e)}` };
8630
+ }
8631
+ }
8632
+ function slug(s) {
8633
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+/, "").slice(0, 40).replace(/-+$/, "") || "image";
8634
+ }
8635
+ var done = (text, details) => ({ content: [{ type: "text", text }], details });
8636
+ var fail = (text) => done(text, { ok: false });
8637
+ function createImageToolExtension(opts) {
8638
+ return {
8639
+ name: "free-pi-image-tool",
8640
+ factory(pi) {
8641
+ pi.registerTool({
8642
+ name: IMAGE_TOOL_NAME,
8643
+ label: "Generate Image",
8644
+ description: "Generate one image from a text prompt and save it as a file under images/ in the project. Returns the file path; you cannot see the image. Use it only when the user asks for an image, mockup, icon or illustration. Each call uses part of the user's daily allowance.",
8645
+ promptSnippet: "Generate an image file from a prompt when the user asks for one (counts against the user's daily allowance)",
8646
+ promptGuidelines: IMAGE_GUIDELINES,
8647
+ parameters: _Object_({
8648
+ prompt: String2({ minLength: 1, maxLength: 1000, description: "What to draw: subject, style, any exact text to render, and composition" }),
8649
+ aspect_ratio: Optional(Union([Literal("1:1"), Literal("16:9"), Literal("9:16"), Literal("4:3"), Literal("3:2")], { description: "Default 1:1" })),
8650
+ name: Optional(String2({ maxLength: 60, description: "Short file name hint, e.g. hero-banner; used only for the saved file's name" }))
8651
+ }),
8652
+ async execute(_id, params, signal) {
8653
+ const r = await post(opts, { prompt: params.prompt, ...params.aspect_ratio ? { aspect_ratio: params.aspect_ratio } : {} }, signal);
8654
+ if (!r.ok)
8655
+ return fail(r.text);
8656
+ const d = r.data;
8657
+ const ext = EXT[d.media_type];
8658
+ if (!ext)
8659
+ return fail(`The server returned an unsupported image type (${d.media_type}). Nothing was saved.`);
8660
+ const buf = Buffer.from(d.image_b64 ?? "", "base64");
8661
+ if (buf.length === 0)
8662
+ return fail("The server returned an empty image. Nothing was saved.");
8663
+ if (buf.length > MAX_BYTES)
8664
+ return fail("The generated image was too large to save (over 8 MB). Nothing was saved.");
8665
+ const rel = `images/${slug(params.name ?? params.prompt)}-${randomBytes(3).toString("hex")}.${ext}`;
8666
+ try {
8667
+ await mkdir2(join3(opts.cwd ?? process.cwd(), "images"), { recursive: true });
8668
+ const fh = await open(join3(opts.cwd ?? process.cwd(), rel), "wx");
8669
+ try {
8670
+ await fh.writeFile(buf);
8671
+ } finally {
8672
+ await fh.close();
8673
+ }
8674
+ } catch (e) {
8675
+ return fail(`Could not save the image: ${String(e)}`);
8676
+ }
8677
+ return done(`Saved image to ${rel} (${ext}, ${buf.length} bytes). You cannot see the image; reference this path in your answer or code, and tell the user you could not check how it looks.`, { ok: true, path: rel, cost_usd: d.cost_usd, model: d.model });
8678
+ }
8679
+ });
8680
+ }
8681
+ };
8682
+ }
8683
+
8548
8684
  // src/web-tools.ts
8549
8685
  var WEB_SEARCH_TOOL_NAME = "web_search";
8550
8686
  var DOCS_LOOKUP_TOOL_NAME = "docs_lookup";
@@ -8559,7 +8695,7 @@ var WEB_SEARCH_GUIDELINES = [
8559
8695
  "Write a specific query the way you would type it into a search engine (include the product name, the version and the exact error text). Read the returned page text before searching again, and do not repeat a query that already returned results. Cite the URLs you relied on in your answer.",
8560
8696
  COST_GUIDELINE
8561
8697
  ];
8562
- var ERROR_TEXT = {
8698
+ var ERROR_TEXT2 = {
8563
8699
  daily_cap: "The user's daily free usage is used up. Do not retry; tell the user and continue without this tool.",
8564
8700
  rolling_cap: "The user's short-term usage limit is reached. Do not retry; continue without this tool.",
8565
8701
  rate_limited: "Too many calls right now. Wait for the current call to finish, and do not call again in this turn unless needed.",
@@ -8567,7 +8703,7 @@ var ERROR_TEXT = {
8567
8703
  not_configured: "This tool is not available on this server. Do not call it again in this session.",
8568
8704
  upstream_error: "The search provider failed. You may retry once."
8569
8705
  };
8570
- async function post(opts, path, body, label, signal) {
8706
+ async function post2(opts, path, body, label, signal) {
8571
8707
  try {
8572
8708
  const timeout = AbortSignal.timeout(30000);
8573
8709
  const res = await (opts.fetchImpl ?? fetch)(`${opts.baseUrl}${path}`, {
@@ -8579,7 +8715,7 @@ async function post(opts, path, body, label, signal) {
8579
8715
  if (res.ok)
8580
8716
  return { ok: true, data: await res.json() };
8581
8717
  const err = await res.json().catch(() => ({}));
8582
- const mapped = ERROR_TEXT[err.code ?? ""];
8718
+ const mapped = ERROR_TEXT2[err.code ?? ""];
8583
8719
  if (mapped)
8584
8720
  return { ok: false, text: typeof mapped === "function" ? mapped(label) : mapped };
8585
8721
  return { ok: false, text: `Request failed (HTTP ${res.status}): ${err.message ?? "no message"}` };
@@ -8589,7 +8725,7 @@ async function post(opts, path, body, label, signal) {
8589
8725
  return { ok: false, text: `Request failed: ${String(e)}` };
8590
8726
  }
8591
8727
  }
8592
- var done = (o) => ({ content: [{ type: "text", text: o.text }], details: o.details });
8728
+ var done2 = (o) => ({ content: [{ type: "text", text: o.text }], details: o.details });
8593
8729
  function createWebToolsExtension(opts) {
8594
8730
  return {
8595
8731
  name: "free-pi-web-tools",
@@ -8605,9 +8741,9 @@ function createWebToolsExtension(opts) {
8605
8741
  num_results: Optional(Integer({ minimum: 1, maximum: 10, description: "1-10, default 5; use 3 for a quick fact, 8-10 for a survey" }))
8606
8742
  }),
8607
8743
  async execute(_id, params, signal) {
8608
- const r = await post(opts, "/tools/web-search", params, "", signal);
8744
+ const r = await post2(opts, "/tools/web-search", params, "", signal);
8609
8745
  if (!r.ok)
8610
- return done({ text: r.text, details: { ok: false } });
8746
+ return done2({ text: r.text, details: { ok: false } });
8611
8747
  const results = r.data.results ?? [];
8612
8748
  const text = results.length ? `Web results for "${params.query}":
8613
8749
 
@@ -8617,7 +8753,7 @@ Published: ${x.published_date}` : ""}
8617
8753
  ${x.text}`).join(`
8618
8754
 
8619
8755
  `) : `No results for "${params.query}". Try a broader or differently worded query.`;
8620
- return done({ text, details: { ok: true, cost_usd: r.data.cost_usd } });
8756
+ return done2({ text, details: { ok: true, cost_usd: r.data.cost_usd } });
8621
8757
  }
8622
8758
  });
8623
8759
  pi.registerTool({
@@ -8632,11 +8768,11 @@ ${x.text}`).join(`
8632
8768
  library_id: Optional(String2({ description: "The library_id printed by an earlier docs_lookup, e.g. /vercel/next.js; skips the name search" }))
8633
8769
  }),
8634
8770
  async execute(_id, params, signal) {
8635
- const r = await post(opts, "/tools/docs", params, params.library, signal);
8771
+ const r = await post2(opts, "/tools/docs", params, params.library, signal);
8636
8772
  if (!r.ok)
8637
- return done({ text: r.text, details: { ok: false } });
8773
+ return done2({ text: r.text, details: { ok: false } });
8638
8774
  const d = r.data;
8639
- return done({ text: `${d.title} (library_id: ${d.library_id})
8775
+ return done2({ text: `${d.title} (library_id: ${d.library_id})
8640
8776
 
8641
8777
  ${d.docs}`, details: { ok: true, cost_usd: d.cost_usd } });
8642
8778
  }
@@ -9051,7 +9187,7 @@ function createTodoToolExtension(options = {}) {
9051
9187
 
9052
9188
  // src/tool-aliases.ts
9053
9189
  import { homedir as homedir2 } from "node:os";
9054
- import { join as join3 } from "node:path";
9190
+ import { join as join4 } from "node:path";
9055
9191
  import { fileURLToPath as fileURLToPath2 } from "node:url";
9056
9192
  import {
9057
9193
  createBashToolDefinition,
@@ -9077,7 +9213,7 @@ function normalizeShellPath(input, platform = process.platform, home = homedir2(
9077
9213
  if (p === "~")
9078
9214
  return home;
9079
9215
  if (p.startsWith("~/") || platform === "win32" && p.startsWith("~\\"))
9080
- return join3(home, p.slice(2));
9216
+ return join4(home, p.slice(2));
9081
9217
  if (/^file:\/\//.test(p))
9082
9218
  return fileURLToPath2(p);
9083
9219
  return p;
@@ -9109,6 +9245,12 @@ function createToolAliasesExtension() {
9109
9245
 
9110
9246
  // src/error-notice.ts
9111
9247
  var NOTICE_DEDUPE_MS = 15000;
9248
+ function optionalNumber(value) {
9249
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
9250
+ }
9251
+ function optionalString(value) {
9252
+ return typeof value === "string" && value.trim() !== "" ? value : undefined;
9253
+ }
9112
9254
  function parseErrorEnvelope(errorMessage) {
9113
9255
  if (typeof errorMessage !== "string")
9114
9256
  return;
@@ -9121,14 +9263,53 @@ function parseErrorEnvelope(errorMessage) {
9121
9263
  const code = body.code ?? body.error?.code;
9122
9264
  if (typeof message !== "string" || message.trim() === "")
9123
9265
  return;
9124
- return { status: Number(m[1]), code: typeof code === "string" ? code : "unknown", message };
9266
+ const parsed = {
9267
+ status: Number(m[1]),
9268
+ code: typeof code === "string" ? code : "unknown",
9269
+ message
9270
+ };
9271
+ const limit = optionalString(body.limit);
9272
+ const retryAfter = optionalNumber(body.retry_after_seconds);
9273
+ const clearsAt = optionalString(body.clears_at);
9274
+ const remaining = optionalNumber(body.remaining_credits_today);
9275
+ const allowance = optionalNumber(body.allowance_credits);
9276
+ if (limit)
9277
+ parsed.limit = limit;
9278
+ if (retryAfter !== undefined)
9279
+ parsed.retry_after_seconds = retryAfter;
9280
+ if (clearsAt)
9281
+ parsed.clears_at = clearsAt;
9282
+ if (remaining !== undefined)
9283
+ parsed.remaining_credits_today = remaining;
9284
+ if (allowance !== undefined)
9285
+ parsed.allowance_credits = allowance;
9286
+ const ceiling = optionalNumber(body.hourly_ceiling);
9287
+ if (body.lane === "slow")
9288
+ parsed.lane = "slow";
9289
+ if (ceiling !== undefined)
9290
+ parsed.hourly_ceiling = ceiling;
9291
+ return parsed;
9125
9292
  } catch {
9126
9293
  return;
9127
9294
  }
9128
9295
  }
9296
+ function formatLimitEnvelope(env, opts) {
9297
+ if (env.code === "concurrent_session" || typeof env.remaining_credits_today !== "number")
9298
+ return env.message;
9299
+ return formatLimitNotice({
9300
+ limit: env.limit ?? env.code,
9301
+ retry_after_seconds: env.retry_after_seconds,
9302
+ clears_at: env.clears_at,
9303
+ remaining_credits_today: env.remaining_credits_today,
9304
+ allowance_credits: env.allowance_credits,
9305
+ lane: env.lane,
9306
+ hourly_ceiling: env.hourly_ceiling
9307
+ }, { localTime: true, timeZone: opts?.timeZone });
9308
+ }
9129
9309
  function createErrorNoticeExtension(opts = {}) {
9130
9310
  const lastAt = new Map;
9131
9311
  const now = opts.now ?? Date.now;
9312
+ const timeZone = opts.timeZone;
9132
9313
  return {
9133
9314
  name: "free-pi-error-notice",
9134
9315
  factory(pi) {
@@ -9143,7 +9324,7 @@ function createErrorNoticeExtension(opts = {}) {
9143
9324
  if (t - (lastAt.get(env.code) ?? -Infinity) < NOTICE_DEDUPE_MS)
9144
9325
  return;
9145
9326
  lastAt.set(env.code, t);
9146
- ctx.ui.notify(env.message, env.status >= 500 ? "error" : "warning");
9327
+ ctx.ui.notify(formatLimitEnvelope(env, { timeZone }), env.status >= 500 ? "error" : "warning");
9147
9328
  });
9148
9329
  }
9149
9330
  };
@@ -9277,6 +9458,13 @@ function installFooterRightAlign(mode) {
9277
9458
 
9278
9459
  // src/changelog.ts
9279
9460
  var CHANGELOG_HIGHLIGHTS = [
9461
+ {
9462
+ version: "0.4.8",
9463
+ highlights: [
9464
+ "The agent can now make images: ask for a mockup, icon or illustration and generate_image saves it into your project's images/ folder. Each image counts toward your daily allowance.",
9465
+ "Clearer limit messages: you now see when a limit clears and how many credits you have left today."
9466
+ ]
9467
+ },
9280
9468
  {
9281
9469
  version: "0.4.7",
9282
9470
  highlights: [
@@ -9872,6 +10060,7 @@ var ALLOWED_TOOL_NAMES = [
9872
10060
  DOCS_TOOL_NAME,
9873
10061
  WEB_SEARCH_TOOL_NAME,
9874
10062
  DOCS_LOOKUP_TOOL_NAME,
10063
+ IMAGE_TOOL_NAME,
9875
10064
  TODO_TOOL_NAME,
9876
10065
  "codemode",
9877
10066
  "tool_search"
@@ -9934,6 +10123,10 @@ function buildRuntimeOptions(opts, sessionId) {
9934
10123
  baseUrl: opts.baseUrl,
9935
10124
  getToken: () => opts.jwt
9936
10125
  });
10126
+ const imageToolExtension = createImageToolExtension({
10127
+ baseUrl: opts.baseUrl,
10128
+ getToken: () => opts.jwt
10129
+ });
9937
10130
  const errorNoticeExtension = createErrorNoticeExtension();
9938
10131
  const toolGuardExtension = createToolGuardExtension({
9939
10132
  baseUrl: opts.baseUrl,
@@ -9962,6 +10155,7 @@ function buildRuntimeOptions(opts, sessionId) {
9962
10155
  buyToolExtension,
9963
10156
  docsToolExtension,
9964
10157
  webToolsExtension,
10158
+ imageToolExtension,
9965
10159
  errorNoticeExtension,
9966
10160
  toolGuardExtension,
9967
10161
  commandsExtension,
@@ -9987,7 +10181,7 @@ function buildRuntimeOptions(opts, sessionId) {
9987
10181
  function getSessionDir(cwd, agentDir) {
9988
10182
  const resolvedCwd = resolve2(cwd);
9989
10183
  const safePath = `--${resolvedCwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
9990
- return join4(resolve2(agentDir), "sessions", safePath);
10184
+ return join5(resolve2(agentDir), "sessions", safePath);
9991
10185
  }
9992
10186
  async function createSessionManager(cwd, sessionDir, sessionId, api = SessionManager) {
9993
10187
  if (sessionId) {
@@ -10132,7 +10326,7 @@ function createRealSpawn(io = { platform: process.platform, spawnSyncImpl: spawn
10132
10326
  // src/relaunch.ts
10133
10327
  import { mkdtempSync, existsSync as existsSync2, rmSync } from "node:fs";
10134
10328
  import { constants as osConstants, tmpdir } from "node:os";
10135
- import { join as join5 } from "node:path";
10329
+ import { join as join6 } from "node:path";
10136
10330
  var SEMVER = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
10137
10331
  var WIN_SAFE_ARG = /^[A-Za-z0-9._:=\/@+,-]+$/;
10138
10332
  async function relaunchViaNpx(latest, deps) {
@@ -10145,8 +10339,8 @@ async function relaunchViaNpx(latest, deps) {
10145
10339
  if (win && !deps.argv.every((a) => WIN_SAFE_ARG.test(a)))
10146
10340
  return;
10147
10341
  const target = latest && SEMVER.test(latest) ? latest : "latest";
10148
- const readyDir = mkdtempSync(join5(tmpdir(), "free-pi-relaunch-"));
10149
- const readyFile = join5(readyDir, "started");
10342
+ const readyDir = mkdtempSync(join6(tmpdir(), "free-pi-relaunch-"));
10343
+ const readyFile = join6(readyDir, "started");
10150
10344
  try {
10151
10345
  deps.log(`Updating free-pi to ${target}…`);
10152
10346
  return await new Promise((resolve) => {
@@ -10576,7 +10770,7 @@ ${HELP_TEXT}`);
10576
10770
  launchPi,
10577
10771
  log: (message) => console.log(message),
10578
10772
  checkClientVersion,
10579
- startGravityPixel: (jwt) => startGravityPixel(jwt, join6(dirname5(agentDir), "gravity")),
10773
+ startGravityPixel: (jwt) => startGravityPixel(jwt, join7(dirname5(agentDir), "gravity")),
10580
10774
  relaunchLatest: (latest) => relaunchViaNpx(latest, {
10581
10775
  env: process.env,
10582
10776
  argv: process.argv.slice(2),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-pi-cli",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
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",