heyio 4.2.5 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/daemon/index.js
CHANGED
|
@@ -79,10 +79,10 @@ var init_constants = __esm({
|
|
|
79
79
|
"packages/shared/dist/constants.js"() {
|
|
80
80
|
"use strict";
|
|
81
81
|
APP_NAME = "io";
|
|
82
|
-
APP_VERSION = "4.
|
|
82
|
+
APP_VERSION = "4.3.0";
|
|
83
83
|
API_PORT = 7777;
|
|
84
84
|
API_HOST = "0.0.0.0";
|
|
85
|
-
DEFAULT_MODEL = "gpt-
|
|
85
|
+
DEFAULT_MODEL = "gpt-4.1-mini";
|
|
86
86
|
SESSION_RESET_THRESHOLD = 50;
|
|
87
87
|
SCHEDULER_INTERVAL_MS = 6e4;
|
|
88
88
|
QA_MAX_REVISIONS = 3;
|
|
@@ -152,6 +152,349 @@ var init_paths = __esm({
|
|
|
152
152
|
}
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
+
// packages/daemon/src/store/db.ts
|
|
156
|
+
import { mkdir } from "node:fs/promises";
|
|
157
|
+
import { dirname } from "node:path";
|
|
158
|
+
import { pathToFileURL } from "node:url";
|
|
159
|
+
import { createClient } from "@libsql/client";
|
|
160
|
+
function asString(value) {
|
|
161
|
+
if (typeof value === "string") {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
if (value === null || value === void 0) {
|
|
165
|
+
throw new Error("Expected string value but received null or undefined");
|
|
166
|
+
}
|
|
167
|
+
return String(value);
|
|
168
|
+
}
|
|
169
|
+
function asNullableString(value) {
|
|
170
|
+
if (value === null || value === void 0) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return typeof value === "string" ? value : String(value);
|
|
174
|
+
}
|
|
175
|
+
function asNumber(value) {
|
|
176
|
+
if (typeof value === "number") {
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
if (typeof value === "bigint") {
|
|
180
|
+
return Number(value);
|
|
181
|
+
}
|
|
182
|
+
if (typeof value === "string") {
|
|
183
|
+
return Number(value);
|
|
184
|
+
}
|
|
185
|
+
throw new Error(`Expected numeric value but received ${typeof value}`);
|
|
186
|
+
}
|
|
187
|
+
function asNullableNumber(value) {
|
|
188
|
+
if (value === null || value === void 0) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
return asNumber(value);
|
|
192
|
+
}
|
|
193
|
+
function asBoolean(value) {
|
|
194
|
+
return asNumber(value) === 1;
|
|
195
|
+
}
|
|
196
|
+
function toSqliteBoolean(value) {
|
|
197
|
+
return value ? 1 : 0;
|
|
198
|
+
}
|
|
199
|
+
function parseJson(value) {
|
|
200
|
+
if (typeof value !== "string") {
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
return JSON.parse(value);
|
|
204
|
+
}
|
|
205
|
+
function serializeJson(value) {
|
|
206
|
+
return JSON.stringify(value);
|
|
207
|
+
}
|
|
208
|
+
async function initDatabase() {
|
|
209
|
+
if (client && !client.closed) {
|
|
210
|
+
return client;
|
|
211
|
+
}
|
|
212
|
+
if (initPromise) {
|
|
213
|
+
return initPromise;
|
|
214
|
+
}
|
|
215
|
+
initPromise = initializeDatabase(defaultConnectionOptions);
|
|
216
|
+
try {
|
|
217
|
+
client = await initPromise;
|
|
218
|
+
return client;
|
|
219
|
+
} finally {
|
|
220
|
+
initPromise = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function getDatabase() {
|
|
224
|
+
return initDatabase();
|
|
225
|
+
}
|
|
226
|
+
async function initializeDatabase(options2 = {}) {
|
|
227
|
+
const url2 = await resolveDatabaseUrl(options2);
|
|
228
|
+
const databaseClient = createClient({ url: url2 });
|
|
229
|
+
await databaseClient.execute("PRAGMA foreign_keys = ON");
|
|
230
|
+
await databaseClient.execute("PRAGMA journal_mode = WAL");
|
|
231
|
+
await ensureMigrationTable(databaseClient);
|
|
232
|
+
await applyMigrations(databaseClient);
|
|
233
|
+
return databaseClient;
|
|
234
|
+
}
|
|
235
|
+
async function resolveDatabaseUrl(options2) {
|
|
236
|
+
if (options2.url) {
|
|
237
|
+
return options2.url;
|
|
238
|
+
}
|
|
239
|
+
const databasePath = options2.path ?? DB_PATH;
|
|
240
|
+
await mkdir(dirname(databasePath), { recursive: true });
|
|
241
|
+
return pathToFileURL(databasePath).href;
|
|
242
|
+
}
|
|
243
|
+
async function ensureMigrationTable(databaseClient) {
|
|
244
|
+
await databaseClient.executeMultiple(`
|
|
245
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
246
|
+
version INTEGER PRIMARY KEY,
|
|
247
|
+
name TEXT NOT NULL,
|
|
248
|
+
applied_at TEXT NOT NULL
|
|
249
|
+
);
|
|
250
|
+
`);
|
|
251
|
+
}
|
|
252
|
+
async function applyMigrations(databaseClient) {
|
|
253
|
+
const appliedVersions = await getAppliedVersions(databaseClient);
|
|
254
|
+
for (const migration of MIGRATIONS) {
|
|
255
|
+
if (appliedVersions.has(migration.version)) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const transaction = await databaseClient.transaction("write");
|
|
259
|
+
try {
|
|
260
|
+
for (const statement of migration.statements) {
|
|
261
|
+
await transaction.execute(statement);
|
|
262
|
+
}
|
|
263
|
+
await transaction.execute({
|
|
264
|
+
sql: "INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)",
|
|
265
|
+
args: [migration.version, migration.name, nowIso()]
|
|
266
|
+
});
|
|
267
|
+
await transaction.commit();
|
|
268
|
+
} catch (error51) {
|
|
269
|
+
if (!transaction.closed) {
|
|
270
|
+
await transaction.rollback();
|
|
271
|
+
}
|
|
272
|
+
throw error51;
|
|
273
|
+
} finally {
|
|
274
|
+
if (!transaction.closed) {
|
|
275
|
+
transaction.close();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function getAppliedVersions(databaseClient) {
|
|
281
|
+
const result = await databaseClient.execute(
|
|
282
|
+
"SELECT version FROM schema_migrations ORDER BY version ASC"
|
|
283
|
+
);
|
|
284
|
+
return new Set(result.rows.map((row) => asNumber(row.version)));
|
|
285
|
+
}
|
|
286
|
+
var MIGRATIONS, client, initPromise, defaultConnectionOptions, generateId, nowIso;
|
|
287
|
+
var init_db = __esm({
|
|
288
|
+
"packages/daemon/src/store/db.ts"() {
|
|
289
|
+
"use strict";
|
|
290
|
+
init_paths();
|
|
291
|
+
MIGRATIONS = [
|
|
292
|
+
{
|
|
293
|
+
version: 1,
|
|
294
|
+
name: "create-store-schema",
|
|
295
|
+
statements: [
|
|
296
|
+
`CREATE TABLE IF NOT EXISTS squads (
|
|
297
|
+
id TEXT PRIMARY KEY,
|
|
298
|
+
name TEXT NOT NULL,
|
|
299
|
+
repo_url TEXT NOT NULL,
|
|
300
|
+
repo_owner TEXT NOT NULL,
|
|
301
|
+
repo_name TEXT NOT NULL,
|
|
302
|
+
status TEXT NOT NULL,
|
|
303
|
+
config TEXT NOT NULL,
|
|
304
|
+
created_at TEXT NOT NULL,
|
|
305
|
+
updated_at TEXT NOT NULL,
|
|
306
|
+
UNIQUE (repo_owner, repo_name)
|
|
307
|
+
)`,
|
|
308
|
+
`CREATE TABLE IF NOT EXISTS squad_members (
|
|
309
|
+
id TEXT PRIMARY KEY,
|
|
310
|
+
squad_id TEXT NOT NULL REFERENCES squads(id) ON DELETE CASCADE,
|
|
311
|
+
role TEXT NOT NULL,
|
|
312
|
+
name TEXT NOT NULL,
|
|
313
|
+
system_prompt TEXT NOT NULL,
|
|
314
|
+
model TEXT,
|
|
315
|
+
created_at TEXT NOT NULL
|
|
316
|
+
)`,
|
|
317
|
+
`CREATE TABLE IF NOT EXISTS objectives (
|
|
318
|
+
id TEXT PRIMARY KEY,
|
|
319
|
+
squad_id TEXT NOT NULL REFERENCES squads(id) ON DELETE CASCADE,
|
|
320
|
+
description TEXT NOT NULL,
|
|
321
|
+
status TEXT NOT NULL,
|
|
322
|
+
plan TEXT,
|
|
323
|
+
revision_count INTEGER NOT NULL DEFAULT 0,
|
|
324
|
+
branch TEXT,
|
|
325
|
+
pr_url TEXT,
|
|
326
|
+
created_at TEXT NOT NULL,
|
|
327
|
+
updated_at TEXT NOT NULL
|
|
328
|
+
)`,
|
|
329
|
+
`CREATE TABLE IF NOT EXISTS tasks (
|
|
330
|
+
id TEXT PRIMARY KEY,
|
|
331
|
+
objective_id TEXT NOT NULL REFERENCES objectives(id) ON DELETE CASCADE,
|
|
332
|
+
assignee_id TEXT REFERENCES squad_members(id) ON DELETE SET NULL,
|
|
333
|
+
title TEXT NOT NULL,
|
|
334
|
+
description TEXT NOT NULL,
|
|
335
|
+
status TEXT NOT NULL,
|
|
336
|
+
result TEXT,
|
|
337
|
+
created_at TEXT NOT NULL,
|
|
338
|
+
updated_at TEXT NOT NULL
|
|
339
|
+
)`,
|
|
340
|
+
`CREATE TABLE IF NOT EXISTS conversations (
|
|
341
|
+
id TEXT PRIMARY KEY,
|
|
342
|
+
title TEXT,
|
|
343
|
+
source TEXT NOT NULL,
|
|
344
|
+
created_at TEXT NOT NULL,
|
|
345
|
+
updated_at TEXT NOT NULL
|
|
346
|
+
)`,
|
|
347
|
+
`CREATE TABLE IF NOT EXISTS messages (
|
|
348
|
+
id TEXT PRIMARY KEY,
|
|
349
|
+
conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
|
|
350
|
+
role TEXT NOT NULL,
|
|
351
|
+
content TEXT NOT NULL,
|
|
352
|
+
model TEXT,
|
|
353
|
+
input_tokens INTEGER,
|
|
354
|
+
output_tokens INTEGER,
|
|
355
|
+
created_at TEXT NOT NULL
|
|
356
|
+
)`,
|
|
357
|
+
`CREATE TABLE IF NOT EXISTS inbox (
|
|
358
|
+
id TEXT PRIMARY KEY,
|
|
359
|
+
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
360
|
+
objective_id TEXT REFERENCES objectives(id) ON DELETE SET NULL,
|
|
361
|
+
type TEXT NOT NULL,
|
|
362
|
+
title TEXT NOT NULL,
|
|
363
|
+
content TEXT NOT NULL,
|
|
364
|
+
status TEXT NOT NULL,
|
|
365
|
+
reply TEXT,
|
|
366
|
+
created_at TEXT NOT NULL,
|
|
367
|
+
updated_at TEXT NOT NULL
|
|
368
|
+
)`,
|
|
369
|
+
`CREATE TABLE IF NOT EXISTS schedules (
|
|
370
|
+
id TEXT PRIMARY KEY,
|
|
371
|
+
name TEXT NOT NULL,
|
|
372
|
+
cron_expression TEXT NOT NULL,
|
|
373
|
+
prompt TEXT NOT NULL,
|
|
374
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
375
|
+
last_run_at TEXT,
|
|
376
|
+
next_run_at TEXT,
|
|
377
|
+
created_at TEXT NOT NULL,
|
|
378
|
+
updated_at TEXT NOT NULL
|
|
379
|
+
)`,
|
|
380
|
+
`CREATE TABLE IF NOT EXISTS token_usage (
|
|
381
|
+
id TEXT PRIMARY KEY,
|
|
382
|
+
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
383
|
+
agent_id TEXT,
|
|
384
|
+
model TEXT NOT NULL,
|
|
385
|
+
input_tokens INTEGER NOT NULL,
|
|
386
|
+
output_tokens INTEGER NOT NULL,
|
|
387
|
+
cost REAL NOT NULL,
|
|
388
|
+
created_at TEXT NOT NULL
|
|
389
|
+
)`,
|
|
390
|
+
`CREATE TABLE IF NOT EXISTS activity (
|
|
391
|
+
id TEXT PRIMARY KEY,
|
|
392
|
+
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
393
|
+
objective_id TEXT REFERENCES objectives(id) ON DELETE SET NULL,
|
|
394
|
+
event TEXT NOT NULL,
|
|
395
|
+
description TEXT NOT NULL,
|
|
396
|
+
metadata TEXT,
|
|
397
|
+
created_at TEXT NOT NULL
|
|
398
|
+
)`,
|
|
399
|
+
`CREATE TABLE IF NOT EXISTS agent_history (
|
|
400
|
+
id TEXT PRIMARY KEY,
|
|
401
|
+
agent_id TEXT NOT NULL,
|
|
402
|
+
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
403
|
+
content TEXT NOT NULL,
|
|
404
|
+
created_at TEXT NOT NULL
|
|
405
|
+
)`,
|
|
406
|
+
`CREATE TABLE IF NOT EXISTS settings (
|
|
407
|
+
key TEXT PRIMARY KEY,
|
|
408
|
+
value TEXT
|
|
409
|
+
)`,
|
|
410
|
+
"CREATE INDEX IF NOT EXISTS idx_squad_members_squad_id ON squad_members(squad_id)",
|
|
411
|
+
"CREATE INDEX IF NOT EXISTS idx_objectives_squad_id ON objectives(squad_id)",
|
|
412
|
+
"CREATE INDEX IF NOT EXISTS idx_objectives_status ON objectives(status)",
|
|
413
|
+
"CREATE INDEX IF NOT EXISTS idx_tasks_objective_id ON tasks(objective_id)",
|
|
414
|
+
"CREATE INDEX IF NOT EXISTS idx_tasks_assignee_id ON tasks(assignee_id)",
|
|
415
|
+
"CREATE INDEX IF NOT EXISTS idx_messages_conversation_id_created_at ON messages(conversation_id, created_at)",
|
|
416
|
+
"CREATE INDEX IF NOT EXISTS idx_inbox_status_created_at ON inbox(status, created_at)",
|
|
417
|
+
"CREATE INDEX IF NOT EXISTS idx_inbox_squad_id ON inbox(squad_id)",
|
|
418
|
+
"CREATE INDEX IF NOT EXISTS idx_inbox_objective_id ON inbox(objective_id)",
|
|
419
|
+
"CREATE INDEX IF NOT EXISTS idx_schedules_enabled_next_run_at ON schedules(enabled, next_run_at)",
|
|
420
|
+
"CREATE INDEX IF NOT EXISTS idx_token_usage_created_at ON token_usage(created_at)",
|
|
421
|
+
"CREATE INDEX IF NOT EXISTS idx_token_usage_squad_id ON token_usage(squad_id)",
|
|
422
|
+
"CREATE INDEX IF NOT EXISTS idx_token_usage_agent_id ON token_usage(agent_id)",
|
|
423
|
+
"CREATE INDEX IF NOT EXISTS idx_token_usage_model ON token_usage(model)",
|
|
424
|
+
"CREATE INDEX IF NOT EXISTS idx_activity_created_at ON activity(created_at)",
|
|
425
|
+
"CREATE INDEX IF NOT EXISTS idx_activity_squad_id ON activity(squad_id)",
|
|
426
|
+
"CREATE INDEX IF NOT EXISTS idx_activity_objective_id ON activity(objective_id)",
|
|
427
|
+
"CREATE INDEX IF NOT EXISTS idx_agent_history_agent_id_created_at ON agent_history(agent_id, created_at)"
|
|
428
|
+
]
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
version: 2,
|
|
432
|
+
name: "add-model-pricing-and-dual-costs",
|
|
433
|
+
statements: [
|
|
434
|
+
`CREATE TABLE IF NOT EXISTS model_pricing (
|
|
435
|
+
id TEXT PRIMARY KEY,
|
|
436
|
+
display_name TEXT NOT NULL,
|
|
437
|
+
premium_multiplier REAL,
|
|
438
|
+
token_input_multiplier REAL,
|
|
439
|
+
token_output_multiplier REAL,
|
|
440
|
+
cached_input_multiplier REAL,
|
|
441
|
+
tier TEXT NOT NULL,
|
|
442
|
+
available INTEGER NOT NULL DEFAULT 1,
|
|
443
|
+
updated_at TEXT NOT NULL
|
|
444
|
+
)`,
|
|
445
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_tier ON model_pricing(tier)",
|
|
446
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_available ON model_pricing(available)",
|
|
447
|
+
"ALTER TABLE token_usage ADD COLUMN premium_request_cost REAL",
|
|
448
|
+
"ALTER TABLE token_usage ADD COLUMN token_unit_cost REAL"
|
|
449
|
+
]
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
version: 3,
|
|
453
|
+
name: "add-squad-instances",
|
|
454
|
+
statements: [
|
|
455
|
+
`CREATE TABLE IF NOT EXISTS squad_instances (
|
|
456
|
+
id TEXT PRIMARY KEY,
|
|
457
|
+
squad_id TEXT NOT NULL REFERENCES squads(id) ON DELETE CASCADE,
|
|
458
|
+
objective_id TEXT REFERENCES objectives(id) ON DELETE SET NULL,
|
|
459
|
+
status TEXT NOT NULL DEFAULT 'queued',
|
|
460
|
+
branch TEXT,
|
|
461
|
+
worktree_path TEXT,
|
|
462
|
+
created_at TEXT NOT NULL,
|
|
463
|
+
started_at TEXT,
|
|
464
|
+
completed_at TEXT,
|
|
465
|
+
error TEXT
|
|
466
|
+
)`,
|
|
467
|
+
"CREATE INDEX IF NOT EXISTS idx_squad_instances_squad_status ON squad_instances(squad_id, status)",
|
|
468
|
+
"CREATE INDEX IF NOT EXISTS idx_squad_instances_status ON squad_instances(status)",
|
|
469
|
+
"CREATE INDEX IF NOT EXISTS idx_squad_instances_objective_id ON squad_instances(objective_id)"
|
|
470
|
+
]
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
version: 4,
|
|
474
|
+
name: "denormalize-usage-names",
|
|
475
|
+
statements: [
|
|
476
|
+
"ALTER TABLE token_usage ADD COLUMN squad_name TEXT",
|
|
477
|
+
"ALTER TABLE token_usage ADD COLUMN agent_name TEXT",
|
|
478
|
+
`UPDATE token_usage SET
|
|
479
|
+
squad_name = (SELECT s.name FROM squads s WHERE s.id = token_usage.squad_id),
|
|
480
|
+
agent_name = (SELECT sm.name FROM squad_members sm WHERE sm.id = token_usage.agent_id)
|
|
481
|
+
WHERE squad_id IS NOT NULL OR agent_id IS NOT NULL`
|
|
482
|
+
]
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
version: 5,
|
|
486
|
+
name: "add-squad-soft-delete",
|
|
487
|
+
statements: ["ALTER TABLE squads ADD COLUMN deleted_at TEXT"]
|
|
488
|
+
}
|
|
489
|
+
];
|
|
490
|
+
client = null;
|
|
491
|
+
initPromise = null;
|
|
492
|
+
defaultConnectionOptions = {};
|
|
493
|
+
generateId = () => crypto.randomUUID();
|
|
494
|
+
nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
155
498
|
// node_modules/cron-parser/dist/fields/types.js
|
|
156
499
|
var require_types = __commonJS({
|
|
157
500
|
"node_modules/cron-parser/dist/fields/types.js"(exports2) {
|
|
@@ -62206,6 +62549,493 @@ var require_websocket_server = __commonJS({
|
|
|
62206
62549
|
}
|
|
62207
62550
|
});
|
|
62208
62551
|
|
|
62552
|
+
// packages/daemon/src/models/catalog.ts
|
|
62553
|
+
import { execFileSync } from "node:child_process";
|
|
62554
|
+
function resolveGitHubToken() {
|
|
62555
|
+
const envToken = process.env.GITHUB_TOKEN?.trim();
|
|
62556
|
+
if (envToken && envToken.length > 0) {
|
|
62557
|
+
return envToken;
|
|
62558
|
+
}
|
|
62559
|
+
try {
|
|
62560
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
62561
|
+
encoding: "utf8",
|
|
62562
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
62563
|
+
}).trim();
|
|
62564
|
+
return token.length > 0 ? token : void 0;
|
|
62565
|
+
} catch {
|
|
62566
|
+
return void 0;
|
|
62567
|
+
}
|
|
62568
|
+
}
|
|
62569
|
+
async function fetchModelCatalog() {
|
|
62570
|
+
const token = resolveGitHubToken();
|
|
62571
|
+
if (!token) {
|
|
62572
|
+
throw new Error("No GitHub token available for model catalog fetch");
|
|
62573
|
+
}
|
|
62574
|
+
const response = await fetch(CATALOG_URL, {
|
|
62575
|
+
headers: {
|
|
62576
|
+
Accept: "application/json",
|
|
62577
|
+
Authorization: `Bearer ${token}`,
|
|
62578
|
+
"X-GitHub-Api-Version": "2024-12-01"
|
|
62579
|
+
}
|
|
62580
|
+
});
|
|
62581
|
+
if (!response.ok) {
|
|
62582
|
+
throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
|
|
62583
|
+
}
|
|
62584
|
+
const data = await response.json();
|
|
62585
|
+
if (!Array.isArray(data)) {
|
|
62586
|
+
throw new Error("Model catalog response is not an array");
|
|
62587
|
+
}
|
|
62588
|
+
const models = [];
|
|
62589
|
+
for (const entry of data) {
|
|
62590
|
+
const id = entry.id ?? entry.name;
|
|
62591
|
+
const displayName = entry.friendly_name ?? entry.name ?? id;
|
|
62592
|
+
if (id && typeof id === "string") {
|
|
62593
|
+
models.push({ id, displayName: displayName ?? id });
|
|
62594
|
+
}
|
|
62595
|
+
}
|
|
62596
|
+
return models;
|
|
62597
|
+
}
|
|
62598
|
+
var CATALOG_URL;
|
|
62599
|
+
var init_catalog = __esm({
|
|
62600
|
+
"packages/daemon/src/models/catalog.ts"() {
|
|
62601
|
+
"use strict";
|
|
62602
|
+
CATALOG_URL = "https://models.github.ai/catalog/models";
|
|
62603
|
+
}
|
|
62604
|
+
});
|
|
62605
|
+
|
|
62606
|
+
// packages/daemon/src/models/pricing-scraper.ts
|
|
62607
|
+
async function scrapeTokenUnitPricing() {
|
|
62608
|
+
const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
|
|
62609
|
+
return parseTokenUnitTable(html);
|
|
62610
|
+
}
|
|
62611
|
+
async function scrapeCopilotPricing() {
|
|
62612
|
+
const markdown = await fetchMarkdown(COPILOT_PRICING_URL);
|
|
62613
|
+
return parseCopilotPricingMarkdown(markdown);
|
|
62614
|
+
}
|
|
62615
|
+
async function scrapePremiumRequestPricing() {
|
|
62616
|
+
const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
|
|
62617
|
+
return parsePremiumMultiplierTable(html);
|
|
62618
|
+
}
|
|
62619
|
+
async function fetchPage(url2) {
|
|
62620
|
+
const response = await fetch(url2, {
|
|
62621
|
+
headers: {
|
|
62622
|
+
Accept: "text/html",
|
|
62623
|
+
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
62624
|
+
},
|
|
62625
|
+
redirect: "follow"
|
|
62626
|
+
});
|
|
62627
|
+
if (!response.ok) {
|
|
62628
|
+
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
62629
|
+
}
|
|
62630
|
+
return response.text();
|
|
62631
|
+
}
|
|
62632
|
+
async function fetchMarkdown(url2) {
|
|
62633
|
+
const response = await fetch(url2, {
|
|
62634
|
+
headers: {
|
|
62635
|
+
Accept: "text/markdown, text/plain, */*",
|
|
62636
|
+
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
62637
|
+
},
|
|
62638
|
+
redirect: "follow"
|
|
62639
|
+
});
|
|
62640
|
+
if (!response.ok) {
|
|
62641
|
+
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
62642
|
+
}
|
|
62643
|
+
return response.text();
|
|
62644
|
+
}
|
|
62645
|
+
function parseTokenUnitTable(html) {
|
|
62646
|
+
const results = [];
|
|
62647
|
+
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
|
|
62648
|
+
for (const match of html.matchAll(tableRowPattern)) {
|
|
62649
|
+
const modelName = cleanCellText(match[1]);
|
|
62650
|
+
const inputMultiplier = Number.parseFloat(match[2]);
|
|
62651
|
+
const cachedInput = match[3].trim().toLowerCase();
|
|
62652
|
+
const outputMultiplier = Number.parseFloat(match[4]);
|
|
62653
|
+
if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
|
|
62654
|
+
results.push({
|
|
62655
|
+
modelName,
|
|
62656
|
+
inputMultiplier,
|
|
62657
|
+
cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
|
|
62658
|
+
outputMultiplier
|
|
62659
|
+
});
|
|
62660
|
+
}
|
|
62661
|
+
}
|
|
62662
|
+
return results;
|
|
62663
|
+
}
|
|
62664
|
+
function parsePremiumMultiplierTable(html) {
|
|
62665
|
+
const results = [];
|
|
62666
|
+
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
|
|
62667
|
+
for (const match of html.matchAll(tableRowPattern)) {
|
|
62668
|
+
const modelName = cleanCellText(match[1]);
|
|
62669
|
+
const multiplier = Number.parseFloat(match[2]);
|
|
62670
|
+
if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
|
|
62671
|
+
results.push({ modelName, multiplier });
|
|
62672
|
+
}
|
|
62673
|
+
}
|
|
62674
|
+
return results;
|
|
62675
|
+
}
|
|
62676
|
+
function cleanCellText(text) {
|
|
62677
|
+
return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
|
|
62678
|
+
}
|
|
62679
|
+
function parseCopilotPricingMarkdown(markdown) {
|
|
62680
|
+
const results = [];
|
|
62681
|
+
const seenModels = /* @__PURE__ */ new Set();
|
|
62682
|
+
const lines = markdown.split("\n");
|
|
62683
|
+
let columnIndices = null;
|
|
62684
|
+
for (const line of lines) {
|
|
62685
|
+
const trimmed = line.trim();
|
|
62686
|
+
if (!trimmed.startsWith("|")) continue;
|
|
62687
|
+
if (/\|\s*Model\s/i.test(trimmed)) {
|
|
62688
|
+
columnIndices = parseHeaderColumns(trimmed);
|
|
62689
|
+
continue;
|
|
62690
|
+
}
|
|
62691
|
+
if (/^[\s|:-]+$/.test(trimmed) || !columnIndices) continue;
|
|
62692
|
+
const entry = parseDataRow(trimmed, columnIndices);
|
|
62693
|
+
if (entry && !seenModels.has(entry.modelName.toLowerCase())) {
|
|
62694
|
+
seenModels.add(entry.modelName.toLowerCase());
|
|
62695
|
+
results.push(entry);
|
|
62696
|
+
}
|
|
62697
|
+
}
|
|
62698
|
+
return results;
|
|
62699
|
+
}
|
|
62700
|
+
function parseDataRow(line, columnIndices) {
|
|
62701
|
+
const cells = splitMarkdownRow(line);
|
|
62702
|
+
if (cells.length <= columnIndices.output) return null;
|
|
62703
|
+
const modelName = cells[columnIndices.model].trim();
|
|
62704
|
+
const inputPrice = parseDollarValue(cells[columnIndices.input]);
|
|
62705
|
+
const cachedPrice = columnIndices.cached >= 0 ? parseDollarValue(cells[columnIndices.cached]) : null;
|
|
62706
|
+
const outputPrice = parseDollarValue(cells[columnIndices.output]);
|
|
62707
|
+
if (!modelName || inputPrice === null || outputPrice === null) return null;
|
|
62708
|
+
return {
|
|
62709
|
+
modelName,
|
|
62710
|
+
inputMultiplier: inputPrice * PRICE_TO_MULTIPLIER,
|
|
62711
|
+
cachedInputMultiplier: cachedPrice !== null ? cachedPrice * PRICE_TO_MULTIPLIER : null,
|
|
62712
|
+
outputMultiplier: outputPrice * PRICE_TO_MULTIPLIER
|
|
62713
|
+
};
|
|
62714
|
+
}
|
|
62715
|
+
function parseHeaderColumns(headerLine) {
|
|
62716
|
+
const cells = splitMarkdownRow(headerLine).map((c) => c.trim().toLowerCase());
|
|
62717
|
+
const model = cells.findIndex((c) => c === "model" || c === "model name");
|
|
62718
|
+
const input = cells.findIndex(
|
|
62719
|
+
(c) => (c === "input" || c === "input multiplier") && !c.includes("cached")
|
|
62720
|
+
);
|
|
62721
|
+
const cached2 = cells.findIndex(
|
|
62722
|
+
(c) => c.includes("cached input") || c === "cached input multiplier"
|
|
62723
|
+
);
|
|
62724
|
+
const output = cells.findIndex(
|
|
62725
|
+
(c) => (c === "output" || c === "output multiplier") && !c.includes("cached")
|
|
62726
|
+
);
|
|
62727
|
+
if (model < 0 || input < 0 || output < 0) {
|
|
62728
|
+
return null;
|
|
62729
|
+
}
|
|
62730
|
+
return { model, input, cached: cached2 >= 0 ? cached2 : -1, output };
|
|
62731
|
+
}
|
|
62732
|
+
function splitMarkdownRow(line) {
|
|
62733
|
+
const parts = line.split("|");
|
|
62734
|
+
if (parts[0].trim() === "") parts.shift();
|
|
62735
|
+
if (parts[parts.length - 1]?.trim() === "") parts.pop();
|
|
62736
|
+
return parts;
|
|
62737
|
+
}
|
|
62738
|
+
function parseDollarValue(cell) {
|
|
62739
|
+
if (!cell) return null;
|
|
62740
|
+
const cleaned = cell.trim().replace(/[$,]/g, "");
|
|
62741
|
+
if (cleaned.toLowerCase() === "n/a" || cleaned === "" || cleaned === "-") {
|
|
62742
|
+
return null;
|
|
62743
|
+
}
|
|
62744
|
+
const value = Number.parseFloat(cleaned);
|
|
62745
|
+
return Number.isNaN(value) ? null : value;
|
|
62746
|
+
}
|
|
62747
|
+
var TOKEN_UNIT_COSTS_URL, PREMIUM_MULTIPLIERS_URL, COPILOT_PRICING_URL, PRICE_TO_MULTIPLIER;
|
|
62748
|
+
var init_pricing_scraper = __esm({
|
|
62749
|
+
"packages/daemon/src/models/pricing-scraper.ts"() {
|
|
62750
|
+
"use strict";
|
|
62751
|
+
TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
|
|
62752
|
+
PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
|
|
62753
|
+
COPILOT_PRICING_URL = "https://docs.github.com/api/article/body?pathname=/en/copilot/reference/copilot-billing/models-and-pricing";
|
|
62754
|
+
PRICE_TO_MULTIPLIER = 0.1;
|
|
62755
|
+
}
|
|
62756
|
+
});
|
|
62757
|
+
|
|
62758
|
+
// packages/daemon/src/models/types.ts
|
|
62759
|
+
function computeTierFromMultiplier(premiumMultiplier) {
|
|
62760
|
+
if (premiumMultiplier === null) {
|
|
62761
|
+
return "standard";
|
|
62762
|
+
}
|
|
62763
|
+
for (const [tier, range] of Object.entries(TIER_RANGES)) {
|
|
62764
|
+
if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
|
|
62765
|
+
return tier;
|
|
62766
|
+
}
|
|
62767
|
+
}
|
|
62768
|
+
return "ultra";
|
|
62769
|
+
}
|
|
62770
|
+
var TIER_RANGES, TOKEN_UNIT_PRICE;
|
|
62771
|
+
var init_types = __esm({
|
|
62772
|
+
"packages/daemon/src/models/types.ts"() {
|
|
62773
|
+
"use strict";
|
|
62774
|
+
TIER_RANGES = {
|
|
62775
|
+
trivial: { min: 0, max: 0.33 },
|
|
62776
|
+
fast: { min: 0.34, max: 1 },
|
|
62777
|
+
standard: { min: 1.1, max: 5 },
|
|
62778
|
+
premium: { min: 5.1, max: 15 },
|
|
62779
|
+
ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
|
|
62780
|
+
};
|
|
62781
|
+
TOKEN_UNIT_PRICE = 1e-5;
|
|
62782
|
+
}
|
|
62783
|
+
});
|
|
62784
|
+
|
|
62785
|
+
// packages/daemon/src/models/registry.ts
|
|
62786
|
+
function normalizeModelName(name) {
|
|
62787
|
+
return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
|
|
62788
|
+
}
|
|
62789
|
+
async function fetchCatalogIntoMap(modelMap, result, logger2) {
|
|
62790
|
+
try {
|
|
62791
|
+
const catalogModels = await fetchModelCatalog();
|
|
62792
|
+
result.catalogFetched = true;
|
|
62793
|
+
for (const m of catalogModels) {
|
|
62794
|
+
const key = normalizeModelName(m.id);
|
|
62795
|
+
modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
|
|
62796
|
+
}
|
|
62797
|
+
} catch (error51) {
|
|
62798
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62799
|
+
result.errors.push(`Catalog fetch failed: ${msg}`);
|
|
62800
|
+
logger2?.warn(`Model catalog fetch failed: ${msg}`);
|
|
62801
|
+
}
|
|
62802
|
+
}
|
|
62803
|
+
async function scrapeTokenPricingIntoMap(modelMap, result, logger2) {
|
|
62804
|
+
try {
|
|
62805
|
+
const tokenPricing = await scrapeTokenUnitPricing();
|
|
62806
|
+
result.tokenPricingScraped = true;
|
|
62807
|
+
for (const tp of tokenPricing) {
|
|
62808
|
+
const key = normalizeModelName(tp.modelName);
|
|
62809
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
62810
|
+
if (existing) {
|
|
62811
|
+
existing.tokenInputMultiplier = tp.inputMultiplier;
|
|
62812
|
+
existing.tokenOutputMultiplier = tp.outputMultiplier;
|
|
62813
|
+
existing.cachedInputMultiplier = tp.cachedInputMultiplier;
|
|
62814
|
+
} else {
|
|
62815
|
+
modelMap.set(key, {
|
|
62816
|
+
id: key,
|
|
62817
|
+
displayName: tp.modelName,
|
|
62818
|
+
tokenInputMultiplier: tp.inputMultiplier,
|
|
62819
|
+
tokenOutputMultiplier: tp.outputMultiplier,
|
|
62820
|
+
cachedInputMultiplier: tp.cachedInputMultiplier,
|
|
62821
|
+
available: true
|
|
62822
|
+
});
|
|
62823
|
+
}
|
|
62824
|
+
}
|
|
62825
|
+
} catch (error51) {
|
|
62826
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62827
|
+
result.errors.push(`Token pricing scrape failed: ${msg}`);
|
|
62828
|
+
logger2?.warn(`Token pricing scrape failed: ${msg}`);
|
|
62829
|
+
}
|
|
62830
|
+
}
|
|
62831
|
+
async function scrapePremiumPricingIntoMap(modelMap, result, logger2) {
|
|
62832
|
+
try {
|
|
62833
|
+
const premiumPricing = await scrapePremiumRequestPricing();
|
|
62834
|
+
result.premiumPricingScraped = true;
|
|
62835
|
+
for (const pp of premiumPricing) {
|
|
62836
|
+
const key = normalizeModelName(pp.modelName);
|
|
62837
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
62838
|
+
if (existing) {
|
|
62839
|
+
existing.premiumMultiplier = pp.multiplier;
|
|
62840
|
+
} else {
|
|
62841
|
+
modelMap.set(key, {
|
|
62842
|
+
id: key,
|
|
62843
|
+
displayName: pp.modelName,
|
|
62844
|
+
premiumMultiplier: pp.multiplier,
|
|
62845
|
+
available: true
|
|
62846
|
+
});
|
|
62847
|
+
}
|
|
62848
|
+
}
|
|
62849
|
+
} catch (error51) {
|
|
62850
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62851
|
+
result.errors.push(`Premium pricing scrape failed: ${msg}`);
|
|
62852
|
+
logger2?.warn(`Premium pricing scrape failed: ${msg}`);
|
|
62853
|
+
}
|
|
62854
|
+
}
|
|
62855
|
+
async function scrapeCopilotPricingIntoMap(modelMap, result, logger2) {
|
|
62856
|
+
try {
|
|
62857
|
+
const copilotPricing = await scrapeCopilotPricing();
|
|
62858
|
+
result.copilotPricingScraped = true;
|
|
62859
|
+
for (const cp of copilotPricing) {
|
|
62860
|
+
const key = normalizeModelName(cp.modelName);
|
|
62861
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
62862
|
+
if (existing) {
|
|
62863
|
+
existing.tokenInputMultiplier = cp.inputMultiplier;
|
|
62864
|
+
existing.tokenOutputMultiplier = cp.outputMultiplier;
|
|
62865
|
+
if (cp.cachedInputMultiplier !== null) {
|
|
62866
|
+
existing.cachedInputMultiplier = cp.cachedInputMultiplier;
|
|
62867
|
+
}
|
|
62868
|
+
} else {
|
|
62869
|
+
modelMap.set(key, {
|
|
62870
|
+
id: key,
|
|
62871
|
+
displayName: cp.modelName,
|
|
62872
|
+
tokenInputMultiplier: cp.inputMultiplier,
|
|
62873
|
+
tokenOutputMultiplier: cp.outputMultiplier,
|
|
62874
|
+
cachedInputMultiplier: cp.cachedInputMultiplier,
|
|
62875
|
+
available: true
|
|
62876
|
+
});
|
|
62877
|
+
}
|
|
62878
|
+
}
|
|
62879
|
+
} catch (error51) {
|
|
62880
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62881
|
+
result.errors.push(`Copilot pricing scrape failed: ${msg}`);
|
|
62882
|
+
logger2?.warn(`Copilot pricing scrape failed: ${msg}`);
|
|
62883
|
+
}
|
|
62884
|
+
}
|
|
62885
|
+
async function refreshModelPricing(logger2) {
|
|
62886
|
+
const result = {
|
|
62887
|
+
modelsUpdated: 0,
|
|
62888
|
+
catalogFetched: false,
|
|
62889
|
+
tokenPricingScraped: false,
|
|
62890
|
+
premiumPricingScraped: false,
|
|
62891
|
+
copilotPricingScraped: false,
|
|
62892
|
+
errors: []
|
|
62893
|
+
};
|
|
62894
|
+
const modelMap = /* @__PURE__ */ new Map();
|
|
62895
|
+
await fetchCatalogIntoMap(modelMap, result, logger2);
|
|
62896
|
+
await scrapeTokenPricingIntoMap(modelMap, result, logger2);
|
|
62897
|
+
await scrapeCopilotPricingIntoMap(modelMap, result, logger2);
|
|
62898
|
+
await scrapePremiumPricingIntoMap(modelMap, result, logger2);
|
|
62899
|
+
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped && !result.copilotPricingScraped) {
|
|
62900
|
+
logger2?.warn("All pricing sources failed, no models available");
|
|
62901
|
+
return result;
|
|
62902
|
+
}
|
|
62903
|
+
const db = await getDatabase();
|
|
62904
|
+
const now = nowIso();
|
|
62905
|
+
for (const model of modelMap.values()) {
|
|
62906
|
+
if (model.tokenInputMultiplier == null) {
|
|
62907
|
+
continue;
|
|
62908
|
+
}
|
|
62909
|
+
const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
|
|
62910
|
+
await upsertModel(db, {
|
|
62911
|
+
id: model.id,
|
|
62912
|
+
displayName: model.displayName,
|
|
62913
|
+
premiumMultiplier: model.premiumMultiplier ?? null,
|
|
62914
|
+
tokenInputMultiplier: model.tokenInputMultiplier,
|
|
62915
|
+
tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
|
|
62916
|
+
cachedInputMultiplier: model.cachedInputMultiplier ?? null,
|
|
62917
|
+
tier,
|
|
62918
|
+
available: model.available ?? true,
|
|
62919
|
+
updatedAt: now
|
|
62920
|
+
});
|
|
62921
|
+
result.modelsUpdated++;
|
|
62922
|
+
}
|
|
62923
|
+
return result;
|
|
62924
|
+
}
|
|
62925
|
+
async function getModelsForTier(tier) {
|
|
62926
|
+
const db = await getDatabase();
|
|
62927
|
+
const result = await db.execute({
|
|
62928
|
+
sql: "SELECT * FROM model_pricing WHERE tier = ? AND available = 1 ORDER BY premium_multiplier ASC NULLS LAST",
|
|
62929
|
+
args: [tier]
|
|
62930
|
+
});
|
|
62931
|
+
return result.rows.map(rowToModelPricing);
|
|
62932
|
+
}
|
|
62933
|
+
async function getCheapestInTier(tier) {
|
|
62934
|
+
const models = await getModelsForTier(tier);
|
|
62935
|
+
return models[0] ?? null;
|
|
62936
|
+
}
|
|
62937
|
+
async function getModelPricing(modelId) {
|
|
62938
|
+
const db = await getDatabase();
|
|
62939
|
+
const result = await db.execute({
|
|
62940
|
+
sql: "SELECT * FROM model_pricing WHERE id = ?",
|
|
62941
|
+
args: [modelId]
|
|
62942
|
+
});
|
|
62943
|
+
if (result.rows.length > 0) {
|
|
62944
|
+
return rowToModelPricing(result.rows[0]);
|
|
62945
|
+
}
|
|
62946
|
+
const allModels = await db.execute(
|
|
62947
|
+
"SELECT * FROM model_pricing WHERE token_input_multiplier IS NOT NULL ORDER BY length(id) DESC"
|
|
62948
|
+
);
|
|
62949
|
+
for (const row of allModels.rows) {
|
|
62950
|
+
const storedId = asString(row.id);
|
|
62951
|
+
if (modelId.startsWith(storedId) || storedId.startsWith(modelId)) {
|
|
62952
|
+
return rowToModelPricing(row);
|
|
62953
|
+
}
|
|
62954
|
+
}
|
|
62955
|
+
return null;
|
|
62956
|
+
}
|
|
62957
|
+
async function getCheapestAvailableModel() {
|
|
62958
|
+
const tiers = ["trivial", "fast", "standard", "premium", "ultra"];
|
|
62959
|
+
for (const tier of tiers) {
|
|
62960
|
+
const model = await getCheapestInTier(tier);
|
|
62961
|
+
if (model) {
|
|
62962
|
+
return model;
|
|
62963
|
+
}
|
|
62964
|
+
}
|
|
62965
|
+
return null;
|
|
62966
|
+
}
|
|
62967
|
+
function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
|
|
62968
|
+
if (inputMultiplier === null || outputMultiplier === null) {
|
|
62969
|
+
return 0;
|
|
62970
|
+
}
|
|
62971
|
+
const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
|
|
62972
|
+
return tokenUnits * TOKEN_UNIT_PRICE;
|
|
62973
|
+
}
|
|
62974
|
+
function getNextTierUp(tier) {
|
|
62975
|
+
const order = ["trivial", "fast", "standard", "premium", "ultra"];
|
|
62976
|
+
const idx = order.indexOf(tier);
|
|
62977
|
+
if (idx < 0 || idx >= order.length - 1) {
|
|
62978
|
+
return null;
|
|
62979
|
+
}
|
|
62980
|
+
return order[idx + 1];
|
|
62981
|
+
}
|
|
62982
|
+
async function upsertModel(db, model) {
|
|
62983
|
+
await db.execute({
|
|
62984
|
+
sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
|
|
62985
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
62986
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
62987
|
+
display_name = excluded.display_name,
|
|
62988
|
+
premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
|
|
62989
|
+
token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
|
|
62990
|
+
token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
|
|
62991
|
+
cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
|
|
62992
|
+
tier = excluded.tier,
|
|
62993
|
+
available = excluded.available,
|
|
62994
|
+
updated_at = excluded.updated_at`,
|
|
62995
|
+
args: [
|
|
62996
|
+
model.id,
|
|
62997
|
+
model.displayName,
|
|
62998
|
+
model.premiumMultiplier,
|
|
62999
|
+
model.tokenInputMultiplier,
|
|
63000
|
+
model.tokenOutputMultiplier,
|
|
63001
|
+
model.cachedInputMultiplier,
|
|
63002
|
+
model.tier,
|
|
63003
|
+
model.available ? 1 : 0,
|
|
63004
|
+
model.updatedAt
|
|
63005
|
+
]
|
|
63006
|
+
});
|
|
63007
|
+
}
|
|
63008
|
+
function rowToModelPricing(row) {
|
|
63009
|
+
return {
|
|
63010
|
+
id: asString(row.id),
|
|
63011
|
+
displayName: asString(row.display_name),
|
|
63012
|
+
premiumMultiplier: asNullableNumber(row.premium_multiplier),
|
|
63013
|
+
tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
|
|
63014
|
+
tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
|
|
63015
|
+
cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
|
|
63016
|
+
tier: asString(row.tier),
|
|
63017
|
+
available: asNumber(row.available) === 1,
|
|
63018
|
+
updatedAt: asString(row.updated_at)
|
|
63019
|
+
};
|
|
63020
|
+
}
|
|
63021
|
+
function findClosestKey(map2, targetKey) {
|
|
63022
|
+
for (const [key, value] of map2) {
|
|
63023
|
+
if (key.includes(targetKey) || targetKey.includes(key)) {
|
|
63024
|
+
return value;
|
|
63025
|
+
}
|
|
63026
|
+
}
|
|
63027
|
+
return void 0;
|
|
63028
|
+
}
|
|
63029
|
+
var init_registry = __esm({
|
|
63030
|
+
"packages/daemon/src/models/registry.ts"() {
|
|
63031
|
+
"use strict";
|
|
63032
|
+
init_db();
|
|
63033
|
+
init_catalog();
|
|
63034
|
+
init_pricing_scraper();
|
|
63035
|
+
init_types();
|
|
63036
|
+
}
|
|
63037
|
+
});
|
|
63038
|
+
|
|
62209
63039
|
// node_modules/grammy/out/filter.js
|
|
62210
63040
|
var require_filter = __commonJS({
|
|
62211
63041
|
"node_modules/grammy/out/filter.js"(exports2) {
|
|
@@ -76932,339 +77762,11 @@ var TypedEventBus = class extends EventEmitter {
|
|
|
76932
77762
|
};
|
|
76933
77763
|
var eventBus = new TypedEventBus();
|
|
76934
77764
|
|
|
76935
|
-
// packages/daemon/src/store/
|
|
76936
|
-
|
|
76937
|
-
import { mkdir } from "node:fs/promises";
|
|
76938
|
-
import { dirname } from "node:path";
|
|
76939
|
-
import { pathToFileURL } from "node:url";
|
|
76940
|
-
import { createClient } from "@libsql/client";
|
|
76941
|
-
var MIGRATIONS = [
|
|
76942
|
-
{
|
|
76943
|
-
version: 1,
|
|
76944
|
-
name: "create-store-schema",
|
|
76945
|
-
statements: [
|
|
76946
|
-
`CREATE TABLE IF NOT EXISTS squads (
|
|
76947
|
-
id TEXT PRIMARY KEY,
|
|
76948
|
-
name TEXT NOT NULL,
|
|
76949
|
-
repo_url TEXT NOT NULL,
|
|
76950
|
-
repo_owner TEXT NOT NULL,
|
|
76951
|
-
repo_name TEXT NOT NULL,
|
|
76952
|
-
status TEXT NOT NULL,
|
|
76953
|
-
config TEXT NOT NULL,
|
|
76954
|
-
created_at TEXT NOT NULL,
|
|
76955
|
-
updated_at TEXT NOT NULL,
|
|
76956
|
-
UNIQUE (repo_owner, repo_name)
|
|
76957
|
-
)`,
|
|
76958
|
-
`CREATE TABLE IF NOT EXISTS squad_members (
|
|
76959
|
-
id TEXT PRIMARY KEY,
|
|
76960
|
-
squad_id TEXT NOT NULL REFERENCES squads(id) ON DELETE CASCADE,
|
|
76961
|
-
role TEXT NOT NULL,
|
|
76962
|
-
name TEXT NOT NULL,
|
|
76963
|
-
system_prompt TEXT NOT NULL,
|
|
76964
|
-
model TEXT,
|
|
76965
|
-
created_at TEXT NOT NULL
|
|
76966
|
-
)`,
|
|
76967
|
-
`CREATE TABLE IF NOT EXISTS objectives (
|
|
76968
|
-
id TEXT PRIMARY KEY,
|
|
76969
|
-
squad_id TEXT NOT NULL REFERENCES squads(id) ON DELETE CASCADE,
|
|
76970
|
-
description TEXT NOT NULL,
|
|
76971
|
-
status TEXT NOT NULL,
|
|
76972
|
-
plan TEXT,
|
|
76973
|
-
revision_count INTEGER NOT NULL DEFAULT 0,
|
|
76974
|
-
branch TEXT,
|
|
76975
|
-
pr_url TEXT,
|
|
76976
|
-
created_at TEXT NOT NULL,
|
|
76977
|
-
updated_at TEXT NOT NULL
|
|
76978
|
-
)`,
|
|
76979
|
-
`CREATE TABLE IF NOT EXISTS tasks (
|
|
76980
|
-
id TEXT PRIMARY KEY,
|
|
76981
|
-
objective_id TEXT NOT NULL REFERENCES objectives(id) ON DELETE CASCADE,
|
|
76982
|
-
assignee_id TEXT REFERENCES squad_members(id) ON DELETE SET NULL,
|
|
76983
|
-
title TEXT NOT NULL,
|
|
76984
|
-
description TEXT NOT NULL,
|
|
76985
|
-
status TEXT NOT NULL,
|
|
76986
|
-
result TEXT,
|
|
76987
|
-
created_at TEXT NOT NULL,
|
|
76988
|
-
updated_at TEXT NOT NULL
|
|
76989
|
-
)`,
|
|
76990
|
-
`CREATE TABLE IF NOT EXISTS conversations (
|
|
76991
|
-
id TEXT PRIMARY KEY,
|
|
76992
|
-
title TEXT,
|
|
76993
|
-
source TEXT NOT NULL,
|
|
76994
|
-
created_at TEXT NOT NULL,
|
|
76995
|
-
updated_at TEXT NOT NULL
|
|
76996
|
-
)`,
|
|
76997
|
-
`CREATE TABLE IF NOT EXISTS messages (
|
|
76998
|
-
id TEXT PRIMARY KEY,
|
|
76999
|
-
conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
|
|
77000
|
-
role TEXT NOT NULL,
|
|
77001
|
-
content TEXT NOT NULL,
|
|
77002
|
-
model TEXT,
|
|
77003
|
-
input_tokens INTEGER,
|
|
77004
|
-
output_tokens INTEGER,
|
|
77005
|
-
created_at TEXT NOT NULL
|
|
77006
|
-
)`,
|
|
77007
|
-
`CREATE TABLE IF NOT EXISTS inbox (
|
|
77008
|
-
id TEXT PRIMARY KEY,
|
|
77009
|
-
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
77010
|
-
objective_id TEXT REFERENCES objectives(id) ON DELETE SET NULL,
|
|
77011
|
-
type TEXT NOT NULL,
|
|
77012
|
-
title TEXT NOT NULL,
|
|
77013
|
-
content TEXT NOT NULL,
|
|
77014
|
-
status TEXT NOT NULL,
|
|
77015
|
-
reply TEXT,
|
|
77016
|
-
created_at TEXT NOT NULL,
|
|
77017
|
-
updated_at TEXT NOT NULL
|
|
77018
|
-
)`,
|
|
77019
|
-
`CREATE TABLE IF NOT EXISTS schedules (
|
|
77020
|
-
id TEXT PRIMARY KEY,
|
|
77021
|
-
name TEXT NOT NULL,
|
|
77022
|
-
cron_expression TEXT NOT NULL,
|
|
77023
|
-
prompt TEXT NOT NULL,
|
|
77024
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
77025
|
-
last_run_at TEXT,
|
|
77026
|
-
next_run_at TEXT,
|
|
77027
|
-
created_at TEXT NOT NULL,
|
|
77028
|
-
updated_at TEXT NOT NULL
|
|
77029
|
-
)`,
|
|
77030
|
-
`CREATE TABLE IF NOT EXISTS token_usage (
|
|
77031
|
-
id TEXT PRIMARY KEY,
|
|
77032
|
-
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
77033
|
-
agent_id TEXT,
|
|
77034
|
-
model TEXT NOT NULL,
|
|
77035
|
-
input_tokens INTEGER NOT NULL,
|
|
77036
|
-
output_tokens INTEGER NOT NULL,
|
|
77037
|
-
cost REAL NOT NULL,
|
|
77038
|
-
created_at TEXT NOT NULL
|
|
77039
|
-
)`,
|
|
77040
|
-
`CREATE TABLE IF NOT EXISTS activity (
|
|
77041
|
-
id TEXT PRIMARY KEY,
|
|
77042
|
-
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
77043
|
-
objective_id TEXT REFERENCES objectives(id) ON DELETE SET NULL,
|
|
77044
|
-
event TEXT NOT NULL,
|
|
77045
|
-
description TEXT NOT NULL,
|
|
77046
|
-
metadata TEXT,
|
|
77047
|
-
created_at TEXT NOT NULL
|
|
77048
|
-
)`,
|
|
77049
|
-
`CREATE TABLE IF NOT EXISTS agent_history (
|
|
77050
|
-
id TEXT PRIMARY KEY,
|
|
77051
|
-
agent_id TEXT NOT NULL,
|
|
77052
|
-
squad_id TEXT REFERENCES squads(id) ON DELETE SET NULL,
|
|
77053
|
-
content TEXT NOT NULL,
|
|
77054
|
-
created_at TEXT NOT NULL
|
|
77055
|
-
)`,
|
|
77056
|
-
`CREATE TABLE IF NOT EXISTS settings (
|
|
77057
|
-
key TEXT PRIMARY KEY,
|
|
77058
|
-
value TEXT
|
|
77059
|
-
)`,
|
|
77060
|
-
"CREATE INDEX IF NOT EXISTS idx_squad_members_squad_id ON squad_members(squad_id)",
|
|
77061
|
-
"CREATE INDEX IF NOT EXISTS idx_objectives_squad_id ON objectives(squad_id)",
|
|
77062
|
-
"CREATE INDEX IF NOT EXISTS idx_objectives_status ON objectives(status)",
|
|
77063
|
-
"CREATE INDEX IF NOT EXISTS idx_tasks_objective_id ON tasks(objective_id)",
|
|
77064
|
-
"CREATE INDEX IF NOT EXISTS idx_tasks_assignee_id ON tasks(assignee_id)",
|
|
77065
|
-
"CREATE INDEX IF NOT EXISTS idx_messages_conversation_id_created_at ON messages(conversation_id, created_at)",
|
|
77066
|
-
"CREATE INDEX IF NOT EXISTS idx_inbox_status_created_at ON inbox(status, created_at)",
|
|
77067
|
-
"CREATE INDEX IF NOT EXISTS idx_inbox_squad_id ON inbox(squad_id)",
|
|
77068
|
-
"CREATE INDEX IF NOT EXISTS idx_inbox_objective_id ON inbox(objective_id)",
|
|
77069
|
-
"CREATE INDEX IF NOT EXISTS idx_schedules_enabled_next_run_at ON schedules(enabled, next_run_at)",
|
|
77070
|
-
"CREATE INDEX IF NOT EXISTS idx_token_usage_created_at ON token_usage(created_at)",
|
|
77071
|
-
"CREATE INDEX IF NOT EXISTS idx_token_usage_squad_id ON token_usage(squad_id)",
|
|
77072
|
-
"CREATE INDEX IF NOT EXISTS idx_token_usage_agent_id ON token_usage(agent_id)",
|
|
77073
|
-
"CREATE INDEX IF NOT EXISTS idx_token_usage_model ON token_usage(model)",
|
|
77074
|
-
"CREATE INDEX IF NOT EXISTS idx_activity_created_at ON activity(created_at)",
|
|
77075
|
-
"CREATE INDEX IF NOT EXISTS idx_activity_squad_id ON activity(squad_id)",
|
|
77076
|
-
"CREATE INDEX IF NOT EXISTS idx_activity_objective_id ON activity(objective_id)",
|
|
77077
|
-
"CREATE INDEX IF NOT EXISTS idx_agent_history_agent_id_created_at ON agent_history(agent_id, created_at)"
|
|
77078
|
-
]
|
|
77079
|
-
},
|
|
77080
|
-
{
|
|
77081
|
-
version: 2,
|
|
77082
|
-
name: "add-model-pricing-and-dual-costs",
|
|
77083
|
-
statements: [
|
|
77084
|
-
`CREATE TABLE IF NOT EXISTS model_pricing (
|
|
77085
|
-
id TEXT PRIMARY KEY,
|
|
77086
|
-
display_name TEXT NOT NULL,
|
|
77087
|
-
premium_multiplier REAL,
|
|
77088
|
-
token_input_multiplier REAL,
|
|
77089
|
-
token_output_multiplier REAL,
|
|
77090
|
-
cached_input_multiplier REAL,
|
|
77091
|
-
tier TEXT NOT NULL,
|
|
77092
|
-
available INTEGER NOT NULL DEFAULT 1,
|
|
77093
|
-
updated_at TEXT NOT NULL
|
|
77094
|
-
)`,
|
|
77095
|
-
"CREATE INDEX IF NOT EXISTS idx_model_pricing_tier ON model_pricing(tier)",
|
|
77096
|
-
"CREATE INDEX IF NOT EXISTS idx_model_pricing_available ON model_pricing(available)",
|
|
77097
|
-
"ALTER TABLE token_usage ADD COLUMN premium_request_cost REAL",
|
|
77098
|
-
"ALTER TABLE token_usage ADD COLUMN token_unit_cost REAL"
|
|
77099
|
-
]
|
|
77100
|
-
},
|
|
77101
|
-
{
|
|
77102
|
-
version: 3,
|
|
77103
|
-
name: "add-squad-instances",
|
|
77104
|
-
statements: [
|
|
77105
|
-
`CREATE TABLE IF NOT EXISTS squad_instances (
|
|
77106
|
-
id TEXT PRIMARY KEY,
|
|
77107
|
-
squad_id TEXT NOT NULL REFERENCES squads(id) ON DELETE CASCADE,
|
|
77108
|
-
objective_id TEXT REFERENCES objectives(id) ON DELETE SET NULL,
|
|
77109
|
-
status TEXT NOT NULL DEFAULT 'queued',
|
|
77110
|
-
branch TEXT,
|
|
77111
|
-
worktree_path TEXT,
|
|
77112
|
-
created_at TEXT NOT NULL,
|
|
77113
|
-
started_at TEXT,
|
|
77114
|
-
completed_at TEXT,
|
|
77115
|
-
error TEXT
|
|
77116
|
-
)`,
|
|
77117
|
-
"CREATE INDEX IF NOT EXISTS idx_squad_instances_squad_status ON squad_instances(squad_id, status)",
|
|
77118
|
-
"CREATE INDEX IF NOT EXISTS idx_squad_instances_status ON squad_instances(status)",
|
|
77119
|
-
"CREATE INDEX IF NOT EXISTS idx_squad_instances_objective_id ON squad_instances(objective_id)"
|
|
77120
|
-
]
|
|
77121
|
-
},
|
|
77122
|
-
{
|
|
77123
|
-
version: 4,
|
|
77124
|
-
name: "denormalize-usage-names",
|
|
77125
|
-
statements: [
|
|
77126
|
-
"ALTER TABLE token_usage ADD COLUMN squad_name TEXT",
|
|
77127
|
-
"ALTER TABLE token_usage ADD COLUMN agent_name TEXT",
|
|
77128
|
-
`UPDATE token_usage SET
|
|
77129
|
-
squad_name = (SELECT s.name FROM squads s WHERE s.id = token_usage.squad_id),
|
|
77130
|
-
agent_name = (SELECT sm.name FROM squad_members sm WHERE sm.id = token_usage.agent_id)
|
|
77131
|
-
WHERE squad_id IS NOT NULL OR agent_id IS NOT NULL`
|
|
77132
|
-
]
|
|
77133
|
-
}
|
|
77134
|
-
];
|
|
77135
|
-
var client = null;
|
|
77136
|
-
var initPromise = null;
|
|
77137
|
-
var defaultConnectionOptions = {};
|
|
77138
|
-
var generateId = () => crypto.randomUUID();
|
|
77139
|
-
var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
77140
|
-
function asString(value) {
|
|
77141
|
-
if (typeof value === "string") {
|
|
77142
|
-
return value;
|
|
77143
|
-
}
|
|
77144
|
-
if (value === null || value === void 0) {
|
|
77145
|
-
throw new Error("Expected string value but received null or undefined");
|
|
77146
|
-
}
|
|
77147
|
-
return String(value);
|
|
77148
|
-
}
|
|
77149
|
-
function asNullableString(value) {
|
|
77150
|
-
if (value === null || value === void 0) {
|
|
77151
|
-
return null;
|
|
77152
|
-
}
|
|
77153
|
-
return typeof value === "string" ? value : String(value);
|
|
77154
|
-
}
|
|
77155
|
-
function asNumber(value) {
|
|
77156
|
-
if (typeof value === "number") {
|
|
77157
|
-
return value;
|
|
77158
|
-
}
|
|
77159
|
-
if (typeof value === "bigint") {
|
|
77160
|
-
return Number(value);
|
|
77161
|
-
}
|
|
77162
|
-
if (typeof value === "string") {
|
|
77163
|
-
return Number(value);
|
|
77164
|
-
}
|
|
77165
|
-
throw new Error(`Expected numeric value but received ${typeof value}`);
|
|
77166
|
-
}
|
|
77167
|
-
function asNullableNumber(value) {
|
|
77168
|
-
if (value === null || value === void 0) {
|
|
77169
|
-
return null;
|
|
77170
|
-
}
|
|
77171
|
-
return asNumber(value);
|
|
77172
|
-
}
|
|
77173
|
-
function asBoolean(value) {
|
|
77174
|
-
return asNumber(value) === 1;
|
|
77175
|
-
}
|
|
77176
|
-
function toSqliteBoolean(value) {
|
|
77177
|
-
return value ? 1 : 0;
|
|
77178
|
-
}
|
|
77179
|
-
function parseJson(value) {
|
|
77180
|
-
if (typeof value !== "string") {
|
|
77181
|
-
return value;
|
|
77182
|
-
}
|
|
77183
|
-
return JSON.parse(value);
|
|
77184
|
-
}
|
|
77185
|
-
function serializeJson(value) {
|
|
77186
|
-
return JSON.stringify(value);
|
|
77187
|
-
}
|
|
77188
|
-
async function initDatabase() {
|
|
77189
|
-
if (client && !client.closed) {
|
|
77190
|
-
return client;
|
|
77191
|
-
}
|
|
77192
|
-
if (initPromise) {
|
|
77193
|
-
return initPromise;
|
|
77194
|
-
}
|
|
77195
|
-
initPromise = initializeDatabase(defaultConnectionOptions);
|
|
77196
|
-
try {
|
|
77197
|
-
client = await initPromise;
|
|
77198
|
-
return client;
|
|
77199
|
-
} finally {
|
|
77200
|
-
initPromise = null;
|
|
77201
|
-
}
|
|
77202
|
-
}
|
|
77203
|
-
async function getDatabase() {
|
|
77204
|
-
return initDatabase();
|
|
77205
|
-
}
|
|
77206
|
-
async function initializeDatabase(options2 = {}) {
|
|
77207
|
-
const url2 = await resolveDatabaseUrl(options2);
|
|
77208
|
-
const databaseClient = createClient({ url: url2 });
|
|
77209
|
-
await databaseClient.execute("PRAGMA foreign_keys = ON");
|
|
77210
|
-
await databaseClient.execute("PRAGMA journal_mode = WAL");
|
|
77211
|
-
await ensureMigrationTable(databaseClient);
|
|
77212
|
-
await applyMigrations(databaseClient);
|
|
77213
|
-
return databaseClient;
|
|
77214
|
-
}
|
|
77215
|
-
async function resolveDatabaseUrl(options2) {
|
|
77216
|
-
if (options2.url) {
|
|
77217
|
-
return options2.url;
|
|
77218
|
-
}
|
|
77219
|
-
const databasePath = options2.path ?? DB_PATH;
|
|
77220
|
-
await mkdir(dirname(databasePath), { recursive: true });
|
|
77221
|
-
return pathToFileURL(databasePath).href;
|
|
77222
|
-
}
|
|
77223
|
-
async function ensureMigrationTable(databaseClient) {
|
|
77224
|
-
await databaseClient.executeMultiple(`
|
|
77225
|
-
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
77226
|
-
version INTEGER PRIMARY KEY,
|
|
77227
|
-
name TEXT NOT NULL,
|
|
77228
|
-
applied_at TEXT NOT NULL
|
|
77229
|
-
);
|
|
77230
|
-
`);
|
|
77231
|
-
}
|
|
77232
|
-
async function applyMigrations(databaseClient) {
|
|
77233
|
-
const appliedVersions = await getAppliedVersions(databaseClient);
|
|
77234
|
-
for (const migration of MIGRATIONS) {
|
|
77235
|
-
if (appliedVersions.has(migration.version)) {
|
|
77236
|
-
continue;
|
|
77237
|
-
}
|
|
77238
|
-
const transaction = await databaseClient.transaction("write");
|
|
77239
|
-
try {
|
|
77240
|
-
for (const statement of migration.statements) {
|
|
77241
|
-
await transaction.execute(statement);
|
|
77242
|
-
}
|
|
77243
|
-
await transaction.execute({
|
|
77244
|
-
sql: "INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)",
|
|
77245
|
-
args: [migration.version, migration.name, nowIso()]
|
|
77246
|
-
});
|
|
77247
|
-
await transaction.commit();
|
|
77248
|
-
} catch (error51) {
|
|
77249
|
-
if (!transaction.closed) {
|
|
77250
|
-
await transaction.rollback();
|
|
77251
|
-
}
|
|
77252
|
-
throw error51;
|
|
77253
|
-
} finally {
|
|
77254
|
-
if (!transaction.closed) {
|
|
77255
|
-
transaction.close();
|
|
77256
|
-
}
|
|
77257
|
-
}
|
|
77258
|
-
}
|
|
77259
|
-
}
|
|
77260
|
-
async function getAppliedVersions(databaseClient) {
|
|
77261
|
-
const result = await databaseClient.execute(
|
|
77262
|
-
"SELECT version FROM schema_migrations ORDER BY version ASC"
|
|
77263
|
-
);
|
|
77264
|
-
return new Set(result.rows.map((row) => asNumber(row.version)));
|
|
77265
|
-
}
|
|
77765
|
+
// packages/daemon/src/store/index.ts
|
|
77766
|
+
init_db();
|
|
77266
77767
|
|
|
77267
77768
|
// packages/daemon/src/store/squads.ts
|
|
77769
|
+
init_db();
|
|
77268
77770
|
async function createSquad(data, db) {
|
|
77269
77771
|
const database = db ?? await getDatabase();
|
|
77270
77772
|
const timestamp = nowIso();
|
|
@@ -77277,7 +77779,8 @@ async function createSquad(data, db) {
|
|
|
77277
77779
|
status: data.status ?? "active",
|
|
77278
77780
|
config: data.config,
|
|
77279
77781
|
createdAt: timestamp,
|
|
77280
|
-
updatedAt: timestamp
|
|
77782
|
+
updatedAt: timestamp,
|
|
77783
|
+
deletedAt: null
|
|
77281
77784
|
};
|
|
77282
77785
|
await database.execute({
|
|
77283
77786
|
sql: `INSERT INTO squads (id, name, repo_url, repo_owner, repo_name, status, config, created_at, updated_at)
|
|
@@ -77299,7 +77802,7 @@ async function createSquad(data, db) {
|
|
|
77299
77802
|
async function getSquad(id, db) {
|
|
77300
77803
|
const database = db ?? await getDatabase();
|
|
77301
77804
|
const squadResult = await database.execute({
|
|
77302
|
-
sql: "SELECT * FROM squads WHERE id = ? LIMIT 1",
|
|
77805
|
+
sql: "SELECT * FROM squads WHERE id = ? AND deleted_at IS NULL LIMIT 1",
|
|
77303
77806
|
args: [id]
|
|
77304
77807
|
});
|
|
77305
77808
|
const row = squadResult.rows[0];
|
|
@@ -77315,7 +77818,7 @@ async function getSquad(id, db) {
|
|
|
77315
77818
|
async function getSquadByName(name, db) {
|
|
77316
77819
|
const database = db ?? await getDatabase();
|
|
77317
77820
|
const result = await database.execute({
|
|
77318
|
-
sql: "SELECT * FROM squads WHERE name = ? LIMIT 1",
|
|
77821
|
+
sql: "SELECT * FROM squads WHERE name = ? AND deleted_at IS NULL LIMIT 1",
|
|
77319
77822
|
args: [name]
|
|
77320
77823
|
});
|
|
77321
77824
|
const row = result.rows[0];
|
|
@@ -77328,7 +77831,9 @@ async function getSquadByName(name, db) {
|
|
|
77328
77831
|
}
|
|
77329
77832
|
async function listSquads(db) {
|
|
77330
77833
|
const database = db ?? await getDatabase();
|
|
77331
|
-
const result = await database.execute(
|
|
77834
|
+
const result = await database.execute(
|
|
77835
|
+
"SELECT * FROM squads WHERE deleted_at IS NULL ORDER BY created_at DESC, id DESC"
|
|
77836
|
+
);
|
|
77332
77837
|
return result.rows.map((row) => mapSquad(row));
|
|
77333
77838
|
}
|
|
77334
77839
|
async function updateSquad(id, data, db) {
|
|
@@ -77347,7 +77852,8 @@ async function updateSquad(id, data, db) {
|
|
|
77347
77852
|
status: data.status ?? existing.status,
|
|
77348
77853
|
config: data.config ?? existing.config,
|
|
77349
77854
|
createdAt: existing.createdAt,
|
|
77350
|
-
updatedAt
|
|
77855
|
+
updatedAt,
|
|
77856
|
+
deletedAt: existing.deletedAt
|
|
77351
77857
|
};
|
|
77352
77858
|
await database.execute({
|
|
77353
77859
|
sql: `UPDATE squads
|
|
@@ -77368,16 +77874,34 @@ async function updateSquad(id, data, db) {
|
|
|
77368
77874
|
}
|
|
77369
77875
|
async function deleteSquad(id, db) {
|
|
77370
77876
|
const database = db ?? await getDatabase();
|
|
77877
|
+
const now = nowIso();
|
|
77371
77878
|
const result = await database.execute({
|
|
77372
|
-
sql: "
|
|
77373
|
-
args: [id]
|
|
77879
|
+
sql: "UPDATE squads SET status = 'deleted', deleted_at = ?, updated_at = ? WHERE id = ? AND deleted_at IS NULL",
|
|
77880
|
+
args: [now, now, id]
|
|
77374
77881
|
});
|
|
77375
77882
|
return result.rowsAffected > 0;
|
|
77376
77883
|
}
|
|
77884
|
+
async function restoreSquad(id, db) {
|
|
77885
|
+
const database = db ?? await getDatabase();
|
|
77886
|
+
const now = nowIso();
|
|
77887
|
+
const result = await database.execute({
|
|
77888
|
+
sql: "UPDATE squads SET status = 'active', deleted_at = NULL, updated_at = ? WHERE id = ? AND deleted_at IS NOT NULL",
|
|
77889
|
+
args: [now, id]
|
|
77890
|
+
});
|
|
77891
|
+
if (result.rowsAffected === 0) return null;
|
|
77892
|
+
return getSquadRow(id, database);
|
|
77893
|
+
}
|
|
77894
|
+
async function listDeletedSquads(db) {
|
|
77895
|
+
const database = db ?? await getDatabase();
|
|
77896
|
+
const result = await database.execute(
|
|
77897
|
+
"SELECT * FROM squads WHERE deleted_at IS NOT NULL ORDER BY deleted_at DESC, id DESC"
|
|
77898
|
+
);
|
|
77899
|
+
return result.rows.map((row) => mapSquad(row));
|
|
77900
|
+
}
|
|
77377
77901
|
async function getSquadByRepo(repoOwner, repoName, db) {
|
|
77378
77902
|
const database = db ?? await getDatabase();
|
|
77379
77903
|
const result = await database.execute({
|
|
77380
|
-
sql: "SELECT * FROM squads WHERE repo_owner = ? AND repo_name = ? LIMIT 1",
|
|
77904
|
+
sql: "SELECT * FROM squads WHERE repo_owner = ? AND repo_name = ? AND deleted_at IS NULL LIMIT 1",
|
|
77381
77905
|
args: [repoOwner, repoName]
|
|
77382
77906
|
});
|
|
77383
77907
|
const row = result.rows[0];
|
|
@@ -77481,7 +78005,8 @@ function mapSquad(row) {
|
|
|
77481
78005
|
status: asString(row.status),
|
|
77482
78006
|
config: parseJson(asString(row.config)),
|
|
77483
78007
|
createdAt: asString(row.created_at),
|
|
77484
|
-
updatedAt: asString(row.updated_at)
|
|
78008
|
+
updatedAt: asString(row.updated_at),
|
|
78009
|
+
deletedAt: asNullableString(row.deleted_at)
|
|
77485
78010
|
};
|
|
77486
78011
|
}
|
|
77487
78012
|
function mapMember(row) {
|
|
@@ -77497,6 +78022,7 @@ function mapMember(row) {
|
|
|
77497
78022
|
}
|
|
77498
78023
|
|
|
77499
78024
|
// packages/daemon/src/store/conversations.ts
|
|
78025
|
+
init_db();
|
|
77500
78026
|
async function createConversation(source, title, db) {
|
|
77501
78027
|
const database = db ?? await getDatabase();
|
|
77502
78028
|
const createdAt = nowIso();
|
|
@@ -77615,6 +78141,7 @@ function mapMessage(row) {
|
|
|
77615
78141
|
}
|
|
77616
78142
|
|
|
77617
78143
|
// packages/daemon/src/store/inbox.ts
|
|
78144
|
+
init_db();
|
|
77618
78145
|
async function createInboxItem(data, db) {
|
|
77619
78146
|
const database = db ?? await getDatabase();
|
|
77620
78147
|
const timestamp = nowIso();
|
|
@@ -77710,6 +78237,7 @@ function mapInboxItem(row) {
|
|
|
77710
78237
|
|
|
77711
78238
|
// packages/daemon/src/store/schedules.ts
|
|
77712
78239
|
var import_cron_parser = __toESM(require_dist(), 1);
|
|
78240
|
+
init_db();
|
|
77713
78241
|
var parseExpression = import_cron_parser.CronExpressionParser.parse;
|
|
77714
78242
|
async function createSchedule(data, db) {
|
|
77715
78243
|
const database = db ?? await getDatabase();
|
|
@@ -77848,6 +78376,7 @@ function mapSchedule(row) {
|
|
|
77848
78376
|
}
|
|
77849
78377
|
|
|
77850
78378
|
// packages/daemon/src/store/token-usage.ts
|
|
78379
|
+
init_db();
|
|
77851
78380
|
async function recordUsage(data, db) {
|
|
77852
78381
|
const database = db ?? await getDatabase();
|
|
77853
78382
|
const usage = {
|
|
@@ -77977,6 +78506,7 @@ async function getUsageRecords(params = {}, db) {
|
|
|
77977
78506
|
}
|
|
77978
78507
|
|
|
77979
78508
|
// packages/daemon/src/store/activity.ts
|
|
78509
|
+
init_db();
|
|
77980
78510
|
async function logActivity(data, db) {
|
|
77981
78511
|
const database = db ?? await getDatabase();
|
|
77982
78512
|
const activity = {
|
|
@@ -78032,6 +78562,7 @@ function mapActivity(row) {
|
|
|
78032
78562
|
}
|
|
78033
78563
|
|
|
78034
78564
|
// packages/daemon/src/store/agent-history.ts
|
|
78565
|
+
init_db();
|
|
78035
78566
|
async function appendHistory(agentId, squadId, content, db) {
|
|
78036
78567
|
const database = db ?? await getDatabase();
|
|
78037
78568
|
const entry = {
|
|
@@ -78067,6 +78598,7 @@ function mapAgentHistory(row) {
|
|
|
78067
78598
|
}
|
|
78068
78599
|
|
|
78069
78600
|
// packages/daemon/src/store/objectives.ts
|
|
78601
|
+
init_db();
|
|
78070
78602
|
async function createObjective(squadId, description, db) {
|
|
78071
78603
|
const database = db ?? await getDatabase();
|
|
78072
78604
|
const timestamp = nowIso();
|
|
@@ -78259,6 +78791,7 @@ function mapTask(row) {
|
|
|
78259
78791
|
}
|
|
78260
78792
|
|
|
78261
78793
|
// packages/daemon/src/store/instances.ts
|
|
78794
|
+
init_db();
|
|
78262
78795
|
async function createInstance(input, db) {
|
|
78263
78796
|
const database = db ?? await getDatabase();
|
|
78264
78797
|
const instance = {
|
|
@@ -79552,6 +80085,7 @@ function emitInstanceEvent(event, instance) {
|
|
|
79552
80085
|
}
|
|
79553
80086
|
|
|
79554
80087
|
// packages/daemon/src/api/routes/squads.ts
|
|
80088
|
+
init_db();
|
|
79555
80089
|
var router7 = (0, import_express7.Router)();
|
|
79556
80090
|
var DEFAULT_CONFIG = {
|
|
79557
80091
|
prMode: "draft-pr",
|
|
@@ -79724,6 +80258,39 @@ router7.delete("/api/squads/:id", async (req, res) => {
|
|
|
79724
80258
|
});
|
|
79725
80259
|
}
|
|
79726
80260
|
});
|
|
80261
|
+
router7.get("/api/squads/deleted", async (_req, res) => {
|
|
80262
|
+
try {
|
|
80263
|
+
const squads = await listDeletedSquads();
|
|
80264
|
+
res.status(200).json({ squads });
|
|
80265
|
+
} catch (error51) {
|
|
80266
|
+
res.status(500).json({
|
|
80267
|
+
error: "Failed to list deleted squads",
|
|
80268
|
+
details: error51 instanceof Error ? error51.message : "Unknown error"
|
|
80269
|
+
});
|
|
80270
|
+
}
|
|
80271
|
+
});
|
|
80272
|
+
router7.post("/api/squads/:id/restore", async (req, res) => {
|
|
80273
|
+
try {
|
|
80274
|
+
const restored = await restoreSquad(req.params.id);
|
|
80275
|
+
if (!restored) {
|
|
80276
|
+
res.status(404).json({ error: "Squad not found or not deleted" });
|
|
80277
|
+
return;
|
|
80278
|
+
}
|
|
80279
|
+
await logActivity({
|
|
80280
|
+
squadId: restored.id,
|
|
80281
|
+
event: EVENT_NAMES.SQUAD_UPDATED,
|
|
80282
|
+
description: `Restored squad ${restored.name}`,
|
|
80283
|
+
metadata: { repoUrl: restored.repoUrl }
|
|
80284
|
+
});
|
|
80285
|
+
eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad: restored });
|
|
80286
|
+
res.status(200).json({ squad: restored });
|
|
80287
|
+
} catch (error51) {
|
|
80288
|
+
res.status(500).json({
|
|
80289
|
+
error: "Failed to restore squad",
|
|
80290
|
+
details: error51 instanceof Error ? error51.message : "Unknown error"
|
|
80291
|
+
});
|
|
80292
|
+
}
|
|
80293
|
+
});
|
|
79727
80294
|
router7.get("/api/squads/:id/members", async (req, res) => {
|
|
79728
80295
|
try {
|
|
79729
80296
|
const squad = await resolveSquad(req.params.id);
|
|
@@ -80777,655 +81344,10 @@ function ensureDataDirectories() {
|
|
|
80777
81344
|
}
|
|
80778
81345
|
}
|
|
80779
81346
|
|
|
80780
|
-
// packages/daemon/src/models/
|
|
80781
|
-
|
|
80782
|
-
|
|
80783
|
-
|
|
80784
|
-
const envToken = process.env.GITHUB_TOKEN?.trim();
|
|
80785
|
-
if (envToken && envToken.length > 0) {
|
|
80786
|
-
return envToken;
|
|
80787
|
-
}
|
|
80788
|
-
try {
|
|
80789
|
-
const token = execFileSync("gh", ["auth", "token"], {
|
|
80790
|
-
encoding: "utf8",
|
|
80791
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
80792
|
-
}).trim();
|
|
80793
|
-
return token.length > 0 ? token : void 0;
|
|
80794
|
-
} catch {
|
|
80795
|
-
return void 0;
|
|
80796
|
-
}
|
|
80797
|
-
}
|
|
80798
|
-
async function fetchModelCatalog() {
|
|
80799
|
-
const token = resolveGitHubToken();
|
|
80800
|
-
if (!token) {
|
|
80801
|
-
throw new Error("No GitHub token available for model catalog fetch");
|
|
80802
|
-
}
|
|
80803
|
-
const response = await fetch(CATALOG_URL, {
|
|
80804
|
-
headers: {
|
|
80805
|
-
Accept: "application/json",
|
|
80806
|
-
Authorization: `Bearer ${token}`,
|
|
80807
|
-
"X-GitHub-Api-Version": "2024-12-01"
|
|
80808
|
-
}
|
|
80809
|
-
});
|
|
80810
|
-
if (!response.ok) {
|
|
80811
|
-
throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
|
|
80812
|
-
}
|
|
80813
|
-
const data = await response.json();
|
|
80814
|
-
if (!Array.isArray(data)) {
|
|
80815
|
-
throw new Error("Model catalog response is not an array");
|
|
80816
|
-
}
|
|
80817
|
-
const models = [];
|
|
80818
|
-
for (const entry of data) {
|
|
80819
|
-
const id = entry.id ?? entry.name;
|
|
80820
|
-
const displayName = entry.friendly_name ?? entry.name ?? id;
|
|
80821
|
-
if (id && typeof id === "string") {
|
|
80822
|
-
models.push({ id, displayName: displayName ?? id });
|
|
80823
|
-
}
|
|
80824
|
-
}
|
|
80825
|
-
return models;
|
|
80826
|
-
}
|
|
80827
|
-
|
|
80828
|
-
// packages/daemon/src/models/pricing-scraper.ts
|
|
80829
|
-
var TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
|
|
80830
|
-
var PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
|
|
80831
|
-
var COPILOT_PRICING_URL = "https://docs.github.com/api/article/body?pathname=/en/copilot/reference/copilot-billing/models-and-pricing";
|
|
80832
|
-
var PRICE_TO_MULTIPLIER = 0.1;
|
|
80833
|
-
async function scrapeTokenUnitPricing() {
|
|
80834
|
-
const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
|
|
80835
|
-
return parseTokenUnitTable(html);
|
|
80836
|
-
}
|
|
80837
|
-
async function scrapeCopilotPricing() {
|
|
80838
|
-
const markdown = await fetchMarkdown(COPILOT_PRICING_URL);
|
|
80839
|
-
return parseCopilotPricingMarkdown(markdown);
|
|
80840
|
-
}
|
|
80841
|
-
async function scrapePremiumRequestPricing() {
|
|
80842
|
-
const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
|
|
80843
|
-
return parsePremiumMultiplierTable(html);
|
|
80844
|
-
}
|
|
80845
|
-
async function fetchPage(url2) {
|
|
80846
|
-
const response = await fetch(url2, {
|
|
80847
|
-
headers: {
|
|
80848
|
-
Accept: "text/html",
|
|
80849
|
-
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
80850
|
-
},
|
|
80851
|
-
redirect: "follow"
|
|
80852
|
-
});
|
|
80853
|
-
if (!response.ok) {
|
|
80854
|
-
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
80855
|
-
}
|
|
80856
|
-
return response.text();
|
|
80857
|
-
}
|
|
80858
|
-
async function fetchMarkdown(url2) {
|
|
80859
|
-
const response = await fetch(url2, {
|
|
80860
|
-
headers: {
|
|
80861
|
-
Accept: "text/markdown, text/plain, */*",
|
|
80862
|
-
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
80863
|
-
},
|
|
80864
|
-
redirect: "follow"
|
|
80865
|
-
});
|
|
80866
|
-
if (!response.ok) {
|
|
80867
|
-
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
80868
|
-
}
|
|
80869
|
-
return response.text();
|
|
80870
|
-
}
|
|
80871
|
-
function parseTokenUnitTable(html) {
|
|
80872
|
-
const results = [];
|
|
80873
|
-
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
|
|
80874
|
-
for (const match of html.matchAll(tableRowPattern)) {
|
|
80875
|
-
const modelName = cleanCellText(match[1]);
|
|
80876
|
-
const inputMultiplier = Number.parseFloat(match[2]);
|
|
80877
|
-
const cachedInput = match[3].trim().toLowerCase();
|
|
80878
|
-
const outputMultiplier = Number.parseFloat(match[4]);
|
|
80879
|
-
if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
|
|
80880
|
-
results.push({
|
|
80881
|
-
modelName,
|
|
80882
|
-
inputMultiplier,
|
|
80883
|
-
cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
|
|
80884
|
-
outputMultiplier
|
|
80885
|
-
});
|
|
80886
|
-
}
|
|
80887
|
-
}
|
|
80888
|
-
return results;
|
|
80889
|
-
}
|
|
80890
|
-
function parsePremiumMultiplierTable(html) {
|
|
80891
|
-
const results = [];
|
|
80892
|
-
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
|
|
80893
|
-
for (const match of html.matchAll(tableRowPattern)) {
|
|
80894
|
-
const modelName = cleanCellText(match[1]);
|
|
80895
|
-
const multiplier = Number.parseFloat(match[2]);
|
|
80896
|
-
if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
|
|
80897
|
-
results.push({ modelName, multiplier });
|
|
80898
|
-
}
|
|
80899
|
-
}
|
|
80900
|
-
return results;
|
|
80901
|
-
}
|
|
80902
|
-
function cleanCellText(text) {
|
|
80903
|
-
return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
|
|
80904
|
-
}
|
|
80905
|
-
function parseCopilotPricingMarkdown(markdown) {
|
|
80906
|
-
const results = [];
|
|
80907
|
-
const seenModels = /* @__PURE__ */ new Set();
|
|
80908
|
-
const lines = markdown.split("\n");
|
|
80909
|
-
let columnIndices = null;
|
|
80910
|
-
for (const line of lines) {
|
|
80911
|
-
const trimmed = line.trim();
|
|
80912
|
-
if (!trimmed.startsWith("|")) continue;
|
|
80913
|
-
if (/\|\s*Model\s/i.test(trimmed)) {
|
|
80914
|
-
columnIndices = parseHeaderColumns(trimmed);
|
|
80915
|
-
continue;
|
|
80916
|
-
}
|
|
80917
|
-
if (/^[\s|:-]+$/.test(trimmed) || !columnIndices) continue;
|
|
80918
|
-
const entry = parseDataRow(trimmed, columnIndices);
|
|
80919
|
-
if (entry && !seenModels.has(entry.modelName.toLowerCase())) {
|
|
80920
|
-
seenModels.add(entry.modelName.toLowerCase());
|
|
80921
|
-
results.push(entry);
|
|
80922
|
-
}
|
|
80923
|
-
}
|
|
80924
|
-
return results;
|
|
80925
|
-
}
|
|
80926
|
-
function parseDataRow(line, columnIndices) {
|
|
80927
|
-
const cells = splitMarkdownRow(line);
|
|
80928
|
-
if (cells.length <= columnIndices.output) return null;
|
|
80929
|
-
const modelName = cells[columnIndices.model].trim();
|
|
80930
|
-
const inputPrice = parseDollarValue(cells[columnIndices.input]);
|
|
80931
|
-
const cachedPrice = columnIndices.cached >= 0 ? parseDollarValue(cells[columnIndices.cached]) : null;
|
|
80932
|
-
const outputPrice = parseDollarValue(cells[columnIndices.output]);
|
|
80933
|
-
if (!modelName || inputPrice === null || outputPrice === null) return null;
|
|
80934
|
-
return {
|
|
80935
|
-
modelName,
|
|
80936
|
-
inputMultiplier: inputPrice * PRICE_TO_MULTIPLIER,
|
|
80937
|
-
cachedInputMultiplier: cachedPrice !== null ? cachedPrice * PRICE_TO_MULTIPLIER : null,
|
|
80938
|
-
outputMultiplier: outputPrice * PRICE_TO_MULTIPLIER
|
|
80939
|
-
};
|
|
80940
|
-
}
|
|
80941
|
-
function parseHeaderColumns(headerLine) {
|
|
80942
|
-
const cells = splitMarkdownRow(headerLine).map((c) => c.trim().toLowerCase());
|
|
80943
|
-
const model = cells.findIndex((c) => c === "model" || c === "model name");
|
|
80944
|
-
const input = cells.findIndex(
|
|
80945
|
-
(c) => (c === "input" || c === "input multiplier") && !c.includes("cached")
|
|
80946
|
-
);
|
|
80947
|
-
const cached2 = cells.findIndex(
|
|
80948
|
-
(c) => c.includes("cached input") || c === "cached input multiplier"
|
|
80949
|
-
);
|
|
80950
|
-
const output = cells.findIndex(
|
|
80951
|
-
(c) => (c === "output" || c === "output multiplier") && !c.includes("cached")
|
|
80952
|
-
);
|
|
80953
|
-
if (model < 0 || input < 0 || output < 0) {
|
|
80954
|
-
return null;
|
|
80955
|
-
}
|
|
80956
|
-
return { model, input, cached: cached2 >= 0 ? cached2 : -1, output };
|
|
80957
|
-
}
|
|
80958
|
-
function splitMarkdownRow(line) {
|
|
80959
|
-
const parts = line.split("|");
|
|
80960
|
-
if (parts[0].trim() === "") parts.shift();
|
|
80961
|
-
if (parts[parts.length - 1]?.trim() === "") parts.pop();
|
|
80962
|
-
return parts;
|
|
80963
|
-
}
|
|
80964
|
-
function parseDollarValue(cell) {
|
|
80965
|
-
if (!cell) return null;
|
|
80966
|
-
const cleaned = cell.trim().replace(/[$,]/g, "");
|
|
80967
|
-
if (cleaned.toLowerCase() === "n/a" || cleaned === "" || cleaned === "-") {
|
|
80968
|
-
return null;
|
|
80969
|
-
}
|
|
80970
|
-
const value = Number.parseFloat(cleaned);
|
|
80971
|
-
return Number.isNaN(value) ? null : value;
|
|
80972
|
-
}
|
|
80973
|
-
|
|
80974
|
-
// packages/daemon/src/models/seed.ts
|
|
80975
|
-
var SEED_MODELS = [
|
|
80976
|
-
{
|
|
80977
|
-
id: "gpt-4o-mini",
|
|
80978
|
-
displayName: "OpenAI GPT-4o mini",
|
|
80979
|
-
premiumMultiplier: 0.33,
|
|
80980
|
-
tokenInputMultiplier: 0.015,
|
|
80981
|
-
tokenOutputMultiplier: 0.06,
|
|
80982
|
-
cachedInputMultiplier: 75e-4,
|
|
80983
|
-
tier: "trivial",
|
|
80984
|
-
available: true
|
|
80985
|
-
},
|
|
80986
|
-
{
|
|
80987
|
-
id: "gpt-4o",
|
|
80988
|
-
displayName: "OpenAI GPT-4o",
|
|
80989
|
-
premiumMultiplier: 0.33,
|
|
80990
|
-
tokenInputMultiplier: 0.25,
|
|
80991
|
-
tokenOutputMultiplier: 1,
|
|
80992
|
-
cachedInputMultiplier: 0.125,
|
|
80993
|
-
tier: "trivial",
|
|
80994
|
-
available: true
|
|
80995
|
-
},
|
|
80996
|
-
{
|
|
80997
|
-
id: "gpt-5-mini",
|
|
80998
|
-
displayName: "GPT-5 mini",
|
|
80999
|
-
premiumMultiplier: 0.33,
|
|
81000
|
-
tokenInputMultiplier: 0.025,
|
|
81001
|
-
tokenOutputMultiplier: 0.2,
|
|
81002
|
-
cachedInputMultiplier: 25e-4,
|
|
81003
|
-
tier: "trivial",
|
|
81004
|
-
available: true
|
|
81005
|
-
},
|
|
81006
|
-
{
|
|
81007
|
-
id: "gpt-5.3-codex",
|
|
81008
|
-
displayName: "GPT-5.3-Codex",
|
|
81009
|
-
premiumMultiplier: 6,
|
|
81010
|
-
tokenInputMultiplier: 0.175,
|
|
81011
|
-
tokenOutputMultiplier: 1.4,
|
|
81012
|
-
cachedInputMultiplier: 0.0175,
|
|
81013
|
-
tier: "premium",
|
|
81014
|
-
available: true
|
|
81015
|
-
},
|
|
81016
|
-
{
|
|
81017
|
-
id: "gpt-5.4",
|
|
81018
|
-
displayName: "GPT-5.4",
|
|
81019
|
-
premiumMultiplier: 6,
|
|
81020
|
-
tokenInputMultiplier: 0.25,
|
|
81021
|
-
tokenOutputMultiplier: 1.5,
|
|
81022
|
-
cachedInputMultiplier: 0.025,
|
|
81023
|
-
tier: "premium",
|
|
81024
|
-
available: true
|
|
81025
|
-
},
|
|
81026
|
-
{
|
|
81027
|
-
id: "gpt-5.4-mini",
|
|
81028
|
-
displayName: "GPT-5.4 mini",
|
|
81029
|
-
premiumMultiplier: 6,
|
|
81030
|
-
tokenInputMultiplier: 0.075,
|
|
81031
|
-
tokenOutputMultiplier: 0.45,
|
|
81032
|
-
cachedInputMultiplier: 75e-4,
|
|
81033
|
-
tier: "premium",
|
|
81034
|
-
available: true
|
|
81035
|
-
},
|
|
81036
|
-
{
|
|
81037
|
-
id: "gpt-5.5",
|
|
81038
|
-
displayName: "GPT-5.5",
|
|
81039
|
-
premiumMultiplier: 57,
|
|
81040
|
-
tokenInputMultiplier: 0.5,
|
|
81041
|
-
tokenOutputMultiplier: 3,
|
|
81042
|
-
cachedInputMultiplier: 0.05,
|
|
81043
|
-
tier: "ultra",
|
|
81044
|
-
available: true
|
|
81045
|
-
},
|
|
81046
|
-
{
|
|
81047
|
-
id: "claude-sonnet-4",
|
|
81048
|
-
displayName: "Claude Sonnet 4",
|
|
81049
|
-
premiumMultiplier: 6,
|
|
81050
|
-
tokenInputMultiplier: 0.3,
|
|
81051
|
-
tokenOutputMultiplier: 1.5,
|
|
81052
|
-
cachedInputMultiplier: 0.03,
|
|
81053
|
-
tier: "premium",
|
|
81054
|
-
available: true
|
|
81055
|
-
},
|
|
81056
|
-
{
|
|
81057
|
-
id: "claude-sonnet-4.5",
|
|
81058
|
-
displayName: "Claude Sonnet 4.5",
|
|
81059
|
-
premiumMultiplier: 6,
|
|
81060
|
-
tokenInputMultiplier: 0.3,
|
|
81061
|
-
tokenOutputMultiplier: 1.5,
|
|
81062
|
-
cachedInputMultiplier: 0.03,
|
|
81063
|
-
tier: "premium",
|
|
81064
|
-
available: true
|
|
81065
|
-
},
|
|
81066
|
-
{
|
|
81067
|
-
id: "claude-sonnet-4.6",
|
|
81068
|
-
displayName: "Claude Sonnet 4.6",
|
|
81069
|
-
premiumMultiplier: 9,
|
|
81070
|
-
tokenInputMultiplier: 0.3,
|
|
81071
|
-
tokenOutputMultiplier: 1.5,
|
|
81072
|
-
cachedInputMultiplier: 0.03,
|
|
81073
|
-
tier: "premium",
|
|
81074
|
-
available: true
|
|
81075
|
-
},
|
|
81076
|
-
{
|
|
81077
|
-
id: "claude-haiku-4.5",
|
|
81078
|
-
displayName: "Claude Haiku 4.5",
|
|
81079
|
-
premiumMultiplier: 0.33,
|
|
81080
|
-
tokenInputMultiplier: 0.1,
|
|
81081
|
-
tokenOutputMultiplier: 0.5,
|
|
81082
|
-
cachedInputMultiplier: 0.01,
|
|
81083
|
-
tier: "trivial",
|
|
81084
|
-
available: true
|
|
81085
|
-
},
|
|
81086
|
-
{
|
|
81087
|
-
id: "claude-opus-4.5",
|
|
81088
|
-
displayName: "Claude Opus 4.5",
|
|
81089
|
-
premiumMultiplier: 15,
|
|
81090
|
-
tokenInputMultiplier: 0.5,
|
|
81091
|
-
tokenOutputMultiplier: 2.5,
|
|
81092
|
-
cachedInputMultiplier: 0.05,
|
|
81093
|
-
tier: "premium",
|
|
81094
|
-
available: true
|
|
81095
|
-
},
|
|
81096
|
-
{
|
|
81097
|
-
id: "claude-opus-4.6",
|
|
81098
|
-
displayName: "Claude Opus 4.6",
|
|
81099
|
-
premiumMultiplier: 27,
|
|
81100
|
-
tokenInputMultiplier: 0.5,
|
|
81101
|
-
tokenOutputMultiplier: 2.5,
|
|
81102
|
-
cachedInputMultiplier: 0.05,
|
|
81103
|
-
tier: "ultra",
|
|
81104
|
-
available: true
|
|
81105
|
-
},
|
|
81106
|
-
{
|
|
81107
|
-
id: "claude-opus-4.7",
|
|
81108
|
-
displayName: "Claude Opus 4.7",
|
|
81109
|
-
premiumMultiplier: 27,
|
|
81110
|
-
tokenInputMultiplier: 0.5,
|
|
81111
|
-
tokenOutputMultiplier: 2.5,
|
|
81112
|
-
cachedInputMultiplier: 0.05,
|
|
81113
|
-
tier: "ultra",
|
|
81114
|
-
available: true
|
|
81115
|
-
},
|
|
81116
|
-
{
|
|
81117
|
-
id: "claude-opus-4.8",
|
|
81118
|
-
displayName: "Claude Opus 4.8",
|
|
81119
|
-
premiumMultiplier: 27,
|
|
81120
|
-
tokenInputMultiplier: 0.5,
|
|
81121
|
-
tokenOutputMultiplier: 2.5,
|
|
81122
|
-
cachedInputMultiplier: 0.05,
|
|
81123
|
-
tier: "ultra",
|
|
81124
|
-
available: true
|
|
81125
|
-
},
|
|
81126
|
-
{
|
|
81127
|
-
id: "gemini-2.5-pro",
|
|
81128
|
-
displayName: "Gemini 2.5 Pro",
|
|
81129
|
-
premiumMultiplier: 1,
|
|
81130
|
-
tokenInputMultiplier: 0.125,
|
|
81131
|
-
tokenOutputMultiplier: 1,
|
|
81132
|
-
cachedInputMultiplier: 0.0125,
|
|
81133
|
-
tier: "fast",
|
|
81134
|
-
available: true
|
|
81135
|
-
},
|
|
81136
|
-
{
|
|
81137
|
-
id: "gemini-3-flash",
|
|
81138
|
-
displayName: "Gemini 3 Flash",
|
|
81139
|
-
premiumMultiplier: 0.33,
|
|
81140
|
-
tokenInputMultiplier: 0.05,
|
|
81141
|
-
tokenOutputMultiplier: 0.3,
|
|
81142
|
-
cachedInputMultiplier: 5e-3,
|
|
81143
|
-
tier: "trivial",
|
|
81144
|
-
available: true
|
|
81145
|
-
},
|
|
81146
|
-
{
|
|
81147
|
-
id: "gemini-3.1-pro",
|
|
81148
|
-
displayName: "Gemini 3.1 Pro",
|
|
81149
|
-
premiumMultiplier: 6,
|
|
81150
|
-
tokenInputMultiplier: 0.2,
|
|
81151
|
-
tokenOutputMultiplier: 1.2,
|
|
81152
|
-
cachedInputMultiplier: 0.02,
|
|
81153
|
-
tier: "premium",
|
|
81154
|
-
available: true
|
|
81155
|
-
},
|
|
81156
|
-
{
|
|
81157
|
-
id: "gemini-3.5-flash",
|
|
81158
|
-
displayName: "Gemini 3.5 Flash",
|
|
81159
|
-
premiumMultiplier: 14,
|
|
81160
|
-
tokenInputMultiplier: 0.15,
|
|
81161
|
-
tokenOutputMultiplier: 0.9,
|
|
81162
|
-
cachedInputMultiplier: 0.015,
|
|
81163
|
-
tier: "premium",
|
|
81164
|
-
available: true
|
|
81165
|
-
},
|
|
81166
|
-
{
|
|
81167
|
-
id: "raptor-mini",
|
|
81168
|
-
displayName: "Raptor mini",
|
|
81169
|
-
premiumMultiplier: 0.33,
|
|
81170
|
-
tokenInputMultiplier: 0.025,
|
|
81171
|
-
tokenOutputMultiplier: 0.2,
|
|
81172
|
-
cachedInputMultiplier: 25e-4,
|
|
81173
|
-
tier: "trivial",
|
|
81174
|
-
available: true
|
|
81175
|
-
},
|
|
81176
|
-
{
|
|
81177
|
-
id: "mai-code-1-flash",
|
|
81178
|
-
displayName: "MAI-Code-1-Flash",
|
|
81179
|
-
premiumMultiplier: 0.33,
|
|
81180
|
-
tokenInputMultiplier: 0.075,
|
|
81181
|
-
tokenOutputMultiplier: 0.45,
|
|
81182
|
-
cachedInputMultiplier: 75e-4,
|
|
81183
|
-
tier: "trivial",
|
|
81184
|
-
available: true
|
|
81185
|
-
}
|
|
81186
|
-
];
|
|
81187
|
-
|
|
81188
|
-
// packages/daemon/src/models/types.ts
|
|
81189
|
-
var TIER_RANGES = {
|
|
81190
|
-
trivial: { min: 0, max: 0.33 },
|
|
81191
|
-
fast: { min: 0.34, max: 1 },
|
|
81192
|
-
standard: { min: 1.1, max: 5 },
|
|
81193
|
-
premium: { min: 5.1, max: 15 },
|
|
81194
|
-
ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
|
|
81195
|
-
};
|
|
81196
|
-
function computeTierFromMultiplier(premiumMultiplier) {
|
|
81197
|
-
if (premiumMultiplier === null) {
|
|
81198
|
-
return "standard";
|
|
81199
|
-
}
|
|
81200
|
-
for (const [tier, range] of Object.entries(TIER_RANGES)) {
|
|
81201
|
-
if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
|
|
81202
|
-
return tier;
|
|
81203
|
-
}
|
|
81204
|
-
}
|
|
81205
|
-
return "ultra";
|
|
81206
|
-
}
|
|
81207
|
-
var TOKEN_UNIT_PRICE = 1e-5;
|
|
81208
|
-
|
|
81209
|
-
// packages/daemon/src/models/registry.ts
|
|
81210
|
-
function normalizeModelName(name) {
|
|
81211
|
-
return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
|
|
81212
|
-
}
|
|
81213
|
-
async function fetchCatalogIntoMap(modelMap, result, logger2) {
|
|
81214
|
-
try {
|
|
81215
|
-
const catalogModels = await fetchModelCatalog();
|
|
81216
|
-
result.catalogFetched = true;
|
|
81217
|
-
for (const m of catalogModels) {
|
|
81218
|
-
const key = normalizeModelName(m.id);
|
|
81219
|
-
modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
|
|
81220
|
-
}
|
|
81221
|
-
} catch (error51) {
|
|
81222
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81223
|
-
result.errors.push(`Catalog fetch failed: ${msg}`);
|
|
81224
|
-
logger2?.warn(`Model catalog fetch failed: ${msg}`);
|
|
81225
|
-
}
|
|
81226
|
-
}
|
|
81227
|
-
async function scrapeTokenPricingIntoMap(modelMap, result, logger2) {
|
|
81228
|
-
try {
|
|
81229
|
-
const tokenPricing = await scrapeTokenUnitPricing();
|
|
81230
|
-
result.tokenPricingScraped = true;
|
|
81231
|
-
for (const tp of tokenPricing) {
|
|
81232
|
-
const key = normalizeModelName(tp.modelName);
|
|
81233
|
-
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
81234
|
-
if (existing) {
|
|
81235
|
-
existing.tokenInputMultiplier = tp.inputMultiplier;
|
|
81236
|
-
existing.tokenOutputMultiplier = tp.outputMultiplier;
|
|
81237
|
-
existing.cachedInputMultiplier = tp.cachedInputMultiplier;
|
|
81238
|
-
} else {
|
|
81239
|
-
modelMap.set(key, {
|
|
81240
|
-
id: key,
|
|
81241
|
-
displayName: tp.modelName,
|
|
81242
|
-
tokenInputMultiplier: tp.inputMultiplier,
|
|
81243
|
-
tokenOutputMultiplier: tp.outputMultiplier,
|
|
81244
|
-
cachedInputMultiplier: tp.cachedInputMultiplier,
|
|
81245
|
-
available: true
|
|
81246
|
-
});
|
|
81247
|
-
}
|
|
81248
|
-
}
|
|
81249
|
-
} catch (error51) {
|
|
81250
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81251
|
-
result.errors.push(`Token pricing scrape failed: ${msg}`);
|
|
81252
|
-
logger2?.warn(`Token pricing scrape failed: ${msg}`);
|
|
81253
|
-
}
|
|
81254
|
-
}
|
|
81255
|
-
async function scrapePremiumPricingIntoMap(modelMap, result, logger2) {
|
|
81256
|
-
try {
|
|
81257
|
-
const premiumPricing = await scrapePremiumRequestPricing();
|
|
81258
|
-
result.premiumPricingScraped = true;
|
|
81259
|
-
for (const pp of premiumPricing) {
|
|
81260
|
-
const key = normalizeModelName(pp.modelName);
|
|
81261
|
-
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
81262
|
-
if (existing) {
|
|
81263
|
-
existing.premiumMultiplier = pp.multiplier;
|
|
81264
|
-
} else {
|
|
81265
|
-
modelMap.set(key, {
|
|
81266
|
-
id: key,
|
|
81267
|
-
displayName: pp.modelName,
|
|
81268
|
-
premiumMultiplier: pp.multiplier,
|
|
81269
|
-
available: true
|
|
81270
|
-
});
|
|
81271
|
-
}
|
|
81272
|
-
}
|
|
81273
|
-
} catch (error51) {
|
|
81274
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81275
|
-
result.errors.push(`Premium pricing scrape failed: ${msg}`);
|
|
81276
|
-
logger2?.warn(`Premium pricing scrape failed: ${msg}`);
|
|
81277
|
-
}
|
|
81278
|
-
}
|
|
81279
|
-
async function scrapeCopilotPricingIntoMap(modelMap, result, logger2) {
|
|
81280
|
-
try {
|
|
81281
|
-
const copilotPricing = await scrapeCopilotPricing();
|
|
81282
|
-
result.copilotPricingScraped = true;
|
|
81283
|
-
for (const cp of copilotPricing) {
|
|
81284
|
-
const key = normalizeModelName(cp.modelName);
|
|
81285
|
-
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
81286
|
-
if (existing) {
|
|
81287
|
-
existing.tokenInputMultiplier = cp.inputMultiplier;
|
|
81288
|
-
existing.tokenOutputMultiplier = cp.outputMultiplier;
|
|
81289
|
-
if (cp.cachedInputMultiplier !== null) {
|
|
81290
|
-
existing.cachedInputMultiplier = cp.cachedInputMultiplier;
|
|
81291
|
-
}
|
|
81292
|
-
} else {
|
|
81293
|
-
modelMap.set(key, {
|
|
81294
|
-
id: key,
|
|
81295
|
-
displayName: cp.modelName,
|
|
81296
|
-
tokenInputMultiplier: cp.inputMultiplier,
|
|
81297
|
-
tokenOutputMultiplier: cp.outputMultiplier,
|
|
81298
|
-
cachedInputMultiplier: cp.cachedInputMultiplier,
|
|
81299
|
-
available: true
|
|
81300
|
-
});
|
|
81301
|
-
}
|
|
81302
|
-
}
|
|
81303
|
-
} catch (error51) {
|
|
81304
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81305
|
-
result.errors.push(`Copilot pricing scrape failed: ${msg}`);
|
|
81306
|
-
logger2?.warn(`Copilot pricing scrape failed: ${msg}`);
|
|
81307
|
-
}
|
|
81308
|
-
}
|
|
81309
|
-
async function refreshModelPricing(logger2) {
|
|
81310
|
-
const result = {
|
|
81311
|
-
modelsUpdated: 0,
|
|
81312
|
-
catalogFetched: false,
|
|
81313
|
-
tokenPricingScraped: false,
|
|
81314
|
-
premiumPricingScraped: false,
|
|
81315
|
-
copilotPricingScraped: false,
|
|
81316
|
-
errors: []
|
|
81317
|
-
};
|
|
81318
|
-
const modelMap = /* @__PURE__ */ new Map();
|
|
81319
|
-
await fetchCatalogIntoMap(modelMap, result, logger2);
|
|
81320
|
-
await scrapeTokenPricingIntoMap(modelMap, result, logger2);
|
|
81321
|
-
await scrapeCopilotPricingIntoMap(modelMap, result, logger2);
|
|
81322
|
-
await scrapePremiumPricingIntoMap(modelMap, result, logger2);
|
|
81323
|
-
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped && !result.copilotPricingScraped) {
|
|
81324
|
-
logger2?.warn("All pricing sources failed, using seed data");
|
|
81325
|
-
await seedFromFallback();
|
|
81326
|
-
result.modelsUpdated = SEED_MODELS.length;
|
|
81327
|
-
return result;
|
|
81328
|
-
}
|
|
81329
|
-
const db = await getDatabase();
|
|
81330
|
-
const now = nowIso();
|
|
81331
|
-
for (const model of modelMap.values()) {
|
|
81332
|
-
const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
|
|
81333
|
-
await upsertModel(db, {
|
|
81334
|
-
id: model.id,
|
|
81335
|
-
displayName: model.displayName,
|
|
81336
|
-
premiumMultiplier: model.premiumMultiplier ?? null,
|
|
81337
|
-
tokenInputMultiplier: model.tokenInputMultiplier ?? null,
|
|
81338
|
-
tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
|
|
81339
|
-
cachedInputMultiplier: model.cachedInputMultiplier ?? null,
|
|
81340
|
-
tier,
|
|
81341
|
-
available: model.available ?? true,
|
|
81342
|
-
updatedAt: now
|
|
81343
|
-
});
|
|
81344
|
-
result.modelsUpdated++;
|
|
81345
|
-
}
|
|
81346
|
-
return result;
|
|
81347
|
-
}
|
|
81348
|
-
async function seedFromFallback() {
|
|
81349
|
-
const db = await getDatabase();
|
|
81350
|
-
const now = nowIso();
|
|
81351
|
-
for (const model of SEED_MODELS) {
|
|
81352
|
-
await upsertModel(db, { ...model, updatedAt: now });
|
|
81353
|
-
}
|
|
81354
|
-
}
|
|
81355
|
-
async function getModelPricing(modelId) {
|
|
81356
|
-
const db = await getDatabase();
|
|
81357
|
-
const result = await db.execute({
|
|
81358
|
-
sql: "SELECT * FROM model_pricing WHERE id = ?",
|
|
81359
|
-
args: [modelId]
|
|
81360
|
-
});
|
|
81361
|
-
if (result.rows.length > 0) {
|
|
81362
|
-
return rowToModelPricing(result.rows[0]);
|
|
81363
|
-
}
|
|
81364
|
-
const allModels = await db.execute(
|
|
81365
|
-
"SELECT * FROM model_pricing WHERE token_input_multiplier IS NOT NULL ORDER BY length(id) DESC"
|
|
81366
|
-
);
|
|
81367
|
-
for (const row of allModels.rows) {
|
|
81368
|
-
const storedId = asString(row.id);
|
|
81369
|
-
if (modelId.startsWith(storedId) || storedId.startsWith(modelId)) {
|
|
81370
|
-
return rowToModelPricing(row);
|
|
81371
|
-
}
|
|
81372
|
-
}
|
|
81373
|
-
return null;
|
|
81374
|
-
}
|
|
81375
|
-
function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
|
|
81376
|
-
if (inputMultiplier === null || outputMultiplier === null) {
|
|
81377
|
-
return 0;
|
|
81378
|
-
}
|
|
81379
|
-
const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
|
|
81380
|
-
return tokenUnits * TOKEN_UNIT_PRICE;
|
|
81381
|
-
}
|
|
81382
|
-
async function upsertModel(db, model) {
|
|
81383
|
-
await db.execute({
|
|
81384
|
-
sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
|
|
81385
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
81386
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
81387
|
-
display_name = excluded.display_name,
|
|
81388
|
-
premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
|
|
81389
|
-
token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
|
|
81390
|
-
token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
|
|
81391
|
-
cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
|
|
81392
|
-
tier = excluded.tier,
|
|
81393
|
-
available = excluded.available,
|
|
81394
|
-
updated_at = excluded.updated_at`,
|
|
81395
|
-
args: [
|
|
81396
|
-
model.id,
|
|
81397
|
-
model.displayName,
|
|
81398
|
-
model.premiumMultiplier,
|
|
81399
|
-
model.tokenInputMultiplier,
|
|
81400
|
-
model.tokenOutputMultiplier,
|
|
81401
|
-
model.cachedInputMultiplier,
|
|
81402
|
-
model.tier,
|
|
81403
|
-
model.available ? 1 : 0,
|
|
81404
|
-
model.updatedAt
|
|
81405
|
-
]
|
|
81406
|
-
});
|
|
81407
|
-
}
|
|
81408
|
-
function rowToModelPricing(row) {
|
|
81409
|
-
return {
|
|
81410
|
-
id: asString(row.id),
|
|
81411
|
-
displayName: asString(row.display_name),
|
|
81412
|
-
premiumMultiplier: asNullableNumber(row.premium_multiplier),
|
|
81413
|
-
tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
|
|
81414
|
-
tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
|
|
81415
|
-
cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
|
|
81416
|
-
tier: asString(row.tier),
|
|
81417
|
-
available: asNumber(row.available) === 1,
|
|
81418
|
-
updatedAt: asString(row.updated_at)
|
|
81419
|
-
};
|
|
81420
|
-
}
|
|
81421
|
-
function findClosestKey(map2, targetKey) {
|
|
81422
|
-
for (const [key, value] of map2) {
|
|
81423
|
-
if (key.includes(targetKey) || targetKey.includes(key)) {
|
|
81424
|
-
return value;
|
|
81425
|
-
}
|
|
81426
|
-
}
|
|
81427
|
-
return void 0;
|
|
81428
|
-
}
|
|
81347
|
+
// packages/daemon/src/models/index.ts
|
|
81348
|
+
init_catalog();
|
|
81349
|
+
init_registry();
|
|
81350
|
+
init_types();
|
|
81429
81351
|
|
|
81430
81352
|
// packages/daemon/src/orchestrator/system-prompt.ts
|
|
81431
81353
|
function formatSquadRoster(squads) {
|
|
@@ -81759,6 +81681,9 @@ async function sendMessage(session, message2, onChunk) {
|
|
|
81759
81681
|
return operation;
|
|
81760
81682
|
}
|
|
81761
81683
|
|
|
81684
|
+
// packages/daemon/src/orchestrator/orchestrator.ts
|
|
81685
|
+
init_registry();
|
|
81686
|
+
|
|
81762
81687
|
// packages/daemon/src/skills/loader.ts
|
|
81763
81688
|
var import_gray_matter2 = __toESM(require_gray_matter(), 1);
|
|
81764
81689
|
init_paths();
|
|
@@ -82110,6 +82035,7 @@ var executeCodingToolCall = async (toolName, rawArgs) => {
|
|
|
82110
82035
|
// packages/daemon/src/orchestrator/tools/inbox.ts
|
|
82111
82036
|
init_dist();
|
|
82112
82037
|
init_zod();
|
|
82038
|
+
init_db();
|
|
82113
82039
|
var inboxReplySchema = external_exports.object({
|
|
82114
82040
|
itemId: external_exports.string().trim().min(1),
|
|
82115
82041
|
reply: external_exports.string().trim().min(1)
|
|
@@ -82381,17 +82307,80 @@ async function isSquadAvailable(squadId) {
|
|
|
82381
82307
|
}
|
|
82382
82308
|
|
|
82383
82309
|
// packages/daemon/src/execution/agent.ts
|
|
82384
|
-
|
|
82310
|
+
init_registry();
|
|
82385
82311
|
import { exec as exec3 } from "node:child_process";
|
|
82386
82312
|
import { mkdir as mkdir9, readFile as readFile7, readdir as readdir5, stat as stat3, writeFile as writeFile6 } from "node:fs/promises";
|
|
82387
82313
|
import { dirname as dirname8, extname as extname3, isAbsolute, join as join11, relative as relative3, resolve as resolve4 } from "node:path";
|
|
82388
82314
|
import { promisify as promisify3 } from "node:util";
|
|
82389
82315
|
import {
|
|
82390
|
-
CopilotClient as
|
|
82391
|
-
approveAll as
|
|
82316
|
+
CopilotClient as CopilotClient3,
|
|
82317
|
+
approveAll as approveAll3,
|
|
82392
82318
|
defineTool
|
|
82393
82319
|
} from "@github/copilot-sdk";
|
|
82394
82320
|
|
|
82321
|
+
// packages/daemon/src/squad/model-selector.ts
|
|
82322
|
+
init_registry();
|
|
82323
|
+
import { CopilotClient as CopilotClient2, approveAll as approveAll2 } from "@github/copilot-sdk";
|
|
82324
|
+
var VALID_TIERS = ["trivial", "fast", "standard", "premium", "ultra"];
|
|
82325
|
+
var CLASSIFICATION_PROMPT = `You are a task complexity classifier. Given a task description, classify its complexity into exactly one tier.
|
|
82326
|
+
|
|
82327
|
+
Tiers (from simplest to most complex):
|
|
82328
|
+
- trivial: Typos, renames, comment changes, config tweaks, formatting
|
|
82329
|
+
- fast: Simple bug fixes, small features, documentation updates, single-file changes
|
|
82330
|
+
- standard: Feature implementation, multi-file changes, moderate refactoring
|
|
82331
|
+
- premium: Architecture changes, complex refactoring, security work, performance optimization
|
|
82332
|
+
- ultra: System-wide redesigns, critical infrastructure, cross-cutting concerns
|
|
82333
|
+
|
|
82334
|
+
Reply with ONLY the tier name (one word, lowercase). Nothing else.`;
|
|
82335
|
+
async function selectModelForTask(taskDescription) {
|
|
82336
|
+
const classifierModel = await getCheapestAvailableModel();
|
|
82337
|
+
if (!classifierModel) {
|
|
82338
|
+
throw new Error("No models available in pricing database");
|
|
82339
|
+
}
|
|
82340
|
+
let tier;
|
|
82341
|
+
try {
|
|
82342
|
+
tier = await classifyTaskComplexity(taskDescription, classifierModel.id);
|
|
82343
|
+
} catch {
|
|
82344
|
+
return classifierModel.id;
|
|
82345
|
+
}
|
|
82346
|
+
const selectedModel = await getCheapestInTier(tier);
|
|
82347
|
+
if (selectedModel) {
|
|
82348
|
+
return selectedModel.id;
|
|
82349
|
+
}
|
|
82350
|
+
const nextTier = getNextTierUp(tier);
|
|
82351
|
+
if (nextTier) {
|
|
82352
|
+
const escalatedModel = await getCheapestInTier(nextTier);
|
|
82353
|
+
if (escalatedModel) {
|
|
82354
|
+
return escalatedModel.id;
|
|
82355
|
+
}
|
|
82356
|
+
}
|
|
82357
|
+
return classifierModel.id;
|
|
82358
|
+
}
|
|
82359
|
+
async function classifyTaskComplexity(taskDescription, modelId) {
|
|
82360
|
+
let client2 = null;
|
|
82361
|
+
try {
|
|
82362
|
+
client2 = new CopilotClient2();
|
|
82363
|
+
await client2.start();
|
|
82364
|
+
const session = await client2.createSession({
|
|
82365
|
+
model: modelId,
|
|
82366
|
+
onPermissionRequest: approveAll2,
|
|
82367
|
+
systemMessage: { content: CLASSIFICATION_PROMPT }
|
|
82368
|
+
});
|
|
82369
|
+
try {
|
|
82370
|
+
const response = await session.sendAndWait({ prompt: `Task: ${taskDescription}` }, 15e3);
|
|
82371
|
+
const raw = (response.text ?? "").trim().toLowerCase();
|
|
82372
|
+
const tier = VALID_TIERS.find((t) => raw.includes(t));
|
|
82373
|
+
return tier ?? "standard";
|
|
82374
|
+
} finally {
|
|
82375
|
+
await session.disconnect().catch(() => void 0);
|
|
82376
|
+
}
|
|
82377
|
+
} finally {
|
|
82378
|
+
if (client2) {
|
|
82379
|
+
await client2.stop().catch(() => void 0);
|
|
82380
|
+
}
|
|
82381
|
+
}
|
|
82382
|
+
}
|
|
82383
|
+
|
|
82395
82384
|
// packages/daemon/src/execution/history.ts
|
|
82396
82385
|
var DEFAULT_CONTEXT_LIMIT = 5;
|
|
82397
82386
|
var MAX_LEARNING_LENGTH = 1e3;
|
|
@@ -82677,14 +82666,15 @@ async function executeAgentTask(member, task, worktreePath, options2) {
|
|
|
82677
82666
|
const mcpServerNote = options2?.mcpServers?.length ? `Available MCP server labels: ${options2.mcpServers.join(", ")}.` : "No additional MCP servers were configured for this run.";
|
|
82678
82667
|
let client2 = null;
|
|
82679
82668
|
try {
|
|
82680
|
-
client2 = new
|
|
82669
|
+
client2 = new CopilotClient3({ workingDirectory: worktreePath });
|
|
82681
82670
|
await client2.start();
|
|
82671
|
+
const model = member.model ?? await selectModelForTask(task.description);
|
|
82682
82672
|
const session = await client2.createSession({
|
|
82683
|
-
model
|
|
82673
|
+
model,
|
|
82684
82674
|
workingDirectory: worktreePath,
|
|
82685
82675
|
tools,
|
|
82686
82676
|
availableTools: ["custom:*"],
|
|
82687
|
-
onPermissionRequest:
|
|
82677
|
+
onPermissionRequest: approveAll3,
|
|
82688
82678
|
systemMessage: {
|
|
82689
82679
|
content: `${member.systemPrompt}
|
|
82690
82680
|
|
|
@@ -82761,12 +82751,11 @@ async function buildInstanceSystemPromptSuffix(squadId, instanceId) {
|
|
|
82761
82751
|
}
|
|
82762
82752
|
|
|
82763
82753
|
// packages/daemon/src/execution/planning.ts
|
|
82764
|
-
init_dist();
|
|
82765
82754
|
import { exec as exec4 } from "node:child_process";
|
|
82766
82755
|
import { access, readFile as readFile8 } from "node:fs/promises";
|
|
82767
82756
|
import { join as join12 } from "node:path";
|
|
82768
82757
|
import { promisify as promisify4 } from "node:util";
|
|
82769
|
-
import { CopilotClient as
|
|
82758
|
+
import { CopilotClient as CopilotClient4, approveAll as approveAll4 } from "@github/copilot-sdk";
|
|
82770
82759
|
|
|
82771
82760
|
// packages/daemon/src/squad/roles.ts
|
|
82772
82761
|
var ROLE_GUIDELINES = [
|
|
@@ -82909,12 +82898,13 @@ Return strict JSON in this shape:
|
|
|
82909
82898
|
}`;
|
|
82910
82899
|
let client2 = null;
|
|
82911
82900
|
try {
|
|
82912
|
-
client2 = new
|
|
82901
|
+
client2 = new CopilotClient4({ workingDirectory: repoPath });
|
|
82913
82902
|
await client2.start();
|
|
82903
|
+
const model = await selectModelForTask(`Create implementation plan: ${objective.description}`);
|
|
82914
82904
|
const session = await client2.createSession({
|
|
82915
|
-
model
|
|
82905
|
+
model,
|
|
82916
82906
|
workingDirectory: repoPath,
|
|
82917
|
-
onPermissionRequest:
|
|
82907
|
+
onPermissionRequest: approveAll4,
|
|
82918
82908
|
systemMessage: {
|
|
82919
82909
|
content: `${TEAM_LEAD_PROMPT}
|
|
82920
82910
|
|
|
@@ -83017,7 +83007,7 @@ async function createPullRequest(options2) {
|
|
|
83017
83007
|
init_dist();
|
|
83018
83008
|
import { exec as exec6 } from "node:child_process";
|
|
83019
83009
|
import { promisify as promisify6 } from "node:util";
|
|
83020
|
-
import { CopilotClient as
|
|
83010
|
+
import { CopilotClient as CopilotClient5, approveAll as approveAll5 } from "@github/copilot-sdk";
|
|
83021
83011
|
var execAsync6 = promisify6(exec6);
|
|
83022
83012
|
var GIT_DIFF_MAX_BUFFER = 10 * 1024 * 1024;
|
|
83023
83013
|
function extractJsonObject2(content) {
|
|
@@ -83057,12 +83047,13 @@ Return strict JSON:
|
|
|
83057
83047
|
}`;
|
|
83058
83048
|
let client2 = null;
|
|
83059
83049
|
try {
|
|
83060
|
-
client2 = new
|
|
83050
|
+
client2 = new CopilotClient5({ workingDirectory: worktreePath });
|
|
83061
83051
|
await client2.start();
|
|
83052
|
+
const model = qaMember.model ?? await selectModelForTask(`QA review: ${objective.description}`);
|
|
83062
83053
|
const session = await client2.createSession({
|
|
83063
|
-
model
|
|
83054
|
+
model,
|
|
83064
83055
|
workingDirectory: worktreePath,
|
|
83065
|
-
onPermissionRequest:
|
|
83056
|
+
onPermissionRequest: approveAll5,
|
|
83066
83057
|
systemMessage: {
|
|
83067
83058
|
content: QA_PROMPT
|
|
83068
83059
|
}
|
|
@@ -83132,8 +83123,7 @@ async function handleQARejection(objectiveId, feedback) {
|
|
|
83132
83123
|
}
|
|
83133
83124
|
|
|
83134
83125
|
// packages/daemon/src/execution/review.ts
|
|
83135
|
-
|
|
83136
|
-
import { CopilotClient as CopilotClient5, approveAll as approveAll5 } from "@github/copilot-sdk";
|
|
83126
|
+
import { CopilotClient as CopilotClient6, approveAll as approveAll6 } from "@github/copilot-sdk";
|
|
83137
83127
|
function extractJsonObject3(content) {
|
|
83138
83128
|
const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
83139
83129
|
if (fenced?.[1]) {
|
|
@@ -83178,11 +83168,12 @@ Return strict JSON:
|
|
|
83178
83168
|
}`;
|
|
83179
83169
|
let client2 = null;
|
|
83180
83170
|
try {
|
|
83181
|
-
client2 = new
|
|
83171
|
+
client2 = new CopilotClient6();
|
|
83182
83172
|
await client2.start();
|
|
83173
|
+
const model = teamLead.model ?? await selectModelForTask(`Code review: ${objective.description}`);
|
|
83183
83174
|
const session = await client2.createSession({
|
|
83184
|
-
model
|
|
83185
|
-
onPermissionRequest:
|
|
83175
|
+
model,
|
|
83176
|
+
onPermissionRequest: approveAll6,
|
|
83186
83177
|
systemMessage: {
|
|
83187
83178
|
content: `${TEAM_LEAD_PROMPT}
|
|
83188
83179
|
|
|
@@ -83647,10 +83638,22 @@ var squadToolDefinitions = [
|
|
|
83647
83638
|
},
|
|
83648
83639
|
{
|
|
83649
83640
|
name: "fire_squad",
|
|
83650
|
-
description: "
|
|
83641
|
+
description: "Soft-delete a squad. The squad can be restored later with restore_squad.",
|
|
83651
83642
|
parameters: squadIdSchema,
|
|
83652
83643
|
skipPermission: true
|
|
83653
83644
|
},
|
|
83645
|
+
{
|
|
83646
|
+
name: "restore_squad",
|
|
83647
|
+
description: "Restore a previously deleted squad and all its members.",
|
|
83648
|
+
parameters: squadIdSchema,
|
|
83649
|
+
skipPermission: true
|
|
83650
|
+
},
|
|
83651
|
+
{
|
|
83652
|
+
name: "list_deleted_squads",
|
|
83653
|
+
description: "List all soft-deleted squads that can be restored.",
|
|
83654
|
+
parameters: external_exports.object({}),
|
|
83655
|
+
skipPermission: true
|
|
83656
|
+
},
|
|
83654
83657
|
{
|
|
83655
83658
|
name: "list_squads",
|
|
83656
83659
|
description: "List all squads and their status.",
|
|
@@ -83850,7 +83853,7 @@ async function processQueue2(squadId) {
|
|
|
83850
83853
|
const next = await getNextQueued(squadId);
|
|
83851
83854
|
if (next) {
|
|
83852
83855
|
const freshSquad = await getSquad(squadId);
|
|
83853
|
-
if (freshSquad) {
|
|
83856
|
+
if (freshSquad && next.objectiveId) {
|
|
83854
83857
|
void startAndExecuteInstance(next.id, freshSquad, next.objectiveId);
|
|
83855
83858
|
}
|
|
83856
83859
|
}
|
|
@@ -83985,13 +83988,28 @@ async function handleFireSquad(rawArgs) {
|
|
|
83985
83988
|
if (activeObjectives.length > 0) {
|
|
83986
83989
|
const updated = await updateSquad(squadId, { status: "inactive" });
|
|
83987
83990
|
return {
|
|
83988
|
-
message: `Squad ${squadId} was deactivated because it still has active objectives.`,
|
|
83991
|
+
message: `Squad ${squadId} was deactivated because it still has active objectives. Use fire_squad again after objectives complete to delete it.`,
|
|
83989
83992
|
squad: updated,
|
|
83990
83993
|
activeObjectives
|
|
83991
83994
|
};
|
|
83992
83995
|
}
|
|
83993
83996
|
await deleteSquad(squadId);
|
|
83994
|
-
return {
|
|
83997
|
+
return {
|
|
83998
|
+
message: `Squad "${squad.name}" has been deleted. It can be restored with restore_squad if needed.`,
|
|
83999
|
+
squadId
|
|
84000
|
+
};
|
|
84001
|
+
}
|
|
84002
|
+
async function handleRestoreSquad(rawArgs) {
|
|
84003
|
+
const { squadId } = squadIdSchema.parse(rawArgs);
|
|
84004
|
+
const restored = await restoreSquad(squadId);
|
|
84005
|
+
if (!restored) {
|
|
84006
|
+
throw new Error(`Squad ${squadId} was not found or is not deleted.`);
|
|
84007
|
+
}
|
|
84008
|
+
eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad: restored });
|
|
84009
|
+
return {
|
|
84010
|
+
message: `Squad "${restored.name}" has been restored.`,
|
|
84011
|
+
squad: restored
|
|
84012
|
+
};
|
|
83995
84013
|
}
|
|
83996
84014
|
async function handleDelegateToSquad(rawArgs) {
|
|
83997
84015
|
const { squadId, objective } = delegateToSquadSchema.parse(rawArgs);
|
|
@@ -84056,11 +84074,26 @@ function createSquadToolExecutor(_config) {
|
|
|
84056
84074
|
return handleAnalyzeRepo(rawArgs);
|
|
84057
84075
|
case "fire_squad":
|
|
84058
84076
|
return handleFireSquad(rawArgs);
|
|
84077
|
+
case "restore_squad":
|
|
84078
|
+
return handleRestoreSquad(rawArgs);
|
|
84079
|
+
case "list_deleted_squads": {
|
|
84080
|
+
const deletedSquads = await listDeletedSquads();
|
|
84081
|
+
if (deletedSquads.length === 0) {
|
|
84082
|
+
return { message: "No deleted squads.", squads: [] };
|
|
84083
|
+
}
|
|
84084
|
+
const list = deletedSquads.map((s) => `${s.id}: ${s.name} (${s.repoOwner}/${s.repoName}) [deleted ${s.deletedAt}]`).join("\n");
|
|
84085
|
+
return { message: list, squads: deletedSquads };
|
|
84086
|
+
}
|
|
84059
84087
|
case "list_squads": {
|
|
84060
84088
|
const squads = await listSquads();
|
|
84089
|
+
const deletedCount = (await listDeletedSquads()).length;
|
|
84090
|
+
const suffix = deletedCount > 0 ? `
|
|
84091
|
+
|
|
84092
|
+
${deletedCount} deleted squad(s) available for restore (use list_deleted_squads to view).` : "";
|
|
84061
84093
|
return {
|
|
84062
|
-
message: formatSquadList(squads),
|
|
84063
|
-
squads
|
|
84094
|
+
message: formatSquadList(squads) + suffix,
|
|
84095
|
+
squads,
|
|
84096
|
+
deletedCount
|
|
84064
84097
|
};
|
|
84065
84098
|
}
|
|
84066
84099
|
case "get_squad_status": {
|
|
@@ -84454,13 +84487,13 @@ var Orchestrator = class {
|
|
|
84454
84487
|
});
|
|
84455
84488
|
this.activeModel = model;
|
|
84456
84489
|
} catch (error51) {
|
|
84457
|
-
if (model !==
|
|
84490
|
+
if (model !== this.config.defaultModel) {
|
|
84458
84491
|
this.activeSession = await createSession({
|
|
84459
|
-
model:
|
|
84492
|
+
model: this.config.defaultModel,
|
|
84460
84493
|
systemPrompt,
|
|
84461
84494
|
tools: createBoundOrchestratorTools(this.config)
|
|
84462
84495
|
});
|
|
84463
|
-
this.activeModel =
|
|
84496
|
+
this.activeModel = this.config.defaultModel;
|
|
84464
84497
|
} else {
|
|
84465
84498
|
throw error51;
|
|
84466
84499
|
}
|
|
@@ -84509,6 +84542,7 @@ function createOrchestrator(config2, eventBus2) {
|
|
|
84509
84542
|
// packages/daemon/src/scheduler/engine.ts
|
|
84510
84543
|
init_dist();
|
|
84511
84544
|
var import_cron_parser2 = __toESM(require_dist(), 1);
|
|
84545
|
+
init_db();
|
|
84512
84546
|
var Scheduler = class {
|
|
84513
84547
|
orchestrator;
|
|
84514
84548
|
eventBus;
|
|
@@ -84596,6 +84630,9 @@ function createScheduler(orchestrator2, eventBus2) {
|
|
|
84596
84630
|
return new Scheduler(orchestrator2, eventBus2);
|
|
84597
84631
|
}
|
|
84598
84632
|
|
|
84633
|
+
// packages/daemon/src/index.ts
|
|
84634
|
+
init_db();
|
|
84635
|
+
|
|
84599
84636
|
// packages/daemon/src/telegram/bot.ts
|
|
84600
84637
|
var import_grammy = __toESM(require_mod2(), 1);
|
|
84601
84638
|
var TelegramBot = class {
|
|
@@ -84616,9 +84653,16 @@ var TelegramBot = class {
|
|
|
84616
84653
|
if (this.started) {
|
|
84617
84654
|
return;
|
|
84618
84655
|
}
|
|
84656
|
+
this.bot.catch((err) => {
|
|
84657
|
+
this.logger.error({ err: err.error }, "Telegram bot error: %s", err.message);
|
|
84658
|
+
});
|
|
84619
84659
|
this.registerHandlers();
|
|
84620
|
-
this.bot.start(
|
|
84621
|
-
|
|
84660
|
+
this.bot.start({
|
|
84661
|
+
onStart: () => {
|
|
84662
|
+
this.logger.info("Telegram bot connected and polling");
|
|
84663
|
+
}
|
|
84664
|
+
}).catch((error51) => {
|
|
84665
|
+
this.logger.error({ err: error51 }, "Telegram polling failed to start");
|
|
84622
84666
|
});
|
|
84623
84667
|
this.started = true;
|
|
84624
84668
|
}
|
|
@@ -84628,11 +84672,26 @@ var TelegramBot = class {
|
|
|
84628
84672
|
}
|
|
84629
84673
|
this.bot.stop();
|
|
84630
84674
|
this.started = false;
|
|
84675
|
+
this.logger.info("Telegram bot stopped");
|
|
84631
84676
|
}
|
|
84632
84677
|
async sendText(chatId, text) {
|
|
84633
84678
|
await this.bot.api.sendMessage(chatId, text);
|
|
84634
84679
|
}
|
|
84680
|
+
isAuthorized(userId) {
|
|
84681
|
+
if (!this.config.telegramUserId) {
|
|
84682
|
+
return false;
|
|
84683
|
+
}
|
|
84684
|
+
return String(userId) === this.config.telegramUserId;
|
|
84685
|
+
}
|
|
84635
84686
|
registerHandlers() {
|
|
84687
|
+
this.bot.use(async (ctx, next) => {
|
|
84688
|
+
const userId = ctx.from?.id;
|
|
84689
|
+
if (!userId || !this.isAuthorized(userId)) {
|
|
84690
|
+
this.logger.warn({ userId }, "Unauthorized Telegram message, ignoring");
|
|
84691
|
+
return;
|
|
84692
|
+
}
|
|
84693
|
+
await next();
|
|
84694
|
+
});
|
|
84636
84695
|
this.bot.command("start", async (ctx) => {
|
|
84637
84696
|
await ctx.reply(
|
|
84638
84697
|
"Hello from Io. Send me a message and I will route it through the daemon orchestrator."
|
|
@@ -84807,8 +84866,7 @@ async function main() {
|
|
|
84807
84866
|
logger2.info("Database initialized");
|
|
84808
84867
|
const pricingResult = await refreshModelPricing(logger2);
|
|
84809
84868
|
if (pricingResult.modelsUpdated === 0) {
|
|
84810
|
-
logger2.warn("Model pricing refresh returned 0 models
|
|
84811
|
-
await seedFromFallback();
|
|
84869
|
+
logger2.warn("Model pricing refresh returned 0 models");
|
|
84812
84870
|
}
|
|
84813
84871
|
logger2.info({ modelsUpdated: pricingResult.modelsUpdated }, "Model pricing initialized");
|
|
84814
84872
|
await scanSkills();
|
|
@@ -84828,8 +84886,13 @@ async function main() {
|
|
|
84828
84886
|
setChatOrchestrator(orchestrator2);
|
|
84829
84887
|
const apiServer = createApiServer(config2);
|
|
84830
84888
|
const telegramBot = createTelegramBot(config2, orchestrator2);
|
|
84831
|
-
telegramBot
|
|
84832
|
-
|
|
84889
|
+
if (telegramBot) {
|
|
84890
|
+
telegramBot.start();
|
|
84891
|
+
createTelegramNotifier(telegramBot, config2, eventBus);
|
|
84892
|
+
logger2.info("Telegram bot initialized");
|
|
84893
|
+
} else {
|
|
84894
|
+
logger2.info("Telegram bot disabled (no token configured)");
|
|
84895
|
+
}
|
|
84833
84896
|
registerShutdownHandlers(logger2, async () => {
|
|
84834
84897
|
if (pricingRefreshTimer) {
|
|
84835
84898
|
clearInterval(pricingRefreshTimer);
|