replicas-engine 0.1.484 → 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 +251 -92
  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-v4";
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) {
@@ -2840,6 +2859,9 @@ var sendChatMessageRequestSchema = z2.object({
2840
2859
  z2.object({ type: z2.literal("automation"), executionId: z2.string() })
2841
2860
  ]).optional()
2842
2861
  }).passthrough();
2862
+ function isChatMessageSender(value) {
2863
+ return isRecord(value) && typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
2864
+ }
2843
2865
  function normalizeCodexAspTranscriptStatus(status, failed = false) {
2844
2866
  if (failed || status === "failed" || status === "declined") return "failed";
2845
2867
  if (status === "completed") return "completed";
@@ -4681,6 +4703,7 @@ var MEDIA_KIND = {
4681
4703
  AUDIO: "audio"
4682
4704
  };
4683
4705
  var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
4706
+ var MAX_ENGINE_LOG_BYTES = 50 * 1024 * 1024;
4684
4707
 
4685
4708
  // ../shared/src/skill-registry.ts
4686
4709
  var SKILL_REGISTRY_MANIFEST_VERSION = 1;
@@ -6126,8 +6149,8 @@ var StreamWriter = class {
6126
6149
  }
6127
6150
  return true;
6128
6151
  }
6129
- flush() {
6130
- return new Promise((resolve4) => {
6152
+ flush(signal) {
6153
+ return new Promise((resolve4, reject) => {
6131
6154
  if (!this.stream) {
6132
6155
  resolve4();
6133
6156
  return;
@@ -6137,8 +6160,17 @@ var StreamWriter = class {
6137
6160
  this.flushTimer = null;
6138
6161
  }
6139
6162
  const s = this.stream;
6140
- this.stream = null;
6141
- 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
+ });
6142
6174
  });
6143
6175
  }
6144
6176
  scheduleDropWarning() {
@@ -6199,8 +6231,11 @@ var EngineLogger = class {
6199
6231
  this.writer.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${message}
6200
6232
  `);
6201
6233
  }
6202
- flush() {
6203
- return this.writer.flush();
6234
+ flush(signal) {
6235
+ return this.writer.flush(signal);
6236
+ }
6237
+ error(...args) {
6238
+ this.log("ERROR", format(...args));
6204
6239
  }
6205
6240
  createSessionId() {
6206
6241
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
@@ -7109,8 +7144,8 @@ async function registerDesktopPreview() {
7109
7144
  // src/services/chat/chat-service.ts
7110
7145
  import { existsSync as existsSync7 } from "fs";
7111
7146
  import { appendFile as appendFile3, copyFile, mkdir as mkdir14, readFile as readFile14, rename as rename2, rm as rm2 } from "fs/promises";
7112
- import { homedir as homedir15 } from "os";
7113
- import { join as join22 } from "path";
7147
+ import { homedir as homedir14 } from "os";
7148
+ import { join as join24 } from "path";
7114
7149
  import { randomUUID as randomUUID5 } from "crypto";
7115
7150
 
7116
7151
  // src/managers/claude-manager.ts
@@ -9848,7 +9883,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
9848
9883
  var MIN_CODEX_CLI_VERSION = "0.144.6";
9849
9884
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
9850
9885
  var codexCliVersionEnsured = null;
9851
- var ENGINE_PACKAGE_VERSION = "0.1.484";
9886
+ var ENGINE_PACKAGE_VERSION = "0.1.492";
9852
9887
  var INITIALIZE_METHOD = "initialize";
9853
9888
  var INITIALIZED_NOTIFICATION = "initialized";
9854
9889
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -14124,13 +14159,31 @@ async function reconcileCanvasItems(filenames) {
14124
14159
 
14125
14160
  // src/services/upload-chat-transcripts.ts
14126
14161
  import { readdir as readdir7, readFile as readFile13 } from "fs/promises";
14127
- import { basename as basename2, join as join21 } from "path";
14128
- import { homedir as homedir14 } from "os";
14129
- var ENGINE_DIR3 = join21(homedir14(), ".replicas", "engine");
14162
+ import { basename as basename2, join as join22 } from "path";
14163
+
14164
+ // src/services/chat/chat-senders.ts
14165
+ import { join as join21 } from "path";
14166
+ var CHAT_SENDERS_DIR = join21(ENGINE_DIR2, "chat-senders");
14167
+ function chatMessageSendersFilePath(chatId) {
14168
+ return join21(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
14169
+ }
14170
+ function parseChatMessageSendersJsonl(content) {
14171
+ return content.split("\n").flatMap((line) => {
14172
+ if (!line.trim()) return [];
14173
+ try {
14174
+ const parsed = JSON.parse(line);
14175
+ return isChatMessageSender(parsed) ? [parsed] : [];
14176
+ } catch {
14177
+ return [];
14178
+ }
14179
+ });
14180
+ }
14181
+
14182
+ // src/services/upload-chat-transcripts.ts
14130
14183
  var HISTORY_DIRS = [
14131
- join21(ENGINE_DIR3, "claude-histories"),
14132
- join21(ENGINE_DIR3, "relay-histories"),
14133
- join21(ENGINE_DIR3, "codex-histories")
14184
+ join22(ENGINE_DIR2, "claude-histories"),
14185
+ join22(ENGINE_DIR2, "relay-histories"),
14186
+ join22(ENGINE_DIR2, "codex-histories")
14134
14187
  ];
14135
14188
  async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
14136
14189
  let flushed = 0;
@@ -14147,7 +14200,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
14147
14200
  if (!entry.endsWith(".jsonl")) continue;
14148
14201
  const chatId = basename2(entry, ".jsonl");
14149
14202
  tasks.push(
14150
- uploadChatTranscript(chatId, join21(dir, entry), chatsById.get(chatId)).then(() => {
14203
+ uploadChatTranscript(chatId, join22(dir, entry), chatsById.get(chatId)).then(() => {
14151
14204
  flushed++;
14152
14205
  }).catch((err) => {
14153
14206
  failed++;
@@ -14162,6 +14215,12 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
14162
14215
  async function uploadChatTranscript(chatId, filePath, chat) {
14163
14216
  const bytes = await readFile13(filePath);
14164
14217
  if (bytes.byteLength === 0) return;
14218
+ let senders;
14219
+ try {
14220
+ senders = parseChatMessageSendersJsonl(await readFile13(chatMessageSendersFilePath(chatId), "utf-8"));
14221
+ } catch (error) {
14222
+ if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
14223
+ }
14165
14224
  const form = new FormData();
14166
14225
  form.append("chat_id", chatId);
14167
14226
  if (chat) {
@@ -14180,6 +14239,9 @@ async function uploadChatTranscript(chatId, filePath, chat) {
14180
14239
  if (metadata.parentChatId) form.append("parent_chat_id", metadata.parentChatId);
14181
14240
  form.append("deleted_at", metadata.deletedAt ?? "");
14182
14241
  }
14242
+ if (senders !== void 0) {
14243
+ form.append("senders", JSON.stringify(senders));
14244
+ }
14183
14245
  form.append(
14184
14246
  "file",
14185
14247
  new Blob([new Uint8Array(bytes)], { type: "application/x-ndjson" }),
@@ -14225,15 +14287,137 @@ async function flushRepoState() {
14225
14287
  }
14226
14288
  }
14227
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
+
14228
14417
  // src/services/chat/chat-service.ts
14229
- var CHAT_SENDERS_DIR = join22(ENGINE_DIR2, "chat-senders");
14230
- var CODEX_AUTH_PATH2 = join22(homedir15(), ".codex", "auth.json");
14231
- var OPENCODE_AUTH_PATH3 = join22(homedir15(), ".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");
14232
14420
  var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
14233
- function isChatMessageSender(value) {
14234
- if (!isRecord4(value)) return false;
14235
- return typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
14236
- }
14237
14421
  function isCodexAvailable() {
14238
14422
  return existsSync7(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
14239
14423
  }
@@ -14506,31 +14690,16 @@ var ChatService = class {
14506
14690
  position: result.position
14507
14691
  };
14508
14692
  }
14509
- senderFilePath(chatId) {
14510
- return join22(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
14511
- }
14512
14693
  async appendSender(chatId, sender) {
14513
14694
  try {
14514
- await appendFile3(this.senderFilePath(chatId), JSON.stringify(sender) + "\n", "utf-8");
14695
+ await appendFile3(chatMessageSendersFilePath(chatId), JSON.stringify(sender) + "\n", "utf-8");
14515
14696
  } catch (error) {
14516
14697
  console.error("[ChatService] Failed to append sender record:", error);
14517
14698
  }
14518
14699
  }
14519
14700
  async readSenders(chatId) {
14520
14701
  try {
14521
- const content = await readFile14(this.senderFilePath(chatId), "utf-8");
14522
- const lines = content.split("\n").filter((line) => line.trim().length > 0);
14523
- const senders = [];
14524
- for (const line of lines) {
14525
- try {
14526
- const parsed = JSON.parse(line);
14527
- if (isChatMessageSender(parsed)) {
14528
- senders.push(parsed);
14529
- }
14530
- } catch {
14531
- }
14532
- }
14533
- return senders;
14702
+ return parseChatMessageSendersJsonl(await readFile14(chatMessageSendersFilePath(chatId), "utf-8"));
14534
14703
  } catch (error) {
14535
14704
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
14536
14705
  return [];
@@ -14709,8 +14878,8 @@ var ChatService = class {
14709
14878
  return descendants;
14710
14879
  }
14711
14880
  async deleteHistoryFile(persisted) {
14712
- await rm2(join22(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
14713
- await rm2(this.senderFilePath(persisted.id), { force: true });
14881
+ await rm2(join24(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
14882
+ await rm2(chatMessageSendersFilePath(persisted.id), { force: true });
14714
14883
  }
14715
14884
  async getChatHistory(chatId, page = {}) {
14716
14885
  const chat = this.requireChat(chatId);
@@ -14752,12 +14921,13 @@ var ChatService = class {
14752
14921
  const chatsById = new Map(
14753
14922
  [...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
14754
14923
  );
14755
- const [chatTranscripts, canvas, repoState] = await Promise.all([
14924
+ const [chatTranscripts, canvas, repoState, engineLogs] = await Promise.all([
14756
14925
  flushAllChatTranscripts(chatsById),
14757
14926
  flushAllCanvasItems(),
14758
- flushRepoState()
14927
+ flushRepoState(),
14928
+ flushAllEngineLogs()
14759
14929
  ]);
14760
- return { chatTranscripts, canvas, repoState };
14930
+ return { chatTranscripts, canvas, repoState, engineLogs };
14761
14931
  }
14762
14932
  createRuntimeChat(persisted) {
14763
14933
  const saveSession = async (sessionId) => {
@@ -14784,7 +14954,7 @@ var ChatService = class {
14784
14954
  if (persisted.provider === "claude") {
14785
14955
  provider = new ClaudeManager({
14786
14956
  workingDirectory: this.workingDirectory,
14787
- historyFilePath: join22(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
14957
+ historyFilePath: join24(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
14788
14958
  initialSessionId: persisted.providerSessionId,
14789
14959
  onSaveSessionId: saveSession,
14790
14960
  onTurnComplete: onProviderTurnComplete,
@@ -14793,7 +14963,7 @@ var ChatService = class {
14793
14963
  } else if (persisted.provider === "relay") {
14794
14964
  provider = new RelayManager({
14795
14965
  workingDirectory: this.workingDirectory,
14796
- historyFilePath: join22(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
14966
+ historyFilePath: join24(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
14797
14967
  initialSessionId: persisted.providerSessionId,
14798
14968
  onSaveSessionId: saveSession,
14799
14969
  onTurnComplete: onProviderTurnComplete,
@@ -14807,7 +14977,7 @@ var ChatService = class {
14807
14977
  } else if (persisted.provider === "cursor") {
14808
14978
  provider = new CursorManager({
14809
14979
  workingDirectory: this.workingDirectory,
14810
- historyFilePath: join22(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
14980
+ historyFilePath: join24(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
14811
14981
  initialSessionId: persisted.providerSessionId,
14812
14982
  onSaveSessionId: saveSession,
14813
14983
  onTurnComplete: onProviderTurnComplete,
@@ -14816,7 +14986,7 @@ var ChatService = class {
14816
14986
  } else if (persisted.provider === "opencode") {
14817
14987
  provider = new OpencodeManager({
14818
14988
  workingDirectory: this.workingDirectory,
14819
- historyFilePath: join22(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
14989
+ historyFilePath: join24(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
14820
14990
  initialSessionId: persisted.providerSessionId,
14821
14991
  onSaveSessionId: saveSession,
14822
14992
  onTurnComplete: onProviderTurnComplete,
@@ -14825,7 +14995,7 @@ var ChatService = class {
14825
14995
  } else if (persisted.provider === "pi") {
14826
14996
  provider = new PiManager({
14827
14997
  workingDirectory: this.workingDirectory,
14828
- historyFilePath: join22(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
14998
+ historyFilePath: join24(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
14829
14999
  initialSessionId: persisted.providerSessionId,
14830
15000
  onSaveSessionId: saveSession,
14831
15001
  onTurnComplete: onProviderTurnComplete,
@@ -14834,7 +15004,7 @@ var ChatService = class {
14834
15004
  } else {
14835
15005
  provider = new CodexAspManager({
14836
15006
  workingDirectory: this.workingDirectory,
14837
- historyFilePath: join22(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
15007
+ historyFilePath: join24(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
14838
15008
  initialSessionId: persisted.providerSessionId,
14839
15009
  onSaveSessionId: saveSession,
14840
15010
  onTurnComplete: onProviderTurnComplete,
@@ -14983,7 +15153,7 @@ var ChatService = class {
14983
15153
  });
14984
15154
  uploadChatTranscript(
14985
15155
  chatId,
14986
- join22(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
15156
+ join24(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
14987
15157
  this.toSummary(chat)
14988
15158
  ).catch((err) => {
14989
15159
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -15118,8 +15288,8 @@ var ChatService = class {
15118
15288
 
15119
15289
  // src/services/repo-file-service.ts
15120
15290
  import { execFile as execFile2 } from "child_process";
15121
- import { readFile as readFile15, realpath, stat as stat4 } from "fs/promises";
15122
- import { join as join23, 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";
15123
15293
  var CACHE_TTL_MS = 3e4;
15124
15294
  var SEARCH_TIMEOUT_MS = 15e3;
15125
15295
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -15279,11 +15449,11 @@ var RepoFileService = class {
15279
15449
  const repo = repos.find((r) => r.name === repoName);
15280
15450
  if (!repo) return null;
15281
15451
  try {
15282
- const fullPath = await realpath(resolve2(join23(repo.path, filePath)));
15452
+ const fullPath = await realpath(resolve2(join25(repo.path, filePath)));
15283
15453
  const repoRoot = await realpath(repo.path);
15284
15454
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
15285
15455
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
15286
- const fileStat = await stat4(fullPath);
15456
+ const fileStat = await stat5(fullPath);
15287
15457
  if (!fileStat.isFile()) return null;
15288
15458
  const sizeBytes = fileStat.size;
15289
15459
  if (isBinaryExtension(filePath)) {
@@ -15386,21 +15556,21 @@ var RepoFileService = class {
15386
15556
  // src/v1-routes.ts
15387
15557
  import { Hono } from "hono";
15388
15558
  import { z as z7 } from "zod";
15389
- import { readdir as readdir9, stat as stat5, readFile as readFile18 } from "fs/promises";
15390
- import { join as join26, 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";
15391
15561
 
15392
15562
  // src/services/warm-hooks-service.ts
15393
15563
  import { spawn as spawn4 } from "child_process";
15394
15564
  import { readFile as readFile17 } from "fs/promises";
15395
15565
  import { existsSync as existsSync8 } from "fs";
15396
- import { join as join25 } from "path";
15566
+ import { join as join27 } from "path";
15397
15567
 
15398
15568
  // src/services/warm-hook-logs-service.ts
15399
- import { mkdir as mkdir15, readFile as readFile16, writeFile as writeFile6, readdir as readdir8, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
15400
- import { homedir as homedir16 } from "os";
15401
- import { join as join24 } from "path";
15402
- var LOGS_DIR2 = join24(homedir16(), ".replicas", "warm-hook-logs");
15403
- var CURRENT_RUN_LOG = join24(LOGS_DIR2, "current-run.log");
15569
+ import { mkdir as mkdir15, readFile as readFile16, writeFile as writeFile6, readdir as readdir9, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
15570
+ import { homedir as homedir15 } from "os";
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");
15404
15574
  var GLOBAL_FILENAME = "global.json";
15405
15575
  function withPreview2(stored) {
15406
15576
  const preview = buildHookOutputPreview(stored.output);
@@ -15417,7 +15587,7 @@ var WarmHookLogsService = class {
15417
15587
  hookName: "organization",
15418
15588
  ...entry
15419
15589
  };
15420
- await writeFile6(join24(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
15590
+ await writeFile6(join26(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
15421
15591
  `, "utf-8");
15422
15592
  }
15423
15593
  async saveEnvironmentHookLog(entry) {
@@ -15427,7 +15597,7 @@ var WarmHookLogsService = class {
15427
15597
  hookName: "environment",
15428
15598
  ...entry
15429
15599
  };
15430
- await writeFile6(join24(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)}
15431
15601
  `, "utf-8");
15432
15602
  }
15433
15603
  async saveRepoHookLog(repoName, entry) {
@@ -15437,13 +15607,13 @@ var WarmHookLogsService = class {
15437
15607
  hookName: repoName,
15438
15608
  ...entry
15439
15609
  };
15440
- await writeFile6(join24(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
15610
+ await writeFile6(join26(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
15441
15611
  `, "utf-8");
15442
15612
  }
15443
15613
  async getAllLogs() {
15444
15614
  let files;
15445
15615
  try {
15446
- files = await readdir8(LOGS_DIR2);
15616
+ files = await readdir9(LOGS_DIR2);
15447
15617
  } catch (err) {
15448
15618
  if (err.code === "ENOENT") {
15449
15619
  return [];
@@ -15456,7 +15626,7 @@ var WarmHookLogsService = class {
15456
15626
  continue;
15457
15627
  }
15458
15628
  try {
15459
- const raw = await readFile16(join24(LOGS_DIR2, file), "utf-8");
15629
+ const raw = await readFile16(join26(LOGS_DIR2, file), "utf-8");
15460
15630
  const stored = JSON.parse(raw);
15461
15631
  logs.push(withPreview2(stored));
15462
15632
  } catch {
@@ -15494,7 +15664,7 @@ var WarmHookLogsService = class {
15494
15664
  async getFullOutput(hookType, hookName) {
15495
15665
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
15496
15666
  try {
15497
- const raw = await readFile16(join24(LOGS_DIR2, filename), "utf-8");
15667
+ const raw = await readFile16(join26(LOGS_DIR2, filename), "utf-8");
15498
15668
  const stored = JSON.parse(raw);
15499
15669
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
15500
15670
  return null;
@@ -15513,7 +15683,7 @@ var warmHookLogsService = new WarmHookLogsService();
15513
15683
  // src/services/warm-hooks-service.ts
15514
15684
  async function readRepoWarmHook(repoPath) {
15515
15685
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
15516
- const configPath = join25(repoPath, filename);
15686
+ const configPath = join27(repoPath, filename);
15517
15687
  if (!existsSync8(configPath)) {
15518
15688
  continue;
15519
15689
  }
@@ -16742,12 +16912,12 @@ data: ${JSON.stringify("Terminal session not found")}
16742
16912
  });
16743
16913
  app2.get("/logs", async (c) => {
16744
16914
  try {
16745
- const files = await readdir9(LOG_DIR).catch(() => []);
16915
+ const files = await readdir10(LOG_DIR).catch(() => []);
16746
16916
  const logFiles = files.filter((f) => f.endsWith(".log"));
16747
16917
  const sessions = await Promise.all(
16748
16918
  logFiles.map(async (filename) => {
16749
- const filePath = join26(LOG_DIR, filename);
16750
- const fileStat = await stat5(filePath);
16919
+ const filePath = join28(LOG_DIR, filename);
16920
+ const fileStat = await stat6(filePath);
16751
16921
  const sessionId = filename.replace(/\.log$/, "");
16752
16922
  return {
16753
16923
  sessionId,
@@ -16772,36 +16942,25 @@ data: ${JSON.stringify("Terminal session not found")}
16772
16942
  app2.get("/logs/:sessionId", async (c) => {
16773
16943
  try {
16774
16944
  const sessionId = c.req.param("sessionId");
16775
- if (!sessionId || /[/\\]/.test(sessionId) || sessionId.includes("..")) {
16945
+ if (!isValidEngineLogSessionId(sessionId)) {
16776
16946
  return c.json(jsonError("Invalid session ID"), 400);
16777
16947
  }
16778
16948
  const filePath = resolve3(LOG_DIR, `${sessionId}.log`);
16779
16949
  if (!filePath.startsWith(resolve3(LOG_DIR))) {
16780
16950
  return c.json(jsonError("Invalid session ID"), 400);
16781
16951
  }
16782
- const offset = parseInt(c.req.query("offset") || "0", 10);
16783
- const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
16784
16952
  let content;
16785
16953
  try {
16786
16954
  content = await readFile18(filePath, "utf-8");
16787
16955
  } catch {
16788
16956
  return c.json(jsonError("Log session not found"), 404);
16789
16957
  }
16790
- const allLines = content.split("\n");
16791
- if (allLines.length > 0 && allLines[allLines.length - 1] === "") {
16792
- allLines.pop();
16793
- }
16794
- const totalLines = allLines.length;
16795
- const slicedLines = allLines.slice(offset, offset + limit);
16796
- const hasMore = offset + limit < totalLines;
16797
- return c.json({
16958
+ return c.json(paginateEngineLogContent({
16798
16959
  sessionId,
16799
- totalLines,
16800
- offset,
16801
- limit,
16802
- hasMore,
16803
- lines: slicedLines
16804
- });
16960
+ content,
16961
+ offset: c.req.query("offset"),
16962
+ limit: c.req.query("limit")
16963
+ }));
16805
16964
  } catch (error) {
16806
16965
  return c.json(
16807
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.484",
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",