heyio 4.0.3 → 4.0.5
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 +807 -245
- package/dist/daemon/index.js +759 -237
- package/dist/web/assets/{index-Bkx7szi0.js → index-DNdaxCqk.js} +76 -76
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/daemon/index.js
CHANGED
|
@@ -27989,7 +27989,7 @@ var require_view = __commonJS({
|
|
|
27989
27989
|
var dirname9 = path.dirname;
|
|
27990
27990
|
var basename6 = path.basename;
|
|
27991
27991
|
var extname4 = path.extname;
|
|
27992
|
-
var
|
|
27992
|
+
var join15 = path.join;
|
|
27993
27993
|
var resolve5 = path.resolve;
|
|
27994
27994
|
module2.exports = View;
|
|
27995
27995
|
function View(name, options2) {
|
|
@@ -28051,12 +28051,12 @@ var require_view = __commonJS({
|
|
|
28051
28051
|
};
|
|
28052
28052
|
View.prototype.resolve = function resolve6(dir, file2) {
|
|
28053
28053
|
var ext = this.ext;
|
|
28054
|
-
var path2 =
|
|
28054
|
+
var path2 = join15(dir, file2);
|
|
28055
28055
|
var stat4 = tryStat(path2);
|
|
28056
28056
|
if (stat4 && stat4.isFile()) {
|
|
28057
28057
|
return path2;
|
|
28058
28058
|
}
|
|
28059
|
-
path2 =
|
|
28059
|
+
path2 = join15(dir, basename6(file2, ext), "index" + ext);
|
|
28060
28060
|
stat4 = tryStat(path2);
|
|
28061
28061
|
if (stat4 && stat4.isFile()) {
|
|
28062
28062
|
return path2;
|
|
@@ -31761,7 +31761,7 @@ var require_send = __commonJS({
|
|
|
31761
31761
|
var Stream = __require("stream");
|
|
31762
31762
|
var util = __require("util");
|
|
31763
31763
|
var extname4 = path.extname;
|
|
31764
|
-
var
|
|
31764
|
+
var join15 = path.join;
|
|
31765
31765
|
var normalize = path.normalize;
|
|
31766
31766
|
var resolve5 = path.resolve;
|
|
31767
31767
|
var sep = path.sep;
|
|
@@ -31933,7 +31933,7 @@ var require_send = __commonJS({
|
|
|
31933
31933
|
return res;
|
|
31934
31934
|
}
|
|
31935
31935
|
parts = path2.split(sep);
|
|
31936
|
-
path2 = normalize(
|
|
31936
|
+
path2 = normalize(join15(root, path2));
|
|
31937
31937
|
} else {
|
|
31938
31938
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
31939
31939
|
debug('malicious path "%s"', path2);
|
|
@@ -32066,7 +32066,7 @@ var require_send = __commonJS({
|
|
|
32066
32066
|
if (err) return self.onStatError(err);
|
|
32067
32067
|
return self.error(404);
|
|
32068
32068
|
}
|
|
32069
|
-
var p =
|
|
32069
|
+
var p = join15(path2, self._index[i]);
|
|
32070
32070
|
debug('stat "%s"', p);
|
|
32071
32071
|
fs.stat(p, function(err2, stat4) {
|
|
32072
32072
|
if (err2) return next(err2);
|
|
@@ -42076,7 +42076,7 @@ var require_thread_stream = __commonJS({
|
|
|
42076
42076
|
var { version: version2 } = require_package();
|
|
42077
42077
|
var { EventEmitter: EventEmitter2 } = __require("events");
|
|
42078
42078
|
var { Worker } = __require("worker_threads");
|
|
42079
|
-
var { join:
|
|
42079
|
+
var { join: join15 } = __require("path");
|
|
42080
42080
|
var { pathToFileURL: pathToFileURL2 } = __require("url");
|
|
42081
42081
|
var { wait } = require_wait();
|
|
42082
42082
|
var {
|
|
@@ -42119,7 +42119,7 @@ var require_thread_stream = __commonJS({
|
|
|
42119
42119
|
function createWorker(stream, opts) {
|
|
42120
42120
|
const { filename, workerData } = opts;
|
|
42121
42121
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
42122
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
42122
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join15(__dirname, "lib", "worker.js");
|
|
42123
42123
|
const worker = new Worker(toExecute, {
|
|
42124
42124
|
...opts.workerOpts,
|
|
42125
42125
|
trackUnmanagedFds: false,
|
|
@@ -42522,7 +42522,7 @@ var require_transport = __commonJS({
|
|
|
42522
42522
|
"use strict";
|
|
42523
42523
|
var { createRequire } = __require("module");
|
|
42524
42524
|
var getCallers = require_caller();
|
|
42525
|
-
var { join:
|
|
42525
|
+
var { join: join15, isAbsolute: isAbsolute2, sep } = __require("node:path");
|
|
42526
42526
|
var sleep = require_atomic_sleep();
|
|
42527
42527
|
var onExit = require_on_exit_leak_free();
|
|
42528
42528
|
var ThreadStream = require_thread_stream();
|
|
@@ -42585,7 +42585,7 @@ var require_transport = __commonJS({
|
|
|
42585
42585
|
throw new Error("only one of target or targets can be specified");
|
|
42586
42586
|
}
|
|
42587
42587
|
if (targets) {
|
|
42588
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
42588
|
+
target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
|
|
42589
42589
|
options2.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
42590
42590
|
return {
|
|
42591
42591
|
...dest,
|
|
@@ -42603,7 +42603,7 @@ var require_transport = __commonJS({
|
|
|
42603
42603
|
});
|
|
42604
42604
|
});
|
|
42605
42605
|
} else if (pipeline) {
|
|
42606
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
42606
|
+
target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
|
|
42607
42607
|
options2.pipelines = [pipeline.map((dest) => {
|
|
42608
42608
|
return {
|
|
42609
42609
|
...dest,
|
|
@@ -42625,7 +42625,7 @@ var require_transport = __commonJS({
|
|
|
42625
42625
|
return origin;
|
|
42626
42626
|
}
|
|
42627
42627
|
if (origin === "pino/file") {
|
|
42628
|
-
return
|
|
42628
|
+
return join15(__dirname, "..", "file.js");
|
|
42629
42629
|
}
|
|
42630
42630
|
let fixTarget2;
|
|
42631
42631
|
for (const filePath of callers) {
|
|
@@ -43614,7 +43614,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
43614
43614
|
return circularValue;
|
|
43615
43615
|
}
|
|
43616
43616
|
let res = "";
|
|
43617
|
-
let
|
|
43617
|
+
let join15 = ",";
|
|
43618
43618
|
const originalIndentation = indentation;
|
|
43619
43619
|
if (Array.isArray(value)) {
|
|
43620
43620
|
if (value.length === 0) {
|
|
@@ -43628,7 +43628,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
43628
43628
|
indentation += spacer;
|
|
43629
43629
|
res += `
|
|
43630
43630
|
${indentation}`;
|
|
43631
|
-
|
|
43631
|
+
join15 = `,
|
|
43632
43632
|
${indentation}`;
|
|
43633
43633
|
}
|
|
43634
43634
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -43636,13 +43636,13 @@ ${indentation}`;
|
|
|
43636
43636
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
43637
43637
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
43638
43638
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
43639
|
-
res +=
|
|
43639
|
+
res += join15;
|
|
43640
43640
|
}
|
|
43641
43641
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
43642
43642
|
res += tmp !== void 0 ? tmp : "null";
|
|
43643
43643
|
if (value.length - 1 > maximumBreadth) {
|
|
43644
43644
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
43645
|
-
res += `${
|
|
43645
|
+
res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
43646
43646
|
}
|
|
43647
43647
|
if (spacer !== "") {
|
|
43648
43648
|
res += `
|
|
@@ -43663,7 +43663,7 @@ ${originalIndentation}`;
|
|
|
43663
43663
|
let separator = "";
|
|
43664
43664
|
if (spacer !== "") {
|
|
43665
43665
|
indentation += spacer;
|
|
43666
|
-
|
|
43666
|
+
join15 = `,
|
|
43667
43667
|
${indentation}`;
|
|
43668
43668
|
whitespace = " ";
|
|
43669
43669
|
}
|
|
@@ -43677,13 +43677,13 @@ ${indentation}`;
|
|
|
43677
43677
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
43678
43678
|
if (tmp !== void 0) {
|
|
43679
43679
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
43680
|
-
separator =
|
|
43680
|
+
separator = join15;
|
|
43681
43681
|
}
|
|
43682
43682
|
}
|
|
43683
43683
|
if (keyLength > maximumBreadth) {
|
|
43684
43684
|
const removedKeys = keyLength - maximumBreadth;
|
|
43685
43685
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
43686
|
-
separator =
|
|
43686
|
+
separator = join15;
|
|
43687
43687
|
}
|
|
43688
43688
|
if (spacer !== "" && separator.length > 1) {
|
|
43689
43689
|
res = `
|
|
@@ -43724,7 +43724,7 @@ ${originalIndentation}`;
|
|
|
43724
43724
|
}
|
|
43725
43725
|
const originalIndentation = indentation;
|
|
43726
43726
|
let res = "";
|
|
43727
|
-
let
|
|
43727
|
+
let join15 = ",";
|
|
43728
43728
|
if (Array.isArray(value)) {
|
|
43729
43729
|
if (value.length === 0) {
|
|
43730
43730
|
return "[]";
|
|
@@ -43737,7 +43737,7 @@ ${originalIndentation}`;
|
|
|
43737
43737
|
indentation += spacer;
|
|
43738
43738
|
res += `
|
|
43739
43739
|
${indentation}`;
|
|
43740
|
-
|
|
43740
|
+
join15 = `,
|
|
43741
43741
|
${indentation}`;
|
|
43742
43742
|
}
|
|
43743
43743
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -43745,13 +43745,13 @@ ${indentation}`;
|
|
|
43745
43745
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
43746
43746
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
43747
43747
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
43748
|
-
res +=
|
|
43748
|
+
res += join15;
|
|
43749
43749
|
}
|
|
43750
43750
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
43751
43751
|
res += tmp !== void 0 ? tmp : "null";
|
|
43752
43752
|
if (value.length - 1 > maximumBreadth) {
|
|
43753
43753
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
43754
|
-
res += `${
|
|
43754
|
+
res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
43755
43755
|
}
|
|
43756
43756
|
if (spacer !== "") {
|
|
43757
43757
|
res += `
|
|
@@ -43764,7 +43764,7 @@ ${originalIndentation}`;
|
|
|
43764
43764
|
let whitespace = "";
|
|
43765
43765
|
if (spacer !== "") {
|
|
43766
43766
|
indentation += spacer;
|
|
43767
|
-
|
|
43767
|
+
join15 = `,
|
|
43768
43768
|
${indentation}`;
|
|
43769
43769
|
whitespace = " ";
|
|
43770
43770
|
}
|
|
@@ -43773,7 +43773,7 @@ ${indentation}`;
|
|
|
43773
43773
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
43774
43774
|
if (tmp !== void 0) {
|
|
43775
43775
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
43776
|
-
separator =
|
|
43776
|
+
separator = join15;
|
|
43777
43777
|
}
|
|
43778
43778
|
}
|
|
43779
43779
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -43831,20 +43831,20 @@ ${originalIndentation}`;
|
|
|
43831
43831
|
indentation += spacer;
|
|
43832
43832
|
let res2 = `
|
|
43833
43833
|
${indentation}`;
|
|
43834
|
-
const
|
|
43834
|
+
const join16 = `,
|
|
43835
43835
|
${indentation}`;
|
|
43836
43836
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
43837
43837
|
let i = 0;
|
|
43838
43838
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
43839
43839
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
43840
43840
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
43841
|
-
res2 +=
|
|
43841
|
+
res2 += join16;
|
|
43842
43842
|
}
|
|
43843
43843
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
43844
43844
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
43845
43845
|
if (value.length - 1 > maximumBreadth) {
|
|
43846
43846
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
43847
|
-
res2 += `${
|
|
43847
|
+
res2 += `${join16}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
43848
43848
|
}
|
|
43849
43849
|
res2 += `
|
|
43850
43850
|
${originalIndentation}`;
|
|
@@ -43860,16 +43860,16 @@ ${originalIndentation}`;
|
|
|
43860
43860
|
return '"[Object]"';
|
|
43861
43861
|
}
|
|
43862
43862
|
indentation += spacer;
|
|
43863
|
-
const
|
|
43863
|
+
const join15 = `,
|
|
43864
43864
|
${indentation}`;
|
|
43865
43865
|
let res = "";
|
|
43866
43866
|
let separator = "";
|
|
43867
43867
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
43868
43868
|
if (isTypedArrayWithEntries(value)) {
|
|
43869
|
-
res += stringifyTypedArray(value,
|
|
43869
|
+
res += stringifyTypedArray(value, join15, maximumBreadth);
|
|
43870
43870
|
keys = keys.slice(value.length);
|
|
43871
43871
|
maximumPropertiesToStringify -= value.length;
|
|
43872
|
-
separator =
|
|
43872
|
+
separator = join15;
|
|
43873
43873
|
}
|
|
43874
43874
|
if (deterministic) {
|
|
43875
43875
|
keys = sort(keys, comparator);
|
|
@@ -43880,13 +43880,13 @@ ${indentation}`;
|
|
|
43880
43880
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
43881
43881
|
if (tmp !== void 0) {
|
|
43882
43882
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
43883
|
-
separator =
|
|
43883
|
+
separator = join15;
|
|
43884
43884
|
}
|
|
43885
43885
|
}
|
|
43886
43886
|
if (keyLength > maximumBreadth) {
|
|
43887
43887
|
const removedKeys = keyLength - maximumBreadth;
|
|
43888
43888
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
43889
|
-
separator =
|
|
43889
|
+
separator = join15;
|
|
43890
43890
|
}
|
|
43891
43891
|
if (separator !== "") {
|
|
43892
43892
|
res = `
|
|
@@ -60175,13 +60175,10 @@ var require_mod2 = __commonJS({
|
|
|
60175
60175
|
|
|
60176
60176
|
// packages/shared/dist/constants.js
|
|
60177
60177
|
var APP_NAME = "io";
|
|
60178
|
-
var APP_VERSION = "4.0.
|
|
60178
|
+
var APP_VERSION = "4.0.5";
|
|
60179
60179
|
var API_PORT = 7777;
|
|
60180
60180
|
var API_HOST = "0.0.0.0";
|
|
60181
|
-
var DEFAULT_MODEL = "gpt-
|
|
60182
|
-
var FAST_MODEL = "gpt-4.1-mini";
|
|
60183
|
-
var STANDARD_MODEL = "claude-sonnet-4.6";
|
|
60184
|
-
var PREMIUM_MODEL = "claude-sonnet-4.6";
|
|
60181
|
+
var DEFAULT_MODEL = "gpt-4o";
|
|
60185
60182
|
var SESSION_RESET_THRESHOLD = 50;
|
|
60186
60183
|
var SCHEDULER_INTERVAL_MS = 6e4;
|
|
60187
60184
|
var QA_MAX_REVISIONS = 3;
|
|
@@ -61831,6 +61828,27 @@ var MIGRATIONS = [
|
|
|
61831
61828
|
"CREATE INDEX IF NOT EXISTS idx_activity_objective_id ON activity(objective_id)",
|
|
61832
61829
|
"CREATE INDEX IF NOT EXISTS idx_agent_history_agent_id_created_at ON agent_history(agent_id, created_at)"
|
|
61833
61830
|
]
|
|
61831
|
+
},
|
|
61832
|
+
{
|
|
61833
|
+
version: 2,
|
|
61834
|
+
name: "add-model-pricing-and-dual-costs",
|
|
61835
|
+
statements: [
|
|
61836
|
+
`CREATE TABLE IF NOT EXISTS model_pricing (
|
|
61837
|
+
id TEXT PRIMARY KEY,
|
|
61838
|
+
display_name TEXT NOT NULL,
|
|
61839
|
+
premium_multiplier REAL,
|
|
61840
|
+
token_input_multiplier REAL,
|
|
61841
|
+
token_output_multiplier REAL,
|
|
61842
|
+
cached_input_multiplier REAL,
|
|
61843
|
+
tier TEXT NOT NULL,
|
|
61844
|
+
available INTEGER NOT NULL DEFAULT 1,
|
|
61845
|
+
updated_at TEXT NOT NULL
|
|
61846
|
+
)`,
|
|
61847
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_tier ON model_pricing(tier)",
|
|
61848
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_available ON model_pricing(available)",
|
|
61849
|
+
"ALTER TABLE token_usage ADD COLUMN premium_request_cost REAL",
|
|
61850
|
+
"ALTER TABLE token_usage ADD COLUMN token_unit_cost REAL"
|
|
61851
|
+
]
|
|
61834
61852
|
}
|
|
61835
61853
|
];
|
|
61836
61854
|
var client = null;
|
|
@@ -62515,8 +62533,8 @@ async function recordUsage(data, db) {
|
|
|
62515
62533
|
createdAt: data.createdAt ?? nowIso()
|
|
62516
62534
|
};
|
|
62517
62535
|
await database.execute({
|
|
62518
|
-
sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, created_at)
|
|
62519
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
62536
|
+
sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, premium_request_cost, token_unit_cost, created_at)
|
|
62537
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
62520
62538
|
args: [
|
|
62521
62539
|
usage.id,
|
|
62522
62540
|
usage.squadId,
|
|
@@ -62525,6 +62543,8 @@ async function recordUsage(data, db) {
|
|
|
62525
62543
|
usage.inputTokens,
|
|
62526
62544
|
usage.outputTokens,
|
|
62527
62545
|
usage.cost,
|
|
62546
|
+
data.premiumRequestCost ?? null,
|
|
62547
|
+
data.tokenUnitCost ?? null,
|
|
62528
62548
|
usage.createdAt
|
|
62529
62549
|
]
|
|
62530
62550
|
});
|
|
@@ -63017,7 +63037,7 @@ async function createNotificationItemIfMissing(eventBus2, payload, type, title,
|
|
|
63017
63037
|
// packages/daemon/src/api/server.ts
|
|
63018
63038
|
import { existsSync as existsSync2 } from "node:fs";
|
|
63019
63039
|
import { createServer } from "node:http";
|
|
63020
|
-
import { dirname as dirname4, resolve as resolve2 } from "node:path";
|
|
63040
|
+
import { dirname as dirname4, join as join4, resolve as resolve2 } from "node:path";
|
|
63021
63041
|
import { fileURLToPath } from "node:url";
|
|
63022
63042
|
var import_express10 = __toESM(require_express2(), 1);
|
|
63023
63043
|
|
|
@@ -77848,7 +77868,8 @@ var configSchema = external_exports.object({
|
|
|
77848
77868
|
telegramUserId: external_exports.string().trim().min(1).nullable().default(null),
|
|
77849
77869
|
supabaseUrl: external_exports.string().trim().min(1).nullable().default(null),
|
|
77850
77870
|
supabaseAnonKey: external_exports.string().trim().min(1).nullable().default(null),
|
|
77851
|
-
sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD)
|
|
77871
|
+
sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD),
|
|
77872
|
+
pricingRefreshHours: external_exports.coerce.number().positive().default(24)
|
|
77852
77873
|
});
|
|
77853
77874
|
function parseConfigFile() {
|
|
77854
77875
|
if (!existsSync(CONFIG_PATH)) {
|
|
@@ -77901,6 +77922,9 @@ function readEnvOverrides() {
|
|
|
77901
77922
|
if (process.env.IO_SESSION_RESET_THRESHOLD !== void 0) {
|
|
77902
77923
|
overrides.sessionResetThreshold = process.env.IO_SESSION_RESET_THRESHOLD;
|
|
77903
77924
|
}
|
|
77925
|
+
if (process.env.IO_PRICING_REFRESH_HOURS !== void 0) {
|
|
77926
|
+
overrides.pricingRefreshHours = process.env.IO_PRICING_REFRESH_HOURS;
|
|
77927
|
+
}
|
|
77904
77928
|
return overrides;
|
|
77905
77929
|
}
|
|
77906
77930
|
function loadConfig() {
|
|
@@ -77922,7 +77946,8 @@ var APP_SETTING_KEYS = [
|
|
|
77922
77946
|
"telegramUserId",
|
|
77923
77947
|
"supabaseUrl",
|
|
77924
77948
|
"supabaseAnonKey",
|
|
77925
|
-
"sessionResetThreshold"
|
|
77949
|
+
"sessionResetThreshold",
|
|
77950
|
+
"pricingRefreshHours"
|
|
77926
77951
|
];
|
|
77927
77952
|
router5.get("/api/settings", async (_req, res) => {
|
|
77928
77953
|
try {
|
|
@@ -78034,11 +78059,16 @@ router6.get("/api/skills", async (_req, res) => {
|
|
|
78034
78059
|
router6.post("/api/skills/install", async (req, res) => {
|
|
78035
78060
|
try {
|
|
78036
78061
|
const body = req.body;
|
|
78037
|
-
if (!body?.url && !(body?.source && body?.slug)) {
|
|
78062
|
+
if (!body?.url && !(body?.source && (body?.slug || body?.skillId || body?.name))) {
|
|
78038
78063
|
res.status(400).json({ error: "Provide url or source+slug to install a skill" });
|
|
78039
78064
|
return;
|
|
78040
78065
|
}
|
|
78041
|
-
const
|
|
78066
|
+
const normalized = {
|
|
78067
|
+
url: body.url,
|
|
78068
|
+
source: body.source,
|
|
78069
|
+
slug: body.slug || body.skillId || body.name
|
|
78070
|
+
};
|
|
78071
|
+
const result = await installSkill(normalized);
|
|
78042
78072
|
res.status(result.created ? 201 : 200).json(result.skill);
|
|
78043
78073
|
} catch (error51) {
|
|
78044
78074
|
const statusCode = error51 instanceof Error && /Invalid skill|fetch/i.test(error51.message) ? 400 : 500;
|
|
@@ -78063,8 +78093,25 @@ router6.delete("/api/skills/:id", async (req, res) => {
|
|
|
78063
78093
|
});
|
|
78064
78094
|
}
|
|
78065
78095
|
});
|
|
78066
|
-
router6.get("/api/skills/discover", async (
|
|
78067
|
-
|
|
78096
|
+
router6.get("/api/skills/discover", async (req, res) => {
|
|
78097
|
+
try {
|
|
78098
|
+
const source = typeof req.query.source === "string" ? req.query.source : "";
|
|
78099
|
+
const query = typeof req.query.q === "string" ? req.query.q.trim() : "";
|
|
78100
|
+
if (source === "skillssh") {
|
|
78101
|
+
const skills = await discoverSkillsSh(query);
|
|
78102
|
+
res.status(200).json(skills);
|
|
78103
|
+
} else if (source === "awesome-copilot") {
|
|
78104
|
+
const skills = await discoverAwesomeCopilot(query);
|
|
78105
|
+
res.status(200).json(skills);
|
|
78106
|
+
} else {
|
|
78107
|
+
res.status(400).json({ error: `Unknown source: ${source}` });
|
|
78108
|
+
}
|
|
78109
|
+
} catch (error51) {
|
|
78110
|
+
res.status(500).json({
|
|
78111
|
+
error: "Failed to discover skills",
|
|
78112
|
+
details: error51 instanceof Error ? error51.message : "Unknown error"
|
|
78113
|
+
});
|
|
78114
|
+
}
|
|
78068
78115
|
});
|
|
78069
78116
|
async function installSkill(request) {
|
|
78070
78117
|
await mkdir3(SKILLS_DIR, { recursive: true });
|
|
@@ -78076,13 +78123,20 @@ async function installSkill(request) {
|
|
|
78076
78123
|
const directory = join2(SKILLS_DIR, directoryName);
|
|
78077
78124
|
const existing = installedSkills.find((skill2) => skill2.id === id);
|
|
78078
78125
|
let entryFile = existing?.entryFile ?? null;
|
|
78079
|
-
|
|
78080
|
-
|
|
78126
|
+
let resolvedUrl = request.url;
|
|
78127
|
+
if (source === "skillssh" && resolvedUrl) {
|
|
78128
|
+
const fetchResult = await fetch(resolvedUrl);
|
|
78129
|
+
if (!fetchResult.ok) {
|
|
78130
|
+
resolvedUrl = await resolveSkillsShUrl(request) ?? resolvedUrl;
|
|
78131
|
+
}
|
|
78132
|
+
}
|
|
78133
|
+
if (resolvedUrl) {
|
|
78134
|
+
const response = await fetch(resolvedUrl);
|
|
78081
78135
|
if (!response.ok) {
|
|
78082
|
-
throw new Error(`Failed to fetch skill from ${
|
|
78136
|
+
throw new Error(`Failed to fetch skill from ${resolvedUrl} (${response.status})`);
|
|
78083
78137
|
}
|
|
78084
78138
|
const body = await response.text();
|
|
78085
|
-
entryFile = chooseEntryFileName(
|
|
78139
|
+
entryFile = chooseEntryFileName(resolvedUrl, response.headers.get("content-type"));
|
|
78086
78140
|
await mkdir3(directory, { recursive: true });
|
|
78087
78141
|
await writeFile2(join2(directory, entryFile), body, "utf8");
|
|
78088
78142
|
} else {
|
|
@@ -78169,6 +78223,115 @@ function chooseEntryFileName(url2, contentType) {
|
|
|
78169
78223
|
function isMissingFileError2(error51) {
|
|
78170
78224
|
return !!error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT";
|
|
78171
78225
|
}
|
|
78226
|
+
async function resolveSkillsShUrl(request) {
|
|
78227
|
+
const slug = request.slug?.trim();
|
|
78228
|
+
if (!slug) return null;
|
|
78229
|
+
try {
|
|
78230
|
+
const searchResponse = await fetch(
|
|
78231
|
+
`https://skills.sh/api/search?q=${encodeURIComponent(slug)}&limit=10`,
|
|
78232
|
+
{ signal: AbortSignal.timeout(1e4) }
|
|
78233
|
+
);
|
|
78234
|
+
if (!searchResponse.ok) return null;
|
|
78235
|
+
const data = await searchResponse.json();
|
|
78236
|
+
const match = data.skills?.find(
|
|
78237
|
+
(s) => s.skillId === slug || s.name === slug || s.id.endsWith(`/${slug}`)
|
|
78238
|
+
);
|
|
78239
|
+
if (!match?.source) return null;
|
|
78240
|
+
const headers = {
|
|
78241
|
+
Accept: "application/vnd.github.v3+json",
|
|
78242
|
+
"User-Agent": "io-daemon"
|
|
78243
|
+
};
|
|
78244
|
+
if (process.env.GITHUB_TOKEN) {
|
|
78245
|
+
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
78246
|
+
}
|
|
78247
|
+
const treeResponse = await fetch(
|
|
78248
|
+
`https://api.github.com/repos/${match.source}/git/trees/main?recursive=1`,
|
|
78249
|
+
{ headers, signal: AbortSignal.timeout(15e3) }
|
|
78250
|
+
);
|
|
78251
|
+
if (!treeResponse.ok) return null;
|
|
78252
|
+
const treeData = await treeResponse.json();
|
|
78253
|
+
const skillFile = treeData.tree?.find(
|
|
78254
|
+
(entry) => entry.type === "blob" && entry.path.endsWith(`/${match.skillId}/SKILL.md`)
|
|
78255
|
+
);
|
|
78256
|
+
if (!skillFile) return null;
|
|
78257
|
+
return `https://raw.githubusercontent.com/${match.source}/main/${skillFile.path}`;
|
|
78258
|
+
} catch {
|
|
78259
|
+
return null;
|
|
78260
|
+
}
|
|
78261
|
+
}
|
|
78262
|
+
async function discoverSkillsSh(query) {
|
|
78263
|
+
if (!query) return [];
|
|
78264
|
+
const endpoint = `https://skills.sh/api/search?q=${encodeURIComponent(query)}&limit=50`;
|
|
78265
|
+
const response = await fetch(endpoint, { signal: AbortSignal.timeout(1e4) });
|
|
78266
|
+
if (!response.ok) {
|
|
78267
|
+
throw new Error(`skills.sh returned ${response.status}`);
|
|
78268
|
+
}
|
|
78269
|
+
const data = await response.json();
|
|
78270
|
+
const installedSkills = await readInstalledSkills();
|
|
78271
|
+
const installedIds = new Set(installedSkills.map((s) => s.id));
|
|
78272
|
+
return (data.skills ?? []).map((entry) => {
|
|
78273
|
+
const skillUrl = entry.source ? `https://raw.githubusercontent.com/${entry.source}/main/skills/${entry.skillId}/SKILL.md` : "";
|
|
78274
|
+
return {
|
|
78275
|
+
name: entry.name || entry.skillId,
|
|
78276
|
+
title: entry.name || entry.skillId,
|
|
78277
|
+
description: `${entry.source ?? ""}`,
|
|
78278
|
+
url: skillUrl,
|
|
78279
|
+
source: "skillssh",
|
|
78280
|
+
installed: installedIds.has(`skillssh:${normalizeSlug(entry.skillId || entry.name)}`),
|
|
78281
|
+
registrySource: entry.source ?? void 0,
|
|
78282
|
+
skillId: entry.skillId ?? void 0,
|
|
78283
|
+
installs: entry.installs ?? 0
|
|
78284
|
+
};
|
|
78285
|
+
});
|
|
78286
|
+
}
|
|
78287
|
+
var awesomeCopilotCache = null;
|
|
78288
|
+
var AWESOME_COPILOT_CACHE_TTL = 60 * 60 * 1e3;
|
|
78289
|
+
async function discoverAwesomeCopilot(query) {
|
|
78290
|
+
const now = Date.now();
|
|
78291
|
+
if (!awesomeCopilotCache || now - awesomeCopilotCache.fetchedAt > AWESOME_COPILOT_CACHE_TTL) {
|
|
78292
|
+
awesomeCopilotCache = { skills: await fetchAwesomeCopilotList(), fetchedAt: now };
|
|
78293
|
+
}
|
|
78294
|
+
const skills = awesomeCopilotCache.skills;
|
|
78295
|
+
if (!query) return skills;
|
|
78296
|
+
const needle = query.toLowerCase();
|
|
78297
|
+
return skills.filter(
|
|
78298
|
+
(s) => s.name.toLowerCase().includes(needle) || s.title.toLowerCase().includes(needle) || s.description.toLowerCase().includes(needle)
|
|
78299
|
+
);
|
|
78300
|
+
}
|
|
78301
|
+
async function fetchAwesomeCopilotList() {
|
|
78302
|
+
const treeUrl = "https://api.github.com/repos/github/awesome-copilot/git/trees/main?recursive=1";
|
|
78303
|
+
const headers = {
|
|
78304
|
+
Accept: "application/vnd.github.v3+json",
|
|
78305
|
+
"User-Agent": "io-daemon"
|
|
78306
|
+
};
|
|
78307
|
+
if (process.env.GITHUB_TOKEN) {
|
|
78308
|
+
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
78309
|
+
}
|
|
78310
|
+
const response = await fetch(treeUrl, { headers, signal: AbortSignal.timeout(15e3) });
|
|
78311
|
+
if (!response.ok) {
|
|
78312
|
+
throw new Error(`GitHub API returned ${response.status}`);
|
|
78313
|
+
}
|
|
78314
|
+
const data = await response.json();
|
|
78315
|
+
const installedSkills = await readInstalledSkills();
|
|
78316
|
+
const installedIds = new Set(installedSkills.map((s) => s.id));
|
|
78317
|
+
const skillEntries = (data.tree ?? []).filter(
|
|
78318
|
+
(entry) => entry.type === "blob" && entry.path.startsWith("skills/") && entry.path.endsWith("/SKILL.md")
|
|
78319
|
+
);
|
|
78320
|
+
return skillEntries.map((entry) => {
|
|
78321
|
+
const parts = entry.path.split("/");
|
|
78322
|
+
const skillName = parts[1] ?? "unknown";
|
|
78323
|
+
const slug = normalizeSlug(skillName);
|
|
78324
|
+
const rawUrl = `https://raw.githubusercontent.com/github/awesome-copilot/main/${entry.path}`;
|
|
78325
|
+
return {
|
|
78326
|
+
name: skillName,
|
|
78327
|
+
title: skillName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
78328
|
+
description: "From github/awesome-copilot",
|
|
78329
|
+
url: rawUrl,
|
|
78330
|
+
source: "awesome-copilot",
|
|
78331
|
+
installed: installedIds.has(`awesome-copilot:${slug}`)
|
|
78332
|
+
};
|
|
78333
|
+
});
|
|
78334
|
+
}
|
|
78172
78335
|
|
|
78173
78336
|
// packages/daemon/src/api/routes/squads.ts
|
|
78174
78337
|
var import_express7 = __toESM(require_express2(), 1);
|
|
@@ -79015,6 +79178,9 @@ function createApiServer(config2) {
|
|
|
79015
79178
|
});
|
|
79016
79179
|
if (existsSync2(webDirectory)) {
|
|
79017
79180
|
app.use(import_express10.default.static(webDirectory));
|
|
79181
|
+
app.get("*", (_req, res) => {
|
|
79182
|
+
res.sendFile(join4(webDirectory, "index.html"));
|
|
79183
|
+
});
|
|
79018
79184
|
}
|
|
79019
79185
|
app.use((error51, _req, res, next) => {
|
|
79020
79186
|
if (res.headersSent) {
|
|
@@ -79061,8 +79227,8 @@ function createApiServer(config2) {
|
|
|
79061
79227
|
|
|
79062
79228
|
// packages/daemon/src/data-dir.ts
|
|
79063
79229
|
import { mkdirSync } from "node:fs";
|
|
79064
|
-
import { join as
|
|
79065
|
-
var WIKI_PAGES_DIR =
|
|
79230
|
+
import { join as join5 } from "node:path";
|
|
79231
|
+
var WIKI_PAGES_DIR = join5(WIKI_DIR, "pages");
|
|
79066
79232
|
function ensureDataDirectories() {
|
|
79067
79233
|
for (const directory of [DATA_DIR, WIKI_DIR, WIKI_PAGES_DIR, SKILLS_DIR, LOGS_DIR]) {
|
|
79068
79234
|
mkdirSync(directory, { recursive: true });
|
|
@@ -79070,10 +79236,10 @@ function ensureDataDirectories() {
|
|
|
79070
79236
|
}
|
|
79071
79237
|
|
|
79072
79238
|
// packages/daemon/src/logging/logger.ts
|
|
79073
|
-
import { join as
|
|
79239
|
+
import { join as join6 } from "node:path";
|
|
79074
79240
|
var import_pino = __toESM(require_pino(), 1);
|
|
79075
79241
|
var import_pino_pretty = __toESM(require_pino_pretty(), 1);
|
|
79076
|
-
var LOG_FILE_PATH =
|
|
79242
|
+
var LOG_FILE_PATH = join6(LOGS_DIR, "io.log");
|
|
79077
79243
|
var rootLogger = null;
|
|
79078
79244
|
function shouldPrettyPrint(logLevel) {
|
|
79079
79245
|
return process.env.NODE_ENV !== "production" || process.env.LOG_LEVEL === "debug" || logLevel === "debug" || logLevel === "trace";
|
|
@@ -79114,6 +79280,448 @@ function createLogger(name) {
|
|
|
79114
79280
|
return getRootLogger().child({ subsystem: name });
|
|
79115
79281
|
}
|
|
79116
79282
|
|
|
79283
|
+
// packages/daemon/src/models/catalog.ts
|
|
79284
|
+
import { execFileSync } from "node:child_process";
|
|
79285
|
+
var CATALOG_URL = "https://models.github.ai/catalog/models";
|
|
79286
|
+
function resolveGitHubToken() {
|
|
79287
|
+
const envToken = process.env.GITHUB_TOKEN?.trim();
|
|
79288
|
+
if (envToken && envToken.length > 0) {
|
|
79289
|
+
return envToken;
|
|
79290
|
+
}
|
|
79291
|
+
try {
|
|
79292
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
79293
|
+
encoding: "utf8",
|
|
79294
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
79295
|
+
}).trim();
|
|
79296
|
+
return token.length > 0 ? token : void 0;
|
|
79297
|
+
} catch {
|
|
79298
|
+
return void 0;
|
|
79299
|
+
}
|
|
79300
|
+
}
|
|
79301
|
+
async function fetchModelCatalog() {
|
|
79302
|
+
const token = resolveGitHubToken();
|
|
79303
|
+
if (!token) {
|
|
79304
|
+
throw new Error("No GitHub token available for model catalog fetch");
|
|
79305
|
+
}
|
|
79306
|
+
const response = await fetch(CATALOG_URL, {
|
|
79307
|
+
headers: {
|
|
79308
|
+
Accept: "application/json",
|
|
79309
|
+
Authorization: `Bearer ${token}`,
|
|
79310
|
+
"X-GitHub-Api-Version": "2024-12-01"
|
|
79311
|
+
}
|
|
79312
|
+
});
|
|
79313
|
+
if (!response.ok) {
|
|
79314
|
+
throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
|
|
79315
|
+
}
|
|
79316
|
+
const data = await response.json();
|
|
79317
|
+
if (!Array.isArray(data)) {
|
|
79318
|
+
throw new Error("Model catalog response is not an array");
|
|
79319
|
+
}
|
|
79320
|
+
const models = [];
|
|
79321
|
+
for (const entry of data) {
|
|
79322
|
+
const id = entry.id ?? entry.name;
|
|
79323
|
+
const displayName = entry.friendly_name ?? entry.name ?? id;
|
|
79324
|
+
if (id && typeof id === "string") {
|
|
79325
|
+
models.push({ id, displayName: displayName ?? id });
|
|
79326
|
+
}
|
|
79327
|
+
}
|
|
79328
|
+
return models;
|
|
79329
|
+
}
|
|
79330
|
+
|
|
79331
|
+
// packages/daemon/src/models/pricing-scraper.ts
|
|
79332
|
+
var TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
|
|
79333
|
+
var PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
|
|
79334
|
+
async function scrapeTokenUnitPricing() {
|
|
79335
|
+
const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
|
|
79336
|
+
return parseTokenUnitTable(html);
|
|
79337
|
+
}
|
|
79338
|
+
async function scrapePremiumRequestPricing() {
|
|
79339
|
+
const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
|
|
79340
|
+
return parsePremiumMultiplierTable(html);
|
|
79341
|
+
}
|
|
79342
|
+
async function fetchPage(url2) {
|
|
79343
|
+
const response = await fetch(url2, {
|
|
79344
|
+
headers: {
|
|
79345
|
+
Accept: "text/html",
|
|
79346
|
+
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
79347
|
+
},
|
|
79348
|
+
redirect: "follow"
|
|
79349
|
+
});
|
|
79350
|
+
if (!response.ok) {
|
|
79351
|
+
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
79352
|
+
}
|
|
79353
|
+
return response.text();
|
|
79354
|
+
}
|
|
79355
|
+
function parseTokenUnitTable(html) {
|
|
79356
|
+
const results = [];
|
|
79357
|
+
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
|
|
79358
|
+
for (const match of html.matchAll(tableRowPattern)) {
|
|
79359
|
+
const modelName = cleanCellText(match[1]);
|
|
79360
|
+
const inputMultiplier = Number.parseFloat(match[2]);
|
|
79361
|
+
const cachedInput = match[3].trim().toLowerCase();
|
|
79362
|
+
const outputMultiplier = Number.parseFloat(match[4]);
|
|
79363
|
+
if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
|
|
79364
|
+
results.push({
|
|
79365
|
+
modelName,
|
|
79366
|
+
inputMultiplier,
|
|
79367
|
+
cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
|
|
79368
|
+
outputMultiplier
|
|
79369
|
+
});
|
|
79370
|
+
}
|
|
79371
|
+
}
|
|
79372
|
+
return results;
|
|
79373
|
+
}
|
|
79374
|
+
function parsePremiumMultiplierTable(html) {
|
|
79375
|
+
const results = [];
|
|
79376
|
+
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
|
|
79377
|
+
for (const match of html.matchAll(tableRowPattern)) {
|
|
79378
|
+
const modelName = cleanCellText(match[1]);
|
|
79379
|
+
const multiplier = Number.parseFloat(match[2]);
|
|
79380
|
+
if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
|
|
79381
|
+
results.push({ modelName, multiplier });
|
|
79382
|
+
}
|
|
79383
|
+
}
|
|
79384
|
+
return results;
|
|
79385
|
+
}
|
|
79386
|
+
function cleanCellText(text) {
|
|
79387
|
+
return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
|
|
79388
|
+
}
|
|
79389
|
+
|
|
79390
|
+
// packages/daemon/src/models/seed.ts
|
|
79391
|
+
var SEED_MODELS = [
|
|
79392
|
+
{
|
|
79393
|
+
id: "gpt-4o-mini",
|
|
79394
|
+
displayName: "OpenAI GPT-4o mini",
|
|
79395
|
+
premiumMultiplier: 0.33,
|
|
79396
|
+
tokenInputMultiplier: 0.015,
|
|
79397
|
+
tokenOutputMultiplier: 0.06,
|
|
79398
|
+
cachedInputMultiplier: 75e-4,
|
|
79399
|
+
tier: "trivial",
|
|
79400
|
+
available: true
|
|
79401
|
+
},
|
|
79402
|
+
{
|
|
79403
|
+
id: "gpt-4o",
|
|
79404
|
+
displayName: "OpenAI GPT-4o",
|
|
79405
|
+
premiumMultiplier: 0.33,
|
|
79406
|
+
tokenInputMultiplier: 0.25,
|
|
79407
|
+
tokenOutputMultiplier: 1,
|
|
79408
|
+
cachedInputMultiplier: 0.125,
|
|
79409
|
+
tier: "trivial",
|
|
79410
|
+
available: true
|
|
79411
|
+
},
|
|
79412
|
+
{
|
|
79413
|
+
id: "gpt-5-mini",
|
|
79414
|
+
displayName: "GPT-5 mini",
|
|
79415
|
+
premiumMultiplier: 0.33,
|
|
79416
|
+
tokenInputMultiplier: null,
|
|
79417
|
+
tokenOutputMultiplier: null,
|
|
79418
|
+
cachedInputMultiplier: null,
|
|
79419
|
+
tier: "trivial",
|
|
79420
|
+
available: true
|
|
79421
|
+
},
|
|
79422
|
+
{
|
|
79423
|
+
id: "claude-sonnet-4",
|
|
79424
|
+
displayName: "Claude Sonnet 4",
|
|
79425
|
+
premiumMultiplier: 6,
|
|
79426
|
+
tokenInputMultiplier: 0.6,
|
|
79427
|
+
tokenOutputMultiplier: 2.4,
|
|
79428
|
+
cachedInputMultiplier: null,
|
|
79429
|
+
tier: "premium",
|
|
79430
|
+
available: true
|
|
79431
|
+
},
|
|
79432
|
+
{
|
|
79433
|
+
id: "claude-haiku-4.5",
|
|
79434
|
+
displayName: "Claude Haiku 4.5",
|
|
79435
|
+
premiumMultiplier: 0.33,
|
|
79436
|
+
tokenInputMultiplier: null,
|
|
79437
|
+
tokenOutputMultiplier: null,
|
|
79438
|
+
cachedInputMultiplier: null,
|
|
79439
|
+
tier: "trivial",
|
|
79440
|
+
available: true
|
|
79441
|
+
},
|
|
79442
|
+
{
|
|
79443
|
+
id: "gemini-2.5-pro",
|
|
79444
|
+
displayName: "Gemini 2.5 Pro",
|
|
79445
|
+
premiumMultiplier: 1,
|
|
79446
|
+
tokenInputMultiplier: null,
|
|
79447
|
+
tokenOutputMultiplier: null,
|
|
79448
|
+
cachedInputMultiplier: null,
|
|
79449
|
+
tier: "fast",
|
|
79450
|
+
available: true
|
|
79451
|
+
},
|
|
79452
|
+
{
|
|
79453
|
+
id: "gpt-5.1",
|
|
79454
|
+
displayName: "GPT-5.1",
|
|
79455
|
+
premiumMultiplier: 3,
|
|
79456
|
+
tokenInputMultiplier: null,
|
|
79457
|
+
tokenOutputMultiplier: null,
|
|
79458
|
+
cachedInputMultiplier: null,
|
|
79459
|
+
tier: "standard",
|
|
79460
|
+
available: true
|
|
79461
|
+
},
|
|
79462
|
+
{
|
|
79463
|
+
id: "gpt-5.2",
|
|
79464
|
+
displayName: "GPT-5.2",
|
|
79465
|
+
premiumMultiplier: 3,
|
|
79466
|
+
tokenInputMultiplier: null,
|
|
79467
|
+
tokenOutputMultiplier: null,
|
|
79468
|
+
cachedInputMultiplier: null,
|
|
79469
|
+
tier: "standard",
|
|
79470
|
+
available: true
|
|
79471
|
+
},
|
|
79472
|
+
{
|
|
79473
|
+
id: "gpt-5.4",
|
|
79474
|
+
displayName: "GPT-5.4",
|
|
79475
|
+
premiumMultiplier: 6,
|
|
79476
|
+
tokenInputMultiplier: null,
|
|
79477
|
+
tokenOutputMultiplier: null,
|
|
79478
|
+
cachedInputMultiplier: null,
|
|
79479
|
+
tier: "premium",
|
|
79480
|
+
available: true
|
|
79481
|
+
},
|
|
79482
|
+
{
|
|
79483
|
+
id: "claude-opus-4.5",
|
|
79484
|
+
displayName: "Claude Opus 4.5",
|
|
79485
|
+
premiumMultiplier: 15,
|
|
79486
|
+
tokenInputMultiplier: null,
|
|
79487
|
+
tokenOutputMultiplier: null,
|
|
79488
|
+
cachedInputMultiplier: null,
|
|
79489
|
+
tier: "premium",
|
|
79490
|
+
available: true
|
|
79491
|
+
},
|
|
79492
|
+
{
|
|
79493
|
+
id: "claude-opus-4.6",
|
|
79494
|
+
displayName: "Claude Opus 4.6",
|
|
79495
|
+
premiumMultiplier: 27,
|
|
79496
|
+
tokenInputMultiplier: null,
|
|
79497
|
+
tokenOutputMultiplier: null,
|
|
79498
|
+
cachedInputMultiplier: null,
|
|
79499
|
+
tier: "ultra",
|
|
79500
|
+
available: true
|
|
79501
|
+
},
|
|
79502
|
+
{
|
|
79503
|
+
id: "claude-opus-4.7",
|
|
79504
|
+
displayName: "Claude Opus 4.7",
|
|
79505
|
+
premiumMultiplier: 27,
|
|
79506
|
+
tokenInputMultiplier: null,
|
|
79507
|
+
tokenOutputMultiplier: null,
|
|
79508
|
+
cachedInputMultiplier: null,
|
|
79509
|
+
tier: "ultra",
|
|
79510
|
+
available: true
|
|
79511
|
+
},
|
|
79512
|
+
{
|
|
79513
|
+
id: "gpt-5.5",
|
|
79514
|
+
displayName: "GPT-5.5",
|
|
79515
|
+
premiumMultiplier: 57,
|
|
79516
|
+
tokenInputMultiplier: null,
|
|
79517
|
+
tokenOutputMultiplier: null,
|
|
79518
|
+
cachedInputMultiplier: null,
|
|
79519
|
+
tier: "ultra",
|
|
79520
|
+
available: true
|
|
79521
|
+
}
|
|
79522
|
+
];
|
|
79523
|
+
|
|
79524
|
+
// packages/daemon/src/models/types.ts
|
|
79525
|
+
var TIER_RANGES = {
|
|
79526
|
+
trivial: { min: 0, max: 0.33 },
|
|
79527
|
+
fast: { min: 0.34, max: 1 },
|
|
79528
|
+
standard: { min: 1.1, max: 5 },
|
|
79529
|
+
premium: { min: 5.1, max: 15 },
|
|
79530
|
+
ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
|
|
79531
|
+
};
|
|
79532
|
+
function computeTierFromMultiplier(premiumMultiplier) {
|
|
79533
|
+
if (premiumMultiplier === null) {
|
|
79534
|
+
return "standard";
|
|
79535
|
+
}
|
|
79536
|
+
for (const [tier, range] of Object.entries(TIER_RANGES)) {
|
|
79537
|
+
if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
|
|
79538
|
+
return tier;
|
|
79539
|
+
}
|
|
79540
|
+
}
|
|
79541
|
+
return "ultra";
|
|
79542
|
+
}
|
|
79543
|
+
var TOKEN_UNIT_PRICE = 1e-5;
|
|
79544
|
+
|
|
79545
|
+
// packages/daemon/src/models/registry.ts
|
|
79546
|
+
function normalizeModelName(name) {
|
|
79547
|
+
return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
|
|
79548
|
+
}
|
|
79549
|
+
async function fetchCatalogIntoMap(modelMap, result, logger) {
|
|
79550
|
+
try {
|
|
79551
|
+
const catalogModels = await fetchModelCatalog();
|
|
79552
|
+
result.catalogFetched = true;
|
|
79553
|
+
for (const m of catalogModels) {
|
|
79554
|
+
const key = normalizeModelName(m.id);
|
|
79555
|
+
modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
|
|
79556
|
+
}
|
|
79557
|
+
} catch (error51) {
|
|
79558
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
79559
|
+
result.errors.push(`Catalog fetch failed: ${msg}`);
|
|
79560
|
+
logger?.warn(`Model catalog fetch failed: ${msg}`);
|
|
79561
|
+
}
|
|
79562
|
+
}
|
|
79563
|
+
async function scrapeTokenPricingIntoMap(modelMap, result, logger) {
|
|
79564
|
+
try {
|
|
79565
|
+
const tokenPricing = await scrapeTokenUnitPricing();
|
|
79566
|
+
result.tokenPricingScraped = true;
|
|
79567
|
+
for (const tp of tokenPricing) {
|
|
79568
|
+
const key = normalizeModelName(tp.modelName);
|
|
79569
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
79570
|
+
if (existing) {
|
|
79571
|
+
existing.tokenInputMultiplier = tp.inputMultiplier;
|
|
79572
|
+
existing.tokenOutputMultiplier = tp.outputMultiplier;
|
|
79573
|
+
existing.cachedInputMultiplier = tp.cachedInputMultiplier;
|
|
79574
|
+
} else {
|
|
79575
|
+
modelMap.set(key, {
|
|
79576
|
+
id: key,
|
|
79577
|
+
displayName: tp.modelName,
|
|
79578
|
+
tokenInputMultiplier: tp.inputMultiplier,
|
|
79579
|
+
tokenOutputMultiplier: tp.outputMultiplier,
|
|
79580
|
+
cachedInputMultiplier: tp.cachedInputMultiplier,
|
|
79581
|
+
available: true
|
|
79582
|
+
});
|
|
79583
|
+
}
|
|
79584
|
+
}
|
|
79585
|
+
} catch (error51) {
|
|
79586
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
79587
|
+
result.errors.push(`Token pricing scrape failed: ${msg}`);
|
|
79588
|
+
logger?.warn(`Token pricing scrape failed: ${msg}`);
|
|
79589
|
+
}
|
|
79590
|
+
}
|
|
79591
|
+
async function scrapePremiumPricingIntoMap(modelMap, result, logger) {
|
|
79592
|
+
try {
|
|
79593
|
+
const premiumPricing = await scrapePremiumRequestPricing();
|
|
79594
|
+
result.premiumPricingScraped = true;
|
|
79595
|
+
for (const pp of premiumPricing) {
|
|
79596
|
+
const key = normalizeModelName(pp.modelName);
|
|
79597
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
79598
|
+
if (existing) {
|
|
79599
|
+
existing.premiumMultiplier = pp.multiplier;
|
|
79600
|
+
} else {
|
|
79601
|
+
modelMap.set(key, {
|
|
79602
|
+
id: key,
|
|
79603
|
+
displayName: pp.modelName,
|
|
79604
|
+
premiumMultiplier: pp.multiplier,
|
|
79605
|
+
available: true
|
|
79606
|
+
});
|
|
79607
|
+
}
|
|
79608
|
+
}
|
|
79609
|
+
} catch (error51) {
|
|
79610
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
79611
|
+
result.errors.push(`Premium pricing scrape failed: ${msg}`);
|
|
79612
|
+
logger?.warn(`Premium pricing scrape failed: ${msg}`);
|
|
79613
|
+
}
|
|
79614
|
+
}
|
|
79615
|
+
async function refreshModelPricing(logger) {
|
|
79616
|
+
const result = {
|
|
79617
|
+
modelsUpdated: 0,
|
|
79618
|
+
catalogFetched: false,
|
|
79619
|
+
tokenPricingScraped: false,
|
|
79620
|
+
premiumPricingScraped: false,
|
|
79621
|
+
errors: []
|
|
79622
|
+
};
|
|
79623
|
+
const modelMap = /* @__PURE__ */ new Map();
|
|
79624
|
+
await fetchCatalogIntoMap(modelMap, result, logger);
|
|
79625
|
+
await scrapeTokenPricingIntoMap(modelMap, result, logger);
|
|
79626
|
+
await scrapePremiumPricingIntoMap(modelMap, result, logger);
|
|
79627
|
+
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped) {
|
|
79628
|
+
logger?.warn("All pricing sources failed, using seed data");
|
|
79629
|
+
await seedFromFallback();
|
|
79630
|
+
result.modelsUpdated = SEED_MODELS.length;
|
|
79631
|
+
return result;
|
|
79632
|
+
}
|
|
79633
|
+
const db = await getDatabase();
|
|
79634
|
+
const now = nowIso();
|
|
79635
|
+
for (const model of modelMap.values()) {
|
|
79636
|
+
const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
|
|
79637
|
+
await upsertModel(db, {
|
|
79638
|
+
id: model.id,
|
|
79639
|
+
displayName: model.displayName,
|
|
79640
|
+
premiumMultiplier: model.premiumMultiplier ?? null,
|
|
79641
|
+
tokenInputMultiplier: model.tokenInputMultiplier ?? null,
|
|
79642
|
+
tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
|
|
79643
|
+
cachedInputMultiplier: model.cachedInputMultiplier ?? null,
|
|
79644
|
+
tier,
|
|
79645
|
+
available: model.available ?? true,
|
|
79646
|
+
updatedAt: now
|
|
79647
|
+
});
|
|
79648
|
+
result.modelsUpdated++;
|
|
79649
|
+
}
|
|
79650
|
+
return result;
|
|
79651
|
+
}
|
|
79652
|
+
async function seedFromFallback() {
|
|
79653
|
+
const db = await getDatabase();
|
|
79654
|
+
const now = nowIso();
|
|
79655
|
+
for (const model of SEED_MODELS) {
|
|
79656
|
+
await upsertModel(db, { ...model, updatedAt: now });
|
|
79657
|
+
}
|
|
79658
|
+
}
|
|
79659
|
+
async function getModelPricing(modelId) {
|
|
79660
|
+
const db = await getDatabase();
|
|
79661
|
+
const result = await db.execute({
|
|
79662
|
+
sql: "SELECT * FROM model_pricing WHERE id = ?",
|
|
79663
|
+
args: [modelId]
|
|
79664
|
+
});
|
|
79665
|
+
if (result.rows.length === 0) {
|
|
79666
|
+
return null;
|
|
79667
|
+
}
|
|
79668
|
+
return rowToModelPricing(result.rows[0]);
|
|
79669
|
+
}
|
|
79670
|
+
function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
|
|
79671
|
+
if (inputMultiplier === null || outputMultiplier === null) {
|
|
79672
|
+
return 0;
|
|
79673
|
+
}
|
|
79674
|
+
const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
|
|
79675
|
+
return tokenUnits * TOKEN_UNIT_PRICE;
|
|
79676
|
+
}
|
|
79677
|
+
async function upsertModel(db, model) {
|
|
79678
|
+
await db.execute({
|
|
79679
|
+
sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
|
|
79680
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
79681
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
79682
|
+
display_name = excluded.display_name,
|
|
79683
|
+
premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
|
|
79684
|
+
token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
|
|
79685
|
+
token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
|
|
79686
|
+
cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
|
|
79687
|
+
tier = excluded.tier,
|
|
79688
|
+
available = excluded.available,
|
|
79689
|
+
updated_at = excluded.updated_at`,
|
|
79690
|
+
args: [
|
|
79691
|
+
model.id,
|
|
79692
|
+
model.displayName,
|
|
79693
|
+
model.premiumMultiplier,
|
|
79694
|
+
model.tokenInputMultiplier,
|
|
79695
|
+
model.tokenOutputMultiplier,
|
|
79696
|
+
model.cachedInputMultiplier,
|
|
79697
|
+
model.tier,
|
|
79698
|
+
model.available ? 1 : 0,
|
|
79699
|
+
model.updatedAt
|
|
79700
|
+
]
|
|
79701
|
+
});
|
|
79702
|
+
}
|
|
79703
|
+
function rowToModelPricing(row) {
|
|
79704
|
+
return {
|
|
79705
|
+
id: asString(row.id),
|
|
79706
|
+
displayName: asString(row.display_name),
|
|
79707
|
+
premiumMultiplier: asNullableNumber(row.premium_multiplier),
|
|
79708
|
+
tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
|
|
79709
|
+
tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
|
|
79710
|
+
cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
|
|
79711
|
+
tier: asString(row.tier),
|
|
79712
|
+
available: asNumber(row.available) === 1,
|
|
79713
|
+
updatedAt: asString(row.updated_at)
|
|
79714
|
+
};
|
|
79715
|
+
}
|
|
79716
|
+
function findClosestKey(map2, targetKey) {
|
|
79717
|
+
for (const [key, value] of map2) {
|
|
79718
|
+
if (key.includes(targetKey) || targetKey.includes(key)) {
|
|
79719
|
+
return value;
|
|
79720
|
+
}
|
|
79721
|
+
}
|
|
79722
|
+
return void 0;
|
|
79723
|
+
}
|
|
79724
|
+
|
|
79117
79725
|
// packages/daemon/src/orchestrator/system-prompt.ts
|
|
79118
79726
|
function formatSquadRoster(squads) {
|
|
79119
79727
|
if (squads.length === 0) {
|
|
@@ -79206,151 +79814,18 @@ function buildFreshSessionContext(summary, wikiContext) {
|
|
|
79206
79814
|
return sections.join("\n\n");
|
|
79207
79815
|
}
|
|
79208
79816
|
|
|
79209
|
-
// packages/daemon/src/copilot/router.ts
|
|
79210
|
-
var FAST_KEYWORDS = [
|
|
79211
|
-
"hi",
|
|
79212
|
-
"hello",
|
|
79213
|
-
"hey",
|
|
79214
|
-
"thanks",
|
|
79215
|
-
"thank you",
|
|
79216
|
-
"status",
|
|
79217
|
-
"list",
|
|
79218
|
-
"show",
|
|
79219
|
-
"ping",
|
|
79220
|
-
"uptime",
|
|
79221
|
-
"what time",
|
|
79222
|
-
"ok",
|
|
79223
|
-
"okay"
|
|
79224
|
-
];
|
|
79225
|
-
var PREMIUM_KEYWORDS = [
|
|
79226
|
-
"architecture",
|
|
79227
|
-
"architect",
|
|
79228
|
-
"design",
|
|
79229
|
-
"tradeoff",
|
|
79230
|
-
"trade-off",
|
|
79231
|
-
"plan",
|
|
79232
|
-
"planning",
|
|
79233
|
-
"strategy",
|
|
79234
|
-
"roadmap",
|
|
79235
|
-
"migration",
|
|
79236
|
-
"refactor",
|
|
79237
|
-
"generate",
|
|
79238
|
-
"implement",
|
|
79239
|
-
"build",
|
|
79240
|
-
"codebase",
|
|
79241
|
-
"repository",
|
|
79242
|
-
"system prompt",
|
|
79243
|
-
"multi-step",
|
|
79244
|
-
"complex",
|
|
79245
|
-
"reason",
|
|
79246
|
-
"compare"
|
|
79247
|
-
];
|
|
79248
|
-
var SIMPLE_QUESTION_PREFIXES = [
|
|
79249
|
-
"is",
|
|
79250
|
-
"are",
|
|
79251
|
-
"do",
|
|
79252
|
-
"does",
|
|
79253
|
-
"did",
|
|
79254
|
-
"can",
|
|
79255
|
-
"could",
|
|
79256
|
-
"should",
|
|
79257
|
-
"would",
|
|
79258
|
-
"will"
|
|
79259
|
-
];
|
|
79260
|
-
var MODEL_SWITCH_COOLDOWN_MESSAGES = 3;
|
|
79261
|
-
var activeTier = null;
|
|
79262
|
-
var messagesSinceLastSwitch = MODEL_SWITCH_COOLDOWN_MESSAGES;
|
|
79263
|
-
function normalizeMessage(message2) {
|
|
79264
|
-
return message2.trim().toLowerCase();
|
|
79265
|
-
}
|
|
79266
|
-
function getWordCount(message2) {
|
|
79267
|
-
const normalized = message2.trim();
|
|
79268
|
-
if (normalized.length === 0) {
|
|
79269
|
-
return 0;
|
|
79270
|
-
}
|
|
79271
|
-
return normalized.split(/\s+/u).length;
|
|
79272
|
-
}
|
|
79273
|
-
function containsAny(message2, keywords) {
|
|
79274
|
-
return keywords.some((keyword) => message2.includes(keyword));
|
|
79275
|
-
}
|
|
79276
|
-
function startsWithSimpleQuestion(message2) {
|
|
79277
|
-
return SIMPLE_QUESTION_PREFIXES.some(
|
|
79278
|
-
(prefix) => message2 === prefix || message2.startsWith(`${prefix} `)
|
|
79279
|
-
);
|
|
79280
|
-
}
|
|
79281
|
-
function classifyMessage(message2) {
|
|
79282
|
-
const normalized = normalizeMessage(message2);
|
|
79283
|
-
const wordCount = getWordCount(normalized);
|
|
79284
|
-
if (normalized.length === 0) {
|
|
79285
|
-
return "fast";
|
|
79286
|
-
}
|
|
79287
|
-
if (containsAny(normalized, PREMIUM_KEYWORDS) || normalized.includes("```") || wordCount >= 80) {
|
|
79288
|
-
return "premium";
|
|
79289
|
-
}
|
|
79290
|
-
if (wordCount <= 20 && (containsAny(normalized, FAST_KEYWORDS) || startsWithSimpleQuestion(normalized) || /^(yes|no|sure|okay|ok|thanks)[!.?]*$/u.test(normalized))) {
|
|
79291
|
-
return "fast";
|
|
79292
|
-
}
|
|
79293
|
-
if (wordCount <= 8 && /^(what|when|where|who)\b/u.test(normalized)) {
|
|
79294
|
-
return "fast";
|
|
79295
|
-
}
|
|
79296
|
-
return "standard";
|
|
79297
|
-
}
|
|
79298
|
-
function getModelForTier(tier) {
|
|
79299
|
-
switch (tier) {
|
|
79300
|
-
case "fast":
|
|
79301
|
-
return FAST_MODEL;
|
|
79302
|
-
case "premium":
|
|
79303
|
-
return PREMIUM_MODEL;
|
|
79304
|
-
default:
|
|
79305
|
-
return STANDARD_MODEL;
|
|
79306
|
-
}
|
|
79307
|
-
}
|
|
79308
|
-
function routeMessage(message2) {
|
|
79309
|
-
const requestedTier = classifyMessage(message2);
|
|
79310
|
-
if (activeTier === null) {
|
|
79311
|
-
activeTier = requestedTier;
|
|
79312
|
-
messagesSinceLastSwitch = 0;
|
|
79313
|
-
return {
|
|
79314
|
-
requestedTier,
|
|
79315
|
-
effectiveTier: activeTier,
|
|
79316
|
-
model: getModelForTier(activeTier),
|
|
79317
|
-
switched: true,
|
|
79318
|
-
cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
|
|
79319
|
-
};
|
|
79320
|
-
}
|
|
79321
|
-
if (requestedTier !== activeTier && messagesSinceLastSwitch >= MODEL_SWITCH_COOLDOWN_MESSAGES) {
|
|
79322
|
-
activeTier = requestedTier;
|
|
79323
|
-
messagesSinceLastSwitch = 0;
|
|
79324
|
-
return {
|
|
79325
|
-
requestedTier,
|
|
79326
|
-
effectiveTier: activeTier,
|
|
79327
|
-
model: getModelForTier(activeTier),
|
|
79328
|
-
switched: true,
|
|
79329
|
-
cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
|
|
79330
|
-
};
|
|
79331
|
-
}
|
|
79332
|
-
messagesSinceLastSwitch += 1;
|
|
79333
|
-
return {
|
|
79334
|
-
requestedTier,
|
|
79335
|
-
effectiveTier: activeTier,
|
|
79336
|
-
model: getModelForTier(activeTier),
|
|
79337
|
-
switched: false,
|
|
79338
|
-
cooldownRemaining: Math.max(MODEL_SWITCH_COOLDOWN_MESSAGES - messagesSinceLastSwitch, 0)
|
|
79339
|
-
};
|
|
79340
|
-
}
|
|
79341
|
-
|
|
79342
79817
|
// packages/daemon/src/copilot/session.ts
|
|
79343
79818
|
import {
|
|
79344
79819
|
approveAll
|
|
79345
79820
|
} from "@github/copilot-sdk";
|
|
79346
79821
|
|
|
79347
79822
|
// packages/daemon/src/copilot/client.ts
|
|
79348
|
-
import { execFileSync } from "node:child_process";
|
|
79823
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
79349
79824
|
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
79350
|
-
import { join as
|
|
79825
|
+
import { join as join7 } from "node:path";
|
|
79351
79826
|
import { CopilotClient } from "@github/copilot-sdk";
|
|
79352
79827
|
var COPILOT_AUTH_ERROR_HINT = "Set GITHUB_TOKEN or authenticate with the GitHub CLI (`gh auth login`) so `gh auth token` returns a valid token.";
|
|
79353
|
-
var COPILOT_BASE_DIRECTORY =
|
|
79828
|
+
var COPILOT_BASE_DIRECTORY = join7(DATA_DIR, "copilot-sdk");
|
|
79354
79829
|
var copilotClientSingleton = null;
|
|
79355
79830
|
var copilotClientInitPromise = null;
|
|
79356
79831
|
function readTokenFromEnvironment() {
|
|
@@ -79359,7 +79834,7 @@ function readTokenFromEnvironment() {
|
|
|
79359
79834
|
}
|
|
79360
79835
|
function readTokenFromGhCli() {
|
|
79361
79836
|
try {
|
|
79362
|
-
const token =
|
|
79837
|
+
const token = execFileSync2("gh", ["auth", "token"], {
|
|
79363
79838
|
encoding: "utf8",
|
|
79364
79839
|
stdio: ["ignore", "pipe", "pipe"]
|
|
79365
79840
|
}).trim();
|
|
@@ -79368,7 +79843,7 @@ function readTokenFromGhCli() {
|
|
|
79368
79843
|
return void 0;
|
|
79369
79844
|
}
|
|
79370
79845
|
}
|
|
79371
|
-
function
|
|
79846
|
+
function resolveGitHubToken2() {
|
|
79372
79847
|
return readTokenFromEnvironment() ?? readTokenFromGhCli();
|
|
79373
79848
|
}
|
|
79374
79849
|
function buildClientOptions(token) {
|
|
@@ -79404,7 +79879,7 @@ async function initCopilotClient() {
|
|
|
79404
79879
|
}
|
|
79405
79880
|
copilotClientInitPromise = (async () => {
|
|
79406
79881
|
try {
|
|
79407
|
-
const token =
|
|
79882
|
+
const token = resolveGitHubToken2();
|
|
79408
79883
|
if (token === void 0) {
|
|
79409
79884
|
throw new Error(
|
|
79410
79885
|
`Unable to find a GitHub token for Copilot SDK authentication. ${COPILOT_AUTH_ERROR_HINT}`
|
|
@@ -79577,7 +80052,7 @@ async function sendMessage(session, message2, onChunk) {
|
|
|
79577
80052
|
// packages/daemon/src/skills/loader.ts
|
|
79578
80053
|
var import_gray_matter2 = __toESM(require_gray_matter(), 1);
|
|
79579
80054
|
import { readFile as readFile4, readdir as readdir2 } from "node:fs/promises";
|
|
79580
|
-
import { basename as basename3, dirname as dirname5, join as
|
|
80055
|
+
import { basename as basename3, dirname as dirname5, join as join8 } from "node:path";
|
|
79581
80056
|
async function scanSkills() {
|
|
79582
80057
|
const skillFilePaths = await collectSkillFiles(SKILLS_DIR);
|
|
79583
80058
|
const skills = await Promise.all(skillFilePaths.map((filePath) => readSkillFromFile(filePath)));
|
|
@@ -79588,7 +80063,7 @@ async function collectSkillFiles(directory) {
|
|
|
79588
80063
|
const entries = await readdir2(directory, { withFileTypes: true });
|
|
79589
80064
|
const skillFiles = [];
|
|
79590
80065
|
for (const entry of entries) {
|
|
79591
|
-
const entryPath =
|
|
80066
|
+
const entryPath = join8(directory, entry.name);
|
|
79592
80067
|
if (entry.isDirectory()) {
|
|
79593
80068
|
skillFiles.push(...await collectSkillFiles(entryPath));
|
|
79594
80069
|
continue;
|
|
@@ -79625,7 +80100,7 @@ function isMissingFileError4(error51) {
|
|
|
79625
80100
|
// packages/daemon/src/skills/manager.ts
|
|
79626
80101
|
var import_gray_matter3 = __toESM(require_gray_matter(), 1);
|
|
79627
80102
|
import { mkdir as mkdir5, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
79628
|
-
import { basename as basename4, dirname as dirname6, join as
|
|
80103
|
+
import { basename as basename4, dirname as dirname6, join as join9 } from "node:path";
|
|
79629
80104
|
async function installSkill2(url2) {
|
|
79630
80105
|
const response = await fetch(url2);
|
|
79631
80106
|
if (!response.ok) {
|
|
@@ -79635,8 +80110,8 @@ async function installSkill2(url2) {
|
|
|
79635
80110
|
}
|
|
79636
80111
|
const skillMarkdown = await response.text();
|
|
79637
80112
|
const skillId = getSkillIdFromUrl(url2);
|
|
79638
|
-
const skillDirectory =
|
|
79639
|
-
const skillPath =
|
|
80113
|
+
const skillDirectory = join9(SKILLS_DIR, skillId);
|
|
80114
|
+
const skillPath = join9(skillDirectory, "SKILL.md");
|
|
79640
80115
|
const parsed = (0, import_gray_matter3.default)(skillMarkdown);
|
|
79641
80116
|
const installedSkill = {
|
|
79642
80117
|
id: skillId,
|
|
@@ -79650,7 +80125,7 @@ async function installSkill2(url2) {
|
|
|
79650
80125
|
return installedSkill;
|
|
79651
80126
|
}
|
|
79652
80127
|
async function removeSkill2(id) {
|
|
79653
|
-
await rm3(
|
|
80128
|
+
await rm3(join9(SKILLS_DIR, id), { recursive: true, force: true });
|
|
79654
80129
|
const lockFile = await readSkillsLock();
|
|
79655
80130
|
lockFile.skills = lockFile.skills.filter((skill) => skill.id !== id);
|
|
79656
80131
|
await writeSkillsLock2(lockFile);
|
|
@@ -80139,7 +80614,7 @@ var executeSkillsToolCall = async (toolName, rawArgs) => {
|
|
|
80139
80614
|
// packages/daemon/src/execution/runner.ts
|
|
80140
80615
|
import { exec as exec7 } from "node:child_process";
|
|
80141
80616
|
import { access as access2 } from "node:fs/promises";
|
|
80142
|
-
import { basename as basename5, join as
|
|
80617
|
+
import { basename as basename5, join as join14 } from "node:path";
|
|
80143
80618
|
import { promisify as promisify7 } from "node:util";
|
|
80144
80619
|
|
|
80145
80620
|
// packages/daemon/src/squad/manager.ts
|
|
@@ -80171,7 +80646,7 @@ async function isSquadAvailable(squadId) {
|
|
|
80171
80646
|
// packages/daemon/src/execution/agent.ts
|
|
80172
80647
|
import { exec as exec2 } from "node:child_process";
|
|
80173
80648
|
import { mkdir as mkdir7, readFile as readFile7, readdir as readdir4, stat as stat3, writeFile as writeFile6 } from "node:fs/promises";
|
|
80174
|
-
import { dirname as dirname8, extname as extname3, isAbsolute, join as
|
|
80649
|
+
import { dirname as dirname8, extname as extname3, isAbsolute, join as join10, relative as relative3, resolve as resolve4 } from "node:path";
|
|
80175
80650
|
import { promisify as promisify2 } from "node:util";
|
|
80176
80651
|
import {
|
|
80177
80652
|
CopilotClient as CopilotClient2,
|
|
@@ -80232,13 +80707,24 @@ function mergeUsage(target, usage) {
|
|
|
80232
80707
|
}
|
|
80233
80708
|
async function persistUsage(member, usageEvents) {
|
|
80234
80709
|
for (const usage of usageEvents) {
|
|
80710
|
+
const model = usage.model;
|
|
80711
|
+
const pricing = await getModelPricing(model);
|
|
80712
|
+
const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
|
|
80713
|
+
const tokenUnitCost = pricing ? calculateTokenUnitCost(
|
|
80714
|
+
usage.inputTokens ?? 0,
|
|
80715
|
+
usage.outputTokens ?? 0,
|
|
80716
|
+
pricing.tokenInputMultiplier,
|
|
80717
|
+
pricing.tokenOutputMultiplier
|
|
80718
|
+
) : 0;
|
|
80235
80719
|
await recordUsage({
|
|
80236
80720
|
squadId: member.squadId,
|
|
80237
80721
|
agentId: member.id,
|
|
80238
|
-
model
|
|
80722
|
+
model,
|
|
80239
80723
|
inputTokens: usage.inputTokens ?? 0,
|
|
80240
80724
|
outputTokens: usage.outputTokens ?? 0,
|
|
80241
|
-
cost:
|
|
80725
|
+
cost: 0,
|
|
80726
|
+
premiumRequestCost,
|
|
80727
|
+
tokenUnitCost
|
|
80242
80728
|
});
|
|
80243
80729
|
}
|
|
80244
80730
|
}
|
|
@@ -80256,7 +80742,7 @@ async function collectFiles(directory, recursive, output) {
|
|
|
80256
80742
|
if (output.length >= MAX_LIST_RESULTS) {
|
|
80257
80743
|
return;
|
|
80258
80744
|
}
|
|
80259
|
-
const fullPath =
|
|
80745
|
+
const fullPath = join10(directory, entry.name);
|
|
80260
80746
|
output.push(fullPath);
|
|
80261
80747
|
if (recursive && entry.isDirectory()) {
|
|
80262
80748
|
await collectFiles(fullPath, recursive, output);
|
|
@@ -80456,7 +80942,7 @@ async function executeAgentTask(member, task, worktreePath, options2) {
|
|
|
80456
80942
|
client2 = new CopilotClient2({ workingDirectory: worktreePath });
|
|
80457
80943
|
await client2.start();
|
|
80458
80944
|
const session = await client2.createSession({
|
|
80459
|
-
model: member.model ??
|
|
80945
|
+
model: member.model ?? DEFAULT_MODEL,
|
|
80460
80946
|
workingDirectory: worktreePath,
|
|
80461
80947
|
tools,
|
|
80462
80948
|
availableTools: ["custom:*"],
|
|
@@ -80512,7 +80998,7 @@ Work only inside the current worktree. Summarize the concrete changes you made,
|
|
|
80512
80998
|
// packages/daemon/src/execution/planning.ts
|
|
80513
80999
|
import { exec as exec3 } from "node:child_process";
|
|
80514
81000
|
import { access, readFile as readFile8 } from "node:fs/promises";
|
|
80515
|
-
import { join as
|
|
81001
|
+
import { join as join11 } from "node:path";
|
|
80516
81002
|
import { promisify as promisify3 } from "node:util";
|
|
80517
81003
|
import { CopilotClient as CopilotClient3, approveAll as approveAll3 } from "@github/copilot-sdk";
|
|
80518
81004
|
|
|
@@ -80611,7 +81097,7 @@ async function fileExists(path) {
|
|
|
80611
81097
|
}
|
|
80612
81098
|
async function buildRepoContext(repoPath) {
|
|
80613
81099
|
for (const candidate of README_CANDIDATES) {
|
|
80614
|
-
const readmePath =
|
|
81100
|
+
const readmePath = join11(repoPath, candidate);
|
|
80615
81101
|
if (await fileExists(readmePath)) {
|
|
80616
81102
|
const content = await readFile8(readmePath, "utf8");
|
|
80617
81103
|
return content.slice(0, MAX_REPO_CONTEXT_LENGTH);
|
|
@@ -80697,7 +81183,7 @@ Return strict JSON in this shape:
|
|
|
80697
81183
|
client2 = new CopilotClient3({ workingDirectory: repoPath });
|
|
80698
81184
|
await client2.start();
|
|
80699
81185
|
const session = await client2.createSession({
|
|
80700
|
-
model:
|
|
81186
|
+
model: DEFAULT_MODEL,
|
|
80701
81187
|
workingDirectory: repoPath,
|
|
80702
81188
|
onPermissionRequest: approveAll3,
|
|
80703
81189
|
systemMessage: {
|
|
@@ -80734,13 +81220,13 @@ ${repoContext}`
|
|
|
80734
81220
|
// packages/daemon/src/execution/pr.ts
|
|
80735
81221
|
import { exec as exec5 } from "node:child_process";
|
|
80736
81222
|
import { rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
|
|
80737
|
-
import { join as
|
|
81223
|
+
import { join as join13 } from "node:path";
|
|
80738
81224
|
import { promisify as promisify5 } from "node:util";
|
|
80739
81225
|
|
|
80740
81226
|
// packages/daemon/src/execution/worktree.ts
|
|
80741
81227
|
import { exec as exec4 } from "node:child_process";
|
|
80742
81228
|
import { mkdir as mkdir8, rm as rm4 } from "node:fs/promises";
|
|
80743
|
-
import { join as
|
|
81229
|
+
import { join as join12 } from "node:path";
|
|
80744
81230
|
import { promisify as promisify4 } from "node:util";
|
|
80745
81231
|
var execAsync4 = promisify4(exec4);
|
|
80746
81232
|
var DEFAULT_REMOTE = "origin";
|
|
@@ -80760,7 +81246,7 @@ async function branchExists(repoPath, branchName) {
|
|
|
80760
81246
|
return output.length > 0;
|
|
80761
81247
|
}
|
|
80762
81248
|
function getWorktreePath(repoPath, branchName) {
|
|
80763
|
-
return
|
|
81249
|
+
return join12(repoPath, ".worktrees", sanitizeBranchName(branchName));
|
|
80764
81250
|
}
|
|
80765
81251
|
async function listWorktrees(repoPath) {
|
|
80766
81252
|
const output = await runGit("git worktree list --porcelain", repoPath);
|
|
@@ -80810,7 +81296,7 @@ async function listWorktrees(repoPath) {
|
|
|
80810
81296
|
}
|
|
80811
81297
|
async function createWorktree(repoPath, branchName, baseBranch) {
|
|
80812
81298
|
const worktreePath = getWorktreePath(repoPath, branchName);
|
|
80813
|
-
await mkdir8(
|
|
81299
|
+
await mkdir8(join12(repoPath, ".worktrees"), { recursive: true });
|
|
80814
81300
|
const existing = (await listWorktrees(repoPath)).find(
|
|
80815
81301
|
(worktree) => worktree.path === worktreePath
|
|
80816
81302
|
);
|
|
@@ -80832,7 +81318,7 @@ async function createWorktree(repoPath, branchName, baseBranch) {
|
|
|
80832
81318
|
}
|
|
80833
81319
|
}
|
|
80834
81320
|
async function cleanupWorktree(worktreePath) {
|
|
80835
|
-
const parentRepoPath =
|
|
81321
|
+
const parentRepoPath = join12(worktreePath, "..", "..");
|
|
80836
81322
|
try {
|
|
80837
81323
|
await runGit(`git worktree remove ${JSON.stringify(worktreePath)}`, parentRepoPath);
|
|
80838
81324
|
} catch (error51) {
|
|
@@ -80874,7 +81360,7 @@ function extractUrl(output) {
|
|
|
80874
81360
|
}
|
|
80875
81361
|
function buildBodyFilePath(repoPath, branchName) {
|
|
80876
81362
|
const safeBranchName = branchName.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
80877
|
-
return
|
|
81363
|
+
return join13(repoPath, `.io-pr-body-${safeBranchName}.md`);
|
|
80878
81364
|
}
|
|
80879
81365
|
function buildPrBody(objective, plan, taskSummaries, qaOutcome) {
|
|
80880
81366
|
const tasks = taskSummaries.map((summary) => `- ${summary}`).join("\n") || "- No task summaries recorded.";
|
|
@@ -80968,7 +81454,7 @@ Return strict JSON:
|
|
|
80968
81454
|
client2 = new CopilotClient4({ workingDirectory: worktreePath });
|
|
80969
81455
|
await client2.start();
|
|
80970
81456
|
const session = await client2.createSession({
|
|
80971
|
-
model: qaMember.model ??
|
|
81457
|
+
model: qaMember.model ?? DEFAULT_MODEL,
|
|
80972
81458
|
workingDirectory: worktreePath,
|
|
80973
81459
|
onPermissionRequest: approveAll4,
|
|
80974
81460
|
systemMessage: {
|
|
@@ -81088,7 +81574,7 @@ Return strict JSON:
|
|
|
81088
81574
|
client2 = new CopilotClient5();
|
|
81089
81575
|
await client2.start();
|
|
81090
81576
|
const session = await client2.createSession({
|
|
81091
|
-
model: teamLead.model ??
|
|
81577
|
+
model: teamLead.model ?? DEFAULT_MODEL,
|
|
81092
81578
|
onPermissionRequest: approveAll5,
|
|
81093
81579
|
systemMessage: {
|
|
81094
81580
|
content: `${TEAM_LEAD_PROMPT}
|
|
@@ -81192,12 +81678,12 @@ async function runGit2(command, cwd) {
|
|
|
81192
81678
|
async function resolveRepoPath(repoUrl, repoName) {
|
|
81193
81679
|
const candidates = [
|
|
81194
81680
|
process.cwd(),
|
|
81195
|
-
|
|
81196
|
-
|
|
81197
|
-
|
|
81681
|
+
join14(process.cwd(), repoName),
|
|
81682
|
+
join14(process.cwd(), "repos", repoName),
|
|
81683
|
+
join14(process.cwd(), "..", repoName)
|
|
81198
81684
|
];
|
|
81199
81685
|
for (const candidate of candidates) {
|
|
81200
|
-
if (!await pathExists(
|
|
81686
|
+
if (!await pathExists(join14(candidate, ".git"))) {
|
|
81201
81687
|
continue;
|
|
81202
81688
|
}
|
|
81203
81689
|
const remoteUrl = await runGit2("git remote get-url origin", candidate).catch(() => "");
|
|
@@ -81543,12 +82029,9 @@ function buildMandatoryRoles() {
|
|
|
81543
82029
|
function modelForRole(role) {
|
|
81544
82030
|
const normalized = slugifyRole(role);
|
|
81545
82031
|
if (normalized === "team-lead") {
|
|
81546
|
-
return
|
|
82032
|
+
return DEFAULT_MODEL;
|
|
81547
82033
|
}
|
|
81548
|
-
|
|
81549
|
-
return STANDARD_MODEL;
|
|
81550
|
-
}
|
|
81551
|
-
return STANDARD_MODEL;
|
|
82034
|
+
return DEFAULT_MODEL;
|
|
81552
82035
|
}
|
|
81553
82036
|
function systemPromptForRole(role, repoContext) {
|
|
81554
82037
|
const normalized = slugifyRole(role);
|
|
@@ -81580,7 +82063,7 @@ ${ROLE_GENERATION_PROMPT}`;
|
|
|
81580
82063
|
client2 = new CopilotClient6();
|
|
81581
82064
|
await client2.start();
|
|
81582
82065
|
const session = await client2.createSession({
|
|
81583
|
-
model:
|
|
82066
|
+
model: DEFAULT_MODEL,
|
|
81584
82067
|
onPermissionRequest: approveAll6,
|
|
81585
82068
|
systemMessage: {
|
|
81586
82069
|
content: ROLE_GENERATION_PROMPT
|
|
@@ -82054,8 +82537,7 @@ var Orchestrator = class {
|
|
|
82054
82537
|
skillsContext,
|
|
82055
82538
|
conversationSummary: [this.latestResetSummary, conversationSummary].filter(Boolean).join("\n\n")
|
|
82056
82539
|
});
|
|
82057
|
-
|
|
82058
|
-
await this.refreshSession(route.model, systemPrompt);
|
|
82540
|
+
await this.refreshSession(this.config.defaultModel, systemPrompt);
|
|
82059
82541
|
const sendResult = await sendMessage(
|
|
82060
82542
|
this.requireActiveSession(),
|
|
82061
82543
|
normalizedMessage,
|
|
@@ -82156,12 +82638,25 @@ var Orchestrator = class {
|
|
|
82156
82638
|
}
|
|
82157
82639
|
async refreshSession(model, systemPrompt) {
|
|
82158
82640
|
await this.disconnectSession();
|
|
82159
|
-
|
|
82160
|
-
|
|
82161
|
-
|
|
82162
|
-
|
|
82163
|
-
|
|
82164
|
-
|
|
82641
|
+
try {
|
|
82642
|
+
this.activeSession = await createSession({
|
|
82643
|
+
model,
|
|
82644
|
+
systemPrompt,
|
|
82645
|
+
tools: createBoundOrchestratorTools(this.config)
|
|
82646
|
+
});
|
|
82647
|
+
this.activeModel = model;
|
|
82648
|
+
} catch (error51) {
|
|
82649
|
+
if (model !== DEFAULT_MODEL) {
|
|
82650
|
+
this.activeSession = await createSession({
|
|
82651
|
+
model: DEFAULT_MODEL,
|
|
82652
|
+
systemPrompt,
|
|
82653
|
+
tools: createBoundOrchestratorTools(this.config)
|
|
82654
|
+
});
|
|
82655
|
+
this.activeModel = DEFAULT_MODEL;
|
|
82656
|
+
} else {
|
|
82657
|
+
throw error51;
|
|
82658
|
+
}
|
|
82659
|
+
}
|
|
82165
82660
|
}
|
|
82166
82661
|
async disconnectSession() {
|
|
82167
82662
|
if (!this.activeSession) {
|
|
@@ -82183,11 +82678,22 @@ var Orchestrator = class {
|
|
|
82183
82678
|
if (usage.inputTokens === 0 && usage.outputTokens === 0) {
|
|
82184
82679
|
return;
|
|
82185
82680
|
}
|
|
82681
|
+
const model = usage.model || this.activeModel || this.config.defaultModel;
|
|
82682
|
+
const pricing = await getModelPricing(model);
|
|
82683
|
+
const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
|
|
82684
|
+
const tokenUnitCost = pricing ? calculateTokenUnitCost(
|
|
82685
|
+
usage.inputTokens,
|
|
82686
|
+
usage.outputTokens,
|
|
82687
|
+
pricing.tokenInputMultiplier,
|
|
82688
|
+
pricing.tokenOutputMultiplier
|
|
82689
|
+
) : 0;
|
|
82186
82690
|
await recordUsage({
|
|
82187
|
-
model
|
|
82691
|
+
model,
|
|
82188
82692
|
inputTokens: usage.inputTokens,
|
|
82189
82693
|
outputTokens: usage.outputTokens,
|
|
82190
|
-
cost: 0
|
|
82694
|
+
cost: 0,
|
|
82695
|
+
premiumRequestCost,
|
|
82696
|
+
tokenUnitCost
|
|
82191
82697
|
});
|
|
82192
82698
|
}
|
|
82193
82699
|
};
|
|
@@ -82484,6 +82990,7 @@ function registerShutdownHandlers(logger, onShutdown) {
|
|
|
82484
82990
|
}
|
|
82485
82991
|
async function main() {
|
|
82486
82992
|
let logger;
|
|
82993
|
+
let pricingRefreshTimer = null;
|
|
82487
82994
|
try {
|
|
82488
82995
|
ensureDataDirectories();
|
|
82489
82996
|
const config2 = loadConfig();
|
|
@@ -82491,6 +82998,12 @@ async function main() {
|
|
|
82491
82998
|
logger.info(`IO Daemon v${APP_VERSION} starting...`);
|
|
82492
82999
|
await initDatabase();
|
|
82493
83000
|
logger.info("Database initialized");
|
|
83001
|
+
const pricingResult = await refreshModelPricing(logger);
|
|
83002
|
+
if (pricingResult.modelsUpdated === 0) {
|
|
83003
|
+
logger.warn("Model pricing refresh returned 0 models, seeding with fallback");
|
|
83004
|
+
await seedFromFallback();
|
|
83005
|
+
}
|
|
83006
|
+
logger.info({ modelsUpdated: pricingResult.modelsUpdated }, "Model pricing initialized");
|
|
82494
83007
|
await scanSkills();
|
|
82495
83008
|
logger.info("Skills scanned");
|
|
82496
83009
|
const orchestrator2 = createOrchestrator(config2, eventBus);
|
|
@@ -82499,12 +83012,21 @@ async function main() {
|
|
|
82499
83012
|
const scheduler = createScheduler(orchestrator2, eventBus);
|
|
82500
83013
|
scheduler.start();
|
|
82501
83014
|
logger.info("Scheduler started");
|
|
83015
|
+
const refreshIntervalMs = config2.pricingRefreshHours * 60 * 60 * 1e3;
|
|
83016
|
+
pricingRefreshTimer = setInterval(() => {
|
|
83017
|
+
void refreshModelPricing(logger).catch((err) => {
|
|
83018
|
+
logger?.warn({ err }, "Periodic model pricing refresh failed");
|
|
83019
|
+
});
|
|
83020
|
+
}, refreshIntervalMs);
|
|
82502
83021
|
setChatOrchestrator(orchestrator2);
|
|
82503
83022
|
const apiServer = createApiServer(config2);
|
|
82504
83023
|
const telegramBot = createTelegramBot(config2, orchestrator2);
|
|
82505
83024
|
telegramBot?.start();
|
|
82506
83025
|
createTelegramNotifier(telegramBot, config2, eventBus);
|
|
82507
83026
|
registerShutdownHandlers(logger, async () => {
|
|
83027
|
+
if (pricingRefreshTimer) {
|
|
83028
|
+
clearInterval(pricingRefreshTimer);
|
|
83029
|
+
}
|
|
82508
83030
|
scheduler.stop();
|
|
82509
83031
|
telegramBot?.stop();
|
|
82510
83032
|
apiServer.server.close();
|