omnexx 0.11.3 → 0.12.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/dist/{chat-context-PTZRLJEV.js → chat-context-SP5XSAF7.js} +7 -6
- package/dist/{chat-context-PTZRLJEV.js.map → chat-context-SP5XSAF7.js.map} +1 -1
- package/dist/chat-history-SMI5AWQG.js +21 -0
- package/dist/{chat-server-HFGPZ7RU.js → chat-server-NKLX3WTV.js} +56 -14
- package/dist/chat-server-NKLX3WTV.js.map +1 -0
- package/dist/{chat-settings-X7R4JKI7.js → chat-settings-K5643AXQ.js} +14 -13
- package/dist/{chat-settings-X7R4JKI7.js.map → chat-settings-K5643AXQ.js.map} +1 -1
- package/dist/{chunk-YAWRCULP.js → chunk-4C3ZA5OG.js} +11 -8
- package/dist/chunk-4C3ZA5OG.js.map +1 -0
- package/dist/{chunk-LRBHQHO6.js → chunk-5XMZIGYS.js} +37 -8
- package/dist/chunk-5XMZIGYS.js.map +1 -0
- package/dist/{chunk-6U3WB5AL.js → chunk-C2RB5YRZ.js} +63 -22
- package/dist/chunk-C2RB5YRZ.js.map +1 -0
- package/dist/{chunk-UAW5RG53.js → chunk-DMJLZWAR.js} +32 -75
- package/dist/chunk-DMJLZWAR.js.map +1 -0
- package/dist/chunk-FR5DUUJ4.js +56 -0
- package/dist/chunk-FR5DUUJ4.js.map +1 -0
- package/dist/{chunk-FLIPPDUD.js → chunk-HWL2XISD.js} +62 -26
- package/dist/chunk-HWL2XISD.js.map +1 -0
- package/dist/{chunk-AXEJCZPA.js → chunk-MHH66U5Y.js} +10 -10
- package/dist/{chunk-4DX4BPIF.js → chunk-P6HRCAYP.js} +2 -2
- package/dist/{chunk-4DX4BPIF.js.map → chunk-P6HRCAYP.js.map} +1 -1
- package/dist/{chunk-E6NF2XSG.js → chunk-Q2B5FUSA.js} +7 -7
- package/dist/{chunk-IGUI6IJP.js → chunk-RYK3WVH4.js} +56 -23
- package/dist/chunk-RYK3WVH4.js.map +1 -0
- package/dist/{chunk-LZGGM2YZ.js → chunk-SFNARMV4.js} +8 -8
- package/dist/{chunk-3Y7NVLVI.js → chunk-UACCTB6B.js} +359 -22
- package/dist/chunk-UACCTB6B.js.map +1 -0
- package/dist/chunk-WXMTUSCS.js +88 -0
- package/dist/chunk-WXMTUSCS.js.map +1 -0
- package/dist/cli.js +86 -62
- package/dist/cli.js.map +1 -1
- package/dist/{list-4L6TDLAZ.js → list-TZWF7XUG.js} +2 -2
- package/dist/{model-picker-JKB7LVQR.js → model-picker-G6EVIQP7.js} +8 -7
- package/dist/{ollama-G2MYWBEE.js → ollama-2BG63S2L.js} +7 -6
- package/dist/{ollama-G2MYWBEE.js.map → ollama-2BG63S2L.js.map} +1 -1
- package/dist/{print-AMD3ZAUQ.js → print-HSFNJOYV.js} +10 -8
- package/dist/{print-AMD3ZAUQ.js.map → print-HSFNJOYV.js.map} +1 -1
- package/dist/{session-OORM43WG.js → session-MNF52NG4.js} +16 -14
- package/dist/session-MNF52NG4.js.map +1 -0
- package/dist/{start-LXX7MA37.js → start-MFBIM4KW.js} +22 -16
- package/dist/start-MFBIM4KW.js.map +1 -0
- package/package.json +1 -1
- package/dist/chat-server-HFGPZ7RU.js.map +0 -1
- package/dist/chunk-3Y7NVLVI.js.map +0 -1
- package/dist/chunk-6U3WB5AL.js.map +0 -1
- package/dist/chunk-FLIPPDUD.js.map +0 -1
- package/dist/chunk-IGUI6IJP.js.map +0 -1
- package/dist/chunk-LRBHQHO6.js.map +0 -1
- package/dist/chunk-UAW5RG53.js.map +0 -1
- package/dist/chunk-YAWRCULP.js.map +0 -1
- package/dist/start-LXX7MA37.js.map +0 -1
- /package/dist/{list-4L6TDLAZ.js.map → chat-history-SMI5AWQG.js.map} +0 -0
- /package/dist/{chunk-AXEJCZPA.js.map → chunk-MHH66U5Y.js.map} +0 -0
- /package/dist/{chunk-E6NF2XSG.js.map → chunk-Q2B5FUSA.js.map} +0 -0
- /package/dist/{chunk-LZGGM2YZ.js.map → chunk-SFNARMV4.js.map} +0 -0
- /package/dist/{model-picker-JKB7LVQR.js.map → list-TZWF7XUG.js.map} +0 -0
- /package/dist/{session-OORM43WG.js.map → model-picker-G6EVIQP7.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
listRunIds,
|
|
5
5
|
repoMemoryFile,
|
|
6
6
|
repoRoot
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5XMZIGYS.js";
|
|
8
8
|
import {
|
|
9
9
|
SECTION_IDS,
|
|
10
10
|
SECTION_INFO,
|
|
@@ -13,14 +13,15 @@ import {
|
|
|
13
13
|
loadConfig,
|
|
14
14
|
readSectionModes,
|
|
15
15
|
sectionsFile
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
resolvePaths
|
|
19
|
-
} from "./chunk-Q3MDYD6K.js";
|
|
16
|
+
} from "./chunk-DMJLZWAR.js";
|
|
20
17
|
import "./chunk-27GOEUTN.js";
|
|
21
18
|
import {
|
|
22
19
|
UsageError
|
|
23
20
|
} from "./chunk-7PNGHZYH.js";
|
|
21
|
+
import "./chunk-FR5DUUJ4.js";
|
|
22
|
+
import {
|
|
23
|
+
resolvePaths
|
|
24
|
+
} from "./chunk-Q3MDYD6K.js";
|
|
24
25
|
import "./chunk-2H7UOFLK.js";
|
|
25
26
|
|
|
26
27
|
// src/cli/chat-context.ts
|
|
@@ -117,4 +118,4 @@ export {
|
|
|
117
118
|
contextFiles,
|
|
118
119
|
setSectionMode
|
|
119
120
|
};
|
|
120
|
-
//# sourceMappingURL=chat-context-
|
|
121
|
+
//# sourceMappingURL=chat-context-SP5XSAF7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/chat-context.ts"],"sourcesContent":["import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport {\n SECTION_IDS,\n SECTION_INFO,\n SECTION_MODES,\n type SectionId,\n type SectionMode,\n} from '../agent/sections.js';\nimport { loadConfig } from '../config/load.js';\nimport { readSectionModes, sectionsFile } from '../config/sections-file.js';\nimport { resolvePaths, type OmnexxPaths } from '../core/paths.js';\nimport { repoMemoryFile } from '../core/repo-memory.js';\nimport { listRunIds, RunStore } from '../core/run-store.js';\nimport { estimateTokens } from '../core/tokens.js';\nimport { UsageError } from '../errors.js';\nimport { repoRoot } from '../git/repo.js';\nimport type { CliIO } from './io.js';\n\n/**\n * The Context explorer's data: every file that feeds the agent's context in this workspace, its\n * size in tokens, whether it can be edited, and (for sections) how it reaches the model. Section\n * modes are saved to context-sections.json and loaded as a config layer.\n */\n\nexport interface ContextFile {\n id: string;\n label: string;\n about: string;\n path: string;\n tokens: number;\n exists: boolean;\n /** Edits are read on the next cycle; generated files are views. */\n editable: boolean;\n /** For context sections: how it reaches the model. */\n section?: SectionId;\n mode?: SectionMode;\n}\n\nexport async function setSectionMode(io: CliIO, id: string, mode: string): Promise<void> {\n if (!(SECTION_IDS as readonly string[]).includes(id)) throw new UsageError(`no section \"${id}\"`);\n if (!(SECTION_MODES as readonly string[]).includes(mode))\n throw new UsageError(`mode is one of ${SECTION_MODES.join(', ')}`);\n const p = resolvePaths(io.env);\n const modes = { ...(await readSectionModes(p)), [id]: mode };\n await mkdir(dirname(sectionsFile(p)), { recursive: true });\n await writeFile(sectionsFile(p), `${JSON.stringify(modes, null, 2)}\\n`);\n}\n\nasync function sizeOf(path: string): Promise<{ exists: boolean; tokens: number }> {\n try {\n if (!(await stat(path)).isFile()) return { exists: false, tokens: 0 };\n return { exists: true, tokens: estimateTokens(await readFile(path, 'utf8')) };\n } catch {\n return { exists: false, tokens: 0 };\n }\n}\n\n/** The newest run started from this repository, if any. */\nasync function latestRun(paths: OmnexxPaths, root: string): Promise<RunStore | undefined> {\n for (const id of [...(await listRunIds(paths))].reverse()) {\n const store = new RunStore(paths, id);\n const s = await store.readState().catch(() => undefined);\n if (s?.repoRoot === root) return store;\n }\n return undefined;\n}\n\nexport async function contextFiles(\n io: CliIO,\n): Promise<{ runId: string | null; files: ContextFile[] }> {\n const paths = resolvePaths(io.env);\n const root = await repoRoot(io.cwd).catch(() => io.cwd);\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const modes = config.context.sections;\n const run = await latestRun(paths, root);\n const state = run ? await run.readState().catch(() => undefined) : undefined;\n const memoryDir = dirname(await repoMemoryFile({ home: paths.home }, root));\n\n const entries: Omit<ContextFile, 'tokens' | 'exists'>[] = [];\n const section = (id: SectionId, path: string, editable: boolean): void => {\n const info = SECTION_INFO[id];\n entries.push({\n id,\n label: info.label,\n about: info.about,\n path,\n editable,\n section: id,\n mode: modes[id],\n });\n };\n if (run) {\n if (state?.taskId) section('role', run.file(join('roles', `${state.taskId}.md`)), true);\n section('codemap', run.file('codemap.md'), false);\n section('intent', run.file('intent.md'), true);\n section('lessons', run.file('notes.md'), false);\n section('progress', run.file('progress.md'), false);\n entries.push({\n id: 'goal',\n label: 'Goal',\n about: 'what this run is for',\n path: run.file('goal.md'),\n editable: true,\n });\n }\n entries.push(\n {\n id: 'repo-role',\n label: 'Repo role note',\n about: 'added to every role card in this repo',\n path: join(root, '.omnexx', 'role.md'),\n editable: true,\n },\n {\n id: 'general-notes',\n label: 'General notes',\n about: 'what the agent keeps about this repo across runs',\n path: join(memoryDir, 'scopes', 'general.md'),\n editable: true,\n },\n {\n id: 'repo-notes',\n label: 'Repo notes',\n about: 'architecture and where things live, across runs',\n path: join(memoryDir, 'scopes', 'repo.md'),\n editable: true,\n },\n );\n const files = await Promise.all(entries.map(async (e) => ({ ...e, ...(await sizeOf(e.path)) })));\n return { runId: run?.runId ?? null, files };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/cli/chat-context.ts"],"sourcesContent":["import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport {\n SECTION_IDS,\n SECTION_INFO,\n SECTION_MODES,\n type SectionId,\n type SectionMode,\n} from '../agent/sections.js';\nimport { loadConfig } from '../config/load.js';\nimport { readSectionModes, sectionsFile } from '../config/sections-file.js';\nimport { resolvePaths, type OmnexxPaths } from '../core/paths.js';\nimport { repoMemoryFile } from '../core/repo-memory.js';\nimport { listRunIds, RunStore } from '../core/run-store.js';\nimport { estimateTokens } from '../core/tokens.js';\nimport { UsageError } from '../errors.js';\nimport { repoRoot } from '../git/repo.js';\nimport type { CliIO } from './io.js';\n\n/**\n * The Context explorer's data: every file that feeds the agent's context in this workspace, its\n * size in tokens, whether it can be edited, and (for sections) how it reaches the model. Section\n * modes are saved to context-sections.json and loaded as a config layer.\n */\n\nexport interface ContextFile {\n id: string;\n label: string;\n about: string;\n path: string;\n tokens: number;\n exists: boolean;\n /** Edits are read on the next cycle; generated files are views. */\n editable: boolean;\n /** For context sections: how it reaches the model. */\n section?: SectionId;\n mode?: SectionMode;\n}\n\nexport async function setSectionMode(io: CliIO, id: string, mode: string): Promise<void> {\n if (!(SECTION_IDS as readonly string[]).includes(id)) throw new UsageError(`no section \"${id}\"`);\n if (!(SECTION_MODES as readonly string[]).includes(mode))\n throw new UsageError(`mode is one of ${SECTION_MODES.join(', ')}`);\n const p = resolvePaths(io.env);\n const modes = { ...(await readSectionModes(p)), [id]: mode };\n await mkdir(dirname(sectionsFile(p)), { recursive: true });\n await writeFile(sectionsFile(p), `${JSON.stringify(modes, null, 2)}\\n`);\n}\n\nasync function sizeOf(path: string): Promise<{ exists: boolean; tokens: number }> {\n try {\n if (!(await stat(path)).isFile()) return { exists: false, tokens: 0 };\n return { exists: true, tokens: estimateTokens(await readFile(path, 'utf8')) };\n } catch {\n return { exists: false, tokens: 0 };\n }\n}\n\n/** The newest run started from this repository, if any. */\nasync function latestRun(paths: OmnexxPaths, root: string): Promise<RunStore | undefined> {\n for (const id of [...(await listRunIds(paths))].reverse()) {\n const store = new RunStore(paths, id);\n const s = await store.readState().catch(() => undefined);\n if (s?.repoRoot === root) return store;\n }\n return undefined;\n}\n\nexport async function contextFiles(\n io: CliIO,\n): Promise<{ runId: string | null; files: ContextFile[] }> {\n const paths = resolvePaths(io.env);\n const root = await repoRoot(io.cwd).catch(() => io.cwd);\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const modes = config.context.sections;\n const run = await latestRun(paths, root);\n const state = run ? await run.readState().catch(() => undefined) : undefined;\n const memoryDir = dirname(await repoMemoryFile({ home: paths.home }, root));\n\n const entries: Omit<ContextFile, 'tokens' | 'exists'>[] = [];\n const section = (id: SectionId, path: string, editable: boolean): void => {\n const info = SECTION_INFO[id];\n entries.push({\n id,\n label: info.label,\n about: info.about,\n path,\n editable,\n section: id,\n mode: modes[id],\n });\n };\n if (run) {\n if (state?.taskId) section('role', run.file(join('roles', `${state.taskId}.md`)), true);\n section('codemap', run.file('codemap.md'), false);\n section('intent', run.file('intent.md'), true);\n section('lessons', run.file('notes.md'), false);\n section('progress', run.file('progress.md'), false);\n entries.push({\n id: 'goal',\n label: 'Goal',\n about: 'what this run is for',\n path: run.file('goal.md'),\n editable: true,\n });\n }\n entries.push(\n {\n id: 'repo-role',\n label: 'Repo role note',\n about: 'added to every role card in this repo',\n path: join(root, '.omnexx', 'role.md'),\n editable: true,\n },\n {\n id: 'general-notes',\n label: 'General notes',\n about: 'what the agent keeps about this repo across runs',\n path: join(memoryDir, 'scopes', 'general.md'),\n editable: true,\n },\n {\n id: 'repo-notes',\n label: 'Repo notes',\n about: 'architecture and where things live, across runs',\n path: join(memoryDir, 'scopes', 'repo.md'),\n editable: true,\n },\n );\n const files = await Promise.all(entries.map(async (e) => ({ ...e, ...(await sizeOf(e.path)) })));\n return { runId: run?.runId ?? null, files };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CHAT_FILE,
|
|
4
|
+
chatTitle,
|
|
5
|
+
listChats,
|
|
6
|
+
readSavedChat,
|
|
7
|
+
transcriptEntries,
|
|
8
|
+
userMessageCount
|
|
9
|
+
} from "./chunk-WXMTUSCS.js";
|
|
10
|
+
import "./chunk-FR5DUUJ4.js";
|
|
11
|
+
import "./chunk-Q3MDYD6K.js";
|
|
12
|
+
import "./chunk-2H7UOFLK.js";
|
|
13
|
+
export {
|
|
14
|
+
CHAT_FILE,
|
|
15
|
+
chatTitle,
|
|
16
|
+
listChats,
|
|
17
|
+
readSavedChat,
|
|
18
|
+
transcriptEntries,
|
|
19
|
+
userMessageCount
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=chat-history-SMI5AWQG.js.map
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
println
|
|
4
4
|
} from "./chunk-7PC4SSYS.js";
|
|
5
|
-
import {
|
|
6
|
-
resolvePaths
|
|
7
|
-
} from "./chunk-Q3MDYD6K.js";
|
|
8
5
|
import {
|
|
9
6
|
describeError
|
|
10
7
|
} from "./chunk-7PNGHZYH.js";
|
|
8
|
+
import {
|
|
9
|
+
resolvePaths
|
|
10
|
+
} from "./chunk-Q3MDYD6K.js";
|
|
11
11
|
import "./chunk-2H7UOFLK.js";
|
|
12
12
|
|
|
13
13
|
// src/cli/chat-server.ts
|
|
@@ -33,8 +33,13 @@ function text(c, key) {
|
|
|
33
33
|
if (typeof v !== "string" || !v.trim()) throw new Error(`"${c.type}" needs a "${key}" string`);
|
|
34
34
|
return v;
|
|
35
35
|
}
|
|
36
|
+
function rawText(c, key) {
|
|
37
|
+
const v = c[key];
|
|
38
|
+
if (typeof v !== "string") throw new Error(`"${c.type}" needs a "${key}" string`);
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
36
41
|
async function serveChat(io, runCli, opts) {
|
|
37
|
-
const { Session } = await import("./session-
|
|
42
|
+
const { Session } = await import("./session-MNF52NG4.js");
|
|
38
43
|
const session = new Session(io, runCli);
|
|
39
44
|
const write = (msg) => {
|
|
40
45
|
println(io.stdout, JSON.stringify(msg));
|
|
@@ -89,6 +94,7 @@ async function serveChat(io, runCli, opts) {
|
|
|
89
94
|
} : null,
|
|
90
95
|
run: session.runId ? { id: session.runId, alive: session.runAlive, ...session.info } : null,
|
|
91
96
|
helpers: session.helpers,
|
|
97
|
+
waiting: session.waiting,
|
|
92
98
|
browser: session.browserFrame ? {
|
|
93
99
|
seq: session.browserFrame.seq,
|
|
94
100
|
url: session.browserFrame.url,
|
|
@@ -104,7 +110,7 @@ async function serveChat(io, runCli, opts) {
|
|
|
104
110
|
};
|
|
105
111
|
session.onChange(schedule);
|
|
106
112
|
const sendModels = async () => {
|
|
107
|
-
const { loadModelChoices } = await import("./model-picker-
|
|
113
|
+
const { loadModelChoices } = await import("./model-picker-G6EVIQP7.js");
|
|
108
114
|
const { items, unreachable } = await loadModelChoices(io);
|
|
109
115
|
write({
|
|
110
116
|
type: "models",
|
|
@@ -114,17 +120,20 @@ async function serveChat(io, runCli, opts) {
|
|
|
114
120
|
});
|
|
115
121
|
};
|
|
116
122
|
const sendSettings = async () => {
|
|
117
|
-
const { providerSettings } = await import("./chat-settings-
|
|
123
|
+
const { providerSettings } = await import("./chat-settings-K5643AXQ.js");
|
|
118
124
|
write({ type: "settings", ...await providerSettings(io) });
|
|
119
125
|
};
|
|
120
126
|
const sendBrowser = async () => {
|
|
121
|
-
const { browserSettings } = await import("./chat-settings-
|
|
127
|
+
const { browserSettings } = await import("./chat-settings-K5643AXQ.js");
|
|
122
128
|
write({ type: "browser", ...await browserSettings(io) });
|
|
123
129
|
};
|
|
124
130
|
const sendContextFiles = async () => {
|
|
125
|
-
const { contextFiles } = await import("./chat-context-
|
|
131
|
+
const { contextFiles } = await import("./chat-context-SP5XSAF7.js");
|
|
126
132
|
write({ type: "contextFiles", ...await contextFiles(io) });
|
|
127
133
|
};
|
|
134
|
+
const sendContext = () => {
|
|
135
|
+
write({ type: "contextItems", items: session.contextItems(), busy: session.chatBusy });
|
|
136
|
+
};
|
|
128
137
|
const sendRoles = async () => {
|
|
129
138
|
const { readRoles } = await import("./roles-IHKWWYJL.js");
|
|
130
139
|
const { QuotaLedger } = await import("./quota-ledger-7F6QTZAM.js");
|
|
@@ -198,7 +207,7 @@ async function serveChat(io, runCli, opts) {
|
|
|
198
207
|
return typeof v === "string" ? v : void 0;
|
|
199
208
|
};
|
|
200
209
|
run(
|
|
201
|
-
import("./chat-settings-
|
|
210
|
+
import("./chat-settings-K5643AXQ.js").then(async ({ addProvider }) => {
|
|
202
211
|
await providersChanged(
|
|
203
212
|
await addProvider(io, {
|
|
204
213
|
provider: str("provider"),
|
|
@@ -212,7 +221,7 @@ async function serveChat(io, runCli, opts) {
|
|
|
212
221
|
}
|
|
213
222
|
case "testProvider":
|
|
214
223
|
run(
|
|
215
|
-
import("./chat-settings-
|
|
224
|
+
import("./chat-settings-K5643AXQ.js").then(async ({ testProvider }) => {
|
|
216
225
|
write({ type: "providerTest", ...await testProvider(io, text(c, "name")) });
|
|
217
226
|
})
|
|
218
227
|
);
|
|
@@ -222,18 +231,51 @@ async function serveChat(io, runCli, opts) {
|
|
|
222
231
|
return;
|
|
223
232
|
case "setSectionMode":
|
|
224
233
|
run(
|
|
225
|
-
import("./chat-context-
|
|
234
|
+
import("./chat-context-SP5XSAF7.js").then(async ({ setSectionMode }) => {
|
|
226
235
|
await setSectionMode(io, text(c, "id"), text(c, "mode"));
|
|
227
236
|
await sendContextFiles();
|
|
228
237
|
})
|
|
229
238
|
);
|
|
230
239
|
return;
|
|
240
|
+
case "chats":
|
|
241
|
+
run(
|
|
242
|
+
session.savedChats().then((chats) => {
|
|
243
|
+
write({ type: "chats", chats });
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
return;
|
|
247
|
+
case "resumeChat":
|
|
248
|
+
run(
|
|
249
|
+
(async () => {
|
|
250
|
+
clearedAt = session.entries.at(-1)?.id ?? clearedAt;
|
|
251
|
+
sentUpTo = clearedAt;
|
|
252
|
+
await session.resumeChat(text(c, "id"));
|
|
253
|
+
write({ type: "ready", version: opts.version });
|
|
254
|
+
emit();
|
|
255
|
+
})()
|
|
256
|
+
);
|
|
257
|
+
return;
|
|
258
|
+
case "contextList":
|
|
259
|
+
sendContext();
|
|
260
|
+
return;
|
|
261
|
+
case "contextUpdate":
|
|
262
|
+
session.changeContext({ op: "edit", id: text(c, "id"), text: rawText(c, "text") });
|
|
263
|
+
sendContext();
|
|
264
|
+
return;
|
|
265
|
+
case "contextDelete":
|
|
266
|
+
session.changeContext({ op: "delete", id: text(c, "id") });
|
|
267
|
+
sendContext();
|
|
268
|
+
return;
|
|
269
|
+
case "contextReset":
|
|
270
|
+
session.changeContext({ op: "reset" });
|
|
271
|
+
sendContext();
|
|
272
|
+
return;
|
|
231
273
|
case "browser":
|
|
232
274
|
run(sendBrowser());
|
|
233
275
|
return;
|
|
234
276
|
case "setBrowser":
|
|
235
277
|
run(
|
|
236
|
-
import("./chat-settings-
|
|
278
|
+
import("./chat-settings-K5643AXQ.js").then(async ({ setBrowserSettings }) => {
|
|
237
279
|
await setBrowserSettings(io, c);
|
|
238
280
|
session.push("system", "browser settings saved; new runs use them");
|
|
239
281
|
await sendBrowser();
|
|
@@ -254,7 +296,7 @@ async function serveChat(io, runCli, opts) {
|
|
|
254
296
|
return;
|
|
255
297
|
case "removeProvider":
|
|
256
298
|
run(
|
|
257
|
-
import("./chat-settings-
|
|
299
|
+
import("./chat-settings-K5643AXQ.js").then(async ({ removeProvider }) => {
|
|
258
300
|
await providersChanged(await removeProvider(io, text(c, "name")));
|
|
259
301
|
})
|
|
260
302
|
);
|
|
@@ -299,4 +341,4 @@ async function serveChat(io, runCli, opts) {
|
|
|
299
341
|
export {
|
|
300
342
|
serveChat
|
|
301
343
|
};
|
|
302
|
-
//# sourceMappingURL=chat-server-
|
|
344
|
+
//# sourceMappingURL=chat-server-NKLX3WTV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/chat-server.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { resolvePaths } from '../core/paths.js';\nimport { readFile } from 'node:fs/promises';\nimport { createInterface } from 'node:readline';\nimport { describeError } from '../errors.js';\nimport type { Telemetry } from '../telemetry/aggregate.js';\nimport type { TodoItem } from '../tools/todo.js';\nimport type { Entry, Helper, Mode, RunCli } from '../tui/session.js';\nimport { println, type CliIO } from './io.js';\n\n/**\n * `omnexx chat-server`: the interactive session behind a JSON-lines protocol on stdin/stdout, for\n * editor panels (the VS Code chat). It drives the same Session as the terminal UI, so slash\n * commands, approvals, model switching and plan mode behave the same way.\n *\n * In (one JSON object per line): send {text} · answer {yes} · stop · pause · resume · mode {mode} ·\n * yolo {on} ·\n * model {ref} · models · connect {value, name?} · undo · clear · compact · sync (resend all) ·\n * settings · addProvider {provider?, key?, url?} · removeProvider {name} · testProvider {name} ·\n * roles · setRoles {roles} · contextFiles · setSectionMode {id, mode} · contextList ·\n * contextUpdate {id, text} · contextDelete {id} · contextReset · chats · resumeChat {id} · browser ·\n * setBrowser {engine, profile?, sessionName?, headless}.\n * Out: ready {version} · state (below; `entries` holds only lines not sent before) · models\n * {items, unreachable, current} · settings {catalog, connected} · providerTest {name, ok, models,\n * detail} · contextItems {items, busy} · chats {chats} · browser {engine, profile, sessionName, headless} ·\n * error {message}.\n */\n\nexport interface ChatUsage {\n /** When the chat's first event happened (epoch ms), for an uptime clock. */\n startedAt: number | null;\n tokens: Telemetry['tokens'];\n usd: number;\n turns: number;\n toolCalls: number;\n toolErrors: number;\n compactions: number;\n}\n\nexport interface ChatState {\n type: 'state';\n entries: Entry[];\n /** The reply as it streams in; empty once it lands in `entries`. */\n live: string;\n todos: TodoItem[];\n /** A question waiting for allow / deny, or null. */\n pending: string | null;\n mode: Mode;\n busy: boolean;\n paused: boolean;\n autoApprove: boolean;\n model: string | null;\n /** Tokens in use, the compaction limit, and what they are made of. */\n context: {\n used: number;\n limit: number;\n parts?: { system: number; tools: number; conversation: number };\n } | null;\n usage: ChatUsage | null;\n run: { id: string; alive: boolean; phase?: string; done?: number; tasks?: number } | null;\n /** Helpers (subagents) for the current message. */\n helpers: Helper[];\n /** A model request in flight: when it went out and how long it usually takes (null: unknown). */\n waiting: { startedAt: number; estimateMs: number | null } | null;\n /** The latest browser frame's number and page; the image comes as a `browserFrame` message. */\n browser: { seq: number; url: string; action: string; at: number } | null;\n}\n\n/** Frames larger than this are skipped rather than pushed through the pipe. */\nconst MAX_FRAME_BYTES = 4 * 1024 * 1024;\n\nconst MODES = new Set<Mode>(['chat', 'plan', 'run']);\n/** Coalesce bursts of changes (a streaming reply fires one per token) into one state line. */\nconst EMIT_EVERY_MS = 40;\n\ntype Command = { type: string } & Record<string, unknown>;\n\nfunction parseCommand(line: string): Command {\n let data: unknown;\n try {\n data = JSON.parse(line);\n } catch {\n throw new Error('expected one JSON object per line');\n }\n if (typeof data !== 'object' || data === null || typeof (data as Command).type !== 'string')\n throw new Error('expected an object with a \"type\"');\n return data as Command;\n}\n\nfunction text(c: Command, key: string): string {\n const v = c[key];\n if (typeof v !== 'string' || !v.trim()) throw new Error(`\"${c.type}\" needs a \"${key}\" string`);\n return v;\n}\n\n/** A string that may be empty (an edit can clear a block's text). */\nfunction rawText(c: Command, key: string): string {\n const v = c[key];\n if (typeof v !== 'string') throw new Error(`\"${c.type}\" needs a \"${key}\" string`);\n return v;\n}\n\nexport async function serveChat(\n io: CliIO,\n runCli: RunCli,\n opts: { version: string },\n): Promise<number> {\n const { Session } = await import('../tui/session.js');\n const session = new Session(io, runCli);\n const write = (msg: object): void => {\n println(io.stdout, JSON.stringify(msg));\n };\n const fail = (err: unknown): void => {\n write({ type: 'error', message: describeError(err) });\n };\n\n let sentUpTo = -1;\n let sentFrame = 0;\n /** Send the browser's latest screenshot once per new frame. */\n const sendFrame = async (): Promise<void> => {\n const f = session.browserFrame;\n if (!f || f.seq === sentFrame) return;\n sentFrame = f.seq;\n const data = await readFile(f.path).catch(() => undefined);\n if (!data || data.length > MAX_FRAME_BYTES) return;\n write({\n type: 'browserFrame',\n seq: f.seq,\n mime: f.mime,\n url: f.url,\n action: f.action,\n data: data.toString('base64'),\n });\n };\n /** The last entry before the latest new chat; a reloaded panel starts after it. */\n let clearedAt = -1;\n let timer: NodeJS.Timeout | undefined;\n const emit = (): void => {\n timer = undefined;\n const fresh = session.entries.filter((e) => e.id > sentUpTo);\n sentUpTo = session.entries.at(-1)?.id ?? sentUpTo;\n const t = session.chatTelemetry;\n const state: ChatState = {\n type: 'state',\n entries: fresh,\n live: session.live,\n todos: session.todos,\n pending: session.pending?.question ?? null,\n mode: session.mode,\n busy: session.busy !== undefined || session.chatBusy,\n paused: session.chatPaused,\n autoApprove: session.autoApprove,\n model: session.chatModelName ?? null,\n context: session.context ?? null,\n usage: t\n ? {\n startedAt: t.startedAt ?? null,\n tokens: t.tokens,\n usd: t.usd,\n turns: t.turns,\n toolCalls: t.toolCalls,\n toolErrors: t.toolErrors,\n compactions: t.compactions,\n }\n : null,\n run: session.runId ? { id: session.runId, alive: session.runAlive, ...session.info } : null,\n helpers: session.helpers,\n waiting: session.waiting,\n browser: session.browserFrame\n ? {\n seq: session.browserFrame.seq,\n url: session.browserFrame.url,\n action: session.browserFrame.action,\n at: session.browserFrame.at,\n }\n : null,\n };\n write(state);\n void sendFrame();\n };\n const schedule = (): void => {\n timer ??= setTimeout(emit, EMIT_EVERY_MS);\n };\n session.onChange(schedule);\n\n const sendModels = async (): Promise<void> => {\n const { loadModelChoices } = await import('../tui/model-picker.js');\n const { items, unreachable } = await loadModelChoices(io);\n write({\n type: 'models',\n items: items.filter((i) => i.kind === 'model'),\n unreachable,\n current: session.chatModelName ?? null,\n });\n };\n const sendSettings = async (): Promise<void> => {\n const { providerSettings } = await import('./chat-settings.js');\n write({ type: 'settings', ...(await providerSettings(io)) });\n };\n const sendBrowser = async (): Promise<void> => {\n const { browserSettings } = await import('./chat-settings.js');\n write({ type: 'browser', ...(await browserSettings(io)) });\n };\n const sendContextFiles = async (): Promise<void> => {\n const { contextFiles } = await import('./chat-context.js');\n write({ type: 'contextFiles', ...(await contextFiles(io)) });\n };\n /** The context inspector's items: what the next request sends, with token counts. */\n const sendContext = (): void => {\n write({ type: 'contextItems', items: session.contextItems(), busy: session.chatBusy });\n };\n /** Roles as saved, plus which models are out of quota right now (ref → when it ran out). */\n const sendRoles = async (): Promise<void> => {\n const { readRoles } = await import('../config/roles.js');\n const { QuotaLedger } = await import('../providers/quota-ledger.js');\n const paths = resolvePaths(io.env);\n write({\n type: 'roles',\n roles: await readRoles(paths),\n exhausted: new QuotaLedger(join(paths.configHome, 'quota.json')).all(),\n });\n };\n /** After a provider change: say what happened, then refresh the settings page and models. */\n const providersChanged = async (message: string): Promise<void> => {\n session.push('system', message);\n await sendSettings();\n await sendModels();\n };\n\n /** Commands still running, so a closed pipe lets them finish before the process exits. */\n const inflight = new Set<Promise<unknown>>();\n const run = (p: Promise<unknown>): void => {\n const tracked = p.catch(fail).finally(() => {\n inflight.delete(tracked);\n schedule();\n });\n inflight.add(tracked);\n };\n\n const handle = (c: Command): void => {\n switch (c.type) {\n case 'send':\n run(session.submit(text(c, 'text')));\n return;\n case 'answer':\n if (!session.pending) throw new Error('nothing is waiting for an answer');\n run(session.submit(c.yes === true ? 'y' : 'n'));\n return;\n case 'stop':\n case 'pause':\n case 'resume':\n case 'undo':\n case 'compact':\n run(session.command(c.type));\n return;\n case 'clear':\n run(\n session.command('clear').then(() => {\n // A new chat starts the panel over: nothing from before the clear is sent again.\n clearedAt = session.entries.at(-1)?.id ?? clearedAt;\n sentUpTo = clearedAt;\n write({ type: 'ready', version: opts.version });\n emit();\n }),\n );\n return;\n case 'mode': {\n const mode = c.mode as Mode;\n if (!MODES.has(mode)) throw new Error('mode is one of chat, plan, run');\n if (session.mode !== mode) session.setMode(mode);\n return;\n }\n case 'yolo':\n // /yolo toggles; only run it when the state differs from what was asked, and without\n // echoing it as a message.\n if ((c.on === true) !== session.autoApprove) run(session.command('yolo'));\n return;\n case 'model':\n run(session.switchModel(text(c, 'ref')));\n return;\n case 'models':\n run(sendModels());\n return;\n case 'settings':\n run(sendSettings());\n return;\n case 'addProvider': {\n const str = (k: string): string | undefined => {\n const v = c[k];\n return typeof v === 'string' ? v : undefined;\n };\n run(\n import('./chat-settings.js').then(async ({ addProvider }) => {\n await providersChanged(\n await addProvider(io, {\n provider: str('provider'),\n key: str('key'),\n url: str('url'),\n }),\n );\n }),\n );\n return;\n }\n case 'testProvider':\n run(\n import('./chat-settings.js').then(async ({ testProvider }) => {\n write({ type: 'providerTest', ...(await testProvider(io, text(c, 'name'))) });\n }),\n );\n return;\n case 'contextFiles':\n run(sendContextFiles());\n return;\n case 'setSectionMode':\n run(\n import('./chat-context.js').then(async ({ setSectionMode }) => {\n await setSectionMode(io, text(c, 'id'), text(c, 'mode'));\n await sendContextFiles();\n }),\n );\n return;\n case 'chats':\n run(\n session.savedChats().then((chats) => {\n write({ type: 'chats', chats });\n }),\n );\n return;\n case 'resumeChat':\n run(\n (async () => {\n // Mark the panel's start first, so a reload shows only the reopened chat.\n clearedAt = session.entries.at(-1)?.id ?? clearedAt;\n sentUpTo = clearedAt;\n await session.resumeChat(text(c, 'id'));\n write({ type: 'ready', version: opts.version });\n emit();\n })(),\n );\n return;\n case 'contextList':\n sendContext();\n return;\n case 'contextUpdate':\n session.changeContext({ op: 'edit', id: text(c, 'id'), text: rawText(c, 'text') });\n sendContext();\n return;\n case 'contextDelete':\n session.changeContext({ op: 'delete', id: text(c, 'id') });\n sendContext();\n return;\n case 'contextReset':\n session.changeContext({ op: 'reset' });\n sendContext();\n return;\n case 'browser':\n run(sendBrowser());\n return;\n case 'setBrowser':\n run(\n import('./chat-settings.js').then(async ({ setBrowserSettings }) => {\n await setBrowserSettings(io, c);\n session.push('system', 'browser settings saved; new runs use them');\n await sendBrowser();\n }),\n );\n return;\n case 'roles':\n run(sendRoles());\n return;\n case 'setRoles':\n run(\n import('../config/roles.js').then(async ({ parseRoles, writeRoles }) => {\n await writeRoles(resolvePaths(io.env), parseRoles(c.roles));\n session.push('system', 'model roles saved; new chats and runs use them');\n await sendRoles();\n }),\n );\n return;\n case 'removeProvider':\n run(\n import('./chat-settings.js').then(async ({ removeProvider }) => {\n await providersChanged(await removeProvider(io, text(c, 'name')));\n }),\n );\n return;\n case 'connect': {\n const name = typeof c.name === 'string' && c.name.trim() ? ` --name ${c.name.trim()}` : '';\n run(session.submit(`/model ${text(c, 'value')}${name}`));\n return;\n }\n case 'sync':\n // A reloaded panel: start it over with the conversation since the last new chat.\n sentUpTo = clearedAt;\n sentFrame = 0;\n write({ type: 'ready', version: opts.version });\n emit();\n return;\n default:\n throw new Error(`unknown command \"${c.type}\"`);\n }\n };\n\n await session.loadCommands();\n await session.continueChat().catch(fail);\n await session.greet(false).catch(fail);\n write({ type: 'ready', version: opts.version });\n emit();\n\n const lines = createInterface({ input: io.stdin, crlfDelay: Infinity });\n for await (const line of lines) {\n if (!line.trim()) continue;\n try {\n handle(parseCommand(line));\n } catch (err) {\n fail(err);\n }\n }\n // The editor closed the pipe: end any turn in progress, let running commands finish, then\n // flush the last state.\n if (session.chatBusy) await session.command('stop');\n await Promise.allSettled([...inflight]);\n if (timer) {\n clearTimeout(timer);\n emit();\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
setTable
|
|
4
|
+
} from "./chunk-QS6LKKWO.js";
|
|
2
5
|
import {
|
|
3
6
|
connect,
|
|
4
7
|
describeConnect
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-SFNARMV4.js";
|
|
6
9
|
import {
|
|
7
10
|
PROVIDER_TEMPLATES,
|
|
8
11
|
removeEndpointBlock
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-7PC4SSYS.js";
|
|
11
|
-
import {
|
|
12
|
-
setTable
|
|
13
|
-
} from "./chunk-QS6LKKWO.js";
|
|
12
|
+
} from "./chunk-MHH66U5Y.js";
|
|
14
13
|
import {
|
|
15
14
|
clearKey,
|
|
16
15
|
findProviderKey,
|
|
17
16
|
maskKey
|
|
18
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-4C3ZA5OG.js";
|
|
19
18
|
import {
|
|
20
19
|
loadConfig
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import
|
|
23
|
-
resolvePaths,
|
|
24
|
-
userConfigFile
|
|
25
|
-
} from "./chunk-Q3MDYD6K.js";
|
|
20
|
+
} from "./chunk-DMJLZWAR.js";
|
|
21
|
+
import "./chunk-7PC4SSYS.js";
|
|
26
22
|
import "./chunk-27GOEUTN.js";
|
|
27
23
|
import {
|
|
28
24
|
UsageError
|
|
29
25
|
} from "./chunk-7PNGHZYH.js";
|
|
30
26
|
import "./chunk-UX4MF6V4.js";
|
|
27
|
+
import "./chunk-FR5DUUJ4.js";
|
|
28
|
+
import {
|
|
29
|
+
resolvePaths,
|
|
30
|
+
userConfigFile
|
|
31
|
+
} from "./chunk-Q3MDYD6K.js";
|
|
31
32
|
import "./chunk-2H7UOFLK.js";
|
|
32
33
|
|
|
33
34
|
// src/cli/chat-settings.ts
|
|
@@ -227,4 +228,4 @@ export {
|
|
|
227
228
|
setBrowserSettings,
|
|
228
229
|
testProvider
|
|
229
230
|
};
|
|
230
|
-
//# sourceMappingURL=chat-settings-
|
|
231
|
+
//# sourceMappingURL=chat-settings-K5643AXQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/chat-settings.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { parse } from 'smol-toml';\nimport { clearKey, findProviderKey, maskKey } from '../auth/keys.js';\nimport { loadConfig } from '../config/load.js';\nimport { resolvePaths, userConfigFile } from '../core/paths.js';\nimport { UsageError } from '../errors.js';\nimport { setTable, type TomlValue } from '../integrations/toml-edit.js';\nimport { PROVIDER_TEMPLATES, removeEndpointBlock } from './commands/providers.js';\nimport { connect, describeConnect } from './connect.js';\nimport type { CliIO } from './io.js';\n\n/**\n * Provider settings for editor panels (`omnexx chat-server`): what can be added, what is set up,\n * and adding or removing one. Keys go to the 0600 credentials file and are only ever shown masked.\n */\n\nexport interface CatalogProvider {\n name: string;\n label: string;\n baseUrl: string;\n /** A local server the person runs themselves: no key. */\n local: boolean;\n /** Where to get a key. */\n signup?: string;\n /** How its keys start, for a placeholder. */\n keyPrefix?: string;\n}\n\nexport interface ConnectedProvider {\n name: string;\n label: string;\n baseUrl: string;\n local: boolean;\n free: boolean;\n /** The key, masked (`sk-…wxyz`), or null when none is set. */\n key: string | null;\n /** Where the key comes from: an environment variable or the credentials file. */\n keySource: string | null;\n}\n\nexport interface ProviderSettings {\n catalog: CatalogProvider[];\n connected: ConnectedProvider[];\n}\n\nconst ANTHROPIC: CatalogProvider = {\n name: 'anthropic',\n label: 'Anthropic (Claude)',\n baseUrl: 'https://api.anthropic.com',\n local: false,\n signup: 'https://console.anthropic.com/settings/keys',\n keyPrefix: 'sk-ant-',\n};\n\nconst NAME = /^[a-z][a-z0-9_-]{0,39}$/;\n\nexport function providerCatalog(): CatalogProvider[] {\n const listed = Object.entries(PROVIDER_TEMPLATES)\n .filter(([name]) => name !== 'custom')\n .map(([name, t]) => ({\n name,\n label: t.label ?? name,\n baseUrl: t.baseUrl,\n local: t.local === true,\n ...(t.signup ? { signup: t.signup } : {}),\n ...(t.keyPrefix ? { keyPrefix: t.keyPrefix } : {}),\n }));\n return [ANTHROPIC, ...listed];\n}\n\nexport async function providerSettings(io: CliIO): Promise<ProviderSettings> {\n const paths = resolvePaths(io.env);\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const catalog = providerCatalog();\n const labelOf = (name: string): string => catalog.find((c) => c.name === name)?.label ?? name;\n const connected: ConnectedProvider[] = [];\n const anthropic = await findProviderKey(paths, io.env, 'anthropic');\n if (anthropic)\n connected.push({\n name: 'anthropic',\n label: ANTHROPIC.label,\n baseUrl: ANTHROPIC.baseUrl,\n local: false,\n free: false,\n key: maskKey(anthropic.key),\n keySource: anthropic.source,\n });\n for (const [name, ep] of Object.entries(config.providers.endpoints)) {\n const found = await findProviderKey(paths, io.env, name, ep.api_key_env);\n connected.push({\n name,\n label: labelOf(name),\n baseUrl: ep.base_url,\n local:\n PROVIDER_TEMPLATES[name]?.local === true ||\n /\\/\\/(localhost|127\\.0\\.0\\.1)[:/]/.test(ep.base_url),\n free: ep.free,\n key: found ? maskKey(found.key) : null,\n keySource: found?.source ?? null,\n });\n }\n return { catalog, connected };\n}\n\nexport interface AddProvider {\n /** A catalog name, or a new name for a custom URL. */\n provider?: string | undefined;\n key?: string | undefined;\n /** For a custom or local OpenAI-compatible server. */\n url?: string | undefined;\n}\n\n/** Connect a provider from the settings form; returns what to tell the person. */\nexport async function addProvider(io: CliIO, a: AddProvider): Promise<string> {\n const url = a.url?.trim();\n const provider = a.provider?.trim().toLowerCase();\n const key = a.key?.trim() ? a.key.trim() : undefined;\n if (url && !/^https?:\\/\\/\\S+$/i.test(url))\n throw new UsageError('the URL must start with http:// or https://');\n if (provider && !NAME.test(provider))\n throw new UsageError(\n `\"${provider}\" is not a usable name`,\n 'lowercase letters, digits, _ and -',\n );\n if (!url && !provider && !key) throw new UsageError('pick a provider or enter a URL');\n // A catalog provider is connected by name; a URL gets the name it was given, if any.\n const target = url ?? provider ?? key ?? '';\n const r = await connect(io, target, url ? key : target === key ? undefined : key, {\n ...(url && provider ? { name: provider } : {}),\n });\n return describeConnect(r);\n}\n\n/** Remove a provider: its endpoint block and its stored key. Environment variables stay. */\nexport async function removeProvider(io: CliIO, name: string): Promise<string> {\n const lower = name.trim().toLowerCase();\n if (!NAME.test(lower)) throw new UsageError(`\"${name}\" is not a provider name`);\n const paths = resolvePaths(io.env);\n const removedBlock =\n lower === 'anthropic' ? false : await removeEndpointBlock(userConfigFile(paths), lower);\n const removedKey = await clearKey(paths, lower);\n if (!removedBlock && !removedKey)\n return `${lower} had nothing to remove (a key from an environment variable stays set)`;\n return `removed ${lower}${removedKey ? ' and its stored key' : ''}`;\n}\n\nexport interface ProviderTest {\n name: string;\n ok: boolean;\n /** How many models the provider listed, when it answered. */\n models: number;\n detail: string;\n}\n\nconst TEST_TIMEOUT_MS = 8_000;\n\n/**\n * Check a provider from the settings page: list its models with the stored key. Anthropic is\n * built in and has no list endpoint here, so it only reports whether a key is set.\n */\nexport async function testProvider(io: CliIO, name: string): Promise<ProviderTest> {\n const lower = name.trim().toLowerCase();\n if (!NAME.test(lower)) throw new UsageError(`\"${name}\" is not a provider name`);\n const paths = resolvePaths(io.env);\n if (lower === 'anthropic') {\n const found = await findProviderKey(paths, io.env, 'anthropic');\n return {\n name: lower,\n ok: found !== undefined,\n models: found ? 3 : 0,\n detail: found ? 'key set (opus, sonnet, haiku)' : 'no key set',\n };\n }\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const ep = config.providers.endpoints[lower];\n if (!ep) return { name: lower, ok: false, models: 0, detail: 'not set up' };\n const key = (await findProviderKey(paths, io.env, lower, ep.api_key_env))?.key;\n const { discoverModels } = await import('../providers/discovery.js');\n const models = await discoverModels(\n { baseUrl: ep.base_url, ...(key ? { apiKey: key } : {}) },\n { timeoutMs: TEST_TIMEOUT_MS, ...(io.fetch ? { fetch: io.fetch } : {}) },\n );\n if (models.length)\n return { name: lower, ok: true, models: models.length, detail: `${models.length} models` };\n return {\n name: lower,\n ok: false,\n models: 0,\n detail: key || ep.free ? \"couldn't reach it or it listed no models\" : 'no key set',\n };\n}\n\n/** The browser tool's engine, profile and session, as the settings page shows and saves them. */\nexport interface BrowserSettings {\n engine: 'chromium' | 'camoufox';\n /** A folder (or Chrome profile name) whose logins the agent reuses; null when unset. */\n profile: string | null;\n sessionName: string | null;\n headless: boolean;\n}\n\nconst ENGINES = ['chromium', 'camoufox'] as const;\n// Same rule as browser.session_name in src/config/sections/browser.ts.\nconst SESSION_NAME = /^[\\w.-]{1,64}$/;\n/** C0 and C1 control characters, newlines included. */\nconst hasControl = (text: string): boolean =>\n Array.from(text).some((ch) => {\n const code = ch.codePointAt(0) ?? 0;\n return code < 0x20 || (code >= 0x7f && code <= 0x9f);\n });\n\nexport async function browserSettings(io: CliIO): Promise<BrowserSettings> {\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const b = config.browser;\n return {\n engine: b.engine,\n profile: b.profile ?? null,\n sessionName: b.session_name ?? null,\n headless: b.headless,\n };\n}\n\n/** A trimmed optional text field: empty is unset; newlines and control characters are refused. */\nfunction optionalText(raw: unknown, label: string): string | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (typeof raw !== 'string') throw new UsageError(`${label} must be text`);\n const trimmed = raw.trim();\n if (hasControl(trimmed))\n throw new UsageError(`${label} can't contain newlines or control characters`);\n return trimmed || undefined;\n}\n\n/** Check what the settings form sent; throws a UsageError naming the bad field. */\nexport function parseBrowserSettings(raw: unknown): BrowserSettings {\n const obj = typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : {};\n const engine = obj.engine ?? 'chromium';\n if (typeof engine !== 'string' || !(ENGINES as readonly string[]).includes(engine))\n throw new UsageError('the browser engine must be chromium or camoufox');\n const profile = optionalText(obj.profile, 'the browser profile');\n const sessionName = optionalText(obj.sessionName, 'the session name');\n if (sessionName && !SESSION_NAME.test(sessionName))\n throw new UsageError(\n `\"${sessionName}\" is not a usable session name`,\n 'up to 64 letters, digits, _ . and -',\n );\n if (obj.headless !== undefined && typeof obj.headless !== 'boolean')\n throw new UsageError('headless must be on or off');\n return {\n engine: engine as BrowserSettings['engine'],\n profile: profile ?? null,\n sessionName: sessionName ?? null,\n headless: obj.headless !== false,\n };\n}\n\nasync function readText(file: string): Promise<string> {\n try {\n return await readFile(file, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return '';\n throw err;\n }\n}\n\n/** The [browser] keys already in the user config, kept when the form saves its four. */\nfunction existingBrowser(text: string): Record<string, TomlValue> {\n const table = (parse(text) as Record<string, unknown>).browser;\n return typeof table === 'object' && table !== null ? (table as Record<string, TomlValue>) : {};\n}\n\n/** Save the browser settings to the user's config.toml; other [browser] keys stay as they were. */\nexport async function setBrowserSettings(io: CliIO, raw: unknown): Promise<BrowserSettings> {\n const next = parseBrowserSettings(raw);\n const file = userConfigFile(resolvePaths(io.env));\n const text = await readText(file);\n const updated = setTable(text, ['browser'], {\n ...existingBrowser(text),\n engine: next.engine,\n profile: next.profile ?? undefined,\n session_name: next.sessionName ?? undefined,\n headless: next.headless,\n });\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, updated, 'utf8');\n return next;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/chat-settings.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { parse } from 'smol-toml';\nimport { clearKey, findProviderKey, maskKey } from '../auth/keys.js';\nimport { loadConfig } from '../config/load.js';\nimport { resolvePaths, userConfigFile } from '../core/paths.js';\nimport { UsageError } from '../errors.js';\nimport { setTable, type TomlValue } from '../integrations/toml-edit.js';\nimport { PROVIDER_TEMPLATES, removeEndpointBlock } from './commands/providers.js';\nimport { connect, describeConnect } from './connect.js';\nimport type { CliIO } from './io.js';\n\n/**\n * Provider settings for editor panels (`omnexx chat-server`): what can be added, what is set up,\n * and adding or removing one. Keys go to the 0600 credentials file and are only ever shown masked.\n */\n\nexport interface CatalogProvider {\n name: string;\n label: string;\n baseUrl: string;\n /** A local server the person runs themselves: no key. */\n local: boolean;\n /** Where to get a key. */\n signup?: string;\n /** How its keys start, for a placeholder. */\n keyPrefix?: string;\n}\n\nexport interface ConnectedProvider {\n name: string;\n label: string;\n baseUrl: string;\n local: boolean;\n free: boolean;\n /** The key, masked (`sk-…wxyz`), or null when none is set. */\n key: string | null;\n /** Where the key comes from: an environment variable or the credentials file. */\n keySource: string | null;\n}\n\nexport interface ProviderSettings {\n catalog: CatalogProvider[];\n connected: ConnectedProvider[];\n}\n\nconst ANTHROPIC: CatalogProvider = {\n name: 'anthropic',\n label: 'Anthropic (Claude)',\n baseUrl: 'https://api.anthropic.com',\n local: false,\n signup: 'https://console.anthropic.com/settings/keys',\n keyPrefix: 'sk-ant-',\n};\n\nconst NAME = /^[a-z][a-z0-9_-]{0,39}$/;\n\nexport function providerCatalog(): CatalogProvider[] {\n const listed = Object.entries(PROVIDER_TEMPLATES)\n .filter(([name]) => name !== 'custom')\n .map(([name, t]) => ({\n name,\n label: t.label ?? name,\n baseUrl: t.baseUrl,\n local: t.local === true,\n ...(t.signup ? { signup: t.signup } : {}),\n ...(t.keyPrefix ? { keyPrefix: t.keyPrefix } : {}),\n }));\n return [ANTHROPIC, ...listed];\n}\n\nexport async function providerSettings(io: CliIO): Promise<ProviderSettings> {\n const paths = resolvePaths(io.env);\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const catalog = providerCatalog();\n const labelOf = (name: string): string => catalog.find((c) => c.name === name)?.label ?? name;\n const connected: ConnectedProvider[] = [];\n const anthropic = await findProviderKey(paths, io.env, 'anthropic');\n if (anthropic)\n connected.push({\n name: 'anthropic',\n label: ANTHROPIC.label,\n baseUrl: ANTHROPIC.baseUrl,\n local: false,\n free: false,\n key: maskKey(anthropic.key),\n keySource: anthropic.source,\n });\n for (const [name, ep] of Object.entries(config.providers.endpoints)) {\n const found = await findProviderKey(paths, io.env, name, ep.api_key_env);\n connected.push({\n name,\n label: labelOf(name),\n baseUrl: ep.base_url,\n local:\n PROVIDER_TEMPLATES[name]?.local === true ||\n /\\/\\/(localhost|127\\.0\\.0\\.1)[:/]/.test(ep.base_url),\n free: ep.free,\n key: found ? maskKey(found.key) : null,\n keySource: found?.source ?? null,\n });\n }\n return { catalog, connected };\n}\n\nexport interface AddProvider {\n /** A catalog name, or a new name for a custom URL. */\n provider?: string | undefined;\n key?: string | undefined;\n /** For a custom or local OpenAI-compatible server. */\n url?: string | undefined;\n}\n\n/** Connect a provider from the settings form; returns what to tell the person. */\nexport async function addProvider(io: CliIO, a: AddProvider): Promise<string> {\n const url = a.url?.trim();\n const provider = a.provider?.trim().toLowerCase();\n const key = a.key?.trim() ? a.key.trim() : undefined;\n if (url && !/^https?:\\/\\/\\S+$/i.test(url))\n throw new UsageError('the URL must start with http:// or https://');\n if (provider && !NAME.test(provider))\n throw new UsageError(\n `\"${provider}\" is not a usable name`,\n 'lowercase letters, digits, _ and -',\n );\n if (!url && !provider && !key) throw new UsageError('pick a provider or enter a URL');\n // A catalog provider is connected by name; a URL gets the name it was given, if any.\n const target = url ?? provider ?? key ?? '';\n const r = await connect(io, target, url ? key : target === key ? undefined : key, {\n ...(url && provider ? { name: provider } : {}),\n });\n return describeConnect(r);\n}\n\n/** Remove a provider: its endpoint block and its stored key. Environment variables stay. */\nexport async function removeProvider(io: CliIO, name: string): Promise<string> {\n const lower = name.trim().toLowerCase();\n if (!NAME.test(lower)) throw new UsageError(`\"${name}\" is not a provider name`);\n const paths = resolvePaths(io.env);\n const removedBlock =\n lower === 'anthropic' ? false : await removeEndpointBlock(userConfigFile(paths), lower);\n const removedKey = await clearKey(paths, lower);\n if (!removedBlock && !removedKey)\n return `${lower} had nothing to remove (a key from an environment variable stays set)`;\n return `removed ${lower}${removedKey ? ' and its stored key' : ''}`;\n}\n\nexport interface ProviderTest {\n name: string;\n ok: boolean;\n /** How many models the provider listed, when it answered. */\n models: number;\n detail: string;\n}\n\nconst TEST_TIMEOUT_MS = 8_000;\n\n/**\n * Check a provider from the settings page: list its models with the stored key. Anthropic is\n * built in and has no list endpoint here, so it only reports whether a key is set.\n */\nexport async function testProvider(io: CliIO, name: string): Promise<ProviderTest> {\n const lower = name.trim().toLowerCase();\n if (!NAME.test(lower)) throw new UsageError(`\"${name}\" is not a provider name`);\n const paths = resolvePaths(io.env);\n if (lower === 'anthropic') {\n const found = await findProviderKey(paths, io.env, 'anthropic');\n return {\n name: lower,\n ok: found !== undefined,\n models: found ? 3 : 0,\n detail: found ? 'key set (opus, sonnet, haiku)' : 'no key set',\n };\n }\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const ep = config.providers.endpoints[lower];\n if (!ep) return { name: lower, ok: false, models: 0, detail: 'not set up' };\n const key = (await findProviderKey(paths, io.env, lower, ep.api_key_env))?.key;\n const { discoverModels } = await import('../providers/discovery.js');\n const models = await discoverModels(\n { baseUrl: ep.base_url, ...(key ? { apiKey: key } : {}) },\n { timeoutMs: TEST_TIMEOUT_MS, ...(io.fetch ? { fetch: io.fetch } : {}) },\n );\n if (models.length)\n return { name: lower, ok: true, models: models.length, detail: `${models.length} models` };\n return {\n name: lower,\n ok: false,\n models: 0,\n detail: key || ep.free ? \"couldn't reach it or it listed no models\" : 'no key set',\n };\n}\n\n/** The browser tool's engine, profile and session, as the settings page shows and saves them. */\nexport interface BrowserSettings {\n engine: 'chromium' | 'camoufox';\n /** A folder (or Chrome profile name) whose logins the agent reuses; null when unset. */\n profile: string | null;\n sessionName: string | null;\n headless: boolean;\n}\n\nconst ENGINES = ['chromium', 'camoufox'] as const;\n// Same rule as browser.session_name in src/config/sections/browser.ts.\nconst SESSION_NAME = /^[\\w.-]{1,64}$/;\n/** C0 and C1 control characters, newlines included. */\nconst hasControl = (text: string): boolean =>\n Array.from(text).some((ch) => {\n const code = ch.codePointAt(0) ?? 0;\n return code < 0x20 || (code >= 0x7f && code <= 0x9f);\n });\n\nexport async function browserSettings(io: CliIO): Promise<BrowserSettings> {\n const { config } = await loadConfig({ cwd: io.cwd, env: io.env });\n const b = config.browser;\n return {\n engine: b.engine,\n profile: b.profile ?? null,\n sessionName: b.session_name ?? null,\n headless: b.headless,\n };\n}\n\n/** A trimmed optional text field: empty is unset; newlines and control characters are refused. */\nfunction optionalText(raw: unknown, label: string): string | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (typeof raw !== 'string') throw new UsageError(`${label} must be text`);\n const trimmed = raw.trim();\n if (hasControl(trimmed))\n throw new UsageError(`${label} can't contain newlines or control characters`);\n return trimmed || undefined;\n}\n\n/** Check what the settings form sent; throws a UsageError naming the bad field. */\nexport function parseBrowserSettings(raw: unknown): BrowserSettings {\n const obj = typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : {};\n const engine = obj.engine ?? 'chromium';\n if (typeof engine !== 'string' || !(ENGINES as readonly string[]).includes(engine))\n throw new UsageError('the browser engine must be chromium or camoufox');\n const profile = optionalText(obj.profile, 'the browser profile');\n const sessionName = optionalText(obj.sessionName, 'the session name');\n if (sessionName && !SESSION_NAME.test(sessionName))\n throw new UsageError(\n `\"${sessionName}\" is not a usable session name`,\n 'up to 64 letters, digits, _ . and -',\n );\n if (obj.headless !== undefined && typeof obj.headless !== 'boolean')\n throw new UsageError('headless must be on or off');\n return {\n engine: engine as BrowserSettings['engine'],\n profile: profile ?? null,\n sessionName: sessionName ?? null,\n headless: obj.headless !== false,\n };\n}\n\nasync function readText(file: string): Promise<string> {\n try {\n return await readFile(file, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return '';\n throw err;\n }\n}\n\n/** The [browser] keys already in the user config, kept when the form saves its four. */\nfunction existingBrowser(text: string): Record<string, TomlValue> {\n const table = (parse(text) as Record<string, unknown>).browser;\n return typeof table === 'object' && table !== null ? (table as Record<string, TomlValue>) : {};\n}\n\n/** Save the browser settings to the user's config.toml; other [browser] keys stay as they were. */\nexport async function setBrowserSettings(io: CliIO, raw: unknown): Promise<BrowserSettings> {\n const next = parseBrowserSettings(raw);\n const file = userConfigFile(resolvePaths(io.env));\n const text = await readText(file);\n const updated = setTable(text, ['browser'], {\n ...existingBrowser(text),\n engine: next.engine,\n profile: next.profile ?? undefined,\n session_name: next.sessionName ?? undefined,\n headless: next.headless,\n });\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, updated, 'utf8');\n return next;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
credentialsDir
|
|
4
|
-
} from "./chunk-Q3MDYD6K.js";
|
|
5
2
|
import {
|
|
6
3
|
ProviderError,
|
|
7
4
|
UsageError
|
|
8
5
|
} from "./chunk-7PNGHZYH.js";
|
|
6
|
+
import {
|
|
7
|
+
credentialsDir
|
|
8
|
+
} from "./chunk-Q3MDYD6K.js";
|
|
9
9
|
|
|
10
10
|
// src/auth/keys.ts
|
|
11
11
|
import { chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
@@ -216,9 +216,11 @@ var GeminiProvider = class {
|
|
|
216
216
|
|
|
217
217
|
// src/providers/openai-compat.ts
|
|
218
218
|
function toChatMessages(req) {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
];
|
|
219
|
+
const stable = req.system.filter((b) => !b.volatile).map((b) => b.text);
|
|
220
|
+
let carried = req.system.filter((b) => b.volatile).map((b) => b.text);
|
|
221
|
+
const out = [{ role: "system", content: stable.join("\n\n") }];
|
|
222
|
+
if (carried.length && !req.messages.some((m) => m.role === "user"))
|
|
223
|
+
out.push({ role: "user", content: carried.join("\n\n") });
|
|
222
224
|
for (const m of req.messages) {
|
|
223
225
|
if (m.role === "assistant") {
|
|
224
226
|
const text = m.content.flatMap((b) => b.type === "text" ? [b.text] : []).join("\n");
|
|
@@ -238,7 +240,8 @@ function toChatMessages(req) {
|
|
|
238
240
|
});
|
|
239
241
|
continue;
|
|
240
242
|
}
|
|
241
|
-
const texts = [];
|
|
243
|
+
const texts = [...carried];
|
|
244
|
+
carried = [];
|
|
242
245
|
const images = [];
|
|
243
246
|
for (const b of m.content) {
|
|
244
247
|
if (b.type === "tool_result")
|
|
@@ -673,4 +676,4 @@ export {
|
|
|
673
676
|
ResponsesProvider,
|
|
674
677
|
makeEndpointProvider
|
|
675
678
|
};
|
|
676
|
-
//# sourceMappingURL=chunk-
|
|
679
|
+
//# sourceMappingURL=chunk-4C3ZA5OG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth/keys.ts","../src/cli/exit-codes.ts","../src/providers/gemini.ts","../src/providers/openai-compat.ts","../src/providers/responses.ts","../src/providers/make.ts"],"sourcesContent":["import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { credentialsDir, type OmnexxPaths } from '../core/paths.js';\nimport { UsageError } from '../errors.js';\n\n/**\n * Any provider name (`anthropic`, `groq`, `openrouter`, ...) can hold a key.\n * `PROVIDERS` keeps the well-known default for help text; `assertProvider`\n * accepts any lowercase `name` so `auth set <any-provider>` works.\n */\nexport const PROVIDERS = ['anthropic'] as const;\nexport type KeyProvider = string;\n\nexport interface KeyLookup {\n key: string;\n source: string;\n}\n\nconst NAME_RE = /^[a-z][a-z0-9_-]*$/;\n\nexport function keyFile(paths: OmnexxPaths, provider: KeyProvider): string {\n return join(credentialsDir(paths), `${provider}.key`);\n}\n\n/** Any lowercase provider name; rejects empty/uppercase/path-like input. */\nexport function assertProvider(name: string): KeyProvider {\n if (NAME_RE.test(name)) return name;\n throw new UsageError(\n `unknown provider \"${name}\"`,\n 'use a lowercase name like anthropic, groq or openrouter',\n );\n}\n\nexport function envNames(provider: string): { omnexx: string; plain: string } {\n const upper = provider.toUpperCase().replace(/[^A-Z0-9]/g, '_');\n return { omnexx: `OMNEXX_${upper}_API_KEY`, plain: `${upper}_API_KEY` };\n}\n\n/**\n * Lookup order: OMNEXX_<NAME>_API_KEY, the endpoint's api_key_env, then the\n * 0600 credentials file. Never touches keychains or other tools' logins.\n * Keeps Anthropic's historical behaviour (OMNEXX_ANTHROPIC_API_KEY, then\n * ANTHROPIC_API_KEY, then file).\n */\nexport async function findProviderKey(\n paths: OmnexxPaths,\n env: NodeJS.ProcessEnv,\n provider: string,\n apiKeyEnv?: string,\n): Promise<KeyLookup | undefined> {\n const names = envNames(provider);\n // Lookup order: OMNEXX_<NAME>_API_KEY, the endpoint's api_key_env, the\n // credentials file. api_key_env usually equals one of the first two.\n const candidates = [names.omnexx];\n if (apiKeyEnv) candidates.push(apiKeyEnv);\n if (!candidates.includes(names.plain)) candidates.push(names.plain);\n for (const name of candidates) {\n const value = env[name]?.trim();\n if (value) return { key: value, source: name };\n }\n try {\n const stored = (await readFile(keyFile(paths, provider), 'utf8')).trim();\n return stored ? { key: stored, source: 'credentials file' } : undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw err;\n }\n}\n\n/** Env first (OMNEXX_ wins), then the 0600 credentials file. Never touches keychains or other tools' logins. */\nexport async function findAnthropicKey(\n paths: OmnexxPaths,\n env: NodeJS.ProcessEnv,\n): Promise<KeyLookup | undefined> {\n return findProviderKey(paths, env, 'anthropic');\n}\n\nexport async function storeKey(\n paths: OmnexxPaths,\n provider: KeyProvider,\n key: string,\n): Promise<string> {\n assertProvider(provider);\n const trimmed = key.trim();\n if (!trimmed) throw new UsageError('empty key', 'paste the key, then press Enter');\n const dir = credentialsDir(paths);\n await mkdir(dir, { recursive: true, mode: 0o700 });\n await chmod(dir, 0o700);\n const file = keyFile(paths, provider);\n await writeFile(file, `${trimmed}\\n`, { mode: 0o600 });\n await chmod(file, 0o600);\n return file;\n}\n\nexport async function clearKey(paths: OmnexxPaths, provider: KeyProvider): Promise<boolean> {\n assertProvider(provider);\n try {\n await rm(keyFile(paths, provider));\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n }\n}\n\n/** `sk-ant-…wxyz`: enough to recognise which key it is, never enough to use it. */\nexport function maskKey(key: string): string {\n const m = /^(sk-ant-|sk-)/.exec(key)?.[0] ?? '';\n const tail = key.length >= 12 ? key.slice(-4) : '';\n return `${m}…${tail}`;\n}\n","/** Plan §5.1. Scripts rely on these, so they never change meaning. */\nexport const EXIT = {\n ok: 0,\n error: 1,\n needsHuman: 2,\n budgetStop: 3,\n userStop: 4,\n} as const;\n","import { ProviderError } from '../errors.js';\nimport type {\n CompletionRequest,\n CompletionResponse,\n ContentBlock,\n Provider,\n StopReason,\n} from './types.js';\n\nexport interface GeminiOptions {\n apiKey: string | undefined;\n /** e.g. https://generativelanguage.googleapis.com (tests inject a loopback URL). */\n baseUrl?: string;\n timeoutMs: number;\n fetch?: typeof fetch;\n}\n\ninterface GeminiPart {\n text?: string;\n functionCall?: { name?: string; args?: unknown };\n functionResponse?: { name?: string; response?: unknown };\n inlineData?: { mimeType: string; data: string };\n}\ninterface GeminiContent {\n role?: string;\n parts?: GeminiPart[];\n}\ninterface GeminiResponse {\n candidates?: { content?: GeminiContent; finishReason?: string }[];\n usageMetadata?: { promptTokenCount?: number; candidatesTokenCount?: number };\n error?: { message?: string };\n}\n\nfunction toContents(req: CompletionRequest): GeminiContent[] {\n const out: GeminiContent[] = [];\n for (const m of req.messages) {\n if (m.role === 'assistant') {\n const parts: GeminiPart[] = [];\n for (const b of m.content) {\n if (b.type === 'text' && b.text) parts.push({ text: b.text });\n else if (b.type === 'tool_use')\n parts.push({ functionCall: { name: b.name, args: b.input ?? {} } });\n }\n if (parts.length) out.push({ role: 'model', parts });\n continue;\n }\n const parts: GeminiPart[] = [];\n for (const b of m.content) {\n if (b.type === 'tool_result')\n parts.push({\n functionResponse: { name: b.toolUseId, response: { result: b.content } },\n });\n else if (b.type === 'text') parts.push({ text: b.text });\n else if (b.type === 'image')\n parts.push({ inlineData: { mimeType: b.mediaType, data: b.data } });\n }\n if (parts.length) out.push({ role: 'user', parts });\n }\n return out;\n}\n\nfunction mapStop(r: string | undefined, hasCalls: boolean): StopReason {\n if (hasCalls || r === 'TOOL_CALL' || r === 'FUNCTION_CALL') return 'tool_use';\n if (r === 'STOP') return 'end_turn';\n if (r === 'MAX_TOKENS') return 'max_tokens';\n if (r === 'SAFETY' || r === 'BLOCKLIST' || r === 'PROHIBITED_CONTENT') return 'refusal';\n return 'other';\n}\n\n/**\n * Native Gemini provider: `generateContent` with function calling and usage\n * metadata. Plain `fetch`, no SDK. Implements the Provider interface in types.ts.\n */\nexport class GeminiProvider implements Provider {\n readonly name = 'gemini';\n constructor(private readonly opts: GeminiOptions) {}\n\n async complete(req: CompletionRequest): Promise<CompletionResponse> {\n const base = (this.opts.baseUrl ?? 'https://generativelanguage.googleapis.com').replace(\n /\\/+$/,\n '',\n );\n const tools = req.tools.length\n ? [\n {\n functionDeclarations: req.tools.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n })),\n },\n ]\n : [];\n const body = {\n system_instruction: req.system.length\n ? { parts: req.system.map((b) => ({ text: b.text })) }\n : undefined,\n contents: toContents(req),\n tools: tools.length ? tools : undefined,\n generationConfig: { maxOutputTokens: req.maxTokens },\n };\n const signals = [AbortSignal.timeout(this.opts.timeoutMs), ...(req.signal ? [req.signal] : [])];\n const url = `${base}/v1beta/models/${encodeURIComponent(req.model)}:generateContent`;\n let res: Response;\n try {\n res = await (this.opts.fetch ?? fetch)(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...(this.opts.apiKey ? { 'x-goog-api-key': this.opts.apiKey } : {}),\n },\n body: JSON.stringify(body),\n signal: AbortSignal.any(signals),\n });\n } catch (err) {\n if (req.signal?.aborted)\n throw new ProviderError('request aborted', { retryable: false, cause: err });\n throw new ProviderError(`network error talking to gemini: ${(err as Error).message}`, {\n retryable: true,\n cause: err,\n });\n }\n let data: GeminiResponse;\n try {\n data = (await res.json()) as GeminiResponse;\n } catch (err) {\n throw new ProviderError(`gemini returned non-JSON (HTTP ${res.status})`, {\n retryable: res.status >= 500,\n status: res.status,\n cause: err,\n });\n }\n if (!res.ok) {\n const s = res.status;\n throw new ProviderError(`gemini error ${s}: ${data.error?.message ?? 'request failed'}`, {\n retryable: s === 408 || s === 409 || s === 429 || s >= 500,\n status: s,\n ...(s === 401 || s === 403 ? { hint: 'check the key for gemini' } : {}),\n });\n }\n const cand = data.candidates?.[0];\n const content: ContentBlock[] = [];\n let call = 0;\n for (const p of cand?.content?.parts ?? []) {\n if (typeof p.text === 'string' && p.text) content.push({ type: 'text', text: p.text });\n if (p.functionCall)\n content.push({\n type: 'tool_use',\n id: `call_${call++}`,\n name: p.functionCall.name ?? '',\n input: p.functionCall.args ?? {},\n });\n }\n return {\n model: req.model,\n stopReason: mapStop(\n cand?.finishReason,\n content.some((b) => b.type === 'tool_use'),\n ),\n content,\n usage: {\n uncached: data.usageMetadata?.promptTokenCount ?? 0,\n cacheWrite5m: 0,\n cacheWrite1h: 0,\n cacheRead: 0,\n output: data.usageMetadata?.candidatesTokenCount ?? 0,\n },\n };\n }\n}\n","import { ProviderError } from '../errors.js';\nimport type {\n CompletionRequest,\n CompletionResponse,\n ContentBlock,\n Provider,\n StopReason,\n} from './types.js';\n\nexport interface OpenAICompatOptions {\n name: string;\n baseUrl: string;\n apiKey: string | undefined;\n timeoutMs: number;\n fetch?: typeof fetch;\n}\n\ntype ChatPart = { type: 'text'; text: string } | { type: 'image_url'; image_url: { url: string } };\n\ninterface ChatMessage {\n role: 'system' | 'user' | 'assistant' | 'tool';\n content: string | ChatPart[] | null;\n tool_calls?: { id: string; type: 'function'; function: { name: string; arguments: string } }[];\n tool_call_id?: string;\n}\n\ninterface ChatResponse {\n model?: string;\n choices?: {\n finish_reason?: string;\n message?: {\n content?: string | null;\n reasoning?: string | null;\n reasoning_content?: string | null;\n tool_calls?: { id?: string; function?: { name?: string; arguments?: string } }[];\n };\n }[];\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n prompt_tokens_details?: { cached_tokens?: number };\n };\n error?: { message?: string; code?: string | number; type?: string };\n detail?: unknown;\n}\n\n/** Map our provider-neutral conversation to Chat Completions messages. */\nexport function toChatMessages(req: CompletionRequest): ChatMessage[] {\n const stable = req.system.filter((b) => !b.volatile).map((b) => b.text);\n let carried = req.system.filter((b) => b.volatile).map((b) => b.text);\n const out: ChatMessage[] = [{ role: 'system', content: stable.join('\\n\\n') }];\n if (carried.length && !req.messages.some((m) => m.role === 'user'))\n out.push({ role: 'user', content: carried.join('\\n\\n') });\n for (const m of req.messages) {\n if (m.role === 'assistant') {\n const text = m.content.flatMap((b) => (b.type === 'text' ? [b.text] : [])).join('\\n');\n const calls = m.content.flatMap((b) =>\n b.type === 'tool_use'\n ? [\n {\n id: b.id,\n type: 'function' as const,\n function: { name: b.name, arguments: JSON.stringify(b.input ?? {}) },\n },\n ]\n : [],\n );\n out.push({\n role: 'assistant',\n content: text || null,\n ...(calls.length ? { tool_calls: calls } : {}),\n });\n continue;\n }\n const texts: string[] = [...carried];\n carried = [];\n const images: ChatPart[] = [];\n for (const b of m.content) {\n if (b.type === 'tool_result')\n out.push({\n role: 'tool',\n tool_call_id: b.toolUseId,\n content: b.isError ? `ERROR: ${b.content}` : b.content,\n });\n else if (b.type === 'text') texts.push(b.text);\n else if (b.type === 'image')\n images.push({\n type: 'image_url',\n image_url: { url: `data:${b.mediaType};base64,${b.data}` },\n });\n }\n if (images.length)\n out.push({ role: 'user', content: [{ type: 'text', text: texts.join('\\n\\n') }, ...images] });\n else if (texts.length) out.push({ role: 'user', content: texts.join('\\n\\n') });\n }\n return out;\n}\n\nfunction mapStop(r: string | undefined, hasCalls: boolean): StopReason {\n if (hasCalls || r === 'tool_calls') return 'tool_use';\n if (r === 'stop') return 'end_turn';\n if (r === 'length') return 'max_tokens';\n if (r === 'content_filter') return 'refusal';\n return 'other';\n}\n\n/**\n * One adapter for every OpenAI-compatible Chat Completions endpoint: OpenAI, OpenRouter, a\n * LiteLLM proxy, Ollama, vLLM. Plain `fetch`, no SDK. Caching is whatever the endpoint does on\n * its own; reported cached prompt tokens are billed at the cache-read rate.\n */\n/** \"Range of max_tokens should be [1, 8192]\", \"max_tokens must be less than or equal to 8192\"… */\nconst MAX_TOKENS_LIMIT =\n /max_(?:completion_)?tokens\\D{0,60}?(?:\\[\\s*1\\s*,\\s*|less than or equal to\\s*|at most\\s*|<=\\s*|maximum(?: value)?(?: is| of)?\\s*)(\\d{3,7})/i;\n\n/** The model's output limit when a 400 says the requested max_tokens was too high. */\nexport function maxTokensLimit(message: string): number | undefined {\n const m = MAX_TOKENS_LIMIT.exec(message);\n return m?.[1] ? Number(m[1]) : undefined;\n}\n\n/** A provider-side content filter refused the request (another model may accept it). */\nconst CONTENT_FILTER =\n /data_inspection_failed|content[_ ](?:filter|policy|management)|inappropriate content|safety (?:system|filter)/i;\n\nexport class OpenAICompatProvider implements Provider {\n readonly name: string;\n /** Output limits learned from 400s, per model: later calls ask for no more than this. */\n private readonly outputCaps = new Map<string, number>();\n\n constructor(private readonly opts: OpenAICompatOptions) {\n this.name = opts.name;\n }\n\n /**\n * Thinking switches differ per model, and a pool can route each request to a different one:\n * some refuse non-streaming calls unless `enable_thinking: false` is sent, thinking-only models\n * refuse that same flag. So a 400 about `enable_thinking` flips the flag for this request and\n * tries again (at most twice). A 400 saying max_tokens is above the model's limit records that\n * limit for the model and retries at it.\n */\n async complete(req: CompletionRequest): Promise<CompletionResponse> {\n let thinking: boolean | undefined;\n for (let i = 0; ; i++) {\n try {\n const cap = this.outputCaps.get(req.model);\n return await this.attempt(\n cap && cap < req.maxTokens ? { ...req, maxTokens: cap } : req,\n thinking,\n );\n } catch (err) {\n if (i >= 3 || !(err instanceof ProviderError) || err.status !== 400) throw err;\n const limit = maxTokensLimit(err.message);\n if (limit && limit < (this.outputCaps.get(req.model) ?? req.maxTokens)) {\n this.outputCaps.set(req.model, limit);\n continue;\n }\n if (!/enable_thinking/i.test(err.message)) throw err;\n thinking = thinking === false ? undefined : false;\n }\n }\n }\n\n private async attempt(\n req: CompletionRequest,\n thinking: boolean | undefined,\n ): Promise<CompletionResponse> {\n const body = {\n model: req.model,\n messages: toChatMessages(req),\n max_tokens: req.maxTokens,\n ...(req.tools.length\n ? {\n tools: req.tools.map((t) => ({\n type: 'function',\n function: { name: t.name, description: t.description, parameters: t.inputSchema },\n })),\n }\n : {}),\n ...(req.toolChoice?.type === 'tool'\n ? { tool_choice: { type: 'function', function: { name: req.toolChoice.name } } }\n : {}),\n ...(req.onDelta ? { stream: true, stream_options: { include_usage: true } } : {}),\n ...(thinking === undefined ? {} : { enable_thinking: thinking }),\n };\n const signals = [AbortSignal.timeout(this.opts.timeoutMs), ...(req.signal ? [req.signal] : [])];\n let res: Response;\n try {\n res = await (this.opts.fetch ?? fetch)(\n `${this.opts.baseUrl.replace(/\\/+$/, '')}/chat/completions`,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...(this.opts.apiKey ? { authorization: `Bearer ${this.opts.apiKey}` } : {}),\n },\n body: JSON.stringify(body),\n signal: AbortSignal.any(signals),\n },\n );\n } catch (err) {\n if (req.signal?.aborted)\n throw new ProviderError('request aborted', { retryable: false, cause: err });\n throw new ProviderError(`network error talking to ${this.name}: ${(err as Error).message}`, {\n retryable: true,\n cause: err,\n });\n }\n let data: ChatResponse;\n try {\n data =\n req.onDelta && res.ok && res.body\n ? await readStream(res.body, req.onDelta)\n : ((await res.json()) as ChatResponse);\n } catch (err) {\n if (req.signal?.aborted)\n throw new ProviderError('request aborted', { retryable: false, cause: err });\n if (err instanceof ProviderError) throw err;\n throw new ProviderError(`${this.name} returned non-JSON (HTTP ${res.status})`, {\n retryable: res.status >= 500,\n status: res.status,\n cause: err,\n });\n }\n if (!res.ok) {\n const s = res.status;\n // OpenAI shape, or FastAPI-style proxies: { \"detail\": \"…\" }.\n const message = data.error?.message ?? (typeof data.detail === 'string' ? data.detail : '');\n if (CONTENT_FILTER.test(`${message} ${String(data.error?.code ?? '')}`))\n throw new ProviderError(`${this.name} content filter refused the request: ${message}`, {\n retryable: false,\n status: s,\n contentFilter: true,\n });\n throw new ProviderError(`${this.name} error ${s}: ${message || 'request failed'}`, {\n retryable: s === 408 || s === 409 || s === 429 || s >= 500,\n status: s,\n ...(s === 401 || s === 403 ? { hint: `check the key for ${this.name}` } : {}),\n });\n }\n const choice = data.choices?.[0];\n const msg = choice?.message;\n const content: ContentBlock[] = [];\n if (msg?.content) content.push({ type: 'text', text: msg.content });\n for (const [i, c] of (msg?.tool_calls ?? []).entries()) {\n const raw = c.function?.arguments;\n let input: unknown;\n try {\n // An empty arguments string means \"no arguments\".\n input = JSON.parse(raw === undefined || raw === '' ? '{}' : raw);\n } catch {\n input = { _unparseable_arguments: c.function?.arguments ?? '' };\n }\n content.push({\n type: 'tool_use',\n id: c.id ?? `call_${i}`,\n name: c.function?.name ?? '',\n input,\n });\n }\n const reasoning = msg?.reasoning ?? msg?.reasoning_content ?? undefined;\n const prompt = data.usage?.prompt_tokens ?? 0;\n const cached = Math.min(prompt, data.usage?.prompt_tokens_details?.cached_tokens ?? 0);\n return {\n model: data.model ?? req.model,\n stopReason: mapStop(\n choice?.finish_reason,\n content.some((b) => b.type === 'tool_use'),\n ),\n content,\n ...(reasoning ? { reasoning } : {}),\n usage: {\n uncached: prompt - cached,\n cacheWrite5m: 0,\n cacheWrite1h: 0,\n cacheRead: cached,\n output: data.usage?.completion_tokens ?? 0,\n },\n };\n }\n}\n\ninterface StreamChunk {\n model?: string;\n choices?: {\n finish_reason?: string | null;\n delta?: {\n content?: string | null;\n reasoning?: string | null;\n reasoning_content?: string | null;\n tool_calls?: {\n index?: number;\n id?: string;\n function?: { name?: string; arguments?: string };\n }[];\n };\n }[];\n usage?: ChatResponse['usage'];\n error?: { message?: string; code?: string | number; type?: string };\n}\n\n/**\n * Assemble a server-sent-events Chat Completions stream into the shape the non-streaming\n * response has, calling `onDelta` for every piece of text and reasoning as it arrives.\n */\nfunction streamErrorStatus(e: { code?: string | number; type?: string }): number {\n const code = Number(e.code);\n if (Number.isInteger(code) && code >= 400 && code < 600) return code;\n if (/invalid_request|invalid_parameter/i.test(`${e.type ?? ''} ${e.code ?? ''}`)) return 400;\n return 502;\n}\n\n/** An error sent inside a 200 stream (proxies and pools do this): typed like an HTTP one. */\nfunction streamError(e: {\n message?: string;\n code?: string | number;\n type?: string;\n}): ProviderError {\n const message = e.message ?? 'stream error';\n const status = streamErrorStatus(e);\n const contentFilter = CONTENT_FILTER.test(`${message} ${e.code ?? ''}`);\n return new ProviderError(`stream error: ${message}`, {\n retryable: !contentFilter && (status === 429 || status >= 500),\n status,\n contentFilter,\n });\n}\n\nasync function readStream(\n body: ReadableStream<Uint8Array>,\n onDelta: NonNullable<CompletionRequest['onDelta']>,\n): Promise<ChatResponse> {\n let text = '';\n let reasoning = '';\n let finish: string | undefined;\n let model: string | undefined;\n let usage: ChatResponse['usage'];\n const calls: { id?: string; name: string; args: string }[] = [];\n const decoder = new TextDecoder();\n let buf = '';\n for await (const chunk of body) {\n buf += decoder.decode(chunk, { stream: true });\n let nl: number;\n while ((nl = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line.startsWith('data:')) continue;\n const payload = line.slice(5).trim();\n if (payload === '[DONE]') continue;\n let c: StreamChunk;\n try {\n c = JSON.parse(payload) as StreamChunk;\n } catch {\n continue;\n }\n if (c.error) throw streamError(c.error);\n model ??= c.model;\n if (c.usage) usage = c.usage;\n const choice = c.choices?.[0];\n if (!choice) continue;\n if (choice.finish_reason) finish = choice.finish_reason;\n const d = choice.delta;\n if (!d) continue;\n const r = d.reasoning ?? d.reasoning_content;\n if (r) {\n reasoning += r;\n onDelta({ reasoning: r });\n }\n if (d.content) {\n text += d.content;\n onDelta({ text: d.content });\n }\n for (const tc of d.tool_calls ?? []) {\n const i = tc.index ?? calls.length;\n const call = (calls[i] ??= { name: '', args: '' });\n if (tc.id) call.id = tc.id;\n if (tc.function?.name) call.name += tc.function.name;\n if (tc.function?.arguments) call.args += tc.function.arguments;\n }\n }\n }\n return {\n ...(model ? { model } : {}),\n ...(usage ? { usage } : {}),\n choices: [\n {\n ...(finish ? { finish_reason: finish } : {}),\n message: {\n content: text || null,\n ...(reasoning ? { reasoning } : {}),\n tool_calls: calls.map((c) => ({\n ...(c.id ? { id: c.id } : {}),\n function: { name: c.name, arguments: c.args },\n })),\n },\n },\n ],\n };\n}\n","import { ProviderError } from '../errors.js';\nimport type {\n CompletionRequest,\n CompletionResponse,\n ContentBlock,\n Provider,\n StopReason,\n} from './types.js';\n\nexport interface ResponsesOptions {\n name: string;\n baseUrl: string;\n apiKey: string | undefined;\n timeoutMs: number;\n fetch?: typeof fetch;\n}\n\ninterface ResponsesOutputItem {\n type?: string;\n text?: string;\n name?: string;\n arguments?: string;\n call_id?: string;\n id?: string;\n}\ninterface ResponsesBody {\n model?: string;\n status?: string;\n incomplete_details?: { reason?: string };\n output?: ResponsesOutputItem[];\n usage?: { input_tokens?: number; output_tokens?: number };\n error?: { message?: string };\n}\n\nfunction toInput(req: CompletionRequest): { role: string; content: unknown }[] {\n const out: { role: string; content: unknown }[] = [];\n if (req.system.length)\n out.push({ role: 'system', content: req.system.map((b) => b.text).join('\\n\\n') });\n for (const m of req.messages) {\n if (m.role === 'assistant') {\n for (const b of m.content) {\n if (b.type === 'text') out.push({ role: 'assistant', content: b.text });\n else if (b.type === 'tool_use')\n out.push({\n role: 'assistant',\n content: [\n {\n type: 'function_call',\n call_id: b.id,\n name: b.name,\n arguments: JSON.stringify(b.input ?? {}),\n },\n ],\n });\n }\n continue;\n }\n for (const b of m.content) {\n if (b.type === 'tool_result')\n out.push({\n role: 'user',\n content: [{ type: 'function_call_output', call_id: b.toolUseId, output: b.content }],\n });\n else if (b.type === 'text') out.push({ role: 'user', content: b.text });\n else if (b.type === 'image')\n out.push({\n role: 'user',\n content: [{ type: 'input_image', image_url: `data:${b.mediaType};base64,${b.data}` }],\n });\n }\n }\n return out;\n}\n\nfunction mapStop(\n status: string | undefined,\n reason: string | undefined,\n hasCalls: boolean,\n): StopReason {\n if (hasCalls) return 'tool_use';\n if (status === 'completed') return 'end_turn';\n if (reason === 'max_output_tokens') return 'max_tokens';\n if (reason === 'content_filter') return 'refusal';\n return 'other';\n}\n\n/**\n * OpenAI Responses API provider (`kind = \"responses\"` endpoint). One POST to\n * `{base_url}/responses` with input items, tools as function tools, and usage\n * mapped to uncached/output. Implements the Provider interface in types.ts.\n */\nexport class ResponsesProvider implements Provider {\n readonly name: string;\n constructor(private readonly opts: ResponsesOptions) {\n this.name = opts.name;\n }\n async complete(req: CompletionRequest): Promise<CompletionResponse> {\n const body = {\n model: req.model,\n input: toInput(req),\n max_output_tokens: req.maxTokens,\n ...(req.tools.length\n ? {\n tools: req.tools.map((t) => ({\n type: 'function',\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n })),\n }\n : {}),\n };\n const signals = [AbortSignal.timeout(this.opts.timeoutMs), ...(req.signal ? [req.signal] : [])];\n let res: Response;\n try {\n res = await (this.opts.fetch ?? fetch)(`${this.opts.baseUrl.replace(/\\/+$/, '')}/responses`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...(this.opts.apiKey ? { authorization: `Bearer ${this.opts.apiKey}` } : {}),\n },\n body: JSON.stringify(body),\n signal: AbortSignal.any(signals),\n });\n } catch (err) {\n if (req.signal?.aborted)\n throw new ProviderError('request aborted', { retryable: false, cause: err });\n throw new ProviderError(`network error talking to ${this.name}: ${(err as Error).message}`, {\n retryable: true,\n cause: err,\n });\n }\n let data: ResponsesBody;\n try {\n data = (await res.json()) as ResponsesBody;\n } catch (err) {\n throw new ProviderError(`${this.name} returned non-JSON (HTTP ${res.status})`, {\n retryable: res.status >= 500,\n status: res.status,\n cause: err,\n });\n }\n if (!res.ok) {\n const s = res.status;\n throw new ProviderError(\n `${this.name} error ${s}: ${data.error?.message ?? 'request failed'}`,\n {\n retryable: s === 408 || s === 409 || s === 429 || s >= 500,\n status: s,\n ...(s === 401 || s === 403 ? { hint: `check the key for ${this.name}` } : {}),\n },\n );\n }\n const content: ContentBlock[] = [];\n for (const item of data.output ?? []) {\n if (item.type === 'function_call') {\n let input: unknown;\n try {\n input = JSON.parse(\n item.arguments === undefined || item.arguments === '' ? '{}' : item.arguments,\n );\n } catch {\n input = { _unparseable_arguments: item.arguments ?? '' };\n }\n content.push({\n type: 'tool_use',\n id: item.call_id ?? item.id ?? 'call_0',\n name: item.name ?? '',\n input,\n });\n } else if (typeof item.text === 'string' && item.text) {\n content.push({ type: 'text', text: item.text });\n }\n }\n return {\n model: data.model ?? req.model,\n stopReason: mapStop(\n data.status,\n data.incomplete_details?.reason,\n content.some((b) => b.type === 'tool_use'),\n ),\n content,\n usage: {\n uncached: data.usage?.input_tokens ?? 0,\n cacheWrite5m: 0,\n cacheWrite1h: 0,\n cacheRead: 0,\n output: data.usage?.output_tokens ?? 0,\n },\n };\n }\n}\n","import type { EndpointConfig } from '../config/schema.js';\nimport { GeminiProvider } from './gemini.js';\nimport { OpenAICompatProvider, type OpenAICompatOptions } from './openai-compat.js';\nimport { ResponsesProvider } from './responses.js';\nimport type { Provider } from './types.js';\n\nconst MAKERS: Record<EndpointConfig['kind'], (o: OpenAICompatOptions) => Provider> = {\n openai: (o) => new OpenAICompatProvider(o),\n responses: (o) => new ResponsesProvider(o),\n gemini: (o) => new GeminiProvider(o),\n};\n\n/** The client for an endpoint's API kind. */\nexport function makeEndpointProvider(\n kind: EndpointConfig['kind'],\n opts: OpenAICompatOptions,\n): Provider {\n return MAKERS[kind](opts);\n}\n"],"mappings":"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","names":["parts","mapStop","mapStop"]}
|