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 +1 -1
- package/dist/{main-ep5298x9.js → main-fka3s7gp.js} +216 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
8744
|
+
const r = await post2(opts, "/tools/web-search", params, "", signal);
|
|
8609
8745
|
if (!r.ok)
|
|
8610
|
-
return
|
|
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
|
|
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
|
|
8771
|
+
const r = await post2(opts, "/tools/docs", params, params.library, signal);
|
|
8636
8772
|
if (!r.ok)
|
|
8637
|
-
return
|
|
8773
|
+
return done2({ text: r.text, details: { ok: false } });
|
|
8638
8774
|
const d = r.data;
|
|
8639
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
10149
|
-
const readyFile =
|
|
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,
|
|
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),
|