quoroom 0.1.8 → 0.1.9

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.
@@ -9382,7 +9382,7 @@ var require_package = __commonJS({
9382
9382
  "package.json"(exports2, module2) {
9383
9383
  module2.exports = {
9384
9384
  name: "quoroom",
9385
- version: "0.1.8",
9385
+ version: "0.1.9",
9386
9386
  description: "Autonomous AI agent collective engine \u2014 Queen, Workers, Quorum",
9387
9387
  main: "./out/mcp/server.js",
9388
9388
  bin: {
@@ -9405,7 +9405,11 @@ var require_package = __commonJS({
9405
9405
  "build:mcp": "node scripts/build-mcp.js",
9406
9406
  "build:ui": "vite build --config src/ui/vite.config.ts",
9407
9407
  dev: `sh -c 'trap "kill 0" INT TERM EXIT; npm run dev:room & npm run dev:cloud & wait'`,
9408
- "dev:room": "npm run build:mcp && npm run build:ui && node scripts/dev-server.js",
9408
+ "dev:room": "QUOROOM_DATA_DIR=$HOME/.quoroom-dev QUOROOM_SKIP_MCP_REGISTER=1 npm run build:mcp && npm run build:ui && node scripts/dev-server.js --port 4700",
9409
+ "dev:room:isolated": "QUOROOM_DATA_DIR=$HOME/.quoroom-dev QUOROOM_SKIP_MCP_REGISTER=1 npm run build:mcp && npm run build:ui && node scripts/dev-server.js --port 4700",
9410
+ "dev:room:shared": "npm run build:mcp && npm run build:ui && node scripts/dev-server.js",
9411
+ "doctor:split": "node scripts/doctor-split.js",
9412
+ "dev:isolated": `sh -c 'trap "kill 0" INT TERM EXIT; npm run dev:room:isolated & npm run dev:cloud & wait'`,
9409
9413
  "dev:cloud": "cd ../cloud && PORT=3710 CLOUD_PUBLIC_URL=http://127.0.0.1:3710 CLOUD_ALLOWED_ORIGINS='http://127.0.0.1:3710,http://localhost:3710,http://localhost:5173,http://127.0.0.1:5173,https://quoroom.ai,https://www.quoroom.ai,https://app.quoroom.ai' npm start",
9410
9414
  "dev:ui": "vite --config src/ui/vite.config.ts",
9411
9415
  "seed:style-demo": "node scripts/seed-style-demo.js",
@@ -10453,9 +10457,6 @@ function getCloudAllowedOrigins() {
10453
10457
  const origins = configured.length > 0 ? configured : DEFAULT_CLOUD_ALLOWED_ORIGINS;
10454
10458
  return new Set(origins.map(normalizeOrigin).filter(Boolean));
10455
10459
  }
10456
- function getCloudUserToken() {
10457
- return (process.env.QUOROOM_CLOUD_USER_TOKEN || "").trim();
10458
- }
10459
10460
  function getCloudJwtSecret() {
10460
10461
  return (process.env.QUOROOM_CLOUD_JWT_SECRET || "").trim();
10461
10462
  }
@@ -10554,9 +10555,9 @@ function generateToken(dataDir) {
10554
10555
  return agentToken;
10555
10556
  }
10556
10557
  }
10557
- agentToken = dataDir ? readLegacyAgentToken(dataDir) ?? import_node_crypto.default.randomBytes(32).toString("hex") : import_node_crypto.default.randomBytes(32).toString("hex");
10558
+ agentToken = dataDir && !isCloudDeployment() ? readLegacyAgentToken(dataDir) ?? import_node_crypto.default.randomBytes(32).toString("hex") : import_node_crypto.default.randomBytes(32).toString("hex");
10558
10559
  userToken = import_node_crypto.default.randomBytes(32).toString("hex");
10559
- if (dataDir) writePersistedTokens(dataDir, agentToken, userToken);
10560
+ if (dataDir && !isCloudDeployment()) writePersistedTokens(dataDir, agentToken, userToken);
10560
10561
  return agentToken;
10561
10562
  }
10562
10563
  function getUserToken() {
@@ -10572,10 +10573,10 @@ function validateToken(authHeader) {
10572
10573
  const cloudRole = validateCloudJwt(provided);
10573
10574
  if (cloudRole) return cloudRole;
10574
10575
  }
10575
- if (isCloudDeployment() && tokenEquals(getCloudUserToken(), provided)) return "user";
10576
10576
  return null;
10577
10577
  }
10578
10578
  function writeTokenFile(dataDir, token, port) {
10579
+ if (isCloudDeployment()) return;
10579
10580
  (0, import_node_fs.mkdirSync)(dataDir, { recursive: true });
10580
10581
  (0, import_node_fs.writeFileSync)((0, import_node_path.join)(dataDir, "api.token"), token, { mode: 384 });
10581
10582
  (0, import_node_fs.writeFileSync)((0, import_node_path.join)(dataDir, "api.port"), String(port));
@@ -26227,12 +26228,12 @@ function registerStatusRoutes(router) {
26227
26228
  const ollama = await checkOllama();
26228
26229
  const resources = getResources();
26229
26230
  const deploymentMode = getDeploymentMode();
26231
+ const isCloud = deploymentMode === "cloud";
26230
26232
  return {
26231
26233
  data: {
26232
26234
  version: getVersion3(),
26233
26235
  uptime: Math.floor((Date.now() - startedAt) / 1e3),
26234
- dataDir,
26235
- dbPath,
26236
+ ...!isCloud && { dataDir, dbPath },
26236
26237
  claude,
26237
26238
  codex,
26238
26239
  ollama,
@@ -27745,6 +27746,39 @@ function serveStatic(staticDir, pathname, res) {
27745
27746
  res.end("Not Found");
27746
27747
  }
27747
27748
  }
27749
+ var RATE_LIMIT_WINDOW_MS = 6e4;
27750
+ var RATE_LIMIT_READ = 300;
27751
+ var RATE_LIMIT_WRITE = 120;
27752
+ var rateBuckets = /* @__PURE__ */ new Map();
27753
+ setInterval(() => {
27754
+ const now = Date.now();
27755
+ for (const [key, bucket] of rateBuckets) {
27756
+ if (now >= bucket.resetAt) rateBuckets.delete(key);
27757
+ }
27758
+ }, 12e4).unref();
27759
+ function checkRateLimit2(ip, method) {
27760
+ const isWrite = method !== "GET" && method !== "HEAD" && method !== "OPTIONS";
27761
+ const limit = isWrite ? RATE_LIMIT_WRITE : RATE_LIMIT_READ;
27762
+ const key = `${ip}:${isWrite ? "w" : "r"}`;
27763
+ const now = Date.now();
27764
+ let bucket = rateBuckets.get(key);
27765
+ if (!bucket || now >= bucket.resetAt) {
27766
+ bucket = { count: 0, resetAt: now + RATE_LIMIT_WINDOW_MS };
27767
+ rateBuckets.set(key, bucket);
27768
+ }
27769
+ bucket.count++;
27770
+ if (bucket.count > limit) {
27771
+ const retryAfter = Math.ceil((bucket.resetAt - now) / 1e3);
27772
+ return { allowed: false, retryAfter };
27773
+ }
27774
+ return { allowed: true, retryAfter: 0 };
27775
+ }
27776
+ var CLOUD_SECURITY_HEADERS = {
27777
+ "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
27778
+ "X-Content-Type-Options": "nosniff",
27779
+ "X-Frame-Options": "DENY",
27780
+ "Referrer-Policy": "strict-origin-when-cross-origin"
27781
+ };
27748
27782
  function createApiServer(options = {}) {
27749
27783
  const db2 = options.db ?? getServerDatabase();
27750
27784
  const port = options.port ?? DEFAULT_PORT;
@@ -27769,7 +27803,20 @@ function createApiServer(options = {}) {
27769
27803
  const responseHeaders = {
27770
27804
  "Content-Type": "application/json"
27771
27805
  };
27806
+ if (isCloudDeployment()) {
27807
+ Object.assign(responseHeaders, CLOUD_SECURITY_HEADERS);
27808
+ }
27772
27809
  setCorsHeaders(origin, responseHeaders);
27810
+ if (isCloudDeployment() && pathname.startsWith("/api/")) {
27811
+ const clientIp = req.socket.remoteAddress || "unknown";
27812
+ const rl = checkRateLimit2(clientIp, req.method || "GET");
27813
+ if (!rl.allowed) {
27814
+ responseHeaders["Retry-After"] = String(rl.retryAfter);
27815
+ res.writeHead(429, responseHeaders);
27816
+ res.end(JSON.stringify({ error: "Too many requests" }));
27817
+ return;
27818
+ }
27819
+ }
27773
27820
  const isSameOrigin = origin && req.headers.host && (() => {
27774
27821
  try {
27775
27822
  return new import_node_url.URL(origin).host === req.headers.host;
package/out/mcp/cli.js CHANGED
@@ -49800,7 +49800,7 @@ var server_exports = {};
49800
49800
  async function main() {
49801
49801
  const server = new McpServer({
49802
49802
  name: "quoroom",
49803
- version: true ? "0.1.8" : "0.0.0"
49803
+ version: true ? "0.1.9" : "0.0.0"
49804
49804
  });
49805
49805
  registerMemoryTools(server);
49806
49806
  registerSchedulerTools(server);
@@ -49934,9 +49934,6 @@ function getCloudAllowedOrigins() {
49934
49934
  const origins = configured.length > 0 ? configured : DEFAULT_CLOUD_ALLOWED_ORIGINS;
49935
49935
  return new Set(origins.map(normalizeOrigin).filter(Boolean));
49936
49936
  }
49937
- function getCloudUserToken() {
49938
- return (process.env.QUOROOM_CLOUD_USER_TOKEN || "").trim();
49939
- }
49940
49937
  function getCloudJwtSecret() {
49941
49938
  return (process.env.QUOROOM_CLOUD_JWT_SECRET || "").trim();
49942
49939
  }
@@ -50035,9 +50032,9 @@ function generateToken(dataDir) {
50035
50032
  return agentToken;
50036
50033
  }
50037
50034
  }
50038
- agentToken = dataDir ? readLegacyAgentToken(dataDir) ?? import_node_crypto2.default.randomBytes(32).toString("hex") : import_node_crypto2.default.randomBytes(32).toString("hex");
50035
+ agentToken = dataDir && !isCloudDeployment() ? readLegacyAgentToken(dataDir) ?? import_node_crypto2.default.randomBytes(32).toString("hex") : import_node_crypto2.default.randomBytes(32).toString("hex");
50039
50036
  userToken = import_node_crypto2.default.randomBytes(32).toString("hex");
50040
- if (dataDir) writePersistedTokens(dataDir, agentToken, userToken);
50037
+ if (dataDir && !isCloudDeployment()) writePersistedTokens(dataDir, agentToken, userToken);
50041
50038
  return agentToken;
50042
50039
  }
50043
50040
  function getUserToken() {
@@ -50053,10 +50050,10 @@ function validateToken(authHeader) {
50053
50050
  const cloudRole = validateCloudJwt(provided);
50054
50051
  if (cloudRole) return cloudRole;
50055
50052
  }
50056
- if (isCloudDeployment() && tokenEquals(getCloudUserToken(), provided)) return "user";
50057
50053
  return null;
50058
50054
  }
50059
50055
  function writeTokenFile(dataDir, token, port) {
50056
+ if (isCloudDeployment()) return;
50060
50057
  (0, import_node_fs.mkdirSync)(dataDir, { recursive: true });
50061
50058
  (0, import_node_fs.writeFileSync)((0, import_node_path.join)(dataDir, "api.token"), token, { mode: 384 });
50062
50059
  (0, import_node_fs.writeFileSync)((0, import_node_path.join)(dataDir, "api.port"), String(port));
@@ -50744,7 +50741,7 @@ var require_package = __commonJS({
50744
50741
  "package.json"(exports2, module2) {
50745
50742
  module2.exports = {
50746
50743
  name: "quoroom",
50747
- version: "0.1.8",
50744
+ version: "0.1.9",
50748
50745
  description: "Autonomous AI agent collective engine \u2014 Queen, Workers, Quorum",
50749
50746
  main: "./out/mcp/server.js",
50750
50747
  bin: {
@@ -50767,7 +50764,11 @@ var require_package = __commonJS({
50767
50764
  "build:mcp": "node scripts/build-mcp.js",
50768
50765
  "build:ui": "vite build --config src/ui/vite.config.ts",
50769
50766
  dev: `sh -c 'trap "kill 0" INT TERM EXIT; npm run dev:room & npm run dev:cloud & wait'`,
50770
- "dev:room": "npm run build:mcp && npm run build:ui && node scripts/dev-server.js",
50767
+ "dev:room": "QUOROOM_DATA_DIR=$HOME/.quoroom-dev QUOROOM_SKIP_MCP_REGISTER=1 npm run build:mcp && npm run build:ui && node scripts/dev-server.js --port 4700",
50768
+ "dev:room:isolated": "QUOROOM_DATA_DIR=$HOME/.quoroom-dev QUOROOM_SKIP_MCP_REGISTER=1 npm run build:mcp && npm run build:ui && node scripts/dev-server.js --port 4700",
50769
+ "dev:room:shared": "npm run build:mcp && npm run build:ui && node scripts/dev-server.js",
50770
+ "doctor:split": "node scripts/doctor-split.js",
50771
+ "dev:isolated": `sh -c 'trap "kill 0" INT TERM EXIT; npm run dev:room:isolated & npm run dev:cloud & wait'`,
50771
50772
  "dev:cloud": "cd ../cloud && PORT=3710 CLOUD_PUBLIC_URL=http://127.0.0.1:3710 CLOUD_ALLOWED_ORIGINS='http://127.0.0.1:3710,http://localhost:3710,http://localhost:5173,http://127.0.0.1:5173,https://quoroom.ai,https://www.quoroom.ai,https://app.quoroom.ai' npm start",
50772
50773
  "dev:ui": "vite --config src/ui/vite.config.ts",
50773
50774
  "seed:style-demo": "node scripts/seed-style-demo.js",
@@ -52713,12 +52714,12 @@ function registerStatusRoutes(router) {
52713
52714
  const ollama = await checkOllama();
52714
52715
  const resources = getResources();
52715
52716
  const deploymentMode = getDeploymentMode();
52717
+ const isCloud = deploymentMode === "cloud";
52716
52718
  return {
52717
52719
  data: {
52718
52720
  version: getVersion3(),
52719
52721
  uptime: Math.floor((Date.now() - startedAt) / 1e3),
52720
- dataDir,
52721
- dbPath,
52722
+ ...!isCloud && { dataDir, dbPath },
52722
52723
  claude,
52723
52724
  codex,
52724
52725
  ollama,
@@ -57952,6 +57953,23 @@ function serveStatic(staticDir, pathname, res) {
57952
57953
  res.end("Not Found");
57953
57954
  }
57954
57955
  }
57956
+ function checkRateLimit2(ip, method) {
57957
+ const isWrite = method !== "GET" && method !== "HEAD" && method !== "OPTIONS";
57958
+ const limit = isWrite ? RATE_LIMIT_WRITE : RATE_LIMIT_READ;
57959
+ const key = `${ip}:${isWrite ? "w" : "r"}`;
57960
+ const now = Date.now();
57961
+ let bucket = rateBuckets.get(key);
57962
+ if (!bucket || now >= bucket.resetAt) {
57963
+ bucket = { count: 0, resetAt: now + RATE_LIMIT_WINDOW_MS };
57964
+ rateBuckets.set(key, bucket);
57965
+ }
57966
+ bucket.count++;
57967
+ if (bucket.count > limit) {
57968
+ const retryAfter = Math.ceil((bucket.resetAt - now) / 1e3);
57969
+ return { allowed: false, retryAfter };
57970
+ }
57971
+ return { allowed: true, retryAfter: 0 };
57972
+ }
57955
57973
  function createApiServer(options = {}) {
57956
57974
  const db3 = options.db ?? getServerDatabase();
57957
57975
  const port = options.port ?? DEFAULT_PORT;
@@ -57976,7 +57994,20 @@ function createApiServer(options = {}) {
57976
57994
  const responseHeaders = {
57977
57995
  "Content-Type": "application/json"
57978
57996
  };
57997
+ if (isCloudDeployment()) {
57998
+ Object.assign(responseHeaders, CLOUD_SECURITY_HEADERS);
57999
+ }
57979
58000
  setCorsHeaders(origin, responseHeaders);
58001
+ if (isCloudDeployment() && pathname.startsWith("/api/")) {
58002
+ const clientIp = req.socket.remoteAddress || "unknown";
58003
+ const rl = checkRateLimit2(clientIp, req.method || "GET");
58004
+ if (!rl.allowed) {
58005
+ responseHeaders["Retry-After"] = String(rl.retryAfter);
58006
+ res.writeHead(429, responseHeaders);
58007
+ res.end(JSON.stringify({ error: "Too many requests" }));
58008
+ return;
58009
+ }
58010
+ }
57980
58011
  const isSameOrigin = origin && req.headers.host && (() => {
57981
58012
  try {
57982
58013
  return new import_node_url.URL(origin).host === req.headers.host;
@@ -58210,7 +58241,7 @@ function startServer(options = {}) {
58210
58241
  process.exit(0);
58211
58242
  });
58212
58243
  }
58213
- var import_node_http, import_node_https2, import_node_url, import_node_fs3, import_node_path4, import_node_os5, import_node_child_process6, DEFAULT_PORT, DEFAULT_BIND_HOST_LOCAL, DEFAULT_BIND_HOST_CLOUD, MIME_TYPES;
58244
+ var import_node_http, import_node_https2, import_node_url, import_node_fs3, import_node_path4, import_node_os5, import_node_child_process6, DEFAULT_PORT, DEFAULT_BIND_HOST_LOCAL, DEFAULT_BIND_HOST_CLOUD, MIME_TYPES, RATE_LIMIT_WINDOW_MS, RATE_LIMIT_READ, RATE_LIMIT_WRITE, rateBuckets, CLOUD_SECURITY_HEADERS;
58214
58245
  var init_server4 = __esm({
58215
58246
  "src/server/index.ts"() {
58216
58247
  "use strict";
@@ -58254,6 +58285,22 @@ var init_server4 = __esm({
58254
58285
  ".webp": "image/webp",
58255
58286
  ".webmanifest": "application/manifest+json"
58256
58287
  };
58288
+ RATE_LIMIT_WINDOW_MS = 6e4;
58289
+ RATE_LIMIT_READ = 300;
58290
+ RATE_LIMIT_WRITE = 120;
58291
+ rateBuckets = /* @__PURE__ */ new Map();
58292
+ setInterval(() => {
58293
+ const now = Date.now();
58294
+ for (const [key, bucket] of rateBuckets) {
58295
+ if (now >= bucket.resetAt) rateBuckets.delete(key);
58296
+ }
58297
+ }, 12e4).unref();
58298
+ CLOUD_SECURITY_HEADERS = {
58299
+ "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
58300
+ "X-Content-Type-Options": "nosniff",
58301
+ "X-Frame-Options": "DENY",
58302
+ "Referrer-Policy": "strict-origin-when-cross-origin"
58303
+ };
58257
58304
  }
58258
58305
  });
58259
58306
 
package/out/mcp/server.js CHANGED
@@ -47101,7 +47101,7 @@ function registerResourceTools(server) {
47101
47101
  async function main() {
47102
47102
  const server = new McpServer({
47103
47103
  name: "quoroom",
47104
- version: true ? "0.1.8" : "0.0.0"
47104
+ version: true ? "0.1.9" : "0.0.0"
47105
47105
  });
47106
47106
  registerMemoryTools(server);
47107
47107
  registerSchedulerTools(server);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quoroom",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Autonomous AI agent collective engine — Queen, Workers, Quorum",
5
5
  "main": "./out/mcp/server.js",
6
6
  "bin": {
@@ -23,7 +23,11 @@
23
23
  "build:mcp": "node scripts/build-mcp.js",
24
24
  "build:ui": "vite build --config src/ui/vite.config.ts",
25
25
  "dev": "sh -c 'trap \"kill 0\" INT TERM EXIT; npm run dev:room & npm run dev:cloud & wait'",
26
- "dev:room": "npm run build:mcp && npm run build:ui && node scripts/dev-server.js",
26
+ "dev:room": "QUOROOM_DATA_DIR=$HOME/.quoroom-dev QUOROOM_SKIP_MCP_REGISTER=1 npm run build:mcp && npm run build:ui && node scripts/dev-server.js --port 4700",
27
+ "dev:room:isolated": "QUOROOM_DATA_DIR=$HOME/.quoroom-dev QUOROOM_SKIP_MCP_REGISTER=1 npm run build:mcp && npm run build:ui && node scripts/dev-server.js --port 4700",
28
+ "dev:room:shared": "npm run build:mcp && npm run build:ui && node scripts/dev-server.js",
29
+ "doctor:split": "node scripts/doctor-split.js",
30
+ "dev:isolated": "sh -c 'trap \"kill 0\" INT TERM EXIT; npm run dev:room:isolated & npm run dev:cloud & wait'",
27
31
  "dev:cloud": "cd ../cloud && PORT=3710 CLOUD_PUBLIC_URL=http://127.0.0.1:3710 CLOUD_ALLOWED_ORIGINS='http://127.0.0.1:3710,http://localhost:3710,http://localhost:5173,http://127.0.0.1:5173,https://quoroom.ai,https://www.quoroom.ai,https://app.quoroom.ai' npm start",
28
32
  "dev:ui": "vite --config src/ui/vite.config.ts",
29
33
  "seed:style-demo": "node scripts/seed-style-demo.js",