zidane 5.6.11 → 5.6.13
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/README.md +19 -2
- package/dist/{agent-C9AKTU_V.d.ts → agent-ClkpElCZ.d.ts} +540 -55
- package/dist/agent-ClkpElCZ.d.ts.map +1 -0
- package/dist/chat.d.ts +47 -17
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +3 -3
- package/dist/{index-6f4T7Gc0.d.ts → index-CTDMMdIy.d.ts} +348 -3
- package/dist/index-CTDMMdIy.d.ts.map +1 -0
- package/dist/{index-DPN7TcXK.d.ts → index-v3Tzobqr.d.ts} +2 -2
- package/dist/{index-DPN7TcXK.d.ts.map → index-v3Tzobqr.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +169 -8
- package/dist/index.js.map +1 -1
- package/dist/{login-BindcfKi.js → login-DS3sf6b5.js} +4 -4
- package/dist/{login-BindcfKi.js.map → login-DS3sf6b5.js.map} +1 -1
- package/dist/{mcp-0jRkIV0g.js → mcp-DGeB7-3D.js} +13 -2
- package/dist/mcp-DGeB7-3D.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-BfmXLDT4.js → messages-Dym8S_YH.js} +303 -8
- package/dist/messages-Dym8S_YH.js.map +1 -0
- package/dist/{presets-CmzMeWg2.js → presets-CZXS_87d.js} +2 -2
- package/dist/{presets-CmzMeWg2.js.map → presets-CZXS_87d.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-C_ahnRBS.js → providers-beXyD9W9.js} +137 -21
- package/dist/providers-beXyD9W9.js.map +1 -0
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +3 -3
- package/dist/restate.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-PUzXZlG6.js → session-BRIsmBSY.js} +5 -2
- package/dist/session-BRIsmBSY.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.js +3 -3
- package/dist/skills.d.ts +2 -2
- package/dist/{tools-CxOfTt3R.js → tools-DE9pR_NG.js} +515 -116
- package/dist/tools-DE9pR_NG.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-DDCHSDdX.d.ts → transcript-anchors-D0TR6djV.d.ts} +4 -4
- package/dist/transcript-anchors-D0TR6djV.d.ts.map +1 -0
- package/dist/tui.d.ts +2 -2
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +12 -8
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CxE8BBau.js → turn-operations-6Yls2HuG.js} +907 -42
- package/dist/turn-operations-6Yls2HuG.js.map +1 -0
- package/dist/types-oKPBdCmL.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/docs/ARCHITECTURE.md +101 -20
- package/docs/CHAT.md +27 -5
- package/docs/RESTATE.md +1 -1
- package/docs/SKILL.md +39 -3
- package/package.json +5 -2
- package/dist/agent-C9AKTU_V.d.ts.map +0 -1
- package/dist/index-6f4T7Gc0.d.ts.map +0 -1
- package/dist/mcp-0jRkIV0g.js.map +0 -1
- package/dist/messages-BfmXLDT4.js.map +0 -1
- package/dist/providers-C_ahnRBS.js.map +0 -1
- package/dist/session-PUzXZlG6.js.map +0 -1
- package/dist/tools-CxOfTt3R.js.map +0 -1
- package/dist/transcript-anchors-DDCHSDdX.d.ts.map +0 -1
- package/dist/turn-operations-CxE8BBau.js.map +0 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { H as previewLine, R as fmtTokens, U as shortId, a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, t as writeFile$1, u as edit, y as shell } from "./tools-
|
|
1
|
+
import { H as previewLine, R as fmtTokens, U as shortId, a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob$1, n as createSpawnTool, o as listFiles, t as writeFile$1, u as edit, y as shell } from "./tools-DE9pR_NG.js";
|
|
2
2
|
import { c as errorMessage } from "./errors-DdZXnyXE.js";
|
|
3
3
|
import { r as toolResultToText } from "./types-oKPBdCmL.js";
|
|
4
|
-
import {
|
|
4
|
+
import { O as joinSystemPrompt } from "./messages-Dym8S_YH.js";
|
|
5
|
+
import { r as normalizeMcpServers, t as connectMcpServers } from "./mcp-DGeB7-3D.js";
|
|
5
6
|
import { a as discoverSkills } from "./interpolate-DM1UcKeQ.js";
|
|
6
7
|
import { n as formatTokenUsage } from "./stats-Lc3zL3RM.js";
|
|
7
|
-
import { n as definePreset, t as composePresets } from "./presets-
|
|
8
|
-
import {
|
|
8
|
+
import { n as definePreset, t as composePresets } from "./presets-CZXS_87d.js";
|
|
9
|
+
import { i as anthropic, n as openai, o as writeFileAtomic, r as cerebras, t as openrouter } from "./providers-beXyD9W9.js";
|
|
9
10
|
import { createRequire } from "node:module";
|
|
10
11
|
import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
11
12
|
import { homedir, tmpdir } from "node:os";
|
|
@@ -13,9 +14,10 @@ import { spawn } from "node:child_process";
|
|
|
13
14
|
import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
14
15
|
import { readdir, stat, writeFile } from "node:fs/promises";
|
|
15
16
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
16
|
-
import { getModel, getModels } from "@
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { getModel, getModels } from "@earendil-works/pi-ai";
|
|
18
|
+
import { createServer } from "node:http";
|
|
19
|
+
import { refreshAnthropicToken, refreshOpenAICodexToken } from "@earendil-works/pi-ai/oauth";
|
|
20
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
19
21
|
import { createGunzip } from "node:zlib";
|
|
20
22
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
21
23
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -33,14 +35,21 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
33
35
|
*
|
|
34
36
|
* The fragments are deliberately byte-stable across sessions — every doctrine
|
|
35
37
|
* piece reads the same on every turn so the system-prompt cache breakpoint
|
|
36
|
-
* (anthropic ephemeral, `src/providers/anthropic.ts
|
|
37
|
-
* Runtime-derived content (cwd, date) lives in {@link envSection} and is
|
|
38
|
-
* computed at agent-build time, not per-turn — same anchor, same cache.
|
|
38
|
+
* (anthropic ephemeral, `src/providers/anthropic.ts`) keeps hitting cache.
|
|
39
39
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* Layout uses {@link SYSTEM_PROMPT_BOUNDARY} from `../system-prompt` to split
|
|
41
|
+
* the rendered prompt into:
|
|
42
|
+
*
|
|
43
|
+
* ┌──────────────┐ cache_control: ephemeral
|
|
44
|
+
* │ STATIC half │ identity + doctrine + guidance + user instructions
|
|
45
|
+
* ├──────────────┤ ← __ZIDANE_SYSTEM_PROMPT_BOUNDARY__
|
|
46
|
+
* │ DYNAMIC half │ envSection(cwd / date / platform)
|
|
47
|
+
* └──────────────┘ (no cache_control)
|
|
48
|
+
*
|
|
49
|
+
* Per-turn cwd / date / platform churn no longer invalidates the doctrine
|
|
50
|
+
* cache above. The TUI's `system:transform` hook rewrites only the dynamic
|
|
51
|
+
* half via `replaceDynamicSection`; the static prefix stays byte-identical
|
|
52
|
+
* for the lifetime of the run.
|
|
44
53
|
*/
|
|
45
54
|
/**
|
|
46
55
|
* Lead line — short, version-stable, written in the same key as Claude Code's
|
|
@@ -218,10 +227,15 @@ function buildEnvSectionFromOpts(opts) {
|
|
|
218
227
|
/**
|
|
219
228
|
* Compose the Build-mode system prompt.
|
|
220
229
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
230
|
+
* Layout (top to bottom):
|
|
231
|
+
*
|
|
232
|
+
* identity → doctrine → guidance → user instructions
|
|
233
|
+
* ─── SYSTEM_PROMPT_BOUNDARY ───
|
|
234
|
+
* env (cwd / project root / date / platform)
|
|
235
|
+
*
|
|
236
|
+
* Everything ABOVE the boundary is byte-stable for the lifetime of the run
|
|
237
|
+
* and rides the prompt cache across turns + sessions. The env block sits
|
|
238
|
+
* BELOW so a moved cwd or rolled-over date doesn't invalidate the doctrine.
|
|
225
239
|
*
|
|
226
240
|
* The fragments are joined with blank lines so the model sees clean section
|
|
227
241
|
* breaks. Don't reorder lightly — every reorder costs one cache miss across
|
|
@@ -229,17 +243,20 @@ function buildEnvSectionFromOpts(opts) {
|
|
|
229
243
|
*/
|
|
230
244
|
function buildBuildSystem(opts = {}) {
|
|
231
245
|
const interactionGuidance = opts.allowInteraction === false ? INTERACTION_GUIDANCE_NO_PROMPTS : INTERACTION_GUIDANCE;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
246
|
+
const env = buildEnvSectionFromOpts(opts);
|
|
247
|
+
return composeWithBoundary({
|
|
248
|
+
static: [
|
|
249
|
+
IDENTITY_PREFIX,
|
|
250
|
+
DOING_TASKS_DOCTRINE,
|
|
251
|
+
ACTIONS_WITH_CARE_DOCTRINE,
|
|
252
|
+
TOKEN_DISCIPLINE_DOCTRINE,
|
|
253
|
+
SUBAGENT_GUIDANCE,
|
|
254
|
+
COMMUNICATION_DOCTRINE,
|
|
255
|
+
interactionGuidance,
|
|
256
|
+
opts.userInstructions || null
|
|
257
|
+
],
|
|
258
|
+
dynamic: [env]
|
|
259
|
+
});
|
|
243
260
|
}
|
|
244
261
|
/**
|
|
245
262
|
* Compose the Plan-mode system prompt. Subset of Build: drops the
|
|
@@ -248,15 +265,28 @@ function buildBuildSystem(opts = {}) {
|
|
|
248
265
|
*/
|
|
249
266
|
function buildPlanSystem(opts = {}) {
|
|
250
267
|
const noPrompts = opts.allowInteraction === false;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
268
|
+
const env = buildEnvSectionFromOpts(opts);
|
|
269
|
+
return composeWithBoundary({
|
|
270
|
+
static: [
|
|
271
|
+
IDENTITY_PREFIX,
|
|
272
|
+
noPrompts ? PLAN_MODE_DOCTRINE_NO_PROMPTS : PLAN_MODE_DOCTRINE,
|
|
273
|
+
TOKEN_DISCIPLINE_DOCTRINE,
|
|
274
|
+
COMMUNICATION_DOCTRINE,
|
|
275
|
+
noPrompts ? INTERACTION_GUIDANCE_NO_PROMPTS : INTERACTION_GUIDANCE,
|
|
276
|
+
opts.userInstructions || null
|
|
277
|
+
],
|
|
278
|
+
dynamic: [env]
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Glue helper — compose a system prompt with the boundary marker between the
|
|
283
|
+
* static and dynamic halves. Returns a marker-free string when the dynamic
|
|
284
|
+
* half is empty (e.g. SDK consumers building the prompt without an
|
|
285
|
+
* `envSection`), so the historical single-block cache path is preserved
|
|
286
|
+
* end-to-end.
|
|
287
|
+
*/
|
|
288
|
+
function composeWithBoundary(parts) {
|
|
289
|
+
return joinSystemPrompt(joinPrompt(parts.static), joinPrompt(parts.dynamic));
|
|
260
290
|
}
|
|
261
291
|
function joinPrompt(parts) {
|
|
262
292
|
return parts.filter((p) => typeof p === "string" && p.length > 0).join("\n\n");
|
|
@@ -858,7 +888,7 @@ function accentColor(accent, COLOR) {
|
|
|
858
888
|
const READ_ONLY_TOOLS = {
|
|
859
889
|
readFile: readFile$1,
|
|
860
890
|
listFiles,
|
|
861
|
-
glob,
|
|
891
|
+
glob: glob$1,
|
|
862
892
|
grep
|
|
863
893
|
};
|
|
864
894
|
/** Full build-mode tool slice — read + write + shell + spawn. */
|
|
@@ -869,7 +899,7 @@ const BUILD_TOOLS = {
|
|
|
869
899
|
listFiles,
|
|
870
900
|
edit,
|
|
871
901
|
multiEdit,
|
|
872
|
-
glob,
|
|
902
|
+
glob: glob$1,
|
|
873
903
|
grep
|
|
874
904
|
};
|
|
875
905
|
/**
|
|
@@ -1271,7 +1301,631 @@ function readIfPresent(path, source) {
|
|
|
1271
1301
|
};
|
|
1272
1302
|
}
|
|
1273
1303
|
//#endregion
|
|
1304
|
+
//#region src/chat/oauth-page/pkce.ts
|
|
1305
|
+
/**
|
|
1306
|
+
* PKCE helper. Inlined here (rather than imported from pi-ai) because
|
|
1307
|
+
* `@earendil-works/pi-ai/oauth` does not re-export it, and we don't want
|
|
1308
|
+
* to reach into `node_modules/.../utils/oauth/pkce.js` — that's an
|
|
1309
|
+
* implementation-detail path that breaks on minor upstream rearrangements.
|
|
1310
|
+
*
|
|
1311
|
+
* Web Crypto API only. Works under Bun + Node 22+ without any node:crypto
|
|
1312
|
+
* import (matches pi-ai's own behavior). Output is base64url per RFC 7636.
|
|
1313
|
+
*/
|
|
1314
|
+
function base64UrlEncode(bytes) {
|
|
1315
|
+
let binary = "";
|
|
1316
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
1317
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1318
|
+
}
|
|
1319
|
+
async function generatePkce() {
|
|
1320
|
+
const verifierBytes = new Uint8Array(32);
|
|
1321
|
+
crypto.getRandomValues(verifierBytes);
|
|
1322
|
+
const verifier = base64UrlEncode(verifierBytes);
|
|
1323
|
+
const data = new TextEncoder().encode(verifier);
|
|
1324
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
1325
|
+
return {
|
|
1326
|
+
verifier,
|
|
1327
|
+
challenge: base64UrlEncode(new Uint8Array(hashBuffer))
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
//#endregion
|
|
1331
|
+
//#region src/chat/oauth-page/server.ts
|
|
1332
|
+
const CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
|
|
1333
|
+
function buildRequestHandler(opts, pending) {
|
|
1334
|
+
return (req, res) => {
|
|
1335
|
+
try {
|
|
1336
|
+
const url = new URL(req.url || "", "http://localhost");
|
|
1337
|
+
if (url.pathname !== opts.path) {
|
|
1338
|
+
respondError(res, 404, opts, "Callback route not found.");
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
const error = url.searchParams.get("error");
|
|
1342
|
+
if (error) {
|
|
1343
|
+
respondError(res, 400, opts, `${opts.providerName} authentication did not complete.`, `Error: ${error}`);
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
const code = url.searchParams.get("code");
|
|
1347
|
+
const state = url.searchParams.get("state");
|
|
1348
|
+
if (!code || !state) {
|
|
1349
|
+
respondError(res, 400, opts, "Missing code or state parameter.");
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (state !== opts.expectedState) {
|
|
1353
|
+
respondError(res, 400, opts, "State mismatch.");
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1357
|
+
res.end(opts.renderPage({
|
|
1358
|
+
kind: "success",
|
|
1359
|
+
provider: opts.providerName,
|
|
1360
|
+
message: opts.successMessage
|
|
1361
|
+
}));
|
|
1362
|
+
pending.settle({
|
|
1363
|
+
code,
|
|
1364
|
+
state
|
|
1365
|
+
});
|
|
1366
|
+
} catch {
|
|
1367
|
+
respondError(res, 500, opts, "Internal error while processing the callback.");
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
function respondError(res, status, opts, message, details) {
|
|
1372
|
+
res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
|
|
1373
|
+
res.end(opts.renderPage({
|
|
1374
|
+
kind: "error",
|
|
1375
|
+
provider: opts.providerName,
|
|
1376
|
+
message,
|
|
1377
|
+
details
|
|
1378
|
+
}));
|
|
1379
|
+
}
|
|
1380
|
+
function buildStubHandle(redirectUri, server) {
|
|
1381
|
+
return {
|
|
1382
|
+
redirectUri,
|
|
1383
|
+
waitForCode: async () => null,
|
|
1384
|
+
cancelWait: () => {},
|
|
1385
|
+
close: () => {
|
|
1386
|
+
try {
|
|
1387
|
+
server?.close();
|
|
1388
|
+
} catch {}
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Start the loopback HTTP callback server. The returned handle is the
|
|
1394
|
+
* caller's contract — they `await waitForCode()`, race it against manual
|
|
1395
|
+
* paste, then `close()` in a `finally`.
|
|
1396
|
+
*/
|
|
1397
|
+
async function startCallbackServer(opts) {
|
|
1398
|
+
const onListenError = opts.onListenError ?? "reject";
|
|
1399
|
+
const redirectUri = `http://localhost:${opts.port}${opts.path}`;
|
|
1400
|
+
return new Promise((resolve, reject) => {
|
|
1401
|
+
let settled = false;
|
|
1402
|
+
const pending = { settle: () => {} };
|
|
1403
|
+
const waitPromise = new Promise((resolveWait) => {
|
|
1404
|
+
pending.settle = (value) => {
|
|
1405
|
+
if (settled) return;
|
|
1406
|
+
settled = true;
|
|
1407
|
+
resolveWait(value);
|
|
1408
|
+
};
|
|
1409
|
+
});
|
|
1410
|
+
const server = createServer(buildRequestHandler(opts, pending));
|
|
1411
|
+
server.on("error", (err) => {
|
|
1412
|
+
if (onListenError === "resolveWithStub") {
|
|
1413
|
+
pending.settle(null);
|
|
1414
|
+
resolve(buildStubHandle(redirectUri, server));
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
reject(err);
|
|
1418
|
+
});
|
|
1419
|
+
server.listen(opts.port, CALLBACK_HOST, () => {
|
|
1420
|
+
resolve({
|
|
1421
|
+
redirectUri,
|
|
1422
|
+
waitForCode: () => waitPromise,
|
|
1423
|
+
cancelWait: () => pending.settle(null),
|
|
1424
|
+
close: () => {
|
|
1425
|
+
try {
|
|
1426
|
+
server.close();
|
|
1427
|
+
} catch {}
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
//#endregion
|
|
1434
|
+
//#region src/chat/oauth-page/anthropic.ts
|
|
1435
|
+
const CLIENT_ID$1 = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
1436
|
+
const AUTHORIZE_URL$1 = "https://claude.ai/oauth/authorize";
|
|
1437
|
+
const TOKEN_URL$1 = "https://platform.claude.com/v1/oauth/token";
|
|
1438
|
+
const CALLBACK_PORT$1 = 53692;
|
|
1439
|
+
const CALLBACK_PATH$1 = "/callback";
|
|
1440
|
+
const SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
|
|
1441
|
+
const PROVIDER_NAME$1 = "Anthropic";
|
|
1442
|
+
/**
|
|
1443
|
+
* Parse what the user pasted into the manual-code prompt. Accepts:
|
|
1444
|
+
* - the bare authorization code
|
|
1445
|
+
* - the full `redirect_uri?code=...&state=...` URL
|
|
1446
|
+
* - the `code#state` shorthand Anthropic surfaces on the page
|
|
1447
|
+
* - a raw query string with `code=...&state=...`
|
|
1448
|
+
*
|
|
1449
|
+
* Matches pi-ai's parse table so an existing user's muscle memory still works.
|
|
1450
|
+
*/
|
|
1451
|
+
function parseAuthorizationInput$1(input) {
|
|
1452
|
+
const value = input.trim();
|
|
1453
|
+
if (!value) return {};
|
|
1454
|
+
try {
|
|
1455
|
+
const url = new URL(value);
|
|
1456
|
+
return {
|
|
1457
|
+
code: url.searchParams.get("code") ?? void 0,
|
|
1458
|
+
state: url.searchParams.get("state") ?? void 0
|
|
1459
|
+
};
|
|
1460
|
+
} catch {}
|
|
1461
|
+
if (value.includes("#")) {
|
|
1462
|
+
const [code, state] = value.split("#", 2);
|
|
1463
|
+
return {
|
|
1464
|
+
code,
|
|
1465
|
+
state
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
if (value.includes("code=")) {
|
|
1469
|
+
const params = new URLSearchParams(value);
|
|
1470
|
+
return {
|
|
1471
|
+
code: params.get("code") ?? void 0,
|
|
1472
|
+
state: params.get("state") ?? void 0
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
return { code: value };
|
|
1476
|
+
}
|
|
1477
|
+
async function postJson(url, body) {
|
|
1478
|
+
const response = await fetch(url, {
|
|
1479
|
+
method: "POST",
|
|
1480
|
+
headers: {
|
|
1481
|
+
"Content-Type": "application/json",
|
|
1482
|
+
"Accept": "application/json"
|
|
1483
|
+
},
|
|
1484
|
+
body: JSON.stringify(body),
|
|
1485
|
+
signal: AbortSignal.timeout(3e4)
|
|
1486
|
+
});
|
|
1487
|
+
const responseBody = await response.text();
|
|
1488
|
+
if (!response.ok) throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
|
|
1489
|
+
return responseBody;
|
|
1490
|
+
}
|
|
1491
|
+
async function exchangeAuthorizationCode$1(code, state, verifier, redirectUri) {
|
|
1492
|
+
const responseBody = await postJson(TOKEN_URL$1, {
|
|
1493
|
+
grant_type: "authorization_code",
|
|
1494
|
+
client_id: CLIENT_ID$1,
|
|
1495
|
+
code,
|
|
1496
|
+
state,
|
|
1497
|
+
redirect_uri: redirectUri,
|
|
1498
|
+
code_verifier: verifier
|
|
1499
|
+
});
|
|
1500
|
+
const tokenData = JSON.parse(responseBody);
|
|
1501
|
+
return {
|
|
1502
|
+
refresh: tokenData.refresh_token,
|
|
1503
|
+
access: tokenData.access_token,
|
|
1504
|
+
expires: Date.now() + tokenData.expires_in * 1e3 - 300 * 1e3
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
async function loginAnthropicWithCustomPage(options) {
|
|
1508
|
+
const { verifier, challenge } = await generatePkce();
|
|
1509
|
+
const server = await startCallbackServer({
|
|
1510
|
+
port: CALLBACK_PORT$1,
|
|
1511
|
+
path: CALLBACK_PATH$1,
|
|
1512
|
+
expectedState: verifier,
|
|
1513
|
+
providerName: PROVIDER_NAME$1,
|
|
1514
|
+
renderPage: options.renderPage,
|
|
1515
|
+
successMessage: "Anthropic authentication completed. You can close this window.",
|
|
1516
|
+
onListenError: "reject"
|
|
1517
|
+
});
|
|
1518
|
+
let code;
|
|
1519
|
+
let state;
|
|
1520
|
+
try {
|
|
1521
|
+
const authParams = new URLSearchParams({
|
|
1522
|
+
code: "true",
|
|
1523
|
+
client_id: CLIENT_ID$1,
|
|
1524
|
+
response_type: "code",
|
|
1525
|
+
redirect_uri: server.redirectUri,
|
|
1526
|
+
scope: SCOPES,
|
|
1527
|
+
code_challenge: challenge,
|
|
1528
|
+
code_challenge_method: "S256",
|
|
1529
|
+
state: verifier
|
|
1530
|
+
});
|
|
1531
|
+
options.onAuth({
|
|
1532
|
+
url: `${AUTHORIZE_URL$1}?${authParams.toString()}`,
|
|
1533
|
+
instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
|
|
1534
|
+
});
|
|
1535
|
+
if (options.onManualCodeInput) {
|
|
1536
|
+
let manualInput;
|
|
1537
|
+
let manualError;
|
|
1538
|
+
const manualPromise = options.onManualCodeInput().then((input) => {
|
|
1539
|
+
manualInput = input;
|
|
1540
|
+
server.cancelWait();
|
|
1541
|
+
}).catch((err) => {
|
|
1542
|
+
manualError = err instanceof Error ? err : new Error(String(err));
|
|
1543
|
+
server.cancelWait();
|
|
1544
|
+
});
|
|
1545
|
+
const result = await server.waitForCode();
|
|
1546
|
+
if (manualError) throw manualError;
|
|
1547
|
+
if (result?.code) {
|
|
1548
|
+
code = result.code;
|
|
1549
|
+
state = result.state;
|
|
1550
|
+
} else if (manualInput) {
|
|
1551
|
+
const parsed = parseAuthorizationInput$1(manualInput);
|
|
1552
|
+
if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
|
|
1553
|
+
code = parsed.code;
|
|
1554
|
+
state = parsed.state ?? verifier;
|
|
1555
|
+
}
|
|
1556
|
+
if (!code) {
|
|
1557
|
+
await manualPromise;
|
|
1558
|
+
if (manualError) throw manualError;
|
|
1559
|
+
if (manualInput) {
|
|
1560
|
+
const parsed = parseAuthorizationInput$1(manualInput);
|
|
1561
|
+
if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
|
|
1562
|
+
code = parsed.code;
|
|
1563
|
+
state = parsed.state ?? verifier;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
} else {
|
|
1567
|
+
const result = await server.waitForCode();
|
|
1568
|
+
if (result?.code) {
|
|
1569
|
+
code = result.code;
|
|
1570
|
+
state = result.state;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
if (!code) {
|
|
1574
|
+
const parsed = parseAuthorizationInput$1(await options.onPrompt({
|
|
1575
|
+
message: "Paste the authorization code or full redirect URL:",
|
|
1576
|
+
placeholder: server.redirectUri
|
|
1577
|
+
}));
|
|
1578
|
+
if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
|
|
1579
|
+
code = parsed.code;
|
|
1580
|
+
state = parsed.state ?? verifier;
|
|
1581
|
+
}
|
|
1582
|
+
if (!code) throw new Error("Missing authorization code");
|
|
1583
|
+
if (!state) throw new Error("Missing OAuth state");
|
|
1584
|
+
options.onProgress?.("Exchanging authorization code for tokens...");
|
|
1585
|
+
return await exchangeAuthorizationCode$1(code, state, verifier, server.redirectUri);
|
|
1586
|
+
} finally {
|
|
1587
|
+
server.close();
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Build an `OAuthProviderInterface` that behaves identically to pi-ai's
|
|
1592
|
+
* `anthropicOAuthProvider` except for the callback page HTML. Drop this
|
|
1593
|
+
* onto `ProviderDescriptor.oauthProvider` to override.
|
|
1594
|
+
*/
|
|
1595
|
+
function createAnthropicOAuthProviderWithCustomPage(renderPage) {
|
|
1596
|
+
return {
|
|
1597
|
+
id: "anthropic",
|
|
1598
|
+
name: "Anthropic (Claude Pro/Max)",
|
|
1599
|
+
usesCallbackServer: true,
|
|
1600
|
+
async login(callbacks) {
|
|
1601
|
+
return loginAnthropicWithCustomPage({
|
|
1602
|
+
renderPage,
|
|
1603
|
+
onAuth: callbacks.onAuth,
|
|
1604
|
+
onPrompt: callbacks.onPrompt,
|
|
1605
|
+
onProgress: callbacks.onProgress,
|
|
1606
|
+
onManualCodeInput: callbacks.onManualCodeInput
|
|
1607
|
+
});
|
|
1608
|
+
},
|
|
1609
|
+
async refreshToken(credentials) {
|
|
1610
|
+
return refreshAnthropicToken(credentials.refresh);
|
|
1611
|
+
},
|
|
1612
|
+
getApiKey(credentials) {
|
|
1613
|
+
return credentials.access;
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
//#endregion
|
|
1618
|
+
//#region src/chat/oauth-page/openai-codex.ts
|
|
1619
|
+
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
1620
|
+
const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
|
|
1621
|
+
const TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
1622
|
+
const CALLBACK_PORT = 1455;
|
|
1623
|
+
const CALLBACK_PATH = "/auth/callback";
|
|
1624
|
+
const SCOPE = "openid profile email offline_access";
|
|
1625
|
+
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
1626
|
+
const PROVIDER_NAME = "OpenAI Codex";
|
|
1627
|
+
function createState() {
|
|
1628
|
+
return randomBytes(16).toString("hex");
|
|
1629
|
+
}
|
|
1630
|
+
function parseAuthorizationInput(input) {
|
|
1631
|
+
const value = input.trim();
|
|
1632
|
+
if (!value) return {};
|
|
1633
|
+
try {
|
|
1634
|
+
const url = new URL(value);
|
|
1635
|
+
return {
|
|
1636
|
+
code: url.searchParams.get("code") ?? void 0,
|
|
1637
|
+
state: url.searchParams.get("state") ?? void 0
|
|
1638
|
+
};
|
|
1639
|
+
} catch {}
|
|
1640
|
+
if (value.includes("#")) {
|
|
1641
|
+
const [code, state] = value.split("#", 2);
|
|
1642
|
+
return {
|
|
1643
|
+
code,
|
|
1644
|
+
state
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
if (value.includes("code=")) {
|
|
1648
|
+
const params = new URLSearchParams(value);
|
|
1649
|
+
return {
|
|
1650
|
+
code: params.get("code") ?? void 0,
|
|
1651
|
+
state: params.get("state") ?? void 0
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
return { code: value };
|
|
1655
|
+
}
|
|
1656
|
+
function decodeJwt(token) {
|
|
1657
|
+
try {
|
|
1658
|
+
const parts = token.split(".");
|
|
1659
|
+
if (parts.length !== 3) return null;
|
|
1660
|
+
const payload = parts[1] ?? "";
|
|
1661
|
+
const decoded = atob(payload);
|
|
1662
|
+
return JSON.parse(decoded);
|
|
1663
|
+
} catch {
|
|
1664
|
+
return null;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
function getAccountId(accessToken) {
|
|
1668
|
+
const accountId = (decodeJwt(accessToken)?.[JWT_CLAIM_PATH])?.chatgpt_account_id;
|
|
1669
|
+
return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
|
|
1670
|
+
}
|
|
1671
|
+
async function exchangeAuthorizationCode(code, verifier, redirectUri) {
|
|
1672
|
+
const response = await fetch(TOKEN_URL, {
|
|
1673
|
+
method: "POST",
|
|
1674
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1675
|
+
body: new URLSearchParams({
|
|
1676
|
+
grant_type: "authorization_code",
|
|
1677
|
+
client_id: CLIENT_ID,
|
|
1678
|
+
code,
|
|
1679
|
+
code_verifier: verifier,
|
|
1680
|
+
redirect_uri: redirectUri
|
|
1681
|
+
})
|
|
1682
|
+
});
|
|
1683
|
+
if (!response.ok) {
|
|
1684
|
+
const text = await response.text().catch(() => "");
|
|
1685
|
+
return {
|
|
1686
|
+
type: "failed",
|
|
1687
|
+
message: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
const json = await response.json();
|
|
1691
|
+
if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") return {
|
|
1692
|
+
type: "failed",
|
|
1693
|
+
message: `OpenAI Codex token exchange response missing fields: ${JSON.stringify(json)}`
|
|
1694
|
+
};
|
|
1695
|
+
return {
|
|
1696
|
+
type: "success",
|
|
1697
|
+
access: json.access_token,
|
|
1698
|
+
refresh: json.refresh_token,
|
|
1699
|
+
expires: Date.now() + json.expires_in * 1e3
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
async function loginOpenAICodexWithCustomPage(options) {
|
|
1703
|
+
const { verifier, challenge } = await generatePkce();
|
|
1704
|
+
const state = createState();
|
|
1705
|
+
const originator = options.originator ?? "pi";
|
|
1706
|
+
const server = await startCallbackServer({
|
|
1707
|
+
port: CALLBACK_PORT,
|
|
1708
|
+
path: CALLBACK_PATH,
|
|
1709
|
+
expectedState: state,
|
|
1710
|
+
providerName: PROVIDER_NAME,
|
|
1711
|
+
renderPage: options.renderPage,
|
|
1712
|
+
successMessage: "OpenAI authentication completed. You can close this window.",
|
|
1713
|
+
onListenError: "resolveWithStub"
|
|
1714
|
+
});
|
|
1715
|
+
const authUrl = new URL(AUTHORIZE_URL);
|
|
1716
|
+
authUrl.searchParams.set("response_type", "code");
|
|
1717
|
+
authUrl.searchParams.set("client_id", CLIENT_ID);
|
|
1718
|
+
authUrl.searchParams.set("redirect_uri", server.redirectUri);
|
|
1719
|
+
authUrl.searchParams.set("scope", SCOPE);
|
|
1720
|
+
authUrl.searchParams.set("code_challenge", challenge);
|
|
1721
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
1722
|
+
authUrl.searchParams.set("state", state);
|
|
1723
|
+
authUrl.searchParams.set("id_token_add_organizations", "true");
|
|
1724
|
+
authUrl.searchParams.set("codex_cli_simplified_flow", "true");
|
|
1725
|
+
authUrl.searchParams.set("originator", originator);
|
|
1726
|
+
options.onAuth({
|
|
1727
|
+
url: authUrl.toString(),
|
|
1728
|
+
instructions: "A browser window should open. Complete login to finish."
|
|
1729
|
+
});
|
|
1730
|
+
let code;
|
|
1731
|
+
try {
|
|
1732
|
+
if (options.onManualCodeInput) {
|
|
1733
|
+
let manualCode;
|
|
1734
|
+
let manualError;
|
|
1735
|
+
const manualPromise = options.onManualCodeInput().then((input) => {
|
|
1736
|
+
manualCode = input;
|
|
1737
|
+
server.cancelWait();
|
|
1738
|
+
}).catch((err) => {
|
|
1739
|
+
manualError = err instanceof Error ? err : new Error(String(err));
|
|
1740
|
+
server.cancelWait();
|
|
1741
|
+
});
|
|
1742
|
+
const result = await server.waitForCode();
|
|
1743
|
+
if (manualError) throw manualError;
|
|
1744
|
+
if (result?.code) code = result.code;
|
|
1745
|
+
else if (manualCode) {
|
|
1746
|
+
const parsed = parseAuthorizationInput(manualCode);
|
|
1747
|
+
if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
|
|
1748
|
+
code = parsed.code;
|
|
1749
|
+
}
|
|
1750
|
+
if (!code) {
|
|
1751
|
+
await manualPromise;
|
|
1752
|
+
if (manualError) throw manualError;
|
|
1753
|
+
if (manualCode) {
|
|
1754
|
+
const parsed = parseAuthorizationInput(manualCode);
|
|
1755
|
+
if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
|
|
1756
|
+
code = parsed.code;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
} else {
|
|
1760
|
+
const result = await server.waitForCode();
|
|
1761
|
+
if (result?.code) code = result.code;
|
|
1762
|
+
}
|
|
1763
|
+
if (!code) {
|
|
1764
|
+
const parsed = parseAuthorizationInput(await options.onPrompt({ message: "Paste the authorization code (or full redirect URL):" }));
|
|
1765
|
+
if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
|
|
1766
|
+
code = parsed.code;
|
|
1767
|
+
}
|
|
1768
|
+
if (!code) throw new Error("Missing authorization code");
|
|
1769
|
+
const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri);
|
|
1770
|
+
if (tokenResult.type !== "success") throw new Error(tokenResult.message);
|
|
1771
|
+
const accountId = getAccountId(tokenResult.access);
|
|
1772
|
+
if (!accountId) throw new Error("Failed to extract accountId from token");
|
|
1773
|
+
return {
|
|
1774
|
+
access: tokenResult.access,
|
|
1775
|
+
refresh: tokenResult.refresh,
|
|
1776
|
+
expires: tokenResult.expires,
|
|
1777
|
+
accountId
|
|
1778
|
+
};
|
|
1779
|
+
} finally {
|
|
1780
|
+
server.close();
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Build an `OAuthProviderInterface` that behaves identically to pi-ai's
|
|
1785
|
+
* `openaiCodexOAuthProvider` except for the callback page HTML.
|
|
1786
|
+
*/
|
|
1787
|
+
function createOpenAICodexOAuthProviderWithCustomPage(renderPage) {
|
|
1788
|
+
return {
|
|
1789
|
+
id: "openai-codex",
|
|
1790
|
+
name: "ChatGPT Plus/Pro (Codex Subscription)",
|
|
1791
|
+
usesCallbackServer: true,
|
|
1792
|
+
async login(callbacks) {
|
|
1793
|
+
return loginOpenAICodexWithCustomPage({
|
|
1794
|
+
renderPage,
|
|
1795
|
+
onAuth: callbacks.onAuth,
|
|
1796
|
+
onPrompt: callbacks.onPrompt,
|
|
1797
|
+
onProgress: callbacks.onProgress,
|
|
1798
|
+
onManualCodeInput: callbacks.onManualCodeInput
|
|
1799
|
+
});
|
|
1800
|
+
},
|
|
1801
|
+
async refreshToken(credentials) {
|
|
1802
|
+
return refreshOpenAICodexToken(credentials.refresh);
|
|
1803
|
+
},
|
|
1804
|
+
getApiKey(credentials) {
|
|
1805
|
+
return credentials.access;
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
//#endregion
|
|
1810
|
+
//#region src/chat/oauth-page/render.ts
|
|
1811
|
+
function escapeHtml(value) {
|
|
1812
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Default zidane-themed page. Visually neutral but distinguishable from
|
|
1816
|
+
* pi-ai's stock page — dark background, mono headings, no logo (host can
|
|
1817
|
+
* pass a custom renderer to add one).
|
|
1818
|
+
*/
|
|
1819
|
+
const renderDefaultCallbackPage = (page) => {
|
|
1820
|
+
const heading = escapeHtml(page.kind === "success" ? `Signed in to ${page.provider}` : `Could not sign in to ${page.provider}`);
|
|
1821
|
+
const message = escapeHtml(page.message);
|
|
1822
|
+
const details = page.details ? escapeHtml(page.details) : void 0;
|
|
1823
|
+
return `<!doctype html>
|
|
1824
|
+
<html lang="en">
|
|
1825
|
+
<head>
|
|
1826
|
+
<meta charset="utf-8" />
|
|
1827
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1828
|
+
<title>${heading}</title>
|
|
1829
|
+
<style>
|
|
1830
|
+
:root {
|
|
1831
|
+
--text: #f4f4f5;
|
|
1832
|
+
--text-dim: #a1a1aa;
|
|
1833
|
+
--accent: ${page.kind === "success" ? "#22d3ee" : "#f87171"};
|
|
1834
|
+
--page-bg: #0a0a0a;
|
|
1835
|
+
--panel-bg: #131316;
|
|
1836
|
+
--border: #27272a;
|
|
1837
|
+
--font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
1838
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1839
|
+
}
|
|
1840
|
+
* { box-sizing: border-box; }
|
|
1841
|
+
html { color-scheme: dark; }
|
|
1842
|
+
body {
|
|
1843
|
+
margin: 0;
|
|
1844
|
+
min-height: 100vh;
|
|
1845
|
+
display: flex;
|
|
1846
|
+
align-items: center;
|
|
1847
|
+
justify-content: center;
|
|
1848
|
+
padding: 24px;
|
|
1849
|
+
background: var(--page-bg);
|
|
1850
|
+
color: var(--text);
|
|
1851
|
+
font-family: var(--font-sans);
|
|
1852
|
+
}
|
|
1853
|
+
main {
|
|
1854
|
+
width: 100%;
|
|
1855
|
+
max-width: 520px;
|
|
1856
|
+
padding: 32px;
|
|
1857
|
+
background: var(--panel-bg);
|
|
1858
|
+
border: 1px solid var(--border);
|
|
1859
|
+
border-radius: 12px;
|
|
1860
|
+
}
|
|
1861
|
+
.label {
|
|
1862
|
+
font-family: var(--font-mono);
|
|
1863
|
+
font-size: 12px;
|
|
1864
|
+
letter-spacing: 0.08em;
|
|
1865
|
+
text-transform: uppercase;
|
|
1866
|
+
color: var(--accent);
|
|
1867
|
+
margin-bottom: 12px;
|
|
1868
|
+
}
|
|
1869
|
+
h1 {
|
|
1870
|
+
margin: 0 0 12px;
|
|
1871
|
+
font-size: 22px;
|
|
1872
|
+
font-weight: 600;
|
|
1873
|
+
letter-spacing: -0.01em;
|
|
1874
|
+
color: var(--text);
|
|
1875
|
+
}
|
|
1876
|
+
p {
|
|
1877
|
+
margin: 0;
|
|
1878
|
+
line-height: 1.6;
|
|
1879
|
+
color: var(--text-dim);
|
|
1880
|
+
font-size: 14px;
|
|
1881
|
+
}
|
|
1882
|
+
.details {
|
|
1883
|
+
margin-top: 18px;
|
|
1884
|
+
padding: 12px 14px;
|
|
1885
|
+
background: var(--page-bg);
|
|
1886
|
+
border: 1px solid var(--border);
|
|
1887
|
+
border-radius: 8px;
|
|
1888
|
+
font-family: var(--font-mono);
|
|
1889
|
+
font-size: 12px;
|
|
1890
|
+
color: var(--text-dim);
|
|
1891
|
+
white-space: pre-wrap;
|
|
1892
|
+
word-break: break-word;
|
|
1893
|
+
}
|
|
1894
|
+
</style>
|
|
1895
|
+
</head>
|
|
1896
|
+
<body>
|
|
1897
|
+
<main>
|
|
1898
|
+
<div class="label">zidane · OAuth · ${escapeHtml(page.provider)}</div>
|
|
1899
|
+
<h1>${heading}</h1>
|
|
1900
|
+
<p>${message}</p>
|
|
1901
|
+
${details ? `<div class="details">${details}</div>` : ""}
|
|
1902
|
+
</main>
|
|
1903
|
+
</body>
|
|
1904
|
+
</html>`;
|
|
1905
|
+
};
|
|
1906
|
+
//#endregion
|
|
1907
|
+
//#region src/chat/oauth-page/index.ts
|
|
1908
|
+
/**
|
|
1909
|
+
* Bundle helper — returns both Anthropic + OpenAI Codex providers wired
|
|
1910
|
+
* with the same renderer. Hosts that want different renderers per provider
|
|
1911
|
+
* should call the individual `create…WithCustomPage` factories instead.
|
|
1912
|
+
*/
|
|
1913
|
+
function createCustomCallbackOAuthProviders(renderPage) {
|
|
1914
|
+
return {
|
|
1915
|
+
anthropic: createAnthropicOAuthProviderWithCustomPage(renderPage),
|
|
1916
|
+
openaiCodex: createOpenAICodexOAuthProviderWithCustomPage(renderPage)
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
//#endregion
|
|
1274
1920
|
//#region src/chat/providers.ts
|
|
1921
|
+
/**
|
|
1922
|
+
* pi-ai's stock Anthropic + Codex OAuth providers bake their own callback
|
|
1923
|
+
* HTML; we route both through `renderDefaultCallbackPage` so the post-redirect
|
|
1924
|
+
* page matches zidane's theme. The override lives in `src/chat/oauth-page/`
|
|
1925
|
+
* — see that folder's `index.ts` for the deletion path once pi-ai exposes
|
|
1926
|
+
* a `renderCallbackPage` hook upstream.
|
|
1927
|
+
*/
|
|
1928
|
+
const { anthropic: anthropicOAuthProvider, openaiCodex: openaiCodexOAuthProvider } = createCustomCallbackOAuthProviders(renderDefaultCallbackPage);
|
|
1275
1929
|
/** Convenience accessor — returns `credentialFileKey ?? key`. */
|
|
1276
1930
|
function credKeyOf(desc) {
|
|
1277
1931
|
return desc.credentialFileKey ?? desc.key;
|
|
@@ -5954,6 +6608,203 @@ const CRUSH_THEME = {
|
|
|
5954
6608
|
}
|
|
5955
6609
|
};
|
|
5956
6610
|
//#endregion
|
|
6611
|
+
//#region src/chat/themes/gruvbox.ts
|
|
6612
|
+
const DARK = {
|
|
6613
|
+
dark: true,
|
|
6614
|
+
bg0: "#282828",
|
|
6615
|
+
bg1: "#3c3836",
|
|
6616
|
+
bg2: "#504945",
|
|
6617
|
+
bg3: "#665c54",
|
|
6618
|
+
bg4: "#7c6f64",
|
|
6619
|
+
fg0: "#fbf1c7",
|
|
6620
|
+
fg1: "#ebdbb2",
|
|
6621
|
+
fg2: "#d5c4a1",
|
|
6622
|
+
fg3: "#bdae93",
|
|
6623
|
+
fg4: "#a89984",
|
|
6624
|
+
red: "#fb4934",
|
|
6625
|
+
green: "#b8bb26",
|
|
6626
|
+
yellow: "#fabd2f",
|
|
6627
|
+
blue: "#83a598",
|
|
6628
|
+
purple: "#d3869b",
|
|
6629
|
+
aqua: "#8ec07c",
|
|
6630
|
+
orange: "#fe8019",
|
|
6631
|
+
gray: "#928374"
|
|
6632
|
+
};
|
|
6633
|
+
const LIGHT = {
|
|
6634
|
+
dark: false,
|
|
6635
|
+
bg0: "#fbf1c7",
|
|
6636
|
+
bg1: "#ebdbb2",
|
|
6637
|
+
bg2: "#d5c4a1",
|
|
6638
|
+
bg3: "#bdae93",
|
|
6639
|
+
bg4: "#a89984",
|
|
6640
|
+
fg0: "#282828",
|
|
6641
|
+
fg1: "#3c3836",
|
|
6642
|
+
fg2: "#504945",
|
|
6643
|
+
fg3: "#665c54",
|
|
6644
|
+
fg4: "#7c6f64",
|
|
6645
|
+
red: "#cc241d",
|
|
6646
|
+
green: "#98971a",
|
|
6647
|
+
yellow: "#d79921",
|
|
6648
|
+
blue: "#458588",
|
|
6649
|
+
purple: "#b16286",
|
|
6650
|
+
aqua: "#689d6a",
|
|
6651
|
+
orange: "#d65d0e",
|
|
6652
|
+
gray: "#928374"
|
|
6653
|
+
};
|
|
6654
|
+
function gruvboxTheme(id, label, p) {
|
|
6655
|
+
return {
|
|
6656
|
+
id,
|
|
6657
|
+
label,
|
|
6658
|
+
colors: {
|
|
6659
|
+
brand: p.yellow,
|
|
6660
|
+
accent: p.green,
|
|
6661
|
+
model: p.blue,
|
|
6662
|
+
warn: p.orange,
|
|
6663
|
+
error: p.red,
|
|
6664
|
+
dim: p.fg3,
|
|
6665
|
+
mute: p.gray,
|
|
6666
|
+
border: p.bg2,
|
|
6667
|
+
borderActive: p.bg4,
|
|
6668
|
+
throbber: {
|
|
6669
|
+
from: p.yellow,
|
|
6670
|
+
to: p.orange
|
|
6671
|
+
},
|
|
6672
|
+
money: p.yellow
|
|
6673
|
+
},
|
|
6674
|
+
select: {
|
|
6675
|
+
backgroundColor: "transparent",
|
|
6676
|
+
focusedBackgroundColor: "transparent",
|
|
6677
|
+
selectedBackgroundColor: "transparent",
|
|
6678
|
+
selectedTextColor: p.yellow,
|
|
6679
|
+
textColor: p.fg2,
|
|
6680
|
+
descriptionColor: p.fg4,
|
|
6681
|
+
selectedDescriptionColor: p.fg3
|
|
6682
|
+
},
|
|
6683
|
+
surfaces: {
|
|
6684
|
+
background: p.dark ? "#1d2021" : "#f9f5d7",
|
|
6685
|
+
modal: p.bg0,
|
|
6686
|
+
chips: {
|
|
6687
|
+
default: {
|
|
6688
|
+
bg: p.yellow,
|
|
6689
|
+
fg: p.dark ? p.bg0 : p.fg0
|
|
6690
|
+
},
|
|
6691
|
+
skills: {
|
|
6692
|
+
bg: p.yellow,
|
|
6693
|
+
fg: p.dark ? p.bg0 : p.fg0
|
|
6694
|
+
},
|
|
6695
|
+
files: {
|
|
6696
|
+
bg: p.blue,
|
|
6697
|
+
fg: p.dark ? p.bg0 : "#fbf1c7"
|
|
6698
|
+
}
|
|
6699
|
+
},
|
|
6700
|
+
selection: p.bg1,
|
|
6701
|
+
diff: {
|
|
6702
|
+
addBg: p.bg1,
|
|
6703
|
+
removeBg: p.bg1,
|
|
6704
|
+
addContentBg: p.bg2,
|
|
6705
|
+
removeContentBg: p.bg2,
|
|
6706
|
+
addFg: p.green,
|
|
6707
|
+
removeFg: p.red
|
|
6708
|
+
}
|
|
6709
|
+
},
|
|
6710
|
+
syntax: {
|
|
6711
|
+
"default": { fg: p.fg1 },
|
|
6712
|
+
"markup.heading": {
|
|
6713
|
+
fg: p.yellow,
|
|
6714
|
+
bold: true
|
|
6715
|
+
},
|
|
6716
|
+
"markup.heading.1": {
|
|
6717
|
+
fg: p.yellow,
|
|
6718
|
+
bold: true
|
|
6719
|
+
},
|
|
6720
|
+
"markup.heading.2": {
|
|
6721
|
+
fg: p.orange,
|
|
6722
|
+
bold: true
|
|
6723
|
+
},
|
|
6724
|
+
"markup.heading.3": {
|
|
6725
|
+
fg: p.blue,
|
|
6726
|
+
bold: true
|
|
6727
|
+
},
|
|
6728
|
+
"markup.bold": {
|
|
6729
|
+
fg: p.fg0,
|
|
6730
|
+
bold: true
|
|
6731
|
+
},
|
|
6732
|
+
"markup.strong": {
|
|
6733
|
+
fg: p.fg0,
|
|
6734
|
+
bold: true
|
|
6735
|
+
},
|
|
6736
|
+
"markup.italic": {
|
|
6737
|
+
fg: p.fg1,
|
|
6738
|
+
italic: true
|
|
6739
|
+
},
|
|
6740
|
+
"markup.link": {
|
|
6741
|
+
fg: p.blue,
|
|
6742
|
+
underline: true
|
|
6743
|
+
},
|
|
6744
|
+
"markup.link.url": {
|
|
6745
|
+
fg: p.blue,
|
|
6746
|
+
underline: true
|
|
6747
|
+
},
|
|
6748
|
+
"markup.list": { fg: p.orange },
|
|
6749
|
+
"markup.raw": { fg: p.green },
|
|
6750
|
+
"markup.raw.block": { fg: p.green },
|
|
6751
|
+
"markup.quote": {
|
|
6752
|
+
fg: p.fg4,
|
|
6753
|
+
italic: true
|
|
6754
|
+
},
|
|
6755
|
+
"keyword": {
|
|
6756
|
+
fg: p.red,
|
|
6757
|
+
bold: true
|
|
6758
|
+
},
|
|
6759
|
+
"keyword.import": {
|
|
6760
|
+
fg: p.red,
|
|
6761
|
+
bold: true
|
|
6762
|
+
},
|
|
6763
|
+
"keyword.operator": { fg: p.orange },
|
|
6764
|
+
"string": { fg: p.green },
|
|
6765
|
+
"string.escape": {
|
|
6766
|
+
fg: p.orange,
|
|
6767
|
+
bold: true
|
|
6768
|
+
},
|
|
6769
|
+
"character": { fg: p.aqua },
|
|
6770
|
+
"comment": {
|
|
6771
|
+
fg: p.gray,
|
|
6772
|
+
italic: true
|
|
6773
|
+
},
|
|
6774
|
+
"number": { fg: p.purple },
|
|
6775
|
+
"boolean": { fg: p.purple },
|
|
6776
|
+
"constant": { fg: p.purple },
|
|
6777
|
+
"constant.builtin": { fg: p.purple },
|
|
6778
|
+
"function": { fg: p.blue },
|
|
6779
|
+
"function.call": { fg: p.blue },
|
|
6780
|
+
"function.method": { fg: p.blue },
|
|
6781
|
+
"function.method.call": { fg: p.blue },
|
|
6782
|
+
"function.builtin": { fg: p.blue },
|
|
6783
|
+
"function.macro": { fg: p.aqua },
|
|
6784
|
+
"type": { fg: p.yellow },
|
|
6785
|
+
"type.builtin": { fg: p.yellow },
|
|
6786
|
+
"constructor": { fg: p.yellow },
|
|
6787
|
+
"attribute": { fg: p.aqua },
|
|
6788
|
+
"tag": { fg: p.aqua },
|
|
6789
|
+
"variable": { fg: p.fg1 },
|
|
6790
|
+
"variable.builtin": { fg: p.red },
|
|
6791
|
+
"variable.parameter": {
|
|
6792
|
+
fg: p.orange,
|
|
6793
|
+
italic: true
|
|
6794
|
+
},
|
|
6795
|
+
"variable.member": { fg: p.fg1 },
|
|
6796
|
+
"property": { fg: p.blue },
|
|
6797
|
+
"operator": { fg: p.orange },
|
|
6798
|
+
"punctuation": { fg: p.fg4 },
|
|
6799
|
+
"punctuation.bracket": { fg: p.fg2 },
|
|
6800
|
+
"punctuation.delimiter": { fg: p.fg4 },
|
|
6801
|
+
"label": { fg: p.aqua }
|
|
6802
|
+
}
|
|
6803
|
+
};
|
|
6804
|
+
}
|
|
6805
|
+
const GRUVBOX_DARK = gruvboxTheme("gruvbox-dark", "Gruvbox Dark", DARK);
|
|
6806
|
+
const GRUVBOX_LIGHT = gruvboxTheme("gruvbox-light", "Gruvbox Light", LIGHT);
|
|
6807
|
+
//#endregion
|
|
5957
6808
|
//#region src/chat/themes/light.ts
|
|
5958
6809
|
const TEXT = "#1f2328";
|
|
5959
6810
|
const DIM = "#656d76";
|
|
@@ -6474,6 +7325,8 @@ const BUILTIN_THEMES = {
|
|
|
6474
7325
|
[CATPPUCCIN_MACCHIATO.id]: CATPPUCCIN_MACCHIATO,
|
|
6475
7326
|
[CATPPUCCIN_FRAPPE.id]: CATPPUCCIN_FRAPPE,
|
|
6476
7327
|
[CATPPUCCIN_LATTE.id]: CATPPUCCIN_LATTE,
|
|
7328
|
+
[GRUVBOX_DARK.id]: GRUVBOX_DARK,
|
|
7329
|
+
[GRUVBOX_LIGHT.id]: GRUVBOX_LIGHT,
|
|
6477
7330
|
[CRUSH_THEME.id]: CRUSH_THEME,
|
|
6478
7331
|
[VAPORWAVE_THEME.id]: VAPORWAVE_THEME
|
|
6479
7332
|
};
|
|
@@ -8817,10 +9670,22 @@ async function runOAuthLogin(descriptor, options) {
|
|
|
8817
9670
|
options.onUrl(info.url, info.instructions);
|
|
8818
9671
|
tryOpenBrowser(info.url);
|
|
8819
9672
|
},
|
|
9673
|
+
onDeviceCode: (info) => {
|
|
9674
|
+
if (options.onDeviceCode) {
|
|
9675
|
+
options.onDeviceCode(info);
|
|
9676
|
+
return;
|
|
9677
|
+
}
|
|
9678
|
+
options.onProgress?.(`Device code: ${info.userCode} — open ${info.verificationUri}`);
|
|
9679
|
+
tryOpenBrowser(info.verificationUri);
|
|
9680
|
+
},
|
|
8820
9681
|
onPrompt: async (prompt) => {
|
|
8821
9682
|
if (!options.onPrompt) throw new Error(`OAuth provider "${descriptor.label}" requested user input ("${prompt.message}") but no onPrompt handler is wired.`);
|
|
8822
9683
|
return options.onPrompt(prompt);
|
|
8823
9684
|
},
|
|
9685
|
+
onSelect: async (prompt) => {
|
|
9686
|
+
if (options.onSelect) return options.onSelect(prompt);
|
|
9687
|
+
return prompt.options[0]?.id;
|
|
9688
|
+
},
|
|
8824
9689
|
onProgress: options.onProgress,
|
|
8825
9690
|
signal: options.signal
|
|
8826
9691
|
};
|
|
@@ -10735,6 +11600,6 @@ function countNeighbors(turnIds, turnId) {
|
|
|
10735
11600
|
};
|
|
10736
11601
|
}
|
|
10737
11602
|
//#endregion
|
|
10738
|
-
export { mcpToolsCachePath as $,
|
|
11603
|
+
export { mcpToolsCachePath as $, KEYBINDING_DEF_BY_ACTION as $n, getModelInfo as $r, CATPPUCCIN_FRAPPE as $t, getSafelist as A, useActiveTodos as Ai, turnSelectionOwnership as An, detectLibc as Ar, clipHintsToWidth as At, oauthUsesManualCodePaste as B, TOKEN_DISCIPLINE_DOCTRINE as Bi, splitLines as Bn, credentialsPath as Br, SETTINGS_CATEGORIES as Bt, resolveSessionExportTarget as C, getArchivedTodosForRun as Ci, saveState as Cn, tryOpenBrowser as Cr, isInteractionTool as Ct, useSafeModeQueue as D, pruneTodosByRun as Di, titleFromTurns as Dn, useUpdateCheck as Dr, useInteractionsActions as Dt, useSafeModeActions as E, pickActiveRunId as Ei, sumRunCosts as En, buildUpdateHint as Er, serializeInteractionResponse as Et, suggestSafelistEntry as F, INTERACTION_GUIDANCE as Fi, computeInlineDiff as Fn, resolvePlatformPackage as Fr, buildHints as Ft, indexOfEntry as G, mergeApprovalAndBodyOutcomes as Gn, writeCredentials as Gr, useSettings as Gt, supportsOAuth as H, buildPlanSystem as Hi, tokenize as Hn, readProviderCredential as Hr, SETTINGS_TOGGLES as Ht, writeProjects as I, INTERACTION_GUIDANCE_NO_PROMPTS as Ii, computeLineDiff as In, AUTO_COMPACT_MIN_GROWTH_FRACTION as Ir, shortChord as It, discoverProjectMcps as J, rewriteMultiEditHeader as Jn, anthropicDescriptor as Jr, resolveChipColor as Jt, buildMcpServers as K, parseEditOutcomesFromResult as Kn, BUILTIN_PROVIDERS as Kr, BUILTIN_THEMES as Kt, splitPromptSegments as L, PLAN_MODE_DOCTRINE as Li, extractEditPayload as Ln, shouldAutoCompact as Lr, listProjectFiles as Lt, matchesSafelistEntry as M, COMMUNICATION_DOCTRINE as Mi, applyEditPayload as Mn, parseSemver as Mr, truncateTrailing as Mt, projectsFilePath as N, DOING_TASKS_DOCTRINE as Ni, buildContextualDiff as Nn, performInPlaceSelfUpdate as Nr, cleanTitle as Nt, IMPLICITLY_SAFE_TOOLS as O, selectActiveTodos as Oi, toolCallPreview as On, checkForUpdate as Or, useInteractionsQueue as Ot, readProjects as P, IDENTITY_PREFIX as Pi, buildUnifiedDiff as Pn, performSelfUpdate as Pr, generateSessionTitle as Pt, loadMcpToolsCache as Q, KEYBINDING_DEFS as Qn, getContextWindow as Qr, GRUVBOX_LIGHT as Qt, formatPathForCwd as R, PLAN_MODE_DOCTRINE_NO_PROMPTS as Ri, filetypeFromPath as Rn, detectAuth as Rr, useEnabledToggleSet as Rt, renderSession as S, createTodoTools as Si, marginTopFor as Sn, buildLinearRamp as Sr, createInteractionTools as St, SafeModeProvider as T, isTodoTool as Ti, stripSpawnTokensLine as Tn, bootTick as Tr, pendingInteractionsFromTurns as Tt, buildModelCatalog as U, envSection as Ui, buildEditOutcomesAnnotation as Un, removeProviderCredential as Ur, SettingsProvider as Ut, runOAuthLogin as V, buildBuildSystem as Vi, summarizeEditPayload as Vn, readCredentials as Vr, SETTINGS_CHOICES as Vt, filterModelCatalog as W, maskToOutcomeKinds as Wn, setProviderCredential as Wr, clampFps as Wt, projectUserPaths as X, summarizeOutcomes as Xn, credKeyOf as Xr, VAPORWAVE_THEME as Xt, parseMcpsFile as Y, stripEditOutcomesAnnotation as Yn, cerebrasDescriptor as Yr, resolveTheme as Yt, clearMcpToolsCache as Z, DEFAULT_KEYBINDINGS as Zn, effectiveContextWindow as Zr, GRUVBOX_DARK as Zt, turnContextSize as _, TODOREAD_TOOL as _i, isTurnHighlighted as _n, collectReferences as _r, splitMarkdownCodeBlocks as _t, computeTurnAnchors as a, discoverAgentsMd as ai, useDiscovery as an, matchesBinding as ar, useMcpToolToggleSet as at, defaultSkillScanPaths as b, TODO_STATUS_GLYPHS as bi, listSessionMeta as bn, useCompletion as br, PRESENT_PLAN_TOOL as bt, formatToolCall as c, BUILD_AGENT as ci, useConfig as cn, readKeybindings as cr, parentServerName as ct, useSelectStyle as d, DEFAULT_BUDGET_EXCLUDE_TOOLS as di, resolveStorageDirs as dn, createSkillsCompletionProvider as dr, patchMcpCredential as dt, modelSupportsReasoning as ei, CATPPUCCIN_LATTE as en, KEYBINDING_KEY_COL_WIDTH as er, refreshMcpToolsCatalog as et, useSurfaces as f, DEFAULT_PERSIST_EXCLUDE_TOOLS as fi, EDIT_TOOL_NAMES as fn, uniqueSkillNamesFromReferences as fr, McpAuthProvider as ft, finalizeStreamingMarkdownForOwner as g, singleAgentRegistry as gi, isEditErrorResult as gn, applyInsert as gr, reduceMcpAuth as gt, finalizeStreamingMarkdown as h, resolveAgentId as hi, eventsFromTurns as hn, uniqueFilesFromReferences as hr, getMcpAuthStatus as ht, turnAsText as i, piIdOf as ii, DiscoveryProvider as in, keybindingsPath as ir, useMcpToolToggleMap as it, isOnSafelist as j, ACTIONS_WITH_CARE_DOCTRINE as ji, updateToolEventOutcomes as jn, detectPackageManager as jr, hintsLength as jt, addToSafelist as k, setTodosForRun as ki, toolResultText as kn, compareSemver as kr, EMPTY_HINTS as kt, ThemeProvider as l, BUILTIN_AGENTS as li, resolveConfig as ln, stripJsonComments as lr, createFileMcpCredentialStore as lt, useTheme as m, accentColor as mi, deriveSessionTitle as mn, createFilesCompletionProvider as mr, useMcpAuthState as mt, deleteTurnSafely as n, openaiDescriptor as ni, CATPPUCCIN_MOCHA as nn, formatBindingForDisplay as nr, subscribeMcpToolsCache as nt, TOOL_DISPLAY as o, renderAgentsMdBlock as oi, useDiscoveryOptional as on, mergeKeybindings as or, buildVisibleMcpRows as ot, useSyntaxStyles as p, PLAN_AGENT as pi, createStateStore as pn, FILES_TRIGGER as pr, useMcpAuthDispatch as pt, defaultMcpsConfigPaths as q, resolveApprovalForPayload as qn, OUTPUT_RESERVE_TOKENS as qr, DEFAULT_THEME as qt, truncateTurnsAt as r, openrouterDescriptor as ri, createDiscoverySlot as rn, groupBindings as rr, buildToolToggle as rt, displayNameFor as s, findGitRoot$1 as si, ConfigProvider as sn, parseBindingSpec as sr, indexOfServerRow as st, countNeighbors as t, modelsForDescriptor as ti, CATPPUCCIN_MACCHIATO as tn, ensureKeybindingsFile as tr, saveMcpToolsCache as tt, useColors as u, DEFAULT_AGENT_ID as ui, resolveStoragePaths as un, SKILLS_TRIGGER as ur, mcpCredentialsPath as ut, useStreamBuffer as v, TODOS_METADATA_KEY as vi, isVisible as vn, findActiveTrigger as vr, ASK_USER_TOOL as vt, writeSessionExport as w, getTodosForRun as wi, selectableTurnIds as wn, bootProfileEnabled as wr, makeRequestInteraction as wt, discoverProjectSkills as x, TODO_WRITE_COUNTS_METADATA_KEY as xi, loadState as xn, blendHsl as xr, buildResumedToolResultsTurn as xt, buildSkillsConfig as y, TODOWRITE_TOOL as yi, lastContextSizeFromTurns as yn, mergeReferences as yr, InteractionsProvider as yt, fetchOAuthRedirect as z, SUBAGENT_GUIDANCE as zi, previewEditPayload as zn, applyApiKeyEnv as zr, DEFAULT_SETTINGS as zt };
|
|
10739
11604
|
|
|
10740
|
-
//# sourceMappingURL=turn-operations-
|
|
11605
|
+
//# sourceMappingURL=turn-operations-6Yls2HuG.js.map
|