replicas-engine 0.1.277 → 0.1.279
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 +76 -713
- package/package.json +1 -2
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-07-
|
|
289
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-07-v4";
|
|
290
290
|
|
|
291
291
|
// ../shared/src/runtime-env.ts
|
|
292
292
|
function parsePosixEnvFile(content) {
|
|
@@ -4664,10 +4664,10 @@ async function registerDesktopPreview() {
|
|
|
4664
4664
|
}
|
|
4665
4665
|
|
|
4666
4666
|
// src/services/chat/chat-service.ts
|
|
4667
|
-
import { existsSync as
|
|
4668
|
-
import { appendFile as appendFile5, copyFile, mkdir as
|
|
4669
|
-
import { homedir as
|
|
4670
|
-
import { join as
|
|
4667
|
+
import { existsSync as existsSync7 } from "fs";
|
|
4668
|
+
import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as readFile8, rename as rename2, rm } from "fs/promises";
|
|
4669
|
+
import { homedir as homedir12 } from "os";
|
|
4670
|
+
import { join as join14 } from "path";
|
|
4671
4671
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
4672
4672
|
|
|
4673
4673
|
// src/managers/claude-manager.ts
|
|
@@ -4847,212 +4847,6 @@ function mapTodoStatus(status) {
|
|
|
4847
4847
|
}
|
|
4848
4848
|
return "pending";
|
|
4849
4849
|
}
|
|
4850
|
-
function convertCodexEvent(event, linearSessionId) {
|
|
4851
|
-
if (event.type === "turn.started") {
|
|
4852
|
-
return {
|
|
4853
|
-
linearSessionId,
|
|
4854
|
-
content: {
|
|
4855
|
-
type: "thought",
|
|
4856
|
-
body: "Processing..."
|
|
4857
|
-
}
|
|
4858
|
-
};
|
|
4859
|
-
}
|
|
4860
|
-
if (event.type === "item.started") {
|
|
4861
|
-
const item = event.item;
|
|
4862
|
-
if (!item) return null;
|
|
4863
|
-
if (item.type === "agent_message") {
|
|
4864
|
-
const text = "text" in item ? String(item.text || "") : "";
|
|
4865
|
-
if (text) {
|
|
4866
|
-
return {
|
|
4867
|
-
linearSessionId,
|
|
4868
|
-
content: {
|
|
4869
|
-
type: "thought",
|
|
4870
|
-
body: text
|
|
4871
|
-
}
|
|
4872
|
-
};
|
|
4873
|
-
}
|
|
4874
|
-
}
|
|
4875
|
-
if (item.type === "reasoning") {
|
|
4876
|
-
const text = "text" in item ? String(item.text || "") : "";
|
|
4877
|
-
if (text) {
|
|
4878
|
-
return {
|
|
4879
|
-
linearSessionId,
|
|
4880
|
-
content: {
|
|
4881
|
-
type: "thought",
|
|
4882
|
-
body: text
|
|
4883
|
-
}
|
|
4884
|
-
};
|
|
4885
|
-
}
|
|
4886
|
-
}
|
|
4887
|
-
if (item.type === "command_execution") {
|
|
4888
|
-
const command = "command" in item ? String(item.command || "") : "";
|
|
4889
|
-
return {
|
|
4890
|
-
linearSessionId,
|
|
4891
|
-
content: {
|
|
4892
|
-
type: "action",
|
|
4893
|
-
action: "Running command",
|
|
4894
|
-
parameter: command
|
|
4895
|
-
}
|
|
4896
|
-
};
|
|
4897
|
-
}
|
|
4898
|
-
if (item.type === "file_change") {
|
|
4899
|
-
const changes = "changes" in item && Array.isArray(item.changes) ? item.changes : [];
|
|
4900
|
-
const paths = changes.map((c) => c.path || "").filter(Boolean);
|
|
4901
|
-
return {
|
|
4902
|
-
linearSessionId,
|
|
4903
|
-
content: {
|
|
4904
|
-
type: "action",
|
|
4905
|
-
action: "File change",
|
|
4906
|
-
parameter: paths.join(", ") || ""
|
|
4907
|
-
}
|
|
4908
|
-
};
|
|
4909
|
-
}
|
|
4910
|
-
if (item.type === "mcp_tool_call") {
|
|
4911
|
-
const tool2 = "tool" in item ? String(item.tool || "") : "";
|
|
4912
|
-
return {
|
|
4913
|
-
linearSessionId,
|
|
4914
|
-
content: {
|
|
4915
|
-
type: "action",
|
|
4916
|
-
action: tool2 || "MCP tool call",
|
|
4917
|
-
parameter: ""
|
|
4918
|
-
}
|
|
4919
|
-
};
|
|
4920
|
-
}
|
|
4921
|
-
if (item.type === "web_search") {
|
|
4922
|
-
const query2 = "query" in item ? String(item.query || "") : "";
|
|
4923
|
-
return {
|
|
4924
|
-
linearSessionId,
|
|
4925
|
-
content: {
|
|
4926
|
-
type: "action",
|
|
4927
|
-
action: "Web search",
|
|
4928
|
-
parameter: query2
|
|
4929
|
-
}
|
|
4930
|
-
};
|
|
4931
|
-
}
|
|
4932
|
-
if (item.type === "todo_list") {
|
|
4933
|
-
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
4934
|
-
const completed = items.filter((t) => t.completed).length;
|
|
4935
|
-
const total = items.length;
|
|
4936
|
-
const currentTask = items.find((t) => !t.completed);
|
|
4937
|
-
const summary = `${completed}/${total}`;
|
|
4938
|
-
const parameter = currentTask?.text ? `${summary}: ${currentTask.text}` : summary;
|
|
4939
|
-
return {
|
|
4940
|
-
linearSessionId,
|
|
4941
|
-
content: {
|
|
4942
|
-
type: "action",
|
|
4943
|
-
action: "Updating plan",
|
|
4944
|
-
parameter
|
|
4945
|
-
}
|
|
4946
|
-
};
|
|
4947
|
-
}
|
|
4948
|
-
}
|
|
4949
|
-
if (event.type === "item.completed") {
|
|
4950
|
-
const item = event.item;
|
|
4951
|
-
if (!item) return null;
|
|
4952
|
-
if (item.type === "command_execution") {
|
|
4953
|
-
const command = "command" in item ? String(item.command || "") : "";
|
|
4954
|
-
const output = "aggregated_output" in item ? String(item.aggregated_output || "") : "";
|
|
4955
|
-
const exitCode = "exit_code" in item ? item.exit_code : void 0;
|
|
4956
|
-
const result = exitCode !== void 0 ? `Exit code: ${exitCode}` : output || "Done";
|
|
4957
|
-
return {
|
|
4958
|
-
linearSessionId,
|
|
4959
|
-
content: {
|
|
4960
|
-
type: "action",
|
|
4961
|
-
action: "Running command",
|
|
4962
|
-
parameter: command,
|
|
4963
|
-
result
|
|
4964
|
-
}
|
|
4965
|
-
};
|
|
4966
|
-
}
|
|
4967
|
-
if (item.type === "file_change") {
|
|
4968
|
-
const changes = "changes" in item && Array.isArray(item.changes) ? item.changes : [];
|
|
4969
|
-
const paths = changes.map((c) => c.path || "").filter(Boolean);
|
|
4970
|
-
const status = "status" in item ? String(item.status || "") : "";
|
|
4971
|
-
return {
|
|
4972
|
-
linearSessionId,
|
|
4973
|
-
content: {
|
|
4974
|
-
type: "action",
|
|
4975
|
-
action: "File change",
|
|
4976
|
-
parameter: paths.join(", ") || "",
|
|
4977
|
-
result: status === "completed" ? "Done" : status || "Done"
|
|
4978
|
-
}
|
|
4979
|
-
};
|
|
4980
|
-
}
|
|
4981
|
-
if (item.type === "mcp_tool_call") {
|
|
4982
|
-
const tool2 = "tool" in item ? String(item.tool || "") : "";
|
|
4983
|
-
const status = "status" in item ? String(item.status || "") : "";
|
|
4984
|
-
return {
|
|
4985
|
-
linearSessionId,
|
|
4986
|
-
content: {
|
|
4987
|
-
type: "action",
|
|
4988
|
-
action: tool2 || "MCP tool call",
|
|
4989
|
-
parameter: "",
|
|
4990
|
-
result: status === "completed" ? "Done" : status || "Done"
|
|
4991
|
-
}
|
|
4992
|
-
};
|
|
4993
|
-
}
|
|
4994
|
-
if (item.type === "web_search") {
|
|
4995
|
-
const query2 = "query" in item ? String(item.query || "") : "";
|
|
4996
|
-
return {
|
|
4997
|
-
linearSessionId,
|
|
4998
|
-
content: {
|
|
4999
|
-
type: "action",
|
|
5000
|
-
action: "Web search",
|
|
5001
|
-
parameter: query2,
|
|
5002
|
-
result: "Done"
|
|
5003
|
-
}
|
|
5004
|
-
};
|
|
5005
|
-
}
|
|
5006
|
-
if (item.type === "todo_list") {
|
|
5007
|
-
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
5008
|
-
const completed = items.filter((t) => t.completed).length;
|
|
5009
|
-
const total = items.length;
|
|
5010
|
-
const currentTask = items.find((t) => !t.completed);
|
|
5011
|
-
const summary = `${completed}/${total}`;
|
|
5012
|
-
const parameter = currentTask?.text ? `${summary}: ${currentTask.text}` : summary;
|
|
5013
|
-
return {
|
|
5014
|
-
linearSessionId,
|
|
5015
|
-
content: {
|
|
5016
|
-
type: "action",
|
|
5017
|
-
action: "Updating plan",
|
|
5018
|
-
parameter,
|
|
5019
|
-
result: "Done"
|
|
5020
|
-
}
|
|
5021
|
-
};
|
|
5022
|
-
}
|
|
5023
|
-
if (item.type === "agent_message") {
|
|
5024
|
-
const text = "text" in item ? String(item.text || "") : "";
|
|
5025
|
-
if (text) {
|
|
5026
|
-
return {
|
|
5027
|
-
linearSessionId,
|
|
5028
|
-
content: {
|
|
5029
|
-
type: "thought",
|
|
5030
|
-
body: text
|
|
5031
|
-
}
|
|
5032
|
-
};
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
}
|
|
5036
|
-
return null;
|
|
5037
|
-
}
|
|
5038
|
-
function extractPlanFromCodexEvent(event) {
|
|
5039
|
-
if (event.type !== "item.started" && event.type !== "item.completed") {
|
|
5040
|
-
return null;
|
|
5041
|
-
}
|
|
5042
|
-
const item = event.item;
|
|
5043
|
-
if (!item || item.type !== "todo_list") {
|
|
5044
|
-
return null;
|
|
5045
|
-
}
|
|
5046
|
-
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
5047
|
-
if (items.length === 0) {
|
|
5048
|
-
return null;
|
|
5049
|
-
}
|
|
5050
|
-
const hasIncomplete = items.some((entry) => !entry.completed);
|
|
5051
|
-
return items.filter((entry) => Boolean(entry.text && entry.text.trim().length > 0)).map((entry) => ({
|
|
5052
|
-
content: entry.text.trim(),
|
|
5053
|
-
status: entry.completed ? "completed" : hasIncomplete ? "inProgress" : "pending"
|
|
5054
|
-
}));
|
|
5055
|
-
}
|
|
5056
4850
|
function codexAspReasoningText(item) {
|
|
5057
4851
|
return [...item.summary, ...item.content].filter(Boolean).join("\n").trim();
|
|
5058
4852
|
}
|
|
@@ -5616,12 +5410,6 @@ function buildCodexAgentEnv() {
|
|
|
5616
5410
|
delete env.GH_CONFIG_DIR;
|
|
5617
5411
|
return env;
|
|
5618
5412
|
}
|
|
5619
|
-
function resolveCodexApiKey() {
|
|
5620
|
-
if (shouldStripOpenAIApiKey()) {
|
|
5621
|
-
return void 0;
|
|
5622
|
-
}
|
|
5623
|
-
return ENGINE_ENV.OPENAI_API_KEY;
|
|
5624
|
-
}
|
|
5625
5413
|
function shouldStripAnthropicApiKey() {
|
|
5626
5414
|
const method = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
|
|
5627
5415
|
return method === "oauth" || method === "bedrock";
|
|
@@ -6744,7 +6532,7 @@ var AspClient = class {
|
|
|
6744
6532
|
// src/managers/codex-asp/app-server-process.ts
|
|
6745
6533
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
6746
6534
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
6747
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6535
|
+
var ENGINE_PACKAGE_VERSION = "0.1.279";
|
|
6748
6536
|
var INITIALIZE_METHOD = "initialize";
|
|
6749
6537
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6750
6538
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -6947,25 +6735,6 @@ function buildCodexRateLimitsSnapshot(fields) {
|
|
|
6947
6735
|
planType: fields.planType
|
|
6948
6736
|
};
|
|
6949
6737
|
}
|
|
6950
|
-
function extractCodexRateLimitsSnapshotFromJsonl(parsed) {
|
|
6951
|
-
if (!isRecord4(parsed)) return null;
|
|
6952
|
-
const payload = isRecord4(parsed.payload) ? parsed.payload : parsed;
|
|
6953
|
-
const rateLimits = isRecord4(payload.rate_limits) ? payload.rate_limits : null;
|
|
6954
|
-
if (!rateLimits) return null;
|
|
6955
|
-
const credits = isRecord4(rateLimits.credits) ? rateLimits.credits : null;
|
|
6956
|
-
const hasCredits = credits && typeof credits.has_credits === "boolean" ? credits.has_credits : null;
|
|
6957
|
-
const unlimited = credits && typeof credits.unlimited === "boolean" ? credits.unlimited : null;
|
|
6958
|
-
const balance = credits && typeof credits.balance === "string" ? credits.balance : null;
|
|
6959
|
-
const rateLimitResetType = typeof rateLimits.rate_limit_reached_type === "string" && rateLimits.rate_limit_reached_type.length > 0 ? rateLimits.rate_limit_reached_type : null;
|
|
6960
|
-
const planType = typeof rateLimits.plan_type === "string" ? rateLimits.plan_type : null;
|
|
6961
|
-
return buildCodexRateLimitsSnapshot({
|
|
6962
|
-
unlimited,
|
|
6963
|
-
hasCredits,
|
|
6964
|
-
balance,
|
|
6965
|
-
rateLimitResetType,
|
|
6966
|
-
planType
|
|
6967
|
-
});
|
|
6968
|
-
}
|
|
6969
6738
|
var CodexQuotaStatusTracker = class {
|
|
6970
6739
|
lastEmittedQuotaState = "ok";
|
|
6971
6740
|
latestQuotaSnapshot = null;
|
|
@@ -8444,410 +8213,6 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8444
8213
|
}
|
|
8445
8214
|
};
|
|
8446
8215
|
|
|
8447
|
-
// src/managers/codex-manager.ts
|
|
8448
|
-
import { Codex } from "@openai/codex-sdk";
|
|
8449
|
-
import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir11, readFile as readFile8 } from "fs/promises";
|
|
8450
|
-
import { existsSync as existsSync7 } from "fs";
|
|
8451
|
-
import { join as join14 } from "path";
|
|
8452
|
-
import { homedir as homedir12 } from "os";
|
|
8453
|
-
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
8454
|
-
var CODEX_CONFIG_PATH = join14(homedir12(), ".codex", "config.toml");
|
|
8455
|
-
function isJsonlEvent2(value) {
|
|
8456
|
-
if (!isRecord4(value)) {
|
|
8457
|
-
return false;
|
|
8458
|
-
}
|
|
8459
|
-
return typeof value.timestamp === "string" && typeof value.type === "string" && isRecord4(value.payload);
|
|
8460
|
-
}
|
|
8461
|
-
function sleep(ms) {
|
|
8462
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
8463
|
-
}
|
|
8464
|
-
var CodexManager = class extends CodingAgentManager {
|
|
8465
|
-
codex;
|
|
8466
|
-
currentThreadId = null;
|
|
8467
|
-
currentThread = null;
|
|
8468
|
-
activeAbortController = null;
|
|
8469
|
-
quotaStatus = new CodexQuotaStatusTracker();
|
|
8470
|
-
constructor(options) {
|
|
8471
|
-
super(options);
|
|
8472
|
-
this.codex = this.createCodexClient();
|
|
8473
|
-
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8474
|
-
}
|
|
8475
|
-
createCodexClient() {
|
|
8476
|
-
const codexApiKey = resolveCodexApiKey();
|
|
8477
|
-
return new Codex({
|
|
8478
|
-
env: buildCodexAgentEnv(),
|
|
8479
|
-
...codexApiKey ? { apiKey: codexApiKey } : {}
|
|
8480
|
-
});
|
|
8481
|
-
}
|
|
8482
|
-
resetCodexClient() {
|
|
8483
|
-
this.codex = this.createCodexClient();
|
|
8484
|
-
this.currentThread = null;
|
|
8485
|
-
}
|
|
8486
|
-
async initialize() {
|
|
8487
|
-
if (this.initialSessionId) {
|
|
8488
|
-
this.currentThreadId = this.initialSessionId;
|
|
8489
|
-
console.log(`[CodexManager] Restored thread ID from persisted state: ${this.currentThreadId}`);
|
|
8490
|
-
}
|
|
8491
|
-
}
|
|
8492
|
-
async flushQuotaSnapshotFromCurrentSession() {
|
|
8493
|
-
if (!this.currentThreadId) return;
|
|
8494
|
-
try {
|
|
8495
|
-
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
8496
|
-
if (!sessionFile) return;
|
|
8497
|
-
const content = await readFile8(sessionFile, "utf-8");
|
|
8498
|
-
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
8499
|
-
let latest = null;
|
|
8500
|
-
for (const line of lines) {
|
|
8501
|
-
try {
|
|
8502
|
-
const parsed = JSON.parse(line);
|
|
8503
|
-
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
8504
|
-
if (snapshot) {
|
|
8505
|
-
latest = snapshot;
|
|
8506
|
-
}
|
|
8507
|
-
} catch {
|
|
8508
|
-
}
|
|
8509
|
-
}
|
|
8510
|
-
if (latest) {
|
|
8511
|
-
this.emitQuotaStatus(latest);
|
|
8512
|
-
}
|
|
8513
|
-
} catch (error) {
|
|
8514
|
-
console.warn("[CodexManager] Failed to flush quota snapshot from session:", error);
|
|
8515
|
-
}
|
|
8516
|
-
}
|
|
8517
|
-
emitQuotaStatus(snapshot, force = false) {
|
|
8518
|
-
const event = this.quotaStatus.apply(snapshot, force);
|
|
8519
|
-
if (event) this.onEvent(event);
|
|
8520
|
-
}
|
|
8521
|
-
async interruptActiveTurn() {
|
|
8522
|
-
if (this.activeAbortController) {
|
|
8523
|
-
this.activeAbortController.abort();
|
|
8524
|
-
}
|
|
8525
|
-
}
|
|
8526
|
-
/**
|
|
8527
|
-
* Update the developer_instructions in ~/.codex/config.toml
|
|
8528
|
-
* This sets the system prompt that Codex will use for this turn
|
|
8529
|
-
*/
|
|
8530
|
-
async updateCodexConfig(developerInstructions) {
|
|
8531
|
-
try {
|
|
8532
|
-
const codexDir = join14(homedir12(), ".codex");
|
|
8533
|
-
await mkdir11(codexDir, { recursive: true });
|
|
8534
|
-
let config = {};
|
|
8535
|
-
if (existsSync7(CODEX_CONFIG_PATH)) {
|
|
8536
|
-
try {
|
|
8537
|
-
const existingContent = await readFile8(CODEX_CONFIG_PATH, "utf-8");
|
|
8538
|
-
const parsed = parseToml(existingContent);
|
|
8539
|
-
if (isRecord4(parsed)) {
|
|
8540
|
-
config = parsed;
|
|
8541
|
-
}
|
|
8542
|
-
} catch (parseError) {
|
|
8543
|
-
console.warn("[CodexManager] Failed to parse existing config.toml, starting fresh:", parseError);
|
|
8544
|
-
}
|
|
8545
|
-
}
|
|
8546
|
-
if (developerInstructions) {
|
|
8547
|
-
config.developer_instructions = developerInstructions;
|
|
8548
|
-
} else {
|
|
8549
|
-
delete config.developer_instructions;
|
|
8550
|
-
}
|
|
8551
|
-
const tomlContent = stringifyToml(config);
|
|
8552
|
-
await writeFile6(CODEX_CONFIG_PATH, tomlContent, "utf-8");
|
|
8553
|
-
console.log("[CodexManager] Updated config.toml with developer_instructions");
|
|
8554
|
-
} catch (error) {
|
|
8555
|
-
console.error("[CodexManager] Failed to update config.toml:", error);
|
|
8556
|
-
}
|
|
8557
|
-
}
|
|
8558
|
-
async processMessageInternal(request) {
|
|
8559
|
-
try {
|
|
8560
|
-
await this.executeCodexTurn(request);
|
|
8561
|
-
} catch (error) {
|
|
8562
|
-
if (isCodexAuthError(error)) {
|
|
8563
|
-
const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
|
|
8564
|
-
if (refreshed) {
|
|
8565
|
-
this.resetCodexClient();
|
|
8566
|
-
await this.executeCodexTurn(request);
|
|
8567
|
-
return;
|
|
8568
|
-
}
|
|
8569
|
-
}
|
|
8570
|
-
throw error;
|
|
8571
|
-
}
|
|
8572
|
-
}
|
|
8573
|
-
async executeCodexTurn(request) {
|
|
8574
|
-
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
8575
|
-
await this.flushQuotaSnapshotFromCurrentSession();
|
|
8576
|
-
if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
|
|
8577
|
-
this.emitQuotaStatus(this.quotaStatus.latestSnapshot, true);
|
|
8578
|
-
try {
|
|
8579
|
-
await this.onTurnComplete();
|
|
8580
|
-
} catch (error) {
|
|
8581
|
-
console.error("[CodexManager] onTurnComplete failed during quota-blocked turn:", error);
|
|
8582
|
-
}
|
|
8583
|
-
return;
|
|
8584
|
-
}
|
|
8585
|
-
}
|
|
8586
|
-
const {
|
|
8587
|
-
message,
|
|
8588
|
-
model,
|
|
8589
|
-
customInstructions,
|
|
8590
|
-
images,
|
|
8591
|
-
permissionMode,
|
|
8592
|
-
thinkingLevel
|
|
8593
|
-
} = request;
|
|
8594
|
-
const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
|
|
8595
|
-
let tempImagePaths = [];
|
|
8596
|
-
let stopTail = null;
|
|
8597
|
-
let abortController = null;
|
|
8598
|
-
try {
|
|
8599
|
-
if (images && images.length > 0) {
|
|
8600
|
-
const normalizedImages = await normalizeImages(images);
|
|
8601
|
-
tempImagePaths = await saveNormalizedImagesToTempFiles(normalizedImages);
|
|
8602
|
-
}
|
|
8603
|
-
const developerInstructions = this.buildCombinedInstructions(customInstructions);
|
|
8604
|
-
await this.updateCodexConfig(developerInstructions);
|
|
8605
|
-
const sandboxMode = "danger-full-access";
|
|
8606
|
-
const webSearchMode = "live";
|
|
8607
|
-
const codexReasoningEffort = codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
8608
|
-
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
8609
|
-
const threadOptions = {
|
|
8610
|
-
workingDirectory: this.workingDirectory,
|
|
8611
|
-
skipGitRepoCheck: true,
|
|
8612
|
-
sandboxMode,
|
|
8613
|
-
model: model || DEFAULT_CODEX_MODEL,
|
|
8614
|
-
webSearchMode,
|
|
8615
|
-
additionalDirectories,
|
|
8616
|
-
...codexReasoningEffort ? { modelReasoningEffort: codexReasoningEffort } : {}
|
|
8617
|
-
};
|
|
8618
|
-
abortController = new AbortController();
|
|
8619
|
-
this.activeAbortController = abortController;
|
|
8620
|
-
if (this.currentThreadId) {
|
|
8621
|
-
this.currentThread = this.codex.resumeThread(this.currentThreadId, threadOptions);
|
|
8622
|
-
} else {
|
|
8623
|
-
this.currentThread = this.codex.startThread(threadOptions);
|
|
8624
|
-
const { events } = await this.currentThread.runStreamed("Hello", { signal: abortController.signal });
|
|
8625
|
-
for await (const event of events) {
|
|
8626
|
-
if (event.type === "thread.started") {
|
|
8627
|
-
this.currentThreadId = event.thread_id;
|
|
8628
|
-
await this.onSaveSessionId(this.currentThreadId);
|
|
8629
|
-
console.log(`[CodexManager] Captured and persisted thread ID: ${this.currentThreadId}`);
|
|
8630
|
-
}
|
|
8631
|
-
}
|
|
8632
|
-
if (!this.currentThreadId && this.currentThread.id) {
|
|
8633
|
-
this.currentThreadId = this.currentThread.id;
|
|
8634
|
-
await this.onSaveSessionId(this.currentThreadId);
|
|
8635
|
-
console.log(`[CodexManager] Captured and persisted thread ID from thread.id: ${this.currentThreadId}`);
|
|
8636
|
-
}
|
|
8637
|
-
}
|
|
8638
|
-
stopTail = this.currentThreadId ? await this.startSessionTail(this.currentThreadId) : null;
|
|
8639
|
-
let input;
|
|
8640
|
-
if (tempImagePaths.length > 0) {
|
|
8641
|
-
const inputItems = [
|
|
8642
|
-
{ type: "text", text: message },
|
|
8643
|
-
...tempImagePaths.map((path4) => ({ type: "local_image", path: path4 }))
|
|
8644
|
-
];
|
|
8645
|
-
input = inputItems;
|
|
8646
|
-
} else {
|
|
8647
|
-
input = message;
|
|
8648
|
-
}
|
|
8649
|
-
try {
|
|
8650
|
-
const { events } = await this.currentThread.runStreamed(input, { signal: abortController.signal });
|
|
8651
|
-
const linearForwarder = new LinearEventForwarder(linearSessionId);
|
|
8652
|
-
for await (const event of events) {
|
|
8653
|
-
if (linearSessionId) {
|
|
8654
|
-
linearForwarder.sendPlan(extractPlanFromCodexEvent(event));
|
|
8655
|
-
linearForwarder.sendEvent(convertCodexEvent(event, linearSessionId));
|
|
8656
|
-
}
|
|
8657
|
-
}
|
|
8658
|
-
linearForwarder.flushThoughtAsResponse();
|
|
8659
|
-
} catch (error) {
|
|
8660
|
-
await this.flushQuotaSnapshotFromCurrentSession();
|
|
8661
|
-
if (this.quotaStatus.blocked) {
|
|
8662
|
-
return;
|
|
8663
|
-
}
|
|
8664
|
-
throw error;
|
|
8665
|
-
}
|
|
8666
|
-
} finally {
|
|
8667
|
-
if (stopTail) {
|
|
8668
|
-
await stopTail();
|
|
8669
|
-
}
|
|
8670
|
-
await removeTempImageFiles(tempImagePaths);
|
|
8671
|
-
try {
|
|
8672
|
-
await this.onTurnComplete();
|
|
8673
|
-
} catch (error) {
|
|
8674
|
-
console.error("[CodexManager] onTurnComplete failed:", error);
|
|
8675
|
-
}
|
|
8676
|
-
this.activeAbortController = null;
|
|
8677
|
-
}
|
|
8678
|
-
}
|
|
8679
|
-
async getHistory() {
|
|
8680
|
-
if (!this.currentThreadId) {
|
|
8681
|
-
return {
|
|
8682
|
-
thread_id: null,
|
|
8683
|
-
events: []
|
|
8684
|
-
};
|
|
8685
|
-
}
|
|
8686
|
-
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
8687
|
-
if (!sessionFile) {
|
|
8688
|
-
return {
|
|
8689
|
-
thread_id: this.currentThreadId,
|
|
8690
|
-
events: []
|
|
8691
|
-
};
|
|
8692
|
-
}
|
|
8693
|
-
const events = await readJSONL(sessionFile);
|
|
8694
|
-
return {
|
|
8695
|
-
thread_id: this.currentThreadId,
|
|
8696
|
-
events
|
|
8697
|
-
};
|
|
8698
|
-
}
|
|
8699
|
-
// Helper methods for finding session files
|
|
8700
|
-
async findSessionFile(threadId) {
|
|
8701
|
-
const sessionsDir = join14(homedir12(), ".codex", "sessions");
|
|
8702
|
-
try {
|
|
8703
|
-
const now = /* @__PURE__ */ new Date();
|
|
8704
|
-
const year = now.getFullYear();
|
|
8705
|
-
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
8706
|
-
const day = String(now.getDate()).padStart(2, "0");
|
|
8707
|
-
const todayDir = join14(sessionsDir, String(year), month, day);
|
|
8708
|
-
const file = await this.findFileInDirectory(todayDir, threadId);
|
|
8709
|
-
if (file) return file;
|
|
8710
|
-
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
8711
|
-
const date = new Date(now);
|
|
8712
|
-
date.setDate(date.getDate() - daysAgo);
|
|
8713
|
-
const searchYear = date.getFullYear();
|
|
8714
|
-
const searchMonth = String(date.getMonth() + 1).padStart(2, "0");
|
|
8715
|
-
const searchDay = String(date.getDate()).padStart(2, "0");
|
|
8716
|
-
const searchDir = join14(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
8717
|
-
const file2 = await this.findFileInDirectory(searchDir, threadId);
|
|
8718
|
-
if (file2) return file2;
|
|
8719
|
-
}
|
|
8720
|
-
return null;
|
|
8721
|
-
} catch (error) {
|
|
8722
|
-
return null;
|
|
8723
|
-
}
|
|
8724
|
-
}
|
|
8725
|
-
async findFileInDirectory(directory, threadId) {
|
|
8726
|
-
try {
|
|
8727
|
-
const files = await readdir3(directory);
|
|
8728
|
-
for (const file of files) {
|
|
8729
|
-
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
8730
|
-
const fullPath = join14(directory, file);
|
|
8731
|
-
const stats = await stat2(fullPath);
|
|
8732
|
-
if (stats.isFile()) {
|
|
8733
|
-
return fullPath;
|
|
8734
|
-
}
|
|
8735
|
-
}
|
|
8736
|
-
}
|
|
8737
|
-
return null;
|
|
8738
|
-
} catch (error) {
|
|
8739
|
-
return null;
|
|
8740
|
-
}
|
|
8741
|
-
}
|
|
8742
|
-
async waitForSessionFile(threadId, timeoutMs = 5e3) {
|
|
8743
|
-
const start = Date.now();
|
|
8744
|
-
while (Date.now() - start < timeoutMs) {
|
|
8745
|
-
const sessionFile = await this.findSessionFile(threadId);
|
|
8746
|
-
if (sessionFile) {
|
|
8747
|
-
return sessionFile;
|
|
8748
|
-
}
|
|
8749
|
-
await sleep(100);
|
|
8750
|
-
}
|
|
8751
|
-
return null;
|
|
8752
|
-
}
|
|
8753
|
-
// @openai/codex-sdk doesn't expose manual /compact (TUI-only); we only mirror the auto-compaction rollout entries to the UI.
|
|
8754
|
-
trackNativeCompaction(event) {
|
|
8755
|
-
if (event.type === "compacted") {
|
|
8756
|
-
this.setCompacting(false);
|
|
8757
|
-
return;
|
|
8758
|
-
}
|
|
8759
|
-
if (event.type !== "event_msg") return;
|
|
8760
|
-
const msg = event.payload.msg;
|
|
8761
|
-
if (!msg) return;
|
|
8762
|
-
const itemType = msg.payload?.item?.type;
|
|
8763
|
-
if (itemType !== "context_compaction") return;
|
|
8764
|
-
if (msg.type === "item_started") {
|
|
8765
|
-
this.setCompacting(true);
|
|
8766
|
-
} else if (msg.type === "item_completed") {
|
|
8767
|
-
this.setCompacting(false);
|
|
8768
|
-
}
|
|
8769
|
-
}
|
|
8770
|
-
async startSessionTail(threadId) {
|
|
8771
|
-
const sessionFile = await this.waitForSessionFile(threadId);
|
|
8772
|
-
if (!sessionFile) {
|
|
8773
|
-
return async () => {
|
|
8774
|
-
};
|
|
8775
|
-
}
|
|
8776
|
-
let active = true;
|
|
8777
|
-
const seenLines = /* @__PURE__ */ new Set();
|
|
8778
|
-
const seedSeenLines = async () => {
|
|
8779
|
-
try {
|
|
8780
|
-
const content = await readFile8(sessionFile, "utf-8");
|
|
8781
|
-
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
8782
|
-
let latest = null;
|
|
8783
|
-
for (const line of lines) {
|
|
8784
|
-
seenLines.add(line);
|
|
8785
|
-
try {
|
|
8786
|
-
const parsed = JSON.parse(line);
|
|
8787
|
-
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
8788
|
-
if (snapshot) latest = snapshot;
|
|
8789
|
-
} catch {
|
|
8790
|
-
}
|
|
8791
|
-
}
|
|
8792
|
-
if (latest) {
|
|
8793
|
-
this.quotaStatus.prime(latest);
|
|
8794
|
-
}
|
|
8795
|
-
} catch {
|
|
8796
|
-
}
|
|
8797
|
-
};
|
|
8798
|
-
await seedSeenLines();
|
|
8799
|
-
const pump = async () => {
|
|
8800
|
-
let emitted = 0;
|
|
8801
|
-
try {
|
|
8802
|
-
const content = await readFile8(sessionFile, "utf-8");
|
|
8803
|
-
const lines = content.split("\n");
|
|
8804
|
-
const completeLines = content.endsWith("\n") ? lines : lines.slice(0, -1);
|
|
8805
|
-
for (const line of completeLines) {
|
|
8806
|
-
const trimmed = line.trim();
|
|
8807
|
-
if (!trimmed || seenLines.has(trimmed)) {
|
|
8808
|
-
continue;
|
|
8809
|
-
}
|
|
8810
|
-
seenLines.add(trimmed);
|
|
8811
|
-
try {
|
|
8812
|
-
const parsed = JSON.parse(trimmed);
|
|
8813
|
-
const snapshot = extractCodexRateLimitsSnapshotFromJsonl(parsed);
|
|
8814
|
-
if (snapshot) {
|
|
8815
|
-
this.emitQuotaStatus(snapshot);
|
|
8816
|
-
}
|
|
8817
|
-
if (isJsonlEvent2(parsed)) {
|
|
8818
|
-
this.trackNativeCompaction(parsed);
|
|
8819
|
-
this.onEvent(parsed);
|
|
8820
|
-
emitted += 1;
|
|
8821
|
-
}
|
|
8822
|
-
} catch {
|
|
8823
|
-
}
|
|
8824
|
-
}
|
|
8825
|
-
} catch {
|
|
8826
|
-
}
|
|
8827
|
-
return emitted;
|
|
8828
|
-
};
|
|
8829
|
-
const loop = (async () => {
|
|
8830
|
-
while (active) {
|
|
8831
|
-
await pump();
|
|
8832
|
-
await sleep(100);
|
|
8833
|
-
}
|
|
8834
|
-
await pump();
|
|
8835
|
-
})();
|
|
8836
|
-
return async () => {
|
|
8837
|
-
active = false;
|
|
8838
|
-
await loop;
|
|
8839
|
-
const deadline = Date.now() + 1500;
|
|
8840
|
-
while (Date.now() < deadline) {
|
|
8841
|
-
const emitted = await pump();
|
|
8842
|
-
if (emitted > 0) {
|
|
8843
|
-
continue;
|
|
8844
|
-
}
|
|
8845
|
-
await sleep(100);
|
|
8846
|
-
}
|
|
8847
|
-
};
|
|
8848
|
-
}
|
|
8849
|
-
};
|
|
8850
|
-
|
|
8851
8216
|
// src/managers/relay-tools.ts
|
|
8852
8217
|
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
8853
8218
|
import { z } from "zod";
|
|
@@ -9449,19 +8814,19 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
9449
8814
|
};
|
|
9450
8815
|
|
|
9451
8816
|
// src/services/chat/chat-service.ts
|
|
9452
|
-
var ENGINE_DIR2 =
|
|
9453
|
-
var CHATS_FILE =
|
|
9454
|
-
var CLAUDE_HISTORY_DIR =
|
|
9455
|
-
var RELAY_HISTORY_DIR =
|
|
9456
|
-
var CHAT_SENDERS_DIR =
|
|
9457
|
-
var CODEX_AUTH_PATH2 =
|
|
8817
|
+
var ENGINE_DIR2 = join14(homedir12(), ".replicas", "engine");
|
|
8818
|
+
var CHATS_FILE = join14(ENGINE_DIR2, "chats.json");
|
|
8819
|
+
var CLAUDE_HISTORY_DIR = join14(ENGINE_DIR2, "claude-histories");
|
|
8820
|
+
var RELAY_HISTORY_DIR = join14(ENGINE_DIR2, "relay-histories");
|
|
8821
|
+
var CHAT_SENDERS_DIR = join14(ENGINE_DIR2, "chat-senders");
|
|
8822
|
+
var CODEX_AUTH_PATH2 = join14(homedir12(), ".codex", "auth.json");
|
|
9458
8823
|
var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
|
|
9459
8824
|
function isChatMessageSender(value) {
|
|
9460
8825
|
if (!isRecord4(value)) return false;
|
|
9461
8826
|
return typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
|
|
9462
8827
|
}
|
|
9463
8828
|
function isCodexAvailable() {
|
|
9464
|
-
return
|
|
8829
|
+
return existsSync7(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
|
|
9465
8830
|
}
|
|
9466
8831
|
function isSameAcceptedUserEvent(event, acceptedEvent) {
|
|
9467
8832
|
if (areSameUserMessageEvents(event, acceptedEvent)) return true;
|
|
@@ -9503,18 +8868,18 @@ function isPersistedChat(value) {
|
|
|
9503
8868
|
return false;
|
|
9504
8869
|
}
|
|
9505
8870
|
const candidate = value;
|
|
9506
|
-
return typeof candidate.id === "string" && (candidate.provider === "claude" || candidate.provider === "codex" || candidate.provider === "relay") && typeof candidate.title === "string" && typeof candidate.createdAt === "string" && typeof candidate.updatedAt === "string" && (candidate.providerSessionId === null || typeof candidate.providerSessionId === "string") && (candidate.parentChatId === void 0 || candidate.parentChatId === null || typeof candidate.parentChatId === "string")
|
|
9507
|
-
}
|
|
9508
|
-
function codexBackendForChat(chat) {
|
|
9509
|
-
if (chat.provider !== "codex") return "asp";
|
|
9510
|
-
if (chat.codexBackend) return chat.codexBackend;
|
|
9511
|
-
return chat.providerSessionId ? "sdk" : "asp";
|
|
8871
|
+
return typeof candidate.id === "string" && (candidate.provider === "claude" || candidate.provider === "codex" || candidate.provider === "relay") && typeof candidate.title === "string" && typeof candidate.createdAt === "string" && typeof candidate.updatedAt === "string" && (candidate.providerSessionId === null || typeof candidate.providerSessionId === "string") && (candidate.parentChatId === void 0 || candidate.parentChatId === null || typeof candidate.parentChatId === "string");
|
|
9512
8872
|
}
|
|
9513
8873
|
function normalizePersistedChat(chat) {
|
|
8874
|
+
const isLegacyCodexSdkChat = chat.provider === "codex" && (chat.codexBackend === "sdk" || chat.codexBackend === void 0 && chat.providerSessionId !== null);
|
|
9514
8875
|
return {
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
8876
|
+
id: chat.id,
|
|
8877
|
+
provider: chat.provider,
|
|
8878
|
+
title: chat.title,
|
|
8879
|
+
createdAt: chat.createdAt,
|
|
8880
|
+
updatedAt: chat.updatedAt,
|
|
8881
|
+
providerSessionId: isLegacyCodexSdkChat ? null : chat.providerSessionId,
|
|
8882
|
+
parentChatId: chat.parentChatId ?? null
|
|
9518
8883
|
};
|
|
9519
8884
|
}
|
|
9520
8885
|
function parsePersistedChatsContent(content) {
|
|
@@ -9549,10 +8914,10 @@ var ChatService = class {
|
|
|
9549
8914
|
chats = /* @__PURE__ */ new Map();
|
|
9550
8915
|
writeChain = Promise.resolve();
|
|
9551
8916
|
async initialize() {
|
|
9552
|
-
await
|
|
9553
|
-
await
|
|
9554
|
-
await
|
|
9555
|
-
await
|
|
8917
|
+
await mkdir11(ENGINE_DIR2, { recursive: true });
|
|
8918
|
+
await mkdir11(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
8919
|
+
await mkdir11(RELAY_HISTORY_DIR, { recursive: true });
|
|
8920
|
+
await mkdir11(CHAT_SENDERS_DIR, { recursive: true });
|
|
9556
8921
|
const persisted = await this.loadChats();
|
|
9557
8922
|
for (const chat of persisted) {
|
|
9558
8923
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -9604,8 +8969,7 @@ var ChatService = class {
|
|
|
9604
8969
|
createdAt: now,
|
|
9605
8970
|
updatedAt: now,
|
|
9606
8971
|
providerSessionId: null,
|
|
9607
|
-
parentChatId
|
|
9608
|
-
...request.provider === "codex" ? { codexBackend: "asp" } : {}
|
|
8972
|
+
parentChatId
|
|
9609
8973
|
};
|
|
9610
8974
|
const runtime = this.createRuntimeChat(persisted);
|
|
9611
8975
|
this.chats.set(persisted.id, runtime);
|
|
@@ -9651,7 +9015,7 @@ var ChatService = class {
|
|
|
9651
9015
|
};
|
|
9652
9016
|
}
|
|
9653
9017
|
senderFilePath(chatId) {
|
|
9654
|
-
return
|
|
9018
|
+
return join14(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
9655
9019
|
}
|
|
9656
9020
|
async appendSender(chatId, sender) {
|
|
9657
9021
|
try {
|
|
@@ -9662,7 +9026,7 @@ var ChatService = class {
|
|
|
9662
9026
|
}
|
|
9663
9027
|
async readSenders(chatId) {
|
|
9664
9028
|
try {
|
|
9665
|
-
const content = await
|
|
9029
|
+
const content = await readFile8(this.senderFilePath(chatId), "utf-8");
|
|
9666
9030
|
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
9667
9031
|
const senders = [];
|
|
9668
9032
|
for (const line of lines) {
|
|
@@ -9801,7 +9165,7 @@ var ChatService = class {
|
|
|
9801
9165
|
async deleteHistoryFile(persisted) {
|
|
9802
9166
|
if (persisted.provider === "claude" || persisted.provider === "relay") {
|
|
9803
9167
|
const dir = persisted.provider === "claude" ? CLAUDE_HISTORY_DIR : RELAY_HISTORY_DIR;
|
|
9804
|
-
await rm(
|
|
9168
|
+
await rm(join14(dir, `${persisted.id}.jsonl`), { force: true });
|
|
9805
9169
|
}
|
|
9806
9170
|
await rm(this.senderFilePath(persisted.id), { force: true });
|
|
9807
9171
|
}
|
|
@@ -9858,7 +9222,7 @@ var ChatService = class {
|
|
|
9858
9222
|
if (persisted.provider === "claude") {
|
|
9859
9223
|
provider = new ClaudeManager({
|
|
9860
9224
|
workingDirectory: this.workingDirectory,
|
|
9861
|
-
historyFilePath:
|
|
9225
|
+
historyFilePath: join14(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
9862
9226
|
initialSessionId: persisted.providerSessionId,
|
|
9863
9227
|
onSaveSessionId: saveSession,
|
|
9864
9228
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -9867,7 +9231,7 @@ var ChatService = class {
|
|
|
9867
9231
|
} else if (persisted.provider === "relay") {
|
|
9868
9232
|
provider = new RelayManager({
|
|
9869
9233
|
workingDirectory: this.workingDirectory,
|
|
9870
|
-
historyFilePath:
|
|
9234
|
+
historyFilePath: join14(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
9871
9235
|
initialSessionId: persisted.providerSessionId,
|
|
9872
9236
|
onSaveSessionId: saveSession,
|
|
9873
9237
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -9876,8 +9240,7 @@ var ChatService = class {
|
|
|
9876
9240
|
codexAvailable: isCodexAvailable()
|
|
9877
9241
|
});
|
|
9878
9242
|
} else {
|
|
9879
|
-
|
|
9880
|
-
provider = new CodexProviderCtor({
|
|
9243
|
+
provider = new CodexAspManager({
|
|
9881
9244
|
workingDirectory: this.workingDirectory,
|
|
9882
9245
|
initialSessionId: persisted.providerSessionId,
|
|
9883
9246
|
onSaveSessionId: saveSession,
|
|
@@ -10019,7 +9382,7 @@ var ChatService = class {
|
|
|
10019
9382
|
}
|
|
10020
9383
|
async loadChats() {
|
|
10021
9384
|
try {
|
|
10022
|
-
const content = await
|
|
9385
|
+
const content = await readFile8(CHATS_FILE, "utf-8");
|
|
10023
9386
|
return parsePersistedChatsContent(content);
|
|
10024
9387
|
} catch (error) {
|
|
10025
9388
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -10034,7 +9397,7 @@ var ChatService = class {
|
|
|
10034
9397
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
10035
9398
|
}
|
|
10036
9399
|
try {
|
|
10037
|
-
const backupContent = await
|
|
9400
|
+
const backupContent = await readFile8(CHATS_BACKUP_FILE, "utf-8");
|
|
10038
9401
|
return parsePersistedChatsContent(backupContent);
|
|
10039
9402
|
} catch (backupError) {
|
|
10040
9403
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -10109,8 +9472,8 @@ var ChatService = class {
|
|
|
10109
9472
|
|
|
10110
9473
|
// src/services/repo-file-service.ts
|
|
10111
9474
|
import { execFile as execFile2 } from "child_process";
|
|
10112
|
-
import { readFile as
|
|
10113
|
-
import { join as
|
|
9475
|
+
import { readFile as readFile9, realpath, stat as stat2 } from "fs/promises";
|
|
9476
|
+
import { join as join15, resolve, extname } from "path";
|
|
10114
9477
|
var CACHE_TTL_MS = 3e4;
|
|
10115
9478
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
10116
9479
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -10270,11 +9633,11 @@ var RepoFileService = class {
|
|
|
10270
9633
|
const repo = repos.find((r) => r.name === repoName);
|
|
10271
9634
|
if (!repo) return null;
|
|
10272
9635
|
try {
|
|
10273
|
-
const fullPath = await realpath(resolve(
|
|
9636
|
+
const fullPath = await realpath(resolve(join15(repo.path, filePath)));
|
|
10274
9637
|
const repoRoot = await realpath(repo.path);
|
|
10275
9638
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
10276
9639
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
10277
|
-
const fileStat = await
|
|
9640
|
+
const fileStat = await stat2(fullPath);
|
|
10278
9641
|
if (!fileStat.isFile()) return null;
|
|
10279
9642
|
const sizeBytes = fileStat.size;
|
|
10280
9643
|
if (isBinaryExtension(filePath)) {
|
|
@@ -10299,7 +9662,7 @@ var RepoFileService = class {
|
|
|
10299
9662
|
tooLarge: true
|
|
10300
9663
|
};
|
|
10301
9664
|
}
|
|
10302
|
-
const content = await
|
|
9665
|
+
const content = await readFile9(fullPath, "utf-8");
|
|
10303
9666
|
return {
|
|
10304
9667
|
repoName,
|
|
10305
9668
|
path: filePath,
|
|
@@ -10377,16 +9740,16 @@ var RepoFileService = class {
|
|
|
10377
9740
|
// src/v1-routes.ts
|
|
10378
9741
|
import { Hono } from "hono";
|
|
10379
9742
|
import { z as z2 } from "zod";
|
|
10380
|
-
import { readdir as
|
|
10381
|
-
import { join as
|
|
9743
|
+
import { readdir as readdir5, stat as stat4, readFile as readFile13 } from "fs/promises";
|
|
9744
|
+
import { join as join19, resolve as resolve2 } from "path";
|
|
10382
9745
|
|
|
10383
9746
|
// src/services/canvas-service.ts
|
|
10384
|
-
import { readdir as
|
|
10385
|
-
import { homedir as
|
|
10386
|
-
import { basename, join as
|
|
9747
|
+
import { readdir as readdir3, readFile as readFile10, stat as stat3 } from "fs/promises";
|
|
9748
|
+
import { homedir as homedir13 } from "os";
|
|
9749
|
+
import { basename, join as join16 } from "path";
|
|
10387
9750
|
var CANVAS_DIRECTORIES = [
|
|
10388
|
-
|
|
10389
|
-
|
|
9751
|
+
join16(homedir13(), ".claude", "plans"),
|
|
9752
|
+
join16(homedir13(), ".replicas", "canvas")
|
|
10390
9753
|
];
|
|
10391
9754
|
var MAX_CANVAS_FILE_BYTES = 5 * 1024 * 1024;
|
|
10392
9755
|
function isTextKind(kind) {
|
|
@@ -10401,7 +9764,7 @@ var CanvasService = class {
|
|
|
10401
9764
|
for (const directory of CANVAS_DIRECTORIES) {
|
|
10402
9765
|
let entries;
|
|
10403
9766
|
try {
|
|
10404
|
-
entries = await
|
|
9767
|
+
entries = await readdir3(directory, { withFileTypes: true });
|
|
10405
9768
|
} catch {
|
|
10406
9769
|
continue;
|
|
10407
9770
|
}
|
|
@@ -10412,7 +9775,7 @@ var CanvasService = class {
|
|
|
10412
9775
|
const { kind } = classifyCanvasFilename(entry.name);
|
|
10413
9776
|
let sizeBytes = 0;
|
|
10414
9777
|
try {
|
|
10415
|
-
const s = await
|
|
9778
|
+
const s = await stat3(join16(directory, entry.name));
|
|
10416
9779
|
sizeBytes = s.size;
|
|
10417
9780
|
} catch {
|
|
10418
9781
|
continue;
|
|
@@ -10427,10 +9790,10 @@ var CanvasService = class {
|
|
|
10427
9790
|
if (!safe || safe !== filename || safe.startsWith(".")) return null;
|
|
10428
9791
|
const { kind, mimeType } = classifyCanvasFilename(safe);
|
|
10429
9792
|
for (const directory of CANVAS_DIRECTORIES) {
|
|
10430
|
-
const filePath =
|
|
9793
|
+
const filePath = join16(directory, safe);
|
|
10431
9794
|
let sizeBytes = 0;
|
|
10432
9795
|
try {
|
|
10433
|
-
const s = await
|
|
9796
|
+
const s = await stat3(filePath);
|
|
10434
9797
|
sizeBytes = s.size;
|
|
10435
9798
|
} catch {
|
|
10436
9799
|
continue;
|
|
@@ -10446,10 +9809,10 @@ var CanvasService = class {
|
|
|
10446
9809
|
}
|
|
10447
9810
|
try {
|
|
10448
9811
|
if (isTextKind(kind)) {
|
|
10449
|
-
const content = await
|
|
9812
|
+
const content = await readFile10(filePath, "utf-8");
|
|
10450
9813
|
return { filename: safe, kind, sizeBytes, mimeType, content };
|
|
10451
9814
|
}
|
|
10452
|
-
const buf = await
|
|
9815
|
+
const buf = await readFile10(filePath);
|
|
10453
9816
|
return { filename: safe, kind, sizeBytes, mimeType, base64: buf.toString("base64") };
|
|
10454
9817
|
} catch {
|
|
10455
9818
|
continue;
|
|
@@ -10462,16 +9825,16 @@ var canvasService = new CanvasService();
|
|
|
10462
9825
|
|
|
10463
9826
|
// src/services/warm-hooks-service.ts
|
|
10464
9827
|
import { spawn as spawn4 } from "child_process";
|
|
10465
|
-
import { readFile as
|
|
10466
|
-
import { existsSync as
|
|
10467
|
-
import { join as
|
|
9828
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
9829
|
+
import { existsSync as existsSync8 } from "fs";
|
|
9830
|
+
import { join as join18 } from "path";
|
|
10468
9831
|
|
|
10469
9832
|
// src/services/warm-hook-logs-service.ts
|
|
10470
|
-
import { mkdir as
|
|
10471
|
-
import { homedir as
|
|
10472
|
-
import { join as
|
|
10473
|
-
var LOGS_DIR2 =
|
|
10474
|
-
var CURRENT_RUN_LOG =
|
|
9833
|
+
import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile6, readdir as readdir4, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
|
|
9834
|
+
import { homedir as homedir14 } from "os";
|
|
9835
|
+
import { join as join17 } from "path";
|
|
9836
|
+
var LOGS_DIR2 = join17(homedir14(), ".replicas", "warm-hook-logs");
|
|
9837
|
+
var CURRENT_RUN_LOG = join17(LOGS_DIR2, "current-run.log");
|
|
10475
9838
|
var GLOBAL_FILENAME = "global.json";
|
|
10476
9839
|
function withPreview2(stored) {
|
|
10477
9840
|
const preview = buildHookOutputPreview(stored.output);
|
|
@@ -10479,7 +9842,7 @@ function withPreview2(stored) {
|
|
|
10479
9842
|
}
|
|
10480
9843
|
var WarmHookLogsService = class {
|
|
10481
9844
|
async ensureDir() {
|
|
10482
|
-
await
|
|
9845
|
+
await mkdir12(LOGS_DIR2, { recursive: true });
|
|
10483
9846
|
}
|
|
10484
9847
|
async saveGlobalHookLog(entry) {
|
|
10485
9848
|
await this.ensureDir();
|
|
@@ -10488,7 +9851,7 @@ var WarmHookLogsService = class {
|
|
|
10488
9851
|
hookName: "organization",
|
|
10489
9852
|
...entry
|
|
10490
9853
|
};
|
|
10491
|
-
await
|
|
9854
|
+
await writeFile6(join17(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
10492
9855
|
`, "utf-8");
|
|
10493
9856
|
}
|
|
10494
9857
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -10498,7 +9861,7 @@ var WarmHookLogsService = class {
|
|
|
10498
9861
|
hookName: "environment",
|
|
10499
9862
|
...entry
|
|
10500
9863
|
};
|
|
10501
|
-
await
|
|
9864
|
+
await writeFile6(join17(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
10502
9865
|
`, "utf-8");
|
|
10503
9866
|
}
|
|
10504
9867
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -10508,13 +9871,13 @@ var WarmHookLogsService = class {
|
|
|
10508
9871
|
hookName: repoName,
|
|
10509
9872
|
...entry
|
|
10510
9873
|
};
|
|
10511
|
-
await
|
|
9874
|
+
await writeFile6(join17(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
10512
9875
|
`, "utf-8");
|
|
10513
9876
|
}
|
|
10514
9877
|
async getAllLogs() {
|
|
10515
9878
|
let files;
|
|
10516
9879
|
try {
|
|
10517
|
-
files = await
|
|
9880
|
+
files = await readdir4(LOGS_DIR2);
|
|
10518
9881
|
} catch (err) {
|
|
10519
9882
|
if (err.code === "ENOENT") {
|
|
10520
9883
|
return [];
|
|
@@ -10527,7 +9890,7 @@ var WarmHookLogsService = class {
|
|
|
10527
9890
|
continue;
|
|
10528
9891
|
}
|
|
10529
9892
|
try {
|
|
10530
|
-
const raw = await
|
|
9893
|
+
const raw = await readFile11(join17(LOGS_DIR2, file), "utf-8");
|
|
10531
9894
|
const stored = JSON.parse(raw);
|
|
10532
9895
|
logs.push(withPreview2(stored));
|
|
10533
9896
|
} catch {
|
|
@@ -10556,7 +9919,7 @@ var WarmHookLogsService = class {
|
|
|
10556
9919
|
}
|
|
10557
9920
|
async getCurrentRunLog() {
|
|
10558
9921
|
try {
|
|
10559
|
-
return await
|
|
9922
|
+
return await readFile11(CURRENT_RUN_LOG, "utf-8");
|
|
10560
9923
|
} catch (err) {
|
|
10561
9924
|
if (err.code === "ENOENT") return null;
|
|
10562
9925
|
throw err;
|
|
@@ -10565,7 +9928,7 @@ var WarmHookLogsService = class {
|
|
|
10565
9928
|
async getFullOutput(hookType, hookName) {
|
|
10566
9929
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
10567
9930
|
try {
|
|
10568
|
-
const raw = await
|
|
9931
|
+
const raw = await readFile11(join17(LOGS_DIR2, filename), "utf-8");
|
|
10569
9932
|
const stored = JSON.parse(raw);
|
|
10570
9933
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
10571
9934
|
return null;
|
|
@@ -10584,12 +9947,12 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
10584
9947
|
// src/services/warm-hooks-service.ts
|
|
10585
9948
|
async function readRepoWarmHook(repoPath) {
|
|
10586
9949
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
10587
|
-
const configPath =
|
|
10588
|
-
if (!
|
|
9950
|
+
const configPath = join18(repoPath, filename);
|
|
9951
|
+
if (!existsSync8(configPath)) {
|
|
10589
9952
|
continue;
|
|
10590
9953
|
}
|
|
10591
9954
|
try {
|
|
10592
|
-
const raw = await
|
|
9955
|
+
const raw = await readFile12(configPath, "utf-8");
|
|
10593
9956
|
const config = parseReplicasConfigString(raw, filename);
|
|
10594
9957
|
if (!config.warmHook) {
|
|
10595
9958
|
return null;
|
|
@@ -11482,12 +10845,12 @@ function createV1Routes(deps) {
|
|
|
11482
10845
|
});
|
|
11483
10846
|
app2.get("/logs", async (c) => {
|
|
11484
10847
|
try {
|
|
11485
|
-
const files = await
|
|
10848
|
+
const files = await readdir5(LOG_DIR).catch(() => []);
|
|
11486
10849
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
11487
10850
|
const sessions = await Promise.all(
|
|
11488
10851
|
logFiles.map(async (filename) => {
|
|
11489
|
-
const filePath =
|
|
11490
|
-
const fileStat = await
|
|
10852
|
+
const filePath = join19(LOG_DIR, filename);
|
|
10853
|
+
const fileStat = await stat4(filePath);
|
|
11491
10854
|
const sessionId = filename.replace(/\.log$/, "");
|
|
11492
10855
|
return {
|
|
11493
10856
|
sessionId,
|
|
@@ -11523,7 +10886,7 @@ function createV1Routes(deps) {
|
|
|
11523
10886
|
const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
|
|
11524
10887
|
let content;
|
|
11525
10888
|
try {
|
|
11526
|
-
content = await
|
|
10889
|
+
content = await readFile13(filePath, "utf-8");
|
|
11527
10890
|
} catch {
|
|
11528
10891
|
return c.json(jsonError("Log session not found"), 404);
|
|
11529
10892
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.279",
|
|
4
4
|
"description": "Lightweight API server for Replicas workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/claude-agent-sdk": "0.2.112",
|
|
34
34
|
"@hono/node-server": "^1.19.5",
|
|
35
|
-
"@openai/codex-sdk": "^0.124.0",
|
|
36
35
|
"hono": "^4.10.3",
|
|
37
36
|
"smol-toml": "^1.6.0",
|
|
38
37
|
"yaml": "^2.8.2",
|