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.
- package/dist/src/index.js +251 -92
- 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-
|
|
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
|
-
|
|
6141
|
-
|
|
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
|
|
7113
|
-
import { join as
|
|
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.
|
|
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
|
|
14128
|
-
|
|
14129
|
-
|
|
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
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
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,
|
|
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
|
|
14230
|
-
var
|
|
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(
|
|
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
|
-
|
|
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(
|
|
14713
|
-
await rm2(
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
15122
|
-
import { join as
|
|
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(
|
|
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
|
|
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
|
|
15390
|
-
import { join as
|
|
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
|
|
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
|
|
15400
|
-
import { homedir as
|
|
15401
|
-
import { join as
|
|
15402
|
-
var LOGS_DIR2 =
|
|
15403
|
-
var 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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
16750
|
-
const fileStat = await
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
16800
|
-
offset,
|
|
16801
|
-
limit
|
|
16802
|
-
|
|
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"),
|