demian-cli 1.0.7 → 1.0.9
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 +13 -2
- package/dist/cli.mjs +1838 -359
- package/dist/index.mjs +1022 -260
- package/dist/tui.mjs +27004 -25460
- package/dist/vscode-worker.mjs +1607 -295
- package/docs/ko/README.md +6 -2
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -92,7 +92,7 @@ var claudeCodeAgent = {
|
|
|
92
92
|
name: "claudecode",
|
|
93
93
|
description: "Claude Code external coding agent for focused delegated workspace tasks.",
|
|
94
94
|
mode: "subagent",
|
|
95
|
-
provider: { profile: "claudecode
|
|
95
|
+
provider: { profile: "claudecode", permissionProfile: "build" },
|
|
96
96
|
tools: [],
|
|
97
97
|
systemPrompt: [
|
|
98
98
|
"You are demian claudecode, a Claude Code external runtime working as a sub agent for Demian.",
|
|
@@ -137,7 +137,7 @@ var claudeCodeExplorerAgent = {
|
|
|
137
137
|
name: "claudecode-explorer",
|
|
138
138
|
description: "Claude Code external read-only explorer for cowork repository inspection.",
|
|
139
139
|
mode: "subagent",
|
|
140
|
-
provider: { profile: "claudecode
|
|
140
|
+
provider: { profile: "claudecode", permissionProfile: "explore" },
|
|
141
141
|
tools: [],
|
|
142
142
|
systemPrompt: [
|
|
143
143
|
"You are demian claudecode-explorer, a Claude Code external runtime working as a read-only cowork sub agent for Demian.",
|
|
@@ -172,7 +172,7 @@ var claudeCodeBuilderAgent = {
|
|
|
172
172
|
name: "claudecode-builder",
|
|
173
173
|
description: "Claude Code-backed builder for bounded cowork implementation tasks.",
|
|
174
174
|
mode: "subagent",
|
|
175
|
-
provider: { profile: "claudecode
|
|
175
|
+
provider: { profile: "claudecode", permissionProfile: "build" },
|
|
176
176
|
tools: [],
|
|
177
177
|
systemPrompt: [
|
|
178
178
|
"You are demian claudecode-builder, a Claude Code external runtime working as a writer cowork sub agent for Demian.",
|
|
@@ -477,7 +477,7 @@ import { readFile as readFile6 } from "node:fs/promises";
|
|
|
477
477
|
import crypto4 from "node:crypto";
|
|
478
478
|
import fs7 from "node:fs";
|
|
479
479
|
import os7 from "node:os";
|
|
480
|
-
import
|
|
480
|
+
import path13 from "node:path";
|
|
481
481
|
|
|
482
482
|
// src/providers/retry.ts
|
|
483
483
|
var RETRY_STATUS = /* @__PURE__ */ new Set([408, 409, 425, 429, 500, 502, 503, 504]);
|
|
@@ -870,12 +870,14 @@ var dynamicImport = new Function("specifier", "return import(specifier)");
|
|
|
870
870
|
var AnthropicProvider = class {
|
|
871
871
|
id = "anthropic";
|
|
872
872
|
#apiKey;
|
|
873
|
+
#baseURL;
|
|
873
874
|
#defaultModel;
|
|
874
875
|
#defaultMaxTokens;
|
|
875
876
|
#client;
|
|
876
877
|
#onRetry;
|
|
877
878
|
constructor(config) {
|
|
878
879
|
this.#apiKey = config.apiKey;
|
|
880
|
+
this.#baseURL = config.baseURL;
|
|
879
881
|
this.#defaultModel = config.defaultModel;
|
|
880
882
|
this.#defaultMaxTokens = config.defaultMaxTokens ?? 4096;
|
|
881
883
|
this.#client = config.client;
|
|
@@ -920,7 +922,7 @@ var AnthropicProvider = class {
|
|
|
920
922
|
if (this.#client) return this.#client;
|
|
921
923
|
if (!this.#apiKey) throw new Error("AnthropicProvider requires apiKey");
|
|
922
924
|
const Anthropic = await loadAnthropicConstructor();
|
|
923
|
-
this.#client = new Anthropic({ apiKey: this.#apiKey });
|
|
925
|
+
this.#client = new Anthropic({ apiKey: this.#apiKey, baseURL: this.#baseURL });
|
|
924
926
|
return this.#client;
|
|
925
927
|
}
|
|
926
928
|
};
|
|
@@ -1153,19 +1155,56 @@ import { execFile } from "node:child_process";
|
|
|
1153
1155
|
import { promisify } from "node:util";
|
|
1154
1156
|
import fs2 from "node:fs";
|
|
1155
1157
|
import { chmod, mkdir as mkdir2, readFile as readFile2, rename, writeFile as writeFile2 } from "node:fs/promises";
|
|
1156
|
-
import
|
|
1158
|
+
import path3 from "node:path";
|
|
1157
1159
|
|
|
1158
1160
|
// src/providers/codex-state.ts
|
|
1159
1161
|
import crypto from "node:crypto";
|
|
1160
1162
|
import fs from "node:fs";
|
|
1161
1163
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1164
|
+
import os2 from "node:os";
|
|
1165
|
+
import path2 from "node:path";
|
|
1166
|
+
|
|
1167
|
+
// src/path-expansion.ts
|
|
1162
1168
|
import os from "node:os";
|
|
1163
1169
|
import path from "node:path";
|
|
1170
|
+
function expandPathReferences(value) {
|
|
1171
|
+
let expanded = expandTilde(value);
|
|
1172
|
+
expanded = expanded.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, braced, bare) => envPathValue(braced ?? bare) ?? match);
|
|
1173
|
+
expanded = expanded.replace(/%([A-Za-z_][A-Za-z0-9_]*)%/g, (match, name) => envPathValue(name) ?? match);
|
|
1174
|
+
return expanded;
|
|
1175
|
+
}
|
|
1176
|
+
function resolveExpandedPath(value) {
|
|
1177
|
+
return path.resolve(expandPathReferences(value));
|
|
1178
|
+
}
|
|
1179
|
+
function expandTilde(value) {
|
|
1180
|
+
if (value === "~") return os.homedir();
|
|
1181
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) return path.join(os.homedir(), value.slice(2));
|
|
1182
|
+
return value;
|
|
1183
|
+
}
|
|
1184
|
+
function envPathValue(name) {
|
|
1185
|
+
if (!name) return void 0;
|
|
1186
|
+
const value = processEnvValue(name);
|
|
1187
|
+
if (value !== void 0) return value;
|
|
1188
|
+
const upper = name.toUpperCase();
|
|
1189
|
+
if (upper === "HOME" || upper === "USERPROFILE") return os.homedir();
|
|
1190
|
+
if (process.platform === "win32" && upper === "HOMEDRIVE") return path.win32.parse(os.homedir()).root.replace(/[\\/]$/, "");
|
|
1191
|
+
if (process.platform === "win32" && upper === "HOMEPATH") return os.homedir().replace(/^[A-Za-z]:/, "");
|
|
1192
|
+
return void 0;
|
|
1193
|
+
}
|
|
1194
|
+
function processEnvValue(name) {
|
|
1195
|
+
if (process.env[name] !== void 0) return process.env[name];
|
|
1196
|
+
if (process.platform !== "win32") return void 0;
|
|
1197
|
+
const upper = name.toUpperCase();
|
|
1198
|
+
const key = Object.keys(process.env).find((item) => item.toUpperCase() === upper);
|
|
1199
|
+
return key ? process.env[key] : void 0;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/providers/codex-state.ts
|
|
1164
1203
|
function resolveCodexHome(configured) {
|
|
1165
|
-
return
|
|
1204
|
+
return resolveExpandedPath(configured ?? process.env.CODEX_HOME ?? path2.join(os2.homedir(), ".codex"));
|
|
1166
1205
|
}
|
|
1167
1206
|
async function loadOrCreateInstallationId(codexHome) {
|
|
1168
|
-
const filePath =
|
|
1207
|
+
const filePath = path2.join(codexHome, "installation_id");
|
|
1169
1208
|
try {
|
|
1170
1209
|
const existing = (await readFile(filePath, "utf8")).trim();
|
|
1171
1210
|
if (isUuid(existing)) return existing;
|
|
@@ -1187,15 +1226,6 @@ function isNodeError(error, code) {
|
|
|
1187
1226
|
function fileExists(filePath) {
|
|
1188
1227
|
return fs.existsSync(filePath);
|
|
1189
1228
|
}
|
|
1190
|
-
function expandCodexPath(value) {
|
|
1191
|
-
let expanded = value;
|
|
1192
|
-
if (expanded === "~") return os.homedir();
|
|
1193
|
-
if (expanded.startsWith("~/")) expanded = path.join(os.homedir(), expanded.slice(2));
|
|
1194
|
-
return expanded.replace(/\$\{([A-Z_][A-Z0-9_]*)\}|\$([A-Z_][A-Z0-9_]*)/gi, (match, braced, bare) => {
|
|
1195
|
-
const name = braced ?? bare;
|
|
1196
|
-
return name && process.env[name] !== void 0 ? process.env[name] : match;
|
|
1197
|
-
});
|
|
1198
|
-
}
|
|
1199
1229
|
|
|
1200
1230
|
// src/providers/codex-auth.ts
|
|
1201
1231
|
var execFileAsync = promisify(execFile);
|
|
@@ -1418,10 +1448,10 @@ var CodexAuthStore = class {
|
|
|
1418
1448
|
}
|
|
1419
1449
|
};
|
|
1420
1450
|
function authFilePath(codexHome) {
|
|
1421
|
-
return
|
|
1451
|
+
return path3.join(codexHome, "auth.json");
|
|
1422
1452
|
}
|
|
1423
1453
|
function codexKeyringAccount(codexHome) {
|
|
1424
|
-
const resolved =
|
|
1454
|
+
const resolved = path3.resolve(codexHome);
|
|
1425
1455
|
const canonical = fs2.existsSync(resolved) ? fs2.realpathSync.native(resolved) : resolved;
|
|
1426
1456
|
const hash = crypto2.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
1427
1457
|
return `cli|${hash}`;
|
|
@@ -1856,8 +1886,8 @@ import { randomUUID } from "node:crypto";
|
|
|
1856
1886
|
import { execFile as execFile2 } from "node:child_process";
|
|
1857
1887
|
import fs3 from "node:fs";
|
|
1858
1888
|
import { chmod as chmod2, mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
|
|
1859
|
-
import
|
|
1860
|
-
import
|
|
1889
|
+
import os3 from "node:os";
|
|
1890
|
+
import path4 from "node:path";
|
|
1861
1891
|
import { promisify as promisify2 } from "node:util";
|
|
1862
1892
|
var execFileAsync2 = promisify2(execFile2);
|
|
1863
1893
|
var CLAUDE_CODE_KEYRING_SERVICE = "Claude Code-credentials";
|
|
@@ -1890,7 +1920,7 @@ function getSharedClaudeCodeAuthStore(options = {}) {
|
|
|
1890
1920
|
proactiveRefreshMinutes: options.proactiveRefreshMinutes ?? 30,
|
|
1891
1921
|
refreshCache: options.refreshCache ?? "claude-store",
|
|
1892
1922
|
refreshTokenURL: options.refreshTokenURL ?? DEFAULT_CLAUDE_CODE_REFRESH_TOKEN_URL,
|
|
1893
|
-
keychainAccount: options.keychainAccount ??
|
|
1923
|
+
keychainAccount: options.keychainAccount ?? os3.userInfo().username
|
|
1894
1924
|
});
|
|
1895
1925
|
const existing = sharedAuthStores2.get(key);
|
|
1896
1926
|
if (existing) return existing;
|
|
@@ -1919,7 +1949,7 @@ var ClaudeCodeAuthStore = class {
|
|
|
1919
1949
|
this.#proactiveRefreshMinutes = options.proactiveRefreshMinutes ?? 30;
|
|
1920
1950
|
this.#refreshCache = options.refreshCache ?? "claude-store";
|
|
1921
1951
|
this.#refreshTokenURL = options.refreshTokenURL ?? DEFAULT_CLAUDE_CODE_REFRESH_TOKEN_URL;
|
|
1922
|
-
this.#keychainAccount = options.keychainAccount ??
|
|
1952
|
+
this.#keychainAccount = options.keychainAccount ?? os3.userInfo().username;
|
|
1923
1953
|
this.#fetch = options.fetch ?? fetch;
|
|
1924
1954
|
this.#keyring = options.keyring ?? new MacOSSecurityKeyring2();
|
|
1925
1955
|
}
|
|
@@ -2120,10 +2150,10 @@ var ClaudeCodeAuthStore = class {
|
|
|
2120
2150
|
}
|
|
2121
2151
|
};
|
|
2122
2152
|
function resolveClaudeConfigDir(configured) {
|
|
2123
|
-
return
|
|
2153
|
+
return resolveExpandedPath(configured ?? process.env.CLAUDE_CONFIG_DIR ?? path4.join(os3.homedir(), ".claude"));
|
|
2124
2154
|
}
|
|
2125
2155
|
function claudeCodeAuthFilePath(claudeConfigDir) {
|
|
2126
|
-
return
|
|
2156
|
+
return path4.join(claudeConfigDir, ".credentials.json");
|
|
2127
2157
|
}
|
|
2128
2158
|
function oauthPayload(auth) {
|
|
2129
2159
|
const raw = auth.claudeAiOauth;
|
|
@@ -2182,15 +2212,6 @@ async function writeClaudeCodeAuthFileAtomic(claudeConfigDir, auth) {
|
|
|
2182
2212
|
await chmod2(tempPath, 384);
|
|
2183
2213
|
await rename2(tempPath, filePath);
|
|
2184
2214
|
}
|
|
2185
|
-
function expandClaudePath(value) {
|
|
2186
|
-
let expanded = value;
|
|
2187
|
-
if (expanded === "~") return os2.homedir();
|
|
2188
|
-
if (expanded.startsWith("~/")) expanded = path3.join(os2.homedir(), expanded.slice(2));
|
|
2189
|
-
return expanded.replace(/\$\{([A-Z_][A-Z0-9_]*)\}|\$([A-Z_][A-Z0-9_]*)/gi, (match, braced, bare) => {
|
|
2190
|
-
const name = braced ?? bare;
|
|
2191
|
-
return name && process.env[name] !== void 0 ? process.env[name] : match;
|
|
2192
|
-
});
|
|
2193
|
-
}
|
|
2194
2215
|
var MacOSSecurityKeyring2 = class {
|
|
2195
2216
|
async load(service, account) {
|
|
2196
2217
|
if (process.platform !== "darwin") return void 0;
|
|
@@ -2427,6 +2448,111 @@ function parseClaudeCodeErrorBody(body) {
|
|
|
2427
2448
|
}
|
|
2428
2449
|
}
|
|
2429
2450
|
|
|
2451
|
+
// src/providers/ollama.ts
|
|
2452
|
+
var OllamaProvider = class {
|
|
2453
|
+
id = "ollama";
|
|
2454
|
+
#baseURL;
|
|
2455
|
+
#apiKey;
|
|
2456
|
+
#defaultModel;
|
|
2457
|
+
#fetch;
|
|
2458
|
+
#onRetry;
|
|
2459
|
+
constructor(config) {
|
|
2460
|
+
this.#baseURL = config.baseURL.replace(/\/+$/, "");
|
|
2461
|
+
this.#apiKey = config.apiKey;
|
|
2462
|
+
this.#defaultModel = config.defaultModel;
|
|
2463
|
+
this.#fetch = config.fetch ?? fetch;
|
|
2464
|
+
this.#onRetry = config.onRetry;
|
|
2465
|
+
}
|
|
2466
|
+
async chat(req) {
|
|
2467
|
+
return chatWithRetry(() => this.#rawChat(req), {
|
|
2468
|
+
signal: req.signal,
|
|
2469
|
+
onRetry: this.#onRetry
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
async #rawChat(req) {
|
|
2473
|
+
const response = await this.#fetch(`${this.#baseURL}/chat`, {
|
|
2474
|
+
method: "POST",
|
|
2475
|
+
headers: {
|
|
2476
|
+
"content-type": "application/json",
|
|
2477
|
+
...this.#authHeaders()
|
|
2478
|
+
},
|
|
2479
|
+
body: JSON.stringify({
|
|
2480
|
+
model: req.model || this.#defaultModel,
|
|
2481
|
+
messages: req.messages.map(toOllamaMessage),
|
|
2482
|
+
...req.tools.length > 0 ? { tools: req.tools.map(toOpenAITool) } : {},
|
|
2483
|
+
stream: false,
|
|
2484
|
+
...req.temperature !== void 0 ? { options: { temperature: req.temperature } } : {}
|
|
2485
|
+
}),
|
|
2486
|
+
signal: req.signal
|
|
2487
|
+
});
|
|
2488
|
+
const text = await response.text();
|
|
2489
|
+
if (!response.ok) throw new ProviderHttpError(response.status, text, parseRetryAfter(response.headers.get("retry-after")));
|
|
2490
|
+
return normalizeOllamaResponse(text ? JSON.parse(text) : {});
|
|
2491
|
+
}
|
|
2492
|
+
#authHeaders() {
|
|
2493
|
+
return this.#apiKey ? { authorization: `Bearer ${this.#apiKey}` } : {};
|
|
2494
|
+
}
|
|
2495
|
+
};
|
|
2496
|
+
function toOllamaMessage(message) {
|
|
2497
|
+
if (message.role === "tool") {
|
|
2498
|
+
return {
|
|
2499
|
+
role: "tool",
|
|
2500
|
+
content: message.content,
|
|
2501
|
+
tool_name: message.name
|
|
2502
|
+
};
|
|
2503
|
+
}
|
|
2504
|
+
const out = {
|
|
2505
|
+
role: message.role,
|
|
2506
|
+
content: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? "")
|
|
2507
|
+
};
|
|
2508
|
+
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
2509
|
+
out.tool_calls = message.toolCalls.map((call) => ({
|
|
2510
|
+
function: {
|
|
2511
|
+
name: call.name,
|
|
2512
|
+
arguments: call.input ?? {}
|
|
2513
|
+
}
|
|
2514
|
+
}));
|
|
2515
|
+
}
|
|
2516
|
+
return out;
|
|
2517
|
+
}
|
|
2518
|
+
function normalizeOllamaResponse(raw) {
|
|
2519
|
+
const data = raw;
|
|
2520
|
+
const toolCalls = [];
|
|
2521
|
+
for (const call of data.message?.tool_calls ?? []) {
|
|
2522
|
+
const name = call.function?.name;
|
|
2523
|
+
if (!name) continue;
|
|
2524
|
+
toolCalls.push({
|
|
2525
|
+
id: call.id ?? `call_${toolCalls.length + 1}`,
|
|
2526
|
+
name,
|
|
2527
|
+
input: normalizeToolArguments(call.function?.arguments)
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
const message = {
|
|
2531
|
+
role: "assistant",
|
|
2532
|
+
content: data.message?.content ?? null,
|
|
2533
|
+
...toolCalls.length ? { toolCalls } : {}
|
|
2534
|
+
};
|
|
2535
|
+
return {
|
|
2536
|
+
message,
|
|
2537
|
+
toolCalls,
|
|
2538
|
+
stopReason: toolCalls.length ? "tool_use" : data.done_reason === "length" ? "max_tokens" : "end_turn",
|
|
2539
|
+
usage: data.prompt_eval_count !== void 0 || data.eval_count !== void 0 ? {
|
|
2540
|
+
inputTokens: data.prompt_eval_count,
|
|
2541
|
+
outputTokens: data.eval_count,
|
|
2542
|
+
totalTokens: (data.prompt_eval_count ?? 0) + (data.eval_count ?? 0)
|
|
2543
|
+
} : void 0,
|
|
2544
|
+
raw
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
function normalizeToolArguments(value) {
|
|
2548
|
+
if (typeof value !== "string") return value ?? {};
|
|
2549
|
+
try {
|
|
2550
|
+
return JSON.parse(value);
|
|
2551
|
+
} catch {
|
|
2552
|
+
return {};
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2430
2556
|
// src/external-runtime/claudecode-cli.ts
|
|
2431
2557
|
import { spawn as spawn3 } from "node:child_process";
|
|
2432
2558
|
import readline from "node:readline";
|
|
@@ -2434,7 +2560,7 @@ import readline from "node:readline";
|
|
|
2434
2560
|
// src/external-runtime/claudecode-attachments.ts
|
|
2435
2561
|
import crypto3 from "node:crypto";
|
|
2436
2562
|
import fs4 from "node:fs";
|
|
2437
|
-
import
|
|
2563
|
+
import path5 from "node:path";
|
|
2438
2564
|
async function resolveClaudeCodeAttachmentPrompt(runtimeLabel, config, req) {
|
|
2439
2565
|
const attachments = req.attachments ?? [];
|
|
2440
2566
|
if (attachments.length === 0) return req.prompt;
|
|
@@ -2476,7 +2602,7 @@ function unsupportedAttachmentError(runtimeLabel, count, detail) {
|
|
|
2476
2602
|
}
|
|
2477
2603
|
function formatAttachmentReference(value, cwd) {
|
|
2478
2604
|
if (isUrl(value)) return imageMimeFromPath(value) ? `[image: ${value} (${imageMimeFromPath(value)}, remote)]` : `[attachment: ${value}]`;
|
|
2479
|
-
const target =
|
|
2605
|
+
const target = path5.isAbsolute(value) ? value : path5.resolve(cwd, value);
|
|
2480
2606
|
try {
|
|
2481
2607
|
const stats = fs4.statSync(target);
|
|
2482
2608
|
if (!stats.isFile()) return `[attachment: ${value} (${stats.size} bytes)]`;
|
|
@@ -2493,7 +2619,7 @@ function isUrl(value) {
|
|
|
2493
2619
|
}
|
|
2494
2620
|
function imageMimeFromPath(value) {
|
|
2495
2621
|
const pathname = isUrl(value) ? new URL(value).pathname : value;
|
|
2496
|
-
const ext =
|
|
2622
|
+
const ext = path5.extname(pathname).toLowerCase();
|
|
2497
2623
|
if (ext === ".png") return "image/png";
|
|
2498
2624
|
if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
|
|
2499
2625
|
if (ext === ".gif") return "image/gif";
|
|
@@ -2525,15 +2651,14 @@ function hasAnthropicApiKeyEnv(env = process.env) {
|
|
|
2525
2651
|
|
|
2526
2652
|
// src/external-runtime/claudecode-paths.ts
|
|
2527
2653
|
import fs5 from "node:fs";
|
|
2528
|
-
import
|
|
2529
|
-
import path5 from "node:path";
|
|
2654
|
+
import path6 from "node:path";
|
|
2530
2655
|
function resolveClaudeCodeCliPath(configured) {
|
|
2531
2656
|
if (configured) return expandHome(configured);
|
|
2532
2657
|
if (process.env.CLAUDE_CODE_CLI) return expandHome(process.env.CLAUDE_CODE_CLI);
|
|
2533
2658
|
const candidates = ["~/.local/bin/claude", "claude"];
|
|
2534
2659
|
for (const candidate of candidates) {
|
|
2535
2660
|
const expanded = expandHome(candidate);
|
|
2536
|
-
if (
|
|
2661
|
+
if (path6.isAbsolute(expanded)) {
|
|
2537
2662
|
if (isExecutableFile(expanded)) return expanded;
|
|
2538
2663
|
continue;
|
|
2539
2664
|
}
|
|
@@ -2542,9 +2667,7 @@ function resolveClaudeCodeCliPath(configured) {
|
|
|
2542
2667
|
return void 0;
|
|
2543
2668
|
}
|
|
2544
2669
|
function expandHome(value) {
|
|
2545
|
-
|
|
2546
|
-
if (value.startsWith("~/")) return path5.join(os3.homedir(), value.slice(2));
|
|
2547
|
-
return value;
|
|
2670
|
+
return expandPathReferences(value);
|
|
2548
2671
|
}
|
|
2549
2672
|
function isExecutableFile(filePath) {
|
|
2550
2673
|
try {
|
|
@@ -2810,14 +2933,14 @@ function numberValue2(value) {
|
|
|
2810
2933
|
// src/external-runtime/session-lock.ts
|
|
2811
2934
|
import { mkdir as mkdir4, open, readFile as readFile4, unlink } from "node:fs/promises";
|
|
2812
2935
|
import os4 from "node:os";
|
|
2813
|
-
import
|
|
2936
|
+
import path7 from "node:path";
|
|
2814
2937
|
var DEFAULT_STALE_MS = 30 * 60 * 1e3;
|
|
2815
2938
|
async function acquireClaudeCodeSessionLock(sessionId, options = {}) {
|
|
2816
|
-
const dir = options.dir ??
|
|
2939
|
+
const dir = options.dir ?? path7.join(os4.homedir(), ".demian", "claude-sessions");
|
|
2817
2940
|
const staleMs = options.staleMs ?? DEFAULT_STALE_MS;
|
|
2818
2941
|
const now2 = options.now ?? (() => Date.now());
|
|
2819
2942
|
await mkdir4(dir, { recursive: true });
|
|
2820
|
-
const lockPath =
|
|
2943
|
+
const lockPath = path7.join(dir, `${encodeURIComponent(sessionId)}.lock`);
|
|
2821
2944
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2822
2945
|
try {
|
|
2823
2946
|
const handle = await open(lockPath, "wx");
|
|
@@ -2881,14 +3004,14 @@ function isAlreadyExists(error) {
|
|
|
2881
3004
|
// src/external-runtime/usage-ledger.ts
|
|
2882
3005
|
import { mkdir as mkdir5, readFile as readFile5, rename as rename3, writeFile as writeFile4 } from "node:fs/promises";
|
|
2883
3006
|
import os5 from "node:os";
|
|
2884
|
-
import
|
|
3007
|
+
import path8 from "node:path";
|
|
2885
3008
|
var processLedger = /* @__PURE__ */ new Map();
|
|
2886
3009
|
var ClaudeCodeUsageLedger = class {
|
|
2887
3010
|
#scope;
|
|
2888
3011
|
#filePath;
|
|
2889
3012
|
constructor(options = {}) {
|
|
2890
3013
|
this.#scope = options.scope ?? "process";
|
|
2891
|
-
this.#filePath = options.filePath ??
|
|
3014
|
+
this.#filePath = options.filePath ?? path8.join(os5.homedir(), ".demian", "usage-ledger.json");
|
|
2892
3015
|
}
|
|
2893
3016
|
async spentUsd(key, now2 = /* @__PURE__ */ new Date()) {
|
|
2894
3017
|
if (this.#scope === "process") return processLedger.get(processKey(key)) ?? 0;
|
|
@@ -2919,7 +3042,7 @@ var ClaudeCodeUsageLedger = class {
|
|
|
2919
3042
|
return { version: 1, buckets: {} };
|
|
2920
3043
|
}
|
|
2921
3044
|
async #write(file) {
|
|
2922
|
-
await mkdir5(
|
|
3045
|
+
await mkdir5(path8.dirname(this.#filePath), { recursive: true });
|
|
2923
3046
|
const temp = `${this.#filePath}.${process.pid}.tmp`;
|
|
2924
3047
|
await writeFile4(temp, `${JSON.stringify(file, null, 2)}
|
|
2925
3048
|
`, "utf8");
|
|
@@ -22580,10 +22703,10 @@ function now() {
|
|
|
22580
22703
|
}
|
|
22581
22704
|
|
|
22582
22705
|
// src/permissions/engine.ts
|
|
22583
|
-
import
|
|
22706
|
+
import path11 from "node:path";
|
|
22584
22707
|
|
|
22585
22708
|
// src/permissions/grants.ts
|
|
22586
|
-
import
|
|
22709
|
+
import path9 from "node:path";
|
|
22587
22710
|
|
|
22588
22711
|
// src/util.ts
|
|
22589
22712
|
function stableStringify(value) {
|
|
@@ -22638,8 +22761,8 @@ function grantKeyFor(tool, input2, cwd) {
|
|
|
22638
22761
|
}
|
|
22639
22762
|
if (tool === "write_file" || tool === "edit_file") {
|
|
22640
22763
|
const filePath = typeof object2.path === "string" ? object2.path : "";
|
|
22641
|
-
const parent = filePath ?
|
|
22642
|
-
return `${tool}:${
|
|
22764
|
+
const parent = filePath ? path9.dirname(path9.resolve(cwd, filePath)) : cwd;
|
|
22765
|
+
return `${tool}:${path9.relative(cwd, parent) || "."}`;
|
|
22643
22766
|
}
|
|
22644
22767
|
return `${tool}:${stableStringify(input2)}`;
|
|
22645
22768
|
}
|
|
@@ -22666,30 +22789,30 @@ function firstCommandTokens(command, count) {
|
|
|
22666
22789
|
}
|
|
22667
22790
|
|
|
22668
22791
|
// src/workspace/paths.ts
|
|
22669
|
-
import
|
|
22792
|
+
import path10 from "node:path";
|
|
22670
22793
|
function normalizePathForMatch(value) {
|
|
22671
|
-
return value.split(
|
|
22794
|
+
return value.split(path10.sep).join("/");
|
|
22672
22795
|
}
|
|
22673
22796
|
function isInsidePath(root, candidate) {
|
|
22674
|
-
const relative =
|
|
22675
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
22797
|
+
const relative = path10.relative(path10.resolve(root), path10.resolve(candidate));
|
|
22798
|
+
return relative === "" || !relative.startsWith("..") && !path10.isAbsolute(relative);
|
|
22676
22799
|
}
|
|
22677
22800
|
function resolveInsideCwd(cwd, inputPath) {
|
|
22678
22801
|
if (typeof inputPath !== "string" || inputPath.length === 0) {
|
|
22679
22802
|
throw new Error("path must be a non-empty string");
|
|
22680
22803
|
}
|
|
22681
|
-
const resolved =
|
|
22804
|
+
const resolved = path10.resolve(cwd, inputPath);
|
|
22682
22805
|
if (!isInsidePath(cwd, resolved)) {
|
|
22683
22806
|
throw new Error(`Path is outside the workspace: ${inputPath}`);
|
|
22684
22807
|
}
|
|
22685
22808
|
return resolved;
|
|
22686
22809
|
}
|
|
22687
22810
|
function relativeToCwd(cwd, absolutePath) {
|
|
22688
|
-
const relative =
|
|
22811
|
+
const relative = path10.relative(cwd, absolutePath);
|
|
22689
22812
|
return normalizePathForMatch(relative || ".");
|
|
22690
22813
|
}
|
|
22691
22814
|
function isEnvFile(filePath) {
|
|
22692
|
-
const base =
|
|
22815
|
+
const base = path10.basename(filePath);
|
|
22693
22816
|
if (base === ".env.example") return false;
|
|
22694
22817
|
return base === ".env" || base.startsWith(".env.");
|
|
22695
22818
|
}
|
|
@@ -22796,7 +22919,7 @@ var PermissionEngine = class {
|
|
|
22796
22919
|
#hardDeny(tool, input2, grantKey) {
|
|
22797
22920
|
const paths = inputPaths(tool, input2);
|
|
22798
22921
|
for (const item of paths) {
|
|
22799
|
-
const resolved =
|
|
22922
|
+
const resolved = path11.resolve(this.#cwd, item);
|
|
22800
22923
|
if (!isInsidePath(this.#cwd, resolved)) {
|
|
22801
22924
|
return {
|
|
22802
22925
|
decision: "deny",
|
|
@@ -22850,7 +22973,7 @@ function matchesRule(rule, tool, input2, cwd) {
|
|
|
22850
22973
|
if (rule.match.pathGlob) {
|
|
22851
22974
|
const paths = inputPaths(tool, input2);
|
|
22852
22975
|
if (paths.length === 0) return false;
|
|
22853
|
-
if (!paths.some((item) => matchesPathRule(rule.match?.pathGlob, relativeToCwd(cwd,
|
|
22976
|
+
if (!paths.some((item) => matchesPathRule(rule.match?.pathGlob, relativeToCwd(cwd, path11.resolve(cwd, item))))) {
|
|
22854
22977
|
return false;
|
|
22855
22978
|
}
|
|
22856
22979
|
}
|
|
@@ -22897,7 +23020,7 @@ function rulePriority(rule, ref) {
|
|
|
22897
23020
|
}
|
|
22898
23021
|
function matchesPathRule(pattern, relativePath) {
|
|
22899
23022
|
if (!pattern) return true;
|
|
22900
|
-
if (
|
|
23023
|
+
if (path11.basename(relativePath) === ".env.example" && pattern.includes(".env.*")) return false;
|
|
22901
23024
|
return matchGlob(pattern, relativePath);
|
|
22902
23025
|
}
|
|
22903
23026
|
function mostSpecificRule(rules, input2, cwd, ref) {
|
|
@@ -22975,13 +23098,13 @@ function permissionLabel(req) {
|
|
|
22975
23098
|
if ((req.tool === "write_file" || req.tool === "edit_file" || req.tool === "read_file") && typeof inputObject.path === "string") {
|
|
22976
23099
|
return `${req.tool}: ${inputObject.path}`;
|
|
22977
23100
|
}
|
|
22978
|
-
const
|
|
22979
|
-
if (
|
|
23101
|
+
const path29 = typeof inputObject.path === "string" ? inputObject.path : typeof inputObject.file_path === "string" ? inputObject.file_path : void 0;
|
|
23102
|
+
if (path29) return `${tool}: ${path29}`;
|
|
22980
23103
|
return tool;
|
|
22981
23104
|
}
|
|
22982
23105
|
|
|
22983
23106
|
// src/workspace/write-scope.ts
|
|
22984
|
-
import
|
|
23107
|
+
import path12 from "node:path";
|
|
22985
23108
|
var WRITE_TOOLS = /* @__PURE__ */ new Set(["write_file", "edit_file", "Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
22986
23109
|
var WORKSPACE_SCOPE = "**";
|
|
22987
23110
|
function normalizeWriteScope(cwd, scope) {
|
|
@@ -23003,7 +23126,7 @@ function enforceToolWriteScope(input2) {
|
|
|
23003
23126
|
if (paths.length === 0) return { ok: true, paths: [] };
|
|
23004
23127
|
const checked = [];
|
|
23005
23128
|
for (const target of paths) {
|
|
23006
|
-
const resolved =
|
|
23129
|
+
const resolved = path12.resolve(input2.cwd, target);
|
|
23007
23130
|
const relative = relativeToCwd(input2.cwd, resolved);
|
|
23008
23131
|
checked.push(relative);
|
|
23009
23132
|
const allowed = isPathAllowedByWriteScope(input2.cwd, target, input2.writeScope);
|
|
@@ -23027,7 +23150,7 @@ function outOfScopeDiffFiles(cwd, summary, writeScope) {
|
|
|
23027
23150
|
return out;
|
|
23028
23151
|
}
|
|
23029
23152
|
function isPathAllowedByWriteScope(cwd, inputPath, writeScope) {
|
|
23030
|
-
const resolved =
|
|
23153
|
+
const resolved = path12.resolve(cwd, inputPath);
|
|
23031
23154
|
if (!isInsidePath(cwd, resolved)) {
|
|
23032
23155
|
return { ok: false, error: "path is outside the workspace", paths: [inputPath] };
|
|
23033
23156
|
}
|
|
@@ -23046,9 +23169,9 @@ function normalizeScopePattern(cwd, raw) {
|
|
|
23046
23169
|
if (!value) return { ok: false, error: "writeScope entries must be non-empty." };
|
|
23047
23170
|
if (value.includes("\0")) return { ok: false, error: "writeScope entries must not contain NUL bytes." };
|
|
23048
23171
|
const hasGlob2 = /[*?[\]{}]/.test(value);
|
|
23049
|
-
if (
|
|
23172
|
+
if (path12.isAbsolute(value)) {
|
|
23050
23173
|
if (hasGlob2) return { ok: false, error: `writeScope absolute globs are not supported: ${raw}` };
|
|
23051
|
-
const resolved =
|
|
23174
|
+
const resolved = path12.resolve(value);
|
|
23052
23175
|
if (!isInsidePath(cwd, resolved)) return { ok: false, error: `writeScope path is outside the workspace: ${raw}` };
|
|
23053
23176
|
value = relativeToCwd(cwd, resolved);
|
|
23054
23177
|
}
|
|
@@ -23425,6 +23548,7 @@ function claudeCodePreviewEnabled(config = {}, env = process.env) {
|
|
|
23425
23548
|
}
|
|
23426
23549
|
|
|
23427
23550
|
// src/config.ts
|
|
23551
|
+
var BUILTIN_PROVIDER_ORDER = ["openai", "anthropic", "gemini", "groq", "azure", "lmstudio", "ollama-local", "ollama-cloud", "llamacpp", "vllm", "codex", "claudecode"];
|
|
23428
23552
|
var CLAUDE_CODE_SONNET_MODEL = "claude-sonnet-4-6";
|
|
23429
23553
|
var CLAUDE_CODE_OPUS_MODEL = "claude-opus-4-7";
|
|
23430
23554
|
var CLAUDE_CODE_MODELS = [CLAUDE_CODE_SONNET_MODEL, CLAUDE_CODE_OPUS_MODEL];
|
|
@@ -23462,7 +23586,7 @@ var defaultConfig = {
|
|
|
23462
23586
|
maxConcurrentExpensive: 1,
|
|
23463
23587
|
maxConcurrentWriters: 1,
|
|
23464
23588
|
maxConcurrentPerProvider: {
|
|
23465
|
-
|
|
23589
|
+
claudecode: 1
|
|
23466
23590
|
},
|
|
23467
23591
|
tokenBudget: null,
|
|
23468
23592
|
defaultMergeStrategy: "synthesize",
|
|
@@ -23517,6 +23641,7 @@ var defaultConfig = {
|
|
|
23517
23641
|
defaultProvider: "brave",
|
|
23518
23642
|
providers: {
|
|
23519
23643
|
brave: {
|
|
23644
|
+
apiKey: "",
|
|
23520
23645
|
apiKeyEnv: "BRAVE_SEARCH_API_KEY",
|
|
23521
23646
|
endpoint: "https://api.search.brave.com/res/v1/web/search",
|
|
23522
23647
|
count: 10,
|
|
@@ -23525,12 +23650,14 @@ var defaultConfig = {
|
|
|
23525
23650
|
safeSearch: "moderate"
|
|
23526
23651
|
},
|
|
23527
23652
|
tavily: {
|
|
23653
|
+
apiKey: "",
|
|
23528
23654
|
apiKeyEnv: "TAVILY_API_KEY",
|
|
23529
23655
|
endpoint: "https://api.tavily.com/search",
|
|
23530
23656
|
maxResults: 5,
|
|
23531
23657
|
searchDepth: "basic"
|
|
23532
23658
|
},
|
|
23533
23659
|
exa: {
|
|
23660
|
+
apiKey: "",
|
|
23534
23661
|
apiKeyEnv: "EXA_API_KEY",
|
|
23535
23662
|
endpoint: "https://api.exa.ai/search",
|
|
23536
23663
|
numResults: 5,
|
|
@@ -23584,73 +23711,113 @@ var defaultConfig = {
|
|
|
23584
23711
|
providers: {
|
|
23585
23712
|
openai: {
|
|
23586
23713
|
type: "openai-compatible",
|
|
23587
|
-
model: "openai-model-name",
|
|
23588
23714
|
baseURL: "https://api.openai.com/v1",
|
|
23589
|
-
|
|
23715
|
+
apiKey: "",
|
|
23716
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
23717
|
+
catalog: {
|
|
23718
|
+
type: "openai-models",
|
|
23719
|
+
endpoint: "https://api.openai.com/v1/models"
|
|
23720
|
+
}
|
|
23721
|
+
},
|
|
23722
|
+
anthropic: {
|
|
23723
|
+
type: "anthropic",
|
|
23724
|
+
baseURL: "https://api.anthropic.com/v1",
|
|
23725
|
+
apiKey: "",
|
|
23726
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
23727
|
+
catalog: {
|
|
23728
|
+
type: "anthropic-models",
|
|
23729
|
+
endpoint: "https://api.anthropic.com/v1/models"
|
|
23730
|
+
}
|
|
23590
23731
|
},
|
|
23591
23732
|
gemini: {
|
|
23592
23733
|
type: "openai-compatible",
|
|
23593
|
-
model: "gemini-model-name",
|
|
23594
23734
|
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
23595
|
-
|
|
23735
|
+
apiKey: "",
|
|
23736
|
+
apiKeyEnv: "GEMINI_API_KEY",
|
|
23737
|
+
apiKeyEnvAliases: ["GOOGLE_API_KEY"],
|
|
23738
|
+
catalog: {
|
|
23739
|
+
type: "gemini-openai-models",
|
|
23740
|
+
endpoint: "https://generativelanguage.googleapis.com/v1beta/openai/models"
|
|
23741
|
+
}
|
|
23596
23742
|
},
|
|
23597
|
-
|
|
23743
|
+
groq: {
|
|
23598
23744
|
type: "openai-compatible",
|
|
23599
|
-
|
|
23600
|
-
|
|
23601
|
-
|
|
23602
|
-
|
|
23745
|
+
baseURL: "https://api.groq.com/openai/v1",
|
|
23746
|
+
apiKey: "",
|
|
23747
|
+
apiKeyEnv: "GROQ_API_KEY",
|
|
23748
|
+
catalog: {
|
|
23749
|
+
type: "groq-models",
|
|
23750
|
+
endpoint: "https://api.groq.com/openai/v1/models"
|
|
23751
|
+
}
|
|
23752
|
+
},
|
|
23753
|
+
azure: {
|
|
23754
|
+
type: "openai-compatible",
|
|
23755
|
+
auth: { type: "api-key", header: "api-key" },
|
|
23756
|
+
modelProfiles: [
|
|
23757
|
+
{
|
|
23758
|
+
name: "azure-example",
|
|
23759
|
+
displayName: "Azure example",
|
|
23760
|
+
model: "azure-deployment-name",
|
|
23761
|
+
baseURL: "https://example.openai.azure.com/openai/v1",
|
|
23762
|
+
apiKey: "",
|
|
23763
|
+
apiKeyEnv: "AZURE_OPENAI_API_KEY"
|
|
23764
|
+
}
|
|
23765
|
+
]
|
|
23603
23766
|
},
|
|
23604
23767
|
lmstudio: {
|
|
23605
23768
|
type: "openai-compatible",
|
|
23606
|
-
model: "local-coder-model",
|
|
23607
23769
|
baseURL: "http://localhost:1234/v1",
|
|
23608
|
-
apiKey: "lm-studio"
|
|
23770
|
+
apiKey: "lm-studio",
|
|
23771
|
+
modelProfiles: [],
|
|
23772
|
+
catalog: {
|
|
23773
|
+
type: "openai-compatible-models",
|
|
23774
|
+
endpoint: "http://localhost:1234/v1/models"
|
|
23775
|
+
}
|
|
23609
23776
|
},
|
|
23610
|
-
|
|
23777
|
+
"ollama-local": {
|
|
23611
23778
|
type: "openai-compatible",
|
|
23612
|
-
|
|
23613
|
-
|
|
23614
|
-
|
|
23779
|
+
baseURL: "http://localhost:11434/v1",
|
|
23780
|
+
apiKey: "ollama",
|
|
23781
|
+
modelProfiles: [],
|
|
23782
|
+
quirks: { omitTemperature: true },
|
|
23783
|
+
catalog: {
|
|
23784
|
+
type: "openai-compatible-models",
|
|
23785
|
+
endpoint: "http://localhost:11434/v1/models"
|
|
23786
|
+
}
|
|
23787
|
+
},
|
|
23788
|
+
"ollama-cloud": {
|
|
23789
|
+
type: "ollama",
|
|
23790
|
+
baseURL: "https://ollama.com/api",
|
|
23791
|
+
apiKey: "",
|
|
23792
|
+
apiKeyEnv: "OLLAMA_API_KEY",
|
|
23793
|
+
modelProfiles: [],
|
|
23794
|
+
catalog: {
|
|
23795
|
+
type: "ollama-tags",
|
|
23796
|
+
endpoint: "https://ollama.com/api/tags"
|
|
23797
|
+
}
|
|
23615
23798
|
},
|
|
23616
23799
|
llamacpp: {
|
|
23617
23800
|
type: "openai-compatible",
|
|
23618
|
-
model: "local-coder-model",
|
|
23619
23801
|
baseURL: "http://localhost:8080/v1",
|
|
23620
|
-
apiKey: "llama.cpp"
|
|
23621
|
-
|
|
23622
|
-
|
|
23623
|
-
|
|
23624
|
-
|
|
23625
|
-
|
|
23626
|
-
apiKeyEnv: "OPENROUTER_API_KEY"
|
|
23627
|
-
},
|
|
23628
|
-
together: {
|
|
23629
|
-
type: "openai-compatible",
|
|
23630
|
-
model: "provider/model-name",
|
|
23631
|
-
baseURL: "https://api.together.xyz/v1",
|
|
23632
|
-
apiKeyEnv: "TOGETHER_API_KEY"
|
|
23633
|
-
},
|
|
23634
|
-
groq: {
|
|
23635
|
-
type: "openai-compatible",
|
|
23636
|
-
model: "provider/model-name",
|
|
23637
|
-
baseURL: "https://api.groq.com/openai/v1",
|
|
23638
|
-
apiKeyEnv: "GROQ_API_KEY"
|
|
23802
|
+
apiKey: "llama.cpp",
|
|
23803
|
+
modelProfiles: [],
|
|
23804
|
+
catalog: {
|
|
23805
|
+
type: "openai-compatible-models",
|
|
23806
|
+
endpoint: "http://localhost:8080/v1/models"
|
|
23807
|
+
}
|
|
23639
23808
|
},
|
|
23640
|
-
|
|
23809
|
+
vllm: {
|
|
23641
23810
|
type: "openai-compatible",
|
|
23642
|
-
|
|
23643
|
-
|
|
23644
|
-
|
|
23645
|
-
|
|
23646
|
-
|
|
23647
|
-
|
|
23648
|
-
|
|
23649
|
-
apiKeyEnv: "ANTHROPIC_API_KEY"
|
|
23811
|
+
baseURL: "http://localhost:8000/v1",
|
|
23812
|
+
apiKey: "vllm",
|
|
23813
|
+
modelProfiles: [],
|
|
23814
|
+
catalog: {
|
|
23815
|
+
type: "openai-compatible-models",
|
|
23816
|
+
endpoint: "http://localhost:8000/v1/models"
|
|
23817
|
+
}
|
|
23650
23818
|
},
|
|
23651
23819
|
codex: {
|
|
23652
23820
|
type: "codex",
|
|
23653
|
-
model: "gpt-5.1-codex",
|
|
23654
23821
|
baseURL: "https://chatgpt.com/backend-api/codex",
|
|
23655
23822
|
authStore: "auto",
|
|
23656
23823
|
allowApiKeyFallback: false,
|
|
@@ -23662,6 +23829,9 @@ var defaultConfig = {
|
|
|
23662
23829
|
effort: "medium",
|
|
23663
23830
|
summary: "auto"
|
|
23664
23831
|
}
|
|
23832
|
+
},
|
|
23833
|
+
catalog: {
|
|
23834
|
+
type: "codex-oauth-models"
|
|
23665
23835
|
}
|
|
23666
23836
|
},
|
|
23667
23837
|
claudecode: {
|
|
@@ -23680,67 +23850,37 @@ var defaultConfig = {
|
|
|
23680
23850
|
useBareMode: false,
|
|
23681
23851
|
usageLedgerScope: "process",
|
|
23682
23852
|
sessionLock: true,
|
|
23683
|
-
abortPolicy: "record-only"
|
|
23684
|
-
},
|
|
23685
|
-
"claudecode-plan": {
|
|
23686
|
-
type: "claudecode",
|
|
23687
|
-
runtime: "agent-sdk",
|
|
23688
|
-
model: CLAUDE_CODE_SONNET_MODEL,
|
|
23689
|
-
models: [...CLAUDE_CODE_MODELS],
|
|
23690
|
-
permissionProfile: "plan",
|
|
23691
|
-
cliPath: "~/.local/bin/claude",
|
|
23692
|
-
cwdMode: "session",
|
|
23693
|
-
historyPolicy: "passthrough-resume",
|
|
23694
|
-
onInvalidResume: "fresh",
|
|
23695
|
-
attachmentFallback: "block",
|
|
23696
|
-
allowSubagents: false,
|
|
23697
|
-
sanitizeApiKeyEnv: true,
|
|
23698
|
-
authPreflight: true,
|
|
23699
|
-
useBareMode: false,
|
|
23700
|
-
usageLedgerScope: "process",
|
|
23701
|
-
sessionLock: true,
|
|
23702
|
-
abortPolicy: "record-only",
|
|
23703
|
-
hidden: true
|
|
23704
|
-
},
|
|
23705
|
-
"claudecode-subagent": {
|
|
23706
|
-
type: "claudecode",
|
|
23707
|
-
runtime: "agent-sdk",
|
|
23708
|
-
model: CLAUDE_CODE_SONNET_MODEL,
|
|
23709
|
-
models: [...CLAUDE_CODE_MODELS],
|
|
23710
|
-
permissionProfile: "build",
|
|
23711
|
-
cliPath: "~/.local/bin/claude",
|
|
23712
|
-
cwdMode: "session",
|
|
23713
|
-
historyPolicy: "passthrough-resume",
|
|
23714
|
-
onInvalidResume: "fresh",
|
|
23715
|
-
attachmentFallback: "block",
|
|
23716
|
-
allowSubagents: false,
|
|
23717
|
-
sanitizeApiKeyEnv: true,
|
|
23718
|
-
authPreflight: true,
|
|
23719
|
-
useBareMode: false,
|
|
23720
|
-
usageLedgerScope: "process",
|
|
23721
|
-
sessionLock: true,
|
|
23722
23853
|
abortPolicy: "record-only",
|
|
23723
|
-
|
|
23854
|
+
catalog: {
|
|
23855
|
+
type: "claudecode-supported-models"
|
|
23856
|
+
}
|
|
23724
23857
|
}
|
|
23725
23858
|
}
|
|
23726
23859
|
};
|
|
23727
23860
|
async function loadConfig(options) {
|
|
23728
23861
|
const configs = [];
|
|
23729
|
-
const userConfigDir =
|
|
23730
|
-
const
|
|
23731
|
-
const configPaths = [
|
|
23732
|
-
path12.join(userConfigDir, "config.json"),
|
|
23733
|
-
path12.join(userConfigDir, "config.jsond"),
|
|
23734
|
-
path12.join(projectConfigDir, "config.json"),
|
|
23735
|
-
path12.join(projectConfigDir, "config.jsond"),
|
|
23736
|
-
options.configPath
|
|
23737
|
-
].filter(Boolean);
|
|
23862
|
+
const userConfigDir = path13.join(os7.homedir(), ".demian");
|
|
23863
|
+
const configPaths = [path13.join(userConfigDir, "config.json"), path13.join(userConfigDir, "config.jsond"), options.configPath].filter(Boolean);
|
|
23738
23864
|
for (const filePath of configPaths) {
|
|
23739
23865
|
if (!fs7.existsSync(filePath)) continue;
|
|
23740
|
-
|
|
23866
|
+
const parsed = parseConfigText(await readFile6(filePath, "utf8"), filePath);
|
|
23867
|
+
warnIfV1Config(parsed, filePath);
|
|
23868
|
+
configs.push(parsed);
|
|
23741
23869
|
}
|
|
23742
23870
|
return configs.reduce((acc, item) => mergeConfig(acc, item), structuredClone(defaultConfig));
|
|
23743
23871
|
}
|
|
23872
|
+
function warnIfV1Config(parsed, filePath) {
|
|
23873
|
+
if (parsed.version === 2) return;
|
|
23874
|
+
const providers = parsed.providers ?? {};
|
|
23875
|
+
const hasLegacyShape = Object.values(providers).some((provider) => {
|
|
23876
|
+
const candidate = provider;
|
|
23877
|
+
return (typeof candidate.model === "string" || Array.isArray(candidate.models)) && !Array.isArray(candidate.modelProfiles);
|
|
23878
|
+
});
|
|
23879
|
+
if (!hasLegacyShape && parsed.version === void 0 && Object.keys(providers).length === 0) return;
|
|
23880
|
+
if (!hasLegacyShape) return;
|
|
23881
|
+
process.stderr.write(`[demian] warning: ${filePath} uses the v1 schema (provider.model/models). v2 expects modelProfiles. Run \`demian config init\` to scaffold a v2 template.
|
|
23882
|
+
`);
|
|
23883
|
+
}
|
|
23744
23884
|
function parseConfigText(text, filePath) {
|
|
23745
23885
|
try {
|
|
23746
23886
|
return JSON.parse(filePath.endsWith(".jsond") ? stripJsonD(text) : text);
|
|
@@ -23947,7 +24087,178 @@ function mergeConfig(base, overlay) {
|
|
|
23947
24087
|
};
|
|
23948
24088
|
}
|
|
23949
24089
|
function normalizeProviderAlias(providerName) {
|
|
23950
|
-
|
|
24090
|
+
if (providerName === "claudecode-plan" || providerName === "claudecode-subagent") return "claudecode";
|
|
24091
|
+
if (providerName === "ollama") return "ollama-local";
|
|
24092
|
+
return providerName;
|
|
24093
|
+
}
|
|
24094
|
+
function sortProviderNames(names) {
|
|
24095
|
+
const order = new Map(BUILTIN_PROVIDER_ORDER.map((name, index) => [name, index]));
|
|
24096
|
+
return [...names].sort((left, right) => {
|
|
24097
|
+
const leftIndex = order.get(left);
|
|
24098
|
+
const rightIndex = order.get(right);
|
|
24099
|
+
if (leftIndex !== void 0 && rightIndex !== void 0) return leftIndex - rightIndex;
|
|
24100
|
+
if (leftIndex !== void 0) return -1;
|
|
24101
|
+
if (rightIndex !== void 0) return 1;
|
|
24102
|
+
return left.localeCompare(right);
|
|
24103
|
+
});
|
|
24104
|
+
}
|
|
24105
|
+
function providerModelProfiles(providerName, providerConfig) {
|
|
24106
|
+
const explicit = Array.isArray(providerConfig.modelProfiles) ? providerConfig.modelProfiles.filter((profile) => typeof profile?.model === "string" && profile.model.trim()).map((profile) => ({
|
|
24107
|
+
...profile,
|
|
24108
|
+
name: profile.name?.trim() || profile.displayName?.trim() || profile.model.trim(),
|
|
24109
|
+
displayName: profile.displayName?.trim() || profile.name?.trim() || profile.model.trim(),
|
|
24110
|
+
model: profile.model.trim()
|
|
24111
|
+
})) : [];
|
|
24112
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
24113
|
+
const seenDisplayNames = /* @__PURE__ */ new Set();
|
|
24114
|
+
const seenModels = /* @__PURE__ */ new Set();
|
|
24115
|
+
const out = [];
|
|
24116
|
+
for (const profile of explicit) {
|
|
24117
|
+
if (seenNames.has(profile.name)) {
|
|
24118
|
+
process.stderr.write(`[demian] warning: provider ${providerName} has duplicate modelProfile name "${profile.name}". Later entry ignored.
|
|
24119
|
+
`);
|
|
24120
|
+
continue;
|
|
24121
|
+
}
|
|
24122
|
+
if (profile.displayName && seenDisplayNames.has(profile.displayName)) {
|
|
24123
|
+
process.stderr.write(`[demian] warning: provider ${providerName} has duplicate modelProfile displayName "${profile.displayName}". Later entry ignored.
|
|
24124
|
+
`);
|
|
24125
|
+
continue;
|
|
24126
|
+
}
|
|
24127
|
+
seenNames.add(profile.name);
|
|
24128
|
+
if (profile.displayName) seenDisplayNames.add(profile.displayName);
|
|
24129
|
+
seenModels.add(profile.model);
|
|
24130
|
+
out.push(profile);
|
|
24131
|
+
}
|
|
24132
|
+
const legacy = [...typeof providerConfig.model === "string" && providerConfig.model.trim() ? [providerConfig.model.trim()] : [], ...(providerConfig.models ?? []).filter((model) => typeof model === "string" && model.trim())];
|
|
24133
|
+
for (const model of legacy) {
|
|
24134
|
+
const trimmed = model.trim();
|
|
24135
|
+
if (!trimmed || seenModels.has(trimmed)) continue;
|
|
24136
|
+
seenModels.add(trimmed);
|
|
24137
|
+
const name = trimmed;
|
|
24138
|
+
if (!seenNames.has(name)) seenNames.add(name);
|
|
24139
|
+
out.push({ name, displayName: name, model: trimmed });
|
|
24140
|
+
}
|
|
24141
|
+
const fallback = fallbackModelForProvider(providerName, providerConfig);
|
|
24142
|
+
if (out.length === 0 && fallback) out.push({ name: fallback, displayName: fallback, model: fallback });
|
|
24143
|
+
return out;
|
|
24144
|
+
}
|
|
24145
|
+
function providerModelOptions(providerName, providerConfig) {
|
|
24146
|
+
return providerModelProfiles(providerName, providerConfig).filter((profile) => !profile.hidden).map((profile) => displayModelForProfile(profile));
|
|
24147
|
+
}
|
|
24148
|
+
function defaultModelForProvider(providerName, providerConfig) {
|
|
24149
|
+
return providerModelProfiles(providerName, providerConfig)[0]?.model ?? "";
|
|
24150
|
+
}
|
|
24151
|
+
function displayModelForProfile(profile) {
|
|
24152
|
+
return profile.displayName?.trim() || profile.name?.trim() || profile.model;
|
|
24153
|
+
}
|
|
24154
|
+
function defaultDisplayModelForProvider(providerName, providerConfig) {
|
|
24155
|
+
const profile = providerModelProfiles(providerName, providerConfig)[0];
|
|
24156
|
+
return profile ? displayModelForProfile(profile) : defaultModelForProvider(providerName, providerConfig);
|
|
24157
|
+
}
|
|
24158
|
+
function resolveConfiguredApiKey(providerConfig) {
|
|
24159
|
+
if (providerConfig.apiKey) return { value: providerConfig.apiKey };
|
|
24160
|
+
for (const envName of [providerConfig.apiKeyEnv, ...providerConfig.apiKeyEnvAliases ?? []]) {
|
|
24161
|
+
if (!envName) continue;
|
|
24162
|
+
const value = process.env[envName];
|
|
24163
|
+
if (value) return { value, envName };
|
|
24164
|
+
}
|
|
24165
|
+
return { envName: providerConfig.apiKeyEnv ?? providerConfig.apiKeyEnvAliases?.[0] };
|
|
24166
|
+
}
|
|
24167
|
+
function resolveProviderRuntimeConfig(config, selection) {
|
|
24168
|
+
const providerName = normalizeProviderAlias(selection.providerName);
|
|
24169
|
+
const providerConfig = resolveProviderConfig(config, providerName);
|
|
24170
|
+
const profiles = providerModelProfiles(providerName, providerConfig);
|
|
24171
|
+
let profile;
|
|
24172
|
+
if (selection.modelProfileName) profile = profiles.find((item) => item.name === selection.modelProfileName);
|
|
24173
|
+
if (!profile && selection.model) profile = profiles.find((item) => item.displayName === selection.model);
|
|
24174
|
+
if (!profile && selection.model) profile = profiles.find((item) => item.model === selection.model);
|
|
24175
|
+
if (!profile) profile = profiles[0];
|
|
24176
|
+
if (!profile) {
|
|
24177
|
+
const model = selection.model ?? defaultModelForProvider(providerName, providerConfig);
|
|
24178
|
+
if (!model) throw new Error(`Provider ${providerName} has no model. Run \`demian config models ${providerName} --refresh\` or add a model profile.`);
|
|
24179
|
+
return { providerName, providerConfig, model };
|
|
24180
|
+
}
|
|
24181
|
+
const merged = mergeModelProfile(providerConfig, profile);
|
|
24182
|
+
return {
|
|
24183
|
+
providerName,
|
|
24184
|
+
providerConfig: merged,
|
|
24185
|
+
model: profile.model,
|
|
24186
|
+
modelProfileName: profile.name
|
|
24187
|
+
};
|
|
24188
|
+
}
|
|
24189
|
+
function resolveInternalAgentBackend(config, options = {}) {
|
|
24190
|
+
const tried = [];
|
|
24191
|
+
const candidates = uniqueProviderCandidates([options.preferredProvider, config.defaultProvider, ...Object.keys(config.providers)]);
|
|
24192
|
+
let lastError;
|
|
24193
|
+
for (const candidate of candidates) {
|
|
24194
|
+
tried.push(candidate);
|
|
24195
|
+
const provider = config.providers[normalizeProviderAlias(candidate)];
|
|
24196
|
+
if (!provider || provider.hidden) continue;
|
|
24197
|
+
try {
|
|
24198
|
+
const runtime = resolveProviderRuntimeConfig(config, {
|
|
24199
|
+
providerName: candidate,
|
|
24200
|
+
modelProfileName: candidate === options.preferredProvider ? options.modelProfileName : void 0,
|
|
24201
|
+
model: candidate === options.preferredProvider ? options.model : void 0
|
|
24202
|
+
});
|
|
24203
|
+
const source = candidate === options.preferredProvider ? "preferred" : candidate === config.defaultProvider ? "default" : "first-available";
|
|
24204
|
+
return {
|
|
24205
|
+
providerName: runtime.providerName,
|
|
24206
|
+
providerConfig: runtime.providerConfig,
|
|
24207
|
+
model: runtime.model,
|
|
24208
|
+
modelProfileName: runtime.modelProfileName,
|
|
24209
|
+
source,
|
|
24210
|
+
fallbackReason: source !== "preferred" && options.preferredProvider ? `preferred provider ${options.preferredProvider} unavailable` : void 0
|
|
24211
|
+
};
|
|
24212
|
+
} catch (error) {
|
|
24213
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
24214
|
+
}
|
|
24215
|
+
}
|
|
24216
|
+
throw new Error(`No usable provider for internal agent. Tried: ${tried.join(", ") || "(none)"}. Last error: ${lastError?.message ?? "no providers configured"}.`);
|
|
24217
|
+
}
|
|
24218
|
+
function uniqueProviderCandidates(values) {
|
|
24219
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24220
|
+
const out = [];
|
|
24221
|
+
for (const value of values) {
|
|
24222
|
+
if (!value) continue;
|
|
24223
|
+
const normalized = normalizeProviderAlias(value);
|
|
24224
|
+
if (seen.has(normalized)) continue;
|
|
24225
|
+
seen.add(normalized);
|
|
24226
|
+
out.push(normalized);
|
|
24227
|
+
}
|
|
24228
|
+
return out;
|
|
24229
|
+
}
|
|
24230
|
+
function mergeModelProfile(providerConfig, profile) {
|
|
24231
|
+
const merged = {
|
|
24232
|
+
...providerConfig,
|
|
24233
|
+
...definedOnly({
|
|
24234
|
+
baseURL: profile.baseURL,
|
|
24235
|
+
apiKey: nonEmptyString(profile.apiKey),
|
|
24236
|
+
apiKeyEnv: profile.apiKeyEnv,
|
|
24237
|
+
apiKeyEnvAliases: profile.apiKeyEnvAliases,
|
|
24238
|
+
auth: profile.auth,
|
|
24239
|
+
headers: profile.headers,
|
|
24240
|
+
quirks: profile.quirks,
|
|
24241
|
+
maxTokens: profile.maxTokens
|
|
24242
|
+
}),
|
|
24243
|
+
model: profile.model
|
|
24244
|
+
};
|
|
24245
|
+
return merged;
|
|
24246
|
+
}
|
|
24247
|
+
function definedOnly(input2) {
|
|
24248
|
+
return Object.fromEntries(Object.entries(input2).filter(([, value]) => value !== void 0));
|
|
24249
|
+
}
|
|
24250
|
+
function nonEmptyString(value) {
|
|
24251
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
24252
|
+
}
|
|
24253
|
+
function fallbackModelForProvider(providerName, providerConfig) {
|
|
24254
|
+
if (typeof providerConfig.model === "string" && providerConfig.model.trim()) return providerConfig.model.trim();
|
|
24255
|
+
if (providerName === "openai") return "gpt-5.5";
|
|
24256
|
+
if (providerName === "anthropic") return CLAUDE_CODE_SONNET_MODEL;
|
|
24257
|
+
if (providerName === "codex" || providerConfig.type === "codex") return "gpt-5.5";
|
|
24258
|
+
if (providerName === "claudecode") return CLAUDE_CODE_SONNET_MODEL;
|
|
24259
|
+
if (providerName === "gemini") return "gemini-2.5-pro";
|
|
24260
|
+
if (providerName === "groq") return "openai/gpt-oss-120b";
|
|
24261
|
+
return void 0;
|
|
23951
24262
|
}
|
|
23952
24263
|
function migrateProviderConfig(provider, baseProvider) {
|
|
23953
24264
|
if (isLegacyClaudeCodeShape(provider)) {
|
|
@@ -24029,12 +24340,14 @@ function resolveAgentMode(config, flagMode) {
|
|
|
24029
24340
|
return "single-agent";
|
|
24030
24341
|
}
|
|
24031
24342
|
function resolveProviderConfig(config, providerName) {
|
|
24032
|
-
const
|
|
24343
|
+
const normalized = normalizeProviderAlias(providerName);
|
|
24344
|
+
const provider = config.providers[normalized];
|
|
24033
24345
|
if (!provider) throw new Error(`Unknown provider: ${providerName}`);
|
|
24034
24346
|
return provider;
|
|
24035
24347
|
}
|
|
24036
24348
|
function resolveProvider(providerConfig, options = {}) {
|
|
24037
24349
|
const model = options.model ?? providerConfig.model;
|
|
24350
|
+
if (!model) throw new Error("Provider has no selected model. Choose a model or add a model profile.");
|
|
24038
24351
|
if (providerConfig.type === "claudecode") {
|
|
24039
24352
|
throw new Error("claudecode is an external agent runtime. Use resolveExecutionBackend().");
|
|
24040
24353
|
}
|
|
@@ -24081,12 +24394,28 @@ function resolveProvider(providerConfig, options = {}) {
|
|
|
24081
24394
|
};
|
|
24082
24395
|
}
|
|
24083
24396
|
if (providerConfig.type === "anthropic") {
|
|
24084
|
-
const apiKey2 =
|
|
24397
|
+
const apiKey2 = resolveConfiguredApiKey(providerConfig).value;
|
|
24085
24398
|
if (!apiKey2) {
|
|
24086
24399
|
throw new Error(`Missing API key for provider. Set ${providerConfig.apiKeyEnv ?? "apiKey"} or configure apiKey.`);
|
|
24087
24400
|
}
|
|
24088
24401
|
return {
|
|
24089
24402
|
provider: new AnthropicProvider({
|
|
24403
|
+
apiKey: apiKey2,
|
|
24404
|
+
baseURL: providerConfig.baseURL,
|
|
24405
|
+
defaultModel: model,
|
|
24406
|
+
onRetry: options.onRetry
|
|
24407
|
+
}),
|
|
24408
|
+
model
|
|
24409
|
+
};
|
|
24410
|
+
}
|
|
24411
|
+
if (providerConfig.type === "ollama") {
|
|
24412
|
+
const apiKey2 = resolveConfiguredApiKey(providerConfig).value;
|
|
24413
|
+
if (!apiKey2) {
|
|
24414
|
+
throw new Error(`Missing API key for provider. Set ${providerConfig.apiKeyEnv ?? "apiKey"} or configure apiKey.`);
|
|
24415
|
+
}
|
|
24416
|
+
return {
|
|
24417
|
+
provider: new OllamaProvider({
|
|
24418
|
+
baseURL: providerConfig.baseURL,
|
|
24090
24419
|
apiKey: apiKey2,
|
|
24091
24420
|
defaultModel: model,
|
|
24092
24421
|
onRetry: options.onRetry
|
|
@@ -24094,7 +24423,7 @@ function resolveProvider(providerConfig, options = {}) {
|
|
|
24094
24423
|
model
|
|
24095
24424
|
};
|
|
24096
24425
|
}
|
|
24097
|
-
const apiKey =
|
|
24426
|
+
const apiKey = resolveConfiguredApiKey(providerConfig).value;
|
|
24098
24427
|
if (!apiKey) {
|
|
24099
24428
|
throw new Error(`Missing API key for provider. Set ${providerConfig.apiKeyEnv ?? "apiKey"} or configure apiKey.`);
|
|
24100
24429
|
}
|
|
@@ -24113,6 +24442,7 @@ function resolveProvider(providerConfig, options = {}) {
|
|
|
24113
24442
|
}
|
|
24114
24443
|
function resolveExecutionBackend(providerConfig, options = {}) {
|
|
24115
24444
|
const model = options.model ?? providerConfig.model;
|
|
24445
|
+
if (!model) throw new Error("Provider has no selected model. Choose a model or add a model profile.");
|
|
24116
24446
|
if (providerConfig.type === "claudecode") {
|
|
24117
24447
|
const runtimeConfig = options.config || providerConfig.permissionProfile ? resolveClaudeCodeRuntimeConfig(providerConfig, options.config ?? defaultConfig, options.agent) : providerConfig;
|
|
24118
24448
|
return {
|
|
@@ -24321,7 +24651,7 @@ function safeJson(value) {
|
|
|
24321
24651
|
}
|
|
24322
24652
|
|
|
24323
24653
|
// src/hooks/dispatcher.ts
|
|
24324
|
-
import
|
|
24654
|
+
import path15 from "node:path";
|
|
24325
24655
|
|
|
24326
24656
|
// src/hooks/command.ts
|
|
24327
24657
|
import { spawn as spawn4 } from "node:child_process";
|
|
@@ -24414,7 +24744,7 @@ var blockDangerousBashHook = {
|
|
|
24414
24744
|
};
|
|
24415
24745
|
|
|
24416
24746
|
// src/hooks/builtin/protect-env-files.ts
|
|
24417
|
-
import
|
|
24747
|
+
import path14 from "node:path";
|
|
24418
24748
|
var protectEnvFilesHook = {
|
|
24419
24749
|
name: "protect-env-files",
|
|
24420
24750
|
event: "PreToolUse",
|
|
@@ -24422,7 +24752,7 @@ var protectEnvFilesHook = {
|
|
|
24422
24752
|
run(ctx) {
|
|
24423
24753
|
if (ctx.toolName !== "write_file" && ctx.toolName !== "edit_file") return { decision: "allow" };
|
|
24424
24754
|
const input2 = ctx.toolInput && typeof ctx.toolInput === "object" ? ctx.toolInput : {};
|
|
24425
|
-
const filePath = typeof input2.path === "string" ?
|
|
24755
|
+
const filePath = typeof input2.path === "string" ? path14.resolve(ctx.cwd, input2.path) : "";
|
|
24426
24756
|
if (filePath && isEnvFile(filePath)) {
|
|
24427
24757
|
return {
|
|
24428
24758
|
decision: "block",
|
|
@@ -24537,14 +24867,426 @@ function matchesHook(match, ctx) {
|
|
|
24537
24867
|
const input2 = ctx.toolInput && typeof ctx.toolInput === "object" ? ctx.toolInput : {};
|
|
24538
24868
|
const filePath = typeof input2.path === "string" ? input2.path : typeof input2.workdir === "string" ? input2.workdir : void 0;
|
|
24539
24869
|
if (!filePath) return false;
|
|
24540
|
-
if (!matchGlob(match.pathGlob, relativeToCwd(ctx.cwd,
|
|
24870
|
+
if (!matchGlob(match.pathGlob, relativeToCwd(ctx.cwd, path15.resolve(ctx.cwd, filePath)))) return false;
|
|
24541
24871
|
}
|
|
24542
24872
|
return true;
|
|
24543
24873
|
}
|
|
24544
24874
|
|
|
24875
|
+
// src/models/catalog.ts
|
|
24876
|
+
import crypto5 from "node:crypto";
|
|
24877
|
+
import { mkdir as mkdir6, readFile as readFile7, rename as rename4, writeFile as writeFile5 } from "node:fs/promises";
|
|
24878
|
+
import os9 from "node:os";
|
|
24879
|
+
import path16 from "node:path";
|
|
24880
|
+
var DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
24881
|
+
var PING_TIMEOUT_MS = 1500;
|
|
24882
|
+
var DEFAULT_CODEX_CLIENT_VERSION = "0.0.0";
|
|
24883
|
+
var dynamicImport2 = new Function("specifier", "return import(specifier)");
|
|
24884
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
24885
|
+
var pingCache = /* @__PURE__ */ new Map();
|
|
24886
|
+
var PING_CACHE_TTL_MS = 30 * 1e3;
|
|
24887
|
+
var packageVersionPromise;
|
|
24888
|
+
async function listProviderModelCatalog(providerName, providerConfig, options = {}) {
|
|
24889
|
+
const key = `${catalogCacheKey(providerName, providerConfig)}:${options.refresh ? "refresh" : "default"}`;
|
|
24890
|
+
const existing = inflight.get(key);
|
|
24891
|
+
if (existing) return existing;
|
|
24892
|
+
const promise = doListProviderModelCatalog(providerName, providerConfig, options).finally(() => inflight.delete(key));
|
|
24893
|
+
inflight.set(key, promise);
|
|
24894
|
+
return promise;
|
|
24895
|
+
}
|
|
24896
|
+
async function doListProviderModelCatalog(providerName, providerConfig, options) {
|
|
24897
|
+
const staticModels = staticModelEntries(providerName, providerConfig);
|
|
24898
|
+
const catalog = providerConfig.catalog;
|
|
24899
|
+
if (!catalog || catalog.type === "static") {
|
|
24900
|
+
return { status: staticModels.length ? "ready" : "unavailable", providerName, models: staticModels, source: "config" };
|
|
24901
|
+
}
|
|
24902
|
+
const missingAuthEnv = missingCatalogAuth(providerConfig);
|
|
24903
|
+
if (missingAuthEnv) {
|
|
24904
|
+
return {
|
|
24905
|
+
status: "missing-auth",
|
|
24906
|
+
providerName,
|
|
24907
|
+
models: staticModels,
|
|
24908
|
+
source: staticModels.length ? "config" : "fallback",
|
|
24909
|
+
message: `missing-auth:${missingAuthEnv}`
|
|
24910
|
+
};
|
|
24911
|
+
}
|
|
24912
|
+
const cacheKey = catalogCacheKey(providerName, providerConfig);
|
|
24913
|
+
const ttlMs = catalog.refreshTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
24914
|
+
const cached = await readCatalogCache(cacheKey, ttlMs, options.refresh !== true);
|
|
24915
|
+
if (cached && !options.refresh) return { ...cached, models: mergeCatalogWithStatic(staticModels, cached.models) };
|
|
24916
|
+
if (catalog.endpoint) {
|
|
24917
|
+
const reachable = await pingEndpoint(catalog.endpoint, options.fetch);
|
|
24918
|
+
if (!reachable) {
|
|
24919
|
+
const stale = await readCatalogCache(cacheKey, Number.POSITIVE_INFINITY, true);
|
|
24920
|
+
if (stale) return { ...stale, models: mergeCatalogWithStatic(staticModels, stale.models), message: `endpoint ${catalog.endpoint} unreachable; using cached models` };
|
|
24921
|
+
if (staticModels.length) return { status: "ready", providerName, models: staticModels, source: "config", message: `endpoint ${catalog.endpoint} unreachable; using configured profiles` };
|
|
24922
|
+
return { status: "unavailable", providerName, models: [], source: "fallback", message: `endpoint ${catalog.endpoint} unreachable` };
|
|
24923
|
+
}
|
|
24924
|
+
}
|
|
24925
|
+
try {
|
|
24926
|
+
const remote = await fetchCatalog(providerName, providerConfig, options);
|
|
24927
|
+
const merged = mergeCatalogWithStatic(staticModels, remote.models);
|
|
24928
|
+
const result = { ...remote, models: merged };
|
|
24929
|
+
if (result.status === "ready") await writeCatalogCache(cacheKey, result);
|
|
24930
|
+
return result;
|
|
24931
|
+
} catch (error) {
|
|
24932
|
+
const authMissing = isMissingAuthError(error);
|
|
24933
|
+
const stale = await readCatalogCache(cacheKey, Number.POSITIVE_INFINITY, true);
|
|
24934
|
+
if (stale) return { ...stale, status: authMissing ? "missing-auth" : stale.status, models: mergeCatalogWithStatic(staticModels, stale.models), message: errorMessage2(error) };
|
|
24935
|
+
if (staticModels.length) return { status: authMissing ? "missing-auth" : "ready", providerName, models: staticModels, source: "config", message: errorMessage2(error) };
|
|
24936
|
+
return { status: authMissing ? "missing-auth" : "unavailable", providerName, models: [], source: "fallback", message: errorMessage2(error) };
|
|
24937
|
+
}
|
|
24938
|
+
}
|
|
24939
|
+
async function pingEndpoint(endpoint, fetcher) {
|
|
24940
|
+
const now2 = Date.now();
|
|
24941
|
+
const cached = pingCache.get(endpoint);
|
|
24942
|
+
if (cached && cached.expiresAt > now2) return cached.ok;
|
|
24943
|
+
const ok2 = await tryPing(endpoint, fetcher);
|
|
24944
|
+
pingCache.set(endpoint, { ok: ok2, expiresAt: now2 + PING_CACHE_TTL_MS });
|
|
24945
|
+
return ok2;
|
|
24946
|
+
}
|
|
24947
|
+
async function tryPing(endpoint, fetcher) {
|
|
24948
|
+
const fn = fetcher ?? fetch;
|
|
24949
|
+
try {
|
|
24950
|
+
const response = await fn(endpoint, { method: "HEAD", signal: AbortSignal.timeout(PING_TIMEOUT_MS) });
|
|
24951
|
+
if (response.status < 500) return true;
|
|
24952
|
+
} catch {
|
|
24953
|
+
}
|
|
24954
|
+
try {
|
|
24955
|
+
const response = await fn(endpoint, { method: "GET", signal: AbortSignal.timeout(PING_TIMEOUT_MS), headers: { range: "bytes=0-0" } });
|
|
24956
|
+
return response.status < 500;
|
|
24957
|
+
} catch {
|
|
24958
|
+
return false;
|
|
24959
|
+
}
|
|
24960
|
+
}
|
|
24961
|
+
function invalidateCatalogPingCache(endpoint) {
|
|
24962
|
+
if (endpoint) pingCache.delete(endpoint);
|
|
24963
|
+
else pingCache.clear();
|
|
24964
|
+
}
|
|
24965
|
+
async function fetchCatalog(providerName, providerConfig, options) {
|
|
24966
|
+
if (providerConfig.type === "codex" || providerConfig.catalog?.type === "codex-oauth-models") return fetchCodexCatalog(providerName, providerConfig, options);
|
|
24967
|
+
if (providerConfig.type === "claudecode" || providerConfig.catalog?.type === "claudecode-supported-models") return fetchClaudeCodeCatalog(providerName, providerConfig, options);
|
|
24968
|
+
if (providerConfig.catalog?.type === "anthropic-models") return fetchAnthropicCatalog(providerName, providerConfig, options);
|
|
24969
|
+
if (providerConfig.catalog?.type === "ollama-tags") return fetchOllamaTagsCatalog(providerName, providerConfig, options);
|
|
24970
|
+
return fetchOpenAIStyleCatalog(providerName, providerConfig, options);
|
|
24971
|
+
}
|
|
24972
|
+
async function fetchOpenAIStyleCatalog(providerName, providerConfig, options) {
|
|
24973
|
+
if (providerConfig.type !== "openai-compatible") throw new Error(`Provider ${providerName} is not OpenAI-compatible.`);
|
|
24974
|
+
const apiKey = resolveConfiguredApiKey(providerConfig);
|
|
24975
|
+
if (!apiKey.value) throw missingAuth(providerConfig.apiKeyEnv ?? "apiKey");
|
|
24976
|
+
const endpoint = providerConfig.catalog?.endpoint ?? `${providerConfig.baseURL.replace(/\/+$/, "")}/models`;
|
|
24977
|
+
const response = await fetchJson(endpoint, {
|
|
24978
|
+
fetcher: options.fetch,
|
|
24979
|
+
timeoutMs: options.timeoutMs,
|
|
24980
|
+
headers: {
|
|
24981
|
+
...authHeadersForOpenAICompatible(providerConfig, apiKey.value),
|
|
24982
|
+
...providerConfig.headers ?? {}
|
|
24983
|
+
}
|
|
24984
|
+
});
|
|
24985
|
+
return {
|
|
24986
|
+
status: "ready",
|
|
24987
|
+
providerName,
|
|
24988
|
+
models: normalizeOpenAIModels(response).map((entry) => ({ ...entry, source: "api" })),
|
|
24989
|
+
source: "api"
|
|
24990
|
+
};
|
|
24991
|
+
}
|
|
24992
|
+
async function fetchAnthropicCatalog(providerName, providerConfig, options) {
|
|
24993
|
+
if (providerConfig.type !== "anthropic") throw new Error(`Provider ${providerName} is not Anthropic.`);
|
|
24994
|
+
const apiKey = resolveConfiguredApiKey(providerConfig);
|
|
24995
|
+
if (!apiKey.value) throw missingAuth(providerConfig.apiKeyEnv ?? "apiKey");
|
|
24996
|
+
const base = providerConfig.baseURL ?? "https://api.anthropic.com/v1";
|
|
24997
|
+
const endpoint = providerConfig.catalog?.endpoint ?? `${base.replace(/\/+$/, "")}/models`;
|
|
24998
|
+
const models = [];
|
|
24999
|
+
let afterId;
|
|
25000
|
+
for (; ; ) {
|
|
25001
|
+
const url = new URL(endpoint);
|
|
25002
|
+
if (afterId) url.searchParams.set("after_id", afterId);
|
|
25003
|
+
url.searchParams.set("limit", "100");
|
|
25004
|
+
const response = await fetchJson(url.toString(), {
|
|
25005
|
+
fetcher: options.fetch,
|
|
25006
|
+
timeoutMs: options.timeoutMs,
|
|
25007
|
+
headers: {
|
|
25008
|
+
"x-api-key": apiKey.value,
|
|
25009
|
+
"anthropic-version": "2023-06-01"
|
|
25010
|
+
}
|
|
25011
|
+
});
|
|
25012
|
+
const page = normalizeAnthropicModels(response);
|
|
25013
|
+
models.push(...page);
|
|
25014
|
+
if (!response.has_more || !response.last_id) break;
|
|
25015
|
+
afterId = response.last_id;
|
|
25016
|
+
}
|
|
25017
|
+
return { status: "ready", providerName, models, source: "api" };
|
|
25018
|
+
}
|
|
25019
|
+
async function fetchOllamaTagsCatalog(providerName, providerConfig, options) {
|
|
25020
|
+
if (providerConfig.type !== "ollama") throw new Error(`Provider ${providerName} is not Ollama native.`);
|
|
25021
|
+
const apiKey = resolveConfiguredApiKey(providerConfig);
|
|
25022
|
+
if (!apiKey.value) throw missingAuth(providerConfig.apiKeyEnv ?? "apiKey");
|
|
25023
|
+
const endpoint = providerConfig.catalog?.endpoint ?? `${providerConfig.baseURL.replace(/\/+$/, "")}/tags`;
|
|
25024
|
+
const response = await fetchJson(endpoint, {
|
|
25025
|
+
fetcher: options.fetch,
|
|
25026
|
+
timeoutMs: options.timeoutMs,
|
|
25027
|
+
headers: { authorization: `Bearer ${apiKey.value}` }
|
|
25028
|
+
});
|
|
25029
|
+
return {
|
|
25030
|
+
status: "ready",
|
|
25031
|
+
providerName,
|
|
25032
|
+
models: normalizeOllamaTags(response).map((entry) => ({ ...entry, source: "api" })),
|
|
25033
|
+
source: "api"
|
|
25034
|
+
};
|
|
25035
|
+
}
|
|
25036
|
+
async function fetchCodexCatalog(providerName, providerConfig, options) {
|
|
25037
|
+
if (providerConfig.type !== "codex") throw new Error(`Provider ${providerName} is not Codex.`);
|
|
25038
|
+
const fetcher = options.fetch ?? fetch;
|
|
25039
|
+
const baseURL = (providerConfig.baseURL ?? "https://chatgpt.com/backend-api/codex").replace(/\/+$/, "");
|
|
25040
|
+
const installationId = await loadOrCreateInstallationId(resolveCodexHome(providerConfig.codexHome));
|
|
25041
|
+
const auth = getSharedCodexAuthStore({
|
|
25042
|
+
codexHome: providerConfig.codexHome,
|
|
25043
|
+
authStore: providerConfig.authStore,
|
|
25044
|
+
allowApiKeyFallback: providerConfig.allowApiKeyFallback,
|
|
25045
|
+
proactiveRefreshMinutes: providerConfig.refresh?.proactiveRefreshMinutes,
|
|
25046
|
+
refreshCache: providerConfig.refresh?.cache,
|
|
25047
|
+
fetch: fetcher
|
|
25048
|
+
});
|
|
25049
|
+
const headers = await auth.requestHeaders(installationId);
|
|
25050
|
+
const url = new URL(`${baseURL}/models`);
|
|
25051
|
+
url.searchParams.set("client_version", await codexClientVersion(options.clientVersion));
|
|
25052
|
+
const response = await fetchJson(url.toString(), {
|
|
25053
|
+
fetcher,
|
|
25054
|
+
timeoutMs: options.timeoutMs,
|
|
25055
|
+
headers
|
|
25056
|
+
});
|
|
25057
|
+
return {
|
|
25058
|
+
status: "ready",
|
|
25059
|
+
providerName,
|
|
25060
|
+
models: normalizeCodexModels(response).map((entry) => ({ ...entry, source: "oauth" })),
|
|
25061
|
+
source: "oauth"
|
|
25062
|
+
};
|
|
25063
|
+
}
|
|
25064
|
+
async function fetchClaudeCodeCatalog(providerName, providerConfig, options) {
|
|
25065
|
+
if (providerConfig.type !== "claudecode") throw new Error(`Provider ${providerName} is not Claude Code.`);
|
|
25066
|
+
const fallback = staticModelEntries(providerName, providerConfig);
|
|
25067
|
+
try {
|
|
25068
|
+
const module = await dynamicImport2("@anthropic-ai/claude-agent-sdk");
|
|
25069
|
+
if (!module.query) throw new Error("Claude Agent SDK query export is unavailable.");
|
|
25070
|
+
const query = module.query({
|
|
25071
|
+
prompt: "",
|
|
25072
|
+
options: {
|
|
25073
|
+
model: defaultModelForProvider(providerName, providerConfig),
|
|
25074
|
+
maxTurns: 0,
|
|
25075
|
+
pathToClaudeCodeExecutable: resolveClaudeCodeCliPath(providerConfig.cliPath),
|
|
25076
|
+
env: sanitizedClaudeCodeEnv(providerConfig.env, providerConfig.sanitizeApiKeyEnv ?? true)
|
|
25077
|
+
}
|
|
25078
|
+
});
|
|
25079
|
+
try {
|
|
25080
|
+
if (typeof query.supportedModels !== "function") throw new Error("Claude Agent SDK supportedModels is unavailable.");
|
|
25081
|
+
const models = await withTimeout(query.supportedModels(), options.timeoutMs ?? 5e3);
|
|
25082
|
+
return {
|
|
25083
|
+
status: "ready",
|
|
25084
|
+
providerName,
|
|
25085
|
+
models: models.map((model, index) => ({
|
|
25086
|
+
name: model.displayName || model.value,
|
|
25087
|
+
displayName: model.displayName || model.value,
|
|
25088
|
+
model: model.value,
|
|
25089
|
+
description: model.description,
|
|
25090
|
+
isDefault: index === 0,
|
|
25091
|
+
source: "oauth"
|
|
25092
|
+
})),
|
|
25093
|
+
source: "oauth"
|
|
25094
|
+
};
|
|
25095
|
+
} finally {
|
|
25096
|
+
query.close?.();
|
|
25097
|
+
}
|
|
25098
|
+
} catch (error) {
|
|
25099
|
+
if (fallback.length) return { status: "ready", providerName, models: fallback, source: "fallback", message: errorMessage2(error) };
|
|
25100
|
+
throw error;
|
|
25101
|
+
}
|
|
25102
|
+
}
|
|
25103
|
+
function staticModelEntries(providerName, providerConfig) {
|
|
25104
|
+
return providerModelProfiles(providerName, providerConfig).filter((profile) => !profile.hidden).map((profile, index) => ({
|
|
25105
|
+
name: profile.displayName ?? profile.name,
|
|
25106
|
+
displayName: profile.displayName ?? profile.name,
|
|
25107
|
+
model: profile.model,
|
|
25108
|
+
description: profile.description,
|
|
25109
|
+
isDefault: index === 0,
|
|
25110
|
+
source: "config"
|
|
25111
|
+
}));
|
|
25112
|
+
}
|
|
25113
|
+
function mergeCatalogWithStatic(staticModels, remoteModels) {
|
|
25114
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
25115
|
+
for (const model of remoteModels) byModel.set(model.model, model);
|
|
25116
|
+
for (const model of staticModels) byModel.set(model.model, { ...byModel.get(model.model), ...model, source: model.source });
|
|
25117
|
+
return [...byModel.values()];
|
|
25118
|
+
}
|
|
25119
|
+
function normalizeOpenAIModels(raw) {
|
|
25120
|
+
const items = Array.isArray(raw.data) ? raw.data : Array.isArray(raw.models) ? raw.models : [];
|
|
25121
|
+
return items.map((item) => {
|
|
25122
|
+
const object2 = item;
|
|
25123
|
+
const id = stringValue4(object2.id) ?? stringValue4(object2.name) ?? stringValue4(object2.model);
|
|
25124
|
+
if (!id) return void 0;
|
|
25125
|
+
return {
|
|
25126
|
+
name: id,
|
|
25127
|
+
displayName: stringValue4(object2.display_name) ?? stringValue4(object2.displayName) ?? id,
|
|
25128
|
+
model: id,
|
|
25129
|
+
description: stringValue4(object2.description)
|
|
25130
|
+
};
|
|
25131
|
+
}).filter(Boolean);
|
|
25132
|
+
}
|
|
25133
|
+
function normalizeAnthropicModels(raw) {
|
|
25134
|
+
return (raw.data ?? []).map((item) => {
|
|
25135
|
+
const object2 = item;
|
|
25136
|
+
const id = stringValue4(object2.id);
|
|
25137
|
+
if (!id) return void 0;
|
|
25138
|
+
return {
|
|
25139
|
+
name: stringValue4(object2.display_name) ?? id,
|
|
25140
|
+
displayName: stringValue4(object2.display_name) ?? id,
|
|
25141
|
+
model: id,
|
|
25142
|
+
description: stringValue4(object2.description),
|
|
25143
|
+
source: "api"
|
|
25144
|
+
};
|
|
25145
|
+
}).filter(Boolean);
|
|
25146
|
+
}
|
|
25147
|
+
function normalizeOllamaTags(raw) {
|
|
25148
|
+
const items = Array.isArray(raw.models) ? raw.models : [];
|
|
25149
|
+
return items.map((item) => {
|
|
25150
|
+
const object2 = item;
|
|
25151
|
+
const id = stringValue4(object2.name) ?? stringValue4(object2.model);
|
|
25152
|
+
if (!id) return void 0;
|
|
25153
|
+
return { name: id, displayName: id, model: id };
|
|
25154
|
+
}).filter(Boolean);
|
|
25155
|
+
}
|
|
25156
|
+
function normalizeCodexModels(raw) {
|
|
25157
|
+
const items = Array.isArray(raw.models) ? raw.models : [];
|
|
25158
|
+
return items.map((item, index) => {
|
|
25159
|
+
const object2 = item;
|
|
25160
|
+
const id = stringValue4(object2.slug) ?? stringValue4(object2.id) ?? stringValue4(object2.model);
|
|
25161
|
+
if (!id) return void 0;
|
|
25162
|
+
return {
|
|
25163
|
+
name: stringValue4(object2.display_name) ?? id,
|
|
25164
|
+
displayName: stringValue4(object2.display_name) ?? id,
|
|
25165
|
+
model: id,
|
|
25166
|
+
description: stringValue4(object2.description),
|
|
25167
|
+
isDefault: index === 0
|
|
25168
|
+
};
|
|
25169
|
+
}).filter(Boolean);
|
|
25170
|
+
}
|
|
25171
|
+
function authHeadersForOpenAICompatible(providerConfig, apiKey) {
|
|
25172
|
+
const auth = inferOpenAICompatibleAuth(providerConfig.baseURL, providerConfig.auth);
|
|
25173
|
+
const type = auth.type ?? "bearer";
|
|
25174
|
+
const header = auth.header ?? (type === "api-key" ? "api-key" : "authorization");
|
|
25175
|
+
return { [header]: type === "bearer" ? `Bearer ${apiKey}` : apiKey };
|
|
25176
|
+
}
|
|
25177
|
+
async function fetchJson(url, options) {
|
|
25178
|
+
const response = await (options.fetcher ?? fetch)(url, {
|
|
25179
|
+
method: "GET",
|
|
25180
|
+
headers: {
|
|
25181
|
+
accept: "application/json",
|
|
25182
|
+
...options.headers ?? {}
|
|
25183
|
+
},
|
|
25184
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 5e3)
|
|
25185
|
+
});
|
|
25186
|
+
const text = await response.text();
|
|
25187
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);
|
|
25188
|
+
return text ? JSON.parse(text) : {};
|
|
25189
|
+
}
|
|
25190
|
+
function cachePath(cacheKey) {
|
|
25191
|
+
return path16.join(os9.homedir(), ".demian", "cache", "models", `${safeCacheName(cacheKey)}.json`);
|
|
25192
|
+
}
|
|
25193
|
+
async function readCatalogCache(cacheKey, ttlMs, allow) {
|
|
25194
|
+
if (!allow) return void 0;
|
|
25195
|
+
try {
|
|
25196
|
+
const raw = JSON.parse(await readFile7(cachePath(cacheKey), "utf8"));
|
|
25197
|
+
if (!raw.result) return void 0;
|
|
25198
|
+
if (ttlMs !== Number.POSITIVE_INFINITY && Date.now() - (raw.savedAt ?? 0) > ttlMs) return void 0;
|
|
25199
|
+
return { ...raw.result, source: "cache", models: raw.result.models.map((model) => ({ ...model, source: "cache" })) };
|
|
25200
|
+
} catch {
|
|
25201
|
+
return void 0;
|
|
25202
|
+
}
|
|
25203
|
+
}
|
|
25204
|
+
async function writeCatalogCache(cacheKey, result) {
|
|
25205
|
+
const filePath = cachePath(cacheKey);
|
|
25206
|
+
await mkdir6(path16.dirname(filePath), { recursive: true, mode: 448 });
|
|
25207
|
+
const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
25208
|
+
await writeFile5(temp, JSON.stringify({ savedAt: Date.now(), result }, null, 2), { mode: 384 });
|
|
25209
|
+
await rename4(temp, filePath);
|
|
25210
|
+
}
|
|
25211
|
+
function safeCacheName(providerName) {
|
|
25212
|
+
return providerName.replace(/[^a-zA-Z0-9_.-]+/g, "_");
|
|
25213
|
+
}
|
|
25214
|
+
function catalogCacheKey(providerName, providerConfig) {
|
|
25215
|
+
const identity = {
|
|
25216
|
+
providerName,
|
|
25217
|
+
type: providerConfig.type,
|
|
25218
|
+
catalogType: providerConfig.catalog?.type,
|
|
25219
|
+
endpoint: providerConfig.catalog?.endpoint,
|
|
25220
|
+
baseURL: "baseURL" in providerConfig ? providerConfig.baseURL : void 0,
|
|
25221
|
+
apiKeyEnv: "apiKeyEnv" in providerConfig ? providerConfig.apiKeyEnv : void 0,
|
|
25222
|
+
apiKeyEnvAliases: "apiKeyEnvAliases" in providerConfig ? providerConfig.apiKeyEnvAliases : void 0,
|
|
25223
|
+
auth: "auth" in providerConfig ? providerConfig.auth : void 0
|
|
25224
|
+
};
|
|
25225
|
+
const digest = crypto5.createHash("sha256").update(JSON.stringify(identity)).digest("hex").slice(0, 16);
|
|
25226
|
+
return `${providerName}-${digest}`;
|
|
25227
|
+
}
|
|
25228
|
+
function missingCatalogAuth(providerConfig) {
|
|
25229
|
+
if (providerConfig.type !== "openai-compatible" && providerConfig.type !== "anthropic" && providerConfig.type !== "ollama") return void 0;
|
|
25230
|
+
if (providerConfig.catalog?.requiresAuth === false) return void 0;
|
|
25231
|
+
const expectsAuth = providerConfig.catalog?.requiresAuth === true || !!providerConfig.apiKey || !!providerConfig.apiKeyEnv || !!providerConfig.apiKeyEnvAliases?.length;
|
|
25232
|
+
if (!expectsAuth) return void 0;
|
|
25233
|
+
const apiKey = resolveConfiguredApiKey(providerConfig);
|
|
25234
|
+
return apiKey.value ? void 0 : apiKey.envName ?? "apiKey";
|
|
25235
|
+
}
|
|
25236
|
+
function stringValue4(value) {
|
|
25237
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
25238
|
+
}
|
|
25239
|
+
function missingAuth(envName) {
|
|
25240
|
+
const error = new Error(`missing-auth:${envName}`);
|
|
25241
|
+
return error;
|
|
25242
|
+
}
|
|
25243
|
+
function isMissingAuthError(error) {
|
|
25244
|
+
if (error instanceof CodexAuthError) {
|
|
25245
|
+
return error.code === "missing_auth" || error.code === "missing_chatgpt_tokens" || error.code === "refresh_unavailable" || error.code === "keyring_write_unsupported" || error.code === "refresh_expired" || error.code === "refresh_invalidated" || error.code === "refresh_reused" || error.code === "refresh_other";
|
|
25246
|
+
}
|
|
25247
|
+
return error instanceof Error && error.message.startsWith("missing-auth:");
|
|
25248
|
+
}
|
|
25249
|
+
function errorMessage2(error) {
|
|
25250
|
+
return error instanceof Error ? error.message : String(error);
|
|
25251
|
+
}
|
|
25252
|
+
async function codexClientVersion(override) {
|
|
25253
|
+
const normalized = semverLike(override);
|
|
25254
|
+
if (normalized) return normalized;
|
|
25255
|
+
packageVersionPromise ??= readPackageVersion();
|
|
25256
|
+
return packageVersionPromise;
|
|
25257
|
+
}
|
|
25258
|
+
async function readPackageVersion() {
|
|
25259
|
+
try {
|
|
25260
|
+
const raw = JSON.parse(await readFile7(new URL("../package.json", import.meta.url), "utf8"));
|
|
25261
|
+
return semverLike(raw.version) ?? DEFAULT_CODEX_CLIENT_VERSION;
|
|
25262
|
+
} catch {
|
|
25263
|
+
return DEFAULT_CODEX_CLIENT_VERSION;
|
|
25264
|
+
}
|
|
25265
|
+
}
|
|
25266
|
+
function semverLike(value) {
|
|
25267
|
+
if (typeof value !== "string") return void 0;
|
|
25268
|
+
const trimmed = value.trim();
|
|
25269
|
+
return /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(trimmed) ? trimmed : void 0;
|
|
25270
|
+
}
|
|
25271
|
+
function withTimeout(promise, timeoutMs) {
|
|
25272
|
+
return new Promise((resolve, reject) => {
|
|
25273
|
+
const timer = setTimeout(() => reject(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
25274
|
+
promise.then(
|
|
25275
|
+
(value) => {
|
|
25276
|
+
clearTimeout(timer);
|
|
25277
|
+
resolve(value);
|
|
25278
|
+
},
|
|
25279
|
+
(error) => {
|
|
25280
|
+
clearTimeout(timer);
|
|
25281
|
+
reject(error);
|
|
25282
|
+
}
|
|
25283
|
+
);
|
|
25284
|
+
});
|
|
25285
|
+
}
|
|
25286
|
+
|
|
24545
25287
|
// src/multimodal.ts
|
|
24546
|
-
import { readFile as
|
|
24547
|
-
import
|
|
25288
|
+
import { readFile as readFile8, stat as stat3 } from "node:fs/promises";
|
|
25289
|
+
import path17 from "node:path";
|
|
24548
25290
|
var DEFAULT_MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
24549
25291
|
async function buildUserContent(prompt, images = [], options) {
|
|
24550
25292
|
if (images.length === 0) return prompt;
|
|
@@ -24568,11 +25310,11 @@ async function imageToUrl(input2, options) {
|
|
|
24568
25310
|
const maxImageBytes = options.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES;
|
|
24569
25311
|
if (info.size > maxImageBytes) throw new Error(`Image is larger than ${maxImageBytes} bytes: ${input2}`);
|
|
24570
25312
|
const mime = mimeFromPath(filePath);
|
|
24571
|
-
const bytes = await
|
|
25313
|
+
const bytes = await readFile8(filePath);
|
|
24572
25314
|
return `data:${mime};base64,${bytes.toString("base64")}`;
|
|
24573
25315
|
}
|
|
24574
25316
|
function mimeFromPath(filePath) {
|
|
24575
|
-
switch (
|
|
25317
|
+
switch (path17.extname(filePath).toLowerCase()) {
|
|
24576
25318
|
case ".jpg":
|
|
24577
25319
|
case ".jpeg":
|
|
24578
25320
|
return "image/jpeg";
|
|
@@ -24583,15 +25325,15 @@ function mimeFromPath(filePath) {
|
|
|
24583
25325
|
case ".webp":
|
|
24584
25326
|
return "image/webp";
|
|
24585
25327
|
default:
|
|
24586
|
-
throw new Error(`Unsupported image extension: ${
|
|
25328
|
+
throw new Error(`Unsupported image extension: ${path17.extname(filePath) || "(none)"}`);
|
|
24587
25329
|
}
|
|
24588
25330
|
}
|
|
24589
25331
|
|
|
24590
25332
|
// src/permissions/persistent-grants.ts
|
|
24591
|
-
import { mkdir as
|
|
25333
|
+
import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile6 } from "node:fs/promises";
|
|
24592
25334
|
import fs8 from "node:fs";
|
|
24593
|
-
import
|
|
24594
|
-
import
|
|
25335
|
+
import os10 from "node:os";
|
|
25336
|
+
import path18 from "node:path";
|
|
24595
25337
|
var DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
24596
25338
|
var PersistentGrantStore = class {
|
|
24597
25339
|
filePath;
|
|
@@ -24622,22 +25364,22 @@ var PersistentGrantStore = class {
|
|
|
24622
25364
|
}
|
|
24623
25365
|
async #read() {
|
|
24624
25366
|
if (!fs8.existsSync(this.filePath)) return { version: 1, grants: [] };
|
|
24625
|
-
const data = JSON.parse(await
|
|
25367
|
+
const data = JSON.parse(await readFile9(this.filePath, "utf8"));
|
|
24626
25368
|
return {
|
|
24627
25369
|
version: 1,
|
|
24628
25370
|
grants: Array.isArray(data.grants) ? data.grants.filter(isGrantRecord) : []
|
|
24629
25371
|
};
|
|
24630
25372
|
}
|
|
24631
25373
|
async #write(file) {
|
|
24632
|
-
await
|
|
24633
|
-
await
|
|
25374
|
+
await mkdir7(path18.dirname(this.filePath), { recursive: true });
|
|
25375
|
+
await writeFile6(this.filePath, `${JSON.stringify(file, null, 2)}
|
|
24634
25376
|
`, { mode: 384 });
|
|
24635
25377
|
}
|
|
24636
25378
|
};
|
|
24637
25379
|
function resolveGrantPath(cwd, config) {
|
|
24638
|
-
if (config.path) return
|
|
24639
|
-
if ((config.scope ?? "project") === "user") return
|
|
24640
|
-
return
|
|
25380
|
+
if (config.path) return path18.resolve(cwd, config.path);
|
|
25381
|
+
if ((config.scope ?? "project") === "user") return path18.join(os10.homedir(), ".demian", "grants.json");
|
|
25382
|
+
return path18.join(cwd, ".demian", "grants.json");
|
|
24641
25383
|
}
|
|
24642
25384
|
function isGrantRecord(value) {
|
|
24643
25385
|
if (!value || typeof value !== "object") return false;
|
|
@@ -24696,12 +25438,13 @@ function applyPermissionPresetToConfig(config, preset) {
|
|
|
24696
25438
|
|
|
24697
25439
|
// src/root-session.ts
|
|
24698
25440
|
import { cp, mkdtemp, rm as rm2 } from "node:fs/promises";
|
|
24699
|
-
import
|
|
24700
|
-
import
|
|
25441
|
+
import os12 from "node:os";
|
|
25442
|
+
import path28 from "node:path";
|
|
24701
25443
|
|
|
24702
25444
|
// src/transcript.ts
|
|
24703
|
-
import { mkdir as
|
|
24704
|
-
import
|
|
25445
|
+
import { mkdir as mkdir8, appendFile, writeFile as writeFile7 } from "node:fs/promises";
|
|
25446
|
+
import os11 from "node:os";
|
|
25447
|
+
import path19 from "node:path";
|
|
24705
25448
|
var TranscriptWriter = class {
|
|
24706
25449
|
filePath;
|
|
24707
25450
|
#enabled;
|
|
@@ -24709,9 +25452,9 @@ var TranscriptWriter = class {
|
|
|
24709
25452
|
#queue = Promise.resolve();
|
|
24710
25453
|
constructor(options) {
|
|
24711
25454
|
this.#enabled = options.enabled !== false;
|
|
24712
|
-
const dir =
|
|
24713
|
-
this.filePath =
|
|
24714
|
-
this.#ready = this.#enabled ?
|
|
25455
|
+
const dir = path19.join(options.storageDir ?? defaultDemianStorageDir(), "transcripts", options.sessionId);
|
|
25456
|
+
this.filePath = path19.join(dir, "session.jsonl");
|
|
25457
|
+
this.#ready = this.#enabled ? mkdir8(dir, { recursive: true }).then(() => writeFile7(this.filePath, "", { flag: "a" })) : Promise.resolve();
|
|
24715
25458
|
}
|
|
24716
25459
|
write(event) {
|
|
24717
25460
|
if (!this.#enabled) return Promise.resolve();
|
|
@@ -24726,12 +25469,15 @@ var TranscriptWriter = class {
|
|
|
24726
25469
|
await this.#queue;
|
|
24727
25470
|
}
|
|
24728
25471
|
};
|
|
25472
|
+
function defaultDemianStorageDir() {
|
|
25473
|
+
return path19.join(os11.homedir(), ".demian");
|
|
25474
|
+
}
|
|
24729
25475
|
|
|
24730
25476
|
// src/external-runtime/snapshot-diff.ts
|
|
24731
25477
|
import { createHash as createHash2 } from "node:crypto";
|
|
24732
25478
|
import { createReadStream } from "node:fs";
|
|
24733
|
-
import { readdir, readFile as
|
|
24734
|
-
import
|
|
25479
|
+
import { readdir, readFile as readFile10, stat as stat4 } from "node:fs/promises";
|
|
25480
|
+
import path20 from "node:path";
|
|
24735
25481
|
|
|
24736
25482
|
// src/workspace/diff.ts
|
|
24737
25483
|
var CONTEXT_LINES = 3;
|
|
@@ -24941,7 +25687,7 @@ async function diffWorkspaceSnapshot(before) {
|
|
|
24941
25687
|
}
|
|
24942
25688
|
async function walk(root, relativeDir, entries, options) {
|
|
24943
25689
|
if (entries.size >= options.maxFiles) return;
|
|
24944
|
-
const absoluteDir =
|
|
25690
|
+
const absoluteDir = path20.join(root, relativeDir);
|
|
24945
25691
|
let items;
|
|
24946
25692
|
try {
|
|
24947
25693
|
items = await readdir(absoluteDir, { withFileTypes: true });
|
|
@@ -24952,8 +25698,8 @@ async function walk(root, relativeDir, entries, options) {
|
|
|
24952
25698
|
if (entries.size >= options.maxFiles) return;
|
|
24953
25699
|
if (item.name.startsWith(".") && item.name !== ".env.example" && SKIP_DIRS.has(item.name)) continue;
|
|
24954
25700
|
if (SKIP_DIRS.has(item.name)) continue;
|
|
24955
|
-
const relativePath = normalizeRelativePath(
|
|
24956
|
-
const absolutePath =
|
|
25701
|
+
const relativePath = normalizeRelativePath(path20.join(relativeDir, item.name));
|
|
25702
|
+
const absolutePath = path20.join(root, relativePath);
|
|
24957
25703
|
if (item.isDirectory()) {
|
|
24958
25704
|
await walk(root, relativePath, entries, options);
|
|
24959
25705
|
continue;
|
|
@@ -24975,7 +25721,7 @@ function snapshotDiffText(entry) {
|
|
|
24975
25721
|
`;
|
|
24976
25722
|
}
|
|
24977
25723
|
async function readTextContent(filePath) {
|
|
24978
|
-
const buffer = await
|
|
25724
|
+
const buffer = await readFile10(filePath);
|
|
24979
25725
|
if (buffer.includes(0)) return void 0;
|
|
24980
25726
|
return buffer.toString("utf8");
|
|
24981
25727
|
}
|
|
@@ -24989,7 +25735,7 @@ function sha256File(filePath) {
|
|
|
24989
25735
|
});
|
|
24990
25736
|
}
|
|
24991
25737
|
function normalizeRelativePath(value) {
|
|
24992
|
-
return value.split(
|
|
25738
|
+
return value.split(path20.sep).join("/");
|
|
24993
25739
|
}
|
|
24994
25740
|
|
|
24995
25741
|
// src/external-runtime/session-runner.ts
|
|
@@ -25682,25 +26428,25 @@ function fail(content, metadata) {
|
|
|
25682
26428
|
}
|
|
25683
26429
|
|
|
25684
26430
|
// src/tools/output.ts
|
|
25685
|
-
import { mkdir as
|
|
25686
|
-
import
|
|
26431
|
+
import { mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
|
|
26432
|
+
import path21 from "node:path";
|
|
25687
26433
|
var DEFAULT_CAP_BYTES = 32 * 1024;
|
|
25688
26434
|
var HALF_PREVIEW_BYTES = 16 * 1024;
|
|
25689
26435
|
async function capToolOutput(result, options) {
|
|
25690
26436
|
const capBytes = options.capBytes ?? DEFAULT_CAP_BYTES;
|
|
25691
26437
|
const bytes = Buffer.byteLength(result.content, "utf8");
|
|
25692
26438
|
if (bytes <= capBytes) return result;
|
|
25693
|
-
const dir =
|
|
25694
|
-
await
|
|
25695
|
-
const outputPath =
|
|
25696
|
-
await
|
|
26439
|
+
const dir = path21.join(options.cwd, ".demian", "tmp");
|
|
26440
|
+
await mkdir9(dir, { recursive: true });
|
|
26441
|
+
const outputPath = path21.join(dir, `output-${safeCallId(options.callId)}.txt`);
|
|
26442
|
+
await writeFile8(outputPath, result.content, "utf8");
|
|
25697
26443
|
return {
|
|
25698
26444
|
...result,
|
|
25699
26445
|
content: [
|
|
25700
26446
|
sliceUtf8(result.content, 0, HALF_PREVIEW_BYTES),
|
|
25701
26447
|
`
|
|
25702
26448
|
|
|
25703
|
-
[Full output saved to ${
|
|
26449
|
+
[Full output saved to ${path21.relative(options.cwd, outputPath)}; showing head and tail because output exceeded ${capBytes} bytes]
|
|
25704
26450
|
|
|
25705
26451
|
`,
|
|
25706
26452
|
sliceUtf8(result.content, Math.max(0, bytes - HALF_PREVIEW_BYTES), bytes)
|
|
@@ -25726,7 +26472,7 @@ function sliceUtf8(text, startByte, endByte) {
|
|
|
25726
26472
|
|
|
25727
26473
|
// src/tools/read-file.ts
|
|
25728
26474
|
import { open as open2, stat as stat5 } from "node:fs/promises";
|
|
25729
|
-
import
|
|
26475
|
+
import path22 from "node:path";
|
|
25730
26476
|
|
|
25731
26477
|
// src/tools/validation.ts
|
|
25732
26478
|
function assertObject(input2, toolName) {
|
|
@@ -25810,7 +26556,7 @@ var readFileTool = {
|
|
|
25810
26556
|
const truncated = start + sliced.length < lines.length;
|
|
25811
26557
|
return ok(content + (truncated ? `
|
|
25812
26558
|
... (${lines.length - start - sliced.length} more lines)` : ""), {
|
|
25813
|
-
path:
|
|
26559
|
+
path: path22.relative(ctx.cwd, filePath),
|
|
25814
26560
|
lines: sliced.length,
|
|
25815
26561
|
totalLines: lines.length,
|
|
25816
26562
|
truncated
|
|
@@ -25848,8 +26594,8 @@ function looksBinary(buffer) {
|
|
|
25848
26594
|
}
|
|
25849
26595
|
|
|
25850
26596
|
// src/tools/write-file.ts
|
|
25851
|
-
import { mkdir as
|
|
25852
|
-
import
|
|
26597
|
+
import { mkdir as mkdir10, readFile as readFile11, writeFile as writeFile9 } from "node:fs/promises";
|
|
26598
|
+
import path23 from "node:path";
|
|
25853
26599
|
var writeFileTool = {
|
|
25854
26600
|
name: "write_file",
|
|
25855
26601
|
description: "Create or replace a text file inside the workspace.",
|
|
@@ -25867,8 +26613,8 @@ var writeFileTool = {
|
|
|
25867
26613
|
const filePath = resolveInsideCwd(ctx.cwd, stringField(object2, "path"));
|
|
25868
26614
|
const content = stringField(object2, "content", { allowEmpty: true });
|
|
25869
26615
|
const before = await readOptionalTextFile(filePath);
|
|
25870
|
-
await
|
|
25871
|
-
await
|
|
26616
|
+
await mkdir10(path23.dirname(filePath), { recursive: true });
|
|
26617
|
+
await writeFile9(filePath, content, "utf8");
|
|
25872
26618
|
const relative = relativeToCwd(ctx.cwd, filePath);
|
|
25873
26619
|
const diff = createTextDiff(before, content, relative);
|
|
25874
26620
|
return ok(`Wrote ${relativeToCwd(ctx.cwd, filePath)} (${Buffer.byteLength(content, "utf8")} bytes).`, {
|
|
@@ -25881,7 +26627,7 @@ var writeFileTool = {
|
|
|
25881
26627
|
};
|
|
25882
26628
|
async function readOptionalTextFile(filePath) {
|
|
25883
26629
|
try {
|
|
25884
|
-
return await
|
|
26630
|
+
return await readFile11(filePath, "utf8");
|
|
25885
26631
|
} catch (error) {
|
|
25886
26632
|
if (isNotFound(error)) return void 0;
|
|
25887
26633
|
throw error;
|
|
@@ -25892,7 +26638,7 @@ function isNotFound(error) {
|
|
|
25892
26638
|
}
|
|
25893
26639
|
|
|
25894
26640
|
// src/tools/edit-file.ts
|
|
25895
|
-
import { readFile as
|
|
26641
|
+
import { readFile as readFile12, writeFile as writeFile10 } from "node:fs/promises";
|
|
25896
26642
|
var editFileTool = {
|
|
25897
26643
|
name: "edit_file",
|
|
25898
26644
|
description: "Replace an exact string in a text file inside the workspace.",
|
|
@@ -25914,12 +26660,12 @@ var editFileTool = {
|
|
|
25914
26660
|
const newString = stringField(object2, "newString", { allowEmpty: true });
|
|
25915
26661
|
const replaceAll = optionalBooleanField(object2, "replaceAll") ?? false;
|
|
25916
26662
|
if (oldString === newString) throw new Error("oldString and newString must be different");
|
|
25917
|
-
const before = await
|
|
26663
|
+
const before = await readFile12(filePath, "utf8");
|
|
25918
26664
|
const matches = countMatches(before, oldString);
|
|
25919
26665
|
if (matches === 0) throw new Error("oldString was not found");
|
|
25920
26666
|
if (matches > 1 && !replaceAll) throw new Error(`oldString matched ${matches} times; set replaceAll=true to replace all matches`);
|
|
25921
26667
|
const after = replaceAll ? before.split(oldString).join(newString) : before.replace(oldString, newString);
|
|
25922
|
-
await
|
|
26668
|
+
await writeFile10(filePath, after, "utf8");
|
|
25923
26669
|
const relative = relativeToCwd(ctx.cwd, filePath);
|
|
25924
26670
|
const diff = createTextDiff(before, after, relative);
|
|
25925
26671
|
return ok(`Edited ${relative} (${replaceAll ? matches : 1} replacement${matches === 1 ? "" : "s"}).`, {
|
|
@@ -25943,7 +26689,7 @@ function countMatches(text, needle) {
|
|
|
25943
26689
|
|
|
25944
26690
|
// src/tools/bash.ts
|
|
25945
26691
|
import { spawn as spawn5 } from "node:child_process";
|
|
25946
|
-
import
|
|
26692
|
+
import path25 from "node:path";
|
|
25947
26693
|
|
|
25948
26694
|
// src/sandbox/env-only.ts
|
|
25949
26695
|
function buildEnvOnlyLaunch(command, config) {
|
|
@@ -26003,7 +26749,7 @@ function bwrapPath() {
|
|
|
26003
26749
|
|
|
26004
26750
|
// src/sandbox/macos.ts
|
|
26005
26751
|
import { existsSync as existsSync3 } from "node:fs";
|
|
26006
|
-
import
|
|
26752
|
+
import path24 from "node:path";
|
|
26007
26753
|
function canUseMacOSSandbox() {
|
|
26008
26754
|
return process.platform === "darwin" && existsSync3("/usr/bin/sandbox-exec");
|
|
26009
26755
|
}
|
|
@@ -26029,7 +26775,7 @@ function macosProfile(cwd, config) {
|
|
|
26029
26775
|
}
|
|
26030
26776
|
if (mode === "workspace-write") {
|
|
26031
26777
|
lines.push("(deny file-write*)");
|
|
26032
|
-
lines.push(`(allow file-write* (subpath "${escapeProfilePath(
|
|
26778
|
+
lines.push(`(allow file-write* (subpath "${escapeProfilePath(path24.resolve(cwd))}"))`);
|
|
26033
26779
|
lines.push('(allow file-write* (subpath "/tmp"))');
|
|
26034
26780
|
lines.push('(allow file-write* (subpath "/private/tmp"))');
|
|
26035
26781
|
lines.push('(allow file-write* (subpath "/private/var/folders"))');
|
|
@@ -26091,7 +26837,7 @@ ${result.stderr}` : ""
|
|
|
26091
26837
|
].filter(Boolean).join("\n");
|
|
26092
26838
|
return ok(content, {
|
|
26093
26839
|
command,
|
|
26094
|
-
workdir:
|
|
26840
|
+
workdir: path25.relative(ctx.cwd, workdir) || ".",
|
|
26095
26841
|
exitCode: result.exitCode,
|
|
26096
26842
|
signal: result.signal,
|
|
26097
26843
|
timedOut: result.timedOut,
|
|
@@ -26143,8 +26889,8 @@ function runCommand(command, cwd, timeoutMs, signal, sandbox = { mode: "workspac
|
|
|
26143
26889
|
|
|
26144
26890
|
// src/tools/grep.ts
|
|
26145
26891
|
import { spawn as spawn6 } from "node:child_process";
|
|
26146
|
-
import { readdir as readdir2, readFile as
|
|
26147
|
-
import
|
|
26892
|
+
import { readdir as readdir2, readFile as readFile13, stat as stat6 } from "node:fs/promises";
|
|
26893
|
+
import path26 from "node:path";
|
|
26148
26894
|
var MAX_MATCHES = 200;
|
|
26149
26895
|
var grepTool = {
|
|
26150
26896
|
name: "grep",
|
|
@@ -26219,7 +26965,7 @@ function runRg(cwd, base, pattern, glob, signal) {
|
|
|
26219
26965
|
}
|
|
26220
26966
|
function rewriteRgPath(cwd, line) {
|
|
26221
26967
|
const [file, rest] = splitFirst(line, ":");
|
|
26222
|
-
if (!
|
|
26968
|
+
if (!path26.isAbsolute(file)) return line;
|
|
26223
26969
|
return `${relativeToCwd(cwd, file)}:${rest}`;
|
|
26224
26970
|
}
|
|
26225
26971
|
function splitFirst(value, delimiter) {
|
|
@@ -26236,11 +26982,11 @@ async function fallbackSearch(cwd, base, pattern, glob) {
|
|
|
26236
26982
|
if (isIgnoredPath(relative)) return;
|
|
26237
26983
|
const info = await stat6(item);
|
|
26238
26984
|
if (info.isDirectory()) {
|
|
26239
|
-
for (const entry of await readdir2(item)) await walk2(
|
|
26985
|
+
for (const entry of await readdir2(item)) await walk2(path26.join(item, entry));
|
|
26240
26986
|
return;
|
|
26241
26987
|
}
|
|
26242
26988
|
if (glob && !matchGlob(glob, relative)) return;
|
|
26243
|
-
const buffer = await
|
|
26989
|
+
const buffer = await readFile13(item);
|
|
26244
26990
|
if (buffer.includes(0)) return;
|
|
26245
26991
|
const lines = buffer.toString("utf8").split(/\r?\n/);
|
|
26246
26992
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -26255,7 +27001,7 @@ async function fallbackSearch(cwd, base, pattern, glob) {
|
|
|
26255
27001
|
|
|
26256
27002
|
// src/tools/glob.ts
|
|
26257
27003
|
import { readdir as readdir3, stat as stat7 } from "node:fs/promises";
|
|
26258
|
-
import
|
|
27004
|
+
import path27 from "node:path";
|
|
26259
27005
|
var MAX_PATHS = 1e3;
|
|
26260
27006
|
var globTool = {
|
|
26261
27007
|
name: "glob",
|
|
@@ -26287,7 +27033,7 @@ async function collectPaths(cwd, root, pattern) {
|
|
|
26287
27033
|
async function walk2(dir) {
|
|
26288
27034
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
26289
27035
|
for (const entry of entries) {
|
|
26290
|
-
const absolute =
|
|
27036
|
+
const absolute = path27.join(dir, entry.name);
|
|
26291
27037
|
const relative = relativeToCwd(cwd, absolute);
|
|
26292
27038
|
if (isIgnoredPath(relative)) continue;
|
|
26293
27039
|
const info = await stat7(absolute);
|
|
@@ -26428,7 +27174,7 @@ async function runBraveSearch(config, options) {
|
|
|
26428
27174
|
if (config.country) url.searchParams.set("country", config.country);
|
|
26429
27175
|
if (config.searchLang) url.searchParams.set("search_lang", config.searchLang);
|
|
26430
27176
|
if (config.safeSearch) url.searchParams.set("safesearch", config.safeSearch);
|
|
26431
|
-
const json = await
|
|
27177
|
+
const json = await fetchJson2(url, {
|
|
26432
27178
|
method: "GET",
|
|
26433
27179
|
headers: {
|
|
26434
27180
|
accept: "application/json",
|
|
@@ -26447,7 +27193,7 @@ async function runTavilySearch(config, options) {
|
|
|
26447
27193
|
if (!apiKey.ok) return apiKey;
|
|
26448
27194
|
const maxResults = Math.min(positiveInteger(options.maxResults, config.maxResults ?? 5, "maxResults"), TAVILY_MAX_RESULTS);
|
|
26449
27195
|
const endpoint = config.endpoint ?? defaultConfig.webSearch.providers.tavily.endpoint ?? "https://api.tavily.com/search";
|
|
26450
|
-
const json = await
|
|
27196
|
+
const json = await fetchJson2(endpoint, {
|
|
26451
27197
|
method: "POST",
|
|
26452
27198
|
headers: {
|
|
26453
27199
|
"content-type": "application/json",
|
|
@@ -26473,7 +27219,7 @@ async function runExaSearch(config, options) {
|
|
|
26473
27219
|
if (!apiKey.ok) return apiKey;
|
|
26474
27220
|
const numResults = Math.min(positiveInteger(options.maxResults, config.numResults ?? 5, "maxResults"), EXA_MAX_RESULTS);
|
|
26475
27221
|
const endpoint = config.endpoint ?? defaultConfig.webSearch.providers.exa.endpoint ?? "https://api.exa.ai/search";
|
|
26476
|
-
const json = await
|
|
27222
|
+
const json = await fetchJson2(endpoint, {
|
|
26477
27223
|
method: "POST",
|
|
26478
27224
|
headers: {
|
|
26479
27225
|
"content-type": "application/json",
|
|
@@ -26498,11 +27244,11 @@ async function runExaSearch(config, options) {
|
|
|
26498
27244
|
return searchResult("exa", options.query, results, json.value);
|
|
26499
27245
|
}
|
|
26500
27246
|
function resolveApiKey(provider, config) {
|
|
26501
|
-
const value = config.apiKey
|
|
27247
|
+
const value = config.apiKey || (config.apiKeyEnv ? process.env[config.apiKeyEnv] : void 0);
|
|
26502
27248
|
if (value) return { ok: true, value };
|
|
26503
27249
|
return toolFail(`Missing API key for web_search provider "${provider}". Set webSearch.providers.${provider}.apiKey or set ${config.apiKeyEnv ?? "the configured apiKeyEnv"}.`);
|
|
26504
27250
|
}
|
|
26505
|
-
async function
|
|
27251
|
+
async function fetchJson2(url, options) {
|
|
26506
27252
|
const { fetcher, provider, ...init } = options;
|
|
26507
27253
|
const response = await fetcher(url, init);
|
|
26508
27254
|
const text = await response.text();
|
|
@@ -26520,10 +27266,10 @@ function normalizeBraveResults(raw, limit) {
|
|
|
26520
27266
|
return (data.web?.results ?? []).slice(0, limit).map((item) => {
|
|
26521
27267
|
const result = item;
|
|
26522
27268
|
return {
|
|
26523
|
-
title:
|
|
26524
|
-
url:
|
|
26525
|
-
snippet: cleanSnippet(
|
|
26526
|
-
publishedDate:
|
|
27269
|
+
title: stringValue5(result.title) ?? "Untitled",
|
|
27270
|
+
url: stringValue5(result.url) ?? "",
|
|
27271
|
+
snippet: cleanSnippet(stringValue5(result.description) ?? stringValue5(result.snippet)),
|
|
27272
|
+
publishedDate: stringValue5(result.age)
|
|
26527
27273
|
};
|
|
26528
27274
|
}).filter((item) => item.url.length > 0);
|
|
26529
27275
|
}
|
|
@@ -26532,9 +27278,9 @@ function normalizeTavilyResults(raw, limit) {
|
|
|
26532
27278
|
return (data.results ?? []).slice(0, limit).map((item) => {
|
|
26533
27279
|
const result = item;
|
|
26534
27280
|
return {
|
|
26535
|
-
title:
|
|
26536
|
-
url:
|
|
26537
|
-
snippet: cleanSnippet(
|
|
27281
|
+
title: stringValue5(result.title) ?? "Untitled",
|
|
27282
|
+
url: stringValue5(result.url) ?? "",
|
|
27283
|
+
snippet: cleanSnippet(stringValue5(result.content) ?? stringValue5(result.raw_content)),
|
|
26538
27284
|
score: numberValue3(result.score)
|
|
26539
27285
|
};
|
|
26540
27286
|
}).filter((item) => item.url.length > 0);
|
|
@@ -26544,10 +27290,10 @@ function normalizeExaResults(raw, limit) {
|
|
|
26544
27290
|
return (data.results ?? []).slice(0, limit).map((item) => {
|
|
26545
27291
|
const result = item;
|
|
26546
27292
|
return {
|
|
26547
|
-
title:
|
|
26548
|
-
url:
|
|
27293
|
+
title: stringValue5(result.title) ?? "Untitled",
|
|
27294
|
+
url: stringValue5(result.url) ?? "",
|
|
26549
27295
|
snippet: exaSnippet(result),
|
|
26550
|
-
publishedDate:
|
|
27296
|
+
publishedDate: stringValue5(result.publishedDate),
|
|
26551
27297
|
score: numberValue3(result.score)
|
|
26552
27298
|
};
|
|
26553
27299
|
}).filter((item) => item.url.length > 0);
|
|
@@ -26558,7 +27304,7 @@ function exaSnippet(result) {
|
|
|
26558
27304
|
const joined = highlights.map((item) => typeof item === "string" ? item : "").filter(Boolean).join(" ");
|
|
26559
27305
|
if (joined) return cleanSnippet(joined);
|
|
26560
27306
|
}
|
|
26561
|
-
return cleanSnippet(
|
|
27307
|
+
return cleanSnippet(stringValue5(result.text) ?? stringValue5(result.summary));
|
|
26562
27308
|
}
|
|
26563
27309
|
function searchResult(provider, query, results, raw) {
|
|
26564
27310
|
const content = [
|
|
@@ -26582,7 +27328,7 @@ function formatResult(index, result) {
|
|
|
26582
27328
|
if (result.snippet) lines.push(` Snippet: ${result.snippet}`);
|
|
26583
27329
|
return lines.join("\n");
|
|
26584
27330
|
}
|
|
26585
|
-
function
|
|
27331
|
+
function stringValue5(value) {
|
|
26586
27332
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
26587
27333
|
}
|
|
26588
27334
|
function numberValue3(value) {
|
|
@@ -27462,7 +28208,7 @@ function stringArray2(value) {
|
|
|
27462
28208
|
}
|
|
27463
28209
|
|
|
27464
28210
|
// src/external-runtime/session-map.ts
|
|
27465
|
-
import
|
|
28211
|
+
import crypto6 from "node:crypto";
|
|
27466
28212
|
var ClaudeCodeSessionMap = class {
|
|
27467
28213
|
#sessions = /* @__PURE__ */ new Map();
|
|
27468
28214
|
get(key) {
|
|
@@ -27515,7 +28261,7 @@ function normalizeInstruction(value) {
|
|
|
27515
28261
|
return value.replace(/\r\n/g, "\n").trim().split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n");
|
|
27516
28262
|
}
|
|
27517
28263
|
function sha256(value) {
|
|
27518
|
-
return
|
|
28264
|
+
return crypto6.createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
27519
28265
|
}
|
|
27520
28266
|
|
|
27521
28267
|
// src/root-session.ts
|
|
@@ -28154,8 +28900,8 @@ Writer changed files outside writeScope: ${outOfScope.join(", ")}` : result.erro
|
|
|
28154
28900
|
}
|
|
28155
28901
|
}
|
|
28156
28902
|
async createCoworkIsolatedWorkspace(groupId, memberId) {
|
|
28157
|
-
const root = await mkdtemp(
|
|
28158
|
-
const cwd =
|
|
28903
|
+
const root = await mkdtemp(path28.join(os12.tmpdir(), `demian-cowork-${groupId}-${memberId}-`));
|
|
28904
|
+
const cwd = path28.join(root, "workspace");
|
|
28159
28905
|
await cp(this.#options.cwd, cwd, {
|
|
28160
28906
|
recursive: true,
|
|
28161
28907
|
filter: (source) => shouldCopyIntoCoworkWorkspace(this.#options.cwd, source)
|
|
@@ -28303,7 +29049,7 @@ Writer changed files outside writeScope: ${outOfScope.join(", ")}` : result.erro
|
|
|
28303
29049
|
const denyTools = ["write_file", "edit_file", "bash", "claudecode.Edit", "claudecode.Write", "claudecode.MultiEdit", "claudecode.Bash"];
|
|
28304
29050
|
return this.withPermissionPreset({
|
|
28305
29051
|
...agent,
|
|
28306
|
-
provider: agent.provider?.profile === "claudecode-
|
|
29052
|
+
provider: agent.provider?.profile === "claudecode" && agent.name === "claudecode-explorer" ? { ...agent.provider, permissionProfile: "explore" } : agent.provider,
|
|
28307
29053
|
tools: agent.tools.filter((tool) => readTools.has(tool)),
|
|
28308
29054
|
permissions: [
|
|
28309
29055
|
...agent.permissions.filter((rule) => !denyTools.includes(rule.tool)),
|
|
@@ -28350,13 +29096,14 @@ Writer changed files outside writeScope: ${outOfScope.join(", ")}` : result.erro
|
|
|
28350
29096
|
return lines.join("\n");
|
|
28351
29097
|
}
|
|
28352
29098
|
resolveInvocationBackend(profileName, modelOverride, agent) {
|
|
28353
|
-
const
|
|
29099
|
+
const runtime = resolveProviderRuntimeConfig(this.#options.config, { providerName: profileName, model: modelOverride });
|
|
29100
|
+
const providerConfig = runtime.providerConfig;
|
|
28354
29101
|
if (this.#options.resolveExecutionBackend) return this.#options.resolveExecutionBackend(profileName, providerConfig, modelOverride);
|
|
28355
29102
|
if (this.#options.resolveProvider) {
|
|
28356
|
-
const resolved = this.#options.resolveProvider(profileName, providerConfig,
|
|
29103
|
+
const resolved = this.#options.resolveProvider(profileName, providerConfig, runtime.model);
|
|
28357
29104
|
return { kind: "provider", ...resolved };
|
|
28358
29105
|
}
|
|
28359
|
-
return resolveExecutionBackend(providerConfig, { model:
|
|
29106
|
+
return resolveExecutionBackend(providerConfig, { model: runtime.model, config: this.#options.config, agent });
|
|
28360
29107
|
}
|
|
28361
29108
|
loadAgentSession(agent, providerProfile, model, sessionScope, cwd = this.#options.cwd) {
|
|
28362
29109
|
const key = `${this.id}:${sessionScope ?? "delegate"}:${agent.name}:${providerProfile}:${model}:${cwd}`;
|
|
@@ -28458,17 +29205,17 @@ function findDependencyCycle(group) {
|
|
|
28458
29205
|
const byId = new Map(group.map((member) => [member.memberId, member]));
|
|
28459
29206
|
const visiting = /* @__PURE__ */ new Set();
|
|
28460
29207
|
const visited = /* @__PURE__ */ new Set();
|
|
28461
|
-
const
|
|
29208
|
+
const path29 = [];
|
|
28462
29209
|
const visit = (id) => {
|
|
28463
|
-
if (visiting.has(id)) return [...
|
|
29210
|
+
if (visiting.has(id)) return [...path29.slice(path29.indexOf(id)), id];
|
|
28464
29211
|
if (visited.has(id)) return void 0;
|
|
28465
29212
|
visiting.add(id);
|
|
28466
|
-
|
|
29213
|
+
path29.push(id);
|
|
28467
29214
|
for (const dep of byId.get(id)?.dependsOn ?? []) {
|
|
28468
29215
|
const cycle = visit(dep);
|
|
28469
29216
|
if (cycle) return cycle;
|
|
28470
29217
|
}
|
|
28471
|
-
|
|
29218
|
+
path29.pop();
|
|
28472
29219
|
visiting.delete(id);
|
|
28473
29220
|
visited.add(id);
|
|
28474
29221
|
return void 0;
|
|
@@ -28509,7 +29256,7 @@ function scopeStaticPrefix(pattern) {
|
|
|
28509
29256
|
return prefix.replace(/\/+$/, "").split("/").filter(Boolean).join("/") || ".";
|
|
28510
29257
|
}
|
|
28511
29258
|
function shouldCopyIntoCoworkWorkspace(root, source) {
|
|
28512
|
-
const relative =
|
|
29259
|
+
const relative = path28.relative(root, source).split(path28.sep).join("/");
|
|
28513
29260
|
if (!relative) return true;
|
|
28514
29261
|
const parts = relative.split("/");
|
|
28515
29262
|
return !parts.includes(".git") && !parts.includes("node_modules") && !parts.includes(".demian");
|
|
@@ -28703,6 +29450,7 @@ function sumUsage(outcomes) {
|
|
|
28703
29450
|
export {
|
|
28704
29451
|
AgentRegistry,
|
|
28705
29452
|
AnthropicProvider,
|
|
29453
|
+
BUILTIN_PROVIDER_ORDER,
|
|
28706
29454
|
CLAUDE_CODE_KEYRING_SERVICE,
|
|
28707
29455
|
CLAUDE_CODE_MODELS,
|
|
28708
29456
|
CLAUDE_CODE_OAUTH_BETA_HEADER,
|
|
@@ -28730,6 +29478,7 @@ export {
|
|
|
28730
29478
|
DEFAULT_CODEX_BASE_URL,
|
|
28731
29479
|
EventBus,
|
|
28732
29480
|
HookDispatcher,
|
|
29481
|
+
OllamaProvider,
|
|
28733
29482
|
OpenAIProvider,
|
|
28734
29483
|
PERMISSION_PRESETS,
|
|
28735
29484
|
PermissionEngine,
|
|
@@ -28764,6 +29513,10 @@ export {
|
|
|
28764
29513
|
decodeJwtPayload,
|
|
28765
29514
|
defaultConfig,
|
|
28766
29515
|
defaultDecisionForPermissionPreset,
|
|
29516
|
+
defaultDemianStorageDir,
|
|
29517
|
+
defaultDisplayModelForProvider,
|
|
29518
|
+
defaultModelForProvider,
|
|
29519
|
+
displayModelForProfile,
|
|
28767
29520
|
estimateMessageTokens,
|
|
28768
29521
|
estimateMessagesTokens,
|
|
28769
29522
|
estimateToolSchemaTokens,
|
|
@@ -28780,6 +29533,7 @@ export {
|
|
|
28780
29533
|
imageToUrl,
|
|
28781
29534
|
inferOpenAICompatibleAuth,
|
|
28782
29535
|
injectClaudeCodeSystemPrefix,
|
|
29536
|
+
invalidateCatalogPingCache,
|
|
28783
29537
|
isAzureOpenAIEndpoint,
|
|
28784
29538
|
isCallableAgent,
|
|
28785
29539
|
isCoworkableAgent,
|
|
@@ -28788,6 +29542,7 @@ export {
|
|
|
28788
29542
|
isInsidePath,
|
|
28789
29543
|
isPermissionPreset,
|
|
28790
29544
|
isPrimaryAgent,
|
|
29545
|
+
listProviderModelCatalog,
|
|
28791
29546
|
loadConfig,
|
|
28792
29547
|
loadOrCreateInstallationId,
|
|
28793
29548
|
matchGlob,
|
|
@@ -28796,6 +29551,7 @@ export {
|
|
|
28796
29551
|
normalizeAgent,
|
|
28797
29552
|
normalizeAnthropicResponse,
|
|
28798
29553
|
normalizeCodexResponse,
|
|
29554
|
+
normalizeOllamaResponse,
|
|
28799
29555
|
normalizeOpenAIResponse,
|
|
28800
29556
|
normalizePathForMatch,
|
|
28801
29557
|
normalizePermissionPreset,
|
|
@@ -28807,16 +29563,22 @@ export {
|
|
|
28807
29563
|
parseOpenAIStream,
|
|
28808
29564
|
parseRetryAfter,
|
|
28809
29565
|
permissionDecisionLine,
|
|
29566
|
+
providerModelOptions,
|
|
29567
|
+
providerModelProfiles,
|
|
28810
29568
|
relativeToCwd,
|
|
28811
29569
|
resolveAgentMode,
|
|
28812
29570
|
resolveClaudeCodeRuntimeConfig,
|
|
28813
29571
|
resolveClaudeConfigDir,
|
|
28814
29572
|
resolveCodexHome,
|
|
29573
|
+
resolveConfiguredApiKey,
|
|
28815
29574
|
resolveExecutionBackend,
|
|
28816
29575
|
resolveGrantPath,
|
|
28817
29576
|
resolveInsideCwd,
|
|
29577
|
+
resolveInternalAgentBackend,
|
|
28818
29578
|
resolveProvider,
|
|
28819
29579
|
resolveProviderConfig,
|
|
29580
|
+
resolveProviderRuntimeConfig,
|
|
29581
|
+
sortProviderNames,
|
|
28820
29582
|
toClaudeCodeMessagesPayload,
|
|
28821
29583
|
toCodexResponsesPayload,
|
|
28822
29584
|
toCodexResponsesTool,
|