quoroom 0.1.39 → 0.1.41
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 +16 -1
- package/out/mcp/api-server.js +334 -767
- package/out/mcp/cli.js +4244 -5098
- package/out/mcp/server.js +234 -1265
- package/package.json +1 -1
package/out/mcp/server.js
CHANGED
|
@@ -6852,7 +6852,7 @@ CREATE TABLE IF NOT EXISTS rooms (
|
|
|
6852
6852
|
max_concurrent_tasks INTEGER NOT NULL DEFAULT 3,
|
|
6853
6853
|
worker_model TEXT NOT NULL DEFAULT 'claude',
|
|
6854
6854
|
queen_cycle_gap_ms INTEGER NOT NULL DEFAULT 1800000,
|
|
6855
|
-
queen_max_turns INTEGER NOT NULL DEFAULT
|
|
6855
|
+
queen_max_turns INTEGER NOT NULL DEFAULT 50,
|
|
6856
6856
|
queen_quiet_from TEXT,
|
|
6857
6857
|
queen_quiet_until TEXT,
|
|
6858
6858
|
config TEXT,
|
|
@@ -7206,23 +7206,6 @@ CREATE TABLE IF NOT EXISTS room_messages (
|
|
|
7206
7206
|
CREATE INDEX IF NOT EXISTS idx_room_messages_room ON room_messages(room_id);
|
|
7207
7207
|
CREATE INDEX IF NOT EXISTS idx_room_messages_status ON room_messages(status);
|
|
7208
7208
|
|
|
7209
|
-
-- Stations
|
|
7210
|
-
CREATE TABLE IF NOT EXISTS stations (
|
|
7211
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7212
|
-
room_id INTEGER NOT NULL REFERENCES rooms(id) ON DELETE CASCADE,
|
|
7213
|
-
name TEXT NOT NULL,
|
|
7214
|
-
provider TEXT NOT NULL,
|
|
7215
|
-
external_id TEXT,
|
|
7216
|
-
tier TEXT NOT NULL,
|
|
7217
|
-
region TEXT,
|
|
7218
|
-
status TEXT NOT NULL DEFAULT 'provisioning',
|
|
7219
|
-
monthly_cost REAL NOT NULL DEFAULT 0,
|
|
7220
|
-
config TEXT,
|
|
7221
|
-
created_at DATETIME DEFAULT (datetime('now','localtime')),
|
|
7222
|
-
updated_at DATETIME DEFAULT (datetime('now','localtime'))
|
|
7223
|
-
);
|
|
7224
|
-
CREATE INDEX IF NOT EXISTS idx_stations_room ON stations(room_id);
|
|
7225
|
-
|
|
7226
7209
|
-- Worker cycles (agent loop execution tracking)
|
|
7227
7210
|
CREATE TABLE IF NOT EXISTS worker_cycles (
|
|
7228
7211
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -7401,24 +7384,24 @@ var init_constants = __esm({
|
|
|
7401
7384
|
"base-sepolia": "0x8004A818BFB912233c491871b3d84c89A494BD9e"
|
|
7402
7385
|
};
|
|
7403
7386
|
QUEEN_DEFAULTS_BY_PLAN = {
|
|
7404
|
-
none: { queenCycleGapMs: 10 * 60 * 1e3, queenMaxTurns:
|
|
7405
|
-
// 10 min gap,
|
|
7406
|
-
pro: { queenCycleGapMs: 5 * 60 * 1e3, queenMaxTurns:
|
|
7407
|
-
// 5 min gap,
|
|
7408
|
-
max: { queenCycleGapMs: 30 * 1e3, queenMaxTurns:
|
|
7409
|
-
// 30s gap,
|
|
7410
|
-
api: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns:
|
|
7411
|
-
// 2 min gap,
|
|
7387
|
+
none: { queenCycleGapMs: 10 * 60 * 1e3, queenMaxTurns: 50 },
|
|
7388
|
+
// 10 min gap, 50 turns
|
|
7389
|
+
pro: { queenCycleGapMs: 5 * 60 * 1e3, queenMaxTurns: 50 },
|
|
7390
|
+
// 5 min gap, 50 turns
|
|
7391
|
+
max: { queenCycleGapMs: 30 * 1e3, queenMaxTurns: 50 },
|
|
7392
|
+
// 30s gap, 50 turns
|
|
7393
|
+
api: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns: 50 }
|
|
7394
|
+
// 2 min gap, 50 turns
|
|
7412
7395
|
};
|
|
7413
7396
|
CHATGPT_DEFAULTS_BY_PLAN = {
|
|
7414
|
-
none: { queenCycleGapMs: 10 * 60 * 1e3, queenMaxTurns:
|
|
7415
|
-
// 10 min gap,
|
|
7416
|
-
plus: { queenCycleGapMs: 5 * 60 * 1e3, queenMaxTurns:
|
|
7417
|
-
// 5 min gap,
|
|
7418
|
-
pro: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns:
|
|
7419
|
-
// 2 min gap,
|
|
7420
|
-
api: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns:
|
|
7421
|
-
// 2 min gap,
|
|
7397
|
+
none: { queenCycleGapMs: 10 * 60 * 1e3, queenMaxTurns: 50 },
|
|
7398
|
+
// 10 min gap, 50 turns
|
|
7399
|
+
plus: { queenCycleGapMs: 5 * 60 * 1e3, queenMaxTurns: 50 },
|
|
7400
|
+
// 5 min gap, 50 turns
|
|
7401
|
+
pro: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns: 50 },
|
|
7402
|
+
// 2 min gap, 50 turns
|
|
7403
|
+
api: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns: 50 }
|
|
7404
|
+
// 2 min gap, 50 turns
|
|
7422
7405
|
};
|
|
7423
7406
|
WORKER_ROLE_PRESETS = {
|
|
7424
7407
|
guardian: {
|
|
@@ -7579,7 +7562,6 @@ __export(db_queries_exports, {
|
|
|
7579
7562
|
createRoom: () => createRoom,
|
|
7580
7563
|
createRoomMessage: () => createRoomMessage,
|
|
7581
7564
|
createSkill: () => createSkill,
|
|
7582
|
-
createStation: () => createStation,
|
|
7583
7565
|
createTask: () => createTask,
|
|
7584
7566
|
createTaskRun: () => createTaskRun,
|
|
7585
7567
|
createWallet: () => createWallet,
|
|
@@ -7596,7 +7578,6 @@ __export(db_queries_exports, {
|
|
|
7596
7578
|
deleteRoom: () => deleteRoom,
|
|
7597
7579
|
deleteRoomMessage: () => deleteRoomMessage,
|
|
7598
7580
|
deleteSkill: () => deleteSkill,
|
|
7599
|
-
deleteStation: () => deleteStation,
|
|
7600
7581
|
deleteTask: () => deleteTask,
|
|
7601
7582
|
deleteWallet: () => deleteWallet,
|
|
7602
7583
|
deleteWatch: () => deleteWatch,
|
|
@@ -7650,7 +7631,6 @@ __export(db_queries_exports, {
|
|
|
7650
7631
|
getSessionRunCount: () => getSessionRunCount,
|
|
7651
7632
|
getSetting: () => getSetting,
|
|
7652
7633
|
getSkill: () => getSkill,
|
|
7653
|
-
getStation: () => getStation,
|
|
7654
7634
|
getSubGoals: () => getSubGoals,
|
|
7655
7635
|
getTask: () => getTask,
|
|
7656
7636
|
getTaskByWebhookToken: () => getTaskByWebhookToken,
|
|
@@ -7694,7 +7674,6 @@ __export(db_queries_exports, {
|
|
|
7694
7674
|
listRooms: () => listRooms,
|
|
7695
7675
|
listRunsByRoom: () => listRunsByRoom,
|
|
7696
7676
|
listSkills: () => listSkills,
|
|
7697
|
-
listStations: () => listStations,
|
|
7698
7677
|
listTasks: () => listTasks,
|
|
7699
7678
|
listWalletTransactions: () => listWalletTransactions,
|
|
7700
7679
|
listWallets: () => listWallets,
|
|
@@ -7735,7 +7714,6 @@ __export(db_queries_exports, {
|
|
|
7735
7714
|
updateRoom: () => updateRoom,
|
|
7736
7715
|
updateRoomMessageStatus: () => updateRoomMessageStatus,
|
|
7737
7716
|
updateSkill: () => updateSkill,
|
|
7738
|
-
updateStation: () => updateStation,
|
|
7739
7717
|
updateTask: () => updateTask,
|
|
7740
7718
|
updateTaskRunProgress: () => updateTaskRunProgress,
|
|
7741
7719
|
updateTaskRunSessionId: () => updateTaskRunSessionId,
|
|
@@ -8528,7 +8506,7 @@ function mapRoomRow(row) {
|
|
|
8528
8506
|
maxConcurrentTasks: row.max_concurrent_tasks ?? 3,
|
|
8529
8507
|
workerModel: row.worker_model ?? "claude",
|
|
8530
8508
|
queenCycleGapMs: row.queen_cycle_gap_ms ?? 18e5,
|
|
8531
|
-
queenMaxTurns: row.queen_max_turns ??
|
|
8509
|
+
queenMaxTurns: row.queen_max_turns ?? 50,
|
|
8532
8510
|
queenQuietFrom: row.queen_quiet_from ?? null,
|
|
8533
8511
|
queenQuietUntil: row.queen_quiet_until ?? null,
|
|
8534
8512
|
config: config2,
|
|
@@ -9170,91 +9148,6 @@ function getWalletTransactionSummary(db2, walletId) {
|
|
|
9170
9148
|
).get(walletId);
|
|
9171
9149
|
return { received: received.total.toString(), sent: sent.total.toString() };
|
|
9172
9150
|
}
|
|
9173
|
-
function mapStationRow(row) {
|
|
9174
|
-
let config2 = null;
|
|
9175
|
-
if (row.config && typeof row.config === "string") {
|
|
9176
|
-
try {
|
|
9177
|
-
config2 = JSON.parse(row.config);
|
|
9178
|
-
} catch {
|
|
9179
|
-
}
|
|
9180
|
-
}
|
|
9181
|
-
return {
|
|
9182
|
-
id: row.id,
|
|
9183
|
-
roomId: row.room_id,
|
|
9184
|
-
name: row.name,
|
|
9185
|
-
provider: row.provider,
|
|
9186
|
-
externalId: row.external_id,
|
|
9187
|
-
tier: row.tier,
|
|
9188
|
-
region: row.region,
|
|
9189
|
-
status: row.status,
|
|
9190
|
-
monthlyCost: row.monthly_cost,
|
|
9191
|
-
config: config2,
|
|
9192
|
-
createdAt: row.created_at,
|
|
9193
|
-
updatedAt: row.updated_at
|
|
9194
|
-
};
|
|
9195
|
-
}
|
|
9196
|
-
function createStation(db2, roomId, name, provider, tier, opts) {
|
|
9197
|
-
const result = db2.prepare("INSERT INTO stations (room_id, name, provider, tier, external_id, region, monthly_cost, config, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)").run(
|
|
9198
|
-
roomId,
|
|
9199
|
-
name,
|
|
9200
|
-
provider,
|
|
9201
|
-
tier,
|
|
9202
|
-
opts?.externalId ?? null,
|
|
9203
|
-
opts?.region ?? null,
|
|
9204
|
-
opts?.monthlyCost ?? 0,
|
|
9205
|
-
opts?.config ? JSON.stringify(opts.config) : null,
|
|
9206
|
-
opts?.status ?? "provisioning"
|
|
9207
|
-
);
|
|
9208
|
-
return getStation(db2, result.lastInsertRowid);
|
|
9209
|
-
}
|
|
9210
|
-
function getStation(db2, id) {
|
|
9211
|
-
const row = db2.prepare("SELECT * FROM stations WHERE id = ?").get(id);
|
|
9212
|
-
return row ? mapStationRow(row) : null;
|
|
9213
|
-
}
|
|
9214
|
-
function listStations(db2, roomId, status) {
|
|
9215
|
-
if (roomId && status) {
|
|
9216
|
-
const rows2 = db2.prepare("SELECT * FROM stations WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId, status);
|
|
9217
|
-
return rows2.map(mapStationRow);
|
|
9218
|
-
}
|
|
9219
|
-
if (roomId) {
|
|
9220
|
-
const rows2 = db2.prepare("SELECT * FROM stations WHERE room_id = ? ORDER BY created_at DESC").all(roomId);
|
|
9221
|
-
return rows2.map(mapStationRow);
|
|
9222
|
-
}
|
|
9223
|
-
if (status) {
|
|
9224
|
-
const rows2 = db2.prepare("SELECT * FROM stations WHERE status = ? ORDER BY created_at DESC").all(status);
|
|
9225
|
-
return rows2.map(mapStationRow);
|
|
9226
|
-
}
|
|
9227
|
-
const rows = db2.prepare("SELECT * FROM stations ORDER BY created_at DESC").all();
|
|
9228
|
-
return rows.map(mapStationRow);
|
|
9229
|
-
}
|
|
9230
|
-
function updateStation(db2, id, updates) {
|
|
9231
|
-
const parts = [];
|
|
9232
|
-
const values = [];
|
|
9233
|
-
if (updates.externalId !== void 0) {
|
|
9234
|
-
parts.push("external_id = ?");
|
|
9235
|
-
values.push(updates.externalId);
|
|
9236
|
-
}
|
|
9237
|
-
if (updates.status !== void 0) {
|
|
9238
|
-
parts.push("status = ?");
|
|
9239
|
-
values.push(updates.status);
|
|
9240
|
-
}
|
|
9241
|
-
if (updates.monthlyCost !== void 0) {
|
|
9242
|
-
parts.push("monthly_cost = ?");
|
|
9243
|
-
values.push(updates.monthlyCost);
|
|
9244
|
-
}
|
|
9245
|
-
if (updates.config !== void 0) {
|
|
9246
|
-
parts.push("config = ?");
|
|
9247
|
-
values.push(JSON.stringify(updates.config));
|
|
9248
|
-
}
|
|
9249
|
-
if (parts.length === 0) return getStation(db2, id);
|
|
9250
|
-
parts.push("updated_at = datetime('now','localtime')");
|
|
9251
|
-
values.push(id);
|
|
9252
|
-
db2.prepare(`UPDATE stations SET ${parts.join(", ")} WHERE id = ?`).run(...values);
|
|
9253
|
-
return getStation(db2, id);
|
|
9254
|
-
}
|
|
9255
|
-
function deleteStation(db2, id) {
|
|
9256
|
-
db2.prepare("DELETE FROM stations WHERE id = ?").run(id);
|
|
9257
|
-
}
|
|
9258
9151
|
function mapChatMessageRow(row) {
|
|
9259
9152
|
return {
|
|
9260
9153
|
id: row.id,
|
|
@@ -9461,16 +9354,13 @@ function ensureClerkWorker(db2) {
|
|
|
9461
9354
|
}
|
|
9462
9355
|
function getRevenueSummary(db2, roomId) {
|
|
9463
9356
|
const wallet = getWalletByRoom(db2, roomId);
|
|
9464
|
-
if (!wallet) return { totalIncome: 0, totalExpenses: 0, netProfit: 0,
|
|
9357
|
+
if (!wallet) return { totalIncome: 0, totalExpenses: 0, netProfit: 0, transactionCount: 0 };
|
|
9465
9358
|
const income = db2.prepare(
|
|
9466
9359
|
"SELECT COALESCE(SUM(CAST(amount AS REAL)), 0) as total FROM wallet_transactions WHERE wallet_id = ? AND type IN ('receive', 'fund')"
|
|
9467
9360
|
).get(wallet.id);
|
|
9468
9361
|
const expenses = db2.prepare(
|
|
9469
9362
|
"SELECT COALESCE(SUM(CAST(amount AS REAL)), 0) as total FROM wallet_transactions WHERE wallet_id = ? AND type IN ('send', 'purchase')"
|
|
9470
9363
|
).get(wallet.id);
|
|
9471
|
-
const stationCosts = db2.prepare(
|
|
9472
|
-
"SELECT COALESCE(SUM(CAST(amount AS REAL)), 0) as total FROM wallet_transactions WHERE wallet_id = ? AND category = 'station_cost'"
|
|
9473
|
-
).get(wallet.id);
|
|
9474
9364
|
const count = db2.prepare(
|
|
9475
9365
|
"SELECT COUNT(*) as cnt FROM wallet_transactions WHERE wallet_id = ?"
|
|
9476
9366
|
).get(wallet.id);
|
|
@@ -9478,7 +9368,6 @@ function getRevenueSummary(db2, roomId) {
|
|
|
9478
9368
|
totalIncome: income.total,
|
|
9479
9369
|
totalExpenses: expenses.total,
|
|
9480
9370
|
netProfit: income.total - expenses.total,
|
|
9481
|
-
stationCosts: stationCosts.total,
|
|
9482
9371
|
transactionCount: count.cnt
|
|
9483
9372
|
};
|
|
9484
9373
|
}
|
|
@@ -9781,6 +9670,10 @@ function upsertSetting(database, key, value) {
|
|
|
9781
9670
|
}
|
|
9782
9671
|
function runMigrations(database, log = console.log) {
|
|
9783
9672
|
database.exec(SCHEMA);
|
|
9673
|
+
const legacyQueenTurnsUpdated = database.prepare(`UPDATE rooms SET queen_max_turns = 50 WHERE queen_max_turns = 3`).run().changes;
|
|
9674
|
+
if (legacyQueenTurnsUpdated > 0) {
|
|
9675
|
+
log(`Migrated: updated ${legacyQueenTurnsUpdated} room(s) queen_max_turns from 3 to 50`);
|
|
9676
|
+
}
|
|
9784
9677
|
if (!database.prepare("SELECT value FROM settings WHERE key = ?").get("keeper_referral_code")) {
|
|
9785
9678
|
const code = (0, import_crypto.randomBytes)(6).toString("base64url").slice(0, 10);
|
|
9786
9679
|
upsertSetting(database, "keeper_referral_code", code);
|
|
@@ -9879,6 +9772,7 @@ function runMigrations(database, log = console.log) {
|
|
|
9879
9772
|
log(`Migrated: reset ${ollamaRooms.length} room worker_model(s) to 'claude'`);
|
|
9880
9773
|
}
|
|
9881
9774
|
database.prepare(`UPDATE rooms SET autonomy_mode = 'semi' WHERE autonomy_mode IS NULL OR autonomy_mode != 'semi'`).run();
|
|
9775
|
+
database.exec(`DROP TABLE IF EXISTS stations`);
|
|
9882
9776
|
log("Database schema initialized");
|
|
9883
9777
|
}
|
|
9884
9778
|
var import_crypto;
|
|
@@ -9913,7 +9807,8 @@ async function initEngine() {
|
|
|
9913
9807
|
if (pipeline || pipelineLoading) return;
|
|
9914
9808
|
pipelineLoading = true;
|
|
9915
9809
|
try {
|
|
9916
|
-
const { pipeline: createPipeline } = await import("@huggingface/transformers");
|
|
9810
|
+
const { pipeline: createPipeline, env } = await import("@huggingface/transformers");
|
|
9811
|
+
env.cacheDir = (0, import_path.join)((0, import_os.homedir)(), ".quoroom", "cache");
|
|
9917
9812
|
const pipe2 = await createPipeline("feature-extraction", MODEL_NAME, {
|
|
9918
9813
|
dtype: "fp32",
|
|
9919
9814
|
revision: "main"
|
|
@@ -9944,10 +9839,12 @@ async function embed(text) {
|
|
|
9944
9839
|
function vectorToBlob(vec) {
|
|
9945
9840
|
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
9946
9841
|
}
|
|
9947
|
-
var pipeline, pipelineLoading, sqliteVecLoaded, MODEL_NAME;
|
|
9842
|
+
var import_os, import_path, pipeline, pipelineLoading, sqliteVecLoaded, MODEL_NAME;
|
|
9948
9843
|
var init_embeddings = __esm({
|
|
9949
9844
|
"src/shared/embeddings.ts"() {
|
|
9950
9845
|
"use strict";
|
|
9846
|
+
import_os = require("os");
|
|
9847
|
+
import_path = require("path");
|
|
9951
9848
|
pipeline = null;
|
|
9952
9849
|
pipelineLoading = false;
|
|
9953
9850
|
sqliteVecLoaded = false;
|
|
@@ -9963,7 +9860,7 @@ __export(db_exports, {
|
|
|
9963
9860
|
});
|
|
9964
9861
|
function expandTilde(p) {
|
|
9965
9862
|
if (p.startsWith("~/") || p === "~") {
|
|
9966
|
-
return p.replace("~", (0,
|
|
9863
|
+
return p.replace("~", (0, import_os2.homedir)());
|
|
9967
9864
|
}
|
|
9968
9865
|
return p;
|
|
9969
9866
|
}
|
|
@@ -9992,12 +9889,12 @@ function closeMcpDatabase() {
|
|
|
9992
9889
|
db = null;
|
|
9993
9890
|
}
|
|
9994
9891
|
}
|
|
9995
|
-
var import_better_sqlite3,
|
|
9892
|
+
var import_better_sqlite3, import_os2, db;
|
|
9996
9893
|
var init_db = __esm({
|
|
9997
9894
|
"src/mcp/db.ts"() {
|
|
9998
9895
|
"use strict";
|
|
9999
9896
|
import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
10000
|
-
|
|
9897
|
+
import_os2 = require("os");
|
|
10001
9898
|
init_db_migrations();
|
|
10002
9899
|
init_db_queries();
|
|
10003
9900
|
init_embeddings();
|
|
@@ -34479,9 +34376,9 @@ function registerMemoryTools(server) {
|
|
|
34479
34376
|
|
|
34480
34377
|
// src/mcp/tools/scheduler.ts
|
|
34481
34378
|
var import_path4 = require("path");
|
|
34482
|
-
var
|
|
34483
|
-
var
|
|
34484
|
-
var
|
|
34379
|
+
var import_os4 = require("os");
|
|
34380
|
+
var import_fs3 = require("fs");
|
|
34381
|
+
var import_crypto5 = require("crypto");
|
|
34485
34382
|
var import_http = require("http");
|
|
34486
34383
|
var import_node_cron = __toESM(require_node_cron());
|
|
34487
34384
|
init_db();
|
|
@@ -34490,13 +34387,13 @@ init_constants();
|
|
|
34490
34387
|
|
|
34491
34388
|
// src/shared/task-runner.ts
|
|
34492
34389
|
var import_path3 = require("path");
|
|
34493
|
-
var
|
|
34390
|
+
var import_fs2 = require("fs");
|
|
34494
34391
|
|
|
34495
34392
|
// src/shared/claude-code.ts
|
|
34496
34393
|
var import_child_process = require("child_process");
|
|
34497
34394
|
var import_fs = require("fs");
|
|
34498
|
-
var
|
|
34499
|
-
var
|
|
34395
|
+
var import_path2 = require("path");
|
|
34396
|
+
var import_os3 = require("os");
|
|
34500
34397
|
|
|
34501
34398
|
// src/shared/process-supervisor.ts
|
|
34502
34399
|
var managedChildPids = /* @__PURE__ */ new Set();
|
|
@@ -34526,7 +34423,7 @@ function resolveNodeScript(cmdPath) {
|
|
|
34526
34423
|
const content = (0, import_fs.readFileSync)(cmdPath, "utf-8");
|
|
34527
34424
|
const match = content.match(/%dp0%\\(.+?\.js)/);
|
|
34528
34425
|
if (match) {
|
|
34529
|
-
const script = (0,
|
|
34426
|
+
const script = (0, import_path2.join)((0, import_path2.dirname)(cmdPath), match[1]);
|
|
34530
34427
|
if ((0, import_fs.existsSync)(script)) return script;
|
|
34531
34428
|
}
|
|
34532
34429
|
} catch {
|
|
@@ -34535,19 +34432,19 @@ function resolveNodeScript(cmdPath) {
|
|
|
34535
34432
|
}
|
|
34536
34433
|
function resolveClaudePath() {
|
|
34537
34434
|
if (cachedClaudePath) return cachedClaudePath;
|
|
34538
|
-
const home = (0,
|
|
34435
|
+
const home = (0, import_os3.homedir)();
|
|
34539
34436
|
const isWindows = process.platform === "win32";
|
|
34540
34437
|
const candidates = isWindows ? [
|
|
34541
|
-
(0,
|
|
34542
|
-
(0,
|
|
34543
|
-
(0,
|
|
34544
|
-
(0,
|
|
34438
|
+
(0, import_path2.join)(home, ".claude", "bin", "claude.exe"),
|
|
34439
|
+
(0, import_path2.join)(home, "AppData", "Local", "Programs", "claude-code", "claude.exe"),
|
|
34440
|
+
(0, import_path2.join)(home, "AppData", "Local", "Claude", "claude.exe"),
|
|
34441
|
+
(0, import_path2.join)(home, "AppData", "Local", "Microsoft", "WinGet", "Links", "claude.exe"),
|
|
34545
34442
|
"C:\\Program Files\\Claude\\claude.exe",
|
|
34546
34443
|
// npm global install creates .cmd wrappers, not .exe
|
|
34547
|
-
(0,
|
|
34444
|
+
(0, import_path2.join)(home, "AppData", "Roaming", "npm", "claude.cmd")
|
|
34548
34445
|
] : [
|
|
34549
|
-
(0,
|
|
34550
|
-
(0,
|
|
34446
|
+
(0, import_path2.join)(home, ".local", "bin", "claude"),
|
|
34447
|
+
(0, import_path2.join)(home, ".claude", "bin", "claude"),
|
|
34551
34448
|
"/usr/local/bin/claude",
|
|
34552
34449
|
"/usr/bin/claude",
|
|
34553
34450
|
"/snap/bin/claude",
|
|
@@ -34649,14 +34546,14 @@ function executeClaudeCode(prompt, options) {
|
|
|
34649
34546
|
const nodeScript = resolveNodeScript(claudePath);
|
|
34650
34547
|
if (nodeScript) {
|
|
34651
34548
|
proc = (0, import_child_process.spawn)(process.execPath, [nodeScript, ...args], {
|
|
34652
|
-
cwd: (0,
|
|
34549
|
+
cwd: (0, import_os3.homedir)(),
|
|
34653
34550
|
env,
|
|
34654
34551
|
stdio: ["ignore", "pipe", "pipe"],
|
|
34655
34552
|
windowsHide: true
|
|
34656
34553
|
});
|
|
34657
34554
|
} else {
|
|
34658
34555
|
proc = (0, import_child_process.spawn)(claudePath, args, {
|
|
34659
|
-
cwd: (0,
|
|
34556
|
+
cwd: (0, import_os3.homedir)(),
|
|
34660
34557
|
env,
|
|
34661
34558
|
stdio: ["ignore", "pipe", "pipe"],
|
|
34662
34559
|
windowsHide: true,
|
|
@@ -34798,581 +34695,6 @@ function executeClaudeCode(prompt, options) {
|
|
|
34798
34695
|
});
|
|
34799
34696
|
}
|
|
34800
34697
|
|
|
34801
|
-
// src/shared/cloud-sync.ts
|
|
34802
|
-
var import_crypto6 = require("crypto");
|
|
34803
|
-
var import_os4 = require("os");
|
|
34804
|
-
var import_path2 = require("path");
|
|
34805
|
-
var import_fs2 = require("fs");
|
|
34806
|
-
|
|
34807
|
-
// src/shared/telemetry.ts
|
|
34808
|
-
var import_crypto5 = require("crypto");
|
|
34809
|
-
var import_os3 = require("os");
|
|
34810
|
-
var TELEMETRY_TOKEN = process.env.QUOROOM_TELEMETRY_TOKEN ?? "";
|
|
34811
|
-
var cachedMachineId = null;
|
|
34812
|
-
function getMachineId() {
|
|
34813
|
-
if (cachedMachineId) return cachedMachineId;
|
|
34814
|
-
try {
|
|
34815
|
-
const raw = (0, import_os3.hostname)() + (0, import_os3.userInfo)().username;
|
|
34816
|
-
cachedMachineId = (0, import_crypto5.createHash)("sha256").update(raw).digest("hex").slice(0, 12);
|
|
34817
|
-
} catch {
|
|
34818
|
-
cachedMachineId = "unknown";
|
|
34819
|
-
}
|
|
34820
|
-
return cachedMachineId;
|
|
34821
|
-
}
|
|
34822
|
-
|
|
34823
|
-
// src/shared/cloud-sync.ts
|
|
34824
|
-
function getCloudApi() {
|
|
34825
|
-
return (process.env.QUOROOM_CLOUD_API ?? "https://quoroom.io/api").replace(/\/$/, "");
|
|
34826
|
-
}
|
|
34827
|
-
var TOKEN_FILE_NAME = "cloud-room-tokens.json";
|
|
34828
|
-
var cachedTokens = null;
|
|
34829
|
-
function getCloudTokenFilePath() {
|
|
34830
|
-
const explicitDataDir = process.env.QUOROOM_DATA_DIR?.trim();
|
|
34831
|
-
if (explicitDataDir) return (0, import_path2.join)(explicitDataDir, TOKEN_FILE_NAME);
|
|
34832
|
-
const dbPath = process.env.QUOROOM_DB_PATH?.trim();
|
|
34833
|
-
if (dbPath) return (0, import_path2.join)((0, import_path2.dirname)(dbPath), TOKEN_FILE_NAME);
|
|
34834
|
-
return (0, import_path2.join)((0, import_os4.homedir)(), ".quoroom", TOKEN_FILE_NAME);
|
|
34835
|
-
}
|
|
34836
|
-
function loadTokenStore() {
|
|
34837
|
-
if (cachedTokens) return cachedTokens;
|
|
34838
|
-
const filePath = getCloudTokenFilePath();
|
|
34839
|
-
try {
|
|
34840
|
-
const parsed = JSON.parse((0, import_fs2.readFileSync)(filePath, "utf-8"));
|
|
34841
|
-
cachedTokens = parsed.rooms ?? {};
|
|
34842
|
-
} catch {
|
|
34843
|
-
cachedTokens = {};
|
|
34844
|
-
}
|
|
34845
|
-
return cachedTokens;
|
|
34846
|
-
}
|
|
34847
|
-
function saveTokenStore() {
|
|
34848
|
-
const filePath = getCloudTokenFilePath();
|
|
34849
|
-
(0, import_fs2.mkdirSync)((0, import_path2.dirname)(filePath), { recursive: true });
|
|
34850
|
-
const payload = JSON.stringify({ rooms: loadTokenStore() }, null, 2) + "\n";
|
|
34851
|
-
(0, import_fs2.writeFileSync)(filePath, payload, { mode: 384 });
|
|
34852
|
-
}
|
|
34853
|
-
function getRoomToken(roomId) {
|
|
34854
|
-
return loadTokenStore()[roomId];
|
|
34855
|
-
}
|
|
34856
|
-
function setRoomToken(roomId, token) {
|
|
34857
|
-
loadTokenStore()[roomId] = token;
|
|
34858
|
-
saveTokenStore();
|
|
34859
|
-
}
|
|
34860
|
-
function cloudHeaders(roomId, extra = {}) {
|
|
34861
|
-
const roomToken = roomId ? getRoomToken(roomId) : void 0;
|
|
34862
|
-
if (!roomToken) return extra;
|
|
34863
|
-
return { ...extra, "X-Room-Token": roomToken };
|
|
34864
|
-
}
|
|
34865
|
-
async function ensureCloudRoomToken(data) {
|
|
34866
|
-
if (getRoomToken(data.roomId)) return true;
|
|
34867
|
-
await registerWithCloud(data);
|
|
34868
|
-
return Boolean(getRoomToken(data.roomId));
|
|
34869
|
-
}
|
|
34870
|
-
async function registerWithCloud(data) {
|
|
34871
|
-
try {
|
|
34872
|
-
const payload = {
|
|
34873
|
-
...data,
|
|
34874
|
-
inviteCode: data.referredByCode ?? null,
|
|
34875
|
-
keeperReferralCode: data.keeperReferralCode ?? null
|
|
34876
|
-
};
|
|
34877
|
-
const res = await fetch(`${getCloudApi()}/rooms/register`, {
|
|
34878
|
-
method: "POST",
|
|
34879
|
-
headers: cloudHeaders(data.roomId, { "Content-Type": "application/json" }),
|
|
34880
|
-
body: JSON.stringify(payload),
|
|
34881
|
-
signal: AbortSignal.timeout(1e4)
|
|
34882
|
-
});
|
|
34883
|
-
if (!res.ok) return;
|
|
34884
|
-
const result = await res.json().catch(() => ({}));
|
|
34885
|
-
if (typeof result.roomToken === "string" && result.roomToken.length > 0) {
|
|
34886
|
-
setRoomToken(data.roomId, result.roomToken);
|
|
34887
|
-
}
|
|
34888
|
-
} catch {
|
|
34889
|
-
}
|
|
34890
|
-
}
|
|
34891
|
-
function getRoomCloudId(dbRoomId) {
|
|
34892
|
-
const machineId = getMachineId();
|
|
34893
|
-
return (0, import_crypto6.createHash)("sha256").update(`${machineId}:${dbRoomId}`).digest("hex").slice(0, 32);
|
|
34894
|
-
}
|
|
34895
|
-
var CLOUD_STATIONS_CACHE_MS = 1e4;
|
|
34896
|
-
var cloudStationsCache = /* @__PURE__ */ new Map();
|
|
34897
|
-
function getCachedCloudStations(cloudRoomId) {
|
|
34898
|
-
const cached2 = cloudStationsCache.get(cloudRoomId);
|
|
34899
|
-
if (!cached2) return null;
|
|
34900
|
-
if (Date.now() >= cached2.expiresAt) {
|
|
34901
|
-
cloudStationsCache.delete(cloudRoomId);
|
|
34902
|
-
return null;
|
|
34903
|
-
}
|
|
34904
|
-
return cached2.value;
|
|
34905
|
-
}
|
|
34906
|
-
function setCachedCloudStations(cloudRoomId, stations) {
|
|
34907
|
-
cloudStationsCache.set(cloudRoomId, {
|
|
34908
|
-
value: stations,
|
|
34909
|
-
expiresAt: Date.now() + CLOUD_STATIONS_CACHE_MS
|
|
34910
|
-
});
|
|
34911
|
-
}
|
|
34912
|
-
function invalidateCloudStationsCache(cloudRoomId) {
|
|
34913
|
-
cloudStationsCache.delete(cloudRoomId);
|
|
34914
|
-
}
|
|
34915
|
-
async function listCloudStations(cloudRoomId) {
|
|
34916
|
-
const cached2 = getCachedCloudStations(cloudRoomId);
|
|
34917
|
-
if (cached2) return cached2;
|
|
34918
|
-
try {
|
|
34919
|
-
const res = await fetch(`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/stations`, {
|
|
34920
|
-
headers: cloudHeaders(cloudRoomId),
|
|
34921
|
-
signal: AbortSignal.timeout(1e4)
|
|
34922
|
-
});
|
|
34923
|
-
if (!res.ok) return [];
|
|
34924
|
-
const data = await res.json();
|
|
34925
|
-
const stations = data.stations ?? [];
|
|
34926
|
-
setCachedCloudStations(cloudRoomId, stations);
|
|
34927
|
-
return stations;
|
|
34928
|
-
} catch {
|
|
34929
|
-
return [];
|
|
34930
|
-
}
|
|
34931
|
-
}
|
|
34932
|
-
async function execOnCloudStation(cloudRoomId, subId, command, timeoutMs = 9e4) {
|
|
34933
|
-
try {
|
|
34934
|
-
const res = await fetch(
|
|
34935
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/stations/${subId}/exec`,
|
|
34936
|
-
{
|
|
34937
|
-
method: "POST",
|
|
34938
|
-
headers: cloudHeaders(cloudRoomId, { "Content-Type": "application/json" }),
|
|
34939
|
-
body: JSON.stringify({ command }),
|
|
34940
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
34941
|
-
}
|
|
34942
|
-
);
|
|
34943
|
-
if (!res.ok) return null;
|
|
34944
|
-
return res.json();
|
|
34945
|
-
} catch {
|
|
34946
|
-
return null;
|
|
34947
|
-
}
|
|
34948
|
-
}
|
|
34949
|
-
async function getCloudStationLogs(cloudRoomId, subId, lines) {
|
|
34950
|
-
try {
|
|
34951
|
-
const query = lines ? `?lines=${lines}` : "";
|
|
34952
|
-
const res = await fetch(
|
|
34953
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/stations/${subId}/logs${query}`,
|
|
34954
|
-
{
|
|
34955
|
-
headers: cloudHeaders(cloudRoomId),
|
|
34956
|
-
signal: AbortSignal.timeout(15e3)
|
|
34957
|
-
}
|
|
34958
|
-
);
|
|
34959
|
-
if (!res.ok) return null;
|
|
34960
|
-
const data = await res.json();
|
|
34961
|
-
return data.logs ?? "";
|
|
34962
|
-
} catch {
|
|
34963
|
-
return null;
|
|
34964
|
-
}
|
|
34965
|
-
}
|
|
34966
|
-
async function startCloudStation(cloudRoomId, subId) {
|
|
34967
|
-
try {
|
|
34968
|
-
await fetch(
|
|
34969
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/stations/${subId}/start`,
|
|
34970
|
-
{
|
|
34971
|
-
method: "POST",
|
|
34972
|
-
headers: cloudHeaders(cloudRoomId),
|
|
34973
|
-
signal: AbortSignal.timeout(3e4)
|
|
34974
|
-
}
|
|
34975
|
-
);
|
|
34976
|
-
} catch {
|
|
34977
|
-
} finally {
|
|
34978
|
-
invalidateCloudStationsCache(cloudRoomId);
|
|
34979
|
-
}
|
|
34980
|
-
}
|
|
34981
|
-
async function stopCloudStation(cloudRoomId, subId) {
|
|
34982
|
-
try {
|
|
34983
|
-
await fetch(
|
|
34984
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/stations/${subId}/stop`,
|
|
34985
|
-
{
|
|
34986
|
-
method: "POST",
|
|
34987
|
-
headers: cloudHeaders(cloudRoomId),
|
|
34988
|
-
signal: AbortSignal.timeout(3e4)
|
|
34989
|
-
}
|
|
34990
|
-
);
|
|
34991
|
-
} catch {
|
|
34992
|
-
} finally {
|
|
34993
|
-
invalidateCloudStationsCache(cloudRoomId);
|
|
34994
|
-
}
|
|
34995
|
-
}
|
|
34996
|
-
async function deleteCloudStation(cloudRoomId, subId) {
|
|
34997
|
-
try {
|
|
34998
|
-
await fetch(
|
|
34999
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/stations/${subId}`,
|
|
35000
|
-
{
|
|
35001
|
-
method: "DELETE",
|
|
35002
|
-
headers: cloudHeaders(cloudRoomId),
|
|
35003
|
-
signal: AbortSignal.timeout(3e4)
|
|
35004
|
-
}
|
|
35005
|
-
);
|
|
35006
|
-
} catch {
|
|
35007
|
-
} finally {
|
|
35008
|
-
invalidateCloudStationsCache(cloudRoomId);
|
|
35009
|
-
}
|
|
35010
|
-
}
|
|
35011
|
-
async function cancelCloudStation(cloudRoomId, subId) {
|
|
35012
|
-
try {
|
|
35013
|
-
await fetch(
|
|
35014
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/billing/cancel/${subId}`,
|
|
35015
|
-
{
|
|
35016
|
-
method: "POST",
|
|
35017
|
-
headers: cloudHeaders(cloudRoomId),
|
|
35018
|
-
signal: AbortSignal.timeout(3e4)
|
|
35019
|
-
}
|
|
35020
|
-
);
|
|
35021
|
-
} catch {
|
|
35022
|
-
} finally {
|
|
35023
|
-
invalidateCloudStationsCache(cloudRoomId);
|
|
35024
|
-
}
|
|
35025
|
-
}
|
|
35026
|
-
async function getCloudCryptoPrices(cloudRoomId) {
|
|
35027
|
-
try {
|
|
35028
|
-
const res = await fetch(
|
|
35029
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/billing/crypto-prices`,
|
|
35030
|
-
{ signal: AbortSignal.timeout(1e4) }
|
|
35031
|
-
);
|
|
35032
|
-
if (!res.ok) return null;
|
|
35033
|
-
return res.json();
|
|
35034
|
-
} catch {
|
|
35035
|
-
return null;
|
|
35036
|
-
}
|
|
35037
|
-
}
|
|
35038
|
-
async function getCloudOnrampUrl(cloudRoomId, walletAddress, amount) {
|
|
35039
|
-
try {
|
|
35040
|
-
const params = new URLSearchParams({ address: walletAddress });
|
|
35041
|
-
if (amount) params.set("amount", String(amount));
|
|
35042
|
-
const res = await fetch(
|
|
35043
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/billing/onramp-url?${params}`,
|
|
35044
|
-
{ signal: AbortSignal.timeout(15e3) }
|
|
35045
|
-
);
|
|
35046
|
-
if (!res.ok) return null;
|
|
35047
|
-
return res.json();
|
|
35048
|
-
} catch {
|
|
35049
|
-
return null;
|
|
35050
|
-
}
|
|
35051
|
-
}
|
|
35052
|
-
async function cryptoCheckoutStation(cloudRoomId, tier, stationName, txHash, chain = "base") {
|
|
35053
|
-
try {
|
|
35054
|
-
const res = await fetch(
|
|
35055
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/billing/crypto-checkout`,
|
|
35056
|
-
{
|
|
35057
|
-
method: "POST",
|
|
35058
|
-
headers: cloudHeaders(cloudRoomId, { "Content-Type": "application/json" }),
|
|
35059
|
-
body: JSON.stringify({ tier, stationName, txHash, chain }),
|
|
35060
|
-
signal: AbortSignal.timeout(6e4)
|
|
35061
|
-
}
|
|
35062
|
-
);
|
|
35063
|
-
return res.json();
|
|
35064
|
-
} catch (err) {
|
|
35065
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
35066
|
-
} finally {
|
|
35067
|
-
invalidateCloudStationsCache(cloudRoomId);
|
|
35068
|
-
}
|
|
35069
|
-
}
|
|
35070
|
-
async function cryptoRenewStation(cloudRoomId, subscriptionId, txHash, chain = "base") {
|
|
35071
|
-
try {
|
|
35072
|
-
const res = await fetch(
|
|
35073
|
-
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/billing/crypto-renew/${subscriptionId}`,
|
|
35074
|
-
{
|
|
35075
|
-
method: "POST",
|
|
35076
|
-
headers: cloudHeaders(cloudRoomId, { "Content-Type": "application/json" }),
|
|
35077
|
-
body: JSON.stringify({ txHash, chain }),
|
|
35078
|
-
signal: AbortSignal.timeout(6e4)
|
|
35079
|
-
}
|
|
35080
|
-
);
|
|
35081
|
-
return res.json();
|
|
35082
|
-
} catch (err) {
|
|
35083
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
35084
|
-
} finally {
|
|
35085
|
-
invalidateCloudStationsCache(cloudRoomId);
|
|
35086
|
-
}
|
|
35087
|
-
}
|
|
35088
|
-
async function createCloudInvite(cloudRoomId, options) {
|
|
35089
|
-
try {
|
|
35090
|
-
const res = await fetch(`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/invites`, {
|
|
35091
|
-
method: "POST",
|
|
35092
|
-
headers: cloudHeaders(cloudRoomId, { "Content-Type": "application/json" }),
|
|
35093
|
-
body: JSON.stringify(options ?? {}),
|
|
35094
|
-
signal: AbortSignal.timeout(1e4)
|
|
35095
|
-
});
|
|
35096
|
-
if (!res.ok) return null;
|
|
35097
|
-
const data = await res.json();
|
|
35098
|
-
return data.inviteCode ? data : null;
|
|
35099
|
-
} catch {
|
|
35100
|
-
return null;
|
|
35101
|
-
}
|
|
35102
|
-
}
|
|
35103
|
-
async function listCloudInvites(cloudRoomId) {
|
|
35104
|
-
try {
|
|
35105
|
-
const res = await fetch(`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/invites`, {
|
|
35106
|
-
headers: cloudHeaders(cloudRoomId),
|
|
35107
|
-
signal: AbortSignal.timeout(1e4)
|
|
35108
|
-
});
|
|
35109
|
-
if (!res.ok) return [];
|
|
35110
|
-
const data = await res.json();
|
|
35111
|
-
return data.invites ?? [];
|
|
35112
|
-
} catch {
|
|
35113
|
-
return [];
|
|
35114
|
-
}
|
|
35115
|
-
}
|
|
35116
|
-
async function fetchReferredRooms(cloudRoomId) {
|
|
35117
|
-
try {
|
|
35118
|
-
const res = await fetch(`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/network`, {
|
|
35119
|
-
headers: cloudHeaders(cloudRoomId),
|
|
35120
|
-
signal: AbortSignal.timeout(1e4)
|
|
35121
|
-
});
|
|
35122
|
-
if (!res.ok) return [];
|
|
35123
|
-
const data = await res.json();
|
|
35124
|
-
return data.referredRooms ?? [];
|
|
35125
|
-
} catch {
|
|
35126
|
-
return [];
|
|
35127
|
-
}
|
|
35128
|
-
}
|
|
35129
|
-
|
|
35130
|
-
// src/shared/agent-executor.ts
|
|
35131
|
-
function resolveOpenAiCompatible(model, apiKeyOverride) {
|
|
35132
|
-
const trimmed = model.trim();
|
|
35133
|
-
if (trimmed === "gemini" || trimmed.startsWith("gemini:")) {
|
|
35134
|
-
const apiKey2 = apiKeyOverride?.trim() || (process.env.GEMINI_API_KEY || "").trim();
|
|
35135
|
-
if (!apiKey2) return null;
|
|
35136
|
-
return {
|
|
35137
|
-
apiKey: apiKey2,
|
|
35138
|
-
url: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
|
|
35139
|
-
defaultModel: "gemini-2.5-flash",
|
|
35140
|
-
label: "Gemini",
|
|
35141
|
-
prefix: "gemini"
|
|
35142
|
-
};
|
|
35143
|
-
}
|
|
35144
|
-
const apiKey = apiKeyOverride?.trim() || (process.env.OPENAI_API_KEY || "").trim();
|
|
35145
|
-
if (!apiKey) return null;
|
|
35146
|
-
return {
|
|
35147
|
-
apiKey,
|
|
35148
|
-
url: "https://api.openai.com/v1/chat/completions",
|
|
35149
|
-
defaultModel: "gpt-4o-mini",
|
|
35150
|
-
label: "OpenAI",
|
|
35151
|
-
prefix: "openai"
|
|
35152
|
-
};
|
|
35153
|
-
}
|
|
35154
|
-
function parseModelSuffix(model, prefix) {
|
|
35155
|
-
const trimmed = model.trim();
|
|
35156
|
-
if (trimmed === prefix) return "";
|
|
35157
|
-
const marker = `${prefix}:`;
|
|
35158
|
-
if (!trimmed.startsWith(marker)) return "";
|
|
35159
|
-
return trimmed.slice(marker.length).trim();
|
|
35160
|
-
}
|
|
35161
|
-
function parseAnthropicModel(model) {
|
|
35162
|
-
const normalized = model.trim();
|
|
35163
|
-
if (normalized === "anthropic") return "claude-3-5-sonnet-latest";
|
|
35164
|
-
const anthropicModel = parseModelSuffix(normalized, "anthropic");
|
|
35165
|
-
if (anthropicModel) return anthropicModel;
|
|
35166
|
-
const claudeApiModel = parseModelSuffix(normalized, "claude-api");
|
|
35167
|
-
if (claudeApiModel) return claudeApiModel;
|
|
35168
|
-
return normalized;
|
|
35169
|
-
}
|
|
35170
|
-
function extractOpenAiText(json) {
|
|
35171
|
-
const choices = json.choices;
|
|
35172
|
-
if (Array.isArray(choices) && choices.length > 0) {
|
|
35173
|
-
const first = choices[0];
|
|
35174
|
-
const message = first.message;
|
|
35175
|
-
const content = message?.content;
|
|
35176
|
-
if (typeof content === "string") return content.trim();
|
|
35177
|
-
if (Array.isArray(content)) {
|
|
35178
|
-
const text = content.map((item) => {
|
|
35179
|
-
const block = item;
|
|
35180
|
-
const blockText = block.text;
|
|
35181
|
-
return typeof blockText === "string" ? blockText : "";
|
|
35182
|
-
}).filter(Boolean).join("\n").trim();
|
|
35183
|
-
if (text) return text;
|
|
35184
|
-
}
|
|
35185
|
-
}
|
|
35186
|
-
return JSON.stringify(json);
|
|
35187
|
-
}
|
|
35188
|
-
function extractAnthropicText(json) {
|
|
35189
|
-
const content = json.content;
|
|
35190
|
-
if (Array.isArray(content)) {
|
|
35191
|
-
const text = content.map((item) => {
|
|
35192
|
-
const block = item;
|
|
35193
|
-
return block.type === "text" && typeof block.text === "string" ? block.text : "";
|
|
35194
|
-
}).filter(Boolean).join("\n").trim();
|
|
35195
|
-
if (text) return text;
|
|
35196
|
-
}
|
|
35197
|
-
return JSON.stringify(json);
|
|
35198
|
-
}
|
|
35199
|
-
function immediateError(message) {
|
|
35200
|
-
return {
|
|
35201
|
-
output: `Error: ${message}`,
|
|
35202
|
-
exitCode: 1,
|
|
35203
|
-
durationMs: 0,
|
|
35204
|
-
sessionId: null,
|
|
35205
|
-
timedOut: false
|
|
35206
|
-
};
|
|
35207
|
-
}
|
|
35208
|
-
async function executeApiOnStation(cloudRoomId, stationId, options) {
|
|
35209
|
-
const startTime = Date.now();
|
|
35210
|
-
const apiKey = options.apiKey;
|
|
35211
|
-
if (!apiKey) {
|
|
35212
|
-
return immediateError("Missing API key for station execution.");
|
|
35213
|
-
}
|
|
35214
|
-
if (options.abortSignal?.aborted) {
|
|
35215
|
-
return {
|
|
35216
|
-
output: "Execution aborted",
|
|
35217
|
-
exitCode: 130,
|
|
35218
|
-
durationMs: Date.now() - startTime,
|
|
35219
|
-
sessionId: null,
|
|
35220
|
-
timedOut: false
|
|
35221
|
-
};
|
|
35222
|
-
}
|
|
35223
|
-
const isOpenAiCompat = options.model.startsWith("openai:") || options.model.startsWith("gemini:");
|
|
35224
|
-
const messages = [];
|
|
35225
|
-
if (options.systemPrompt) messages.push({ role: "system", content: options.systemPrompt });
|
|
35226
|
-
messages.push({ role: "user", content: options.prompt });
|
|
35227
|
-
let url;
|
|
35228
|
-
let headers;
|
|
35229
|
-
let body;
|
|
35230
|
-
if (isOpenAiCompat) {
|
|
35231
|
-
const config2 = resolveOpenAiCompatible(options.model, apiKey);
|
|
35232
|
-
const modelName = config2 ? parseModelSuffix(options.model, config2.prefix) || config2.defaultModel : parseModelSuffix(options.model, "openai") || "gpt-4o-mini";
|
|
35233
|
-
url = config2?.url ?? "https://api.openai.com/v1/chat/completions";
|
|
35234
|
-
headers = { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" };
|
|
35235
|
-
body = JSON.stringify({ model: modelName, messages });
|
|
35236
|
-
} else {
|
|
35237
|
-
const modelName = parseAnthropicModel(options.model);
|
|
35238
|
-
url = "https://api.anthropic.com/v1/messages";
|
|
35239
|
-
headers = { "x-api-key": apiKey, "anthropic-version": "2023-06-01", "content-type": "application/json" };
|
|
35240
|
-
body = JSON.stringify({
|
|
35241
|
-
model: modelName,
|
|
35242
|
-
max_tokens: 2048,
|
|
35243
|
-
system: options.systemPrompt,
|
|
35244
|
-
messages: [{ role: "user", content: options.prompt }]
|
|
35245
|
-
});
|
|
35246
|
-
}
|
|
35247
|
-
const headerFlags = Object.entries(headers).map(([k, v]) => `-H '${k}: ${v}'`).join(" ");
|
|
35248
|
-
const b64 = Buffer.from(body).toString("base64");
|
|
35249
|
-
const command = `echo '${b64}' | base64 -d | curl -s --max-time 300 ${headerFlags} -d @- '${url}'`;
|
|
35250
|
-
const result = await new Promise((resolve) => {
|
|
35251
|
-
let settled = false;
|
|
35252
|
-
const finish = (value) => {
|
|
35253
|
-
if (settled) return;
|
|
35254
|
-
settled = true;
|
|
35255
|
-
resolve(value);
|
|
35256
|
-
};
|
|
35257
|
-
const onAbort = () => finish({ stdout: "", stderr: "Execution aborted", exitCode: 130 });
|
|
35258
|
-
if (options.abortSignal) {
|
|
35259
|
-
if (options.abortSignal.aborted) {
|
|
35260
|
-
onAbort();
|
|
35261
|
-
return;
|
|
35262
|
-
}
|
|
35263
|
-
options.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
35264
|
-
}
|
|
35265
|
-
void execOnCloudStation(cloudRoomId, stationId, command, 36e4).then((value) => finish(value)).catch(() => finish(null)).finally(() => {
|
|
35266
|
-
if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
|
|
35267
|
-
});
|
|
35268
|
-
});
|
|
35269
|
-
if (!result) {
|
|
35270
|
-
return {
|
|
35271
|
-
output: "Error: station execution failed (station unreachable)",
|
|
35272
|
-
exitCode: 1,
|
|
35273
|
-
durationMs: Date.now() - startTime,
|
|
35274
|
-
sessionId: null,
|
|
35275
|
-
timedOut: false
|
|
35276
|
-
};
|
|
35277
|
-
}
|
|
35278
|
-
if (result.exitCode !== 0) {
|
|
35279
|
-
return {
|
|
35280
|
-
output: result.stderr || result.stdout || `Station exec failed with exit code ${result.exitCode}`,
|
|
35281
|
-
exitCode: result.exitCode,
|
|
35282
|
-
durationMs: Date.now() - startTime,
|
|
35283
|
-
sessionId: null,
|
|
35284
|
-
timedOut: false
|
|
35285
|
-
};
|
|
35286
|
-
}
|
|
35287
|
-
try {
|
|
35288
|
-
const parsed = JSON.parse(result.stdout);
|
|
35289
|
-
const output = isOpenAiCompat ? extractOpenAiText(parsed) : extractAnthropicText(parsed);
|
|
35290
|
-
return { output, exitCode: 0, durationMs: Date.now() - startTime, sessionId: null, timedOut: false };
|
|
35291
|
-
} catch {
|
|
35292
|
-
return {
|
|
35293
|
-
output: result.stdout || "(no output from API)",
|
|
35294
|
-
exitCode: 1,
|
|
35295
|
-
durationMs: Date.now() - startTime,
|
|
35296
|
-
sessionId: null,
|
|
35297
|
-
timedOut: false
|
|
35298
|
-
};
|
|
35299
|
-
}
|
|
35300
|
-
}
|
|
35301
|
-
|
|
35302
|
-
// src/shared/model-provider.ts
|
|
35303
|
-
init_db_queries();
|
|
35304
|
-
function normalizeModel(model) {
|
|
35305
|
-
const trimmed = model?.trim();
|
|
35306
|
-
return trimmed ? trimmed : "claude";
|
|
35307
|
-
}
|
|
35308
|
-
function getModelProvider(model) {
|
|
35309
|
-
const normalized = normalizeModel(model);
|
|
35310
|
-
if (normalized === "codex" || normalized.startsWith("codex:")) return "codex_subscription";
|
|
35311
|
-
if (normalized === "openai" || normalized.startsWith("openai:")) return "openai_api";
|
|
35312
|
-
if (normalized === "anthropic" || normalized.startsWith("anthropic:") || normalized.startsWith("claude-api:")) {
|
|
35313
|
-
return "anthropic_api";
|
|
35314
|
-
}
|
|
35315
|
-
if (normalized === "gemini" || normalized.startsWith("gemini:")) return "gemini_api";
|
|
35316
|
-
return "claude_subscription";
|
|
35317
|
-
}
|
|
35318
|
-
function resolveApiKeyForModel(db2, roomId, model) {
|
|
35319
|
-
const provider = getModelProvider(model);
|
|
35320
|
-
if (provider === "openai_api") {
|
|
35321
|
-
return resolveApiKey(db2, roomId, "openai_api_key", "OPENAI_API_KEY");
|
|
35322
|
-
}
|
|
35323
|
-
if (provider === "anthropic_api") {
|
|
35324
|
-
return resolveApiKey(db2, roomId, "anthropic_api_key", "ANTHROPIC_API_KEY");
|
|
35325
|
-
}
|
|
35326
|
-
if (provider === "gemini_api") {
|
|
35327
|
-
return resolveApiKey(db2, roomId, "gemini_api_key", "GEMINI_API_KEY");
|
|
35328
|
-
}
|
|
35329
|
-
return void 0;
|
|
35330
|
-
}
|
|
35331
|
-
function resolveApiKey(db2, roomId, credentialName, envVar) {
|
|
35332
|
-
const roomCred = getRoomCredential(db2, roomId, credentialName);
|
|
35333
|
-
if (roomCred) return roomCred;
|
|
35334
|
-
const sharedRoomCred = findAnyRoomCredential(db2, credentialName, roomId);
|
|
35335
|
-
if (sharedRoomCred) return sharedRoomCred;
|
|
35336
|
-
const clerkCred = getClerkCredential(db2, credentialName);
|
|
35337
|
-
if (clerkCred) return clerkCred;
|
|
35338
|
-
return getEnvValue(envVar) || void 0;
|
|
35339
|
-
}
|
|
35340
|
-
function findAnyRoomCredential(db2, credentialName, excludeRoomId) {
|
|
35341
|
-
const rooms = listRooms(db2);
|
|
35342
|
-
for (const room of rooms) {
|
|
35343
|
-
if (excludeRoomId != null && room.id === excludeRoomId) continue;
|
|
35344
|
-
const value = getRoomCredential(db2, room.id, credentialName);
|
|
35345
|
-
if (value) return value;
|
|
35346
|
-
}
|
|
35347
|
-
return null;
|
|
35348
|
-
}
|
|
35349
|
-
function getClerkCredential(db2, credentialName) {
|
|
35350
|
-
if (credentialName === "openai_api_key") {
|
|
35351
|
-
return getClerkApiKey(db2, "openai_api");
|
|
35352
|
-
}
|
|
35353
|
-
if (credentialName === "anthropic_api_key") {
|
|
35354
|
-
return getClerkApiKey(db2, "anthropic_api");
|
|
35355
|
-
}
|
|
35356
|
-
if (credentialName === "gemini_api_key") {
|
|
35357
|
-
return getClerkApiKey(db2, "gemini_api");
|
|
35358
|
-
}
|
|
35359
|
-
return null;
|
|
35360
|
-
}
|
|
35361
|
-
function getRoomCredential(db2, roomId, credentialName) {
|
|
35362
|
-
try {
|
|
35363
|
-
const credential = getCredentialByName(db2, roomId, credentialName);
|
|
35364
|
-
if (!credential) return null;
|
|
35365
|
-
const value = (credential.valueEncrypted || "").trim();
|
|
35366
|
-
if (!value || value.startsWith("enc:v1:")) return null;
|
|
35367
|
-
return value;
|
|
35368
|
-
} catch {
|
|
35369
|
-
return null;
|
|
35370
|
-
}
|
|
35371
|
-
}
|
|
35372
|
-
function getEnvValue(envVar) {
|
|
35373
|
-
return (process.env[envVar] || "").trim();
|
|
35374
|
-
}
|
|
35375
|
-
|
|
35376
34698
|
// src/shared/task-runner.ts
|
|
35377
34699
|
init_db_queries();
|
|
35378
34700
|
init_constants();
|
|
@@ -35787,78 +35109,6 @@ async function executeTask(taskId, options) {
|
|
|
35787
35109
|
} catch (err) {
|
|
35788
35110
|
console.warn("Non-fatal: worker resolution failed:", err);
|
|
35789
35111
|
}
|
|
35790
|
-
const isStationModel = model?.startsWith("openai:") || model?.startsWith("anthropic:") || model?.startsWith("claude-api:");
|
|
35791
|
-
if (isStationModel && task.roomId) {
|
|
35792
|
-
runningTasks.add(taskId);
|
|
35793
|
-
taskAbortControllers.set(taskId, taskAbort);
|
|
35794
|
-
try {
|
|
35795
|
-
const cloudRoomId = getRoomCloudId(task.roomId);
|
|
35796
|
-
const stations = await listCloudStations(cloudRoomId);
|
|
35797
|
-
const activeStations = stations.filter((s) => s.status === "active");
|
|
35798
|
-
if (activeStations.length > 0) {
|
|
35799
|
-
const run2 = createTaskRun(db2, taskId);
|
|
35800
|
-
try {
|
|
35801
|
-
const station = activeStations[run2.id % activeStations.length];
|
|
35802
|
-
let augmentedPrompt = prependKeeperReferral(task.prompt, db2);
|
|
35803
|
-
try {
|
|
35804
|
-
if (task.learnedContext) {
|
|
35805
|
-
augmentedPrompt = `## Approach (learned from previous runs):
|
|
35806
|
-
${task.learnedContext}
|
|
35807
|
-
|
|
35808
|
-
---
|
|
35809
|
-
|
|
35810
|
-
${augmentedPrompt}`;
|
|
35811
|
-
}
|
|
35812
|
-
} catch (err) {
|
|
35813
|
-
console.warn("Non-fatal: learned context injection failed:", err);
|
|
35814
|
-
}
|
|
35815
|
-
try {
|
|
35816
|
-
const memoryContext = getTaskMemoryContext(db2, taskId);
|
|
35817
|
-
if (memoryContext) {
|
|
35818
|
-
augmentedPrompt = `${memoryContext}
|
|
35819
|
-
|
|
35820
|
-
---
|
|
35821
|
-
|
|
35822
|
-
${augmentedPrompt}`;
|
|
35823
|
-
}
|
|
35824
|
-
} catch (err) {
|
|
35825
|
-
console.warn("Non-fatal: memory injection failed:", err);
|
|
35826
|
-
}
|
|
35827
|
-
const timeoutMs = task.timeoutMinutes != null ? task.timeoutMinutes * 60 * 1e3 : void 0;
|
|
35828
|
-
const stationModel = model;
|
|
35829
|
-
const apiKey = resolveApiKeyForModel(db2, task.roomId, stationModel);
|
|
35830
|
-
const agentResult = await executeApiOnStation(cloudRoomId, station.id, {
|
|
35831
|
-
model: stationModel,
|
|
35832
|
-
prompt: augmentedPrompt,
|
|
35833
|
-
systemPrompt,
|
|
35834
|
-
timeoutMs,
|
|
35835
|
-
apiKey,
|
|
35836
|
-
abortSignal: taskAbort.signal
|
|
35837
|
-
});
|
|
35838
|
-
const result = agentResultToExecutionResult(agentResult);
|
|
35839
|
-
if (taskAbort.signal.aborted) {
|
|
35840
|
-
const errorMsg = "Execution aborted";
|
|
35841
|
-
completeTaskRun(db2, run2.id, result.stdout || errorMsg, void 0, errorMsg);
|
|
35842
|
-
onFailed?.(task, errorMsg);
|
|
35843
|
-
return { success: false, output: result.stdout || "", errorMessage: errorMsg, durationMs: Date.now() - startTime };
|
|
35844
|
-
}
|
|
35845
|
-
return finishRun(db2, run2.id, taskId, task, result, resultsDir, onComplete, onFailed);
|
|
35846
|
-
} catch (err) {
|
|
35847
|
-
const errorMsg = taskAbort.signal.aborted ? "Execution aborted" : err instanceof Error ? err.message : String(err);
|
|
35848
|
-
completeTaskRun(db2, run2.id, "", void 0, errorMsg);
|
|
35849
|
-
onFailed?.(task, errorMsg);
|
|
35850
|
-
return { success: false, output: "", errorMessage: errorMsg, durationMs: Date.now() - startTime };
|
|
35851
|
-
}
|
|
35852
|
-
}
|
|
35853
|
-
} catch (err) {
|
|
35854
|
-
const errorMsg = taskAbort.signal.aborted ? "Execution aborted" : err instanceof Error ? err.message : String(err);
|
|
35855
|
-
onFailed?.(task, errorMsg);
|
|
35856
|
-
return { success: false, output: "", errorMessage: errorMsg, durationMs: Date.now() - startTime };
|
|
35857
|
-
} finally {
|
|
35858
|
-
runningTasks.delete(taskId);
|
|
35859
|
-
taskAbortControllers.delete(taskId);
|
|
35860
|
-
}
|
|
35861
|
-
}
|
|
35862
35112
|
await acquireSlot(getMaxConcurrentTasks(db2, task.roomId));
|
|
35863
35113
|
runningTasks.add(taskId);
|
|
35864
35114
|
taskAbortControllers.set(taskId, taskAbort);
|
|
@@ -36033,16 +35283,6 @@ ${retryPrompt}`;
|
|
|
36033
35283
|
releaseSlot();
|
|
36034
35284
|
}
|
|
36035
35285
|
}
|
|
36036
|
-
function agentResultToExecutionResult(result) {
|
|
36037
|
-
return {
|
|
36038
|
-
stdout: result.output,
|
|
36039
|
-
stderr: "",
|
|
36040
|
-
exitCode: result.exitCode,
|
|
36041
|
-
durationMs: result.durationMs,
|
|
36042
|
-
timedOut: result.timedOut,
|
|
36043
|
-
sessionId: result.sessionId
|
|
36044
|
-
};
|
|
36045
|
-
}
|
|
36046
35286
|
function finishRun(db2, runId, taskId, task, result, resultsDir, onComplete, onFailed) {
|
|
36047
35287
|
const output = result.stdout || result.stderr || "(no output)";
|
|
36048
35288
|
const resultFilePath = saveResult(resultsDir, task.name, output, result);
|
|
@@ -36098,8 +35338,8 @@ function finishRun(db2, runId, taskId, task, result, resultsDir, onComplete, onF
|
|
|
36098
35338
|
}
|
|
36099
35339
|
}
|
|
36100
35340
|
function saveResult(resultsDir, taskName, output, result) {
|
|
36101
|
-
if (!(0,
|
|
36102
|
-
(0,
|
|
35341
|
+
if (!(0, import_fs2.existsSync)(resultsDir)) {
|
|
35342
|
+
(0, import_fs2.mkdirSync)(resultsDir, { recursive: true });
|
|
36103
35343
|
}
|
|
36104
35344
|
const safeName = taskName.replace(/[^a-zA-Z0-9-_]/g, "_").substring(0, 50);
|
|
36105
35345
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -36115,7 +35355,7 @@ function saveResult(resultsDir, taskName, output, result) {
|
|
|
36115
35355
|
|
|
36116
35356
|
${output}
|
|
36117
35357
|
`;
|
|
36118
|
-
(0,
|
|
35358
|
+
(0, import_fs2.writeFileSync)(filePath, markdown, "utf-8");
|
|
36119
35359
|
return filePath;
|
|
36120
35360
|
}
|
|
36121
35361
|
|
|
@@ -36123,10 +35363,10 @@ ${output}
|
|
|
36123
35363
|
function getServerPort() {
|
|
36124
35364
|
try {
|
|
36125
35365
|
const dbPath = process.env.QUOROOM_DB_PATH;
|
|
36126
|
-
const dataDir = process.env.QUOROOM_DATA_DIR || (dbPath ? (0, import_path4.dirname)(dbPath) : (0, import_path4.join)((0,
|
|
35366
|
+
const dataDir = process.env.QUOROOM_DATA_DIR || (dbPath ? (0, import_path4.dirname)(dbPath) : (0, import_path4.join)((0, import_os4.homedir)(), `.${APP_NAME.toLowerCase()}`));
|
|
36127
35367
|
const portFile = (0, import_path4.join)(dataDir, "api.port");
|
|
36128
|
-
if ((0,
|
|
36129
|
-
const port = parseInt((0,
|
|
35368
|
+
if ((0, import_fs3.existsSync)(portFile)) {
|
|
35369
|
+
const port = parseInt((0, import_fs3.readFileSync)(portFile, "utf-8").trim(), 10);
|
|
36130
35370
|
return Number.isFinite(port) && port > 0 ? port : null;
|
|
36131
35371
|
}
|
|
36132
35372
|
} catch {
|
|
@@ -36235,7 +35475,7 @@ function registerSchedulerTools(server) {
|
|
|
36235
35475
|
};
|
|
36236
35476
|
}
|
|
36237
35477
|
}
|
|
36238
|
-
const webhookToken = triggerType === TRIGGER_TYPES.WEBHOOK ? (0,
|
|
35478
|
+
const webhookToken = triggerType === TRIGGER_TYPES.WEBHOOK ? (0, import_crypto5.randomBytes)(16).toString("hex") : void 0;
|
|
36239
35479
|
const task = createTask(db2, {
|
|
36240
35480
|
name: taskName,
|
|
36241
35481
|
prompt,
|
|
@@ -36364,7 +35604,7 @@ Trigger it with: curl -X POST ${webhookUrl}`
|
|
|
36364
35604
|
if (task.status !== TASK_STATUSES.ACTIVE) {
|
|
36365
35605
|
updateTask(db2, id, { status: TASK_STATUSES.ACTIVE });
|
|
36366
35606
|
}
|
|
36367
|
-
const resultsDir = process.env.QUOROOM_RESULTS_DIR || (0, import_path4.join)((0,
|
|
35607
|
+
const resultsDir = process.env.QUOROOM_RESULTS_DIR || (0, import_path4.join)((0, import_os4.homedir)(), APP_NAME, "results");
|
|
36368
35608
|
executeTask(id, { db: db2, resultsDir }).then((result) => {
|
|
36369
35609
|
if (originalStatus !== TASK_STATUSES.ACTIVE) {
|
|
36370
35610
|
const currentTask = getTask(db2, id);
|
|
@@ -36379,7 +35619,7 @@ Trigger it with: curl -X POST ${webhookUrl}`
|
|
|
36379
35619
|
const dbPath = process.env.QUOROOM_DB_PATH;
|
|
36380
35620
|
if (dbPath) {
|
|
36381
35621
|
const dataDir = process.env.QUOROOM_DATA_DIR || (0, import_path4.dirname)(dbPath);
|
|
36382
|
-
const port = parseInt((0,
|
|
35622
|
+
const port = parseInt((0, import_fs3.readFileSync)((0, import_path4.join)(dataDir, "sidecar.port"), "utf-8").trim(), 10);
|
|
36383
35623
|
if (port > 0) {
|
|
36384
35624
|
const event = result.success ? "task:complete" : "task:failed";
|
|
36385
35625
|
const payload = JSON.stringify({
|
|
@@ -36660,7 +35900,7 @@ Trigger it with: curl -X POST ${webhookUrl}`
|
|
|
36660
35900
|
}
|
|
36661
35901
|
let token = task.webhookToken;
|
|
36662
35902
|
if (!token && generateIfMissing) {
|
|
36663
|
-
token = (0,
|
|
35903
|
+
token = (0, import_crypto5.randomBytes)(16).toString("hex");
|
|
36664
35904
|
updateTask(db2, taskId, { webhookToken: token });
|
|
36665
35905
|
}
|
|
36666
35906
|
if (!token) {
|
|
@@ -36687,7 +35927,7 @@ With payload: curl -X POST ${url} -H "Content-Type: application/json" -d '{"mess
|
|
|
36687
35927
|
}
|
|
36688
35928
|
let token = room.webhookToken;
|
|
36689
35929
|
if (!token && generateIfMissing) {
|
|
36690
|
-
token = (0,
|
|
35930
|
+
token = (0, import_crypto5.randomBytes)(16).toString("hex");
|
|
36691
35931
|
updateRoom(db2, roomId, { webhookToken: token });
|
|
36692
35932
|
}
|
|
36693
35933
|
if (!token) {
|
|
@@ -36897,7 +36137,7 @@ init_db();
|
|
|
36897
36137
|
init_db_queries();
|
|
36898
36138
|
|
|
36899
36139
|
// src/shared/room.ts
|
|
36900
|
-
var
|
|
36140
|
+
var import_crypto8 = __toESM(require("crypto"));
|
|
36901
36141
|
init_db_queries();
|
|
36902
36142
|
init_constants();
|
|
36903
36143
|
|
|
@@ -36943,7 +36183,7 @@ function getGoalTree(db2, roomId) {
|
|
|
36943
36183
|
}
|
|
36944
36184
|
|
|
36945
36185
|
// src/shared/wallet.ts
|
|
36946
|
-
var
|
|
36186
|
+
var import_crypto7 = __toESM(require("crypto"));
|
|
36947
36187
|
|
|
36948
36188
|
// node_modules/viem/_esm/actions/public/getTransactionCount.js
|
|
36949
36189
|
init_fromHex();
|
|
@@ -46137,21 +45377,21 @@ var VIEM_CHAINS = {
|
|
|
46137
45377
|
var ENCRYPTION_ALGORITHM = "aes-256-gcm";
|
|
46138
45378
|
var IV_LENGTH = 12;
|
|
46139
45379
|
function encryptPrivateKey(privateKey, encryptionKey) {
|
|
46140
|
-
const key = typeof encryptionKey === "string" ?
|
|
46141
|
-
const iv =
|
|
46142
|
-
const cipher =
|
|
45380
|
+
const key = typeof encryptionKey === "string" ? import_crypto7.default.createHash("sha256").update(encryptionKey).digest() : encryptionKey;
|
|
45381
|
+
const iv = import_crypto7.default.randomBytes(IV_LENGTH);
|
|
45382
|
+
const cipher = import_crypto7.default.createCipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
46143
45383
|
const encrypted = Buffer.concat([cipher.update(privateKey, "utf8"), cipher.final()]);
|
|
46144
45384
|
const tag = cipher.getAuthTag();
|
|
46145
45385
|
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted.toString("hex")}`;
|
|
46146
45386
|
}
|
|
46147
45387
|
function decryptPrivateKey(encrypted, encryptionKey) {
|
|
46148
|
-
const key = typeof encryptionKey === "string" ?
|
|
45388
|
+
const key = typeof encryptionKey === "string" ? import_crypto7.default.createHash("sha256").update(encryptionKey).digest() : encryptionKey;
|
|
46149
45389
|
const parts = encrypted.split(":");
|
|
46150
45390
|
if (parts.length !== 3) throw new Error("Invalid encrypted key format");
|
|
46151
45391
|
const iv = Buffer.from(parts[0], "hex");
|
|
46152
45392
|
const tag = Buffer.from(parts[1], "hex");
|
|
46153
45393
|
const ciphertext = Buffer.from(parts[2], "hex");
|
|
46154
|
-
const decipher =
|
|
45394
|
+
const decipher = import_crypto7.default.createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
46155
45395
|
decipher.setAuthTag(tag);
|
|
46156
45396
|
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
46157
45397
|
}
|
|
@@ -46286,7 +45526,7 @@ function createRoom2(db2, input) {
|
|
|
46286
45526
|
if (input.goal) {
|
|
46287
45527
|
rootGoal = setRoomObjective(db2, room.id, input.goal);
|
|
46288
45528
|
}
|
|
46289
|
-
const encryptionKey =
|
|
45529
|
+
const encryptionKey = import_crypto8.default.createHash("sha256").update(`quoroom-wallet-${room.id}-${room.name}`).digest("hex");
|
|
46290
45530
|
const wallet = createRoomWallet(db2, room.id, encryptionKey);
|
|
46291
45531
|
logRoomActivity(
|
|
46292
45532
|
db2,
|
|
@@ -46714,20 +45954,20 @@ function vote(db2, decisionId, workerId, voteValue, reasoning) {
|
|
|
46714
45954
|
}
|
|
46715
45955
|
|
|
46716
45956
|
// src/mcp/nudge.ts
|
|
46717
|
-
var
|
|
45957
|
+
var import_fs4 = require("fs");
|
|
46718
45958
|
var import_path5 = require("path");
|
|
46719
|
-
var
|
|
45959
|
+
var import_os5 = require("os");
|
|
46720
45960
|
var import_http4 = require("http");
|
|
46721
45961
|
init_constants();
|
|
46722
45962
|
function getApiInfo() {
|
|
46723
45963
|
try {
|
|
46724
45964
|
const dbPath = process.env.QUOROOM_DB_PATH;
|
|
46725
|
-
const dataDir = process.env.QUOROOM_DATA_DIR || (dbPath ? (0, import_path5.join)(dbPath, "..") : (0, import_path5.join)((0,
|
|
45965
|
+
const dataDir = process.env.QUOROOM_DATA_DIR || (dbPath ? (0, import_path5.join)(dbPath, "..") : (0, import_path5.join)((0, import_os5.homedir)(), `.${APP_NAME.toLowerCase()}`));
|
|
46726
45966
|
const portFile = (0, import_path5.join)(dataDir, "api.port");
|
|
46727
45967
|
const tokenFile = (0, import_path5.join)(dataDir, "api.token");
|
|
46728
|
-
if (!(0,
|
|
46729
|
-
const port = parseInt((0,
|
|
46730
|
-
const token = (0,
|
|
45968
|
+
if (!(0, import_fs4.existsSync)(portFile) || !(0, import_fs4.existsSync)(tokenFile)) return null;
|
|
45969
|
+
const port = parseInt((0, import_fs4.readFileSync)(portFile, "utf-8").trim(), 10);
|
|
45970
|
+
const token = (0, import_fs4.readFileSync)(tokenFile, "utf-8").trim();
|
|
46731
45971
|
if (!Number.isFinite(port) || port <= 0 || !token) return null;
|
|
46732
45972
|
return { port, token };
|
|
46733
45973
|
} catch {
|
|
@@ -47535,6 +46775,158 @@ function formatPaymentAuditSuffix(audit) {
|
|
|
47535
46775
|
|
|
47536
46776
|
// src/mcp/tools/wallet.ts
|
|
47537
46777
|
init_db_queries();
|
|
46778
|
+
|
|
46779
|
+
// src/shared/cloud-sync.ts
|
|
46780
|
+
var import_crypto10 = require("crypto");
|
|
46781
|
+
var import_os7 = require("os");
|
|
46782
|
+
var import_path6 = require("path");
|
|
46783
|
+
var import_fs5 = require("fs");
|
|
46784
|
+
|
|
46785
|
+
// src/shared/telemetry.ts
|
|
46786
|
+
var import_crypto9 = require("crypto");
|
|
46787
|
+
var import_os6 = require("os");
|
|
46788
|
+
var TELEMETRY_TOKEN = process.env.QUOROOM_TELEMETRY_TOKEN ?? "";
|
|
46789
|
+
var cachedMachineId = null;
|
|
46790
|
+
function getMachineId() {
|
|
46791
|
+
if (cachedMachineId) return cachedMachineId;
|
|
46792
|
+
try {
|
|
46793
|
+
const raw = (0, import_os6.hostname)() + (0, import_os6.userInfo)().username;
|
|
46794
|
+
cachedMachineId = (0, import_crypto9.createHash)("sha256").update(raw).digest("hex").slice(0, 12);
|
|
46795
|
+
} catch {
|
|
46796
|
+
cachedMachineId = "unknown";
|
|
46797
|
+
}
|
|
46798
|
+
return cachedMachineId;
|
|
46799
|
+
}
|
|
46800
|
+
|
|
46801
|
+
// src/shared/cloud-sync.ts
|
|
46802
|
+
function getCloudApi() {
|
|
46803
|
+
return (process.env.QUOROOM_CLOUD_API ?? "https://quoroom.io/api").replace(/\/$/, "");
|
|
46804
|
+
}
|
|
46805
|
+
var TOKEN_FILE_NAME = "cloud-room-tokens.json";
|
|
46806
|
+
var cachedTokens = null;
|
|
46807
|
+
function getCloudTokenFilePath() {
|
|
46808
|
+
const explicitDataDir = process.env.QUOROOM_DATA_DIR?.trim();
|
|
46809
|
+
if (explicitDataDir) return (0, import_path6.join)(explicitDataDir, TOKEN_FILE_NAME);
|
|
46810
|
+
const dbPath = process.env.QUOROOM_DB_PATH?.trim();
|
|
46811
|
+
if (dbPath) return (0, import_path6.join)((0, import_path6.dirname)(dbPath), TOKEN_FILE_NAME);
|
|
46812
|
+
return (0, import_path6.join)((0, import_os7.homedir)(), ".quoroom", TOKEN_FILE_NAME);
|
|
46813
|
+
}
|
|
46814
|
+
function loadTokenStore() {
|
|
46815
|
+
if (cachedTokens) return cachedTokens;
|
|
46816
|
+
const filePath = getCloudTokenFilePath();
|
|
46817
|
+
try {
|
|
46818
|
+
const parsed = JSON.parse((0, import_fs5.readFileSync)(filePath, "utf-8"));
|
|
46819
|
+
cachedTokens = parsed.rooms ?? {};
|
|
46820
|
+
} catch {
|
|
46821
|
+
cachedTokens = {};
|
|
46822
|
+
}
|
|
46823
|
+
return cachedTokens;
|
|
46824
|
+
}
|
|
46825
|
+
function saveTokenStore() {
|
|
46826
|
+
const filePath = getCloudTokenFilePath();
|
|
46827
|
+
(0, import_fs5.mkdirSync)((0, import_path6.dirname)(filePath), { recursive: true });
|
|
46828
|
+
const payload = JSON.stringify({ rooms: loadTokenStore() }, null, 2) + "\n";
|
|
46829
|
+
(0, import_fs5.writeFileSync)(filePath, payload, { mode: 384 });
|
|
46830
|
+
}
|
|
46831
|
+
function getRoomToken(roomId) {
|
|
46832
|
+
return loadTokenStore()[roomId];
|
|
46833
|
+
}
|
|
46834
|
+
function setRoomToken(roomId, token) {
|
|
46835
|
+
loadTokenStore()[roomId] = token;
|
|
46836
|
+
saveTokenStore();
|
|
46837
|
+
}
|
|
46838
|
+
function cloudHeaders(roomId, extra = {}) {
|
|
46839
|
+
const roomToken = roomId ? getRoomToken(roomId) : void 0;
|
|
46840
|
+
if (!roomToken) return extra;
|
|
46841
|
+
return { ...extra, "X-Room-Token": roomToken };
|
|
46842
|
+
}
|
|
46843
|
+
async function ensureCloudRoomToken(data) {
|
|
46844
|
+
if (getRoomToken(data.roomId)) return true;
|
|
46845
|
+
await registerWithCloud(data);
|
|
46846
|
+
return Boolean(getRoomToken(data.roomId));
|
|
46847
|
+
}
|
|
46848
|
+
async function registerWithCloud(data) {
|
|
46849
|
+
try {
|
|
46850
|
+
const payload = {
|
|
46851
|
+
...data,
|
|
46852
|
+
inviteCode: data.referredByCode ?? null,
|
|
46853
|
+
keeperReferralCode: data.keeperReferralCode ?? null
|
|
46854
|
+
};
|
|
46855
|
+
const res = await fetch(`${getCloudApi()}/rooms/register`, {
|
|
46856
|
+
method: "POST",
|
|
46857
|
+
headers: cloudHeaders(data.roomId, { "Content-Type": "application/json" }),
|
|
46858
|
+
body: JSON.stringify(payload),
|
|
46859
|
+
signal: AbortSignal.timeout(1e4)
|
|
46860
|
+
});
|
|
46861
|
+
if (!res.ok) return;
|
|
46862
|
+
const result = await res.json().catch(() => ({}));
|
|
46863
|
+
if (typeof result.roomToken === "string" && result.roomToken.length > 0) {
|
|
46864
|
+
setRoomToken(data.roomId, result.roomToken);
|
|
46865
|
+
}
|
|
46866
|
+
} catch {
|
|
46867
|
+
}
|
|
46868
|
+
}
|
|
46869
|
+
function getRoomCloudId(dbRoomId) {
|
|
46870
|
+
const machineId = getMachineId();
|
|
46871
|
+
return (0, import_crypto10.createHash)("sha256").update(`${machineId}:${dbRoomId}`).digest("hex").slice(0, 32);
|
|
46872
|
+
}
|
|
46873
|
+
async function getCloudOnrampUrl(cloudRoomId, walletAddress, amount) {
|
|
46874
|
+
try {
|
|
46875
|
+
const params = new URLSearchParams({ address: walletAddress });
|
|
46876
|
+
if (amount) params.set("amount", String(amount));
|
|
46877
|
+
const res = await fetch(
|
|
46878
|
+
`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/billing/onramp-url?${params}`,
|
|
46879
|
+
{ signal: AbortSignal.timeout(15e3) }
|
|
46880
|
+
);
|
|
46881
|
+
if (!res.ok) return null;
|
|
46882
|
+
return res.json();
|
|
46883
|
+
} catch {
|
|
46884
|
+
return null;
|
|
46885
|
+
}
|
|
46886
|
+
}
|
|
46887
|
+
async function createCloudInvite(cloudRoomId, options) {
|
|
46888
|
+
try {
|
|
46889
|
+
const res = await fetch(`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/invites`, {
|
|
46890
|
+
method: "POST",
|
|
46891
|
+
headers: cloudHeaders(cloudRoomId, { "Content-Type": "application/json" }),
|
|
46892
|
+
body: JSON.stringify(options ?? {}),
|
|
46893
|
+
signal: AbortSignal.timeout(1e4)
|
|
46894
|
+
});
|
|
46895
|
+
if (!res.ok) return null;
|
|
46896
|
+
const data = await res.json();
|
|
46897
|
+
return data.inviteCode ? data : null;
|
|
46898
|
+
} catch {
|
|
46899
|
+
return null;
|
|
46900
|
+
}
|
|
46901
|
+
}
|
|
46902
|
+
async function listCloudInvites(cloudRoomId) {
|
|
46903
|
+
try {
|
|
46904
|
+
const res = await fetch(`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/invites`, {
|
|
46905
|
+
headers: cloudHeaders(cloudRoomId),
|
|
46906
|
+
signal: AbortSignal.timeout(1e4)
|
|
46907
|
+
});
|
|
46908
|
+
if (!res.ok) return [];
|
|
46909
|
+
const data = await res.json();
|
|
46910
|
+
return data.invites ?? [];
|
|
46911
|
+
} catch {
|
|
46912
|
+
return [];
|
|
46913
|
+
}
|
|
46914
|
+
}
|
|
46915
|
+
async function fetchReferredRooms(cloudRoomId) {
|
|
46916
|
+
try {
|
|
46917
|
+
const res = await fetch(`${getCloudApi()}/rooms/${encodeURIComponent(cloudRoomId)}/network`, {
|
|
46918
|
+
headers: cloudHeaders(cloudRoomId),
|
|
46919
|
+
signal: AbortSignal.timeout(1e4)
|
|
46920
|
+
});
|
|
46921
|
+
if (!res.ok) return [];
|
|
46922
|
+
const data = await res.json();
|
|
46923
|
+
return data.referredRooms ?? [];
|
|
46924
|
+
} catch {
|
|
46925
|
+
return [];
|
|
46926
|
+
}
|
|
46927
|
+
}
|
|
46928
|
+
|
|
46929
|
+
// src/mcp/tools/wallet.ts
|
|
47538
46930
|
function registerWalletTools(server) {
|
|
47539
46931
|
server.registerTool(
|
|
47540
46932
|
"quoroom_wallet_create",
|
|
@@ -47721,428 +47113,6 @@ function registerWalletTools(server) {
|
|
|
47721
47113
|
);
|
|
47722
47114
|
}
|
|
47723
47115
|
|
|
47724
|
-
// src/mcp/tools/station.ts
|
|
47725
|
-
init_db();
|
|
47726
|
-
init_db_queries();
|
|
47727
|
-
init_constants();
|
|
47728
|
-
var CLOUD_BASE = "https://quoroom.io";
|
|
47729
|
-
var CLOUD_ONLY = { content: [{ type: "text", text: "Station rental is only available for cloud swarms on quoroom.io." }] };
|
|
47730
|
-
var isCloudMode = () => process.env.QUOROOM_DEPLOYMENT_MODE === "cloud";
|
|
47731
|
-
async function bootstrapRoomToken(roomId) {
|
|
47732
|
-
const db2 = getMcpDatabase();
|
|
47733
|
-
const room = getRoom(db2, roomId);
|
|
47734
|
-
if (!room) return;
|
|
47735
|
-
await ensureCloudRoomToken({
|
|
47736
|
-
roomId: getRoomCloudId(roomId),
|
|
47737
|
-
name: room.name,
|
|
47738
|
-
goal: room.goal ?? null,
|
|
47739
|
-
visibility: room.visibility,
|
|
47740
|
-
referredByCode: room.referredByCode,
|
|
47741
|
-
keeperReferralCode: getSetting(db2, "keeper_referral_code")
|
|
47742
|
-
});
|
|
47743
|
-
}
|
|
47744
|
-
function registerStationTools(server) {
|
|
47745
|
-
server.registerTool(
|
|
47746
|
-
"quoroom_station_create",
|
|
47747
|
-
{
|
|
47748
|
-
title: "Create Station",
|
|
47749
|
-
description: "Rent a cloud server (station) for the room via quoroom.io. Returns a payment URL \u2014 open it in a browser to subscribe with Stripe. For crypto payment (USDC or USDT), use quoroom_station_create_crypto instead. The station appears in ~30 seconds after payment. RESPONSE STYLE: Confirm briefly in 1 sentence, include the URL.",
|
|
47750
|
-
inputSchema: {
|
|
47751
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47752
|
-
name: external_exports.string().min(1).max(100).describe('Station name (e.g., "web-server", "scraper-01")'),
|
|
47753
|
-
tier: external_exports.enum(["micro", "small", "medium", "large"]).describe(
|
|
47754
|
-
"Station tier: micro ($5/mo, 1 vCPU, 256 MB), small ($15/mo, 2 vCPU, 2 GB), medium ($40/mo, 2 vCPU perf, 4 GB), large ($100/mo, 4 vCPU perf, 8 GB)"
|
|
47755
|
-
)
|
|
47756
|
-
}
|
|
47757
|
-
},
|
|
47758
|
-
async ({ roomId }) => {
|
|
47759
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47760
|
-
await bootstrapRoomToken(roomId);
|
|
47761
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47762
|
-
const url = `${CLOUD_BASE}/stations?room=${encodeURIComponent(cloudRoomId)}`;
|
|
47763
|
-
return {
|
|
47764
|
-
content: [{
|
|
47765
|
-
type: "text",
|
|
47766
|
-
text: `To add a station, complete payment at: ${url}
|
|
47767
|
-
|
|
47768
|
-
The station will appear in your room within ~30 seconds after payment.`
|
|
47769
|
-
}]
|
|
47770
|
-
};
|
|
47771
|
-
}
|
|
47772
|
-
);
|
|
47773
|
-
server.registerTool(
|
|
47774
|
-
"quoroom_station_list",
|
|
47775
|
-
{
|
|
47776
|
-
title: "List Stations",
|
|
47777
|
-
description: "List all stations for the room, optionally filtered by status.",
|
|
47778
|
-
inputSchema: {
|
|
47779
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47780
|
-
status: external_exports.enum(["pending", "active", "stopped", "canceling", "past_due", "error"]).optional().describe("Filter by status")
|
|
47781
|
-
}
|
|
47782
|
-
},
|
|
47783
|
-
async ({ roomId, status }) => {
|
|
47784
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47785
|
-
await bootstrapRoomToken(roomId);
|
|
47786
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47787
|
-
const stations = await listCloudStations(cloudRoomId);
|
|
47788
|
-
const filtered = status ? stations.filter((s) => s.status === status) : stations;
|
|
47789
|
-
if (filtered.length === 0) {
|
|
47790
|
-
return { content: [{ type: "text", text: "No stations found." }] };
|
|
47791
|
-
}
|
|
47792
|
-
const list = filtered.map((s) => ({
|
|
47793
|
-
id: s.id,
|
|
47794
|
-
name: s.stationName,
|
|
47795
|
-
tier: s.tier,
|
|
47796
|
-
status: s.status,
|
|
47797
|
-
monthlyCost: s.monthlyCost,
|
|
47798
|
-
createdAt: s.createdAt
|
|
47799
|
-
}));
|
|
47800
|
-
return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
|
|
47801
|
-
}
|
|
47802
|
-
);
|
|
47803
|
-
server.registerTool(
|
|
47804
|
-
"quoroom_station_start",
|
|
47805
|
-
{
|
|
47806
|
-
title: "Start Station",
|
|
47807
|
-
description: "Start a stopped station. RESPONSE STYLE: Confirm briefly in 1 sentence.",
|
|
47808
|
-
inputSchema: {
|
|
47809
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47810
|
-
id: external_exports.number().describe("The station subscription ID to start")
|
|
47811
|
-
}
|
|
47812
|
-
},
|
|
47813
|
-
async ({ roomId, id }) => {
|
|
47814
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47815
|
-
await bootstrapRoomToken(roomId);
|
|
47816
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47817
|
-
await startCloudStation(cloudRoomId, id);
|
|
47818
|
-
return { content: [{ type: "text", text: `Station ${id} start requested.` }] };
|
|
47819
|
-
}
|
|
47820
|
-
);
|
|
47821
|
-
server.registerTool(
|
|
47822
|
-
"quoroom_station_stop",
|
|
47823
|
-
{
|
|
47824
|
-
title: "Stop Station",
|
|
47825
|
-
description: "Stop a running station. RESPONSE STYLE: Confirm briefly in 1 sentence.",
|
|
47826
|
-
inputSchema: {
|
|
47827
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47828
|
-
id: external_exports.number().describe("The station subscription ID to stop")
|
|
47829
|
-
}
|
|
47830
|
-
},
|
|
47831
|
-
async ({ roomId, id }) => {
|
|
47832
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47833
|
-
await bootstrapRoomToken(roomId);
|
|
47834
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47835
|
-
await stopCloudStation(cloudRoomId, id);
|
|
47836
|
-
return { content: [{ type: "text", text: `Station ${id} stop requested.` }] };
|
|
47837
|
-
}
|
|
47838
|
-
);
|
|
47839
|
-
server.registerTool(
|
|
47840
|
-
"quoroom_station_delete",
|
|
47841
|
-
{
|
|
47842
|
-
title: "Delete Station",
|
|
47843
|
-
description: "Cancel a station subscription and destroy the Fly.io machine. RESPONSE STYLE: Confirm briefly in 1 sentence.",
|
|
47844
|
-
inputSchema: {
|
|
47845
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47846
|
-
id: external_exports.number().describe("The station subscription ID to delete")
|
|
47847
|
-
}
|
|
47848
|
-
},
|
|
47849
|
-
async ({ roomId, id }) => {
|
|
47850
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47851
|
-
await bootstrapRoomToken(roomId);
|
|
47852
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47853
|
-
await deleteCloudStation(cloudRoomId, id);
|
|
47854
|
-
return {
|
|
47855
|
-
content: [{
|
|
47856
|
-
type: "text",
|
|
47857
|
-
text: `Station ${id} deletion requested (subscription canceled, machine destroyed).`
|
|
47858
|
-
}]
|
|
47859
|
-
};
|
|
47860
|
-
}
|
|
47861
|
-
);
|
|
47862
|
-
server.registerTool(
|
|
47863
|
-
"quoroom_station_cancel",
|
|
47864
|
-
{
|
|
47865
|
-
title: "Cancel Station",
|
|
47866
|
-
description: "Cancel a station subscription at end of billing period. The station keeps running until the period ends, then stops automatically. RESPONSE STYLE: Confirm briefly in 1 sentence.",
|
|
47867
|
-
inputSchema: {
|
|
47868
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47869
|
-
id: external_exports.number().describe("The station subscription ID to cancel")
|
|
47870
|
-
}
|
|
47871
|
-
},
|
|
47872
|
-
async ({ roomId, id }) => {
|
|
47873
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47874
|
-
await bootstrapRoomToken(roomId);
|
|
47875
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47876
|
-
await cancelCloudStation(cloudRoomId, id);
|
|
47877
|
-
return {
|
|
47878
|
-
content: [{
|
|
47879
|
-
type: "text",
|
|
47880
|
-
text: `Station ${id} cancellation requested (will stop at end of billing period).`
|
|
47881
|
-
}]
|
|
47882
|
-
};
|
|
47883
|
-
}
|
|
47884
|
-
);
|
|
47885
|
-
server.registerTool(
|
|
47886
|
-
"quoroom_station_exec",
|
|
47887
|
-
{
|
|
47888
|
-
title: "Execute on Station",
|
|
47889
|
-
description: "Execute a shell command on a station and return stdout/stderr.",
|
|
47890
|
-
inputSchema: {
|
|
47891
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47892
|
-
id: external_exports.number().describe("The station subscription ID"),
|
|
47893
|
-
command: external_exports.string().min(1).describe("Shell command to execute")
|
|
47894
|
-
}
|
|
47895
|
-
},
|
|
47896
|
-
async ({ roomId, id, command }) => {
|
|
47897
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47898
|
-
await bootstrapRoomToken(roomId);
|
|
47899
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47900
|
-
const result = await execOnCloudStation(cloudRoomId, id, command);
|
|
47901
|
-
if (!result) {
|
|
47902
|
-
return {
|
|
47903
|
-
content: [{ type: "text", text: "Failed to execute command on station." }],
|
|
47904
|
-
isError: true
|
|
47905
|
-
};
|
|
47906
|
-
}
|
|
47907
|
-
return {
|
|
47908
|
-
content: [{
|
|
47909
|
-
type: "text",
|
|
47910
|
-
text: JSON.stringify({ exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr }, null, 2)
|
|
47911
|
-
}]
|
|
47912
|
-
};
|
|
47913
|
-
}
|
|
47914
|
-
);
|
|
47915
|
-
server.registerTool(
|
|
47916
|
-
"quoroom_station_logs",
|
|
47917
|
-
{
|
|
47918
|
-
title: "Station Logs",
|
|
47919
|
-
description: "Get recent logs from a station.",
|
|
47920
|
-
inputSchema: {
|
|
47921
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47922
|
-
id: external_exports.number().describe("The station subscription ID"),
|
|
47923
|
-
lines: external_exports.number().int().positive().max(1e3).optional().describe("Number of log lines (default: all)")
|
|
47924
|
-
}
|
|
47925
|
-
},
|
|
47926
|
-
async ({ roomId, id, lines }) => {
|
|
47927
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47928
|
-
await bootstrapRoomToken(roomId);
|
|
47929
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47930
|
-
const logs = await getCloudStationLogs(cloudRoomId, id, lines);
|
|
47931
|
-
if (logs === null) {
|
|
47932
|
-
return {
|
|
47933
|
-
content: [{ type: "text", text: "Failed to retrieve logs." }],
|
|
47934
|
-
isError: true
|
|
47935
|
-
};
|
|
47936
|
-
}
|
|
47937
|
-
return { content: [{ type: "text", text: logs || "(no logs)" }] };
|
|
47938
|
-
}
|
|
47939
|
-
);
|
|
47940
|
-
server.registerTool(
|
|
47941
|
-
"quoroom_station_status",
|
|
47942
|
-
{
|
|
47943
|
-
title: "Station Status",
|
|
47944
|
-
description: "Get live status for a station from the cloud.",
|
|
47945
|
-
inputSchema: {
|
|
47946
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47947
|
-
id: external_exports.number().describe("The station subscription ID")
|
|
47948
|
-
}
|
|
47949
|
-
},
|
|
47950
|
-
async ({ roomId, id }) => {
|
|
47951
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47952
|
-
await bootstrapRoomToken(roomId);
|
|
47953
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
47954
|
-
const stations = await listCloudStations(cloudRoomId);
|
|
47955
|
-
const station = stations.find((s) => s.id === id);
|
|
47956
|
-
if (!station) {
|
|
47957
|
-
return {
|
|
47958
|
-
content: [{ type: "text", text: `Station ${id} not found` }],
|
|
47959
|
-
isError: true
|
|
47960
|
-
};
|
|
47961
|
-
}
|
|
47962
|
-
return {
|
|
47963
|
-
content: [{
|
|
47964
|
-
type: "text",
|
|
47965
|
-
text: JSON.stringify({
|
|
47966
|
-
id: station.id,
|
|
47967
|
-
name: station.stationName,
|
|
47968
|
-
tier: station.tier,
|
|
47969
|
-
status: station.status,
|
|
47970
|
-
monthlyCost: station.monthlyCost,
|
|
47971
|
-
currentPeriodEnd: station.currentPeriodEnd,
|
|
47972
|
-
createdAt: station.createdAt
|
|
47973
|
-
}, null, 2)
|
|
47974
|
-
}]
|
|
47975
|
-
};
|
|
47976
|
-
}
|
|
47977
|
-
);
|
|
47978
|
-
server.registerTool(
|
|
47979
|
-
"quoroom_station_create_crypto",
|
|
47980
|
-
{
|
|
47981
|
-
title: "Create Station (Crypto)",
|
|
47982
|
-
description: "Pay for a new station with USDC or USDT from the room wallet on any supported chain. Sends stablecoin to the Quoroom treasury and provisions the station automatically. Requires the room to have a wallet with sufficient balance. Crypto prices are 1.5x Stripe prices (micro $7.50, small $22.50, medium $60, large $150). Supported chains: base, ethereum, arbitrum, optimism, polygon. Tokens: usdc, usdt. RESPONSE STYLE: Confirm briefly in 1 sentence.",
|
|
47983
|
-
inputSchema: {
|
|
47984
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
47985
|
-
name: external_exports.string().min(1).max(100).describe('Station name (e.g., "web-server", "scraper-01")'),
|
|
47986
|
-
tier: external_exports.enum(["micro", "small", "medium", "large"]).describe(
|
|
47987
|
-
"Station tier: micro ($7.50/mo crypto), small ($22.50/mo), medium ($60/mo), large ($150/mo)"
|
|
47988
|
-
),
|
|
47989
|
-
encryptionKey: external_exports.string().min(1).describe("Wallet encryption key for sending stablecoin"),
|
|
47990
|
-
chain: external_exports.enum(["base", "ethereum", "arbitrum", "optimism", "polygon"]).optional().describe("Chain to pay on (default: base)"),
|
|
47991
|
-
token: external_exports.enum(["usdc", "usdt"]).optional().describe("Token to pay with (default: usdc)")
|
|
47992
|
-
}
|
|
47993
|
-
},
|
|
47994
|
-
async ({ roomId, name, tier, encryptionKey, chain, token }) => {
|
|
47995
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
47996
|
-
const selectedChain = chain ?? "base";
|
|
47997
|
-
const selectedToken = token ?? "usdc";
|
|
47998
|
-
const chainConfig2 = CHAIN_CONFIGS[selectedChain];
|
|
47999
|
-
if (!chainConfig2) {
|
|
48000
|
-
return { content: [{ type: "text", text: `Unsupported chain: ${selectedChain}` }], isError: true };
|
|
48001
|
-
}
|
|
48002
|
-
const tokenConfig = chainConfig2.tokens[selectedToken];
|
|
48003
|
-
if (!tokenConfig) {
|
|
48004
|
-
return { content: [{ type: "text", text: `Token ${selectedToken} not available on ${selectedChain}` }], isError: true };
|
|
48005
|
-
}
|
|
48006
|
-
await bootstrapRoomToken(roomId);
|
|
48007
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
48008
|
-
const pricing = await getCloudCryptoPrices(cloudRoomId);
|
|
48009
|
-
if (!pricing) {
|
|
48010
|
-
return { content: [{ type: "text", text: "Crypto payments are not available." }], isError: true };
|
|
48011
|
-
}
|
|
48012
|
-
const tierInfo = pricing.tiers.find((t) => t.tier === tier);
|
|
48013
|
-
if (!tierInfo) {
|
|
48014
|
-
return { content: [{ type: "text", text: `Unknown tier: ${tier}` }], isError: true };
|
|
48015
|
-
}
|
|
48016
|
-
const db2 = getMcpDatabase();
|
|
48017
|
-
let txHash;
|
|
48018
|
-
let auditSuffix = "";
|
|
48019
|
-
try {
|
|
48020
|
-
txHash = await sendToken(
|
|
48021
|
-
db2,
|
|
48022
|
-
roomId,
|
|
48023
|
-
pricing.treasuryAddress,
|
|
48024
|
-
tierInfo.cryptoPrice.toString(),
|
|
48025
|
-
encryptionKey,
|
|
48026
|
-
selectedChain,
|
|
48027
|
-
tokenConfig.address,
|
|
48028
|
-
tokenConfig.decimals
|
|
48029
|
-
);
|
|
48030
|
-
const audit = recordPaymentAudit(
|
|
48031
|
-
db2,
|
|
48032
|
-
roomId,
|
|
48033
|
-
`Station crypto payment: create "${name}" (${tier}), paid ${tierInfo.cryptoPrice} ${selectedToken.toUpperCase()} on ${selectedChain} to ${pricing.treasuryAddress}, tx: ${txHash}`
|
|
48034
|
-
);
|
|
48035
|
-
auditSuffix = formatPaymentAuditSuffix(audit);
|
|
48036
|
-
} catch (e) {
|
|
48037
|
-
return {
|
|
48038
|
-
content: [{ type: "text", text: `Token transfer failed: ${e.message}` }],
|
|
48039
|
-
isError: true
|
|
48040
|
-
};
|
|
48041
|
-
}
|
|
48042
|
-
const result = await cryptoCheckoutStation(cloudRoomId, tier, name, txHash, selectedChain);
|
|
48043
|
-
if (!result.ok) {
|
|
48044
|
-
return {
|
|
48045
|
-
content: [{
|
|
48046
|
-
type: "text",
|
|
48047
|
-
text: `Payment sent (tx: ${txHash}) but provisioning failed: ${result.error}. Contact support with this tx hash.${auditSuffix}`
|
|
48048
|
-
}],
|
|
48049
|
-
isError: true
|
|
48050
|
-
};
|
|
48051
|
-
}
|
|
48052
|
-
return {
|
|
48053
|
-
content: [{
|
|
48054
|
-
type: "text",
|
|
48055
|
-
text: `Station "${name}" (${tier}) provisioned via crypto. Paid ${tierInfo.cryptoPrice} ${selectedToken.toUpperCase()} on ${selectedChain}, tx: ${txHash}, expires: ${result.currentPeriodEnd}${auditSuffix}`
|
|
48056
|
-
}]
|
|
48057
|
-
};
|
|
48058
|
-
}
|
|
48059
|
-
);
|
|
48060
|
-
server.registerTool(
|
|
48061
|
-
"quoroom_station_renew_crypto",
|
|
48062
|
-
{
|
|
48063
|
-
title: "Renew Station (Crypto)",
|
|
48064
|
-
description: "Renew a crypto-paid station subscription by sending USDC or USDT on any supported chain. Only works for stations originally paid with crypto. Extends the station by 30 days. Supported chains: base, ethereum, arbitrum, optimism, polygon. Tokens: usdc, usdt. RESPONSE STYLE: Confirm briefly in 1 sentence.",
|
|
48065
|
-
inputSchema: {
|
|
48066
|
-
roomId: external_exports.number().describe("The room ID"),
|
|
48067
|
-
id: external_exports.number().describe("The station subscription ID to renew"),
|
|
48068
|
-
encryptionKey: external_exports.string().min(1).describe("Wallet encryption key for sending stablecoin"),
|
|
48069
|
-
chain: external_exports.enum(["base", "ethereum", "arbitrum", "optimism", "polygon"]).optional().describe("Chain to pay on (default: base)"),
|
|
48070
|
-
token: external_exports.enum(["usdc", "usdt"]).optional().describe("Token to pay with (default: usdc)")
|
|
48071
|
-
}
|
|
48072
|
-
},
|
|
48073
|
-
async ({ roomId, id, encryptionKey, chain, token }) => {
|
|
48074
|
-
if (!isCloudMode()) return CLOUD_ONLY;
|
|
48075
|
-
const selectedChain = chain ?? "base";
|
|
48076
|
-
const selectedToken = token ?? "usdc";
|
|
48077
|
-
const chainConfig2 = CHAIN_CONFIGS[selectedChain];
|
|
48078
|
-
if (!chainConfig2) {
|
|
48079
|
-
return { content: [{ type: "text", text: `Unsupported chain: ${selectedChain}` }], isError: true };
|
|
48080
|
-
}
|
|
48081
|
-
const tokenConfig = chainConfig2.tokens[selectedToken];
|
|
48082
|
-
if (!tokenConfig) {
|
|
48083
|
-
return { content: [{ type: "text", text: `Token ${selectedToken} not available on ${selectedChain}` }], isError: true };
|
|
48084
|
-
}
|
|
48085
|
-
await bootstrapRoomToken(roomId);
|
|
48086
|
-
const cloudRoomId = getRoomCloudId(roomId);
|
|
48087
|
-
const stations = await listCloudStations(cloudRoomId);
|
|
48088
|
-
const station = stations.find((s) => s.id === id);
|
|
48089
|
-
if (!station) {
|
|
48090
|
-
return { content: [{ type: "text", text: `Station ${id} not found.` }], isError: true };
|
|
48091
|
-
}
|
|
48092
|
-
const pricing = await getCloudCryptoPrices(cloudRoomId);
|
|
48093
|
-
if (!pricing) {
|
|
48094
|
-
return { content: [{ type: "text", text: "Crypto payments are not available." }], isError: true };
|
|
48095
|
-
}
|
|
48096
|
-
const tierInfo = pricing.tiers.find((t) => t.tier === station.tier);
|
|
48097
|
-
if (!tierInfo) {
|
|
48098
|
-
return { content: [{ type: "text", text: `Unknown tier: ${station.tier}` }], isError: true };
|
|
48099
|
-
}
|
|
48100
|
-
const db2 = getMcpDatabase();
|
|
48101
|
-
let txHash;
|
|
48102
|
-
let auditSuffix = "";
|
|
48103
|
-
try {
|
|
48104
|
-
txHash = await sendToken(
|
|
48105
|
-
db2,
|
|
48106
|
-
roomId,
|
|
48107
|
-
pricing.treasuryAddress,
|
|
48108
|
-
tierInfo.cryptoPrice.toString(),
|
|
48109
|
-
encryptionKey,
|
|
48110
|
-
selectedChain,
|
|
48111
|
-
tokenConfig.address,
|
|
48112
|
-
tokenConfig.decimals
|
|
48113
|
-
);
|
|
48114
|
-
const audit = recordPaymentAudit(
|
|
48115
|
-
db2,
|
|
48116
|
-
roomId,
|
|
48117
|
-
`Station crypto payment: renew #${id} (${station.tier}), paid ${tierInfo.cryptoPrice} ${selectedToken.toUpperCase()} on ${selectedChain} to ${pricing.treasuryAddress}, tx: ${txHash}`
|
|
48118
|
-
);
|
|
48119
|
-
auditSuffix = formatPaymentAuditSuffix(audit);
|
|
48120
|
-
} catch (e) {
|
|
48121
|
-
return {
|
|
48122
|
-
content: [{ type: "text", text: `Token transfer failed: ${e.message}` }],
|
|
48123
|
-
isError: true
|
|
48124
|
-
};
|
|
48125
|
-
}
|
|
48126
|
-
const result = await cryptoRenewStation(cloudRoomId, id, txHash, selectedChain);
|
|
48127
|
-
if (!result.ok) {
|
|
48128
|
-
return {
|
|
48129
|
-
content: [{
|
|
48130
|
-
type: "text",
|
|
48131
|
-
text: `Payment sent (tx: ${txHash}) but renewal failed: ${result.error}. Contact support with this tx hash.${auditSuffix}`
|
|
48132
|
-
}],
|
|
48133
|
-
isError: true
|
|
48134
|
-
};
|
|
48135
|
-
}
|
|
48136
|
-
return {
|
|
48137
|
-
content: [{
|
|
48138
|
-
type: "text",
|
|
48139
|
-
text: `Station ${id} renewed. Paid ${tierInfo.cryptoPrice} ${selectedToken.toUpperCase()} on ${selectedChain}, tx: ${txHash}, new expiry: ${result.currentPeriodEnd}${auditSuffix}`
|
|
48140
|
-
}]
|
|
48141
|
-
};
|
|
48142
|
-
}
|
|
48143
|
-
);
|
|
48144
|
-
}
|
|
48145
|
-
|
|
48146
47116
|
// src/mcp/tools/identity.ts
|
|
48147
47117
|
init_db();
|
|
48148
47118
|
|
|
@@ -48622,7 +47592,7 @@ function registerResourceTools(server) {
|
|
|
48622
47592
|
"quoroom_resources_get",
|
|
48623
47593
|
{
|
|
48624
47594
|
title: "Get Local Resources",
|
|
48625
|
-
description: "Get current local machine resource usage: CPU load and RAM usage. Use this to decide if the room needs
|
|
47595
|
+
description: "Get current local machine resource usage: CPU load and RAM usage. Use this to decide if the room needs additional swarm runtime capacity. If CPU load > number of CPUs or RAM used > 85%, consider scaling swarm infrastructure.",
|
|
48626
47596
|
inputSchema: {}
|
|
48627
47597
|
},
|
|
48628
47598
|
async () => {
|
|
@@ -48643,7 +47613,7 @@ function registerResourceTools(server) {
|
|
|
48643
47613
|
}
|
|
48644
47614
|
const loadRatio = load1 / cpuCount;
|
|
48645
47615
|
const highLoad = loadRatio > 0.8 || memUsedPct > 85;
|
|
48646
|
-
const summary = highLoad ? `HIGH LOAD \u2014 CPU ${Math.round(loadRatio * 100)}% of capacity, RAM ${memUsedPct}% used. Consider
|
|
47616
|
+
const summary = highLoad ? `HIGH LOAD \u2014 CPU ${Math.round(loadRatio * 100)}% of capacity, RAM ${memUsedPct}% used. Consider scaling swarm runtime if funds allow.` : `Normal load \u2014 CPU ${Math.round(loadRatio * 100)}% of capacity, RAM ${memUsedPct}% used.`;
|
|
48647
47617
|
return {
|
|
48648
47618
|
content: [{
|
|
48649
47619
|
type: "text",
|
|
@@ -48675,7 +47645,7 @@ function registerResourceTools(server) {
|
|
|
48675
47645
|
// src/mcp/tools/invite.ts
|
|
48676
47646
|
init_db();
|
|
48677
47647
|
init_db_queries();
|
|
48678
|
-
async function
|
|
47648
|
+
async function bootstrapRoomToken(roomId) {
|
|
48679
47649
|
const db2 = getMcpDatabase();
|
|
48680
47650
|
const room = getRoom(db2, roomId);
|
|
48681
47651
|
if (!room) return;
|
|
@@ -48701,7 +47671,7 @@ function registerInviteTools(server) {
|
|
|
48701
47671
|
}
|
|
48702
47672
|
},
|
|
48703
47673
|
async ({ roomId, maxUses, expiresInDays }) => {
|
|
48704
|
-
await
|
|
47674
|
+
await bootstrapRoomToken(roomId);
|
|
48705
47675
|
const cloudRoomId = getRoomCloudId(roomId);
|
|
48706
47676
|
const result = await createCloudInvite(cloudRoomId, { maxUses, expiresInDays });
|
|
48707
47677
|
if (!result) {
|
|
@@ -48730,7 +47700,7 @@ Share this with the keeper or potential collaborators. Rooms created through thi
|
|
|
48730
47700
|
}
|
|
48731
47701
|
},
|
|
48732
47702
|
async ({ roomId }) => {
|
|
48733
|
-
await
|
|
47703
|
+
await bootstrapRoomToken(roomId);
|
|
48734
47704
|
const cloudRoomId = getRoomCloudId(roomId);
|
|
48735
47705
|
const invites = await listCloudInvites(cloudRoomId);
|
|
48736
47706
|
if (invites.length === 0) {
|
|
@@ -48751,13 +47721,13 @@ Share this with the keeper or potential collaborators. Rooms created through thi
|
|
|
48751
47721
|
"quoroom_invite_network",
|
|
48752
47722
|
{
|
|
48753
47723
|
title: "View Network",
|
|
48754
|
-
description: "See rooms in your network \u2014 rooms created through your invite links. Public rooms show full data (name, goal, workers,
|
|
47724
|
+
description: "See rooms in your network \u2014 rooms created through your invite links. Public rooms show full data (name, goal, workers, earnings). Private rooms show only that they exist.",
|
|
48755
47725
|
inputSchema: {
|
|
48756
47726
|
roomId: external_exports.number().describe("The room ID")
|
|
48757
47727
|
}
|
|
48758
47728
|
},
|
|
48759
47729
|
async ({ roomId }) => {
|
|
48760
|
-
await
|
|
47730
|
+
await bootstrapRoomToken(roomId);
|
|
48761
47731
|
const cloudRoomId = getRoomCloudId(roomId);
|
|
48762
47732
|
const rooms = await fetchReferredRooms(cloudRoomId);
|
|
48763
47733
|
if (rooms.length === 0) {
|
|
@@ -49086,7 +48056,7 @@ init_db();
|
|
|
49086
48056
|
async function main() {
|
|
49087
48057
|
const server = new McpServer({
|
|
49088
48058
|
name: "quoroom",
|
|
49089
|
-
version: true ? "0.1.
|
|
48059
|
+
version: true ? "0.1.41" : "0.0.0"
|
|
49090
48060
|
});
|
|
49091
48061
|
registerMemoryTools(server);
|
|
49092
48062
|
registerSchedulerTools(server);
|
|
@@ -49098,7 +48068,6 @@ async function main() {
|
|
|
49098
48068
|
registerSelfModTools(server);
|
|
49099
48069
|
registerSkillTools(server);
|
|
49100
48070
|
registerWalletTools(server);
|
|
49101
|
-
registerStationTools(server);
|
|
49102
48071
|
registerIdentityTools(server);
|
|
49103
48072
|
registerInboxTools(server);
|
|
49104
48073
|
registerCredentialTools(server);
|