free-pi-cli 0.4.2 → 0.4.4
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-xkn61g24.js +17974 -0
- package/dist/index.js +1 -1
- package/dist/{main-37kszx0y.js → main-4wwh354d.js} +470 -93
- package/package.json +4 -2
- package/skills/long-jobs/SKILL.md +14 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
|
+
import { dirname as dirname5, join as join5 } from "node:path";
|
|
5
6
|
|
|
6
7
|
// src/browser.ts
|
|
7
8
|
import { spawn } from "node:child_process";
|
|
@@ -44,7 +45,7 @@ function openBrowser(url, platform = process.platform, spawnImpl = spawn) {
|
|
|
44
45
|
// package.json
|
|
45
46
|
var package_default = {
|
|
46
47
|
name: "free-pi-cli",
|
|
47
|
-
version: "0.4.
|
|
48
|
+
version: "0.4.4",
|
|
48
49
|
description: "Free, ad-supported distribution of the pi coding agent. Inference paid for by ads.",
|
|
49
50
|
license: "MIT",
|
|
50
51
|
type: "module",
|
|
@@ -53,6 +54,7 @@ var package_default = {
|
|
|
53
54
|
},
|
|
54
55
|
files: [
|
|
55
56
|
"dist",
|
|
57
|
+
"skills",
|
|
56
58
|
"README.md"
|
|
57
59
|
],
|
|
58
60
|
engines: {
|
|
@@ -63,7 +65,8 @@ var package_default = {
|
|
|
63
65
|
prepublishOnly: "bun run build"
|
|
64
66
|
},
|
|
65
67
|
dependencies: {
|
|
66
|
-
"@earendil-works/pi-coding-agent": "1.2.0"
|
|
68
|
+
"@earendil-works/pi-coding-agent": "1.2.0",
|
|
69
|
+
"@gravity-ai/api": "1.2.1"
|
|
67
70
|
},
|
|
68
71
|
devDependencies: {
|
|
69
72
|
"@freepi/pi-ads": "workspace:*",
|
|
@@ -91,6 +94,7 @@ Usage:
|
|
|
91
94
|
Environment:
|
|
92
95
|
FREEPI_BASE_URL override the backend base URL (default https://api.freepi.ai)
|
|
93
96
|
FREEPI_NO_AUTO_UPDATE disable in-place self-update for this run
|
|
97
|
+
FREEPI_NO_GRAVITY_PIXEL=1 do not run Gravity's ad-measurement pixel
|
|
94
98
|
`.trim();
|
|
95
99
|
function versionLine() {
|
|
96
100
|
return `free-pi-cli ${CLI_VERSION}`;
|
|
@@ -221,18 +225,8 @@ function getConfigPath() {
|
|
|
221
225
|
return join2(homedir(), FREE_PI_DIR_NAME, "config.json");
|
|
222
226
|
}
|
|
223
227
|
|
|
224
|
-
// src/
|
|
225
|
-
import {
|
|
226
|
-
createAgentSessionFromServices,
|
|
227
|
-
createAgentSessionRuntime,
|
|
228
|
-
createAgentSessionServices,
|
|
229
|
-
createCodemodeExtension,
|
|
230
|
-
createMcpExtension,
|
|
231
|
-
createToolSearchExtension,
|
|
232
|
-
InteractiveMode,
|
|
233
|
-
SessionManager,
|
|
234
|
-
SettingsManager
|
|
235
|
-
} from "@earendil-works/pi-coding-agent";
|
|
228
|
+
// src/gravity-pixel.ts
|
|
229
|
+
import { createHash } from "node:crypto";
|
|
236
230
|
|
|
237
231
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/core/core.js
|
|
238
232
|
var _a;
|
|
@@ -4828,6 +4822,7 @@ var MeResponseSchema = object({
|
|
|
4828
4822
|
cap_usd_today: number2(),
|
|
4829
4823
|
tier: AccountTierSchema,
|
|
4830
4824
|
credit_usd: number2(),
|
|
4825
|
+
consent_version: string2().nullable().optional(),
|
|
4831
4826
|
notice: string2().optional(),
|
|
4832
4827
|
buy_url: string2().optional(),
|
|
4833
4828
|
allowance_credits: number2().optional(),
|
|
@@ -4874,11 +4869,16 @@ var AdNextResponseSchema = object({
|
|
|
4874
4869
|
ad_id: string2(),
|
|
4875
4870
|
click_token: string2(),
|
|
4876
4871
|
creative: AdCreativeSchema,
|
|
4877
|
-
click_url: string2()
|
|
4872
|
+
click_url: string2(),
|
|
4873
|
+
source: _enum(["gravity", "own"]).optional(),
|
|
4874
|
+
imp_url: string2().optional()
|
|
4878
4875
|
});
|
|
4876
|
+
var GRAVITY_CONTEXT_HEADER = "x-gravity-context";
|
|
4877
|
+
var GRAVITY_CONTEXT_MAX_BYTES = 8192;
|
|
4879
4878
|
var AdImpressionRequestSchema = object({
|
|
4880
4879
|
ad_id: string2(),
|
|
4881
|
-
click_token: string2()
|
|
4880
|
+
click_token: string2(),
|
|
4881
|
+
imp_fired: boolean2().optional()
|
|
4882
4882
|
});
|
|
4883
4883
|
var AdminAdRequestSchema = object({
|
|
4884
4884
|
slot: AdSlotSchema,
|
|
@@ -5085,8 +5085,85 @@ function formatCreditsLeftLine(remaining, allowance, refillAt) {
|
|
|
5085
5085
|
return `${left} / ${total} credits left today · refills ${formatRefillEt(refillAt)}`;
|
|
5086
5086
|
}
|
|
5087
5087
|
var SLOW_LANE_METER_LINE = "Slow lane: cheapest model until refill";
|
|
5088
|
+
// src/gravity-pixel.ts
|
|
5089
|
+
var GRAVITY_PIXEL_ID = "6cd74804-0541-4d9b-899d-d7fceb465b6f";
|
|
5090
|
+
function userIdFromJwt(jwt) {
|
|
5091
|
+
try {
|
|
5092
|
+
const payload = JSON.parse(Buffer.from(jwt.split(".")[1] ?? "", "base64url").toString("utf8"));
|
|
5093
|
+
return typeof payload.sub === "string" ? payload.sub : undefined;
|
|
5094
|
+
} catch {
|
|
5095
|
+
return;
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
function dropPixelSignalHandlers() {
|
|
5099
|
+
const onNew = (event, fn) => {
|
|
5100
|
+
if (event !== "SIGINT" && event !== "SIGTERM" || typeof fn !== "function")
|
|
5101
|
+
return;
|
|
5102
|
+
if (!/shutdownSync/.test(Function.prototype.toString.call(fn)))
|
|
5103
|
+
return;
|
|
5104
|
+
queueMicrotask(() => process.removeListener(event, fn));
|
|
5105
|
+
};
|
|
5106
|
+
process.on("newListener", onNew);
|
|
5107
|
+
return () => {
|
|
5108
|
+
process.removeListener("newListener", onNew);
|
|
5109
|
+
};
|
|
5110
|
+
}
|
|
5111
|
+
function startGravityPixel(jwt, storageDir, env = process.env, load = () => import("./index-xkn61g24.js")) {
|
|
5112
|
+
if (env.FREEPI_NO_GRAVITY_PIXEL === "1")
|
|
5113
|
+
return;
|
|
5114
|
+
const userId = userIdFromJwt(jwt);
|
|
5115
|
+
if (!userId)
|
|
5116
|
+
return;
|
|
5117
|
+
const userHash = createHash("sha256").update(userId).digest("hex");
|
|
5118
|
+
let sdk;
|
|
5119
|
+
let pixel;
|
|
5120
|
+
(async () => {
|
|
5121
|
+
try {
|
|
5122
|
+
sdk = await load();
|
|
5123
|
+
const p = sdk.gravityPixel({ pixelId: GRAVITY_PIXEL_ID, appName: "free-pi", storageDir });
|
|
5124
|
+
pixel = p;
|
|
5125
|
+
const stopWatching = dropPixelSignalHandlers();
|
|
5126
|
+
try {
|
|
5127
|
+
await p.start();
|
|
5128
|
+
} finally {
|
|
5129
|
+
stopWatching();
|
|
5130
|
+
}
|
|
5131
|
+
} catch {
|
|
5132
|
+
pixel = undefined;
|
|
5133
|
+
}
|
|
5134
|
+
})();
|
|
5135
|
+
return () => {
|
|
5136
|
+
try {
|
|
5137
|
+
const sessionId = pixel?.getSessionId();
|
|
5138
|
+
if (!sdk || !pixel || !sessionId)
|
|
5139
|
+
return;
|
|
5140
|
+
const ctx = sdk.gravityContext({
|
|
5141
|
+
sessionId,
|
|
5142
|
+
user: { userId: userHash, gruid: pixel.getVisitorId(), grclid: pixel.getClickId(), graid: pixel.getGraid() }
|
|
5143
|
+
});
|
|
5144
|
+
const encoded = Buffer.from(JSON.stringify(ctx)).toString("base64url");
|
|
5145
|
+
return encoded.length <= GRAVITY_CONTEXT_MAX_BYTES ? encoded : undefined;
|
|
5146
|
+
} catch {
|
|
5147
|
+
return;
|
|
5148
|
+
}
|
|
5149
|
+
};
|
|
5150
|
+
}
|
|
5151
|
+
|
|
5152
|
+
// src/pi-launch.ts
|
|
5153
|
+
import {
|
|
5154
|
+
createAgentSessionFromServices,
|
|
5155
|
+
createAgentSessionRuntime,
|
|
5156
|
+
createAgentSessionServices,
|
|
5157
|
+
createCodemodeExtension,
|
|
5158
|
+
createMcpExtension,
|
|
5159
|
+
createToolSearchExtension,
|
|
5160
|
+
InteractiveMode,
|
|
5161
|
+
SessionManager,
|
|
5162
|
+
SettingsManager
|
|
5163
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5164
|
+
|
|
5088
5165
|
// ../pi-ads/src/api.ts
|
|
5089
|
-
var REQUEST_TIMEOUT_MS =
|
|
5166
|
+
var REQUEST_TIMEOUT_MS = 5000;
|
|
5090
5167
|
async function authedFetch(deps, path, init) {
|
|
5091
5168
|
const token = await deps.getToken();
|
|
5092
5169
|
const doFetch = deps.fetchImpl ?? fetch;
|
|
@@ -5098,10 +5175,38 @@ async function authedFetch(deps, path, init) {
|
|
|
5098
5175
|
...init?.headers ?? {},
|
|
5099
5176
|
Authorization: `Bearer ${token}`,
|
|
5100
5177
|
"Content-Type": "application/json",
|
|
5101
|
-
...deps.sessionId && (path.startsWith("/ads/next") || path === "/ads/impression") ? { "x-session-id": deps.sessionId } : {}
|
|
5178
|
+
...deps.sessionId && (path.startsWith("/ads/next") || path === "/ads/impression") ? { "x-session-id": deps.sessionId } : {},
|
|
5179
|
+
...path.startsWith("/ads/next") ? gravityHeader(deps) : {}
|
|
5102
5180
|
}
|
|
5103
5181
|
});
|
|
5104
5182
|
}
|
|
5183
|
+
function gravityHeader(deps) {
|
|
5184
|
+
try {
|
|
5185
|
+
const ctx = deps.getGravityContext?.();
|
|
5186
|
+
return ctx ? { [GRAVITY_CONTEXT_HEADER]: ctx } : {};
|
|
5187
|
+
} catch {
|
|
5188
|
+
return {};
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
var IMP_TIMEOUT_MS = 3000;
|
|
5192
|
+
function isGravityImpUrl(raw) {
|
|
5193
|
+
if (!raw)
|
|
5194
|
+
return false;
|
|
5195
|
+
try {
|
|
5196
|
+
const u = new URL(raw);
|
|
5197
|
+
return u.protocol === "https:" && /(^|\.)trygravity\.(ai|com)$/.test(u.hostname);
|
|
5198
|
+
} catch {
|
|
5199
|
+
return false;
|
|
5200
|
+
}
|
|
5201
|
+
}
|
|
5202
|
+
function fireGravityImpression(deps, ad) {
|
|
5203
|
+
if (ad.source !== "gravity" || !isGravityImpUrl(ad.imp_url))
|
|
5204
|
+
return false;
|
|
5205
|
+
try {
|
|
5206
|
+
Promise.resolve((deps.fetchImpl ?? fetch)(ad.imp_url, { method: "GET", signal: AbortSignal.timeout(IMP_TIMEOUT_MS) })).catch(() => {});
|
|
5207
|
+
} catch {}
|
|
5208
|
+
return true;
|
|
5209
|
+
}
|
|
5105
5210
|
async function fetchAdNext(deps, slot) {
|
|
5106
5211
|
try {
|
|
5107
5212
|
const res = await authedFetch(deps, `/ads/next?slot=${slot}`);
|
|
@@ -5112,11 +5217,11 @@ async function fetchAdNext(deps, slot) {
|
|
|
5112
5217
|
return;
|
|
5113
5218
|
}
|
|
5114
5219
|
}
|
|
5115
|
-
async function postImpression(deps, adId, clickToken) {
|
|
5220
|
+
async function postImpression(deps, adId, clickToken, impFired = false) {
|
|
5116
5221
|
try {
|
|
5117
5222
|
await authedFetch(deps, "/ads/impression", {
|
|
5118
5223
|
method: "POST",
|
|
5119
|
-
body: JSON.stringify({ ad_id: adId, click_token: clickToken })
|
|
5224
|
+
body: JSON.stringify({ ad_id: adId, click_token: clickToken, ...impFired ? { imp_fired: true } : {} })
|
|
5120
5225
|
});
|
|
5121
5226
|
} catch {}
|
|
5122
5227
|
}
|
|
@@ -5132,7 +5237,7 @@ async function fetchMe(deps) {
|
|
|
5132
5237
|
}
|
|
5133
5238
|
|
|
5134
5239
|
// ../pi-ads/src/style.ts
|
|
5135
|
-
import { createHash } from "node:crypto";
|
|
5240
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
5136
5241
|
var OSC_RE = /\x1b\][\s\S]*?(?:\x07|\x1b\\)/g;
|
|
5137
5242
|
var CSI_RE = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
5138
5243
|
var C1_RE = /[\x80-\x9f]/g;
|
|
@@ -5165,7 +5270,7 @@ function renderAdCard(creative, clickUrl, theme, columns) {
|
|
|
5165
5270
|
const label = " AD ░▒▓ ";
|
|
5166
5271
|
const top = `┌${label}${"─".repeat(Math.max(0, innerWidth + 2 - label.length))}┐`;
|
|
5167
5272
|
const bottom = `└${"─".repeat(innerWidth + 2)}┘`;
|
|
5168
|
-
const linkId =
|
|
5273
|
+
const linkId = createHash2("sha256").update(url).digest("hex").slice(0, 12);
|
|
5169
5274
|
const row = (text, opts) => {
|
|
5170
5275
|
const plain = pad(truncate(text, innerWidth), innerWidth);
|
|
5171
5276
|
let styled = theme.fg("accent", plain);
|
|
@@ -5199,14 +5304,33 @@ function renderWarningLine(message, theme) {
|
|
|
5199
5304
|
var BANNER_WIDGET_ID = "freepi-ad";
|
|
5200
5305
|
function createBannerRenderer(opts = {}) {
|
|
5201
5306
|
let lastPostedToken;
|
|
5307
|
+
const screen = opts.screen ?? {};
|
|
5202
5308
|
return {
|
|
5203
5309
|
async render(deps, ctx, config, columnsOverride) {
|
|
5204
5310
|
const ad = await fetchAdNext(deps, "banner");
|
|
5311
|
+
if (opts.isActive && !opts.isActive())
|
|
5312
|
+
return;
|
|
5205
5313
|
if (!ad) {
|
|
5314
|
+
screen.banner = undefined;
|
|
5206
5315
|
ctx.ui.setWidget(BANNER_WIDGET_ID, undefined);
|
|
5207
5316
|
opts.onBannerAd?.(undefined);
|
|
5208
5317
|
return;
|
|
5209
5318
|
}
|
|
5319
|
+
const isGravity = ad.source === "gravity";
|
|
5320
|
+
if (screen.inline === "gravity") {
|
|
5321
|
+
screen.banner = undefined;
|
|
5322
|
+
ctx.ui.setWidget(BANNER_WIDGET_ID, undefined);
|
|
5323
|
+
opts.onBannerAd?.(undefined);
|
|
5324
|
+
return;
|
|
5325
|
+
}
|
|
5326
|
+
if (isGravity && screen.inline)
|
|
5327
|
+
screen.clearInline?.();
|
|
5328
|
+
screen.banner = isGravity ? "gravity" : "own";
|
|
5329
|
+
screen.clearBanner = () => {
|
|
5330
|
+
screen.banner = undefined;
|
|
5331
|
+
ctx.ui.setWidget(BANNER_WIDGET_ID, undefined);
|
|
5332
|
+
opts.onBannerAd?.(undefined);
|
|
5333
|
+
};
|
|
5210
5334
|
const theme = ctx.ui.theme;
|
|
5211
5335
|
const columns = columnsOverride ?? process.stdout.columns ?? 80;
|
|
5212
5336
|
const lines = columns >= config.adMinColumns ? renderAdCard(ad.creative, ad.click_url, theme, columns) : [renderPlainAdLine(ad.creative, ad.click_url, theme, columns)];
|
|
@@ -5214,7 +5338,8 @@ function createBannerRenderer(opts = {}) {
|
|
|
5214
5338
|
opts.onBannerAd?.(ad.click_url);
|
|
5215
5339
|
if (lastPostedToken !== ad.click_token) {
|
|
5216
5340
|
lastPostedToken = ad.click_token;
|
|
5217
|
-
|
|
5341
|
+
const impFired = fireGravityImpression(deps, ad);
|
|
5342
|
+
await postImpression(deps, ad.ad_id, ad.click_token, impFired);
|
|
5218
5343
|
}
|
|
5219
5344
|
}
|
|
5220
5345
|
};
|
|
@@ -5222,37 +5347,48 @@ function createBannerRenderer(opts = {}) {
|
|
|
5222
5347
|
|
|
5223
5348
|
// ../pi-ads/src/inline.ts
|
|
5224
5349
|
var INLINE_WIDGET_ID = "freepi-ad-inline";
|
|
5225
|
-
function createInlineRenderer() {
|
|
5350
|
+
function createInlineRenderer(screen = {}) {
|
|
5226
5351
|
let turnCount = 0;
|
|
5227
5352
|
let inlineVisible = false;
|
|
5228
5353
|
let lastPostedToken;
|
|
5354
|
+
const clearInline = (ctx) => {
|
|
5355
|
+
if (inlineVisible)
|
|
5356
|
+
ctx.ui.setWidget(INLINE_WIDGET_ID, undefined);
|
|
5357
|
+
inlineVisible = false;
|
|
5358
|
+
screen.inline = undefined;
|
|
5359
|
+
};
|
|
5229
5360
|
return {
|
|
5230
5361
|
async onTurnEnd(deps, ctx, config, columnsOverride) {
|
|
5231
5362
|
turnCount += 1;
|
|
5232
|
-
|
|
5233
|
-
ctx.ui.setWidget(INLINE_WIDGET_ID, undefined);
|
|
5234
|
-
inlineVisible = false;
|
|
5235
|
-
}
|
|
5363
|
+
clearInline(ctx);
|
|
5236
5364
|
if (turnCount % config.adInlineTurnFrequency !== 0)
|
|
5237
5365
|
return;
|
|
5366
|
+
if (screen.banner === "gravity")
|
|
5367
|
+
return;
|
|
5238
5368
|
const ad = await fetchAdNext(deps, "inline");
|
|
5239
5369
|
if (!ad)
|
|
5240
5370
|
return;
|
|
5371
|
+
const isGravity = ad.source === "gravity";
|
|
5372
|
+
if (screen.banner === "gravity")
|
|
5373
|
+
return;
|
|
5374
|
+
if (isGravity && screen.banner)
|
|
5375
|
+
screen.clearBanner?.();
|
|
5241
5376
|
const theme = ctx.ui.theme;
|
|
5242
5377
|
const columns = columnsOverride ?? process.stdout.columns ?? 80;
|
|
5243
5378
|
const lines = columns >= config.adMinColumns ? renderAdCard(ad.creative, ad.click_url, theme, columns) : [renderPlainAdLine(ad.creative, ad.click_url, theme, columns)];
|
|
5244
5379
|
ctx.ui.setWidget(INLINE_WIDGET_ID, lines, { placement: "belowEditor" });
|
|
5245
5380
|
inlineVisible = true;
|
|
5381
|
+
screen.inline = isGravity ? "gravity" : "own";
|
|
5382
|
+
screen.clearInline = () => clearInline(ctx);
|
|
5246
5383
|
if (lastPostedToken !== ad.click_token) {
|
|
5247
5384
|
lastPostedToken = ad.click_token;
|
|
5248
|
-
|
|
5385
|
+
const impFired = fireGravityImpression(deps, ad);
|
|
5386
|
+
await postImpression(deps, ad.ad_id, ad.click_token, impFired);
|
|
5249
5387
|
}
|
|
5250
5388
|
},
|
|
5251
5389
|
reset(ctx) {
|
|
5252
5390
|
turnCount = 0;
|
|
5253
|
-
|
|
5254
|
-
ctx.ui.setWidget(INLINE_WIDGET_ID, undefined);
|
|
5255
|
-
inlineVisible = false;
|
|
5391
|
+
clearInline(ctx);
|
|
5256
5392
|
}
|
|
5257
5393
|
};
|
|
5258
5394
|
}
|
|
@@ -5361,34 +5497,56 @@ async function renderMeter(deps, ctx) {
|
|
|
5361
5497
|
|
|
5362
5498
|
// ../pi-ads/src/index.ts
|
|
5363
5499
|
var BANNER_REFRESH_MS = 10 * 60 * 1000;
|
|
5500
|
+
var BANNER_MIN_REFRESH_MS = 60 * 1000;
|
|
5364
5501
|
function createAdsExtension(opts) {
|
|
5365
5502
|
const deps = {
|
|
5366
5503
|
baseUrl: opts.baseUrl,
|
|
5367
5504
|
getToken: opts.getToken,
|
|
5368
5505
|
sessionId: opts.sessionId,
|
|
5369
|
-
fetchImpl: opts.fetchImpl
|
|
5506
|
+
fetchImpl: opts.fetchImpl,
|
|
5507
|
+
getGravityContext: opts.getGravityContext
|
|
5370
5508
|
};
|
|
5371
5509
|
const config = loadConfig();
|
|
5372
5510
|
return {
|
|
5373
5511
|
name: "free-pi-ads",
|
|
5374
5512
|
factory(pi) {
|
|
5375
|
-
|
|
5376
|
-
const
|
|
5513
|
+
let stopped = false;
|
|
5514
|
+
const screen = {};
|
|
5515
|
+
const banner = createBannerRenderer({ onBannerAd: opts.onBannerAd, isActive: () => !stopped, screen });
|
|
5516
|
+
const inline = createInlineRenderer(screen);
|
|
5377
5517
|
let bannerInterval;
|
|
5518
|
+
let lastBannerAt = 0;
|
|
5519
|
+
let bannerInFlight = false;
|
|
5520
|
+
const renderBanner = async (ctx, force = false) => {
|
|
5521
|
+
if (stopped)
|
|
5522
|
+
return;
|
|
5523
|
+
if (!force && (bannerInFlight || Date.now() - lastBannerAt < BANNER_MIN_REFRESH_MS))
|
|
5524
|
+
return;
|
|
5525
|
+
bannerInFlight = true;
|
|
5526
|
+
try {
|
|
5527
|
+
await banner.render(deps, ctx, config);
|
|
5528
|
+
} finally {
|
|
5529
|
+
bannerInFlight = false;
|
|
5530
|
+
lastBannerAt = Date.now();
|
|
5531
|
+
}
|
|
5532
|
+
};
|
|
5378
5533
|
pi.on("session_start", async (_event, ctx) => {
|
|
5534
|
+
stopped = false;
|
|
5379
5535
|
inline.reset(ctx);
|
|
5380
|
-
await Promise.all([
|
|
5536
|
+
await Promise.all([renderBanner(ctx, true), renderMeter(deps, ctx)]);
|
|
5381
5537
|
if (bannerInterval)
|
|
5382
5538
|
clearInterval(bannerInterval);
|
|
5383
5539
|
bannerInterval = setInterval(() => {
|
|
5384
|
-
|
|
5540
|
+
renderBanner(ctx);
|
|
5385
5541
|
}, BANNER_REFRESH_MS);
|
|
5386
5542
|
bannerInterval.unref?.();
|
|
5387
5543
|
});
|
|
5388
5544
|
pi.on("turn_end", async (_event, ctx) => {
|
|
5545
|
+
renderBanner(ctx);
|
|
5389
5546
|
await Promise.all([inline.onTurnEnd(deps, ctx, config), renderMeter(deps, ctx)]);
|
|
5390
5547
|
});
|
|
5391
5548
|
pi.on("session_shutdown", () => {
|
|
5549
|
+
stopped = true;
|
|
5392
5550
|
if (bannerInterval) {
|
|
5393
5551
|
clearInterval(bannerInterval);
|
|
5394
5552
|
bannerInterval = undefined;
|
|
@@ -5399,7 +5557,8 @@ function createAdsExtension(opts) {
|
|
|
5399
5557
|
}
|
|
5400
5558
|
|
|
5401
5559
|
// src/pi-launch.ts
|
|
5402
|
-
import { join as
|
|
5560
|
+
import { dirname as dirname3, join as join4, resolve as resolve2 } from "node:path";
|
|
5561
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
5403
5562
|
|
|
5404
5563
|
// src/provider.ts
|
|
5405
5564
|
var PROVIDER_NAME = "free-pi";
|
|
@@ -5428,7 +5587,7 @@ function buildProviderConfig(baseUrl, jwt, sessionId, models) {
|
|
|
5428
5587
|
};
|
|
5429
5588
|
}
|
|
5430
5589
|
|
|
5431
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5590
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/metrics.mjs
|
|
5432
5591
|
var Metrics = {
|
|
5433
5592
|
assign: 0,
|
|
5434
5593
|
create: 0,
|
|
@@ -5436,7 +5595,8 @@ var Metrics = {
|
|
|
5436
5595
|
discard: 0,
|
|
5437
5596
|
update: 0
|
|
5438
5597
|
};
|
|
5439
|
-
|
|
5598
|
+
|
|
5599
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/guard/guard.mjs
|
|
5440
5600
|
function IsArray(value) {
|
|
5441
5601
|
return Array.isArray(value);
|
|
5442
5602
|
}
|
|
@@ -5481,7 +5641,7 @@ function Keys(value) {
|
|
|
5481
5641
|
function Symbols(value) {
|
|
5482
5642
|
return Object.getOwnPropertySymbols(value);
|
|
5483
5643
|
}
|
|
5484
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5644
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/guard/globals.mjs
|
|
5485
5645
|
function IsTypeArray(value) {
|
|
5486
5646
|
return globalThis.ArrayBuffer.isView(value);
|
|
5487
5647
|
}
|
|
@@ -5494,7 +5654,25 @@ function IsSet(value) {
|
|
|
5494
5654
|
function IsMap(value) {
|
|
5495
5655
|
return value instanceof globalThis.Map;
|
|
5496
5656
|
}
|
|
5497
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5657
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/settings/settings.mjs
|
|
5658
|
+
var settings = {
|
|
5659
|
+
immutableTypes: false,
|
|
5660
|
+
maxErrors: 8,
|
|
5661
|
+
maxInstantiationCount: 128,
|
|
5662
|
+
useAcceleration: true,
|
|
5663
|
+
exactOptionalPropertyTypes: false,
|
|
5664
|
+
enumerableKind: false,
|
|
5665
|
+
correctiveParse: false,
|
|
5666
|
+
unionPrioritySort: true
|
|
5667
|
+
};
|
|
5668
|
+
function Get() {
|
|
5669
|
+
return settings;
|
|
5670
|
+
}
|
|
5671
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/freeze.mjs
|
|
5672
|
+
function Freeze(value) {
|
|
5673
|
+
return Get().immutableTypes ? Object.freeze(value) : value;
|
|
5674
|
+
}
|
|
5675
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/clone.mjs
|
|
5498
5676
|
function FromClassInstance(value) {
|
|
5499
5677
|
return value;
|
|
5500
5678
|
}
|
|
@@ -5553,21 +5731,7 @@ function Clone(value) {
|
|
|
5553
5731
|
Metrics.clone += 1;
|
|
5554
5732
|
return FromValue(value);
|
|
5555
5733
|
}
|
|
5556
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5557
|
-
var settings = {
|
|
5558
|
-
immutableTypes: false,
|
|
5559
|
-
maxErrors: 8,
|
|
5560
|
-
maxInstantiationCount: 128,
|
|
5561
|
-
useAcceleration: true,
|
|
5562
|
-
exactOptionalPropertyTypes: false,
|
|
5563
|
-
enumerableKind: false,
|
|
5564
|
-
correctiveParse: false,
|
|
5565
|
-
unionPrioritySort: true
|
|
5566
|
-
};
|
|
5567
|
-
function Get() {
|
|
5568
|
-
return settings;
|
|
5569
|
-
}
|
|
5570
|
-
// ../../node_modules/.bun/typebox@1.3.16/node_modules/typebox/build/system/memory/create.mjs
|
|
5734
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/create.mjs
|
|
5571
5735
|
function MergeHidden(left, right) {
|
|
5572
5736
|
for (const key of Object.keys(right)) {
|
|
5573
5737
|
Object.defineProperty(left, key, {
|
|
@@ -5584,12 +5748,11 @@ function Merge(left, right) {
|
|
|
5584
5748
|
}
|
|
5585
5749
|
function Create(hidden, enumerable, options = {}) {
|
|
5586
5750
|
Metrics.create += 1;
|
|
5587
|
-
const settings = Get();
|
|
5588
5751
|
const withOptions = Merge(enumerable, options);
|
|
5589
|
-
const withHidden =
|
|
5590
|
-
return
|
|
5752
|
+
const withHidden = Get().enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
|
|
5753
|
+
return Freeze(withHidden);
|
|
5591
5754
|
}
|
|
5592
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5755
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/memory/update.mjs
|
|
5593
5756
|
function Update(current, hidden, enumerable) {
|
|
5594
5757
|
Metrics.update += 1;
|
|
5595
5758
|
const settings = Get();
|
|
@@ -5610,14 +5773,14 @@ function Update(current, hidden, enumerable) {
|
|
|
5610
5773
|
value: enumerable[key]
|
|
5611
5774
|
});
|
|
5612
5775
|
}
|
|
5613
|
-
return result;
|
|
5776
|
+
return Freeze(result);
|
|
5614
5777
|
}
|
|
5615
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5778
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/schema.mjs
|
|
5616
5779
|
function IsSchema(value) {
|
|
5617
5780
|
return IsObject(value);
|
|
5618
5781
|
}
|
|
5619
5782
|
|
|
5620
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5783
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs
|
|
5621
5784
|
function AddOptionalOperation(type) {
|
|
5622
5785
|
return Update(type, { "~optional": true }, {});
|
|
5623
5786
|
}
|
|
@@ -5626,12 +5789,12 @@ function AddOptionalAction(type, options) {
|
|
|
5626
5789
|
return result;
|
|
5627
5790
|
}
|
|
5628
5791
|
|
|
5629
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5792
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/action/_add_optional.mjs
|
|
5630
5793
|
function AddOptional(type, options = {}) {
|
|
5631
5794
|
return AddOptionalAction(type, options);
|
|
5632
5795
|
}
|
|
5633
5796
|
|
|
5634
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5797
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/_optional.mjs
|
|
5635
5798
|
function Optional(type) {
|
|
5636
5799
|
return AddOptional(type);
|
|
5637
5800
|
}
|
|
@@ -5639,18 +5802,18 @@ function IsOptional(value) {
|
|
|
5639
5802
|
return IsSchema(value) && HasPropertyKey(value, "~optional");
|
|
5640
5803
|
}
|
|
5641
5804
|
|
|
5642
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5805
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/properties.mjs
|
|
5643
5806
|
function RequiredArray(properties) {
|
|
5644
5807
|
return Keys(properties).filter((key) => !IsOptional(properties[key]));
|
|
5645
5808
|
}
|
|
5646
5809
|
|
|
5647
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5810
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/object.mjs
|
|
5648
5811
|
function _Object_(properties, options = {}) {
|
|
5649
5812
|
const requiredKeys = RequiredArray(properties);
|
|
5650
5813
|
const required = requiredKeys.length > 0 ? { required: requiredKeys } : {};
|
|
5651
5814
|
return Create({ "~kind": "Object" }, { type: "object", ...required, properties }, options);
|
|
5652
5815
|
}
|
|
5653
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5816
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/system/hashing/hash.mjs
|
|
5654
5817
|
var ByteMarker;
|
|
5655
5818
|
(function(ByteMarker) {
|
|
5656
5819
|
ByteMarker[ByteMarker["Array"] = 0] = "Array";
|
|
@@ -5675,9 +5838,9 @@ var F64 = new Float64Array(1);
|
|
|
5675
5838
|
var F64In = new DataView(F64.buffer);
|
|
5676
5839
|
var F64Out = new Uint8Array(F64.buffer);
|
|
5677
5840
|
var encoder = new TextEncoder;
|
|
5678
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5841
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/integer.mjs
|
|
5679
5842
|
var IntegerPattern = "-?(?:0|[1-9][0-9]*)";
|
|
5680
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5843
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/literal.mjs
|
|
5681
5844
|
class InvalidLiteralValue extends Error {
|
|
5682
5845
|
constructor(value) {
|
|
5683
5846
|
super(`Invalid Literal value`);
|
|
@@ -5697,27 +5860,27 @@ function LiteralTypeName(value) {
|
|
|
5697
5860
|
function Literal(value, options) {
|
|
5698
5861
|
return Create({ "~kind": "Literal" }, { type: LiteralTypeName(value), const: value }, options);
|
|
5699
5862
|
}
|
|
5700
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5863
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/number.mjs
|
|
5701
5864
|
var NumberPattern = "-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?";
|
|
5702
5865
|
function Number2(options) {
|
|
5703
5866
|
return Create({ "~kind": "Number" }, { type: "number" }, options);
|
|
5704
5867
|
}
|
|
5705
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5868
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/string.mjs
|
|
5706
5869
|
var StringPattern = ".*";
|
|
5707
5870
|
function String2(options) {
|
|
5708
5871
|
return Create({ "~kind": "String" }, { type: "string" }, options);
|
|
5709
5872
|
}
|
|
5710
5873
|
|
|
5711
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5874
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/union.mjs
|
|
5712
5875
|
function Union(anyOf, options = {}) {
|
|
5713
5876
|
return Create({ "~kind": "Union" }, { anyOf }, options);
|
|
5714
5877
|
}
|
|
5715
5878
|
|
|
5716
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5879
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/types/record.mjs
|
|
5717
5880
|
var IntegerKey = `^${IntegerPattern}$`;
|
|
5718
5881
|
var NumberKey = `^${NumberPattern}$`;
|
|
5719
5882
|
var StringKey = `^${StringPattern}$`;
|
|
5720
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5883
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/internal/char.mjs
|
|
5721
5884
|
function Range(start, end) {
|
|
5722
5885
|
return Array.from({ length: end - start + 1 }, (_, i) => String.fromCharCode(start + i));
|
|
5723
5886
|
}
|
|
@@ -5731,17 +5894,17 @@ var Digit = [Zero, ...NonZero];
|
|
|
5731
5894
|
var UnderScore = "_";
|
|
5732
5895
|
var DollarSign = "$";
|
|
5733
5896
|
|
|
5734
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5897
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/unsigned_integer.mjs
|
|
5735
5898
|
var AllowedDigits = [...Digit, UnderScore];
|
|
5736
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5899
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/ident.mjs
|
|
5737
5900
|
var Initial = [...Alpha, UnderScore, DollarSign];
|
|
5738
5901
|
var Remaining = [...Initial, ...Digit];
|
|
5739
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5902
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/script/token/unsigned_number.mjs
|
|
5740
5903
|
var AllowedDigits2 = [...Digit, UnderScore];
|
|
5741
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5904
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/engine/helpers/keys.mjs
|
|
5742
5905
|
var integerKeyPattern = new RegExp("^(?:0|[1-9][0-9]*)$");
|
|
5743
5906
|
|
|
5744
|
-
// ../../node_modules/.bun/typebox@1.3.
|
|
5907
|
+
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/engine/indexed/from_object.mjs
|
|
5745
5908
|
var NumericKeyPattern = new RegExp(IntegerKey);
|
|
5746
5909
|
// src/usage-tool.ts
|
|
5747
5910
|
var USAGE_TOOL_NAME = "free_pi_usage";
|
|
@@ -6211,6 +6374,64 @@ function createTodoToolExtension(options = {}) {
|
|
|
6211
6374
|
};
|
|
6212
6375
|
}
|
|
6213
6376
|
|
|
6377
|
+
// src/tool-aliases.ts
|
|
6378
|
+
import { homedir as homedir2 } from "node:os";
|
|
6379
|
+
import { join as join3 } from "node:path";
|
|
6380
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6381
|
+
import {
|
|
6382
|
+
createBashToolDefinition,
|
|
6383
|
+
createReadToolDefinition
|
|
6384
|
+
} from "@earendil-works/pi-coding-agent";
|
|
6385
|
+
function aliasArgs(input, key, aliases) {
|
|
6386
|
+
if (!input || typeof input !== "object" || key in input)
|
|
6387
|
+
return input;
|
|
6388
|
+
const args = input;
|
|
6389
|
+
const alias = aliases.find((a) => (a in args));
|
|
6390
|
+
if (!alias)
|
|
6391
|
+
return input;
|
|
6392
|
+
const { [alias]: value, ...rest } = args;
|
|
6393
|
+
return { ...rest, [key]: value };
|
|
6394
|
+
}
|
|
6395
|
+
function normalizeShellPath(input, platform = process.platform, home = homedir2()) {
|
|
6396
|
+
let p = input;
|
|
6397
|
+
if (platform === "win32" && p.startsWith("/") && !p.startsWith("//") && !p.includes("\\")) {
|
|
6398
|
+
const m = p.match(/^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i);
|
|
6399
|
+
if (m)
|
|
6400
|
+
p = `${m[1].toUpperCase()}:\\${m[2]?.replaceAll("/", "\\") ?? ""}`;
|
|
6401
|
+
}
|
|
6402
|
+
if (p === "~")
|
|
6403
|
+
return home;
|
|
6404
|
+
if (p.startsWith("~/") || platform === "win32" && p.startsWith("~\\"))
|
|
6405
|
+
return join3(home, p.slice(2));
|
|
6406
|
+
if (/^file:\/\//.test(p))
|
|
6407
|
+
return fileURLToPath2(p);
|
|
6408
|
+
return p;
|
|
6409
|
+
}
|
|
6410
|
+
function createToolAliasesExtension() {
|
|
6411
|
+
return {
|
|
6412
|
+
name: "free-pi-tool-aliases",
|
|
6413
|
+
factory: (pi) => {
|
|
6414
|
+
const cwd = process.cwd();
|
|
6415
|
+
const bash = createBashToolDefinition(cwd);
|
|
6416
|
+
const read = createReadToolDefinition(cwd);
|
|
6417
|
+
pi.registerTool({
|
|
6418
|
+
...bash,
|
|
6419
|
+
prepareArguments: (a) => aliasArgs(bash.prepareArguments ? bash.prepareArguments(a) : a, "command", ["cmd"]),
|
|
6420
|
+
execute: (...args) => {
|
|
6421
|
+
const s = pi.getSettings();
|
|
6422
|
+
const shellPath = s.shellPath ? normalizeShellPath(s.shellPath) : s.shellPath;
|
|
6423
|
+
return createBashToolDefinition(cwd, { commandPrefix: s.shellCommandPrefix, shellPath }).execute(...args);
|
|
6424
|
+
}
|
|
6425
|
+
});
|
|
6426
|
+
pi.registerTool({
|
|
6427
|
+
...read,
|
|
6428
|
+
prepareArguments: (a) => aliasArgs(read.prepareArguments ? read.prepareArguments(a) : a, "path", ["file", "file_path"]),
|
|
6429
|
+
execute: (...args) => createReadToolDefinition(cwd, { autoResizeImages: pi.getSettings().images?.autoResize ?? true }).execute(...args)
|
|
6430
|
+
});
|
|
6431
|
+
}
|
|
6432
|
+
};
|
|
6433
|
+
}
|
|
6434
|
+
|
|
6214
6435
|
// src/error-notice.ts
|
|
6215
6436
|
var NOTICE_DEDUPE_MS = 15000;
|
|
6216
6437
|
function parseErrorEnvelope(errorMessage) {
|
|
@@ -6253,6 +6474,16 @@ function createErrorNoticeExtension(opts = {}) {
|
|
|
6253
6474
|
};
|
|
6254
6475
|
}
|
|
6255
6476
|
|
|
6477
|
+
// src/agent-rules.ts
|
|
6478
|
+
var AGENT_RULES = `## Working rules
|
|
6479
|
+
- When you say you will run, check, or change something, make that tool call in the same
|
|
6480
|
+
turn. Never end a turn with only a plan or a script printed as text.
|
|
6481
|
+
- If the same error appears twice, stop retrying variants. Read the relevant source, docs
|
|
6482
|
+
or full error output, or try a different approach. Do not re-read a file you already read
|
|
6483
|
+
unless it has changed.
|
|
6484
|
+
- Before saying a task is done, check the result the user will see: run the program, open
|
|
6485
|
+
the output, or run the specific test. Say plainly what you checked and what you did not.`;
|
|
6486
|
+
|
|
6256
6487
|
// src/tool-guard.ts
|
|
6257
6488
|
async function reportToolGuardTrigger(opts, toolName) {
|
|
6258
6489
|
try {
|
|
@@ -6371,6 +6602,20 @@ function installFooterRightAlign(mode) {
|
|
|
6371
6602
|
|
|
6372
6603
|
// src/changelog.ts
|
|
6373
6604
|
var CHANGELOG_HIGHLIGHTS = [
|
|
6605
|
+
{
|
|
6606
|
+
version: "0.4.4",
|
|
6607
|
+
highlights: [
|
|
6608
|
+
"Updated terms: you'll be asked to accept them once.",
|
|
6609
|
+
"Some ads now come from Gravity, measured with a small tracker. Turn it off with FREEPI_NO_GRAVITY_PIXEL=1."
|
|
6610
|
+
]
|
|
6611
|
+
},
|
|
6612
|
+
{
|
|
6613
|
+
version: "0.4.3",
|
|
6614
|
+
highlights: [
|
|
6615
|
+
"The ad banner now refreshes while you work, at most once a minute.",
|
|
6616
|
+
"If our terms have changed since you signed up, you'll be asked to review and accept them."
|
|
6617
|
+
]
|
|
6618
|
+
},
|
|
6374
6619
|
{
|
|
6375
6620
|
version: "0.4.2",
|
|
6376
6621
|
highlights: ["Credits now sit on the right of the footer, under the model name."]
|
|
@@ -6736,12 +6981,89 @@ function createHeaderExtension(opts) {
|
|
|
6736
6981
|
};
|
|
6737
6982
|
}
|
|
6738
6983
|
|
|
6984
|
+
// src/fusion.ts
|
|
6985
|
+
var FUSION_ID = "fusion";
|
|
6986
|
+
var FUSION_NAME = "Fusion · Argon plans, DeepSeek builds";
|
|
6987
|
+
var FUSION_PLANNER_ID = "gemini-4-argon-flex";
|
|
6988
|
+
var FUSION_EXECUTOR_ID = "deepseek-v4-flash";
|
|
6989
|
+
function editedThisTurn(messages) {
|
|
6990
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
6991
|
+
const message = messages[i];
|
|
6992
|
+
if (message.role === "user")
|
|
6993
|
+
break;
|
|
6994
|
+
if (message.role === "toolResult" && (message.toolName === "edit" || message.toolName === "write") && !message.isError) {
|
|
6995
|
+
return true;
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
return false;
|
|
6999
|
+
}
|
|
7000
|
+
function hasNewUserInput(request) {
|
|
7001
|
+
const failedAt = request.failed?.message.timestamp;
|
|
7002
|
+
if (failedAt === undefined)
|
|
7003
|
+
return false;
|
|
7004
|
+
for (let i = request.messages.length - 1;i >= 0; i--) {
|
|
7005
|
+
const message = request.messages[i];
|
|
7006
|
+
if (message.role === "assistant")
|
|
7007
|
+
return false;
|
|
7008
|
+
if (message.role === "user")
|
|
7009
|
+
return message.timestamp > failedAt;
|
|
7010
|
+
}
|
|
7011
|
+
return false;
|
|
7012
|
+
}
|
|
7013
|
+
function routeFusion(request, models) {
|
|
7014
|
+
const phase = request.state?.phase ?? "plan";
|
|
7015
|
+
const restartPlan = request.reason === "user" || request.reason === "retry" && hasNewUserInput(request);
|
|
7016
|
+
const route = (model, nextPhase = phase) => ({
|
|
7017
|
+
model,
|
|
7018
|
+
thinkingLevel: request.thinkingLevel,
|
|
7019
|
+
state: request.reason === "direct" ? undefined : request.state?.phase === nextPhase ? request.state : { phase: nextPhase }
|
|
7020
|
+
});
|
|
7021
|
+
if (!models.planner || !models.executor) {
|
|
7022
|
+
return route(models.defaultModel, restartPlan ? "plan" : phase);
|
|
7023
|
+
}
|
|
7024
|
+
switch (request.reason) {
|
|
7025
|
+
case "user":
|
|
7026
|
+
return route(models.planner, "plan");
|
|
7027
|
+
case "direct":
|
|
7028
|
+
return route(models.executor);
|
|
7029
|
+
case "retry": {
|
|
7030
|
+
if (restartPlan)
|
|
7031
|
+
return route(models.planner, "plan");
|
|
7032
|
+
const failed = request.failed;
|
|
7033
|
+
if (!failed || failed.model.provider === PROVIDER_NAME && failed.model.id === FUSION_PLANNER_ID) {
|
|
7034
|
+
return route(models.executor, "build");
|
|
7035
|
+
}
|
|
7036
|
+
return { ...route(failed.model), thinkingLevel: failed.thinkingLevel ?? request.thinkingLevel };
|
|
7037
|
+
}
|
|
7038
|
+
case "continuation":
|
|
7039
|
+
return phase === "build" || editedThisTurn(request.messages) ? route(models.executor, "build") : route(models.planner, "plan");
|
|
7040
|
+
}
|
|
7041
|
+
}
|
|
7042
|
+
function createFusionExtension(defaultModelId) {
|
|
7043
|
+
return {
|
|
7044
|
+
name: "free-pi-fusion",
|
|
7045
|
+
factory: (pi) => {
|
|
7046
|
+
pi.registerVirtualModel({
|
|
7047
|
+
provider: PROVIDER_NAME,
|
|
7048
|
+
id: FUSION_ID,
|
|
7049
|
+
name: FUSION_NAME,
|
|
7050
|
+
input: ["text"],
|
|
7051
|
+
route: (request, ctx) => routeFusion(request, {
|
|
7052
|
+
planner: ctx.modelRegistry.find(PROVIDER_NAME, FUSION_PLANNER_ID),
|
|
7053
|
+
executor: ctx.modelRegistry.find(PROVIDER_NAME, FUSION_EXECUTOR_ID),
|
|
7054
|
+
defaultModel: ctx.modelRegistry.find(PROVIDER_NAME, defaultModelId)
|
|
7055
|
+
})
|
|
7056
|
+
});
|
|
7057
|
+
}
|
|
7058
|
+
};
|
|
7059
|
+
}
|
|
7060
|
+
|
|
6739
7061
|
// src/provider-lock.ts
|
|
6740
7062
|
function resolveFreePiScope(modelRuntime, providerName, modelIds, env = process.env) {
|
|
6741
7063
|
if (env.FREEPI_KEEP_ENV_PROVIDERS)
|
|
6742
7064
|
return [];
|
|
6743
7065
|
const scoped = [];
|
|
6744
|
-
for (const id of modelIds) {
|
|
7066
|
+
for (const id of new Set([...modelIds, FUSION_ID])) {
|
|
6745
7067
|
const model = modelRuntime.getModel(providerName, id);
|
|
6746
7068
|
if (model)
|
|
6747
7069
|
scoped.push({ model });
|
|
@@ -6834,6 +7156,7 @@ var SDK_TOOLS = [
|
|
|
6834
7156
|
"list_mcp_resource_templates",
|
|
6835
7157
|
"read_mcp_resource"
|
|
6836
7158
|
];
|
|
7159
|
+
var SKILLS_DIR = resolve2(dirname3(fileURLToPath3(import.meta.url)), "..", "skills");
|
|
6837
7160
|
function resolveModelName(opts) {
|
|
6838
7161
|
return opts.models?.[0]?.name || opts.model || MODEL_ID;
|
|
6839
7162
|
}
|
|
@@ -6866,6 +7189,7 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
6866
7189
|
baseUrl: opts.baseUrl,
|
|
6867
7190
|
getToken: () => opts.jwt,
|
|
6868
7191
|
sessionId,
|
|
7192
|
+
getGravityContext: opts.getGravityContext,
|
|
6869
7193
|
onBannerAd: (u) => {
|
|
6870
7194
|
advertiserUrl = u;
|
|
6871
7195
|
}
|
|
@@ -6895,11 +7219,13 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
6895
7219
|
updateLatest: opts.updateLatest
|
|
6896
7220
|
});
|
|
6897
7221
|
const todoToolExtension = createTodoToolExtension();
|
|
7222
|
+
const toolAliasesExtension = createToolAliasesExtension();
|
|
6898
7223
|
const mcpExtension = { name: "pi-mcp", factory: createMcpExtension() };
|
|
6899
7224
|
const codemodeExtension = { name: "pi-codemode", factory: createCodemodeExtension({ models: false }) };
|
|
6900
7225
|
const toolSearchExtension = { name: "pi-tool-search", factory: createToolSearchExtension() };
|
|
6901
7226
|
const extensionFactories = [
|
|
6902
7227
|
providerExtension,
|
|
7228
|
+
createFusionExtension(models[0].id),
|
|
6903
7229
|
adsExtension,
|
|
6904
7230
|
usageToolExtension,
|
|
6905
7231
|
buyToolExtension,
|
|
@@ -6909,6 +7235,7 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
6909
7235
|
commandsExtension,
|
|
6910
7236
|
headerExtension,
|
|
6911
7237
|
todoToolExtension,
|
|
7238
|
+
toolAliasesExtension,
|
|
6912
7239
|
mcpExtension,
|
|
6913
7240
|
codemodeExtension,
|
|
6914
7241
|
toolSearchExtension
|
|
@@ -6916,7 +7243,11 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
6916
7243
|
const settingsManager = buildSettingsManager(opts, models, opts.projectTrusted === true);
|
|
6917
7244
|
return {
|
|
6918
7245
|
settingsManager,
|
|
6919
|
-
resourceLoaderOptions: {
|
|
7246
|
+
resourceLoaderOptions: {
|
|
7247
|
+
extensionFactories,
|
|
7248
|
+
additionalSkillPaths: [SKILLS_DIR],
|
|
7249
|
+
appendSystemPromptOverride: (base) => [...base, AGENT_RULES]
|
|
7250
|
+
},
|
|
6920
7251
|
extensionNames: extensionFactories.map((e) => e.name),
|
|
6921
7252
|
tools: SDK_TOOLS
|
|
6922
7253
|
};
|
|
@@ -6924,7 +7255,7 @@ function buildRuntimeOptions(opts, sessionId) {
|
|
|
6924
7255
|
function getSessionDir(cwd, agentDir) {
|
|
6925
7256
|
const resolvedCwd = resolve2(cwd);
|
|
6926
7257
|
const safePath = `--${resolvedCwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
6927
|
-
return
|
|
7258
|
+
return join4(resolve2(agentDir), "sessions", safePath);
|
|
6928
7259
|
}
|
|
6929
7260
|
async function createSessionManager(cwd, sessionDir, sessionId, api = SessionManager) {
|
|
6930
7261
|
if (sessionId) {
|
|
@@ -6999,6 +7330,9 @@ free-pi — before you start
|
|
|
6999
7330
|
* Ad cards render in the terminal UI. Some ads come from a third-party ad
|
|
7000
7331
|
provider (Gravity), which receives excerpts of your recent conversation
|
|
7001
7332
|
to pick a relevant ad.
|
|
7333
|
+
* Gravity also keeps a session and visitor ID on your machine (in
|
|
7334
|
+
~/.free-pi) and receives usage events from free-pi to measure and
|
|
7335
|
+
attribute ads.
|
|
7002
7336
|
* Your coding sessions (prompts, code, files, and model responses) are saved
|
|
7003
7337
|
as traces and used to train models. We may share or sell that data to
|
|
7004
7338
|
third parties. Once you accept, there is no opt-out, except where the law
|
|
@@ -7017,24 +7351,25 @@ and the Privacy Policy (https://freepi.ai/privacy).
|
|
|
7017
7351
|
|
|
7018
7352
|
Decline and nothing is sent to the free-pi server.
|
|
7019
7353
|
`.trim();
|
|
7020
|
-
var CONSENT_VERSION = "
|
|
7354
|
+
var CONSENT_VERSION = "4";
|
|
7021
7355
|
var NON_INTERACTIVE_CONSENT_TEXT = "free-pi first-run consent needs an interactive terminal (stdin is not a TTY). " + "Run `npx free-pi-cli` in a terminal once to review and accept; " + "after that, non-interactive runs work.";
|
|
7022
7356
|
var CONSENT_PROMPT = "Accept? [y/N] ";
|
|
7023
7357
|
function parseConsentAnswer(answer) {
|
|
7024
7358
|
return /^\s*y(es)?\s*$/i.test(answer);
|
|
7025
7359
|
}
|
|
7360
|
+
var REPROMPT_CONSENT_TEXT = CONSENT_TEXT.replace("Decline and nothing is sent to the free-pi server.", "Decline and free-pi exits; you will be asked again next time.");
|
|
7026
7361
|
|
|
7027
7362
|
// src/prompt.ts
|
|
7028
|
-
async function promptConsent(deps) {
|
|
7363
|
+
async function promptConsent(deps, text = CONSENT_TEXT) {
|
|
7029
7364
|
if (!deps.stdinIsTTY) {
|
|
7030
7365
|
deps.log(NON_INTERACTIVE_CONSENT_TEXT);
|
|
7031
7366
|
return false;
|
|
7032
7367
|
}
|
|
7033
|
-
deps.log(
|
|
7368
|
+
deps.log(text);
|
|
7034
7369
|
const answer = await deps.askLine(CONSENT_PROMPT);
|
|
7035
7370
|
return parseConsentAnswer(answer);
|
|
7036
7371
|
}
|
|
7037
|
-
async function promptConsentInteractive() {
|
|
7372
|
+
async function promptConsentInteractive(text) {
|
|
7038
7373
|
return promptConsent({
|
|
7039
7374
|
stdinIsTTY: process.stdin.isTTY === true,
|
|
7040
7375
|
log: (message) => console.log(message),
|
|
@@ -7046,7 +7381,7 @@ async function promptConsentInteractive() {
|
|
|
7046
7381
|
rl.close();
|
|
7047
7382
|
}
|
|
7048
7383
|
}
|
|
7049
|
-
});
|
|
7384
|
+
}, text);
|
|
7050
7385
|
}
|
|
7051
7386
|
|
|
7052
7387
|
// src/real-spawn.ts
|
|
@@ -7142,6 +7477,24 @@ async function checkToken(baseUrl, jwt) {
|
|
|
7142
7477
|
});
|
|
7143
7478
|
if (res.status === 401)
|
|
7144
7479
|
throw new UnauthorizedError;
|
|
7480
|
+
if (!res.ok)
|
|
7481
|
+
return;
|
|
7482
|
+
const body = await res.json().catch(() => {
|
|
7483
|
+
return;
|
|
7484
|
+
});
|
|
7485
|
+
const v = body?.consent_version;
|
|
7486
|
+
return typeof v === "string" || v === null ? v : undefined;
|
|
7487
|
+
}
|
|
7488
|
+
async function submitUserConsent(baseUrl, jwt, consentVersion) {
|
|
7489
|
+
const res = await fetch(new URL("/me/consent", baseUrl), {
|
|
7490
|
+
method: "POST",
|
|
7491
|
+
headers: { authorization: `Bearer ${jwt}`, "content-type": "application/json" },
|
|
7492
|
+
body: JSON.stringify({ consent_version: consentVersion })
|
|
7493
|
+
});
|
|
7494
|
+
if (res.status === 401)
|
|
7495
|
+
throw new UnauthorizedError;
|
|
7496
|
+
if (!res.ok)
|
|
7497
|
+
throw new Error(`consent submission failed: HTTP ${res.status}`);
|
|
7145
7498
|
}
|
|
7146
7499
|
|
|
7147
7500
|
// src/run.ts
|
|
@@ -7175,7 +7528,7 @@ ${INTRO_TEXT}
|
|
|
7175
7528
|
|
|
7176
7529
|
// src/whats-new.ts
|
|
7177
7530
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
7178
|
-
import { dirname as
|
|
7531
|
+
import { dirname as dirname4 } from "node:path";
|
|
7179
7532
|
function maybeWhatsNew(cliVersion, agentDir) {
|
|
7180
7533
|
try {
|
|
7181
7534
|
const path = getLastSeenVersionPath(agentDir);
|
|
@@ -7195,7 +7548,7 @@ function maybeWhatsNew(cliVersion, agentDir) {
|
|
|
7195
7548
|
}
|
|
7196
7549
|
}
|
|
7197
7550
|
function writeLastSeen(path, version) {
|
|
7198
|
-
mkdirSync(
|
|
7551
|
+
mkdirSync(dirname4(path), { recursive: true });
|
|
7199
7552
|
writeFileSync(path, version, "utf-8");
|
|
7200
7553
|
}
|
|
7201
7554
|
|
|
@@ -7233,9 +7586,24 @@ async function run(deps, argv = process.argv) {
|
|
|
7233
7586
|
return 1;
|
|
7234
7587
|
}
|
|
7235
7588
|
let jwt = await loadJwt(deps.credentialsPath);
|
|
7589
|
+
let consentCurrent = false;
|
|
7236
7590
|
if (jwt) {
|
|
7237
7591
|
try {
|
|
7238
|
-
await checkToken(deps.baseUrl, jwt);
|
|
7592
|
+
const serverConsent = await checkToken(deps.baseUrl, jwt);
|
|
7593
|
+
consentCurrent = serverConsent === CONSENT_VERSION;
|
|
7594
|
+
if (serverConsent !== undefined && serverConsent !== CONSENT_VERSION) {
|
|
7595
|
+
if (!await deps.promptConsent(REPROMPT_CONSENT_TEXT)) {
|
|
7596
|
+
deps.log("Declined. free-pi needs you to accept the updated terms to continue; you will be asked again next time.");
|
|
7597
|
+
return 1;
|
|
7598
|
+
}
|
|
7599
|
+
consentCurrent = true;
|
|
7600
|
+
try {
|
|
7601
|
+
await submitUserConsent(deps.baseUrl, jwt, CONSENT_VERSION);
|
|
7602
|
+
} catch (err) {
|
|
7603
|
+
if (err instanceof UnauthorizedError)
|
|
7604
|
+
throw err;
|
|
7605
|
+
}
|
|
7606
|
+
}
|
|
7239
7607
|
} catch (err) {
|
|
7240
7608
|
if (err instanceof UnauthorizedError) {
|
|
7241
7609
|
await clearJwt(deps.credentialsPath);
|
|
@@ -7251,6 +7619,7 @@ async function run(deps, argv = process.argv) {
|
|
|
7251
7619
|
}
|
|
7252
7620
|
showIntro(deps.log);
|
|
7253
7621
|
jwt = await loginFlow(deps);
|
|
7622
|
+
consentCurrent = true;
|
|
7254
7623
|
}
|
|
7255
7624
|
const versionCheck = await deps.checkClientVersion(deps.baseUrl, CLI_VERSION);
|
|
7256
7625
|
let updateLatest;
|
|
@@ -7271,6 +7640,12 @@ async function run(deps, argv = process.argv) {
|
|
|
7271
7640
|
deps.log(whatsNew);
|
|
7272
7641
|
if (versionCheck.notice)
|
|
7273
7642
|
deps.log(versionCheck.notice);
|
|
7643
|
+
let getGravityContext;
|
|
7644
|
+
if (consentCurrent) {
|
|
7645
|
+
try {
|
|
7646
|
+
getGravityContext = deps.startGravityPixel?.(jwt);
|
|
7647
|
+
} catch {}
|
|
7648
|
+
}
|
|
7274
7649
|
const finalSessionId = await deps.launchPi({
|
|
7275
7650
|
baseUrl: deps.baseUrl,
|
|
7276
7651
|
jwt,
|
|
@@ -7278,7 +7653,8 @@ async function run(deps, argv = process.argv) {
|
|
|
7278
7653
|
session: sessionArg.session,
|
|
7279
7654
|
model: versionCheck.model,
|
|
7280
7655
|
models: versionCheck.models,
|
|
7281
|
-
updateLatest
|
|
7656
|
+
updateLatest,
|
|
7657
|
+
getGravityContext
|
|
7282
7658
|
});
|
|
7283
7659
|
deps.log(finalSessionId ? `
|
|
7284
7660
|
free-pi session ended. Start it again anytime: npx free-pi-cli --session ${finalSessionId}` : `
|
|
@@ -7404,6 +7780,7 @@ ${HELP_TEXT}`);
|
|
|
7404
7780
|
launchPi,
|
|
7405
7781
|
log: (message) => console.log(message),
|
|
7406
7782
|
checkClientVersion,
|
|
7783
|
+
startGravityPixel: (jwt) => startGravityPixel(jwt, join5(dirname5(agentDir), "gravity")),
|
|
7407
7784
|
maybeSelfUpdate: (action, latest) => maybeSelfUpdate(action, latest, {
|
|
7408
7785
|
env: process.env,
|
|
7409
7786
|
autoUpdateEnabled: () => readAutoUpdate(getConfigPath()),
|