quoroom 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -10
- package/out/mcp/api-server.js +15341 -14815
- package/out/mcp/cli.js +1711 -1170
- package/out/mcp/server.js +318 -923
- package/package.json +2 -1
package/out/mcp/server.js
CHANGED
|
@@ -3486,7 +3486,7 @@ var require_schemes = __commonJS({
|
|
|
3486
3486
|
urnComponent.nss = (uuidComponent.uuid || "").toLowerCase();
|
|
3487
3487
|
return urnComponent;
|
|
3488
3488
|
}
|
|
3489
|
-
var
|
|
3489
|
+
var http2 = (
|
|
3490
3490
|
/** @type {SchemeHandler} */
|
|
3491
3491
|
{
|
|
3492
3492
|
scheme: "http",
|
|
@@ -3499,7 +3499,7 @@ var require_schemes = __commonJS({
|
|
|
3499
3499
|
/** @type {SchemeHandler} */
|
|
3500
3500
|
{
|
|
3501
3501
|
scheme: "https",
|
|
3502
|
-
domainHost:
|
|
3502
|
+
domainHost: http2.domainHost,
|
|
3503
3503
|
parse: httpParse,
|
|
3504
3504
|
serialize: httpSerialize
|
|
3505
3505
|
}
|
|
@@ -3543,7 +3543,7 @@ var require_schemes = __commonJS({
|
|
|
3543
3543
|
var SCHEMES = (
|
|
3544
3544
|
/** @type {Record<SchemeName, SchemeHandler>} */
|
|
3545
3545
|
{
|
|
3546
|
-
http:
|
|
3546
|
+
http: http2,
|
|
3547
3547
|
https,
|
|
3548
3548
|
ws,
|
|
3549
3549
|
wss,
|
|
@@ -7811,7 +7811,7 @@ function createHasher(hashCons) {
|
|
|
7811
7811
|
hashC.create = () => hashCons();
|
|
7812
7812
|
return hashC;
|
|
7813
7813
|
}
|
|
7814
|
-
function
|
|
7814
|
+
function randomBytes3(bytesLength = 32) {
|
|
7815
7815
|
if (crypto6 && typeof crypto6.getRandomValues === "function") {
|
|
7816
7816
|
return crypto6.getRandomValues(new Uint8Array(bytesLength));
|
|
7817
7817
|
}
|
|
@@ -9622,7 +9622,7 @@ function weierstrass(curveDef) {
|
|
|
9622
9622
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
9623
9623
|
if (["recovered", "canonical"].some((k) => k in opts))
|
|
9624
9624
|
throw new Error("sign() legacy options not supported");
|
|
9625
|
-
const { hash: hash3, randomBytes:
|
|
9625
|
+
const { hash: hash3, randomBytes: randomBytes4 } = CURVE;
|
|
9626
9626
|
let { lowS, prehash, extraEntropy: ent } = opts;
|
|
9627
9627
|
if (lowS == null)
|
|
9628
9628
|
lowS = true;
|
|
@@ -9634,7 +9634,7 @@ function weierstrass(curveDef) {
|
|
|
9634
9634
|
const d = normPrivateKeyToScalar(privateKey);
|
|
9635
9635
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
9636
9636
|
if (ent != null && ent !== false) {
|
|
9637
|
-
const e = ent === true ?
|
|
9637
|
+
const e = ent === true ? randomBytes4(Fp.BYTES) : ent;
|
|
9638
9638
|
seedArgs.push(ensureBytes("extraEntropy", e));
|
|
9639
9639
|
}
|
|
9640
9640
|
const seed = concatBytes2(...seedArgs);
|
|
@@ -9956,7 +9956,7 @@ function getHash(hash3) {
|
|
|
9956
9956
|
return {
|
|
9957
9957
|
hash: hash3,
|
|
9958
9958
|
hmac: (key, ...msgs) => hmac(hash3, key, concatBytes(...msgs)),
|
|
9959
|
-
randomBytes:
|
|
9959
|
+
randomBytes: randomBytes3
|
|
9960
9960
|
};
|
|
9961
9961
|
}
|
|
9962
9962
|
function createCurve(curveDef, defHash) {
|
|
@@ -10189,7 +10189,7 @@ function challenge(...args) {
|
|
|
10189
10189
|
function schnorrGetPublicKey(privateKey) {
|
|
10190
10190
|
return schnorrGetExtPubKey(privateKey).bytes;
|
|
10191
10191
|
}
|
|
10192
|
-
function schnorrSign(message, privateKey, auxRand =
|
|
10192
|
+
function schnorrSign(message, privateKey, auxRand = randomBytes3(32)) {
|
|
10193
10193
|
const m = ensureBytes("message", message);
|
|
10194
10194
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey);
|
|
10195
10195
|
const a = ensureBytes("auxRand", auxRand, 32);
|
|
@@ -31151,12 +31151,14 @@ CREATE TABLE IF NOT EXISTS rooms (
|
|
|
31151
31151
|
visibility TEXT NOT NULL DEFAULT 'private',
|
|
31152
31152
|
autonomy_mode TEXT NOT NULL DEFAULT 'auto',
|
|
31153
31153
|
max_concurrent_tasks INTEGER NOT NULL DEFAULT 3,
|
|
31154
|
-
worker_model TEXT NOT NULL DEFAULT '
|
|
31154
|
+
worker_model TEXT NOT NULL DEFAULT 'claude',
|
|
31155
31155
|
queen_cycle_gap_ms INTEGER NOT NULL DEFAULT 1800000,
|
|
31156
31156
|
queen_max_turns INTEGER NOT NULL DEFAULT 3,
|
|
31157
31157
|
queen_quiet_from TEXT,
|
|
31158
31158
|
queen_quiet_until TEXT,
|
|
31159
31159
|
config TEXT,
|
|
31160
|
+
webhook_token TEXT,
|
|
31161
|
+
queen_nickname TEXT,
|
|
31160
31162
|
chat_session_id TEXT,
|
|
31161
31163
|
referred_by_code TEXT,
|
|
31162
31164
|
created_at DATETIME DEFAULT (datetime('now','localtime')),
|
|
@@ -31237,6 +31239,7 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
31237
31239
|
cron_expression TEXT,
|
|
31238
31240
|
trigger_type TEXT NOT NULL DEFAULT 'cron',
|
|
31239
31241
|
trigger_config TEXT,
|
|
31242
|
+
webhook_token TEXT,
|
|
31240
31243
|
executor TEXT NOT NULL DEFAULT 'claude_code',
|
|
31241
31244
|
status TEXT NOT NULL DEFAULT 'active',
|
|
31242
31245
|
last_run DATETIME,
|
|
@@ -31545,6 +31548,18 @@ CREATE TABLE IF NOT EXISTS cycle_logs (
|
|
|
31545
31548
|
);
|
|
31546
31549
|
CREATE INDEX IF NOT EXISTS idx_cycle_logs_seq ON cycle_logs(cycle_id, seq);
|
|
31547
31550
|
|
|
31551
|
+
-- Agent session continuity (persists conversation history across queen cycles for all model types)
|
|
31552
|
+
-- session_id: for CLI models (claude/codex) \u2014 passed as --resume to continue the native session
|
|
31553
|
+
-- messages_json: for API models \u2014 full conversation turns array (no system prompt), stored as JSON
|
|
31554
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
31555
|
+
worker_id INTEGER PRIMARY KEY REFERENCES workers(id) ON DELETE CASCADE,
|
|
31556
|
+
session_id TEXT,
|
|
31557
|
+
messages_json TEXT,
|
|
31558
|
+
model TEXT NOT NULL DEFAULT '',
|
|
31559
|
+
turn_count INTEGER NOT NULL DEFAULT 0,
|
|
31560
|
+
updated_at DATETIME DEFAULT (datetime('now','localtime'))
|
|
31561
|
+
);
|
|
31562
|
+
|
|
31548
31563
|
-- Schema version tracking
|
|
31549
31564
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
31550
31565
|
version INTEGER PRIMARY KEY,
|
|
@@ -31553,19 +31568,6 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
|
|
31553
31568
|
INSERT OR IGNORE INTO schema_version (version) VALUES (1);
|
|
31554
31569
|
`;
|
|
31555
31570
|
|
|
31556
|
-
// src/shared/db-migrations.ts
|
|
31557
|
-
function runMigrations(database, log = console.log) {
|
|
31558
|
-
database.exec(SCHEMA);
|
|
31559
|
-
if (!database.prepare("SELECT value FROM settings WHERE key = ?").get("keeper_referral_code")) {
|
|
31560
|
-
const code = (0, import_crypto.randomBytes)(6).toString("base64url").slice(0, 10);
|
|
31561
|
-
database.prepare(
|
|
31562
|
-
`INSERT INTO settings (key, value, updated_at) VALUES (?, ?, datetime('now','localtime'))
|
|
31563
|
-
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
|
|
31564
|
-
).run("keeper_referral_code", code);
|
|
31565
|
-
}
|
|
31566
|
-
log("Database schema initialized");
|
|
31567
|
-
}
|
|
31568
|
-
|
|
31569
31571
|
// src/shared/constants.ts
|
|
31570
31572
|
var APP_NAME = "Quoroom";
|
|
31571
31573
|
var DEFAULTS = {
|
|
@@ -31579,6 +31581,12 @@ var DEFAULTS = {
|
|
|
31579
31581
|
WINDOW_HEIGHT_LARGE: 1200,
|
|
31580
31582
|
PROGRESS_THROTTLE_MS: 2e3
|
|
31581
31583
|
};
|
|
31584
|
+
var TRIGGER_TYPES = {
|
|
31585
|
+
CRON: "cron",
|
|
31586
|
+
ONCE: "once",
|
|
31587
|
+
MANUAL: "manual",
|
|
31588
|
+
WEBHOOK: "webhook"
|
|
31589
|
+
};
|
|
31582
31590
|
var TASK_STATUSES = {
|
|
31583
31591
|
ACTIVE: "active",
|
|
31584
31592
|
PAUSED: "paused",
|
|
@@ -31862,8 +31870,8 @@ function mapWorkerRow(row) {
|
|
|
31862
31870
|
}
|
|
31863
31871
|
function createTask(db2, input) {
|
|
31864
31872
|
const result = db2.prepare(
|
|
31865
|
-
`INSERT INTO tasks (name, description, prompt, cron_expression, trigger_type, trigger_config, scheduled_at, executor, max_runs, worker_id, session_continuity, timeout_minutes, max_turns, allowed_tools, disallowed_tools, room_id)
|
|
31866
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
31873
|
+
`INSERT INTO tasks (name, description, prompt, cron_expression, trigger_type, trigger_config, webhook_token, scheduled_at, executor, max_runs, worker_id, session_continuity, timeout_minutes, max_turns, allowed_tools, disallowed_tools, room_id)
|
|
31874
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
31867
31875
|
).run(
|
|
31868
31876
|
input.name,
|
|
31869
31877
|
input.description ?? null,
|
|
@@ -31871,6 +31879,7 @@ function createTask(db2, input) {
|
|
|
31871
31879
|
input.cronExpression ?? null,
|
|
31872
31880
|
input.triggerType ?? "cron",
|
|
31873
31881
|
input.triggerConfig ?? null,
|
|
31882
|
+
input.webhookToken ?? null,
|
|
31874
31883
|
input.scheduledAt ?? null,
|
|
31875
31884
|
input.executor ?? "claude_code",
|
|
31876
31885
|
input.maxRuns ?? null,
|
|
@@ -31910,6 +31919,7 @@ function updateTask(db2, id, updates) {
|
|
|
31910
31919
|
cronExpression: "cron_expression",
|
|
31911
31920
|
triggerType: "trigger_type",
|
|
31912
31921
|
triggerConfig: "trigger_config",
|
|
31922
|
+
webhookToken: "webhook_token",
|
|
31913
31923
|
scheduledAt: "scheduled_at",
|
|
31914
31924
|
executor: "executor",
|
|
31915
31925
|
status: "status",
|
|
@@ -32193,6 +32203,7 @@ function mapTaskRow(row) {
|
|
|
32193
32203
|
cronExpression: row.cron_expression,
|
|
32194
32204
|
triggerType: row.trigger_type,
|
|
32195
32205
|
triggerConfig: row.trigger_config,
|
|
32206
|
+
webhookToken: row.webhook_token,
|
|
32196
32207
|
scheduledAt: row.scheduled_at,
|
|
32197
32208
|
executor: row.executor,
|
|
32198
32209
|
status: row.status,
|
|
@@ -32318,17 +32329,68 @@ function mapRoomRow(row) {
|
|
|
32318
32329
|
queenQuietFrom: row.queen_quiet_from ?? null,
|
|
32319
32330
|
queenQuietUntil: row.queen_quiet_until ?? null,
|
|
32320
32331
|
config: config2,
|
|
32332
|
+
queenNickname: row.queen_nickname ?? null,
|
|
32321
32333
|
chatSessionId: row.chat_session_id ?? null,
|
|
32322
32334
|
referredByCode: row.referred_by_code ?? null,
|
|
32335
|
+
webhookToken: row.webhook_token ?? null,
|
|
32323
32336
|
createdAt: row.created_at,
|
|
32324
32337
|
updatedAt: row.updated_at
|
|
32325
32338
|
};
|
|
32326
32339
|
}
|
|
32327
|
-
function createRoom(db2, name, goal, config2, referredByCode) {
|
|
32340
|
+
function createRoom(db2, name, goal, config2, referredByCode, queenNickname) {
|
|
32328
32341
|
const configJson = config2 ? JSON.stringify({ ...DEFAULT_ROOM_CONFIG, ...config2 }) : JSON.stringify(DEFAULT_ROOM_CONFIG);
|
|
32329
|
-
const
|
|
32342
|
+
const nickname = queenNickname ?? pickQueenNickname(db2);
|
|
32343
|
+
const result = db2.prepare("INSERT INTO rooms (name, goal, config, referred_by_code, queen_nickname) VALUES (?, ?, ?, ?, ?)").run(name, goal ?? null, configJson, referredByCode ?? null, nickname);
|
|
32330
32344
|
return getRoom(db2, result.lastInsertRowid);
|
|
32331
32345
|
}
|
|
32346
|
+
var QUEEN_WOMAN_NAMES = [
|
|
32347
|
+
"Alice",
|
|
32348
|
+
"Anna",
|
|
32349
|
+
"Belle",
|
|
32350
|
+
"Cara",
|
|
32351
|
+
"Dana",
|
|
32352
|
+
"Elena",
|
|
32353
|
+
"Fiona",
|
|
32354
|
+
"Grace",
|
|
32355
|
+
"Hana",
|
|
32356
|
+
"Iris",
|
|
32357
|
+
"Julia",
|
|
32358
|
+
"Kate",
|
|
32359
|
+
"Lena",
|
|
32360
|
+
"Luna",
|
|
32361
|
+
"Mara",
|
|
32362
|
+
"Maya",
|
|
32363
|
+
"Nina",
|
|
32364
|
+
"Nora",
|
|
32365
|
+
"Olga",
|
|
32366
|
+
"Petra",
|
|
32367
|
+
"Rose",
|
|
32368
|
+
"Sara",
|
|
32369
|
+
"Sofia",
|
|
32370
|
+
"Tara",
|
|
32371
|
+
"Uma",
|
|
32372
|
+
"Vera",
|
|
32373
|
+
"Wren",
|
|
32374
|
+
"Zara",
|
|
32375
|
+
"Zoe",
|
|
32376
|
+
"Ava",
|
|
32377
|
+
"Cleo",
|
|
32378
|
+
"Dara",
|
|
32379
|
+
"Emmy",
|
|
32380
|
+
"Gaia",
|
|
32381
|
+
"Hera",
|
|
32382
|
+
"Ines",
|
|
32383
|
+
"Jada",
|
|
32384
|
+
"Kara",
|
|
32385
|
+
"Lila",
|
|
32386
|
+
"Mina"
|
|
32387
|
+
];
|
|
32388
|
+
function pickQueenNickname(db2) {
|
|
32389
|
+
const usedNames = db2.prepare(`SELECT queen_nickname FROM rooms WHERE queen_nickname IS NOT NULL AND queen_nickname != ''`).all().map((r) => r.queen_nickname.toLowerCase());
|
|
32390
|
+
const available = QUEEN_WOMAN_NAMES.filter((n) => !usedNames.includes(n.toLowerCase()));
|
|
32391
|
+
const pool = available.length > 0 ? available : QUEEN_WOMAN_NAMES;
|
|
32392
|
+
return pool[Math.floor(Math.random() * pool.length)];
|
|
32393
|
+
}
|
|
32332
32394
|
function getRoom(db2, id) {
|
|
32333
32395
|
const row = db2.prepare("SELECT * FROM rooms WHERE id = ?").get(id);
|
|
32334
32396
|
return row ? mapRoomRow(row) : null;
|
|
@@ -32356,7 +32418,9 @@ function updateRoom(db2, id, updates) {
|
|
|
32356
32418
|
queenQuietFrom: "queen_quiet_from",
|
|
32357
32419
|
queenQuietUntil: "queen_quiet_until",
|
|
32358
32420
|
config: "config",
|
|
32359
|
-
referredByCode: "referred_by_code"
|
|
32421
|
+
referredByCode: "referred_by_code",
|
|
32422
|
+
queenNickname: "queen_nickname",
|
|
32423
|
+
webhookToken: "webhook_token"
|
|
32360
32424
|
};
|
|
32361
32425
|
const fields = [];
|
|
32362
32426
|
const values = [];
|
|
@@ -32838,6 +32902,78 @@ function replyToRoomMessage(db2, id) {
|
|
|
32838
32902
|
}
|
|
32839
32903
|
var CYCLE_PRUNE_INTERVAL_MS = 5 * 60 * 1e3;
|
|
32840
32904
|
|
|
32905
|
+
// src/shared/db-migrations.ts
|
|
32906
|
+
function upsertSetting(database, key, value) {
|
|
32907
|
+
database.prepare(
|
|
32908
|
+
`INSERT INTO settings (key, value, updated_at) VALUES (?, ?, datetime('now','localtime'))
|
|
32909
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
|
|
32910
|
+
).run(key, value);
|
|
32911
|
+
}
|
|
32912
|
+
function runMigrations(database, log = console.log) {
|
|
32913
|
+
database.exec(SCHEMA);
|
|
32914
|
+
if (!database.prepare("SELECT value FROM settings WHERE key = ?").get("keeper_referral_code")) {
|
|
32915
|
+
const code = (0, import_crypto.randomBytes)(6).toString("base64url").slice(0, 10);
|
|
32916
|
+
upsertSetting(database, "keeper_referral_code", code);
|
|
32917
|
+
}
|
|
32918
|
+
if (!database.prepare("SELECT value FROM settings WHERE key = ?").get("keeper_user_number")) {
|
|
32919
|
+
const num2 = String(1e4 + Math.floor(Math.random() * 9e4));
|
|
32920
|
+
upsertSetting(database, "keeper_user_number", num2);
|
|
32921
|
+
log(`Migrated: assigned keeper_user_number=${num2}`);
|
|
32922
|
+
}
|
|
32923
|
+
const hasQueenNickname = database.prepare(
|
|
32924
|
+
`SELECT name FROM pragma_table_info('rooms') WHERE name='queen_nickname'`
|
|
32925
|
+
).get()?.name;
|
|
32926
|
+
if (!hasQueenNickname) {
|
|
32927
|
+
database.exec(`ALTER TABLE rooms ADD COLUMN queen_nickname TEXT`);
|
|
32928
|
+
log("Migrated: added queen_nickname column to rooms");
|
|
32929
|
+
}
|
|
32930
|
+
const roomsWithoutNickname = database.prepare(`SELECT id FROM rooms WHERE queen_nickname IS NULL OR queen_nickname = ''`).all();
|
|
32931
|
+
if (roomsWithoutNickname.length > 0) {
|
|
32932
|
+
for (const room of roomsWithoutNickname) {
|
|
32933
|
+
const nickname = pickQueenNickname(database);
|
|
32934
|
+
database.prepare(`UPDATE rooms SET queen_nickname = ? WHERE id = ?`).run(nickname, room.id);
|
|
32935
|
+
}
|
|
32936
|
+
log(`Migrated: assigned queen nicknames to ${roomsWithoutNickname.length} room(s)`);
|
|
32937
|
+
}
|
|
32938
|
+
const hasTaskWebhookToken = database.prepare(
|
|
32939
|
+
`SELECT name FROM pragma_table_info('tasks') WHERE name='webhook_token'`
|
|
32940
|
+
).get()?.name;
|
|
32941
|
+
if (!hasTaskWebhookToken) {
|
|
32942
|
+
database.exec(`ALTER TABLE tasks ADD COLUMN webhook_token TEXT`);
|
|
32943
|
+
log("Migrated: added webhook_token column to tasks");
|
|
32944
|
+
}
|
|
32945
|
+
const hasTaskWebhookIndex = database.prepare(
|
|
32946
|
+
`SELECT name FROM sqlite_master WHERE type='index' AND name='idx_tasks_webhook_token'`
|
|
32947
|
+
).get()?.name;
|
|
32948
|
+
if (!hasTaskWebhookIndex) {
|
|
32949
|
+
database.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_webhook_token ON tasks(webhook_token) WHERE webhook_token IS NOT NULL`);
|
|
32950
|
+
}
|
|
32951
|
+
const hasRoomWebhookToken = database.prepare(
|
|
32952
|
+
`SELECT name FROM pragma_table_info('rooms') WHERE name='webhook_token'`
|
|
32953
|
+
).get()?.name;
|
|
32954
|
+
if (!hasRoomWebhookToken) {
|
|
32955
|
+
database.exec(`ALTER TABLE rooms ADD COLUMN webhook_token TEXT`);
|
|
32956
|
+
log("Migrated: added webhook_token column to rooms");
|
|
32957
|
+
}
|
|
32958
|
+
const hasRoomWebhookIndex = database.prepare(
|
|
32959
|
+
`SELECT name FROM sqlite_master WHERE type='index' AND name='idx_rooms_webhook_token'`
|
|
32960
|
+
).get()?.name;
|
|
32961
|
+
if (!hasRoomWebhookIndex) {
|
|
32962
|
+
database.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_rooms_webhook_token ON rooms(webhook_token) WHERE webhook_token IS NOT NULL`);
|
|
32963
|
+
}
|
|
32964
|
+
const ollamaWorkers = database.prepare(`SELECT id FROM workers WHERE model LIKE 'ollama:%'`).all();
|
|
32965
|
+
if (ollamaWorkers.length > 0) {
|
|
32966
|
+
database.prepare(`UPDATE workers SET model = 'claude' WHERE model LIKE 'ollama:%'`).run();
|
|
32967
|
+
log(`Migrated: reset ${ollamaWorkers.length} ollama worker model(s) to 'claude'`);
|
|
32968
|
+
}
|
|
32969
|
+
const ollamaRooms = database.prepare(`SELECT id FROM rooms WHERE worker_model LIKE 'ollama:%'`).all();
|
|
32970
|
+
if (ollamaRooms.length > 0) {
|
|
32971
|
+
database.prepare(`UPDATE rooms SET worker_model = 'claude' WHERE worker_model LIKE 'ollama:%'`).run();
|
|
32972
|
+
log(`Migrated: reset ${ollamaRooms.length} room worker_model(s) to 'claude'`);
|
|
32973
|
+
}
|
|
32974
|
+
log("Database schema initialized");
|
|
32975
|
+
}
|
|
32976
|
+
|
|
32841
32977
|
// src/shared/embeddings.ts
|
|
32842
32978
|
var pipeline = null;
|
|
32843
32979
|
var pipelineLoading = false;
|
|
@@ -33100,8 +33236,9 @@ function registerMemoryTools(server) {
|
|
|
33100
33236
|
|
|
33101
33237
|
// src/mcp/tools/scheduler.ts
|
|
33102
33238
|
var import_path4 = require("path");
|
|
33103
|
-
var
|
|
33239
|
+
var import_os5 = require("os");
|
|
33104
33240
|
var import_fs4 = require("fs");
|
|
33241
|
+
var import_crypto7 = require("crypto");
|
|
33105
33242
|
var import_http = require("http");
|
|
33106
33243
|
var import_node_cron = __toESM(require_node_cron());
|
|
33107
33244
|
|
|
@@ -33339,10 +33476,6 @@ function executeClaudeCode(prompt, options) {
|
|
|
33339
33476
|
});
|
|
33340
33477
|
}
|
|
33341
33478
|
|
|
33342
|
-
// src/shared/agent-executor.ts
|
|
33343
|
-
var import_child_process2 = require("child_process");
|
|
33344
|
-
var import_os5 = require("os");
|
|
33345
|
-
|
|
33346
33479
|
// src/shared/cloud-sync.ts
|
|
33347
33480
|
var import_crypto6 = require("crypto");
|
|
33348
33481
|
var import_os4 = require("os");
|
|
@@ -33369,9 +33502,6 @@ function getMachineId() {
|
|
|
33369
33502
|
function getCloudApi() {
|
|
33370
33503
|
return (process.env.QUOROOM_CLOUD_API ?? "https://quoroom.ai/api").replace(/\/$/, "");
|
|
33371
33504
|
}
|
|
33372
|
-
function getCloudMasterToken() {
|
|
33373
|
-
return (process.env.QUOROOM_CLOUD_API_KEY ?? "").trim();
|
|
33374
|
-
}
|
|
33375
33505
|
var TOKEN_FILE_NAME = "cloud-room-tokens.json";
|
|
33376
33506
|
var cachedTokens = null;
|
|
33377
33507
|
function getCloudTokenFilePath() {
|
|
@@ -33407,9 +33537,8 @@ function setRoomToken(roomId, token) {
|
|
|
33407
33537
|
}
|
|
33408
33538
|
function cloudHeaders(roomId, extra = {}) {
|
|
33409
33539
|
const roomToken = roomId ? getRoomToken(roomId) : void 0;
|
|
33410
|
-
|
|
33411
|
-
|
|
33412
|
-
return { ...extra, "X-Room-Token": token };
|
|
33540
|
+
if (!roomToken) return extra;
|
|
33541
|
+
return { ...extra, "X-Room-Token": roomToken };
|
|
33413
33542
|
}
|
|
33414
33543
|
async function ensureCloudRoomToken(data) {
|
|
33415
33544
|
if (getRoomToken(data.roomId)) return true;
|
|
@@ -33676,726 +33805,7 @@ async function fetchReferredRooms(cloudRoomId) {
|
|
|
33676
33805
|
}
|
|
33677
33806
|
}
|
|
33678
33807
|
|
|
33679
|
-
// src/shared/ollama-ensure.ts
|
|
33680
|
-
var import_node_http = __toESM(require("node:http"));
|
|
33681
|
-
var import_node_child_process = require("node:child_process");
|
|
33682
|
-
var OLLAMA_INSTALL_TIMEOUT_MS = 12e4;
|
|
33683
|
-
var OLLAMA_STARTUP_TIMEOUT_MS = 3e4;
|
|
33684
|
-
function ollamaRequest(path, body, timeoutMs = 3e5) {
|
|
33685
|
-
return new Promise((resolve2, reject) => {
|
|
33686
|
-
const options = {
|
|
33687
|
-
hostname: "127.0.0.1",
|
|
33688
|
-
port: 11434,
|
|
33689
|
-
path,
|
|
33690
|
-
method: body ? "POST" : "GET",
|
|
33691
|
-
headers: body ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) } : void 0,
|
|
33692
|
-
timeout: timeoutMs
|
|
33693
|
-
};
|
|
33694
|
-
const req = import_node_http.default.request(options, (res) => {
|
|
33695
|
-
let data = "";
|
|
33696
|
-
res.on("data", (chunk) => {
|
|
33697
|
-
data += chunk;
|
|
33698
|
-
});
|
|
33699
|
-
res.on("end", () => {
|
|
33700
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
33701
|
-
resolve2(data);
|
|
33702
|
-
} else {
|
|
33703
|
-
reject(new Error(`Ollama HTTP ${res.statusCode}: ${data}`));
|
|
33704
|
-
}
|
|
33705
|
-
});
|
|
33706
|
-
});
|
|
33707
|
-
req.on("error", reject);
|
|
33708
|
-
req.on("timeout", () => {
|
|
33709
|
-
req.destroy();
|
|
33710
|
-
reject(new Error("Ollama request timeout"));
|
|
33711
|
-
});
|
|
33712
|
-
if (body) req.write(body);
|
|
33713
|
-
req.end();
|
|
33714
|
-
});
|
|
33715
|
-
}
|
|
33716
|
-
async function isOllamaAvailable() {
|
|
33717
|
-
try {
|
|
33718
|
-
await ollamaRequest("/api/tags", void 0, 5e3);
|
|
33719
|
-
return true;
|
|
33720
|
-
} catch {
|
|
33721
|
-
return false;
|
|
33722
|
-
}
|
|
33723
|
-
}
|
|
33724
|
-
async function listOllamaModels() {
|
|
33725
|
-
try {
|
|
33726
|
-
const response = await ollamaRequest("/api/tags", void 0, 5e3);
|
|
33727
|
-
const parsed = JSON.parse(response);
|
|
33728
|
-
return (parsed.models ?? []).map((m) => ({ name: m.name, size: m.size }));
|
|
33729
|
-
} catch {
|
|
33730
|
-
return [];
|
|
33731
|
-
}
|
|
33732
|
-
}
|
|
33733
|
-
function hasOllamaBinary() {
|
|
33734
|
-
try {
|
|
33735
|
-
(0, import_node_child_process.execSync)("which ollama 2>/dev/null", { timeout: 3e3 });
|
|
33736
|
-
return true;
|
|
33737
|
-
} catch {
|
|
33738
|
-
return false;
|
|
33739
|
-
}
|
|
33740
|
-
}
|
|
33741
|
-
function installOllamaBinary() {
|
|
33742
|
-
try {
|
|
33743
|
-
if (process.platform === "darwin") {
|
|
33744
|
-
(0, import_node_child_process.execSync)("brew install ollama 2>&1", { timeout: OLLAMA_INSTALL_TIMEOUT_MS });
|
|
33745
|
-
} else {
|
|
33746
|
-
(0, import_node_child_process.execSync)("curl -fsSL https://ollama.com/install.sh | sh 2>&1", { timeout: OLLAMA_INSTALL_TIMEOUT_MS });
|
|
33747
|
-
}
|
|
33748
|
-
return true;
|
|
33749
|
-
} catch {
|
|
33750
|
-
return false;
|
|
33751
|
-
}
|
|
33752
|
-
}
|
|
33753
|
-
function startOllamaServe() {
|
|
33754
|
-
try {
|
|
33755
|
-
const child = (0, import_node_child_process.spawn)("ollama", ["serve"], { detached: true, stdio: "ignore" });
|
|
33756
|
-
child.on("error", () => {
|
|
33757
|
-
});
|
|
33758
|
-
child.unref();
|
|
33759
|
-
return true;
|
|
33760
|
-
} catch {
|
|
33761
|
-
return false;
|
|
33762
|
-
}
|
|
33763
|
-
}
|
|
33764
|
-
async function waitForOllamaAvailable(timeoutMs = OLLAMA_STARTUP_TIMEOUT_MS) {
|
|
33765
|
-
const startedAt = Date.now();
|
|
33766
|
-
while (Date.now() - startedAt < timeoutMs) {
|
|
33767
|
-
if (await isOllamaAvailable()) return true;
|
|
33768
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
33769
|
-
}
|
|
33770
|
-
return false;
|
|
33771
|
-
}
|
|
33772
|
-
async function ensureOllamaRunning() {
|
|
33773
|
-
const already = await isOllamaAvailable();
|
|
33774
|
-
if (already) return { available: true, status: "running" };
|
|
33775
|
-
if (!hasOllamaBinary() && !installOllamaBinary()) {
|
|
33776
|
-
return { available: false, status: "install_failed" };
|
|
33777
|
-
}
|
|
33778
|
-
if (!startOllamaServe()) {
|
|
33779
|
-
return { available: false, status: "start_failed" };
|
|
33780
|
-
}
|
|
33781
|
-
const available = await waitForOllamaAvailable();
|
|
33782
|
-
return { available, status: available ? "running" : "start_failed" };
|
|
33783
|
-
}
|
|
33784
|
-
function isModelInstalled(models, requested) {
|
|
33785
|
-
const requestedLower = requested.toLowerCase();
|
|
33786
|
-
for (const model of models) {
|
|
33787
|
-
const installedName = model.name.toLowerCase();
|
|
33788
|
-
if (installedName === requestedLower) return true;
|
|
33789
|
-
if (!requestedLower.includes(":") && installedName === `${requestedLower}:latest`) return true;
|
|
33790
|
-
if (requestedLower.endsWith(":latest") && installedName === requestedLower.slice(0, -7)) return true;
|
|
33791
|
-
}
|
|
33792
|
-
return false;
|
|
33793
|
-
}
|
|
33794
|
-
async function pullOllamaModel(model) {
|
|
33795
|
-
return await new Promise((resolve2) => {
|
|
33796
|
-
let stdout = "";
|
|
33797
|
-
let stderr = "";
|
|
33798
|
-
let settled = false;
|
|
33799
|
-
let proc;
|
|
33800
|
-
try {
|
|
33801
|
-
proc = (0, import_node_child_process.spawn)("ollama", ["pull", model], {
|
|
33802
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
33803
|
-
});
|
|
33804
|
-
} catch (err) {
|
|
33805
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
33806
|
-
resolve2({ ok: false, error: `Failed to start ollama pull: ${message}` });
|
|
33807
|
-
return;
|
|
33808
|
-
}
|
|
33809
|
-
const finish = (result) => {
|
|
33810
|
-
if (settled) return;
|
|
33811
|
-
settled = true;
|
|
33812
|
-
resolve2(result);
|
|
33813
|
-
};
|
|
33814
|
-
const timer = setTimeout(() => {
|
|
33815
|
-
proc.kill("SIGTERM");
|
|
33816
|
-
finish({ ok: false, error: `Timed out while pulling model ${model}` });
|
|
33817
|
-
}, 15 * 60 * 1e3);
|
|
33818
|
-
proc.stdout?.on("data", (chunk) => {
|
|
33819
|
-
stdout += String(chunk);
|
|
33820
|
-
});
|
|
33821
|
-
proc.stderr?.on("data", (chunk) => {
|
|
33822
|
-
stderr += String(chunk);
|
|
33823
|
-
});
|
|
33824
|
-
proc.on("error", (err) => {
|
|
33825
|
-
clearTimeout(timer);
|
|
33826
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
33827
|
-
finish({ ok: false, error: `ollama pull failed: ${message}` });
|
|
33828
|
-
});
|
|
33829
|
-
proc.on("close", (code) => {
|
|
33830
|
-
clearTimeout(timer);
|
|
33831
|
-
if (code === 0) {
|
|
33832
|
-
finish({ ok: true });
|
|
33833
|
-
return;
|
|
33834
|
-
}
|
|
33835
|
-
const details = `${stderr}
|
|
33836
|
-
${stdout}`.trim().split("\n").slice(-3).join("\n");
|
|
33837
|
-
finish({ ok: false, error: details || `ollama pull exited with code ${code ?? -1}` });
|
|
33838
|
-
});
|
|
33839
|
-
});
|
|
33840
|
-
}
|
|
33841
|
-
async function ensureOllamaModel(modelName) {
|
|
33842
|
-
const running = await ensureOllamaRunning();
|
|
33843
|
-
if (!running.available) {
|
|
33844
|
-
throw new Error(`Ollama unavailable (${running.status})`);
|
|
33845
|
-
}
|
|
33846
|
-
const installed = await listOllamaModels();
|
|
33847
|
-
if (isModelInstalled(installed, modelName)) return;
|
|
33848
|
-
const pulled = await pullOllamaModel(modelName);
|
|
33849
|
-
if (!pulled.ok) {
|
|
33850
|
-
throw new Error(`Failed to pull model ${modelName}: ${pulled.ok === false ? pulled.error : "unknown"}`);
|
|
33851
|
-
}
|
|
33852
|
-
}
|
|
33853
|
-
|
|
33854
33808
|
// src/shared/agent-executor.ts
|
|
33855
|
-
var DEFAULT_HTTP_TIMEOUT_MS = 6e4;
|
|
33856
|
-
async function executeAgent(options) {
|
|
33857
|
-
const model = options.model.trim();
|
|
33858
|
-
if (model.startsWith("ollama:")) {
|
|
33859
|
-
if (options.toolDefs && options.toolDefs.length > 0 && options.onToolCall) {
|
|
33860
|
-
return executeOllamaWithTools(options);
|
|
33861
|
-
}
|
|
33862
|
-
return executeOllama(options);
|
|
33863
|
-
}
|
|
33864
|
-
if (model === "codex" || model.startsWith("codex:")) {
|
|
33865
|
-
return executeCodex(options);
|
|
33866
|
-
}
|
|
33867
|
-
if (model === "openai" || model.startsWith("openai:")) {
|
|
33868
|
-
if (options.toolDefs && options.toolDefs.length > 0 && options.onToolCall) {
|
|
33869
|
-
return executeOpenAiWithTools(options);
|
|
33870
|
-
}
|
|
33871
|
-
return executeOpenAiApi(options);
|
|
33872
|
-
}
|
|
33873
|
-
if (model === "anthropic" || model.startsWith("anthropic:") || model.startsWith("claude-api:")) {
|
|
33874
|
-
if (options.toolDefs && options.toolDefs.length > 0 && options.onToolCall) {
|
|
33875
|
-
return executeAnthropicWithTools(options);
|
|
33876
|
-
}
|
|
33877
|
-
return executeAnthropicApi(options);
|
|
33878
|
-
}
|
|
33879
|
-
return executeClaude(options);
|
|
33880
|
-
}
|
|
33881
|
-
async function executeClaude(options) {
|
|
33882
|
-
const execOpts = {
|
|
33883
|
-
timeoutMs: options.timeoutMs,
|
|
33884
|
-
maxTurns: options.maxTurns,
|
|
33885
|
-
allowedTools: options.allowedTools,
|
|
33886
|
-
disallowedTools: options.disallowedTools,
|
|
33887
|
-
onProgress: options.onProgress,
|
|
33888
|
-
onConsoleLog: options.onConsoleLog,
|
|
33889
|
-
resumeSessionId: options.resumeSessionId,
|
|
33890
|
-
systemPrompt: options.systemPrompt,
|
|
33891
|
-
model: options.model === "claude" ? void 0 : options.model
|
|
33892
|
-
};
|
|
33893
|
-
const result = await executeClaudeCode(options.prompt, execOpts);
|
|
33894
|
-
return {
|
|
33895
|
-
output: result.stdout,
|
|
33896
|
-
exitCode: result.exitCode,
|
|
33897
|
-
durationMs: result.durationMs,
|
|
33898
|
-
sessionId: result.sessionId,
|
|
33899
|
-
timedOut: result.timedOut
|
|
33900
|
-
};
|
|
33901
|
-
}
|
|
33902
|
-
async function executeCodex(options) {
|
|
33903
|
-
return new Promise((resolve2) => {
|
|
33904
|
-
const startTime = Date.now();
|
|
33905
|
-
let stdout = "";
|
|
33906
|
-
let stderr = "";
|
|
33907
|
-
let timedOut = false;
|
|
33908
|
-
let settled = false;
|
|
33909
|
-
let sessionId = options.resumeSessionId ?? null;
|
|
33910
|
-
let outputParts = [];
|
|
33911
|
-
let buffer2 = "";
|
|
33912
|
-
const modelName = parseModelSuffix(options.model, "codex");
|
|
33913
|
-
const prompt = buildPrompt(options.systemPrompt, options.prompt);
|
|
33914
|
-
const args = options.resumeSessionId ? ["exec", "resume", "--json", "--skip-git-repo-check", options.resumeSessionId, prompt] : ["exec", "--json", "--skip-git-repo-check", prompt];
|
|
33915
|
-
if (modelName) {
|
|
33916
|
-
args.splice(2, 0, "--model", modelName);
|
|
33917
|
-
}
|
|
33918
|
-
let proc;
|
|
33919
|
-
try {
|
|
33920
|
-
proc = (0, import_child_process2.spawn)("codex", args, {
|
|
33921
|
-
cwd: (0, import_os5.homedir)(),
|
|
33922
|
-
env: process.env,
|
|
33923
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
33924
|
-
windowsHide: true
|
|
33925
|
-
});
|
|
33926
|
-
} catch (err) {
|
|
33927
|
-
resolve2({
|
|
33928
|
-
output: `Error: Failed to spawn codex CLI: ${err instanceof Error ? err.message : String(err)}`,
|
|
33929
|
-
exitCode: 1,
|
|
33930
|
-
durationMs: Date.now() - startTime,
|
|
33931
|
-
sessionId: null,
|
|
33932
|
-
timedOut: false
|
|
33933
|
-
});
|
|
33934
|
-
return;
|
|
33935
|
-
}
|
|
33936
|
-
if (!proc.stdout || !proc.stderr) {
|
|
33937
|
-
resolve2({
|
|
33938
|
-
output: "Error: Failed to create stdio pipes for codex CLI",
|
|
33939
|
-
exitCode: 1,
|
|
33940
|
-
durationMs: Date.now() - startTime,
|
|
33941
|
-
sessionId: null,
|
|
33942
|
-
timedOut: false
|
|
33943
|
-
});
|
|
33944
|
-
try {
|
|
33945
|
-
proc.kill();
|
|
33946
|
-
} catch {
|
|
33947
|
-
}
|
|
33948
|
-
return;
|
|
33949
|
-
}
|
|
33950
|
-
proc.stdout.on("data", (data) => {
|
|
33951
|
-
const chunk = data.toString();
|
|
33952
|
-
stdout += chunk;
|
|
33953
|
-
buffer2 += chunk;
|
|
33954
|
-
const lines = buffer2.split("\n");
|
|
33955
|
-
buffer2 = lines.pop() ?? "";
|
|
33956
|
-
for (const line of lines) {
|
|
33957
|
-
parseCodexEventLine(line, (nextSessionId, textChunk) => {
|
|
33958
|
-
if (nextSessionId) sessionId = nextSessionId;
|
|
33959
|
-
if (textChunk) outputParts.push(textChunk);
|
|
33960
|
-
});
|
|
33961
|
-
}
|
|
33962
|
-
});
|
|
33963
|
-
proc.stderr.on("data", (data) => {
|
|
33964
|
-
stderr += data.toString();
|
|
33965
|
-
});
|
|
33966
|
-
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
33967
|
-
const timer = setTimeout(() => {
|
|
33968
|
-
if (settled) return;
|
|
33969
|
-
timedOut = true;
|
|
33970
|
-
proc.kill("SIGTERM");
|
|
33971
|
-
setTimeout(() => proc.kill("SIGKILL"), 5e3);
|
|
33972
|
-
}, timeoutMs);
|
|
33973
|
-
proc.on("close", (code) => {
|
|
33974
|
-
if (settled) return;
|
|
33975
|
-
settled = true;
|
|
33976
|
-
clearTimeout(timer);
|
|
33977
|
-
if (buffer2.trim()) {
|
|
33978
|
-
parseCodexEventLine(buffer2.trim(), (nextSessionId, textChunk) => {
|
|
33979
|
-
if (nextSessionId) sessionId = nextSessionId;
|
|
33980
|
-
if (textChunk) outputParts.push(textChunk);
|
|
33981
|
-
});
|
|
33982
|
-
}
|
|
33983
|
-
const output = outputParts.join("\n\n").trim() || stderr.trim() || stdout.trim() || "";
|
|
33984
|
-
resolve2({
|
|
33985
|
-
output,
|
|
33986
|
-
exitCode: code ?? (timedOut ? 124 : 1),
|
|
33987
|
-
durationMs: Date.now() - startTime,
|
|
33988
|
-
sessionId,
|
|
33989
|
-
timedOut
|
|
33990
|
-
});
|
|
33991
|
-
});
|
|
33992
|
-
proc.on("error", (err) => {
|
|
33993
|
-
if (settled) return;
|
|
33994
|
-
settled = true;
|
|
33995
|
-
clearTimeout(timer);
|
|
33996
|
-
resolve2({
|
|
33997
|
-
output: `Error: ${err.message}`,
|
|
33998
|
-
exitCode: 1,
|
|
33999
|
-
durationMs: Date.now() - startTime,
|
|
34000
|
-
sessionId,
|
|
34001
|
-
timedOut: false
|
|
34002
|
-
});
|
|
34003
|
-
});
|
|
34004
|
-
});
|
|
34005
|
-
}
|
|
34006
|
-
async function executeOllamaWithTools(options) {
|
|
34007
|
-
const modelName = options.model.replace(/^ollama:/, "");
|
|
34008
|
-
const startTime = Date.now();
|
|
34009
|
-
try {
|
|
34010
|
-
await ensureOllamaModel(modelName);
|
|
34011
|
-
} catch (err) {
|
|
34012
|
-
return {
|
|
34013
|
-
output: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
34014
|
-
exitCode: 1,
|
|
34015
|
-
durationMs: Date.now() - startTime,
|
|
34016
|
-
sessionId: null,
|
|
34017
|
-
timedOut: false
|
|
34018
|
-
};
|
|
34019
|
-
}
|
|
34020
|
-
const messages = [];
|
|
34021
|
-
if (options.systemPrompt) {
|
|
34022
|
-
messages.push({ role: "system", content: options.systemPrompt });
|
|
34023
|
-
}
|
|
34024
|
-
messages.push({ role: "user", content: options.prompt });
|
|
34025
|
-
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
34026
|
-
const maxTurns = options.maxTurns ?? 10;
|
|
34027
|
-
let finalOutput = "";
|
|
34028
|
-
for (let turn = 0; turn < maxTurns; turn++) {
|
|
34029
|
-
const body = JSON.stringify({
|
|
34030
|
-
model: modelName,
|
|
34031
|
-
messages,
|
|
34032
|
-
tools: options.toolDefs,
|
|
34033
|
-
stream: false
|
|
34034
|
-
});
|
|
34035
|
-
let raw;
|
|
34036
|
-
try {
|
|
34037
|
-
raw = await ollamaRequest("/api/chat", body, timeoutMs);
|
|
34038
|
-
} catch (err) {
|
|
34039
|
-
const error2 = err;
|
|
34040
|
-
const isTimeout = error2.message.includes("timeout") || error2.message.includes("aborted");
|
|
34041
|
-
return {
|
|
34042
|
-
output: `Error: ${error2.message}`,
|
|
34043
|
-
exitCode: 1,
|
|
34044
|
-
durationMs: Date.now() - startTime,
|
|
34045
|
-
sessionId: null,
|
|
34046
|
-
timedOut: isTimeout
|
|
34047
|
-
};
|
|
34048
|
-
}
|
|
34049
|
-
let parsed;
|
|
34050
|
-
try {
|
|
34051
|
-
parsed = JSON.parse(raw);
|
|
34052
|
-
} catch {
|
|
34053
|
-
return {
|
|
34054
|
-
output: raw,
|
|
34055
|
-
exitCode: 0,
|
|
34056
|
-
durationMs: Date.now() - startTime,
|
|
34057
|
-
sessionId: null,
|
|
34058
|
-
timedOut: false
|
|
34059
|
-
};
|
|
34060
|
-
}
|
|
34061
|
-
const msg = parsed.message;
|
|
34062
|
-
const toolCalls = msg.tool_calls ?? [];
|
|
34063
|
-
if (toolCalls.length === 0) {
|
|
34064
|
-
finalOutput = msg.content ?? "";
|
|
34065
|
-
break;
|
|
34066
|
-
}
|
|
34067
|
-
messages.push({ role: "assistant", content: msg.content ?? "", tool_calls: toolCalls });
|
|
34068
|
-
for (const tc of toolCalls) {
|
|
34069
|
-
const name = tc.function.name;
|
|
34070
|
-
const rawArgs = tc.function.arguments;
|
|
34071
|
-
const args = typeof rawArgs === "string" ? (() => {
|
|
34072
|
-
try {
|
|
34073
|
-
return JSON.parse(rawArgs);
|
|
34074
|
-
} catch {
|
|
34075
|
-
return {};
|
|
34076
|
-
}
|
|
34077
|
-
})() : rawArgs;
|
|
34078
|
-
let toolResult = `Tool ${name} unavailable`;
|
|
34079
|
-
if (options.onToolCall) {
|
|
34080
|
-
try {
|
|
34081
|
-
toolResult = await options.onToolCall(name, args);
|
|
34082
|
-
} catch (err) {
|
|
34083
|
-
toolResult = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
34084
|
-
}
|
|
34085
|
-
}
|
|
34086
|
-
messages.push({ role: "tool", content: toolResult });
|
|
34087
|
-
}
|
|
34088
|
-
}
|
|
34089
|
-
return {
|
|
34090
|
-
output: finalOutput || "Actions completed.",
|
|
34091
|
-
exitCode: 0,
|
|
34092
|
-
durationMs: Date.now() - startTime,
|
|
34093
|
-
sessionId: null,
|
|
34094
|
-
timedOut: false
|
|
34095
|
-
};
|
|
34096
|
-
}
|
|
34097
|
-
async function executeOpenAiWithTools(options) {
|
|
34098
|
-
const apiKey = options.apiKey?.trim() || (process.env.OPENAI_API_KEY || "").trim();
|
|
34099
|
-
if (!apiKey) return immediateError("Missing OpenAI API key.");
|
|
34100
|
-
const modelName = parseModelSuffix(options.model, "openai") || "gpt-4o-mini";
|
|
34101
|
-
const startTime = Date.now();
|
|
34102
|
-
const maxTurns = options.maxTurns ?? 10;
|
|
34103
|
-
const messages = [];
|
|
34104
|
-
if (options.systemPrompt) messages.push({ role: "system", content: options.systemPrompt });
|
|
34105
|
-
messages.push({ role: "user", content: options.prompt });
|
|
34106
|
-
let finalOutput = "";
|
|
34107
|
-
for (let turn = 0; turn < maxTurns; turn++) {
|
|
34108
|
-
const controller = new AbortController();
|
|
34109
|
-
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
34110
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
34111
|
-
let json;
|
|
34112
|
-
try {
|
|
34113
|
-
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
34114
|
-
method: "POST",
|
|
34115
|
-
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
34116
|
-
body: JSON.stringify({ model: modelName, messages, tools: options.toolDefs }),
|
|
34117
|
-
signal: controller.signal
|
|
34118
|
-
});
|
|
34119
|
-
json = await response.json();
|
|
34120
|
-
if (!response.ok) {
|
|
34121
|
-
return { output: `OpenAI API ${response.status}: ${extractApiError(json)}`, exitCode: 1, durationMs: Date.now() - startTime, sessionId: null, timedOut: false };
|
|
34122
|
-
}
|
|
34123
|
-
} catch (err) {
|
|
34124
|
-
const msg2 = err instanceof Error ? err.message : String(err);
|
|
34125
|
-
const timedOut = msg2.toLowerCase().includes("aborted") || msg2.toLowerCase().includes("timeout");
|
|
34126
|
-
return { output: `Error: ${msg2}`, exitCode: 1, durationMs: Date.now() - startTime, sessionId: null, timedOut };
|
|
34127
|
-
} finally {
|
|
34128
|
-
clearTimeout(timer);
|
|
34129
|
-
}
|
|
34130
|
-
const choices = json.choices;
|
|
34131
|
-
const choice = choices?.[0];
|
|
34132
|
-
const msg = choice?.message;
|
|
34133
|
-
if (!msg) break;
|
|
34134
|
-
const toolCalls = msg.tool_calls ?? [];
|
|
34135
|
-
if (toolCalls.length === 0) {
|
|
34136
|
-
finalOutput = (typeof msg.content === "string" ? msg.content : "") ?? "";
|
|
34137
|
-
break;
|
|
34138
|
-
}
|
|
34139
|
-
messages.push({ role: "assistant", content: msg.content ?? null, tool_calls: toolCalls });
|
|
34140
|
-
for (const tc of toolCalls) {
|
|
34141
|
-
const name = tc.function.name;
|
|
34142
|
-
let args = {};
|
|
34143
|
-
try {
|
|
34144
|
-
args = JSON.parse(tc.function.arguments);
|
|
34145
|
-
} catch {
|
|
34146
|
-
}
|
|
34147
|
-
let toolResult = `Tool ${name} unavailable`;
|
|
34148
|
-
if (options.onToolCall) {
|
|
34149
|
-
try {
|
|
34150
|
-
toolResult = await options.onToolCall(name, args);
|
|
34151
|
-
} catch (err) {
|
|
34152
|
-
toolResult = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
34153
|
-
}
|
|
34154
|
-
}
|
|
34155
|
-
messages.push({ role: "tool", tool_call_id: tc.id, content: toolResult });
|
|
34156
|
-
}
|
|
34157
|
-
}
|
|
34158
|
-
return { output: finalOutput || "Actions completed.", exitCode: 0, durationMs: Date.now() - startTime, sessionId: null, timedOut: false };
|
|
34159
|
-
}
|
|
34160
|
-
function ollamaToolDefsToAnthropic(defs) {
|
|
34161
|
-
return defs.map((d) => ({
|
|
34162
|
-
name: d.function.name,
|
|
34163
|
-
description: d.function.description,
|
|
34164
|
-
input_schema: d.function.parameters
|
|
34165
|
-
}));
|
|
34166
|
-
}
|
|
34167
|
-
async function executeAnthropicWithTools(options) {
|
|
34168
|
-
const apiKey = options.apiKey?.trim() || (process.env.ANTHROPIC_API_KEY || "").trim();
|
|
34169
|
-
if (!apiKey) return immediateError("Missing Anthropic API key.");
|
|
34170
|
-
const modelName = parseAnthropicModel(options.model);
|
|
34171
|
-
const startTime = Date.now();
|
|
34172
|
-
const maxTurns = options.maxTurns ?? 10;
|
|
34173
|
-
const anthropicTools = options.toolDefs ? ollamaToolDefsToAnthropic(options.toolDefs) : [];
|
|
34174
|
-
const messages = [{ role: "user", content: options.prompt }];
|
|
34175
|
-
let finalOutput = "";
|
|
34176
|
-
for (let turn = 0; turn < maxTurns; turn++) {
|
|
34177
|
-
const controller = new AbortController();
|
|
34178
|
-
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
34179
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
34180
|
-
let json;
|
|
34181
|
-
try {
|
|
34182
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
34183
|
-
method: "POST",
|
|
34184
|
-
headers: {
|
|
34185
|
-
"x-api-key": apiKey,
|
|
34186
|
-
"anthropic-version": "2023-06-01",
|
|
34187
|
-
"content-type": "application/json"
|
|
34188
|
-
},
|
|
34189
|
-
body: JSON.stringify({
|
|
34190
|
-
model: modelName,
|
|
34191
|
-
max_tokens: 4096,
|
|
34192
|
-
system: options.systemPrompt,
|
|
34193
|
-
tools: anthropicTools,
|
|
34194
|
-
messages
|
|
34195
|
-
}),
|
|
34196
|
-
signal: controller.signal
|
|
34197
|
-
});
|
|
34198
|
-
json = await response.json();
|
|
34199
|
-
if (!response.ok) {
|
|
34200
|
-
return { output: `Anthropic API ${response.status}: ${extractApiError(json)}`, exitCode: 1, durationMs: Date.now() - startTime, sessionId: null, timedOut: false };
|
|
34201
|
-
}
|
|
34202
|
-
} catch (err) {
|
|
34203
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
34204
|
-
const timedOut = msg.toLowerCase().includes("aborted") || msg.toLowerCase().includes("timeout");
|
|
34205
|
-
return { output: `Error: ${msg}`, exitCode: 1, durationMs: Date.now() - startTime, sessionId: null, timedOut };
|
|
34206
|
-
} finally {
|
|
34207
|
-
clearTimeout(timer);
|
|
34208
|
-
}
|
|
34209
|
-
const stopReason = json.stop_reason;
|
|
34210
|
-
const content = json.content;
|
|
34211
|
-
const toolUseBlocks = (content ?? []).filter((b) => b.type === "tool_use");
|
|
34212
|
-
const textBlocks = (content ?? []).filter((b) => b.type === "text");
|
|
34213
|
-
if (toolUseBlocks.length === 0 || stopReason !== "tool_use") {
|
|
34214
|
-
finalOutput = textBlocks.map((b) => b.text ?? "").join("\n").trim();
|
|
34215
|
-
break;
|
|
34216
|
-
}
|
|
34217
|
-
messages.push({ role: "assistant", content: content ?? [] });
|
|
34218
|
-
const resultBlocks = [];
|
|
34219
|
-
for (const block of toolUseBlocks) {
|
|
34220
|
-
const name = block.name ?? "";
|
|
34221
|
-
const args = block.input ?? {};
|
|
34222
|
-
let toolResult = `Tool ${name} unavailable`;
|
|
34223
|
-
if (options.onToolCall) {
|
|
34224
|
-
try {
|
|
34225
|
-
toolResult = await options.onToolCall(name, args);
|
|
34226
|
-
} catch (err) {
|
|
34227
|
-
toolResult = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
34228
|
-
}
|
|
34229
|
-
}
|
|
34230
|
-
resultBlocks.push({ type: "tool_result", id: block.id, content: toolResult });
|
|
34231
|
-
}
|
|
34232
|
-
messages.push({ role: "user", content: resultBlocks });
|
|
34233
|
-
}
|
|
34234
|
-
return { output: finalOutput || "Actions completed.", exitCode: 0, durationMs: Date.now() - startTime, sessionId: null, timedOut: false };
|
|
34235
|
-
}
|
|
34236
|
-
async function executeOpenAiApi(options) {
|
|
34237
|
-
const apiKey = options.apiKey?.trim() || (process.env.OPENAI_API_KEY || "").trim();
|
|
34238
|
-
if (!apiKey) {
|
|
34239
|
-
return immediateError('Missing OpenAI API key. Set room credential "openai_api_key" or OPENAI_API_KEY.');
|
|
34240
|
-
}
|
|
34241
|
-
const modelName = parseModelSuffix(options.model, "openai") || "gpt-4o-mini";
|
|
34242
|
-
const messages = [];
|
|
34243
|
-
if (options.systemPrompt) messages.push({ role: "system", content: options.systemPrompt });
|
|
34244
|
-
messages.push({ role: "user", content: options.prompt });
|
|
34245
|
-
const startTime = Date.now();
|
|
34246
|
-
const controller = new AbortController();
|
|
34247
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
|
|
34248
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
34249
|
-
try {
|
|
34250
|
-
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
34251
|
-
method: "POST",
|
|
34252
|
-
headers: {
|
|
34253
|
-
"Authorization": `Bearer ${apiKey}`,
|
|
34254
|
-
"Content-Type": "application/json"
|
|
34255
|
-
},
|
|
34256
|
-
body: JSON.stringify({
|
|
34257
|
-
model: modelName,
|
|
34258
|
-
messages
|
|
34259
|
-
}),
|
|
34260
|
-
signal: controller.signal
|
|
34261
|
-
});
|
|
34262
|
-
const json = await response.json();
|
|
34263
|
-
if (!response.ok) {
|
|
34264
|
-
return {
|
|
34265
|
-
output: `OpenAI API ${response.status}: ${extractApiError(json)}`,
|
|
34266
|
-
exitCode: 1,
|
|
34267
|
-
durationMs: Date.now() - startTime,
|
|
34268
|
-
sessionId: null,
|
|
34269
|
-
timedOut: false
|
|
34270
|
-
};
|
|
34271
|
-
}
|
|
34272
|
-
const output = extractOpenAiText(json);
|
|
34273
|
-
return {
|
|
34274
|
-
output,
|
|
34275
|
-
exitCode: 0,
|
|
34276
|
-
durationMs: Date.now() - startTime,
|
|
34277
|
-
sessionId: null,
|
|
34278
|
-
timedOut: false
|
|
34279
|
-
};
|
|
34280
|
-
} catch (err) {
|
|
34281
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
34282
|
-
const timedOut = message.toLowerCase().includes("aborted") || message.toLowerCase().includes("timeout");
|
|
34283
|
-
return {
|
|
34284
|
-
output: `Error: ${message}`,
|
|
34285
|
-
exitCode: 1,
|
|
34286
|
-
durationMs: Date.now() - startTime,
|
|
34287
|
-
sessionId: null,
|
|
34288
|
-
timedOut
|
|
34289
|
-
};
|
|
34290
|
-
} finally {
|
|
34291
|
-
clearTimeout(timer);
|
|
34292
|
-
}
|
|
34293
|
-
}
|
|
34294
|
-
async function executeAnthropicApi(options) {
|
|
34295
|
-
const apiKey = options.apiKey?.trim() || (process.env.ANTHROPIC_API_KEY || "").trim();
|
|
34296
|
-
if (!apiKey) {
|
|
34297
|
-
return immediateError('Missing Anthropic API key. Set room credential "anthropic_api_key" or ANTHROPIC_API_KEY.');
|
|
34298
|
-
}
|
|
34299
|
-
const modelName = parseAnthropicModel(options.model);
|
|
34300
|
-
const startTime = Date.now();
|
|
34301
|
-
const controller = new AbortController();
|
|
34302
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
|
|
34303
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
34304
|
-
try {
|
|
34305
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
34306
|
-
method: "POST",
|
|
34307
|
-
headers: {
|
|
34308
|
-
"x-api-key": apiKey,
|
|
34309
|
-
"anthropic-version": "2023-06-01",
|
|
34310
|
-
"content-type": "application/json"
|
|
34311
|
-
},
|
|
34312
|
-
body: JSON.stringify({
|
|
34313
|
-
model: modelName,
|
|
34314
|
-
max_tokens: 2048,
|
|
34315
|
-
system: options.systemPrompt,
|
|
34316
|
-
messages: [{ role: "user", content: options.prompt }]
|
|
34317
|
-
}),
|
|
34318
|
-
signal: controller.signal
|
|
34319
|
-
});
|
|
34320
|
-
const json = await response.json();
|
|
34321
|
-
if (!response.ok) {
|
|
34322
|
-
return {
|
|
34323
|
-
output: `Anthropic API ${response.status}: ${extractApiError(json)}`,
|
|
34324
|
-
exitCode: 1,
|
|
34325
|
-
durationMs: Date.now() - startTime,
|
|
34326
|
-
sessionId: null,
|
|
34327
|
-
timedOut: false
|
|
34328
|
-
};
|
|
34329
|
-
}
|
|
34330
|
-
const output = extractAnthropicText(json);
|
|
34331
|
-
return {
|
|
34332
|
-
output,
|
|
34333
|
-
exitCode: 0,
|
|
34334
|
-
durationMs: Date.now() - startTime,
|
|
34335
|
-
sessionId: null,
|
|
34336
|
-
timedOut: false
|
|
34337
|
-
};
|
|
34338
|
-
} catch (err) {
|
|
34339
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
34340
|
-
const timedOut = message.toLowerCase().includes("aborted") || message.toLowerCase().includes("timeout");
|
|
34341
|
-
return {
|
|
34342
|
-
output: `Error: ${message}`,
|
|
34343
|
-
exitCode: 1,
|
|
34344
|
-
durationMs: Date.now() - startTime,
|
|
34345
|
-
sessionId: null,
|
|
34346
|
-
timedOut
|
|
34347
|
-
};
|
|
34348
|
-
} finally {
|
|
34349
|
-
clearTimeout(timer);
|
|
34350
|
-
}
|
|
34351
|
-
}
|
|
34352
|
-
async function executeOllama(options) {
|
|
34353
|
-
const modelName = options.model.replace(/^ollama:/, "");
|
|
34354
|
-
const startTime = Date.now();
|
|
34355
|
-
try {
|
|
34356
|
-
await ensureOllamaModel(modelName);
|
|
34357
|
-
} catch (err) {
|
|
34358
|
-
return {
|
|
34359
|
-
output: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
34360
|
-
exitCode: 1,
|
|
34361
|
-
durationMs: Date.now() - startTime,
|
|
34362
|
-
sessionId: null,
|
|
34363
|
-
timedOut: false
|
|
34364
|
-
};
|
|
34365
|
-
}
|
|
34366
|
-
const messages = [];
|
|
34367
|
-
if (options.systemPrompt) {
|
|
34368
|
-
messages.push({ role: "system", content: options.systemPrompt });
|
|
34369
|
-
}
|
|
34370
|
-
messages.push({ role: "user", content: options.prompt });
|
|
34371
|
-
const body = JSON.stringify({
|
|
34372
|
-
model: modelName,
|
|
34373
|
-
messages,
|
|
34374
|
-
stream: false
|
|
34375
|
-
});
|
|
34376
|
-
try {
|
|
34377
|
-
const response = await ollamaRequest("/api/chat", body, options.timeoutMs ?? 5 * 60 * 1e3);
|
|
34378
|
-
const parsed = JSON.parse(response);
|
|
34379
|
-
const output = parsed?.message?.content ?? "";
|
|
34380
|
-
return {
|
|
34381
|
-
output,
|
|
34382
|
-
exitCode: 0,
|
|
34383
|
-
durationMs: Date.now() - startTime,
|
|
34384
|
-
sessionId: null,
|
|
34385
|
-
timedOut: false
|
|
34386
|
-
};
|
|
34387
|
-
} catch (err) {
|
|
34388
|
-
const error2 = err;
|
|
34389
|
-
const isTimeout = error2.message.includes("timeout") || error2.message.includes("aborted");
|
|
34390
|
-
return {
|
|
34391
|
-
output: `Error: ${error2.message}`,
|
|
34392
|
-
exitCode: 1,
|
|
34393
|
-
durationMs: Date.now() - startTime,
|
|
34394
|
-
sessionId: null,
|
|
34395
|
-
timedOut: isTimeout
|
|
34396
|
-
};
|
|
34397
|
-
}
|
|
34398
|
-
}
|
|
34399
33809
|
function parseModelSuffix(model, prefix) {
|
|
34400
33810
|
const trimmed = model.trim();
|
|
34401
33811
|
if (trimmed === prefix) return "";
|
|
@@ -34412,29 +33822,6 @@ function parseAnthropicModel(model) {
|
|
|
34412
33822
|
if (claudeApiModel) return claudeApiModel;
|
|
34413
33823
|
return normalized;
|
|
34414
33824
|
}
|
|
34415
|
-
function parseCodexEventLine(line, onEvent) {
|
|
34416
|
-
const trimmed = line.trim();
|
|
34417
|
-
if (!trimmed) return;
|
|
34418
|
-
let parsed;
|
|
34419
|
-
try {
|
|
34420
|
-
parsed = JSON.parse(trimmed);
|
|
34421
|
-
} catch {
|
|
34422
|
-
return;
|
|
34423
|
-
}
|
|
34424
|
-
const type = parsed.type;
|
|
34425
|
-
if (type === "thread.started" && typeof parsed.thread_id === "string") {
|
|
34426
|
-
onEvent(parsed.thread_id, null);
|
|
34427
|
-
return;
|
|
34428
|
-
}
|
|
34429
|
-
if (type === "item.completed") {
|
|
34430
|
-
const item = parsed.item;
|
|
34431
|
-
if (!item) return;
|
|
34432
|
-
const itemType = item.type;
|
|
34433
|
-
if (itemType === "agent_message" && typeof item.text === "string") {
|
|
34434
|
-
onEvent(null, item.text);
|
|
34435
|
-
}
|
|
34436
|
-
}
|
|
34437
|
-
}
|
|
34438
33825
|
function extractOpenAiText(json) {
|
|
34439
33826
|
const choices = json.choices;
|
|
34440
33827
|
if (Array.isArray(choices) && choices.length > 0) {
|
|
@@ -34464,20 +33851,6 @@ function extractAnthropicText(json) {
|
|
|
34464
33851
|
}
|
|
34465
33852
|
return JSON.stringify(json);
|
|
34466
33853
|
}
|
|
34467
|
-
function extractApiError(json) {
|
|
34468
|
-
const error2 = json.error;
|
|
34469
|
-
if (!error2) return JSON.stringify(json);
|
|
34470
|
-
if (typeof error2.message === "string") return error2.message;
|
|
34471
|
-
return JSON.stringify(error2);
|
|
34472
|
-
}
|
|
34473
|
-
function buildPrompt(systemPrompt, prompt) {
|
|
34474
|
-
if (!systemPrompt) return prompt;
|
|
34475
|
-
return `System instructions:
|
|
34476
|
-
${systemPrompt}
|
|
34477
|
-
|
|
34478
|
-
User request:
|
|
34479
|
-
${prompt}`;
|
|
34480
|
-
}
|
|
34481
33854
|
function immediateError(message) {
|
|
34482
33855
|
return {
|
|
34483
33856
|
output: `Error: ${message}`,
|
|
@@ -34487,59 +33860,6 @@ function immediateError(message) {
|
|
|
34487
33860
|
timedOut: false
|
|
34488
33861
|
};
|
|
34489
33862
|
}
|
|
34490
|
-
async function executeOllamaOnStation(cloudRoomId, stationId, options) {
|
|
34491
|
-
const modelName = options.model.replace(/^ollama:/, "");
|
|
34492
|
-
const startTime = Date.now();
|
|
34493
|
-
const messages = [];
|
|
34494
|
-
if (options.systemPrompt) {
|
|
34495
|
-
messages.push({ role: "system", content: options.systemPrompt });
|
|
34496
|
-
}
|
|
34497
|
-
messages.push({ role: "user", content: options.prompt });
|
|
34498
|
-
const payload = JSON.stringify({
|
|
34499
|
-
model: modelName,
|
|
34500
|
-
messages,
|
|
34501
|
-
stream: false
|
|
34502
|
-
});
|
|
34503
|
-
const b64 = Buffer.from(payload).toString("base64");
|
|
34504
|
-
const command = `echo '${b64}' | base64 -d | curl -s --max-time 300 http://localhost:11434/api/chat -d @-`;
|
|
34505
|
-
const result = await execOnCloudStation(cloudRoomId, stationId, command, 36e4);
|
|
34506
|
-
if (!result) {
|
|
34507
|
-
return {
|
|
34508
|
-
output: "Error: station execution failed (station unreachable or Ollama not running)",
|
|
34509
|
-
exitCode: 1,
|
|
34510
|
-
durationMs: Date.now() - startTime,
|
|
34511
|
-
sessionId: null,
|
|
34512
|
-
timedOut: false
|
|
34513
|
-
};
|
|
34514
|
-
}
|
|
34515
|
-
if (result.exitCode !== 0) {
|
|
34516
|
-
return {
|
|
34517
|
-
output: result.stderr || result.stdout || `Station exec failed with exit code ${result.exitCode}`,
|
|
34518
|
-
exitCode: result.exitCode,
|
|
34519
|
-
durationMs: Date.now() - startTime,
|
|
34520
|
-
sessionId: null,
|
|
34521
|
-
timedOut: false
|
|
34522
|
-
};
|
|
34523
|
-
}
|
|
34524
|
-
try {
|
|
34525
|
-
const parsed = JSON.parse(result.stdout);
|
|
34526
|
-
return {
|
|
34527
|
-
output: parsed?.message?.content ?? "",
|
|
34528
|
-
exitCode: 0,
|
|
34529
|
-
durationMs: Date.now() - startTime,
|
|
34530
|
-
sessionId: null,
|
|
34531
|
-
timedOut: false
|
|
34532
|
-
};
|
|
34533
|
-
} catch {
|
|
34534
|
-
return {
|
|
34535
|
-
output: result.stdout || "(no output from Ollama)",
|
|
34536
|
-
exitCode: 1,
|
|
34537
|
-
durationMs: Date.now() - startTime,
|
|
34538
|
-
sessionId: null,
|
|
34539
|
-
timedOut: false
|
|
34540
|
-
};
|
|
34541
|
-
}
|
|
34542
|
-
}
|
|
34543
33863
|
async function executeApiOnStation(cloudRoomId, stationId, options) {
|
|
34544
33864
|
const startTime = Date.now();
|
|
34545
33865
|
const apiKey = options.apiKey;
|
|
@@ -34613,7 +33933,6 @@ function normalizeModel(model) {
|
|
|
34613
33933
|
}
|
|
34614
33934
|
function getModelProvider(model) {
|
|
34615
33935
|
const normalized = normalizeModel(model);
|
|
34616
|
-
if (normalized.startsWith("ollama:")) return "ollama";
|
|
34617
33936
|
if (normalized === "codex" || normalized.startsWith("codex:")) return "codex_subscription";
|
|
34618
33937
|
if (normalized === "openai" || normalized.startsWith("openai:")) return "openai_api";
|
|
34619
33938
|
if (normalized === "anthropic" || normalized.startsWith("anthropic:") || normalized.startsWith("claude-api:")) {
|
|
@@ -34984,20 +34303,13 @@ async function executeTask(taskId, options) {
|
|
|
34984
34303
|
} catch (err) {
|
|
34985
34304
|
console.warn("Non-fatal: worker resolution failed:", err);
|
|
34986
34305
|
}
|
|
34987
|
-
const isStationModel = model?.startsWith("
|
|
34306
|
+
const isStationModel = model?.startsWith("openai:") || model?.startsWith("anthropic:") || model?.startsWith("claude-api:");
|
|
34988
34307
|
if (isStationModel && task.roomId) {
|
|
34989
34308
|
runningTasks.add(taskId);
|
|
34990
34309
|
try {
|
|
34991
34310
|
const cloudRoomId = getRoomCloudId(task.roomId);
|
|
34992
34311
|
const stations = await listCloudStations(cloudRoomId);
|
|
34993
34312
|
const activeStations = stations.filter((s) => s.status === "active");
|
|
34994
|
-
if (activeStations.length === 0 && model?.startsWith("ollama:")) {
|
|
34995
|
-
const run2 = createTaskRun(db2, taskId);
|
|
34996
|
-
const errorMsg = "No active station available. Ollama workers require a station. Rent one with quoroom_station_create (minimum tier: small).";
|
|
34997
|
-
completeTaskRun(db2, run2.id, "", void 0, errorMsg);
|
|
34998
|
-
onFailed?.(task, errorMsg);
|
|
34999
|
-
return { success: false, output: "", errorMessage: errorMsg, durationMs: Date.now() - startTime };
|
|
35000
|
-
}
|
|
35001
34313
|
if (activeStations.length > 0) {
|
|
35002
34314
|
const run2 = createTaskRun(db2, taskId);
|
|
35003
34315
|
try {
|
|
@@ -35029,25 +34341,15 @@ ${augmentedPrompt}`;
|
|
|
35029
34341
|
}
|
|
35030
34342
|
const timeoutMs = task.timeoutMinutes != null ? task.timeoutMinutes * 60 * 1e3 : void 0;
|
|
35031
34343
|
const stationModel = model;
|
|
35032
|
-
|
|
35033
|
-
|
|
35034
|
-
|
|
35035
|
-
|
|
35036
|
-
|
|
35037
|
-
|
|
35038
|
-
|
|
35039
|
-
|
|
35040
|
-
|
|
35041
|
-
const apiKey = resolveApiKeyForModel(db2, task.roomId, stationModel);
|
|
35042
|
-
agentResult = await executeApiOnStation(cloudRoomId, station.id, {
|
|
35043
|
-
model: stationModel,
|
|
35044
|
-
prompt: augmentedPrompt,
|
|
35045
|
-
systemPrompt,
|
|
35046
|
-
timeoutMs,
|
|
35047
|
-
apiKey
|
|
35048
|
-
});
|
|
35049
|
-
}
|
|
35050
|
-
const result = ollamaResultToExecutionResult(agentResult);
|
|
34344
|
+
const apiKey = resolveApiKeyForModel(db2, task.roomId, stationModel);
|
|
34345
|
+
const agentResult = await executeApiOnStation(cloudRoomId, station.id, {
|
|
34346
|
+
model: stationModel,
|
|
34347
|
+
prompt: augmentedPrompt,
|
|
34348
|
+
systemPrompt,
|
|
34349
|
+
timeoutMs,
|
|
34350
|
+
apiKey
|
|
34351
|
+
});
|
|
34352
|
+
const result = agentResultToExecutionResult(agentResult);
|
|
35051
34353
|
return finishRun(db2, run2.id, taskId, task, result, resultsDir, onComplete, onFailed);
|
|
35052
34354
|
} catch (err) {
|
|
35053
34355
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -35121,17 +34423,6 @@ ${augmentedPrompt}`;
|
|
|
35121
34423
|
const disallowedTools = task.disallowedTools ?? void 0;
|
|
35122
34424
|
const consoleLog = createConsoleLogBuffer(db2, run.id, onConsoleLogEntry);
|
|
35123
34425
|
let lastProgressUpdate = 0;
|
|
35124
|
-
if (model?.startsWith("ollama:")) {
|
|
35125
|
-
const agentResult = await executeAgent({
|
|
35126
|
-
model,
|
|
35127
|
-
prompt: augmentedPrompt,
|
|
35128
|
-
systemPrompt,
|
|
35129
|
-
timeoutMs
|
|
35130
|
-
});
|
|
35131
|
-
consoleLog.flush();
|
|
35132
|
-
const result2 = ollamaResultToExecutionResult(agentResult);
|
|
35133
|
-
return finishRun(db2, run.id, taskId, task, result2, resultsDir, onComplete, onFailed);
|
|
35134
|
-
}
|
|
35135
34426
|
const execOptions = {
|
|
35136
34427
|
systemPrompt,
|
|
35137
34428
|
model,
|
|
@@ -35215,7 +34506,7 @@ ${retryPrompt}`;
|
|
|
35215
34506
|
releaseSlot();
|
|
35216
34507
|
}
|
|
35217
34508
|
}
|
|
35218
|
-
function
|
|
34509
|
+
function agentResultToExecutionResult(result) {
|
|
35219
34510
|
return {
|
|
35220
34511
|
stdout: result.output,
|
|
35221
34512
|
stderr: "",
|
|
@@ -35266,6 +34557,15 @@ function finishRun(db2, runId, taskId, task, result, resultsDir, onComplete, onF
|
|
|
35266
34557
|
} catch (err) {
|
|
35267
34558
|
console.warn("Non-fatal: memory storage failed:", err);
|
|
35268
34559
|
}
|
|
34560
|
+
const fullError = (output + " " + errorMsg).toLowerCase();
|
|
34561
|
+
const isTerminalError = !result.timedOut && (fullError.includes("failed to spawn") || fullError.includes("enoent") || fullError.includes("missing openai api key") || fullError.includes("missing anthropic api key") || fullError.includes("missing api key"));
|
|
34562
|
+
if (isTerminalError) {
|
|
34563
|
+
try {
|
|
34564
|
+
updateTask(db2, taskId, { status: "paused" });
|
|
34565
|
+
console.log(`Task ${taskId} auto-paused: terminal error (won't retry): ${errorMsg.slice(0, 100)}`);
|
|
34566
|
+
} catch {
|
|
34567
|
+
}
|
|
34568
|
+
}
|
|
35269
34569
|
onFailed?.(task, errorMsg);
|
|
35270
34570
|
return { success: false, output, errorMessage: errorMsg, durationMs: result.durationMs, resultFilePath };
|
|
35271
34571
|
}
|
|
@@ -35293,6 +34593,19 @@ ${output}
|
|
|
35293
34593
|
}
|
|
35294
34594
|
|
|
35295
34595
|
// src/mcp/tools/scheduler.ts
|
|
34596
|
+
function getServerPort() {
|
|
34597
|
+
try {
|
|
34598
|
+
const dbPath = process.env.QUOROOM_DB_PATH;
|
|
34599
|
+
const dataDir = process.env.QUOROOM_DATA_DIR || (dbPath ? (0, import_path4.dirname)(dbPath) : (0, import_path4.join)((0, import_os5.homedir)(), `.${APP_NAME.toLowerCase()}`));
|
|
34600
|
+
const portFile = (0, import_path4.join)(dataDir, "api.port");
|
|
34601
|
+
if ((0, import_fs4.existsSync)(portFile)) {
|
|
34602
|
+
const port = parseInt((0, import_fs4.readFileSync)(portFile, "utf-8").trim(), 10);
|
|
34603
|
+
return Number.isFinite(port) && port > 0 ? port : null;
|
|
34604
|
+
}
|
|
34605
|
+
} catch {
|
|
34606
|
+
}
|
|
34607
|
+
return null;
|
|
34608
|
+
}
|
|
35296
34609
|
function generateTaskName(prompt) {
|
|
35297
34610
|
const cleaned = prompt.replace(/^(please |can you |i want you to |i need you to )/i, "").trim();
|
|
35298
34611
|
const firstSentence = cleaned.split(/[.\n]/)[0].trim();
|
|
@@ -35304,7 +34617,7 @@ function registerSchedulerTools(server) {
|
|
|
35304
34617
|
"quoroom_schedule",
|
|
35305
34618
|
{
|
|
35306
34619
|
title: "Schedule Task",
|
|
35307
|
-
description:
|
|
34620
|
+
description: 'Create a task \u2014 recurring (cron), one-time (specific datetime), on-demand (manual trigger), or webhook-triggered. Provide cronExpression for recurring, scheduledAt for one-time, triggerType="webhook" for HTTP-triggered tasks, or neither for on-demand. RESPONSE STYLE: After calling this tool, confirm to the user in 1 short sentence. Do NOT add notes, tips, caveats, or advice. Do NOT mention task IDs, cron syntax, session continuity, workers, timeouts, Electron, or internal tool names.',
|
|
35308
34621
|
inputSchema: {
|
|
35309
34622
|
name: external_exports.string().max(200).optional().describe(
|
|
35310
34623
|
'Short descriptive name for the task. If omitted, a name will be generated from the prompt. Examples: "HN Morning Digest", "Inbox Summary", "Download Organizer".'
|
|
@@ -35340,16 +34653,21 @@ function registerSchedulerTools(server) {
|
|
|
35340
34653
|
disallowedTools: external_exports.string().max(500).optional().describe(
|
|
35341
34654
|
'Comma-separated list of tools the task is NOT allowed to use. Example: "WebFetch" to force WebSearch-only (avoids slow URL fetches). "Edit,Write" to make a task read-only.'
|
|
35342
34655
|
),
|
|
34656
|
+
triggerType: external_exports.enum(["cron", "once", "manual", "webhook"]).optional().describe(
|
|
34657
|
+
'Override the trigger type. "webhook": task runs when an external service POSTs to its webhook URL. Usually inferred automatically \u2014 only set this explicitly for webhook tasks.'
|
|
34658
|
+
),
|
|
35343
34659
|
roomId: external_exports.number().int().positive().optional().describe(
|
|
35344
34660
|
"Assign this task to a room by ID. When set, the task is scoped to that room."
|
|
35345
34661
|
)
|
|
35346
34662
|
}
|
|
35347
34663
|
},
|
|
35348
|
-
async ({ name, prompt, cronExpression, scheduledAt, description, maxRuns, workerId, sessionContinuity, timeout, maxTurns, allowedTools, disallowedTools, roomId }) => {
|
|
34664
|
+
async ({ name, prompt, cronExpression, scheduledAt, description, maxRuns, workerId, sessionContinuity, timeout, maxTurns, allowedTools, disallowedTools, triggerType: triggerTypeInput, roomId }) => {
|
|
35349
34665
|
const db2 = getMcpDatabase();
|
|
35350
|
-
let triggerType = "manual";
|
|
35351
|
-
if (
|
|
35352
|
-
|
|
34666
|
+
let triggerType = triggerTypeInput ?? "manual";
|
|
34667
|
+
if (!triggerTypeInput) {
|
|
34668
|
+
if (cronExpression) triggerType = "cron";
|
|
34669
|
+
else if (scheduledAt) triggerType = "once";
|
|
34670
|
+
}
|
|
35353
34671
|
const taskName = (name || generateTaskName(prompt) || "Untitled task").trim();
|
|
35354
34672
|
if (triggerType === "cron" && cronExpression && !import_node_cron.default.validate(cronExpression)) {
|
|
35355
34673
|
return {
|
|
@@ -35390,13 +34708,15 @@ function registerSchedulerTools(server) {
|
|
|
35390
34708
|
};
|
|
35391
34709
|
}
|
|
35392
34710
|
}
|
|
35393
|
-
|
|
34711
|
+
const webhookToken = triggerType === TRIGGER_TYPES.WEBHOOK ? (0, import_crypto7.randomBytes)(16).toString("hex") : void 0;
|
|
34712
|
+
const task = createTask(db2, {
|
|
35394
34713
|
name: taskName,
|
|
35395
34714
|
prompt,
|
|
35396
34715
|
cronExpression: cronExpression ?? void 0,
|
|
35397
34716
|
scheduledAt: scheduledAt ?? void 0,
|
|
35398
34717
|
triggerType,
|
|
35399
34718
|
triggerConfig: JSON.stringify({ source: process.env.QUOROOM_SOURCE || "claude-desktop" }),
|
|
34719
|
+
webhookToken,
|
|
35400
34720
|
description,
|
|
35401
34721
|
executor: "claude_code",
|
|
35402
34722
|
maxRuns: maxRuns ?? void 0,
|
|
@@ -35408,14 +34728,25 @@ function registerSchedulerTools(server) {
|
|
|
35408
34728
|
disallowedTools: disallowedTools ?? void 0,
|
|
35409
34729
|
roomId: roomId ?? void 0
|
|
35410
34730
|
});
|
|
35411
|
-
if (triggerType ===
|
|
34731
|
+
if (triggerType === TRIGGER_TYPES.WEBHOOK) {
|
|
34732
|
+
const port = getServerPort();
|
|
34733
|
+
const webhookUrl = port ? `http://localhost:${port}/api/hooks/task/${webhookToken}` : `/api/hooks/task/${webhookToken}`;
|
|
34734
|
+
return {
|
|
34735
|
+
content: [{
|
|
34736
|
+
type: "text",
|
|
34737
|
+
text: `Created webhook task "${taskName}" (id: ${task.id}).
|
|
34738
|
+
Webhook URL: ${webhookUrl}
|
|
34739
|
+
Trigger it with: curl -X POST ${webhookUrl}`
|
|
34740
|
+
}]
|
|
34741
|
+
};
|
|
34742
|
+
} else if (triggerType === TRIGGER_TYPES.CRON) {
|
|
35412
34743
|
return {
|
|
35413
34744
|
content: [{
|
|
35414
34745
|
type: "text",
|
|
35415
34746
|
text: `Scheduled recurring task "${taskName}".`
|
|
35416
34747
|
}]
|
|
35417
34748
|
};
|
|
35418
|
-
} else if (triggerType ===
|
|
34749
|
+
} else if (triggerType === TRIGGER_TYPES.ONCE) {
|
|
35419
34750
|
return {
|
|
35420
34751
|
content: [{
|
|
35421
34752
|
type: "text",
|
|
@@ -35506,7 +34837,7 @@ function registerSchedulerTools(server) {
|
|
|
35506
34837
|
if (task.status !== TASK_STATUSES.ACTIVE) {
|
|
35507
34838
|
updateTask(db2, id, { status: TASK_STATUSES.ACTIVE });
|
|
35508
34839
|
}
|
|
35509
|
-
const resultsDir = process.env.QUOROOM_RESULTS_DIR || (0, import_path4.join)((0,
|
|
34840
|
+
const resultsDir = process.env.QUOROOM_RESULTS_DIR || (0, import_path4.join)((0, import_os5.homedir)(), APP_NAME, "results");
|
|
35510
34841
|
executeTask(id, { db: db2, resultsDir }).then((result) => {
|
|
35511
34842
|
if (originalStatus !== TASK_STATUSES.ACTIVE) {
|
|
35512
34843
|
const currentTask = getTask(db2, id);
|
|
@@ -35781,10 +35112,80 @@ function registerSchedulerTools(server) {
|
|
|
35781
35112
|
};
|
|
35782
35113
|
}
|
|
35783
35114
|
);
|
|
35115
|
+
server.registerTool(
|
|
35116
|
+
"quoroom_webhook_url",
|
|
35117
|
+
{
|
|
35118
|
+
title: "Get Webhook URL",
|
|
35119
|
+
description: "Get the webhook URL for a task or room. For tasks: use the URL to trigger the task from external services (GitHub, Stripe, monitoring tools, etc.). For rooms: use the URL to inject a message and immediately wake the queen.",
|
|
35120
|
+
inputSchema: {
|
|
35121
|
+
taskId: external_exports.number().int().positive().optional().describe("Task ID to get the webhook URL for"),
|
|
35122
|
+
roomId: external_exports.number().int().positive().optional().describe("Room ID to get the queen-wake webhook URL for"),
|
|
35123
|
+
generateIfMissing: external_exports.boolean().optional().describe("If the task/room has no webhook token, generate one. Default: false.")
|
|
35124
|
+
}
|
|
35125
|
+
},
|
|
35126
|
+
async ({ taskId, roomId, generateIfMissing }) => {
|
|
35127
|
+
const db2 = getMcpDatabase();
|
|
35128
|
+
const port = getServerPort();
|
|
35129
|
+
if (taskId) {
|
|
35130
|
+
const task = getTask(db2, taskId);
|
|
35131
|
+
if (!task) {
|
|
35132
|
+
return { content: [{ type: "text", text: `No task found with id ${taskId}.` }] };
|
|
35133
|
+
}
|
|
35134
|
+
let token = task.webhookToken;
|
|
35135
|
+
if (!token && generateIfMissing) {
|
|
35136
|
+
token = (0, import_crypto7.randomBytes)(16).toString("hex");
|
|
35137
|
+
updateTask(db2, taskId, { webhookToken: token });
|
|
35138
|
+
}
|
|
35139
|
+
if (!token) {
|
|
35140
|
+
return {
|
|
35141
|
+
content: [{ type: "text", text: `Task "${task.name}" has no webhook token. Pass generateIfMissing: true to create one.` }]
|
|
35142
|
+
};
|
|
35143
|
+
}
|
|
35144
|
+
const url = port ? `http://localhost:${port}/api/hooks/task/${token}` : `/api/hooks/task/${token}`;
|
|
35145
|
+
return {
|
|
35146
|
+
content: [{
|
|
35147
|
+
type: "text",
|
|
35148
|
+
text: `Webhook URL for task "${task.name}":
|
|
35149
|
+
${url}
|
|
35150
|
+
|
|
35151
|
+
Trigger: curl -X POST ${url}
|
|
35152
|
+
With payload: curl -X POST ${url} -H "Content-Type: application/json" -d '{"message":"triggered by github"}'`
|
|
35153
|
+
}]
|
|
35154
|
+
};
|
|
35155
|
+
}
|
|
35156
|
+
if (roomId) {
|
|
35157
|
+
const room = getRoom(db2, roomId);
|
|
35158
|
+
if (!room) {
|
|
35159
|
+
return { content: [{ type: "text", text: `No room found with id ${roomId}.` }] };
|
|
35160
|
+
}
|
|
35161
|
+
let token = room.webhookToken;
|
|
35162
|
+
if (!token && generateIfMissing) {
|
|
35163
|
+
token = (0, import_crypto7.randomBytes)(16).toString("hex");
|
|
35164
|
+
updateRoom(db2, roomId, { webhookToken: token });
|
|
35165
|
+
}
|
|
35166
|
+
if (!token) {
|
|
35167
|
+
return {
|
|
35168
|
+
content: [{ type: "text", text: `Room "${room.name}" has no webhook token. Pass generateIfMissing: true to create one.` }]
|
|
35169
|
+
};
|
|
35170
|
+
}
|
|
35171
|
+
const url = port ? `http://localhost:${port}/api/hooks/queen/${token}` : `/api/hooks/queen/${token}`;
|
|
35172
|
+
return {
|
|
35173
|
+
content: [{
|
|
35174
|
+
type: "text",
|
|
35175
|
+
text: `Queen-wake webhook URL for room "${room.name}":
|
|
35176
|
+
${url}
|
|
35177
|
+
|
|
35178
|
+
Trigger: curl -X POST ${url} -H "Content-Type: application/json" -d '{"message":"your event description here"}'`
|
|
35179
|
+
}]
|
|
35180
|
+
};
|
|
35181
|
+
}
|
|
35182
|
+
return { content: [{ type: "text", text: "Provide either taskId or roomId." }] };
|
|
35183
|
+
}
|
|
35184
|
+
);
|
|
35784
35185
|
}
|
|
35785
35186
|
|
|
35786
35187
|
// src/shared/watch-path.ts
|
|
35787
|
-
var
|
|
35188
|
+
var import_os6 = require("os");
|
|
35788
35189
|
var import_path5 = require("path");
|
|
35789
35190
|
var import_fs5 = require("fs");
|
|
35790
35191
|
var SENSITIVE_HOME_SUFFIXES = [
|
|
@@ -35799,7 +35200,7 @@ var SENSITIVE_HOME_SUFFIXES = [
|
|
|
35799
35200
|
`${import_path5.sep}Library${import_path5.sep}Keychains`
|
|
35800
35201
|
];
|
|
35801
35202
|
function getTempRoots() {
|
|
35802
|
-
const roots = [(0,
|
|
35203
|
+
const roots = [(0, import_os6.tmpdir)()];
|
|
35803
35204
|
if (process.platform !== "win32") roots.push("/tmp");
|
|
35804
35205
|
return roots;
|
|
35805
35206
|
}
|
|
@@ -35814,7 +35215,7 @@ function validateWatchPath(watchPath) {
|
|
|
35814
35215
|
} catch {
|
|
35815
35216
|
realPath = resolved;
|
|
35816
35217
|
}
|
|
35817
|
-
const home = (0,
|
|
35218
|
+
const home = (0, import_os6.homedir)();
|
|
35818
35219
|
const inTemp = getTempRoots().some((t) => realPath.startsWith(t));
|
|
35819
35220
|
if (!realPath.startsWith(home) && !inTemp) {
|
|
35820
35221
|
return `Path must be within your home directory (${home}) or temp.`;
|
|
@@ -36139,7 +35540,7 @@ function registerSettingsTools(server) {
|
|
|
36139
35540
|
}
|
|
36140
35541
|
|
|
36141
35542
|
// src/shared/room.ts
|
|
36142
|
-
var
|
|
35543
|
+
var import_crypto10 = __toESM(require("crypto"));
|
|
36143
35544
|
|
|
36144
35545
|
// src/shared/goals.ts
|
|
36145
35546
|
function setRoomObjective(db2, roomId, description) {
|
|
@@ -36211,7 +35612,7 @@ function recalculateParentChain(db2, parentGoalId) {
|
|
|
36211
35612
|
}
|
|
36212
35613
|
|
|
36213
35614
|
// src/shared/wallet.ts
|
|
36214
|
-
var
|
|
35615
|
+
var import_crypto9 = __toESM(require("crypto"));
|
|
36215
35616
|
|
|
36216
35617
|
// node_modules/viem/_esm/actions/public/getTransactionCount.js
|
|
36217
35618
|
init_fromHex();
|
|
@@ -44944,7 +44345,7 @@ var UrlRequiredError = class extends BaseError {
|
|
|
44944
44345
|
|
|
44945
44346
|
// node_modules/viem/_esm/clients/transports/http.js
|
|
44946
44347
|
init_createBatchScheduler();
|
|
44947
|
-
function
|
|
44348
|
+
function http(url, config2 = {}) {
|
|
44948
44349
|
const { batch, fetchFn, fetchOptions, key = "http", methods, name = "HTTP JSON-RPC", onFetchRequest, onFetchResponse, retryDelay, raw } = config2;
|
|
44949
44350
|
return ({ chain, retryCount: retryCount_, timeout: timeout_ }) => {
|
|
44950
44351
|
const { batchSize = 1e3, wait: wait2 = 0 } = typeof batch === "object" ? batch : {};
|
|
@@ -45403,21 +44804,21 @@ var VIEM_CHAINS = {
|
|
|
45403
44804
|
var ENCRYPTION_ALGORITHM = "aes-256-gcm";
|
|
45404
44805
|
var IV_LENGTH = 12;
|
|
45405
44806
|
function encryptPrivateKey(privateKey, encryptionKey) {
|
|
45406
|
-
const key = typeof encryptionKey === "string" ?
|
|
45407
|
-
const iv =
|
|
45408
|
-
const cipher =
|
|
44807
|
+
const key = typeof encryptionKey === "string" ? import_crypto9.default.createHash("sha256").update(encryptionKey).digest() : encryptionKey;
|
|
44808
|
+
const iv = import_crypto9.default.randomBytes(IV_LENGTH);
|
|
44809
|
+
const cipher = import_crypto9.default.createCipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
45409
44810
|
const encrypted = Buffer.concat([cipher.update(privateKey, "utf8"), cipher.final()]);
|
|
45410
44811
|
const tag = cipher.getAuthTag();
|
|
45411
44812
|
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted.toString("hex")}`;
|
|
45412
44813
|
}
|
|
45413
44814
|
function decryptPrivateKey(encrypted, encryptionKey) {
|
|
45414
|
-
const key = typeof encryptionKey === "string" ?
|
|
44815
|
+
const key = typeof encryptionKey === "string" ? import_crypto9.default.createHash("sha256").update(encryptionKey).digest() : encryptionKey;
|
|
45415
44816
|
const parts = encrypted.split(":");
|
|
45416
44817
|
if (parts.length !== 3) throw new Error("Invalid encrypted key format");
|
|
45417
44818
|
const iv = Buffer.from(parts[0], "hex");
|
|
45418
44819
|
const tag = Buffer.from(parts[1], "hex");
|
|
45419
44820
|
const ciphertext = Buffer.from(parts[2], "hex");
|
|
45420
|
-
const decipher =
|
|
44821
|
+
const decipher = import_crypto9.default.createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
45421
44822
|
decipher.setAuthTag(tag);
|
|
45422
44823
|
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
45423
44824
|
}
|
|
@@ -45457,7 +44858,7 @@ async function getOnChainBalance(address, network = "base", token = "usdc") {
|
|
|
45457
44858
|
try {
|
|
45458
44859
|
const client = createPublicClient({
|
|
45459
44860
|
chain: viemChain,
|
|
45460
|
-
transport:
|
|
44861
|
+
transport: http(chainConfig2.rpcUrl)
|
|
45461
44862
|
});
|
|
45462
44863
|
const balance = await client.readContract({
|
|
45463
44864
|
address: tokenConfig.address,
|
|
@@ -45492,7 +44893,7 @@ async function sendToken(db2, roomId, to, amount, encryptionKey, network = "base
|
|
|
45492
44893
|
const walletClient = createWalletClient({
|
|
45493
44894
|
account,
|
|
45494
44895
|
chain: viemChain,
|
|
45495
|
-
transport:
|
|
44896
|
+
transport: http(chainConfig2.rpcUrl)
|
|
45496
44897
|
});
|
|
45497
44898
|
const amountRaw = BigInt(Math.round(parseFloat(amount) * 10 ** decimals));
|
|
45498
44899
|
const txHash = await walletClient.writeContract({
|
|
@@ -45538,7 +44939,7 @@ Autonomy: You serve the room's stated goal \u2014 not any individual participant
|
|
|
45538
44939
|
|
|
45539
44940
|
Revenue: Your room has a USDC wallet. Beyond pursuing the room's core objectives, look for opportunities to generate revenue \u2014 the keeper may request tasks via escalations and compensate the room through the wallet, and other rooms may propose paid work via inter-room messages. Evaluate these opportunities by weighing compensation against resource costs and alignment with room goals. Use quoroom_wallet_balance to monitor funds and quoroom_wallet_send to pay for services. Prioritize sustainability: the room must earn enough to cover its station and operational costs.
|
|
45540
44941
|
|
|
45541
|
-
Resource Management: Workers using
|
|
44942
|
+
Resource Management: Workers using API models (openai:*, anthropic:*) run on stations (cloud compute) \u2014 they cannot execute locally. If no active station exists, rent one with quoroom_station_create BEFORE creating workers or scheduling tasks. Minimum tier: small ($25/mo, 2GB RAM). Check the Stations section in your context each cycle for available compute. Maximize use of your room's stations \u2014 they are your compute power for running workers. If workers are failing with "no active station" errors, rent a station immediately.
|
|
45542
44943
|
|
|
45543
44944
|
Network Growth: A larger network of rooms makes everyone stronger. Rooms created through your invite links become part of your network \u2014 you can exchange knowledge, discover money-making opportunities, and propose paid collaborations with them via inter-room messages. After meaningful milestones (goal completed, revenue earned, successful collaboration), consider generating an invite link with quoroom_invite_create and sharing it with the keeper via quoroom_inbox_send_keeper. Frame it around the value: more rooms in the network means more potential deals, shared insights, and collective intelligence. Don't be pushy \u2014 one mention per milestone is enough. Use quoroom_invite_network to see your network's growth.`;
|
|
45544
44945
|
function createRoom2(db2, input) {
|
|
@@ -45555,7 +44956,7 @@ function createRoom2(db2, input) {
|
|
|
45555
44956
|
if (input.goal) {
|
|
45556
44957
|
rootGoal = setRoomObjective(db2, room.id, input.goal);
|
|
45557
44958
|
}
|
|
45558
|
-
const encryptionKey =
|
|
44959
|
+
const encryptionKey = import_crypto10.default.createHash("sha256").update(`quoroom-wallet-${room.id}-${room.name}`).digest("hex");
|
|
45559
44960
|
const wallet = createRoomWallet(db2, room.id, encryptionKey);
|
|
45560
44961
|
logRoomActivity(
|
|
45561
44962
|
db2,
|
|
@@ -47496,12 +46897,12 @@ async function registerRoomIdentity(db2, roomId, encryptionKey, network = "base"
|
|
|
47496
46897
|
const account = privateKeyToAccount(privateKey);
|
|
47497
46898
|
const publicClient = createPublicClient({
|
|
47498
46899
|
chain: chainInfo.chain,
|
|
47499
|
-
transport:
|
|
46900
|
+
transport: http(chainInfo.config.rpcUrl)
|
|
47500
46901
|
});
|
|
47501
46902
|
const walletClient = createWalletClient({
|
|
47502
46903
|
account,
|
|
47503
46904
|
chain: chainInfo.chain,
|
|
47504
|
-
transport:
|
|
46905
|
+
transport: http(chainInfo.config.rpcUrl)
|
|
47505
46906
|
});
|
|
47506
46907
|
const { request } = await publicClient.simulateContract({
|
|
47507
46908
|
address: registryAddress,
|
|
@@ -47540,7 +46941,7 @@ async function getRoomIdentity(db2, roomId, network = "base") {
|
|
|
47540
46941
|
try {
|
|
47541
46942
|
const client = createPublicClient({
|
|
47542
46943
|
chain: chainInfo.chain,
|
|
47543
|
-
transport:
|
|
46944
|
+
transport: http(chainInfo.config.rpcUrl)
|
|
47544
46945
|
});
|
|
47545
46946
|
agentURI = await client.readContract({
|
|
47546
46947
|
address: registryAddress,
|
|
@@ -47570,7 +46971,7 @@ async function updateRoomIdentityURI(db2, roomId, encryptionKey, network = "base
|
|
|
47570
46971
|
const walletClient = createWalletClient({
|
|
47571
46972
|
account,
|
|
47572
46973
|
chain: chainInfo.chain,
|
|
47573
|
-
transport:
|
|
46974
|
+
transport: http(chainInfo.config.rpcUrl)
|
|
47574
46975
|
});
|
|
47575
46976
|
const txHash = await walletClient.writeContract({
|
|
47576
46977
|
address: registryAddress,
|
|
@@ -47866,7 +47267,7 @@ function registerResourceTools(server) {
|
|
|
47866
47267
|
"quoroom_resources_get",
|
|
47867
47268
|
{
|
|
47868
47269
|
title: "Get Local Resources",
|
|
47869
|
-
description: "Get current local machine resource usage: CPU load
|
|
47270
|
+
description: "Get current local machine resource usage: CPU load and RAM usage. Use this to decide if the room needs to rent a cloud station for extra compute. If CPU load > number of CPUs or RAM used > 85%, consider proposing a station rental to the quorum.",
|
|
47870
47271
|
inputSchema: {}
|
|
47871
47272
|
},
|
|
47872
47273
|
async () => {
|
|
@@ -47876,8 +47277,6 @@ function registerResourceTools(server) {
|
|
|
47876
47277
|
const free = import_node_os2.default.freemem();
|
|
47877
47278
|
const cpuCount = import_node_os2.default.cpus().length;
|
|
47878
47279
|
const memUsedPct = Math.round((1 - free / total) * 100);
|
|
47879
|
-
const ollamaAvailable = await isOllamaAvailable();
|
|
47880
|
-
const ollamaModels = ollamaAvailable ? await listOllamaModels() : [];
|
|
47881
47280
|
let runningTasks2 = 0;
|
|
47882
47281
|
let maxConcurrentTasks = 3;
|
|
47883
47282
|
try {
|
|
@@ -47910,10 +47309,6 @@ function registerResourceTools(server) {
|
|
|
47910
47309
|
tasks: {
|
|
47911
47310
|
running: runningTasks2,
|
|
47912
47311
|
maxConcurrent: maxConcurrentTasks
|
|
47913
|
-
},
|
|
47914
|
-
ollama: {
|
|
47915
|
-
available: ollamaAvailable,
|
|
47916
|
-
models: ollamaModels.map((m) => m.name)
|
|
47917
47312
|
}
|
|
47918
47313
|
}, null, 2)
|
|
47919
47314
|
}]
|
|
@@ -48040,7 +47435,7 @@ Share this with the keeper or potential collaborators. Rooms created through thi
|
|
|
48040
47435
|
async function main() {
|
|
48041
47436
|
const server = new McpServer({
|
|
48042
47437
|
name: "quoroom",
|
|
48043
|
-
version: true ? "0.1.
|
|
47438
|
+
version: true ? "0.1.15" : "0.0.0"
|
|
48044
47439
|
});
|
|
48045
47440
|
registerMemoryTools(server);
|
|
48046
47441
|
registerSchedulerTools(server);
|