free-pi-cli 0.4.4 → 0.4.6
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
|
@@ -45,7 +45,7 @@ function openBrowser(url, platform = process.platform, spawnImpl = spawn) {
|
|
|
45
45
|
// package.json
|
|
46
46
|
var package_default = {
|
|
47
47
|
name: "free-pi-cli",
|
|
48
|
-
version: "0.4.
|
|
48
|
+
version: "0.4.6",
|
|
49
49
|
description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
|
|
50
50
|
license: "MIT",
|
|
51
51
|
type: "module",
|
|
@@ -5840,6 +5840,9 @@ var F64Out = new Uint8Array(F64.buffer);
|
|
|
5840
5840
|
var encoder = new TextEncoder;
|
|
5841
5841
|
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/integer.mjs
|
|
5842
5842
|
var IntegerPattern = "-?(?:0|[1-9][0-9]*)";
|
|
5843
|
+
function Integer(options) {
|
|
5844
|
+
return Create({ "~kind": "Integer" }, { type: "integer" }, options);
|
|
5845
|
+
}
|
|
5843
5846
|
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/literal.mjs
|
|
5844
5847
|
class InvalidLiteralValue extends Error {
|
|
5845
5848
|
constructor(value) {
|
|
@@ -5970,6 +5973,106 @@ function createUsageToolExtension(opts) {
|
|
|
5970
5973
|
};
|
|
5971
5974
|
}
|
|
5972
5975
|
|
|
5976
|
+
// src/web-tools.ts
|
|
5977
|
+
var WEB_SEARCH_TOOL_NAME = "web_search";
|
|
5978
|
+
var DOCS_LOOKUP_TOOL_NAME = "docs_lookup";
|
|
5979
|
+
var COST_GUIDELINE = "Each web_search or docs_lookup call costs part of the user's daily allowance; one or two well-aimed calls usually suffice.";
|
|
5980
|
+
var DOCS_LOOKUP_GUIDELINES = [
|
|
5981
|
+
"Use docs_lookup before writing or fixing code against a third-party library, framework, SDK or API whose exact signature or config you are not sure of, and whenever an import, method or option you used turns out not to exist.",
|
|
5982
|
+
'Pass the package name as `library` (e.g. "next.js", "prisma", "@tanstack/react-query") and one specific question as `query` (e.g. "configure middleware matcher for API routes"), not a bare topic. Reuse the returned `library_id` for follow-up lookups on the same library.',
|
|
5983
|
+
COST_GUIDELINE
|
|
5984
|
+
];
|
|
5985
|
+
var WEB_SEARCH_GUIDELINES = [
|
|
5986
|
+
"Use web_search for things that change or are newer than your knowledge: release notes, current versions, exact error messages you cannot explain from the code, service status, and pages the user links or mentions. Prefer docs_lookup for library API docs, and read/grep for anything in the local project.",
|
|
5987
|
+
"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.",
|
|
5988
|
+
COST_GUIDELINE
|
|
5989
|
+
];
|
|
5990
|
+
var ERROR_TEXT = {
|
|
5991
|
+
daily_cap: "The user's daily free usage is used up. Do not retry; tell the user and continue without this tool.",
|
|
5992
|
+
rolling_cap: "The user's short-term usage limit is reached. Do not retry; continue without this tool.",
|
|
5993
|
+
rate_limited: "Too many calls right now. Wait for the current call to finish, and do not call again in this turn unless needed.",
|
|
5994
|
+
not_found: (library) => `No documentation library matched "${library}". Retry once with the package's exact name, or use web_search.`,
|
|
5995
|
+
not_configured: "This tool is not available on this server. Do not call it again in this session.",
|
|
5996
|
+
upstream_error: "The search provider failed. You may retry once."
|
|
5997
|
+
};
|
|
5998
|
+
async function post(opts, path, body, label, signal) {
|
|
5999
|
+
try {
|
|
6000
|
+
const timeout = AbortSignal.timeout(30000);
|
|
6001
|
+
const res = await (opts.fetchImpl ?? fetch)(`${opts.baseUrl}${path}`, {
|
|
6002
|
+
method: "POST",
|
|
6003
|
+
headers: { Authorization: `Bearer ${await opts.getToken()}`, "Content-Type": "application/json" },
|
|
6004
|
+
body: JSON.stringify(body),
|
|
6005
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout
|
|
6006
|
+
});
|
|
6007
|
+
if (res.ok)
|
|
6008
|
+
return { ok: true, data: await res.json() };
|
|
6009
|
+
const err = await res.json().catch(() => ({}));
|
|
6010
|
+
const mapped = ERROR_TEXT[err.code ?? ""];
|
|
6011
|
+
if (mapped)
|
|
6012
|
+
return { ok: false, text: typeof mapped === "function" ? mapped(label) : mapped };
|
|
6013
|
+
return { ok: false, text: `Request failed (HTTP ${res.status}): ${err.message ?? "no message"}` };
|
|
6014
|
+
} catch (e) {
|
|
6015
|
+
if (e instanceof Error && e.name === "TimeoutError")
|
|
6016
|
+
return { ok: false, text: "Request timed out after 30 seconds." };
|
|
6017
|
+
return { ok: false, text: `Request failed: ${String(e)}` };
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
var done = (o) => ({ content: [{ type: "text", text: o.text }], details: o.details });
|
|
6021
|
+
function createWebToolsExtension(opts) {
|
|
6022
|
+
return {
|
|
6023
|
+
name: "free-pi-web-tools",
|
|
6024
|
+
factory(pi) {
|
|
6025
|
+
pi.registerTool({
|
|
6026
|
+
name: WEB_SEARCH_TOOL_NAME,
|
|
6027
|
+
label: "Web Search",
|
|
6028
|
+
description: "Search the web and return a numbered list of results, each with title, URL, publish date and the page text. Use it for current or recent information that your training data may lack. Each call uses part of the user's daily allowance.",
|
|
6029
|
+
promptSnippet: "Search the web for current information (counts against the user's daily allowance)",
|
|
6030
|
+
promptGuidelines: WEB_SEARCH_GUIDELINES,
|
|
6031
|
+
parameters: _Object_({
|
|
6032
|
+
query: String2({ minLength: 1, maxLength: 500, description: 'Specific query as you would type it into a search engine, e.g. "vite 6 release notes breaking changes"' }),
|
|
6033
|
+
num_results: Optional(Integer({ minimum: 1, maximum: 10, description: "1-10, default 5; use 3 for a quick fact, 8-10 for a survey" }))
|
|
6034
|
+
}),
|
|
6035
|
+
async execute(_id, params, signal) {
|
|
6036
|
+
const r = await post(opts, "/tools/web-search", params, "", signal);
|
|
6037
|
+
if (!r.ok)
|
|
6038
|
+
return done({ text: r.text, details: { ok: false } });
|
|
6039
|
+
const results = r.data.results ?? [];
|
|
6040
|
+
const text = results.length ? `Web results for "${params.query}":
|
|
6041
|
+
|
|
6042
|
+
` + results.map((x, i) => `${i + 1}. ${x.title}
|
|
6043
|
+
${x.url}${x.published_date ? `
|
|
6044
|
+
Published: ${x.published_date}` : ""}
|
|
6045
|
+
${x.text}`).join(`
|
|
6046
|
+
|
|
6047
|
+
`) : `No results for "${params.query}". Try a broader or differently worded query.`;
|
|
6048
|
+
return done({ text, details: { ok: true, cost_usd: r.data.cost_usd } });
|
|
6049
|
+
}
|
|
6050
|
+
});
|
|
6051
|
+
pi.registerTool({
|
|
6052
|
+
name: DOCS_LOOKUP_TOOL_NAME,
|
|
6053
|
+
label: "Docs Lookup",
|
|
6054
|
+
description: "Look up current documentation for a third-party library, framework or API. Returns the matching library's title and library_id, then documentation text relevant to your query. Use it instead of relying on memory for exact signatures and options. Each call uses part of the user's daily allowance.",
|
|
6055
|
+
promptSnippet: "Look up library/framework documentation before relying on memory (counts against the user's daily allowance)",
|
|
6056
|
+
promptGuidelines: DOCS_LOOKUP_GUIDELINES,
|
|
6057
|
+
parameters: _Object_({
|
|
6058
|
+
library: String2({ minLength: 1, maxLength: 200, description: 'Package or library name, e.g. "next.js", "prisma", "@tanstack/react-query"' }),
|
|
6059
|
+
query: String2({ minLength: 1, maxLength: 500, description: 'One specific question, e.g. "configure middleware matcher for API routes"' }),
|
|
6060
|
+
library_id: Optional(String2({ description: "The library_id printed by an earlier docs_lookup, e.g. /vercel/next.js; skips the name search" }))
|
|
6061
|
+
}),
|
|
6062
|
+
async execute(_id, params, signal) {
|
|
6063
|
+
const r = await post(opts, "/tools/docs", params, params.library, signal);
|
|
6064
|
+
if (!r.ok)
|
|
6065
|
+
return done({ text: r.text, details: { ok: false } });
|
|
6066
|
+
const d = r.data;
|
|
6067
|
+
return done({ text: `${d.title} (library_id: ${d.library_id})
|
|
6068
|
+
|
|
6069
|
+
${d.docs}`, details: { ok: true, cost_usd: d.cost_usd } });
|
|
6070
|
+
}
|
|
6071
|
+
});
|
|
6072
|
+
}
|
|
6073
|
+
};
|
|
6074
|
+
}
|
|
6075
|
+
|
|
5973
6076
|
// src/buy-tool.ts
|
|
5974
6077
|
async function resolveBuyPage(opts) {
|
|
5975
6078
|
try {
|
|
@@ -6602,6 +6705,18 @@ function installFooterRightAlign(mode) {
|
|
|
6602
6705
|
|
|
6603
6706
|
// src/changelog.ts
|
|
6604
6707
|
var CHANGELOG_HIGHLIGHTS = [
|
|
6708
|
+
{
|
|
6709
|
+
version: "0.4.6",
|
|
6710
|
+
highlights: [
|
|
6711
|
+
"Free web search and documentation lookup: the agent can now search the web (web_search) and look up current library docs (docs_lookup). Both run on the free-pi server and count toward your daily allowance."
|
|
6712
|
+
]
|
|
6713
|
+
},
|
|
6714
|
+
{
|
|
6715
|
+
version: "0.4.5",
|
|
6716
|
+
highlights: [
|
|
6717
|
+
"New /reverse-engineer-anything command: loads REA reverse-engineering tools (apps, binaries, APKs, websites) into the session. For lawful, authorized analysis only."
|
|
6718
|
+
]
|
|
6719
|
+
},
|
|
6605
6720
|
{
|
|
6606
6721
|
version: "0.4.4",
|
|
6607
6722
|
highlights: [
|
|
@@ -6848,6 +6963,33 @@ async function showUsage(opts, ctx) {
|
|
|
6848
6963
|
const r = await fetchUsageText(opts);
|
|
6849
6964
|
ctx.ui.notify(r.text, r.ok ? "info" : "error");
|
|
6850
6965
|
}
|
|
6966
|
+
var REA_ARGS = ["-y", "rea-agents@6.7.0", "mcp"];
|
|
6967
|
+
var REA_DESCRIPTION = "Reverse-engineering tools (REA): native binaries, JS/Electron apps, APKs, .NET, websites. Use for lawful, authorized analysis only.";
|
|
6968
|
+
var REA_NODE_RANGE = "^22.19.0, ^24.11.0, or >=26.0.0";
|
|
6969
|
+
function reaSupportsNode(version) {
|
|
6970
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
6971
|
+
return major >= 26 || major === 24 && minor >= 11 || major === 22 && minor >= 19;
|
|
6972
|
+
}
|
|
6973
|
+
function loadRea(pi, opts, ctx) {
|
|
6974
|
+
const nodeVersion = opts.nodeVersion ?? process.versions.node;
|
|
6975
|
+
if (!reaSupportsNode(nodeVersion)) {
|
|
6976
|
+
ctx.ui.notify(`REA needs Node ${REA_NODE_RANGE}; you are on Node ${nodeVersion}. Update Node and restart free-pi-cli.`, "error");
|
|
6977
|
+
return;
|
|
6978
|
+
}
|
|
6979
|
+
if (pi.getMcpServers().some((s) => s.name === "rea")) {
|
|
6980
|
+
ctx.ui.notify("REA is already loaded in this session.", "info");
|
|
6981
|
+
return;
|
|
6982
|
+
}
|
|
6983
|
+
const win = (opts.platform ?? process.platform) === "win32";
|
|
6984
|
+
const server = opts.reaServer ?? (win ? { command: "cmd.exe", args: ["/d", "/c", "npx", ...REA_ARGS] } : { command: "npx", args: REA_ARGS });
|
|
6985
|
+
try {
|
|
6986
|
+
pi.registerMcpServer("rea", { ...server, exposure: "codemode", description: REA_DESCRIPTION, timeout: 300 });
|
|
6987
|
+
} catch {
|
|
6988
|
+
ctx.ui.notify("Couldn't load REA in this session.", "error");
|
|
6989
|
+
return;
|
|
6990
|
+
}
|
|
6991
|
+
ctx.ui.notify("Loading REA (reverse-engineering tools) for this session. The first run downloads rea-agents, so it can take a minute. Deep native analysis needs your own Hopper, Ghidra, or IDA install.", "info");
|
|
6992
|
+
}
|
|
6851
6993
|
function createFreePiCommandsExtension(opts) {
|
|
6852
6994
|
return {
|
|
6853
6995
|
name: "free-pi-commands",
|
|
@@ -6885,6 +7027,10 @@ function createFreePiCommandsExtension(opts) {
|
|
|
6885
7027
|
description: "Open the free-pi Privacy Policy in your browser.",
|
|
6886
7028
|
handler: (_args, ctx) => openPrivacyPolicy(opts, ctx)
|
|
6887
7029
|
});
|
|
7030
|
+
pi.registerCommand("reverse-engineer-anything", {
|
|
7031
|
+
description: "Load reverse-engineering tools (REA: binaries, apps, APKs, websites) for this session.",
|
|
7032
|
+
handler: async (_args, ctx) => loadRea(pi, opts, ctx)
|
|
7033
|
+
});
|
|
6888
7034
|
}
|
|
6889
7035
|
};
|
|
6890
7036
|
}
|
|
@@ -7145,6 +7291,8 @@ var ALLOWED_TOOL_NAMES = [
|
|
|
7145
7291
|
USAGE_TOOL_NAME,
|
|
7146
7292
|
BUY_TOOL_NAME,
|
|
7147
7293
|
DOCS_TOOL_NAME,
|
|
7294
|
+
WEB_SEARCH_TOOL_NAME,
|
|
7295
|
+
DOCS_LOOKUP_TOOL_NAME,
|
|
7148
7296
|
TODO_TOOL_NAME,
|
|
7149
7297
|
"codemode",
|
|
7150
7298
|
"tool_search"
|
|
@@ -7203,6 +7351,10 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
7203
7351
|
getToken: () => opts.jwt
|
|
7204
7352
|
});
|
|
7205
7353
|
const docsToolExtension = createDocsToolExtension();
|
|
7354
|
+
const webToolsExtension = createWebToolsExtension({
|
|
7355
|
+
baseUrl: opts.baseUrl,
|
|
7356
|
+
getToken: () => opts.jwt
|
|
7357
|
+
});
|
|
7206
7358
|
const errorNoticeExtension = createErrorNoticeExtension();
|
|
7207
7359
|
const toolGuardExtension = createToolGuardExtension({
|
|
7208
7360
|
baseUrl: opts.baseUrl,
|
|
@@ -7230,6 +7382,7 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
7230
7382
|
usageToolExtension,
|
|
7231
7383
|
buyToolExtension,
|
|
7232
7384
|
docsToolExtension,
|
|
7385
|
+
webToolsExtension,
|
|
7233
7386
|
errorNoticeExtension,
|
|
7234
7387
|
toolGuardExtension,
|
|
7235
7388
|
commandsExtension,
|