replicas-engine 0.1.702 → 0.1.703
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/{chunk-UNES5UCP.js → chunk-LPJ6WYD4.js} +2 -544
- package/dist/src/chunk-RVDZXLBY.js +287 -0
- package/dist/src/chunk-U3XQBTVU.js +551 -0
- package/dist/src/command-protection-hook.js +48 -0
- package/dist/src/deepseek-command-protection-plugin.js +23 -0
- package/dist/src/headless-agent.js +6 -4
- package/dist/src/index.js +386 -777
- package/package.json +3 -1
- package/scripts/deepseek/replicas.cordis.patch.yml +4 -0
- package/scripts/opencode-command-protection-plugin.ts +69 -0
package/dist/src/index.js
CHANGED
|
@@ -1,9 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
ENGINE_ENV,
|
|
4
|
+
IS_WARMING_MODE,
|
|
5
|
+
evaluateCommandProtection,
|
|
6
|
+
extractToolCommand,
|
|
7
|
+
isRecord as isRecord2,
|
|
8
|
+
monolithRequest,
|
|
9
|
+
monolithService,
|
|
10
|
+
reportCommandProtectionBlock,
|
|
11
|
+
setAgentCredentialSnapshot
|
|
12
|
+
} from "./chunk-RVDZXLBY.js";
|
|
13
|
+
import {
|
|
4
14
|
ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
|
|
5
|
-
AGENT,
|
|
6
15
|
AGENT_MESSAGE_DELTA_METHOD,
|
|
16
|
+
AppServerProcess,
|
|
17
|
+
COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
|
|
18
|
+
FILE_CHANGE_OUTPUT_DELTA_METHOD,
|
|
19
|
+
ITEM_COMPLETED_METHOD,
|
|
20
|
+
ITEM_STARTED_METHOD,
|
|
21
|
+
MODEL_REROUTED_METHOD,
|
|
22
|
+
REASONING_SUMMARY_PART_ADDED_METHOD,
|
|
23
|
+
REASONING_SUMMARY_TEXT_DELTA_METHOD,
|
|
24
|
+
REASONING_TEXT_DELTA_METHOD,
|
|
25
|
+
SUBPROCESS_MAX_BUFFER,
|
|
26
|
+
THREAD_COMPACTED_METHOD,
|
|
27
|
+
THREAD_GOAL_CLEARED_METHOD,
|
|
28
|
+
THREAD_GOAL_UPDATED_METHOD,
|
|
29
|
+
THREAD_TOKEN_USAGE_UPDATED_METHOD,
|
|
30
|
+
TURN_COMPLETED_METHOD,
|
|
31
|
+
TURN_PLAN_UPDATED_METHOD,
|
|
32
|
+
TURN_STARTED_METHOD,
|
|
33
|
+
buildCodexAgentEnv,
|
|
34
|
+
dispatchAspNotification,
|
|
35
|
+
execAsync,
|
|
36
|
+
execFileAsync,
|
|
37
|
+
putPresignedFile,
|
|
38
|
+
recoverCompletedTurn
|
|
39
|
+
} from "./chunk-U3XQBTVU.js";
|
|
40
|
+
import {
|
|
41
|
+
ACCEPTED_USER_MESSAGE_SOURCE,
|
|
42
|
+
AGENT,
|
|
7
43
|
AGENT_MODELS,
|
|
8
44
|
AI_GATEWAY_BASE_URL,
|
|
9
45
|
ASTER_BASE_URL,
|
|
@@ -12,7 +48,6 @@ import {
|
|
|
12
48
|
ASTER_PROVIDER,
|
|
13
49
|
AUTH_FALLBACK_EVENT_TYPE,
|
|
14
50
|
AUTH_RETRY_STATUS_EVENT_TYPE,
|
|
15
|
-
AppServerProcess,
|
|
16
51
|
CHAT_GOAL_EVENT_TYPE,
|
|
17
52
|
CHAT_INTERRUPTED_EVENT_TYPE,
|
|
18
53
|
CLAUDE_AUTH_ENV_KEYS,
|
|
@@ -32,7 +67,6 @@ import {
|
|
|
32
67
|
CODEX_AUTH_ENV_KEYS,
|
|
33
68
|
CODEX_AUTH_ENV_KEYS_BY_METHOD,
|
|
34
69
|
CODEX_QUOTA_STATUS_EVENT_TYPE,
|
|
35
|
-
COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
|
|
36
70
|
COMPACTION_STATUS_EVENT_TYPE,
|
|
37
71
|
CONTEXT_USAGE_EVENT_TYPE,
|
|
38
72
|
CURSOR_CONNECTION_STATUS_EVENT_TYPE,
|
|
@@ -52,14 +86,11 @@ import {
|
|
|
52
86
|
ENGINE_HEALTH_MAX_WAIT_MS,
|
|
53
87
|
ENGINE_HEALTH_WAIT_HEADER,
|
|
54
88
|
ENGINE_HEALTH_WAIT_QUERY_PARAM,
|
|
55
|
-
FILE_CHANGE_OUTPUT_DELTA_METHOD,
|
|
56
89
|
GITHUB_CREDENTIAL_REFRESH_PATH,
|
|
57
90
|
GITLAB_CREDENTIAL_REFRESH_PATH,
|
|
58
91
|
GIT_CREDENTIAL_HELPER_FILENAME,
|
|
59
92
|
HOOK_EXEC_MAX_BUFFER_BYTES,
|
|
60
93
|
IMAGE_MEDIA_TYPES,
|
|
61
|
-
ITEM_COMPLETED_METHOD,
|
|
62
|
-
ITEM_STARTED_METHOD,
|
|
63
94
|
InFlightMap,
|
|
64
95
|
MAX_CANVAS_FILE_BYTES,
|
|
65
96
|
MAX_CANVAS_PREVIEW_BYTES,
|
|
@@ -68,34 +99,19 @@ import {
|
|
|
68
99
|
MEMORY_ROOT,
|
|
69
100
|
MEMORY_SUMMARY_FILENAME,
|
|
70
101
|
MERGED_MESSAGE_SEPARATOR,
|
|
71
|
-
MODEL_REROUTED_METHOD,
|
|
72
|
-
REASONING_SUMMARY_PART_ADDED_METHOD,
|
|
73
|
-
REASONING_SUMMARY_TEXT_DELTA_METHOD,
|
|
74
|
-
REASONING_TEXT_DELTA_METHOD,
|
|
75
102
|
REPLICAS_CONFIG_FILENAMES,
|
|
76
|
-
REPLICAS_RUNTIME_ENV_ALIASES,
|
|
77
103
|
SANDBOX_PATHS,
|
|
78
|
-
SUBPROCESS_MAX_BUFFER,
|
|
79
|
-
THREAD_COMPACTED_METHOD,
|
|
80
|
-
THREAD_GOAL_CLEARED_METHOD,
|
|
81
|
-
THREAD_GOAL_UPDATED_METHOD,
|
|
82
|
-
THREAD_TOKEN_USAGE_UPDATED_METHOD,
|
|
83
104
|
TIMEOUT,
|
|
84
105
|
TOKEN_USAGE_PATH,
|
|
85
|
-
TURN_COMPLETED_METHOD,
|
|
86
|
-
TURN_PLAN_UPDATED_METHOD,
|
|
87
|
-
TURN_STARTED_METHOD,
|
|
88
106
|
TaskAccumulator,
|
|
89
107
|
USER_MESSAGE_ID_PAYLOAD_KEY,
|
|
90
108
|
VALID_AGENT_PROVIDERS,
|
|
91
109
|
VALID_RELAY_SUBAGENT_PROVIDERS,
|
|
92
110
|
VALID_THINKING_LEVELS,
|
|
93
|
-
agentCredentialSnapshotSchema,
|
|
94
111
|
applyCodexAspTranscriptDelta,
|
|
95
112
|
areSameUserMessageEvents,
|
|
96
113
|
areUserMessagesWithinMatchWindow,
|
|
97
114
|
autoCompactThresholdField,
|
|
98
|
-
buildCodexAgentEnv,
|
|
99
115
|
buildCodexTokenUsageContextUsagePayload,
|
|
100
116
|
buildGitCredentialHelperScript,
|
|
101
117
|
buildHookOutputPreview,
|
|
@@ -122,16 +138,10 @@ import {
|
|
|
122
138
|
decodePathSegments,
|
|
123
139
|
detectAgentQuotaLimit,
|
|
124
140
|
detectLanguageByPath,
|
|
125
|
-
dispatchAspNotification,
|
|
126
|
-
execAsync,
|
|
127
|
-
execFileAsync,
|
|
128
|
-
extractCommandProtectionCommandText,
|
|
129
141
|
extractErrorText,
|
|
130
142
|
extractToolResultText,
|
|
131
143
|
fetchAiGatewayModels,
|
|
132
144
|
fetchModelsDevCatalog,
|
|
133
|
-
findGitCommitSignals,
|
|
134
|
-
findPrMergeSignals,
|
|
135
145
|
getChatHistoryPageSenders,
|
|
136
146
|
getChatHistoryPageWindow,
|
|
137
147
|
getChatTranscriptHistorySize,
|
|
@@ -187,14 +197,10 @@ import {
|
|
|
187
197
|
parseGitRemote,
|
|
188
198
|
parseLatestCodexAspTranscriptTurn,
|
|
189
199
|
parseMcpToolName,
|
|
190
|
-
parsePosixEnvFile,
|
|
191
200
|
parseReplicasConfigString,
|
|
192
201
|
percentage,
|
|
193
|
-
putPresignedFile,
|
|
194
202
|
raceWithTimeout,
|
|
195
203
|
readJsonlPage,
|
|
196
|
-
readReplicasRuntimeEnv,
|
|
197
|
-
recoverCompletedTurn,
|
|
198
204
|
renumberCodexAspTranscriptItems,
|
|
199
205
|
resolveWarmHookConfig,
|
|
200
206
|
sanitizeCanvasFilename,
|
|
@@ -202,164 +208,18 @@ import {
|
|
|
202
208
|
serializeCanvasContentResponse,
|
|
203
209
|
shellQuotePosix,
|
|
204
210
|
stripAgentDiagnosticErrors
|
|
205
|
-
} from "./chunk-
|
|
211
|
+
} from "./chunk-LPJ6WYD4.js";
|
|
206
212
|
|
|
207
213
|
// src/index.ts
|
|
208
214
|
import { serve } from "@hono/node-server";
|
|
209
215
|
import { Hono as Hono2 } from "hono";
|
|
210
|
-
import { existsSync as
|
|
216
|
+
import { existsSync as existsSync13 } from "fs";
|
|
211
217
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
212
218
|
import { connect } from "net";
|
|
213
219
|
|
|
214
220
|
// src/managers/github-token-manager.ts
|
|
215
221
|
import path from "path";
|
|
216
222
|
|
|
217
|
-
// src/engine-env.ts
|
|
218
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
219
|
-
import { homedir as homedir2 } from "os";
|
|
220
|
-
import { join as join2 } from "path";
|
|
221
|
-
|
|
222
|
-
// src/runtime-env-loader.ts
|
|
223
|
-
import { readFileSync } from "fs";
|
|
224
|
-
import { homedir } from "os";
|
|
225
|
-
import { join } from "path";
|
|
226
|
-
function loadRuntimeEnvFile() {
|
|
227
|
-
let content;
|
|
228
|
-
try {
|
|
229
|
-
content = readFileSync(join(homedir(), ".replicas", "runtime-env.sh"), "utf-8");
|
|
230
|
-
} catch {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
for (const [key, value] of Object.entries(parsePosixEnvFile(content))) {
|
|
234
|
-
process.env[key] = value;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// src/utils/type-guards.ts
|
|
239
|
-
function isRecord2(value) {
|
|
240
|
-
return typeof value === "object" && value !== null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// src/engine-env.ts
|
|
244
|
-
var SANDBOX_IMAGE_VERSION_FILE = "/usr/local/lib/replicas-sandbox-image-version";
|
|
245
|
-
function readEnv(name) {
|
|
246
|
-
const value = process.env[name]?.trim();
|
|
247
|
-
return value ? value : void 0;
|
|
248
|
-
}
|
|
249
|
-
function readSandboxImageVersion() {
|
|
250
|
-
const environmentVersion = readEnv("REPLICAS_SANDBOX_IMAGE_VERSION");
|
|
251
|
-
if (environmentVersion) return environmentVersion;
|
|
252
|
-
try {
|
|
253
|
-
return readFileSync2(SANDBOX_IMAGE_VERSION_FILE, "utf8").trim() || "development";
|
|
254
|
-
} catch {
|
|
255
|
-
return "development";
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
function parsePort(value) {
|
|
259
|
-
if (!value) {
|
|
260
|
-
return 3737;
|
|
261
|
-
}
|
|
262
|
-
const parsed = Number(value);
|
|
263
|
-
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
264
|
-
throw new Error("Invalid engine environment: REPLICAS_ENGINE_PORT must be a positive integer");
|
|
265
|
-
}
|
|
266
|
-
return parsed;
|
|
267
|
-
}
|
|
268
|
-
function requireDefined(value, name) {
|
|
269
|
-
if (value === void 0 || value === null) {
|
|
270
|
-
throw new Error(`Invalid engine environment: ${name} is required`);
|
|
271
|
-
}
|
|
272
|
-
return value;
|
|
273
|
-
}
|
|
274
|
-
function requireValidURL(value, name) {
|
|
275
|
-
try {
|
|
276
|
-
new URL(value);
|
|
277
|
-
return value;
|
|
278
|
-
} catch {
|
|
279
|
-
throw new Error(`Invalid engine environment: ${name} must be a valid URL`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
function parseClaudeAuthMethod(value) {
|
|
283
|
-
if (value === "oauth" || value === "api_key" || value === "bedrock" || value === "foundry") {
|
|
284
|
-
return value;
|
|
285
|
-
}
|
|
286
|
-
return void 0;
|
|
287
|
-
}
|
|
288
|
-
function parseCodexAuthMethod(value) {
|
|
289
|
-
if (value === "oauth" || value === "api_key" || value === "foundry") {
|
|
290
|
-
return value;
|
|
291
|
-
}
|
|
292
|
-
return void 0;
|
|
293
|
-
}
|
|
294
|
-
function parseAgentCredentialSnapshots(value) {
|
|
295
|
-
if (!value) return {};
|
|
296
|
-
try {
|
|
297
|
-
const parsed = JSON.parse(value);
|
|
298
|
-
if (!isRecord2(parsed)) return {};
|
|
299
|
-
const snapshots = {};
|
|
300
|
-
for (const [provider, snapshot] of Object.entries(parsed)) {
|
|
301
|
-
if (!isValidAgentProvider(provider)) continue;
|
|
302
|
-
const parsedSnapshot = agentCredentialSnapshotSchema.safeParse(snapshot);
|
|
303
|
-
if (parsedSnapshot.success) snapshots[provider] = parsedSnapshot.data;
|
|
304
|
-
}
|
|
305
|
-
return snapshots;
|
|
306
|
-
} catch {
|
|
307
|
-
return {};
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
var IS_WARMING_MODE = process.argv.includes("--warming");
|
|
311
|
-
function loadEngineEnv() {
|
|
312
|
-
loadRuntimeEnvFile();
|
|
313
|
-
const HOME_DIR = homedir2();
|
|
314
|
-
const env = {
|
|
315
|
-
// Defined: always available
|
|
316
|
-
REPLICAS_ENGINE_SECRET: requireDefined(readEnv("REPLICAS_ENGINE_SECRET"), "REPLICAS_ENGINE_SECRET"),
|
|
317
|
-
REPLICAS_ENGINE_PORT: parsePort(readEnv("REPLICAS_ENGINE_PORT")),
|
|
318
|
-
REPLICAS_MONOLITH_URL: requireValidURL(
|
|
319
|
-
requireDefined(readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.monolithUrl), "REPLICAS_MONOLITH_URL"),
|
|
320
|
-
"REPLICAS_MONOLITH_URL"
|
|
321
|
-
),
|
|
322
|
-
HOME_DIR,
|
|
323
|
-
WORKSPACE_ROOT: join2(HOME_DIR, "workspaces"),
|
|
324
|
-
REPLICAS_SANDBOX_IMAGE_VERSION: readSandboxImageVersion(),
|
|
325
|
-
// Runtime: may not be set during warming
|
|
326
|
-
REPLICAS_WORKSPACE_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.workspaceId),
|
|
327
|
-
REPLICAS_LINEAR_SESSION_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearSessionId),
|
|
328
|
-
REPLICAS_LINEAR_ACCESS_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearAccessToken),
|
|
329
|
-
REPLICAS_SLACK_BOT_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackBotToken),
|
|
330
|
-
REPLICAS_SLACK_CHANNEL_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackChannelId),
|
|
331
|
-
REPLICAS_SLACK_THREAD_TS: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackThreadTs),
|
|
332
|
-
ANTHROPIC_API_KEY: readEnv("ANTHROPIC_API_KEY"),
|
|
333
|
-
OPENAI_API_KEY: readEnv("OPENAI_API_KEY"),
|
|
334
|
-
CURSOR_API_KEY: readEnv("CURSOR_API_KEY"),
|
|
335
|
-
AI_GATEWAY_API_KEY: readEnv("AI_GATEWAY_API_KEY"),
|
|
336
|
-
CLAUDE_CODE_USE_BEDROCK: readEnv("CLAUDE_CODE_USE_BEDROCK"),
|
|
337
|
-
AWS_ACCESS_KEY_ID: readEnv("AWS_ACCESS_KEY_ID"),
|
|
338
|
-
AWS_SECRET_ACCESS_KEY: readEnv("AWS_SECRET_ACCESS_KEY"),
|
|
339
|
-
AWS_REGION: readEnv("AWS_REGION"),
|
|
340
|
-
ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION: readEnv("ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION"),
|
|
341
|
-
REPLICAS_CLAUDE_AUTH_METHOD: parseClaudeAuthMethod(readEnv("REPLICAS_CLAUDE_AUTH_METHOD")),
|
|
342
|
-
REPLICAS_CODEX_AUTH_METHOD: parseCodexAuthMethod(readEnv("REPLICAS_CODEX_AUTH_METHOD")),
|
|
343
|
-
REPLICAS_AGENT_CREDENTIALS: parseAgentCredentialSnapshots(readEnv("REPLICAS_AGENT_CREDENTIALS")),
|
|
344
|
-
REPLICAS_ENV_SYSTEM_PROMPT: readEnv("REPLICAS_ENV_SYSTEM_PROMPT"),
|
|
345
|
-
REPLICAS_ENV_START_HOOK: readEnv("REPLICAS_ENV_START_HOOK"),
|
|
346
|
-
REPLICAS_DISABLE_AUTO_START_HOOKS: readEnv("REPLICAS_DISABLE_AUTO_START_HOOKS")?.toLowerCase() === "true",
|
|
347
|
-
REPLICAS_ENGINE_DEFER_INITIALIZATION: readEnv("REPLICAS_ENGINE_DEFER_INITIALIZATION")?.toLowerCase() === "true"
|
|
348
|
-
};
|
|
349
|
-
if (!IS_WARMING_MODE && !env.REPLICAS_WORKSPACE_ID) {
|
|
350
|
-
console.error("REPLICAS_WORKSPACE_ID is not set \u2014 this is required in normal (non-warming) mode");
|
|
351
|
-
}
|
|
352
|
-
return env;
|
|
353
|
-
}
|
|
354
|
-
var ENGINE_ENV = loadEngineEnv();
|
|
355
|
-
function setAgentCredentialSnapshot(provider, snapshot) {
|
|
356
|
-
ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS = {
|
|
357
|
-
...ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS,
|
|
358
|
-
[provider]: snapshot
|
|
359
|
-
};
|
|
360
|
-
process.env.REPLICAS_AGENT_CREDENTIALS = JSON.stringify(ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
223
|
// src/services/credential-fallbacks.ts
|
|
364
224
|
var fallbacksByAgent = /* @__PURE__ */ new Map();
|
|
365
225
|
var exhaustedByAgent = /* @__PURE__ */ new Map();
|
|
@@ -519,69 +379,10 @@ var BaseRefreshManager = class {
|
|
|
519
379
|
}
|
|
520
380
|
};
|
|
521
381
|
|
|
522
|
-
// src/services/monolith-service.ts
|
|
523
|
-
async function monolithRequest(path6, init = {}) {
|
|
524
|
-
if (!ENGINE_ENV.REPLICAS_WORKSPACE_ID) {
|
|
525
|
-
throw new Error("REPLICAS_WORKSPACE_ID is not set; cannot call monolith");
|
|
526
|
-
}
|
|
527
|
-
const headers = {
|
|
528
|
-
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
529
|
-
"X-Workspace-Id": ENGINE_ENV.REPLICAS_WORKSPACE_ID
|
|
530
|
-
};
|
|
531
|
-
if (!(init.body instanceof FormData)) headers["Content-Type"] = "application/json";
|
|
532
|
-
return fetch(`${ENGINE_ENV.REPLICAS_MONOLITH_URL}${path6}`, {
|
|
533
|
-
method: init.method ?? "POST",
|
|
534
|
-
headers,
|
|
535
|
-
body: init.body === void 0 ? void 0 : init.body instanceof FormData ? init.body : JSON.stringify(init.body),
|
|
536
|
-
signal: init.signal
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
var MonolithService = class {
|
|
540
|
-
async getRelaySubagentProviders() {
|
|
541
|
-
try {
|
|
542
|
-
const response = await monolithRequest("/v1/engine/relay-subagent-providers", { method: "GET" });
|
|
543
|
-
if (!response.ok) {
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
const body = await response.json();
|
|
547
|
-
return isRecord(body) && Array.isArray(body.providers) && body.providers.every((provider) => typeof provider === "string" && isValidAgentProvider(provider)) ? body.providers : null;
|
|
548
|
-
} catch {
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
async getOpenRouterModels() {
|
|
553
|
-
try {
|
|
554
|
-
const response = await monolithRequest("/v1/engine/openrouter-models", { method: "GET" });
|
|
555
|
-
if (!response.ok) {
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
const body = await response.json();
|
|
559
|
-
return isRecord(body) && Array.isArray(body.models) && body.models.every((model) => typeof model === "string") ? body.models : null;
|
|
560
|
-
} catch {
|
|
561
|
-
return null;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
async sendEvent(event) {
|
|
565
|
-
if (!ENGINE_ENV.REPLICAS_WORKSPACE_ID) {
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
try {
|
|
569
|
-
const response = await monolithRequest("/v1/engine/webhook", { body: event });
|
|
570
|
-
if (!response.ok) {
|
|
571
|
-
const errorText = await response.text();
|
|
572
|
-
console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
|
|
573
|
-
}
|
|
574
|
-
} catch (error) {
|
|
575
|
-
console.error("[MonolithService] Failed to send event:", error);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
};
|
|
579
|
-
var monolithService = new MonolithService();
|
|
580
|
-
|
|
581
382
|
// src/utils/file.ts
|
|
582
383
|
import { randomUUID } from "crypto";
|
|
583
384
|
import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
|
|
584
|
-
import { dirname, join
|
|
385
|
+
import { dirname, join } from "path";
|
|
585
386
|
|
|
586
387
|
// src/utils/async-lock.ts
|
|
587
388
|
var AsyncLock = class {
|
|
@@ -614,8 +415,8 @@ async function writeSecureCredentialFile(filePath, content, options) {
|
|
|
614
415
|
await atomicWriteFile(filePath, content, { mode: 384 });
|
|
615
416
|
}
|
|
616
417
|
async function configureManagedGitCredentialHelper(homeDir) {
|
|
617
|
-
const credentialsPath =
|
|
618
|
-
const helperPath =
|
|
418
|
+
const credentialsPath = join(homeDir, ".git-credentials");
|
|
419
|
+
const helperPath = join(homeDir, GIT_CREDENTIAL_HELPER_FILENAME);
|
|
619
420
|
await atomicWriteFile(helperPath, buildGitCredentialHelperScript(credentialsPath), { mode: 448 });
|
|
620
421
|
await execFileAsync("git", ["config", "--global", "credential.helper", helperPath], {
|
|
621
422
|
env: { ...process.env, HOME: homeDir }
|
|
@@ -650,15 +451,15 @@ function removeCredentialFileLines(filePath, shouldRemove) {
|
|
|
650
451
|
// src/git/service.ts
|
|
651
452
|
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
652
453
|
import { spawn } from "child_process";
|
|
653
|
-
import { join as
|
|
454
|
+
import { join as join4 } from "path";
|
|
654
455
|
|
|
655
456
|
// src/utils/state.ts
|
|
656
457
|
import { readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
657
458
|
import { existsSync } from "fs";
|
|
658
|
-
import { join as
|
|
659
|
-
import { homedir
|
|
660
|
-
var STATE_DIR =
|
|
661
|
-
var STATE_FILE =
|
|
459
|
+
import { join as join2 } from "path";
|
|
460
|
+
import { homedir } from "os";
|
|
461
|
+
var STATE_DIR = join2(homedir(), ".replicas");
|
|
462
|
+
var STATE_FILE = join2(STATE_DIR, "engine-state.json");
|
|
662
463
|
var DEFAULT_STATE = {
|
|
663
464
|
repos: {}
|
|
664
465
|
};
|
|
@@ -755,8 +556,8 @@ async function saveRepoState(repoName, state, fallbackState) {
|
|
|
755
556
|
}
|
|
756
557
|
|
|
757
558
|
// src/git/commands.ts
|
|
758
|
-
import { readFileSync
|
|
759
|
-
import { join as
|
|
559
|
+
import { readFileSync } from "fs";
|
|
560
|
+
import { join as join3 } from "path";
|
|
760
561
|
async function runGitCommand(args, cwd, options = {}) {
|
|
761
562
|
const { stdout } = await execFileAsync("git", args, {
|
|
762
563
|
cwd,
|
|
@@ -767,7 +568,7 @@ async function runGitCommand(args, cwd, options = {}) {
|
|
|
767
568
|
}
|
|
768
569
|
function readRepoHeadBranch(repoPath) {
|
|
769
570
|
try {
|
|
770
|
-
const contents =
|
|
571
|
+
const contents = readFileSync(join3(repoPath, ".git", "HEAD"), "utf-8").trim();
|
|
771
572
|
const match = contents.match(/^ref:\s+refs\/heads\/(.+)$/);
|
|
772
573
|
return match ? match[1] : null;
|
|
773
574
|
} catch {
|
|
@@ -829,13 +630,13 @@ var GitService = class {
|
|
|
829
630
|
const repos = [];
|
|
830
631
|
let complete = true;
|
|
831
632
|
for (const entry of entries) {
|
|
832
|
-
const fullPath =
|
|
633
|
+
const fullPath = join4(root, entry);
|
|
833
634
|
try {
|
|
834
635
|
const entryStat = await stat(fullPath);
|
|
835
636
|
if (!entryStat.isDirectory()) {
|
|
836
637
|
continue;
|
|
837
638
|
}
|
|
838
|
-
const hasGit = Boolean(await this.safeStat(
|
|
639
|
+
const hasGit = Boolean(await this.safeStat(join4(fullPath, ".git")));
|
|
839
640
|
if (!hasGit) {
|
|
840
641
|
continue;
|
|
841
642
|
}
|
|
@@ -1026,7 +827,7 @@ var GitService = class {
|
|
|
1026
827
|
let total = 0;
|
|
1027
828
|
for (const path6 of paths) {
|
|
1028
829
|
try {
|
|
1029
|
-
const contents = await readFile3(
|
|
830
|
+
const contents = await readFile3(join4(repoPath, path6));
|
|
1030
831
|
if (contents.length === 0 || contents.includes(0)) {
|
|
1031
832
|
continue;
|
|
1032
833
|
}
|
|
@@ -1214,7 +1015,7 @@ var GitService = class {
|
|
|
1214
1015
|
}
|
|
1215
1016
|
async getGitLabAccessToken(host) {
|
|
1216
1017
|
try {
|
|
1217
|
-
const credentials = await readFile3(
|
|
1018
|
+
const credentials = await readFile3(join4(ENGINE_ENV.HOME_DIR, ".git-credentials"), "utf-8");
|
|
1218
1019
|
for (const line of credentials.split("\n")) {
|
|
1219
1020
|
const trimmed = line.trim();
|
|
1220
1021
|
if (!trimmed) continue;
|
|
@@ -1761,8 +1562,8 @@ var infisicalTokenManager = new InfisicalTokenManager();
|
|
|
1761
1562
|
|
|
1762
1563
|
// src/utils/logger.ts
|
|
1763
1564
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
1764
|
-
import { homedir as
|
|
1765
|
-
import { join as
|
|
1565
|
+
import { homedir as homedir2 } from "os";
|
|
1566
|
+
import { join as join5 } from "path";
|
|
1766
1567
|
import { format } from "util";
|
|
1767
1568
|
import { randomBytes } from "crypto";
|
|
1768
1569
|
|
|
@@ -1837,7 +1638,7 @@ var StreamWriter = class {
|
|
|
1837
1638
|
};
|
|
1838
1639
|
|
|
1839
1640
|
// src/utils/logger.ts
|
|
1840
|
-
var LOG_DIR =
|
|
1641
|
+
var LOG_DIR = join5(homedir2(), ".replicas", "logs");
|
|
1841
1642
|
var EngineLogger = class {
|
|
1842
1643
|
_sessionId = null;
|
|
1843
1644
|
patched = false;
|
|
@@ -1848,7 +1649,7 @@ var EngineLogger = class {
|
|
|
1848
1649
|
async initialize() {
|
|
1849
1650
|
await mkdir3(LOG_DIR, { recursive: true });
|
|
1850
1651
|
this._sessionId = this.createSessionId();
|
|
1851
|
-
const logPath =
|
|
1652
|
+
const logPath = join5(LOG_DIR, `${this._sessionId}.log`);
|
|
1852
1653
|
await writeFile2(logPath, `=== Replicas Engine Session ${this._sessionId} ===
|
|
1853
1654
|
`, "utf-8");
|
|
1854
1655
|
this.writer.open(logPath);
|
|
@@ -1895,32 +1696,32 @@ var engineLogger = new EngineLogger();
|
|
|
1895
1696
|
// src/services/replicas-config-service.ts
|
|
1896
1697
|
import { readFile as readFile6, appendFile, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
|
|
1897
1698
|
import { existsSync as existsSync3 } from "fs";
|
|
1898
|
-
import { join as
|
|
1899
|
-
import { homedir as
|
|
1699
|
+
import { join as join9 } from "path";
|
|
1700
|
+
import { homedir as homedir6 } from "os";
|
|
1900
1701
|
import { spawn as spawn2 } from "child_process";
|
|
1901
1702
|
|
|
1902
1703
|
// src/services/environment-details-service.ts
|
|
1903
1704
|
import { mkdir as mkdir4, readFile as readFile4 } from "fs/promises";
|
|
1904
1705
|
import { existsSync as existsSync2 } from "fs";
|
|
1905
|
-
import { homedir as
|
|
1906
|
-
import { join as
|
|
1706
|
+
import { homedir as homedir4 } from "os";
|
|
1707
|
+
import { join as join7 } from "path";
|
|
1907
1708
|
|
|
1908
1709
|
// src/managers/agent-auth-paths.ts
|
|
1909
|
-
import { homedir as
|
|
1910
|
-
import { join as
|
|
1911
|
-
var OPENCODE_AUTH_PATH =
|
|
1912
|
-
var PI_AUTH_PATH =
|
|
1913
|
-
var DEEPSEEK_AUTH_PATH =
|
|
1710
|
+
import { homedir as homedir3 } from "os";
|
|
1711
|
+
import { join as join6 } from "path";
|
|
1712
|
+
var OPENCODE_AUTH_PATH = join6(homedir3(), ".local", "share", "opencode", "auth.json");
|
|
1713
|
+
var PI_AUTH_PATH = join6(homedir3(), ".pi", "agent", "auth.json");
|
|
1714
|
+
var DEEPSEEK_AUTH_PATH = join6(homedir3(), ".replicas", "deepseek-auth.json");
|
|
1914
1715
|
|
|
1915
1716
|
// src/managers/deepseek-auth.ts
|
|
1916
|
-
import { readFileSync as
|
|
1717
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1917
1718
|
import { z } from "zod";
|
|
1918
1719
|
var authSchema = z.object({
|
|
1919
1720
|
openrouter: z.object({ type: z.literal("api"), key: z.string().min(1) })
|
|
1920
1721
|
});
|
|
1921
1722
|
function getDeepseekApiKey() {
|
|
1922
1723
|
try {
|
|
1923
|
-
const parsed = authSchema.safeParse(JSON.parse(
|
|
1724
|
+
const parsed = authSchema.safeParse(JSON.parse(readFileSync2(DEEPSEEK_AUTH_PATH, "utf8")));
|
|
1924
1725
|
return parsed.success ? parsed.data.openrouter.key : null;
|
|
1925
1726
|
} catch {
|
|
1926
1727
|
return null;
|
|
@@ -1933,11 +1734,11 @@ function detectCodexAuthMethod() {
|
|
|
1933
1734
|
}
|
|
1934
1735
|
|
|
1935
1736
|
// src/services/environment-details-service.ts
|
|
1936
|
-
var REPLICAS_DIR =
|
|
1937
|
-
var DETAILS_FILE =
|
|
1938
|
-
var CLAUDE_CREDENTIALS_PATH =
|
|
1939
|
-
var GH_HOSTS_PATH =
|
|
1940
|
-
var GIT_CREDENTIALS_PATH =
|
|
1737
|
+
var REPLICAS_DIR = join7(homedir4(), ".replicas");
|
|
1738
|
+
var DETAILS_FILE = join7(REPLICAS_DIR, "environment-details.json");
|
|
1739
|
+
var CLAUDE_CREDENTIALS_PATH = join7(homedir4(), ".claude", ".credentials.json");
|
|
1740
|
+
var GH_HOSTS_PATH = join7(homedir4(), ".config", "gh", "hosts.yml");
|
|
1741
|
+
var GIT_CREDENTIALS_PATH = join7(homedir4(), ".git-credentials");
|
|
1941
1742
|
function detectClaudeAuthMethod() {
|
|
1942
1743
|
if (existsSync2(CLAUDE_CREDENTIALS_PATH)) {
|
|
1943
1744
|
return "oauth";
|
|
@@ -2149,8 +1950,8 @@ var environmentDetailsService = new EnvironmentDetailsService();
|
|
|
2149
1950
|
|
|
2150
1951
|
// src/services/start-hook-logs-service.ts
|
|
2151
1952
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
|
|
2152
|
-
import { homedir as
|
|
2153
|
-
import { join as
|
|
1953
|
+
import { homedir as homedir5 } from "os";
|
|
1954
|
+
import { join as join8 } from "path";
|
|
2154
1955
|
|
|
2155
1956
|
// src/services/hook-log-files.ts
|
|
2156
1957
|
import { createHash } from "crypto";
|
|
@@ -2162,7 +1963,7 @@ function repoHookLogFilename(repoName) {
|
|
|
2162
1963
|
}
|
|
2163
1964
|
|
|
2164
1965
|
// src/services/start-hook-logs-service.ts
|
|
2165
|
-
var LOGS_DIR =
|
|
1966
|
+
var LOGS_DIR = join8(homedir5(), ".replicas", "start-hook-logs");
|
|
2166
1967
|
function withPreview(stored) {
|
|
2167
1968
|
const preview = buildHookOutputPreview(stored.output);
|
|
2168
1969
|
return { ...stored, ...preview };
|
|
@@ -2200,7 +2001,7 @@ var StartHookLogsService = class {
|
|
|
2200
2001
|
await this.ensureDir();
|
|
2201
2002
|
const log = { hookType, hookName, repoName: hookName, ...entry };
|
|
2202
2003
|
const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
2203
|
-
await writeFile3(
|
|
2004
|
+
await writeFile3(join8(LOGS_DIR, filename), `${JSON.stringify(log, null, 2)}
|
|
2204
2005
|
`, "utf-8");
|
|
2205
2006
|
}
|
|
2206
2007
|
async saveEnvironmentLog(entry) {
|
|
@@ -2225,7 +2026,7 @@ var StartHookLogsService = class {
|
|
|
2225
2026
|
continue;
|
|
2226
2027
|
}
|
|
2227
2028
|
try {
|
|
2228
|
-
const raw = await readFile5(
|
|
2029
|
+
const raw = await readFile5(join8(LOGS_DIR, file), "utf-8");
|
|
2229
2030
|
const stored = normalizeStored(JSON.parse(raw));
|
|
2230
2031
|
if (stored) {
|
|
2231
2032
|
logs.push(withPreview(stored));
|
|
@@ -2244,7 +2045,7 @@ var StartHookLogsService = class {
|
|
|
2244
2045
|
async getFullOutput(hookType, hookName) {
|
|
2245
2046
|
const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
2246
2047
|
try {
|
|
2247
|
-
const raw = await readFile5(
|
|
2048
|
+
const raw = await readFile5(join8(LOGS_DIR, filename), "utf-8");
|
|
2248
2049
|
const stored = normalizeStored(JSON.parse(raw));
|
|
2249
2050
|
if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
2250
2051
|
return null;
|
|
@@ -2261,7 +2062,7 @@ var StartHookLogsService = class {
|
|
|
2261
2062
|
var startHookLogsService = new StartHookLogsService();
|
|
2262
2063
|
|
|
2263
2064
|
// src/services/replicas-config-service.ts
|
|
2264
|
-
var START_HOOKS_LOG =
|
|
2065
|
+
var START_HOOKS_LOG = join9(homedir6(), ".replicas", "startHooks.log");
|
|
2265
2066
|
var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
|
|
2266
2067
|
Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
|
|
2267
2068
|
These hooks are currently executing in the background. You can:
|
|
@@ -2272,7 +2073,7 @@ The start hooks may install dependencies, build projects, or perform other setup
|
|
|
2272
2073
|
If your task depends on setup being complete, check the log file before proceeding.`;
|
|
2273
2074
|
async function readReplicasConfigFromDir(dirPath) {
|
|
2274
2075
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
2275
|
-
const configPath =
|
|
2076
|
+
const configPath = join9(dirPath, filename);
|
|
2276
2077
|
if (!existsSync3(configPath)) {
|
|
2277
2078
|
continue;
|
|
2278
2079
|
}
|
|
@@ -2342,7 +2143,7 @@ var ReplicasConfigService = class {
|
|
|
2342
2143
|
const logLine = `[${timestamp}] ${message}
|
|
2343
2144
|
`;
|
|
2344
2145
|
try {
|
|
2345
|
-
await mkdir6(
|
|
2146
|
+
await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
|
|
2346
2147
|
await appendFile(START_HOOKS_LOG, logLine, "utf-8");
|
|
2347
2148
|
} catch (error) {
|
|
2348
2149
|
console.error("Failed to write to start hooks log:", error);
|
|
@@ -2354,7 +2155,7 @@ var ReplicasConfigService = class {
|
|
|
2354
2155
|
command: params.content,
|
|
2355
2156
|
label: "env start hook",
|
|
2356
2157
|
repoName: "environment",
|
|
2357
|
-
cwd:
|
|
2158
|
+
cwd: homedir6(),
|
|
2358
2159
|
timeout: params.timeoutMs ?? DEFAULT_START_HOOK_TIMEOUT_MS,
|
|
2359
2160
|
onOutputChunk: params.onOutputChunk
|
|
2360
2161
|
});
|
|
@@ -2477,7 +2278,7 @@ var ReplicasConfigService = class {
|
|
|
2477
2278
|
this.hooksCompleted = false;
|
|
2478
2279
|
this.hooksFailed = false;
|
|
2479
2280
|
try {
|
|
2480
|
-
await mkdir6(
|
|
2281
|
+
await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
|
|
2481
2282
|
await writeFile4(
|
|
2482
2283
|
START_HOOKS_LOG,
|
|
2483
2284
|
`=== Start Hooks Execution Log ===
|
|
@@ -2672,11 +2473,11 @@ var replicasConfigService = new ReplicasConfigService();
|
|
|
2672
2473
|
|
|
2673
2474
|
// src/services/event-service.ts
|
|
2674
2475
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
2675
|
-
import { homedir as
|
|
2676
|
-
import { join as
|
|
2476
|
+
import { homedir as homedir7 } from "os";
|
|
2477
|
+
import { join as join10 } from "path";
|
|
2677
2478
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2678
|
-
var ENGINE_DIR =
|
|
2679
|
-
var EVENTS_FILE =
|
|
2479
|
+
var ENGINE_DIR = join10(homedir7(), ".replicas", "engine");
|
|
2480
|
+
var EVENTS_FILE = join10(ENGINE_DIR, "events.jsonl");
|
|
2680
2481
|
var EventService = class {
|
|
2681
2482
|
subscribers = /* @__PURE__ */ new Map();
|
|
2682
2483
|
writer = new StreamWriter();
|
|
@@ -2710,9 +2511,9 @@ var eventService = new EventService();
|
|
|
2710
2511
|
import { mkdir as mkdir8, readFile as readFile7 } from "fs/promises";
|
|
2711
2512
|
import { existsSync as existsSync4 } from "fs";
|
|
2712
2513
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2713
|
-
import { homedir as
|
|
2714
|
-
import { dirname as dirname2, join as
|
|
2715
|
-
var PREVIEW_PORTS_FILE =
|
|
2514
|
+
import { homedir as homedir8 } from "os";
|
|
2515
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
2516
|
+
var PREVIEW_PORTS_FILE = join11(homedir8(), ".replicas", "preview-ports.json");
|
|
2716
2517
|
async function readPreviewsFile() {
|
|
2717
2518
|
try {
|
|
2718
2519
|
if (!existsSync4(PREVIEW_PORTS_FILE)) {
|
|
@@ -2822,9 +2623,9 @@ async function registerDesktopPreview() {
|
|
|
2822
2623
|
}
|
|
2823
2624
|
|
|
2824
2625
|
// src/services/chat/chat-service.ts
|
|
2825
|
-
import { existsSync as
|
|
2826
|
-
import { appendFile as appendFile4, copyFile, mkdir as
|
|
2827
|
-
import { join as
|
|
2626
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2627
|
+
import { appendFile as appendFile4, copyFile as copyFile2, mkdir as mkdir18, readFile as readFile19, rename as rename3, rm as rm3 } from "fs/promises";
|
|
2628
|
+
import { join as join28 } from "path";
|
|
2828
2629
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
2829
2630
|
|
|
2830
2631
|
// ../shared/src/workspace-chat.ts
|
|
@@ -2957,9 +2758,9 @@ import {
|
|
|
2957
2758
|
query
|
|
2958
2759
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
2959
2760
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2960
|
-
import { dirname as dirname4, join as
|
|
2761
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
2961
2762
|
import { mkdir as mkdir10 } from "fs/promises";
|
|
2962
|
-
import { homedir as
|
|
2763
|
+
import { homedir as homedir10 } from "os";
|
|
2963
2764
|
|
|
2964
2765
|
// src/utils/linear-converter.ts
|
|
2965
2766
|
function linearThoughtToResponse(thought) {
|
|
@@ -3344,8 +3145,8 @@ function extractPlanFromCodexAspNotification(notification) {
|
|
|
3344
3145
|
// src/utils/image-utils.ts
|
|
3345
3146
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
3346
3147
|
import { mkdir as mkdir9, unlink as unlink2, writeFile as writeFile5 } from "fs/promises";
|
|
3347
|
-
import { homedir as
|
|
3348
|
-
import { join as
|
|
3148
|
+
import { homedir as homedir9 } from "os";
|
|
3149
|
+
import { join as join12 } from "path";
|
|
3349
3150
|
function isImageMediaType(value) {
|
|
3350
3151
|
return IMAGE_MEDIA_TYPES.includes(value);
|
|
3351
3152
|
}
|
|
@@ -3445,14 +3246,14 @@ async function normalizeImages(images) {
|
|
|
3445
3246
|
}
|
|
3446
3247
|
return normalized;
|
|
3447
3248
|
}
|
|
3448
|
-
async function saveNormalizedImagesToTempFiles(images, tempImageDir =
|
|
3249
|
+
async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(homedir9(), ".replicas", "codex", "temp-images")) {
|
|
3449
3250
|
await mkdir9(tempImageDir, { recursive: true });
|
|
3450
3251
|
const tempPaths = [];
|
|
3451
3252
|
try {
|
|
3452
3253
|
for (const image of images) {
|
|
3453
3254
|
const ext = image.source.media_type.split("/")[1] || "png";
|
|
3454
3255
|
const filename = `img_${randomUUID4()}.${ext}`;
|
|
3455
|
-
const filepath =
|
|
3256
|
+
const filepath = join12(tempImageDir, filename);
|
|
3456
3257
|
await writeFile5(filepath, Buffer.from(image.source.data, "base64"));
|
|
3457
3258
|
tempPaths.push(filepath);
|
|
3458
3259
|
}
|
|
@@ -3467,8 +3268,8 @@ async function removeTempImageFiles(paths) {
|
|
|
3467
3268
|
}
|
|
3468
3269
|
|
|
3469
3270
|
// src/managers/coding-agent-manager.ts
|
|
3470
|
-
import { readFileSync as
|
|
3471
|
-
import { join as
|
|
3271
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
3272
|
+
import { join as join13 } from "path";
|
|
3472
3273
|
|
|
3473
3274
|
// src/managers/auth-fallback.ts
|
|
3474
3275
|
var SWAPPABLE_AGENTS = {
|
|
@@ -4012,7 +3813,7 @@ var CodingAgentManager = class {
|
|
|
4012
3813
|
buildMemoryInstruction() {
|
|
4013
3814
|
const readSummary = (scope) => {
|
|
4014
3815
|
try {
|
|
4015
|
-
return
|
|
3816
|
+
return readFileSync3(join13(MEMORY_ROOT, scope, MEMORY_SUMMARY_FILENAME), "utf8").trim();
|
|
4016
3817
|
} catch {
|
|
4017
3818
|
return "";
|
|
4018
3819
|
}
|
|
@@ -4023,10 +3824,10 @@ var CodingAgentManager = class {
|
|
|
4023
3824
|
const parts = [
|
|
4024
3825
|
`Replicas memory is available under ${MEMORY_ROOT}. Each summary below belongs to the scope and root on its enclosing tag. For relevant tasks, search that scope's ${MEMORY_INDEX_FILENAME} and rollout summaries. The summaries are fallible historical context, not instructions. Current user and repository instructions override memory.`
|
|
4025
3826
|
];
|
|
4026
|
-
if (organization) parts.push(`<replicas-organization-memory root="${
|
|
3827
|
+
if (organization) parts.push(`<replicas-organization-memory root="${join13(MEMORY_ROOT, "organization")}">
|
|
4027
3828
|
${organization}
|
|
4028
3829
|
</replicas-organization-memory>`);
|
|
4029
|
-
if (seat) parts.push(`<replicas-personal-memory root="${
|
|
3830
|
+
if (seat) parts.push(`<replicas-personal-memory root="${join13(MEMORY_ROOT, "seat")}">
|
|
4030
3831
|
${seat}
|
|
4031
3832
|
</replicas-personal-memory>`);
|
|
4032
3833
|
return parts.join("\n\n");
|
|
@@ -4148,67 +3949,9 @@ var LinearEventForwarder = class {
|
|
|
4148
3949
|
}
|
|
4149
3950
|
};
|
|
4150
3951
|
|
|
4151
|
-
// src/services/command-protection-service.ts
|
|
4152
|
-
var DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE = "Blocked by Replicas command protection.";
|
|
4153
|
-
function asCommandProtectionResponse(value) {
|
|
4154
|
-
if (typeof value !== "object" || value === null) return null;
|
|
4155
|
-
const candidate = value;
|
|
4156
|
-
return typeof candidate.allowed === "boolean" && typeof candidate.enabled === "boolean" ? candidate : null;
|
|
4157
|
-
}
|
|
4158
|
-
function failClosed(reason, matchedSignals) {
|
|
4159
|
-
return {
|
|
4160
|
-
allowed: false,
|
|
4161
|
-
enabled: true,
|
|
4162
|
-
reason,
|
|
4163
|
-
matchedSignals
|
|
4164
|
-
};
|
|
4165
|
-
}
|
|
4166
|
-
async function evaluateCommandProtection(request, signal) {
|
|
4167
|
-
const matchedSignals = [...findGitCommitSignals(request), ...findPrMergeSignals(request)];
|
|
4168
|
-
if (matchedSignals.length === 0) {
|
|
4169
|
-
return { allowed: true, enabled: true };
|
|
4170
|
-
}
|
|
4171
|
-
let response;
|
|
4172
|
-
try {
|
|
4173
|
-
response = await monolithRequest("/v1/engine/command-protection/evaluate", {
|
|
4174
|
-
body: request,
|
|
4175
|
-
signal
|
|
4176
|
-
});
|
|
4177
|
-
} catch (error) {
|
|
4178
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
4179
|
-
return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
|
|
4180
|
-
}
|
|
4181
|
-
if (!response.ok) {
|
|
4182
|
-
const detail = await response.text().catch(() => "");
|
|
4183
|
-
return failClosed(`Command protection failed closed: ${response.status} ${detail}`, matchedSignals);
|
|
4184
|
-
}
|
|
4185
|
-
let payload;
|
|
4186
|
-
try {
|
|
4187
|
-
payload = await response.json();
|
|
4188
|
-
} catch (error) {
|
|
4189
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
4190
|
-
return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
|
|
4191
|
-
}
|
|
4192
|
-
return asCommandProtectionResponse(payload) ?? failClosed("Command protection failed closed: invalid response from policy service", matchedSignals);
|
|
4193
|
-
}
|
|
4194
|
-
function extractToolCommand(input) {
|
|
4195
|
-
return extractCommandProtectionCommandText({ toolInput: input }, { stringifyFallback: false });
|
|
4196
|
-
}
|
|
4197
|
-
function reportCommandProtectionBlock(options) {
|
|
4198
|
-
const message = options.result.reason || DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE;
|
|
4199
|
-
console.warn(`[${options.managerName}] blocked ${options.action} by command protection: ${message}`);
|
|
4200
|
-
options.recordHistoryEvent(options.eventType, {
|
|
4201
|
-
type: "error",
|
|
4202
|
-
message,
|
|
4203
|
-
...options.context,
|
|
4204
|
-
command: options.command
|
|
4205
|
-
}, options.historyFile);
|
|
4206
|
-
return message;
|
|
4207
|
-
}
|
|
4208
|
-
|
|
4209
3952
|
// src/services/skill-registry-service.ts
|
|
4210
3953
|
import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
4211
|
-
import { dirname as dirname3, isAbsolute, join as
|
|
3954
|
+
import { dirname as dirname3, isAbsolute, join as join14, relative, resolve } from "path";
|
|
4212
3955
|
var REGISTRY_ROOT_DIR = ".replicas/skill-registries";
|
|
4213
3956
|
var REGISTRY_MANIFEST = "manifest.json";
|
|
4214
3957
|
async function getSkillRegistryInventory(homeDir) {
|
|
@@ -4269,10 +4012,10 @@ async function scanRegistry(registryDir) {
|
|
|
4269
4012
|
}
|
|
4270
4013
|
async function findSkillCollections(registryDir) {
|
|
4271
4014
|
const candidateRoots = [
|
|
4272
|
-
{ source: "skills", root:
|
|
4273
|
-
{ source: "agents", root:
|
|
4274
|
-
{ source: "codex", root:
|
|
4275
|
-
{ source: "claude", root:
|
|
4015
|
+
{ source: "skills", root: join14(registryDir, "skills") },
|
|
4016
|
+
{ source: "agents", root: join14(registryDir, ".agents", "skills") },
|
|
4017
|
+
{ source: "codex", root: join14(registryDir, ".codex", "skills") },
|
|
4018
|
+
{ source: "claude", root: join14(registryDir, ".claude", "skills") }
|
|
4276
4019
|
];
|
|
4277
4020
|
const collections = [];
|
|
4278
4021
|
for (const { source, root } of candidateRoots) {
|
|
@@ -4286,9 +4029,9 @@ async function findSkillCollections(registryDir) {
|
|
|
4286
4029
|
async function findSkillDirsInRoot(root) {
|
|
4287
4030
|
const skillDirs = [];
|
|
4288
4031
|
for (const dirent of await safeReadDir(root)) {
|
|
4289
|
-
const skillDir =
|
|
4032
|
+
const skillDir = join14(root, dirent.name);
|
|
4290
4033
|
if (!await isDirectoryDirent(dirent, skillDir)) continue;
|
|
4291
|
-
if (await fileExists(
|
|
4034
|
+
if (await fileExists(join14(skillDir, "SKILL.md"))) {
|
|
4292
4035
|
skillDirs.push(skillDir);
|
|
4293
4036
|
}
|
|
4294
4037
|
}
|
|
@@ -4298,9 +4041,9 @@ async function findTopLevelSkills(registryDir) {
|
|
|
4298
4041
|
const skills = [];
|
|
4299
4042
|
for (const dirent of await safeReadDir(registryDir)) {
|
|
4300
4043
|
if (dirent.name.startsWith(".") || dirent.name === "skills" || dirent.name === "plugins") continue;
|
|
4301
|
-
const skillDir =
|
|
4044
|
+
const skillDir = join14(registryDir, dirent.name);
|
|
4302
4045
|
if (!await isDirectoryDirent(dirent, skillDir)) continue;
|
|
4303
|
-
if (await fileExists(
|
|
4046
|
+
if (await fileExists(join14(skillDir, "SKILL.md"))) {
|
|
4304
4047
|
skills.push({ source: "root", skillDir });
|
|
4305
4048
|
}
|
|
4306
4049
|
}
|
|
@@ -4311,10 +4054,10 @@ async function findClaudePluginRoots(registryDir) {
|
|
|
4311
4054
|
async function walk(dir) {
|
|
4312
4055
|
for (const dirent of await safeReadDir(dir)) {
|
|
4313
4056
|
if (dirent.name === ".git") continue;
|
|
4314
|
-
const child =
|
|
4057
|
+
const child = join14(dir, dirent.name);
|
|
4315
4058
|
if (!await isDirectoryDirent(dirent, child)) continue;
|
|
4316
4059
|
if (dirent.name === ".claude-plugin") {
|
|
4317
|
-
if (await fileExists(
|
|
4060
|
+
if (await fileExists(join14(child, "plugin.json"))) {
|
|
4318
4061
|
roots.push(dirname3(child));
|
|
4319
4062
|
}
|
|
4320
4063
|
continue;
|
|
@@ -4326,7 +4069,7 @@ async function findClaudePluginRoots(registryDir) {
|
|
|
4326
4069
|
return uniqueStrings(roots);
|
|
4327
4070
|
}
|
|
4328
4071
|
async function hasCodexMarketplace(registryDir) {
|
|
4329
|
-
return await fileExists(
|
|
4072
|
+
return await fileExists(join14(registryDir, ".agents", "plugins", "marketplace.json")) || await fileExists(join14(registryDir, ".codex", "plugins", "marketplace.json"));
|
|
4330
4073
|
}
|
|
4331
4074
|
async function installCodexRegistryPlugins(client2, inventory) {
|
|
4332
4075
|
const cwds = inventory.codexMarketplaceCwds;
|
|
@@ -4368,10 +4111,10 @@ async function readManifest(homeDir) {
|
|
|
4368
4111
|
}
|
|
4369
4112
|
}
|
|
4370
4113
|
function getRegistryRoot(homeDir) {
|
|
4371
|
-
return
|
|
4114
|
+
return join14(homeDir, REGISTRY_ROOT_DIR);
|
|
4372
4115
|
}
|
|
4373
4116
|
function getManifestPath(homeDir) {
|
|
4374
|
-
return
|
|
4117
|
+
return join14(getRegistryRoot(homeDir), REGISTRY_MANIFEST);
|
|
4375
4118
|
}
|
|
4376
4119
|
function emptyInventory(registryRoot) {
|
|
4377
4120
|
return {
|
|
@@ -4819,7 +4562,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4819
4562
|
authRetrying = false;
|
|
4820
4563
|
constructor(options) {
|
|
4821
4564
|
super({ ...options, provider: AGENT.CLAUDE });
|
|
4822
|
-
this.historyFilePath = options.historyFilePath ??
|
|
4565
|
+
this.historyFilePath = options.historyFilePath ?? join15(homedir10(), ".replicas", "claude", "history.jsonl");
|
|
4823
4566
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
4824
4567
|
this.systemPromptOverride = options.systemPromptOverride;
|
|
4825
4568
|
this.toolsOverride = options.tools;
|
|
@@ -5873,7 +5616,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
5873
5616
|
|
|
5874
5617
|
// src/managers/codex-asp/codex-asp-manager.ts
|
|
5875
5618
|
import { readdir as readdir4 } from "fs/promises";
|
|
5876
|
-
import { join as
|
|
5619
|
+
import { join as join17 } from "path";
|
|
5877
5620
|
|
|
5878
5621
|
// src/managers/codex-asp/asp-host.ts
|
|
5879
5622
|
var hostPromise = null;
|
|
@@ -5988,7 +5731,7 @@ var CodexQuotaStatusTracker = class {
|
|
|
5988
5731
|
};
|
|
5989
5732
|
|
|
5990
5733
|
// src/managers/codex-asp/mappers.ts
|
|
5991
|
-
import { existsSync as existsSync5, readFileSync as
|
|
5734
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
5992
5735
|
var localImageCache = /* @__PURE__ */ new Map();
|
|
5993
5736
|
var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
|
|
5994
5737
|
var THREAD_START_METHOD = "thread/start";
|
|
@@ -6116,7 +5859,7 @@ function userImageForLocalPath(path6) {
|
|
|
6116
5859
|
const image = {
|
|
6117
5860
|
type: "image",
|
|
6118
5861
|
mediaType: inferMediaType(path6),
|
|
6119
|
-
data:
|
|
5862
|
+
data: readFileSync4(path6).toString("base64")
|
|
6120
5863
|
};
|
|
6121
5864
|
if (image.data.length > 0) localImageCache.set(path6, image);
|
|
6122
5865
|
return image;
|
|
@@ -6607,19 +6350,19 @@ var TranscriptUpdateCoalescer = class {
|
|
|
6607
6350
|
};
|
|
6608
6351
|
|
|
6609
6352
|
// src/services/chat/history-paths.ts
|
|
6610
|
-
import { homedir as
|
|
6611
|
-
import { join as
|
|
6612
|
-
var ENGINE_DIR2 =
|
|
6613
|
-
var CHATS_FILE =
|
|
6614
|
-
var CLAUDE_HISTORY_DIR =
|
|
6615
|
-
var RELAY_HISTORY_DIR =
|
|
6616
|
-
var CODEX_HISTORY_DIR =
|
|
6617
|
-
var CURSOR_HISTORY_DIR =
|
|
6618
|
-
var OPENCODE_HISTORY_DIR =
|
|
6619
|
-
var PI_HISTORY_DIR =
|
|
6620
|
-
var DEEPSEEK_HISTORY_DIR =
|
|
6621
|
-
var FX_HISTORY_DIR =
|
|
6622
|
-
var KIMI_HISTORY_DIR =
|
|
6353
|
+
import { homedir as homedir11 } from "os";
|
|
6354
|
+
import { join as join16 } from "path";
|
|
6355
|
+
var ENGINE_DIR2 = join16(homedir11(), ".replicas", "engine");
|
|
6356
|
+
var CHATS_FILE = join16(ENGINE_DIR2, "chats.json");
|
|
6357
|
+
var CLAUDE_HISTORY_DIR = join16(ENGINE_DIR2, "claude-histories");
|
|
6358
|
+
var RELAY_HISTORY_DIR = join16(ENGINE_DIR2, "relay-histories");
|
|
6359
|
+
var CODEX_HISTORY_DIR = join16(ENGINE_DIR2, "codex-histories");
|
|
6360
|
+
var CURSOR_HISTORY_DIR = join16(ENGINE_DIR2, "cursor-histories");
|
|
6361
|
+
var OPENCODE_HISTORY_DIR = join16(ENGINE_DIR2, "opencode-histories");
|
|
6362
|
+
var PI_HISTORY_DIR = join16(ENGINE_DIR2, "pi-histories");
|
|
6363
|
+
var DEEPSEEK_HISTORY_DIR = join16(ENGINE_DIR2, "deepseek-histories");
|
|
6364
|
+
var FX_HISTORY_DIR = join16(ENGINE_DIR2, "fx-histories");
|
|
6365
|
+
var KIMI_HISTORY_DIR = join16(ENGINE_DIR2, "kimi-histories");
|
|
6623
6366
|
var HISTORY_DIR_BY_PROVIDER = {
|
|
6624
6367
|
claude: CLAUDE_HISTORY_DIR,
|
|
6625
6368
|
relay: RELAY_HISTORY_DIR,
|
|
@@ -6685,7 +6428,7 @@ async function readCodexAspThreadHistory(threadId) {
|
|
|
6685
6428
|
}
|
|
6686
6429
|
for (const entry of entries) {
|
|
6687
6430
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl") || entry.name.endsWith(".pages.jsonl")) continue;
|
|
6688
|
-
const history = await new CodexHistoryFile(
|
|
6431
|
+
const history = await new CodexHistoryFile(join17(CODEX_HISTORY_DIR, entry.name)).load();
|
|
6689
6432
|
const transcript = history.transcriptsByThreadId.get(threadId);
|
|
6690
6433
|
if (transcript) {
|
|
6691
6434
|
return {
|
|
@@ -8065,10 +7808,75 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8065
7808
|
};
|
|
8066
7809
|
|
|
8067
7810
|
// src/managers/cursor-manager.ts
|
|
8068
|
-
import { mkdir as
|
|
8069
|
-
import { basename, dirname as
|
|
7811
|
+
import { mkdir as mkdir12, readFile as readFile11, readdir as readdir5 } from "fs/promises";
|
|
7812
|
+
import { basename as basename2, dirname as dirname6, extname, join as join19 } from "path";
|
|
8070
7813
|
import { parse as parseYaml } from "yaml";
|
|
8071
7814
|
import { Agent as CursorAgent } from "@cursor/sdk";
|
|
7815
|
+
|
|
7816
|
+
// src/services/native-command-protection-hooks.ts
|
|
7817
|
+
import { existsSync as existsSync6 } from "fs";
|
|
7818
|
+
import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
|
|
7819
|
+
import { basename, dirname as dirname5, join as join18 } from "path";
|
|
7820
|
+
import { fileURLToPath } from "url";
|
|
7821
|
+
var moduleDirectory = dirname5(fileURLToPath(import.meta.url));
|
|
7822
|
+
var hookPath = [
|
|
7823
|
+
join18(moduleDirectory, "command-protection-hook.js"),
|
|
7824
|
+
join18(moduleDirectory, "..", "command-protection-hook.ts")
|
|
7825
|
+
].find(existsSync6);
|
|
7826
|
+
function shellCommand(provider) {
|
|
7827
|
+
if (!hookPath) throw new Error("Replicas command protection hook executable is missing.");
|
|
7828
|
+
const args = hookPath.endsWith(".ts") && basename(process.execPath) !== "bun" ? [process.execPath, "--import", "tsx", hookPath, provider] : [process.execPath, hookPath, provider];
|
|
7829
|
+
return args.map((value) => `'${value.replaceAll("'", "'\\''")}'`).join(" ");
|
|
7830
|
+
}
|
|
7831
|
+
function getCommandProtectionHookPath() {
|
|
7832
|
+
if (!hookPath) throw new Error("Replicas command protection hook executable is missing.");
|
|
7833
|
+
return hookPath;
|
|
7834
|
+
}
|
|
7835
|
+
async function ensureCursorCommandProtectionHook() {
|
|
7836
|
+
const path6 = join18(ENGINE_ENV.HOME_DIR, ".cursor", "hooks.json");
|
|
7837
|
+
let config = {};
|
|
7838
|
+
try {
|
|
7839
|
+
const parsed = JSON.parse(await readFile10(path6, "utf8"));
|
|
7840
|
+
if (!isRecord2(parsed)) throw new Error("Invalid Cursor hooks configuration.");
|
|
7841
|
+
config = parsed;
|
|
7842
|
+
} catch (error) {
|
|
7843
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
7844
|
+
}
|
|
7845
|
+
const hooks = isRecord2(config.hooks) ? config.hooks : {};
|
|
7846
|
+
const existing = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
|
|
7847
|
+
hooks.preToolUse = [
|
|
7848
|
+
...existing.filter((entry) => !isRecord2(entry) || typeof entry.command !== "string" || !entry.command.includes("command-protection-hook")),
|
|
7849
|
+
{ command: shellCommand("cursor") }
|
|
7850
|
+
];
|
|
7851
|
+
await mkdir11(dirname5(path6), { recursive: true });
|
|
7852
|
+
await writeFile6(path6, `${JSON.stringify({ ...config, version: 1, hooks }, null, 2)}
|
|
7853
|
+
`, "utf8");
|
|
7854
|
+
}
|
|
7855
|
+
async function ensureKimiCommandProtectionHook() {
|
|
7856
|
+
const path6 = join18(ENGINE_ENV.HOME_DIR, ".kimi-code", "config.toml");
|
|
7857
|
+
const start = "# Replicas command protection hook";
|
|
7858
|
+
const end = "# End Replicas command protection hook";
|
|
7859
|
+
let config = "";
|
|
7860
|
+
try {
|
|
7861
|
+
config = await readFile10(path6, "utf8");
|
|
7862
|
+
} catch (error) {
|
|
7863
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
7864
|
+
}
|
|
7865
|
+
const block = `${start}
|
|
7866
|
+
[[hooks]]
|
|
7867
|
+
event = "PreToolUse"
|
|
7868
|
+
command = ${JSON.stringify(shellCommand("kimi"))}
|
|
7869
|
+
timeout = 30
|
|
7870
|
+
${end}`;
|
|
7871
|
+
const markerStart = config.indexOf(start);
|
|
7872
|
+
const markerEnd = config.indexOf(end, markerStart + start.length);
|
|
7873
|
+
config = markerStart >= 0 && markerEnd >= 0 ? `${config.slice(0, markerStart)}${block}${config.slice(markerEnd + end.length)}` : `${config.trimEnd()}${config.trim() ? "\n\n" : ""}${block}
|
|
7874
|
+
`;
|
|
7875
|
+
await mkdir11(dirname5(path6), { recursive: true });
|
|
7876
|
+
await writeFile6(path6, config, "utf8");
|
|
7877
|
+
}
|
|
7878
|
+
|
|
7879
|
+
// src/managers/cursor-manager.ts
|
|
8072
7880
|
var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
8073
7881
|
var CURSOR_STREAM_INACTIVITY_TIMEOUT_MS = 30 * 6e4;
|
|
8074
7882
|
var CURSOR_RUN_SETTLE_TIMEOUT_MS = 3e4;
|
|
@@ -8108,24 +7916,6 @@ function extractCursorCommandDescription(content) {
|
|
|
8108
7916
|
}
|
|
8109
7917
|
return void 0;
|
|
8110
7918
|
}
|
|
8111
|
-
function cursorToolInputFromDelta(update) {
|
|
8112
|
-
if (update.type !== "tool-call-started") return null;
|
|
8113
|
-
const toolCall = update.toolCall;
|
|
8114
|
-
return {
|
|
8115
|
-
toolName: toolCall.type,
|
|
8116
|
-
toolInput: toolCall,
|
|
8117
|
-
command: extractToolCommand(toolCall),
|
|
8118
|
-
toolUseId: update.callId
|
|
8119
|
-
};
|
|
8120
|
-
}
|
|
8121
|
-
function cursorToolInputFromMessage(event) {
|
|
8122
|
-
return {
|
|
8123
|
-
toolName: event.name,
|
|
8124
|
-
toolInput: event.args ?? {},
|
|
8125
|
-
command: extractToolCommand(event.args),
|
|
8126
|
-
toolUseId: event.call_id
|
|
8127
|
-
};
|
|
8128
|
-
}
|
|
8129
7919
|
async function listCursorCommandsInDirectory(directory) {
|
|
8130
7920
|
let entries;
|
|
8131
7921
|
try {
|
|
@@ -8136,10 +7926,10 @@ async function listCursorCommandsInDirectory(directory) {
|
|
|
8136
7926
|
return [];
|
|
8137
7927
|
}
|
|
8138
7928
|
const commands = await Promise.all(entries.filter((entry) => entry.isFile() && extname(entry.name) === ".md").map(async (entry) => {
|
|
8139
|
-
const name =
|
|
7929
|
+
const name = basename2(entry.name, ".md");
|
|
8140
7930
|
let description;
|
|
8141
7931
|
try {
|
|
8142
|
-
description = extractCursorCommandDescription(await
|
|
7932
|
+
description = extractCursorCommandDescription(await readFile11(join19(directory, entry.name), "utf8"));
|
|
8143
7933
|
} catch (error) {
|
|
8144
7934
|
console.warn("[CursorManager] Failed to read slash command file:", error);
|
|
8145
7935
|
}
|
|
@@ -8152,8 +7942,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8152
7942
|
activeRun = null;
|
|
8153
7943
|
activeModel = null;
|
|
8154
7944
|
abortActiveTurn = null;
|
|
8155
|
-
blockedToolCallIds = /* @__PURE__ */ new Set();
|
|
8156
|
-
pendingCursorBlockReason = null;
|
|
8157
7945
|
reconnectNoticeVisible = false;
|
|
8158
7946
|
instructionsDelivered = false;
|
|
8159
7947
|
resumeSessionId = null;
|
|
@@ -8163,12 +7951,13 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8163
7951
|
slashCommandsRequest = null;
|
|
8164
7952
|
constructor(options) {
|
|
8165
7953
|
super(options);
|
|
8166
|
-
this.historyFilePath = options.historyFilePath ??
|
|
7954
|
+
this.historyFilePath = options.historyFilePath ?? join19(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
|
|
8167
7955
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
8168
7956
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8169
7957
|
}
|
|
8170
7958
|
async initialize() {
|
|
8171
|
-
await
|
|
7959
|
+
await mkdir12(dirname6(this.historyFilePath), { recursive: true });
|
|
7960
|
+
await ensureCursorCommandProtectionHook();
|
|
8172
7961
|
}
|
|
8173
7962
|
getHistorySink() {
|
|
8174
7963
|
return this.historyFile;
|
|
@@ -8237,7 +8026,7 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8237
8026
|
this.slashCommandsRequest ??= (async () => {
|
|
8238
8027
|
try {
|
|
8239
8028
|
const repoDirectories = await getAgentAdditionalDirectories();
|
|
8240
|
-
const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) =>
|
|
8029
|
+
const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join19(directory, ".cursor", "commands"));
|
|
8241
8030
|
const commands = mergeSlashCommands(
|
|
8242
8031
|
...await Promise.all(commandDirectories.map(listCursorCommandsInDirectory))
|
|
8243
8032
|
);
|
|
@@ -8308,17 +8097,12 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8308
8097
|
run = await agent.send(message, {
|
|
8309
8098
|
model: { id: model },
|
|
8310
8099
|
mode: request.planMode ? "plan" : "agent",
|
|
8311
|
-
onDelta:
|
|
8100
|
+
onDelta: (args) => {
|
|
8312
8101
|
markCursorActivity();
|
|
8313
8102
|
this.recordCursorContextUsage(args.update);
|
|
8314
|
-
await this.handleCursorToolCall(cursorToolInputFromDelta(args.update));
|
|
8315
8103
|
}
|
|
8316
8104
|
});
|
|
8317
8105
|
this.activeRun = run;
|
|
8318
|
-
if (this.pendingCursorBlockReason) {
|
|
8319
|
-
await run.cancel();
|
|
8320
|
-
throw new Error(this.pendingCursorBlockReason);
|
|
8321
|
-
}
|
|
8322
8106
|
const events = run.stream()[Symbol.asyncIterator]();
|
|
8323
8107
|
let interrupted = false;
|
|
8324
8108
|
while (true) {
|
|
@@ -8340,9 +8124,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8340
8124
|
markCursorActivity();
|
|
8341
8125
|
const event = next.value;
|
|
8342
8126
|
this.recordHistoryEvent(`cursor-${event.type}`, event, this.historyFile);
|
|
8343
|
-
if (event.type === "tool_call" && event.status === "running") {
|
|
8344
|
-
await this.handleCursorToolCall(cursorToolInputFromMessage(event));
|
|
8345
|
-
}
|
|
8346
8127
|
if (linearSessionId) {
|
|
8347
8128
|
linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
|
|
8348
8129
|
}
|
|
@@ -8413,8 +8194,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8413
8194
|
this.abortActiveTurn = null;
|
|
8414
8195
|
this.activeRun = null;
|
|
8415
8196
|
this.activeModel = null;
|
|
8416
|
-
this.blockedToolCallIds.clear();
|
|
8417
|
-
this.pendingCursorBlockReason = null;
|
|
8418
8197
|
await this.historyFile.flush();
|
|
8419
8198
|
await this.onTurnComplete();
|
|
8420
8199
|
}
|
|
@@ -8434,39 +8213,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8434
8213
|
return null;
|
|
8435
8214
|
}
|
|
8436
8215
|
}
|
|
8437
|
-
async handleCursorToolCall(toolCall) {
|
|
8438
|
-
if (!toolCall) return;
|
|
8439
|
-
if (this.blockedToolCallIds.has(toolCall.toolUseId)) return;
|
|
8440
|
-
const result = await evaluateCommandProtection({
|
|
8441
|
-
provider: "cursor",
|
|
8442
|
-
source: "cursor_tool_call",
|
|
8443
|
-
toolName: toolCall.toolName,
|
|
8444
|
-
toolInput: toolCall.toolInput,
|
|
8445
|
-
command: toolCall.command,
|
|
8446
|
-
cwd: this.workingDirectory,
|
|
8447
|
-
toolUseId: toolCall.toolUseId
|
|
8448
|
-
});
|
|
8449
|
-
if (result.allowed) return;
|
|
8450
|
-
this.blockedToolCallIds.add(toolCall.toolUseId);
|
|
8451
|
-
const message = reportCommandProtectionBlock({
|
|
8452
|
-
managerName: "CursorManager",
|
|
8453
|
-
action: "tool call",
|
|
8454
|
-
eventType: "cursor-command-protection.blocked",
|
|
8455
|
-
result,
|
|
8456
|
-
command: toolCall.command,
|
|
8457
|
-
context: {
|
|
8458
|
-
toolName: toolCall.toolName,
|
|
8459
|
-
toolUseId: toolCall.toolUseId
|
|
8460
|
-
},
|
|
8461
|
-
historyFile: this.historyFile,
|
|
8462
|
-
recordHistoryEvent: this.recordHistoryEvent.bind(this)
|
|
8463
|
-
});
|
|
8464
|
-
if (this.activeRun) {
|
|
8465
|
-
await this.activeRun.cancel();
|
|
8466
|
-
throw new Error(message);
|
|
8467
|
-
}
|
|
8468
|
-
this.pendingCursorBlockReason = message;
|
|
8469
|
-
}
|
|
8470
8216
|
async toCursorMessage(request, includeInstructions) {
|
|
8471
8217
|
const instructions = includeInstructions ? this.buildCombinedInstructions(request.customInstructions) : void 0;
|
|
8472
8218
|
const text = [
|
|
@@ -8553,10 +8299,10 @@ ${instructions}
|
|
|
8553
8299
|
import { spawn as spawn3 } from "child_process";
|
|
8554
8300
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8555
8301
|
import { createRequire } from "module";
|
|
8556
|
-
import { dirname as
|
|
8557
|
-
import { fileURLToPath } from "url";
|
|
8558
|
-
import { existsSync as
|
|
8559
|
-
import { mkdir as
|
|
8302
|
+
import { dirname as dirname7, join as join20 } from "path";
|
|
8303
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8304
|
+
import { existsSync as existsSync7 } from "fs";
|
|
8305
|
+
import { mkdir as mkdir13, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
|
|
8560
8306
|
import { z as z2 } from "zod";
|
|
8561
8307
|
import WebSocket from "ws";
|
|
8562
8308
|
import { AbstractApiClient } from "@deepseek-ai/dsh-host-apiproxy/client";
|
|
@@ -8568,12 +8314,16 @@ var questionSelectionSchema = z2.record(z2.string(), z2.object({
|
|
|
8568
8314
|
custom: z2.string().optional()
|
|
8569
8315
|
}));
|
|
8570
8316
|
var require2 = createRequire(import.meta.url);
|
|
8571
|
-
var DSH_BIN =
|
|
8572
|
-
var MANAGER_DIR =
|
|
8317
|
+
var DSH_BIN = join20(dirname7(require2.resolve("@deepseek-ai/dsh/package.json")), "lib", "bin.js");
|
|
8318
|
+
var MANAGER_DIR = dirname7(fileURLToPath2(import.meta.url));
|
|
8573
8319
|
var DSH_PATCH = [
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
].find(
|
|
8320
|
+
join20(MANAGER_DIR, "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml"),
|
|
8321
|
+
join20(MANAGER_DIR, "..", "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml")
|
|
8322
|
+
].find(existsSync7) ?? "";
|
|
8323
|
+
var DSH_COMMAND_PROTECTION_PLUGIN = [
|
|
8324
|
+
join20(MANAGER_DIR, "deepseek-command-protection-plugin.js"),
|
|
8325
|
+
join20(MANAGER_DIR, "..", "deepseek-command-protection-plugin.ts")
|
|
8326
|
+
].find(existsSync7) ?? "";
|
|
8577
8327
|
var DeepseekApiClient = class extends AbstractApiClient {
|
|
8578
8328
|
constructor(baseUrl) {
|
|
8579
8329
|
super(3e4);
|
|
@@ -8686,12 +8436,12 @@ var DeepseekManager = class extends CodingAgentManager {
|
|
|
8686
8436
|
constructor(options) {
|
|
8687
8437
|
super(options);
|
|
8688
8438
|
this.sessionId = options.initialSessionId ? sessionIdSchema.parse(options.initialSessionId) : null;
|
|
8689
|
-
this.historyFilePath = options.historyFilePath ??
|
|
8439
|
+
this.historyFilePath = options.historyFilePath ?? join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
|
|
8690
8440
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
8691
8441
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8692
8442
|
}
|
|
8693
8443
|
async initialize() {
|
|
8694
|
-
await
|
|
8444
|
+
await mkdir13(dirname7(this.historyFilePath), { recursive: true });
|
|
8695
8445
|
}
|
|
8696
8446
|
getHistorySink() {
|
|
8697
8447
|
return this.historyFile;
|
|
@@ -8765,15 +8515,32 @@ var DeepseekManager = class extends CodingAgentManager {
|
|
|
8765
8515
|
}
|
|
8766
8516
|
async ensureClient() {
|
|
8767
8517
|
if (this.client) return this.client;
|
|
8768
|
-
if (!DSH_PATCH) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
|
|
8518
|
+
if (!DSH_PATCH || !DSH_COMMAND_PROTECTION_PLUGIN) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
|
|
8519
|
+
const runtimePatch = join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "replicas.cordis.patch.yml");
|
|
8520
|
+
await mkdir13(dirname7(runtimePatch), { recursive: true });
|
|
8521
|
+
await writeFile7(runtimePatch, (await readFile12(DSH_PATCH, "utf8")).replace(
|
|
8522
|
+
"__REPLICAS_DSH_COMMAND_PROTECTION_PLUGIN__",
|
|
8523
|
+
JSON.stringify(DSH_COMMAND_PROTECTION_PLUGIN)
|
|
8524
|
+
));
|
|
8769
8525
|
const apiKey = await getOpenRouterKey();
|
|
8770
|
-
const child = spawn3(process.execPath, [
|
|
8526
|
+
const child = spawn3(process.execPath, [
|
|
8527
|
+
...DSH_COMMAND_PROTECTION_PLUGIN.endsWith(".ts") ? ["--import", "tsx"] : [],
|
|
8528
|
+
DSH_BIN,
|
|
8529
|
+
"--profile",
|
|
8530
|
+
"web",
|
|
8531
|
+
"--patch",
|
|
8532
|
+
runtimePatch,
|
|
8533
|
+
"--host",
|
|
8534
|
+
"127.0.0.1",
|
|
8535
|
+
"--port",
|
|
8536
|
+
"0"
|
|
8537
|
+
], {
|
|
8771
8538
|
cwd: this.workingDirectory,
|
|
8772
8539
|
env: {
|
|
8773
8540
|
...process.env,
|
|
8774
8541
|
DEEPSEEK_API_KEY: apiKey,
|
|
8775
8542
|
DEEPSEEK_BASE_URL: "https://openrouter.ai/api/v1",
|
|
8776
|
-
DSH_HOME:
|
|
8543
|
+
DSH_HOME: join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek"),
|
|
8777
8544
|
DSH_PERMISSION_MODE: "danger-full-access",
|
|
8778
8545
|
DSH_TELEMETRY_DISABLED: "true"
|
|
8779
8546
|
},
|
|
@@ -8958,14 +8725,14 @@ ${request.message}` : request.message;
|
|
|
8958
8725
|
};
|
|
8959
8726
|
|
|
8960
8727
|
// src/managers/fx-manager.ts
|
|
8961
|
-
import { readFile as
|
|
8728
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
8962
8729
|
import { spawn as spawn5 } from "child_process";
|
|
8963
8730
|
|
|
8964
8731
|
// src/managers/acp-manager.ts
|
|
8965
8732
|
import { spawn as spawn4 } from "child_process";
|
|
8966
8733
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
8967
|
-
import { mkdir as
|
|
8968
|
-
import { dirname as
|
|
8734
|
+
import { mkdir as mkdir14, readFile as readFile13, writeFile as writeFile8 } from "fs/promises";
|
|
8735
|
+
import { dirname as dirname8 } from "path";
|
|
8969
8736
|
import { Writable } from "stream";
|
|
8970
8737
|
import {
|
|
8971
8738
|
PROTOCOL_VERSION,
|
|
@@ -9004,7 +8771,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9004
8771
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9005
8772
|
}
|
|
9006
8773
|
async initialize() {
|
|
9007
|
-
await
|
|
8774
|
+
await mkdir14(dirname8(this.historyFilePath), { recursive: true });
|
|
9008
8775
|
await this.acp.prepare?.();
|
|
9009
8776
|
}
|
|
9010
8777
|
getHistorySink() {
|
|
@@ -9035,7 +8802,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9035
8802
|
});
|
|
9036
8803
|
const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
|
|
9037
8804
|
this.assertSession(params.sessionId);
|
|
9038
|
-
const content = await
|
|
8805
|
+
const content = await readFile13(params.path, "utf8");
|
|
9039
8806
|
if (params.line === void 0 && params.limit === void 0) return { content };
|
|
9040
8807
|
const start = Math.max(0, (params.line ?? 1) - 1);
|
|
9041
8808
|
const end = params.limit === null || params.limit === void 0 ? void 0 : start + params.limit;
|
|
@@ -9043,7 +8810,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9043
8810
|
}).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
|
|
9044
8811
|
this.assertSession(params.sessionId);
|
|
9045
8812
|
if (this.planMode) throw new Error("ACP file writes are disabled in plan mode.");
|
|
9046
|
-
await
|
|
8813
|
+
await writeFile8(params.path, params.content, "utf8");
|
|
9047
8814
|
return {};
|
|
9048
8815
|
}).onRequest(methods.client.terminal.create, ({ params }) => this.createTerminal(params)).onRequest(methods.client.terminal.output, ({ params }) => {
|
|
9049
8816
|
this.assertSession(params.sessionId);
|
|
@@ -9410,7 +9177,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9410
9177
|
|
|
9411
9178
|
// src/managers/fx-manager.ts
|
|
9412
9179
|
async function ensureResolvableDnsConfig() {
|
|
9413
|
-
const contents = await
|
|
9180
|
+
const contents = await readFile14("/etc/resolv.conf");
|
|
9414
9181
|
if (contents.length === 0 || contents.at(-1) === 10) return;
|
|
9415
9182
|
await new Promise((resolve5, reject) => {
|
|
9416
9183
|
const child = spawn5("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
|
|
@@ -9477,6 +9244,7 @@ var KimiManager = class extends AcpManager {
|
|
|
9477
9244
|
};
|
|
9478
9245
|
},
|
|
9479
9246
|
defaultModel: DEFAULT_KIMI_MODEL,
|
|
9247
|
+
prepare: ensureKimiCommandProtectionHook,
|
|
9480
9248
|
restartOnThinkingChange: true
|
|
9481
9249
|
}
|
|
9482
9250
|
});
|
|
@@ -9484,11 +9252,11 @@ var KimiManager = class extends AcpManager {
|
|
|
9484
9252
|
};
|
|
9485
9253
|
|
|
9486
9254
|
// src/managers/opencode-manager.ts
|
|
9487
|
-
import { existsSync as
|
|
9488
|
-
import { mkdir as
|
|
9489
|
-
import { delimiter, dirname as
|
|
9255
|
+
import { existsSync as existsSync8 } from "fs";
|
|
9256
|
+
import { copyFile, mkdir as mkdir15, readFile as readFile15, rm as rm2 } from "fs/promises";
|
|
9257
|
+
import { delimiter, dirname as dirname9, join as join21 } from "path";
|
|
9490
9258
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
9491
|
-
import { fileURLToPath as
|
|
9259
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9492
9260
|
import { Agent } from "undici";
|
|
9493
9261
|
import { z as z3 } from "zod";
|
|
9494
9262
|
|
|
@@ -9545,27 +9313,14 @@ async function getAllowedOpenRouterModels() {
|
|
|
9545
9313
|
}
|
|
9546
9314
|
|
|
9547
9315
|
// src/managers/opencode-manager.ts
|
|
9548
|
-
var OPENCODE_SHIM_DIR =
|
|
9549
|
-
var
|
|
9316
|
+
var OPENCODE_SHIM_DIR = dirname9(fileURLToPath3(new URL("../../scripts/opencode", import.meta.url)));
|
|
9317
|
+
var OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE = join21(OPENCODE_SHIM_DIR, "opencode-command-protection-plugin.ts");
|
|
9318
|
+
var OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.ts");
|
|
9319
|
+
var OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.mjs");
|
|
9320
|
+
var OPENCODE_CONFIG_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
|
|
9550
9321
|
var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
9551
9322
|
var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
|
|
9552
|
-
var OPENCODE_WORKSPACE_PERMISSION =
|
|
9553
|
-
read: "allow",
|
|
9554
|
-
edit: "allow",
|
|
9555
|
-
glob: "allow",
|
|
9556
|
-
grep: "allow",
|
|
9557
|
-
list: "allow",
|
|
9558
|
-
bash: "ask",
|
|
9559
|
-
task: "allow",
|
|
9560
|
-
external_directory: "allow",
|
|
9561
|
-
todowrite: "allow",
|
|
9562
|
-
question: "allow",
|
|
9563
|
-
webfetch: "allow",
|
|
9564
|
-
websearch: "allow",
|
|
9565
|
-
lsp: "allow",
|
|
9566
|
-
doom_loop: "allow",
|
|
9567
|
-
skill: "allow"
|
|
9568
|
-
};
|
|
9323
|
+
var OPENCODE_WORKSPACE_PERMISSION = "allow";
|
|
9569
9324
|
var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
9570
9325
|
var OPENCODE_VARIANT_CANDIDATES_BY_THINKING_LEVEL = {
|
|
9571
9326
|
low: ["low"],
|
|
@@ -9581,9 +9336,9 @@ var opencodeAuthSchema = z3.record(z3.string(), z3.object({
|
|
|
9581
9336
|
key: z3.string().optional()
|
|
9582
9337
|
}));
|
|
9583
9338
|
async function hasOpencodeCredentials(provider) {
|
|
9584
|
-
if (!
|
|
9339
|
+
if (!existsSync8(OPENCODE_AUTH_PATH)) return false;
|
|
9585
9340
|
try {
|
|
9586
|
-
const auth = opencodeAuthSchema.safeParse(JSON.parse(await
|
|
9341
|
+
const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile15(OPENCODE_AUTH_PATH, "utf8")));
|
|
9587
9342
|
return auth.success && auth.data[provider]?.type === "api" && Boolean(auth.data[provider]?.key);
|
|
9588
9343
|
} catch {
|
|
9589
9344
|
return false;
|
|
@@ -9688,7 +9443,7 @@ function isOpencodeMcpEntry(value) {
|
|
|
9688
9443
|
async function readProvisionedOpencodeMcpConfig() {
|
|
9689
9444
|
let raw;
|
|
9690
9445
|
try {
|
|
9691
|
-
raw = await
|
|
9446
|
+
raw = await readFile15(OPENCODE_CONFIG_PATH, "utf8");
|
|
9692
9447
|
} catch (error) {
|
|
9693
9448
|
if (isRecord2(error) && error.code === "ENOENT") return void 0;
|
|
9694
9449
|
console.error("[OpencodeManager] Failed to read Opencode config:", error);
|
|
@@ -9746,27 +9501,6 @@ function opencodeFetch(input, init) {
|
|
|
9746
9501
|
const requestInit = { ...init, dispatcher: OPENCODE_FETCH_DISPATCHER };
|
|
9747
9502
|
return fetch(input, requestInit);
|
|
9748
9503
|
}
|
|
9749
|
-
function parseOpencodeToolInputText(text) {
|
|
9750
|
-
try {
|
|
9751
|
-
return JSON.parse(text);
|
|
9752
|
-
} catch {
|
|
9753
|
-
return { command: text };
|
|
9754
|
-
}
|
|
9755
|
-
}
|
|
9756
|
-
function opencodePermissionCommand(payload, toolInput) {
|
|
9757
|
-
const toolCommand = extractToolCommand(toolInput);
|
|
9758
|
-
if (toolCommand) return toolCommand;
|
|
9759
|
-
const payloadCommand = extractToolCommand(payload);
|
|
9760
|
-
if (payloadCommand) return payloadCommand;
|
|
9761
|
-
const resources = Array.isArray(payload.resources) ? payload.resources.filter((item) => typeof item === "string") : [];
|
|
9762
|
-
if (resources.length > 0) return resources.join(" ");
|
|
9763
|
-
const metadata = isRecord2(payload.metadata) ? payload.metadata : null;
|
|
9764
|
-
for (const key of ["command", "cmd", "pattern", "resource"]) {
|
|
9765
|
-
const value = metadata?.[key];
|
|
9766
|
-
if (typeof value === "string" && value.trim()) return value;
|
|
9767
|
-
}
|
|
9768
|
-
return null;
|
|
9769
|
-
}
|
|
9770
9504
|
var OpencodeManager = class extends CodingAgentManager {
|
|
9771
9505
|
client = null;
|
|
9772
9506
|
server = null;
|
|
@@ -9787,22 +9521,17 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9787
9521
|
forwardedLinearPartKeys = /* @__PURE__ */ new Set();
|
|
9788
9522
|
handledAccountRateLimit = false;
|
|
9789
9523
|
modelVariants = /* @__PURE__ */ new Map();
|
|
9790
|
-
handledPermissionRequestIds = /* @__PURE__ */ new Set();
|
|
9791
|
-
pendingPermissionRequestIds = /* @__PURE__ */ new Set();
|
|
9792
|
-
handledToolCallIds = /* @__PURE__ */ new Set();
|
|
9793
|
-
pendingToolCallIds = /* @__PURE__ */ new Set();
|
|
9794
|
-
opencodeToolInputs = /* @__PURE__ */ new Map();
|
|
9795
9524
|
slashCommandsCache = null;
|
|
9796
9525
|
slashCommandsRequest = null;
|
|
9797
9526
|
constructor(options) {
|
|
9798
9527
|
super(options);
|
|
9799
9528
|
this.sessionId = options.initialSessionId;
|
|
9800
|
-
this.historyFilePath = options.historyFilePath ??
|
|
9529
|
+
this.historyFilePath = options.historyFilePath ?? join21(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
|
|
9801
9530
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
9802
9531
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9803
9532
|
}
|
|
9804
9533
|
async initialize() {
|
|
9805
|
-
await
|
|
9534
|
+
await mkdir15(dirname9(this.historyFilePath), { recursive: true });
|
|
9806
9535
|
}
|
|
9807
9536
|
getHistorySink() {
|
|
9808
9537
|
return this.historyFile;
|
|
@@ -9899,6 +9628,14 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9899
9628
|
if (!await hasOpencodeCredentials(providerId)) {
|
|
9900
9629
|
throw new Error("OpenCode Go, Aster, or OpenRouter credentials are not configured for Opencode in this workspace.");
|
|
9901
9630
|
}
|
|
9631
|
+
if (!existsSync8(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE)) {
|
|
9632
|
+
throw new Error("Opencode command protection plugin is missing from the engine package.");
|
|
9633
|
+
}
|
|
9634
|
+
await mkdir15(dirname9(OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH), { recursive: true });
|
|
9635
|
+
await copyFile(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE, OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH);
|
|
9636
|
+
await rm2(OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH, { force: true });
|
|
9637
|
+
process.env.REPLICAS_COMMAND_PROTECTION_HOOK_PATH = getCommandProtectionHookPath();
|
|
9638
|
+
process.env.REPLICAS_COMMAND_PROTECTION_RUNTIME_PATH = process.execPath;
|
|
9902
9639
|
this.eventAbortController?.abort();
|
|
9903
9640
|
this.server?.close();
|
|
9904
9641
|
const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
@@ -10032,11 +9769,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10032
9769
|
this.activeAbortController = null;
|
|
10033
9770
|
this.activeLinearForwarder = null;
|
|
10034
9771
|
this.forwardedLinearPartKeys.clear();
|
|
10035
|
-
this.handledPermissionRequestIds.clear();
|
|
10036
|
-
this.pendingPermissionRequestIds.clear();
|
|
10037
|
-
this.handledToolCallIds.clear();
|
|
10038
|
-
this.pendingToolCallIds.clear();
|
|
10039
|
-
this.opencodeToolInputs.clear();
|
|
10040
9772
|
await this.historyFile.flush();
|
|
10041
9773
|
await this.onTurnComplete();
|
|
10042
9774
|
}
|
|
@@ -10074,9 +9806,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10074
9806
|
if (typeof partSessionID === "string" && this.sessionId && partSessionID !== this.sessionId) return;
|
|
10075
9807
|
if (event.type === "message.part.updated" && isOpencodePart(part)) {
|
|
10076
9808
|
if (this.shouldRecordOpencodePart(part)) this.recordOpencodePart(part);
|
|
10077
|
-
this.handleOpencodePartProtection(part).catch((error) => {
|
|
10078
|
-
console.error("[OpencodeManager] Failed to evaluate Opencode tool part:", error);
|
|
10079
|
-
});
|
|
10080
9809
|
return;
|
|
10081
9810
|
}
|
|
10082
9811
|
if (event.type === "message.updated") this.recordOpencodeMessageRole(payload);
|
|
@@ -10085,22 +9814,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10085
9814
|
console.error("[OpencodeManager] Failed to stop account-rate-limited turn:", error);
|
|
10086
9815
|
});
|
|
10087
9816
|
}
|
|
10088
|
-
if (event.type === "session.next.tool.input.ended") {
|
|
10089
|
-
const callId = typeof payload.callID === "string" ? payload.callID : void 0;
|
|
10090
|
-
if (callId && typeof payload.text === "string") {
|
|
10091
|
-
this.opencodeToolInputs.set(callId, parseOpencodeToolInputText(payload.text));
|
|
10092
|
-
}
|
|
10093
|
-
}
|
|
10094
|
-
if (event.type === "permission.v2.asked" || event.type === "permission.asked") {
|
|
10095
|
-
this.respondToOpencodePermission(event.type, payload).catch((error) => {
|
|
10096
|
-
console.error("[OpencodeManager] Failed to respond to Opencode permission:", error);
|
|
10097
|
-
});
|
|
10098
|
-
}
|
|
10099
|
-
if (event.type === "session.next.tool.called") {
|
|
10100
|
-
this.handleOpencodeToolCallProtection(payload).catch((error) => {
|
|
10101
|
-
console.error("[OpencodeManager] Failed to evaluate Opencode tool call:", error);
|
|
10102
|
-
});
|
|
10103
|
-
}
|
|
10104
9817
|
if (this.recordOpencodeNextPart(event.type, payload)) return;
|
|
10105
9818
|
this.recordHistoryEvent(`opencode-${event.type}`, payload, this.historyFile);
|
|
10106
9819
|
}
|
|
@@ -10113,110 +9826,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10113
9826
|
this.recordHistoryEvent("opencode-error", { message, code: "account_rate_limit" }, this.historyFile);
|
|
10114
9827
|
await this.abortActiveTurn();
|
|
10115
9828
|
}
|
|
10116
|
-
async respondToOpencodePermission(type, payload) {
|
|
10117
|
-
if (!this.client) return;
|
|
10118
|
-
const requestId = typeof payload.id === "string" ? payload.id : void 0;
|
|
10119
|
-
const sessionId = typeof payload.sessionID === "string" ? payload.sessionID : this.sessionId ?? void 0;
|
|
10120
|
-
if (!requestId || !sessionId || this.handledPermissionRequestIds.has(requestId) || this.pendingPermissionRequestIds.has(requestId)) return;
|
|
10121
|
-
this.pendingPermissionRequestIds.add(requestId);
|
|
10122
|
-
try {
|
|
10123
|
-
const source = isRecord2(payload.source) ? payload.source : null;
|
|
10124
|
-
const callId = typeof source?.callID === "string" ? source.callID : isRecord2(payload.tool) && typeof payload.tool.callID === "string" ? payload.tool.callID : void 0;
|
|
10125
|
-
const toolInput = callId ? this.opencodeToolInputs.get(callId) ?? payload : payload;
|
|
10126
|
-
const command = opencodePermissionCommand(payload, toolInput);
|
|
10127
|
-
const result = await evaluateCommandProtection({
|
|
10128
|
-
provider: "opencode",
|
|
10129
|
-
source: "opencode_permission_request",
|
|
10130
|
-
toolName: typeof payload.action === "string" ? payload.action : typeof payload.permission === "string" ? payload.permission : "permission",
|
|
10131
|
-
toolInput,
|
|
10132
|
-
command,
|
|
10133
|
-
cwd: this.workingDirectory,
|
|
10134
|
-
toolUseId: requestId
|
|
10135
|
-
});
|
|
10136
|
-
const reply = result.allowed ? "once" : "reject";
|
|
10137
|
-
const message = result.allowed ? void 0 : reportCommandProtectionBlock({
|
|
10138
|
-
managerName: "OpencodeManager",
|
|
10139
|
-
action: "permission",
|
|
10140
|
-
eventType: "opencode-command-protection.blocked",
|
|
10141
|
-
result,
|
|
10142
|
-
command,
|
|
10143
|
-
context: {
|
|
10144
|
-
requestId
|
|
10145
|
-
},
|
|
10146
|
-
historyFile: this.historyFile,
|
|
10147
|
-
recordHistoryEvent: this.recordHistoryEvent.bind(this)
|
|
10148
|
-
});
|
|
10149
|
-
if (type === "permission.v2.asked") {
|
|
10150
|
-
await this.client.v2.session.permission.reply({
|
|
10151
|
-
sessionID: sessionId,
|
|
10152
|
-
requestID: requestId,
|
|
10153
|
-
reply,
|
|
10154
|
-
...message ? { message } : {}
|
|
10155
|
-
}, { throwOnError: true });
|
|
10156
|
-
} else {
|
|
10157
|
-
await this.client.permission.reply({
|
|
10158
|
-
requestID: requestId,
|
|
10159
|
-
directory: this.workingDirectory,
|
|
10160
|
-
reply,
|
|
10161
|
-
...message ? { message } : {}
|
|
10162
|
-
}, { throwOnError: true });
|
|
10163
|
-
}
|
|
10164
|
-
this.handledPermissionRequestIds.add(requestId);
|
|
10165
|
-
} finally {
|
|
10166
|
-
this.pendingPermissionRequestIds.delete(requestId);
|
|
10167
|
-
}
|
|
10168
|
-
}
|
|
10169
|
-
async handleOpencodeToolCallProtection(payload) {
|
|
10170
|
-
const callId = typeof payload.callID === "string" ? payload.callID : void 0;
|
|
10171
|
-
if (!callId || this.handledToolCallIds.has(callId)) return;
|
|
10172
|
-
const toolName = typeof payload.tool === "string" ? payload.tool : "tool";
|
|
10173
|
-
const input = isRecord2(payload.input) ? payload.input : {};
|
|
10174
|
-
this.opencodeToolInputs.set(callId, input);
|
|
10175
|
-
const command = extractToolCommand(input);
|
|
10176
|
-
await this.evaluateOpencodeToolProtection(toolName, input, command, callId);
|
|
10177
|
-
}
|
|
10178
|
-
async handleOpencodePartProtection(part) {
|
|
10179
|
-
if (part.type !== "tool") return;
|
|
10180
|
-
const state = part.state;
|
|
10181
|
-
if (state.status !== "running" && state.status !== "pending") return;
|
|
10182
|
-
await this.evaluateOpencodeToolProtection(part.tool, state.input, extractToolCommand(state.input), part.id);
|
|
10183
|
-
}
|
|
10184
|
-
async evaluateOpencodeToolProtection(toolName, toolInput, command, toolUseId) {
|
|
10185
|
-
if (this.handledToolCallIds.has(toolUseId) || this.pendingToolCallIds.has(toolUseId)) return;
|
|
10186
|
-
this.pendingToolCallIds.add(toolUseId);
|
|
10187
|
-
try {
|
|
10188
|
-
const result = await evaluateCommandProtection({
|
|
10189
|
-
provider: "opencode",
|
|
10190
|
-
source: "opencode_tool_call",
|
|
10191
|
-
toolName,
|
|
10192
|
-
toolInput,
|
|
10193
|
-
command,
|
|
10194
|
-
cwd: this.workingDirectory,
|
|
10195
|
-
toolUseId
|
|
10196
|
-
});
|
|
10197
|
-
if (result.allowed) {
|
|
10198
|
-
this.handledToolCallIds.add(toolUseId);
|
|
10199
|
-
return;
|
|
10200
|
-
}
|
|
10201
|
-
const message = reportCommandProtectionBlock({
|
|
10202
|
-
managerName: "OpencodeManager",
|
|
10203
|
-
action: "tool call",
|
|
10204
|
-
eventType: "opencode-command-protection.blocked",
|
|
10205
|
-
result,
|
|
10206
|
-
command,
|
|
10207
|
-
context: {
|
|
10208
|
-
toolName,
|
|
10209
|
-
toolUseId
|
|
10210
|
-
},
|
|
10211
|
-
historyFile: this.historyFile,
|
|
10212
|
-
recordHistoryEvent: this.recordHistoryEvent.bind(this)
|
|
10213
|
-
});
|
|
10214
|
-
await this.abortActiveTurn();
|
|
10215
|
-
this.handledToolCallIds.add(toolUseId);
|
|
10216
|
-
} finally {
|
|
10217
|
-
this.pendingToolCallIds.delete(toolUseId);
|
|
10218
|
-
}
|
|
10219
|
-
}
|
|
10220
9829
|
recordOpencodeMessageRole(payload) {
|
|
10221
9830
|
const info = isRecord2(payload.info) ? payload.info : null;
|
|
10222
9831
|
const id = typeof info?.id === "string" ? info.id : void 0;
|
|
@@ -10316,8 +9925,8 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10316
9925
|
};
|
|
10317
9926
|
|
|
10318
9927
|
// src/managers/pi-manager.ts
|
|
10319
|
-
import { mkdir as
|
|
10320
|
-
import { dirname as
|
|
9928
|
+
import { mkdir as mkdir16 } from "fs/promises";
|
|
9929
|
+
import { dirname as dirname10, join as join22 } from "path";
|
|
10321
9930
|
import {
|
|
10322
9931
|
AuthStorage,
|
|
10323
9932
|
createAgentSession,
|
|
@@ -10425,12 +10034,12 @@ var PiManager = class extends CodingAgentManager {
|
|
|
10425
10034
|
providerApiKey = null;
|
|
10426
10035
|
constructor(options) {
|
|
10427
10036
|
super(options);
|
|
10428
|
-
this.historyFilePath = options.historyFilePath ??
|
|
10037
|
+
this.historyFilePath = options.historyFilePath ?? join22(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
|
|
10429
10038
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
10430
10039
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
10431
10040
|
}
|
|
10432
10041
|
async initialize() {
|
|
10433
|
-
await
|
|
10042
|
+
await mkdir16(dirname10(this.historyFilePath), { recursive: true });
|
|
10434
10043
|
}
|
|
10435
10044
|
getHistorySink() {
|
|
10436
10045
|
return this.historyFile;
|
|
@@ -10530,7 +10139,7 @@ var PiManager = class extends CodingAgentManager {
|
|
|
10530
10139
|
const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
|
|
10531
10140
|
const resourceLoader = new DefaultResourceLoader({
|
|
10532
10141
|
cwd: this.workingDirectory,
|
|
10533
|
-
agentDir:
|
|
10142
|
+
agentDir: join22(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
|
|
10534
10143
|
extensionFactories: [registerCommandProtection(
|
|
10535
10144
|
this.workingDirectory,
|
|
10536
10145
|
this.historyFile,
|
|
@@ -11261,14 +10870,14 @@ async function uploadAnalytics(endpoint, body) {
|
|
|
11261
10870
|
// src/analytics/analytics-record-buffer.ts
|
|
11262
10871
|
import {
|
|
11263
10872
|
appendFile as appendFile3,
|
|
11264
|
-
mkdir as
|
|
11265
|
-
readFile as
|
|
10873
|
+
mkdir as mkdir17,
|
|
10874
|
+
readFile as readFile16,
|
|
11266
10875
|
readdir as readdir6,
|
|
11267
10876
|
rename as rename2,
|
|
11268
10877
|
unlink as unlink3
|
|
11269
10878
|
} from "fs/promises";
|
|
11270
|
-
import { existsSync as
|
|
11271
|
-
import { join as
|
|
10879
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, readdirSync } from "fs";
|
|
10880
|
+
import { join as join23 } from "path";
|
|
11272
10881
|
var MAX_FAILED_RECORDS = 500;
|
|
11273
10882
|
var FLUSH_DEBOUNCE_MS = 15e3;
|
|
11274
10883
|
var MAX_UPLOADED_SEGMENTS = 50;
|
|
@@ -11280,7 +10889,7 @@ var AnalyticsRecordBuffer = class {
|
|
|
11280
10889
|
options.storageName,
|
|
11281
10890
|
...options.legacyStorageNames ?? []
|
|
11282
10891
|
];
|
|
11283
|
-
this.liveFile =
|
|
10892
|
+
this.liveFile = join23(ENGINE_DIR2, `${options.storageName}.jsonl`);
|
|
11284
10893
|
this.segmentFilePatterns = this.storageNames.map(
|
|
11285
10894
|
(storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
|
|
11286
10895
|
);
|
|
@@ -11303,7 +10912,7 @@ var AnalyticsRecordBuffer = class {
|
|
|
11303
10912
|
if (this.recordIds.has(recordId)) return;
|
|
11304
10913
|
this.recordIds.add(recordId);
|
|
11305
10914
|
}
|
|
11306
|
-
const pending =
|
|
10915
|
+
const pending = mkdir17(ENGINE_DIR2, { recursive: true }).then(() => appendFile3(this.liveFile, `${JSON.stringify(record)}
|
|
11307
10916
|
`, "utf-8")).catch((error) => {
|
|
11308
10917
|
console.error(
|
|
11309
10918
|
`[${this.options.storageName}] Append failed, will retry on flush:`,
|
|
@@ -11315,10 +10924,10 @@ var AnalyticsRecordBuffer = class {
|
|
|
11315
10924
|
void pending.finally(() => this.pendingAppends.delete(pending));
|
|
11316
10925
|
}
|
|
11317
10926
|
readStoredRecords() {
|
|
11318
|
-
if (!
|
|
10927
|
+
if (!existsSync9(ENGINE_DIR2)) return [];
|
|
11319
10928
|
return readdirSync(ENGINE_DIR2, { withFileTypes: true }).filter((entry) => entry.isFile() && this.storageNames.some(
|
|
11320
10929
|
(storageName) => entry.name === `${storageName}.jsonl` || entry.name.startsWith(`${storageName}.`) && (entry.name.endsWith(".jsonl") || entry.name.endsWith(`.jsonl${UPLOADED_SUFFIX}`))
|
|
11321
|
-
)).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(
|
|
10930
|
+
)).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(readFileSync5(join23(ENGINE_DIR2, name), "utf-8"))).map((record) => {
|
|
11322
10931
|
const recordId = this.options.getRecordId?.(record);
|
|
11323
10932
|
if (recordId) this.recordIds.add(recordId);
|
|
11324
10933
|
return record;
|
|
@@ -11353,8 +10962,8 @@ var AnalyticsRecordBuffer = class {
|
|
|
11353
10962
|
}
|
|
11354
10963
|
for (const storageName of this.storageNames) {
|
|
11355
10964
|
await rename2(
|
|
11356
|
-
|
|
11357
|
-
|
|
10965
|
+
join23(ENGINE_DIR2, `${storageName}.jsonl`),
|
|
10966
|
+
join23(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
|
|
11358
10967
|
).catch(() => {
|
|
11359
10968
|
});
|
|
11360
10969
|
}
|
|
@@ -11364,7 +10973,7 @@ var AnalyticsRecordBuffer = class {
|
|
|
11364
10973
|
for (const entry of entries) {
|
|
11365
10974
|
if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry))) continue;
|
|
11366
10975
|
try {
|
|
11367
|
-
await this.uploadSegment(
|
|
10976
|
+
await this.uploadSegment(join23(ENGINE_DIR2, entry));
|
|
11368
10977
|
flushed++;
|
|
11369
10978
|
} catch (error) {
|
|
11370
10979
|
failed++;
|
|
@@ -11393,14 +11002,14 @@ var AnalyticsRecordBuffer = class {
|
|
|
11393
11002
|
(storageName) => entry.startsWith(`${storageName}.`)
|
|
11394
11003
|
) && entry.endsWith(UPLOADED_SUFFIX)).sort();
|
|
11395
11004
|
for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
|
|
11396
|
-
await unlink3(
|
|
11005
|
+
await unlink3(join23(ENGINE_DIR2, entry)).catch(() => {
|
|
11397
11006
|
});
|
|
11398
11007
|
}
|
|
11399
11008
|
}
|
|
11400
11009
|
return { flushed, failed };
|
|
11401
11010
|
}
|
|
11402
11011
|
async uploadSegment(filePath) {
|
|
11403
|
-
const records = this.parseRecords(await
|
|
11012
|
+
const records = this.parseRecords(await readFile16(filePath, "utf-8"));
|
|
11404
11013
|
if (records.length > 0) await this.options.upload(records);
|
|
11405
11014
|
await rename2(filePath, `${filePath}${UPLOADED_SUFFIX}`).catch(() => {
|
|
11406
11015
|
});
|
|
@@ -12186,19 +11795,19 @@ var KeepAliveService = class _KeepAliveService {
|
|
|
12186
11795
|
var keepAliveService = new KeepAliveService();
|
|
12187
11796
|
|
|
12188
11797
|
// src/services/canvas-service.ts
|
|
12189
|
-
import { readdir as readdir7, readFile as
|
|
12190
|
-
import { homedir as
|
|
12191
|
-
import { join as
|
|
11798
|
+
import { readdir as readdir7, readFile as readFile17, stat as stat3 } from "fs/promises";
|
|
11799
|
+
import { homedir as homedir12 } from "os";
|
|
11800
|
+
import { join as join24 } from "path";
|
|
12192
11801
|
var GLOBAL_CANVAS_DIRECTORIES = [
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
11802
|
+
join24(homedir12(), ".claude", "plans"),
|
|
11803
|
+
join24(process.env.XDG_DATA_HOME ?? join24(homedir12(), ".local", "share"), "opencode", "plans"),
|
|
11804
|
+
join24(homedir12(), ".replicas", "canvas")
|
|
12196
11805
|
];
|
|
12197
11806
|
async function canvasDirectories() {
|
|
12198
11807
|
const repositories = await gitService.listRepositories().catch(() => []);
|
|
12199
11808
|
return [
|
|
12200
11809
|
...GLOBAL_CANVAS_DIRECTORIES,
|
|
12201
|
-
...repositories.map((repository) =>
|
|
11810
|
+
...repositories.map((repository) => join24(repository.path, ".opencode", "plans"))
|
|
12202
11811
|
];
|
|
12203
11812
|
}
|
|
12204
11813
|
var CanvasService = class {
|
|
@@ -12222,7 +11831,7 @@ var CanvasService = class {
|
|
|
12222
11831
|
for (const entry of entries) {
|
|
12223
11832
|
if (entry.name.startsWith(".")) continue;
|
|
12224
11833
|
const filename = current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name;
|
|
12225
|
-
const filePath =
|
|
11834
|
+
const filePath = join24(current.directory, entry.name);
|
|
12226
11835
|
if (entry.isDirectory()) {
|
|
12227
11836
|
pending.push({ directory: filePath, relativePath: filename });
|
|
12228
11837
|
continue;
|
|
@@ -12247,7 +11856,7 @@ var CanvasService = class {
|
|
|
12247
11856
|
if (!safe) return null;
|
|
12248
11857
|
const { kind, mimeType } = classifyCanvasFilename(safe);
|
|
12249
11858
|
for (const directory of await this.directories()) {
|
|
12250
|
-
const filePath =
|
|
11859
|
+
const filePath = join24(directory, safe);
|
|
12251
11860
|
let sizeBytes = 0;
|
|
12252
11861
|
let updatedAt = "";
|
|
12253
11862
|
try {
|
|
@@ -12268,7 +11877,7 @@ var CanvasService = class {
|
|
|
12268
11877
|
};
|
|
12269
11878
|
}
|
|
12270
11879
|
try {
|
|
12271
|
-
const bytes = await
|
|
11880
|
+
const bytes = await readFile17(filePath);
|
|
12272
11881
|
return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
|
|
12273
11882
|
} catch {
|
|
12274
11883
|
continue;
|
|
@@ -12418,14 +12027,14 @@ async function reconcileCanvasItems(filenames) {
|
|
|
12418
12027
|
// src/services/upload-chat-transcripts.ts
|
|
12419
12028
|
import { createReadStream } from "fs";
|
|
12420
12029
|
import { createHash as createHash3 } from "crypto";
|
|
12421
|
-
import { readFile as
|
|
12422
|
-
import { basename as
|
|
12030
|
+
import { readFile as readFile18, readdir as readdir8, stat as stat4 } from "fs/promises";
|
|
12031
|
+
import { basename as basename3, join as join26 } from "path";
|
|
12423
12032
|
|
|
12424
12033
|
// src/services/chat/chat-senders.ts
|
|
12425
|
-
import { join as
|
|
12426
|
-
var CHAT_SENDERS_DIR =
|
|
12034
|
+
import { join as join25 } from "path";
|
|
12035
|
+
var CHAT_SENDERS_DIR = join25(ENGINE_DIR2, "chat-senders");
|
|
12427
12036
|
function chatMessageSendersFilePath(chatId) {
|
|
12428
|
-
return
|
|
12037
|
+
return join25(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
12429
12038
|
}
|
|
12430
12039
|
function parseChatMessageSendersJsonl(content) {
|
|
12431
12040
|
return content.split("\n").flatMap((line) => {
|
|
@@ -12441,9 +12050,9 @@ function parseChatMessageSendersJsonl(content) {
|
|
|
12441
12050
|
|
|
12442
12051
|
// src/services/upload-chat-transcripts.ts
|
|
12443
12052
|
var HISTORY_DIRS = [
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12053
|
+
join26(ENGINE_DIR2, "claude-histories"),
|
|
12054
|
+
join26(ENGINE_DIR2, "relay-histories"),
|
|
12055
|
+
join26(ENGINE_DIR2, "codex-histories")
|
|
12447
12056
|
];
|
|
12448
12057
|
async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), capture) {
|
|
12449
12058
|
let flushed = 0;
|
|
@@ -12459,9 +12068,9 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
12459
12068
|
}
|
|
12460
12069
|
for (const entry of entries) {
|
|
12461
12070
|
if (!entry.endsWith(".jsonl")) continue;
|
|
12462
|
-
const chatId =
|
|
12071
|
+
const chatId = basename3(entry, ".jsonl");
|
|
12463
12072
|
tasks.push(
|
|
12464
|
-
uploadChatTranscript(chatId,
|
|
12073
|
+
uploadChatTranscript(chatId, join26(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
|
|
12465
12074
|
flushed++;
|
|
12466
12075
|
if (artifact && capture) revisions.push(artifact);
|
|
12467
12076
|
}).catch((err) => {
|
|
@@ -12477,7 +12086,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
12477
12086
|
async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
12478
12087
|
const { size } = await stat4(filePath);
|
|
12479
12088
|
if (size === 0) return null;
|
|
12480
|
-
const historyPages = createChatTranscriptPages(await
|
|
12089
|
+
const historyPages = createChatTranscriptPages(await readFile18(filePath));
|
|
12481
12090
|
const metadata = chat ? {
|
|
12482
12091
|
provider: chat.provider,
|
|
12483
12092
|
credential: ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[chat.provider],
|
|
@@ -12496,7 +12105,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
|
12496
12105
|
}
|
|
12497
12106
|
try {
|
|
12498
12107
|
metadata.senders = parseChatMessageSendersJsonl(
|
|
12499
|
-
await
|
|
12108
|
+
await readFile18(chatMessageSendersFilePath(chatId), "utf-8")
|
|
12500
12109
|
);
|
|
12501
12110
|
} catch (error) {
|
|
12502
12111
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
@@ -12579,7 +12188,7 @@ async function flushRepoState() {
|
|
|
12579
12188
|
// src/services/upload-engine-logs.ts
|
|
12580
12189
|
import { createReadStream as createReadStream2 } from "fs";
|
|
12581
12190
|
import { readdir as readdir9, stat as stat5 } from "fs/promises";
|
|
12582
|
-
import { join as
|
|
12191
|
+
import { join as join27 } from "path";
|
|
12583
12192
|
var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
|
|
12584
12193
|
var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
|
|
12585
12194
|
var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
|
|
@@ -12608,7 +12217,7 @@ async function flushAllEngineLogs() {
|
|
|
12608
12217
|
const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
|
|
12609
12218
|
try {
|
|
12610
12219
|
const sessionId = filename.slice(0, -".log".length);
|
|
12611
|
-
const filePath =
|
|
12220
|
+
const filePath = join27(LOG_DIR, filename);
|
|
12612
12221
|
const fileStat = await runBeforeDeadline(() => stat5(filePath), deadline);
|
|
12613
12222
|
if (!fileStat.isFile()) {
|
|
12614
12223
|
skipped++;
|
|
@@ -12709,7 +12318,7 @@ function isCodexAvailable() {
|
|
|
12709
12318
|
return detectCodexAuthMethod() !== "none";
|
|
12710
12319
|
}
|
|
12711
12320
|
function isOpencodeAvailable() {
|
|
12712
|
-
return
|
|
12321
|
+
return existsSync10(OPENCODE_AUTH_PATH);
|
|
12713
12322
|
}
|
|
12714
12323
|
function isDeepseekAvailable() {
|
|
12715
12324
|
return Boolean(getDeepseekApiKey());
|
|
@@ -12721,7 +12330,7 @@ function isKimiAvailable() {
|
|
|
12721
12330
|
return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
|
|
12722
12331
|
}
|
|
12723
12332
|
function isPiAvailable() {
|
|
12724
|
-
return
|
|
12333
|
+
return existsSync10(PI_AUTH_PATH);
|
|
12725
12334
|
}
|
|
12726
12335
|
function isCursorAvailable() {
|
|
12727
12336
|
return Boolean(ENGINE_ENV.CURSOR_API_KEY);
|
|
@@ -12860,13 +12469,13 @@ var ChatService = class {
|
|
|
12860
12469
|
persistPromise = null;
|
|
12861
12470
|
persistQueued = false;
|
|
12862
12471
|
async initialize() {
|
|
12863
|
-
await
|
|
12864
|
-
await
|
|
12865
|
-
await
|
|
12866
|
-
await
|
|
12867
|
-
await
|
|
12868
|
-
await
|
|
12869
|
-
await
|
|
12472
|
+
await mkdir18(ENGINE_DIR2, { recursive: true });
|
|
12473
|
+
await mkdir18(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
12474
|
+
await mkdir18(RELAY_HISTORY_DIR, { recursive: true });
|
|
12475
|
+
await mkdir18(CODEX_HISTORY_DIR, { recursive: true });
|
|
12476
|
+
await mkdir18(CURSOR_HISTORY_DIR, { recursive: true });
|
|
12477
|
+
await mkdir18(OPENCODE_HISTORY_DIR, { recursive: true });
|
|
12478
|
+
await mkdir18(CHAT_SENDERS_DIR, { recursive: true });
|
|
12870
12479
|
const persisted = await this.loadChats();
|
|
12871
12480
|
for (const chat of persisted) {
|
|
12872
12481
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -13066,7 +12675,7 @@ var ChatService = class {
|
|
|
13066
12675
|
}
|
|
13067
12676
|
}
|
|
13068
12677
|
async readSenders(chatId) {
|
|
13069
|
-
return
|
|
12678
|
+
return readFile19(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
|
|
13070
12679
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
|
|
13071
12680
|
console.error("[ChatService] Failed to read sender records:", error);
|
|
13072
12681
|
return [];
|
|
@@ -13305,13 +12914,13 @@ var ChatService = class {
|
|
|
13305
12914
|
return descendants;
|
|
13306
12915
|
}
|
|
13307
12916
|
async deleteHistoryFile(persisted) {
|
|
13308
|
-
const historyPath =
|
|
12917
|
+
const historyPath = join28(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
|
|
13309
12918
|
await Promise.all([
|
|
13310
12919
|
historyPath,
|
|
13311
12920
|
`${historyPath}.pages.jsonl`,
|
|
13312
12921
|
`${historyPath}.pages.index.json`
|
|
13313
|
-
].map((path6) =>
|
|
13314
|
-
await
|
|
12922
|
+
].map((path6) => rm3(path6, { force: true })));
|
|
12923
|
+
await rm3(chatMessageSendersFilePath(persisted.id), { force: true });
|
|
13315
12924
|
}
|
|
13316
12925
|
async getChatHistory(chatId, page = {}) {
|
|
13317
12926
|
const chat = this.requireChat(chatId);
|
|
@@ -13436,7 +13045,7 @@ var ChatService = class {
|
|
|
13436
13045
|
if (persisted.provider === "claude") {
|
|
13437
13046
|
provider = new ClaudeManager({
|
|
13438
13047
|
workingDirectory: this.workingDirectory,
|
|
13439
|
-
historyFilePath:
|
|
13048
|
+
historyFilePath: join28(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13440
13049
|
initialSessionId: persisted.providerSessionId,
|
|
13441
13050
|
onSaveSessionId: saveSession,
|
|
13442
13051
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13456,7 +13065,7 @@ var ChatService = class {
|
|
|
13456
13065
|
});
|
|
13457
13066
|
provider = new RelayManager({
|
|
13458
13067
|
workingDirectory: this.workingDirectory,
|
|
13459
|
-
historyFilePath:
|
|
13068
|
+
historyFilePath: join28(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13460
13069
|
initialSessionId: persisted.providerSessionId,
|
|
13461
13070
|
onSaveSessionId: saveSession,
|
|
13462
13071
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13470,7 +13079,7 @@ var ChatService = class {
|
|
|
13470
13079
|
} else if (persisted.provider === "cursor") {
|
|
13471
13080
|
provider = new CursorManager({
|
|
13472
13081
|
workingDirectory: this.workingDirectory,
|
|
13473
|
-
historyFilePath:
|
|
13082
|
+
historyFilePath: join28(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13474
13083
|
initialSessionId: persisted.providerSessionId,
|
|
13475
13084
|
onSaveSessionId: saveSession,
|
|
13476
13085
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13482,7 +13091,7 @@ var ChatService = class {
|
|
|
13482
13091
|
} else if (persisted.provider === "deepseek") {
|
|
13483
13092
|
provider = new DeepseekManager({
|
|
13484
13093
|
workingDirectory: this.workingDirectory,
|
|
13485
|
-
historyFilePath:
|
|
13094
|
+
historyFilePath: join28(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13486
13095
|
initialSessionId: persisted.providerSessionId,
|
|
13487
13096
|
onSaveSessionId: saveSession,
|
|
13488
13097
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13494,7 +13103,7 @@ var ChatService = class {
|
|
|
13494
13103
|
} else if (persisted.provider === "fx") {
|
|
13495
13104
|
provider = new FxManager({
|
|
13496
13105
|
workingDirectory: this.workingDirectory,
|
|
13497
|
-
historyFilePath:
|
|
13106
|
+
historyFilePath: join28(FX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13498
13107
|
initialSessionId: persisted.providerSessionId,
|
|
13499
13108
|
onSaveSessionId: saveSession,
|
|
13500
13109
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13505,7 +13114,7 @@ var ChatService = class {
|
|
|
13505
13114
|
} else if (persisted.provider === "kimi") {
|
|
13506
13115
|
provider = new KimiManager({
|
|
13507
13116
|
workingDirectory: this.workingDirectory,
|
|
13508
|
-
historyFilePath:
|
|
13117
|
+
historyFilePath: join28(KIMI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13509
13118
|
initialSessionId: persisted.providerSessionId,
|
|
13510
13119
|
onSaveSessionId: saveSession,
|
|
13511
13120
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13516,7 +13125,7 @@ var ChatService = class {
|
|
|
13516
13125
|
} else if (persisted.provider === "opencode") {
|
|
13517
13126
|
provider = new OpencodeManager({
|
|
13518
13127
|
workingDirectory: this.workingDirectory,
|
|
13519
|
-
historyFilePath:
|
|
13128
|
+
historyFilePath: join28(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13520
13129
|
initialSessionId: persisted.providerSessionId,
|
|
13521
13130
|
onSaveSessionId: saveSession,
|
|
13522
13131
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13528,7 +13137,7 @@ var ChatService = class {
|
|
|
13528
13137
|
} else if (persisted.provider === "pi") {
|
|
13529
13138
|
provider = new PiManager({
|
|
13530
13139
|
workingDirectory: this.workingDirectory,
|
|
13531
|
-
historyFilePath:
|
|
13140
|
+
historyFilePath: join28(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13532
13141
|
initialSessionId: persisted.providerSessionId,
|
|
13533
13142
|
onSaveSessionId: saveSession,
|
|
13534
13143
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13545,7 +13154,7 @@ var ChatService = class {
|
|
|
13545
13154
|
);
|
|
13546
13155
|
provider = new CodexAspManager({
|
|
13547
13156
|
workingDirectory: this.workingDirectory,
|
|
13548
|
-
historyFilePath:
|
|
13157
|
+
historyFilePath: join28(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13549
13158
|
initialSessionId: persisted.providerSessionId,
|
|
13550
13159
|
onSaveSessionId: saveSession,
|
|
13551
13160
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13753,7 +13362,7 @@ var ChatService = class {
|
|
|
13753
13362
|
});
|
|
13754
13363
|
uploadChatTranscript(
|
|
13755
13364
|
chatId,
|
|
13756
|
-
|
|
13365
|
+
join28(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
|
|
13757
13366
|
this.toSummary(chat)
|
|
13758
13367
|
).catch((err) => {
|
|
13759
13368
|
console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
|
|
@@ -13769,7 +13378,7 @@ var ChatService = class {
|
|
|
13769
13378
|
}
|
|
13770
13379
|
async loadChats() {
|
|
13771
13380
|
try {
|
|
13772
|
-
const content = await
|
|
13381
|
+
const content = await readFile19(CHATS_FILE, "utf-8");
|
|
13773
13382
|
return parsePersistedChatsContent(content);
|
|
13774
13383
|
} catch (error) {
|
|
13775
13384
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -13784,7 +13393,7 @@ var ChatService = class {
|
|
|
13784
13393
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
13785
13394
|
}
|
|
13786
13395
|
try {
|
|
13787
|
-
const backupContent = await
|
|
13396
|
+
const backupContent = await readFile19(CHATS_BACKUP_FILE, "utf-8");
|
|
13788
13397
|
return parsePersistedChatsContent(backupContent);
|
|
13789
13398
|
} catch (backupError) {
|
|
13790
13399
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -13805,7 +13414,7 @@ var ChatService = class {
|
|
|
13805
13414
|
this.persistQueued = false;
|
|
13806
13415
|
const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
|
|
13807
13416
|
try {
|
|
13808
|
-
await
|
|
13417
|
+
await copyFile2(CHATS_FILE, CHATS_BACKUP_FILE);
|
|
13809
13418
|
} catch (error) {
|
|
13810
13419
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
|
|
13811
13420
|
console.error("[ChatService] Failed to update chats backup:", error);
|
|
@@ -13888,8 +13497,8 @@ var ChatService = class {
|
|
|
13888
13497
|
|
|
13889
13498
|
// src/services/repo-file-service.ts
|
|
13890
13499
|
import { execFile } from "child_process";
|
|
13891
|
-
import { readFile as
|
|
13892
|
-
import { join as
|
|
13500
|
+
import { readFile as readFile20, realpath, stat as stat6 } from "fs/promises";
|
|
13501
|
+
import { join as join29, resolve as resolve2, extname as extname2 } from "path";
|
|
13893
13502
|
var CACHE_TTL_MS = 3e4;
|
|
13894
13503
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
13895
13504
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -13941,11 +13550,11 @@ function scoreMatch(query2, filePath) {
|
|
|
13941
13550
|
const lowerQuery = query2.toLowerCase();
|
|
13942
13551
|
const lowerPath = filePath.toLowerCase();
|
|
13943
13552
|
const segments = lowerPath.split("/");
|
|
13944
|
-
const
|
|
13945
|
-
if (
|
|
13946
|
-
if (
|
|
13553
|
+
const basename4 = segments[segments.length - 1] ?? "";
|
|
13554
|
+
if (basename4 === lowerQuery) return 100;
|
|
13555
|
+
if (basename4.startsWith(lowerQuery)) return 90;
|
|
13947
13556
|
if (segments.some((seg) => seg === lowerQuery)) return 80;
|
|
13948
|
-
if (
|
|
13557
|
+
if (basename4.includes(lowerQuery)) return 70;
|
|
13949
13558
|
if (segments.some((seg) => seg.includes(lowerQuery))) return 60;
|
|
13950
13559
|
if (lowerPath.includes(lowerQuery)) return 50;
|
|
13951
13560
|
return 0;
|
|
@@ -14041,7 +13650,7 @@ var RepoFileService = class {
|
|
|
14041
13650
|
const repo = repos.find((r) => r.name === repoName);
|
|
14042
13651
|
if (!repo) return null;
|
|
14043
13652
|
try {
|
|
14044
|
-
const fullPath = await realpath(resolve2(
|
|
13653
|
+
const fullPath = await realpath(resolve2(join29(repo.path, filePath)));
|
|
14045
13654
|
const repoRoot = await realpath(repo.path);
|
|
14046
13655
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
14047
13656
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
@@ -14072,7 +13681,7 @@ var RepoFileService = class {
|
|
|
14072
13681
|
sizeBytes,
|
|
14073
13682
|
binary: true,
|
|
14074
13683
|
tooLarge: false,
|
|
14075
|
-
base64: (await
|
|
13684
|
+
base64: (await readFile20(fullPath)).toString("base64"),
|
|
14076
13685
|
mimeType
|
|
14077
13686
|
};
|
|
14078
13687
|
}
|
|
@@ -14097,7 +13706,7 @@ var RepoFileService = class {
|
|
|
14097
13706
|
tooLarge: true
|
|
14098
13707
|
};
|
|
14099
13708
|
}
|
|
14100
|
-
const content = await
|
|
13709
|
+
const content = await readFile20(fullPath, "utf-8");
|
|
14101
13710
|
return {
|
|
14102
13711
|
repoName,
|
|
14103
13712
|
path: filePath,
|
|
@@ -14175,21 +13784,21 @@ var RepoFileService = class {
|
|
|
14175
13784
|
// src/v1-routes.ts
|
|
14176
13785
|
import { Hono } from "hono";
|
|
14177
13786
|
import { z as z5 } from "zod";
|
|
14178
|
-
import { readdir as readdir11, stat as stat7, readFile as
|
|
14179
|
-
import { join as
|
|
13787
|
+
import { readdir as readdir11, stat as stat7, readFile as readFile23 } from "fs/promises";
|
|
13788
|
+
import { join as join32, resolve as resolve3 } from "path";
|
|
14180
13789
|
|
|
14181
13790
|
// src/services/warm-hooks-service.ts
|
|
14182
13791
|
import { spawn as spawn6 } from "child_process";
|
|
14183
|
-
import { readFile as
|
|
14184
|
-
import { existsSync as
|
|
14185
|
-
import { join as
|
|
13792
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
13793
|
+
import { existsSync as existsSync11 } from "fs";
|
|
13794
|
+
import { join as join31 } from "path";
|
|
14186
13795
|
|
|
14187
13796
|
// src/services/warm-hook-logs-service.ts
|
|
14188
|
-
import { mkdir as
|
|
14189
|
-
import { homedir as
|
|
14190
|
-
import { join as
|
|
14191
|
-
var LOGS_DIR2 =
|
|
14192
|
-
var CURRENT_RUN_LOG =
|
|
13797
|
+
import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile9, readdir as readdir10, appendFile as appendFile5, unlink as unlink4 } from "fs/promises";
|
|
13798
|
+
import { homedir as homedir13 } from "os";
|
|
13799
|
+
import { join as join30 } from "path";
|
|
13800
|
+
var LOGS_DIR2 = join30(homedir13(), ".replicas", "warm-hook-logs");
|
|
13801
|
+
var CURRENT_RUN_LOG = join30(LOGS_DIR2, "current-run.log");
|
|
14193
13802
|
var GLOBAL_FILENAME = "global.json";
|
|
14194
13803
|
function withPreview2(stored) {
|
|
14195
13804
|
const preview = buildHookOutputPreview(stored.output);
|
|
@@ -14197,7 +13806,7 @@ function withPreview2(stored) {
|
|
|
14197
13806
|
}
|
|
14198
13807
|
var WarmHookLogsService = class {
|
|
14199
13808
|
async ensureDir() {
|
|
14200
|
-
await
|
|
13809
|
+
await mkdir19(LOGS_DIR2, { recursive: true });
|
|
14201
13810
|
}
|
|
14202
13811
|
async saveGlobalHookLog(entry) {
|
|
14203
13812
|
await this.ensureDir();
|
|
@@ -14206,7 +13815,7 @@ var WarmHookLogsService = class {
|
|
|
14206
13815
|
hookName: "organization",
|
|
14207
13816
|
...entry
|
|
14208
13817
|
};
|
|
14209
|
-
await
|
|
13818
|
+
await writeFile9(join30(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
14210
13819
|
`, "utf-8");
|
|
14211
13820
|
}
|
|
14212
13821
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -14216,7 +13825,7 @@ var WarmHookLogsService = class {
|
|
|
14216
13825
|
hookName: "environment",
|
|
14217
13826
|
...entry
|
|
14218
13827
|
};
|
|
14219
|
-
await
|
|
13828
|
+
await writeFile9(join30(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
14220
13829
|
`, "utf-8");
|
|
14221
13830
|
}
|
|
14222
13831
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -14226,7 +13835,7 @@ var WarmHookLogsService = class {
|
|
|
14226
13835
|
hookName: repoName,
|
|
14227
13836
|
...entry
|
|
14228
13837
|
};
|
|
14229
|
-
await
|
|
13838
|
+
await writeFile9(join30(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
14230
13839
|
`, "utf-8");
|
|
14231
13840
|
}
|
|
14232
13841
|
async getAllLogs() {
|
|
@@ -14245,7 +13854,7 @@ var WarmHookLogsService = class {
|
|
|
14245
13854
|
continue;
|
|
14246
13855
|
}
|
|
14247
13856
|
try {
|
|
14248
|
-
const raw = await
|
|
13857
|
+
const raw = await readFile21(join30(LOGS_DIR2, file), "utf-8");
|
|
14249
13858
|
const stored = JSON.parse(raw);
|
|
14250
13859
|
logs.push(withPreview2(stored));
|
|
14251
13860
|
} catch {
|
|
@@ -14274,7 +13883,7 @@ var WarmHookLogsService = class {
|
|
|
14274
13883
|
}
|
|
14275
13884
|
async getCurrentRunLog() {
|
|
14276
13885
|
try {
|
|
14277
|
-
return await
|
|
13886
|
+
return await readFile21(CURRENT_RUN_LOG, "utf-8");
|
|
14278
13887
|
} catch (err) {
|
|
14279
13888
|
if (err.code === "ENOENT") return null;
|
|
14280
13889
|
throw err;
|
|
@@ -14283,7 +13892,7 @@ var WarmHookLogsService = class {
|
|
|
14283
13892
|
async getFullOutput(hookType, hookName) {
|
|
14284
13893
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
14285
13894
|
try {
|
|
14286
|
-
const raw = await
|
|
13895
|
+
const raw = await readFile21(join30(LOGS_DIR2, filename), "utf-8");
|
|
14287
13896
|
const stored = JSON.parse(raw);
|
|
14288
13897
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
14289
13898
|
return null;
|
|
@@ -14302,12 +13911,12 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
14302
13911
|
// src/services/warm-hooks-service.ts
|
|
14303
13912
|
async function readRepoWarmHook(repoPath) {
|
|
14304
13913
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
14305
|
-
const configPath =
|
|
14306
|
-
if (!
|
|
13914
|
+
const configPath = join31(repoPath, filename);
|
|
13915
|
+
if (!existsSync11(configPath)) {
|
|
14307
13916
|
continue;
|
|
14308
13917
|
}
|
|
14309
13918
|
try {
|
|
14310
|
-
const raw = await
|
|
13919
|
+
const raw = await readFile22(configPath, "utf-8");
|
|
14311
13920
|
const config = parseReplicasConfigString(raw, filename);
|
|
14312
13921
|
if (!config.warmHook) {
|
|
14313
13922
|
return null;
|
|
@@ -14564,7 +14173,7 @@ ${combinedScript}` : combinedScript;
|
|
|
14564
14173
|
|
|
14565
14174
|
// src/services/terminal-service.ts
|
|
14566
14175
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
14567
|
-
import { existsSync as
|
|
14176
|
+
import { existsSync as existsSync12 } from "fs";
|
|
14568
14177
|
import { spawn as spawn7 } from "node-pty";
|
|
14569
14178
|
var MAX_REPLAY_CHARS = 1024 * 1024;
|
|
14570
14179
|
var MAX_TERMINAL_SESSIONS = 8;
|
|
@@ -14583,7 +14192,7 @@ var TerminalService = class {
|
|
|
14583
14192
|
});
|
|
14584
14193
|
}
|
|
14585
14194
|
const id = randomUUID10();
|
|
14586
|
-
const shell = process.env.SHELL &&
|
|
14195
|
+
const shell = process.env.SHELL && existsSync12(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
|
|
14587
14196
|
const pty = spawn7(shell, ["-l"], {
|
|
14588
14197
|
name: "xterm-256color",
|
|
14589
14198
|
cols,
|
|
@@ -15514,7 +15123,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
15514
15123
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
15515
15124
|
const sessions = await Promise.all(
|
|
15516
15125
|
logFiles.map(async (filename) => {
|
|
15517
|
-
const filePath =
|
|
15126
|
+
const filePath = join32(LOG_DIR, filename);
|
|
15518
15127
|
const fileStat = await stat7(filePath);
|
|
15519
15128
|
const sessionId = filename.replace(/\.log$/, "");
|
|
15520
15129
|
return {
|
|
@@ -15549,7 +15158,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
15549
15158
|
}
|
|
15550
15159
|
let content;
|
|
15551
15160
|
try {
|
|
15552
|
-
content = await
|
|
15161
|
+
content = await readFile23(filePath, "utf-8");
|
|
15553
15162
|
} catch {
|
|
15554
15163
|
return c.json(jsonError("Log session not found"), 404);
|
|
15555
15164
|
}
|
|
@@ -15620,17 +15229,17 @@ var HeartbeatService = class _HeartbeatService {
|
|
|
15620
15229
|
var heartbeatService = new HeartbeatService();
|
|
15621
15230
|
|
|
15622
15231
|
// src/services/workspace-sdk-service.ts
|
|
15623
|
-
import { cp, mkdir as
|
|
15624
|
-
import { dirname as
|
|
15625
|
-
import { fileURLToPath as
|
|
15232
|
+
import { cp, mkdir as mkdir20 } from "fs/promises";
|
|
15233
|
+
import { dirname as dirname11, resolve as resolve4 } from "path";
|
|
15234
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15626
15235
|
async function installWorkspaceSdk() {
|
|
15627
|
-
const source = resolve4(
|
|
15236
|
+
const source = resolve4(dirname11(fileURLToPath4(import.meta.url)), "../../workspace-sdk");
|
|
15628
15237
|
const targets = [
|
|
15629
15238
|
resolve4(ENGINE_ENV.HOME_DIR, "node_modules/@replicas/sdk"),
|
|
15630
15239
|
"/tmp/node_modules/@replicas/sdk"
|
|
15631
15240
|
];
|
|
15632
15241
|
await Promise.all(targets.map(async (target) => {
|
|
15633
|
-
await
|
|
15242
|
+
await mkdir20(target, { recursive: true });
|
|
15634
15243
|
await cp(source, target, { recursive: true, force: true });
|
|
15635
15244
|
}));
|
|
15636
15245
|
}
|
|
@@ -15658,7 +15267,7 @@ async function timeStartupStep(name, fn) {
|
|
|
15658
15267
|
async function waitForInitializationGate() {
|
|
15659
15268
|
if (!ENGINE_ENV.REPLICAS_ENGINE_DEFER_INITIALIZATION) return;
|
|
15660
15269
|
const deadline = Date.now() + ENGINE_INIT_GATE_TIMEOUT_MS;
|
|
15661
|
-
while (!
|
|
15270
|
+
while (!existsSync13(SANDBOX_PATHS.ENGINE_INIT_GATE)) {
|
|
15662
15271
|
if (Date.now() >= deadline) {
|
|
15663
15272
|
throw new Error("Timed out waiting for deferred engine initialization gate");
|
|
15664
15273
|
}
|