@proxysoul/soulforge 2.13.2 → 2.14.0
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 +157 -98
- package/dist/index.js +124288 -109447
- package/dist/workers/intelligence.worker.js +2689 -1006
- package/dist/workers/io.worker.js +367 -144
- package/package.json +7 -5
|
@@ -129,6 +129,9 @@ function createWorkerHandler(handlers, onInit, onDispose) {
|
|
|
129
129
|
const allHandlers = {
|
|
130
130
|
...handlers,
|
|
131
131
|
__memoryUsage: () => {
|
|
132
|
+
try {
|
|
133
|
+
Bun.gc(true);
|
|
134
|
+
} catch {}
|
|
132
135
|
const usage = process.memoryUsage();
|
|
133
136
|
return {
|
|
134
137
|
heapUsed: usage.heapUsed,
|
|
@@ -41832,7 +41835,9 @@ var init_ui = __esm(() => {
|
|
|
41832
41835
|
floatingTerminal: false,
|
|
41833
41836
|
updateModal: false,
|
|
41834
41837
|
mcpSettings: false,
|
|
41835
|
-
|
|
41838
|
+
hearthSettings: false,
|
|
41839
|
+
tabNamePopup: false,
|
|
41840
|
+
uiDemo: false
|
|
41836
41841
|
};
|
|
41837
41842
|
useUIStore = create()(subscribeWithSelector((set2) => ({
|
|
41838
41843
|
modals: {
|
|
@@ -49515,7 +49520,7 @@ var package_default;
|
|
|
49515
49520
|
var init_package = __esm(() => {
|
|
49516
49521
|
package_default = {
|
|
49517
49522
|
name: "@proxysoul/soulforge",
|
|
49518
|
-
version: "2.
|
|
49523
|
+
version: "2.14.0",
|
|
49519
49524
|
description: "Graph-powered code intelligence \u2014 multi-agent coding with codebase-aware AI",
|
|
49520
49525
|
repository: {
|
|
49521
49526
|
type: "git",
|
|
@@ -49532,7 +49537,8 @@ var init_package = __esm(() => {
|
|
|
49532
49537
|
author: "proxySoul",
|
|
49533
49538
|
bin: {
|
|
49534
49539
|
soulforge: "./dist/bin.sh",
|
|
49535
|
-
sf: "./dist/bin.sh"
|
|
49540
|
+
sf: "./dist/bin.sh",
|
|
49541
|
+
"soulforge-remote": "./dist/soulforge-remote.sh"
|
|
49536
49542
|
},
|
|
49537
49543
|
engines: {
|
|
49538
49544
|
bun: ">=1.2.0"
|
|
@@ -49573,7 +49579,7 @@ var init_package = __esm(() => {
|
|
|
49573
49579
|
"@types/node": "25.6.0",
|
|
49574
49580
|
"@types/react": "19.2.14",
|
|
49575
49581
|
"babel-plugin-react-compiler": "1.0.0",
|
|
49576
|
-
"bun-types": "1.3.
|
|
49582
|
+
"bun-types": "1.3.13",
|
|
49577
49583
|
typescript: "6.0.3"
|
|
49578
49584
|
},
|
|
49579
49585
|
dependencies: {
|
|
@@ -49593,20 +49599,21 @@ var init_package = __esm(() => {
|
|
|
49593
49599
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49594
49600
|
"@mozilla/readability": "0.6.0",
|
|
49595
49601
|
"@openrouter/ai-sdk-provider": "2.8.0",
|
|
49596
|
-
"@opentui/react": "0.1.
|
|
49602
|
+
"@opentui/react": "0.1.102",
|
|
49597
49603
|
ai: "6.0.168",
|
|
49598
49604
|
"ghostty-opentui": "1.4.10",
|
|
49599
49605
|
isbinaryfile: "6.0.0",
|
|
49600
49606
|
jsonrepair: "^3.14.0",
|
|
49601
49607
|
linkedom: "0.18.12",
|
|
49602
49608
|
"linkify-it": "5.0.0",
|
|
49603
|
-
marked: "18.0.
|
|
49609
|
+
marked: "18.0.2",
|
|
49604
49610
|
neovim: "5.4.0",
|
|
49605
49611
|
react: "19.2.5",
|
|
49606
49612
|
shiki: "4.0.2",
|
|
49607
49613
|
"strip-ansi": "7.2.0",
|
|
49608
49614
|
"tree-sitter-wasms": "0.1.13",
|
|
49609
49615
|
"ts-morph": "28.0.0",
|
|
49616
|
+
undici: "^8.1.0",
|
|
49610
49617
|
"vercel-minimax-ai-provider": "^0.0.2",
|
|
49611
49618
|
"web-tree-sitter": "0.25.10",
|
|
49612
49619
|
zod: "4.3.6",
|
|
@@ -74331,11 +74338,162 @@ var init_openrouter = __esm(() => {
|
|
|
74331
74338
|
};
|
|
74332
74339
|
});
|
|
74333
74340
|
|
|
74341
|
+
// src/core/proxy/key-resolver.ts
|
|
74342
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
74343
|
+
import { homedir as homedir5 } from "os";
|
|
74344
|
+
import { join as join7 } from "path";
|
|
74345
|
+
function candidateConfigPaths() {
|
|
74346
|
+
const paths = [process.env.PROXY_CONFIG_PATH, join7(homedir5(), ".soulforge", "proxy", "config.yaml"), "/opt/homebrew/etc/cliproxyapi.conf", "/opt/homebrew/etc/cliproxyapi/config.yaml", "/usr/local/etc/cliproxyapi.conf", "/usr/local/etc/cliproxyapi/config.yaml", "/etc/cliproxyapi.conf", "/etc/cliproxyapi/config.yaml", join7(homedir5(), ".config", "cliproxyapi", "config.yaml"), join7(homedir5(), ".cli-proxy-api", "config.yaml")];
|
|
74347
|
+
return paths.filter((p) => typeof p === "string" && p.length > 0);
|
|
74348
|
+
}
|
|
74349
|
+
function isPlausibleKey(value) {
|
|
74350
|
+
const s = value.trim();
|
|
74351
|
+
if (!s)
|
|
74352
|
+
return false;
|
|
74353
|
+
if (PLACEHOLDER_PATTERNS.some((re) => re.test(s)))
|
|
74354
|
+
return false;
|
|
74355
|
+
if (BCRYPT_RE.test(s))
|
|
74356
|
+
return false;
|
|
74357
|
+
if (SHA256_HEX_RE.test(s))
|
|
74358
|
+
return false;
|
|
74359
|
+
return true;
|
|
74360
|
+
}
|
|
74361
|
+
function parseApiKeys(content) {
|
|
74362
|
+
const lines = content.split(`
|
|
74363
|
+
`);
|
|
74364
|
+
const keys = [];
|
|
74365
|
+
let inList = false;
|
|
74366
|
+
let listIndent = -1;
|
|
74367
|
+
for (const rawLine of lines) {
|
|
74368
|
+
const line = rawLine.replace(/\r$/, "");
|
|
74369
|
+
const stripped = line.replace(/^\s*/, "");
|
|
74370
|
+
if (stripped.startsWith("#"))
|
|
74371
|
+
continue;
|
|
74372
|
+
if (!inList) {
|
|
74373
|
+
if (/^api-keys\s*:\s*$/.test(stripped) && !line.startsWith(" ") && !line.startsWith("\t")) {
|
|
74374
|
+
inList = true;
|
|
74375
|
+
listIndent = -1;
|
|
74376
|
+
}
|
|
74377
|
+
continue;
|
|
74378
|
+
}
|
|
74379
|
+
if (stripped === "")
|
|
74380
|
+
continue;
|
|
74381
|
+
const indentMatch = line.match(/^(\s+)/);
|
|
74382
|
+
const indent = indentMatch?.[1]?.length ?? 0;
|
|
74383
|
+
if (indent === 0)
|
|
74384
|
+
break;
|
|
74385
|
+
if (listIndent === -1)
|
|
74386
|
+
listIndent = indent;
|
|
74387
|
+
if (indent < listIndent)
|
|
74388
|
+
break;
|
|
74389
|
+
const item = line.slice(indent).match(/^-\s+(?:"([^"]*)"|'([^']*)'|(.+?))\s*$/);
|
|
74390
|
+
if (!item)
|
|
74391
|
+
continue;
|
|
74392
|
+
const v = item[1] ?? item[2] ?? item[3] ?? "";
|
|
74393
|
+
keys.push(v);
|
|
74394
|
+
}
|
|
74395
|
+
return keys;
|
|
74396
|
+
}
|
|
74397
|
+
function discoverApiKeys() {
|
|
74398
|
+
const out = [];
|
|
74399
|
+
const seen = new Set;
|
|
74400
|
+
for (const path of candidateConfigPaths()) {
|
|
74401
|
+
try {
|
|
74402
|
+
if (!existsSync2(path))
|
|
74403
|
+
continue;
|
|
74404
|
+
const content = readFileSync2(path, "utf-8");
|
|
74405
|
+
for (const k of parseApiKeys(content)) {
|
|
74406
|
+
if (!isPlausibleKey(k))
|
|
74407
|
+
continue;
|
|
74408
|
+
if (seen.has(k))
|
|
74409
|
+
continue;
|
|
74410
|
+
seen.add(k);
|
|
74411
|
+
out.push({
|
|
74412
|
+
key: k,
|
|
74413
|
+
source: path
|
|
74414
|
+
});
|
|
74415
|
+
}
|
|
74416
|
+
} catch {}
|
|
74417
|
+
}
|
|
74418
|
+
return out;
|
|
74419
|
+
}
|
|
74420
|
+
function primaryConfigPath() {
|
|
74421
|
+
for (const path of candidateConfigPaths()) {
|
|
74422
|
+
try {
|
|
74423
|
+
if (!existsSync2(path))
|
|
74424
|
+
continue;
|
|
74425
|
+
const content = readFileSync2(path, "utf-8");
|
|
74426
|
+
if (/^api-keys\s*:\s*$/m.test(content))
|
|
74427
|
+
return path;
|
|
74428
|
+
} catch {}
|
|
74429
|
+
}
|
|
74430
|
+
return null;
|
|
74431
|
+
}
|
|
74432
|
+
function getActiveProxyApiKey() {
|
|
74433
|
+
return cached2;
|
|
74434
|
+
}
|
|
74435
|
+
function setActiveProxyApiKey(key) {
|
|
74436
|
+
cached2 = key;
|
|
74437
|
+
cachedIsProbed = true;
|
|
74438
|
+
}
|
|
74439
|
+
function candidateApiKeys() {
|
|
74440
|
+
const out = [];
|
|
74441
|
+
const push = (v) => {
|
|
74442
|
+
if (!v)
|
|
74443
|
+
return;
|
|
74444
|
+
const s = v.trim();
|
|
74445
|
+
if (!s)
|
|
74446
|
+
return;
|
|
74447
|
+
if (!out.includes(s))
|
|
74448
|
+
out.push(s);
|
|
74449
|
+
};
|
|
74450
|
+
push(process.env.PROXY_API_KEY);
|
|
74451
|
+
push("soulforge");
|
|
74452
|
+
for (const d of discoverApiKeys())
|
|
74453
|
+
push(d.key);
|
|
74454
|
+
return out;
|
|
74455
|
+
}
|
|
74456
|
+
var PLACEHOLDER_PATTERNS, BCRYPT_RE, SHA256_HEX_RE, cached2, cachedIsProbed = false;
|
|
74457
|
+
var init_key_resolver = __esm(() => {
|
|
74458
|
+
PLACEHOLDER_PATTERNS = [/^your[-_]?(?:api[-_]?)?key/i, /^changeme$/i, /^replace[-_ ]?me/i, /^xxx+$/i];
|
|
74459
|
+
BCRYPT_RE = /^\$2[aby]\$/;
|
|
74460
|
+
SHA256_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
74461
|
+
cached2 = process.env.PROXY_API_KEY?.trim() || "soulforge";
|
|
74462
|
+
});
|
|
74463
|
+
|
|
74334
74464
|
// src/core/setup/install.ts
|
|
74335
74465
|
import { execSync } from "child_process";
|
|
74336
|
-
import { existsSync as
|
|
74337
|
-
import { homedir as
|
|
74338
|
-
import { join as
|
|
74466
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, symlinkSync, unlinkSync } from "fs";
|
|
74467
|
+
import { homedir as homedir6, platform } from "os";
|
|
74468
|
+
import { join as join8 } from "path";
|
|
74469
|
+
async function fetchLatestProxyVersion(timeoutMs = 5000) {
|
|
74470
|
+
try {
|
|
74471
|
+
const ctl = new AbortController;
|
|
74472
|
+
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
74473
|
+
const res = await fetch(PROXY_RELEASES_URL, {
|
|
74474
|
+
signal: ctl.signal,
|
|
74475
|
+
headers: {
|
|
74476
|
+
Accept: "application/vnd.github+json"
|
|
74477
|
+
}
|
|
74478
|
+
});
|
|
74479
|
+
clearTimeout(t);
|
|
74480
|
+
if (!res.ok)
|
|
74481
|
+
return null;
|
|
74482
|
+
const data = await res.json();
|
|
74483
|
+
return data.tag_name?.replace(/^v/, "")?.trim() || null;
|
|
74484
|
+
} catch {
|
|
74485
|
+
return null;
|
|
74486
|
+
}
|
|
74487
|
+
}
|
|
74488
|
+
async function resolveProxyVersion() {
|
|
74489
|
+
const env = process.env.SOULFORGE_PROXY_VERSION?.trim();
|
|
74490
|
+
if (env)
|
|
74491
|
+
return env;
|
|
74492
|
+
const latest = await fetchLatestProxyVersion();
|
|
74493
|
+
if (latest)
|
|
74494
|
+
return latest;
|
|
74495
|
+
return FALLBACK_PROXY_VERSION;
|
|
74496
|
+
}
|
|
74339
74497
|
function getPlatformKey() {
|
|
74340
74498
|
const key = `${process.platform}-${process.arch}`;
|
|
74341
74499
|
if (key !== "darwin-arm64" && key !== "darwin-x64" && key !== "linux-x64" && key !== "linux-arm64") {
|
|
@@ -74347,22 +74505,22 @@ async function installBinary(config2) {
|
|
|
74347
74505
|
ensureDirs();
|
|
74348
74506
|
const key = getPlatformKey();
|
|
74349
74507
|
const asset = config2.getAsset(key);
|
|
74350
|
-
const extractDir =
|
|
74351
|
-
if (!
|
|
74508
|
+
const extractDir = join8(INSTALLS_DIR, `${config2.name}-${config2.version}`);
|
|
74509
|
+
if (!existsSync3(asset.binPath)) {
|
|
74352
74510
|
await downloadAndExtract(asset.url, extractDir);
|
|
74353
74511
|
}
|
|
74354
|
-
if (!
|
|
74512
|
+
if (!existsSync3(asset.binPath)) {
|
|
74355
74513
|
throw new Error(`${config2.name} binary not found after extraction at ${asset.binPath}`);
|
|
74356
74514
|
}
|
|
74357
74515
|
execSync(`chmod +x "${asset.binPath}"`, {
|
|
74358
74516
|
stdio: "ignore"
|
|
74359
74517
|
});
|
|
74360
|
-
createSymlink(asset.binPath,
|
|
74361
|
-
return
|
|
74518
|
+
createSymlink(asset.binPath, join8(BIN_DIR, config2.binName));
|
|
74519
|
+
return join8(BIN_DIR, config2.binName);
|
|
74362
74520
|
}
|
|
74363
74521
|
function getVendoredPath(binary) {
|
|
74364
|
-
const binLink =
|
|
74365
|
-
return
|
|
74522
|
+
const binLink = join8(BIN_DIR, binary);
|
|
74523
|
+
return existsSync3(binLink) ? binLink : null;
|
|
74366
74524
|
}
|
|
74367
74525
|
function ensureDirs() {
|
|
74368
74526
|
mkdirSync2(BIN_DIR, {
|
|
@@ -74380,7 +74538,7 @@ async function downloadAndExtract(url2, extractDir) {
|
|
|
74380
74538
|
if (!response.ok) {
|
|
74381
74539
|
throw new Error(`Download failed: ${response.status} ${response.statusText} (${url2})`);
|
|
74382
74540
|
}
|
|
74383
|
-
const tmpFile =
|
|
74541
|
+
const tmpFile = join8(extractDir, "download.tar.gz");
|
|
74384
74542
|
const buffer = await response.arrayBuffer();
|
|
74385
74543
|
await Bun.write(tmpFile, buffer);
|
|
74386
74544
|
execSync(`tar xzf "${tmpFile}" -C "${extractDir}"`, {
|
|
@@ -74389,14 +74547,14 @@ async function downloadAndExtract(url2, extractDir) {
|
|
|
74389
74547
|
unlinkSync(tmpFile);
|
|
74390
74548
|
}
|
|
74391
74549
|
function createSymlink(target, link) {
|
|
74392
|
-
if (
|
|
74550
|
+
if (existsSync3(link)) {
|
|
74393
74551
|
unlinkSync(link);
|
|
74394
74552
|
}
|
|
74395
74553
|
symlinkSync(target, link);
|
|
74396
74554
|
}
|
|
74397
74555
|
async function installProxy(version2) {
|
|
74398
|
-
const v = version2 ??
|
|
74399
|
-
|
|
74556
|
+
const v = version2 ?? await resolveProxyVersion();
|
|
74557
|
+
const path = await installBinary({
|
|
74400
74558
|
name: "cliproxyapi",
|
|
74401
74559
|
binName: "cli-proxy-api",
|
|
74402
74560
|
version: v,
|
|
@@ -74405,17 +74563,21 @@ async function installProxy(version2) {
|
|
|
74405
74563
|
const asset = `CLIProxyAPI_${v}_${suffix}.tar.gz`;
|
|
74406
74564
|
return {
|
|
74407
74565
|
url: `https://github.com/router-for-me/CLIProxyAPI/releases/download/v${v}/${asset}`,
|
|
74408
|
-
binPath:
|
|
74566
|
+
binPath: join8(INSTALLS_DIR, `cliproxyapi-${v}`, "cli-proxy-api")
|
|
74409
74567
|
};
|
|
74410
74568
|
}
|
|
74411
74569
|
});
|
|
74570
|
+
return {
|
|
74571
|
+
path,
|
|
74572
|
+
version: v
|
|
74573
|
+
};
|
|
74412
74574
|
}
|
|
74413
|
-
var SOULFORGE_DIR, BIN_DIR, INSTALLS_DIR, FONTS_DIR,
|
|
74575
|
+
var SOULFORGE_DIR, BIN_DIR, INSTALLS_DIR, FONTS_DIR, FALLBACK_PROXY_VERSION = "6.9.29", PROXY_RELEASES_URL = "https://api.github.com/repos/router-for-me/CLIProxyAPI/releases/latest", PROXY_SUFFIXES;
|
|
74414
74576
|
var init_install = __esm(() => {
|
|
74415
|
-
SOULFORGE_DIR =
|
|
74416
|
-
BIN_DIR =
|
|
74417
|
-
INSTALLS_DIR =
|
|
74418
|
-
FONTS_DIR =
|
|
74577
|
+
SOULFORGE_DIR = join8(homedir6(), ".soulforge");
|
|
74578
|
+
BIN_DIR = join8(SOULFORGE_DIR, "bin");
|
|
74579
|
+
INSTALLS_DIR = join8(SOULFORGE_DIR, "installs");
|
|
74580
|
+
FONTS_DIR = join8(SOULFORGE_DIR, "fonts");
|
|
74419
74581
|
PROXY_SUFFIXES = {
|
|
74420
74582
|
"darwin-arm64": "darwin_arm64",
|
|
74421
74583
|
"darwin-x64": "darwin_amd64",
|
|
@@ -74426,80 +74588,51 @@ var init_install = __esm(() => {
|
|
|
74426
74588
|
|
|
74427
74589
|
// src/core/proxy/lifecycle.ts
|
|
74428
74590
|
import { execFileSync, execSync as execSync2, spawn as spawn4 } from "child_process";
|
|
74429
|
-
import { existsSync as
|
|
74430
|
-
import { homedir as
|
|
74431
|
-
import { join as
|
|
74591
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
74592
|
+
import { homedir as homedir7 } from "os";
|
|
74593
|
+
import { join as join9 } from "path";
|
|
74432
74594
|
function setState(state, error48 = null) {
|
|
74433
74595
|
currentState = state;
|
|
74434
74596
|
lastError = error48;
|
|
74435
74597
|
for (const fn of stateListeners)
|
|
74436
74598
|
fn(state, error48);
|
|
74437
74599
|
}
|
|
74438
|
-
function getInstalledProxyVersion() {
|
|
74439
|
-
try {
|
|
74440
|
-
if (existsSync3(VERSION_FILE)) {
|
|
74441
|
-
const v = readFileSync2(VERSION_FILE, "utf-8").trim();
|
|
74442
|
-
if (v)
|
|
74443
|
-
return v;
|
|
74444
|
-
}
|
|
74445
|
-
} catch {}
|
|
74446
|
-
return PROXY_VERSION;
|
|
74447
|
-
}
|
|
74448
74600
|
function saveInstalledProxyVersion(version2) {
|
|
74449
74601
|
mkdirSync3(PROXY_CONFIG_DIR, {
|
|
74450
74602
|
recursive: true
|
|
74451
74603
|
});
|
|
74452
74604
|
writeFileSync2(VERSION_FILE, version2);
|
|
74453
74605
|
}
|
|
74454
|
-
function
|
|
74455
|
-
|
|
74456
|
-
|
|
74457
|
-
|
|
74458
|
-
|
|
74459
|
-
|
|
74460
|
-
|
|
74461
|
-
|
|
74462
|
-
|
|
74463
|
-
|
|
74464
|
-
|
|
74465
|
-
|
|
74466
|
-
return
|
|
74606
|
+
function stripLegacyPerfBlock(content) {
|
|
74607
|
+
if (!content.includes(LEGACY_PERF_MARKER_PREFIX))
|
|
74608
|
+
return content;
|
|
74609
|
+
const lines = content.split(`
|
|
74610
|
+
`);
|
|
74611
|
+
const start = lines.findIndex((l) => l.startsWith(LEGACY_PERF_MARKER_PREFIX));
|
|
74612
|
+
if (start === -1)
|
|
74613
|
+
return content;
|
|
74614
|
+
let end = start + 1;
|
|
74615
|
+
while (end < lines.length && lines[end]?.trim() !== "")
|
|
74616
|
+
end++;
|
|
74617
|
+
lines.splice(start, end - start);
|
|
74618
|
+
return lines.join(`
|
|
74619
|
+
`);
|
|
74467
74620
|
}
|
|
74468
74621
|
function ensureConfig() {
|
|
74469
74622
|
mkdirSync3(PROXY_CONFIG_DIR, {
|
|
74470
74623
|
recursive: true
|
|
74471
74624
|
});
|
|
74472
|
-
if (!
|
|
74473
|
-
writeFileSync2(PROXY_CONFIG_PATH, ["host: 127.0.0.1", "port: 8317", 'auth-dir: "~/.cli-proxy-api"', "api-keys:", ' - "soulforge"', ""
|
|
74625
|
+
if (!existsSync4(PROXY_CONFIG_PATH)) {
|
|
74626
|
+
writeFileSync2(PROXY_CONFIG_PATH, ["host: 127.0.0.1", "port: 8317", 'auth-dir: "~/.cli-proxy-api"', "api-keys:", ' - "soulforge"', ""].join(`
|
|
74474
74627
|
`));
|
|
74475
74628
|
return;
|
|
74476
74629
|
}
|
|
74477
74630
|
try {
|
|
74478
|
-
const existing =
|
|
74479
|
-
|
|
74480
|
-
|
|
74481
|
-
|
|
74482
|
-
if (existing.includes(PERF_MARKER_PREFIX)) {
|
|
74483
|
-
const lines = existing.split(`
|
|
74484
|
-
`);
|
|
74485
|
-
const start = lines.findIndex((l) => l.startsWith(PERF_MARKER_PREFIX));
|
|
74486
|
-
if (start !== -1) {
|
|
74487
|
-
let end = start + 1;
|
|
74488
|
-
while (end < lines.length && lines[end]?.trim() !== "")
|
|
74489
|
-
end++;
|
|
74490
|
-
lines.splice(start, end - start);
|
|
74491
|
-
cleaned = lines.join(`
|
|
74492
|
-
`);
|
|
74493
|
-
}
|
|
74631
|
+
const existing = readFileSync3(PROXY_CONFIG_PATH, "utf-8");
|
|
74632
|
+
const cleaned = stripLegacyPerfBlock(existing);
|
|
74633
|
+
if (cleaned !== existing) {
|
|
74634
|
+
writeFileSync2(PROXY_CONFIG_PATH, cleaned);
|
|
74494
74635
|
}
|
|
74495
|
-
if (hasConflictingKeys(cleaned))
|
|
74496
|
-
return;
|
|
74497
|
-
const sep = cleaned.endsWith(`
|
|
74498
|
-
`) ? "" : `
|
|
74499
|
-
`;
|
|
74500
|
-
writeFileSync2(PROXY_CONFIG_PATH, `${cleaned}${sep}
|
|
74501
|
-
${PERF_BLOCK}
|
|
74502
|
-
`);
|
|
74503
74636
|
} catch {}
|
|
74504
74637
|
}
|
|
74505
74638
|
function commandExists(cmd) {
|
|
@@ -74512,24 +74645,76 @@ function commandExists(cmd) {
|
|
|
74512
74645
|
return false;
|
|
74513
74646
|
}
|
|
74514
74647
|
}
|
|
74648
|
+
function getBinaryVersion(binary) {
|
|
74649
|
+
const parse5 = (s) => {
|
|
74650
|
+
const m = s.match(/Version:\s*(\d+\.\d+\.\d+)/);
|
|
74651
|
+
return m?.[1] ?? null;
|
|
74652
|
+
};
|
|
74653
|
+
try {
|
|
74654
|
+
const out = execFileSync(binary, ["-help"], {
|
|
74655
|
+
encoding: "utf-8",
|
|
74656
|
+
timeout: 2000,
|
|
74657
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
74658
|
+
});
|
|
74659
|
+
return parse5(out);
|
|
74660
|
+
} catch (err) {
|
|
74661
|
+
const e = err;
|
|
74662
|
+
const stdout = typeof e.stdout === "string" ? e.stdout : e.stdout?.toString() ?? "";
|
|
74663
|
+
const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString() ?? "";
|
|
74664
|
+
return parse5(stdout + stderr);
|
|
74665
|
+
}
|
|
74666
|
+
}
|
|
74667
|
+
function compareVersions(a, b) {
|
|
74668
|
+
const ap = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
74669
|
+
const bp = b.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
74670
|
+
const len = Math.max(ap.length, bp.length);
|
|
74671
|
+
for (let i = 0;i < len; i++) {
|
|
74672
|
+
const diff = (ap[i] ?? 0) - (bp[i] ?? 0);
|
|
74673
|
+
if (diff !== 0)
|
|
74674
|
+
return diff;
|
|
74675
|
+
}
|
|
74676
|
+
return 0;
|
|
74677
|
+
}
|
|
74515
74678
|
function getProxyBinary() {
|
|
74679
|
+
const systemBinary = commandExists("cli-proxy-api") ? "cli-proxy-api" : commandExists("cliproxyapi") ? "cliproxyapi" : null;
|
|
74516
74680
|
const vendored = getVendoredPath("cli-proxy-api");
|
|
74517
|
-
if (vendored)
|
|
74518
|
-
|
|
74519
|
-
|
|
74520
|
-
|
|
74521
|
-
|
|
74522
|
-
|
|
74523
|
-
|
|
74681
|
+
if (systemBinary && vendored) {
|
|
74682
|
+
const sysVersion = getBinaryVersion(systemBinary);
|
|
74683
|
+
const vendoredVersion = getBinaryVersion(vendored);
|
|
74684
|
+
if (sysVersion && vendoredVersion) {
|
|
74685
|
+
if (compareVersions(sysVersion, vendoredVersion) >= 0)
|
|
74686
|
+
return systemBinary;
|
|
74687
|
+
logBackgroundError("CLIProxyAPI", `system binary v${sysVersion} is older than vendored v${vendoredVersion} \u2014 using vendored`);
|
|
74688
|
+
return vendored;
|
|
74689
|
+
}
|
|
74690
|
+
return systemBinary;
|
|
74691
|
+
}
|
|
74692
|
+
return systemBinary ?? vendored;
|
|
74524
74693
|
}
|
|
74525
|
-
|
|
74694
|
+
function portIsOccupied() {
|
|
74695
|
+
const portMatch = PROXY_URL.match(/:([0-9]+)/);
|
|
74696
|
+
if (!portMatch)
|
|
74697
|
+
return false;
|
|
74698
|
+
const port = portMatch[1];
|
|
74699
|
+
try {
|
|
74700
|
+
const out = execFileSync("lsof", ["-ti", `tcp:${port}`], {
|
|
74701
|
+
encoding: "utf-8",
|
|
74702
|
+
timeout: 3000,
|
|
74703
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
74704
|
+
}).trim();
|
|
74705
|
+
return out.length > 0;
|
|
74706
|
+
} catch {
|
|
74707
|
+
return false;
|
|
74708
|
+
}
|
|
74709
|
+
}
|
|
74710
|
+
async function healthCheck(key) {
|
|
74526
74711
|
try {
|
|
74527
74712
|
const controller = new AbortController;
|
|
74528
74713
|
const timeout = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
74529
74714
|
const res = await fetch(`${PROXY_URL}/models`, {
|
|
74530
74715
|
signal: controller.signal,
|
|
74531
74716
|
headers: {
|
|
74532
|
-
Authorization: `Bearer ${
|
|
74717
|
+
Authorization: `Bearer ${key}`
|
|
74533
74718
|
}
|
|
74534
74719
|
});
|
|
74535
74720
|
clearTimeout(timeout);
|
|
@@ -74542,6 +74727,33 @@ async function healthCheck() {
|
|
|
74542
74727
|
return "unreachable";
|
|
74543
74728
|
}
|
|
74544
74729
|
}
|
|
74730
|
+
async function probeForWorkingKey() {
|
|
74731
|
+
let sawAuthRequired = false;
|
|
74732
|
+
let sawUnreachable = false;
|
|
74733
|
+
for (const candidate of candidateApiKeys()) {
|
|
74734
|
+
const r = await healthCheck(candidate);
|
|
74735
|
+
if (r === "ok")
|
|
74736
|
+
return {
|
|
74737
|
+
key: candidate,
|
|
74738
|
+
state: "ok"
|
|
74739
|
+
};
|
|
74740
|
+
if (r === "auth-required")
|
|
74741
|
+
sawAuthRequired = true;
|
|
74742
|
+
if (r === "unreachable")
|
|
74743
|
+
sawUnreachable = true;
|
|
74744
|
+
}
|
|
74745
|
+
if (sawAuthRequired)
|
|
74746
|
+
return {
|
|
74747
|
+
state: "auth-required"
|
|
74748
|
+
};
|
|
74749
|
+
if (sawUnreachable)
|
|
74750
|
+
return {
|
|
74751
|
+
state: "unreachable"
|
|
74752
|
+
};
|
|
74753
|
+
return {
|
|
74754
|
+
state: "unreachable"
|
|
74755
|
+
};
|
|
74756
|
+
}
|
|
74545
74757
|
async function ensureProxy() {
|
|
74546
74758
|
if (currentState === "starting") {
|
|
74547
74759
|
return {
|
|
@@ -74549,32 +74761,43 @@ async function ensureProxy() {
|
|
|
74549
74761
|
error: "Proxy is already starting"
|
|
74550
74762
|
};
|
|
74551
74763
|
}
|
|
74552
|
-
const
|
|
74553
|
-
|
|
74554
|
-
|
|
74555
|
-
stopProxy();
|
|
74556
|
-
killProxyOnPort();
|
|
74557
|
-
}
|
|
74558
|
-
const health = await healthCheck();
|
|
74559
|
-
if (health === "ok" && !needsUpgrade) {
|
|
74764
|
+
const probe = await probeForWorkingKey();
|
|
74765
|
+
if (probe.state === "ok") {
|
|
74766
|
+
setActiveProxyApiKey(probe.key);
|
|
74560
74767
|
setState("running");
|
|
74561
74768
|
return {
|
|
74562
74769
|
ok: true
|
|
74563
74770
|
};
|
|
74564
74771
|
}
|
|
74565
|
-
if (
|
|
74772
|
+
if (probe.state === "auth-required") {
|
|
74773
|
+
const cfg = primaryConfigPath();
|
|
74774
|
+
const discovered = discoverApiKeys();
|
|
74775
|
+
if (cfg && discovered.length === 0) {
|
|
74776
|
+
const msg = `Proxy rejected every candidate API key. Edit ${cfg} (replace placeholder in \`api-keys:\`) or set PROXY_API_KEY, then restart the proxy.`;
|
|
74777
|
+
setState("needs-auth", msg);
|
|
74778
|
+
return {
|
|
74779
|
+
ok: false,
|
|
74780
|
+
error: msg
|
|
74781
|
+
};
|
|
74782
|
+
}
|
|
74566
74783
|
setState("needs-auth", "Authentication required \u2014 run /proxy login");
|
|
74567
74784
|
return {
|
|
74568
74785
|
ok: false,
|
|
74569
74786
|
error: "Authentication required \u2014 run /proxy login"
|
|
74570
74787
|
};
|
|
74571
74788
|
}
|
|
74789
|
+
if (portIsOccupied()) {
|
|
74790
|
+
logBackgroundError("CLIProxyAPI", "orphan process on port \u2014 clearing");
|
|
74791
|
+
killProxyOnPort();
|
|
74792
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
74793
|
+
}
|
|
74572
74794
|
setState("starting");
|
|
74573
74795
|
let binary = getProxyBinary();
|
|
74574
74796
|
if (!binary) {
|
|
74575
74797
|
try {
|
|
74576
|
-
|
|
74577
|
-
|
|
74798
|
+
const installed = await installProxy();
|
|
74799
|
+
binary = installed.path;
|
|
74800
|
+
saveInstalledProxyVersion(installed.version);
|
|
74578
74801
|
} catch (err) {
|
|
74579
74802
|
const msg = toErrorMessage(err);
|
|
74580
74803
|
setState("error", `Failed to install CLIProxyAPI: ${msg}`);
|
|
@@ -74620,7 +74843,7 @@ async function ensureProxy() {
|
|
|
74620
74843
|
}
|
|
74621
74844
|
for (let i = 0;i < STARTUP_POLL_ATTEMPTS; i++) {
|
|
74622
74845
|
await new Promise((r) => setTimeout(r, STARTUP_POLL_MS));
|
|
74623
|
-
const status = await healthCheck();
|
|
74846
|
+
const status = await healthCheck(getActiveProxyApiKey());
|
|
74624
74847
|
if (status === "ok") {
|
|
74625
74848
|
setState("running");
|
|
74626
74849
|
return {
|
|
@@ -74657,7 +74880,7 @@ function stopProxy() {
|
|
|
74657
74880
|
killProxyOnPort();
|
|
74658
74881
|
setState("stopped");
|
|
74659
74882
|
}
|
|
74660
|
-
function killProxyOnPort() {
|
|
74883
|
+
function killProxyOnPort(force = false) {
|
|
74661
74884
|
const portMatch = PROXY_URL.match(/:([0-9]+)/);
|
|
74662
74885
|
if (!portMatch)
|
|
74663
74886
|
return;
|
|
@@ -74666,13 +74889,15 @@ function killProxyOnPort() {
|
|
|
74666
74889
|
try {
|
|
74667
74890
|
out = execFileSync("lsof", ["-ti", `tcp:${port}`], {
|
|
74668
74891
|
encoding: "utf-8",
|
|
74669
|
-
timeout: 3000
|
|
74892
|
+
timeout: 3000,
|
|
74893
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
74670
74894
|
}).trim();
|
|
74671
74895
|
} catch {
|
|
74672
74896
|
try {
|
|
74673
74897
|
out = execFileSync("fuser", [`${port}/tcp`], {
|
|
74674
74898
|
encoding: "utf-8",
|
|
74675
|
-
timeout: 3000
|
|
74899
|
+
timeout: 3000,
|
|
74900
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
74676
74901
|
}).trim();
|
|
74677
74902
|
} catch {
|
|
74678
74903
|
return;
|
|
@@ -74680,31 +74905,28 @@ function killProxyOnPort() {
|
|
|
74680
74905
|
}
|
|
74681
74906
|
if (!out)
|
|
74682
74907
|
return;
|
|
74908
|
+
const signal = force ? "SIGKILL" : "SIGTERM";
|
|
74683
74909
|
for (const token of out.split(/[\s\n]+/)) {
|
|
74684
74910
|
const pid = Number.parseInt(token.trim(), 10);
|
|
74685
74911
|
if (pid > 0 && pid !== process.pid) {
|
|
74686
74912
|
try {
|
|
74687
|
-
process.kill(pid,
|
|
74913
|
+
process.kill(pid, signal);
|
|
74688
74914
|
} catch {}
|
|
74689
74915
|
}
|
|
74690
74916
|
}
|
|
74691
74917
|
}
|
|
74692
|
-
var proxyProcess = null, PROXY_URL,
|
|
74918
|
+
var proxyProcess = null, PROXY_URL, PROXY_CONFIG_DIR, PROXY_CONFIG_PATH, HEALTH_TIMEOUT_MS = 2000, STARTUP_POLL_MS = 500, STARTUP_POLL_ATTEMPTS = 10, currentState = "stopped", lastError = null, stateListeners, VERSION_FILE, LEGACY_PERF_MARKER_PREFIX = "# soulforge-perf-defaults", AUTH_DIR, VERSION_CACHE_TTL;
|
|
74693
74919
|
var init_lifecycle = __esm(() => {
|
|
74694
74920
|
init_errors4();
|
|
74695
74921
|
init_process_tracker();
|
|
74696
74922
|
init_install();
|
|
74923
|
+
init_key_resolver();
|
|
74697
74924
|
PROXY_URL = process.env.PROXY_API_URL || "http://127.0.0.1:8317/v1";
|
|
74698
|
-
|
|
74699
|
-
|
|
74700
|
-
PROXY_CONFIG_PATH = join8(PROXY_CONFIG_DIR, "config.yaml");
|
|
74925
|
+
PROXY_CONFIG_DIR = join9(homedir7(), ".soulforge", "proxy");
|
|
74926
|
+
PROXY_CONFIG_PATH = join9(PROXY_CONFIG_DIR, "config.yaml");
|
|
74701
74927
|
stateListeners = new Set;
|
|
74702
|
-
VERSION_FILE =
|
|
74703
|
-
|
|
74704
|
-
PERF_KEYS = ["request-retry", "max-retry-interval", "max-retry-credentials", "streaming", "nonstream-keepalive-interval"];
|
|
74705
|
-
PERF_BLOCK = [PERF_MARKER, "request-retry: 1", "max-retry-interval: 10", "max-retry-credentials: 2", "streaming:", " keepalive-seconds: 15", " bootstrap-retries: 1", "nonstream-keepalive-interval: 30"].join(`
|
|
74706
|
-
`);
|
|
74707
|
-
AUTH_DIR = join8(homedir6(), ".cli-proxy-api");
|
|
74928
|
+
VERSION_FILE = join9(PROXY_CONFIG_DIR, "version");
|
|
74929
|
+
AUTH_DIR = join9(homedir7(), ".cli-proxy-api");
|
|
74708
74930
|
VERSION_CACHE_TTL = 10 * 60 * 1000;
|
|
74709
74931
|
});
|
|
74710
74932
|
|
|
@@ -74712,14 +74934,14 @@ var init_lifecycle = __esm(() => {
|
|
|
74712
74934
|
function isAnthropicModel2(modelId) {
|
|
74713
74935
|
return modelId.toLowerCase().startsWith("claude");
|
|
74714
74936
|
}
|
|
74715
|
-
var baseURL,
|
|
74937
|
+
var baseURL, proxy;
|
|
74716
74938
|
var init_proxy = __esm(() => {
|
|
74717
74939
|
init_dist6();
|
|
74718
74940
|
init_dist8();
|
|
74941
|
+
init_key_resolver();
|
|
74719
74942
|
init_lifecycle();
|
|
74720
74943
|
init_context_windows();
|
|
74721
74944
|
baseURL = process.env.PROXY_API_URL || "http://127.0.0.1:8317/v1";
|
|
74722
|
-
apiKey = process.env.PROXY_API_KEY || "soulforge";
|
|
74723
74945
|
proxy = {
|
|
74724
74946
|
id: "proxy",
|
|
74725
74947
|
name: "Proxy",
|
|
@@ -74728,6 +74950,7 @@ var init_proxy = __esm(() => {
|
|
|
74728
74950
|
asciiIcon: "\u26E8",
|
|
74729
74951
|
grouped: true,
|
|
74730
74952
|
createModel(modelId) {
|
|
74953
|
+
const apiKey = getActiveProxyApiKey();
|
|
74731
74954
|
if (isAnthropicModel2(modelId)) {
|
|
74732
74955
|
return createAnthropic({
|
|
74733
74956
|
baseURL,
|
|
@@ -74838,8 +75061,8 @@ var init_vercel_gateway = __esm(() => {
|
|
|
74838
75061
|
description: "Vercel AI Gateway",
|
|
74839
75062
|
grouped: true,
|
|
74840
75063
|
createModel(modelId) {
|
|
74841
|
-
const
|
|
74842
|
-
if (!
|
|
75064
|
+
const apiKey = getProviderApiKey("AI_GATEWAY_API_KEY");
|
|
75065
|
+
if (!apiKey) {
|
|
74843
75066
|
throw new Error("AI_GATEWAY_API_KEY is not set");
|
|
74844
75067
|
}
|
|
74845
75068
|
return gateway(modelId);
|
|
@@ -77807,21 +78030,21 @@ var init_xai = __esm(() => {
|
|
|
77807
78030
|
asciiIcon: "X",
|
|
77808
78031
|
description: "Grok models",
|
|
77809
78032
|
createModel(modelId) {
|
|
77810
|
-
const
|
|
77811
|
-
if (!
|
|
78033
|
+
const apiKey = getProviderApiKey("XAI_API_KEY");
|
|
78034
|
+
if (!apiKey) {
|
|
77812
78035
|
throw new Error("XAI_API_KEY is not set");
|
|
77813
78036
|
}
|
|
77814
78037
|
return createXai({
|
|
77815
|
-
apiKey
|
|
78038
|
+
apiKey
|
|
77816
78039
|
})(modelId);
|
|
77817
78040
|
},
|
|
77818
78041
|
async fetchModels() {
|
|
77819
|
-
const
|
|
77820
|
-
if (!
|
|
78042
|
+
const apiKey = getProviderApiKey("XAI_API_KEY");
|
|
78043
|
+
if (!apiKey)
|
|
77821
78044
|
return null;
|
|
77822
78045
|
const res = await fetch("https://api.x.ai/v1/models", {
|
|
77823
78046
|
headers: {
|
|
77824
|
-
Authorization: `Bearer ${
|
|
78047
|
+
Authorization: `Bearer ${apiKey}`
|
|
77825
78048
|
}
|
|
77826
78049
|
});
|
|
77827
78050
|
if (!res.ok)
|
|
@@ -78120,9 +78343,9 @@ var init_summarize = __esm(() => {
|
|
|
78120
78343
|
});
|
|
78121
78344
|
|
|
78122
78345
|
// src/core/workers/io.worker.ts
|
|
78123
|
-
import { existsSync as
|
|
78346
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync } from "fs";
|
|
78124
78347
|
import { readFile, rename, writeFile as writeFile2 } from "fs/promises";
|
|
78125
|
-
import { extname, join as
|
|
78348
|
+
import { extname, join as join10 } from "path";
|
|
78126
78349
|
|
|
78127
78350
|
// node_modules/isbinaryfile/lib/index.js
|
|
78128
78351
|
import { open, stat } from "fs/promises";
|
|
@@ -78496,7 +78719,7 @@ var handlers = {
|
|
|
78496
78719
|
const dir = sessionDir;
|
|
78497
78720
|
const sessionMeta = meta3;
|
|
78498
78721
|
const entries = tabEntries;
|
|
78499
|
-
if (!
|
|
78722
|
+
if (!existsSync5(dir)) {
|
|
78500
78723
|
mkdirSync4(dir, {
|
|
78501
78724
|
recursive: true,
|
|
78502
78725
|
mode: 448
|
|
@@ -78524,8 +78747,8 @@ var handlers = {
|
|
|
78524
78747
|
const metaJson = JSON.stringify(updatedMeta, null, 2);
|
|
78525
78748
|
const lines = allMessages.map((m) => JSON.stringify(m)).join(`
|
|
78526
78749
|
`);
|
|
78527
|
-
const metaPath =
|
|
78528
|
-
const jsonlPath =
|
|
78750
|
+
const metaPath = join10(dir, "meta.json");
|
|
78751
|
+
const jsonlPath = join10(dir, "messages.jsonl");
|
|
78529
78752
|
const suffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
78530
78753
|
const metaTmp = `${metaPath}.${suffix}.tmp`;
|
|
78531
78754
|
const jsonlTmp = `${jsonlPath}.${suffix}.tmp`;
|
|
@@ -78546,7 +78769,7 @@ var handlers = {
|
|
|
78546
78769
|
for (const [tabId, msgs] of cores) {
|
|
78547
78770
|
coreData[tabId] = msgs;
|
|
78548
78771
|
}
|
|
78549
|
-
const corePath =
|
|
78772
|
+
const corePath = join10(dir, "core.json");
|
|
78550
78773
|
const coreTmp = `${corePath}.${suffix}.tmp`;
|
|
78551
78774
|
await writeFile2(coreTmp, JSON.stringify(coreData), {
|
|
78552
78775
|
encoding: "utf-8",
|
|
@@ -78557,14 +78780,14 @@ var handlers = {
|
|
|
78557
78780
|
},
|
|
78558
78781
|
loadSession: async (sessionDir) => {
|
|
78559
78782
|
const dir = sessionDir;
|
|
78560
|
-
const metaPath =
|
|
78561
|
-
if (!
|
|
78783
|
+
const metaPath = join10(dir, "meta.json");
|
|
78784
|
+
if (!existsSync5(metaPath))
|
|
78562
78785
|
return null;
|
|
78563
|
-
const meta3 = JSON.parse(
|
|
78564
|
-
const jsonlPath =
|
|
78786
|
+
const meta3 = JSON.parse(readFileSync4(metaPath, "utf-8"));
|
|
78787
|
+
const jsonlPath = join10(dir, "messages.jsonl");
|
|
78565
78788
|
const allMessages = [];
|
|
78566
|
-
if (
|
|
78567
|
-
const content =
|
|
78789
|
+
if (existsSync5(jsonlPath)) {
|
|
78790
|
+
const content = readFileSync4(jsonlPath, "utf-8").trim();
|
|
78568
78791
|
if (content) {
|
|
78569
78792
|
for (const line of content.split(`
|
|
78570
78793
|
`)) {
|
|
@@ -78586,11 +78809,11 @@ var handlers = {
|
|
|
78586
78809
|
} = tab.messageRange;
|
|
78587
78810
|
tabEntries.push([tab.id, allMessages.slice(startLine, endLine)]);
|
|
78588
78811
|
}
|
|
78589
|
-
const corePath =
|
|
78812
|
+
const corePath = join10(dir, "core.json");
|
|
78590
78813
|
let coreEntries;
|
|
78591
|
-
if (
|
|
78814
|
+
if (existsSync5(corePath)) {
|
|
78592
78815
|
try {
|
|
78593
|
-
const coreData = JSON.parse(
|
|
78816
|
+
const coreData = JSON.parse(readFileSync4(corePath, "utf-8"));
|
|
78594
78817
|
coreEntries = Object.entries(coreData);
|
|
78595
78818
|
} catch {}
|
|
78596
78819
|
}
|
|
@@ -78602,27 +78825,27 @@ var handlers = {
|
|
|
78602
78825
|
},
|
|
78603
78826
|
listSessions: (sessionsDir) => {
|
|
78604
78827
|
const dir = sessionsDir;
|
|
78605
|
-
if (!
|
|
78828
|
+
if (!existsSync5(dir))
|
|
78606
78829
|
return [];
|
|
78607
78830
|
try {
|
|
78608
78831
|
const entries = readdirSync4(dir);
|
|
78609
78832
|
const metas = [];
|
|
78610
78833
|
for (const entry of entries) {
|
|
78611
78834
|
try {
|
|
78612
|
-
const fullPath =
|
|
78835
|
+
const fullPath = join10(dir, entry);
|
|
78613
78836
|
const s = statSync(fullPath);
|
|
78614
78837
|
if (!s.isDirectory())
|
|
78615
78838
|
continue;
|
|
78616
|
-
const metaPath =
|
|
78617
|
-
if (!
|
|
78839
|
+
const metaPath = join10(fullPath, "meta.json");
|
|
78840
|
+
if (!existsSync5(metaPath))
|
|
78618
78841
|
continue;
|
|
78619
|
-
const raw =
|
|
78842
|
+
const raw = readFileSync4(metaPath, "utf-8");
|
|
78620
78843
|
const meta3 = JSON.parse(raw);
|
|
78621
78844
|
const totalMessages = (meta3.tabs ?? []).reduce((sum, t) => sum + (t.messageRange.endLine - t.messageRange.startLine), 0);
|
|
78622
78845
|
let sizeBytes = 0;
|
|
78623
78846
|
for (const file2 of ["meta.json", "messages.jsonl"]) {
|
|
78624
78847
|
try {
|
|
78625
|
-
sizeBytes += statSync(
|
|
78848
|
+
sizeBytes += statSync(join10(fullPath, file2)).size;
|
|
78626
78849
|
} catch {}
|
|
78627
78850
|
}
|
|
78628
78851
|
metas.push({
|