replicas-engine 0.1.61 → 0.1.63
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 +335 -79
- package/package.json +3 -3
- package/dist/src/chunk-ZXMDA7VB.js +0 -16
- package/dist/src/lib-WNJM7YOZ.js +0 -3011
package/dist/src/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-ZXMDA7VB.js";
|
|
3
2
|
|
|
4
3
|
// src/index.ts
|
|
5
4
|
import { serve } from "@hono/node-server";
|
|
6
5
|
import { Hono as Hono2 } from "hono";
|
|
7
|
-
import { readFile as
|
|
6
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
8
7
|
import { execSync } from "child_process";
|
|
9
8
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
10
9
|
|
|
@@ -800,15 +799,18 @@ import { format } from "util";
|
|
|
800
799
|
import { randomBytes } from "crypto";
|
|
801
800
|
var LOG_DIR = join4(homedir3(), ".replicas", "logs");
|
|
802
801
|
var EngineLogger = class {
|
|
803
|
-
|
|
802
|
+
_sessionId = null;
|
|
804
803
|
filePath = null;
|
|
805
804
|
writeChain = Promise.resolve();
|
|
806
805
|
patched = false;
|
|
806
|
+
get sessionId() {
|
|
807
|
+
return this._sessionId;
|
|
808
|
+
}
|
|
807
809
|
async initialize() {
|
|
808
810
|
await mkdir2(LOG_DIR, { recursive: true });
|
|
809
|
-
this.
|
|
810
|
-
this.filePath = join4(LOG_DIR, `${this.
|
|
811
|
-
await writeFile2(this.filePath, `=== Replicas Engine Session ${this.
|
|
811
|
+
this._sessionId = this.createSessionId();
|
|
812
|
+
this.filePath = join4(LOG_DIR, `${this._sessionId}.log`);
|
|
813
|
+
await writeFile2(this.filePath, `=== Replicas Engine Session ${this._sessionId} ===
|
|
812
814
|
`, "utf-8");
|
|
813
815
|
this.patchConsole();
|
|
814
816
|
this.log("INFO", `Engine logging initialized at ${this.filePath}`);
|
|
@@ -854,9 +856,9 @@ var EngineLogger = class {
|
|
|
854
856
|
var engineLogger = new EngineLogger();
|
|
855
857
|
|
|
856
858
|
// src/services/replicas-config-service.ts
|
|
857
|
-
import { readFile as
|
|
859
|
+
import { readFile as readFile4, appendFile as appendFile2, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
858
860
|
import { existsSync as existsSync4 } from "fs";
|
|
859
|
-
import { join as
|
|
861
|
+
import { join as join7 } from "path";
|
|
860
862
|
import { homedir as homedir5 } from "os";
|
|
861
863
|
import { exec } from "child_process";
|
|
862
864
|
import { promisify as promisify2 } from "util";
|
|
@@ -1203,9 +1205,57 @@ var EnvironmentDetailsService = class {
|
|
|
1203
1205
|
};
|
|
1204
1206
|
var environmentDetailsService = new EnvironmentDetailsService();
|
|
1205
1207
|
|
|
1208
|
+
// src/services/start-hook-logs-service.ts
|
|
1209
|
+
import { createHash } from "crypto";
|
|
1210
|
+
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4, readdir as readdir2 } from "fs/promises";
|
|
1211
|
+
import { join as join6 } from "path";
|
|
1212
|
+
var LOGS_DIR = join6(SANDBOX_PATHS.REPLICAS_DIR, "start-hook-logs");
|
|
1213
|
+
function sanitizeFilename(name) {
|
|
1214
|
+
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
1215
|
+
const hash = createHash("sha256").update(name).digest("hex").slice(0, 8);
|
|
1216
|
+
return `${safe}-${hash}`;
|
|
1217
|
+
}
|
|
1218
|
+
var StartHookLogsService = class {
|
|
1219
|
+
async ensureDir() {
|
|
1220
|
+
await mkdir4(LOGS_DIR, { recursive: true });
|
|
1221
|
+
}
|
|
1222
|
+
async saveRepoLog(repoName, entry) {
|
|
1223
|
+
await this.ensureDir();
|
|
1224
|
+
const log = { repoName, ...entry };
|
|
1225
|
+
const filename = `repo-${sanitizeFilename(repoName)}.json`;
|
|
1226
|
+
await writeFile4(join6(LOGS_DIR, filename), `${JSON.stringify(log, null, 2)}
|
|
1227
|
+
`, "utf-8");
|
|
1228
|
+
}
|
|
1229
|
+
async getAllLogs() {
|
|
1230
|
+
let files;
|
|
1231
|
+
try {
|
|
1232
|
+
files = await readdir2(LOGS_DIR);
|
|
1233
|
+
} catch (err) {
|
|
1234
|
+
if (err.code === "ENOENT") {
|
|
1235
|
+
return [];
|
|
1236
|
+
}
|
|
1237
|
+
throw err;
|
|
1238
|
+
}
|
|
1239
|
+
const logs = [];
|
|
1240
|
+
for (const file of files) {
|
|
1241
|
+
if (!file.endsWith(".json")) {
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
try {
|
|
1245
|
+
const raw = await readFile3(join6(LOGS_DIR, file), "utf-8");
|
|
1246
|
+
logs.push(JSON.parse(raw));
|
|
1247
|
+
} catch {
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
logs.sort((a, b) => a.repoName.localeCompare(b.repoName));
|
|
1251
|
+
return logs;
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
var startHookLogsService = new StartHookLogsService();
|
|
1255
|
+
|
|
1206
1256
|
// src/services/replicas-config-service.ts
|
|
1207
1257
|
var execAsync = promisify2(exec);
|
|
1208
|
-
var START_HOOKS_LOG =
|
|
1258
|
+
var START_HOOKS_LOG = join7(homedir5(), ".replicas", "startHooks.log");
|
|
1209
1259
|
var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
|
|
1210
1260
|
Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
|
|
1211
1261
|
These hooks are currently executing in the background. You can:
|
|
@@ -1216,11 +1266,11 @@ The start hooks may install dependencies, build projects, or perform other setup
|
|
|
1216
1266
|
If your task depends on setup being complete, check the log file before proceeding.`;
|
|
1217
1267
|
async function readReplicasConfigFromDir(dirPath) {
|
|
1218
1268
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
1219
|
-
const configPath =
|
|
1269
|
+
const configPath = join7(dirPath, filename);
|
|
1220
1270
|
if (!existsSync4(configPath)) {
|
|
1221
1271
|
continue;
|
|
1222
1272
|
}
|
|
1223
|
-
const data = await
|
|
1273
|
+
const data = await readFile4(configPath, "utf-8");
|
|
1224
1274
|
const config = parseReplicasConfigString(data, filename);
|
|
1225
1275
|
return { config, filename };
|
|
1226
1276
|
}
|
|
@@ -1280,7 +1330,7 @@ var ReplicasConfigService = class {
|
|
|
1280
1330
|
const logLine = `[${timestamp}] ${message}
|
|
1281
1331
|
`;
|
|
1282
1332
|
try {
|
|
1283
|
-
await
|
|
1333
|
+
await mkdir5(join7(homedir5(), ".replicas"), { recursive: true });
|
|
1284
1334
|
await appendFile2(START_HOOKS_LOG, logLine, "utf-8");
|
|
1285
1335
|
} catch (error) {
|
|
1286
1336
|
console.error("Failed to write to start hooks log:", error);
|
|
@@ -1304,8 +1354,8 @@ var ReplicasConfigService = class {
|
|
|
1304
1354
|
this.hooksRunning = true;
|
|
1305
1355
|
this.hooksCompleted = false;
|
|
1306
1356
|
try {
|
|
1307
|
-
await
|
|
1308
|
-
await
|
|
1357
|
+
await mkdir5(join7(homedir5(), ".replicas"), { recursive: true });
|
|
1358
|
+
await writeFile5(
|
|
1309
1359
|
START_HOOKS_LOG,
|
|
1310
1360
|
`=== Start Hooks Execution Log ===
|
|
1311
1361
|
Started: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
@@ -1334,6 +1384,9 @@ Repositories: ${hookEntries.length}
|
|
|
1334
1384
|
}
|
|
1335
1385
|
const timeout = startHookConfig.timeout ?? 3e5;
|
|
1336
1386
|
let repoFailed = false;
|
|
1387
|
+
let lastExitCode = 0;
|
|
1388
|
+
let repoTimedOut = false;
|
|
1389
|
+
const repoOutput = [];
|
|
1337
1390
|
await this.logToFile(`[${entry.repoName}] Executing ${startHookConfig.commands.length} hook(s) with timeout ${timeout}ms`);
|
|
1338
1391
|
for (const hook of startHookConfig.commands) {
|
|
1339
1392
|
try {
|
|
@@ -1344,20 +1397,35 @@ Repositories: ${hookEntries.length}
|
|
|
1344
1397
|
env: process.env
|
|
1345
1398
|
});
|
|
1346
1399
|
if (stdout) {
|
|
1400
|
+
repoOutput.push(stdout);
|
|
1347
1401
|
await this.logToFile(`[${entry.repoName}] [stdout] ${stdout}`);
|
|
1348
1402
|
}
|
|
1349
1403
|
if (stderr) {
|
|
1404
|
+
repoOutput.push(stderr);
|
|
1350
1405
|
await this.logToFile(`[${entry.repoName}] [stderr] ${stderr}`);
|
|
1351
1406
|
}
|
|
1352
1407
|
await this.logToFile(`[${entry.repoName}] --- Completed: ${hook} ---`);
|
|
1353
1408
|
} catch (error) {
|
|
1354
1409
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1410
|
+
const execError = error;
|
|
1411
|
+
lastExitCode = execError.code ?? 1;
|
|
1412
|
+
if (execError.killed) {
|
|
1413
|
+
repoTimedOut = true;
|
|
1414
|
+
}
|
|
1355
1415
|
this.hooksFailed = true;
|
|
1356
1416
|
repoFailed = true;
|
|
1417
|
+
repoOutput.push(`[ERROR] ${hook} failed: ${errorMessage}`);
|
|
1357
1418
|
await this.logToFile(`[${entry.repoName}] [ERROR] ${hook} failed: ${errorMessage}`);
|
|
1358
1419
|
await environmentDetailsService.setRepositoryStartHook(entry.repoName, "no");
|
|
1359
1420
|
}
|
|
1360
1421
|
}
|
|
1422
|
+
await startHookLogsService.saveRepoLog(entry.repoName, {
|
|
1423
|
+
hookCommands: startHookConfig.commands,
|
|
1424
|
+
output: repoOutput.join("\n"),
|
|
1425
|
+
exitCode: lastExitCode,
|
|
1426
|
+
timedOut: repoTimedOut,
|
|
1427
|
+
executedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1428
|
+
});
|
|
1361
1429
|
const fallbackRepoState = persistedRepoState ?? {
|
|
1362
1430
|
name: entry.repoName,
|
|
1363
1431
|
path: entry.workingDirectory,
|
|
@@ -1426,17 +1494,17 @@ Repositories: ${hookEntries.length}
|
|
|
1426
1494
|
var replicasConfigService = new ReplicasConfigService();
|
|
1427
1495
|
|
|
1428
1496
|
// src/services/event-service.ts
|
|
1429
|
-
import { appendFile as appendFile3, mkdir as
|
|
1497
|
+
import { appendFile as appendFile3, mkdir as mkdir6 } from "fs/promises";
|
|
1430
1498
|
import { homedir as homedir6 } from "os";
|
|
1431
|
-
import { join as
|
|
1499
|
+
import { join as join8 } from "path";
|
|
1432
1500
|
import { randomUUID } from "crypto";
|
|
1433
|
-
var ENGINE_DIR =
|
|
1434
|
-
var EVENTS_FILE =
|
|
1501
|
+
var ENGINE_DIR = join8(homedir6(), ".replicas", "engine");
|
|
1502
|
+
var EVENTS_FILE = join8(ENGINE_DIR, "events.jsonl");
|
|
1435
1503
|
var EventService = class {
|
|
1436
1504
|
subscribers = /* @__PURE__ */ new Map();
|
|
1437
1505
|
writeChain = Promise.resolve();
|
|
1438
1506
|
async initialize() {
|
|
1439
|
-
await
|
|
1507
|
+
await mkdir6(ENGINE_DIR, { recursive: true });
|
|
1440
1508
|
}
|
|
1441
1509
|
subscribe(subscriber) {
|
|
1442
1510
|
const id = randomUUID();
|
|
@@ -1466,7 +1534,7 @@ var EventService = class {
|
|
|
1466
1534
|
var eventService = new EventService();
|
|
1467
1535
|
|
|
1468
1536
|
// src/services/preview-service.ts
|
|
1469
|
-
import { mkdir as
|
|
1537
|
+
import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
1470
1538
|
import { existsSync as existsSync5 } from "fs";
|
|
1471
1539
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1472
1540
|
import { dirname } from "path";
|
|
@@ -1476,7 +1544,7 @@ async function readPreviewsFile() {
|
|
|
1476
1544
|
if (!existsSync5(PREVIEW_PORTS_FILE)) {
|
|
1477
1545
|
return { previews: [] };
|
|
1478
1546
|
}
|
|
1479
|
-
const raw = await
|
|
1547
|
+
const raw = await readFile5(PREVIEW_PORTS_FILE, "utf-8");
|
|
1480
1548
|
return JSON.parse(raw);
|
|
1481
1549
|
} catch {
|
|
1482
1550
|
return { previews: [] };
|
|
@@ -1484,8 +1552,8 @@ async function readPreviewsFile() {
|
|
|
1484
1552
|
}
|
|
1485
1553
|
async function writePreviewsFile(data) {
|
|
1486
1554
|
const dir = dirname(PREVIEW_PORTS_FILE);
|
|
1487
|
-
await
|
|
1488
|
-
await
|
|
1555
|
+
await mkdir7(dir, { recursive: true });
|
|
1556
|
+
await writeFile6(PREVIEW_PORTS_FILE, `${JSON.stringify(data, null, 2)}
|
|
1489
1557
|
`, "utf-8");
|
|
1490
1558
|
}
|
|
1491
1559
|
var PreviewService = class {
|
|
@@ -1521,21 +1589,21 @@ var PreviewService = class {
|
|
|
1521
1589
|
var previewService = new PreviewService();
|
|
1522
1590
|
|
|
1523
1591
|
// src/services/chat/chat-service.ts
|
|
1524
|
-
import { mkdir as
|
|
1592
|
+
import { mkdir as mkdir10, readFile as readFile8, rm, writeFile as writeFile8 } from "fs/promises";
|
|
1525
1593
|
import { homedir as homedir9 } from "os";
|
|
1526
|
-
import { join as
|
|
1594
|
+
import { join as join11 } from "path";
|
|
1527
1595
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
1528
1596
|
|
|
1529
1597
|
// src/managers/claude-manager.ts
|
|
1530
1598
|
import {
|
|
1531
1599
|
query
|
|
1532
1600
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
1533
|
-
import { join as
|
|
1534
|
-
import { mkdir as
|
|
1601
|
+
import { join as join9 } from "path";
|
|
1602
|
+
import { mkdir as mkdir8, appendFile as appendFile4 } from "fs/promises";
|
|
1535
1603
|
import { homedir as homedir7 } from "os";
|
|
1536
1604
|
|
|
1537
1605
|
// src/utils/jsonl-reader.ts
|
|
1538
|
-
import { readFile as
|
|
1606
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
1539
1607
|
function isJsonlEvent(value) {
|
|
1540
1608
|
if (!isRecord(value)) {
|
|
1541
1609
|
return false;
|
|
@@ -1557,7 +1625,7 @@ function parseJsonlEvents(lines) {
|
|
|
1557
1625
|
}
|
|
1558
1626
|
async function readJSONL(filePath) {
|
|
1559
1627
|
try {
|
|
1560
|
-
const content = await
|
|
1628
|
+
const content = await readFile6(filePath, "utf-8");
|
|
1561
1629
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
1562
1630
|
return parseJsonlEvents(lines);
|
|
1563
1631
|
} catch (error) {
|
|
@@ -2276,8 +2344,8 @@ var PromptStream = class {
|
|
|
2276
2344
|
if (this.closed) {
|
|
2277
2345
|
return Promise.resolve({ value: void 0, done: true });
|
|
2278
2346
|
}
|
|
2279
|
-
return new Promise((
|
|
2280
|
-
this.waiters.push(
|
|
2347
|
+
return new Promise((resolve2) => {
|
|
2348
|
+
this.waiters.push(resolve2);
|
|
2281
2349
|
});
|
|
2282
2350
|
}
|
|
2283
2351
|
};
|
|
@@ -2294,7 +2362,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
2294
2362
|
pendingInterrupt = false;
|
|
2295
2363
|
constructor(options) {
|
|
2296
2364
|
super(options);
|
|
2297
|
-
this.historyFile = options.historyFilePath ??
|
|
2365
|
+
this.historyFile = options.historyFilePath ?? join9(homedir7(), ".replicas", "claude", "history.jsonl");
|
|
2298
2366
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
2299
2367
|
}
|
|
2300
2368
|
async interruptActiveTurn() {
|
|
@@ -2463,8 +2531,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
2463
2531
|
};
|
|
2464
2532
|
}
|
|
2465
2533
|
async initialize() {
|
|
2466
|
-
const historyDir =
|
|
2467
|
-
await
|
|
2534
|
+
const historyDir = join9(homedir7(), ".replicas", "claude");
|
|
2535
|
+
await mkdir8(historyDir, { recursive: true });
|
|
2468
2536
|
if (this.initialSessionId) {
|
|
2469
2537
|
this.sessionId = this.initialSessionId;
|
|
2470
2538
|
console.log(`[ClaudeManager] Restored session ID from persisted state: ${this.sessionId}`);
|
|
@@ -2515,13 +2583,13 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
2515
2583
|
// src/managers/codex-manager.ts
|
|
2516
2584
|
import { Codex } from "@openai/codex-sdk";
|
|
2517
2585
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2518
|
-
import { readdir as
|
|
2586
|
+
import { readdir as readdir3, stat as stat2, writeFile as writeFile7, mkdir as mkdir9, readFile as readFile7 } from "fs/promises";
|
|
2519
2587
|
import { existsSync as existsSync6 } from "fs";
|
|
2520
|
-
import { join as
|
|
2588
|
+
import { join as join10 } from "path";
|
|
2521
2589
|
import { homedir as homedir8 } from "os";
|
|
2522
2590
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
2523
2591
|
var DEFAULT_MODEL = "gpt-5.4";
|
|
2524
|
-
var CODEX_CONFIG_PATH =
|
|
2592
|
+
var CODEX_CONFIG_PATH = join10(homedir8(), ".codex", "config.toml");
|
|
2525
2593
|
function isLinearThoughtEvent2(event) {
|
|
2526
2594
|
return event.content.type === "thought";
|
|
2527
2595
|
}
|
|
@@ -2532,7 +2600,7 @@ function isJsonlEvent2(value) {
|
|
|
2532
2600
|
return typeof value.timestamp === "string" && typeof value.type === "string" && isRecord(value.payload);
|
|
2533
2601
|
}
|
|
2534
2602
|
function sleep(ms) {
|
|
2535
|
-
return new Promise((
|
|
2603
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2536
2604
|
}
|
|
2537
2605
|
var CodexManager = class extends CodingAgentManager {
|
|
2538
2606
|
codex;
|
|
@@ -2543,7 +2611,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2543
2611
|
constructor(options) {
|
|
2544
2612
|
super(options);
|
|
2545
2613
|
this.codex = new Codex();
|
|
2546
|
-
this.tempImageDir =
|
|
2614
|
+
this.tempImageDir = join10(homedir8(), ".replicas", "codex", "temp-images");
|
|
2547
2615
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
2548
2616
|
}
|
|
2549
2617
|
async initialize() {
|
|
@@ -2563,12 +2631,12 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2563
2631
|
*/
|
|
2564
2632
|
async updateCodexConfig(developerInstructions) {
|
|
2565
2633
|
try {
|
|
2566
|
-
const codexDir =
|
|
2567
|
-
await
|
|
2634
|
+
const codexDir = join10(homedir8(), ".codex");
|
|
2635
|
+
await mkdir9(codexDir, { recursive: true });
|
|
2568
2636
|
let config = {};
|
|
2569
2637
|
if (existsSync6(CODEX_CONFIG_PATH)) {
|
|
2570
2638
|
try {
|
|
2571
|
-
const existingContent = await
|
|
2639
|
+
const existingContent = await readFile7(CODEX_CONFIG_PATH, "utf-8");
|
|
2572
2640
|
const parsed = parseToml(existingContent);
|
|
2573
2641
|
if (isRecord(parsed)) {
|
|
2574
2642
|
config = parsed;
|
|
@@ -2583,7 +2651,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2583
2651
|
delete config.developer_instructions;
|
|
2584
2652
|
}
|
|
2585
2653
|
const tomlContent = stringifyToml(config);
|
|
2586
|
-
await
|
|
2654
|
+
await writeFile7(CODEX_CONFIG_PATH, tomlContent, "utf-8");
|
|
2587
2655
|
console.log("[CodexManager] Updated config.toml with developer_instructions");
|
|
2588
2656
|
} catch (error) {
|
|
2589
2657
|
console.error("[CodexManager] Failed to update config.toml:", error);
|
|
@@ -2594,14 +2662,14 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2594
2662
|
* @returns Array of temp file paths
|
|
2595
2663
|
*/
|
|
2596
2664
|
async saveImagesToTempFiles(images) {
|
|
2597
|
-
await
|
|
2665
|
+
await mkdir9(this.tempImageDir, { recursive: true });
|
|
2598
2666
|
const tempPaths = [];
|
|
2599
2667
|
for (const image of images) {
|
|
2600
2668
|
const ext = image.source.media_type.split("/")[1] || "png";
|
|
2601
2669
|
const filename = `img_${randomUUID3()}.${ext}`;
|
|
2602
|
-
const filepath =
|
|
2670
|
+
const filepath = join10(this.tempImageDir, filename);
|
|
2603
2671
|
const buffer = Buffer.from(image.source.data, "base64");
|
|
2604
|
-
await
|
|
2672
|
+
await writeFile7(filepath, buffer);
|
|
2605
2673
|
tempPaths.push(filepath);
|
|
2606
2674
|
}
|
|
2607
2675
|
return tempPaths;
|
|
@@ -2731,13 +2799,13 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2731
2799
|
}
|
|
2732
2800
|
// Helper methods for finding session files
|
|
2733
2801
|
async findSessionFile(threadId) {
|
|
2734
|
-
const sessionsDir =
|
|
2802
|
+
const sessionsDir = join10(homedir8(), ".codex", "sessions");
|
|
2735
2803
|
try {
|
|
2736
2804
|
const now = /* @__PURE__ */ new Date();
|
|
2737
2805
|
const year = now.getFullYear();
|
|
2738
2806
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2739
2807
|
const day = String(now.getDate()).padStart(2, "0");
|
|
2740
|
-
const todayDir =
|
|
2808
|
+
const todayDir = join10(sessionsDir, String(year), month, day);
|
|
2741
2809
|
const file = await this.findFileInDirectory(todayDir, threadId);
|
|
2742
2810
|
if (file) return file;
|
|
2743
2811
|
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
@@ -2746,7 +2814,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2746
2814
|
const searchYear = date.getFullYear();
|
|
2747
2815
|
const searchMonth = String(date.getMonth() + 1).padStart(2, "0");
|
|
2748
2816
|
const searchDay = String(date.getDate()).padStart(2, "0");
|
|
2749
|
-
const searchDir =
|
|
2817
|
+
const searchDir = join10(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
2750
2818
|
const file2 = await this.findFileInDirectory(searchDir, threadId);
|
|
2751
2819
|
if (file2) return file2;
|
|
2752
2820
|
}
|
|
@@ -2757,10 +2825,10 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2757
2825
|
}
|
|
2758
2826
|
async findFileInDirectory(directory, threadId) {
|
|
2759
2827
|
try {
|
|
2760
|
-
const files = await
|
|
2828
|
+
const files = await readdir3(directory);
|
|
2761
2829
|
for (const file of files) {
|
|
2762
2830
|
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
2763
|
-
const fullPath =
|
|
2831
|
+
const fullPath = join10(directory, file);
|
|
2764
2832
|
const stats = await stat2(fullPath);
|
|
2765
2833
|
if (stats.isFile()) {
|
|
2766
2834
|
return fullPath;
|
|
@@ -2793,7 +2861,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2793
2861
|
const seenLines = /* @__PURE__ */ new Set();
|
|
2794
2862
|
const seedSeenLines = async () => {
|
|
2795
2863
|
try {
|
|
2796
|
-
const content = await
|
|
2864
|
+
const content = await readFile7(sessionFile, "utf-8");
|
|
2797
2865
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
2798
2866
|
for (const line of lines) {
|
|
2799
2867
|
seenLines.add(line);
|
|
@@ -2805,7 +2873,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2805
2873
|
const pump = async () => {
|
|
2806
2874
|
let emitted = 0;
|
|
2807
2875
|
try {
|
|
2808
|
-
const content = await
|
|
2876
|
+
const content = await readFile7(sessionFile, "utf-8");
|
|
2809
2877
|
const lines = content.split("\n");
|
|
2810
2878
|
const completeLines = content.endsWith("\n") ? lines : lines.slice(0, -1);
|
|
2811
2879
|
for (const line of completeLines) {
|
|
@@ -2876,9 +2944,9 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
2876
2944
|
};
|
|
2877
2945
|
|
|
2878
2946
|
// src/services/chat/chat-service.ts
|
|
2879
|
-
var ENGINE_DIR2 =
|
|
2880
|
-
var CHATS_FILE =
|
|
2881
|
-
var CLAUDE_HISTORY_DIR =
|
|
2947
|
+
var ENGINE_DIR2 = join11(homedir9(), ".replicas", "engine");
|
|
2948
|
+
var CHATS_FILE = join11(ENGINE_DIR2, "chats.json");
|
|
2949
|
+
var CLAUDE_HISTORY_DIR = join11(ENGINE_DIR2, "claude-histories");
|
|
2882
2950
|
function isPersistedChat(value) {
|
|
2883
2951
|
if (!isRecord(value)) {
|
|
2884
2952
|
return false;
|
|
@@ -2893,8 +2961,8 @@ var ChatService = class {
|
|
|
2893
2961
|
chats = /* @__PURE__ */ new Map();
|
|
2894
2962
|
writeChain = Promise.resolve();
|
|
2895
2963
|
async initialize() {
|
|
2896
|
-
await
|
|
2897
|
-
await
|
|
2964
|
+
await mkdir10(ENGINE_DIR2, { recursive: true });
|
|
2965
|
+
await mkdir10(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
2898
2966
|
const persisted = await this.loadChats();
|
|
2899
2967
|
for (const chat of persisted) {
|
|
2900
2968
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -2990,7 +3058,7 @@ var ChatService = class {
|
|
|
2990
3058
|
this.chats.delete(chatId);
|
|
2991
3059
|
await this.persistAllChats();
|
|
2992
3060
|
if (chat.persisted.provider === "claude") {
|
|
2993
|
-
const historyFilePath =
|
|
3061
|
+
const historyFilePath = join11(CLAUDE_HISTORY_DIR, `${chatId}.jsonl`);
|
|
2994
3062
|
await rm(historyFilePath, { force: true });
|
|
2995
3063
|
}
|
|
2996
3064
|
await this.publish({
|
|
@@ -3033,7 +3101,7 @@ var ChatService = class {
|
|
|
3033
3101
|
};
|
|
3034
3102
|
const provider = persisted.provider === "claude" ? new ClaudeManager({
|
|
3035
3103
|
workingDirectory: this.workingDirectory,
|
|
3036
|
-
historyFilePath:
|
|
3104
|
+
historyFilePath: join11(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
3037
3105
|
initialSessionId: persisted.providerSessionId,
|
|
3038
3106
|
onSaveSessionId: saveSession,
|
|
3039
3107
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -3129,7 +3197,7 @@ var ChatService = class {
|
|
|
3129
3197
|
}
|
|
3130
3198
|
async loadChats() {
|
|
3131
3199
|
try {
|
|
3132
|
-
const content = await
|
|
3200
|
+
const content = await readFile8(CHATS_FILE, "utf-8");
|
|
3133
3201
|
const parsed = JSON.parse(content);
|
|
3134
3202
|
if (!Array.isArray(parsed)) {
|
|
3135
3203
|
return [];
|
|
@@ -3146,7 +3214,7 @@ var ChatService = class {
|
|
|
3146
3214
|
this.writeChain = this.writeChain.catch(() => {
|
|
3147
3215
|
}).then(async () => {
|
|
3148
3216
|
const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
|
|
3149
|
-
await
|
|
3217
|
+
await writeFile8(CHATS_FILE, JSON.stringify(payload, null, 2), "utf-8");
|
|
3150
3218
|
});
|
|
3151
3219
|
await this.writeChain;
|
|
3152
3220
|
}
|
|
@@ -3174,14 +3242,16 @@ var ChatService = class {
|
|
|
3174
3242
|
// src/v1-routes.ts
|
|
3175
3243
|
import { Hono } from "hono";
|
|
3176
3244
|
import { z } from "zod";
|
|
3245
|
+
import { readdir as readdir6, stat as stat3, readFile as readFile12 } from "fs/promises";
|
|
3246
|
+
import { join as join15, resolve } from "path";
|
|
3177
3247
|
|
|
3178
3248
|
// src/services/plan-service.ts
|
|
3179
|
-
import { readdir as
|
|
3249
|
+
import { readdir as readdir4, readFile as readFile9 } from "fs/promises";
|
|
3180
3250
|
import { homedir as homedir10 } from "os";
|
|
3181
|
-
import { basename, join as
|
|
3251
|
+
import { basename, join as join12 } from "path";
|
|
3182
3252
|
var PLAN_DIRECTORIES = [
|
|
3183
|
-
|
|
3184
|
-
|
|
3253
|
+
join12(homedir10(), ".claude", "plans"),
|
|
3254
|
+
join12(homedir10(), ".replicas", "plans")
|
|
3185
3255
|
];
|
|
3186
3256
|
function isMarkdownFile(filename) {
|
|
3187
3257
|
return filename.toLowerCase().endsWith(".md");
|
|
@@ -3194,7 +3264,7 @@ var PlanService = class {
|
|
|
3194
3264
|
const planNames = /* @__PURE__ */ new Set();
|
|
3195
3265
|
for (const directory of PLAN_DIRECTORIES) {
|
|
3196
3266
|
try {
|
|
3197
|
-
const entries = await
|
|
3267
|
+
const entries = await readdir4(directory, { withFileTypes: true });
|
|
3198
3268
|
for (const entry of entries) {
|
|
3199
3269
|
if (!entry.isFile()) {
|
|
3200
3270
|
continue;
|
|
@@ -3215,9 +3285,9 @@ var PlanService = class {
|
|
|
3215
3285
|
return null;
|
|
3216
3286
|
}
|
|
3217
3287
|
for (const directory of PLAN_DIRECTORIES) {
|
|
3218
|
-
const filePath =
|
|
3288
|
+
const filePath = join12(directory, safeFilename);
|
|
3219
3289
|
try {
|
|
3220
|
-
const content = await
|
|
3290
|
+
const content = await readFile9(filePath, "utf-8");
|
|
3221
3291
|
return { filename: safeFilename, content };
|
|
3222
3292
|
} catch {
|
|
3223
3293
|
}
|
|
@@ -3230,9 +3300,78 @@ var planService = new PlanService();
|
|
|
3230
3300
|
// src/services/warm-hooks-service.ts
|
|
3231
3301
|
import { execFile as execFile2 } from "child_process";
|
|
3232
3302
|
import { promisify as promisify3 } from "util";
|
|
3233
|
-
import { readFile as
|
|
3303
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
3234
3304
|
import { existsSync as existsSync7 } from "fs";
|
|
3235
|
-
import { join as
|
|
3305
|
+
import { join as join14 } from "path";
|
|
3306
|
+
|
|
3307
|
+
// src/services/warm-hook-logs-service.ts
|
|
3308
|
+
import { createHash as createHash2 } from "crypto";
|
|
3309
|
+
import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile9, readdir as readdir5 } from "fs/promises";
|
|
3310
|
+
import { join as join13 } from "path";
|
|
3311
|
+
var LOGS_DIR2 = join13(SANDBOX_PATHS.REPLICAS_DIR, "warm-hook-logs");
|
|
3312
|
+
function sanitizeFilename2(name) {
|
|
3313
|
+
const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3314
|
+
const hash = createHash2("sha256").update(name).digest("hex").slice(0, 8);
|
|
3315
|
+
return `${safe}-${hash}`;
|
|
3316
|
+
}
|
|
3317
|
+
var WarmHookLogsService = class {
|
|
3318
|
+
async ensureDir() {
|
|
3319
|
+
await mkdir11(LOGS_DIR2, { recursive: true });
|
|
3320
|
+
}
|
|
3321
|
+
async saveGlobalHookLog(entry) {
|
|
3322
|
+
await this.ensureDir();
|
|
3323
|
+
const log = {
|
|
3324
|
+
hookType: "global",
|
|
3325
|
+
hookName: "organization",
|
|
3326
|
+
...entry
|
|
3327
|
+
};
|
|
3328
|
+
await writeFile9(join13(LOGS_DIR2, "global.json"), `${JSON.stringify(log, null, 2)}
|
|
3329
|
+
`, "utf-8");
|
|
3330
|
+
}
|
|
3331
|
+
async saveRepoHookLog(repoName, entry) {
|
|
3332
|
+
await this.ensureDir();
|
|
3333
|
+
const log = {
|
|
3334
|
+
hookType: "repository",
|
|
3335
|
+
hookName: repoName,
|
|
3336
|
+
...entry
|
|
3337
|
+
};
|
|
3338
|
+
const filename = `repo-${sanitizeFilename2(repoName)}.json`;
|
|
3339
|
+
await writeFile9(join13(LOGS_DIR2, filename), `${JSON.stringify(log, null, 2)}
|
|
3340
|
+
`, "utf-8");
|
|
3341
|
+
}
|
|
3342
|
+
async getAllLogs() {
|
|
3343
|
+
let files;
|
|
3344
|
+
try {
|
|
3345
|
+
files = await readdir5(LOGS_DIR2);
|
|
3346
|
+
} catch (err) {
|
|
3347
|
+
if (err.code === "ENOENT") {
|
|
3348
|
+
return [];
|
|
3349
|
+
}
|
|
3350
|
+
throw err;
|
|
3351
|
+
}
|
|
3352
|
+
const logs = [];
|
|
3353
|
+
for (const file of files) {
|
|
3354
|
+
if (!file.endsWith(".json")) {
|
|
3355
|
+
continue;
|
|
3356
|
+
}
|
|
3357
|
+
try {
|
|
3358
|
+
const raw = await readFile10(join13(LOGS_DIR2, file), "utf-8");
|
|
3359
|
+
logs.push(JSON.parse(raw));
|
|
3360
|
+
} catch {
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
logs.sort((a, b) => {
|
|
3364
|
+
if (a.hookType !== b.hookType) {
|
|
3365
|
+
return a.hookType === "global" ? -1 : 1;
|
|
3366
|
+
}
|
|
3367
|
+
return a.hookName.localeCompare(b.hookName);
|
|
3368
|
+
});
|
|
3369
|
+
return logs;
|
|
3370
|
+
}
|
|
3371
|
+
};
|
|
3372
|
+
var warmHookLogsService = new WarmHookLogsService();
|
|
3373
|
+
|
|
3374
|
+
// src/services/warm-hooks-service.ts
|
|
3236
3375
|
var execFileAsync2 = promisify3(execFile2);
|
|
3237
3376
|
async function executeHookScript(params) {
|
|
3238
3377
|
const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
|
|
@@ -3263,12 +3402,12 @@ async function executeHookScript(params) {
|
|
|
3263
3402
|
}
|
|
3264
3403
|
async function readRepoWarmHook(repoPath) {
|
|
3265
3404
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
3266
|
-
const configPath =
|
|
3405
|
+
const configPath = join14(repoPath, filename);
|
|
3267
3406
|
if (!existsSync7(configPath)) {
|
|
3268
3407
|
continue;
|
|
3269
3408
|
}
|
|
3270
3409
|
try {
|
|
3271
|
-
const raw = await
|
|
3410
|
+
const raw = await readFile11(configPath, "utf-8");
|
|
3272
3411
|
const config = parseReplicasConfigString(raw, filename);
|
|
3273
3412
|
if (!config.warmHook) {
|
|
3274
3413
|
return null;
|
|
@@ -3314,6 +3453,13 @@ async function runWarmHooks(params) {
|
|
|
3314
3453
|
});
|
|
3315
3454
|
outputBlocks.push(orgResult.output);
|
|
3316
3455
|
await environmentDetailsService.setGlobalWarmHook(orgResult.exitCode === 0 ? "yes" : "no", orgResult.output);
|
|
3456
|
+
await warmHookLogsService.saveGlobalHookLog({
|
|
3457
|
+
hookScript: orgHook,
|
|
3458
|
+
output: orgResult.output,
|
|
3459
|
+
exitCode: orgResult.exitCode,
|
|
3460
|
+
timedOut: orgResult.timedOut,
|
|
3461
|
+
executedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3462
|
+
});
|
|
3317
3463
|
if (orgResult.exitCode !== 0) {
|
|
3318
3464
|
return {
|
|
3319
3465
|
exitCode: orgResult.exitCode,
|
|
@@ -3334,6 +3480,11 @@ async function runWarmHooks(params) {
|
|
|
3334
3480
|
}
|
|
3335
3481
|
}
|
|
3336
3482
|
for (const repoHook of repoHooks) {
|
|
3483
|
+
const combinedScript = repoHook.commands.join("\n");
|
|
3484
|
+
const repoOutputBlocks = [];
|
|
3485
|
+
let repoFailed = false;
|
|
3486
|
+
let repoTimedOut = false;
|
|
3487
|
+
let repoExitCode = 0;
|
|
3337
3488
|
for (const command of repoHook.commands) {
|
|
3338
3489
|
const repoResult = await executeHookScript({
|
|
3339
3490
|
label: `repo-warm-hook:${repoHook.repoName}`,
|
|
@@ -3342,18 +3493,32 @@ async function runWarmHooks(params) {
|
|
|
3342
3493
|
timeoutMs: repoHook.timeoutMs
|
|
3343
3494
|
});
|
|
3344
3495
|
outputBlocks.push(repoResult.output);
|
|
3496
|
+
repoOutputBlocks.push(repoResult.output);
|
|
3345
3497
|
await environmentDetailsService.setRepositoryWarmHook(
|
|
3346
3498
|
repoHook.repoName,
|
|
3347
3499
|
repoResult.exitCode === 0 ? "yes" : "no"
|
|
3348
3500
|
);
|
|
3349
3501
|
if (repoResult.exitCode !== 0) {
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
};
|
|
3502
|
+
repoFailed = true;
|
|
3503
|
+
repoTimedOut = repoResult.timedOut;
|
|
3504
|
+
repoExitCode = repoResult.exitCode;
|
|
3505
|
+
break;
|
|
3355
3506
|
}
|
|
3356
3507
|
}
|
|
3508
|
+
await warmHookLogsService.saveRepoHookLog(repoHook.repoName, {
|
|
3509
|
+
hookScript: combinedScript,
|
|
3510
|
+
output: repoOutputBlocks.join("\n\n"),
|
|
3511
|
+
exitCode: repoFailed ? repoExitCode : 0,
|
|
3512
|
+
timedOut: repoTimedOut,
|
|
3513
|
+
executedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3514
|
+
});
|
|
3515
|
+
if (repoFailed) {
|
|
3516
|
+
return {
|
|
3517
|
+
exitCode: repoExitCode,
|
|
3518
|
+
output: outputBlocks.join("\n\n"),
|
|
3519
|
+
timedOut: repoTimedOut
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3357
3522
|
}
|
|
3358
3523
|
} else {
|
|
3359
3524
|
const repos = await gitService.listRepositories();
|
|
@@ -3651,6 +3816,28 @@ function createV1Routes(deps) {
|
|
|
3651
3816
|
);
|
|
3652
3817
|
}
|
|
3653
3818
|
});
|
|
3819
|
+
app2.get("/warm-hooks/logs", async (c) => {
|
|
3820
|
+
try {
|
|
3821
|
+
const logs = await warmHookLogsService.getAllLogs();
|
|
3822
|
+
return c.json({ logs });
|
|
3823
|
+
} catch (error) {
|
|
3824
|
+
return c.json(
|
|
3825
|
+
jsonError("Failed to read warm hook logs", error instanceof Error ? error.message : "Unknown error"),
|
|
3826
|
+
500
|
|
3827
|
+
);
|
|
3828
|
+
}
|
|
3829
|
+
});
|
|
3830
|
+
app2.get("/start-hooks/logs", async (c) => {
|
|
3831
|
+
try {
|
|
3832
|
+
const logs = await startHookLogsService.getAllLogs();
|
|
3833
|
+
return c.json({ logs });
|
|
3834
|
+
} catch (error) {
|
|
3835
|
+
return c.json(
|
|
3836
|
+
jsonError("Failed to read start hook logs", error instanceof Error ? error.message : "Unknown error"),
|
|
3837
|
+
500
|
|
3838
|
+
);
|
|
3839
|
+
}
|
|
3840
|
+
});
|
|
3654
3841
|
app2.post("/workspace-name", async (c) => {
|
|
3655
3842
|
try {
|
|
3656
3843
|
const body = setWorkspaceNameSchema.parse(await c.req.json());
|
|
@@ -3688,6 +3875,75 @@ function createV1Routes(deps) {
|
|
|
3688
3875
|
return c.json(jsonError("Failed to create preview", details), 400);
|
|
3689
3876
|
}
|
|
3690
3877
|
});
|
|
3878
|
+
app2.get("/logs", async (c) => {
|
|
3879
|
+
try {
|
|
3880
|
+
const files = await readdir6(LOG_DIR).catch(() => []);
|
|
3881
|
+
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
3882
|
+
const sessions = await Promise.all(
|
|
3883
|
+
logFiles.map(async (filename) => {
|
|
3884
|
+
const filePath = join15(LOG_DIR, filename);
|
|
3885
|
+
const fileStat = await stat3(filePath);
|
|
3886
|
+
const sessionId = filename.replace(/\.log$/, "");
|
|
3887
|
+
return {
|
|
3888
|
+
sessionId,
|
|
3889
|
+
filename,
|
|
3890
|
+
sizeBytes: fileStat.size,
|
|
3891
|
+
updatedAt: fileStat.mtime.toISOString()
|
|
3892
|
+
};
|
|
3893
|
+
})
|
|
3894
|
+
);
|
|
3895
|
+
sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
3896
|
+
return c.json({
|
|
3897
|
+
currentSessionId: engineLogger.sessionId,
|
|
3898
|
+
sessions
|
|
3899
|
+
});
|
|
3900
|
+
} catch (error) {
|
|
3901
|
+
return c.json(
|
|
3902
|
+
jsonError("Failed to list logs", error instanceof Error ? error.message : "Unknown error"),
|
|
3903
|
+
500
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3906
|
+
});
|
|
3907
|
+
app2.get("/logs/:sessionId", async (c) => {
|
|
3908
|
+
try {
|
|
3909
|
+
const sessionId = c.req.param("sessionId");
|
|
3910
|
+
if (!sessionId || /[/\\]/.test(sessionId) || sessionId.includes("..")) {
|
|
3911
|
+
return c.json(jsonError("Invalid session ID"), 400);
|
|
3912
|
+
}
|
|
3913
|
+
const filePath = resolve(LOG_DIR, `${sessionId}.log`);
|
|
3914
|
+
if (!filePath.startsWith(resolve(LOG_DIR))) {
|
|
3915
|
+
return c.json(jsonError("Invalid session ID"), 400);
|
|
3916
|
+
}
|
|
3917
|
+
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
3918
|
+
const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
|
|
3919
|
+
let content;
|
|
3920
|
+
try {
|
|
3921
|
+
content = await readFile12(filePath, "utf-8");
|
|
3922
|
+
} catch {
|
|
3923
|
+
return c.json(jsonError("Log session not found"), 404);
|
|
3924
|
+
}
|
|
3925
|
+
const allLines = content.split("\n");
|
|
3926
|
+
if (allLines.length > 0 && allLines[allLines.length - 1] === "") {
|
|
3927
|
+
allLines.pop();
|
|
3928
|
+
}
|
|
3929
|
+
const totalLines = allLines.length;
|
|
3930
|
+
const slicedLines = allLines.slice(offset, offset + limit);
|
|
3931
|
+
const hasMore = offset + limit < totalLines;
|
|
3932
|
+
return c.json({
|
|
3933
|
+
sessionId,
|
|
3934
|
+
totalLines,
|
|
3935
|
+
offset,
|
|
3936
|
+
limit,
|
|
3937
|
+
hasMore,
|
|
3938
|
+
lines: slicedLines
|
|
3939
|
+
});
|
|
3940
|
+
} catch (error) {
|
|
3941
|
+
return c.json(
|
|
3942
|
+
jsonError("Failed to read log", error instanceof Error ? error.message : "Unknown error"),
|
|
3943
|
+
500
|
|
3944
|
+
);
|
|
3945
|
+
}
|
|
3946
|
+
});
|
|
3691
3947
|
return app2;
|
|
3692
3948
|
}
|
|
3693
3949
|
|
|
@@ -3728,7 +3984,7 @@ app.get("/health", async (c) => {
|
|
|
3728
3984
|
return c.json({ status: "initializing", timestamp: (/* @__PURE__ */ new Date()).toISOString() }, 503);
|
|
3729
3985
|
}
|
|
3730
3986
|
try {
|
|
3731
|
-
const logContent = await
|
|
3987
|
+
const logContent = await readFile13("/var/log/cloud-init-output.log", "utf-8");
|
|
3732
3988
|
let status;
|
|
3733
3989
|
if (logContent.includes(COMPLETION_MESSAGE)) {
|
|
3734
3990
|
status = "active";
|