@proxysoul/soulforge 2.13.2 → 2.14.1
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/{ghostty-opentui-wj67em0v.node → ghostty-opentui-97zf3rxa.node} +0 -0
- package/dist/{ghostty-opentui-rym4fe6k.node → ghostty-opentui-dgswj2kn.node} +0 -0
- package/dist/{ghostty-opentui-jqndrqz0.node → ghostty-opentui-f2mew9sn.node} +0 -0
- package/dist/{ghostty-opentui-wvtvvtsy.node → ghostty-opentui-hpb2vcxc.node} +0 -0
- package/dist/{ghostty-opentui-m3jfs005.node → ghostty-opentui-y599pszh.node} +0 -0
- package/dist/index.js +128525 -113254
- package/dist/workers/intelligence.worker.js +2689 -1006
- package/dist/workers/io.worker.js +470 -222
- package/package.json +13 -11
|
@@ -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: {
|
|
@@ -41850,7 +41855,7 @@ var init_ui = __esm(() => {
|
|
|
41850
41855
|
reasoningExpanded: {},
|
|
41851
41856
|
suspended: false,
|
|
41852
41857
|
editorSplit: 60,
|
|
41853
|
-
lockIn:
|
|
41858
|
+
lockIn: true,
|
|
41854
41859
|
openModal: (name21) => set2(() => ({
|
|
41855
41860
|
modals: {
|
|
41856
41861
|
...INITIAL_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.1",
|
|
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"
|
|
@@ -49566,14 +49572,14 @@ var init_package = __esm(() => {
|
|
|
49566
49572
|
},
|
|
49567
49573
|
devDependencies: {
|
|
49568
49574
|
"@babel/core": "7.29.0",
|
|
49569
|
-
"@biomejs/biome": "2.4.
|
|
49575
|
+
"@biomejs/biome": "2.4.13",
|
|
49570
49576
|
"@types/babel__core": "7.20.5",
|
|
49571
|
-
"@types/bun": "1.3.
|
|
49577
|
+
"@types/bun": "1.3.13",
|
|
49572
49578
|
"@types/linkify-it": "5.0.0",
|
|
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: {
|
|
@@ -49588,25 +49594,26 @@ var init_package = __esm(() => {
|
|
|
49588
49594
|
"@ai-sdk/openai": "3.0.53",
|
|
49589
49595
|
"@ai-sdk/openai-compatible": "^2.0.41",
|
|
49590
49596
|
"@ai-sdk/xai": "3.0.83",
|
|
49591
|
-
"@anthropic-ai/sdk": "0.
|
|
49592
|
-
"@llmgateway/ai-sdk-provider": "3.
|
|
49597
|
+
"@anthropic-ai/sdk": "0.91.1",
|
|
49598
|
+
"@llmgateway/ai-sdk-provider": "3.7.0",
|
|
49593
49599
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49594
49600
|
"@mozilla/readability": "0.6.0",
|
|
49595
|
-
"@openrouter/ai-sdk-provider": "2.8.
|
|
49596
|
-
"@opentui/react": "0.
|
|
49601
|
+
"@openrouter/ai-sdk-provider": "2.8.1",
|
|
49602
|
+
"@opentui/react": "0.2.0",
|
|
49597
49603
|
ai: "6.0.168",
|
|
49598
|
-
"ghostty-opentui": "1.4.
|
|
49604
|
+
"ghostty-opentui": "1.4.11",
|
|
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",
|
|
@@ -49634,26 +49641,36 @@ async function exchangeToken(githubToken) {
|
|
|
49634
49641
|
if (cachedBearer && Date.now() / 1000 < cachedBearer.expiresAt - 60) {
|
|
49635
49642
|
return cachedBearer.token;
|
|
49636
49643
|
}
|
|
49637
|
-
|
|
49638
|
-
|
|
49639
|
-
|
|
49640
|
-
|
|
49641
|
-
|
|
49642
|
-
|
|
49643
|
-
|
|
49644
|
-
|
|
49645
|
-
|
|
49646
|
-
|
|
49647
|
-
|
|
49648
|
-
|
|
49649
|
-
|
|
49650
|
-
|
|
49651
|
-
|
|
49652
|
-
|
|
49653
|
-
|
|
49654
|
-
|
|
49655
|
-
|
|
49656
|
-
|
|
49644
|
+
if (bearerInflight)
|
|
49645
|
+
return bearerInflight;
|
|
49646
|
+
bearerInflight = (async () => {
|
|
49647
|
+
try {
|
|
49648
|
+
const res = await fetch(TOKEN_EXCHANGE, {
|
|
49649
|
+
headers: {
|
|
49650
|
+
Authorization: `Token ${githubToken}`,
|
|
49651
|
+
"User-Agent": `SoulForge/${CURRENT_VERSION}`
|
|
49652
|
+
},
|
|
49653
|
+
signal: AbortSignal.timeout(1e4)
|
|
49654
|
+
});
|
|
49655
|
+
if (!res.ok) {
|
|
49656
|
+
cachedBearer = null;
|
|
49657
|
+
const body = await res.text().catch(() => "");
|
|
49658
|
+
const hint = res.status === 401 || res.status === 403 ? " \u2014 your GitHub OAuth token is invalid or expired. Re-run the login flow in VS Code/JetBrains and copy the fresh oauth_token from ~/.config/github-copilot/apps.json." : res.status >= 500 ? " \u2014 GitHub is having issues, try again in a moment." : "";
|
|
49659
|
+
throw new Error(`Copilot token exchange failed (${String(res.status)})${body ? `: ${body.slice(0, 200)}` : ""}${hint}`);
|
|
49660
|
+
}
|
|
49661
|
+
const data = await res.json();
|
|
49662
|
+
if (!data.token)
|
|
49663
|
+
throw new Error("Copilot token exchange returned empty token");
|
|
49664
|
+
cachedBearer = {
|
|
49665
|
+
token: data.token,
|
|
49666
|
+
expiresAt: data.expires_at
|
|
49667
|
+
};
|
|
49668
|
+
return data.token;
|
|
49669
|
+
} finally {
|
|
49670
|
+
bearerInflight = null;
|
|
49671
|
+
}
|
|
49672
|
+
})();
|
|
49673
|
+
return bearerInflight;
|
|
49657
49674
|
}
|
|
49658
49675
|
function invalidateBearer() {
|
|
49659
49676
|
cachedBearer = null;
|
|
@@ -49664,16 +49681,19 @@ function getGitHubToken() {
|
|
|
49664
49681
|
return stored;
|
|
49665
49682
|
throw new Error("GitHub Copilot requires an OAuth token. Sign in via VS Code or JetBrains, then copy oauth_token from ~/.config/github-copilot/apps.json and save it with /keys or --set-key copilot.");
|
|
49666
49683
|
}
|
|
49667
|
-
function
|
|
49668
|
-
|
|
49669
|
-
|
|
49670
|
-
|
|
49671
|
-
|
|
49672
|
-
|
|
49673
|
-
|
|
49674
|
-
|
|
49675
|
-
|
|
49676
|
-
|
|
49684
|
+
function detectInitiator(body) {
|
|
49685
|
+
if (typeof body !== "string")
|
|
49686
|
+
return "user";
|
|
49687
|
+
try {
|
|
49688
|
+
const parsed = JSON.parse(body);
|
|
49689
|
+
if (Array.isArray(parsed.messages)) {
|
|
49690
|
+
for (const m of parsed.messages) {
|
|
49691
|
+
if (m?.role === "assistant" || m?.role === "tool")
|
|
49692
|
+
return "agent";
|
|
49693
|
+
}
|
|
49694
|
+
}
|
|
49695
|
+
} catch {}
|
|
49696
|
+
return "user";
|
|
49677
49697
|
}
|
|
49678
49698
|
function createCopilotFetch(githubToken) {
|
|
49679
49699
|
return async (url2, init) => {
|
|
@@ -49684,38 +49704,41 @@ function createCopilotFetch(githubToken) {
|
|
|
49684
49704
|
invalidateBearer();
|
|
49685
49705
|
bearer = await exchangeToken(githubToken);
|
|
49686
49706
|
}
|
|
49687
|
-
const
|
|
49688
|
-
|
|
49707
|
+
const buildHeaders = (token) => {
|
|
49708
|
+
const h = new Headers(init?.headers);
|
|
49709
|
+
h.set("Authorization", `Bearer ${token}`);
|
|
49710
|
+
h.set("X-Request-Id", crypto.randomUUID());
|
|
49711
|
+
h.set("X-Initiator", detectInitiator(init?.body));
|
|
49712
|
+
return h;
|
|
49713
|
+
};
|
|
49689
49714
|
const res = await fetch(url2, {
|
|
49690
49715
|
...init,
|
|
49691
|
-
headers
|
|
49716
|
+
headers: buildHeaders(bearer)
|
|
49692
49717
|
});
|
|
49693
49718
|
if (res.status === 401) {
|
|
49694
49719
|
invalidateBearer();
|
|
49695
49720
|
const retryBearer = await exchangeToken(githubToken);
|
|
49696
|
-
const retryHeaders = new Headers(init?.headers);
|
|
49697
|
-
retryHeaders.set("Authorization", `Bearer ${retryBearer}`);
|
|
49698
49721
|
return fetch(url2, {
|
|
49699
49722
|
...init,
|
|
49700
|
-
headers:
|
|
49723
|
+
headers: buildHeaders(retryBearer)
|
|
49701
49724
|
});
|
|
49702
49725
|
}
|
|
49703
49726
|
return res;
|
|
49704
49727
|
};
|
|
49705
49728
|
}
|
|
49729
|
+
function assertChatCompletionsSupported(modelId) {
|
|
49730
|
+
const endpoints = supportedEndpoints.get(modelId);
|
|
49731
|
+
if (!endpoints || endpoints.length === 0)
|
|
49732
|
+
return;
|
|
49733
|
+
const hasChat = endpoints.some((e) => e.includes("chat") || e.includes("completions"));
|
|
49734
|
+
if (hasChat)
|
|
49735
|
+
return;
|
|
49736
|
+
throw new Error(`Copilot model "${modelId}" only supports ${endpoints.join(", ")} \u2014 ` + "SoulForge routes Copilot through /chat/completions. " + "Try claude-sonnet-4.6, gpt-4.1, or another chat-compatible model.");
|
|
49737
|
+
}
|
|
49706
49738
|
function createCopilotModel(modelId) {
|
|
49739
|
+
assertChatCompletionsSupported(modelId);
|
|
49707
49740
|
const githubToken = getGitHubToken();
|
|
49708
49741
|
const copilotFetch = createCopilotFetch(githubToken);
|
|
49709
|
-
if (isAnthropicModel(modelId)) {
|
|
49710
|
-
return createAnthropic({
|
|
49711
|
-
baseURL: COPILOT_API,
|
|
49712
|
-
apiKey: "copilot",
|
|
49713
|
-
headers: {
|
|
49714
|
-
...COPILOT_HEADERS
|
|
49715
|
-
},
|
|
49716
|
-
fetch: copilotFetch
|
|
49717
|
-
})(modelId);
|
|
49718
|
-
}
|
|
49719
49742
|
const client = createOpenAI({
|
|
49720
49743
|
baseURL: COPILOT_API,
|
|
49721
49744
|
apiKey: "copilot",
|
|
@@ -49724,22 +49747,23 @@ function createCopilotModel(modelId) {
|
|
|
49724
49747
|
},
|
|
49725
49748
|
fetch: copilotFetch
|
|
49726
49749
|
});
|
|
49727
|
-
if (needsResponsesApi(modelId)) {
|
|
49728
|
-
return client.responses(modelId);
|
|
49729
|
-
}
|
|
49730
49750
|
return client.chat(modelId);
|
|
49731
49751
|
}
|
|
49732
|
-
var ENV_VAR = "COPILOT_API_KEY", COPILOT_API = "https://api.githubcopilot.com", TOKEN_EXCHANGE = "https://api.github.com/copilot_internal/v2/token", COPILOT_HEADERS, cachedBearer = null, copilot;
|
|
49752
|
+
var ENV_VAR = "COPILOT_API_KEY", COPILOT_API = "https://api.githubcopilot.com", TOKEN_EXCHANGE = "https://api.github.com/copilot_internal/v2/token", COPILOT_CHAT_VERSION = "0.26.7", COPILOT_API_VERSION = "2025-04-01", COPILOT_HEADERS, cachedBearer = null, bearerInflight = null, supportedEndpoints, copilot;
|
|
49733
49753
|
var init_copilot = __esm(() => {
|
|
49734
|
-
init_dist6();
|
|
49735
49754
|
init_dist8();
|
|
49736
49755
|
init_secrets();
|
|
49737
49756
|
init_version();
|
|
49738
49757
|
COPILOT_HEADERS = {
|
|
49739
|
-
"Editor-Version":
|
|
49740
|
-
"Editor-Plugin-Version": `
|
|
49741
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
49742
|
-
|
|
49758
|
+
"Editor-Version": "vscode/1.95.0",
|
|
49759
|
+
"Editor-Plugin-Version": `copilot-chat/${COPILOT_CHAT_VERSION}`,
|
|
49760
|
+
"Copilot-Integration-Id": "vscode-chat",
|
|
49761
|
+
"User-Agent": `GitHubCopilotChat/${COPILOT_CHAT_VERSION}`,
|
|
49762
|
+
"OpenAI-Intent": "conversation-panel",
|
|
49763
|
+
"X-GitHub-Api-Version": COPILOT_API_VERSION,
|
|
49764
|
+
"X-VSCode-User-Agent-Library-Version": "electron-fetch"
|
|
49765
|
+
};
|
|
49766
|
+
supportedEndpoints = new Map;
|
|
49743
49767
|
copilot = {
|
|
49744
49768
|
id: "copilot",
|
|
49745
49769
|
name: "GitHub Copilot",
|
|
@@ -49774,6 +49798,9 @@ var init_copilot = __esm(() => {
|
|
|
49774
49798
|
continue;
|
|
49775
49799
|
if (result.some((r) => r.id === m.id))
|
|
49776
49800
|
continue;
|
|
49801
|
+
if (Array.isArray(m.supported_endpoints)) {
|
|
49802
|
+
supportedEndpoints.set(m.id, m.supported_endpoints);
|
|
49803
|
+
}
|
|
49777
49804
|
result.push({
|
|
49778
49805
|
id: m.id,
|
|
49779
49806
|
name: m.id
|
|
@@ -57851,7 +57878,8 @@ function createLLMGateway(options = {}) {
|
|
|
57851
57878
|
provider: "llmgateway.image",
|
|
57852
57879
|
url: ({ path }) => `${baseURL}${path}`,
|
|
57853
57880
|
headers: getHeaders,
|
|
57854
|
-
fetch: options.fetch
|
|
57881
|
+
fetch: options.fetch,
|
|
57882
|
+
extraBody: options.extraBody
|
|
57855
57883
|
});
|
|
57856
57884
|
const createLanguageModel = (modelId, settings) => {
|
|
57857
57885
|
if (new.target) {
|
|
@@ -59105,13 +59133,19 @@ var __defProp3, __defProps, __getOwnPropDescs, __getOwnPropSymbols, __hasOwnProp
|
|
|
59105
59133
|
if (options.aspectRatio != null) {
|
|
59106
59134
|
body.aspect_ratio = options.aspectRatio;
|
|
59107
59135
|
}
|
|
59136
|
+
if (options.quality != null) {
|
|
59137
|
+
body.quality = options.quality;
|
|
59138
|
+
}
|
|
59139
|
+
const providerOptions = options.providerOptions || {};
|
|
59140
|
+
const llmgatewayOptions = providerOptions.llmgateway || {};
|
|
59141
|
+
const requestBody = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, body), this.config.extraBody), this.settings.extraBody), llmgatewayOptions);
|
|
59108
59142
|
const { value: response, responseHeaders } = await postJsonToApi2({
|
|
59109
59143
|
url: this.config.url({
|
|
59110
59144
|
path: hasFiles ? "/images/edits" : "/images/generations",
|
|
59111
59145
|
modelId: this.modelId
|
|
59112
59146
|
}),
|
|
59113
59147
|
headers: combineHeaders2(this.config.headers(), options.headers),
|
|
59114
|
-
body,
|
|
59148
|
+
body: requestBody,
|
|
59115
59149
|
failedResponseHandler: llmgatewayFailedResponseHandler,
|
|
59116
59150
|
successfulResponseHandler: createJsonResponseHandler2(LLMGatewayImageResponseSchema),
|
|
59117
59151
|
abortSignal: options.abortSignal,
|
|
@@ -71066,9 +71100,9 @@ function convertToOpenRouterChatMessages(prompt) {
|
|
|
71066
71100
|
const parsedProviderOptions = OpenRouterProviderOptionsSchema.safeParse(providerOptions);
|
|
71067
71101
|
const messageReasoningDetails = parsedProviderOptions.success ? (_e = (_d = parsedProviderOptions.data) == null ? undefined : _d.openrouter) == null ? undefined : _e.reasoning_details : undefined;
|
|
71068
71102
|
const messageAnnotations = parsedProviderOptions.success ? (_g = (_f = parsedProviderOptions.data) == null ? undefined : _f.openrouter) == null ? undefined : _g.annotations : undefined;
|
|
71069
|
-
const candidateReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails)
|
|
71103
|
+
const candidateReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) ? messageReasoningDetails : findFirstReasoningDetails(content);
|
|
71070
71104
|
let finalReasoningDetails;
|
|
71071
|
-
if (candidateReasoningDetails
|
|
71105
|
+
if (candidateReasoningDetails) {
|
|
71072
71106
|
const validDetails = candidateReasoningDetails.filter((detail) => {
|
|
71073
71107
|
var _a173;
|
|
71074
71108
|
if (detail.type !== "reasoning.text") {
|
|
@@ -71092,9 +71126,9 @@ function convertToOpenRouterChatMessages(prompt) {
|
|
|
71092
71126
|
uniqueDetails.push(detail);
|
|
71093
71127
|
}
|
|
71094
71128
|
}
|
|
71095
|
-
finalReasoningDetails = uniqueDetails
|
|
71129
|
+
finalReasoningDetails = uniqueDetails;
|
|
71096
71130
|
}
|
|
71097
|
-
const effectiveReasoning = reasoning && finalReasoningDetails ? reasoning : undefined;
|
|
71131
|
+
const effectiveReasoning = reasoning && finalReasoningDetails && finalReasoningDetails.length > 0 ? reasoning : undefined;
|
|
71098
71132
|
messages.push({
|
|
71099
71133
|
role: "assistant",
|
|
71100
71134
|
content: text2,
|
|
@@ -72537,11 +72571,11 @@ var __defProp4, __defProps2, __getOwnPropDescs2, __getOwnPropSymbols2, __hasOwnP
|
|
|
72537
72571
|
controller.enqueue({
|
|
72538
72572
|
type: "reasoning-end",
|
|
72539
72573
|
id: reasoningId || generateId5(),
|
|
72540
|
-
providerMetadata:
|
|
72574
|
+
providerMetadata: {
|
|
72541
72575
|
openrouter: {
|
|
72542
72576
|
reasoning_details: accumulatedReasoningDetails
|
|
72543
72577
|
}
|
|
72544
|
-
}
|
|
72578
|
+
}
|
|
72545
72579
|
});
|
|
72546
72580
|
reasoningStarted = false;
|
|
72547
72581
|
}
|
|
@@ -72772,11 +72806,11 @@ var __defProp4, __defProps2, __getOwnPropDescs2, __getOwnPropSymbols2, __hasOwnP
|
|
|
72772
72806
|
controller.enqueue({
|
|
72773
72807
|
type: "reasoning-end",
|
|
72774
72808
|
id: reasoningId || generateId5(),
|
|
72775
|
-
providerMetadata:
|
|
72809
|
+
providerMetadata: {
|
|
72776
72810
|
openrouter: {
|
|
72777
72811
|
reasoning_details: accumulatedReasoningDetails
|
|
72778
72812
|
}
|
|
72779
|
-
}
|
|
72813
|
+
}
|
|
72780
72814
|
});
|
|
72781
72815
|
}
|
|
72782
72816
|
if (textStarted) {
|
|
@@ -72791,9 +72825,7 @@ var __defProp4, __defProps2, __getOwnPropDescs2, __getOwnPropSymbols2, __hasOwnP
|
|
|
72791
72825
|
if (provider !== undefined) {
|
|
72792
72826
|
openrouterMetadata.provider = provider;
|
|
72793
72827
|
}
|
|
72794
|
-
|
|
72795
|
-
openrouterMetadata.reasoning_details = accumulatedReasoningDetails;
|
|
72796
|
-
}
|
|
72828
|
+
openrouterMetadata.reasoning_details = accumulatedReasoningDetails;
|
|
72797
72829
|
if (accumulatedFileAnnotations.length > 0) {
|
|
72798
72830
|
openrouterMetadata.annotations = accumulatedFileAnnotations;
|
|
72799
72831
|
}
|
|
@@ -73245,7 +73277,7 @@ var __defProp4, __defProps2, __getOwnPropDescs2, __getOwnPropSymbols2, __hasOwnP
|
|
|
73245
73277
|
usage
|
|
73246
73278
|
};
|
|
73247
73279
|
}
|
|
73248
|
-
}, DEFAULT_IMAGE_MEDIA_TYPE = "image/png", webSearchInputSchema2, webSearch2, VERSION22 = "2.8.
|
|
73280
|
+
}, DEFAULT_IMAGE_MEDIA_TYPE = "image/png", webSearchInputSchema2, webSearch2, VERSION22 = "2.8.1", VideoGenerationSubmitResponseSchema, VideoGenerationPollResponseSchema, DEFAULT_POLL_INTERVAL_MS = 2000, DEFAULT_MAX_POLL_TIME_MS = 600000, OpenRouterVideoModel = class {
|
|
73249
73281
|
constructor(modelId, settings, config2) {
|
|
73250
73282
|
this.specificationVersion = "v3";
|
|
73251
73283
|
this.provider = "openrouter";
|
|
@@ -74331,11 +74363,162 @@ var init_openrouter = __esm(() => {
|
|
|
74331
74363
|
};
|
|
74332
74364
|
});
|
|
74333
74365
|
|
|
74366
|
+
// src/core/proxy/key-resolver.ts
|
|
74367
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
74368
|
+
import { homedir as homedir5 } from "os";
|
|
74369
|
+
import { join as join7 } from "path";
|
|
74370
|
+
function candidateConfigPaths() {
|
|
74371
|
+
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")];
|
|
74372
|
+
return paths.filter((p) => typeof p === "string" && p.length > 0);
|
|
74373
|
+
}
|
|
74374
|
+
function isPlausibleKey(value) {
|
|
74375
|
+
const s = value.trim();
|
|
74376
|
+
if (!s)
|
|
74377
|
+
return false;
|
|
74378
|
+
if (PLACEHOLDER_PATTERNS.some((re) => re.test(s)))
|
|
74379
|
+
return false;
|
|
74380
|
+
if (BCRYPT_RE.test(s))
|
|
74381
|
+
return false;
|
|
74382
|
+
if (SHA256_HEX_RE.test(s))
|
|
74383
|
+
return false;
|
|
74384
|
+
return true;
|
|
74385
|
+
}
|
|
74386
|
+
function parseApiKeys(content) {
|
|
74387
|
+
const lines = content.split(`
|
|
74388
|
+
`);
|
|
74389
|
+
const keys = [];
|
|
74390
|
+
let inList = false;
|
|
74391
|
+
let listIndent = -1;
|
|
74392
|
+
for (const rawLine of lines) {
|
|
74393
|
+
const line = rawLine.replace(/\r$/, "");
|
|
74394
|
+
const stripped = line.replace(/^\s*/, "");
|
|
74395
|
+
if (stripped.startsWith("#"))
|
|
74396
|
+
continue;
|
|
74397
|
+
if (!inList) {
|
|
74398
|
+
if (/^api-keys\s*:\s*$/.test(stripped) && !line.startsWith(" ") && !line.startsWith("\t")) {
|
|
74399
|
+
inList = true;
|
|
74400
|
+
listIndent = -1;
|
|
74401
|
+
}
|
|
74402
|
+
continue;
|
|
74403
|
+
}
|
|
74404
|
+
if (stripped === "")
|
|
74405
|
+
continue;
|
|
74406
|
+
const indentMatch = line.match(/^(\s+)/);
|
|
74407
|
+
const indent = indentMatch?.[1]?.length ?? 0;
|
|
74408
|
+
if (indent === 0)
|
|
74409
|
+
break;
|
|
74410
|
+
if (listIndent === -1)
|
|
74411
|
+
listIndent = indent;
|
|
74412
|
+
if (indent < listIndent)
|
|
74413
|
+
break;
|
|
74414
|
+
const item = line.slice(indent).match(/^-\s+(?:"([^"]*)"|'([^']*)'|(.+?))\s*$/);
|
|
74415
|
+
if (!item)
|
|
74416
|
+
continue;
|
|
74417
|
+
const v = item[1] ?? item[2] ?? item[3] ?? "";
|
|
74418
|
+
keys.push(v);
|
|
74419
|
+
}
|
|
74420
|
+
return keys;
|
|
74421
|
+
}
|
|
74422
|
+
function discoverApiKeys() {
|
|
74423
|
+
const out = [];
|
|
74424
|
+
const seen = new Set;
|
|
74425
|
+
for (const path of candidateConfigPaths()) {
|
|
74426
|
+
try {
|
|
74427
|
+
if (!existsSync2(path))
|
|
74428
|
+
continue;
|
|
74429
|
+
const content = readFileSync2(path, "utf-8");
|
|
74430
|
+
for (const k of parseApiKeys(content)) {
|
|
74431
|
+
if (!isPlausibleKey(k))
|
|
74432
|
+
continue;
|
|
74433
|
+
if (seen.has(k))
|
|
74434
|
+
continue;
|
|
74435
|
+
seen.add(k);
|
|
74436
|
+
out.push({
|
|
74437
|
+
key: k,
|
|
74438
|
+
source: path
|
|
74439
|
+
});
|
|
74440
|
+
}
|
|
74441
|
+
} catch {}
|
|
74442
|
+
}
|
|
74443
|
+
return out;
|
|
74444
|
+
}
|
|
74445
|
+
function primaryConfigPath() {
|
|
74446
|
+
for (const path of candidateConfigPaths()) {
|
|
74447
|
+
try {
|
|
74448
|
+
if (!existsSync2(path))
|
|
74449
|
+
continue;
|
|
74450
|
+
const content = readFileSync2(path, "utf-8");
|
|
74451
|
+
if (/^api-keys\s*:\s*$/m.test(content))
|
|
74452
|
+
return path;
|
|
74453
|
+
} catch {}
|
|
74454
|
+
}
|
|
74455
|
+
return null;
|
|
74456
|
+
}
|
|
74457
|
+
function getActiveProxyApiKey() {
|
|
74458
|
+
return cached2;
|
|
74459
|
+
}
|
|
74460
|
+
function setActiveProxyApiKey(key) {
|
|
74461
|
+
cached2 = key;
|
|
74462
|
+
cachedIsProbed = true;
|
|
74463
|
+
}
|
|
74464
|
+
function candidateApiKeys() {
|
|
74465
|
+
const out = [];
|
|
74466
|
+
const push = (v) => {
|
|
74467
|
+
if (!v)
|
|
74468
|
+
return;
|
|
74469
|
+
const s = v.trim();
|
|
74470
|
+
if (!s)
|
|
74471
|
+
return;
|
|
74472
|
+
if (!out.includes(s))
|
|
74473
|
+
out.push(s);
|
|
74474
|
+
};
|
|
74475
|
+
push(process.env.PROXY_API_KEY);
|
|
74476
|
+
push("soulforge");
|
|
74477
|
+
for (const d of discoverApiKeys())
|
|
74478
|
+
push(d.key);
|
|
74479
|
+
return out;
|
|
74480
|
+
}
|
|
74481
|
+
var PLACEHOLDER_PATTERNS, BCRYPT_RE, SHA256_HEX_RE, cached2, cachedIsProbed = false;
|
|
74482
|
+
var init_key_resolver = __esm(() => {
|
|
74483
|
+
PLACEHOLDER_PATTERNS = [/^your[-_]?(?:api[-_]?)?key/i, /^changeme$/i, /^replace[-_ ]?me/i, /^xxx+$/i];
|
|
74484
|
+
BCRYPT_RE = /^\$2[aby]\$/;
|
|
74485
|
+
SHA256_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
74486
|
+
cached2 = process.env.PROXY_API_KEY?.trim() || "soulforge";
|
|
74487
|
+
});
|
|
74488
|
+
|
|
74334
74489
|
// src/core/setup/install.ts
|
|
74335
74490
|
import { execSync } from "child_process";
|
|
74336
|
-
import { existsSync as
|
|
74337
|
-
import { homedir as
|
|
74338
|
-
import { join as
|
|
74491
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, symlinkSync, unlinkSync } from "fs";
|
|
74492
|
+
import { homedir as homedir6, platform } from "os";
|
|
74493
|
+
import { join as join8 } from "path";
|
|
74494
|
+
async function fetchLatestProxyVersion(timeoutMs = 5000) {
|
|
74495
|
+
try {
|
|
74496
|
+
const ctl = new AbortController;
|
|
74497
|
+
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
74498
|
+
const res = await fetch(PROXY_RELEASES_URL, {
|
|
74499
|
+
signal: ctl.signal,
|
|
74500
|
+
headers: {
|
|
74501
|
+
Accept: "application/vnd.github+json"
|
|
74502
|
+
}
|
|
74503
|
+
});
|
|
74504
|
+
clearTimeout(t);
|
|
74505
|
+
if (!res.ok)
|
|
74506
|
+
return null;
|
|
74507
|
+
const data = await res.json();
|
|
74508
|
+
return data.tag_name?.replace(/^v/, "")?.trim() || null;
|
|
74509
|
+
} catch {
|
|
74510
|
+
return null;
|
|
74511
|
+
}
|
|
74512
|
+
}
|
|
74513
|
+
async function resolveProxyVersion() {
|
|
74514
|
+
const env = process.env.SOULFORGE_PROXY_VERSION?.trim();
|
|
74515
|
+
if (env)
|
|
74516
|
+
return env;
|
|
74517
|
+
const latest = await fetchLatestProxyVersion();
|
|
74518
|
+
if (latest)
|
|
74519
|
+
return latest;
|
|
74520
|
+
return FALLBACK_PROXY_VERSION;
|
|
74521
|
+
}
|
|
74339
74522
|
function getPlatformKey() {
|
|
74340
74523
|
const key = `${process.platform}-${process.arch}`;
|
|
74341
74524
|
if (key !== "darwin-arm64" && key !== "darwin-x64" && key !== "linux-x64" && key !== "linux-arm64") {
|
|
@@ -74347,22 +74530,22 @@ async function installBinary(config2) {
|
|
|
74347
74530
|
ensureDirs();
|
|
74348
74531
|
const key = getPlatformKey();
|
|
74349
74532
|
const asset = config2.getAsset(key);
|
|
74350
|
-
const extractDir =
|
|
74351
|
-
if (!
|
|
74533
|
+
const extractDir = join8(INSTALLS_DIR, `${config2.name}-${config2.version}`);
|
|
74534
|
+
if (!existsSync3(asset.binPath)) {
|
|
74352
74535
|
await downloadAndExtract(asset.url, extractDir);
|
|
74353
74536
|
}
|
|
74354
|
-
if (!
|
|
74537
|
+
if (!existsSync3(asset.binPath)) {
|
|
74355
74538
|
throw new Error(`${config2.name} binary not found after extraction at ${asset.binPath}`);
|
|
74356
74539
|
}
|
|
74357
74540
|
execSync(`chmod +x "${asset.binPath}"`, {
|
|
74358
74541
|
stdio: "ignore"
|
|
74359
74542
|
});
|
|
74360
|
-
createSymlink(asset.binPath,
|
|
74361
|
-
return
|
|
74543
|
+
createSymlink(asset.binPath, join8(BIN_DIR, config2.binName));
|
|
74544
|
+
return join8(BIN_DIR, config2.binName);
|
|
74362
74545
|
}
|
|
74363
74546
|
function getVendoredPath(binary) {
|
|
74364
|
-
const binLink =
|
|
74365
|
-
return
|
|
74547
|
+
const binLink = join8(BIN_DIR, binary);
|
|
74548
|
+
return existsSync3(binLink) ? binLink : null;
|
|
74366
74549
|
}
|
|
74367
74550
|
function ensureDirs() {
|
|
74368
74551
|
mkdirSync2(BIN_DIR, {
|
|
@@ -74380,7 +74563,7 @@ async function downloadAndExtract(url2, extractDir) {
|
|
|
74380
74563
|
if (!response.ok) {
|
|
74381
74564
|
throw new Error(`Download failed: ${response.status} ${response.statusText} (${url2})`);
|
|
74382
74565
|
}
|
|
74383
|
-
const tmpFile =
|
|
74566
|
+
const tmpFile = join8(extractDir, "download.tar.gz");
|
|
74384
74567
|
const buffer = await response.arrayBuffer();
|
|
74385
74568
|
await Bun.write(tmpFile, buffer);
|
|
74386
74569
|
execSync(`tar xzf "${tmpFile}" -C "${extractDir}"`, {
|
|
@@ -74389,14 +74572,14 @@ async function downloadAndExtract(url2, extractDir) {
|
|
|
74389
74572
|
unlinkSync(tmpFile);
|
|
74390
74573
|
}
|
|
74391
74574
|
function createSymlink(target, link) {
|
|
74392
|
-
if (
|
|
74575
|
+
if (existsSync3(link)) {
|
|
74393
74576
|
unlinkSync(link);
|
|
74394
74577
|
}
|
|
74395
74578
|
symlinkSync(target, link);
|
|
74396
74579
|
}
|
|
74397
74580
|
async function installProxy(version2) {
|
|
74398
|
-
const v = version2 ??
|
|
74399
|
-
|
|
74581
|
+
const v = version2 ?? await resolveProxyVersion();
|
|
74582
|
+
const path = await installBinary({
|
|
74400
74583
|
name: "cliproxyapi",
|
|
74401
74584
|
binName: "cli-proxy-api",
|
|
74402
74585
|
version: v,
|
|
@@ -74405,17 +74588,21 @@ async function installProxy(version2) {
|
|
|
74405
74588
|
const asset = `CLIProxyAPI_${v}_${suffix}.tar.gz`;
|
|
74406
74589
|
return {
|
|
74407
74590
|
url: `https://github.com/router-for-me/CLIProxyAPI/releases/download/v${v}/${asset}`,
|
|
74408
|
-
binPath:
|
|
74591
|
+
binPath: join8(INSTALLS_DIR, `cliproxyapi-${v}`, "cli-proxy-api")
|
|
74409
74592
|
};
|
|
74410
74593
|
}
|
|
74411
74594
|
});
|
|
74595
|
+
return {
|
|
74596
|
+
path,
|
|
74597
|
+
version: v
|
|
74598
|
+
};
|
|
74412
74599
|
}
|
|
74413
|
-
var SOULFORGE_DIR, BIN_DIR, INSTALLS_DIR, FONTS_DIR,
|
|
74600
|
+
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
74601
|
var init_install = __esm(() => {
|
|
74415
|
-
SOULFORGE_DIR =
|
|
74416
|
-
BIN_DIR =
|
|
74417
|
-
INSTALLS_DIR =
|
|
74418
|
-
FONTS_DIR =
|
|
74602
|
+
SOULFORGE_DIR = join8(homedir6(), ".soulforge");
|
|
74603
|
+
BIN_DIR = join8(SOULFORGE_DIR, "bin");
|
|
74604
|
+
INSTALLS_DIR = join8(SOULFORGE_DIR, "installs");
|
|
74605
|
+
FONTS_DIR = join8(SOULFORGE_DIR, "fonts");
|
|
74419
74606
|
PROXY_SUFFIXES = {
|
|
74420
74607
|
"darwin-arm64": "darwin_arm64",
|
|
74421
74608
|
"darwin-x64": "darwin_amd64",
|
|
@@ -74426,80 +74613,51 @@ var init_install = __esm(() => {
|
|
|
74426
74613
|
|
|
74427
74614
|
// src/core/proxy/lifecycle.ts
|
|
74428
74615
|
import { execFileSync, execSync as execSync2, spawn as spawn4 } from "child_process";
|
|
74429
|
-
import { existsSync as
|
|
74430
|
-
import { homedir as
|
|
74431
|
-
import { join as
|
|
74616
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
74617
|
+
import { homedir as homedir7 } from "os";
|
|
74618
|
+
import { join as join9 } from "path";
|
|
74432
74619
|
function setState(state, error48 = null) {
|
|
74433
74620
|
currentState = state;
|
|
74434
74621
|
lastError = error48;
|
|
74435
74622
|
for (const fn of stateListeners)
|
|
74436
74623
|
fn(state, error48);
|
|
74437
74624
|
}
|
|
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
74625
|
function saveInstalledProxyVersion(version2) {
|
|
74449
74626
|
mkdirSync3(PROXY_CONFIG_DIR, {
|
|
74450
74627
|
recursive: true
|
|
74451
74628
|
});
|
|
74452
74629
|
writeFileSync2(VERSION_FILE, version2);
|
|
74453
74630
|
}
|
|
74454
|
-
function
|
|
74455
|
-
|
|
74456
|
-
|
|
74457
|
-
|
|
74458
|
-
|
|
74459
|
-
|
|
74460
|
-
|
|
74461
|
-
|
|
74462
|
-
|
|
74463
|
-
|
|
74464
|
-
|
|
74465
|
-
|
|
74466
|
-
return
|
|
74631
|
+
function stripLegacyPerfBlock(content) {
|
|
74632
|
+
if (!content.includes(LEGACY_PERF_MARKER_PREFIX))
|
|
74633
|
+
return content;
|
|
74634
|
+
const lines = content.split(`
|
|
74635
|
+
`);
|
|
74636
|
+
const start = lines.findIndex((l) => l.startsWith(LEGACY_PERF_MARKER_PREFIX));
|
|
74637
|
+
if (start === -1)
|
|
74638
|
+
return content;
|
|
74639
|
+
let end = start + 1;
|
|
74640
|
+
while (end < lines.length && lines[end]?.trim() !== "")
|
|
74641
|
+
end++;
|
|
74642
|
+
lines.splice(start, end - start);
|
|
74643
|
+
return lines.join(`
|
|
74644
|
+
`);
|
|
74467
74645
|
}
|
|
74468
74646
|
function ensureConfig() {
|
|
74469
74647
|
mkdirSync3(PROXY_CONFIG_DIR, {
|
|
74470
74648
|
recursive: true
|
|
74471
74649
|
});
|
|
74472
|
-
if (!
|
|
74473
|
-
writeFileSync2(PROXY_CONFIG_PATH, ["host: 127.0.0.1", "port: 8317", 'auth-dir: "~/.cli-proxy-api"', "api-keys:", ' - "soulforge"', ""
|
|
74650
|
+
if (!existsSync4(PROXY_CONFIG_PATH)) {
|
|
74651
|
+
writeFileSync2(PROXY_CONFIG_PATH, ["host: 127.0.0.1", "port: 8317", 'auth-dir: "~/.cli-proxy-api"', "api-keys:", ' - "soulforge"', ""].join(`
|
|
74474
74652
|
`));
|
|
74475
74653
|
return;
|
|
74476
74654
|
}
|
|
74477
74655
|
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
|
-
}
|
|
74656
|
+
const existing = readFileSync3(PROXY_CONFIG_PATH, "utf-8");
|
|
74657
|
+
const cleaned = stripLegacyPerfBlock(existing);
|
|
74658
|
+
if (cleaned !== existing) {
|
|
74659
|
+
writeFileSync2(PROXY_CONFIG_PATH, cleaned);
|
|
74494
74660
|
}
|
|
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
74661
|
} catch {}
|
|
74504
74662
|
}
|
|
74505
74663
|
function commandExists(cmd) {
|
|
@@ -74512,24 +74670,76 @@ function commandExists(cmd) {
|
|
|
74512
74670
|
return false;
|
|
74513
74671
|
}
|
|
74514
74672
|
}
|
|
74673
|
+
function getBinaryVersion(binary) {
|
|
74674
|
+
const parse5 = (s) => {
|
|
74675
|
+
const m = s.match(/Version:\s*(\d+\.\d+\.\d+)/);
|
|
74676
|
+
return m?.[1] ?? null;
|
|
74677
|
+
};
|
|
74678
|
+
try {
|
|
74679
|
+
const out = execFileSync(binary, ["-help"], {
|
|
74680
|
+
encoding: "utf-8",
|
|
74681
|
+
timeout: 2000,
|
|
74682
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
74683
|
+
});
|
|
74684
|
+
return parse5(out);
|
|
74685
|
+
} catch (err) {
|
|
74686
|
+
const e = err;
|
|
74687
|
+
const stdout = typeof e.stdout === "string" ? e.stdout : e.stdout?.toString() ?? "";
|
|
74688
|
+
const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString() ?? "";
|
|
74689
|
+
return parse5(stdout + stderr);
|
|
74690
|
+
}
|
|
74691
|
+
}
|
|
74692
|
+
function compareVersions(a, b) {
|
|
74693
|
+
const ap = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
74694
|
+
const bp = b.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
74695
|
+
const len = Math.max(ap.length, bp.length);
|
|
74696
|
+
for (let i = 0;i < len; i++) {
|
|
74697
|
+
const diff = (ap[i] ?? 0) - (bp[i] ?? 0);
|
|
74698
|
+
if (diff !== 0)
|
|
74699
|
+
return diff;
|
|
74700
|
+
}
|
|
74701
|
+
return 0;
|
|
74702
|
+
}
|
|
74515
74703
|
function getProxyBinary() {
|
|
74704
|
+
const systemBinary = commandExists("cli-proxy-api") ? "cli-proxy-api" : commandExists("cliproxyapi") ? "cliproxyapi" : null;
|
|
74516
74705
|
const vendored = getVendoredPath("cli-proxy-api");
|
|
74517
|
-
if (vendored)
|
|
74518
|
-
|
|
74519
|
-
|
|
74520
|
-
|
|
74521
|
-
|
|
74522
|
-
|
|
74523
|
-
|
|
74706
|
+
if (systemBinary && vendored) {
|
|
74707
|
+
const sysVersion = getBinaryVersion(systemBinary);
|
|
74708
|
+
const vendoredVersion = getBinaryVersion(vendored);
|
|
74709
|
+
if (sysVersion && vendoredVersion) {
|
|
74710
|
+
if (compareVersions(sysVersion, vendoredVersion) >= 0)
|
|
74711
|
+
return systemBinary;
|
|
74712
|
+
logBackgroundError("CLIProxyAPI", `system binary v${sysVersion} is older than vendored v${vendoredVersion} \u2014 using vendored`);
|
|
74713
|
+
return vendored;
|
|
74714
|
+
}
|
|
74715
|
+
return systemBinary;
|
|
74716
|
+
}
|
|
74717
|
+
return systemBinary ?? vendored;
|
|
74718
|
+
}
|
|
74719
|
+
function portIsOccupied() {
|
|
74720
|
+
const portMatch = PROXY_URL.match(/:([0-9]+)/);
|
|
74721
|
+
if (!portMatch)
|
|
74722
|
+
return false;
|
|
74723
|
+
const port = portMatch[1];
|
|
74724
|
+
try {
|
|
74725
|
+
const out = execFileSync("lsof", ["-ti", `tcp:${port}`], {
|
|
74726
|
+
encoding: "utf-8",
|
|
74727
|
+
timeout: 3000,
|
|
74728
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
74729
|
+
}).trim();
|
|
74730
|
+
return out.length > 0;
|
|
74731
|
+
} catch {
|
|
74732
|
+
return false;
|
|
74733
|
+
}
|
|
74524
74734
|
}
|
|
74525
|
-
async function healthCheck() {
|
|
74735
|
+
async function healthCheck(key) {
|
|
74526
74736
|
try {
|
|
74527
74737
|
const controller = new AbortController;
|
|
74528
74738
|
const timeout = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
74529
74739
|
const res = await fetch(`${PROXY_URL}/models`, {
|
|
74530
74740
|
signal: controller.signal,
|
|
74531
74741
|
headers: {
|
|
74532
|
-
Authorization: `Bearer ${
|
|
74742
|
+
Authorization: `Bearer ${key}`
|
|
74533
74743
|
}
|
|
74534
74744
|
});
|
|
74535
74745
|
clearTimeout(timeout);
|
|
@@ -74542,6 +74752,33 @@ async function healthCheck() {
|
|
|
74542
74752
|
return "unreachable";
|
|
74543
74753
|
}
|
|
74544
74754
|
}
|
|
74755
|
+
async function probeForWorkingKey() {
|
|
74756
|
+
let sawAuthRequired = false;
|
|
74757
|
+
let sawUnreachable = false;
|
|
74758
|
+
for (const candidate of candidateApiKeys()) {
|
|
74759
|
+
const r = await healthCheck(candidate);
|
|
74760
|
+
if (r === "ok")
|
|
74761
|
+
return {
|
|
74762
|
+
key: candidate,
|
|
74763
|
+
state: "ok"
|
|
74764
|
+
};
|
|
74765
|
+
if (r === "auth-required")
|
|
74766
|
+
sawAuthRequired = true;
|
|
74767
|
+
if (r === "unreachable")
|
|
74768
|
+
sawUnreachable = true;
|
|
74769
|
+
}
|
|
74770
|
+
if (sawAuthRequired)
|
|
74771
|
+
return {
|
|
74772
|
+
state: "auth-required"
|
|
74773
|
+
};
|
|
74774
|
+
if (sawUnreachable)
|
|
74775
|
+
return {
|
|
74776
|
+
state: "unreachable"
|
|
74777
|
+
};
|
|
74778
|
+
return {
|
|
74779
|
+
state: "unreachable"
|
|
74780
|
+
};
|
|
74781
|
+
}
|
|
74545
74782
|
async function ensureProxy() {
|
|
74546
74783
|
if (currentState === "starting") {
|
|
74547
74784
|
return {
|
|
@@ -74549,32 +74786,43 @@ async function ensureProxy() {
|
|
|
74549
74786
|
error: "Proxy is already starting"
|
|
74550
74787
|
};
|
|
74551
74788
|
}
|
|
74552
|
-
const
|
|
74553
|
-
|
|
74554
|
-
|
|
74555
|
-
stopProxy();
|
|
74556
|
-
killProxyOnPort();
|
|
74557
|
-
}
|
|
74558
|
-
const health = await healthCheck();
|
|
74559
|
-
if (health === "ok" && !needsUpgrade) {
|
|
74789
|
+
const probe = await probeForWorkingKey();
|
|
74790
|
+
if (probe.state === "ok") {
|
|
74791
|
+
setActiveProxyApiKey(probe.key);
|
|
74560
74792
|
setState("running");
|
|
74561
74793
|
return {
|
|
74562
74794
|
ok: true
|
|
74563
74795
|
};
|
|
74564
74796
|
}
|
|
74565
|
-
if (
|
|
74797
|
+
if (probe.state === "auth-required") {
|
|
74798
|
+
const cfg = primaryConfigPath();
|
|
74799
|
+
const discovered = discoverApiKeys();
|
|
74800
|
+
if (cfg && discovered.length === 0) {
|
|
74801
|
+
const msg = `Proxy rejected every candidate API key. Edit ${cfg} (replace placeholder in \`api-keys:\`) or set PROXY_API_KEY, then restart the proxy.`;
|
|
74802
|
+
setState("needs-auth", msg);
|
|
74803
|
+
return {
|
|
74804
|
+
ok: false,
|
|
74805
|
+
error: msg
|
|
74806
|
+
};
|
|
74807
|
+
}
|
|
74566
74808
|
setState("needs-auth", "Authentication required \u2014 run /proxy login");
|
|
74567
74809
|
return {
|
|
74568
74810
|
ok: false,
|
|
74569
74811
|
error: "Authentication required \u2014 run /proxy login"
|
|
74570
74812
|
};
|
|
74571
74813
|
}
|
|
74814
|
+
if (portIsOccupied()) {
|
|
74815
|
+
logBackgroundError("CLIProxyAPI", "orphan process on port \u2014 clearing");
|
|
74816
|
+
killProxyOnPort();
|
|
74817
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
74818
|
+
}
|
|
74572
74819
|
setState("starting");
|
|
74573
74820
|
let binary = getProxyBinary();
|
|
74574
74821
|
if (!binary) {
|
|
74575
74822
|
try {
|
|
74576
|
-
|
|
74577
|
-
|
|
74823
|
+
const installed = await installProxy();
|
|
74824
|
+
binary = installed.path;
|
|
74825
|
+
saveInstalledProxyVersion(installed.version);
|
|
74578
74826
|
} catch (err) {
|
|
74579
74827
|
const msg = toErrorMessage(err);
|
|
74580
74828
|
setState("error", `Failed to install CLIProxyAPI: ${msg}`);
|
|
@@ -74620,7 +74868,7 @@ async function ensureProxy() {
|
|
|
74620
74868
|
}
|
|
74621
74869
|
for (let i = 0;i < STARTUP_POLL_ATTEMPTS; i++) {
|
|
74622
74870
|
await new Promise((r) => setTimeout(r, STARTUP_POLL_MS));
|
|
74623
|
-
const status = await healthCheck();
|
|
74871
|
+
const status = await healthCheck(getActiveProxyApiKey());
|
|
74624
74872
|
if (status === "ok") {
|
|
74625
74873
|
setState("running");
|
|
74626
74874
|
return {
|
|
@@ -74657,7 +74905,7 @@ function stopProxy() {
|
|
|
74657
74905
|
killProxyOnPort();
|
|
74658
74906
|
setState("stopped");
|
|
74659
74907
|
}
|
|
74660
|
-
function killProxyOnPort() {
|
|
74908
|
+
function killProxyOnPort(force = false) {
|
|
74661
74909
|
const portMatch = PROXY_URL.match(/:([0-9]+)/);
|
|
74662
74910
|
if (!portMatch)
|
|
74663
74911
|
return;
|
|
@@ -74666,13 +74914,15 @@ function killProxyOnPort() {
|
|
|
74666
74914
|
try {
|
|
74667
74915
|
out = execFileSync("lsof", ["-ti", `tcp:${port}`], {
|
|
74668
74916
|
encoding: "utf-8",
|
|
74669
|
-
timeout: 3000
|
|
74917
|
+
timeout: 3000,
|
|
74918
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
74670
74919
|
}).trim();
|
|
74671
74920
|
} catch {
|
|
74672
74921
|
try {
|
|
74673
74922
|
out = execFileSync("fuser", [`${port}/tcp`], {
|
|
74674
74923
|
encoding: "utf-8",
|
|
74675
|
-
timeout: 3000
|
|
74924
|
+
timeout: 3000,
|
|
74925
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
74676
74926
|
}).trim();
|
|
74677
74927
|
} catch {
|
|
74678
74928
|
return;
|
|
@@ -74680,46 +74930,43 @@ function killProxyOnPort() {
|
|
|
74680
74930
|
}
|
|
74681
74931
|
if (!out)
|
|
74682
74932
|
return;
|
|
74933
|
+
const signal = force ? "SIGKILL" : "SIGTERM";
|
|
74683
74934
|
for (const token of out.split(/[\s\n]+/)) {
|
|
74684
74935
|
const pid = Number.parseInt(token.trim(), 10);
|
|
74685
74936
|
if (pid > 0 && pid !== process.pid) {
|
|
74686
74937
|
try {
|
|
74687
|
-
process.kill(pid,
|
|
74938
|
+
process.kill(pid, signal);
|
|
74688
74939
|
} catch {}
|
|
74689
74940
|
}
|
|
74690
74941
|
}
|
|
74691
74942
|
}
|
|
74692
|
-
var proxyProcess = null, PROXY_URL,
|
|
74943
|
+
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
74944
|
var init_lifecycle = __esm(() => {
|
|
74694
74945
|
init_errors4();
|
|
74695
74946
|
init_process_tracker();
|
|
74696
74947
|
init_install();
|
|
74948
|
+
init_key_resolver();
|
|
74697
74949
|
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");
|
|
74950
|
+
PROXY_CONFIG_DIR = join9(homedir7(), ".soulforge", "proxy");
|
|
74951
|
+
PROXY_CONFIG_PATH = join9(PROXY_CONFIG_DIR, "config.yaml");
|
|
74701
74952
|
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");
|
|
74953
|
+
VERSION_FILE = join9(PROXY_CONFIG_DIR, "version");
|
|
74954
|
+
AUTH_DIR = join9(homedir7(), ".cli-proxy-api");
|
|
74708
74955
|
VERSION_CACHE_TTL = 10 * 60 * 1000;
|
|
74709
74956
|
});
|
|
74710
74957
|
|
|
74711
74958
|
// src/core/llm/providers/proxy.ts
|
|
74712
|
-
function
|
|
74959
|
+
function isAnthropicModel(modelId) {
|
|
74713
74960
|
return modelId.toLowerCase().startsWith("claude");
|
|
74714
74961
|
}
|
|
74715
|
-
var baseURL,
|
|
74962
|
+
var baseURL, proxy;
|
|
74716
74963
|
var init_proxy = __esm(() => {
|
|
74717
74964
|
init_dist6();
|
|
74718
74965
|
init_dist8();
|
|
74966
|
+
init_key_resolver();
|
|
74719
74967
|
init_lifecycle();
|
|
74720
74968
|
init_context_windows();
|
|
74721
74969
|
baseURL = process.env.PROXY_API_URL || "http://127.0.0.1:8317/v1";
|
|
74722
|
-
apiKey = process.env.PROXY_API_KEY || "soulforge";
|
|
74723
74970
|
proxy = {
|
|
74724
74971
|
id: "proxy",
|
|
74725
74972
|
name: "Proxy",
|
|
@@ -74728,7 +74975,8 @@ var init_proxy = __esm(() => {
|
|
|
74728
74975
|
asciiIcon: "\u26E8",
|
|
74729
74976
|
grouped: true,
|
|
74730
74977
|
createModel(modelId) {
|
|
74731
|
-
|
|
74978
|
+
const apiKey = getActiveProxyApiKey();
|
|
74979
|
+
if (isAnthropicModel(modelId)) {
|
|
74732
74980
|
return createAnthropic({
|
|
74733
74981
|
baseURL,
|
|
74734
74982
|
apiKey
|
|
@@ -74838,8 +75086,8 @@ var init_vercel_gateway = __esm(() => {
|
|
|
74838
75086
|
description: "Vercel AI Gateway",
|
|
74839
75087
|
grouped: true,
|
|
74840
75088
|
createModel(modelId) {
|
|
74841
|
-
const
|
|
74842
|
-
if (!
|
|
75089
|
+
const apiKey = getProviderApiKey("AI_GATEWAY_API_KEY");
|
|
75090
|
+
if (!apiKey) {
|
|
74843
75091
|
throw new Error("AI_GATEWAY_API_KEY is not set");
|
|
74844
75092
|
}
|
|
74845
75093
|
return gateway(modelId);
|
|
@@ -77807,21 +78055,21 @@ var init_xai = __esm(() => {
|
|
|
77807
78055
|
asciiIcon: "X",
|
|
77808
78056
|
description: "Grok models",
|
|
77809
78057
|
createModel(modelId) {
|
|
77810
|
-
const
|
|
77811
|
-
if (!
|
|
78058
|
+
const apiKey = getProviderApiKey("XAI_API_KEY");
|
|
78059
|
+
if (!apiKey) {
|
|
77812
78060
|
throw new Error("XAI_API_KEY is not set");
|
|
77813
78061
|
}
|
|
77814
78062
|
return createXai({
|
|
77815
|
-
apiKey
|
|
78063
|
+
apiKey
|
|
77816
78064
|
})(modelId);
|
|
77817
78065
|
},
|
|
77818
78066
|
async fetchModels() {
|
|
77819
|
-
const
|
|
77820
|
-
if (!
|
|
78067
|
+
const apiKey = getProviderApiKey("XAI_API_KEY");
|
|
78068
|
+
if (!apiKey)
|
|
77821
78069
|
return null;
|
|
77822
78070
|
const res = await fetch("https://api.x.ai/v1/models", {
|
|
77823
78071
|
headers: {
|
|
77824
|
-
Authorization: `Bearer ${
|
|
78072
|
+
Authorization: `Bearer ${apiKey}`
|
|
77825
78073
|
}
|
|
77826
78074
|
});
|
|
77827
78075
|
if (!res.ok)
|
|
@@ -78120,9 +78368,9 @@ var init_summarize = __esm(() => {
|
|
|
78120
78368
|
});
|
|
78121
78369
|
|
|
78122
78370
|
// src/core/workers/io.worker.ts
|
|
78123
|
-
import { existsSync as
|
|
78371
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync } from "fs";
|
|
78124
78372
|
import { readFile, rename, writeFile as writeFile2 } from "fs/promises";
|
|
78125
|
-
import { extname, join as
|
|
78373
|
+
import { extname, join as join10 } from "path";
|
|
78126
78374
|
|
|
78127
78375
|
// node_modules/isbinaryfile/lib/index.js
|
|
78128
78376
|
import { open, stat } from "fs/promises";
|
|
@@ -78496,7 +78744,7 @@ var handlers = {
|
|
|
78496
78744
|
const dir = sessionDir;
|
|
78497
78745
|
const sessionMeta = meta3;
|
|
78498
78746
|
const entries = tabEntries;
|
|
78499
|
-
if (!
|
|
78747
|
+
if (!existsSync5(dir)) {
|
|
78500
78748
|
mkdirSync4(dir, {
|
|
78501
78749
|
recursive: true,
|
|
78502
78750
|
mode: 448
|
|
@@ -78524,8 +78772,8 @@ var handlers = {
|
|
|
78524
78772
|
const metaJson = JSON.stringify(updatedMeta, null, 2);
|
|
78525
78773
|
const lines = allMessages.map((m) => JSON.stringify(m)).join(`
|
|
78526
78774
|
`);
|
|
78527
|
-
const metaPath =
|
|
78528
|
-
const jsonlPath =
|
|
78775
|
+
const metaPath = join10(dir, "meta.json");
|
|
78776
|
+
const jsonlPath = join10(dir, "messages.jsonl");
|
|
78529
78777
|
const suffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
78530
78778
|
const metaTmp = `${metaPath}.${suffix}.tmp`;
|
|
78531
78779
|
const jsonlTmp = `${jsonlPath}.${suffix}.tmp`;
|
|
@@ -78546,7 +78794,7 @@ var handlers = {
|
|
|
78546
78794
|
for (const [tabId, msgs] of cores) {
|
|
78547
78795
|
coreData[tabId] = msgs;
|
|
78548
78796
|
}
|
|
78549
|
-
const corePath =
|
|
78797
|
+
const corePath = join10(dir, "core.json");
|
|
78550
78798
|
const coreTmp = `${corePath}.${suffix}.tmp`;
|
|
78551
78799
|
await writeFile2(coreTmp, JSON.stringify(coreData), {
|
|
78552
78800
|
encoding: "utf-8",
|
|
@@ -78557,14 +78805,14 @@ var handlers = {
|
|
|
78557
78805
|
},
|
|
78558
78806
|
loadSession: async (sessionDir) => {
|
|
78559
78807
|
const dir = sessionDir;
|
|
78560
|
-
const metaPath =
|
|
78561
|
-
if (!
|
|
78808
|
+
const metaPath = join10(dir, "meta.json");
|
|
78809
|
+
if (!existsSync5(metaPath))
|
|
78562
78810
|
return null;
|
|
78563
|
-
const meta3 = JSON.parse(
|
|
78564
|
-
const jsonlPath =
|
|
78811
|
+
const meta3 = JSON.parse(readFileSync4(metaPath, "utf-8"));
|
|
78812
|
+
const jsonlPath = join10(dir, "messages.jsonl");
|
|
78565
78813
|
const allMessages = [];
|
|
78566
|
-
if (
|
|
78567
|
-
const content =
|
|
78814
|
+
if (existsSync5(jsonlPath)) {
|
|
78815
|
+
const content = readFileSync4(jsonlPath, "utf-8").trim();
|
|
78568
78816
|
if (content) {
|
|
78569
78817
|
for (const line of content.split(`
|
|
78570
78818
|
`)) {
|
|
@@ -78586,11 +78834,11 @@ var handlers = {
|
|
|
78586
78834
|
} = tab.messageRange;
|
|
78587
78835
|
tabEntries.push([tab.id, allMessages.slice(startLine, endLine)]);
|
|
78588
78836
|
}
|
|
78589
|
-
const corePath =
|
|
78837
|
+
const corePath = join10(dir, "core.json");
|
|
78590
78838
|
let coreEntries;
|
|
78591
|
-
if (
|
|
78839
|
+
if (existsSync5(corePath)) {
|
|
78592
78840
|
try {
|
|
78593
|
-
const coreData = JSON.parse(
|
|
78841
|
+
const coreData = JSON.parse(readFileSync4(corePath, "utf-8"));
|
|
78594
78842
|
coreEntries = Object.entries(coreData);
|
|
78595
78843
|
} catch {}
|
|
78596
78844
|
}
|
|
@@ -78602,27 +78850,27 @@ var handlers = {
|
|
|
78602
78850
|
},
|
|
78603
78851
|
listSessions: (sessionsDir) => {
|
|
78604
78852
|
const dir = sessionsDir;
|
|
78605
|
-
if (!
|
|
78853
|
+
if (!existsSync5(dir))
|
|
78606
78854
|
return [];
|
|
78607
78855
|
try {
|
|
78608
78856
|
const entries = readdirSync4(dir);
|
|
78609
78857
|
const metas = [];
|
|
78610
78858
|
for (const entry of entries) {
|
|
78611
78859
|
try {
|
|
78612
|
-
const fullPath =
|
|
78860
|
+
const fullPath = join10(dir, entry);
|
|
78613
78861
|
const s = statSync(fullPath);
|
|
78614
78862
|
if (!s.isDirectory())
|
|
78615
78863
|
continue;
|
|
78616
|
-
const metaPath =
|
|
78617
|
-
if (!
|
|
78864
|
+
const metaPath = join10(fullPath, "meta.json");
|
|
78865
|
+
if (!existsSync5(metaPath))
|
|
78618
78866
|
continue;
|
|
78619
|
-
const raw =
|
|
78867
|
+
const raw = readFileSync4(metaPath, "utf-8");
|
|
78620
78868
|
const meta3 = JSON.parse(raw);
|
|
78621
78869
|
const totalMessages = (meta3.tabs ?? []).reduce((sum, t) => sum + (t.messageRange.endLine - t.messageRange.startLine), 0);
|
|
78622
78870
|
let sizeBytes = 0;
|
|
78623
78871
|
for (const file2 of ["meta.json", "messages.jsonl"]) {
|
|
78624
78872
|
try {
|
|
78625
|
-
sizeBytes += statSync(
|
|
78873
|
+
sizeBytes += statSync(join10(fullPath, file2)).size;
|
|
78626
78874
|
} catch {}
|
|
78627
78875
|
}
|
|
78628
78876
|
metas.push({
|