replicas-engine 0.1.62 → 0.1.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/src/index.js +121 -16
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/index.ts
4
4
  import { serve } from "@hono/node-server";
5
5
  import { Hono as Hono2 } from "hono";
6
- import { readFile as readFile12 } from "fs/promises";
6
+ import { readFile as readFile13 } from "fs/promises";
7
7
  import { execSync } from "child_process";
8
8
  import { randomUUID as randomUUID5 } from "crypto";
9
9
 
@@ -799,15 +799,18 @@ import { format } from "util";
799
799
  import { randomBytes } from "crypto";
800
800
  var LOG_DIR = join4(homedir3(), ".replicas", "logs");
801
801
  var EngineLogger = class {
802
- sessionId = null;
802
+ _sessionId = null;
803
803
  filePath = null;
804
804
  writeChain = Promise.resolve();
805
805
  patched = false;
806
+ get sessionId() {
807
+ return this._sessionId;
808
+ }
806
809
  async initialize() {
807
810
  await mkdir2(LOG_DIR, { recursive: true });
808
- this.sessionId = this.createSessionId();
809
- this.filePath = join4(LOG_DIR, `${this.sessionId}.log`);
810
- await writeFile2(this.filePath, `=== Replicas Engine Session ${this.sessionId} ===
811
+ this._sessionId = this.createSessionId();
812
+ this.filePath = join4(LOG_DIR, `${this._sessionId}.log`);
813
+ await writeFile2(this.filePath, `=== Replicas Engine Session ${this._sessionId} ===
811
814
  `, "utf-8");
812
815
  this.patchConsole();
813
816
  this.log("INFO", `Engine logging initialized at ${this.filePath}`);
@@ -844,6 +847,10 @@ var EngineLogger = class {
844
847
  }
845
848
  });
846
849
  }
850
+ /** Wait for all queued log writes to complete. */
851
+ flush() {
852
+ return this.writeChain;
853
+ }
847
854
  createSessionId() {
848
855
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
849
856
  const suffix = randomBytes(3).toString("hex");
@@ -2147,7 +2154,9 @@ var MessageQueueService = class {
2147
2154
  position: this.queue.length
2148
2155
  };
2149
2156
  }
2150
- this.startProcessing(queuedMessage);
2157
+ this.startProcessing(queuedMessage).catch((error) => {
2158
+ console.error("[MessageQueue] Unhandled error in startProcessing:", error);
2159
+ });
2151
2160
  return {
2152
2161
  queued: false,
2153
2162
  messageId,
@@ -2341,8 +2350,8 @@ var PromptStream = class {
2341
2350
  if (this.closed) {
2342
2351
  return Promise.resolve({ value: void 0, done: true });
2343
2352
  }
2344
- return new Promise((resolve) => {
2345
- this.waiters.push(resolve);
2353
+ return new Promise((resolve2) => {
2354
+ this.waiters.push(resolve2);
2346
2355
  });
2347
2356
  }
2348
2357
  };
@@ -2504,7 +2513,11 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
2504
2513
  this.activePromptStream?.close();
2505
2514
  this.activePromptStream = null;
2506
2515
  this.pendingInterrupt = false;
2507
- await this.onTurnComplete();
2516
+ try {
2517
+ await this.onTurnComplete();
2518
+ } catch (error) {
2519
+ console.error("[ClaudeManager] onTurnComplete failed:", error);
2520
+ }
2508
2521
  }
2509
2522
  }
2510
2523
  /**
@@ -2597,7 +2610,7 @@ function isJsonlEvent2(value) {
2597
2610
  return typeof value.timestamp === "string" && typeof value.type === "string" && isRecord(value.payload);
2598
2611
  }
2599
2612
  function sleep(ms) {
2600
- return new Promise((resolve) => setTimeout(resolve, ms));
2613
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2601
2614
  }
2602
2615
  var CodexManager = class extends CodingAgentManager {
2603
2616
  codex;
@@ -2770,7 +2783,11 @@ var CodexManager = class extends CodingAgentManager {
2770
2783
  if (stopTail) {
2771
2784
  await stopTail();
2772
2785
  }
2773
- await this.onTurnComplete();
2786
+ try {
2787
+ await this.onTurnComplete();
2788
+ } catch (error) {
2789
+ console.error("[CodexManager] onTurnComplete failed:", error);
2790
+ }
2774
2791
  this.activeAbortController = null;
2775
2792
  }
2776
2793
  }
@@ -3229,16 +3246,27 @@ var ChatService = class {
3229
3246
  } catch {
3230
3247
  }
3231
3248
  const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
3232
- const repoStatuses = await gitService.refreshRepos();
3233
- console.log(`Repository Statuses Refreshed: `, repoStatuses);
3234
- const payload = linearSessionId ? { linearSessionId, repoStatuses } : { repoStatuses };
3235
- await monolithService.sendEvent({ type: "agent_turn_complete", payload });
3249
+ let repoStatuses;
3250
+ try {
3251
+ repoStatuses = await gitService.refreshRepos();
3252
+ console.log(`Repository Statuses Refreshed: `, repoStatuses);
3253
+ } catch (error) {
3254
+ console.error("[ChatService] Failed to refresh repo statuses:", error);
3255
+ }
3256
+ try {
3257
+ const payload = linearSessionId ? { linearSessionId, repoStatuses: repoStatuses ?? [] } : { repoStatuses: repoStatuses ?? [] };
3258
+ await monolithService.sendEvent({ type: "agent_turn_complete", payload });
3259
+ } catch (error) {
3260
+ console.error("[ChatService] Failed to send agent_turn_complete event:", error);
3261
+ }
3236
3262
  }
3237
3263
  };
3238
3264
 
3239
3265
  // src/v1-routes.ts
3240
3266
  import { Hono } from "hono";
3241
3267
  import { z } from "zod";
3268
+ import { readdir as readdir6, stat as stat3, readFile as readFile12 } from "fs/promises";
3269
+ import { join as join15, resolve } from "path";
3242
3270
 
3243
3271
  // src/services/plan-service.ts
3244
3272
  import { readdir as readdir4, readFile as readFile9 } from "fs/promises";
@@ -3870,11 +3898,88 @@ function createV1Routes(deps) {
3870
3898
  return c.json(jsonError("Failed to create preview", details), 400);
3871
3899
  }
3872
3900
  });
3901
+ app2.get("/logs", async (c) => {
3902
+ try {
3903
+ const files = await readdir6(LOG_DIR).catch(() => []);
3904
+ const logFiles = files.filter((f) => f.endsWith(".log"));
3905
+ const sessions = await Promise.all(
3906
+ logFiles.map(async (filename) => {
3907
+ const filePath = join15(LOG_DIR, filename);
3908
+ const fileStat = await stat3(filePath);
3909
+ const sessionId = filename.replace(/\.log$/, "");
3910
+ return {
3911
+ sessionId,
3912
+ filename,
3913
+ sizeBytes: fileStat.size,
3914
+ updatedAt: fileStat.mtime.toISOString()
3915
+ };
3916
+ })
3917
+ );
3918
+ sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
3919
+ return c.json({
3920
+ currentSessionId: engineLogger.sessionId,
3921
+ sessions
3922
+ });
3923
+ } catch (error) {
3924
+ return c.json(
3925
+ jsonError("Failed to list logs", error instanceof Error ? error.message : "Unknown error"),
3926
+ 500
3927
+ );
3928
+ }
3929
+ });
3930
+ app2.get("/logs/:sessionId", async (c) => {
3931
+ try {
3932
+ const sessionId = c.req.param("sessionId");
3933
+ if (!sessionId || /[/\\]/.test(sessionId) || sessionId.includes("..")) {
3934
+ return c.json(jsonError("Invalid session ID"), 400);
3935
+ }
3936
+ const filePath = resolve(LOG_DIR, `${sessionId}.log`);
3937
+ if (!filePath.startsWith(resolve(LOG_DIR))) {
3938
+ return c.json(jsonError("Invalid session ID"), 400);
3939
+ }
3940
+ const offset = parseInt(c.req.query("offset") || "0", 10);
3941
+ const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
3942
+ let content;
3943
+ try {
3944
+ content = await readFile12(filePath, "utf-8");
3945
+ } catch {
3946
+ return c.json(jsonError("Log session not found"), 404);
3947
+ }
3948
+ const allLines = content.split("\n");
3949
+ if (allLines.length > 0 && allLines[allLines.length - 1] === "") {
3950
+ allLines.pop();
3951
+ }
3952
+ const totalLines = allLines.length;
3953
+ const slicedLines = allLines.slice(offset, offset + limit);
3954
+ const hasMore = offset + limit < totalLines;
3955
+ return c.json({
3956
+ sessionId,
3957
+ totalLines,
3958
+ offset,
3959
+ limit,
3960
+ hasMore,
3961
+ lines: slicedLines
3962
+ });
3963
+ } catch (error) {
3964
+ return c.json(
3965
+ jsonError("Failed to read log", error instanceof Error ? error.message : "Unknown error"),
3966
+ 500
3967
+ );
3968
+ }
3969
+ });
3873
3970
  return app2;
3874
3971
  }
3875
3972
 
3876
3973
  // src/index.ts
3877
3974
  await engineLogger.initialize();
3975
+ process.on("uncaughtException", (error) => {
3976
+ console.error("[FATAL] Uncaught exception:", error);
3977
+ engineLogger.flush().finally(() => process.exit(1));
3978
+ });
3979
+ process.on("unhandledRejection", (reason) => {
3980
+ console.error("[FATAL] Unhandled rejection:", reason);
3981
+ engineLogger.flush().finally(() => process.exit(1));
3982
+ });
3878
3983
  await eventService.initialize();
3879
3984
  var READY_MESSAGE = "========= REPLICAS WORKSPACE READY ==========";
3880
3985
  var COMPLETION_MESSAGE = "========= REPLICAS WORKSPACE INITIALIZATION COMPLETE ==========";
@@ -3910,7 +4015,7 @@ app.get("/health", async (c) => {
3910
4015
  return c.json({ status: "initializing", timestamp: (/* @__PURE__ */ new Date()).toISOString() }, 503);
3911
4016
  }
3912
4017
  try {
3913
- const logContent = await readFile12("/var/log/cloud-init-output.log", "utf-8");
4018
+ const logContent = await readFile13("/var/log/cloud-init-output.log", "utf-8");
3914
4019
  let status;
3915
4020
  if (logContent.includes(COMPLETION_MESSAGE)) {
3916
4021
  status = "active";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",