heyio 4.2.7 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/daemon/cli.js +194 -32
- package/dist/daemon/index.js +1003 -791
- package/dist/web/assets/{index-fi5rwdUX.js → index-BBx9hgnQ.js} +2 -2
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
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.1";
|
|
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,498 @@ 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 stripVendorPrefix(id) {
|
|
62787
|
+
const slashIndex = id.indexOf("/");
|
|
62788
|
+
return slashIndex >= 0 ? id.slice(slashIndex + 1) : id;
|
|
62789
|
+
}
|
|
62790
|
+
function normalizeModelName(name) {
|
|
62791
|
+
return stripVendorPrefix(name).toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
|
|
62792
|
+
}
|
|
62793
|
+
async function fetchCatalogIntoMap(modelMap, result, logger2) {
|
|
62794
|
+
try {
|
|
62795
|
+
const catalogModels = await fetchModelCatalog();
|
|
62796
|
+
result.catalogFetched = true;
|
|
62797
|
+
for (const m of catalogModels) {
|
|
62798
|
+
const key = normalizeModelName(m.id);
|
|
62799
|
+
const id = stripVendorPrefix(m.id);
|
|
62800
|
+
modelMap.set(key, { id, displayName: m.displayName, available: true });
|
|
62801
|
+
}
|
|
62802
|
+
} catch (error51) {
|
|
62803
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62804
|
+
result.errors.push(`Catalog fetch failed: ${msg}`);
|
|
62805
|
+
logger2?.warn(`Model catalog fetch failed: ${msg}`);
|
|
62806
|
+
}
|
|
62807
|
+
}
|
|
62808
|
+
async function scrapeTokenPricingIntoMap(modelMap, result, logger2) {
|
|
62809
|
+
try {
|
|
62810
|
+
const tokenPricing = await scrapeTokenUnitPricing();
|
|
62811
|
+
result.tokenPricingScraped = true;
|
|
62812
|
+
for (const tp of tokenPricing) {
|
|
62813
|
+
const key = normalizeModelName(tp.modelName);
|
|
62814
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
62815
|
+
if (existing) {
|
|
62816
|
+
existing.tokenInputMultiplier = tp.inputMultiplier;
|
|
62817
|
+
existing.tokenOutputMultiplier = tp.outputMultiplier;
|
|
62818
|
+
existing.cachedInputMultiplier = tp.cachedInputMultiplier;
|
|
62819
|
+
} else {
|
|
62820
|
+
modelMap.set(key, {
|
|
62821
|
+
id: key,
|
|
62822
|
+
displayName: tp.modelName,
|
|
62823
|
+
tokenInputMultiplier: tp.inputMultiplier,
|
|
62824
|
+
tokenOutputMultiplier: tp.outputMultiplier,
|
|
62825
|
+
cachedInputMultiplier: tp.cachedInputMultiplier,
|
|
62826
|
+
available: true
|
|
62827
|
+
});
|
|
62828
|
+
}
|
|
62829
|
+
}
|
|
62830
|
+
} catch (error51) {
|
|
62831
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62832
|
+
result.errors.push(`Token pricing scrape failed: ${msg}`);
|
|
62833
|
+
logger2?.warn(`Token pricing scrape failed: ${msg}`);
|
|
62834
|
+
}
|
|
62835
|
+
}
|
|
62836
|
+
async function scrapePremiumPricingIntoMap(modelMap, result, logger2) {
|
|
62837
|
+
try {
|
|
62838
|
+
const premiumPricing = await scrapePremiumRequestPricing();
|
|
62839
|
+
result.premiumPricingScraped = true;
|
|
62840
|
+
for (const pp of premiumPricing) {
|
|
62841
|
+
const key = normalizeModelName(pp.modelName);
|
|
62842
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
62843
|
+
if (existing) {
|
|
62844
|
+
existing.premiumMultiplier = pp.multiplier;
|
|
62845
|
+
} else {
|
|
62846
|
+
modelMap.set(key, {
|
|
62847
|
+
id: key,
|
|
62848
|
+
displayName: pp.modelName,
|
|
62849
|
+
premiumMultiplier: pp.multiplier,
|
|
62850
|
+
available: true
|
|
62851
|
+
});
|
|
62852
|
+
}
|
|
62853
|
+
}
|
|
62854
|
+
} catch (error51) {
|
|
62855
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62856
|
+
result.errors.push(`Premium pricing scrape failed: ${msg}`);
|
|
62857
|
+
logger2?.warn(`Premium pricing scrape failed: ${msg}`);
|
|
62858
|
+
}
|
|
62859
|
+
}
|
|
62860
|
+
async function scrapeCopilotPricingIntoMap(modelMap, result, logger2) {
|
|
62861
|
+
try {
|
|
62862
|
+
const copilotPricing = await scrapeCopilotPricing();
|
|
62863
|
+
result.copilotPricingScraped = true;
|
|
62864
|
+
for (const cp of copilotPricing) {
|
|
62865
|
+
const key = normalizeModelName(cp.modelName);
|
|
62866
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
62867
|
+
if (existing) {
|
|
62868
|
+
existing.tokenInputMultiplier = cp.inputMultiplier;
|
|
62869
|
+
existing.tokenOutputMultiplier = cp.outputMultiplier;
|
|
62870
|
+
if (cp.cachedInputMultiplier !== null) {
|
|
62871
|
+
existing.cachedInputMultiplier = cp.cachedInputMultiplier;
|
|
62872
|
+
}
|
|
62873
|
+
} else {
|
|
62874
|
+
modelMap.set(key, {
|
|
62875
|
+
id: key,
|
|
62876
|
+
displayName: cp.modelName,
|
|
62877
|
+
tokenInputMultiplier: cp.inputMultiplier,
|
|
62878
|
+
tokenOutputMultiplier: cp.outputMultiplier,
|
|
62879
|
+
cachedInputMultiplier: cp.cachedInputMultiplier,
|
|
62880
|
+
available: true
|
|
62881
|
+
});
|
|
62882
|
+
}
|
|
62883
|
+
}
|
|
62884
|
+
} catch (error51) {
|
|
62885
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
62886
|
+
result.errors.push(`Copilot pricing scrape failed: ${msg}`);
|
|
62887
|
+
logger2?.warn(`Copilot pricing scrape failed: ${msg}`);
|
|
62888
|
+
}
|
|
62889
|
+
}
|
|
62890
|
+
async function refreshModelPricing(logger2) {
|
|
62891
|
+
const result = {
|
|
62892
|
+
modelsUpdated: 0,
|
|
62893
|
+
catalogFetched: false,
|
|
62894
|
+
tokenPricingScraped: false,
|
|
62895
|
+
premiumPricingScraped: false,
|
|
62896
|
+
copilotPricingScraped: false,
|
|
62897
|
+
errors: []
|
|
62898
|
+
};
|
|
62899
|
+
const modelMap = /* @__PURE__ */ new Map();
|
|
62900
|
+
await fetchCatalogIntoMap(modelMap, result, logger2);
|
|
62901
|
+
await scrapeTokenPricingIntoMap(modelMap, result, logger2);
|
|
62902
|
+
await scrapeCopilotPricingIntoMap(modelMap, result, logger2);
|
|
62903
|
+
await scrapePremiumPricingIntoMap(modelMap, result, logger2);
|
|
62904
|
+
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped && !result.copilotPricingScraped) {
|
|
62905
|
+
logger2?.warn("All pricing sources failed, no models available");
|
|
62906
|
+
return result;
|
|
62907
|
+
}
|
|
62908
|
+
const db = await getDatabase();
|
|
62909
|
+
const now = nowIso();
|
|
62910
|
+
for (const model of modelMap.values()) {
|
|
62911
|
+
if (model.tokenInputMultiplier == null) {
|
|
62912
|
+
continue;
|
|
62913
|
+
}
|
|
62914
|
+
const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
|
|
62915
|
+
await upsertModel(db, {
|
|
62916
|
+
id: model.id,
|
|
62917
|
+
displayName: model.displayName,
|
|
62918
|
+
premiumMultiplier: model.premiumMultiplier ?? null,
|
|
62919
|
+
tokenInputMultiplier: model.tokenInputMultiplier,
|
|
62920
|
+
tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
|
|
62921
|
+
cachedInputMultiplier: model.cachedInputMultiplier ?? null,
|
|
62922
|
+
tier,
|
|
62923
|
+
available: model.available ?? true,
|
|
62924
|
+
updatedAt: now
|
|
62925
|
+
});
|
|
62926
|
+
result.modelsUpdated++;
|
|
62927
|
+
}
|
|
62928
|
+
return result;
|
|
62929
|
+
}
|
|
62930
|
+
async function getModelsForTier(tier) {
|
|
62931
|
+
const db = await getDatabase();
|
|
62932
|
+
const result = await db.execute({
|
|
62933
|
+
sql: "SELECT * FROM model_pricing WHERE tier = ? AND available = 1 ORDER BY premium_multiplier ASC NULLS LAST",
|
|
62934
|
+
args: [tier]
|
|
62935
|
+
});
|
|
62936
|
+
return result.rows.map(rowToModelPricing);
|
|
62937
|
+
}
|
|
62938
|
+
async function getCheapestInTier(tier) {
|
|
62939
|
+
const models = await getModelsForTier(tier);
|
|
62940
|
+
return models[0] ?? null;
|
|
62941
|
+
}
|
|
62942
|
+
async function getModelPricing(modelId) {
|
|
62943
|
+
const db = await getDatabase();
|
|
62944
|
+
const result = await db.execute({
|
|
62945
|
+
sql: "SELECT * FROM model_pricing WHERE id = ?",
|
|
62946
|
+
args: [modelId]
|
|
62947
|
+
});
|
|
62948
|
+
if (result.rows.length > 0) {
|
|
62949
|
+
return rowToModelPricing(result.rows[0]);
|
|
62950
|
+
}
|
|
62951
|
+
const allModels = await db.execute(
|
|
62952
|
+
"SELECT * FROM model_pricing WHERE token_input_multiplier IS NOT NULL ORDER BY length(id) DESC"
|
|
62953
|
+
);
|
|
62954
|
+
for (const row of allModels.rows) {
|
|
62955
|
+
const storedId = asString(row.id);
|
|
62956
|
+
if (modelId.startsWith(storedId) || storedId.startsWith(modelId)) {
|
|
62957
|
+
return rowToModelPricing(row);
|
|
62958
|
+
}
|
|
62959
|
+
}
|
|
62960
|
+
return null;
|
|
62961
|
+
}
|
|
62962
|
+
async function getCheapestAvailableModel() {
|
|
62963
|
+
const tiers = ["trivial", "fast", "standard", "premium", "ultra"];
|
|
62964
|
+
for (const tier of tiers) {
|
|
62965
|
+
const model = await getCheapestInTier(tier);
|
|
62966
|
+
if (model) {
|
|
62967
|
+
return model;
|
|
62968
|
+
}
|
|
62969
|
+
}
|
|
62970
|
+
return null;
|
|
62971
|
+
}
|
|
62972
|
+
function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
|
|
62973
|
+
if (inputMultiplier === null || outputMultiplier === null) {
|
|
62974
|
+
return 0;
|
|
62975
|
+
}
|
|
62976
|
+
const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
|
|
62977
|
+
return tokenUnits * TOKEN_UNIT_PRICE;
|
|
62978
|
+
}
|
|
62979
|
+
function getNextTierUp(tier) {
|
|
62980
|
+
const order = ["trivial", "fast", "standard", "premium", "ultra"];
|
|
62981
|
+
const idx = order.indexOf(tier);
|
|
62982
|
+
if (idx < 0 || idx >= order.length - 1) {
|
|
62983
|
+
return null;
|
|
62984
|
+
}
|
|
62985
|
+
return order[idx + 1];
|
|
62986
|
+
}
|
|
62987
|
+
async function upsertModel(db, model) {
|
|
62988
|
+
await db.execute({
|
|
62989
|
+
sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
|
|
62990
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
62991
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
62992
|
+
display_name = excluded.display_name,
|
|
62993
|
+
premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
|
|
62994
|
+
token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
|
|
62995
|
+
token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
|
|
62996
|
+
cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
|
|
62997
|
+
tier = excluded.tier,
|
|
62998
|
+
available = excluded.available,
|
|
62999
|
+
updated_at = excluded.updated_at`,
|
|
63000
|
+
args: [
|
|
63001
|
+
model.id,
|
|
63002
|
+
model.displayName,
|
|
63003
|
+
model.premiumMultiplier,
|
|
63004
|
+
model.tokenInputMultiplier,
|
|
63005
|
+
model.tokenOutputMultiplier,
|
|
63006
|
+
model.cachedInputMultiplier,
|
|
63007
|
+
model.tier,
|
|
63008
|
+
model.available ? 1 : 0,
|
|
63009
|
+
model.updatedAt
|
|
63010
|
+
]
|
|
63011
|
+
});
|
|
63012
|
+
}
|
|
63013
|
+
function rowToModelPricing(row) {
|
|
63014
|
+
return {
|
|
63015
|
+
id: asString(row.id),
|
|
63016
|
+
displayName: asString(row.display_name),
|
|
63017
|
+
premiumMultiplier: asNullableNumber(row.premium_multiplier),
|
|
63018
|
+
tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
|
|
63019
|
+
tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
|
|
63020
|
+
cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
|
|
63021
|
+
tier: asString(row.tier),
|
|
63022
|
+
available: asNumber(row.available) === 1,
|
|
63023
|
+
updatedAt: asString(row.updated_at)
|
|
63024
|
+
};
|
|
63025
|
+
}
|
|
63026
|
+
function findClosestKey(map2, targetKey) {
|
|
63027
|
+
for (const [key, value] of map2) {
|
|
63028
|
+
if (key.includes(targetKey) || targetKey.includes(key)) {
|
|
63029
|
+
return value;
|
|
63030
|
+
}
|
|
63031
|
+
}
|
|
63032
|
+
return void 0;
|
|
63033
|
+
}
|
|
63034
|
+
var init_registry = __esm({
|
|
63035
|
+
"packages/daemon/src/models/registry.ts"() {
|
|
63036
|
+
"use strict";
|
|
63037
|
+
init_db();
|
|
63038
|
+
init_catalog();
|
|
63039
|
+
init_pricing_scraper();
|
|
63040
|
+
init_types();
|
|
63041
|
+
}
|
|
63042
|
+
});
|
|
63043
|
+
|
|
62209
63044
|
// node_modules/grammy/out/filter.js
|
|
62210
63045
|
var require_filter = __commonJS({
|
|
62211
63046
|
"node_modules/grammy/out/filter.js"(exports2) {
|
|
@@ -76932,344 +77767,11 @@ var TypedEventBus = class extends EventEmitter {
|
|
|
76932
77767
|
};
|
|
76933
77768
|
var eventBus = new TypedEventBus();
|
|
76934
77769
|
|
|
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
|
-
version: 5,
|
|
77136
|
-
name: "add-squad-soft-delete",
|
|
77137
|
-
statements: ["ALTER TABLE squads ADD COLUMN deleted_at TEXT"]
|
|
77138
|
-
}
|
|
77139
|
-
];
|
|
77140
|
-
var client = null;
|
|
77141
|
-
var initPromise = null;
|
|
77142
|
-
var defaultConnectionOptions = {};
|
|
77143
|
-
var generateId = () => crypto.randomUUID();
|
|
77144
|
-
var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
77145
|
-
function asString(value) {
|
|
77146
|
-
if (typeof value === "string") {
|
|
77147
|
-
return value;
|
|
77148
|
-
}
|
|
77149
|
-
if (value === null || value === void 0) {
|
|
77150
|
-
throw new Error("Expected string value but received null or undefined");
|
|
77151
|
-
}
|
|
77152
|
-
return String(value);
|
|
77153
|
-
}
|
|
77154
|
-
function asNullableString(value) {
|
|
77155
|
-
if (value === null || value === void 0) {
|
|
77156
|
-
return null;
|
|
77157
|
-
}
|
|
77158
|
-
return typeof value === "string" ? value : String(value);
|
|
77159
|
-
}
|
|
77160
|
-
function asNumber(value) {
|
|
77161
|
-
if (typeof value === "number") {
|
|
77162
|
-
return value;
|
|
77163
|
-
}
|
|
77164
|
-
if (typeof value === "bigint") {
|
|
77165
|
-
return Number(value);
|
|
77166
|
-
}
|
|
77167
|
-
if (typeof value === "string") {
|
|
77168
|
-
return Number(value);
|
|
77169
|
-
}
|
|
77170
|
-
throw new Error(`Expected numeric value but received ${typeof value}`);
|
|
77171
|
-
}
|
|
77172
|
-
function asNullableNumber(value) {
|
|
77173
|
-
if (value === null || value === void 0) {
|
|
77174
|
-
return null;
|
|
77175
|
-
}
|
|
77176
|
-
return asNumber(value);
|
|
77177
|
-
}
|
|
77178
|
-
function asBoolean(value) {
|
|
77179
|
-
return asNumber(value) === 1;
|
|
77180
|
-
}
|
|
77181
|
-
function toSqliteBoolean(value) {
|
|
77182
|
-
return value ? 1 : 0;
|
|
77183
|
-
}
|
|
77184
|
-
function parseJson(value) {
|
|
77185
|
-
if (typeof value !== "string") {
|
|
77186
|
-
return value;
|
|
77187
|
-
}
|
|
77188
|
-
return JSON.parse(value);
|
|
77189
|
-
}
|
|
77190
|
-
function serializeJson(value) {
|
|
77191
|
-
return JSON.stringify(value);
|
|
77192
|
-
}
|
|
77193
|
-
async function initDatabase() {
|
|
77194
|
-
if (client && !client.closed) {
|
|
77195
|
-
return client;
|
|
77196
|
-
}
|
|
77197
|
-
if (initPromise) {
|
|
77198
|
-
return initPromise;
|
|
77199
|
-
}
|
|
77200
|
-
initPromise = initializeDatabase(defaultConnectionOptions);
|
|
77201
|
-
try {
|
|
77202
|
-
client = await initPromise;
|
|
77203
|
-
return client;
|
|
77204
|
-
} finally {
|
|
77205
|
-
initPromise = null;
|
|
77206
|
-
}
|
|
77207
|
-
}
|
|
77208
|
-
async function getDatabase() {
|
|
77209
|
-
return initDatabase();
|
|
77210
|
-
}
|
|
77211
|
-
async function initializeDatabase(options2 = {}) {
|
|
77212
|
-
const url2 = await resolveDatabaseUrl(options2);
|
|
77213
|
-
const databaseClient = createClient({ url: url2 });
|
|
77214
|
-
await databaseClient.execute("PRAGMA foreign_keys = ON");
|
|
77215
|
-
await databaseClient.execute("PRAGMA journal_mode = WAL");
|
|
77216
|
-
await ensureMigrationTable(databaseClient);
|
|
77217
|
-
await applyMigrations(databaseClient);
|
|
77218
|
-
return databaseClient;
|
|
77219
|
-
}
|
|
77220
|
-
async function resolveDatabaseUrl(options2) {
|
|
77221
|
-
if (options2.url) {
|
|
77222
|
-
return options2.url;
|
|
77223
|
-
}
|
|
77224
|
-
const databasePath = options2.path ?? DB_PATH;
|
|
77225
|
-
await mkdir(dirname(databasePath), { recursive: true });
|
|
77226
|
-
return pathToFileURL(databasePath).href;
|
|
77227
|
-
}
|
|
77228
|
-
async function ensureMigrationTable(databaseClient) {
|
|
77229
|
-
await databaseClient.executeMultiple(`
|
|
77230
|
-
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
77231
|
-
version INTEGER PRIMARY KEY,
|
|
77232
|
-
name TEXT NOT NULL,
|
|
77233
|
-
applied_at TEXT NOT NULL
|
|
77234
|
-
);
|
|
77235
|
-
`);
|
|
77236
|
-
}
|
|
77237
|
-
async function applyMigrations(databaseClient) {
|
|
77238
|
-
const appliedVersions = await getAppliedVersions(databaseClient);
|
|
77239
|
-
for (const migration of MIGRATIONS) {
|
|
77240
|
-
if (appliedVersions.has(migration.version)) {
|
|
77241
|
-
continue;
|
|
77242
|
-
}
|
|
77243
|
-
const transaction = await databaseClient.transaction("write");
|
|
77244
|
-
try {
|
|
77245
|
-
for (const statement of migration.statements) {
|
|
77246
|
-
await transaction.execute(statement);
|
|
77247
|
-
}
|
|
77248
|
-
await transaction.execute({
|
|
77249
|
-
sql: "INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)",
|
|
77250
|
-
args: [migration.version, migration.name, nowIso()]
|
|
77251
|
-
});
|
|
77252
|
-
await transaction.commit();
|
|
77253
|
-
} catch (error51) {
|
|
77254
|
-
if (!transaction.closed) {
|
|
77255
|
-
await transaction.rollback();
|
|
77256
|
-
}
|
|
77257
|
-
throw error51;
|
|
77258
|
-
} finally {
|
|
77259
|
-
if (!transaction.closed) {
|
|
77260
|
-
transaction.close();
|
|
77261
|
-
}
|
|
77262
|
-
}
|
|
77263
|
-
}
|
|
77264
|
-
}
|
|
77265
|
-
async function getAppliedVersions(databaseClient) {
|
|
77266
|
-
const result = await databaseClient.execute(
|
|
77267
|
-
"SELECT version FROM schema_migrations ORDER BY version ASC"
|
|
77268
|
-
);
|
|
77269
|
-
return new Set(result.rows.map((row) => asNumber(row.version)));
|
|
77270
|
-
}
|
|
77770
|
+
// packages/daemon/src/store/index.ts
|
|
77771
|
+
init_db();
|
|
77271
77772
|
|
|
77272
77773
|
// packages/daemon/src/store/squads.ts
|
|
77774
|
+
init_db();
|
|
77273
77775
|
async function createSquad(data, db) {
|
|
77274
77776
|
const database = db ?? await getDatabase();
|
|
77275
77777
|
const timestamp = nowIso();
|
|
@@ -77525,6 +78027,7 @@ function mapMember(row) {
|
|
|
77525
78027
|
}
|
|
77526
78028
|
|
|
77527
78029
|
// packages/daemon/src/store/conversations.ts
|
|
78030
|
+
init_db();
|
|
77528
78031
|
async function createConversation(source, title, db) {
|
|
77529
78032
|
const database = db ?? await getDatabase();
|
|
77530
78033
|
const createdAt = nowIso();
|
|
@@ -77643,6 +78146,7 @@ function mapMessage(row) {
|
|
|
77643
78146
|
}
|
|
77644
78147
|
|
|
77645
78148
|
// packages/daemon/src/store/inbox.ts
|
|
78149
|
+
init_db();
|
|
77646
78150
|
async function createInboxItem(data, db) {
|
|
77647
78151
|
const database = db ?? await getDatabase();
|
|
77648
78152
|
const timestamp = nowIso();
|
|
@@ -77721,6 +78225,14 @@ async function markRead(id, db) {
|
|
|
77721
78225
|
}
|
|
77722
78226
|
return getInboxItem(id, database);
|
|
77723
78227
|
}
|
|
78228
|
+
async function deleteInboxItem(id, db) {
|
|
78229
|
+
const database = db ?? await getDatabase();
|
|
78230
|
+
const result = await database.execute({
|
|
78231
|
+
sql: "DELETE FROM inbox WHERE id = ?",
|
|
78232
|
+
args: [id]
|
|
78233
|
+
});
|
|
78234
|
+
return (result.rowsAffected ?? 0) > 0;
|
|
78235
|
+
}
|
|
77724
78236
|
function mapInboxItem(row) {
|
|
77725
78237
|
return {
|
|
77726
78238
|
id: asString(row.id),
|
|
@@ -77738,6 +78250,7 @@ function mapInboxItem(row) {
|
|
|
77738
78250
|
|
|
77739
78251
|
// packages/daemon/src/store/schedules.ts
|
|
77740
78252
|
var import_cron_parser = __toESM(require_dist(), 1);
|
|
78253
|
+
init_db();
|
|
77741
78254
|
var parseExpression = import_cron_parser.CronExpressionParser.parse;
|
|
77742
78255
|
async function createSchedule(data, db) {
|
|
77743
78256
|
const database = db ?? await getDatabase();
|
|
@@ -77876,6 +78389,7 @@ function mapSchedule(row) {
|
|
|
77876
78389
|
}
|
|
77877
78390
|
|
|
77878
78391
|
// packages/daemon/src/store/token-usage.ts
|
|
78392
|
+
init_db();
|
|
77879
78393
|
async function recordUsage(data, db) {
|
|
77880
78394
|
const database = db ?? await getDatabase();
|
|
77881
78395
|
const usage = {
|
|
@@ -78005,6 +78519,7 @@ async function getUsageRecords(params = {}, db) {
|
|
|
78005
78519
|
}
|
|
78006
78520
|
|
|
78007
78521
|
// packages/daemon/src/store/activity.ts
|
|
78522
|
+
init_db();
|
|
78008
78523
|
async function logActivity(data, db) {
|
|
78009
78524
|
const database = db ?? await getDatabase();
|
|
78010
78525
|
const activity = {
|
|
@@ -78060,6 +78575,7 @@ function mapActivity(row) {
|
|
|
78060
78575
|
}
|
|
78061
78576
|
|
|
78062
78577
|
// packages/daemon/src/store/agent-history.ts
|
|
78578
|
+
init_db();
|
|
78063
78579
|
async function appendHistory(agentId, squadId, content, db) {
|
|
78064
78580
|
const database = db ?? await getDatabase();
|
|
78065
78581
|
const entry = {
|
|
@@ -78095,6 +78611,7 @@ function mapAgentHistory(row) {
|
|
|
78095
78611
|
}
|
|
78096
78612
|
|
|
78097
78613
|
// packages/daemon/src/store/objectives.ts
|
|
78614
|
+
init_db();
|
|
78098
78615
|
async function createObjective(squadId, description, db) {
|
|
78099
78616
|
const database = db ?? await getDatabase();
|
|
78100
78617
|
const timestamp = nowIso();
|
|
@@ -78287,6 +78804,7 @@ function mapTask(row) {
|
|
|
78287
78804
|
}
|
|
78288
78805
|
|
|
78289
78806
|
// packages/daemon/src/store/instances.ts
|
|
78807
|
+
init_db();
|
|
78290
78808
|
async function createInstance(input, db) {
|
|
78291
78809
|
const database = db ?? await getDatabase();
|
|
78292
78810
|
const instance = {
|
|
@@ -78688,6 +79206,21 @@ router3.put("/api/inbox/:id/read", async (req, res) => {
|
|
|
78688
79206
|
});
|
|
78689
79207
|
}
|
|
78690
79208
|
});
|
|
79209
|
+
router3.delete("/api/inbox/:id", async (req, res) => {
|
|
79210
|
+
try {
|
|
79211
|
+
const deleted = await deleteInboxItem(req.params.id);
|
|
79212
|
+
if (!deleted) {
|
|
79213
|
+
res.status(404).json({ error: "Inbox item not found" });
|
|
79214
|
+
return;
|
|
79215
|
+
}
|
|
79216
|
+
res.status(204).end();
|
|
79217
|
+
} catch (error51) {
|
|
79218
|
+
res.status(500).json({
|
|
79219
|
+
error: "Failed to delete inbox item",
|
|
79220
|
+
details: error51 instanceof Error ? error51.message : "Unknown error"
|
|
79221
|
+
});
|
|
79222
|
+
}
|
|
79223
|
+
});
|
|
78691
79224
|
function parsePositiveInteger3(value, fallback) {
|
|
78692
79225
|
const parsed = Number(value);
|
|
78693
79226
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -79580,6 +80113,7 @@ function emitInstanceEvent(event, instance) {
|
|
|
79580
80113
|
}
|
|
79581
80114
|
|
|
79582
80115
|
// packages/daemon/src/api/routes/squads.ts
|
|
80116
|
+
init_db();
|
|
79583
80117
|
var router7 = (0, import_express7.Router)();
|
|
79584
80118
|
var DEFAULT_CONFIG = {
|
|
79585
80119
|
prMode: "draft-pr",
|
|
@@ -80838,432 +81372,10 @@ function ensureDataDirectories() {
|
|
|
80838
81372
|
}
|
|
80839
81373
|
}
|
|
80840
81374
|
|
|
80841
|
-
// packages/daemon/src/models/
|
|
80842
|
-
|
|
80843
|
-
|
|
80844
|
-
|
|
80845
|
-
const envToken = process.env.GITHUB_TOKEN?.trim();
|
|
80846
|
-
if (envToken && envToken.length > 0) {
|
|
80847
|
-
return envToken;
|
|
80848
|
-
}
|
|
80849
|
-
try {
|
|
80850
|
-
const token = execFileSync("gh", ["auth", "token"], {
|
|
80851
|
-
encoding: "utf8",
|
|
80852
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
80853
|
-
}).trim();
|
|
80854
|
-
return token.length > 0 ? token : void 0;
|
|
80855
|
-
} catch {
|
|
80856
|
-
return void 0;
|
|
80857
|
-
}
|
|
80858
|
-
}
|
|
80859
|
-
async function fetchModelCatalog() {
|
|
80860
|
-
const token = resolveGitHubToken();
|
|
80861
|
-
if (!token) {
|
|
80862
|
-
throw new Error("No GitHub token available for model catalog fetch");
|
|
80863
|
-
}
|
|
80864
|
-
const response = await fetch(CATALOG_URL, {
|
|
80865
|
-
headers: {
|
|
80866
|
-
Accept: "application/json",
|
|
80867
|
-
Authorization: `Bearer ${token}`,
|
|
80868
|
-
"X-GitHub-Api-Version": "2024-12-01"
|
|
80869
|
-
}
|
|
80870
|
-
});
|
|
80871
|
-
if (!response.ok) {
|
|
80872
|
-
throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
|
|
80873
|
-
}
|
|
80874
|
-
const data = await response.json();
|
|
80875
|
-
if (!Array.isArray(data)) {
|
|
80876
|
-
throw new Error("Model catalog response is not an array");
|
|
80877
|
-
}
|
|
80878
|
-
const models = [];
|
|
80879
|
-
for (const entry of data) {
|
|
80880
|
-
const id = entry.id ?? entry.name;
|
|
80881
|
-
const displayName = entry.friendly_name ?? entry.name ?? id;
|
|
80882
|
-
if (id && typeof id === "string") {
|
|
80883
|
-
models.push({ id, displayName: displayName ?? id });
|
|
80884
|
-
}
|
|
80885
|
-
}
|
|
80886
|
-
return models;
|
|
80887
|
-
}
|
|
80888
|
-
|
|
80889
|
-
// packages/daemon/src/models/pricing-scraper.ts
|
|
80890
|
-
var TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
|
|
80891
|
-
var PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
|
|
80892
|
-
var COPILOT_PRICING_URL = "https://docs.github.com/api/article/body?pathname=/en/copilot/reference/copilot-billing/models-and-pricing";
|
|
80893
|
-
var PRICE_TO_MULTIPLIER = 0.1;
|
|
80894
|
-
async function scrapeTokenUnitPricing() {
|
|
80895
|
-
const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
|
|
80896
|
-
return parseTokenUnitTable(html);
|
|
80897
|
-
}
|
|
80898
|
-
async function scrapeCopilotPricing() {
|
|
80899
|
-
const markdown = await fetchMarkdown(COPILOT_PRICING_URL);
|
|
80900
|
-
return parseCopilotPricingMarkdown(markdown);
|
|
80901
|
-
}
|
|
80902
|
-
async function scrapePremiumRequestPricing() {
|
|
80903
|
-
const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
|
|
80904
|
-
return parsePremiumMultiplierTable(html);
|
|
80905
|
-
}
|
|
80906
|
-
async function fetchPage(url2) {
|
|
80907
|
-
const response = await fetch(url2, {
|
|
80908
|
-
headers: {
|
|
80909
|
-
Accept: "text/html",
|
|
80910
|
-
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
80911
|
-
},
|
|
80912
|
-
redirect: "follow"
|
|
80913
|
-
});
|
|
80914
|
-
if (!response.ok) {
|
|
80915
|
-
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
80916
|
-
}
|
|
80917
|
-
return response.text();
|
|
80918
|
-
}
|
|
80919
|
-
async function fetchMarkdown(url2) {
|
|
80920
|
-
const response = await fetch(url2, {
|
|
80921
|
-
headers: {
|
|
80922
|
-
Accept: "text/markdown, text/plain, */*",
|
|
80923
|
-
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
80924
|
-
},
|
|
80925
|
-
redirect: "follow"
|
|
80926
|
-
});
|
|
80927
|
-
if (!response.ok) {
|
|
80928
|
-
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
80929
|
-
}
|
|
80930
|
-
return response.text();
|
|
80931
|
-
}
|
|
80932
|
-
function parseTokenUnitTable(html) {
|
|
80933
|
-
const results = [];
|
|
80934
|
-
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
|
|
80935
|
-
for (const match of html.matchAll(tableRowPattern)) {
|
|
80936
|
-
const modelName = cleanCellText(match[1]);
|
|
80937
|
-
const inputMultiplier = Number.parseFloat(match[2]);
|
|
80938
|
-
const cachedInput = match[3].trim().toLowerCase();
|
|
80939
|
-
const outputMultiplier = Number.parseFloat(match[4]);
|
|
80940
|
-
if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
|
|
80941
|
-
results.push({
|
|
80942
|
-
modelName,
|
|
80943
|
-
inputMultiplier,
|
|
80944
|
-
cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
|
|
80945
|
-
outputMultiplier
|
|
80946
|
-
});
|
|
80947
|
-
}
|
|
80948
|
-
}
|
|
80949
|
-
return results;
|
|
80950
|
-
}
|
|
80951
|
-
function parsePremiumMultiplierTable(html) {
|
|
80952
|
-
const results = [];
|
|
80953
|
-
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
|
|
80954
|
-
for (const match of html.matchAll(tableRowPattern)) {
|
|
80955
|
-
const modelName = cleanCellText(match[1]);
|
|
80956
|
-
const multiplier = Number.parseFloat(match[2]);
|
|
80957
|
-
if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
|
|
80958
|
-
results.push({ modelName, multiplier });
|
|
80959
|
-
}
|
|
80960
|
-
}
|
|
80961
|
-
return results;
|
|
80962
|
-
}
|
|
80963
|
-
function cleanCellText(text) {
|
|
80964
|
-
return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
|
|
80965
|
-
}
|
|
80966
|
-
function parseCopilotPricingMarkdown(markdown) {
|
|
80967
|
-
const results = [];
|
|
80968
|
-
const seenModels = /* @__PURE__ */ new Set();
|
|
80969
|
-
const lines = markdown.split("\n");
|
|
80970
|
-
let columnIndices = null;
|
|
80971
|
-
for (const line of lines) {
|
|
80972
|
-
const trimmed = line.trim();
|
|
80973
|
-
if (!trimmed.startsWith("|")) continue;
|
|
80974
|
-
if (/\|\s*Model\s/i.test(trimmed)) {
|
|
80975
|
-
columnIndices = parseHeaderColumns(trimmed);
|
|
80976
|
-
continue;
|
|
80977
|
-
}
|
|
80978
|
-
if (/^[\s|:-]+$/.test(trimmed) || !columnIndices) continue;
|
|
80979
|
-
const entry = parseDataRow(trimmed, columnIndices);
|
|
80980
|
-
if (entry && !seenModels.has(entry.modelName.toLowerCase())) {
|
|
80981
|
-
seenModels.add(entry.modelName.toLowerCase());
|
|
80982
|
-
results.push(entry);
|
|
80983
|
-
}
|
|
80984
|
-
}
|
|
80985
|
-
return results;
|
|
80986
|
-
}
|
|
80987
|
-
function parseDataRow(line, columnIndices) {
|
|
80988
|
-
const cells = splitMarkdownRow(line);
|
|
80989
|
-
if (cells.length <= columnIndices.output) return null;
|
|
80990
|
-
const modelName = cells[columnIndices.model].trim();
|
|
80991
|
-
const inputPrice = parseDollarValue(cells[columnIndices.input]);
|
|
80992
|
-
const cachedPrice = columnIndices.cached >= 0 ? parseDollarValue(cells[columnIndices.cached]) : null;
|
|
80993
|
-
const outputPrice = parseDollarValue(cells[columnIndices.output]);
|
|
80994
|
-
if (!modelName || inputPrice === null || outputPrice === null) return null;
|
|
80995
|
-
return {
|
|
80996
|
-
modelName,
|
|
80997
|
-
inputMultiplier: inputPrice * PRICE_TO_MULTIPLIER,
|
|
80998
|
-
cachedInputMultiplier: cachedPrice !== null ? cachedPrice * PRICE_TO_MULTIPLIER : null,
|
|
80999
|
-
outputMultiplier: outputPrice * PRICE_TO_MULTIPLIER
|
|
81000
|
-
};
|
|
81001
|
-
}
|
|
81002
|
-
function parseHeaderColumns(headerLine) {
|
|
81003
|
-
const cells = splitMarkdownRow(headerLine).map((c) => c.trim().toLowerCase());
|
|
81004
|
-
const model = cells.findIndex((c) => c === "model" || c === "model name");
|
|
81005
|
-
const input = cells.findIndex(
|
|
81006
|
-
(c) => (c === "input" || c === "input multiplier") && !c.includes("cached")
|
|
81007
|
-
);
|
|
81008
|
-
const cached2 = cells.findIndex(
|
|
81009
|
-
(c) => c.includes("cached input") || c === "cached input multiplier"
|
|
81010
|
-
);
|
|
81011
|
-
const output = cells.findIndex(
|
|
81012
|
-
(c) => (c === "output" || c === "output multiplier") && !c.includes("cached")
|
|
81013
|
-
);
|
|
81014
|
-
if (model < 0 || input < 0 || output < 0) {
|
|
81015
|
-
return null;
|
|
81016
|
-
}
|
|
81017
|
-
return { model, input, cached: cached2 >= 0 ? cached2 : -1, output };
|
|
81018
|
-
}
|
|
81019
|
-
function splitMarkdownRow(line) {
|
|
81020
|
-
const parts = line.split("|");
|
|
81021
|
-
if (parts[0].trim() === "") parts.shift();
|
|
81022
|
-
if (parts[parts.length - 1]?.trim() === "") parts.pop();
|
|
81023
|
-
return parts;
|
|
81024
|
-
}
|
|
81025
|
-
function parseDollarValue(cell) {
|
|
81026
|
-
if (!cell) return null;
|
|
81027
|
-
const cleaned = cell.trim().replace(/[$,]/g, "");
|
|
81028
|
-
if (cleaned.toLowerCase() === "n/a" || cleaned === "" || cleaned === "-") {
|
|
81029
|
-
return null;
|
|
81030
|
-
}
|
|
81031
|
-
const value = Number.parseFloat(cleaned);
|
|
81032
|
-
return Number.isNaN(value) ? null : value;
|
|
81033
|
-
}
|
|
81034
|
-
|
|
81035
|
-
// packages/daemon/src/models/types.ts
|
|
81036
|
-
var TIER_RANGES = {
|
|
81037
|
-
trivial: { min: 0, max: 0.33 },
|
|
81038
|
-
fast: { min: 0.34, max: 1 },
|
|
81039
|
-
standard: { min: 1.1, max: 5 },
|
|
81040
|
-
premium: { min: 5.1, max: 15 },
|
|
81041
|
-
ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
|
|
81042
|
-
};
|
|
81043
|
-
function computeTierFromMultiplier(premiumMultiplier) {
|
|
81044
|
-
if (premiumMultiplier === null) {
|
|
81045
|
-
return "standard";
|
|
81046
|
-
}
|
|
81047
|
-
for (const [tier, range] of Object.entries(TIER_RANGES)) {
|
|
81048
|
-
if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
|
|
81049
|
-
return tier;
|
|
81050
|
-
}
|
|
81051
|
-
}
|
|
81052
|
-
return "ultra";
|
|
81053
|
-
}
|
|
81054
|
-
var TOKEN_UNIT_PRICE = 1e-5;
|
|
81055
|
-
|
|
81056
|
-
// packages/daemon/src/models/registry.ts
|
|
81057
|
-
function normalizeModelName(name) {
|
|
81058
|
-
return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
|
|
81059
|
-
}
|
|
81060
|
-
async function fetchCatalogIntoMap(modelMap, result, logger2) {
|
|
81061
|
-
try {
|
|
81062
|
-
const catalogModels = await fetchModelCatalog();
|
|
81063
|
-
result.catalogFetched = true;
|
|
81064
|
-
for (const m of catalogModels) {
|
|
81065
|
-
const key = normalizeModelName(m.id);
|
|
81066
|
-
modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
|
|
81067
|
-
}
|
|
81068
|
-
} catch (error51) {
|
|
81069
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81070
|
-
result.errors.push(`Catalog fetch failed: ${msg}`);
|
|
81071
|
-
logger2?.warn(`Model catalog fetch failed: ${msg}`);
|
|
81072
|
-
}
|
|
81073
|
-
}
|
|
81074
|
-
async function scrapeTokenPricingIntoMap(modelMap, result, logger2) {
|
|
81075
|
-
try {
|
|
81076
|
-
const tokenPricing = await scrapeTokenUnitPricing();
|
|
81077
|
-
result.tokenPricingScraped = true;
|
|
81078
|
-
for (const tp of tokenPricing) {
|
|
81079
|
-
const key = normalizeModelName(tp.modelName);
|
|
81080
|
-
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
81081
|
-
if (existing) {
|
|
81082
|
-
existing.tokenInputMultiplier = tp.inputMultiplier;
|
|
81083
|
-
existing.tokenOutputMultiplier = tp.outputMultiplier;
|
|
81084
|
-
existing.cachedInputMultiplier = tp.cachedInputMultiplier;
|
|
81085
|
-
} else {
|
|
81086
|
-
modelMap.set(key, {
|
|
81087
|
-
id: key,
|
|
81088
|
-
displayName: tp.modelName,
|
|
81089
|
-
tokenInputMultiplier: tp.inputMultiplier,
|
|
81090
|
-
tokenOutputMultiplier: tp.outputMultiplier,
|
|
81091
|
-
cachedInputMultiplier: tp.cachedInputMultiplier,
|
|
81092
|
-
available: true
|
|
81093
|
-
});
|
|
81094
|
-
}
|
|
81095
|
-
}
|
|
81096
|
-
} catch (error51) {
|
|
81097
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81098
|
-
result.errors.push(`Token pricing scrape failed: ${msg}`);
|
|
81099
|
-
logger2?.warn(`Token pricing scrape failed: ${msg}`);
|
|
81100
|
-
}
|
|
81101
|
-
}
|
|
81102
|
-
async function scrapePremiumPricingIntoMap(modelMap, result, logger2) {
|
|
81103
|
-
try {
|
|
81104
|
-
const premiumPricing = await scrapePremiumRequestPricing();
|
|
81105
|
-
result.premiumPricingScraped = true;
|
|
81106
|
-
for (const pp of premiumPricing) {
|
|
81107
|
-
const key = normalizeModelName(pp.modelName);
|
|
81108
|
-
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
81109
|
-
if (existing) {
|
|
81110
|
-
existing.premiumMultiplier = pp.multiplier;
|
|
81111
|
-
} else {
|
|
81112
|
-
modelMap.set(key, {
|
|
81113
|
-
id: key,
|
|
81114
|
-
displayName: pp.modelName,
|
|
81115
|
-
premiumMultiplier: pp.multiplier,
|
|
81116
|
-
available: true
|
|
81117
|
-
});
|
|
81118
|
-
}
|
|
81119
|
-
}
|
|
81120
|
-
} catch (error51) {
|
|
81121
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81122
|
-
result.errors.push(`Premium pricing scrape failed: ${msg}`);
|
|
81123
|
-
logger2?.warn(`Premium pricing scrape failed: ${msg}`);
|
|
81124
|
-
}
|
|
81125
|
-
}
|
|
81126
|
-
async function scrapeCopilotPricingIntoMap(modelMap, result, logger2) {
|
|
81127
|
-
try {
|
|
81128
|
-
const copilotPricing = await scrapeCopilotPricing();
|
|
81129
|
-
result.copilotPricingScraped = true;
|
|
81130
|
-
for (const cp of copilotPricing) {
|
|
81131
|
-
const key = normalizeModelName(cp.modelName);
|
|
81132
|
-
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
81133
|
-
if (existing) {
|
|
81134
|
-
existing.tokenInputMultiplier = cp.inputMultiplier;
|
|
81135
|
-
existing.tokenOutputMultiplier = cp.outputMultiplier;
|
|
81136
|
-
if (cp.cachedInputMultiplier !== null) {
|
|
81137
|
-
existing.cachedInputMultiplier = cp.cachedInputMultiplier;
|
|
81138
|
-
}
|
|
81139
|
-
} else {
|
|
81140
|
-
modelMap.set(key, {
|
|
81141
|
-
id: key,
|
|
81142
|
-
displayName: cp.modelName,
|
|
81143
|
-
tokenInputMultiplier: cp.inputMultiplier,
|
|
81144
|
-
tokenOutputMultiplier: cp.outputMultiplier,
|
|
81145
|
-
cachedInputMultiplier: cp.cachedInputMultiplier,
|
|
81146
|
-
available: true
|
|
81147
|
-
});
|
|
81148
|
-
}
|
|
81149
|
-
}
|
|
81150
|
-
} catch (error51) {
|
|
81151
|
-
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
81152
|
-
result.errors.push(`Copilot pricing scrape failed: ${msg}`);
|
|
81153
|
-
logger2?.warn(`Copilot pricing scrape failed: ${msg}`);
|
|
81154
|
-
}
|
|
81155
|
-
}
|
|
81156
|
-
async function refreshModelPricing(logger2) {
|
|
81157
|
-
const result = {
|
|
81158
|
-
modelsUpdated: 0,
|
|
81159
|
-
catalogFetched: false,
|
|
81160
|
-
tokenPricingScraped: false,
|
|
81161
|
-
premiumPricingScraped: false,
|
|
81162
|
-
copilotPricingScraped: false,
|
|
81163
|
-
errors: []
|
|
81164
|
-
};
|
|
81165
|
-
const modelMap = /* @__PURE__ */ new Map();
|
|
81166
|
-
await fetchCatalogIntoMap(modelMap, result, logger2);
|
|
81167
|
-
await scrapeTokenPricingIntoMap(modelMap, result, logger2);
|
|
81168
|
-
await scrapeCopilotPricingIntoMap(modelMap, result, logger2);
|
|
81169
|
-
await scrapePremiumPricingIntoMap(modelMap, result, logger2);
|
|
81170
|
-
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped && !result.copilotPricingScraped) {
|
|
81171
|
-
logger2?.warn("All pricing sources failed, no models available");
|
|
81172
|
-
return result;
|
|
81173
|
-
}
|
|
81174
|
-
const db = await getDatabase();
|
|
81175
|
-
const now = nowIso();
|
|
81176
|
-
for (const model of modelMap.values()) {
|
|
81177
|
-
const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
|
|
81178
|
-
await upsertModel(db, {
|
|
81179
|
-
id: model.id,
|
|
81180
|
-
displayName: model.displayName,
|
|
81181
|
-
premiumMultiplier: model.premiumMultiplier ?? null,
|
|
81182
|
-
tokenInputMultiplier: model.tokenInputMultiplier ?? null,
|
|
81183
|
-
tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
|
|
81184
|
-
cachedInputMultiplier: model.cachedInputMultiplier ?? null,
|
|
81185
|
-
tier,
|
|
81186
|
-
available: model.available ?? true,
|
|
81187
|
-
updatedAt: now
|
|
81188
|
-
});
|
|
81189
|
-
result.modelsUpdated++;
|
|
81190
|
-
}
|
|
81191
|
-
return result;
|
|
81192
|
-
}
|
|
81193
|
-
async function getModelPricing(modelId) {
|
|
81194
|
-
const db = await getDatabase();
|
|
81195
|
-
const result = await db.execute({
|
|
81196
|
-
sql: "SELECT * FROM model_pricing WHERE id = ?",
|
|
81197
|
-
args: [modelId]
|
|
81198
|
-
});
|
|
81199
|
-
if (result.rows.length > 0) {
|
|
81200
|
-
return rowToModelPricing(result.rows[0]);
|
|
81201
|
-
}
|
|
81202
|
-
const allModels = await db.execute(
|
|
81203
|
-
"SELECT * FROM model_pricing WHERE token_input_multiplier IS NOT NULL ORDER BY length(id) DESC"
|
|
81204
|
-
);
|
|
81205
|
-
for (const row of allModels.rows) {
|
|
81206
|
-
const storedId = asString(row.id);
|
|
81207
|
-
if (modelId.startsWith(storedId) || storedId.startsWith(modelId)) {
|
|
81208
|
-
return rowToModelPricing(row);
|
|
81209
|
-
}
|
|
81210
|
-
}
|
|
81211
|
-
return null;
|
|
81212
|
-
}
|
|
81213
|
-
function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
|
|
81214
|
-
if (inputMultiplier === null || outputMultiplier === null) {
|
|
81215
|
-
return 0;
|
|
81216
|
-
}
|
|
81217
|
-
const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
|
|
81218
|
-
return tokenUnits * TOKEN_UNIT_PRICE;
|
|
81219
|
-
}
|
|
81220
|
-
async function upsertModel(db, model) {
|
|
81221
|
-
await db.execute({
|
|
81222
|
-
sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
|
|
81223
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
81224
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
81225
|
-
display_name = excluded.display_name,
|
|
81226
|
-
premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
|
|
81227
|
-
token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
|
|
81228
|
-
token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
|
|
81229
|
-
cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
|
|
81230
|
-
tier = excluded.tier,
|
|
81231
|
-
available = excluded.available,
|
|
81232
|
-
updated_at = excluded.updated_at`,
|
|
81233
|
-
args: [
|
|
81234
|
-
model.id,
|
|
81235
|
-
model.displayName,
|
|
81236
|
-
model.premiumMultiplier,
|
|
81237
|
-
model.tokenInputMultiplier,
|
|
81238
|
-
model.tokenOutputMultiplier,
|
|
81239
|
-
model.cachedInputMultiplier,
|
|
81240
|
-
model.tier,
|
|
81241
|
-
model.available ? 1 : 0,
|
|
81242
|
-
model.updatedAt
|
|
81243
|
-
]
|
|
81244
|
-
});
|
|
81245
|
-
}
|
|
81246
|
-
function rowToModelPricing(row) {
|
|
81247
|
-
return {
|
|
81248
|
-
id: asString(row.id),
|
|
81249
|
-
displayName: asString(row.display_name),
|
|
81250
|
-
premiumMultiplier: asNullableNumber(row.premium_multiplier),
|
|
81251
|
-
tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
|
|
81252
|
-
tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
|
|
81253
|
-
cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
|
|
81254
|
-
tier: asString(row.tier),
|
|
81255
|
-
available: asNumber(row.available) === 1,
|
|
81256
|
-
updatedAt: asString(row.updated_at)
|
|
81257
|
-
};
|
|
81258
|
-
}
|
|
81259
|
-
function findClosestKey(map2, targetKey) {
|
|
81260
|
-
for (const [key, value] of map2) {
|
|
81261
|
-
if (key.includes(targetKey) || targetKey.includes(key)) {
|
|
81262
|
-
return value;
|
|
81263
|
-
}
|
|
81264
|
-
}
|
|
81265
|
-
return void 0;
|
|
81266
|
-
}
|
|
81375
|
+
// packages/daemon/src/models/index.ts
|
|
81376
|
+
init_catalog();
|
|
81377
|
+
init_registry();
|
|
81378
|
+
init_types();
|
|
81267
81379
|
|
|
81268
81380
|
// packages/daemon/src/orchestrator/system-prompt.ts
|
|
81269
81381
|
function formatSquadRoster(squads) {
|
|
@@ -81597,6 +81709,9 @@ async function sendMessage(session, message2, onChunk) {
|
|
|
81597
81709
|
return operation;
|
|
81598
81710
|
}
|
|
81599
81711
|
|
|
81712
|
+
// packages/daemon/src/orchestrator/orchestrator.ts
|
|
81713
|
+
init_registry();
|
|
81714
|
+
|
|
81600
81715
|
// packages/daemon/src/skills/loader.ts
|
|
81601
81716
|
var import_gray_matter2 = __toESM(require_gray_matter(), 1);
|
|
81602
81717
|
init_paths();
|
|
@@ -81948,6 +82063,7 @@ var executeCodingToolCall = async (toolName, rawArgs) => {
|
|
|
81948
82063
|
// packages/daemon/src/orchestrator/tools/inbox.ts
|
|
81949
82064
|
init_dist();
|
|
81950
82065
|
init_zod();
|
|
82066
|
+
init_db();
|
|
81951
82067
|
var inboxReplySchema = external_exports.object({
|
|
81952
82068
|
itemId: external_exports.string().trim().min(1),
|
|
81953
82069
|
reply: external_exports.string().trim().min(1)
|
|
@@ -82219,17 +82335,80 @@ async function isSquadAvailable(squadId) {
|
|
|
82219
82335
|
}
|
|
82220
82336
|
|
|
82221
82337
|
// packages/daemon/src/execution/agent.ts
|
|
82222
|
-
|
|
82338
|
+
init_registry();
|
|
82223
82339
|
import { exec as exec3 } from "node:child_process";
|
|
82224
82340
|
import { mkdir as mkdir9, readFile as readFile7, readdir as readdir5, stat as stat3, writeFile as writeFile6 } from "node:fs/promises";
|
|
82225
82341
|
import { dirname as dirname8, extname as extname3, isAbsolute, join as join11, relative as relative3, resolve as resolve4 } from "node:path";
|
|
82226
82342
|
import { promisify as promisify3 } from "node:util";
|
|
82227
82343
|
import {
|
|
82228
|
-
CopilotClient as
|
|
82229
|
-
approveAll as
|
|
82344
|
+
CopilotClient as CopilotClient3,
|
|
82345
|
+
approveAll as approveAll3,
|
|
82230
82346
|
defineTool
|
|
82231
82347
|
} from "@github/copilot-sdk";
|
|
82232
82348
|
|
|
82349
|
+
// packages/daemon/src/squad/model-selector.ts
|
|
82350
|
+
init_registry();
|
|
82351
|
+
import { CopilotClient as CopilotClient2, approveAll as approveAll2 } from "@github/copilot-sdk";
|
|
82352
|
+
var VALID_TIERS = ["trivial", "fast", "standard", "premium", "ultra"];
|
|
82353
|
+
var CLASSIFICATION_PROMPT = `You are a task complexity classifier. Given a task description, classify its complexity into exactly one tier.
|
|
82354
|
+
|
|
82355
|
+
Tiers (from simplest to most complex):
|
|
82356
|
+
- trivial: Typos, renames, comment changes, config tweaks, formatting
|
|
82357
|
+
- fast: Simple bug fixes, small features, documentation updates, single-file changes
|
|
82358
|
+
- standard: Feature implementation, multi-file changes, moderate refactoring
|
|
82359
|
+
- premium: Architecture changes, complex refactoring, security work, performance optimization
|
|
82360
|
+
- ultra: System-wide redesigns, critical infrastructure, cross-cutting concerns
|
|
82361
|
+
|
|
82362
|
+
Reply with ONLY the tier name (one word, lowercase). Nothing else.`;
|
|
82363
|
+
async function selectModelForTask(taskDescription) {
|
|
82364
|
+
const classifierModel = await getCheapestAvailableModel();
|
|
82365
|
+
if (!classifierModel) {
|
|
82366
|
+
throw new Error("No models available in pricing database");
|
|
82367
|
+
}
|
|
82368
|
+
let tier;
|
|
82369
|
+
try {
|
|
82370
|
+
tier = await classifyTaskComplexity(taskDescription, classifierModel.id);
|
|
82371
|
+
} catch {
|
|
82372
|
+
return classifierModel.id;
|
|
82373
|
+
}
|
|
82374
|
+
const selectedModel = await getCheapestInTier(tier);
|
|
82375
|
+
if (selectedModel) {
|
|
82376
|
+
return selectedModel.id;
|
|
82377
|
+
}
|
|
82378
|
+
const nextTier = getNextTierUp(tier);
|
|
82379
|
+
if (nextTier) {
|
|
82380
|
+
const escalatedModel = await getCheapestInTier(nextTier);
|
|
82381
|
+
if (escalatedModel) {
|
|
82382
|
+
return escalatedModel.id;
|
|
82383
|
+
}
|
|
82384
|
+
}
|
|
82385
|
+
return classifierModel.id;
|
|
82386
|
+
}
|
|
82387
|
+
async function classifyTaskComplexity(taskDescription, modelId) {
|
|
82388
|
+
let client2 = null;
|
|
82389
|
+
try {
|
|
82390
|
+
client2 = new CopilotClient2();
|
|
82391
|
+
await client2.start();
|
|
82392
|
+
const session = await client2.createSession({
|
|
82393
|
+
model: modelId,
|
|
82394
|
+
onPermissionRequest: approveAll2,
|
|
82395
|
+
systemMessage: { content: CLASSIFICATION_PROMPT }
|
|
82396
|
+
});
|
|
82397
|
+
try {
|
|
82398
|
+
const response = await session.sendAndWait({ prompt: `Task: ${taskDescription}` }, 15e3);
|
|
82399
|
+
const raw = (response.text ?? "").trim().toLowerCase();
|
|
82400
|
+
const tier = VALID_TIERS.find((t) => raw.includes(t));
|
|
82401
|
+
return tier ?? "standard";
|
|
82402
|
+
} finally {
|
|
82403
|
+
await session.disconnect().catch(() => void 0);
|
|
82404
|
+
}
|
|
82405
|
+
} finally {
|
|
82406
|
+
if (client2) {
|
|
82407
|
+
await client2.stop().catch(() => void 0);
|
|
82408
|
+
}
|
|
82409
|
+
}
|
|
82410
|
+
}
|
|
82411
|
+
|
|
82233
82412
|
// packages/daemon/src/execution/history.ts
|
|
82234
82413
|
var DEFAULT_CONTEXT_LIMIT = 5;
|
|
82235
82414
|
var MAX_LEARNING_LENGTH = 1e3;
|
|
@@ -82515,14 +82694,15 @@ async function executeAgentTask(member, task, worktreePath, options2) {
|
|
|
82515
82694
|
const mcpServerNote = options2?.mcpServers?.length ? `Available MCP server labels: ${options2.mcpServers.join(", ")}.` : "No additional MCP servers were configured for this run.";
|
|
82516
82695
|
let client2 = null;
|
|
82517
82696
|
try {
|
|
82518
|
-
client2 = new
|
|
82697
|
+
client2 = new CopilotClient3({ workingDirectory: worktreePath });
|
|
82519
82698
|
await client2.start();
|
|
82699
|
+
const model = member.model ?? await selectModelForTask(task.description);
|
|
82520
82700
|
const session = await client2.createSession({
|
|
82521
|
-
model
|
|
82701
|
+
model,
|
|
82522
82702
|
workingDirectory: worktreePath,
|
|
82523
82703
|
tools,
|
|
82524
82704
|
availableTools: ["custom:*"],
|
|
82525
|
-
onPermissionRequest:
|
|
82705
|
+
onPermissionRequest: approveAll3,
|
|
82526
82706
|
systemMessage: {
|
|
82527
82707
|
content: `${member.systemPrompt}
|
|
82528
82708
|
|
|
@@ -82599,12 +82779,11 @@ async function buildInstanceSystemPromptSuffix(squadId, instanceId) {
|
|
|
82599
82779
|
}
|
|
82600
82780
|
|
|
82601
82781
|
// packages/daemon/src/execution/planning.ts
|
|
82602
|
-
init_dist();
|
|
82603
82782
|
import { exec as exec4 } from "node:child_process";
|
|
82604
82783
|
import { access, readFile as readFile8 } from "node:fs/promises";
|
|
82605
82784
|
import { join as join12 } from "node:path";
|
|
82606
82785
|
import { promisify as promisify4 } from "node:util";
|
|
82607
|
-
import { CopilotClient as
|
|
82786
|
+
import { CopilotClient as CopilotClient4, approveAll as approveAll4 } from "@github/copilot-sdk";
|
|
82608
82787
|
|
|
82609
82788
|
// packages/daemon/src/squad/roles.ts
|
|
82610
82789
|
var ROLE_GUIDELINES = [
|
|
@@ -82747,12 +82926,13 @@ Return strict JSON in this shape:
|
|
|
82747
82926
|
}`;
|
|
82748
82927
|
let client2 = null;
|
|
82749
82928
|
try {
|
|
82750
|
-
client2 = new
|
|
82929
|
+
client2 = new CopilotClient4({ workingDirectory: repoPath });
|
|
82751
82930
|
await client2.start();
|
|
82931
|
+
const model = await selectModelForTask(`Create implementation plan: ${objective.description}`);
|
|
82752
82932
|
const session = await client2.createSession({
|
|
82753
|
-
model
|
|
82933
|
+
model,
|
|
82754
82934
|
workingDirectory: repoPath,
|
|
82755
|
-
onPermissionRequest:
|
|
82935
|
+
onPermissionRequest: approveAll4,
|
|
82756
82936
|
systemMessage: {
|
|
82757
82937
|
content: `${TEAM_LEAD_PROMPT}
|
|
82758
82938
|
|
|
@@ -82855,7 +83035,7 @@ async function createPullRequest(options2) {
|
|
|
82855
83035
|
init_dist();
|
|
82856
83036
|
import { exec as exec6 } from "node:child_process";
|
|
82857
83037
|
import { promisify as promisify6 } from "node:util";
|
|
82858
|
-
import { CopilotClient as
|
|
83038
|
+
import { CopilotClient as CopilotClient5, approveAll as approveAll5 } from "@github/copilot-sdk";
|
|
82859
83039
|
var execAsync6 = promisify6(exec6);
|
|
82860
83040
|
var GIT_DIFF_MAX_BUFFER = 10 * 1024 * 1024;
|
|
82861
83041
|
function extractJsonObject2(content) {
|
|
@@ -82895,12 +83075,13 @@ Return strict JSON:
|
|
|
82895
83075
|
}`;
|
|
82896
83076
|
let client2 = null;
|
|
82897
83077
|
try {
|
|
82898
|
-
client2 = new
|
|
83078
|
+
client2 = new CopilotClient5({ workingDirectory: worktreePath });
|
|
82899
83079
|
await client2.start();
|
|
83080
|
+
const model = qaMember.model ?? await selectModelForTask(`QA review: ${objective.description}`);
|
|
82900
83081
|
const session = await client2.createSession({
|
|
82901
|
-
model
|
|
83082
|
+
model,
|
|
82902
83083
|
workingDirectory: worktreePath,
|
|
82903
|
-
onPermissionRequest:
|
|
83084
|
+
onPermissionRequest: approveAll5,
|
|
82904
83085
|
systemMessage: {
|
|
82905
83086
|
content: QA_PROMPT
|
|
82906
83087
|
}
|
|
@@ -82970,8 +83151,7 @@ async function handleQARejection(objectiveId, feedback) {
|
|
|
82970
83151
|
}
|
|
82971
83152
|
|
|
82972
83153
|
// packages/daemon/src/execution/review.ts
|
|
82973
|
-
|
|
82974
|
-
import { CopilotClient as CopilotClient5, approveAll as approveAll5 } from "@github/copilot-sdk";
|
|
83154
|
+
import { CopilotClient as CopilotClient6, approveAll as approveAll6 } from "@github/copilot-sdk";
|
|
82975
83155
|
function extractJsonObject3(content) {
|
|
82976
83156
|
const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
82977
83157
|
if (fenced?.[1]) {
|
|
@@ -83016,11 +83196,12 @@ Return strict JSON:
|
|
|
83016
83196
|
}`;
|
|
83017
83197
|
let client2 = null;
|
|
83018
83198
|
try {
|
|
83019
|
-
client2 = new
|
|
83199
|
+
client2 = new CopilotClient6();
|
|
83020
83200
|
await client2.start();
|
|
83201
|
+
const model = teamLead.model ?? await selectModelForTask(`Code review: ${objective.description}`);
|
|
83021
83202
|
const session = await client2.createSession({
|
|
83022
|
-
model
|
|
83023
|
-
onPermissionRequest:
|
|
83203
|
+
model,
|
|
83204
|
+
onPermissionRequest: approveAll6,
|
|
83024
83205
|
systemMessage: {
|
|
83025
83206
|
content: `${TEAM_LEAD_PROMPT}
|
|
83026
83207
|
|
|
@@ -84334,13 +84515,13 @@ var Orchestrator = class {
|
|
|
84334
84515
|
});
|
|
84335
84516
|
this.activeModel = model;
|
|
84336
84517
|
} catch (error51) {
|
|
84337
|
-
if (model !==
|
|
84518
|
+
if (model !== this.config.defaultModel) {
|
|
84338
84519
|
this.activeSession = await createSession({
|
|
84339
|
-
model:
|
|
84520
|
+
model: this.config.defaultModel,
|
|
84340
84521
|
systemPrompt,
|
|
84341
84522
|
tools: createBoundOrchestratorTools(this.config)
|
|
84342
84523
|
});
|
|
84343
|
-
this.activeModel =
|
|
84524
|
+
this.activeModel = this.config.defaultModel;
|
|
84344
84525
|
} else {
|
|
84345
84526
|
throw error51;
|
|
84346
84527
|
}
|
|
@@ -84389,6 +84570,7 @@ function createOrchestrator(config2, eventBus2) {
|
|
|
84389
84570
|
// packages/daemon/src/scheduler/engine.ts
|
|
84390
84571
|
init_dist();
|
|
84391
84572
|
var import_cron_parser2 = __toESM(require_dist(), 1);
|
|
84573
|
+
init_db();
|
|
84392
84574
|
var Scheduler = class {
|
|
84393
84575
|
orchestrator;
|
|
84394
84576
|
eventBus;
|
|
@@ -84476,6 +84658,9 @@ function createScheduler(orchestrator2, eventBus2) {
|
|
|
84476
84658
|
return new Scheduler(orchestrator2, eventBus2);
|
|
84477
84659
|
}
|
|
84478
84660
|
|
|
84661
|
+
// packages/daemon/src/index.ts
|
|
84662
|
+
init_db();
|
|
84663
|
+
|
|
84479
84664
|
// packages/daemon/src/telegram/bot.ts
|
|
84480
84665
|
var import_grammy = __toESM(require_mod2(), 1);
|
|
84481
84666
|
var TelegramBot = class {
|
|
@@ -84496,9 +84681,16 @@ var TelegramBot = class {
|
|
|
84496
84681
|
if (this.started) {
|
|
84497
84682
|
return;
|
|
84498
84683
|
}
|
|
84684
|
+
this.bot.catch((err) => {
|
|
84685
|
+
this.logger.error({ err: err.error }, "Telegram bot error: %s", err.message);
|
|
84686
|
+
});
|
|
84499
84687
|
this.registerHandlers();
|
|
84500
|
-
this.bot.start(
|
|
84501
|
-
|
|
84688
|
+
this.bot.start({
|
|
84689
|
+
onStart: () => {
|
|
84690
|
+
this.logger.info("Telegram bot connected and polling");
|
|
84691
|
+
}
|
|
84692
|
+
}).catch((error51) => {
|
|
84693
|
+
this.logger.error({ err: error51 }, "Telegram polling failed to start");
|
|
84502
84694
|
});
|
|
84503
84695
|
this.started = true;
|
|
84504
84696
|
}
|
|
@@ -84508,11 +84700,26 @@ var TelegramBot = class {
|
|
|
84508
84700
|
}
|
|
84509
84701
|
this.bot.stop();
|
|
84510
84702
|
this.started = false;
|
|
84703
|
+
this.logger.info("Telegram bot stopped");
|
|
84511
84704
|
}
|
|
84512
84705
|
async sendText(chatId, text) {
|
|
84513
84706
|
await this.bot.api.sendMessage(chatId, text);
|
|
84514
84707
|
}
|
|
84708
|
+
isAuthorized(userId) {
|
|
84709
|
+
if (!this.config.telegramUserId) {
|
|
84710
|
+
return false;
|
|
84711
|
+
}
|
|
84712
|
+
return String(userId) === this.config.telegramUserId;
|
|
84713
|
+
}
|
|
84515
84714
|
registerHandlers() {
|
|
84715
|
+
this.bot.use(async (ctx, next) => {
|
|
84716
|
+
const userId = ctx.from?.id;
|
|
84717
|
+
if (!userId || !this.isAuthorized(userId)) {
|
|
84718
|
+
this.logger.warn({ userId }, "Unauthorized Telegram message, ignoring");
|
|
84719
|
+
return;
|
|
84720
|
+
}
|
|
84721
|
+
await next();
|
|
84722
|
+
});
|
|
84516
84723
|
this.bot.command("start", async (ctx) => {
|
|
84517
84724
|
await ctx.reply(
|
|
84518
84725
|
"Hello from Io. Send me a message and I will route it through the daemon orchestrator."
|
|
@@ -84707,8 +84914,13 @@ async function main() {
|
|
|
84707
84914
|
setChatOrchestrator(orchestrator2);
|
|
84708
84915
|
const apiServer = createApiServer(config2);
|
|
84709
84916
|
const telegramBot = createTelegramBot(config2, orchestrator2);
|
|
84710
|
-
telegramBot
|
|
84711
|
-
|
|
84917
|
+
if (telegramBot) {
|
|
84918
|
+
telegramBot.start();
|
|
84919
|
+
createTelegramNotifier(telegramBot, config2, eventBus);
|
|
84920
|
+
logger2.info("Telegram bot initialized");
|
|
84921
|
+
} else {
|
|
84922
|
+
logger2.info("Telegram bot disabled (no token configured)");
|
|
84923
|
+
}
|
|
84712
84924
|
registerShutdownHandlers(logger2, async () => {
|
|
84713
84925
|
if (pricingRefreshTimer) {
|
|
84714
84926
|
clearInterval(pricingRefreshTimer);
|