@roamcode.ai/server 1.0.11 → 1.0.13
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 +76 -32
- package/dist/start.js +76 -32
- package/package.json +2 -2
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.
|
|
2432
|
+
var RUNNING_VERSION = "1.0.13" ? "1.0.13".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
|
|
4983
|
-
|
|
4984
|
-
backfilledFileSessions.
|
|
4985
|
-
const
|
|
4986
|
-
|
|
4987
|
-
const
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
const
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
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 () => {
|
|
@@ -5048,9 +5091,10 @@ function createServer(config, deps = {}) {
|
|
|
5048
5091
|
const queryToken = typeof q?.token === "string" ? q.token : void 0;
|
|
5049
5092
|
const queryTicket = typeof q?.ticket === "string" ? q.ticket : void 0;
|
|
5050
5093
|
const isWsUpgradePath = path.endsWith("/ws") || path.endsWith("/terminal");
|
|
5094
|
+
const isTerminalFileContent = isGetLike && /^\/sessions\/[^/]+\/files\/[^/]+\/content$/.test(path);
|
|
5051
5095
|
const ticketOk = isWsUpgradePath && queryTicket !== void 0 && wsTickets.consume(queryTicket);
|
|
5052
5096
|
if (!ticketOk) {
|
|
5053
|
-
const queryTokenAllowed = isWsUpgradePath || path.startsWith("/images/") || path === "/fs/download";
|
|
5097
|
+
const queryTokenAllowed = isWsUpgradePath || path.startsWith("/images/") || path === "/fs/download" || isTerminalFileContent;
|
|
5054
5098
|
const token = extractBearerToken(request.headers.authorization) ?? (queryTokenAllowed ? queryToken : void 0);
|
|
5055
5099
|
const result = authGate.check(token, request.ip);
|
|
5056
5100
|
if (!result.ok) {
|
|
@@ -6054,10 +6098,10 @@ function createServer(config, deps = {}) {
|
|
|
6054
6098
|
reply.code(404).send({ error: "terminal session not found" });
|
|
6055
6099
|
return;
|
|
6056
6100
|
}
|
|
6057
|
-
await backfillManagedFiles(request.params.id);
|
|
6101
|
+
await completionWithin(backfillManagedFiles(request.params.id), FILE_HISTORY_BACKFILL_BUDGET_MS);
|
|
6058
6102
|
const files = await Promise.all(
|
|
6059
6103
|
store.listFiles(request.params.id).map(async (file) => {
|
|
6060
|
-
const available =
|
|
6104
|
+
const available = await fileAvailableWithinBudget(file);
|
|
6061
6105
|
return publicSessionFile(file, available);
|
|
6062
6106
|
})
|
|
6063
6107
|
);
|
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.
|
|
874
|
+
var RUNNING_VERSION = "1.0.13" ? "1.0.13".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
|
|
4370
|
-
|
|
4371
|
-
backfilledFileSessions.
|
|
4372
|
-
const
|
|
4373
|
-
|
|
4374
|
-
const
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
const
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
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 () => {
|
|
@@ -4435,9 +4478,10 @@ function createServer(config, deps = {}) {
|
|
|
4435
4478
|
const queryToken = typeof q?.token === "string" ? q.token : void 0;
|
|
4436
4479
|
const queryTicket = typeof q?.ticket === "string" ? q.ticket : void 0;
|
|
4437
4480
|
const isWsUpgradePath = path.endsWith("/ws") || path.endsWith("/terminal");
|
|
4481
|
+
const isTerminalFileContent = isGetLike && /^\/sessions\/[^/]+\/files\/[^/]+\/content$/.test(path);
|
|
4438
4482
|
const ticketOk = isWsUpgradePath && queryTicket !== void 0 && wsTickets.consume(queryTicket);
|
|
4439
4483
|
if (!ticketOk) {
|
|
4440
|
-
const queryTokenAllowed = isWsUpgradePath || path.startsWith("/images/") || path === "/fs/download";
|
|
4484
|
+
const queryTokenAllowed = isWsUpgradePath || path.startsWith("/images/") || path === "/fs/download" || isTerminalFileContent;
|
|
4441
4485
|
const token = extractBearerToken(request.headers.authorization) ?? (queryTokenAllowed ? queryToken : void 0);
|
|
4442
4486
|
const result = authGate.check(token, request.ip);
|
|
4443
4487
|
if (!result.ok) {
|
|
@@ -5441,10 +5485,10 @@ function createServer(config, deps = {}) {
|
|
|
5441
5485
|
reply.code(404).send({ error: "terminal session not found" });
|
|
5442
5486
|
return;
|
|
5443
5487
|
}
|
|
5444
|
-
await backfillManagedFiles(request.params.id);
|
|
5488
|
+
await completionWithin(backfillManagedFiles(request.params.id), FILE_HISTORY_BACKFILL_BUDGET_MS);
|
|
5445
5489
|
const files = await Promise.all(
|
|
5446
5490
|
store.listFiles(request.params.id).map(async (file) => {
|
|
5447
|
-
const available =
|
|
5491
|
+
const available = await fileAvailableWithinBudget(file);
|
|
5448
5492
|
return publicSessionFile(file, available);
|
|
5449
5493
|
})
|
|
5450
5494
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roamcode.ai/server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
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.
|
|
43
|
+
"@roamcode.ai/web": "1.0.13"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/better-sqlite3": "^7.6.13",
|