@roamcode.ai/server 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2429,7 +2429,7 @@ async function installManagedRelease(opts) {
2429
2429
  }
2430
2430
 
2431
2431
  // src/updater.ts
2432
- var RUNNING_VERSION = "1.0.11" ? "1.0.11".replace(/^v/, "") : packageVersion();
2432
+ var RUNNING_VERSION = "1.0.12" ? "1.0.12".replace(/^v/, "") : packageVersion();
2433
2433
  var RUNNING_BUILD = RUNNING_VERSION;
2434
2434
  var RELEASES_API = "https://api.github.com/repos/burakgon/roamcode/releases?per_page=100";
2435
2435
  var RELEASE_MANIFEST_ASSET = "roamcode-release.json";
@@ -4908,6 +4908,24 @@ var IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
4908
4908
  "heic",
4909
4909
  "heif"
4910
4910
  ]);
4911
+ var FILE_HISTORY_BACKFILL_BUDGET_MS = 150;
4912
+ var FILE_HISTORY_AVAILABILITY_BUDGET_MS = 150;
4913
+ function completionWithin(task, timeoutMs) {
4914
+ return new Promise((resolve7) => {
4915
+ let settled = false;
4916
+ const finish = (completed) => {
4917
+ if (settled) return;
4918
+ settled = true;
4919
+ clearTimeout(timer);
4920
+ resolve7(completed);
4921
+ };
4922
+ const timer = setTimeout(() => finish(false), timeoutMs);
4923
+ void task.then(
4924
+ () => finish(true),
4925
+ () => finish(false)
4926
+ );
4927
+ });
4928
+ }
4911
4929
  function attachmentMedia(filename, declared = "application/octet-stream") {
4912
4930
  const ext = filename.includes(".") ? filename.slice(filename.lastIndexOf(".") + 1).toLowerCase() : "";
4913
4931
  if (declared.startsWith("image/") || IMAGE_FILE_EXTENSIONS.has(ext)) {
@@ -4979,34 +4997,59 @@ function createServer(config, deps = {}) {
4979
4997
  const fsService = new FsService({ root: config.fsRoot });
4980
4998
  const terminalSharedRoot = terminalSharedBase({ dataDir: config.dataDir, fsRoot: config.fsRoot });
4981
4999
  const backfilledFileSessions = /* @__PURE__ */ new Set();
4982
- const backfillManagedFiles = async (sessionId) => {
4983
- if (backfilledFileSessions.has(sessionId)) return;
4984
- backfilledFileSessions.add(sessionId);
4985
- const sessionDir = terminalSharedDir({ dataDir: config.dataDir, fsRoot: config.fsRoot, sessionId });
4986
- const discovered = await fsService.discoverManagedFiles(sessionDir);
4987
- const knownPaths = new Set(store.listFiles(sessionId, true).map((file) => file.path));
4988
- for (const file of discovered) {
4989
- if (knownPaths.has(file.path)) continue;
4990
- const expiresAt = file.mtimeMs + TERMINAL_FILE_TTL_MS;
4991
- if (expiresAt <= Date.now()) continue;
4992
- const now = Date.now();
4993
- const media = attachmentMedia(file.filename);
4994
- store.putFile({
4995
- id: randomUUID4(),
4996
- sessionId,
4997
- direction: "sent",
4998
- storage: "managed",
4999
- name: file.filename,
5000
- path: file.path,
5001
- mimeType: media.mimeType,
5002
- size: file.size,
5003
- kind: media.kind,
5004
- createdAt: file.mtimeMs,
5005
- updatedAt: now,
5006
- expiresAt
5007
- });
5008
- knownPaths.add(file.path);
5009
- }
5000
+ const fileBackfillsInFlight = /* @__PURE__ */ new Map();
5001
+ const backfillManagedFiles = (sessionId) => {
5002
+ if (backfilledFileSessions.has(sessionId)) return Promise.resolve();
5003
+ const existing = fileBackfillsInFlight.get(sessionId);
5004
+ if (existing) return existing;
5005
+ const task = (async () => {
5006
+ const sessionDir = terminalSharedDir({ dataDir: config.dataDir, fsRoot: config.fsRoot, sessionId });
5007
+ const discovered = await fsService.discoverManagedFiles(sessionDir);
5008
+ const knownPaths = new Set(store.listFiles(sessionId, true).map((file) => file.path));
5009
+ for (const file of discovered) {
5010
+ if (knownPaths.has(file.path)) continue;
5011
+ const expiresAt = file.mtimeMs + TERMINAL_FILE_TTL_MS;
5012
+ if (expiresAt <= Date.now()) continue;
5013
+ const now = Date.now();
5014
+ const media = attachmentMedia(file.filename);
5015
+ store.putFile({
5016
+ id: randomUUID4(),
5017
+ sessionId,
5018
+ direction: "sent",
5019
+ storage: "managed",
5020
+ name: file.filename,
5021
+ path: file.path,
5022
+ mimeType: media.mimeType,
5023
+ size: file.size,
5024
+ kind: media.kind,
5025
+ createdAt: file.mtimeMs,
5026
+ updatedAt: now,
5027
+ expiresAt
5028
+ });
5029
+ knownPaths.add(file.path);
5030
+ }
5031
+ backfilledFileSessions.add(sessionId);
5032
+ })();
5033
+ fileBackfillsInFlight.set(sessionId, task);
5034
+ void task.catch(() => void 0).finally(() => fileBackfillsInFlight.delete(sessionId));
5035
+ return task;
5036
+ };
5037
+ const fileAvailableWithinBudget = async (file) => {
5038
+ if (file.expiresAt <= Date.now()) return false;
5039
+ return new Promise((resolve7) => {
5040
+ let settled = false;
5041
+ const finish = (available) => {
5042
+ if (settled) return;
5043
+ settled = true;
5044
+ clearTimeout(timer);
5045
+ resolve7(available);
5046
+ };
5047
+ const timer = setTimeout(() => finish(true), FILE_HISTORY_AVAILABILITY_BUDGET_MS);
5048
+ void fsService.describeFile(file.path).then(
5049
+ () => finish(true),
5050
+ () => finish(false)
5051
+ );
5052
+ });
5010
5053
  };
5011
5054
  const sweepSharedFiles = () => {
5012
5055
  void (async () => {
@@ -6054,10 +6097,10 @@ function createServer(config, deps = {}) {
6054
6097
  reply.code(404).send({ error: "terminal session not found" });
6055
6098
  return;
6056
6099
  }
6057
- await backfillManagedFiles(request.params.id);
6100
+ await completionWithin(backfillManagedFiles(request.params.id), FILE_HISTORY_BACKFILL_BUDGET_MS);
6058
6101
  const files = await Promise.all(
6059
6102
  store.listFiles(request.params.id).map(async (file) => {
6060
- const available = file.expiresAt > Date.now() && await fsService.describeFile(file.path).then(() => true).catch(() => false);
6103
+ const available = await fileAvailableWithinBudget(file);
6061
6104
  return publicSessionFile(file, available);
6062
6105
  })
6063
6106
  );
package/dist/start.js CHANGED
@@ -871,7 +871,7 @@ function readPreviousVersion(root) {
871
871
  }
872
872
 
873
873
  // src/updater.ts
874
- var RUNNING_VERSION = "1.0.11" ? "1.0.11".replace(/^v/, "") : packageVersion();
874
+ var RUNNING_VERSION = "1.0.12" ? "1.0.12".replace(/^v/, "") : packageVersion();
875
875
  var RELEASES_API = "https://api.github.com/repos/burakgon/roamcode/releases?per_page=100";
876
876
  var RELEASE_MANIFEST_ASSET = "roamcode-release.json";
877
877
  var CHECK_CACHE_MS = 15 * 6e4;
@@ -4295,6 +4295,24 @@ var IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
4295
4295
  "heic",
4296
4296
  "heif"
4297
4297
  ]);
4298
+ var FILE_HISTORY_BACKFILL_BUDGET_MS = 150;
4299
+ var FILE_HISTORY_AVAILABILITY_BUDGET_MS = 150;
4300
+ function completionWithin(task, timeoutMs) {
4301
+ return new Promise((resolve7) => {
4302
+ let settled = false;
4303
+ const finish = (completed) => {
4304
+ if (settled) return;
4305
+ settled = true;
4306
+ clearTimeout(timer);
4307
+ resolve7(completed);
4308
+ };
4309
+ const timer = setTimeout(() => finish(false), timeoutMs);
4310
+ void task.then(
4311
+ () => finish(true),
4312
+ () => finish(false)
4313
+ );
4314
+ });
4315
+ }
4298
4316
  function attachmentMedia(filename, declared = "application/octet-stream") {
4299
4317
  const ext = filename.includes(".") ? filename.slice(filename.lastIndexOf(".") + 1).toLowerCase() : "";
4300
4318
  if (declared.startsWith("image/") || IMAGE_FILE_EXTENSIONS.has(ext)) {
@@ -4366,34 +4384,59 @@ function createServer(config, deps = {}) {
4366
4384
  const fsService = new FsService({ root: config.fsRoot });
4367
4385
  const terminalSharedRoot = terminalSharedBase({ dataDir: config.dataDir, fsRoot: config.fsRoot });
4368
4386
  const backfilledFileSessions = /* @__PURE__ */ new Set();
4369
- const backfillManagedFiles = async (sessionId) => {
4370
- if (backfilledFileSessions.has(sessionId)) return;
4371
- backfilledFileSessions.add(sessionId);
4372
- const sessionDir = terminalSharedDir({ dataDir: config.dataDir, fsRoot: config.fsRoot, sessionId });
4373
- const discovered = await fsService.discoverManagedFiles(sessionDir);
4374
- const knownPaths = new Set(store.listFiles(sessionId, true).map((file) => file.path));
4375
- for (const file of discovered) {
4376
- if (knownPaths.has(file.path)) continue;
4377
- const expiresAt = file.mtimeMs + TERMINAL_FILE_TTL_MS;
4378
- if (expiresAt <= Date.now()) continue;
4379
- const now = Date.now();
4380
- const media = attachmentMedia(file.filename);
4381
- store.putFile({
4382
- id: randomUUID4(),
4383
- sessionId,
4384
- direction: "sent",
4385
- storage: "managed",
4386
- name: file.filename,
4387
- path: file.path,
4388
- mimeType: media.mimeType,
4389
- size: file.size,
4390
- kind: media.kind,
4391
- createdAt: file.mtimeMs,
4392
- updatedAt: now,
4393
- expiresAt
4394
- });
4395
- knownPaths.add(file.path);
4396
- }
4387
+ const fileBackfillsInFlight = /* @__PURE__ */ new Map();
4388
+ const backfillManagedFiles = (sessionId) => {
4389
+ if (backfilledFileSessions.has(sessionId)) return Promise.resolve();
4390
+ const existing = fileBackfillsInFlight.get(sessionId);
4391
+ if (existing) return existing;
4392
+ const task = (async () => {
4393
+ const sessionDir = terminalSharedDir({ dataDir: config.dataDir, fsRoot: config.fsRoot, sessionId });
4394
+ const discovered = await fsService.discoverManagedFiles(sessionDir);
4395
+ const knownPaths = new Set(store.listFiles(sessionId, true).map((file) => file.path));
4396
+ for (const file of discovered) {
4397
+ if (knownPaths.has(file.path)) continue;
4398
+ const expiresAt = file.mtimeMs + TERMINAL_FILE_TTL_MS;
4399
+ if (expiresAt <= Date.now()) continue;
4400
+ const now = Date.now();
4401
+ const media = attachmentMedia(file.filename);
4402
+ store.putFile({
4403
+ id: randomUUID4(),
4404
+ sessionId,
4405
+ direction: "sent",
4406
+ storage: "managed",
4407
+ name: file.filename,
4408
+ path: file.path,
4409
+ mimeType: media.mimeType,
4410
+ size: file.size,
4411
+ kind: media.kind,
4412
+ createdAt: file.mtimeMs,
4413
+ updatedAt: now,
4414
+ expiresAt
4415
+ });
4416
+ knownPaths.add(file.path);
4417
+ }
4418
+ backfilledFileSessions.add(sessionId);
4419
+ })();
4420
+ fileBackfillsInFlight.set(sessionId, task);
4421
+ void task.catch(() => void 0).finally(() => fileBackfillsInFlight.delete(sessionId));
4422
+ return task;
4423
+ };
4424
+ const fileAvailableWithinBudget = async (file) => {
4425
+ if (file.expiresAt <= Date.now()) return false;
4426
+ return new Promise((resolve7) => {
4427
+ let settled = false;
4428
+ const finish = (available) => {
4429
+ if (settled) return;
4430
+ settled = true;
4431
+ clearTimeout(timer);
4432
+ resolve7(available);
4433
+ };
4434
+ const timer = setTimeout(() => finish(true), FILE_HISTORY_AVAILABILITY_BUDGET_MS);
4435
+ void fsService.describeFile(file.path).then(
4436
+ () => finish(true),
4437
+ () => finish(false)
4438
+ );
4439
+ });
4397
4440
  };
4398
4441
  const sweepSharedFiles = () => {
4399
4442
  void (async () => {
@@ -5441,10 +5484,10 @@ function createServer(config, deps = {}) {
5441
5484
  reply.code(404).send({ error: "terminal session not found" });
5442
5485
  return;
5443
5486
  }
5444
- await backfillManagedFiles(request.params.id);
5487
+ await completionWithin(backfillManagedFiles(request.params.id), FILE_HISTORY_BACKFILL_BUDGET_MS);
5445
5488
  const files = await Promise.all(
5446
5489
  store.listFiles(request.params.id).map(async (file) => {
5447
- const available = file.expiresAt > Date.now() && await fsService.describeFile(file.path).then(() => true).catch(() => false);
5490
+ const available = await fileAvailableWithinBudget(file);
5448
5491
  return publicSessionFile(file, available);
5449
5492
  })
5450
5493
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roamcode.ai/server",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Host-native server for RoamCode",
5
5
  "homepage": "https://roamcode.ai",
6
6
  "bugs": "https://github.com/burakgon/roamcode/issues",
@@ -40,7 +40,7 @@
40
40
  "node-pty": "1.1.0",
41
41
  "web-push": "^3.6.7",
42
42
  "zod": "^4.4.3",
43
- "@roamcode.ai/web": "1.0.11"
43
+ "@roamcode.ai/web": "1.0.12"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/better-sqlite3": "^7.6.13",