replicas-engine 0.1.259 → 0.1.260

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 CHANGED
@@ -31,10 +31,10 @@ Chats:
31
31
  - `POST /chats/:chatId/messages`
32
32
  - `POST /chats/:chatId/interrupt`
33
33
 
34
- Plans:
34
+ Canvas:
35
35
 
36
- - `GET /plans`
37
- - `GET /plans/:filename`
36
+ - `GET /canvas`
37
+ - `GET /canvas/:filename`
38
38
 
39
39
  Repos and hooks:
40
40
 
@@ -93,9 +93,9 @@ Engine persistence locations:
93
93
  - `~/.replicas/engine/events.jsonl`
94
94
  - `~/.replicas/engine-state.json`
95
95
  - `~/.replicas/startHooks.log`
96
- - Plan read locations (for `/plans` endpoints):
97
- - `~/.claude/plans`
98
- - `~/.replicas/plans`
96
+ - Canvas read locations (for `/canvas` endpoints):
97
+ - `~/.claude/plans` (where Claude Code writes its plans)
98
+ - `~/.replicas/canvas`
99
99
  - Health endpoint readiness signal file:
100
100
  - `/var/log/cloud-init-output.log` (if missing, `/health` reports `initializing`)
101
101
 
package/dist/src/index.js CHANGED
@@ -286,7 +286,7 @@ var WORKSPACE_SIZES = ["small", "large"];
286
286
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
287
287
 
288
288
  // ../shared/src/e2b.ts
289
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-04-v1";
289
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-04-v2";
290
290
 
291
291
  // ../shared/src/runtime-env.ts
292
292
  function parsePosixEnvFile(content) {
@@ -1839,6 +1839,29 @@ var MODEL_LABELS = {
1839
1839
  "gpt-5": "GPT-5"
1840
1840
  };
1841
1841
  var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
1842
+ var CANVAS_KIND_BY_EXTENSION = {
1843
+ ".md": { kind: "markdown", mimeType: "text/markdown; charset=utf-8" },
1844
+ ".markdown": { kind: "markdown", mimeType: "text/markdown; charset=utf-8" },
1845
+ ".html": { kind: "html", mimeType: "text/html; charset=utf-8" },
1846
+ ".htm": { kind: "html", mimeType: "text/html; charset=utf-8" },
1847
+ ".png": { kind: "image", mimeType: "image/png" },
1848
+ ".jpg": { kind: "image", mimeType: "image/jpeg" },
1849
+ ".jpeg": { kind: "image", mimeType: "image/jpeg" },
1850
+ ".gif": { kind: "image", mimeType: "image/gif" },
1851
+ ".webp": { kind: "image", mimeType: "image/webp" },
1852
+ ".svg": { kind: "image", mimeType: "image/svg+xml" },
1853
+ ".mp4": { kind: "video", mimeType: "video/mp4" },
1854
+ ".webm": { kind: "video", mimeType: "video/webm" },
1855
+ ".mov": { kind: "video", mimeType: "video/quicktime" },
1856
+ ".mp3": { kind: "audio", mimeType: "audio/mpeg" },
1857
+ ".wav": { kind: "audio", mimeType: "audio/wav" },
1858
+ ".ogg": { kind: "audio", mimeType: "audio/ogg" }
1859
+ };
1860
+ function classifyCanvasFilename(filename) {
1861
+ const idx = filename.lastIndexOf(".");
1862
+ const ext = idx === -1 ? "" : filename.slice(idx).toLowerCase();
1863
+ return CANVAS_KIND_BY_EXTENSION[ext] ?? { kind: "other", mimeType: "application/octet-stream" };
1864
+ }
1842
1865
 
1843
1866
  // ../shared/src/engine/v1.ts
1844
1867
  var MERGED_MESSAGE_SEPARATOR = "\n\n<!-- replicas:merged -->\n\n";
@@ -6349,7 +6372,7 @@ var AspClient = class {
6349
6372
  // src/managers/codex-asp/app-server-process.ts
6350
6373
  var DEFAULT_CODEX_BINARY = "codex";
6351
6374
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
6352
- var ENGINE_PACKAGE_VERSION = "0.1.259";
6375
+ var ENGINE_PACKAGE_VERSION = "0.1.260";
6353
6376
  var INITIALIZE_METHOD = "initialize";
6354
6377
  var INITIALIZED_NOTIFICATION = "initialized";
6355
6378
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -9904,60 +9927,88 @@ var RepoFileService = class {
9904
9927
  // src/v1-routes.ts
9905
9928
  import { Hono } from "hono";
9906
9929
  import { z as z2 } from "zod";
9907
- import { readdir as readdir6, stat as stat4, readFile as readFile14 } from "fs/promises";
9930
+ import { readdir as readdir6, stat as stat5, readFile as readFile14 } from "fs/promises";
9908
9931
  import { join as join20, resolve as resolve2 } from "path";
9909
9932
 
9910
- // src/services/plan-service.ts
9911
- import { readdir as readdir4, readFile as readFile11 } from "fs/promises";
9933
+ // src/services/canvas-service.ts
9934
+ import { readdir as readdir4, readFile as readFile11, stat as stat4 } from "fs/promises";
9912
9935
  import { homedir as homedir14 } from "os";
9913
9936
  import { basename, join as join17 } from "path";
9914
- var PLAN_DIRECTORIES = [
9937
+ var CANVAS_DIRECTORIES = [
9915
9938
  join17(homedir14(), ".claude", "plans"),
9916
- join17(homedir14(), ".replicas", "plans")
9939
+ join17(homedir14(), ".replicas", "canvas")
9917
9940
  ];
9918
- function isMarkdownFile(filename) {
9919
- return filename.toLowerCase().endsWith(".md");
9941
+ var MAX_CANVAS_FILE_BYTES = 5 * 1024 * 1024;
9942
+ function isTextKind(kind) {
9943
+ return kind === "markdown" || kind === "html";
9920
9944
  }
9921
- function sanitizePlanFilename(filename) {
9945
+ function sanitizeFilename(filename) {
9922
9946
  return basename(filename);
9923
9947
  }
9924
- var PlanService = class {
9925
- async listPlans() {
9926
- const planNames = /* @__PURE__ */ new Set();
9927
- for (const directory of PLAN_DIRECTORIES) {
9948
+ var CanvasService = class {
9949
+ async listItems() {
9950
+ const items = /* @__PURE__ */ new Map();
9951
+ for (const directory of CANVAS_DIRECTORIES) {
9952
+ let entries;
9928
9953
  try {
9929
- const entries = await readdir4(directory, { withFileTypes: true });
9930
- for (const entry of entries) {
9931
- if (!entry.isFile()) {
9932
- continue;
9933
- }
9934
- if (!isMarkdownFile(entry.name)) {
9935
- continue;
9936
- }
9937
- planNames.add(entry.name);
9938
- }
9954
+ entries = await readdir4(directory, { withFileTypes: true });
9939
9955
  } catch {
9956
+ continue;
9957
+ }
9958
+ for (const entry of entries) {
9959
+ if (!entry.isFile()) continue;
9960
+ if (entry.name.startsWith(".")) continue;
9961
+ if (items.has(entry.name)) continue;
9962
+ const { kind } = classifyCanvasFilename(entry.name);
9963
+ let sizeBytes = 0;
9964
+ try {
9965
+ const s = await stat4(join17(directory, entry.name));
9966
+ sizeBytes = s.size;
9967
+ } catch {
9968
+ continue;
9969
+ }
9970
+ items.set(entry.name, { filename: entry.name, kind, sizeBytes });
9940
9971
  }
9941
9972
  }
9942
- return Array.from(planNames).sort((a, b) => a.localeCompare(b));
9973
+ return Array.from(items.values()).sort((a, b) => a.filename.localeCompare(b.filename));
9943
9974
  }
9944
- async getPlan(filename) {
9945
- const safeFilename = sanitizePlanFilename(filename);
9946
- if (!safeFilename || safeFilename !== filename || !isMarkdownFile(safeFilename)) {
9947
- return null;
9948
- }
9949
- for (const directory of PLAN_DIRECTORIES) {
9950
- const filePath = join17(directory, safeFilename);
9975
+ async getItem(filename) {
9976
+ const safe = sanitizeFilename(filename);
9977
+ if (!safe || safe !== filename || safe.startsWith(".")) return null;
9978
+ const { kind, mimeType } = classifyCanvasFilename(safe);
9979
+ for (const directory of CANVAS_DIRECTORIES) {
9980
+ const filePath = join17(directory, safe);
9981
+ let sizeBytes = 0;
9951
9982
  try {
9952
- const content = await readFile11(filePath, "utf-8");
9953
- return { filename: safeFilename, content };
9983
+ const s = await stat4(filePath);
9984
+ sizeBytes = s.size;
9954
9985
  } catch {
9986
+ continue;
9987
+ }
9988
+ if (sizeBytes > MAX_CANVAS_FILE_BYTES) {
9989
+ return {
9990
+ filename: safe,
9991
+ kind,
9992
+ sizeBytes,
9993
+ mimeType,
9994
+ tooLarge: true
9995
+ };
9996
+ }
9997
+ try {
9998
+ if (isTextKind(kind)) {
9999
+ const content = await readFile11(filePath, "utf-8");
10000
+ return { filename: safe, kind, sizeBytes, mimeType, content };
10001
+ }
10002
+ const buf = await readFile11(filePath);
10003
+ return { filename: safe, kind, sizeBytes, mimeType, base64: buf.toString("base64") };
10004
+ } catch {
10005
+ continue;
9955
10006
  }
9956
10007
  }
9957
10008
  return null;
9958
10009
  }
9959
10010
  };
9960
- var planService = new PlanService();
10011
+ var canvasService = new CanvasService();
9961
10012
 
9962
10013
  // src/services/warm-hooks-service.ts
9963
10014
  import { spawn as spawn4 } from "child_process";
@@ -10648,31 +10699,31 @@ function createV1Routes(deps) {
10648
10699
  }
10649
10700
  return c.json(result);
10650
10701
  });
10651
- app2.get("/plans", async (c) => {
10702
+ app2.get("/canvas", async (c) => {
10652
10703
  try {
10653
- const plans = await planService.listPlans();
10654
- return c.json({ plans });
10704
+ const items = await canvasService.listItems();
10705
+ return c.json({ items });
10655
10706
  } catch (error) {
10656
10707
  return c.json(
10657
- jsonError("Failed to list plans", error instanceof Error ? error.message : "Unknown error"),
10708
+ jsonError("Failed to list canvas items", error instanceof Error ? error.message : "Unknown error"),
10658
10709
  500
10659
10710
  );
10660
10711
  }
10661
10712
  });
10662
- app2.get("/plans/:filename", async (c) => {
10713
+ app2.get("/canvas/:filename", async (c) => {
10663
10714
  try {
10664
10715
  const filename = c.req.param("filename");
10665
10716
  if (!filename) {
10666
10717
  return c.json(jsonError("Filename required"), 400);
10667
10718
  }
10668
- const plan = await planService.getPlan(filename);
10669
- if (!plan) {
10670
- return c.json(jsonError("Plan not found"), 404);
10719
+ const item = await canvasService.getItem(filename);
10720
+ if (!item) {
10721
+ return c.json(jsonError("Canvas item not found"), 404);
10671
10722
  }
10672
- return c.json(plan);
10723
+ return c.json(item);
10673
10724
  } catch (error) {
10674
10725
  return c.json(
10675
- jsonError("Failed to read plan", error instanceof Error ? error.message : "Unknown error"),
10726
+ jsonError("Failed to read canvas item", error instanceof Error ? error.message : "Unknown error"),
10676
10727
  500
10677
10728
  );
10678
10729
  }
@@ -10986,7 +11037,7 @@ function createV1Routes(deps) {
10986
11037
  const sessions = await Promise.all(
10987
11038
  logFiles.map(async (filename) => {
10988
11039
  const filePath = join20(LOG_DIR, filename);
10989
- const fileStat = await stat4(filePath);
11040
+ const fileStat = await stat5(filePath);
10990
11041
  const sessionId = filename.replace(/\.log$/, "");
10991
11042
  return {
10992
11043
  sessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.259",
3
+ "version": "0.1.260",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",