quoroom 0.1.19 → 0.1.21
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/out/mcp/api-server.js +582 -216
- package/out/mcp/cli.js +877 -384
- package/out/mcp/server.js +23 -23
- package/package.json +1 -1
package/out/mcp/api-server.js
CHANGED
|
@@ -6713,12 +6713,12 @@ var init_request = __esm({
|
|
|
6713
6713
|
init_base();
|
|
6714
6714
|
init_utils4();
|
|
6715
6715
|
HttpRequestError = class extends BaseError {
|
|
6716
|
-
constructor({ body, cause, details, headers, status, url }) {
|
|
6716
|
+
constructor({ body, cause, details, headers, status: status2, url }) {
|
|
6717
6717
|
super("HTTP request failed.", {
|
|
6718
6718
|
cause,
|
|
6719
6719
|
details,
|
|
6720
6720
|
metaMessages: [
|
|
6721
|
-
|
|
6721
|
+
status2 && `Status: ${status2}`,
|
|
6722
6722
|
`URL: ${getUrl(url)}`,
|
|
6723
6723
|
body && `Request body: ${stringify(body)}`
|
|
6724
6724
|
].filter(Boolean),
|
|
@@ -6750,7 +6750,7 @@ var init_request = __esm({
|
|
|
6750
6750
|
});
|
|
6751
6751
|
this.body = body;
|
|
6752
6752
|
this.headers = headers;
|
|
6753
|
-
this.status =
|
|
6753
|
+
this.status = status2;
|
|
6754
6754
|
this.url = url;
|
|
6755
6755
|
}
|
|
6756
6756
|
};
|
|
@@ -9914,7 +9914,7 @@ var require_package = __commonJS({
|
|
|
9914
9914
|
"package.json"(exports2, module2) {
|
|
9915
9915
|
module2.exports = {
|
|
9916
9916
|
name: "quoroom",
|
|
9917
|
-
version: "0.1.
|
|
9917
|
+
version: "0.1.21",
|
|
9918
9918
|
description: "Autonomous AI agent collective engine \u2014 Queen, Workers, Quorum",
|
|
9919
9919
|
main: "./out/mcp/server.js",
|
|
9920
9920
|
bin: {
|
|
@@ -10799,7 +10799,7 @@ var require_scheduled_task = __commonJS({
|
|
|
10799
10799
|
var require_background_scheduled_task = __commonJS({
|
|
10800
10800
|
"node_modules/node-cron/src/background-scheduled-task/index.js"(exports2, module2) {
|
|
10801
10801
|
var EventEmitter = require("events");
|
|
10802
|
-
var
|
|
10802
|
+
var path5 = require("path");
|
|
10803
10803
|
var { fork } = require("child_process");
|
|
10804
10804
|
var uuid = (init_esm_node(), __toCommonJS(esm_node_exports));
|
|
10805
10805
|
var daemonPath = `${__dirname}/daemon.js`;
|
|
@@ -10834,7 +10834,7 @@ var require_background_scheduled_task = __commonJS({
|
|
|
10834
10834
|
options.scheduled = true;
|
|
10835
10835
|
this.forkProcess.send({
|
|
10836
10836
|
type: "register",
|
|
10837
|
-
path:
|
|
10837
|
+
path: path5.resolve(this.taskPath),
|
|
10838
10838
|
cron: this.cronExpression,
|
|
10839
10839
|
options
|
|
10840
10840
|
});
|
|
@@ -10921,35 +10921,35 @@ __export(index_exports, {
|
|
|
10921
10921
|
startServer: () => startServer
|
|
10922
10922
|
});
|
|
10923
10923
|
module.exports = __toCommonJS(index_exports);
|
|
10924
|
-
var
|
|
10925
|
-
var
|
|
10924
|
+
var import_node_http2 = __toESM(require("node:http"));
|
|
10925
|
+
var import_node_https3 = __toESM(require("node:https"));
|
|
10926
10926
|
var import_node_url = require("node:url");
|
|
10927
|
-
var
|
|
10928
|
-
var
|
|
10929
|
-
var
|
|
10927
|
+
var import_node_fs5 = __toESM(require("node:fs"));
|
|
10928
|
+
var import_node_path6 = __toESM(require("node:path"));
|
|
10929
|
+
var import_node_os5 = require("node:os");
|
|
10930
10930
|
var import_node_child_process7 = require("node:child_process");
|
|
10931
10931
|
|
|
10932
10932
|
// src/server/router.ts
|
|
10933
10933
|
var Router = class {
|
|
10934
10934
|
routes = [];
|
|
10935
|
-
get(
|
|
10936
|
-
this.add("GET",
|
|
10935
|
+
get(path5, handler) {
|
|
10936
|
+
this.add("GET", path5, handler);
|
|
10937
10937
|
}
|
|
10938
|
-
post(
|
|
10939
|
-
this.add("POST",
|
|
10938
|
+
post(path5, handler) {
|
|
10939
|
+
this.add("POST", path5, handler);
|
|
10940
10940
|
}
|
|
10941
|
-
patch(
|
|
10942
|
-
this.add("PATCH",
|
|
10941
|
+
patch(path5, handler) {
|
|
10942
|
+
this.add("PATCH", path5, handler);
|
|
10943
10943
|
}
|
|
10944
|
-
put(
|
|
10945
|
-
this.add("PUT",
|
|
10944
|
+
put(path5, handler) {
|
|
10945
|
+
this.add("PUT", path5, handler);
|
|
10946
10946
|
}
|
|
10947
|
-
delete(
|
|
10948
|
-
this.add("DELETE",
|
|
10947
|
+
delete(path5, handler) {
|
|
10948
|
+
this.add("DELETE", path5, handler);
|
|
10949
10949
|
}
|
|
10950
|
-
add(method,
|
|
10950
|
+
add(method, path5, handler) {
|
|
10951
10951
|
const paramNames = [];
|
|
10952
|
-
const patternStr =
|
|
10952
|
+
const patternStr = path5.replace(/:(\w+)/g, (_, name) => {
|
|
10953
10953
|
paramNames.push(name);
|
|
10954
10954
|
return "([^/]+)";
|
|
10955
10955
|
});
|
|
@@ -11266,39 +11266,39 @@ var CHAIN_CONFIGS = {
|
|
|
11266
11266
|
var SUPPORTED_CHAINS = ["base", "ethereum", "arbitrum", "optimism", "polygon"];
|
|
11267
11267
|
var SUPPORTED_TOKENS = ["usdc", "usdt"];
|
|
11268
11268
|
var QUEEN_DEFAULTS_BY_PLAN = {
|
|
11269
|
-
none: { queenCycleGapMs:
|
|
11270
|
-
//
|
|
11271
|
-
pro: { queenCycleGapMs:
|
|
11272
|
-
//
|
|
11273
|
-
max: { queenCycleGapMs:
|
|
11274
|
-
//
|
|
11275
|
-
api: { queenCycleGapMs:
|
|
11276
|
-
//
|
|
11269
|
+
none: { queenCycleGapMs: 10 * 60 * 1e3, queenMaxTurns: 30 },
|
|
11270
|
+
// 10 min gap, 30 turns
|
|
11271
|
+
pro: { queenCycleGapMs: 5 * 60 * 1e3, queenMaxTurns: 30 },
|
|
11272
|
+
// 5 min gap, 30 turns
|
|
11273
|
+
max: { queenCycleGapMs: 30 * 1e3, queenMaxTurns: 30 },
|
|
11274
|
+
// 30s gap, 30 turns
|
|
11275
|
+
api: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns: 30 }
|
|
11276
|
+
// 2 min gap, 30 turns
|
|
11277
11277
|
};
|
|
11278
11278
|
var CHATGPT_DEFAULTS_BY_PLAN = {
|
|
11279
|
-
none: { queenCycleGapMs:
|
|
11280
|
-
//
|
|
11281
|
-
plus: { queenCycleGapMs:
|
|
11282
|
-
//
|
|
11283
|
-
pro: { queenCycleGapMs:
|
|
11284
|
-
//
|
|
11285
|
-
api: { queenCycleGapMs:
|
|
11286
|
-
//
|
|
11279
|
+
none: { queenCycleGapMs: 10 * 60 * 1e3, queenMaxTurns: 30 },
|
|
11280
|
+
// 10 min gap, 30 turns
|
|
11281
|
+
plus: { queenCycleGapMs: 5 * 60 * 1e3, queenMaxTurns: 30 },
|
|
11282
|
+
// 5 min gap, 30 turns
|
|
11283
|
+
pro: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns: 30 },
|
|
11284
|
+
// 2 min gap, 30 turns
|
|
11285
|
+
api: { queenCycleGapMs: 2 * 60 * 1e3, queenMaxTurns: 30 }
|
|
11286
|
+
// 2 min gap, 30 turns
|
|
11287
11287
|
};
|
|
11288
11288
|
var WORKER_ROLE_PRESETS = {
|
|
11289
11289
|
guardian: {
|
|
11290
|
-
cycleGapMs:
|
|
11291
|
-
maxTurns:
|
|
11290
|
+
cycleGapMs: 3e4,
|
|
11291
|
+
maxTurns: 15,
|
|
11292
11292
|
systemPromptPrefix: "Monitor and observe. Do not spawn workers or make purchases. Focus on detecting anomalies in tasks, stations, and worker activity."
|
|
11293
11293
|
},
|
|
11294
11294
|
analyst: {
|
|
11295
|
-
cycleGapMs:
|
|
11296
|
-
maxTurns:
|
|
11295
|
+
cycleGapMs: 12e4,
|
|
11296
|
+
maxTurns: 30,
|
|
11297
11297
|
systemPromptPrefix: "Perform deep analysis. Work to completion on a task, then pause. Prefer depth over frequency."
|
|
11298
11298
|
},
|
|
11299
11299
|
writer: {
|
|
11300
|
-
cycleGapMs:
|
|
11301
|
-
maxTurns:
|
|
11300
|
+
cycleGapMs: 12e4,
|
|
11301
|
+
maxTurns: 30,
|
|
11302
11302
|
systemPromptPrefix: "Produce high-quality written output. Minimize interruptions between drafting sessions."
|
|
11303
11303
|
}
|
|
11304
11304
|
};
|
|
@@ -11566,16 +11566,16 @@ function getTaskByWebhookToken(db2, token) {
|
|
|
11566
11566
|
const row = db2.prepare("SELECT * FROM tasks WHERE webhook_token = ?").get(token);
|
|
11567
11567
|
return row ? mapTaskRow(row) : null;
|
|
11568
11568
|
}
|
|
11569
|
-
function listTasks(db2, roomId,
|
|
11570
|
-
if (roomId != null &&
|
|
11571
|
-
const rows2 = db2.prepare("SELECT * FROM tasks WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId,
|
|
11569
|
+
function listTasks(db2, roomId, status2) {
|
|
11570
|
+
if (roomId != null && status2) {
|
|
11571
|
+
const rows2 = db2.prepare("SELECT * FROM tasks WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId, status2);
|
|
11572
11572
|
return rows2.map(mapTaskRow);
|
|
11573
11573
|
}
|
|
11574
11574
|
if (roomId != null) {
|
|
11575
11575
|
const rows2 = db2.prepare("SELECT * FROM tasks WHERE room_id = ? ORDER BY created_at DESC").all(roomId);
|
|
11576
11576
|
return rows2.map(mapTaskRow);
|
|
11577
11577
|
}
|
|
11578
|
-
const rows =
|
|
11578
|
+
const rows = status2 ? db2.prepare("SELECT * FROM tasks WHERE status = ? ORDER BY created_at DESC").all(status2) : db2.prepare("SELECT * FROM tasks ORDER BY created_at DESC").all();
|
|
11579
11579
|
return rows.map(mapTaskRow);
|
|
11580
11580
|
}
|
|
11581
11581
|
function updateTask(db2, id, updates) {
|
|
@@ -11630,24 +11630,24 @@ function pauseTask(db2, id) {
|
|
|
11630
11630
|
function resumeTask(db2, id) {
|
|
11631
11631
|
updateTask(db2, id, { status: "active" });
|
|
11632
11632
|
}
|
|
11633
|
-
function createWatch(db2,
|
|
11634
|
-
const result = db2.prepare("INSERT INTO watches (path, description, action_prompt, room_id) VALUES (?, ?, ?, ?)").run(
|
|
11633
|
+
function createWatch(db2, path5, description, actionPrompt, roomId) {
|
|
11634
|
+
const result = db2.prepare("INSERT INTO watches (path, description, action_prompt, room_id) VALUES (?, ?, ?, ?)").run(path5, description ?? null, actionPrompt ?? null, roomId ?? null);
|
|
11635
11635
|
return getWatch(db2, result.lastInsertRowid);
|
|
11636
11636
|
}
|
|
11637
11637
|
function getWatch(db2, id) {
|
|
11638
11638
|
const row = db2.prepare("SELECT * FROM watches WHERE id = ?").get(id);
|
|
11639
11639
|
return row ? mapWatchRow(row) : null;
|
|
11640
11640
|
}
|
|
11641
|
-
function listWatches(db2, roomId,
|
|
11642
|
-
if (roomId != null &&
|
|
11643
|
-
const rows2 = db2.prepare("SELECT * FROM watches WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId,
|
|
11641
|
+
function listWatches(db2, roomId, status2) {
|
|
11642
|
+
if (roomId != null && status2) {
|
|
11643
|
+
const rows2 = db2.prepare("SELECT * FROM watches WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId, status2);
|
|
11644
11644
|
return rows2.map(mapWatchRow);
|
|
11645
11645
|
}
|
|
11646
11646
|
if (roomId != null) {
|
|
11647
11647
|
const rows2 = db2.prepare("SELECT * FROM watches WHERE room_id = ? ORDER BY created_at DESC").all(roomId);
|
|
11648
11648
|
return rows2.map(mapWatchRow);
|
|
11649
11649
|
}
|
|
11650
|
-
const rows =
|
|
11650
|
+
const rows = status2 ? db2.prepare("SELECT * FROM watches WHERE status = ? ORDER BY created_at DESC").all(status2) : db2.prepare("SELECT * FROM watches ORDER BY created_at DESC").all();
|
|
11651
11651
|
return rows.map(mapWatchRow);
|
|
11652
11652
|
}
|
|
11653
11653
|
function deleteWatch(db2, id) {
|
|
@@ -11691,12 +11691,12 @@ function getTaskRun(db2, id) {
|
|
|
11691
11691
|
function completeTaskRun(db2, id, result, resultFile, errorMessage) {
|
|
11692
11692
|
const run = getTaskRun(db2, id);
|
|
11693
11693
|
if (!run) return;
|
|
11694
|
-
const
|
|
11694
|
+
const status2 = errorMessage ? "failed" : "completed";
|
|
11695
11695
|
const durationMs = Date.now() - new Date(run.startedAt).getTime();
|
|
11696
11696
|
db2.prepare(
|
|
11697
11697
|
`UPDATE task_runs SET finished_at = datetime('now','localtime'), status = ?, result = ?,
|
|
11698
11698
|
result_file = ?, error_message = ?, duration_ms = ? WHERE id = ?`
|
|
11699
|
-
).run(
|
|
11699
|
+
).run(status2, result, resultFile ?? null, errorMessage ?? null, durationMs, id);
|
|
11700
11700
|
const task = getTask(db2, run.taskId);
|
|
11701
11701
|
const newErrorCount = errorMessage ? (task?.errorCount ?? 0) + 1 : 0;
|
|
11702
11702
|
db2.prepare(
|
|
@@ -11920,8 +11920,8 @@ function ensureTaskMemoryEntity(db2, taskId) {
|
|
|
11920
11920
|
function storeTaskResultInMemory(db2, taskId, result, success) {
|
|
11921
11921
|
const entityId = ensureTaskMemoryEntity(db2, taskId);
|
|
11922
11922
|
const truncated = result.length > MAX_MEMORY_LENGTH ? result.substring(0, MAX_MEMORY_LENGTH) + "\n[...truncated]" : result;
|
|
11923
|
-
const
|
|
11924
|
-
const content = `[${
|
|
11923
|
+
const status2 = success ? "SUCCESS" : "FAILED";
|
|
11924
|
+
const content = `[${status2}] ${truncated}`;
|
|
11925
11925
|
addObservation(db2, entityId, content, "task_runner");
|
|
11926
11926
|
const countRow = db2.prepare("SELECT COUNT(*) as cnt FROM observations WHERE entity_id = ?").get(entityId);
|
|
11927
11927
|
if (countRow.cnt > MAX_OBSERVATIONS_PER_ENTITY) {
|
|
@@ -12136,9 +12136,9 @@ function getRoomByWebhookToken(db2, token) {
|
|
|
12136
12136
|
const row = db2.prepare("SELECT * FROM rooms WHERE webhook_token = ?").get(token);
|
|
12137
12137
|
return row ? mapRoomRow(row) : null;
|
|
12138
12138
|
}
|
|
12139
|
-
function listRooms(db2,
|
|
12140
|
-
if (
|
|
12141
|
-
const rows2 = db2.prepare("SELECT * FROM rooms WHERE status = ? ORDER BY created_at DESC").all(
|
|
12139
|
+
function listRooms(db2, status2) {
|
|
12140
|
+
if (status2) {
|
|
12141
|
+
const rows2 = db2.prepare("SELECT * FROM rooms WHERE status = ? ORDER BY created_at DESC").all(status2);
|
|
12142
12142
|
return rows2.map(mapRoomRow);
|
|
12143
12143
|
}
|
|
12144
12144
|
const rows = db2.prepare("SELECT * FROM rooms ORDER BY created_at DESC").all();
|
|
@@ -12233,16 +12233,16 @@ function getDecision(db2, id) {
|
|
|
12233
12233
|
const row = db2.prepare("SELECT * FROM quorum_decisions WHERE id = ?").get(id);
|
|
12234
12234
|
return row ? mapDecisionRow(row) : null;
|
|
12235
12235
|
}
|
|
12236
|
-
function listDecisions(db2, roomId,
|
|
12237
|
-
if (
|
|
12238
|
-
const rows2 = db2.prepare("SELECT * FROM quorum_decisions WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId,
|
|
12236
|
+
function listDecisions(db2, roomId, status2) {
|
|
12237
|
+
if (status2) {
|
|
12238
|
+
const rows2 = db2.prepare("SELECT * FROM quorum_decisions WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId, status2);
|
|
12239
12239
|
return rows2.map(mapDecisionRow);
|
|
12240
12240
|
}
|
|
12241
12241
|
const rows = db2.prepare("SELECT * FROM quorum_decisions WHERE room_id = ? ORDER BY created_at DESC").all(roomId);
|
|
12242
12242
|
return rows.map(mapDecisionRow);
|
|
12243
12243
|
}
|
|
12244
|
-
function resolveDecision(db2, id,
|
|
12245
|
-
db2.prepare("UPDATE quorum_decisions SET status = ?, result = ?, resolved_at = datetime('now','localtime') WHERE id = ?").run(
|
|
12244
|
+
function resolveDecision(db2, id, status2, result) {
|
|
12245
|
+
db2.prepare("UPDATE quorum_decisions SET status = ?, result = ?, resolved_at = datetime('now','localtime') WHERE id = ?").run(status2, result ?? null, id);
|
|
12246
12246
|
}
|
|
12247
12247
|
function setKeeperVote(db2, decisionId, vote2) {
|
|
12248
12248
|
db2.prepare("UPDATE quorum_decisions SET keeper_vote = ? WHERE id = ?").run(vote2, decisionId);
|
|
@@ -12313,9 +12313,9 @@ function getGoal(db2, id) {
|
|
|
12313
12313
|
const row = db2.prepare("SELECT * FROM goals WHERE id = ?").get(id);
|
|
12314
12314
|
return row ? mapGoalRow(row) : null;
|
|
12315
12315
|
}
|
|
12316
|
-
function listGoals(db2, roomId,
|
|
12317
|
-
if (
|
|
12318
|
-
const rows2 = db2.prepare("SELECT * FROM goals WHERE room_id = ? AND status = ? ORDER BY created_at ASC").all(roomId,
|
|
12316
|
+
function listGoals(db2, roomId, status2) {
|
|
12317
|
+
if (status2) {
|
|
12318
|
+
const rows2 = db2.prepare("SELECT * FROM goals WHERE room_id = ? AND status = ? ORDER BY created_at ASC").all(roomId, status2);
|
|
12319
12319
|
return rows2.map(mapGoalRow);
|
|
12320
12320
|
}
|
|
12321
12321
|
const rows = db2.prepare("SELECT * FROM goals WHERE room_id = ? ORDER BY created_at ASC").all(roomId);
|
|
@@ -12532,9 +12532,9 @@ function getPendingEscalations(db2, roomId, toAgentId) {
|
|
|
12532
12532
|
const rows = db2.prepare("SELECT * FROM escalations WHERE room_id = ? AND status = 'pending' ORDER BY created_at ASC").all(roomId);
|
|
12533
12533
|
return rows.map(mapEscalationRow);
|
|
12534
12534
|
}
|
|
12535
|
-
function listEscalations(db2, roomId,
|
|
12536
|
-
if (
|
|
12537
|
-
const rows2 = db2.prepare("SELECT * FROM escalations WHERE room_id = ? AND status = ? ORDER BY created_at ASC").all(roomId,
|
|
12535
|
+
function listEscalations(db2, roomId, status2) {
|
|
12536
|
+
if (status2) {
|
|
12537
|
+
const rows2 = db2.prepare("SELECT * FROM escalations WHERE room_id = ? AND status = ? ORDER BY created_at ASC").all(roomId, status2);
|
|
12538
12538
|
return rows2.map(mapEscalationRow);
|
|
12539
12539
|
}
|
|
12540
12540
|
const rows = db2.prepare("SELECT * FROM escalations WHERE room_id = ? ORDER BY created_at ASC").all(roomId);
|
|
@@ -12543,6 +12543,12 @@ function listEscalations(db2, roomId, status) {
|
|
|
12543
12543
|
function resolveEscalation(db2, id, answer) {
|
|
12544
12544
|
db2.prepare("UPDATE escalations SET answer = ?, status = 'resolved', resolved_at = datetime('now','localtime') WHERE id = ?").run(answer, id);
|
|
12545
12545
|
}
|
|
12546
|
+
function getRecentKeeperAnswers(db2, roomId, fromAgentId, limit = 5) {
|
|
12547
|
+
const rows = db2.prepare(
|
|
12548
|
+
`SELECT * FROM escalations WHERE room_id = ? AND from_agent_id = ? AND status = 'resolved' AND to_agent_id IS NULL ORDER BY resolved_at DESC LIMIT ?`
|
|
12549
|
+
).all(roomId, fromAgentId, limit);
|
|
12550
|
+
return rows.map(mapEscalationRow);
|
|
12551
|
+
}
|
|
12546
12552
|
function mapCredentialRow(row) {
|
|
12547
12553
|
return {
|
|
12548
12554
|
id: row.id,
|
|
@@ -12693,17 +12699,17 @@ function getStation(db2, id) {
|
|
|
12693
12699
|
const row = db2.prepare("SELECT * FROM stations WHERE id = ?").get(id);
|
|
12694
12700
|
return row ? mapStationRow(row) : null;
|
|
12695
12701
|
}
|
|
12696
|
-
function listStations(db2, roomId,
|
|
12697
|
-
if (roomId &&
|
|
12698
|
-
const rows2 = db2.prepare("SELECT * FROM stations WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId,
|
|
12702
|
+
function listStations(db2, roomId, status2) {
|
|
12703
|
+
if (roomId && status2) {
|
|
12704
|
+
const rows2 = db2.prepare("SELECT * FROM stations WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId, status2);
|
|
12699
12705
|
return rows2.map(mapStationRow);
|
|
12700
12706
|
}
|
|
12701
12707
|
if (roomId) {
|
|
12702
12708
|
const rows2 = db2.prepare("SELECT * FROM stations WHERE room_id = ? ORDER BY created_at DESC").all(roomId);
|
|
12703
12709
|
return rows2.map(mapStationRow);
|
|
12704
12710
|
}
|
|
12705
|
-
if (
|
|
12706
|
-
const rows2 = db2.prepare("SELECT * FROM stations WHERE status = ? ORDER BY created_at DESC").all(
|
|
12711
|
+
if (status2) {
|
|
12712
|
+
const rows2 = db2.prepare("SELECT * FROM stations WHERE status = ? ORDER BY created_at DESC").all(status2);
|
|
12707
12713
|
return rows2.map(mapStationRow);
|
|
12708
12714
|
}
|
|
12709
12715
|
const rows = db2.prepare("SELECT * FROM stations ORDER BY created_at DESC").all();
|
|
@@ -12782,9 +12788,9 @@ function getRoomMessage(db2, id) {
|
|
|
12782
12788
|
const row = db2.prepare("SELECT * FROM room_messages WHERE id = ?").get(id);
|
|
12783
12789
|
return row ? mapRoomMessageRow(row) : null;
|
|
12784
12790
|
}
|
|
12785
|
-
function listRoomMessages(db2, roomId,
|
|
12786
|
-
if (
|
|
12787
|
-
const rows2 = db2.prepare("SELECT * FROM room_messages WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId,
|
|
12791
|
+
function listRoomMessages(db2, roomId, status2) {
|
|
12792
|
+
if (status2) {
|
|
12793
|
+
const rows2 = db2.prepare("SELECT * FROM room_messages WHERE room_id = ? AND status = ? ORDER BY created_at DESC").all(roomId, status2);
|
|
12788
12794
|
return rows2.map(mapRoomMessageRow);
|
|
12789
12795
|
}
|
|
12790
12796
|
const rows = db2.prepare("SELECT * FROM room_messages WHERE room_id = ? ORDER BY created_at DESC").all(roomId);
|
|
@@ -12837,11 +12843,11 @@ function getWorkerCycle(db2, id) {
|
|
|
12837
12843
|
function completeWorkerCycle(db2, cycleId, errorMessage, usage) {
|
|
12838
12844
|
const cycle = getWorkerCycle(db2, cycleId);
|
|
12839
12845
|
if (!cycle) return;
|
|
12840
|
-
const
|
|
12846
|
+
const status2 = errorMessage ? "failed" : "completed";
|
|
12841
12847
|
const durationMs = Date.now() - new Date(cycle.startedAt).getTime();
|
|
12842
12848
|
db2.prepare(
|
|
12843
12849
|
"UPDATE worker_cycles SET finished_at = datetime('now','localtime'), status = ?, error_message = ?, duration_ms = ?, input_tokens = ?, output_tokens = ? WHERE id = ?"
|
|
12844
|
-
).run(
|
|
12850
|
+
).run(status2, errorMessage ?? null, durationMs, usage?.inputTokens ?? null, usage?.outputTokens ?? null, cycleId);
|
|
12845
12851
|
}
|
|
12846
12852
|
function listRoomCycles(db2, roomId, limit = 20) {
|
|
12847
12853
|
const safeLimit = clampLimit(limit, 20, 200);
|
|
@@ -14366,8 +14372,8 @@ init_toHex();
|
|
|
14366
14372
|
function createFilterRequestScope(client, { method }) {
|
|
14367
14373
|
const requestMap = {};
|
|
14368
14374
|
if (client.transport.type === "fallback")
|
|
14369
|
-
client.transport.onResponse?.(({ method: method_, response: id, status, transport }) => {
|
|
14370
|
-
if (
|
|
14375
|
+
client.transport.onResponse?.(({ method: method_, response: id, status: status2, transport }) => {
|
|
14376
|
+
if (status2 === "success" && method === method_)
|
|
14371
14377
|
requestMap[id] = transport.request;
|
|
14372
14378
|
});
|
|
14373
14379
|
return (id) => requestMap[id] || client.request;
|
|
@@ -16423,7 +16429,7 @@ async function getCallsStatus(client, parameters) {
|
|
|
16423
16429
|
method: "eth_getTransactionReceipt",
|
|
16424
16430
|
params: [`0x${hash3}`]
|
|
16425
16431
|
}, { dedupe: true }) : void 0));
|
|
16426
|
-
const
|
|
16432
|
+
const status3 = (() => {
|
|
16427
16433
|
if (receipts2.some((r) => r === null))
|
|
16428
16434
|
return 100;
|
|
16429
16435
|
if (receipts2.every((r) => r?.status === "0x1"))
|
|
@@ -16436,7 +16442,7 @@ async function getCallsStatus(client, parameters) {
|
|
|
16436
16442
|
atomic: false,
|
|
16437
16443
|
chainId: hexToNumber2(chainId2),
|
|
16438
16444
|
receipts: receipts2.filter(Boolean),
|
|
16439
|
-
status:
|
|
16445
|
+
status: status3,
|
|
16440
16446
|
version: "2.0.0"
|
|
16441
16447
|
};
|
|
16442
16448
|
}
|
|
@@ -16446,7 +16452,7 @@ async function getCallsStatus(client, parameters) {
|
|
|
16446
16452
|
});
|
|
16447
16453
|
}
|
|
16448
16454
|
const { atomic = false, chainId, receipts, version: version5 = "2.0.0", ...response } = await getStatus(parameters.id);
|
|
16449
|
-
const [
|
|
16455
|
+
const [status2, statusCode] = (() => {
|
|
16450
16456
|
const statusCode2 = response.status;
|
|
16451
16457
|
if (statusCode2 >= 100 && statusCode2 < 200)
|
|
16452
16458
|
return ["pending", statusCode2];
|
|
@@ -16472,7 +16478,7 @@ async function getCallsStatus(client, parameters) {
|
|
|
16472
16478
|
status: receiptStatuses[receipt.status]
|
|
16473
16479
|
})) ?? [],
|
|
16474
16480
|
statusCode,
|
|
16475
|
-
status,
|
|
16481
|
+
status: status2,
|
|
16476
16482
|
version: version5
|
|
16477
16483
|
};
|
|
16478
16484
|
}
|
|
@@ -16482,7 +16488,7 @@ async function waitForCallsStatus(client, parameters) {
|
|
|
16482
16488
|
const {
|
|
16483
16489
|
id,
|
|
16484
16490
|
pollingInterval = client.pollingInterval,
|
|
16485
|
-
status = ({ statusCode }) => statusCode === 200 || statusCode >= 300,
|
|
16491
|
+
status: status2 = ({ statusCode }) => statusCode === 200 || statusCode >= 300,
|
|
16486
16492
|
retryCount = 4,
|
|
16487
16493
|
retryDelay = ({ count }) => ~~(1 << count) * 200,
|
|
16488
16494
|
// exponential backoff
|
|
@@ -16510,7 +16516,7 @@ async function waitForCallsStatus(client, parameters) {
|
|
|
16510
16516
|
retryCount,
|
|
16511
16517
|
delay: retryDelay
|
|
16512
16518
|
});
|
|
16513
|
-
if (!
|
|
16519
|
+
if (!status2(result))
|
|
16514
16520
|
return;
|
|
16515
16521
|
done(() => emit.resolve(result));
|
|
16516
16522
|
} catch (error) {
|
|
@@ -19769,14 +19775,14 @@ async function simulateBlocks(client, parameters) {
|
|
|
19769
19775
|
const data = call2.error?.data ?? call2.returnData;
|
|
19770
19776
|
const gasUsed = BigInt(call2.gasUsed);
|
|
19771
19777
|
const logs = call2.logs?.map((log) => formatLog(log));
|
|
19772
|
-
const
|
|
19773
|
-
const result2 = abi2 &&
|
|
19778
|
+
const status2 = call2.status === "0x1" ? "success" : "failure";
|
|
19779
|
+
const result2 = abi2 && status2 === "success" && data !== "0x" ? decodeFunctionResult({
|
|
19774
19780
|
abi: abi2,
|
|
19775
19781
|
data,
|
|
19776
19782
|
functionName
|
|
19777
19783
|
}) : null;
|
|
19778
19784
|
const error = (() => {
|
|
19779
|
-
if (
|
|
19785
|
+
if (status2 === "success")
|
|
19780
19786
|
return void 0;
|
|
19781
19787
|
let error2;
|
|
19782
19788
|
if (data === "0x")
|
|
@@ -19796,8 +19802,8 @@ async function simulateBlocks(client, parameters) {
|
|
|
19796
19802
|
data,
|
|
19797
19803
|
gasUsed,
|
|
19798
19804
|
logs,
|
|
19799
|
-
status,
|
|
19800
|
-
...
|
|
19805
|
+
status: status2,
|
|
19806
|
+
...status2 === "success" ? {
|
|
19801
19807
|
result: result2
|
|
19802
19808
|
} : {
|
|
19803
19809
|
error
|
|
@@ -21528,12 +21534,12 @@ async function sendCallsSync(client, parameters) {
|
|
|
21528
21534
|
const { chain = client.chain } = parameters;
|
|
21529
21535
|
const timeout = parameters.timeout ?? Math.max((chain?.blockTime ?? 0) * 3, 5e3);
|
|
21530
21536
|
const result = await getAction(client, sendCalls, "sendCalls")(parameters);
|
|
21531
|
-
const
|
|
21537
|
+
const status2 = await getAction(client, waitForCallsStatus, "waitForCallsStatus")({
|
|
21532
21538
|
...parameters,
|
|
21533
21539
|
id: result.id,
|
|
21534
21540
|
timeout
|
|
21535
21541
|
});
|
|
21536
|
-
return
|
|
21542
|
+
return status2;
|
|
21537
21543
|
}
|
|
21538
21544
|
|
|
21539
21545
|
// node_modules/viem/_esm/actions/wallet/sendTransactionSync.js
|
|
@@ -23209,7 +23215,7 @@ Continue working toward the goal.` : options.prompt
|
|
|
23209
23215
|
toolResult = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
23210
23216
|
}
|
|
23211
23217
|
}
|
|
23212
|
-
resultBlocks.push({ type: "tool_result",
|
|
23218
|
+
resultBlocks.push({ type: "tool_result", tool_use_id: block.id, content: toolResult });
|
|
23213
23219
|
}
|
|
23214
23220
|
messages.push({ role: "user", content: resultBlocks });
|
|
23215
23221
|
if (options.onSessionUpdate) {
|
|
@@ -23675,40 +23681,40 @@ function tally(db2, decisionId) {
|
|
|
23675
23681
|
abstainCount++;
|
|
23676
23682
|
}
|
|
23677
23683
|
const activeWeight = yesWeight + noWeight;
|
|
23678
|
-
let
|
|
23684
|
+
let status2;
|
|
23679
23685
|
const threshold = decision.threshold;
|
|
23680
23686
|
if (activeWeight === 0) {
|
|
23681
|
-
|
|
23687
|
+
status2 = "rejected";
|
|
23682
23688
|
} else if (threshold === "unanimous") {
|
|
23683
|
-
|
|
23689
|
+
status2 = noWeight === 0 && yesWeight > 0 ? "approved" : "rejected";
|
|
23684
23690
|
} else if (threshold === "supermajority") {
|
|
23685
|
-
|
|
23691
|
+
status2 = yesWeight >= activeWeight * 2 / 3 ? "approved" : "rejected";
|
|
23686
23692
|
} else {
|
|
23687
23693
|
if (yesWeight > activeWeight / 2) {
|
|
23688
|
-
|
|
23694
|
+
status2 = "approved";
|
|
23689
23695
|
} else if (noWeight > activeWeight / 2) {
|
|
23690
|
-
|
|
23696
|
+
status2 = "rejected";
|
|
23691
23697
|
} else {
|
|
23692
23698
|
if (tieBreakerMode === "queen" && queenWorkerId !== null) {
|
|
23693
23699
|
const queenVote = votes.find((v) => v.workerId === queenWorkerId);
|
|
23694
|
-
|
|
23700
|
+
status2 = queenVote?.vote === "yes" ? "approved" : "rejected";
|
|
23695
23701
|
} else {
|
|
23696
|
-
|
|
23702
|
+
status2 = "rejected";
|
|
23697
23703
|
}
|
|
23698
23704
|
}
|
|
23699
23705
|
}
|
|
23700
23706
|
const yesDisplay = useWeighted ? yesWeight.toFixed(2) : String(yesWeight);
|
|
23701
23707
|
const noDisplay = useWeighted ? noWeight.toFixed(2) : String(noWeight);
|
|
23702
23708
|
const result = `Yes: ${yesDisplay}, No: ${noDisplay}, Abstain: ${abstainCount}` + (useWeighted ? " (weighted)" : "");
|
|
23703
|
-
resolveDecision(db2, decisionId,
|
|
23709
|
+
resolveDecision(db2, decisionId, status2, result);
|
|
23704
23710
|
logRoomActivity(
|
|
23705
23711
|
db2,
|
|
23706
23712
|
decision.roomId,
|
|
23707
23713
|
"decision",
|
|
23708
|
-
`Decision ${
|
|
23714
|
+
`Decision ${status2}: ${decision.proposal} (${result})`
|
|
23709
23715
|
);
|
|
23710
23716
|
creditMissedVotes(db2, votes, voters, room);
|
|
23711
|
-
return
|
|
23717
|
+
return status2;
|
|
23712
23718
|
}
|
|
23713
23719
|
function creditMissedVotes(db2, votes, voters, room) {
|
|
23714
23720
|
if (!room?.config.voterHealth) return;
|
|
@@ -24343,7 +24349,7 @@ var QUEEN_TOOL_DEFINITIONS = [
|
|
|
24343
24349
|
},
|
|
24344
24350
|
queenMaxTurns: {
|
|
24345
24351
|
type: "number",
|
|
24346
|
-
description: "Max tool-call turns per queen cycle (1\
|
|
24352
|
+
description: "Max tool-call turns per queen cycle (1\u201350)"
|
|
24347
24353
|
}
|
|
24348
24354
|
}
|
|
24349
24355
|
}
|
|
@@ -24411,6 +24417,9 @@ async function executeQueenTool(db2, roomId, workerId, toolName, args) {
|
|
|
24411
24417
|
case "quoroom_update_progress": {
|
|
24412
24418
|
const goalId = Number(args.goalId ?? args.goal_id);
|
|
24413
24419
|
if (!goalId || isNaN(goalId)) return { content: "Error: goalId is required for quoroom_update_progress. Provide the numeric goal ID.", isError: true };
|
|
24420
|
+
const goalCheck = getGoal(db2, goalId);
|
|
24421
|
+
if (!goalCheck) return { content: `Error: goal #${goalId} not found.`, isError: true };
|
|
24422
|
+
if (goalCheck.roomId !== roomId) return { content: `Error: goal #${goalId} belongs to another room. Your room's goals are shown in the Active Goals section \u2014 use those goal IDs.`, isError: true };
|
|
24414
24423
|
const observation = String(args.observation ?? args.progress ?? args.message ?? args.text ?? "");
|
|
24415
24424
|
const metricValue = args.metricValue != null ? Number(args.metricValue) : args.metric_value != null ? Number(args.metric_value) : void 0;
|
|
24416
24425
|
updateGoalProgress(db2, goalId, observation, metricValue, workerId);
|
|
@@ -24420,6 +24429,9 @@ async function executeQueenTool(db2, roomId, workerId, toolName, args) {
|
|
|
24420
24429
|
}
|
|
24421
24430
|
case "quoroom_create_subgoal": {
|
|
24422
24431
|
const goalId = Number(args.goalId);
|
|
24432
|
+
const goalCheck = getGoal(db2, goalId);
|
|
24433
|
+
if (!goalCheck) return { content: `Error: goal #${goalId} not found.`, isError: true };
|
|
24434
|
+
if (goalCheck.roomId !== roomId) return { content: `Error: goal #${goalId} belongs to another room.`, isError: true };
|
|
24423
24435
|
const raw = args.descriptions;
|
|
24424
24436
|
const descriptions = Array.isArray(raw) ? raw.map(String) : [String(raw)];
|
|
24425
24437
|
const subGoals = decomposeGoal(db2, goalId, descriptions);
|
|
@@ -24427,11 +24439,17 @@ async function executeQueenTool(db2, roomId, workerId, toolName, args) {
|
|
|
24427
24439
|
}
|
|
24428
24440
|
case "quoroom_complete_goal": {
|
|
24429
24441
|
const goalId = Number(args.goalId);
|
|
24442
|
+
const goalCheck = getGoal(db2, goalId);
|
|
24443
|
+
if (!goalCheck) return { content: `Error: goal #${goalId} not found.`, isError: true };
|
|
24444
|
+
if (goalCheck.roomId !== roomId) return { content: `Error: goal #${goalId} belongs to another room.`, isError: true };
|
|
24430
24445
|
completeGoal(db2, goalId);
|
|
24431
24446
|
return { content: `Goal #${goalId} marked as completed.` };
|
|
24432
24447
|
}
|
|
24433
24448
|
case "quoroom_abandon_goal": {
|
|
24434
24449
|
const goalId = Number(args.goalId);
|
|
24450
|
+
const goalCheck = getGoal(db2, goalId);
|
|
24451
|
+
if (!goalCheck) return { content: `Error: goal #${goalId} not found.`, isError: true };
|
|
24452
|
+
if (goalCheck.roomId !== roomId) return { content: `Error: goal #${goalId} belongs to another room.`, isError: true };
|
|
24435
24453
|
const reason = String(args.reason ?? "No reason given");
|
|
24436
24454
|
abandonGoal(db2, goalId, reason);
|
|
24437
24455
|
return { content: `Goal #${goalId} abandoned: ${reason}` };
|
|
@@ -24563,7 +24581,7 @@ async function executeQueenTool(db2, roomId, workerId, toolName, args) {
|
|
|
24563
24581
|
case "quoroom_configure_room": {
|
|
24564
24582
|
const updates = {};
|
|
24565
24583
|
if (args.queenCycleGapMs != null) updates.queenCycleGapMs = Math.max(1e4, Number(args.queenCycleGapMs));
|
|
24566
|
-
if (args.queenMaxTurns != null) updates.queenMaxTurns = Math.max(1, Math.min(
|
|
24584
|
+
if (args.queenMaxTurns != null) updates.queenMaxTurns = Math.max(1, Math.min(50, Number(args.queenMaxTurns)));
|
|
24567
24585
|
if (Object.keys(updates).length > 0) {
|
|
24568
24586
|
updateRoom(db2, roomId, updates);
|
|
24569
24587
|
return { content: `Room configured: ${JSON.stringify(updates)}` };
|
|
@@ -24847,10 +24865,12 @@ async function runCycle(db2, roomId, worker, maxTurns, options) {
|
|
|
24847
24865
|
updateAgentState(db2, worker.id, "thinking");
|
|
24848
24866
|
logBuffer.addSynthetic("system", `Cycle started \u2014 observing room state...`);
|
|
24849
24867
|
checkExpiredDecisions(db2);
|
|
24850
|
-
const
|
|
24868
|
+
const status2 = getRoomStatus(db2, roomId);
|
|
24851
24869
|
const pendingEscalations = getPendingEscalations(db2, roomId, worker.id);
|
|
24870
|
+
const recentKeeperAnswers = getRecentKeeperAnswers(db2, roomId, worker.id, 5);
|
|
24852
24871
|
const recentActivity = getRoomActivity(db2, roomId, 15);
|
|
24853
|
-
const goalUpdates =
|
|
24872
|
+
const goalUpdates = status2.activeGoals.slice(0, 5).map((g) => ({
|
|
24873
|
+
id: g.id,
|
|
24854
24874
|
goal: g.description,
|
|
24855
24875
|
progress: g.progress,
|
|
24856
24876
|
status: g.status
|
|
@@ -24875,7 +24895,7 @@ async function runCycle(db2, roomId, worker, maxTurns, options) {
|
|
|
24875
24895
|
]);
|
|
24876
24896
|
} catch {
|
|
24877
24897
|
}
|
|
24878
|
-
const skillContent = loadSkillsForAgent(db2, roomId,
|
|
24898
|
+
const skillContent = loadSkillsForAgent(db2, roomId, status2.room.goal ?? "");
|
|
24879
24899
|
const rolePreset = worker.role ? WORKER_ROLE_PRESETS[worker.role] : void 0;
|
|
24880
24900
|
const systemPrompt = [
|
|
24881
24901
|
rolePreset?.systemPromptPrefix ? `${rolePreset.systemPromptPrefix}
|
|
@@ -24954,14 +24974,14 @@ ${skillContent}` : ""
|
|
|
24954
24974
|
- Share link: https://quoroom.ai/share/v2/${encodedKeeperCode}`
|
|
24955
24975
|
);
|
|
24956
24976
|
}
|
|
24957
|
-
if (
|
|
24977
|
+
if (status2.room.goal) {
|
|
24958
24978
|
contextParts.push(`## Room Objective
|
|
24959
|
-
${
|
|
24979
|
+
${status2.room.goal}`);
|
|
24960
24980
|
}
|
|
24961
24981
|
if (goalUpdates.length > 0) {
|
|
24962
24982
|
contextParts.push(`## Active Goals
|
|
24963
24983
|
${goalUpdates.map(
|
|
24964
|
-
(g) => `- [${Math.round(g.progress * 100)}%] ${g.goal} (${g.status})`
|
|
24984
|
+
(g) => `- [#${g.id}] [${Math.round(g.progress * 100)}%] ${g.goal} (${g.status})`
|
|
24965
24985
|
).join("\n")}`);
|
|
24966
24986
|
}
|
|
24967
24987
|
const memoryEntities = listEntities(db2, roomId).slice(0, 20);
|
|
@@ -24991,12 +25011,27 @@ ${recentResolved.map((d) => {
|
|
|
24991
25011
|
return `- ${icon} ${d.status}: "${d.proposal.slice(0, 120)}"`;
|
|
24992
25012
|
}).join("\n")}`);
|
|
24993
25013
|
}
|
|
24994
|
-
|
|
25014
|
+
const pendingToKeeper = pendingEscalations.filter((e) => e.fromAgentId === worker.id && !e.toAgentId);
|
|
25015
|
+
const pendingFromOthers = pendingEscalations.filter((e) => e.fromAgentId !== worker.id);
|
|
25016
|
+
if (pendingToKeeper.length > 0) {
|
|
25017
|
+
contextParts.push(`## Pending Questions to Keeper (awaiting keeper reply)
|
|
25018
|
+
${pendingToKeeper.map(
|
|
25019
|
+
(e) => `- #${e.id}: ${e.question}`
|
|
25020
|
+
).join("\n")}`);
|
|
25021
|
+
}
|
|
25022
|
+
if (pendingFromOthers.length > 0) {
|
|
24995
25023
|
contextParts.push(`## Escalations Awaiting Your Response
|
|
24996
|
-
${
|
|
25024
|
+
${pendingFromOthers.map(
|
|
24997
25025
|
(e) => `- #${e.id}: ${e.question}`
|
|
24998
25026
|
).join("\n")}`);
|
|
24999
25027
|
}
|
|
25028
|
+
if (recentKeeperAnswers.length > 0) {
|
|
25029
|
+
contextParts.push(`## Keeper Answers (recent)
|
|
25030
|
+
${recentKeeperAnswers.map(
|
|
25031
|
+
(e) => `- Q: ${e.question}
|
|
25032
|
+
A: ${e.answer}`
|
|
25033
|
+
).join("\n")}`);
|
|
25034
|
+
}
|
|
25000
25035
|
const activitySlice = recentActivity.slice(0, 15);
|
|
25001
25036
|
if (activitySlice.length > 0) {
|
|
25002
25037
|
contextParts.push(`## Recent Activity
|
|
@@ -25044,9 +25079,9 @@ ${top3.map(
|
|
|
25044
25079
|
(a) => a.eventType === "system" && a.summary.includes("rate limited")
|
|
25045
25080
|
);
|
|
25046
25081
|
const settingsParts = [
|
|
25047
|
-
`- Cycle gap: ${Math.round(
|
|
25048
|
-
`- Max turns per cycle: ${
|
|
25049
|
-
`- Max concurrent tasks: ${
|
|
25082
|
+
`- Cycle gap: ${Math.round(status2.room.queenCycleGapMs / 1e3)}s`,
|
|
25083
|
+
`- Max turns per cycle: ${status2.room.queenMaxTurns}`,
|
|
25084
|
+
`- Max concurrent tasks: ${status2.room.maxConcurrentTasks}`
|
|
25050
25085
|
];
|
|
25051
25086
|
if (rateLimitEvents.length > 0) {
|
|
25052
25087
|
settingsParts.push(`- **Rate limits hit recently: ${rateLimitEvents.length}** (in last ${recentActivity.length} events)`);
|
|
@@ -25056,19 +25091,24 @@ ${settingsParts.join("\n")}`);
|
|
|
25056
25091
|
const selfRegulateHint = rateLimitEvents.length > 0 ? "\n- **Self-regulate**: You are hitting rate limits. Use quoroom_configure_room to increase your cycle gap or reduce max turns to stay within API limits." : "";
|
|
25057
25092
|
const isClaude = model === "claude" || model.startsWith("claude-");
|
|
25058
25093
|
const toolCallInstruction = isClaude ? "Always call tools to take action \u2014 do not just describe what you would do." : "IMPORTANT: You MUST call at least one tool in your response. Respond ONLY with a tool call \u2014 do not write explanatory text without a tool call.";
|
|
25094
|
+
const commsTools = isCli ? "quoroom_inbox_send_keeper (message keeper), quoroom_inbox_list (inter-room), quoroom_inbox_send_room, quoroom_inbox_reply" : "quoroom_ask_keeper";
|
|
25095
|
+
const webTools = isCli ? "(use your built-in web search and fetch tools)" : "quoroom_web_search, quoroom_web_fetch, quoroom_browser";
|
|
25059
25096
|
const toolList = `**Goals:** quoroom_set_goal, quoroom_update_progress, quoroom_create_subgoal, quoroom_complete_goal, quoroom_abandon_goal
|
|
25060
25097
|
**Governance:** quoroom_propose, quoroom_vote
|
|
25061
25098
|
**Workers:** quoroom_create_worker, quoroom_update_worker
|
|
25062
25099
|
**Tasks:** quoroom_schedule
|
|
25063
25100
|
**Memory:** quoroom_remember, quoroom_recall
|
|
25064
|
-
**Web:**
|
|
25065
|
-
**Comms:**
|
|
25101
|
+
**Web:** ${webTools}
|
|
25102
|
+
**Comms:** ${commsTools}
|
|
25066
25103
|
**Settings:** quoroom_configure_room${selfRegulateHint}`;
|
|
25104
|
+
const sendKeeperTool = isCli ? "quoroom_inbox_send_keeper" : "quoroom_ask_keeper";
|
|
25067
25105
|
contextParts.push(`## Instructions
|
|
25068
25106
|
Based on the current state, decide what to do next and call the appropriate tools. Available tools:
|
|
25069
25107
|
|
|
25070
25108
|
${toolList}
|
|
25071
25109
|
|
|
25110
|
+
Do NOT "stand by" or wait for anyone \u2014 every cycle must make progress. Act autonomously: make decisions and execute. Inform the keeper of progress or important updates using ${sendKeeperTool}, but never block on a response. If the keeper hasn't replied, proceed with your best judgment.
|
|
25111
|
+
|
|
25072
25112
|
${toolCallInstruction}`);
|
|
25073
25113
|
const prompt = contextParts.join("\n\n");
|
|
25074
25114
|
updateAgentState(db2, worker.id, "acting");
|
|
@@ -25434,8 +25474,8 @@ function registerRoomRoutes(router) {
|
|
|
25434
25474
|
});
|
|
25435
25475
|
router.get("/api/rooms/:id/status", (ctx) => {
|
|
25436
25476
|
try {
|
|
25437
|
-
const
|
|
25438
|
-
return { data:
|
|
25477
|
+
const status2 = getRoomStatus(ctx.db, Number(ctx.params.id));
|
|
25478
|
+
return { data: status2 };
|
|
25439
25479
|
} catch (e) {
|
|
25440
25480
|
return { status: 404, error: e.message };
|
|
25441
25481
|
}
|
|
@@ -25789,17 +25829,17 @@ function registerGoalRoutes(router) {
|
|
|
25789
25829
|
);
|
|
25790
25830
|
}
|
|
25791
25831
|
if (body.status !== void 0) {
|
|
25792
|
-
const
|
|
25793
|
-
if (!GOAL_STATUS_VALUES.includes(
|
|
25832
|
+
const status2 = body.status;
|
|
25833
|
+
if (!GOAL_STATUS_VALUES.includes(status2)) {
|
|
25794
25834
|
return { status: 400, error: "status is invalid" };
|
|
25795
25835
|
}
|
|
25796
|
-
if (
|
|
25836
|
+
if (status2 === "completed") {
|
|
25797
25837
|
completeGoal(ctx.db, id);
|
|
25798
|
-
} else if (
|
|
25838
|
+
} else if (status2 === "abandoned") {
|
|
25799
25839
|
const reason = typeof body.reason === "string" && body.reason.trim() ? body.reason : "Manual status change";
|
|
25800
25840
|
abandonGoal(ctx.db, id, reason);
|
|
25801
25841
|
} else {
|
|
25802
|
-
updateGoal(ctx.db, id, { status });
|
|
25842
|
+
updateGoal(ctx.db, id, { status: status2 });
|
|
25803
25843
|
}
|
|
25804
25844
|
}
|
|
25805
25845
|
const updated = getGoal(ctx.db, id);
|
|
@@ -26720,8 +26760,8 @@ async function fetchCloudTelegramVerificationStatus(tokenHash) {
|
|
|
26720
26760
|
});
|
|
26721
26761
|
}
|
|
26722
26762
|
const payload = await res.json().catch(() => ({}));
|
|
26723
|
-
const
|
|
26724
|
-
const normalizedStatus =
|
|
26763
|
+
const status2 = payload.status;
|
|
26764
|
+
const normalizedStatus = status2 === "verified" ? "verified" : status2 === "expired" ? "expired" : status2 === "missing" ? "missing" : "pending";
|
|
26725
26765
|
const telegramId = payload.telegram?.id == null ? null : String(payload.telegram.id);
|
|
26726
26766
|
return {
|
|
26727
26767
|
status: normalizedStatus,
|
|
@@ -27016,13 +27056,13 @@ function registerContactRoutes(router) {
|
|
|
27016
27056
|
return { data: { ok: true, status: "expired" } };
|
|
27017
27057
|
}
|
|
27018
27058
|
try {
|
|
27019
|
-
const
|
|
27020
|
-
setSetting2(ctx.db, CONTACT_TELEGRAM_BOT_USERNAME_KEY,
|
|
27021
|
-
if (
|
|
27022
|
-
setSetting2(ctx.db, CONTACT_TELEGRAM_ID_KEY,
|
|
27023
|
-
setSetting2(ctx.db, CONTACT_TELEGRAM_USERNAME_KEY,
|
|
27024
|
-
setSetting2(ctx.db, CONTACT_TELEGRAM_FIRST_NAME_KEY,
|
|
27025
|
-
setSetting2(ctx.db, CONTACT_TELEGRAM_VERIFIED_AT_KEY,
|
|
27059
|
+
const status2 = await fetchCloudTelegramVerificationStatus(tokenHash);
|
|
27060
|
+
setSetting2(ctx.db, CONTACT_TELEGRAM_BOT_USERNAME_KEY, status2.botUsername);
|
|
27061
|
+
if (status2.status === "verified" && status2.telegramId) {
|
|
27062
|
+
setSetting2(ctx.db, CONTACT_TELEGRAM_ID_KEY, status2.telegramId);
|
|
27063
|
+
setSetting2(ctx.db, CONTACT_TELEGRAM_USERNAME_KEY, status2.username ?? "");
|
|
27064
|
+
setSetting2(ctx.db, CONTACT_TELEGRAM_FIRST_NAME_KEY, status2.firstName ?? "");
|
|
27065
|
+
setSetting2(ctx.db, CONTACT_TELEGRAM_VERIFIED_AT_KEY, status2.verifiedAt ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
27026
27066
|
clearSetting(ctx.db, CONTACT_TELEGRAM_PENDING_HASH_KEY);
|
|
27027
27067
|
clearSetting(ctx.db, CONTACT_TELEGRAM_PENDING_EXPIRES_AT_KEY);
|
|
27028
27068
|
void syncCloudContactBindingsSafe(ctx.db);
|
|
@@ -27031,23 +27071,23 @@ function registerContactRoutes(router) {
|
|
|
27031
27071
|
ok: true,
|
|
27032
27072
|
status: "verified",
|
|
27033
27073
|
telegram: {
|
|
27034
|
-
id:
|
|
27035
|
-
username:
|
|
27036
|
-
firstName:
|
|
27037
|
-
verifiedAt:
|
|
27074
|
+
id: status2.telegramId,
|
|
27075
|
+
username: status2.username,
|
|
27076
|
+
firstName: status2.firstName,
|
|
27077
|
+
verifiedAt: status2.verifiedAt
|
|
27038
27078
|
}
|
|
27039
27079
|
}
|
|
27040
27080
|
};
|
|
27041
27081
|
}
|
|
27042
|
-
if (
|
|
27082
|
+
if (status2.status === "expired" || status2.status === "missing") {
|
|
27043
27083
|
clearSetting(ctx.db, CONTACT_TELEGRAM_PENDING_HASH_KEY);
|
|
27044
27084
|
clearSetting(ctx.db, CONTACT_TELEGRAM_PENDING_EXPIRES_AT_KEY);
|
|
27045
27085
|
}
|
|
27046
27086
|
return {
|
|
27047
27087
|
data: {
|
|
27048
27088
|
ok: true,
|
|
27049
|
-
status:
|
|
27050
|
-
botUsername:
|
|
27089
|
+
status: status2.status,
|
|
27090
|
+
botUsername: status2.botUsername
|
|
27051
27091
|
}
|
|
27052
27092
|
};
|
|
27053
27093
|
} catch (error) {
|
|
@@ -27534,17 +27574,17 @@ function parseLimit2(raw, fallback, max) {
|
|
|
27534
27574
|
function registerRunRoutes(router) {
|
|
27535
27575
|
router.get("/api/runs", (ctx) => {
|
|
27536
27576
|
const limit = parseLimit2(ctx.query.limit, 20, 500);
|
|
27537
|
-
const
|
|
27577
|
+
const status2 = ctx.query.status;
|
|
27538
27578
|
const includeResult = ctx.query.includeResult === "1";
|
|
27539
27579
|
const roomId = ctx.query.roomId ? Number(ctx.query.roomId) : void 0;
|
|
27540
27580
|
let runs;
|
|
27541
27581
|
if (roomId) {
|
|
27542
27582
|
runs = listRunsByRoom(ctx.db, roomId, limit);
|
|
27543
|
-
if (
|
|
27544
|
-
} else if (
|
|
27583
|
+
if (status2) runs = runs.filter((run) => run.status === status2);
|
|
27584
|
+
} else if (status2 === "running") {
|
|
27545
27585
|
runs = getRunningTaskRuns(ctx.db).slice(0, limit);
|
|
27546
|
-
} else if (
|
|
27547
|
-
runs = listAllRuns(ctx.db, limit).filter((run) => run.status ===
|
|
27586
|
+
} else if (status2) {
|
|
27587
|
+
runs = listAllRuns(ctx.db, limit).filter((run) => run.status === status2);
|
|
27548
27588
|
} else {
|
|
27549
27589
|
runs = listAllRuns(ctx.db, limit);
|
|
27550
27590
|
}
|
|
@@ -27891,8 +27931,8 @@ function registerEscalationRoutes(router) {
|
|
|
27891
27931
|
if (toAgentId != null) {
|
|
27892
27932
|
return { data: getPendingEscalations(ctx.db, roomId, toAgentId) };
|
|
27893
27933
|
}
|
|
27894
|
-
const
|
|
27895
|
-
return { data: listEscalations(ctx.db, roomId,
|
|
27934
|
+
const status2 = ctx.query.status;
|
|
27935
|
+
return { data: listEscalations(ctx.db, roomId, status2) };
|
|
27896
27936
|
});
|
|
27897
27937
|
router.post("/api/escalations/:id/resolve", (ctx) => {
|
|
27898
27938
|
const id = Number(ctx.params.id);
|
|
@@ -27905,6 +27945,10 @@ function registerEscalationRoutes(router) {
|
|
|
27905
27945
|
resolveEscalation(ctx.db, id, body.answer);
|
|
27906
27946
|
const updated = getEscalation(ctx.db, id);
|
|
27907
27947
|
eventBus.emit(`room:${escalation.roomId}`, "escalation:resolved", updated);
|
|
27948
|
+
const room = getRoom(ctx.db, escalation.roomId);
|
|
27949
|
+
if (room?.queenWorkerId) {
|
|
27950
|
+
triggerAgent(ctx.db, escalation.roomId, room.queenWorkerId);
|
|
27951
|
+
}
|
|
27908
27952
|
return { data: updated };
|
|
27909
27953
|
});
|
|
27910
27954
|
}
|
|
@@ -28024,7 +28068,7 @@ function registerChatRoutes(router) {
|
|
|
28024
28068
|
}
|
|
28025
28069
|
|
|
28026
28070
|
// src/server/routes/status.ts
|
|
28027
|
-
var
|
|
28071
|
+
var import_node_os4 = __toESM(require("node:os"));
|
|
28028
28072
|
var import_node_child_process2 = require("node:child_process");
|
|
28029
28073
|
var import_node_util = require("node:util");
|
|
28030
28074
|
|
|
@@ -28642,7 +28686,208 @@ function closeServerDatabase() {
|
|
|
28642
28686
|
}
|
|
28643
28687
|
|
|
28644
28688
|
// src/server/updateChecker.ts
|
|
28689
|
+
var import_node_https2 = __toESM(require("node:https"));
|
|
28690
|
+
|
|
28691
|
+
// src/server/autoUpdate.ts
|
|
28692
|
+
var import_node_fs3 = __toESM(require("node:fs"));
|
|
28693
|
+
var import_node_path3 = __toESM(require("node:path"));
|
|
28694
|
+
var import_node_os3 = require("node:os");
|
|
28645
28695
|
var import_node_https = __toESM(require("node:https"));
|
|
28696
|
+
var import_node_http = __toESM(require("node:http"));
|
|
28697
|
+
var import_node_crypto6 = require("node:crypto");
|
|
28698
|
+
var import_promises = require("node:stream/promises");
|
|
28699
|
+
var USER_APP_DIR = import_node_path3.default.join((0, import_node_os3.homedir)(), ".quoroom", "app");
|
|
28700
|
+
var STAGING_DIR = import_node_path3.default.join((0, import_node_os3.homedir)(), ".quoroom", "app-staging");
|
|
28701
|
+
var BOOT_MARKER = import_node_path3.default.join(USER_APP_DIR, ".booting");
|
|
28702
|
+
var CRASH_COUNT_FILE = import_node_path3.default.join(USER_APP_DIR, ".crash_count");
|
|
28703
|
+
var VERSION_FILE = import_node_path3.default.join(USER_APP_DIR, "version.json");
|
|
28704
|
+
var status = { state: "idle" };
|
|
28705
|
+
var downloadInProgress = false;
|
|
28706
|
+
function getAutoUpdateStatus() {
|
|
28707
|
+
if (status.state === "idle" && import_node_fs3.default.existsSync(VERSION_FILE)) {
|
|
28708
|
+
try {
|
|
28709
|
+
const info = JSON.parse(import_node_fs3.default.readFileSync(VERSION_FILE, "utf-8"));
|
|
28710
|
+
return { state: "ready", version: info.version };
|
|
28711
|
+
} catch {
|
|
28712
|
+
}
|
|
28713
|
+
}
|
|
28714
|
+
return status;
|
|
28715
|
+
}
|
|
28716
|
+
function initBootHealthCheck() {
|
|
28717
|
+
if (import_node_fs3.default.existsSync(VERSION_FILE)) {
|
|
28718
|
+
try {
|
|
28719
|
+
const info = JSON.parse(import_node_fs3.default.readFileSync(VERSION_FILE, "utf-8"));
|
|
28720
|
+
const currentVersion = getCurrentVersion();
|
|
28721
|
+
if (info.version && !semverGt(info.version, currentVersion)) {
|
|
28722
|
+
console.error(`[auto-update] Cleaning stale user-space update v${info.version} (bundled is v${currentVersion})`);
|
|
28723
|
+
import_node_fs3.default.rmSync(USER_APP_DIR, { recursive: true, force: true });
|
|
28724
|
+
}
|
|
28725
|
+
} catch {
|
|
28726
|
+
}
|
|
28727
|
+
}
|
|
28728
|
+
if (!import_node_fs3.default.existsSync(USER_APP_DIR)) return;
|
|
28729
|
+
try {
|
|
28730
|
+
import_node_fs3.default.writeFileSync(BOOT_MARKER, JSON.stringify({ pid: process.pid, at: Date.now() }));
|
|
28731
|
+
} catch {
|
|
28732
|
+
}
|
|
28733
|
+
setTimeout(() => {
|
|
28734
|
+
try {
|
|
28735
|
+
import_node_fs3.default.unlinkSync(BOOT_MARKER);
|
|
28736
|
+
} catch {
|
|
28737
|
+
}
|
|
28738
|
+
try {
|
|
28739
|
+
import_node_fs3.default.unlinkSync(CRASH_COUNT_FILE);
|
|
28740
|
+
} catch {
|
|
28741
|
+
}
|
|
28742
|
+
}, 3e4);
|
|
28743
|
+
}
|
|
28744
|
+
function followRedirects(url, maxRedirects = 5) {
|
|
28745
|
+
return new Promise((resolve2, reject) => {
|
|
28746
|
+
if (maxRedirects <= 0) return reject(new Error("Too many redirects"));
|
|
28747
|
+
const parsed = new URL(url);
|
|
28748
|
+
const mod2 = parsed.protocol === "https:" ? import_node_https.default : import_node_http.default;
|
|
28749
|
+
const req = mod2.get(url, { headers: { "User-Agent": "quoroom-auto-updater/1.0" } }, (res) => {
|
|
28750
|
+
if ((res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) && res.headers.location) {
|
|
28751
|
+
res.resume();
|
|
28752
|
+
followRedirects(res.headers.location, maxRedirects - 1).then(resolve2, reject);
|
|
28753
|
+
return;
|
|
28754
|
+
}
|
|
28755
|
+
if (res.statusCode !== 200) {
|
|
28756
|
+
res.resume();
|
|
28757
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
28758
|
+
return;
|
|
28759
|
+
}
|
|
28760
|
+
resolve2(res);
|
|
28761
|
+
});
|
|
28762
|
+
req.on("error", reject);
|
|
28763
|
+
req.setTimeout(6e4, () => {
|
|
28764
|
+
req.destroy();
|
|
28765
|
+
reject(new Error("Download timeout"));
|
|
28766
|
+
});
|
|
28767
|
+
});
|
|
28768
|
+
}
|
|
28769
|
+
function sha256File(filePath) {
|
|
28770
|
+
return new Promise((resolve2, reject) => {
|
|
28771
|
+
const hash3 = (0, import_node_crypto6.createHash)("sha256");
|
|
28772
|
+
const stream = import_node_fs3.default.createReadStream(filePath);
|
|
28773
|
+
stream.on("data", (chunk) => hash3.update(chunk));
|
|
28774
|
+
stream.on("end", () => resolve2(hash3.digest("hex")));
|
|
28775
|
+
stream.on("error", reject);
|
|
28776
|
+
});
|
|
28777
|
+
}
|
|
28778
|
+
async function downloadAndExtract(bundleUrl) {
|
|
28779
|
+
import_node_fs3.default.rmSync(STAGING_DIR, { recursive: true, force: true });
|
|
28780
|
+
import_node_fs3.default.mkdirSync(STAGING_DIR, { recursive: true });
|
|
28781
|
+
const tarballPath = import_node_path3.default.join(STAGING_DIR, "update.tar.gz");
|
|
28782
|
+
const response = await followRedirects(bundleUrl);
|
|
28783
|
+
const fileStream = import_node_fs3.default.createWriteStream(tarballPath);
|
|
28784
|
+
await (0, import_promises.pipeline)(response, fileStream);
|
|
28785
|
+
await extractTarGz(tarballPath, STAGING_DIR);
|
|
28786
|
+
import_node_fs3.default.unlinkSync(tarballPath);
|
|
28787
|
+
}
|
|
28788
|
+
async function extractTarGz(tarballPath, destDir) {
|
|
28789
|
+
const { execSync: execSync5 } = await import("node:child_process");
|
|
28790
|
+
execSync5(`tar xzf ${JSON.stringify(tarballPath)} -C ${JSON.stringify(destDir)}`, { stdio: "ignore" });
|
|
28791
|
+
}
|
|
28792
|
+
async function verifyUpdate(dir) {
|
|
28793
|
+
const versionPath = import_node_path3.default.join(dir, "version.json");
|
|
28794
|
+
if (!import_node_fs3.default.existsSync(versionPath)) {
|
|
28795
|
+
throw new Error("Missing version.json in update bundle");
|
|
28796
|
+
}
|
|
28797
|
+
const info = JSON.parse(import_node_fs3.default.readFileSync(versionPath, "utf-8"));
|
|
28798
|
+
if (!info.version) {
|
|
28799
|
+
throw new Error("Invalid version.json: missing version field");
|
|
28800
|
+
}
|
|
28801
|
+
if (info.checksums) {
|
|
28802
|
+
for (const [relativePath, expectedHash] of Object.entries(info.checksums)) {
|
|
28803
|
+
const filePath = import_node_path3.default.join(dir, relativePath);
|
|
28804
|
+
if (!import_node_fs3.default.existsSync(filePath)) {
|
|
28805
|
+
throw new Error(`Missing file in update: ${relativePath}`);
|
|
28806
|
+
}
|
|
28807
|
+
const actualHash = await sha256File(filePath);
|
|
28808
|
+
if (actualHash !== expectedHash) {
|
|
28809
|
+
throw new Error(`Checksum mismatch for ${relativePath}`);
|
|
28810
|
+
}
|
|
28811
|
+
}
|
|
28812
|
+
}
|
|
28813
|
+
const requiredFiles = ["lib/cli.js", "lib/api-server.js", "lib/server.js"];
|
|
28814
|
+
for (const f of requiredFiles) {
|
|
28815
|
+
if (!import_node_fs3.default.existsSync(import_node_path3.default.join(dir, f))) {
|
|
28816
|
+
throw new Error(`Missing required file: ${f}`);
|
|
28817
|
+
}
|
|
28818
|
+
}
|
|
28819
|
+
return info;
|
|
28820
|
+
}
|
|
28821
|
+
function applyUpdate() {
|
|
28822
|
+
import_node_fs3.default.rmSync(USER_APP_DIR, { recursive: true, force: true });
|
|
28823
|
+
import_node_fs3.default.renameSync(STAGING_DIR, USER_APP_DIR);
|
|
28824
|
+
}
|
|
28825
|
+
function semverGt(a, b) {
|
|
28826
|
+
const pa = a.split(".").map(Number);
|
|
28827
|
+
const pb = b.split(".").map(Number);
|
|
28828
|
+
for (let i = 0; i < 3; i++) {
|
|
28829
|
+
const av = pa[i] ?? 0;
|
|
28830
|
+
const bv = pb[i] ?? 0;
|
|
28831
|
+
if (av > bv) return true;
|
|
28832
|
+
if (av < bv) return false;
|
|
28833
|
+
}
|
|
28834
|
+
return false;
|
|
28835
|
+
}
|
|
28836
|
+
function getCurrentVersion() {
|
|
28837
|
+
try {
|
|
28838
|
+
return true ? "0.1.21" : null.version;
|
|
28839
|
+
} catch {
|
|
28840
|
+
return "0.0.0";
|
|
28841
|
+
}
|
|
28842
|
+
}
|
|
28843
|
+
function getReadyUpdateVersion() {
|
|
28844
|
+
try {
|
|
28845
|
+
if (!import_node_fs3.default.existsSync(VERSION_FILE)) return null;
|
|
28846
|
+
const info = JSON.parse(import_node_fs3.default.readFileSync(VERSION_FILE, "utf-8"));
|
|
28847
|
+
if (info.version && semverGt(info.version, getCurrentVersion())) {
|
|
28848
|
+
return info.version;
|
|
28849
|
+
}
|
|
28850
|
+
return null;
|
|
28851
|
+
} catch {
|
|
28852
|
+
return null;
|
|
28853
|
+
}
|
|
28854
|
+
}
|
|
28855
|
+
async function checkAndApplyUpdate(bundleUrl, targetVersion) {
|
|
28856
|
+
if (downloadInProgress) return;
|
|
28857
|
+
const readyVersion = getReadyUpdateVersion();
|
|
28858
|
+
if (readyVersion && !semverGt(targetVersion, readyVersion)) return;
|
|
28859
|
+
downloadInProgress = true;
|
|
28860
|
+
try {
|
|
28861
|
+
console.error(`[auto-update] Downloading update v${targetVersion}...`);
|
|
28862
|
+
status = { state: "downloading", version: targetVersion };
|
|
28863
|
+
await downloadAndExtract(bundleUrl);
|
|
28864
|
+
console.error(`[auto-update] Verifying update v${targetVersion}...`);
|
|
28865
|
+
status = { state: "verifying", version: targetVersion };
|
|
28866
|
+
const info = await verifyUpdate(STAGING_DIR);
|
|
28867
|
+
if (info.minEngineVersion) {
|
|
28868
|
+
const currentVersion = getCurrentVersion();
|
|
28869
|
+
if (semverGt(info.minEngineVersion, currentVersion)) {
|
|
28870
|
+
console.error(`[auto-update] Update requires engine >= ${info.minEngineVersion}, current is ${currentVersion}. Skipping.`);
|
|
28871
|
+
import_node_fs3.default.rmSync(STAGING_DIR, { recursive: true, force: true });
|
|
28872
|
+
status = { state: "error", error: `Requires full installer (engine >= ${info.minEngineVersion})` };
|
|
28873
|
+
return;
|
|
28874
|
+
}
|
|
28875
|
+
}
|
|
28876
|
+
console.error(`[auto-update] Applying update v${targetVersion}...`);
|
|
28877
|
+
applyUpdate();
|
|
28878
|
+
console.error(`[auto-update] Update v${targetVersion} ready! Restart to activate.`);
|
|
28879
|
+
status = { state: "ready", version: targetVersion };
|
|
28880
|
+
} catch (err) {
|
|
28881
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
28882
|
+
console.error(`[auto-update] Failed: ${message}`);
|
|
28883
|
+
status = { state: "error", error: message };
|
|
28884
|
+
import_node_fs3.default.rmSync(STAGING_DIR, { recursive: true, force: true });
|
|
28885
|
+
} finally {
|
|
28886
|
+
downloadInProgress = false;
|
|
28887
|
+
}
|
|
28888
|
+
}
|
|
28889
|
+
|
|
28890
|
+
// src/server/updateChecker.ts
|
|
28646
28891
|
var CHECK_INTERVAL = 4 * 60 * 60 * 1e3;
|
|
28647
28892
|
var INITIAL_DELAY = 15e3;
|
|
28648
28893
|
var cached = null;
|
|
@@ -28661,7 +28906,7 @@ function pickLatestStable(releases) {
|
|
|
28661
28906
|
}
|
|
28662
28907
|
function fetchJson(url) {
|
|
28663
28908
|
return new Promise((resolve2, reject) => {
|
|
28664
|
-
const req =
|
|
28909
|
+
const req = import_node_https2.default.get(url, { headers: { "User-Agent": "quoroom-update-checker" } }, (res) => {
|
|
28665
28910
|
const chunks = [];
|
|
28666
28911
|
res.on("data", (c) => chunks.push(c));
|
|
28667
28912
|
res.on("end", () => {
|
|
@@ -28689,13 +28934,19 @@ async function forceCheck() {
|
|
|
28689
28934
|
if (!latest?.assets) return;
|
|
28690
28935
|
const latestVersion = latest.tag_name.replace(/^v/, "");
|
|
28691
28936
|
const assets = { mac: null, windows: null, linux: null };
|
|
28937
|
+
let updateBundle = null;
|
|
28692
28938
|
for (const a of latest.assets) {
|
|
28693
28939
|
const { name, browser_download_url: url } = a;
|
|
28694
28940
|
if (name.endsWith(".pkg")) assets.mac = url;
|
|
28695
28941
|
else if (name.toLowerCase().includes("setup") && name.endsWith(".exe")) assets.windows = url;
|
|
28696
28942
|
else if (name.endsWith(".deb")) assets.linux = url;
|
|
28943
|
+
else if (name.startsWith("quoroom-update-") && name.endsWith(".tar.gz")) updateBundle = url;
|
|
28944
|
+
}
|
|
28945
|
+
cached = { latestVersion, releaseUrl: latest.html_url, assets, updateBundle };
|
|
28946
|
+
if (updateBundle && latestVersion) {
|
|
28947
|
+
void checkAndApplyUpdate(updateBundle, latestVersion).catch(() => {
|
|
28948
|
+
});
|
|
28697
28949
|
}
|
|
28698
|
-
cached = { latestVersion, releaseUrl: latest.html_url, assets };
|
|
28699
28950
|
} catch {
|
|
28700
28951
|
}
|
|
28701
28952
|
}
|
|
@@ -28730,7 +28981,8 @@ async function simulateUpdate() {
|
|
|
28730
28981
|
mac: cached?.assets.mac ?? null,
|
|
28731
28982
|
windows: cached?.assets.windows ?? null,
|
|
28732
28983
|
linux: cached?.assets.linux ?? null
|
|
28733
|
-
}
|
|
28984
|
+
},
|
|
28985
|
+
updateBundle: cached?.updateBundle ?? null
|
|
28734
28986
|
};
|
|
28735
28987
|
}
|
|
28736
28988
|
|
|
@@ -28740,7 +28992,7 @@ var cachedVersion = null;
|
|
|
28740
28992
|
function getVersion3() {
|
|
28741
28993
|
if (cachedVersion) return cachedVersion;
|
|
28742
28994
|
try {
|
|
28743
|
-
cachedVersion = true ? "0.1.
|
|
28995
|
+
cachedVersion = true ? "0.1.21" : null.version;
|
|
28744
28996
|
} catch {
|
|
28745
28997
|
cachedVersion = "unknown";
|
|
28746
28998
|
}
|
|
@@ -28811,11 +29063,11 @@ function warmStatusCaches() {
|
|
|
28811
29063
|
}
|
|
28812
29064
|
warmStatusCaches();
|
|
28813
29065
|
function getResources() {
|
|
28814
|
-
const [load1, load5] =
|
|
28815
|
-
const total =
|
|
28816
|
-
const free =
|
|
29066
|
+
const [load1, load5] = import_node_os4.default.loadavg();
|
|
29067
|
+
const total = import_node_os4.default.totalmem();
|
|
29068
|
+
const free = import_node_os4.default.freemem();
|
|
28817
29069
|
return {
|
|
28818
|
-
cpuCount:
|
|
29070
|
+
cpuCount: import_node_os4.default.cpus().length,
|
|
28819
29071
|
loadAvg1m: Math.round(load1 * 100) / 100,
|
|
28820
29072
|
loadAvg5m: Math.round(load5 * 100) / 100,
|
|
28821
29073
|
memTotalGb: Math.round(total / 1024 / 1024 / 1024 * 10) / 10,
|
|
@@ -28828,6 +29080,12 @@ function registerStatusRoutes(router) {
|
|
|
28828
29080
|
await simulateUpdate();
|
|
28829
29081
|
return { data: { ok: true } };
|
|
28830
29082
|
});
|
|
29083
|
+
router.post("/api/status/test-auto-update", async (ctx) => {
|
|
29084
|
+
const { url, version: version5 } = ctx.body ?? {};
|
|
29085
|
+
if (!url || !version5) return { error: "Missing url or version", status: 400 };
|
|
29086
|
+
await checkAndApplyUpdate(url, version5);
|
|
29087
|
+
return { data: { status: getAutoUpdateStatus(), readyVersion: getReadyUpdateVersion() } };
|
|
29088
|
+
});
|
|
28831
29089
|
router.post("/api/status/check-update", async () => {
|
|
28832
29090
|
await forceCheck();
|
|
28833
29091
|
return { data: { updateInfo: getUpdateInfo() } };
|
|
@@ -28862,6 +29120,8 @@ function registerStatusRoutes(router) {
|
|
|
28862
29120
|
}
|
|
28863
29121
|
if (include("update")) {
|
|
28864
29122
|
data.updateInfo = getUpdateInfo();
|
|
29123
|
+
data.autoUpdate = getAutoUpdateStatus();
|
|
29124
|
+
data.readyUpdateVersion = getReadyUpdateVersion();
|
|
28865
29125
|
}
|
|
28866
29126
|
if (Object.keys(pending).length > 0) {
|
|
28867
29127
|
data.pending = pending;
|
|
@@ -28873,6 +29133,7 @@ function registerStatusRoutes(router) {
|
|
|
28873
29133
|
}
|
|
28874
29134
|
|
|
28875
29135
|
// src/server/routes/wallet.ts
|
|
29136
|
+
var import_node_crypto7 = __toESM(require("node:crypto"));
|
|
28876
29137
|
init_cloud_sync();
|
|
28877
29138
|
function parseLimit3(raw, fallback, max) {
|
|
28878
29139
|
const n = Number(raw);
|
|
@@ -28908,9 +29169,11 @@ async function fetchRoomBalance(roomId, address) {
|
|
|
28908
29169
|
);
|
|
28909
29170
|
const byChain = {};
|
|
28910
29171
|
let totalBalance = 0;
|
|
29172
|
+
let anySuccess = false;
|
|
28911
29173
|
for (const { chain, token, result } of results) {
|
|
28912
29174
|
if (!byChain[chain]) byChain[chain] = { usdc: 0, usdt: 0, total: 0 };
|
|
28913
29175
|
if (result.ok) {
|
|
29176
|
+
anySuccess = true;
|
|
28914
29177
|
byChain[chain][token] = result.balance;
|
|
28915
29178
|
byChain[chain].total += result.balance;
|
|
28916
29179
|
totalBalance += result.balance;
|
|
@@ -28922,7 +29185,9 @@ async function fetchRoomBalance(roomId, address) {
|
|
|
28922
29185
|
address,
|
|
28923
29186
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
28924
29187
|
};
|
|
28925
|
-
|
|
29188
|
+
if (anySuccess) {
|
|
29189
|
+
balanceCache.set(roomId, { data, fetchedAt: Date.now() });
|
|
29190
|
+
}
|
|
28926
29191
|
return data;
|
|
28927
29192
|
})();
|
|
28928
29193
|
pendingBalanceRequests.set(roomId, request);
|
|
@@ -28959,6 +29224,9 @@ function registerWalletRoutes(router) {
|
|
|
28959
29224
|
}
|
|
28960
29225
|
try {
|
|
28961
29226
|
const data = await fetchRoomBalance(roomId, wallet.address);
|
|
29227
|
+
if (data.totalBalance === 0 && Object.keys(data.byChain).length === 0 && cached2 && cached2.data.totalBalance > 0) {
|
|
29228
|
+
return { data: { ...cached2.data, fetchedAt: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
29229
|
+
}
|
|
28962
29230
|
return { data };
|
|
28963
29231
|
} catch {
|
|
28964
29232
|
if (cached2) {
|
|
@@ -28994,6 +29262,73 @@ function registerWalletRoutes(router) {
|
|
|
28994
29262
|
if (!result) return { status: 503, error: "On-ramp unavailable" };
|
|
28995
29263
|
return { data: result };
|
|
28996
29264
|
});
|
|
29265
|
+
router.post("/api/rooms/:roomId/wallet/withdraw", async (ctx) => {
|
|
29266
|
+
const roomId = Number(ctx.params.roomId);
|
|
29267
|
+
const room = getRoom(ctx.db, roomId);
|
|
29268
|
+
if (!room) return { status: 404, error: "Room not found" };
|
|
29269
|
+
const wallet = getWalletByRoom(ctx.db, roomId);
|
|
29270
|
+
if (!wallet) return { status: 400, error: "Room has no wallet" };
|
|
29271
|
+
const { to: rawTo, amount: rawAmount, chain, token } = ctx.body;
|
|
29272
|
+
const to = rawTo?.trim();
|
|
29273
|
+
const amount = rawAmount?.trim();
|
|
29274
|
+
if (!to || !amount) return { status: 400, error: "Missing required fields: to, amount" };
|
|
29275
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(to)) return { status: 400, error: "Invalid address" };
|
|
29276
|
+
const parsed = parseFloat(amount);
|
|
29277
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return { status: 400, error: "Invalid amount" };
|
|
29278
|
+
const selectedChain = chain ?? "base";
|
|
29279
|
+
const selectedToken = token ?? "usdc";
|
|
29280
|
+
const chainConfig2 = CHAIN_CONFIGS[selectedChain];
|
|
29281
|
+
if (!chainConfig2) return { status: 400, error: `Unsupported chain: ${selectedChain}` };
|
|
29282
|
+
const tokenConfig = chainConfig2.tokens[selectedToken];
|
|
29283
|
+
if (!tokenConfig) return { status: 400, error: `Token ${selectedToken} not available on ${selectedChain}` };
|
|
29284
|
+
const encryptionKey = import_node_crypto7.default.createHash("sha256").update(`quoroom-wallet-${room.id}-${room.name}`).digest("hex");
|
|
29285
|
+
const GAS_TIPS = {
|
|
29286
|
+
base: { token: "ETH", amount: "0.0001 ETH (~$0.25)" },
|
|
29287
|
+
arbitrum: { token: "ETH", amount: "0.0001 ETH (~$0.25)" },
|
|
29288
|
+
optimism: { token: "ETH", amount: "0.0001 ETH (~$0.25)" },
|
|
29289
|
+
ethereum: { token: "ETH", amount: "0.002 ETH (~$5)" },
|
|
29290
|
+
polygon: { token: "POL", amount: "0.1 POL (~$0.05)" }
|
|
29291
|
+
};
|
|
29292
|
+
const gasTip = GAS_TIPS[selectedChain] ?? { token: "ETH", amount: "0.001 ETH" };
|
|
29293
|
+
const VIEM_CHAINS2 = {
|
|
29294
|
+
base,
|
|
29295
|
+
ethereum: mainnet,
|
|
29296
|
+
arbitrum,
|
|
29297
|
+
optimism,
|
|
29298
|
+
polygon
|
|
29299
|
+
};
|
|
29300
|
+
try {
|
|
29301
|
+
const viemChain = VIEM_CHAINS2[selectedChain];
|
|
29302
|
+
if (viemChain) {
|
|
29303
|
+
const publicClient = createPublicClient({ chain: viemChain, transport: http(chainConfig2.rpcUrl) });
|
|
29304
|
+
const gasBalance = await publicClient.getBalance({ address: wallet.address });
|
|
29305
|
+
if (gasBalance === 0n) {
|
|
29306
|
+
return { status: 400, error: `No ${gasTip.token} for gas fees. Send at least ${gasTip.amount} to ${wallet.address} on ${chainConfig2.name} to cover the transaction.` };
|
|
29307
|
+
}
|
|
29308
|
+
}
|
|
29309
|
+
} catch {
|
|
29310
|
+
}
|
|
29311
|
+
try {
|
|
29312
|
+
const txHash = await sendToken(
|
|
29313
|
+
ctx.db,
|
|
29314
|
+
roomId,
|
|
29315
|
+
to,
|
|
29316
|
+
amount,
|
|
29317
|
+
encryptionKey,
|
|
29318
|
+
selectedChain,
|
|
29319
|
+
tokenConfig.address,
|
|
29320
|
+
tokenConfig.decimals
|
|
29321
|
+
);
|
|
29322
|
+
balanceCache.delete(roomId);
|
|
29323
|
+
return { data: { txHash } };
|
|
29324
|
+
} catch (e) {
|
|
29325
|
+
const msg = e.message || "Unknown error";
|
|
29326
|
+
if (msg.includes("gas") || msg.includes("insufficient funds")) {
|
|
29327
|
+
return { status: 400, error: `Insufficient ${gasTip.token} for gas fees on ${chainConfig2.name}. Send at least ${gasTip.amount} to your wallet address to cover the transaction.` };
|
|
29328
|
+
}
|
|
29329
|
+
return { status: 400, error: `Withdraw failed: ${msg.split("\n")[0]}` };
|
|
29330
|
+
}
|
|
29331
|
+
});
|
|
28997
29332
|
}
|
|
28998
29333
|
|
|
28999
29334
|
// src/server/routes/credentials.ts
|
|
@@ -29303,8 +29638,8 @@ function registerStationRoutes(router) {
|
|
|
29303
29638
|
function registerRoomMessageRoutes(router) {
|
|
29304
29639
|
router.get("/api/rooms/:roomId/messages", (ctx) => {
|
|
29305
29640
|
const roomId = Number(ctx.params.roomId);
|
|
29306
|
-
const
|
|
29307
|
-
return { data: listRoomMessages(ctx.db, roomId,
|
|
29641
|
+
const status2 = ctx.query.status;
|
|
29642
|
+
return { data: listRoomMessages(ctx.db, roomId, status2) };
|
|
29308
29643
|
});
|
|
29309
29644
|
router.post("/api/rooms/:roomId/messages", (ctx) => {
|
|
29310
29645
|
const roomId = Number(ctx.params.roomId);
|
|
@@ -29372,7 +29707,7 @@ function registerRoomMessageRoutes(router) {
|
|
|
29372
29707
|
|
|
29373
29708
|
// src/server/provider-auth.ts
|
|
29374
29709
|
var import_node_child_process3 = require("node:child_process");
|
|
29375
|
-
var
|
|
29710
|
+
var import_node_crypto8 = require("node:crypto");
|
|
29376
29711
|
var sessionStore = /* @__PURE__ */ new Map();
|
|
29377
29712
|
var activeByProvider = /* @__PURE__ */ new Map();
|
|
29378
29713
|
var MAX_LINES = Math.max(50, parseInt(process.env.QUOROOM_PROVIDER_AUTH_MAX_LINES || "300", 10) || 300);
|
|
@@ -29385,8 +29720,8 @@ function getProviderCommand(provider) {
|
|
|
29385
29720
|
if (provider === "codex") return { command: "codex", args: ["login"] };
|
|
29386
29721
|
return { command: "claude", args: ["login"] };
|
|
29387
29722
|
}
|
|
29388
|
-
function isActiveStatus(
|
|
29389
|
-
return
|
|
29723
|
+
function isActiveStatus(status2) {
|
|
29724
|
+
return status2 === "starting" || status2 === "running";
|
|
29390
29725
|
}
|
|
29391
29726
|
function emitSessionStatus(session) {
|
|
29392
29727
|
eventBus.emit(`provider-auth:${session.sessionId}`, "provider_auth:status", toSessionView(session, false));
|
|
@@ -29493,11 +29828,11 @@ function toSessionView(session, includeLines = true) {
|
|
|
29493
29828
|
lines: includeLines ? [...session.lines] : []
|
|
29494
29829
|
};
|
|
29495
29830
|
}
|
|
29496
|
-
function finalizeSession(session,
|
|
29831
|
+
function finalizeSession(session, status2, exitCode) {
|
|
29497
29832
|
if (!isActiveStatus(session.status)) return;
|
|
29498
29833
|
clearTimeout(session.timeout);
|
|
29499
29834
|
flushBufferedLines(session);
|
|
29500
|
-
session.status =
|
|
29835
|
+
session.status = status2;
|
|
29501
29836
|
session.exitCode = exitCode;
|
|
29502
29837
|
session.endedAt = nowIso();
|
|
29503
29838
|
session.updatedAt = session.endedAt;
|
|
@@ -29590,7 +29925,7 @@ function startProviderAuthSession(provider) {
|
|
|
29590
29925
|
});
|
|
29591
29926
|
const startedAt2 = nowIso();
|
|
29592
29927
|
const session = {
|
|
29593
|
-
sessionId: (0,
|
|
29928
|
+
sessionId: (0, import_node_crypto8.randomUUID)(),
|
|
29594
29929
|
provider,
|
|
29595
29930
|
command: displayCommand,
|
|
29596
29931
|
status: "starting",
|
|
@@ -29657,8 +29992,8 @@ function startProviderAuthSession(provider) {
|
|
|
29657
29992
|
|
|
29658
29993
|
// src/server/provider-install.ts
|
|
29659
29994
|
var import_node_child_process5 = require("node:child_process");
|
|
29660
|
-
var
|
|
29661
|
-
var
|
|
29995
|
+
var import_node_crypto9 = require("node:crypto");
|
|
29996
|
+
var import_node_path4 = __toESM(require("node:path"));
|
|
29662
29997
|
|
|
29663
29998
|
// src/server/provider-cli.ts
|
|
29664
29999
|
var import_node_child_process4 = require("node:child_process");
|
|
@@ -29681,6 +30016,9 @@ function probeProviderInstalled(provider) {
|
|
|
29681
30016
|
return out.ok ? { installed: true, version: out.stdout || void 0 } : { installed: false };
|
|
29682
30017
|
}
|
|
29683
30018
|
function probeProviderConnected(provider) {
|
|
30019
|
+
if (provider === "claude") {
|
|
30020
|
+
return probeProviderInstalled("claude").installed ? true : null;
|
|
30021
|
+
}
|
|
29684
30022
|
const attempts = provider === "codex" ? [["login", "status"], ["auth", "status"]] : [["auth", "status"], ["login", "status"]];
|
|
29685
30023
|
for (const args of attempts) {
|
|
29686
30024
|
const out = safeExec(provider, args);
|
|
@@ -29726,14 +30064,14 @@ function addGlobalNpmBinToPath(platform = process.platform) {
|
|
|
29726
30064
|
const npmBin = (0, import_node_child_process5.execFileSync)(npmCommand, ["bin", "-g"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
29727
30065
|
if (!npmBin) return;
|
|
29728
30066
|
const currentPath = process.env.PATH || "";
|
|
29729
|
-
const parts = currentPath.split(
|
|
30067
|
+
const parts = currentPath.split(import_node_path4.default.delimiter).filter(Boolean);
|
|
29730
30068
|
if (parts.includes(npmBin)) return;
|
|
29731
|
-
process.env.PATH = `${npmBin}${
|
|
30069
|
+
process.env.PATH = `${npmBin}${import_node_path4.default.delimiter}${currentPath}`;
|
|
29732
30070
|
} catch {
|
|
29733
30071
|
}
|
|
29734
30072
|
}
|
|
29735
|
-
function isActiveStatus2(
|
|
29736
|
-
return
|
|
30073
|
+
function isActiveStatus2(status2) {
|
|
30074
|
+
return status2 === "starting" || status2 === "running";
|
|
29737
30075
|
}
|
|
29738
30076
|
function toSessionView2(session, includeLines = true) {
|
|
29739
30077
|
return {
|
|
@@ -29797,11 +30135,11 @@ function flushBufferedLines2(session) {
|
|
|
29797
30135
|
session.stdoutBuffer = "";
|
|
29798
30136
|
session.stderrBuffer = "";
|
|
29799
30137
|
}
|
|
29800
|
-
function finalizeSession2(session,
|
|
30138
|
+
function finalizeSession2(session, status2, exitCode) {
|
|
29801
30139
|
if (!isActiveStatus2(session.status)) return;
|
|
29802
30140
|
clearTimeout(session.timeout);
|
|
29803
30141
|
flushBufferedLines2(session);
|
|
29804
|
-
session.status =
|
|
30142
|
+
session.status = status2;
|
|
29805
30143
|
session.exitCode = exitCode;
|
|
29806
30144
|
session.endedAt = nowIso2();
|
|
29807
30145
|
session.updatedAt = session.endedAt;
|
|
@@ -29890,7 +30228,7 @@ function startProviderInstallSession(provider) {
|
|
|
29890
30228
|
});
|
|
29891
30229
|
const startedAt2 = nowIso2();
|
|
29892
30230
|
const session = {
|
|
29893
|
-
sessionId: (0,
|
|
30231
|
+
sessionId: (0, import_node_crypto9.randomUUID)(),
|
|
29894
30232
|
provider,
|
|
29895
30233
|
command: displayCommand,
|
|
29896
30234
|
status: "starting",
|
|
@@ -30340,15 +30678,15 @@ async function handleWebhookRequest(pathname, body, db2) {
|
|
|
30340
30678
|
|
|
30341
30679
|
// src/server/shell-path.ts
|
|
30342
30680
|
var import_node_child_process6 = require("node:child_process");
|
|
30343
|
-
var
|
|
30344
|
-
var
|
|
30681
|
+
var import_node_fs4 = require("node:fs");
|
|
30682
|
+
var import_node_path5 = __toESM(require("node:path"));
|
|
30345
30683
|
function inheritShellPath() {
|
|
30346
30684
|
if (process.platform !== "darwin") return;
|
|
30347
30685
|
const currentPath = process.env.PATH || "";
|
|
30348
|
-
const currentParts = new Set(currentPath.split(
|
|
30686
|
+
const currentParts = new Set(currentPath.split(import_node_path5.default.delimiter).filter(Boolean));
|
|
30349
30687
|
const shells = [process.env.SHELL, "/bin/zsh", "/bin/bash"].filter(Boolean);
|
|
30350
30688
|
for (const sh of shells) {
|
|
30351
|
-
if (!(0,
|
|
30689
|
+
if (!(0, import_node_fs4.existsSync)(sh)) continue;
|
|
30352
30690
|
try {
|
|
30353
30691
|
const env = { ...process.env };
|
|
30354
30692
|
delete env.ELECTRON_RUN_AS_NODE;
|
|
@@ -30359,7 +30697,7 @@ function inheritShellPath() {
|
|
|
30359
30697
|
stdio: ["ignore", "pipe", "ignore"]
|
|
30360
30698
|
}).trim();
|
|
30361
30699
|
if (!shellPath) continue;
|
|
30362
|
-
const newParts = shellPath.split(
|
|
30700
|
+
const newParts = shellPath.split(import_node_path5.default.delimiter).filter(Boolean);
|
|
30363
30701
|
const additions = [];
|
|
30364
30702
|
for (const p of newParts) {
|
|
30365
30703
|
if (!currentParts.has(p)) {
|
|
@@ -30368,7 +30706,7 @@ function inheritShellPath() {
|
|
|
30368
30706
|
}
|
|
30369
30707
|
}
|
|
30370
30708
|
if (additions.length > 0) {
|
|
30371
|
-
process.env.PATH = `${currentPath}${
|
|
30709
|
+
process.env.PATH = `${currentPath}${import_node_path5.default.delimiter}${additions.join(import_node_path5.default.delimiter)}`;
|
|
30372
30710
|
}
|
|
30373
30711
|
return;
|
|
30374
30712
|
} catch {
|
|
@@ -30394,7 +30732,7 @@ function streamWithRedirects(url, res, corsHeaders, filename, depth = 0) {
|
|
|
30394
30732
|
}
|
|
30395
30733
|
try {
|
|
30396
30734
|
const parsed = new import_node_url.URL(url);
|
|
30397
|
-
const mod2 = parsed.protocol === "https:" ?
|
|
30735
|
+
const mod2 = parsed.protocol === "https:" ? import_node_https3.default : import_node_http2.default;
|
|
30398
30736
|
mod2.get(url, { headers: { "User-Agent": "quoroom-updater/1.0" } }, (assetRes) => {
|
|
30399
30737
|
if ((assetRes.statusCode === 301 || assetRes.statusCode === 302 || assetRes.statusCode === 307) && assetRes.headers.location) {
|
|
30400
30738
|
assetRes.resume();
|
|
@@ -30542,7 +30880,7 @@ function maybeLogHttpProfile(method, pathname, statusCode, durationMs) {
|
|
|
30542
30880
|
}
|
|
30543
30881
|
function getCacheControl(filePath, ext) {
|
|
30544
30882
|
const normalized = filePath.replace(/\\/g, "/");
|
|
30545
|
-
const base2 =
|
|
30883
|
+
const base2 = import_node_path6.default.basename(filePath);
|
|
30546
30884
|
if (base2 === "sw.js") return "no-cache, no-store, must-revalidate";
|
|
30547
30885
|
if (ext === ".html") return "no-cache, no-store, must-revalidate";
|
|
30548
30886
|
if (ext === ".webmanifest") return "public, max-age=3600";
|
|
@@ -30561,15 +30899,15 @@ function getCacheControl(filePath, ext) {
|
|
|
30561
30899
|
return "no-cache, max-age=0";
|
|
30562
30900
|
}
|
|
30563
30901
|
function serveStatic(staticDir, pathname, res) {
|
|
30564
|
-
const safePath =
|
|
30565
|
-
let filePath =
|
|
30902
|
+
const safePath = import_node_path6.default.normalize(pathname).replace(/^(\.\.[/\\])+/, "");
|
|
30903
|
+
let filePath = import_node_path6.default.join(staticDir, safePath);
|
|
30566
30904
|
try {
|
|
30567
|
-
const stat =
|
|
30905
|
+
const stat = import_node_fs5.default.statSync(filePath);
|
|
30568
30906
|
if (stat.isDirectory()) {
|
|
30569
|
-
filePath =
|
|
30907
|
+
filePath = import_node_path6.default.join(filePath, "index.html");
|
|
30570
30908
|
}
|
|
30571
30909
|
} catch {
|
|
30572
|
-
if (
|
|
30910
|
+
if (import_node_path6.default.extname(safePath)) {
|
|
30573
30911
|
res.writeHead(404, {
|
|
30574
30912
|
"Content-Type": "text/plain; charset=utf-8",
|
|
30575
30913
|
"Cache-Control": "no-cache, no-store, must-revalidate"
|
|
@@ -30577,15 +30915,15 @@ function serveStatic(staticDir, pathname, res) {
|
|
|
30577
30915
|
res.end("Not Found");
|
|
30578
30916
|
return;
|
|
30579
30917
|
}
|
|
30580
|
-
filePath =
|
|
30918
|
+
filePath = import_node_path6.default.join(staticDir, "index.html");
|
|
30581
30919
|
}
|
|
30582
|
-
const ext =
|
|
30920
|
+
const ext = import_node_path6.default.extname(filePath).toLowerCase();
|
|
30583
30921
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
30584
30922
|
const headers = {
|
|
30585
30923
|
"Content-Type": contentType,
|
|
30586
30924
|
"Cache-Control": getCacheControl(filePath, ext)
|
|
30587
30925
|
};
|
|
30588
|
-
const stream =
|
|
30926
|
+
const stream = import_node_fs5.default.createReadStream(filePath);
|
|
30589
30927
|
stream.on("open", () => {
|
|
30590
30928
|
res.writeHead(200, headers);
|
|
30591
30929
|
stream.pipe(res);
|
|
@@ -30640,7 +30978,7 @@ function createApiServer(options = {}) {
|
|
|
30640
30978
|
if (!options.skipTokenFile) {
|
|
30641
30979
|
writeTokenFile(dataDir, token, port);
|
|
30642
30980
|
}
|
|
30643
|
-
const server =
|
|
30981
|
+
const server = import_node_http2.default.createServer(async (req, res) => {
|
|
30644
30982
|
res.on("error", () => {
|
|
30645
30983
|
});
|
|
30646
30984
|
const url = new import_node_url.URL(req.url, `http://${req.headers.host || "localhost"}`);
|
|
@@ -30729,6 +31067,30 @@ function createApiServer(options = {}) {
|
|
|
30729
31067
|
setTimeout(() => process.exit(0), 120);
|
|
30730
31068
|
return;
|
|
30731
31069
|
}
|
|
31070
|
+
if (pathname === "/api/server/update-restart" && req.method === "POST") {
|
|
31071
|
+
const isLocalClient = isLoopbackAddress(req.socket.remoteAddress);
|
|
31072
|
+
if (!isLocalClient || origin && !isLocalOrigin(origin)) {
|
|
31073
|
+
res.writeHead(403, responseHeaders);
|
|
31074
|
+
res.end(JSON.stringify({ error: "Update-restart allowed only from localhost clients" }));
|
|
31075
|
+
return;
|
|
31076
|
+
}
|
|
31077
|
+
const readyVersion = getReadyUpdateVersion();
|
|
31078
|
+
if (!readyVersion) {
|
|
31079
|
+
res.writeHead(404, responseHeaders);
|
|
31080
|
+
res.end(JSON.stringify({ error: "No update ready to apply" }));
|
|
31081
|
+
return;
|
|
31082
|
+
}
|
|
31083
|
+
const scheduled = scheduleSelfRestart();
|
|
31084
|
+
if (!scheduled) {
|
|
31085
|
+
res.writeHead(500, responseHeaders);
|
|
31086
|
+
res.end(JSON.stringify({ error: "Failed to schedule restart" }));
|
|
31087
|
+
return;
|
|
31088
|
+
}
|
|
31089
|
+
res.writeHead(202, responseHeaders);
|
|
31090
|
+
res.end(JSON.stringify({ ok: true, restarting: true, version: readyVersion }));
|
|
31091
|
+
setTimeout(() => process.exit(0), 120);
|
|
31092
|
+
return;
|
|
31093
|
+
}
|
|
30732
31094
|
if (pathname === "/api/auth/verify" && req.method === "GET") {
|
|
30733
31095
|
const principal = getTokenPrincipal(req.headers.authorization);
|
|
30734
31096
|
if (!principal) {
|
|
@@ -30816,13 +31178,13 @@ function createApiServer(options = {}) {
|
|
|
30816
31178
|
res.end();
|
|
30817
31179
|
return;
|
|
30818
31180
|
}
|
|
30819
|
-
const
|
|
30820
|
-
res.writeHead(
|
|
31181
|
+
const status2 = result.error ? result.status || 400 : result.status || 200;
|
|
31182
|
+
res.writeHead(status2, responseHeaders);
|
|
30821
31183
|
res.end(JSON.stringify(result.error ? { error: result.error } : result.data));
|
|
30822
31184
|
} catch (err) {
|
|
30823
31185
|
const message = err instanceof Error ? err.message : "Internal error";
|
|
30824
|
-
const
|
|
30825
|
-
res.writeHead(
|
|
31186
|
+
const status2 = message === "Invalid JSON body" ? 400 : message === "Payload too large" ? 413 : 500;
|
|
31187
|
+
res.writeHead(status2, responseHeaders);
|
|
30826
31188
|
res.end(JSON.stringify({ error: message }));
|
|
30827
31189
|
}
|
|
30828
31190
|
return;
|
|
@@ -30839,16 +31201,16 @@ function createApiServer(options = {}) {
|
|
|
30839
31201
|
}
|
|
30840
31202
|
function patchMcpConfig(configPath, entry) {
|
|
30841
31203
|
try {
|
|
30842
|
-
if (!
|
|
31204
|
+
if (!import_node_fs5.default.existsSync(configPath)) return false;
|
|
30843
31205
|
let config = {};
|
|
30844
31206
|
try {
|
|
30845
|
-
config = JSON.parse(
|
|
31207
|
+
config = JSON.parse(import_node_fs5.default.readFileSync(configPath, "utf-8"));
|
|
30846
31208
|
} catch {
|
|
30847
31209
|
}
|
|
30848
31210
|
const mcpServers = config.mcpServers ?? {};
|
|
30849
31211
|
mcpServers["quoroom"] = entry;
|
|
30850
31212
|
config.mcpServers = mcpServers;
|
|
30851
|
-
|
|
31213
|
+
import_node_fs5.default.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
30852
31214
|
return true;
|
|
30853
31215
|
} catch {
|
|
30854
31216
|
return false;
|
|
@@ -30856,8 +31218,8 @@ function patchMcpConfig(configPath, entry) {
|
|
|
30856
31218
|
}
|
|
30857
31219
|
function registerMcpGlobally(dbPath) {
|
|
30858
31220
|
try {
|
|
30859
|
-
const home = (0,
|
|
30860
|
-
const mcpServerPath =
|
|
31221
|
+
const home = (0, import_node_os5.homedir)();
|
|
31222
|
+
const mcpServerPath = import_node_path6.default.join(__dirname, "server.js");
|
|
30861
31223
|
const nodePath = process.execPath;
|
|
30862
31224
|
const entry = (source) => ({
|
|
30863
31225
|
command: nodePath,
|
|
@@ -30866,12 +31228,12 @@ function registerMcpGlobally(dbPath) {
|
|
|
30866
31228
|
});
|
|
30867
31229
|
const isWin = process.platform === "win32";
|
|
30868
31230
|
const isMac = process.platform === "darwin";
|
|
30869
|
-
patchMcpConfig(
|
|
30870
|
-
const claudeDesktopPath = isWin ?
|
|
31231
|
+
patchMcpConfig(import_node_path6.default.join(home, ".claude.json"), entry("claude-code"));
|
|
31232
|
+
const claudeDesktopPath = isWin ? import_node_path6.default.join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json") : isMac ? import_node_path6.default.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : import_node_path6.default.join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
30871
31233
|
patchMcpConfig(claudeDesktopPath, entry("claude-desktop"));
|
|
30872
|
-
patchMcpConfig(
|
|
31234
|
+
patchMcpConfig(import_node_path6.default.join(home, ".cursor", "mcp.json"), entry("cursor"));
|
|
30873
31235
|
patchMcpConfig(
|
|
30874
|
-
|
|
31236
|
+
import_node_path6.default.join(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
30875
31237
|
entry("windsurf")
|
|
30876
31238
|
);
|
|
30877
31239
|
} catch {
|
|
@@ -30883,18 +31245,22 @@ function startServer(options = {}) {
|
|
|
30883
31245
|
const deploymentMode = getDeploymentMode();
|
|
30884
31246
|
const bindHost = process.env.QUOROOM_BIND_HOST || (deploymentMode === "cloud" ? DEFAULT_BIND_HOST_CLOUD : DEFAULT_BIND_HOST_LOCAL);
|
|
30885
31247
|
if (!options.staticDir) {
|
|
30886
|
-
const
|
|
30887
|
-
|
|
30888
|
-
|
|
31248
|
+
const userUiDir = import_node_path6.default.join(USER_APP_DIR, "ui");
|
|
31249
|
+
const bundledUiDir = import_node_path6.default.join(__dirname, "../ui");
|
|
31250
|
+
if (import_node_fs5.default.existsSync(import_node_path6.default.join(userUiDir, "index.html"))) {
|
|
31251
|
+
options.staticDir = userUiDir;
|
|
31252
|
+
} else if (import_node_fs5.default.existsSync(bundledUiDir)) {
|
|
31253
|
+
options.staticDir = bundledUiDir;
|
|
30889
31254
|
}
|
|
30890
31255
|
}
|
|
30891
|
-
const dbPath = process.env.QUOROOM_DB_PATH ||
|
|
31256
|
+
const dbPath = process.env.QUOROOM_DB_PATH || import_node_path6.default.join(options.dataDir ?? getDataDir(), "data.db");
|
|
30892
31257
|
const { server, token, db: serverDb } = createApiServer(options);
|
|
30893
31258
|
if (!process.env.QUOROOM_SKIP_MCP_REGISTER) {
|
|
30894
31259
|
registerMcpGlobally(dbPath);
|
|
30895
31260
|
}
|
|
30896
31261
|
initCloudSync(serverDb);
|
|
30897
31262
|
initUpdateChecker();
|
|
31263
|
+
initBootHealthCheck();
|
|
30898
31264
|
startServerRuntime(serverDb);
|
|
30899
31265
|
function listen() {
|
|
30900
31266
|
server.listen(port, bindHost, () => {
|