replicas-engine 0.1.312 → 0.1.314

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 +271 -123
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -117,9 +117,9 @@ var EXT_TO_LANGUAGE = {
117
117
  function detectLanguageByPath(filePath) {
118
118
  const dot = filePath.lastIndexOf(".");
119
119
  if (dot === -1) {
120
- const basename3 = filePath.split("/").pop() ?? "";
121
- if (basename3 === "Dockerfile") return "dockerfile";
122
- if (basename3 === "Makefile") return "makefile";
120
+ const basename2 = filePath.split("/").pop() ?? "";
121
+ if (basename2 === "Dockerfile") return "dockerfile";
122
+ if (basename2 === "Makefile") return "makefile";
123
123
  return null;
124
124
  }
125
125
  const ext = filePath.slice(dot).toLowerCase();
@@ -287,7 +287,7 @@ var WORKSPACE_SIZES = ["small", "large"];
287
287
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
288
288
 
289
289
  // ../shared/src/e2b.ts
290
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-13-v8";
290
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-13-v10";
291
291
 
292
292
  // ../shared/src/runtime-env.ts
293
293
  function parsePosixEnvFile(content) {
@@ -2076,6 +2076,35 @@ var MODEL_LABELS = {
2076
2076
  "gpt-5.2": "GPT-5.2"
2077
2077
  };
2078
2078
  var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
2079
+ var MAX_CANVAS_FILE_BYTES = 5 * 1024 * 1024;
2080
+ function isCanvasTextKind(kind) {
2081
+ return kind === "markdown" || kind === "html";
2082
+ }
2083
+ function sanitizeCanvasFilename(filename) {
2084
+ const safe = filename.split(/[\\/]/).pop() ?? "";
2085
+ if (!safe || safe !== filename || safe.startsWith(".")) return null;
2086
+ return safe;
2087
+ }
2088
+ function bytesToBase64(bytes) {
2089
+ let binary = "";
2090
+ const chunkSize = 32768;
2091
+ for (let i = 0; i < bytes.length; i += chunkSize) {
2092
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
2093
+ }
2094
+ return btoa(binary);
2095
+ }
2096
+ function serializeCanvasContentResponse(input) {
2097
+ const base = {
2098
+ filename: input.filename,
2099
+ kind: input.kind,
2100
+ sizeBytes: input.sizeBytes,
2101
+ mimeType: input.mimeType
2102
+ };
2103
+ if (isCanvasTextKind(input.kind)) {
2104
+ return { ...base, content: new TextDecoder().decode(input.bytes) };
2105
+ }
2106
+ return { ...base, base64: bytesToBase64(input.bytes) };
2107
+ }
2079
2108
  var CANVAS_KIND_BY_EXTENSION = {
2080
2109
  ".md": { kind: "markdown", mimeType: "text/markdown; charset=utf-8" },
2081
2110
  ".markdown": { kind: "markdown", mimeType: "text/markdown; charset=utf-8" },
@@ -4904,9 +4933,9 @@ async function registerDesktopPreview() {
4904
4933
 
4905
4934
  // src/services/chat/chat-service.ts
4906
4935
  import { existsSync as existsSync7 } from "fs";
4907
- import { appendFile as appendFile4, copyFile, mkdir as mkdir11, readFile as readFile11, rename as rename2, rm } from "fs/promises";
4908
- import { homedir as homedir13 } from "os";
4909
- import { join as join15 } from "path";
4936
+ import { appendFile as appendFile4, copyFile, mkdir as mkdir11, readFile as readFile12, rename as rename2, rm } from "fs/promises";
4937
+ import { homedir as homedir14 } from "os";
4938
+ import { join as join16 } from "path";
4910
4939
  import { randomUUID as randomUUID5 } from "crypto";
4911
4940
 
4912
4941
  // src/managers/claude-manager.ts
@@ -6851,7 +6880,7 @@ var AspClient = class {
6851
6880
  // src/managers/codex-asp/app-server-process.ts
6852
6881
  var DEFAULT_CODEX_BINARY = "codex";
6853
6882
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
6854
- var ENGINE_PACKAGE_VERSION = "0.1.312";
6883
+ var ENGINE_PACKAGE_VERSION = "0.1.314";
6855
6884
  var INITIALIZE_METHOD = "initialize";
6856
6885
  var INITIALIZED_NOTIFICATION = "initialized";
6857
6886
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -9154,15 +9183,190 @@ var KeepAliveService = class _KeepAliveService {
9154
9183
  };
9155
9184
  var keepAliveService = new KeepAliveService();
9156
9185
 
9157
- // src/services/upload-chat-transcripts.ts
9158
- import { readdir as readdir3, readFile as readFile10 } from "fs/promises";
9159
- import { basename, join as join14 } from "path";
9186
+ // src/services/canvas-service.ts
9187
+ import { readdir as readdir3, readFile as readFile10, stat as stat2 } from "fs/promises";
9160
9188
  import { homedir as homedir12 } from "os";
9161
- var ENGINE_DIR2 = join14(homedir12(), ".replicas", "engine");
9189
+ import { join as join14 } from "path";
9190
+ var CANVAS_DIRECTORIES = [
9191
+ join14(homedir12(), ".claude", "plans"),
9192
+ join14(homedir12(), ".replicas", "canvas")
9193
+ ];
9194
+ var CanvasService = class {
9195
+ async listItems() {
9196
+ const items = /* @__PURE__ */ new Map();
9197
+ for (const directory of CANVAS_DIRECTORIES) {
9198
+ let entries;
9199
+ try {
9200
+ entries = await readdir3(directory, { withFileTypes: true });
9201
+ } catch {
9202
+ continue;
9203
+ }
9204
+ for (const entry of entries) {
9205
+ if (!entry.isFile()) continue;
9206
+ if (entry.name.startsWith(".")) continue;
9207
+ if (items.has(entry.name)) continue;
9208
+ const { kind } = classifyCanvasFilename(entry.name);
9209
+ let sizeBytes = 0;
9210
+ try {
9211
+ const s = await stat2(join14(directory, entry.name));
9212
+ sizeBytes = s.size;
9213
+ } catch {
9214
+ continue;
9215
+ }
9216
+ items.set(entry.name, { filename: entry.name, kind, sizeBytes });
9217
+ }
9218
+ }
9219
+ return Array.from(items.values()).sort((a, b) => a.filename.localeCompare(b.filename));
9220
+ }
9221
+ async getItemFile(filename) {
9222
+ const safe = sanitizeCanvasFilename(filename);
9223
+ if (!safe) return null;
9224
+ const { kind, mimeType } = classifyCanvasFilename(safe);
9225
+ for (const directory of CANVAS_DIRECTORIES) {
9226
+ const filePath = join14(directory, safe);
9227
+ let sizeBytes = 0;
9228
+ let updatedAt = "";
9229
+ try {
9230
+ const s = await stat2(filePath);
9231
+ sizeBytes = s.size;
9232
+ updatedAt = s.mtime.toISOString();
9233
+ } catch {
9234
+ continue;
9235
+ }
9236
+ if (sizeBytes > MAX_CANVAS_FILE_BYTES) {
9237
+ return {
9238
+ filename: safe,
9239
+ kind,
9240
+ sizeBytes,
9241
+ mimeType,
9242
+ updatedAt,
9243
+ tooLarge: true
9244
+ };
9245
+ }
9246
+ try {
9247
+ const bytes = await readFile10(filePath);
9248
+ return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
9249
+ } catch {
9250
+ continue;
9251
+ }
9252
+ }
9253
+ return null;
9254
+ }
9255
+ async getItem(filename) {
9256
+ const item = await this.getItemFile(filename);
9257
+ if (!item) return null;
9258
+ if (item.tooLarge || !item.bytes) {
9259
+ return {
9260
+ filename: item.filename,
9261
+ kind: item.kind,
9262
+ sizeBytes: item.sizeBytes,
9263
+ mimeType: item.mimeType,
9264
+ tooLarge: true
9265
+ };
9266
+ }
9267
+ return serializeCanvasContentResponse({
9268
+ filename: item.filename,
9269
+ kind: item.kind,
9270
+ sizeBytes: item.sizeBytes,
9271
+ mimeType: item.mimeType,
9272
+ bytes: item.bytes
9273
+ });
9274
+ }
9275
+ };
9276
+ var canvasService = new CanvasService();
9277
+
9278
+ // src/services/upload-canvas-items.ts
9279
+ var uploadedCanvasItems = /* @__PURE__ */ new Map();
9280
+ function isReconcileCanvasItemsResponse(value) {
9281
+ return typeof value === "object" && value !== null && "deleted" in value && typeof value.deleted === "number" && (!("skipped" in value) || typeof value.skipped === "string");
9282
+ }
9283
+ async function flushAllCanvasItems() {
9284
+ let flushed = 0;
9285
+ let skipped = 0;
9286
+ let failed = 0;
9287
+ let deleted = 0;
9288
+ const filenames = [];
9289
+ const items = await canvasService.listItems();
9290
+ await Promise.all(items.map(async (item) => {
9291
+ if (item.sizeBytes > MAX_CANVAS_FILE_BYTES) {
9292
+ skipped++;
9293
+ return;
9294
+ }
9295
+ try {
9296
+ const file = await canvasService.getItemFile(item.filename);
9297
+ if (!file?.bytes || file.tooLarge) {
9298
+ skipped++;
9299
+ return;
9300
+ }
9301
+ const uploaded = uploadedCanvasItems.get(file.filename);
9302
+ if (uploaded?.sizeBytes === file.sizeBytes && uploaded.updatedAt === file.updatedAt) {
9303
+ filenames.push(file.filename);
9304
+ skipped++;
9305
+ return;
9306
+ }
9307
+ await uploadCanvasItem(file);
9308
+ uploadedCanvasItems.set(file.filename, { sizeBytes: file.sizeBytes, updatedAt: file.updatedAt });
9309
+ filenames.push(file.filename);
9310
+ flushed++;
9311
+ } catch (err) {
9312
+ failed++;
9313
+ console.error("[CanvasUploader] upload failed:", { filename: item.filename, err });
9314
+ }
9315
+ }));
9316
+ if (failed === 0) {
9317
+ try {
9318
+ deleted = await reconcileCanvasItems(filenames);
9319
+ const currentFilenames = new Set(filenames);
9320
+ for (const filename of uploadedCanvasItems.keys()) {
9321
+ if (!currentFilenames.has(filename)) uploadedCanvasItems.delete(filename);
9322
+ }
9323
+ } catch (err) {
9324
+ failed++;
9325
+ console.error("[CanvasUploader] reconcile failed:", err);
9326
+ }
9327
+ }
9328
+ return { flushed, skipped, failed, deleted };
9329
+ }
9330
+ async function uploadCanvasItem(item) {
9331
+ if (!item.bytes || item.tooLarge) return;
9332
+ const form = new FormData();
9333
+ form.append("filename", item.filename);
9334
+ form.append("updated_at", item.updatedAt);
9335
+ form.append(
9336
+ "file",
9337
+ new Blob([item.bytes], { type: item.mimeType }),
9338
+ item.filename
9339
+ );
9340
+ const response = await monolithRequest("/v1/engine/canvas", { body: form });
9341
+ if (!response.ok) {
9342
+ const errorText = await response.text();
9343
+ throw new Error(`upload failed: ${response.status} ${errorText}`);
9344
+ }
9345
+ }
9346
+ async function reconcileCanvasItems(filenames) {
9347
+ const response = await monolithRequest("/v1/engine/canvas/reconcile", {
9348
+ body: { filenames }
9349
+ });
9350
+ if (!response.ok) {
9351
+ const errorText = await response.text();
9352
+ throw new Error(`reconcile failed: ${response.status} ${errorText}`);
9353
+ }
9354
+ const data = await response.json();
9355
+ if (!isReconcileCanvasItemsResponse(data)) {
9356
+ throw new Error("Invalid response from reconcile endpoint");
9357
+ }
9358
+ return data.deleted;
9359
+ }
9360
+
9361
+ // src/services/upload-chat-transcripts.ts
9362
+ import { readdir as readdir4, readFile as readFile11 } from "fs/promises";
9363
+ import { basename, join as join15 } from "path";
9364
+ import { homedir as homedir13 } from "os";
9365
+ var ENGINE_DIR2 = join15(homedir13(), ".replicas", "engine");
9162
9366
  var HISTORY_DIRS = [
9163
- join14(ENGINE_DIR2, "claude-histories"),
9164
- join14(ENGINE_DIR2, "relay-histories"),
9165
- join14(ENGINE_DIR2, "codex-histories")
9367
+ join15(ENGINE_DIR2, "claude-histories"),
9368
+ join15(ENGINE_DIR2, "relay-histories"),
9369
+ join15(ENGINE_DIR2, "codex-histories")
9166
9370
  ];
9167
9371
  async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9168
9372
  let flushed = 0;
@@ -9171,7 +9375,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9171
9375
  for (const dir of HISTORY_DIRS) {
9172
9376
  let entries;
9173
9377
  try {
9174
- entries = await readdir3(dir);
9378
+ entries = await readdir4(dir);
9175
9379
  } catch {
9176
9380
  continue;
9177
9381
  }
@@ -9179,7 +9383,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9179
9383
  if (!entry.endsWith(".jsonl")) continue;
9180
9384
  const chatId = basename(entry, ".jsonl");
9181
9385
  tasks.push(
9182
- uploadChatTranscript(chatId, join14(dir, entry), chatsById.get(chatId)).then(() => {
9386
+ uploadChatTranscript(chatId, join15(dir, entry), chatsById.get(chatId)).then(() => {
9183
9387
  flushed++;
9184
9388
  }).catch((err) => {
9185
9389
  failed++;
@@ -9192,7 +9396,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9192
9396
  return { flushed, failed };
9193
9397
  }
9194
9398
  async function uploadChatTranscript(chatId, filePath, chat) {
9195
- const bytes = await readFile10(filePath);
9399
+ const bytes = await readFile11(filePath);
9196
9400
  if (bytes.byteLength === 0) return;
9197
9401
  const form = new FormData();
9198
9402
  form.append("chat_id", chatId);
@@ -9249,18 +9453,18 @@ var DuplicateDefaultChatError = class extends Error {
9249
9453
  };
9250
9454
 
9251
9455
  // src/services/chat/chat-service.ts
9252
- var ENGINE_DIR3 = join15(homedir13(), ".replicas", "engine");
9253
- var CHATS_FILE = join15(ENGINE_DIR3, "chats.json");
9254
- var CLAUDE_HISTORY_DIR = join15(ENGINE_DIR3, "claude-histories");
9255
- var RELAY_HISTORY_DIR = join15(ENGINE_DIR3, "relay-histories");
9256
- var CODEX_HISTORY_DIR = join15(ENGINE_DIR3, "codex-histories");
9456
+ var ENGINE_DIR3 = join16(homedir14(), ".replicas", "engine");
9457
+ var CHATS_FILE = join16(ENGINE_DIR3, "chats.json");
9458
+ var CLAUDE_HISTORY_DIR = join16(ENGINE_DIR3, "claude-histories");
9459
+ var RELAY_HISTORY_DIR = join16(ENGINE_DIR3, "relay-histories");
9460
+ var CODEX_HISTORY_DIR = join16(ENGINE_DIR3, "codex-histories");
9257
9461
  var HISTORY_DIR_BY_PROVIDER = {
9258
9462
  claude: CLAUDE_HISTORY_DIR,
9259
9463
  relay: RELAY_HISTORY_DIR,
9260
9464
  codex: CODEX_HISTORY_DIR
9261
9465
  };
9262
- var CHAT_SENDERS_DIR = join15(ENGINE_DIR3, "chat-senders");
9263
- var CODEX_AUTH_PATH2 = join15(homedir13(), ".codex", "auth.json");
9466
+ var CHAT_SENDERS_DIR = join16(ENGINE_DIR3, "chat-senders");
9467
+ var CODEX_AUTH_PATH2 = join16(homedir14(), ".codex", "auth.json");
9264
9468
  var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
9265
9469
  function isChatMessageSender(value) {
9266
9470
  if (!isRecord4(value)) return false;
@@ -9458,7 +9662,7 @@ var ChatService = class {
9458
9662
  };
9459
9663
  }
9460
9664
  senderFilePath(chatId) {
9461
- return join15(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
9665
+ return join16(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
9462
9666
  }
9463
9667
  async appendSender(chatId, sender) {
9464
9668
  try {
@@ -9469,7 +9673,7 @@ var ChatService = class {
9469
9673
  }
9470
9674
  async readSenders(chatId) {
9471
9675
  try {
9472
- const content = await readFile11(this.senderFilePath(chatId), "utf-8");
9676
+ const content = await readFile12(this.senderFilePath(chatId), "utf-8");
9473
9677
  const lines = content.split("\n").filter((line) => line.trim().length > 0);
9474
9678
  const senders = [];
9475
9679
  for (const line of lines) {
@@ -9614,7 +9818,7 @@ var ChatService = class {
9614
9818
  return descendants;
9615
9819
  }
9616
9820
  async deleteHistoryFile(persisted) {
9617
- await rm(join15(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
9821
+ await rm(join16(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
9618
9822
  await rm(this.senderFilePath(persisted.id), { force: true });
9619
9823
  }
9620
9824
  async getChatHistory(chatId) {
@@ -9652,6 +9856,16 @@ var ChatService = class {
9652
9856
  [...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
9653
9857
  ));
9654
9858
  }
9859
+ async flushAllWorkspaceArtifacts() {
9860
+ const chatsById = new Map(
9861
+ [...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
9862
+ );
9863
+ const [chatTranscripts, canvas] = await Promise.all([
9864
+ flushAllChatTranscripts(chatsById),
9865
+ flushAllCanvasItems()
9866
+ ]);
9867
+ return { chatTranscripts, canvas };
9868
+ }
9655
9869
  createRuntimeChat(persisted) {
9656
9870
  const saveSession = async (sessionId) => {
9657
9871
  persisted.providerSessionId = sessionId;
@@ -9675,7 +9889,7 @@ var ChatService = class {
9675
9889
  if (persisted.provider === "claude") {
9676
9890
  provider = new ClaudeManager({
9677
9891
  workingDirectory: this.workingDirectory,
9678
- historyFilePath: join15(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
9892
+ historyFilePath: join16(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
9679
9893
  initialSessionId: persisted.providerSessionId,
9680
9894
  onSaveSessionId: saveSession,
9681
9895
  onTurnComplete: onProviderTurnComplete,
@@ -9684,7 +9898,7 @@ var ChatService = class {
9684
9898
  } else if (persisted.provider === "relay") {
9685
9899
  provider = new RelayManager({
9686
9900
  workingDirectory: this.workingDirectory,
9687
- historyFilePath: join15(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
9901
+ historyFilePath: join16(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
9688
9902
  initialSessionId: persisted.providerSessionId,
9689
9903
  onSaveSessionId: saveSession,
9690
9904
  onTurnComplete: onProviderTurnComplete,
@@ -9695,7 +9909,7 @@ var ChatService = class {
9695
9909
  } else {
9696
9910
  provider = new CodexAspManager({
9697
9911
  workingDirectory: this.workingDirectory,
9698
- historyFilePath: join15(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
9912
+ historyFilePath: join16(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
9699
9913
  initialSessionId: persisted.providerSessionId,
9700
9914
  onSaveSessionId: saveSession,
9701
9915
  onTurnComplete: onProviderTurnComplete,
@@ -9833,11 +10047,14 @@ var ChatService = class {
9833
10047
  });
9834
10048
  uploadChatTranscript(
9835
10049
  chatId,
9836
- join15(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
10050
+ join16(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
9837
10051
  this.toSummary(chat)
9838
10052
  ).catch((err) => {
9839
10053
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
9840
10054
  });
10055
+ flushAllCanvasItems().catch((err) => {
10056
+ console.error("[ChatService] Failed to upload canvas items:", { chatId, err });
10057
+ });
9841
10058
  await this.publishAgentTurnCompleteWebhook(chat);
9842
10059
  }
9843
10060
  getRuntimeChat(chatId) {
@@ -9845,7 +10062,7 @@ var ChatService = class {
9845
10062
  }
9846
10063
  async loadChats() {
9847
10064
  try {
9848
- const content = await readFile11(CHATS_FILE, "utf-8");
10065
+ const content = await readFile12(CHATS_FILE, "utf-8");
9849
10066
  return parsePersistedChatsContent(content);
9850
10067
  } catch (error) {
9851
10068
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
@@ -9860,7 +10077,7 @@ var ChatService = class {
9860
10077
  console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
9861
10078
  }
9862
10079
  try {
9863
- const backupContent = await readFile11(CHATS_BACKUP_FILE, "utf-8");
10080
+ const backupContent = await readFile12(CHATS_BACKUP_FILE, "utf-8");
9864
10081
  return parsePersistedChatsContent(backupContent);
9865
10082
  } catch (backupError) {
9866
10083
  if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
@@ -9945,8 +10162,8 @@ var ChatService = class {
9945
10162
 
9946
10163
  // src/services/repo-file-service.ts
9947
10164
  import { execFile as execFile2 } from "child_process";
9948
- import { readFile as readFile12, realpath, stat as stat2 } from "fs/promises";
9949
- import { join as join16, resolve, extname } from "path";
10165
+ import { readFile as readFile13, realpath, stat as stat3 } from "fs/promises";
10166
+ import { join as join17, resolve, extname } from "path";
9950
10167
  var CACHE_TTL_MS = 3e4;
9951
10168
  var SEARCH_TIMEOUT_MS = 15e3;
9952
10169
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -10006,11 +10223,11 @@ function scoreMatch(query2, filePath) {
10006
10223
  const lowerQuery = query2.toLowerCase();
10007
10224
  const lowerPath = filePath.toLowerCase();
10008
10225
  const segments = lowerPath.split("/");
10009
- const basename3 = segments[segments.length - 1] ?? "";
10010
- if (basename3 === lowerQuery) return 100;
10011
- if (basename3.startsWith(lowerQuery)) return 90;
10226
+ const basename2 = segments[segments.length - 1] ?? "";
10227
+ if (basename2 === lowerQuery) return 100;
10228
+ if (basename2.startsWith(lowerQuery)) return 90;
10012
10229
  if (segments.some((seg) => seg === lowerQuery)) return 80;
10013
- if (basename3.includes(lowerQuery)) return 70;
10230
+ if (basename2.includes(lowerQuery)) return 70;
10014
10231
  if (segments.some((seg) => seg.includes(lowerQuery))) return 60;
10015
10232
  if (lowerPath.includes(lowerQuery)) return 50;
10016
10233
  return 0;
@@ -10106,11 +10323,11 @@ var RepoFileService = class {
10106
10323
  const repo = repos.find((r) => r.name === repoName);
10107
10324
  if (!repo) return null;
10108
10325
  try {
10109
- const fullPath = await realpath(resolve(join16(repo.path, filePath)));
10326
+ const fullPath = await realpath(resolve(join17(repo.path, filePath)));
10110
10327
  const repoRoot = await realpath(repo.path);
10111
10328
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
10112
10329
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
10113
- const fileStat = await stat2(fullPath);
10330
+ const fileStat = await stat3(fullPath);
10114
10331
  if (!fileStat.isFile()) return null;
10115
10332
  const sizeBytes = fileStat.size;
10116
10333
  if (isBinaryExtension(filePath)) {
@@ -10135,7 +10352,7 @@ var RepoFileService = class {
10135
10352
  tooLarge: true
10136
10353
  };
10137
10354
  }
10138
- const content = await readFile12(fullPath, "utf-8");
10355
+ const content = await readFile13(fullPath, "utf-8");
10139
10356
  return {
10140
10357
  repoName,
10141
10358
  path: filePath,
@@ -10216,86 +10433,6 @@ import { z as z2 } from "zod";
10216
10433
  import { readdir as readdir6, stat as stat4, readFile as readFile16 } from "fs/promises";
10217
10434
  import { join as join20, resolve as resolve2 } from "path";
10218
10435
 
10219
- // src/services/canvas-service.ts
10220
- import { readdir as readdir4, readFile as readFile13, stat as stat3 } from "fs/promises";
10221
- import { homedir as homedir14 } from "os";
10222
- import { basename as basename2, join as join17 } from "path";
10223
- var CANVAS_DIRECTORIES = [
10224
- join17(homedir14(), ".claude", "plans"),
10225
- join17(homedir14(), ".replicas", "canvas")
10226
- ];
10227
- var MAX_CANVAS_FILE_BYTES = 5 * 1024 * 1024;
10228
- function isTextKind(kind) {
10229
- return kind === "markdown" || kind === "html";
10230
- }
10231
- function sanitizeFilename(filename) {
10232
- return basename2(filename);
10233
- }
10234
- var CanvasService = class {
10235
- async listItems() {
10236
- const items = /* @__PURE__ */ new Map();
10237
- for (const directory of CANVAS_DIRECTORIES) {
10238
- let entries;
10239
- try {
10240
- entries = await readdir4(directory, { withFileTypes: true });
10241
- } catch {
10242
- continue;
10243
- }
10244
- for (const entry of entries) {
10245
- if (!entry.isFile()) continue;
10246
- if (entry.name.startsWith(".")) continue;
10247
- if (items.has(entry.name)) continue;
10248
- const { kind } = classifyCanvasFilename(entry.name);
10249
- let sizeBytes = 0;
10250
- try {
10251
- const s = await stat3(join17(directory, entry.name));
10252
- sizeBytes = s.size;
10253
- } catch {
10254
- continue;
10255
- }
10256
- items.set(entry.name, { filename: entry.name, kind, sizeBytes });
10257
- }
10258
- }
10259
- return Array.from(items.values()).sort((a, b) => a.filename.localeCompare(b.filename));
10260
- }
10261
- async getItem(filename) {
10262
- const safe = sanitizeFilename(filename);
10263
- if (!safe || safe !== filename || safe.startsWith(".")) return null;
10264
- const { kind, mimeType } = classifyCanvasFilename(safe);
10265
- for (const directory of CANVAS_DIRECTORIES) {
10266
- const filePath = join17(directory, safe);
10267
- let sizeBytes = 0;
10268
- try {
10269
- const s = await stat3(filePath);
10270
- sizeBytes = s.size;
10271
- } catch {
10272
- continue;
10273
- }
10274
- if (sizeBytes > MAX_CANVAS_FILE_BYTES) {
10275
- return {
10276
- filename: safe,
10277
- kind,
10278
- sizeBytes,
10279
- mimeType,
10280
- tooLarge: true
10281
- };
10282
- }
10283
- try {
10284
- if (isTextKind(kind)) {
10285
- const content = await readFile13(filePath, "utf-8");
10286
- return { filename: safe, kind, sizeBytes, mimeType, content };
10287
- }
10288
- const buf = await readFile13(filePath);
10289
- return { filename: safe, kind, sizeBytes, mimeType, base64: buf.toString("base64") };
10290
- } catch {
10291
- continue;
10292
- }
10293
- }
10294
- return null;
10295
- }
10296
- };
10297
- var canvasService = new CanvasService();
10298
-
10299
10436
  // src/services/warm-hooks-service.ts
10300
10437
  import { spawn as spawn4 } from "child_process";
10301
10438
  import { readFile as readFile15 } from "fs/promises";
@@ -10912,6 +11049,17 @@ function createV1Routes(deps) {
10912
11049
  );
10913
11050
  }
10914
11051
  });
11052
+ app2.post("/workspace-artifacts/flush-all", async (c) => {
11053
+ try {
11054
+ const result = await deps.chatService.flushAllWorkspaceArtifacts();
11055
+ return c.json(result);
11056
+ } catch (error) {
11057
+ return c.json(
11058
+ jsonError("Failed to flush workspace artifacts", error instanceof Error ? error.message : "Unknown error"),
11059
+ 500
11060
+ );
11061
+ }
11062
+ });
10915
11063
  app2.get("/chats/:chatId/queue", (c) => {
10916
11064
  try {
10917
11065
  const result = deps.chatService.getChatQueue(c.req.param("chatId"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.312",
3
+ "version": "0.1.314",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",