@garuda-agent/garuda 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +30 -17
- package/dist/{acpCommand-FZ2P5JFO.js → acpCommand-AJJBKAE5.js} +64 -26
- package/dist/chunk-3EQU2GUX.js +27 -0
- package/dist/{chunk-K3SIUOAG.js → chunk-44QN5SWZ.js} +2 -2
- package/dist/{chunk-INRZR772.js → chunk-B6FA4I7Q.js} +2 -2
- package/dist/chunk-EIXUSVFX.js +116 -0
- package/dist/{chunk-PQNT34LY.js → chunk-ENLDPN4S.js} +4 -4
- package/dist/{chunk-SI2QJYCR.js → chunk-GXZACJUI.js} +6 -25
- package/dist/{chunk-2S62ZNTC.js → chunk-HE2K657U.js} +9 -7
- package/dist/chunk-NP6RIC25.js +331 -0
- package/dist/chunk-R7I2NBPX.js +71 -0
- package/dist/{chunk-2TAXSFWN.js → chunk-TUUEBHG2.js} +2 -2
- package/dist/{chunk-HRGX53VC.js → chunk-ULAJPMY7.js} +1 -67
- package/dist/{chunk-S77XDSD4.js → chunk-USCUXCAU.js} +2 -2
- package/dist/{chunk-K7KK7U44.js → chunk-WIJL72ZE.js} +125 -226
- package/dist/chunk-XOK5OZOC.js +9 -0
- package/dist/cli/index.js +50 -33
- package/dist/{create-N4I7TIZE.js → create-VXIH2GUZ.js} +6 -5
- package/dist/{evalCommand-3SSFMZQD.js → evalCommand-OX2LQYEK.js} +31 -17
- package/dist/{inkChat-34BTFG2K.js → inkChat-YMMMGU7Y.js} +10 -7
- package/dist/{install-VRPBZY5H.js → install-EWDSPFFZ.js} +4 -3
- package/dist/{jobCommand-XJ5GULS5.js → jobCommand-ACSGSBQ4.js} +9 -7
- package/dist/{launchd-QVHQ3VNN.js → launchd-SSX362VG.js} +5 -4
- package/dist/{lspCommand-XNTBHXLA.js → lspCommand-JRXKRSL7.js} +4 -3
- package/dist/{manager-CSI76AWF.js → manager-AWU4NHHK.js} +4 -3
- package/dist/{nightCommand-CR6ZHYGP.js → nightCommand-65MUEV32.js} +9 -7
- package/dist/{remove-LE7Q7K46.js → remove-6GDSTAIO.js} +4 -3
- package/dist/{server-R3GNIC33.js → server-KHHNHUYV.js} +24 -6
- package/dist/setupCommand-VA7D5MZK.js +303 -0
- package/package.json +1 -1
- package/dist/chunk-EPKWDCV4.js +0 -9
- package/dist/chunk-YFQJXRIJ.js +0 -193
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
keepSecretForRedaction,
|
|
4
|
+
writeFileAtomic
|
|
5
|
+
} from "./chunk-EIXUSVFX.js";
|
|
6
|
+
import {
|
|
7
|
+
lookupModel
|
|
8
|
+
} from "./chunk-R7I2NBPX.js";
|
|
9
|
+
import {
|
|
10
|
+
isLoopbackHost
|
|
11
|
+
} from "./chunk-ATW7BNIG.js";
|
|
12
|
+
|
|
13
|
+
// src/model/credentials.ts
|
|
14
|
+
import { randomBytes } from "crypto";
|
|
15
|
+
import { constants } from "fs";
|
|
16
|
+
import { chmod, mkdir, open, rename, rm, writeFile } from "fs/promises";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { dirname, join } from "path";
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
var CREDENTIALS_FILE = join(".garuda", "credentials");
|
|
21
|
+
var CREDENTIALS_MAX_BYTES = 64 * 1024;
|
|
22
|
+
var KEY_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
23
|
+
var fileSchema = z.record(z.string().regex(KEY_NAME), z.string().min(1).max(4096));
|
|
24
|
+
function credentialsPath(home = homedir()) {
|
|
25
|
+
return join(home, CREDENTIALS_FILE);
|
|
26
|
+
}
|
|
27
|
+
async function readCredentials(home = homedir()) {
|
|
28
|
+
const file = credentialsPath(home);
|
|
29
|
+
const ignored = (why) => ({
|
|
30
|
+
keys: {},
|
|
31
|
+
warning: `${file} ${why} Garuda ignores it.`
|
|
32
|
+
});
|
|
33
|
+
let handle;
|
|
34
|
+
try {
|
|
35
|
+
handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
const code = error.code;
|
|
38
|
+
if (code === "ENOENT" || code === "ENOTDIR") return { keys: {} };
|
|
39
|
+
if (code === "ELOOP") return ignored("is a symbolic link.");
|
|
40
|
+
return ignored(`cannot be read (${code ?? "error"}).`);
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const info = await handle.stat();
|
|
44
|
+
if (!info.isFile()) return ignored("is not a regular file.");
|
|
45
|
+
const uid = process.getuid?.();
|
|
46
|
+
if (uid !== void 0 && info.uid !== uid) return ignored("is not owned by you.");
|
|
47
|
+
if ((info.mode & 63) !== 0) {
|
|
48
|
+
return ignored(`can be read by other users. Run: chmod 600 ${file}.`);
|
|
49
|
+
}
|
|
50
|
+
if (info.size > CREDENTIALS_MAX_BYTES) return ignored("is too large.");
|
|
51
|
+
const text = await handle.readFile("utf8");
|
|
52
|
+
let data;
|
|
53
|
+
try {
|
|
54
|
+
data = JSON.parse(text);
|
|
55
|
+
} catch {
|
|
56
|
+
return ignored("is not valid JSON.");
|
|
57
|
+
}
|
|
58
|
+
const parsed = fileSchema.safeParse(data);
|
|
59
|
+
if (!parsed.success) {
|
|
60
|
+
return ignored(
|
|
61
|
+
'is not valid: it must be a JSON object of key names to keys, for example { "ANTHROPIC_API_KEY": "\u2026" }.'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return { keys: parsed.data };
|
|
65
|
+
} finally {
|
|
66
|
+
await handle.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function writeCredentials(keys, home = homedir()) {
|
|
70
|
+
const file = credentialsPath(home);
|
|
71
|
+
if (Object.keys(keys).length === 0) {
|
|
72
|
+
await rm(file, { force: true });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
await mkdir(dirname(file), { recursive: true, mode: 448 });
|
|
76
|
+
const temp = join(dirname(file), `.credentials.garuda-${randomBytes(4).toString("hex")}.tmp`);
|
|
77
|
+
await writeFile(temp, `${JSON.stringify(keys, null, 2)}
|
|
78
|
+
`, { flag: "wx", mode: 384 });
|
|
79
|
+
try {
|
|
80
|
+
await chmod(temp, 384);
|
|
81
|
+
await rename(temp, file);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
await rm(temp, { force: true });
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function maskKey(key) {
|
|
88
|
+
if (key.length < 16) return "\u2026";
|
|
89
|
+
if (key.length < 32) return `\u2026${key.slice(-4)}`;
|
|
90
|
+
const start = key.match(/^[A-Za-z]+-(?:[A-Za-z]+-)?/)?.[0] ?? key.slice(0, 3);
|
|
91
|
+
return `${start.slice(0, 8)}\u2026${key.slice(-4)}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/model/providers.ts
|
|
95
|
+
import { mkdir as mkdir2, readFile } from "fs/promises";
|
|
96
|
+
import { homedir as homedir2 } from "os";
|
|
97
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
98
|
+
import { z as z2 } from "zod";
|
|
99
|
+
var MODELS_FILE = join2(".garuda", "models.json");
|
|
100
|
+
var NO_MODEL = "Set a model with --model <id> or the GARUDA_MODEL variable, or run `garuda setup`.";
|
|
101
|
+
var priceSchema = z2.strictObject({
|
|
102
|
+
input: z2.number().min(0),
|
|
103
|
+
output: z2.number().min(0),
|
|
104
|
+
cacheRead: z2.number().min(0),
|
|
105
|
+
cacheWrite: z2.number().min(0)
|
|
106
|
+
});
|
|
107
|
+
var providerSchema = z2.strictObject({
|
|
108
|
+
type: z2.enum(["anthropic", "openai-compatible"]),
|
|
109
|
+
/** For openai-compatible: the URL that ends before /chat/completions. */
|
|
110
|
+
baseUrl: z2.url().optional(),
|
|
111
|
+
/** The environment variable that holds the API key. */
|
|
112
|
+
apiKeyEnv: z2.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional(),
|
|
113
|
+
/** A server on this machine: price 0 unless a model entry says otherwise. */
|
|
114
|
+
local: z2.boolean().optional(),
|
|
115
|
+
/** Allow plain http to a host that is not this machine. Off: an API key could travel in clear text. */
|
|
116
|
+
allowInsecureHttp: z2.boolean().optional()
|
|
117
|
+
});
|
|
118
|
+
var modelSchema = z2.strictObject({
|
|
119
|
+
contextWindow: z2.number().int().min(4096).optional(),
|
|
120
|
+
price: priceSchema.optional(),
|
|
121
|
+
maxTokens: z2.number().int().min(256).optional(),
|
|
122
|
+
/**
|
|
123
|
+
* Tool calls that the model writes as JSON text (openai-compatible only): "whole" (default) when
|
|
124
|
+
* the whole reply is calls, "lines" when calls stand on their own lines between prose, "off".
|
|
125
|
+
*/
|
|
126
|
+
textToolCalls: z2.enum(["off", "whole", "lines"]).optional()
|
|
127
|
+
});
|
|
128
|
+
var fileSchema2 = z2.strictObject({
|
|
129
|
+
providers: z2.record(z2.string().regex(/^[a-z][a-z0-9-]{0,31}$/), providerSchema).default({}),
|
|
130
|
+
/** Keyed by the full spec, for example "ollama/qwen3-coder:30b". */
|
|
131
|
+
models: z2.record(z2.string(), modelSchema).default({}),
|
|
132
|
+
/** The model when neither --model nor GARUDA_MODEL names one (0.16; `garuda setup` writes it). */
|
|
133
|
+
default: z2.string().min(1).optional()
|
|
134
|
+
});
|
|
135
|
+
var BUILTIN_PROVIDERS = {
|
|
136
|
+
anthropic: { type: "anthropic" },
|
|
137
|
+
ollama: { type: "openai-compatible", baseUrl: "http://localhost:11434/v1", local: true },
|
|
138
|
+
lmstudio: { type: "openai-compatible", baseUrl: "http://localhost:1234/v1", local: true },
|
|
139
|
+
llamacpp: { type: "openai-compatible", baseUrl: "http://localhost:8080/v1", local: true },
|
|
140
|
+
vllm: { type: "openai-compatible", baseUrl: "http://localhost:8000/v1", local: true },
|
|
141
|
+
openrouter: {
|
|
142
|
+
type: "openai-compatible",
|
|
143
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
144
|
+
apiKeyEnv: "OPENROUTER_API_KEY"
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var OPEN_MODEL_DEFAULT_WINDOW = 32768;
|
|
148
|
+
var ZERO_PRICE = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
149
|
+
function chooseModel(config, ...given) {
|
|
150
|
+
for (const spec of given) if (spec !== void 0 && spec !== "") return spec;
|
|
151
|
+
return config.default;
|
|
152
|
+
}
|
|
153
|
+
async function loadModelsConfig(home = homedir2()) {
|
|
154
|
+
const file = join2(home, MODELS_FILE);
|
|
155
|
+
let text;
|
|
156
|
+
try {
|
|
157
|
+
text = await readFile(file, "utf8");
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error.code === "ENOENT")
|
|
160
|
+
return { config: { providers: {}, models: {} } };
|
|
161
|
+
return {
|
|
162
|
+
config: { providers: {}, models: {} },
|
|
163
|
+
problem: `${file}: ${error.message}`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const parsed = fileSchema2.safeParse(JSON.parse(text));
|
|
168
|
+
if (!parsed.success) {
|
|
169
|
+
return {
|
|
170
|
+
config: { providers: {}, models: {} },
|
|
171
|
+
problem: `${file}: ${z2.prettifyError(parsed.error)}`
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return { config: parsed.data };
|
|
175
|
+
} catch (error) {
|
|
176
|
+
return {
|
|
177
|
+
config: { providers: {}, models: {} },
|
|
178
|
+
problem: `${file}: invalid JSON: ${error.message}`
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function setDefaultModel(spec, home = homedir2()) {
|
|
183
|
+
const file = join2(home, MODELS_FILE);
|
|
184
|
+
let data = {};
|
|
185
|
+
let exists = true;
|
|
186
|
+
try {
|
|
187
|
+
const parsed = JSON.parse(await readFile(file, "utf8"));
|
|
188
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
189
|
+
throw new Error(`${file} is not a JSON object.`);
|
|
190
|
+
}
|
|
191
|
+
data = parsed;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (error.code !== "ENOENT") throw error;
|
|
194
|
+
exists = false;
|
|
195
|
+
}
|
|
196
|
+
data.default = spec;
|
|
197
|
+
await mkdir2(dirname2(file), { recursive: true, mode: 448 });
|
|
198
|
+
await writeFileAtomic(file, `${JSON.stringify(data, null, 2)}
|
|
199
|
+
`, { createOnly: !exists });
|
|
200
|
+
}
|
|
201
|
+
var keptKeys = /* @__PURE__ */ new Map();
|
|
202
|
+
function keepProviderKey(name) {
|
|
203
|
+
const value = process.env[name];
|
|
204
|
+
if (value !== void 0 && value !== "") {
|
|
205
|
+
keptKeys.set(name, { value, from: "environment" });
|
|
206
|
+
keepSecretForRedaction(name, value);
|
|
207
|
+
delete process.env[name];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function keepStoredKey(name, value) {
|
|
211
|
+
if ((process.env[name] ?? "") !== "") return false;
|
|
212
|
+
if (keptKeys.get(name)?.from === "environment") return false;
|
|
213
|
+
keptKeys.set(name, { value, from: "file" });
|
|
214
|
+
keepSecretForRedaction(name, value);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
function providerKeySource(name) {
|
|
218
|
+
if ((process.env[name] ?? "") !== "") return "environment";
|
|
219
|
+
return keptKeys.get(name)?.from;
|
|
220
|
+
}
|
|
221
|
+
async function keepProviderKeys(names, home) {
|
|
222
|
+
const wanted = names.filter((name) => name !== void 0);
|
|
223
|
+
for (const name of wanted) keepProviderKey(name);
|
|
224
|
+
const stored = await readCredentials(home);
|
|
225
|
+
for (const [name, kept] of keptKeys) if (kept.from === "file") keptKeys.delete(name);
|
|
226
|
+
for (const [name, value] of Object.entries(stored.keys)) {
|
|
227
|
+
if (!keepStoredKey(name, value)) keepSecretForRedaction(name, value);
|
|
228
|
+
}
|
|
229
|
+
return stored.warning;
|
|
230
|
+
}
|
|
231
|
+
function providerKey(env, name) {
|
|
232
|
+
if (name === void 0) return void 0;
|
|
233
|
+
return keptKeys.get(name)?.value ?? env[name];
|
|
234
|
+
}
|
|
235
|
+
function resolveModel(spec, config = { providers: {}, models: {} }) {
|
|
236
|
+
const providers = { ...BUILTIN_PROVIDERS, ...config.providers };
|
|
237
|
+
const slash = spec.indexOf("/");
|
|
238
|
+
const [provider, model] = slash === -1 ? ["anthropic", spec] : [spec.slice(0, slash), spec.slice(slash + 1)];
|
|
239
|
+
const def = providers[provider];
|
|
240
|
+
if (def === void 0) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`Unknown model provider "${provider}" in "${spec}". Known providers: ${Object.keys(providers).join(", ")}. Add others in ~/${MODELS_FILE}.`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (model === "") throw new Error(`"${spec}" names no model. Use <provider>/<model>.`);
|
|
246
|
+
if (def.type === "openai-compatible") checkBaseUrl(provider, def);
|
|
247
|
+
const entry = config.models[spec] ?? (provider === "anthropic" ? config.models[model] : void 0);
|
|
248
|
+
const base = def.type === "anthropic" ? lookupModel(model) : { contextWindow: OPEN_MODEL_DEFAULT_WINDOW, ...def.local ? { price: ZERO_PRICE } : {} };
|
|
249
|
+
const info = {
|
|
250
|
+
contextWindow: entry?.contextWindow ?? base.contextWindow,
|
|
251
|
+
...entry?.price !== void 0 ? { price: entry.price } : base.price !== void 0 ? { price: base.price } : {},
|
|
252
|
+
// Thinking by model (0.9): only Claude models on the Anthropic provider have it.
|
|
253
|
+
...base.thinking === void 0 ? {} : { thinking: base.thinking }
|
|
254
|
+
};
|
|
255
|
+
const notes = [];
|
|
256
|
+
if (def.type === "openai-compatible" && entry?.contextWindow === void 0) {
|
|
257
|
+
notes.push(
|
|
258
|
+
`Garuda assumes a ${OPEN_MODEL_DEFAULT_WINDOW}-token context window for ${spec}. Set the real value ("contextWindow") for "${spec}" in ~/${MODELS_FILE}; the server must allow it too (for Ollama: OLLAMA_CONTEXT_LENGTH).`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
spec,
|
|
263
|
+
provider,
|
|
264
|
+
model,
|
|
265
|
+
def,
|
|
266
|
+
info,
|
|
267
|
+
notes,
|
|
268
|
+
...entry?.maxTokens === void 0 ? {} : { maxTokens: entry.maxTokens },
|
|
269
|
+
...def.type === "anthropic" ? {
|
|
270
|
+
async createBatch(env = process.env, options = {}) {
|
|
271
|
+
const apiKey = providerKey(env, def.apiKeyEnv ?? "ANTHROPIC_API_KEY");
|
|
272
|
+
const { AnthropicBatchClient } = await import("./anthropic-NQDBCHJU.js");
|
|
273
|
+
return new AnthropicBatchClient({ model, ...apiKey ? { apiKey } : {}, ...options });
|
|
274
|
+
}
|
|
275
|
+
} : {},
|
|
276
|
+
async create(env = process.env) {
|
|
277
|
+
const apiKey = providerKey(env, def.apiKeyEnv);
|
|
278
|
+
if (def.apiKeyEnv !== void 0 && (apiKey === void 0 || apiKey === "")) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Set ${def.apiKeyEnv} to use the ${provider} provider, or run \`garuda setup\`.`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (def.type === "anthropic") {
|
|
284
|
+
const key = providerKey(env, def.apiKeyEnv ?? "ANTHROPIC_API_KEY");
|
|
285
|
+
const { AnthropicClient } = await import("./anthropic-NQDBCHJU.js");
|
|
286
|
+
return new AnthropicClient({ model, ...key ? { apiKey: key } : {} });
|
|
287
|
+
}
|
|
288
|
+
const { OpenAICompatibleClient } = await import("./openaiCompatible-CJWVJP5M.js");
|
|
289
|
+
return new OpenAICompatibleClient({
|
|
290
|
+
provider,
|
|
291
|
+
baseUrl: def.baseUrl,
|
|
292
|
+
model,
|
|
293
|
+
...apiKey === void 0 ? {} : { apiKey },
|
|
294
|
+
...entry?.textToolCalls === void 0 ? {} : { textToolCalls: entry.textToolCalls }
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function checkBaseUrl(provider, def) {
|
|
300
|
+
if (def.baseUrl === void 0) throw new Error(`Provider "${provider}" needs a baseUrl.`);
|
|
301
|
+
const url = new URL(def.baseUrl);
|
|
302
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
303
|
+
throw new Error(`Provider "${provider}": the baseUrl must use http or https.`);
|
|
304
|
+
}
|
|
305
|
+
if (url.username !== "" || url.password !== "") {
|
|
306
|
+
throw new Error(`Provider "${provider}": put credentials in apiKeyEnv, not in the baseUrl.`);
|
|
307
|
+
}
|
|
308
|
+
if (url.protocol === "http:" && !isLoopbackHost(url.hostname) && def.allowInsecureHttp !== true) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`Provider "${provider}" uses plain http to ${url.hostname}. Use https, or set "allowInsecureHttp": true for a trusted network.`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export {
|
|
316
|
+
KEY_NAME,
|
|
317
|
+
credentialsPath,
|
|
318
|
+
readCredentials,
|
|
319
|
+
writeCredentials,
|
|
320
|
+
maskKey,
|
|
321
|
+
MODELS_FILE,
|
|
322
|
+
NO_MODEL,
|
|
323
|
+
BUILTIN_PROVIDERS,
|
|
324
|
+
chooseModel,
|
|
325
|
+
loadModelsConfig,
|
|
326
|
+
setDefaultModel,
|
|
327
|
+
providerKeySource,
|
|
328
|
+
keepProviderKeys,
|
|
329
|
+
resolveModel
|
|
330
|
+
};
|
|
331
|
+
//# sourceMappingURL=chunk-NP6RIC25.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
EFFORTS
|
|
4
|
+
} from "./chunk-3EQU2GUX.js";
|
|
5
|
+
|
|
6
|
+
// src/model/pricing.ts
|
|
7
|
+
var DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
8
|
+
var M = 1e6;
|
|
9
|
+
var ALWAYS = { mode: "always", efforts: EFFORTS };
|
|
10
|
+
var OPTIONAL = { mode: "optional", efforts: EFFORTS };
|
|
11
|
+
var OPTIONAL_46 = {
|
|
12
|
+
mode: "optional",
|
|
13
|
+
efforts: EFFORTS.filter((e) => e !== "xhigh")
|
|
14
|
+
};
|
|
15
|
+
var KNOWN = [
|
|
16
|
+
["claude-fable-5-1", { price: p(10, 50, 0.25, 12.5), contextWindow: M, thinking: ALWAYS }],
|
|
17
|
+
["claude-fable-5", { price: p(10, 50, 1, 12.5), contextWindow: M, thinking: ALWAYS }],
|
|
18
|
+
["claude-opus-5-5", { price: p(4, 20, 0.2, 5), contextWindow: M, thinking: ALWAYS }],
|
|
19
|
+
["claude-opus-5", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: ALWAYS }],
|
|
20
|
+
["claude-opus-4-8", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: OPTIONAL }],
|
|
21
|
+
["claude-opus-4-7", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: OPTIONAL }],
|
|
22
|
+
["claude-opus-4-6", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: OPTIONAL_46 }],
|
|
23
|
+
["claude-opus-4-5", { price: p(5, 25, 0.5, 6.25), contextWindow: 2e5 }],
|
|
24
|
+
["claude-sonnet-5", { price: p(2, 10, 0.2, 2.5), contextWindow: M, thinking: ALWAYS }],
|
|
25
|
+
["claude-sonnet-4-6", { price: p(3, 15, 0.3, 3.75), contextWindow: M, thinking: OPTIONAL_46 }],
|
|
26
|
+
["claude-sonnet-4-5", { price: p(3, 15, 0.3, 3.75), contextWindow: 2e5 }],
|
|
27
|
+
["claude-haiku-4-5", { price: p(1, 5, 0.1, 1.25), contextWindow: 2e5 }]
|
|
28
|
+
];
|
|
29
|
+
function p(input, output, cacheRead, cacheWrite) {
|
|
30
|
+
return { input, output, cacheRead, cacheWrite };
|
|
31
|
+
}
|
|
32
|
+
function aliasModel(alias) {
|
|
33
|
+
if (!/^(haiku|sonnet|opus|fable)$/.test(alias)) return void 0;
|
|
34
|
+
return KNOWN.find(([prefix]) => prefix.startsWith(`claude-${alias}-`))?.[0];
|
|
35
|
+
}
|
|
36
|
+
function knownModels() {
|
|
37
|
+
return KNOWN.map(([id, info]) => ({ id, info }));
|
|
38
|
+
}
|
|
39
|
+
function lookupModel(modelId) {
|
|
40
|
+
let best;
|
|
41
|
+
for (const [prefix, info] of KNOWN) {
|
|
42
|
+
if (modelId.startsWith(prefix) && prefix.length > (best?.prefix.length ?? 0)) {
|
|
43
|
+
best = { prefix, info };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return best?.info ?? { contextWindow: DEFAULT_CONTEXT_WINDOW };
|
|
47
|
+
}
|
|
48
|
+
var WEB_SEARCH_USD = 0.01;
|
|
49
|
+
function costOf(usage, price, factor = 1) {
|
|
50
|
+
return (usage.inputTokens * price.input + usage.outputTokens * price.output + usage.cacheReadTokens * price.cacheRead + usage.cacheWriteTokens * price.cacheWrite) / M * factor + (usage.webSearches ?? 0) * WEB_SEARCH_USD;
|
|
51
|
+
}
|
|
52
|
+
function responseCost(response, price) {
|
|
53
|
+
return costOf(response.usage, price, response.priceFactor ?? 1);
|
|
54
|
+
}
|
|
55
|
+
function totalTokens(usage) {
|
|
56
|
+
return usage.inputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
57
|
+
}
|
|
58
|
+
function contextSize(usage) {
|
|
59
|
+
return totalTokens(usage);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
aliasModel,
|
|
64
|
+
knownModels,
|
|
65
|
+
lookupModel,
|
|
66
|
+
WEB_SEARCH_USD,
|
|
67
|
+
responseCost,
|
|
68
|
+
totalTokens,
|
|
69
|
+
contextSize
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=chunk-R7I2NBPX.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_ENV_ALLOWLIST
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GXZACJUI.js";
|
|
5
5
|
|
|
6
6
|
// src/jobs/git.ts
|
|
7
7
|
var GIT_TIMEOUT_MS = 12e4;
|
|
@@ -46,4 +46,4 @@ export {
|
|
|
46
46
|
git,
|
|
47
47
|
hostCommand
|
|
48
48
|
};
|
|
49
|
-
//# sourceMappingURL=chunk-
|
|
49
|
+
//# sourceMappingURL=chunk-TUUEBHG2.js.map
|
|
@@ -2,69 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
cleanLine
|
|
4
4
|
} from "./chunk-PZS3V6P3.js";
|
|
5
|
-
import {
|
|
6
|
-
EFFORTS
|
|
7
|
-
} from "./chunk-SI2QJYCR.js";
|
|
8
5
|
import {
|
|
9
6
|
formatRule
|
|
10
7
|
} from "./chunk-3NJNVZTK.js";
|
|
11
8
|
|
|
12
|
-
// src/model/pricing.ts
|
|
13
|
-
var DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
14
|
-
var M = 1e6;
|
|
15
|
-
var ALWAYS = { mode: "always", efforts: EFFORTS };
|
|
16
|
-
var OPTIONAL = { mode: "optional", efforts: EFFORTS };
|
|
17
|
-
var OPTIONAL_46 = {
|
|
18
|
-
mode: "optional",
|
|
19
|
-
efforts: EFFORTS.filter((e) => e !== "xhigh")
|
|
20
|
-
};
|
|
21
|
-
var KNOWN = [
|
|
22
|
-
["claude-fable-5-1", { price: p(10, 50, 0.25, 12.5), contextWindow: M, thinking: ALWAYS }],
|
|
23
|
-
["claude-fable-5", { price: p(10, 50, 1, 12.5), contextWindow: M, thinking: ALWAYS }],
|
|
24
|
-
["claude-opus-5-5", { price: p(4, 20, 0.2, 5), contextWindow: M, thinking: ALWAYS }],
|
|
25
|
-
["claude-opus-5", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: ALWAYS }],
|
|
26
|
-
["claude-opus-4-8", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: OPTIONAL }],
|
|
27
|
-
["claude-opus-4-7", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: OPTIONAL }],
|
|
28
|
-
["claude-opus-4-6", { price: p(5, 25, 0.5, 6.25), contextWindow: M, thinking: OPTIONAL_46 }],
|
|
29
|
-
["claude-opus-4-5", { price: p(5, 25, 0.5, 6.25), contextWindow: 2e5 }],
|
|
30
|
-
["claude-sonnet-5", { price: p(2, 10, 0.2, 2.5), contextWindow: M, thinking: ALWAYS }],
|
|
31
|
-
["claude-sonnet-4-6", { price: p(3, 15, 0.3, 3.75), contextWindow: M, thinking: OPTIONAL_46 }],
|
|
32
|
-
["claude-sonnet-4-5", { price: p(3, 15, 0.3, 3.75), contextWindow: 2e5 }],
|
|
33
|
-
["claude-haiku-4-5", { price: p(1, 5, 0.1, 1.25), contextWindow: 2e5 }]
|
|
34
|
-
];
|
|
35
|
-
function p(input, output, cacheRead, cacheWrite) {
|
|
36
|
-
return { input, output, cacheRead, cacheWrite };
|
|
37
|
-
}
|
|
38
|
-
function aliasModel(alias) {
|
|
39
|
-
if (!/^(haiku|sonnet|opus|fable)$/.test(alias)) return void 0;
|
|
40
|
-
return KNOWN.find(([prefix]) => prefix.startsWith(`claude-${alias}-`))?.[0];
|
|
41
|
-
}
|
|
42
|
-
function knownModels() {
|
|
43
|
-
return KNOWN.map(([id, info]) => ({ id, info }));
|
|
44
|
-
}
|
|
45
|
-
function lookupModel(modelId) {
|
|
46
|
-
let best;
|
|
47
|
-
for (const [prefix, info] of KNOWN) {
|
|
48
|
-
if (modelId.startsWith(prefix) && prefix.length > (best?.prefix.length ?? 0)) {
|
|
49
|
-
best = { prefix, info };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return best?.info ?? { contextWindow: DEFAULT_CONTEXT_WINDOW };
|
|
53
|
-
}
|
|
54
|
-
var WEB_SEARCH_USD = 0.01;
|
|
55
|
-
function costOf(usage, price, factor = 1) {
|
|
56
|
-
return (usage.inputTokens * price.input + usage.outputTokens * price.output + usage.cacheReadTokens * price.cacheRead + usage.cacheWriteTokens * price.cacheWrite) / M * factor + (usage.webSearches ?? 0) * WEB_SEARCH_USD;
|
|
57
|
-
}
|
|
58
|
-
function responseCost(response, price) {
|
|
59
|
-
return costOf(response.usage, price, response.priceFactor ?? 1);
|
|
60
|
-
}
|
|
61
|
-
function totalTokens(usage) {
|
|
62
|
-
return usage.inputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
63
|
-
}
|
|
64
|
-
function contextSize(usage) {
|
|
65
|
-
return totalTokens(usage);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
9
|
// src/permissions/projectSettings.ts
|
|
69
10
|
import { createHash } from "crypto";
|
|
70
11
|
function settingsRisk(settings) {
|
|
@@ -161,13 +102,6 @@ async function gateProjectSettings(options) {
|
|
|
161
102
|
}
|
|
162
103
|
|
|
163
104
|
export {
|
|
164
|
-
aliasModel,
|
|
165
|
-
knownModels,
|
|
166
|
-
lookupModel,
|
|
167
|
-
WEB_SEARCH_USD,
|
|
168
|
-
responseCost,
|
|
169
|
-
totalTokens,
|
|
170
|
-
contextSize,
|
|
171
105
|
gateProjectSettings
|
|
172
106
|
};
|
|
173
|
-
//# sourceMappingURL=chunk-
|
|
107
|
+
//# sourceMappingURL=chunk-ULAJPMY7.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
hostCommand,
|
|
4
4
|
shellWord
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TUUEBHG2.js";
|
|
6
6
|
import {
|
|
7
7
|
JOBS_DIR
|
|
8
8
|
} from "./chunk-KKB5W47S.js";
|
|
@@ -154,4 +154,4 @@ export {
|
|
|
154
154
|
nightAgentTime,
|
|
155
155
|
removeAgent
|
|
156
156
|
};
|
|
157
|
-
//# sourceMappingURL=chunk-
|
|
157
|
+
//# sourceMappingURL=chunk-USCUXCAU.js.map
|