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
@@ -27,4 +27,4 @@ if (err) {
27
27
  console.error(err);
28
28
  process.exit(1);
29
29
  }
30
- await import("./main-4wwh354d.js");
30
+ await import("./main-cksegxnv.js");
@@ -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.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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-pi-cli",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
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",