replicas-engine 0.1.533 → 0.1.535
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/README.md +1 -0
- package/dist/src/index.js +258 -96
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -203,6 +203,11 @@ function detectLanguageByPath(filePath) {
|
|
|
203
203
|
return EXT_TO_LANGUAGE[ext] ?? null;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
// ../shared/src/chat-turn-usage.ts
|
|
207
|
+
function isChatTurnUsageRecord(value) {
|
|
208
|
+
return isRecord(value) && typeof value.turnId === "string" && typeof value.chatId === "string" && typeof value.provider === "string" && isValidAgentProvider(value.provider) && typeof value.model === "string" && (value.senderUserId === void 0 || typeof value.senderUserId === "string") && typeof value.occurredAt === "string" && typeof value.seconds === "number" && Number.isFinite(value.seconds) && value.seconds >= 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
206
211
|
// ../shared/src/aster.ts
|
|
207
212
|
var ASTER_PROVIDER = "aster";
|
|
208
213
|
var ASTER_BASE_URL = "https://api.asterlab.ai/v1";
|
|
@@ -258,6 +263,17 @@ var OPENROUTER_MODELS = [
|
|
|
258
263
|
"xiaomi/mimo-v2.5-pro",
|
|
259
264
|
"moonshotai/kimi-k2.6"
|
|
260
265
|
];
|
|
266
|
+
var FALLBACK_AGENT_MODEL = {
|
|
267
|
+
claude: DEFAULT_CLAUDE_MODEL,
|
|
268
|
+
codex: DEFAULT_CODEX_MODEL,
|
|
269
|
+
cursor: DEFAULT_CURSOR_MODEL,
|
|
270
|
+
opencode: DEFAULT_OPENCODE_MODEL,
|
|
271
|
+
pi: DEFAULT_PI_MODEL,
|
|
272
|
+
relay: DEFAULT_CLAUDE_MODEL
|
|
273
|
+
};
|
|
274
|
+
function getDefaultAgentModel(provider, overrides) {
|
|
275
|
+
return overrides?.[provider]?.[0] ?? FALLBACK_AGENT_MODEL[provider];
|
|
276
|
+
}
|
|
261
277
|
function normalizeClaudeModel(model) {
|
|
262
278
|
if (model === "opus" || model === CLAUDE_OPUS_1M_MODEL || model === LEGACY_CLAUDE_OPUS_1M_MODEL) {
|
|
263
279
|
return DEFAULT_CLAUDE_MODEL;
|
|
@@ -623,7 +639,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
623
639
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
624
640
|
|
|
625
641
|
// ../shared/src/e2b.ts
|
|
626
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-31-
|
|
642
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-31-v6";
|
|
627
643
|
|
|
628
644
|
// ../shared/src/runtime-env.ts
|
|
629
645
|
function shellQuotePosix(value) {
|
|
@@ -4949,7 +4965,7 @@ var MAX_ENGINE_LOG_BYTES = 50 * 1024 * 1024;
|
|
|
4949
4965
|
var SKILL_REGISTRY_MANIFEST_VERSION = 1;
|
|
4950
4966
|
|
|
4951
4967
|
// src/index.ts
|
|
4952
|
-
import { randomUUID as
|
|
4968
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
4953
4969
|
import { connect } from "net";
|
|
4954
4970
|
|
|
4955
4971
|
// src/utils/exec.ts
|
|
@@ -7401,10 +7417,10 @@ async function registerDesktopPreview() {
|
|
|
7401
7417
|
|
|
7402
7418
|
// src/services/chat/chat-service.ts
|
|
7403
7419
|
import { existsSync as existsSync7 } from "fs";
|
|
7404
|
-
import { appendFile as
|
|
7420
|
+
import { appendFile as appendFile4, copyFile, mkdir as mkdir15, readFile as readFile15, rename as rename3, rm as rm2 } from "fs/promises";
|
|
7405
7421
|
import { homedir as homedir15 } from "os";
|
|
7406
|
-
import { join as
|
|
7407
|
-
import { randomUUID as
|
|
7422
|
+
import { join as join27 } from "path";
|
|
7423
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
7408
7424
|
|
|
7409
7425
|
// src/managers/claude-manager.ts
|
|
7410
7426
|
import {
|
|
@@ -10162,7 +10178,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
|
10162
10178
|
var MIN_CODEX_CLI_VERSION = "0.144.6";
|
|
10163
10179
|
var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
|
|
10164
10180
|
var codexCliVersionEnsured = null;
|
|
10165
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
10181
|
+
var ENGINE_PACKAGE_VERSION = "0.1.535";
|
|
10166
10182
|
var INITIALIZE_METHOD = "initialize";
|
|
10167
10183
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
10168
10184
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -14348,6 +14364,146 @@ var RelayManager = class {
|
|
|
14348
14364
|
}
|
|
14349
14365
|
};
|
|
14350
14366
|
|
|
14367
|
+
// src/services/chat/turn-usage.ts
|
|
14368
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
14369
|
+
import { appendFile as appendFile3, mkdir as mkdir14, readFile as readFile12, readdir as readdir6, rename as rename2, unlink as unlink3 } from "fs/promises";
|
|
14370
|
+
import { join as join22 } from "path";
|
|
14371
|
+
var LIVE_FILE = join22(ENGINE_DIR2, "turn-usage.jsonl");
|
|
14372
|
+
var SEGMENT_FILE_RE = /^turn-usage\.(\d+)\.jsonl$/;
|
|
14373
|
+
var UPLOADED_SUFFIX = ".uploaded";
|
|
14374
|
+
var MAX_TRACKED_MESSAGES = 500;
|
|
14375
|
+
var MAX_FAILED_RECORDS = 500;
|
|
14376
|
+
var FLUSH_DEBOUNCE_MS = 15e3;
|
|
14377
|
+
var MAX_UPLOADED_SEGMENTS = 50;
|
|
14378
|
+
var messageAttrs = /* @__PURE__ */ new Map();
|
|
14379
|
+
var activeTurns = /* @__PURE__ */ new Map();
|
|
14380
|
+
var flushTimer = null;
|
|
14381
|
+
var activeFlush = Promise.resolve({ flushed: 0, failed: 0 });
|
|
14382
|
+
var pendingAppends = /* @__PURE__ */ new Set();
|
|
14383
|
+
var failedRecords = [];
|
|
14384
|
+
function noteMessageAccepted(messageId, request) {
|
|
14385
|
+
messageAttrs.set(messageId, {
|
|
14386
|
+
...request.model ? { model: request.model } : {},
|
|
14387
|
+
...request.senderUserId ? { senderUserId: request.senderUserId } : {}
|
|
14388
|
+
});
|
|
14389
|
+
for (const key of messageAttrs.keys()) {
|
|
14390
|
+
if (messageAttrs.size <= MAX_TRACKED_MESSAGES) break;
|
|
14391
|
+
messageAttrs.delete(key);
|
|
14392
|
+
}
|
|
14393
|
+
}
|
|
14394
|
+
function noteTurnStarted(chatId, messageId, provider) {
|
|
14395
|
+
activeTurns.set(chatId, { messageId, startedAtMs: Date.now(), provider });
|
|
14396
|
+
}
|
|
14397
|
+
function noteTurnEnded(chatId) {
|
|
14398
|
+
const turn = activeTurns.get(chatId);
|
|
14399
|
+
if (!turn) return;
|
|
14400
|
+
activeTurns.delete(chatId);
|
|
14401
|
+
recordTurn(chatId, turn);
|
|
14402
|
+
scheduleFlush();
|
|
14403
|
+
}
|
|
14404
|
+
function scheduleFlush() {
|
|
14405
|
+
if (flushTimer) return;
|
|
14406
|
+
flushTimer = setTimeout(() => {
|
|
14407
|
+
flushTimer = null;
|
|
14408
|
+
void flushAllTurnUsage(false).catch((err) => {
|
|
14409
|
+
console.error("[TurnUsage] Scheduled flush failed, retrying on the next turn:", err);
|
|
14410
|
+
});
|
|
14411
|
+
}, FLUSH_DEBOUNCE_MS);
|
|
14412
|
+
flushTimer.unref?.();
|
|
14413
|
+
}
|
|
14414
|
+
function recordTurn(chatId, turn) {
|
|
14415
|
+
const attrs = messageAttrs.get(turn.messageId) ?? {};
|
|
14416
|
+
messageAttrs.delete(turn.messageId);
|
|
14417
|
+
writeRecord({
|
|
14418
|
+
turnId: randomUUID5(),
|
|
14419
|
+
chatId,
|
|
14420
|
+
provider: turn.provider,
|
|
14421
|
+
...attrs,
|
|
14422
|
+
// A request without a model runs the agent's default, so record that rather than nothing.
|
|
14423
|
+
model: attrs.model ?? getDefaultAgentModel(turn.provider),
|
|
14424
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14425
|
+
seconds: Math.round((Date.now() - turn.startedAtMs) / 1e3)
|
|
14426
|
+
});
|
|
14427
|
+
}
|
|
14428
|
+
function writeRecord(record) {
|
|
14429
|
+
const pending = mkdir14(ENGINE_DIR2, { recursive: true }).then(() => appendFile3(LIVE_FILE, JSON.stringify(record) + "\n", "utf-8")).catch((err) => {
|
|
14430
|
+
console.error("[TurnUsage] Append failed, will retry on flush:", err);
|
|
14431
|
+
if (failedRecords.length < MAX_FAILED_RECORDS) failedRecords.push(record);
|
|
14432
|
+
});
|
|
14433
|
+
pendingAppends.add(pending);
|
|
14434
|
+
void pending.finally(() => pendingAppends.delete(pending));
|
|
14435
|
+
}
|
|
14436
|
+
function flushAllTurnUsage(finalizeInFlight = true) {
|
|
14437
|
+
if (flushTimer) {
|
|
14438
|
+
clearTimeout(flushTimer);
|
|
14439
|
+
flushTimer = null;
|
|
14440
|
+
}
|
|
14441
|
+
activeFlush = activeFlush.catch(() => {
|
|
14442
|
+
}).then(() => runFlush(finalizeInFlight));
|
|
14443
|
+
return activeFlush;
|
|
14444
|
+
}
|
|
14445
|
+
async function runFlush(finalizeInFlight) {
|
|
14446
|
+
for (const record of failedRecords.splice(0)) writeRecord(record);
|
|
14447
|
+
if (finalizeInFlight) {
|
|
14448
|
+
for (const [chatId, turn] of activeTurns) recordTurn(chatId, turn);
|
|
14449
|
+
activeTurns.clear();
|
|
14450
|
+
}
|
|
14451
|
+
while (pendingAppends.size > 0) await Promise.allSettled([...pendingAppends]);
|
|
14452
|
+
await rename2(LIVE_FILE, join22(ENGINE_DIR2, `turn-usage.${Date.now()}.jsonl`)).catch(() => {
|
|
14453
|
+
});
|
|
14454
|
+
const entries = await readdir6(ENGINE_DIR2).catch(() => []);
|
|
14455
|
+
let flushed = 0;
|
|
14456
|
+
let failed = 0;
|
|
14457
|
+
for (const entry of entries) {
|
|
14458
|
+
if (!SEGMENT_FILE_RE.test(entry)) continue;
|
|
14459
|
+
try {
|
|
14460
|
+
await uploadSegment(join22(ENGINE_DIR2, entry));
|
|
14461
|
+
flushed++;
|
|
14462
|
+
} catch (err) {
|
|
14463
|
+
failed++;
|
|
14464
|
+
console.error("[TurnUsage] Segment upload failed, retained for retry:", { entry, err });
|
|
14465
|
+
}
|
|
14466
|
+
}
|
|
14467
|
+
const stranded = failedRecords.splice(0);
|
|
14468
|
+
if (stranded.length > 0) {
|
|
14469
|
+
try {
|
|
14470
|
+
await uploadTurns(stranded);
|
|
14471
|
+
flushed++;
|
|
14472
|
+
} catch (err) {
|
|
14473
|
+
failed++;
|
|
14474
|
+
failedRecords.unshift(...stranded.slice(0, MAX_FAILED_RECORDS));
|
|
14475
|
+
console.error("[TurnUsage] Stranded-record upload failed, retained for retry:", err);
|
|
14476
|
+
}
|
|
14477
|
+
}
|
|
14478
|
+
const uploaded = entries.filter((entry) => entry.endsWith(UPLOADED_SUFFIX)).sort();
|
|
14479
|
+
for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
|
|
14480
|
+
await unlink3(join22(ENGINE_DIR2, entry)).catch(() => {
|
|
14481
|
+
});
|
|
14482
|
+
}
|
|
14483
|
+
return { flushed, failed };
|
|
14484
|
+
}
|
|
14485
|
+
async function uploadSegment(filePath) {
|
|
14486
|
+
const turns = (await readFile12(filePath, "utf-8")).split("\n").flatMap((line) => {
|
|
14487
|
+
try {
|
|
14488
|
+
const parsed = JSON.parse(line);
|
|
14489
|
+
return isChatTurnUsageRecord(parsed) ? [parsed] : [];
|
|
14490
|
+
} catch {
|
|
14491
|
+
return [];
|
|
14492
|
+
}
|
|
14493
|
+
});
|
|
14494
|
+
await uploadTurns(turns);
|
|
14495
|
+
await rename2(filePath, `${filePath}${UPLOADED_SUFFIX}`).catch(() => {
|
|
14496
|
+
});
|
|
14497
|
+
}
|
|
14498
|
+
async function uploadTurns(turns) {
|
|
14499
|
+
if (turns.length === 0) return;
|
|
14500
|
+
const body = { turns };
|
|
14501
|
+
const response = await monolithRequest("/v1/engine/chat-turn-usage", { body });
|
|
14502
|
+
if (!response.ok) {
|
|
14503
|
+
throw new Error(`upload failed: ${response.status} ${await response.text()}`);
|
|
14504
|
+
}
|
|
14505
|
+
}
|
|
14506
|
+
|
|
14351
14507
|
// src/services/keep-alive-service.ts
|
|
14352
14508
|
var KeepAliveService = class _KeepAliveService {
|
|
14353
14509
|
interval = null;
|
|
@@ -14392,19 +14548,19 @@ var KeepAliveService = class _KeepAliveService {
|
|
|
14392
14548
|
var keepAliveService = new KeepAliveService();
|
|
14393
14549
|
|
|
14394
14550
|
// src/services/canvas-service.ts
|
|
14395
|
-
import { readdir as
|
|
14551
|
+
import { readdir as readdir7, readFile as readFile13, stat as stat3 } from "fs/promises";
|
|
14396
14552
|
import { homedir as homedir14 } from "os";
|
|
14397
|
-
import { join as
|
|
14553
|
+
import { join as join23 } from "path";
|
|
14398
14554
|
var GLOBAL_CANVAS_DIRECTORIES = [
|
|
14399
|
-
|
|
14400
|
-
|
|
14401
|
-
|
|
14555
|
+
join23(homedir14(), ".claude", "plans"),
|
|
14556
|
+
join23(process.env.XDG_DATA_HOME ?? join23(homedir14(), ".local", "share"), "opencode", "plans"),
|
|
14557
|
+
join23(homedir14(), ".replicas", "canvas")
|
|
14402
14558
|
];
|
|
14403
14559
|
async function canvasDirectories() {
|
|
14404
14560
|
const repositories = await gitService.listRepositories().catch(() => []);
|
|
14405
14561
|
return [
|
|
14406
14562
|
...GLOBAL_CANVAS_DIRECTORIES,
|
|
14407
|
-
...repositories.map((repository) =>
|
|
14563
|
+
...repositories.map((repository) => join23(repository.path, ".opencode", "plans"))
|
|
14408
14564
|
];
|
|
14409
14565
|
}
|
|
14410
14566
|
var CanvasService = class {
|
|
@@ -14413,7 +14569,7 @@ var CanvasService = class {
|
|
|
14413
14569
|
for (const directory of await canvasDirectories()) {
|
|
14414
14570
|
let entries;
|
|
14415
14571
|
try {
|
|
14416
|
-
entries = await
|
|
14572
|
+
entries = await readdir7(directory, { withFileTypes: true });
|
|
14417
14573
|
} catch {
|
|
14418
14574
|
continue;
|
|
14419
14575
|
}
|
|
@@ -14424,7 +14580,7 @@ var CanvasService = class {
|
|
|
14424
14580
|
const { kind } = classifyCanvasFilename(entry.name);
|
|
14425
14581
|
let sizeBytes = 0;
|
|
14426
14582
|
try {
|
|
14427
|
-
const s = await stat3(
|
|
14583
|
+
const s = await stat3(join23(directory, entry.name));
|
|
14428
14584
|
sizeBytes = s.size;
|
|
14429
14585
|
} catch {
|
|
14430
14586
|
continue;
|
|
@@ -14439,7 +14595,7 @@ var CanvasService = class {
|
|
|
14439
14595
|
if (!safe) return null;
|
|
14440
14596
|
const { kind, mimeType } = classifyCanvasFilename(safe);
|
|
14441
14597
|
for (const directory of await canvasDirectories()) {
|
|
14442
|
-
const filePath =
|
|
14598
|
+
const filePath = join23(directory, safe);
|
|
14443
14599
|
let sizeBytes = 0;
|
|
14444
14600
|
let updatedAt = "";
|
|
14445
14601
|
try {
|
|
@@ -14460,7 +14616,7 @@ var CanvasService = class {
|
|
|
14460
14616
|
};
|
|
14461
14617
|
}
|
|
14462
14618
|
try {
|
|
14463
|
-
const bytes = await
|
|
14619
|
+
const bytes = await readFile13(filePath);
|
|
14464
14620
|
return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
|
|
14465
14621
|
} catch {
|
|
14466
14622
|
continue;
|
|
@@ -14576,16 +14732,16 @@ async function reconcileCanvasItems(filenames) {
|
|
|
14576
14732
|
|
|
14577
14733
|
// src/services/upload-chat-transcripts.ts
|
|
14578
14734
|
import { createReadStream } from "fs";
|
|
14579
|
-
import { readdir as
|
|
14735
|
+
import { readdir as readdir8, readFile as readFile14, stat as stat4 } from "fs/promises";
|
|
14580
14736
|
import { request as httpRequest } from "http";
|
|
14581
14737
|
import { request as httpsRequest } from "https";
|
|
14582
|
-
import { basename as basename2, join as
|
|
14738
|
+
import { basename as basename2, join as join25 } from "path";
|
|
14583
14739
|
|
|
14584
14740
|
// src/services/chat/chat-senders.ts
|
|
14585
|
-
import { join as
|
|
14586
|
-
var CHAT_SENDERS_DIR =
|
|
14741
|
+
import { join as join24 } from "path";
|
|
14742
|
+
var CHAT_SENDERS_DIR = join24(ENGINE_DIR2, "chat-senders");
|
|
14587
14743
|
function chatMessageSendersFilePath(chatId) {
|
|
14588
|
-
return
|
|
14744
|
+
return join24(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
14589
14745
|
}
|
|
14590
14746
|
function parseChatMessageSendersJsonl(content) {
|
|
14591
14747
|
return content.split("\n").flatMap((line) => {
|
|
@@ -14601,9 +14757,9 @@ function parseChatMessageSendersJsonl(content) {
|
|
|
14601
14757
|
|
|
14602
14758
|
// src/services/upload-chat-transcripts.ts
|
|
14603
14759
|
var HISTORY_DIRS = [
|
|
14604
|
-
|
|
14605
|
-
|
|
14606
|
-
|
|
14760
|
+
join25(ENGINE_DIR2, "claude-histories"),
|
|
14761
|
+
join25(ENGINE_DIR2, "relay-histories"),
|
|
14762
|
+
join25(ENGINE_DIR2, "codex-histories")
|
|
14607
14763
|
];
|
|
14608
14764
|
async function putTranscript(uploadUrl, filePath, size) {
|
|
14609
14765
|
await new Promise((resolve4, reject) => {
|
|
@@ -14640,7 +14796,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
|
|
|
14640
14796
|
for (const dir of HISTORY_DIRS) {
|
|
14641
14797
|
let entries;
|
|
14642
14798
|
try {
|
|
14643
|
-
entries = await
|
|
14799
|
+
entries = await readdir8(dir);
|
|
14644
14800
|
} catch {
|
|
14645
14801
|
continue;
|
|
14646
14802
|
}
|
|
@@ -14648,7 +14804,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
|
|
|
14648
14804
|
if (!entry.endsWith(".jsonl")) continue;
|
|
14649
14805
|
const chatId = basename2(entry, ".jsonl");
|
|
14650
14806
|
tasks.push(
|
|
14651
|
-
uploadChatTranscript(chatId,
|
|
14807
|
+
uploadChatTranscript(chatId, join25(dir, entry), chatsById.get(chatId)).then(() => {
|
|
14652
14808
|
flushed++;
|
|
14653
14809
|
}).catch((err) => {
|
|
14654
14810
|
failed++;
|
|
@@ -14673,7 +14829,7 @@ async function uploadChatTranscript(chatId, filePath, chat) {
|
|
|
14673
14829
|
} : {};
|
|
14674
14830
|
try {
|
|
14675
14831
|
metadata.senders = parseChatMessageSendersJsonl(
|
|
14676
|
-
await
|
|
14832
|
+
await readFile14(chatMessageSendersFilePath(chatId), "utf-8")
|
|
14677
14833
|
);
|
|
14678
14834
|
} catch (error) {
|
|
14679
14835
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
@@ -14734,8 +14890,8 @@ async function flushRepoState() {
|
|
|
14734
14890
|
|
|
14735
14891
|
// src/services/upload-engine-logs.ts
|
|
14736
14892
|
import { createReadStream as createReadStream2 } from "fs";
|
|
14737
|
-
import { readdir as
|
|
14738
|
-
import { join as
|
|
14893
|
+
import { readdir as readdir9, stat as stat5 } from "fs/promises";
|
|
14894
|
+
import { join as join26 } from "path";
|
|
14739
14895
|
var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
|
|
14740
14896
|
var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
|
|
14741
14897
|
var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
|
|
@@ -14745,7 +14901,7 @@ async function flushAllEngineLogs() {
|
|
|
14745
14901
|
let failed = 0;
|
|
14746
14902
|
const deadline = Date.now() + ENGINE_LOG_FLUSH_TIMEOUT_MS;
|
|
14747
14903
|
await runBeforeDeadline((signal) => engineLogger.flush(signal), deadline);
|
|
14748
|
-
const files = await runBeforeDeadline(() =>
|
|
14904
|
+
const files = await runBeforeDeadline(() => readdir9(LOG_DIR), deadline).catch(() => []);
|
|
14749
14905
|
const currentFilename = engineLogger.sessionId ? `${engineLogger.sessionId}.log` : null;
|
|
14750
14906
|
const filenames = files.filter((filename) => {
|
|
14751
14907
|
if (!filename.endsWith(".log")) return false;
|
|
@@ -14764,7 +14920,7 @@ async function flushAllEngineLogs() {
|
|
|
14764
14920
|
const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
|
|
14765
14921
|
try {
|
|
14766
14922
|
const sessionId = filename.slice(0, -".log".length);
|
|
14767
|
-
const filePath =
|
|
14923
|
+
const filePath = join26(LOG_DIR, filename);
|
|
14768
14924
|
const fileStat = await runBeforeDeadline(() => stat5(filePath), deadline);
|
|
14769
14925
|
if (!fileStat.isFile()) {
|
|
14770
14926
|
skipped++;
|
|
@@ -14860,7 +15016,7 @@ async function uploadEngineLog(input, timeoutMs) {
|
|
|
14860
15016
|
}
|
|
14861
15017
|
|
|
14862
15018
|
// src/services/chat/chat-service.ts
|
|
14863
|
-
var CODEX_AUTH_PATH2 =
|
|
15019
|
+
var CODEX_AUTH_PATH2 = join27(homedir15(), ".codex", "auth.json");
|
|
14864
15020
|
var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
|
|
14865
15021
|
function isCodexAvailable() {
|
|
14866
15022
|
return existsSync7(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
|
|
@@ -14988,13 +15144,13 @@ var ChatService = class {
|
|
|
14988
15144
|
persistInFlight = false;
|
|
14989
15145
|
persistQueued = false;
|
|
14990
15146
|
async initialize() {
|
|
14991
|
-
await
|
|
14992
|
-
await
|
|
14993
|
-
await
|
|
14994
|
-
await
|
|
14995
|
-
await
|
|
14996
|
-
await
|
|
14997
|
-
await
|
|
15147
|
+
await mkdir15(ENGINE_DIR2, { recursive: true });
|
|
15148
|
+
await mkdir15(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
15149
|
+
await mkdir15(RELAY_HISTORY_DIR, { recursive: true });
|
|
15150
|
+
await mkdir15(CODEX_HISTORY_DIR, { recursive: true });
|
|
15151
|
+
await mkdir15(CURSOR_HISTORY_DIR, { recursive: true });
|
|
15152
|
+
await mkdir15(OPENCODE_HISTORY_DIR, { recursive: true });
|
|
15153
|
+
await mkdir15(CHAT_SENDERS_DIR, { recursive: true });
|
|
14998
15154
|
const persisted = await this.loadChats();
|
|
14999
15155
|
for (const chat of persisted) {
|
|
15000
15156
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -15061,7 +15217,7 @@ var ChatService = class {
|
|
|
15061
15217
|
throw new ChatNotFoundError(parentChatId);
|
|
15062
15218
|
}
|
|
15063
15219
|
const persisted = {
|
|
15064
|
-
id: request.id ??
|
|
15220
|
+
id: request.id ?? randomUUID6(),
|
|
15065
15221
|
provider: request.provider,
|
|
15066
15222
|
title,
|
|
15067
15223
|
createdAt: now,
|
|
@@ -15105,6 +15261,7 @@ var ChatService = class {
|
|
|
15105
15261
|
request.images
|
|
15106
15262
|
);
|
|
15107
15263
|
chat.pendingMessageIds.push(result.messageId);
|
|
15264
|
+
noteMessageAccepted(result.messageId, request);
|
|
15108
15265
|
if (request.errorNotificationTarget) {
|
|
15109
15266
|
chat.errorNotificationTargets.set(result.messageId, request.errorNotificationTarget);
|
|
15110
15267
|
}
|
|
@@ -15141,14 +15298,14 @@ var ChatService = class {
|
|
|
15141
15298
|
}
|
|
15142
15299
|
async appendSender(chatId, sender) {
|
|
15143
15300
|
try {
|
|
15144
|
-
await
|
|
15301
|
+
await appendFile4(chatMessageSendersFilePath(chatId), JSON.stringify(sender) + "\n", "utf-8");
|
|
15145
15302
|
} catch (error) {
|
|
15146
15303
|
console.error("[ChatService] Failed to append sender record:", error);
|
|
15147
15304
|
}
|
|
15148
15305
|
}
|
|
15149
15306
|
async readSenders(chatId) {
|
|
15150
15307
|
try {
|
|
15151
|
-
return parseChatMessageSendersJsonl(await
|
|
15308
|
+
return parseChatMessageSendersJsonl(await readFile15(chatMessageSendersFilePath(chatId), "utf-8"));
|
|
15152
15309
|
} catch (error) {
|
|
15153
15310
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
15154
15311
|
return [];
|
|
@@ -15160,6 +15317,7 @@ var ChatService = class {
|
|
|
15160
15317
|
async interrupt(chatId) {
|
|
15161
15318
|
const chat = this.requireChat(chatId);
|
|
15162
15319
|
const result = await chat.provider.interrupt();
|
|
15320
|
+
noteTurnEnded(chatId);
|
|
15163
15321
|
chat.hasActiveTurn = false;
|
|
15164
15322
|
chat.activeMessageId = null;
|
|
15165
15323
|
chat.pendingMessageIds = [];
|
|
@@ -15181,6 +15339,7 @@ var ChatService = class {
|
|
|
15181
15339
|
return { interrupted: false, queue: [], goal: null };
|
|
15182
15340
|
}
|
|
15183
15341
|
const interruptResult = await chat.provider.interrupt();
|
|
15342
|
+
noteTurnEnded(chatId);
|
|
15184
15343
|
chat.hasActiveTurn = false;
|
|
15185
15344
|
chat.activeMessageId = null;
|
|
15186
15345
|
chat.pendingMessageIds = [];
|
|
@@ -15327,7 +15486,7 @@ var ChatService = class {
|
|
|
15327
15486
|
return descendants;
|
|
15328
15487
|
}
|
|
15329
15488
|
async deleteHistoryFile(persisted) {
|
|
15330
|
-
await rm2(
|
|
15489
|
+
await rm2(join27(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
|
|
15331
15490
|
await rm2(chatMessageSendersFilePath(persisted.id), { force: true });
|
|
15332
15491
|
}
|
|
15333
15492
|
async getChatHistory(chatId, page = {}) {
|
|
@@ -15370,13 +15529,14 @@ var ChatService = class {
|
|
|
15370
15529
|
const chatsById = new Map(
|
|
15371
15530
|
[...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
|
|
15372
15531
|
);
|
|
15373
|
-
const [chatTranscripts, canvas, repoState, engineLogs] = await Promise.all([
|
|
15532
|
+
const [chatTranscripts, canvas, repoState, engineLogs, turnUsage] = await Promise.all([
|
|
15374
15533
|
flushAllChatTranscripts(chatsById),
|
|
15375
15534
|
flushAllCanvasItems(),
|
|
15376
15535
|
flushRepoState(),
|
|
15377
|
-
flushAllEngineLogs()
|
|
15536
|
+
flushAllEngineLogs(),
|
|
15537
|
+
flushAllTurnUsage()
|
|
15378
15538
|
]);
|
|
15379
|
-
return { chatTranscripts, canvas, repoState, engineLogs };
|
|
15539
|
+
return { chatTranscripts, canvas, repoState, engineLogs, turnUsage };
|
|
15380
15540
|
}
|
|
15381
15541
|
createRuntimeChat(persisted) {
|
|
15382
15542
|
const saveSession = async (sessionId) => {
|
|
@@ -15403,7 +15563,7 @@ var ChatService = class {
|
|
|
15403
15563
|
if (persisted.provider === "claude") {
|
|
15404
15564
|
provider = new ClaudeManager({
|
|
15405
15565
|
workingDirectory: this.workingDirectory,
|
|
15406
|
-
historyFilePath:
|
|
15566
|
+
historyFilePath: join27(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
15407
15567
|
initialSessionId: persisted.providerSessionId,
|
|
15408
15568
|
onSaveSessionId: saveSession,
|
|
15409
15569
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -15412,7 +15572,7 @@ var ChatService = class {
|
|
|
15412
15572
|
} else if (persisted.provider === "relay") {
|
|
15413
15573
|
provider = new RelayManager({
|
|
15414
15574
|
workingDirectory: this.workingDirectory,
|
|
15415
|
-
historyFilePath:
|
|
15575
|
+
historyFilePath: join27(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
15416
15576
|
initialSessionId: persisted.providerSessionId,
|
|
15417
15577
|
onSaveSessionId: saveSession,
|
|
15418
15578
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -15426,7 +15586,7 @@ var ChatService = class {
|
|
|
15426
15586
|
} else if (persisted.provider === "cursor") {
|
|
15427
15587
|
provider = new CursorManager({
|
|
15428
15588
|
workingDirectory: this.workingDirectory,
|
|
15429
|
-
historyFilePath:
|
|
15589
|
+
historyFilePath: join27(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
15430
15590
|
initialSessionId: persisted.providerSessionId,
|
|
15431
15591
|
onSaveSessionId: saveSession,
|
|
15432
15592
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -15435,7 +15595,7 @@ var ChatService = class {
|
|
|
15435
15595
|
} else if (persisted.provider === "opencode") {
|
|
15436
15596
|
provider = new OpencodeManager({
|
|
15437
15597
|
workingDirectory: this.workingDirectory,
|
|
15438
|
-
historyFilePath:
|
|
15598
|
+
historyFilePath: join27(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
15439
15599
|
initialSessionId: persisted.providerSessionId,
|
|
15440
15600
|
onSaveSessionId: saveSession,
|
|
15441
15601
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -15444,7 +15604,7 @@ var ChatService = class {
|
|
|
15444
15604
|
} else if (persisted.provider === "pi") {
|
|
15445
15605
|
provider = new PiManager({
|
|
15446
15606
|
workingDirectory: this.workingDirectory,
|
|
15447
|
-
historyFilePath:
|
|
15607
|
+
historyFilePath: join27(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
15448
15608
|
initialSessionId: persisted.providerSessionId,
|
|
15449
15609
|
onSaveSessionId: saveSession,
|
|
15450
15610
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -15453,7 +15613,7 @@ var ChatService = class {
|
|
|
15453
15613
|
} else {
|
|
15454
15614
|
provider = new CodexAspManager({
|
|
15455
15615
|
workingDirectory: this.workingDirectory,
|
|
15456
|
-
historyFilePath:
|
|
15616
|
+
historyFilePath: join27(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
15457
15617
|
initialSessionId: persisted.providerSessionId,
|
|
15458
15618
|
onSaveSessionId: saveSession,
|
|
15459
15619
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -15516,6 +15676,7 @@ var ChatService = class {
|
|
|
15516
15676
|
}
|
|
15517
15677
|
chat.hasActiveTurn = true;
|
|
15518
15678
|
chat.activeMessageId = messageId;
|
|
15679
|
+
noteTurnStarted(chat.persisted.id, messageId, chat.persisted.provider);
|
|
15519
15680
|
chat.activeErrorNotificationTarget = chat.errorNotificationTargets.get(messageId) ?? null;
|
|
15520
15681
|
chat.errorNotificationTargets.delete(messageId);
|
|
15521
15682
|
this.publish({
|
|
@@ -15590,6 +15751,7 @@ var ChatService = class {
|
|
|
15590
15751
|
}
|
|
15591
15752
|
chat.hasActiveTurn = false;
|
|
15592
15753
|
chat.activeMessageId = null;
|
|
15754
|
+
noteTurnEnded(chatId);
|
|
15593
15755
|
this.publish({
|
|
15594
15756
|
type: "chat.turn.completed",
|
|
15595
15757
|
payload: {
|
|
@@ -15603,7 +15765,7 @@ var ChatService = class {
|
|
|
15603
15765
|
});
|
|
15604
15766
|
uploadChatTranscript(
|
|
15605
15767
|
chatId,
|
|
15606
|
-
|
|
15768
|
+
join27(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
|
|
15607
15769
|
this.toSummary(chat)
|
|
15608
15770
|
).catch((err) => {
|
|
15609
15771
|
console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
|
|
@@ -15619,7 +15781,7 @@ var ChatService = class {
|
|
|
15619
15781
|
}
|
|
15620
15782
|
async loadChats() {
|
|
15621
15783
|
try {
|
|
15622
|
-
const content = await
|
|
15784
|
+
const content = await readFile15(CHATS_FILE, "utf-8");
|
|
15623
15785
|
return parsePersistedChatsContent(content);
|
|
15624
15786
|
} catch (error) {
|
|
15625
15787
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -15628,13 +15790,13 @@ var ChatService = class {
|
|
|
15628
15790
|
const quarantinePath = corruptChatsFilePath();
|
|
15629
15791
|
console.error(`[ChatService] Failed to load ${CHATS_FILE}; quarantining and trying backup:`, error);
|
|
15630
15792
|
try {
|
|
15631
|
-
await
|
|
15793
|
+
await rename3(CHATS_FILE, quarantinePath);
|
|
15632
15794
|
console.error(`[ChatService] Quarantined corrupt chats file at ${quarantinePath}`);
|
|
15633
15795
|
} catch (renameError) {
|
|
15634
15796
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
15635
15797
|
}
|
|
15636
15798
|
try {
|
|
15637
|
-
const backupContent = await
|
|
15799
|
+
const backupContent = await readFile15(CHATS_BACKUP_FILE, "utf-8");
|
|
15638
15800
|
return parsePersistedChatsContent(backupContent);
|
|
15639
15801
|
} catch (backupError) {
|
|
15640
15802
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -15673,7 +15835,7 @@ var ChatService = class {
|
|
|
15673
15835
|
}
|
|
15674
15836
|
async publish(input) {
|
|
15675
15837
|
const event = {
|
|
15676
|
-
id:
|
|
15838
|
+
id: randomUUID6(),
|
|
15677
15839
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15678
15840
|
...input
|
|
15679
15841
|
};
|
|
@@ -15738,8 +15900,8 @@ var ChatService = class {
|
|
|
15738
15900
|
|
|
15739
15901
|
// src/services/repo-file-service.ts
|
|
15740
15902
|
import { execFile as execFile2 } from "child_process";
|
|
15741
|
-
import { readFile as
|
|
15742
|
-
import { join as
|
|
15903
|
+
import { readFile as readFile16, realpath, stat as stat6 } from "fs/promises";
|
|
15904
|
+
import { join as join28, resolve as resolve2, extname as extname2 } from "path";
|
|
15743
15905
|
var CACHE_TTL_MS = 3e4;
|
|
15744
15906
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
15745
15907
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -15899,7 +16061,7 @@ var RepoFileService = class {
|
|
|
15899
16061
|
const repo = repos.find((r) => r.name === repoName);
|
|
15900
16062
|
if (!repo) return null;
|
|
15901
16063
|
try {
|
|
15902
|
-
const fullPath = await realpath(resolve2(
|
|
16064
|
+
const fullPath = await realpath(resolve2(join28(repo.path, filePath)));
|
|
15903
16065
|
const repoRoot = await realpath(repo.path);
|
|
15904
16066
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
15905
16067
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
@@ -15928,7 +16090,7 @@ var RepoFileService = class {
|
|
|
15928
16090
|
tooLarge: true
|
|
15929
16091
|
};
|
|
15930
16092
|
}
|
|
15931
|
-
const content = await
|
|
16093
|
+
const content = await readFile16(fullPath, "utf-8");
|
|
15932
16094
|
return {
|
|
15933
16095
|
repoName,
|
|
15934
16096
|
path: filePath,
|
|
@@ -16006,21 +16168,21 @@ var RepoFileService = class {
|
|
|
16006
16168
|
// src/v1-routes.ts
|
|
16007
16169
|
import { Hono } from "hono";
|
|
16008
16170
|
import { z as z7 } from "zod";
|
|
16009
|
-
import { readdir as
|
|
16010
|
-
import { join as
|
|
16171
|
+
import { readdir as readdir11, stat as stat7, readFile as readFile19 } from "fs/promises";
|
|
16172
|
+
import { join as join31, resolve as resolve3 } from "path";
|
|
16011
16173
|
|
|
16012
16174
|
// src/services/warm-hooks-service.ts
|
|
16013
16175
|
import { spawn as spawn4 } from "child_process";
|
|
16014
|
-
import { readFile as
|
|
16176
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
16015
16177
|
import { existsSync as existsSync8 } from "fs";
|
|
16016
|
-
import { join as
|
|
16178
|
+
import { join as join30 } from "path";
|
|
16017
16179
|
|
|
16018
16180
|
// src/services/warm-hook-logs-service.ts
|
|
16019
|
-
import { mkdir as
|
|
16181
|
+
import { mkdir as mkdir16, readFile as readFile17, writeFile as writeFile6, readdir as readdir10, appendFile as appendFile5, unlink as unlink4 } from "fs/promises";
|
|
16020
16182
|
import { homedir as homedir16 } from "os";
|
|
16021
|
-
import { join as
|
|
16022
|
-
var LOGS_DIR2 =
|
|
16023
|
-
var CURRENT_RUN_LOG =
|
|
16183
|
+
import { join as join29 } from "path";
|
|
16184
|
+
var LOGS_DIR2 = join29(homedir16(), ".replicas", "warm-hook-logs");
|
|
16185
|
+
var CURRENT_RUN_LOG = join29(LOGS_DIR2, "current-run.log");
|
|
16024
16186
|
var GLOBAL_FILENAME = "global.json";
|
|
16025
16187
|
function withPreview2(stored) {
|
|
16026
16188
|
const preview = buildHookOutputPreview(stored.output);
|
|
@@ -16028,7 +16190,7 @@ function withPreview2(stored) {
|
|
|
16028
16190
|
}
|
|
16029
16191
|
var WarmHookLogsService = class {
|
|
16030
16192
|
async ensureDir() {
|
|
16031
|
-
await
|
|
16193
|
+
await mkdir16(LOGS_DIR2, { recursive: true });
|
|
16032
16194
|
}
|
|
16033
16195
|
async saveGlobalHookLog(entry) {
|
|
16034
16196
|
await this.ensureDir();
|
|
@@ -16037,7 +16199,7 @@ var WarmHookLogsService = class {
|
|
|
16037
16199
|
hookName: "organization",
|
|
16038
16200
|
...entry
|
|
16039
16201
|
};
|
|
16040
|
-
await writeFile6(
|
|
16202
|
+
await writeFile6(join29(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
16041
16203
|
`, "utf-8");
|
|
16042
16204
|
}
|
|
16043
16205
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -16047,7 +16209,7 @@ var WarmHookLogsService = class {
|
|
|
16047
16209
|
hookName: "environment",
|
|
16048
16210
|
...entry
|
|
16049
16211
|
};
|
|
16050
|
-
await writeFile6(
|
|
16212
|
+
await writeFile6(join29(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
16051
16213
|
`, "utf-8");
|
|
16052
16214
|
}
|
|
16053
16215
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -16057,13 +16219,13 @@ var WarmHookLogsService = class {
|
|
|
16057
16219
|
hookName: repoName,
|
|
16058
16220
|
...entry
|
|
16059
16221
|
};
|
|
16060
|
-
await writeFile6(
|
|
16222
|
+
await writeFile6(join29(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
16061
16223
|
`, "utf-8");
|
|
16062
16224
|
}
|
|
16063
16225
|
async getAllLogs() {
|
|
16064
16226
|
let files;
|
|
16065
16227
|
try {
|
|
16066
|
-
files = await
|
|
16228
|
+
files = await readdir10(LOGS_DIR2);
|
|
16067
16229
|
} catch (err) {
|
|
16068
16230
|
if (err.code === "ENOENT") {
|
|
16069
16231
|
return [];
|
|
@@ -16076,7 +16238,7 @@ var WarmHookLogsService = class {
|
|
|
16076
16238
|
continue;
|
|
16077
16239
|
}
|
|
16078
16240
|
try {
|
|
16079
|
-
const raw = await
|
|
16241
|
+
const raw = await readFile17(join29(LOGS_DIR2, file), "utf-8");
|
|
16080
16242
|
const stored = JSON.parse(raw);
|
|
16081
16243
|
logs.push(withPreview2(stored));
|
|
16082
16244
|
} catch {
|
|
@@ -16094,18 +16256,18 @@ var WarmHookLogsService = class {
|
|
|
16094
16256
|
async resetCurrentRunLog() {
|
|
16095
16257
|
await this.ensureDir();
|
|
16096
16258
|
try {
|
|
16097
|
-
await
|
|
16259
|
+
await unlink4(CURRENT_RUN_LOG);
|
|
16098
16260
|
} catch (err) {
|
|
16099
16261
|
if (err.code !== "ENOENT") throw err;
|
|
16100
16262
|
}
|
|
16101
16263
|
}
|
|
16102
16264
|
async appendCurrentRunLog(chunk) {
|
|
16103
16265
|
if (!chunk) return;
|
|
16104
|
-
await
|
|
16266
|
+
await appendFile5(CURRENT_RUN_LOG, chunk, "utf-8");
|
|
16105
16267
|
}
|
|
16106
16268
|
async getCurrentRunLog() {
|
|
16107
16269
|
try {
|
|
16108
|
-
return await
|
|
16270
|
+
return await readFile17(CURRENT_RUN_LOG, "utf-8");
|
|
16109
16271
|
} catch (err) {
|
|
16110
16272
|
if (err.code === "ENOENT") return null;
|
|
16111
16273
|
throw err;
|
|
@@ -16114,7 +16276,7 @@ var WarmHookLogsService = class {
|
|
|
16114
16276
|
async getFullOutput(hookType, hookName) {
|
|
16115
16277
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
16116
16278
|
try {
|
|
16117
|
-
const raw = await
|
|
16279
|
+
const raw = await readFile17(join29(LOGS_DIR2, filename), "utf-8");
|
|
16118
16280
|
const stored = JSON.parse(raw);
|
|
16119
16281
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
16120
16282
|
return null;
|
|
@@ -16133,12 +16295,12 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
16133
16295
|
// src/services/warm-hooks-service.ts
|
|
16134
16296
|
async function readRepoWarmHook(repoPath) {
|
|
16135
16297
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
16136
|
-
const configPath =
|
|
16298
|
+
const configPath = join30(repoPath, filename);
|
|
16137
16299
|
if (!existsSync8(configPath)) {
|
|
16138
16300
|
continue;
|
|
16139
16301
|
}
|
|
16140
16302
|
try {
|
|
16141
|
-
const raw = await
|
|
16303
|
+
const raw = await readFile18(configPath, "utf-8");
|
|
16142
16304
|
const config = parseReplicasConfigString(raw, filename);
|
|
16143
16305
|
if (!config.warmHook) {
|
|
16144
16306
|
return null;
|
|
@@ -16394,7 +16556,7 @@ ${combinedScript}` : combinedScript;
|
|
|
16394
16556
|
}
|
|
16395
16557
|
|
|
16396
16558
|
// src/services/terminal-service.ts
|
|
16397
|
-
import { randomUUID as
|
|
16559
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
16398
16560
|
import { existsSync as existsSync9 } from "fs";
|
|
16399
16561
|
import { spawn as spawn5 } from "node-pty";
|
|
16400
16562
|
var MAX_REPLAY_CHARS = 1024 * 1024;
|
|
@@ -16413,7 +16575,7 @@ var TerminalService = class {
|
|
|
16413
16575
|
code: "limit"
|
|
16414
16576
|
});
|
|
16415
16577
|
}
|
|
16416
|
-
const id =
|
|
16578
|
+
const id = randomUUID7();
|
|
16417
16579
|
const shell = process.env.SHELL && existsSync9(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
|
|
16418
16580
|
const pty = spawn5(shell, ["-l"], {
|
|
16419
16581
|
name: "xterm-256color",
|
|
@@ -17391,11 +17553,11 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
17391
17553
|
});
|
|
17392
17554
|
app2.get("/logs", async (c) => {
|
|
17393
17555
|
try {
|
|
17394
|
-
const files = await
|
|
17556
|
+
const files = await readdir11(LOG_DIR).catch(() => []);
|
|
17395
17557
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
17396
17558
|
const sessions = await Promise.all(
|
|
17397
17559
|
logFiles.map(async (filename) => {
|
|
17398
|
-
const filePath =
|
|
17560
|
+
const filePath = join31(LOG_DIR, filename);
|
|
17399
17561
|
const fileStat = await stat7(filePath);
|
|
17400
17562
|
const sessionId = filename.replace(/\.log$/, "");
|
|
17401
17563
|
return {
|
|
@@ -17430,7 +17592,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
17430
17592
|
}
|
|
17431
17593
|
let content;
|
|
17432
17594
|
try {
|
|
17433
|
-
content = await
|
|
17595
|
+
content = await readFile19(filePath, "utf-8");
|
|
17434
17596
|
} catch {
|
|
17435
17597
|
return c.json(jsonError("Log session not found"), 404);
|
|
17436
17598
|
}
|
|
@@ -17669,7 +17831,7 @@ function startStatusBroadcaster() {
|
|
|
17669
17831
|
if (serialized !== previousRepoStatus) {
|
|
17670
17832
|
previousRepoStatus = serialized;
|
|
17671
17833
|
eventService.publish({
|
|
17672
|
-
id:
|
|
17834
|
+
id: randomUUID8(),
|
|
17673
17835
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17674
17836
|
type: "repo.status.changed",
|
|
17675
17837
|
payload: { repos }
|
|
@@ -17690,7 +17852,7 @@ function startStatusBroadcaster() {
|
|
|
17690
17852
|
if (engineStatusJson !== previousEngineStatus) {
|
|
17691
17853
|
previousEngineStatus = engineStatusJson;
|
|
17692
17854
|
eventService.publish({
|
|
17693
|
-
id:
|
|
17855
|
+
id: randomUUID8(),
|
|
17694
17856
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17695
17857
|
type: "engine.status.changed",
|
|
17696
17858
|
payload: { status: engineStatus }
|
|
@@ -17709,7 +17871,7 @@ function startStatusBroadcaster() {
|
|
|
17709
17871
|
previousHookStatus = hookSnapshot;
|
|
17710
17872
|
if (!lastHooksRunning && hooksRunning) {
|
|
17711
17873
|
eventService.publish({
|
|
17712
|
-
id:
|
|
17874
|
+
id: randomUUID8(),
|
|
17713
17875
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17714
17876
|
type: "hooks.started",
|
|
17715
17877
|
payload: { running: true, completed: false }
|
|
@@ -17718,7 +17880,7 @@ function startStatusBroadcaster() {
|
|
|
17718
17880
|
}
|
|
17719
17881
|
if (hooksRunning) {
|
|
17720
17882
|
eventService.publish({
|
|
17721
|
-
id:
|
|
17883
|
+
id: randomUUID8(),
|
|
17722
17884
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17723
17885
|
type: "hooks.progress",
|
|
17724
17886
|
payload: { running: true, completed: false }
|
|
@@ -17727,7 +17889,7 @@ function startStatusBroadcaster() {
|
|
|
17727
17889
|
}
|
|
17728
17890
|
if (lastHooksRunning && !hooksRunning && hooksCompleted && !hooksFailed) {
|
|
17729
17891
|
eventService.publish({
|
|
17730
|
-
id:
|
|
17892
|
+
id: randomUUID8(),
|
|
17731
17893
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17732
17894
|
type: "hooks.completed",
|
|
17733
17895
|
payload: { running: false, completed: true }
|
|
@@ -17736,7 +17898,7 @@ function startStatusBroadcaster() {
|
|
|
17736
17898
|
}
|
|
17737
17899
|
if (lastHooksRunning && !hooksRunning && hooksFailed) {
|
|
17738
17900
|
eventService.publish({
|
|
17739
|
-
id:
|
|
17901
|
+
id: randomUUID8(),
|
|
17740
17902
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17741
17903
|
type: "hooks.failed",
|
|
17742
17904
|
payload: { running: false, completed: hooksCompleted }
|
|
@@ -17744,7 +17906,7 @@ function startStatusBroadcaster() {
|
|
|
17744
17906
|
});
|
|
17745
17907
|
}
|
|
17746
17908
|
eventService.publish({
|
|
17747
|
-
id:
|
|
17909
|
+
id: randomUUID8(),
|
|
17748
17910
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17749
17911
|
type: "hooks.status",
|
|
17750
17912
|
payload: {
|
|
@@ -17798,20 +17960,20 @@ serve(
|
|
|
17798
17960
|
}
|
|
17799
17961
|
const repos = await gitService.listRepos();
|
|
17800
17962
|
await eventService.publish({
|
|
17801
|
-
id:
|
|
17963
|
+
id: randomUUID8(),
|
|
17802
17964
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17803
17965
|
type: "repo.discovered",
|
|
17804
17966
|
payload: { repos }
|
|
17805
17967
|
});
|
|
17806
17968
|
const repoStatuses = await gitService.listRepos();
|
|
17807
17969
|
await eventService.publish({
|
|
17808
|
-
id:
|
|
17970
|
+
id: randomUUID8(),
|
|
17809
17971
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17810
17972
|
type: "repo.status.changed",
|
|
17811
17973
|
payload: { repos: repoStatuses }
|
|
17812
17974
|
});
|
|
17813
17975
|
await eventService.publish({
|
|
17814
|
-
id:
|
|
17976
|
+
id: randomUUID8(),
|
|
17815
17977
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17816
17978
|
type: "engine.ready",
|
|
17817
17979
|
payload: { version: "v1" }
|