replicas-engine 0.1.485 → 0.1.492

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.
Files changed (2) hide show
  1. package/dist/src/index.js +209 -60
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -408,6 +408,25 @@ function classifyCanvasFilename(filename) {
408
408
  const ext = idx === -1 ? "" : filename.slice(idx).toLowerCase();
409
409
  return CANVAS_KIND_BY_EXTENSION[ext] ?? { kind: "other", mimeType: "application/octet-stream" };
410
410
  }
411
+ function isValidEngineLogSessionId(sessionId) {
412
+ return Boolean(sessionId) && !/[/\\]/.test(sessionId) && !sessionId.includes("..");
413
+ }
414
+ function paginateEngineLogContent(input) {
415
+ const parsedOffset = Number.parseInt(input.offset ?? "0", 10);
416
+ const parsedLimit = Number.parseInt(input.limit ?? "500", 10);
417
+ const offset = Number.isFinite(parsedOffset) && parsedOffset >= 0 ? parsedOffset : 0;
418
+ const limit = Number.isFinite(parsedLimit) && parsedLimit >= 0 ? Math.min(parsedLimit, 5e3) : 500;
419
+ const lines = input.content.split("\n");
420
+ if (lines.at(-1) === "") lines.pop();
421
+ return {
422
+ sessionId: input.sessionId,
423
+ totalLines: lines.length,
424
+ offset,
425
+ limit,
426
+ hasMore: offset + limit < lines.length,
427
+ lines: lines.slice(offset, offset + limit)
428
+ };
429
+ }
411
430
 
412
431
  // ../shared/src/context-usage.ts
413
432
  var CODEX_CATEGORY_COLORS = {
@@ -583,7 +602,7 @@ var WORKSPACE_SIZES = ["small", "large"];
583
602
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
584
603
 
585
604
  // ../shared/src/e2b.ts
586
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-23-v5";
605
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-23-v12";
587
606
 
588
607
  // ../shared/src/runtime-env.ts
589
608
  function shellQuotePosix(value) {
@@ -4684,6 +4703,7 @@ var MEDIA_KIND = {
4684
4703
  AUDIO: "audio"
4685
4704
  };
4686
4705
  var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
4706
+ var MAX_ENGINE_LOG_BYTES = 50 * 1024 * 1024;
4687
4707
 
4688
4708
  // ../shared/src/skill-registry.ts
4689
4709
  var SKILL_REGISTRY_MANIFEST_VERSION = 1;
@@ -6129,8 +6149,8 @@ var StreamWriter = class {
6129
6149
  }
6130
6150
  return true;
6131
6151
  }
6132
- flush() {
6133
- return new Promise((resolve4) => {
6152
+ flush(signal) {
6153
+ return new Promise((resolve4, reject) => {
6134
6154
  if (!this.stream) {
6135
6155
  resolve4();
6136
6156
  return;
@@ -6140,8 +6160,17 @@ var StreamWriter = class {
6140
6160
  this.flushTimer = null;
6141
6161
  }
6142
6162
  const s = this.stream;
6143
- this.stream = null;
6144
- s.end(() => resolve4());
6163
+ const onAbort = () => reject(signal?.reason);
6164
+ if (signal?.aborted) {
6165
+ reject(signal.reason);
6166
+ return;
6167
+ }
6168
+ signal?.addEventListener("abort", onAbort, { once: true });
6169
+ s.write("", (error) => {
6170
+ signal?.removeEventListener("abort", onAbort);
6171
+ if (error) reject(error);
6172
+ else resolve4();
6173
+ });
6145
6174
  });
6146
6175
  }
6147
6176
  scheduleDropWarning() {
@@ -6202,8 +6231,11 @@ var EngineLogger = class {
6202
6231
  this.writer.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${message}
6203
6232
  `);
6204
6233
  }
6205
- flush() {
6206
- return this.writer.flush();
6234
+ flush(signal) {
6235
+ return this.writer.flush(signal);
6236
+ }
6237
+ error(...args) {
6238
+ this.log("ERROR", format(...args));
6207
6239
  }
6208
6240
  createSessionId() {
6209
6241
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
@@ -7113,7 +7145,7 @@ async function registerDesktopPreview() {
7113
7145
  import { existsSync as existsSync7 } from "fs";
7114
7146
  import { appendFile as appendFile3, copyFile, mkdir as mkdir14, readFile as readFile14, rename as rename2, rm as rm2 } from "fs/promises";
7115
7147
  import { homedir as homedir14 } from "os";
7116
- import { join as join23 } from "path";
7148
+ import { join as join24 } from "path";
7117
7149
  import { randomUUID as randomUUID5 } from "crypto";
7118
7150
 
7119
7151
  // src/managers/claude-manager.ts
@@ -9851,7 +9883,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
9851
9883
  var MIN_CODEX_CLI_VERSION = "0.144.6";
9852
9884
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
9853
9885
  var codexCliVersionEnsured = null;
9854
- var ENGINE_PACKAGE_VERSION = "0.1.485";
9886
+ var ENGINE_PACKAGE_VERSION = "0.1.492";
9855
9887
  var INITIALIZE_METHOD = "initialize";
9856
9888
  var INITIALIZED_NOTIFICATION = "initialized";
9857
9889
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -14255,9 +14287,136 @@ async function flushRepoState() {
14255
14287
  }
14256
14288
  }
14257
14289
 
14290
+ // src/services/upload-engine-logs.ts
14291
+ import { createReadStream } from "fs";
14292
+ import { readdir as readdir8, stat as stat4 } from "fs/promises";
14293
+ import { join as join23 } from "path";
14294
+ var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
14295
+ var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
14296
+ var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
14297
+ async function flushAllEngineLogs() {
14298
+ let flushed = 0;
14299
+ let skipped = 0;
14300
+ let failed = 0;
14301
+ const deadline = Date.now() + ENGINE_LOG_FLUSH_TIMEOUT_MS;
14302
+ await runBeforeDeadline((signal) => engineLogger.flush(signal), deadline);
14303
+ const files = await runBeforeDeadline(() => readdir8(LOG_DIR), deadline).catch(() => []);
14304
+ const currentFilename = engineLogger.sessionId ? `${engineLogger.sessionId}.log` : null;
14305
+ const filenames = files.filter((filename) => {
14306
+ if (!filename.endsWith(".log")) return false;
14307
+ const sessionId = filename.slice(0, -".log".length);
14308
+ if (!isValidEngineLogSessionId(sessionId)) {
14309
+ skipped++;
14310
+ return false;
14311
+ }
14312
+ return true;
14313
+ }).sort((a, b) => {
14314
+ if (a === currentFilename) return -1;
14315
+ if (b === currentFilename) return 1;
14316
+ return b.localeCompare(a);
14317
+ });
14318
+ skipped += Math.max(0, filenames.length - MAX_ENGINE_LOG_FLUSH_SESSIONS);
14319
+ const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
14320
+ try {
14321
+ const sessionId = filename.slice(0, -".log".length);
14322
+ const filePath = join23(LOG_DIR, filename);
14323
+ const fileStat = await runBeforeDeadline(() => stat4(filePath), deadline);
14324
+ if (!fileStat.isFile()) {
14325
+ skipped++;
14326
+ return null;
14327
+ }
14328
+ return { sessionId, filename, filePath, fileStat };
14329
+ } catch (error) {
14330
+ failed++;
14331
+ engineLogger.error("[EngineLogUploader] upload failed:", { filename, error });
14332
+ return null;
14333
+ }
14334
+ }))).filter((candidate) => candidate !== null);
14335
+ const selected = [];
14336
+ let selectedBytes = 0;
14337
+ for (const candidate of candidates) {
14338
+ const length = Math.min(candidate.fileStat.size, MAX_ENGINE_LOG_FLUSH_BYTES);
14339
+ if (selected.length >= MAX_ENGINE_LOG_FLUSH_SESSIONS || selectedBytes + length > MAX_ENGINE_LOG_FLUSH_BYTES) {
14340
+ skipped++;
14341
+ continue;
14342
+ }
14343
+ selected.push({ ...candidate, offset: candidate.fileStat.size - length, length });
14344
+ selectedBytes += length;
14345
+ }
14346
+ for (const [index, candidate] of selected.entries()) {
14347
+ if (Date.now() >= deadline) {
14348
+ skipped += selected.length - index;
14349
+ break;
14350
+ }
14351
+ try {
14352
+ const content = await runBeforeDeadline(async (signal) => {
14353
+ if (candidate.length === 0) return Buffer.alloc(0);
14354
+ const chunks = [];
14355
+ for await (const chunk of createReadStream(candidate.filePath, {
14356
+ start: candidate.offset,
14357
+ end: candidate.offset + candidate.length - 1,
14358
+ signal
14359
+ })) {
14360
+ chunks.push(Buffer.from(chunk));
14361
+ }
14362
+ return Buffer.concat(chunks);
14363
+ }, deadline);
14364
+ const timeoutMs = deadline - Date.now();
14365
+ if (timeoutMs <= 0) {
14366
+ skipped += selected.length - index;
14367
+ break;
14368
+ }
14369
+ await uploadEngineLog({
14370
+ sessionId: candidate.sessionId,
14371
+ filename: candidate.filename,
14372
+ updatedAt: candidate.fileStat.mtime.toISOString(),
14373
+ content
14374
+ }, timeoutMs);
14375
+ flushed++;
14376
+ } catch (error) {
14377
+ failed++;
14378
+ engineLogger.error("[EngineLogUploader] upload failed:", { filename: candidate.filename, error });
14379
+ }
14380
+ }
14381
+ return { flushed, skipped, failed };
14382
+ }
14383
+ function runBeforeDeadline(operation, deadline) {
14384
+ const timeoutMs = deadline - Date.now();
14385
+ if (timeoutMs <= 0) return Promise.reject(new DOMException("engine log flush deadline exceeded", "TimeoutError"));
14386
+ const signal = AbortSignal.timeout(timeoutMs);
14387
+ return new Promise((resolve4, reject) => {
14388
+ const onAbort = () => reject(signal.reason);
14389
+ signal.addEventListener("abort", onAbort, { once: true });
14390
+ operation(signal).then(
14391
+ (value) => {
14392
+ signal.removeEventListener("abort", onAbort);
14393
+ resolve4(value);
14394
+ },
14395
+ (error) => {
14396
+ signal.removeEventListener("abort", onAbort);
14397
+ reject(error);
14398
+ }
14399
+ );
14400
+ });
14401
+ }
14402
+ async function uploadEngineLog(input, timeoutMs) {
14403
+ const form = new FormData();
14404
+ form.append("session_id", input.sessionId);
14405
+ form.append("filename", input.filename);
14406
+ form.append("updated_at", input.updatedAt);
14407
+ form.append("file", new Blob([input.content], { type: "text/plain" }), input.filename);
14408
+ const response = await monolithRequest("/v1/engine/logs", {
14409
+ body: form,
14410
+ signal: AbortSignal.timeout(timeoutMs)
14411
+ });
14412
+ if (!response.ok) {
14413
+ throw new Error(`upload failed: ${response.status} ${await response.text()}`);
14414
+ }
14415
+ }
14416
+
14258
14417
  // src/services/chat/chat-service.ts
14259
- var CODEX_AUTH_PATH2 = join23(homedir14(), ".codex", "auth.json");
14260
- var OPENCODE_AUTH_PATH3 = join23(homedir14(), ".local", "share", "opencode", "auth.json");
14418
+ var CODEX_AUTH_PATH2 = join24(homedir14(), ".codex", "auth.json");
14419
+ var OPENCODE_AUTH_PATH3 = join24(homedir14(), ".local", "share", "opencode", "auth.json");
14261
14420
  var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
14262
14421
  function isCodexAvailable() {
14263
14422
  return existsSync7(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
@@ -14719,7 +14878,7 @@ var ChatService = class {
14719
14878
  return descendants;
14720
14879
  }
14721
14880
  async deleteHistoryFile(persisted) {
14722
- await rm2(join23(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
14881
+ await rm2(join24(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
14723
14882
  await rm2(chatMessageSendersFilePath(persisted.id), { force: true });
14724
14883
  }
14725
14884
  async getChatHistory(chatId, page = {}) {
@@ -14762,12 +14921,13 @@ var ChatService = class {
14762
14921
  const chatsById = new Map(
14763
14922
  [...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
14764
14923
  );
14765
- const [chatTranscripts, canvas, repoState] = await Promise.all([
14924
+ const [chatTranscripts, canvas, repoState, engineLogs] = await Promise.all([
14766
14925
  flushAllChatTranscripts(chatsById),
14767
14926
  flushAllCanvasItems(),
14768
- flushRepoState()
14927
+ flushRepoState(),
14928
+ flushAllEngineLogs()
14769
14929
  ]);
14770
- return { chatTranscripts, canvas, repoState };
14930
+ return { chatTranscripts, canvas, repoState, engineLogs };
14771
14931
  }
14772
14932
  createRuntimeChat(persisted) {
14773
14933
  const saveSession = async (sessionId) => {
@@ -14794,7 +14954,7 @@ var ChatService = class {
14794
14954
  if (persisted.provider === "claude") {
14795
14955
  provider = new ClaudeManager({
14796
14956
  workingDirectory: this.workingDirectory,
14797
- historyFilePath: join23(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
14957
+ historyFilePath: join24(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
14798
14958
  initialSessionId: persisted.providerSessionId,
14799
14959
  onSaveSessionId: saveSession,
14800
14960
  onTurnComplete: onProviderTurnComplete,
@@ -14803,7 +14963,7 @@ var ChatService = class {
14803
14963
  } else if (persisted.provider === "relay") {
14804
14964
  provider = new RelayManager({
14805
14965
  workingDirectory: this.workingDirectory,
14806
- historyFilePath: join23(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
14966
+ historyFilePath: join24(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
14807
14967
  initialSessionId: persisted.providerSessionId,
14808
14968
  onSaveSessionId: saveSession,
14809
14969
  onTurnComplete: onProviderTurnComplete,
@@ -14817,7 +14977,7 @@ var ChatService = class {
14817
14977
  } else if (persisted.provider === "cursor") {
14818
14978
  provider = new CursorManager({
14819
14979
  workingDirectory: this.workingDirectory,
14820
- historyFilePath: join23(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
14980
+ historyFilePath: join24(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
14821
14981
  initialSessionId: persisted.providerSessionId,
14822
14982
  onSaveSessionId: saveSession,
14823
14983
  onTurnComplete: onProviderTurnComplete,
@@ -14826,7 +14986,7 @@ var ChatService = class {
14826
14986
  } else if (persisted.provider === "opencode") {
14827
14987
  provider = new OpencodeManager({
14828
14988
  workingDirectory: this.workingDirectory,
14829
- historyFilePath: join23(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
14989
+ historyFilePath: join24(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
14830
14990
  initialSessionId: persisted.providerSessionId,
14831
14991
  onSaveSessionId: saveSession,
14832
14992
  onTurnComplete: onProviderTurnComplete,
@@ -14835,7 +14995,7 @@ var ChatService = class {
14835
14995
  } else if (persisted.provider === "pi") {
14836
14996
  provider = new PiManager({
14837
14997
  workingDirectory: this.workingDirectory,
14838
- historyFilePath: join23(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
14998
+ historyFilePath: join24(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
14839
14999
  initialSessionId: persisted.providerSessionId,
14840
15000
  onSaveSessionId: saveSession,
14841
15001
  onTurnComplete: onProviderTurnComplete,
@@ -14844,7 +15004,7 @@ var ChatService = class {
14844
15004
  } else {
14845
15005
  provider = new CodexAspManager({
14846
15006
  workingDirectory: this.workingDirectory,
14847
- historyFilePath: join23(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
15007
+ historyFilePath: join24(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
14848
15008
  initialSessionId: persisted.providerSessionId,
14849
15009
  onSaveSessionId: saveSession,
14850
15010
  onTurnComplete: onProviderTurnComplete,
@@ -14993,7 +15153,7 @@ var ChatService = class {
14993
15153
  });
14994
15154
  uploadChatTranscript(
14995
15155
  chatId,
14996
- join23(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
15156
+ join24(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
14997
15157
  this.toSummary(chat)
14998
15158
  ).catch((err) => {
14999
15159
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -15128,8 +15288,8 @@ var ChatService = class {
15128
15288
 
15129
15289
  // src/services/repo-file-service.ts
15130
15290
  import { execFile as execFile2 } from "child_process";
15131
- import { readFile as readFile15, realpath, stat as stat4 } from "fs/promises";
15132
- import { join as join24, resolve as resolve2, extname as extname2 } from "path";
15291
+ import { readFile as readFile15, realpath, stat as stat5 } from "fs/promises";
15292
+ import { join as join25, resolve as resolve2, extname as extname2 } from "path";
15133
15293
  var CACHE_TTL_MS = 3e4;
15134
15294
  var SEARCH_TIMEOUT_MS = 15e3;
15135
15295
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -15289,11 +15449,11 @@ var RepoFileService = class {
15289
15449
  const repo = repos.find((r) => r.name === repoName);
15290
15450
  if (!repo) return null;
15291
15451
  try {
15292
- const fullPath = await realpath(resolve2(join24(repo.path, filePath)));
15452
+ const fullPath = await realpath(resolve2(join25(repo.path, filePath)));
15293
15453
  const repoRoot = await realpath(repo.path);
15294
15454
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
15295
15455
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
15296
- const fileStat = await stat4(fullPath);
15456
+ const fileStat = await stat5(fullPath);
15297
15457
  if (!fileStat.isFile()) return null;
15298
15458
  const sizeBytes = fileStat.size;
15299
15459
  if (isBinaryExtension(filePath)) {
@@ -15396,21 +15556,21 @@ var RepoFileService = class {
15396
15556
  // src/v1-routes.ts
15397
15557
  import { Hono } from "hono";
15398
15558
  import { z as z7 } from "zod";
15399
- import { readdir as readdir9, stat as stat5, readFile as readFile18 } from "fs/promises";
15400
- import { join as join27, resolve as resolve3 } from "path";
15559
+ import { readdir as readdir10, stat as stat6, readFile as readFile18 } from "fs/promises";
15560
+ import { join as join28, resolve as resolve3 } from "path";
15401
15561
 
15402
15562
  // src/services/warm-hooks-service.ts
15403
15563
  import { spawn as spawn4 } from "child_process";
15404
15564
  import { readFile as readFile17 } from "fs/promises";
15405
15565
  import { existsSync as existsSync8 } from "fs";
15406
- import { join as join26 } from "path";
15566
+ import { join as join27 } from "path";
15407
15567
 
15408
15568
  // src/services/warm-hook-logs-service.ts
15409
- import { mkdir as mkdir15, readFile as readFile16, writeFile as writeFile6, readdir as readdir8, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
15569
+ import { mkdir as mkdir15, readFile as readFile16, writeFile as writeFile6, readdir as readdir9, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
15410
15570
  import { homedir as homedir15 } from "os";
15411
- import { join as join25 } from "path";
15412
- var LOGS_DIR2 = join25(homedir15(), ".replicas", "warm-hook-logs");
15413
- var CURRENT_RUN_LOG = join25(LOGS_DIR2, "current-run.log");
15571
+ import { join as join26 } from "path";
15572
+ var LOGS_DIR2 = join26(homedir15(), ".replicas", "warm-hook-logs");
15573
+ var CURRENT_RUN_LOG = join26(LOGS_DIR2, "current-run.log");
15414
15574
  var GLOBAL_FILENAME = "global.json";
15415
15575
  function withPreview2(stored) {
15416
15576
  const preview = buildHookOutputPreview(stored.output);
@@ -15427,7 +15587,7 @@ var WarmHookLogsService = class {
15427
15587
  hookName: "organization",
15428
15588
  ...entry
15429
15589
  };
15430
- await writeFile6(join25(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
15590
+ await writeFile6(join26(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
15431
15591
  `, "utf-8");
15432
15592
  }
15433
15593
  async saveEnvironmentHookLog(entry) {
@@ -15437,7 +15597,7 @@ var WarmHookLogsService = class {
15437
15597
  hookName: "environment",
15438
15598
  ...entry
15439
15599
  };
15440
- await writeFile6(join25(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
15600
+ await writeFile6(join26(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
15441
15601
  `, "utf-8");
15442
15602
  }
15443
15603
  async saveRepoHookLog(repoName, entry) {
@@ -15447,13 +15607,13 @@ var WarmHookLogsService = class {
15447
15607
  hookName: repoName,
15448
15608
  ...entry
15449
15609
  };
15450
- await writeFile6(join25(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
15610
+ await writeFile6(join26(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
15451
15611
  `, "utf-8");
15452
15612
  }
15453
15613
  async getAllLogs() {
15454
15614
  let files;
15455
15615
  try {
15456
- files = await readdir8(LOGS_DIR2);
15616
+ files = await readdir9(LOGS_DIR2);
15457
15617
  } catch (err) {
15458
15618
  if (err.code === "ENOENT") {
15459
15619
  return [];
@@ -15466,7 +15626,7 @@ var WarmHookLogsService = class {
15466
15626
  continue;
15467
15627
  }
15468
15628
  try {
15469
- const raw = await readFile16(join25(LOGS_DIR2, file), "utf-8");
15629
+ const raw = await readFile16(join26(LOGS_DIR2, file), "utf-8");
15470
15630
  const stored = JSON.parse(raw);
15471
15631
  logs.push(withPreview2(stored));
15472
15632
  } catch {
@@ -15504,7 +15664,7 @@ var WarmHookLogsService = class {
15504
15664
  async getFullOutput(hookType, hookName) {
15505
15665
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
15506
15666
  try {
15507
- const raw = await readFile16(join25(LOGS_DIR2, filename), "utf-8");
15667
+ const raw = await readFile16(join26(LOGS_DIR2, filename), "utf-8");
15508
15668
  const stored = JSON.parse(raw);
15509
15669
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
15510
15670
  return null;
@@ -15523,7 +15683,7 @@ var warmHookLogsService = new WarmHookLogsService();
15523
15683
  // src/services/warm-hooks-service.ts
15524
15684
  async function readRepoWarmHook(repoPath) {
15525
15685
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
15526
- const configPath = join26(repoPath, filename);
15686
+ const configPath = join27(repoPath, filename);
15527
15687
  if (!existsSync8(configPath)) {
15528
15688
  continue;
15529
15689
  }
@@ -16752,12 +16912,12 @@ data: ${JSON.stringify("Terminal session not found")}
16752
16912
  });
16753
16913
  app2.get("/logs", async (c) => {
16754
16914
  try {
16755
- const files = await readdir9(LOG_DIR).catch(() => []);
16915
+ const files = await readdir10(LOG_DIR).catch(() => []);
16756
16916
  const logFiles = files.filter((f) => f.endsWith(".log"));
16757
16917
  const sessions = await Promise.all(
16758
16918
  logFiles.map(async (filename) => {
16759
- const filePath = join27(LOG_DIR, filename);
16760
- const fileStat = await stat5(filePath);
16919
+ const filePath = join28(LOG_DIR, filename);
16920
+ const fileStat = await stat6(filePath);
16761
16921
  const sessionId = filename.replace(/\.log$/, "");
16762
16922
  return {
16763
16923
  sessionId,
@@ -16782,36 +16942,25 @@ data: ${JSON.stringify("Terminal session not found")}
16782
16942
  app2.get("/logs/:sessionId", async (c) => {
16783
16943
  try {
16784
16944
  const sessionId = c.req.param("sessionId");
16785
- if (!sessionId || /[/\\]/.test(sessionId) || sessionId.includes("..")) {
16945
+ if (!isValidEngineLogSessionId(sessionId)) {
16786
16946
  return c.json(jsonError("Invalid session ID"), 400);
16787
16947
  }
16788
16948
  const filePath = resolve3(LOG_DIR, `${sessionId}.log`);
16789
16949
  if (!filePath.startsWith(resolve3(LOG_DIR))) {
16790
16950
  return c.json(jsonError("Invalid session ID"), 400);
16791
16951
  }
16792
- const offset = parseInt(c.req.query("offset") || "0", 10);
16793
- const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
16794
16952
  let content;
16795
16953
  try {
16796
16954
  content = await readFile18(filePath, "utf-8");
16797
16955
  } catch {
16798
16956
  return c.json(jsonError("Log session not found"), 404);
16799
16957
  }
16800
- const allLines = content.split("\n");
16801
- if (allLines.length > 0 && allLines[allLines.length - 1] === "") {
16802
- allLines.pop();
16803
- }
16804
- const totalLines = allLines.length;
16805
- const slicedLines = allLines.slice(offset, offset + limit);
16806
- const hasMore = offset + limit < totalLines;
16807
- return c.json({
16958
+ return c.json(paginateEngineLogContent({
16808
16959
  sessionId,
16809
- totalLines,
16810
- offset,
16811
- limit,
16812
- hasMore,
16813
- lines: slicedLines
16814
- });
16960
+ content,
16961
+ offset: c.req.query("offset"),
16962
+ limit: c.req.query("limit")
16963
+ }));
16815
16964
  } catch (error) {
16816
16965
  return c.json(
16817
16966
  jsonError("Failed to read log", error instanceof Error ? error.message : "Unknown error"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.485",
3
+ "version": "0.1.492",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",