replicas-engine 0.1.702 → 0.1.704
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-2RB7SIP3.js +10 -0
- package/dist/src/chunk-EC4MFY4M.js +551 -0
- package/dist/src/chunk-GDCMH7YA.js +23 -0
- package/dist/src/{chunk-UNES5UCP.js → chunk-WUJ4IALO.js} +42 -544
- package/dist/src/chunk-ZCBMGGAC.js +284 -0
- package/dist/src/command-protection-hook.js +50 -0
- package/dist/src/deepseek-command-protection-plugin.js +32 -0
- package/dist/src/headless-agent.js +6 -4
- package/dist/src/index.js +521 -818
- package/dist/src/post-tool-pr-hook.js +26 -0
- package/package.json +4 -1
- package/scripts/deepseek/replicas.cordis.patch.yml +4 -0
- package/scripts/opencode-command-protection-plugin.ts +88 -0
package/dist/src/index.js
CHANGED
|
@@ -1,9 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
ENGINE_ENV,
|
|
4
|
+
IS_WARMING_MODE,
|
|
5
|
+
evaluateCommandProtection,
|
|
6
|
+
extractToolCommand,
|
|
7
|
+
monolithRequest,
|
|
8
|
+
monolithService,
|
|
9
|
+
reportCommandProtectionBlock,
|
|
10
|
+
setAgentCredentialSnapshot
|
|
11
|
+
} from "./chunk-ZCBMGGAC.js";
|
|
12
|
+
import {
|
|
4
13
|
ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
|
|
5
|
-
AGENT,
|
|
6
14
|
AGENT_MESSAGE_DELTA_METHOD,
|
|
15
|
+
AppServerProcess,
|
|
16
|
+
COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
|
|
17
|
+
FILE_CHANGE_OUTPUT_DELTA_METHOD,
|
|
18
|
+
ITEM_COMPLETED_METHOD,
|
|
19
|
+
ITEM_STARTED_METHOD,
|
|
20
|
+
MODEL_REROUTED_METHOD,
|
|
21
|
+
REASONING_SUMMARY_PART_ADDED_METHOD,
|
|
22
|
+
REASONING_SUMMARY_TEXT_DELTA_METHOD,
|
|
23
|
+
REASONING_TEXT_DELTA_METHOD,
|
|
24
|
+
SUBPROCESS_MAX_BUFFER,
|
|
25
|
+
THREAD_COMPACTED_METHOD,
|
|
26
|
+
THREAD_GOAL_CLEARED_METHOD,
|
|
27
|
+
THREAD_GOAL_UPDATED_METHOD,
|
|
28
|
+
THREAD_TOKEN_USAGE_UPDATED_METHOD,
|
|
29
|
+
TURN_COMPLETED_METHOD,
|
|
30
|
+
TURN_PLAN_UPDATED_METHOD,
|
|
31
|
+
TURN_STARTED_METHOD,
|
|
32
|
+
buildCodexAgentEnv,
|
|
33
|
+
dispatchAspNotification,
|
|
34
|
+
execAsync,
|
|
35
|
+
execFileAsync,
|
|
36
|
+
putPresignedFile,
|
|
37
|
+
recoverCompletedTurn
|
|
38
|
+
} from "./chunk-EC4MFY4M.js";
|
|
39
|
+
import {
|
|
40
|
+
isRecord as isRecord2
|
|
41
|
+
} from "./chunk-2RB7SIP3.js";
|
|
42
|
+
import {
|
|
43
|
+
ACCEPTED_USER_MESSAGE_SOURCE,
|
|
44
|
+
AGENT,
|
|
7
45
|
AGENT_MODELS,
|
|
8
46
|
AI_GATEWAY_BASE_URL,
|
|
9
47
|
ASTER_BASE_URL,
|
|
@@ -12,7 +50,6 @@ import {
|
|
|
12
50
|
ASTER_PROVIDER,
|
|
13
51
|
AUTH_FALLBACK_EVENT_TYPE,
|
|
14
52
|
AUTH_RETRY_STATUS_EVENT_TYPE,
|
|
15
|
-
AppServerProcess,
|
|
16
53
|
CHAT_GOAL_EVENT_TYPE,
|
|
17
54
|
CHAT_INTERRUPTED_EVENT_TYPE,
|
|
18
55
|
CLAUDE_AUTH_ENV_KEYS,
|
|
@@ -32,7 +69,6 @@ import {
|
|
|
32
69
|
CODEX_AUTH_ENV_KEYS,
|
|
33
70
|
CODEX_AUTH_ENV_KEYS_BY_METHOD,
|
|
34
71
|
CODEX_QUOTA_STATUS_EVENT_TYPE,
|
|
35
|
-
COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
|
|
36
72
|
COMPACTION_STATUS_EVENT_TYPE,
|
|
37
73
|
CONTEXT_USAGE_EVENT_TYPE,
|
|
38
74
|
CURSOR_CONNECTION_STATUS_EVENT_TYPE,
|
|
@@ -52,14 +88,11 @@ import {
|
|
|
52
88
|
ENGINE_HEALTH_MAX_WAIT_MS,
|
|
53
89
|
ENGINE_HEALTH_WAIT_HEADER,
|
|
54
90
|
ENGINE_HEALTH_WAIT_QUERY_PARAM,
|
|
55
|
-
FILE_CHANGE_OUTPUT_DELTA_METHOD,
|
|
56
91
|
GITHUB_CREDENTIAL_REFRESH_PATH,
|
|
57
92
|
GITLAB_CREDENTIAL_REFRESH_PATH,
|
|
58
93
|
GIT_CREDENTIAL_HELPER_FILENAME,
|
|
59
94
|
HOOK_EXEC_MAX_BUFFER_BYTES,
|
|
60
95
|
IMAGE_MEDIA_TYPES,
|
|
61
|
-
ITEM_COMPLETED_METHOD,
|
|
62
|
-
ITEM_STARTED_METHOD,
|
|
63
96
|
InFlightMap,
|
|
64
97
|
MAX_CANVAS_FILE_BYTES,
|
|
65
98
|
MAX_CANVAS_PREVIEW_BYTES,
|
|
@@ -68,34 +101,19 @@ import {
|
|
|
68
101
|
MEMORY_ROOT,
|
|
69
102
|
MEMORY_SUMMARY_FILENAME,
|
|
70
103
|
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
104
|
REPLICAS_CONFIG_FILENAMES,
|
|
76
|
-
REPLICAS_RUNTIME_ENV_ALIASES,
|
|
77
105
|
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
106
|
TIMEOUT,
|
|
84
107
|
TOKEN_USAGE_PATH,
|
|
85
|
-
TURN_COMPLETED_METHOD,
|
|
86
|
-
TURN_PLAN_UPDATED_METHOD,
|
|
87
|
-
TURN_STARTED_METHOD,
|
|
88
108
|
TaskAccumulator,
|
|
89
109
|
USER_MESSAGE_ID_PAYLOAD_KEY,
|
|
90
110
|
VALID_AGENT_PROVIDERS,
|
|
91
111
|
VALID_RELAY_SUBAGENT_PROVIDERS,
|
|
92
112
|
VALID_THINKING_LEVELS,
|
|
93
|
-
agentCredentialSnapshotSchema,
|
|
94
113
|
applyCodexAspTranscriptDelta,
|
|
95
114
|
areSameUserMessageEvents,
|
|
96
115
|
areUserMessagesWithinMatchWindow,
|
|
97
116
|
autoCompactThresholdField,
|
|
98
|
-
buildCodexAgentEnv,
|
|
99
117
|
buildCodexTokenUsageContextUsagePayload,
|
|
100
118
|
buildGitCredentialHelperScript,
|
|
101
119
|
buildHookOutputPreview,
|
|
@@ -122,16 +140,10 @@ import {
|
|
|
122
140
|
decodePathSegments,
|
|
123
141
|
detectAgentQuotaLimit,
|
|
124
142
|
detectLanguageByPath,
|
|
125
|
-
dispatchAspNotification,
|
|
126
|
-
execAsync,
|
|
127
|
-
execFileAsync,
|
|
128
|
-
extractCommandProtectionCommandText,
|
|
129
143
|
extractErrorText,
|
|
130
144
|
extractToolResultText,
|
|
131
145
|
fetchAiGatewayModels,
|
|
132
146
|
fetchModelsDevCatalog,
|
|
133
|
-
findGitCommitSignals,
|
|
134
|
-
findPrMergeSignals,
|
|
135
147
|
getChatHistoryPageSenders,
|
|
136
148
|
getChatHistoryPageWindow,
|
|
137
149
|
getChatTranscriptHistorySize,
|
|
@@ -172,6 +184,7 @@ import {
|
|
|
172
184
|
isTransientErrorText,
|
|
173
185
|
isValidAgentProvider,
|
|
174
186
|
isValidEngineLogSessionId,
|
|
187
|
+
mayCreatePullRequest,
|
|
175
188
|
mergeSlashCommands,
|
|
176
189
|
normalizeAutoCompactThreshold,
|
|
177
190
|
normalizeClaudeModel,
|
|
@@ -187,179 +200,30 @@ import {
|
|
|
187
200
|
parseGitRemote,
|
|
188
201
|
parseLatestCodexAspTranscriptTurn,
|
|
189
202
|
parseMcpToolName,
|
|
190
|
-
parsePosixEnvFile,
|
|
191
203
|
parseReplicasConfigString,
|
|
192
204
|
percentage,
|
|
193
|
-
putPresignedFile,
|
|
194
205
|
raceWithTimeout,
|
|
195
206
|
readJsonlPage,
|
|
196
|
-
readReplicasRuntimeEnv,
|
|
197
|
-
recoverCompletedTurn,
|
|
198
207
|
renumberCodexAspTranscriptItems,
|
|
199
208
|
resolveWarmHookConfig,
|
|
200
209
|
sanitizeCanvasFilename,
|
|
201
210
|
sendChatMessageRequestSchema,
|
|
202
211
|
serializeCanvasContentResponse,
|
|
203
212
|
shellQuotePosix,
|
|
204
|
-
stripAgentDiagnosticErrors
|
|
205
|
-
|
|
213
|
+
stripAgentDiagnosticErrors,
|
|
214
|
+
withTimeout
|
|
215
|
+
} from "./chunk-WUJ4IALO.js";
|
|
206
216
|
|
|
207
217
|
// src/index.ts
|
|
208
218
|
import { serve } from "@hono/node-server";
|
|
209
219
|
import { Hono as Hono2 } from "hono";
|
|
210
|
-
import { existsSync as
|
|
220
|
+
import { existsSync as existsSync13 } from "fs";
|
|
211
221
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
212
222
|
import { connect } from "net";
|
|
213
223
|
|
|
214
224
|
// src/managers/github-token-manager.ts
|
|
215
225
|
import path from "path";
|
|
216
226
|
|
|
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
227
|
// src/services/credential-fallbacks.ts
|
|
364
228
|
var fallbacksByAgent = /* @__PURE__ */ new Map();
|
|
365
229
|
var exhaustedByAgent = /* @__PURE__ */ new Map();
|
|
@@ -519,69 +383,10 @@ var BaseRefreshManager = class {
|
|
|
519
383
|
}
|
|
520
384
|
};
|
|
521
385
|
|
|
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
386
|
// src/utils/file.ts
|
|
582
387
|
import { randomUUID } from "crypto";
|
|
583
388
|
import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
|
|
584
|
-
import { dirname, join
|
|
389
|
+
import { dirname, join } from "path";
|
|
585
390
|
|
|
586
391
|
// src/utils/async-lock.ts
|
|
587
392
|
var AsyncLock = class {
|
|
@@ -614,8 +419,8 @@ async function writeSecureCredentialFile(filePath, content, options) {
|
|
|
614
419
|
await atomicWriteFile(filePath, content, { mode: 384 });
|
|
615
420
|
}
|
|
616
421
|
async function configureManagedGitCredentialHelper(homeDir) {
|
|
617
|
-
const credentialsPath =
|
|
618
|
-
const helperPath =
|
|
422
|
+
const credentialsPath = join(homeDir, ".git-credentials");
|
|
423
|
+
const helperPath = join(homeDir, GIT_CREDENTIAL_HELPER_FILENAME);
|
|
619
424
|
await atomicWriteFile(helperPath, buildGitCredentialHelperScript(credentialsPath), { mode: 448 });
|
|
620
425
|
await execFileAsync("git", ["config", "--global", "credential.helper", helperPath], {
|
|
621
426
|
env: { ...process.env, HOME: homeDir }
|
|
@@ -650,15 +455,15 @@ function removeCredentialFileLines(filePath, shouldRemove) {
|
|
|
650
455
|
// src/git/service.ts
|
|
651
456
|
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
652
457
|
import { spawn } from "child_process";
|
|
653
|
-
import { join as
|
|
458
|
+
import { join as join4 } from "path";
|
|
654
459
|
|
|
655
460
|
// src/utils/state.ts
|
|
656
461
|
import { readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
657
462
|
import { existsSync } from "fs";
|
|
658
|
-
import { join as
|
|
659
|
-
import { homedir
|
|
660
|
-
var STATE_DIR =
|
|
661
|
-
var STATE_FILE =
|
|
463
|
+
import { join as join2 } from "path";
|
|
464
|
+
import { homedir } from "os";
|
|
465
|
+
var STATE_DIR = join2(homedir(), ".replicas");
|
|
466
|
+
var STATE_FILE = join2(STATE_DIR, "engine-state.json");
|
|
662
467
|
var DEFAULT_STATE = {
|
|
663
468
|
repos: {}
|
|
664
469
|
};
|
|
@@ -755,8 +560,8 @@ async function saveRepoState(repoName, state, fallbackState) {
|
|
|
755
560
|
}
|
|
756
561
|
|
|
757
562
|
// src/git/commands.ts
|
|
758
|
-
import { readFileSync
|
|
759
|
-
import { join as
|
|
563
|
+
import { readFileSync } from "fs";
|
|
564
|
+
import { join as join3 } from "path";
|
|
760
565
|
async function runGitCommand(args, cwd, options = {}) {
|
|
761
566
|
const { stdout } = await execFileAsync("git", args, {
|
|
762
567
|
cwd,
|
|
@@ -767,7 +572,7 @@ async function runGitCommand(args, cwd, options = {}) {
|
|
|
767
572
|
}
|
|
768
573
|
function readRepoHeadBranch(repoPath) {
|
|
769
574
|
try {
|
|
770
|
-
const contents =
|
|
575
|
+
const contents = readFileSync(join3(repoPath, ".git", "HEAD"), "utf-8").trim();
|
|
771
576
|
const match = contents.match(/^ref:\s+refs\/heads\/(.+)$/);
|
|
772
577
|
return match ? match[1] : null;
|
|
773
578
|
} catch {
|
|
@@ -829,13 +634,13 @@ var GitService = class {
|
|
|
829
634
|
const repos = [];
|
|
830
635
|
let complete = true;
|
|
831
636
|
for (const entry of entries) {
|
|
832
|
-
const fullPath =
|
|
637
|
+
const fullPath = join4(root, entry);
|
|
833
638
|
try {
|
|
834
639
|
const entryStat = await stat(fullPath);
|
|
835
640
|
if (!entryStat.isDirectory()) {
|
|
836
641
|
continue;
|
|
837
642
|
}
|
|
838
|
-
const hasGit = Boolean(await this.safeStat(
|
|
643
|
+
const hasGit = Boolean(await this.safeStat(join4(fullPath, ".git")));
|
|
839
644
|
if (!hasGit) {
|
|
840
645
|
continue;
|
|
841
646
|
}
|
|
@@ -1026,7 +831,7 @@ var GitService = class {
|
|
|
1026
831
|
let total = 0;
|
|
1027
832
|
for (const path6 of paths) {
|
|
1028
833
|
try {
|
|
1029
|
-
const contents = await readFile3(
|
|
834
|
+
const contents = await readFile3(join4(repoPath, path6));
|
|
1030
835
|
if (contents.length === 0 || contents.includes(0)) {
|
|
1031
836
|
continue;
|
|
1032
837
|
}
|
|
@@ -1214,7 +1019,7 @@ var GitService = class {
|
|
|
1214
1019
|
}
|
|
1215
1020
|
async getGitLabAccessToken(host) {
|
|
1216
1021
|
try {
|
|
1217
|
-
const credentials = await readFile3(
|
|
1022
|
+
const credentials = await readFile3(join4(ENGINE_ENV.HOME_DIR, ".git-credentials"), "utf-8");
|
|
1218
1023
|
for (const line of credentials.split("\n")) {
|
|
1219
1024
|
const trimmed = line.trim();
|
|
1220
1025
|
if (!trimmed) continue;
|
|
@@ -1761,8 +1566,8 @@ var infisicalTokenManager = new InfisicalTokenManager();
|
|
|
1761
1566
|
|
|
1762
1567
|
// src/utils/logger.ts
|
|
1763
1568
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
1764
|
-
import { homedir as
|
|
1765
|
-
import { join as
|
|
1569
|
+
import { homedir as homedir2 } from "os";
|
|
1570
|
+
import { join as join5 } from "path";
|
|
1766
1571
|
import { format } from "util";
|
|
1767
1572
|
import { randomBytes } from "crypto";
|
|
1768
1573
|
|
|
@@ -1837,7 +1642,7 @@ var StreamWriter = class {
|
|
|
1837
1642
|
};
|
|
1838
1643
|
|
|
1839
1644
|
// src/utils/logger.ts
|
|
1840
|
-
var LOG_DIR =
|
|
1645
|
+
var LOG_DIR = join5(homedir2(), ".replicas", "logs");
|
|
1841
1646
|
var EngineLogger = class {
|
|
1842
1647
|
_sessionId = null;
|
|
1843
1648
|
patched = false;
|
|
@@ -1848,7 +1653,7 @@ var EngineLogger = class {
|
|
|
1848
1653
|
async initialize() {
|
|
1849
1654
|
await mkdir3(LOG_DIR, { recursive: true });
|
|
1850
1655
|
this._sessionId = this.createSessionId();
|
|
1851
|
-
const logPath =
|
|
1656
|
+
const logPath = join5(LOG_DIR, `${this._sessionId}.log`);
|
|
1852
1657
|
await writeFile2(logPath, `=== Replicas Engine Session ${this._sessionId} ===
|
|
1853
1658
|
`, "utf-8");
|
|
1854
1659
|
this.writer.open(logPath);
|
|
@@ -1895,32 +1700,32 @@ var engineLogger = new EngineLogger();
|
|
|
1895
1700
|
// src/services/replicas-config-service.ts
|
|
1896
1701
|
import { readFile as readFile6, appendFile, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
|
|
1897
1702
|
import { existsSync as existsSync3 } from "fs";
|
|
1898
|
-
import { join as
|
|
1899
|
-
import { homedir as
|
|
1703
|
+
import { join as join9 } from "path";
|
|
1704
|
+
import { homedir as homedir6 } from "os";
|
|
1900
1705
|
import { spawn as spawn2 } from "child_process";
|
|
1901
1706
|
|
|
1902
1707
|
// src/services/environment-details-service.ts
|
|
1903
1708
|
import { mkdir as mkdir4, readFile as readFile4 } from "fs/promises";
|
|
1904
1709
|
import { existsSync as existsSync2 } from "fs";
|
|
1905
|
-
import { homedir as
|
|
1906
|
-
import { join as
|
|
1710
|
+
import { homedir as homedir4 } from "os";
|
|
1711
|
+
import { join as join7 } from "path";
|
|
1907
1712
|
|
|
1908
1713
|
// 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 =
|
|
1714
|
+
import { homedir as homedir3 } from "os";
|
|
1715
|
+
import { join as join6 } from "path";
|
|
1716
|
+
var OPENCODE_AUTH_PATH = join6(homedir3(), ".local", "share", "opencode", "auth.json");
|
|
1717
|
+
var PI_AUTH_PATH = join6(homedir3(), ".pi", "agent", "auth.json");
|
|
1718
|
+
var DEEPSEEK_AUTH_PATH = join6(homedir3(), ".replicas", "deepseek-auth.json");
|
|
1914
1719
|
|
|
1915
1720
|
// src/managers/deepseek-auth.ts
|
|
1916
|
-
import { readFileSync as
|
|
1721
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1917
1722
|
import { z } from "zod";
|
|
1918
1723
|
var authSchema = z.object({
|
|
1919
1724
|
openrouter: z.object({ type: z.literal("api"), key: z.string().min(1) })
|
|
1920
1725
|
});
|
|
1921
1726
|
function getDeepseekApiKey() {
|
|
1922
1727
|
try {
|
|
1923
|
-
const parsed = authSchema.safeParse(JSON.parse(
|
|
1728
|
+
const parsed = authSchema.safeParse(JSON.parse(readFileSync2(DEEPSEEK_AUTH_PATH, "utf8")));
|
|
1924
1729
|
return parsed.success ? parsed.data.openrouter.key : null;
|
|
1925
1730
|
} catch {
|
|
1926
1731
|
return null;
|
|
@@ -1933,11 +1738,11 @@ function detectCodexAuthMethod() {
|
|
|
1933
1738
|
}
|
|
1934
1739
|
|
|
1935
1740
|
// 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 =
|
|
1741
|
+
var REPLICAS_DIR = join7(homedir4(), ".replicas");
|
|
1742
|
+
var DETAILS_FILE = join7(REPLICAS_DIR, "environment-details.json");
|
|
1743
|
+
var CLAUDE_CREDENTIALS_PATH = join7(homedir4(), ".claude", ".credentials.json");
|
|
1744
|
+
var GH_HOSTS_PATH = join7(homedir4(), ".config", "gh", "hosts.yml");
|
|
1745
|
+
var GIT_CREDENTIALS_PATH = join7(homedir4(), ".git-credentials");
|
|
1941
1746
|
function detectClaudeAuthMethod() {
|
|
1942
1747
|
if (existsSync2(CLAUDE_CREDENTIALS_PATH)) {
|
|
1943
1748
|
return "oauth";
|
|
@@ -2149,8 +1954,8 @@ var environmentDetailsService = new EnvironmentDetailsService();
|
|
|
2149
1954
|
|
|
2150
1955
|
// src/services/start-hook-logs-service.ts
|
|
2151
1956
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
|
|
2152
|
-
import { homedir as
|
|
2153
|
-
import { join as
|
|
1957
|
+
import { homedir as homedir5 } from "os";
|
|
1958
|
+
import { join as join8 } from "path";
|
|
2154
1959
|
|
|
2155
1960
|
// src/services/hook-log-files.ts
|
|
2156
1961
|
import { createHash } from "crypto";
|
|
@@ -2162,7 +1967,7 @@ function repoHookLogFilename(repoName) {
|
|
|
2162
1967
|
}
|
|
2163
1968
|
|
|
2164
1969
|
// src/services/start-hook-logs-service.ts
|
|
2165
|
-
var LOGS_DIR =
|
|
1970
|
+
var LOGS_DIR = join8(homedir5(), ".replicas", "start-hook-logs");
|
|
2166
1971
|
function withPreview(stored) {
|
|
2167
1972
|
const preview = buildHookOutputPreview(stored.output);
|
|
2168
1973
|
return { ...stored, ...preview };
|
|
@@ -2200,7 +2005,7 @@ var StartHookLogsService = class {
|
|
|
2200
2005
|
await this.ensureDir();
|
|
2201
2006
|
const log = { hookType, hookName, repoName: hookName, ...entry };
|
|
2202
2007
|
const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
2203
|
-
await writeFile3(
|
|
2008
|
+
await writeFile3(join8(LOGS_DIR, filename), `${JSON.stringify(log, null, 2)}
|
|
2204
2009
|
`, "utf-8");
|
|
2205
2010
|
}
|
|
2206
2011
|
async saveEnvironmentLog(entry) {
|
|
@@ -2225,7 +2030,7 @@ var StartHookLogsService = class {
|
|
|
2225
2030
|
continue;
|
|
2226
2031
|
}
|
|
2227
2032
|
try {
|
|
2228
|
-
const raw = await readFile5(
|
|
2033
|
+
const raw = await readFile5(join8(LOGS_DIR, file), "utf-8");
|
|
2229
2034
|
const stored = normalizeStored(JSON.parse(raw));
|
|
2230
2035
|
if (stored) {
|
|
2231
2036
|
logs.push(withPreview(stored));
|
|
@@ -2244,7 +2049,7 @@ var StartHookLogsService = class {
|
|
|
2244
2049
|
async getFullOutput(hookType, hookName) {
|
|
2245
2050
|
const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
2246
2051
|
try {
|
|
2247
|
-
const raw = await readFile5(
|
|
2052
|
+
const raw = await readFile5(join8(LOGS_DIR, filename), "utf-8");
|
|
2248
2053
|
const stored = normalizeStored(JSON.parse(raw));
|
|
2249
2054
|
if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
2250
2055
|
return null;
|
|
@@ -2261,7 +2066,7 @@ var StartHookLogsService = class {
|
|
|
2261
2066
|
var startHookLogsService = new StartHookLogsService();
|
|
2262
2067
|
|
|
2263
2068
|
// src/services/replicas-config-service.ts
|
|
2264
|
-
var START_HOOKS_LOG =
|
|
2069
|
+
var START_HOOKS_LOG = join9(homedir6(), ".replicas", "startHooks.log");
|
|
2265
2070
|
var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
|
|
2266
2071
|
Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
|
|
2267
2072
|
These hooks are currently executing in the background. You can:
|
|
@@ -2272,7 +2077,7 @@ The start hooks may install dependencies, build projects, or perform other setup
|
|
|
2272
2077
|
If your task depends on setup being complete, check the log file before proceeding.`;
|
|
2273
2078
|
async function readReplicasConfigFromDir(dirPath) {
|
|
2274
2079
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
2275
|
-
const configPath =
|
|
2080
|
+
const configPath = join9(dirPath, filename);
|
|
2276
2081
|
if (!existsSync3(configPath)) {
|
|
2277
2082
|
continue;
|
|
2278
2083
|
}
|
|
@@ -2342,7 +2147,7 @@ var ReplicasConfigService = class {
|
|
|
2342
2147
|
const logLine = `[${timestamp}] ${message}
|
|
2343
2148
|
`;
|
|
2344
2149
|
try {
|
|
2345
|
-
await mkdir6(
|
|
2150
|
+
await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
|
|
2346
2151
|
await appendFile(START_HOOKS_LOG, logLine, "utf-8");
|
|
2347
2152
|
} catch (error) {
|
|
2348
2153
|
console.error("Failed to write to start hooks log:", error);
|
|
@@ -2354,7 +2159,7 @@ var ReplicasConfigService = class {
|
|
|
2354
2159
|
command: params.content,
|
|
2355
2160
|
label: "env start hook",
|
|
2356
2161
|
repoName: "environment",
|
|
2357
|
-
cwd:
|
|
2162
|
+
cwd: homedir6(),
|
|
2358
2163
|
timeout: params.timeoutMs ?? DEFAULT_START_HOOK_TIMEOUT_MS,
|
|
2359
2164
|
onOutputChunk: params.onOutputChunk
|
|
2360
2165
|
});
|
|
@@ -2477,7 +2282,7 @@ var ReplicasConfigService = class {
|
|
|
2477
2282
|
this.hooksCompleted = false;
|
|
2478
2283
|
this.hooksFailed = false;
|
|
2479
2284
|
try {
|
|
2480
|
-
await mkdir6(
|
|
2285
|
+
await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
|
|
2481
2286
|
await writeFile4(
|
|
2482
2287
|
START_HOOKS_LOG,
|
|
2483
2288
|
`=== Start Hooks Execution Log ===
|
|
@@ -2672,11 +2477,11 @@ var replicasConfigService = new ReplicasConfigService();
|
|
|
2672
2477
|
|
|
2673
2478
|
// src/services/event-service.ts
|
|
2674
2479
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
2675
|
-
import { homedir as
|
|
2676
|
-
import { join as
|
|
2480
|
+
import { homedir as homedir7 } from "os";
|
|
2481
|
+
import { join as join10 } from "path";
|
|
2677
2482
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2678
|
-
var ENGINE_DIR =
|
|
2679
|
-
var EVENTS_FILE =
|
|
2483
|
+
var ENGINE_DIR = join10(homedir7(), ".replicas", "engine");
|
|
2484
|
+
var EVENTS_FILE = join10(ENGINE_DIR, "events.jsonl");
|
|
2680
2485
|
var EventService = class {
|
|
2681
2486
|
subscribers = /* @__PURE__ */ new Map();
|
|
2682
2487
|
writer = new StreamWriter();
|
|
@@ -2710,9 +2515,9 @@ var eventService = new EventService();
|
|
|
2710
2515
|
import { mkdir as mkdir8, readFile as readFile7 } from "fs/promises";
|
|
2711
2516
|
import { existsSync as existsSync4 } from "fs";
|
|
2712
2517
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2713
|
-
import { homedir as
|
|
2714
|
-
import { dirname as dirname2, join as
|
|
2715
|
-
var PREVIEW_PORTS_FILE =
|
|
2518
|
+
import { homedir as homedir8 } from "os";
|
|
2519
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
2520
|
+
var PREVIEW_PORTS_FILE = join11(homedir8(), ".replicas", "preview-ports.json");
|
|
2716
2521
|
async function readPreviewsFile() {
|
|
2717
2522
|
try {
|
|
2718
2523
|
if (!existsSync4(PREVIEW_PORTS_FILE)) {
|
|
@@ -2822,9 +2627,9 @@ async function registerDesktopPreview() {
|
|
|
2822
2627
|
}
|
|
2823
2628
|
|
|
2824
2629
|
// src/services/chat/chat-service.ts
|
|
2825
|
-
import { existsSync as
|
|
2826
|
-
import { appendFile as appendFile4, copyFile, mkdir as
|
|
2827
|
-
import { join as
|
|
2630
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2631
|
+
import { appendFile as appendFile4, copyFile as copyFile2, mkdir as mkdir18, readFile as readFile19, rename as rename3, rm as rm3 } from "fs/promises";
|
|
2632
|
+
import { join as join28 } from "path";
|
|
2828
2633
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
2829
2634
|
|
|
2830
2635
|
// ../shared/src/workspace-chat.ts
|
|
@@ -2957,9 +2762,9 @@ import {
|
|
|
2957
2762
|
query
|
|
2958
2763
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
2959
2764
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2960
|
-
import { dirname as dirname4, join as
|
|
2765
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
2961
2766
|
import { mkdir as mkdir10 } from "fs/promises";
|
|
2962
|
-
import { homedir as
|
|
2767
|
+
import { homedir as homedir10 } from "os";
|
|
2963
2768
|
|
|
2964
2769
|
// src/utils/linear-converter.ts
|
|
2965
2770
|
function linearThoughtToResponse(thought) {
|
|
@@ -3344,8 +3149,8 @@ function extractPlanFromCodexAspNotification(notification) {
|
|
|
3344
3149
|
// src/utils/image-utils.ts
|
|
3345
3150
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
3346
3151
|
import { mkdir as mkdir9, unlink as unlink2, writeFile as writeFile5 } from "fs/promises";
|
|
3347
|
-
import { homedir as
|
|
3348
|
-
import { join as
|
|
3152
|
+
import { homedir as homedir9 } from "os";
|
|
3153
|
+
import { join as join12 } from "path";
|
|
3349
3154
|
function isImageMediaType(value) {
|
|
3350
3155
|
return IMAGE_MEDIA_TYPES.includes(value);
|
|
3351
3156
|
}
|
|
@@ -3445,14 +3250,14 @@ async function normalizeImages(images) {
|
|
|
3445
3250
|
}
|
|
3446
3251
|
return normalized;
|
|
3447
3252
|
}
|
|
3448
|
-
async function saveNormalizedImagesToTempFiles(images, tempImageDir =
|
|
3253
|
+
async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(homedir9(), ".replicas", "codex", "temp-images")) {
|
|
3449
3254
|
await mkdir9(tempImageDir, { recursive: true });
|
|
3450
3255
|
const tempPaths = [];
|
|
3451
3256
|
try {
|
|
3452
3257
|
for (const image of images) {
|
|
3453
3258
|
const ext = image.source.media_type.split("/")[1] || "png";
|
|
3454
3259
|
const filename = `img_${randomUUID4()}.${ext}`;
|
|
3455
|
-
const filepath =
|
|
3260
|
+
const filepath = join12(tempImageDir, filename);
|
|
3456
3261
|
await writeFile5(filepath, Buffer.from(image.source.data, "base64"));
|
|
3457
3262
|
tempPaths.push(filepath);
|
|
3458
3263
|
}
|
|
@@ -3467,8 +3272,8 @@ async function removeTempImageFiles(paths) {
|
|
|
3467
3272
|
}
|
|
3468
3273
|
|
|
3469
3274
|
// src/managers/coding-agent-manager.ts
|
|
3470
|
-
import { readFileSync as
|
|
3471
|
-
import { join as
|
|
3275
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
3276
|
+
import { join as join13 } from "path";
|
|
3472
3277
|
|
|
3473
3278
|
// src/managers/auth-fallback.ts
|
|
3474
3279
|
var SWAPPABLE_AGENTS = {
|
|
@@ -4012,7 +3817,7 @@ var CodingAgentManager = class {
|
|
|
4012
3817
|
buildMemoryInstruction() {
|
|
4013
3818
|
const readSummary = (scope) => {
|
|
4014
3819
|
try {
|
|
4015
|
-
return
|
|
3820
|
+
return readFileSync3(join13(MEMORY_ROOT, scope, MEMORY_SUMMARY_FILENAME), "utf8").trim();
|
|
4016
3821
|
} catch {
|
|
4017
3822
|
return "";
|
|
4018
3823
|
}
|
|
@@ -4023,10 +3828,10 @@ var CodingAgentManager = class {
|
|
|
4023
3828
|
const parts = [
|
|
4024
3829
|
`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
3830
|
];
|
|
4026
|
-
if (organization) parts.push(`<replicas-organization-memory root="${
|
|
3831
|
+
if (organization) parts.push(`<replicas-organization-memory root="${join13(MEMORY_ROOT, "organization")}">
|
|
4027
3832
|
${organization}
|
|
4028
3833
|
</replicas-organization-memory>`);
|
|
4029
|
-
if (seat) parts.push(`<replicas-personal-memory root="${
|
|
3834
|
+
if (seat) parts.push(`<replicas-personal-memory root="${join13(MEMORY_ROOT, "seat")}">
|
|
4030
3835
|
${seat}
|
|
4031
3836
|
</replicas-personal-memory>`);
|
|
4032
3837
|
return parts.join("\n\n");
|
|
@@ -4148,67 +3953,95 @@ var LinearEventForwarder = class {
|
|
|
4148
3953
|
}
|
|
4149
3954
|
};
|
|
4150
3955
|
|
|
4151
|
-
// src/services/
|
|
4152
|
-
var
|
|
4153
|
-
function
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
3956
|
+
// src/services/upload-repo-state.ts
|
|
3957
|
+
var uploadQueue = Promise.resolve();
|
|
3958
|
+
async function sendRepoState(repos) {
|
|
3959
|
+
const form = new FormData();
|
|
3960
|
+
form.append(
|
|
3961
|
+
"file",
|
|
3962
|
+
new Blob([JSON.stringify({ repos })], { type: "application/json" }),
|
|
3963
|
+
"repo-state.json"
|
|
3964
|
+
);
|
|
3965
|
+
const response = await monolithRequest("/v1/engine/repo-state", { body: form });
|
|
3966
|
+
if (!response.ok) {
|
|
3967
|
+
const errorText = await response.text();
|
|
3968
|
+
throw new Error(`upload failed: ${response.status} ${errorText}`);
|
|
3969
|
+
}
|
|
4157
3970
|
}
|
|
4158
|
-
function
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
matchedSignals
|
|
4164
|
-
};
|
|
3971
|
+
function uploadRepoState(repos) {
|
|
3972
|
+
const upload = uploadQueue.then(() => sendRepoState(repos));
|
|
3973
|
+
uploadQueue = upload.catch(() => {
|
|
3974
|
+
});
|
|
3975
|
+
return upload;
|
|
4165
3976
|
}
|
|
4166
|
-
async function
|
|
4167
|
-
const matchedSignals = [...findGitCommitSignals(request), ...findPrMergeSignals(request)];
|
|
4168
|
-
if (matchedSignals.length === 0) {
|
|
4169
|
-
return { allowed: true, enabled: true };
|
|
4170
|
-
}
|
|
4171
|
-
let response;
|
|
3977
|
+
async function flushRepoState() {
|
|
4172
3978
|
try {
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
3979
|
+
const { repos, complete } = await gitService.listReposWithCompleteness({ includeDiffs: true });
|
|
3980
|
+
if (!complete) return { flushed: 0, skipped: 1, failed: 0 };
|
|
3981
|
+
await uploadRepoState(repos);
|
|
3982
|
+
return { flushed: 1, skipped: 0, failed: 0 };
|
|
3983
|
+
} catch (err) {
|
|
3984
|
+
console.error("[RepoStateUploader] upload failed:", err);
|
|
3985
|
+
return { flushed: 0, skipped: 0, failed: 1 };
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3989
|
+
// src/services/refresh-repo-state.ts
|
|
3990
|
+
async function refreshRepoStatuses(observedBranchesByRepo, includeDiffs = false) {
|
|
3991
|
+
await Promise.all([
|
|
3992
|
+
githubTokenManager.refreshOnce().catch(() => {
|
|
3993
|
+
}),
|
|
3994
|
+
gitlabTokenManager.refreshOnce().catch(() => {
|
|
3995
|
+
})
|
|
3996
|
+
]);
|
|
3997
|
+
const { repos, complete } = await gitService.refreshReposWithCompleteness(
|
|
3998
|
+
observedBranchesByRepo,
|
|
3999
|
+
{ includeDiffs }
|
|
4000
|
+
);
|
|
4001
|
+
if (complete) {
|
|
4002
|
+
uploadRepoState(repos).catch((error) => {
|
|
4003
|
+
console.error("[RefreshRepoState] Failed to upload repository state:", error);
|
|
4176
4004
|
});
|
|
4177
|
-
} catch (error) {
|
|
4178
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
4179
|
-
return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
|
|
4180
4005
|
}
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4006
|
+
return repos;
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
// src/services/pull-request-check-service.ts
|
|
4010
|
+
var CHECK_TIMEOUT_MS = 3e4;
|
|
4011
|
+
var PullRequestCheckService = class {
|
|
4012
|
+
running = null;
|
|
4013
|
+
trailing = null;
|
|
4014
|
+
// Plain single-flight would hand a caller a result gathered before its PR
|
|
4015
|
+
// existed, so callers arriving mid-run instead share one trailing run that
|
|
4016
|
+
// starts after the current one settles.
|
|
4017
|
+
check() {
|
|
4018
|
+
const running = this.running;
|
|
4019
|
+
if (!running) return this.start();
|
|
4020
|
+
this.trailing ??= running.catch(() => {
|
|
4021
|
+
}).then(() => {
|
|
4022
|
+
this.trailing = null;
|
|
4023
|
+
return this.start();
|
|
4024
|
+
});
|
|
4025
|
+
return this.trailing;
|
|
4184
4026
|
}
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
return
|
|
4027
|
+
start() {
|
|
4028
|
+
const run = withTimeout(this.refresh(), CHECK_TIMEOUT_MS, "Pull request check timed out").finally(() => {
|
|
4029
|
+
if (this.running === run) this.running = null;
|
|
4030
|
+
});
|
|
4031
|
+
this.running = run;
|
|
4032
|
+
return run;
|
|
4191
4033
|
}
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
}
|
|
4197
|
-
|
|
4198
|
-
|
|
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
|
-
}
|
|
4034
|
+
async refresh() {
|
|
4035
|
+
const repoStatuses = await refreshRepoStatuses();
|
|
4036
|
+
await monolithService.sendEvent({ type: "pull_request_check", payload: { repoStatuses } });
|
|
4037
|
+
return repoStatuses;
|
|
4038
|
+
}
|
|
4039
|
+
};
|
|
4040
|
+
var pullRequestCheckService = new PullRequestCheckService();
|
|
4208
4041
|
|
|
4209
4042
|
// src/services/skill-registry-service.ts
|
|
4210
4043
|
import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
4211
|
-
import { dirname as dirname3, isAbsolute, join as
|
|
4044
|
+
import { dirname as dirname3, isAbsolute, join as join14, relative, resolve } from "path";
|
|
4212
4045
|
var REGISTRY_ROOT_DIR = ".replicas/skill-registries";
|
|
4213
4046
|
var REGISTRY_MANIFEST = "manifest.json";
|
|
4214
4047
|
async function getSkillRegistryInventory(homeDir) {
|
|
@@ -4269,10 +4102,10 @@ async function scanRegistry(registryDir) {
|
|
|
4269
4102
|
}
|
|
4270
4103
|
async function findSkillCollections(registryDir) {
|
|
4271
4104
|
const candidateRoots = [
|
|
4272
|
-
{ source: "skills", root:
|
|
4273
|
-
{ source: "agents", root:
|
|
4274
|
-
{ source: "codex", root:
|
|
4275
|
-
{ source: "claude", root:
|
|
4105
|
+
{ source: "skills", root: join14(registryDir, "skills") },
|
|
4106
|
+
{ source: "agents", root: join14(registryDir, ".agents", "skills") },
|
|
4107
|
+
{ source: "codex", root: join14(registryDir, ".codex", "skills") },
|
|
4108
|
+
{ source: "claude", root: join14(registryDir, ".claude", "skills") }
|
|
4276
4109
|
];
|
|
4277
4110
|
const collections = [];
|
|
4278
4111
|
for (const { source, root } of candidateRoots) {
|
|
@@ -4286,9 +4119,9 @@ async function findSkillCollections(registryDir) {
|
|
|
4286
4119
|
async function findSkillDirsInRoot(root) {
|
|
4287
4120
|
const skillDirs = [];
|
|
4288
4121
|
for (const dirent of await safeReadDir(root)) {
|
|
4289
|
-
const skillDir =
|
|
4122
|
+
const skillDir = join14(root, dirent.name);
|
|
4290
4123
|
if (!await isDirectoryDirent(dirent, skillDir)) continue;
|
|
4291
|
-
if (await fileExists(
|
|
4124
|
+
if (await fileExists(join14(skillDir, "SKILL.md"))) {
|
|
4292
4125
|
skillDirs.push(skillDir);
|
|
4293
4126
|
}
|
|
4294
4127
|
}
|
|
@@ -4298,9 +4131,9 @@ async function findTopLevelSkills(registryDir) {
|
|
|
4298
4131
|
const skills = [];
|
|
4299
4132
|
for (const dirent of await safeReadDir(registryDir)) {
|
|
4300
4133
|
if (dirent.name.startsWith(".") || dirent.name === "skills" || dirent.name === "plugins") continue;
|
|
4301
|
-
const skillDir =
|
|
4134
|
+
const skillDir = join14(registryDir, dirent.name);
|
|
4302
4135
|
if (!await isDirectoryDirent(dirent, skillDir)) continue;
|
|
4303
|
-
if (await fileExists(
|
|
4136
|
+
if (await fileExists(join14(skillDir, "SKILL.md"))) {
|
|
4304
4137
|
skills.push({ source: "root", skillDir });
|
|
4305
4138
|
}
|
|
4306
4139
|
}
|
|
@@ -4311,10 +4144,10 @@ async function findClaudePluginRoots(registryDir) {
|
|
|
4311
4144
|
async function walk(dir) {
|
|
4312
4145
|
for (const dirent of await safeReadDir(dir)) {
|
|
4313
4146
|
if (dirent.name === ".git") continue;
|
|
4314
|
-
const child =
|
|
4147
|
+
const child = join14(dir, dirent.name);
|
|
4315
4148
|
if (!await isDirectoryDirent(dirent, child)) continue;
|
|
4316
4149
|
if (dirent.name === ".claude-plugin") {
|
|
4317
|
-
if (await fileExists(
|
|
4150
|
+
if (await fileExists(join14(child, "plugin.json"))) {
|
|
4318
4151
|
roots.push(dirname3(child));
|
|
4319
4152
|
}
|
|
4320
4153
|
continue;
|
|
@@ -4326,7 +4159,7 @@ async function findClaudePluginRoots(registryDir) {
|
|
|
4326
4159
|
return uniqueStrings(roots);
|
|
4327
4160
|
}
|
|
4328
4161
|
async function hasCodexMarketplace(registryDir) {
|
|
4329
|
-
return await fileExists(
|
|
4162
|
+
return await fileExists(join14(registryDir, ".agents", "plugins", "marketplace.json")) || await fileExists(join14(registryDir, ".codex", "plugins", "marketplace.json"));
|
|
4330
4163
|
}
|
|
4331
4164
|
async function installCodexRegistryPlugins(client2, inventory) {
|
|
4332
4165
|
const cwds = inventory.codexMarketplaceCwds;
|
|
@@ -4368,10 +4201,10 @@ async function readManifest(homeDir) {
|
|
|
4368
4201
|
}
|
|
4369
4202
|
}
|
|
4370
4203
|
function getRegistryRoot(homeDir) {
|
|
4371
|
-
return
|
|
4204
|
+
return join14(homeDir, REGISTRY_ROOT_DIR);
|
|
4372
4205
|
}
|
|
4373
4206
|
function getManifestPath(homeDir) {
|
|
4374
|
-
return
|
|
4207
|
+
return join14(getRegistryRoot(homeDir), REGISTRY_MANIFEST);
|
|
4375
4208
|
}
|
|
4376
4209
|
function emptyInventory(registryRoot) {
|
|
4377
4210
|
return {
|
|
@@ -4819,7 +4652,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
4819
4652
|
authRetrying = false;
|
|
4820
4653
|
constructor(options) {
|
|
4821
4654
|
super({ ...options, provider: AGENT.CLAUDE });
|
|
4822
|
-
this.historyFilePath = options.historyFilePath ??
|
|
4655
|
+
this.historyFilePath = options.historyFilePath ?? join15(homedir10(), ".replicas", "claude", "history.jsonl");
|
|
4823
4656
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
4824
4657
|
this.systemPromptOverride = options.systemPromptOverride;
|
|
4825
4658
|
this.toolsOverride = options.tools;
|
|
@@ -5409,6 +5242,17 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
5409
5242
|
matcher: "*",
|
|
5410
5243
|
hooks: [this.buildCommandProtectionHook()],
|
|
5411
5244
|
timeout: 30
|
|
5245
|
+
}],
|
|
5246
|
+
PostToolUse: [{
|
|
5247
|
+
matcher: "*",
|
|
5248
|
+
hooks: [async (input) => {
|
|
5249
|
+
if (input.hook_event_name === "PostToolUse" && mayCreatePullRequest(input.tool_name, input.tool_input)) {
|
|
5250
|
+
pullRequestCheckService.check().catch(() => {
|
|
5251
|
+
});
|
|
5252
|
+
}
|
|
5253
|
+
return {};
|
|
5254
|
+
}],
|
|
5255
|
+
timeout: 30
|
|
5412
5256
|
}]
|
|
5413
5257
|
}
|
|
5414
5258
|
}
|
|
@@ -5873,7 +5717,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
5873
5717
|
|
|
5874
5718
|
// src/managers/codex-asp/codex-asp-manager.ts
|
|
5875
5719
|
import { readdir as readdir4 } from "fs/promises";
|
|
5876
|
-
import { join as
|
|
5720
|
+
import { join as join17 } from "path";
|
|
5877
5721
|
|
|
5878
5722
|
// src/managers/codex-asp/asp-host.ts
|
|
5879
5723
|
var hostPromise = null;
|
|
@@ -5988,7 +5832,7 @@ var CodexQuotaStatusTracker = class {
|
|
|
5988
5832
|
};
|
|
5989
5833
|
|
|
5990
5834
|
// src/managers/codex-asp/mappers.ts
|
|
5991
|
-
import { existsSync as existsSync5, readFileSync as
|
|
5835
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
5992
5836
|
var localImageCache = /* @__PURE__ */ new Map();
|
|
5993
5837
|
var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
|
|
5994
5838
|
var THREAD_START_METHOD = "thread/start";
|
|
@@ -6116,7 +5960,7 @@ function userImageForLocalPath(path6) {
|
|
|
6116
5960
|
const image = {
|
|
6117
5961
|
type: "image",
|
|
6118
5962
|
mediaType: inferMediaType(path6),
|
|
6119
|
-
data:
|
|
5963
|
+
data: readFileSync4(path6).toString("base64")
|
|
6120
5964
|
};
|
|
6121
5965
|
if (image.data.length > 0) localImageCache.set(path6, image);
|
|
6122
5966
|
return image;
|
|
@@ -6607,19 +6451,19 @@ var TranscriptUpdateCoalescer = class {
|
|
|
6607
6451
|
};
|
|
6608
6452
|
|
|
6609
6453
|
// 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 =
|
|
6454
|
+
import { homedir as homedir11 } from "os";
|
|
6455
|
+
import { join as join16 } from "path";
|
|
6456
|
+
var ENGINE_DIR2 = join16(homedir11(), ".replicas", "engine");
|
|
6457
|
+
var CHATS_FILE = join16(ENGINE_DIR2, "chats.json");
|
|
6458
|
+
var CLAUDE_HISTORY_DIR = join16(ENGINE_DIR2, "claude-histories");
|
|
6459
|
+
var RELAY_HISTORY_DIR = join16(ENGINE_DIR2, "relay-histories");
|
|
6460
|
+
var CODEX_HISTORY_DIR = join16(ENGINE_DIR2, "codex-histories");
|
|
6461
|
+
var CURSOR_HISTORY_DIR = join16(ENGINE_DIR2, "cursor-histories");
|
|
6462
|
+
var OPENCODE_HISTORY_DIR = join16(ENGINE_DIR2, "opencode-histories");
|
|
6463
|
+
var PI_HISTORY_DIR = join16(ENGINE_DIR2, "pi-histories");
|
|
6464
|
+
var DEEPSEEK_HISTORY_DIR = join16(ENGINE_DIR2, "deepseek-histories");
|
|
6465
|
+
var FX_HISTORY_DIR = join16(ENGINE_DIR2, "fx-histories");
|
|
6466
|
+
var KIMI_HISTORY_DIR = join16(ENGINE_DIR2, "kimi-histories");
|
|
6623
6467
|
var HISTORY_DIR_BY_PROVIDER = {
|
|
6624
6468
|
claude: CLAUDE_HISTORY_DIR,
|
|
6625
6469
|
relay: RELAY_HISTORY_DIR,
|
|
@@ -6685,7 +6529,7 @@ async function readCodexAspThreadHistory(threadId) {
|
|
|
6685
6529
|
}
|
|
6686
6530
|
for (const entry of entries) {
|
|
6687
6531
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl") || entry.name.endsWith(".pages.jsonl")) continue;
|
|
6688
|
-
const history = await new CodexHistoryFile(
|
|
6532
|
+
const history = await new CodexHistoryFile(join17(CODEX_HISTORY_DIR, entry.name)).load();
|
|
6689
6533
|
const transcript = history.transcriptsByThreadId.get(threadId);
|
|
6690
6534
|
if (transcript) {
|
|
6691
6535
|
return {
|
|
@@ -7358,6 +7202,11 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7358
7202
|
);
|
|
7359
7203
|
this.emitTranscriptUpdated(notification.params.threadId, { immediate: true });
|
|
7360
7204
|
linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
|
|
7205
|
+
const completedItem = notification.params.item;
|
|
7206
|
+
if (completedItem.type === "commandExecution" && completedItem.exitCode === 0 && mayCreatePullRequest("commandExecution", { command: completedItem.command }) || completedItem.type === "mcpToolCall" && completedItem.status === "completed" && mayCreatePullRequest(completedItem.tool, completedItem.arguments) || completedItem.type === "dynamicToolCall" && completedItem.success !== false && mayCreatePullRequest(completedItem.tool, completedItem.arguments)) {
|
|
7207
|
+
pullRequestCheckService.check().catch(() => {
|
|
7208
|
+
});
|
|
7209
|
+
}
|
|
7361
7210
|
},
|
|
7362
7211
|
[AGENT_MESSAGE_DELTA_METHOD]: (notification) => {
|
|
7363
7212
|
if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
|
|
@@ -8065,10 +7914,98 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8065
7914
|
};
|
|
8066
7915
|
|
|
8067
7916
|
// src/managers/cursor-manager.ts
|
|
8068
|
-
import { mkdir as
|
|
8069
|
-
import { basename, dirname as
|
|
7917
|
+
import { mkdir as mkdir12, readFile as readFile11, readdir as readdir5 } from "fs/promises";
|
|
7918
|
+
import { basename as basename2, dirname as dirname6, extname, join as join19 } from "path";
|
|
8070
7919
|
import { parse as parseYaml } from "yaml";
|
|
8071
7920
|
import { Agent as CursorAgent } from "@cursor/sdk";
|
|
7921
|
+
|
|
7922
|
+
// src/services/native-command-protection-hooks.ts
|
|
7923
|
+
import { existsSync as existsSync6 } from "fs";
|
|
7924
|
+
import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
|
|
7925
|
+
import { basename, dirname as dirname5, join as join18 } from "path";
|
|
7926
|
+
import { fileURLToPath } from "url";
|
|
7927
|
+
var moduleDirectory = dirname5(fileURLToPath(import.meta.url));
|
|
7928
|
+
var hookPath = [
|
|
7929
|
+
join18(moduleDirectory, "command-protection-hook.js"),
|
|
7930
|
+
join18(moduleDirectory, "..", "command-protection-hook.ts")
|
|
7931
|
+
].find(existsSync6);
|
|
7932
|
+
var postToolHookPath = [
|
|
7933
|
+
join18(moduleDirectory, "post-tool-pr-hook.js"),
|
|
7934
|
+
join18(moduleDirectory, "..", "post-tool-pr-hook.ts")
|
|
7935
|
+
].find(existsSync6);
|
|
7936
|
+
function shellCommand(path6, provider) {
|
|
7937
|
+
if (!path6) throw new Error("Replicas hook executable is missing.");
|
|
7938
|
+
const args = [
|
|
7939
|
+
process.execPath,
|
|
7940
|
+
...path6.endsWith(".ts") && basename(process.execPath) !== "bun" ? ["--import", "tsx"] : [],
|
|
7941
|
+
path6,
|
|
7942
|
+
...provider ? [provider] : []
|
|
7943
|
+
];
|
|
7944
|
+
return args.map((value) => `'${value.replaceAll("'", "'\\''")}'`).join(" ");
|
|
7945
|
+
}
|
|
7946
|
+
function getCommandProtectionHookPath() {
|
|
7947
|
+
if (!hookPath) throw new Error("Replicas command protection hook executable is missing.");
|
|
7948
|
+
return hookPath;
|
|
7949
|
+
}
|
|
7950
|
+
function getPostToolPrHookPath() {
|
|
7951
|
+
if (!postToolHookPath) throw new Error("Replicas post-tool PR hook executable is missing.");
|
|
7952
|
+
return postToolHookPath;
|
|
7953
|
+
}
|
|
7954
|
+
async function ensureCursorCommandProtectionHook() {
|
|
7955
|
+
const path6 = join18(ENGINE_ENV.HOME_DIR, ".cursor", "hooks.json");
|
|
7956
|
+
let config = {};
|
|
7957
|
+
try {
|
|
7958
|
+
const parsed = JSON.parse(await readFile10(path6, "utf8"));
|
|
7959
|
+
if (!isRecord2(parsed)) throw new Error("Invalid Cursor hooks configuration.");
|
|
7960
|
+
config = parsed;
|
|
7961
|
+
} catch (error) {
|
|
7962
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
7963
|
+
}
|
|
7964
|
+
const hooks = isRecord2(config.hooks) ? config.hooks : {};
|
|
7965
|
+
const existing = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
|
|
7966
|
+
hooks.preToolUse = [
|
|
7967
|
+
...existing.filter((entry) => !isRecord2(entry) || typeof entry.command !== "string" || !entry.command.includes("command-protection-hook")),
|
|
7968
|
+
{ command: shellCommand(hookPath, "cursor") }
|
|
7969
|
+
];
|
|
7970
|
+
const existingPost = Array.isArray(hooks.postToolUse) ? hooks.postToolUse : [];
|
|
7971
|
+
hooks.postToolUse = [
|
|
7972
|
+
...existingPost.filter((entry) => !isRecord2(entry) || typeof entry.command !== "string" || !entry.command.includes("post-tool-pr-hook")),
|
|
7973
|
+
{ command: shellCommand(postToolHookPath) }
|
|
7974
|
+
];
|
|
7975
|
+
await mkdir11(dirname5(path6), { recursive: true });
|
|
7976
|
+
await writeFile6(path6, `${JSON.stringify({ ...config, version: 1, hooks }, null, 2)}
|
|
7977
|
+
`, "utf8");
|
|
7978
|
+
}
|
|
7979
|
+
async function ensureKimiCommandProtectionHook() {
|
|
7980
|
+
const path6 = join18(ENGINE_ENV.HOME_DIR, ".kimi-code", "config.toml");
|
|
7981
|
+
const start = "# Replicas command protection hook";
|
|
7982
|
+
const end = "# End Replicas command protection hook";
|
|
7983
|
+
let config = "";
|
|
7984
|
+
try {
|
|
7985
|
+
config = await readFile10(path6, "utf8");
|
|
7986
|
+
} catch (error) {
|
|
7987
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
7988
|
+
}
|
|
7989
|
+
const block = `${start}
|
|
7990
|
+
[[hooks]]
|
|
7991
|
+
event = "PreToolUse"
|
|
7992
|
+
command = ${JSON.stringify(shellCommand(hookPath, "kimi"))}
|
|
7993
|
+
timeout = 30
|
|
7994
|
+
|
|
7995
|
+
[[hooks]]
|
|
7996
|
+
event = "PostToolUse"
|
|
7997
|
+
command = ${JSON.stringify(shellCommand(postToolHookPath))}
|
|
7998
|
+
timeout = 30
|
|
7999
|
+
${end}`;
|
|
8000
|
+
const markerStart = config.indexOf(start);
|
|
8001
|
+
const markerEnd = config.indexOf(end, markerStart + start.length);
|
|
8002
|
+
config = markerStart >= 0 && markerEnd >= 0 ? `${config.slice(0, markerStart)}${block}${config.slice(markerEnd + end.length)}` : `${config.trimEnd()}${config.trim() ? "\n\n" : ""}${block}
|
|
8003
|
+
`;
|
|
8004
|
+
await mkdir11(dirname5(path6), { recursive: true });
|
|
8005
|
+
await writeFile6(path6, config, "utf8");
|
|
8006
|
+
}
|
|
8007
|
+
|
|
8008
|
+
// src/managers/cursor-manager.ts
|
|
8072
8009
|
var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
8073
8010
|
var CURSOR_STREAM_INACTIVITY_TIMEOUT_MS = 30 * 6e4;
|
|
8074
8011
|
var CURSOR_RUN_SETTLE_TIMEOUT_MS = 3e4;
|
|
@@ -8108,24 +8045,6 @@ function extractCursorCommandDescription(content) {
|
|
|
8108
8045
|
}
|
|
8109
8046
|
return void 0;
|
|
8110
8047
|
}
|
|
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
8048
|
async function listCursorCommandsInDirectory(directory) {
|
|
8130
8049
|
let entries;
|
|
8131
8050
|
try {
|
|
@@ -8136,10 +8055,10 @@ async function listCursorCommandsInDirectory(directory) {
|
|
|
8136
8055
|
return [];
|
|
8137
8056
|
}
|
|
8138
8057
|
const commands = await Promise.all(entries.filter((entry) => entry.isFile() && extname(entry.name) === ".md").map(async (entry) => {
|
|
8139
|
-
const name =
|
|
8058
|
+
const name = basename2(entry.name, ".md");
|
|
8140
8059
|
let description;
|
|
8141
8060
|
try {
|
|
8142
|
-
description = extractCursorCommandDescription(await
|
|
8061
|
+
description = extractCursorCommandDescription(await readFile11(join19(directory, entry.name), "utf8"));
|
|
8143
8062
|
} catch (error) {
|
|
8144
8063
|
console.warn("[CursorManager] Failed to read slash command file:", error);
|
|
8145
8064
|
}
|
|
@@ -8152,8 +8071,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8152
8071
|
activeRun = null;
|
|
8153
8072
|
activeModel = null;
|
|
8154
8073
|
abortActiveTurn = null;
|
|
8155
|
-
blockedToolCallIds = /* @__PURE__ */ new Set();
|
|
8156
|
-
pendingCursorBlockReason = null;
|
|
8157
8074
|
reconnectNoticeVisible = false;
|
|
8158
8075
|
instructionsDelivered = false;
|
|
8159
8076
|
resumeSessionId = null;
|
|
@@ -8163,12 +8080,13 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8163
8080
|
slashCommandsRequest = null;
|
|
8164
8081
|
constructor(options) {
|
|
8165
8082
|
super(options);
|
|
8166
|
-
this.historyFilePath = options.historyFilePath ??
|
|
8083
|
+
this.historyFilePath = options.historyFilePath ?? join19(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
|
|
8167
8084
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
8168
8085
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8169
8086
|
}
|
|
8170
8087
|
async initialize() {
|
|
8171
|
-
await
|
|
8088
|
+
await mkdir12(dirname6(this.historyFilePath), { recursive: true });
|
|
8089
|
+
await ensureCursorCommandProtectionHook();
|
|
8172
8090
|
}
|
|
8173
8091
|
getHistorySink() {
|
|
8174
8092
|
return this.historyFile;
|
|
@@ -8237,7 +8155,7 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8237
8155
|
this.slashCommandsRequest ??= (async () => {
|
|
8238
8156
|
try {
|
|
8239
8157
|
const repoDirectories = await getAgentAdditionalDirectories();
|
|
8240
|
-
const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) =>
|
|
8158
|
+
const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join19(directory, ".cursor", "commands"));
|
|
8241
8159
|
const commands = mergeSlashCommands(
|
|
8242
8160
|
...await Promise.all(commandDirectories.map(listCursorCommandsInDirectory))
|
|
8243
8161
|
);
|
|
@@ -8308,17 +8226,12 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8308
8226
|
run = await agent.send(message, {
|
|
8309
8227
|
model: { id: model },
|
|
8310
8228
|
mode: request.planMode ? "plan" : "agent",
|
|
8311
|
-
onDelta:
|
|
8229
|
+
onDelta: (args) => {
|
|
8312
8230
|
markCursorActivity();
|
|
8313
8231
|
this.recordCursorContextUsage(args.update);
|
|
8314
|
-
await this.handleCursorToolCall(cursorToolInputFromDelta(args.update));
|
|
8315
8232
|
}
|
|
8316
8233
|
});
|
|
8317
8234
|
this.activeRun = run;
|
|
8318
|
-
if (this.pendingCursorBlockReason) {
|
|
8319
|
-
await run.cancel();
|
|
8320
|
-
throw new Error(this.pendingCursorBlockReason);
|
|
8321
|
-
}
|
|
8322
8235
|
const events = run.stream()[Symbol.asyncIterator]();
|
|
8323
8236
|
let interrupted = false;
|
|
8324
8237
|
while (true) {
|
|
@@ -8340,9 +8253,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8340
8253
|
markCursorActivity();
|
|
8341
8254
|
const event = next.value;
|
|
8342
8255
|
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
8256
|
if (linearSessionId) {
|
|
8347
8257
|
linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
|
|
8348
8258
|
}
|
|
@@ -8413,8 +8323,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8413
8323
|
this.abortActiveTurn = null;
|
|
8414
8324
|
this.activeRun = null;
|
|
8415
8325
|
this.activeModel = null;
|
|
8416
|
-
this.blockedToolCallIds.clear();
|
|
8417
|
-
this.pendingCursorBlockReason = null;
|
|
8418
8326
|
await this.historyFile.flush();
|
|
8419
8327
|
await this.onTurnComplete();
|
|
8420
8328
|
}
|
|
@@ -8434,39 +8342,6 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
8434
8342
|
return null;
|
|
8435
8343
|
}
|
|
8436
8344
|
}
|
|
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
8345
|
async toCursorMessage(request, includeInstructions) {
|
|
8471
8346
|
const instructions = includeInstructions ? this.buildCombinedInstructions(request.customInstructions) : void 0;
|
|
8472
8347
|
const text = [
|
|
@@ -8553,10 +8428,10 @@ ${instructions}
|
|
|
8553
8428
|
import { spawn as spawn3 } from "child_process";
|
|
8554
8429
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8555
8430
|
import { createRequire } from "module";
|
|
8556
|
-
import { dirname as
|
|
8557
|
-
import { fileURLToPath } from "url";
|
|
8558
|
-
import { existsSync as
|
|
8559
|
-
import { mkdir as
|
|
8431
|
+
import { dirname as dirname7, join as join20 } from "path";
|
|
8432
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8433
|
+
import { existsSync as existsSync7 } from "fs";
|
|
8434
|
+
import { mkdir as mkdir13, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
|
|
8560
8435
|
import { z as z2 } from "zod";
|
|
8561
8436
|
import WebSocket from "ws";
|
|
8562
8437
|
import { AbstractApiClient } from "@deepseek-ai/dsh-host-apiproxy/client";
|
|
@@ -8568,12 +8443,16 @@ var questionSelectionSchema = z2.record(z2.string(), z2.object({
|
|
|
8568
8443
|
custom: z2.string().optional()
|
|
8569
8444
|
}));
|
|
8570
8445
|
var require2 = createRequire(import.meta.url);
|
|
8571
|
-
var DSH_BIN =
|
|
8572
|
-
var MANAGER_DIR =
|
|
8446
|
+
var DSH_BIN = join20(dirname7(require2.resolve("@deepseek-ai/dsh/package.json")), "lib", "bin.js");
|
|
8447
|
+
var MANAGER_DIR = dirname7(fileURLToPath2(import.meta.url));
|
|
8573
8448
|
var DSH_PATCH = [
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
].find(
|
|
8449
|
+
join20(MANAGER_DIR, "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml"),
|
|
8450
|
+
join20(MANAGER_DIR, "..", "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml")
|
|
8451
|
+
].find(existsSync7) ?? "";
|
|
8452
|
+
var DSH_COMMAND_PROTECTION_PLUGIN = [
|
|
8453
|
+
join20(MANAGER_DIR, "deepseek-command-protection-plugin.js"),
|
|
8454
|
+
join20(MANAGER_DIR, "..", "deepseek-command-protection-plugin.ts")
|
|
8455
|
+
].find(existsSync7) ?? "";
|
|
8577
8456
|
var DeepseekApiClient = class extends AbstractApiClient {
|
|
8578
8457
|
constructor(baseUrl) {
|
|
8579
8458
|
super(3e4);
|
|
@@ -8686,12 +8565,12 @@ var DeepseekManager = class extends CodingAgentManager {
|
|
|
8686
8565
|
constructor(options) {
|
|
8687
8566
|
super(options);
|
|
8688
8567
|
this.sessionId = options.initialSessionId ? sessionIdSchema.parse(options.initialSessionId) : null;
|
|
8689
|
-
this.historyFilePath = options.historyFilePath ??
|
|
8568
|
+
this.historyFilePath = options.historyFilePath ?? join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
|
|
8690
8569
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
8691
8570
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8692
8571
|
}
|
|
8693
8572
|
async initialize() {
|
|
8694
|
-
await
|
|
8573
|
+
await mkdir13(dirname7(this.historyFilePath), { recursive: true });
|
|
8695
8574
|
}
|
|
8696
8575
|
getHistorySink() {
|
|
8697
8576
|
return this.historyFile;
|
|
@@ -8765,15 +8644,32 @@ var DeepseekManager = class extends CodingAgentManager {
|
|
|
8765
8644
|
}
|
|
8766
8645
|
async ensureClient() {
|
|
8767
8646
|
if (this.client) return this.client;
|
|
8768
|
-
if (!DSH_PATCH) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
|
|
8647
|
+
if (!DSH_PATCH || !DSH_COMMAND_PROTECTION_PLUGIN) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
|
|
8648
|
+
const runtimePatch = join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "replicas.cordis.patch.yml");
|
|
8649
|
+
await mkdir13(dirname7(runtimePatch), { recursive: true });
|
|
8650
|
+
await writeFile7(runtimePatch, (await readFile12(DSH_PATCH, "utf8")).replace(
|
|
8651
|
+
"__REPLICAS_DSH_COMMAND_PROTECTION_PLUGIN__",
|
|
8652
|
+
JSON.stringify(DSH_COMMAND_PROTECTION_PLUGIN)
|
|
8653
|
+
));
|
|
8769
8654
|
const apiKey = await getOpenRouterKey();
|
|
8770
|
-
const child = spawn3(process.execPath, [
|
|
8655
|
+
const child = spawn3(process.execPath, [
|
|
8656
|
+
...DSH_COMMAND_PROTECTION_PLUGIN.endsWith(".ts") ? ["--import", "tsx"] : [],
|
|
8657
|
+
DSH_BIN,
|
|
8658
|
+
"--profile",
|
|
8659
|
+
"web",
|
|
8660
|
+
"--patch",
|
|
8661
|
+
runtimePatch,
|
|
8662
|
+
"--host",
|
|
8663
|
+
"127.0.0.1",
|
|
8664
|
+
"--port",
|
|
8665
|
+
"0"
|
|
8666
|
+
], {
|
|
8771
8667
|
cwd: this.workingDirectory,
|
|
8772
8668
|
env: {
|
|
8773
8669
|
...process.env,
|
|
8774
8670
|
DEEPSEEK_API_KEY: apiKey,
|
|
8775
8671
|
DEEPSEEK_BASE_URL: "https://openrouter.ai/api/v1",
|
|
8776
|
-
DSH_HOME:
|
|
8672
|
+
DSH_HOME: join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek"),
|
|
8777
8673
|
DSH_PERMISSION_MODE: "danger-full-access",
|
|
8778
8674
|
DSH_TELEMETRY_DISABLED: "true"
|
|
8779
8675
|
},
|
|
@@ -8958,14 +8854,14 @@ ${request.message}` : request.message;
|
|
|
8958
8854
|
};
|
|
8959
8855
|
|
|
8960
8856
|
// src/managers/fx-manager.ts
|
|
8961
|
-
import { readFile as
|
|
8857
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
8962
8858
|
import { spawn as spawn5 } from "child_process";
|
|
8963
8859
|
|
|
8964
8860
|
// src/managers/acp-manager.ts
|
|
8965
8861
|
import { spawn as spawn4 } from "child_process";
|
|
8966
8862
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
8967
|
-
import { mkdir as
|
|
8968
|
-
import { dirname as
|
|
8863
|
+
import { mkdir as mkdir14, readFile as readFile13, writeFile as writeFile8 } from "fs/promises";
|
|
8864
|
+
import { dirname as dirname8 } from "path";
|
|
8969
8865
|
import { Writable } from "stream";
|
|
8970
8866
|
import {
|
|
8971
8867
|
PROTOCOL_VERSION,
|
|
@@ -9004,7 +8900,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9004
8900
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9005
8901
|
}
|
|
9006
8902
|
async initialize() {
|
|
9007
|
-
await
|
|
8903
|
+
await mkdir14(dirname8(this.historyFilePath), { recursive: true });
|
|
9008
8904
|
await this.acp.prepare?.();
|
|
9009
8905
|
}
|
|
9010
8906
|
getHistorySink() {
|
|
@@ -9035,7 +8931,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9035
8931
|
});
|
|
9036
8932
|
const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
|
|
9037
8933
|
this.assertSession(params.sessionId);
|
|
9038
|
-
const content = await
|
|
8934
|
+
const content = await readFile13(params.path, "utf8");
|
|
9039
8935
|
if (params.line === void 0 && params.limit === void 0) return { content };
|
|
9040
8936
|
const start = Math.max(0, (params.line ?? 1) - 1);
|
|
9041
8937
|
const end = params.limit === null || params.limit === void 0 ? void 0 : start + params.limit;
|
|
@@ -9043,7 +8939,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9043
8939
|
}).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
|
|
9044
8940
|
this.assertSession(params.sessionId);
|
|
9045
8941
|
if (this.planMode) throw new Error("ACP file writes are disabled in plan mode.");
|
|
9046
|
-
await
|
|
8942
|
+
await writeFile8(params.path, params.content, "utf8");
|
|
9047
8943
|
return {};
|
|
9048
8944
|
}).onRequest(methods.client.terminal.create, ({ params }) => this.createTerminal(params)).onRequest(methods.client.terminal.output, ({ params }) => {
|
|
9049
8945
|
this.assertSession(params.sessionId);
|
|
@@ -9410,7 +9306,7 @@ var AcpManager = class extends CodingAgentManager {
|
|
|
9410
9306
|
|
|
9411
9307
|
// src/managers/fx-manager.ts
|
|
9412
9308
|
async function ensureResolvableDnsConfig() {
|
|
9413
|
-
const contents = await
|
|
9309
|
+
const contents = await readFile14("/etc/resolv.conf");
|
|
9414
9310
|
if (contents.length === 0 || contents.at(-1) === 10) return;
|
|
9415
9311
|
await new Promise((resolve5, reject) => {
|
|
9416
9312
|
const child = spawn5("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
|
|
@@ -9477,6 +9373,7 @@ var KimiManager = class extends AcpManager {
|
|
|
9477
9373
|
};
|
|
9478
9374
|
},
|
|
9479
9375
|
defaultModel: DEFAULT_KIMI_MODEL,
|
|
9376
|
+
prepare: ensureKimiCommandProtectionHook,
|
|
9480
9377
|
restartOnThinkingChange: true
|
|
9481
9378
|
}
|
|
9482
9379
|
});
|
|
@@ -9484,11 +9381,11 @@ var KimiManager = class extends AcpManager {
|
|
|
9484
9381
|
};
|
|
9485
9382
|
|
|
9486
9383
|
// src/managers/opencode-manager.ts
|
|
9487
|
-
import { existsSync as
|
|
9488
|
-
import { mkdir as
|
|
9489
|
-
import { delimiter, dirname as
|
|
9384
|
+
import { existsSync as existsSync8 } from "fs";
|
|
9385
|
+
import { copyFile, mkdir as mkdir15, readFile as readFile15, rm as rm2 } from "fs/promises";
|
|
9386
|
+
import { delimiter, dirname as dirname9, join as join21 } from "path";
|
|
9490
9387
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
9491
|
-
import { fileURLToPath as
|
|
9388
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9492
9389
|
import { Agent } from "undici";
|
|
9493
9390
|
import { z as z3 } from "zod";
|
|
9494
9391
|
|
|
@@ -9545,27 +9442,14 @@ async function getAllowedOpenRouterModels() {
|
|
|
9545
9442
|
}
|
|
9546
9443
|
|
|
9547
9444
|
// src/managers/opencode-manager.ts
|
|
9548
|
-
var OPENCODE_SHIM_DIR =
|
|
9549
|
-
var
|
|
9445
|
+
var OPENCODE_SHIM_DIR = dirname9(fileURLToPath3(new URL("../../scripts/opencode", import.meta.url)));
|
|
9446
|
+
var OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE = join21(OPENCODE_SHIM_DIR, "opencode-command-protection-plugin.ts");
|
|
9447
|
+
var OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.ts");
|
|
9448
|
+
var OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.mjs");
|
|
9449
|
+
var OPENCODE_CONFIG_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
|
|
9550
9450
|
var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
9551
9451
|
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
|
-
};
|
|
9452
|
+
var OPENCODE_WORKSPACE_PERMISSION = "allow";
|
|
9569
9453
|
var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
9570
9454
|
var OPENCODE_VARIANT_CANDIDATES_BY_THINKING_LEVEL = {
|
|
9571
9455
|
low: ["low"],
|
|
@@ -9581,9 +9465,9 @@ var opencodeAuthSchema = z3.record(z3.string(), z3.object({
|
|
|
9581
9465
|
key: z3.string().optional()
|
|
9582
9466
|
}));
|
|
9583
9467
|
async function hasOpencodeCredentials(provider) {
|
|
9584
|
-
if (!
|
|
9468
|
+
if (!existsSync8(OPENCODE_AUTH_PATH)) return false;
|
|
9585
9469
|
try {
|
|
9586
|
-
const auth = opencodeAuthSchema.safeParse(JSON.parse(await
|
|
9470
|
+
const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile15(OPENCODE_AUTH_PATH, "utf8")));
|
|
9587
9471
|
return auth.success && auth.data[provider]?.type === "api" && Boolean(auth.data[provider]?.key);
|
|
9588
9472
|
} catch {
|
|
9589
9473
|
return false;
|
|
@@ -9688,7 +9572,7 @@ function isOpencodeMcpEntry(value) {
|
|
|
9688
9572
|
async function readProvisionedOpencodeMcpConfig() {
|
|
9689
9573
|
let raw;
|
|
9690
9574
|
try {
|
|
9691
|
-
raw = await
|
|
9575
|
+
raw = await readFile15(OPENCODE_CONFIG_PATH, "utf8");
|
|
9692
9576
|
} catch (error) {
|
|
9693
9577
|
if (isRecord2(error) && error.code === "ENOENT") return void 0;
|
|
9694
9578
|
console.error("[OpencodeManager] Failed to read Opencode config:", error);
|
|
@@ -9746,27 +9630,6 @@ function opencodeFetch(input, init) {
|
|
|
9746
9630
|
const requestInit = { ...init, dispatcher: OPENCODE_FETCH_DISPATCHER };
|
|
9747
9631
|
return fetch(input, requestInit);
|
|
9748
9632
|
}
|
|
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
9633
|
var OpencodeManager = class extends CodingAgentManager {
|
|
9771
9634
|
client = null;
|
|
9772
9635
|
server = null;
|
|
@@ -9787,22 +9650,17 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9787
9650
|
forwardedLinearPartKeys = /* @__PURE__ */ new Set();
|
|
9788
9651
|
handledAccountRateLimit = false;
|
|
9789
9652
|
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
9653
|
slashCommandsCache = null;
|
|
9796
9654
|
slashCommandsRequest = null;
|
|
9797
9655
|
constructor(options) {
|
|
9798
9656
|
super(options);
|
|
9799
9657
|
this.sessionId = options.initialSessionId;
|
|
9800
|
-
this.historyFilePath = options.historyFilePath ??
|
|
9658
|
+
this.historyFilePath = options.historyFilePath ?? join21(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
|
|
9801
9659
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
9802
9660
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9803
9661
|
}
|
|
9804
9662
|
async initialize() {
|
|
9805
|
-
await
|
|
9663
|
+
await mkdir15(dirname9(this.historyFilePath), { recursive: true });
|
|
9806
9664
|
}
|
|
9807
9665
|
getHistorySink() {
|
|
9808
9666
|
return this.historyFile;
|
|
@@ -9899,6 +9757,15 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9899
9757
|
if (!await hasOpencodeCredentials(providerId)) {
|
|
9900
9758
|
throw new Error("OpenCode Go, Aster, or OpenRouter credentials are not configured for Opencode in this workspace.");
|
|
9901
9759
|
}
|
|
9760
|
+
if (!existsSync8(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE)) {
|
|
9761
|
+
throw new Error("Opencode command protection plugin is missing from the engine package.");
|
|
9762
|
+
}
|
|
9763
|
+
await mkdir15(dirname9(OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH), { recursive: true });
|
|
9764
|
+
await copyFile(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE, OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH);
|
|
9765
|
+
await rm2(OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH, { force: true });
|
|
9766
|
+
process.env.REPLICAS_COMMAND_PROTECTION_HOOK_PATH = getCommandProtectionHookPath();
|
|
9767
|
+
process.env.REPLICAS_POST_TOOL_PR_HOOK_PATH = getPostToolPrHookPath();
|
|
9768
|
+
process.env.REPLICAS_COMMAND_PROTECTION_RUNTIME_PATH = process.execPath;
|
|
9902
9769
|
this.eventAbortController?.abort();
|
|
9903
9770
|
this.server?.close();
|
|
9904
9771
|
const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
@@ -10032,11 +9899,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10032
9899
|
this.activeAbortController = null;
|
|
10033
9900
|
this.activeLinearForwarder = null;
|
|
10034
9901
|
this.forwardedLinearPartKeys.clear();
|
|
10035
|
-
this.handledPermissionRequestIds.clear();
|
|
10036
|
-
this.pendingPermissionRequestIds.clear();
|
|
10037
|
-
this.handledToolCallIds.clear();
|
|
10038
|
-
this.pendingToolCallIds.clear();
|
|
10039
|
-
this.opencodeToolInputs.clear();
|
|
10040
9902
|
await this.historyFile.flush();
|
|
10041
9903
|
await this.onTurnComplete();
|
|
10042
9904
|
}
|
|
@@ -10074,9 +9936,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10074
9936
|
if (typeof partSessionID === "string" && this.sessionId && partSessionID !== this.sessionId) return;
|
|
10075
9937
|
if (event.type === "message.part.updated" && isOpencodePart(part)) {
|
|
10076
9938
|
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
9939
|
return;
|
|
10081
9940
|
}
|
|
10082
9941
|
if (event.type === "message.updated") this.recordOpencodeMessageRole(payload);
|
|
@@ -10085,22 +9944,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10085
9944
|
console.error("[OpencodeManager] Failed to stop account-rate-limited turn:", error);
|
|
10086
9945
|
});
|
|
10087
9946
|
}
|
|
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
9947
|
if (this.recordOpencodeNextPart(event.type, payload)) return;
|
|
10105
9948
|
this.recordHistoryEvent(`opencode-${event.type}`, payload, this.historyFile);
|
|
10106
9949
|
}
|
|
@@ -10113,110 +9956,6 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10113
9956
|
this.recordHistoryEvent("opencode-error", { message, code: "account_rate_limit" }, this.historyFile);
|
|
10114
9957
|
await this.abortActiveTurn();
|
|
10115
9958
|
}
|
|
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
9959
|
recordOpencodeMessageRole(payload) {
|
|
10221
9960
|
const info = isRecord2(payload.info) ? payload.info : null;
|
|
10222
9961
|
const id = typeof info?.id === "string" ? info.id : void 0;
|
|
@@ -10316,8 +10055,8 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
10316
10055
|
};
|
|
10317
10056
|
|
|
10318
10057
|
// src/managers/pi-manager.ts
|
|
10319
|
-
import { mkdir as
|
|
10320
|
-
import { dirname as
|
|
10058
|
+
import { mkdir as mkdir16 } from "fs/promises";
|
|
10059
|
+
import { dirname as dirname10, join as join22 } from "path";
|
|
10321
10060
|
import {
|
|
10322
10061
|
AuthStorage,
|
|
10323
10062
|
createAgentSession,
|
|
@@ -10413,6 +10152,12 @@ function registerCommandProtection(cwd, historyFile, recordHistoryEvent) {
|
|
|
10413
10152
|
}
|
|
10414
10153
|
return void 0;
|
|
10415
10154
|
});
|
|
10155
|
+
pi.on("tool_result", (event) => {
|
|
10156
|
+
if (!event.isError && mayCreatePullRequest(event.toolName, event.input)) {
|
|
10157
|
+
pullRequestCheckService.check().catch(() => {
|
|
10158
|
+
});
|
|
10159
|
+
}
|
|
10160
|
+
});
|
|
10416
10161
|
};
|
|
10417
10162
|
}
|
|
10418
10163
|
var PiManager = class extends CodingAgentManager {
|
|
@@ -10425,12 +10170,12 @@ var PiManager = class extends CodingAgentManager {
|
|
|
10425
10170
|
providerApiKey = null;
|
|
10426
10171
|
constructor(options) {
|
|
10427
10172
|
super(options);
|
|
10428
|
-
this.historyFilePath = options.historyFilePath ??
|
|
10173
|
+
this.historyFilePath = options.historyFilePath ?? join22(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
|
|
10429
10174
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
10430
10175
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
10431
10176
|
}
|
|
10432
10177
|
async initialize() {
|
|
10433
|
-
await
|
|
10178
|
+
await mkdir16(dirname10(this.historyFilePath), { recursive: true });
|
|
10434
10179
|
}
|
|
10435
10180
|
getHistorySink() {
|
|
10436
10181
|
return this.historyFile;
|
|
@@ -10530,7 +10275,7 @@ var PiManager = class extends CodingAgentManager {
|
|
|
10530
10275
|
const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
|
|
10531
10276
|
const resourceLoader = new DefaultResourceLoader({
|
|
10532
10277
|
cwd: this.workingDirectory,
|
|
10533
|
-
agentDir:
|
|
10278
|
+
agentDir: join22(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
|
|
10534
10279
|
extensionFactories: [registerCommandProtection(
|
|
10535
10280
|
this.workingDirectory,
|
|
10536
10281
|
this.historyFile,
|
|
@@ -11261,14 +11006,14 @@ async function uploadAnalytics(endpoint, body) {
|
|
|
11261
11006
|
// src/analytics/analytics-record-buffer.ts
|
|
11262
11007
|
import {
|
|
11263
11008
|
appendFile as appendFile3,
|
|
11264
|
-
mkdir as
|
|
11265
|
-
readFile as
|
|
11009
|
+
mkdir as mkdir17,
|
|
11010
|
+
readFile as readFile16,
|
|
11266
11011
|
readdir as readdir6,
|
|
11267
11012
|
rename as rename2,
|
|
11268
11013
|
unlink as unlink3
|
|
11269
11014
|
} from "fs/promises";
|
|
11270
|
-
import { existsSync as
|
|
11271
|
-
import { join as
|
|
11015
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, readdirSync } from "fs";
|
|
11016
|
+
import { join as join23 } from "path";
|
|
11272
11017
|
var MAX_FAILED_RECORDS = 500;
|
|
11273
11018
|
var FLUSH_DEBOUNCE_MS = 15e3;
|
|
11274
11019
|
var MAX_UPLOADED_SEGMENTS = 50;
|
|
@@ -11280,7 +11025,7 @@ var AnalyticsRecordBuffer = class {
|
|
|
11280
11025
|
options.storageName,
|
|
11281
11026
|
...options.legacyStorageNames ?? []
|
|
11282
11027
|
];
|
|
11283
|
-
this.liveFile =
|
|
11028
|
+
this.liveFile = join23(ENGINE_DIR2, `${options.storageName}.jsonl`);
|
|
11284
11029
|
this.segmentFilePatterns = this.storageNames.map(
|
|
11285
11030
|
(storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
|
|
11286
11031
|
);
|
|
@@ -11303,7 +11048,7 @@ var AnalyticsRecordBuffer = class {
|
|
|
11303
11048
|
if (this.recordIds.has(recordId)) return;
|
|
11304
11049
|
this.recordIds.add(recordId);
|
|
11305
11050
|
}
|
|
11306
|
-
const pending =
|
|
11051
|
+
const pending = mkdir17(ENGINE_DIR2, { recursive: true }).then(() => appendFile3(this.liveFile, `${JSON.stringify(record)}
|
|
11307
11052
|
`, "utf-8")).catch((error) => {
|
|
11308
11053
|
console.error(
|
|
11309
11054
|
`[${this.options.storageName}] Append failed, will retry on flush:`,
|
|
@@ -11315,10 +11060,10 @@ var AnalyticsRecordBuffer = class {
|
|
|
11315
11060
|
void pending.finally(() => this.pendingAppends.delete(pending));
|
|
11316
11061
|
}
|
|
11317
11062
|
readStoredRecords() {
|
|
11318
|
-
if (!
|
|
11063
|
+
if (!existsSync9(ENGINE_DIR2)) return [];
|
|
11319
11064
|
return readdirSync(ENGINE_DIR2, { withFileTypes: true }).filter((entry) => entry.isFile() && this.storageNames.some(
|
|
11320
11065
|
(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(
|
|
11066
|
+
)).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(readFileSync5(join23(ENGINE_DIR2, name), "utf-8"))).map((record) => {
|
|
11322
11067
|
const recordId = this.options.getRecordId?.(record);
|
|
11323
11068
|
if (recordId) this.recordIds.add(recordId);
|
|
11324
11069
|
return record;
|
|
@@ -11353,8 +11098,8 @@ var AnalyticsRecordBuffer = class {
|
|
|
11353
11098
|
}
|
|
11354
11099
|
for (const storageName of this.storageNames) {
|
|
11355
11100
|
await rename2(
|
|
11356
|
-
|
|
11357
|
-
|
|
11101
|
+
join23(ENGINE_DIR2, `${storageName}.jsonl`),
|
|
11102
|
+
join23(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
|
|
11358
11103
|
).catch(() => {
|
|
11359
11104
|
});
|
|
11360
11105
|
}
|
|
@@ -11364,7 +11109,7 @@ var AnalyticsRecordBuffer = class {
|
|
|
11364
11109
|
for (const entry of entries) {
|
|
11365
11110
|
if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry))) continue;
|
|
11366
11111
|
try {
|
|
11367
|
-
await this.uploadSegment(
|
|
11112
|
+
await this.uploadSegment(join23(ENGINE_DIR2, entry));
|
|
11368
11113
|
flushed++;
|
|
11369
11114
|
} catch (error) {
|
|
11370
11115
|
failed++;
|
|
@@ -11393,14 +11138,14 @@ var AnalyticsRecordBuffer = class {
|
|
|
11393
11138
|
(storageName) => entry.startsWith(`${storageName}.`)
|
|
11394
11139
|
) && entry.endsWith(UPLOADED_SUFFIX)).sort();
|
|
11395
11140
|
for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
|
|
11396
|
-
await unlink3(
|
|
11141
|
+
await unlink3(join23(ENGINE_DIR2, entry)).catch(() => {
|
|
11397
11142
|
});
|
|
11398
11143
|
}
|
|
11399
11144
|
}
|
|
11400
11145
|
return { flushed, failed };
|
|
11401
11146
|
}
|
|
11402
11147
|
async uploadSegment(filePath) {
|
|
11403
|
-
const records = this.parseRecords(await
|
|
11148
|
+
const records = this.parseRecords(await readFile16(filePath, "utf-8"));
|
|
11404
11149
|
if (records.length > 0) await this.options.upload(records);
|
|
11405
11150
|
await rename2(filePath, `${filePath}${UPLOADED_SUFFIX}`).catch(() => {
|
|
11406
11151
|
});
|
|
@@ -12186,19 +11931,19 @@ var KeepAliveService = class _KeepAliveService {
|
|
|
12186
11931
|
var keepAliveService = new KeepAliveService();
|
|
12187
11932
|
|
|
12188
11933
|
// src/services/canvas-service.ts
|
|
12189
|
-
import { readdir as readdir7, readFile as
|
|
12190
|
-
import { homedir as
|
|
12191
|
-
import { join as
|
|
11934
|
+
import { readdir as readdir7, readFile as readFile17, stat as stat3 } from "fs/promises";
|
|
11935
|
+
import { homedir as homedir12 } from "os";
|
|
11936
|
+
import { join as join24 } from "path";
|
|
12192
11937
|
var GLOBAL_CANVAS_DIRECTORIES = [
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
11938
|
+
join24(homedir12(), ".claude", "plans"),
|
|
11939
|
+
join24(process.env.XDG_DATA_HOME ?? join24(homedir12(), ".local", "share"), "opencode", "plans"),
|
|
11940
|
+
join24(homedir12(), ".replicas", "canvas")
|
|
12196
11941
|
];
|
|
12197
11942
|
async function canvasDirectories() {
|
|
12198
11943
|
const repositories = await gitService.listRepositories().catch(() => []);
|
|
12199
11944
|
return [
|
|
12200
11945
|
...GLOBAL_CANVAS_DIRECTORIES,
|
|
12201
|
-
...repositories.map((repository) =>
|
|
11946
|
+
...repositories.map((repository) => join24(repository.path, ".opencode", "plans"))
|
|
12202
11947
|
];
|
|
12203
11948
|
}
|
|
12204
11949
|
var CanvasService = class {
|
|
@@ -12222,7 +11967,7 @@ var CanvasService = class {
|
|
|
12222
11967
|
for (const entry of entries) {
|
|
12223
11968
|
if (entry.name.startsWith(".")) continue;
|
|
12224
11969
|
const filename = current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name;
|
|
12225
|
-
const filePath =
|
|
11970
|
+
const filePath = join24(current.directory, entry.name);
|
|
12226
11971
|
if (entry.isDirectory()) {
|
|
12227
11972
|
pending.push({ directory: filePath, relativePath: filename });
|
|
12228
11973
|
continue;
|
|
@@ -12247,7 +11992,7 @@ var CanvasService = class {
|
|
|
12247
11992
|
if (!safe) return null;
|
|
12248
11993
|
const { kind, mimeType } = classifyCanvasFilename(safe);
|
|
12249
11994
|
for (const directory of await this.directories()) {
|
|
12250
|
-
const filePath =
|
|
11995
|
+
const filePath = join24(directory, safe);
|
|
12251
11996
|
let sizeBytes = 0;
|
|
12252
11997
|
let updatedAt = "";
|
|
12253
11998
|
try {
|
|
@@ -12268,7 +12013,7 @@ var CanvasService = class {
|
|
|
12268
12013
|
};
|
|
12269
12014
|
}
|
|
12270
12015
|
try {
|
|
12271
|
-
const bytes = await
|
|
12016
|
+
const bytes = await readFile17(filePath);
|
|
12272
12017
|
return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
|
|
12273
12018
|
} catch {
|
|
12274
12019
|
continue;
|
|
@@ -12418,14 +12163,14 @@ async function reconcileCanvasItems(filenames) {
|
|
|
12418
12163
|
// src/services/upload-chat-transcripts.ts
|
|
12419
12164
|
import { createReadStream } from "fs";
|
|
12420
12165
|
import { createHash as createHash3 } from "crypto";
|
|
12421
|
-
import { readFile as
|
|
12422
|
-
import { basename as
|
|
12166
|
+
import { readFile as readFile18, readdir as readdir8, stat as stat4 } from "fs/promises";
|
|
12167
|
+
import { basename as basename3, join as join26 } from "path";
|
|
12423
12168
|
|
|
12424
12169
|
// src/services/chat/chat-senders.ts
|
|
12425
|
-
import { join as
|
|
12426
|
-
var CHAT_SENDERS_DIR =
|
|
12170
|
+
import { join as join25 } from "path";
|
|
12171
|
+
var CHAT_SENDERS_DIR = join25(ENGINE_DIR2, "chat-senders");
|
|
12427
12172
|
function chatMessageSendersFilePath(chatId) {
|
|
12428
|
-
return
|
|
12173
|
+
return join25(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
12429
12174
|
}
|
|
12430
12175
|
function parseChatMessageSendersJsonl(content) {
|
|
12431
12176
|
return content.split("\n").flatMap((line) => {
|
|
@@ -12441,9 +12186,9 @@ function parseChatMessageSendersJsonl(content) {
|
|
|
12441
12186
|
|
|
12442
12187
|
// src/services/upload-chat-transcripts.ts
|
|
12443
12188
|
var HISTORY_DIRS = [
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12189
|
+
join26(ENGINE_DIR2, "claude-histories"),
|
|
12190
|
+
join26(ENGINE_DIR2, "relay-histories"),
|
|
12191
|
+
join26(ENGINE_DIR2, "codex-histories")
|
|
12447
12192
|
];
|
|
12448
12193
|
async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), capture) {
|
|
12449
12194
|
let flushed = 0;
|
|
@@ -12459,9 +12204,9 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
12459
12204
|
}
|
|
12460
12205
|
for (const entry of entries) {
|
|
12461
12206
|
if (!entry.endsWith(".jsonl")) continue;
|
|
12462
|
-
const chatId =
|
|
12207
|
+
const chatId = basename3(entry, ".jsonl");
|
|
12463
12208
|
tasks.push(
|
|
12464
|
-
uploadChatTranscript(chatId,
|
|
12209
|
+
uploadChatTranscript(chatId, join26(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
|
|
12465
12210
|
flushed++;
|
|
12466
12211
|
if (artifact && capture) revisions.push(artifact);
|
|
12467
12212
|
}).catch((err) => {
|
|
@@ -12477,7 +12222,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
12477
12222
|
async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
12478
12223
|
const { size } = await stat4(filePath);
|
|
12479
12224
|
if (size === 0) return null;
|
|
12480
|
-
const historyPages = createChatTranscriptPages(await
|
|
12225
|
+
const historyPages = createChatTranscriptPages(await readFile18(filePath));
|
|
12481
12226
|
const metadata = chat ? {
|
|
12482
12227
|
provider: chat.provider,
|
|
12483
12228
|
credential: ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[chat.provider],
|
|
@@ -12496,7 +12241,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
|
12496
12241
|
}
|
|
12497
12242
|
try {
|
|
12498
12243
|
metadata.senders = parseChatMessageSendersJsonl(
|
|
12499
|
-
await
|
|
12244
|
+
await readFile18(chatMessageSendersFilePath(chatId), "utf-8")
|
|
12500
12245
|
);
|
|
12501
12246
|
} catch (error) {
|
|
12502
12247
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
@@ -12543,43 +12288,10 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
|
12543
12288
|
return artifact;
|
|
12544
12289
|
}
|
|
12545
12290
|
|
|
12546
|
-
// src/services/upload-repo-state.ts
|
|
12547
|
-
var uploadQueue = Promise.resolve();
|
|
12548
|
-
async function sendRepoState(repos) {
|
|
12549
|
-
const form = new FormData();
|
|
12550
|
-
form.append(
|
|
12551
|
-
"file",
|
|
12552
|
-
new Blob([JSON.stringify({ repos })], { type: "application/json" }),
|
|
12553
|
-
"repo-state.json"
|
|
12554
|
-
);
|
|
12555
|
-
const response = await monolithRequest("/v1/engine/repo-state", { body: form });
|
|
12556
|
-
if (!response.ok) {
|
|
12557
|
-
const errorText = await response.text();
|
|
12558
|
-
throw new Error(`upload failed: ${response.status} ${errorText}`);
|
|
12559
|
-
}
|
|
12560
|
-
}
|
|
12561
|
-
function uploadRepoState(repos) {
|
|
12562
|
-
const upload = uploadQueue.then(() => sendRepoState(repos));
|
|
12563
|
-
uploadQueue = upload.catch(() => {
|
|
12564
|
-
});
|
|
12565
|
-
return upload;
|
|
12566
|
-
}
|
|
12567
|
-
async function flushRepoState() {
|
|
12568
|
-
try {
|
|
12569
|
-
const { repos, complete } = await gitService.listReposWithCompleteness({ includeDiffs: true });
|
|
12570
|
-
if (!complete) return { flushed: 0, skipped: 1, failed: 0 };
|
|
12571
|
-
await uploadRepoState(repos);
|
|
12572
|
-
return { flushed: 1, skipped: 0, failed: 0 };
|
|
12573
|
-
} catch (err) {
|
|
12574
|
-
console.error("[RepoStateUploader] upload failed:", err);
|
|
12575
|
-
return { flushed: 0, skipped: 0, failed: 1 };
|
|
12576
|
-
}
|
|
12577
|
-
}
|
|
12578
|
-
|
|
12579
12291
|
// src/services/upload-engine-logs.ts
|
|
12580
12292
|
import { createReadStream as createReadStream2 } from "fs";
|
|
12581
12293
|
import { readdir as readdir9, stat as stat5 } from "fs/promises";
|
|
12582
|
-
import { join as
|
|
12294
|
+
import { join as join27 } from "path";
|
|
12583
12295
|
var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
|
|
12584
12296
|
var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
|
|
12585
12297
|
var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
|
|
@@ -12608,7 +12320,7 @@ async function flushAllEngineLogs() {
|
|
|
12608
12320
|
const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
|
|
12609
12321
|
try {
|
|
12610
12322
|
const sessionId = filename.slice(0, -".log".length);
|
|
12611
|
-
const filePath =
|
|
12323
|
+
const filePath = join27(LOG_DIR, filename);
|
|
12612
12324
|
const fileStat = await runBeforeDeadline(() => stat5(filePath), deadline);
|
|
12613
12325
|
if (!fileStat.isFile()) {
|
|
12614
12326
|
skipped++;
|
|
@@ -12709,7 +12421,7 @@ function isCodexAvailable() {
|
|
|
12709
12421
|
return detectCodexAuthMethod() !== "none";
|
|
12710
12422
|
}
|
|
12711
12423
|
function isOpencodeAvailable() {
|
|
12712
|
-
return
|
|
12424
|
+
return existsSync10(OPENCODE_AUTH_PATH);
|
|
12713
12425
|
}
|
|
12714
12426
|
function isDeepseekAvailable() {
|
|
12715
12427
|
return Boolean(getDeepseekApiKey());
|
|
@@ -12721,7 +12433,7 @@ function isKimiAvailable() {
|
|
|
12721
12433
|
return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
|
|
12722
12434
|
}
|
|
12723
12435
|
function isPiAvailable() {
|
|
12724
|
-
return
|
|
12436
|
+
return existsSync10(PI_AUTH_PATH);
|
|
12725
12437
|
}
|
|
12726
12438
|
function isCursorAvailable() {
|
|
12727
12439
|
return Boolean(ENGINE_ENV.CURSOR_API_KEY);
|
|
@@ -12860,13 +12572,13 @@ var ChatService = class {
|
|
|
12860
12572
|
persistPromise = null;
|
|
12861
12573
|
persistQueued = false;
|
|
12862
12574
|
async initialize() {
|
|
12863
|
-
await
|
|
12864
|
-
await
|
|
12865
|
-
await
|
|
12866
|
-
await
|
|
12867
|
-
await
|
|
12868
|
-
await
|
|
12869
|
-
await
|
|
12575
|
+
await mkdir18(ENGINE_DIR2, { recursive: true });
|
|
12576
|
+
await mkdir18(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
12577
|
+
await mkdir18(RELAY_HISTORY_DIR, { recursive: true });
|
|
12578
|
+
await mkdir18(CODEX_HISTORY_DIR, { recursive: true });
|
|
12579
|
+
await mkdir18(CURSOR_HISTORY_DIR, { recursive: true });
|
|
12580
|
+
await mkdir18(OPENCODE_HISTORY_DIR, { recursive: true });
|
|
12581
|
+
await mkdir18(CHAT_SENDERS_DIR, { recursive: true });
|
|
12870
12582
|
const persisted = await this.loadChats();
|
|
12871
12583
|
for (const chat of persisted) {
|
|
12872
12584
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -13066,7 +12778,7 @@ var ChatService = class {
|
|
|
13066
12778
|
}
|
|
13067
12779
|
}
|
|
13068
12780
|
async readSenders(chatId) {
|
|
13069
|
-
return
|
|
12781
|
+
return readFile19(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
|
|
13070
12782
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
|
|
13071
12783
|
console.error("[ChatService] Failed to read sender records:", error);
|
|
13072
12784
|
return [];
|
|
@@ -13305,13 +13017,13 @@ var ChatService = class {
|
|
|
13305
13017
|
return descendants;
|
|
13306
13018
|
}
|
|
13307
13019
|
async deleteHistoryFile(persisted) {
|
|
13308
|
-
const historyPath =
|
|
13020
|
+
const historyPath = join28(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
|
|
13309
13021
|
await Promise.all([
|
|
13310
13022
|
historyPath,
|
|
13311
13023
|
`${historyPath}.pages.jsonl`,
|
|
13312
13024
|
`${historyPath}.pages.index.json`
|
|
13313
|
-
].map((path6) =>
|
|
13314
|
-
await
|
|
13025
|
+
].map((path6) => rm3(path6, { force: true })));
|
|
13026
|
+
await rm3(chatMessageSendersFilePath(persisted.id), { force: true });
|
|
13315
13027
|
}
|
|
13316
13028
|
async getChatHistory(chatId, page = {}) {
|
|
13317
13029
|
const chat = this.requireChat(chatId);
|
|
@@ -13436,7 +13148,7 @@ var ChatService = class {
|
|
|
13436
13148
|
if (persisted.provider === "claude") {
|
|
13437
13149
|
provider = new ClaudeManager({
|
|
13438
13150
|
workingDirectory: this.workingDirectory,
|
|
13439
|
-
historyFilePath:
|
|
13151
|
+
historyFilePath: join28(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13440
13152
|
initialSessionId: persisted.providerSessionId,
|
|
13441
13153
|
onSaveSessionId: saveSession,
|
|
13442
13154
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13456,7 +13168,7 @@ var ChatService = class {
|
|
|
13456
13168
|
});
|
|
13457
13169
|
provider = new RelayManager({
|
|
13458
13170
|
workingDirectory: this.workingDirectory,
|
|
13459
|
-
historyFilePath:
|
|
13171
|
+
historyFilePath: join28(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13460
13172
|
initialSessionId: persisted.providerSessionId,
|
|
13461
13173
|
onSaveSessionId: saveSession,
|
|
13462
13174
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13470,7 +13182,7 @@ var ChatService = class {
|
|
|
13470
13182
|
} else if (persisted.provider === "cursor") {
|
|
13471
13183
|
provider = new CursorManager({
|
|
13472
13184
|
workingDirectory: this.workingDirectory,
|
|
13473
|
-
historyFilePath:
|
|
13185
|
+
historyFilePath: join28(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13474
13186
|
initialSessionId: persisted.providerSessionId,
|
|
13475
13187
|
onSaveSessionId: saveSession,
|
|
13476
13188
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13482,7 +13194,7 @@ var ChatService = class {
|
|
|
13482
13194
|
} else if (persisted.provider === "deepseek") {
|
|
13483
13195
|
provider = new DeepseekManager({
|
|
13484
13196
|
workingDirectory: this.workingDirectory,
|
|
13485
|
-
historyFilePath:
|
|
13197
|
+
historyFilePath: join28(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13486
13198
|
initialSessionId: persisted.providerSessionId,
|
|
13487
13199
|
onSaveSessionId: saveSession,
|
|
13488
13200
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13494,7 +13206,7 @@ var ChatService = class {
|
|
|
13494
13206
|
} else if (persisted.provider === "fx") {
|
|
13495
13207
|
provider = new FxManager({
|
|
13496
13208
|
workingDirectory: this.workingDirectory,
|
|
13497
|
-
historyFilePath:
|
|
13209
|
+
historyFilePath: join28(FX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13498
13210
|
initialSessionId: persisted.providerSessionId,
|
|
13499
13211
|
onSaveSessionId: saveSession,
|
|
13500
13212
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13505,7 +13217,7 @@ var ChatService = class {
|
|
|
13505
13217
|
} else if (persisted.provider === "kimi") {
|
|
13506
13218
|
provider = new KimiManager({
|
|
13507
13219
|
workingDirectory: this.workingDirectory,
|
|
13508
|
-
historyFilePath:
|
|
13220
|
+
historyFilePath: join28(KIMI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13509
13221
|
initialSessionId: persisted.providerSessionId,
|
|
13510
13222
|
onSaveSessionId: saveSession,
|
|
13511
13223
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13516,7 +13228,7 @@ var ChatService = class {
|
|
|
13516
13228
|
} else if (persisted.provider === "opencode") {
|
|
13517
13229
|
provider = new OpencodeManager({
|
|
13518
13230
|
workingDirectory: this.workingDirectory,
|
|
13519
|
-
historyFilePath:
|
|
13231
|
+
historyFilePath: join28(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13520
13232
|
initialSessionId: persisted.providerSessionId,
|
|
13521
13233
|
onSaveSessionId: saveSession,
|
|
13522
13234
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13528,7 +13240,7 @@ var ChatService = class {
|
|
|
13528
13240
|
} else if (persisted.provider === "pi") {
|
|
13529
13241
|
provider = new PiManager({
|
|
13530
13242
|
workingDirectory: this.workingDirectory,
|
|
13531
|
-
historyFilePath:
|
|
13243
|
+
historyFilePath: join28(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13532
13244
|
initialSessionId: persisted.providerSessionId,
|
|
13533
13245
|
onSaveSessionId: saveSession,
|
|
13534
13246
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13545,7 +13257,7 @@ var ChatService = class {
|
|
|
13545
13257
|
);
|
|
13546
13258
|
provider = new CodexAspManager({
|
|
13547
13259
|
workingDirectory: this.workingDirectory,
|
|
13548
|
-
historyFilePath:
|
|
13260
|
+
historyFilePath: join28(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
13549
13261
|
initialSessionId: persisted.providerSessionId,
|
|
13550
13262
|
onSaveSessionId: saveSession,
|
|
13551
13263
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -13753,7 +13465,7 @@ var ChatService = class {
|
|
|
13753
13465
|
});
|
|
13754
13466
|
uploadChatTranscript(
|
|
13755
13467
|
chatId,
|
|
13756
|
-
|
|
13468
|
+
join28(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
|
|
13757
13469
|
this.toSummary(chat)
|
|
13758
13470
|
).catch((err) => {
|
|
13759
13471
|
console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
|
|
@@ -13769,7 +13481,7 @@ var ChatService = class {
|
|
|
13769
13481
|
}
|
|
13770
13482
|
async loadChats() {
|
|
13771
13483
|
try {
|
|
13772
|
-
const content = await
|
|
13484
|
+
const content = await readFile19(CHATS_FILE, "utf-8");
|
|
13773
13485
|
return parsePersistedChatsContent(content);
|
|
13774
13486
|
} catch (error) {
|
|
13775
13487
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -13784,7 +13496,7 @@ var ChatService = class {
|
|
|
13784
13496
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
13785
13497
|
}
|
|
13786
13498
|
try {
|
|
13787
|
-
const backupContent = await
|
|
13499
|
+
const backupContent = await readFile19(CHATS_BACKUP_FILE, "utf-8");
|
|
13788
13500
|
return parsePersistedChatsContent(backupContent);
|
|
13789
13501
|
} catch (backupError) {
|
|
13790
13502
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -13805,7 +13517,7 @@ var ChatService = class {
|
|
|
13805
13517
|
this.persistQueued = false;
|
|
13806
13518
|
const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
|
|
13807
13519
|
try {
|
|
13808
|
-
await
|
|
13520
|
+
await copyFile2(CHATS_FILE, CHATS_BACKUP_FILE);
|
|
13809
13521
|
} catch (error) {
|
|
13810
13522
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
|
|
13811
13523
|
console.error("[ChatService] Failed to update chats backup:", error);
|
|
@@ -13842,12 +13554,6 @@ var ChatService = class {
|
|
|
13842
13554
|
}
|
|
13843
13555
|
}
|
|
13844
13556
|
async publishAgentTurnCompleteWebhook(chat, completedAt, processing = false) {
|
|
13845
|
-
await Promise.all([
|
|
13846
|
-
githubTokenManager.refreshOnce().catch(() => {
|
|
13847
|
-
}),
|
|
13848
|
-
gitlabTokenManager.refreshOnce().catch(() => {
|
|
13849
|
-
})
|
|
13850
|
-
]);
|
|
13851
13557
|
const linearSessionId = ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID;
|
|
13852
13558
|
const observedBranches = chat.observedBranchesByRepo;
|
|
13853
13559
|
chat.observedBranchesByRepo = /* @__PURE__ */ new Map();
|
|
@@ -13856,20 +13562,12 @@ var ChatService = class {
|
|
|
13856
13562
|
const errorNotificationTarget = chat.activeErrorNotificationTarget;
|
|
13857
13563
|
chat.activeErrorNotificationTarget = null;
|
|
13858
13564
|
let repoStatuses = [];
|
|
13859
|
-
let repoStateComplete = false;
|
|
13860
13565
|
try {
|
|
13861
|
-
|
|
13862
|
-
repoStatuses = refreshed.repos;
|
|
13863
|
-
repoStateComplete = refreshed.complete;
|
|
13566
|
+
repoStatuses = await refreshRepoStatuses(observedBranches, true);
|
|
13864
13567
|
console.log(`Repository Statuses Refreshed: `, repoStatuses);
|
|
13865
13568
|
} catch (error) {
|
|
13866
13569
|
console.error("[ChatService] Failed to refresh repo statuses:", error);
|
|
13867
13570
|
}
|
|
13868
|
-
if (repoStateComplete) {
|
|
13869
|
-
uploadRepoState(repoStatuses).catch((err) => {
|
|
13870
|
-
console.error("[ChatService] Failed to upload repository state:", err);
|
|
13871
|
-
});
|
|
13872
|
-
}
|
|
13873
13571
|
try {
|
|
13874
13572
|
const payload = {
|
|
13875
13573
|
...linearSessionId ? { linearSessionId } : {},
|
|
@@ -13888,8 +13586,8 @@ var ChatService = class {
|
|
|
13888
13586
|
|
|
13889
13587
|
// src/services/repo-file-service.ts
|
|
13890
13588
|
import { execFile } from "child_process";
|
|
13891
|
-
import { readFile as
|
|
13892
|
-
import { join as
|
|
13589
|
+
import { readFile as readFile20, realpath, stat as stat6 } from "fs/promises";
|
|
13590
|
+
import { join as join29, resolve as resolve2, extname as extname2 } from "path";
|
|
13893
13591
|
var CACHE_TTL_MS = 3e4;
|
|
13894
13592
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
13895
13593
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -13941,11 +13639,11 @@ function scoreMatch(query2, filePath) {
|
|
|
13941
13639
|
const lowerQuery = query2.toLowerCase();
|
|
13942
13640
|
const lowerPath = filePath.toLowerCase();
|
|
13943
13641
|
const segments = lowerPath.split("/");
|
|
13944
|
-
const
|
|
13945
|
-
if (
|
|
13946
|
-
if (
|
|
13642
|
+
const basename4 = segments[segments.length - 1] ?? "";
|
|
13643
|
+
if (basename4 === lowerQuery) return 100;
|
|
13644
|
+
if (basename4.startsWith(lowerQuery)) return 90;
|
|
13947
13645
|
if (segments.some((seg) => seg === lowerQuery)) return 80;
|
|
13948
|
-
if (
|
|
13646
|
+
if (basename4.includes(lowerQuery)) return 70;
|
|
13949
13647
|
if (segments.some((seg) => seg.includes(lowerQuery))) return 60;
|
|
13950
13648
|
if (lowerPath.includes(lowerQuery)) return 50;
|
|
13951
13649
|
return 0;
|
|
@@ -14041,7 +13739,7 @@ var RepoFileService = class {
|
|
|
14041
13739
|
const repo = repos.find((r) => r.name === repoName);
|
|
14042
13740
|
if (!repo) return null;
|
|
14043
13741
|
try {
|
|
14044
|
-
const fullPath = await realpath(resolve2(
|
|
13742
|
+
const fullPath = await realpath(resolve2(join29(repo.path, filePath)));
|
|
14045
13743
|
const repoRoot = await realpath(repo.path);
|
|
14046
13744
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
14047
13745
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
@@ -14072,7 +13770,7 @@ var RepoFileService = class {
|
|
|
14072
13770
|
sizeBytes,
|
|
14073
13771
|
binary: true,
|
|
14074
13772
|
tooLarge: false,
|
|
14075
|
-
base64: (await
|
|
13773
|
+
base64: (await readFile20(fullPath)).toString("base64"),
|
|
14076
13774
|
mimeType
|
|
14077
13775
|
};
|
|
14078
13776
|
}
|
|
@@ -14097,7 +13795,7 @@ var RepoFileService = class {
|
|
|
14097
13795
|
tooLarge: true
|
|
14098
13796
|
};
|
|
14099
13797
|
}
|
|
14100
|
-
const content = await
|
|
13798
|
+
const content = await readFile20(fullPath, "utf-8");
|
|
14101
13799
|
return {
|
|
14102
13800
|
repoName,
|
|
14103
13801
|
path: filePath,
|
|
@@ -14175,21 +13873,21 @@ var RepoFileService = class {
|
|
|
14175
13873
|
// src/v1-routes.ts
|
|
14176
13874
|
import { Hono } from "hono";
|
|
14177
13875
|
import { z as z5 } from "zod";
|
|
14178
|
-
import { readdir as readdir11, stat as stat7, readFile as
|
|
14179
|
-
import { join as
|
|
13876
|
+
import { readdir as readdir11, stat as stat7, readFile as readFile23 } from "fs/promises";
|
|
13877
|
+
import { join as join32, resolve as resolve3 } from "path";
|
|
14180
13878
|
|
|
14181
13879
|
// src/services/warm-hooks-service.ts
|
|
14182
13880
|
import { spawn as spawn6 } from "child_process";
|
|
14183
|
-
import { readFile as
|
|
14184
|
-
import { existsSync as
|
|
14185
|
-
import { join as
|
|
13881
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
13882
|
+
import { existsSync as existsSync11 } from "fs";
|
|
13883
|
+
import { join as join31 } from "path";
|
|
14186
13884
|
|
|
14187
13885
|
// 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 =
|
|
13886
|
+
import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile9, readdir as readdir10, appendFile as appendFile5, unlink as unlink4 } from "fs/promises";
|
|
13887
|
+
import { homedir as homedir13 } from "os";
|
|
13888
|
+
import { join as join30 } from "path";
|
|
13889
|
+
var LOGS_DIR2 = join30(homedir13(), ".replicas", "warm-hook-logs");
|
|
13890
|
+
var CURRENT_RUN_LOG = join30(LOGS_DIR2, "current-run.log");
|
|
14193
13891
|
var GLOBAL_FILENAME = "global.json";
|
|
14194
13892
|
function withPreview2(stored) {
|
|
14195
13893
|
const preview = buildHookOutputPreview(stored.output);
|
|
@@ -14197,7 +13895,7 @@ function withPreview2(stored) {
|
|
|
14197
13895
|
}
|
|
14198
13896
|
var WarmHookLogsService = class {
|
|
14199
13897
|
async ensureDir() {
|
|
14200
|
-
await
|
|
13898
|
+
await mkdir19(LOGS_DIR2, { recursive: true });
|
|
14201
13899
|
}
|
|
14202
13900
|
async saveGlobalHookLog(entry) {
|
|
14203
13901
|
await this.ensureDir();
|
|
@@ -14206,7 +13904,7 @@ var WarmHookLogsService = class {
|
|
|
14206
13904
|
hookName: "organization",
|
|
14207
13905
|
...entry
|
|
14208
13906
|
};
|
|
14209
|
-
await
|
|
13907
|
+
await writeFile9(join30(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
14210
13908
|
`, "utf-8");
|
|
14211
13909
|
}
|
|
14212
13910
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -14216,7 +13914,7 @@ var WarmHookLogsService = class {
|
|
|
14216
13914
|
hookName: "environment",
|
|
14217
13915
|
...entry
|
|
14218
13916
|
};
|
|
14219
|
-
await
|
|
13917
|
+
await writeFile9(join30(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
14220
13918
|
`, "utf-8");
|
|
14221
13919
|
}
|
|
14222
13920
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -14226,7 +13924,7 @@ var WarmHookLogsService = class {
|
|
|
14226
13924
|
hookName: repoName,
|
|
14227
13925
|
...entry
|
|
14228
13926
|
};
|
|
14229
|
-
await
|
|
13927
|
+
await writeFile9(join30(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
14230
13928
|
`, "utf-8");
|
|
14231
13929
|
}
|
|
14232
13930
|
async getAllLogs() {
|
|
@@ -14245,7 +13943,7 @@ var WarmHookLogsService = class {
|
|
|
14245
13943
|
continue;
|
|
14246
13944
|
}
|
|
14247
13945
|
try {
|
|
14248
|
-
const raw = await
|
|
13946
|
+
const raw = await readFile21(join30(LOGS_DIR2, file), "utf-8");
|
|
14249
13947
|
const stored = JSON.parse(raw);
|
|
14250
13948
|
logs.push(withPreview2(stored));
|
|
14251
13949
|
} catch {
|
|
@@ -14274,7 +13972,7 @@ var WarmHookLogsService = class {
|
|
|
14274
13972
|
}
|
|
14275
13973
|
async getCurrentRunLog() {
|
|
14276
13974
|
try {
|
|
14277
|
-
return await
|
|
13975
|
+
return await readFile21(CURRENT_RUN_LOG, "utf-8");
|
|
14278
13976
|
} catch (err) {
|
|
14279
13977
|
if (err.code === "ENOENT") return null;
|
|
14280
13978
|
throw err;
|
|
@@ -14283,7 +13981,7 @@ var WarmHookLogsService = class {
|
|
|
14283
13981
|
async getFullOutput(hookType, hookName) {
|
|
14284
13982
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
14285
13983
|
try {
|
|
14286
|
-
const raw = await
|
|
13984
|
+
const raw = await readFile21(join30(LOGS_DIR2, filename), "utf-8");
|
|
14287
13985
|
const stored = JSON.parse(raw);
|
|
14288
13986
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
14289
13987
|
return null;
|
|
@@ -14302,12 +14000,12 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
14302
14000
|
// src/services/warm-hooks-service.ts
|
|
14303
14001
|
async function readRepoWarmHook(repoPath) {
|
|
14304
14002
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
14305
|
-
const configPath =
|
|
14306
|
-
if (!
|
|
14003
|
+
const configPath = join31(repoPath, filename);
|
|
14004
|
+
if (!existsSync11(configPath)) {
|
|
14307
14005
|
continue;
|
|
14308
14006
|
}
|
|
14309
14007
|
try {
|
|
14310
|
-
const raw = await
|
|
14008
|
+
const raw = await readFile22(configPath, "utf-8");
|
|
14311
14009
|
const config = parseReplicasConfigString(raw, filename);
|
|
14312
14010
|
if (!config.warmHook) {
|
|
14313
14011
|
return null;
|
|
@@ -14564,7 +14262,7 @@ ${combinedScript}` : combinedScript;
|
|
|
14564
14262
|
|
|
14565
14263
|
// src/services/terminal-service.ts
|
|
14566
14264
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
14567
|
-
import { existsSync as
|
|
14265
|
+
import { existsSync as existsSync12 } from "fs";
|
|
14568
14266
|
import { spawn as spawn7 } from "node-pty";
|
|
14569
14267
|
var MAX_REPLAY_CHARS = 1024 * 1024;
|
|
14570
14268
|
var MAX_TERMINAL_SESSIONS = 8;
|
|
@@ -14583,7 +14281,7 @@ var TerminalService = class {
|
|
|
14583
14281
|
});
|
|
14584
14282
|
}
|
|
14585
14283
|
const id = randomUUID10();
|
|
14586
|
-
const shell = process.env.SHELL &&
|
|
14284
|
+
const shell = process.env.SHELL && existsSync12(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
|
|
14587
14285
|
const pty = spawn7(shell, ["-l"], {
|
|
14588
14286
|
name: "xterm-256color",
|
|
14589
14287
|
cols,
|
|
@@ -14710,6 +14408,11 @@ function jsonError(message, details) {
|
|
|
14710
14408
|
}
|
|
14711
14409
|
function createV1Routes(deps) {
|
|
14712
14410
|
const app2 = new Hono();
|
|
14411
|
+
app2.post("/pull-requests/check", (c) => {
|
|
14412
|
+
pullRequestCheckService.check().catch(() => {
|
|
14413
|
+
});
|
|
14414
|
+
return c.json({ accepted: true }, 202);
|
|
14415
|
+
});
|
|
14713
14416
|
const loadChatHistory = async (c, chatId, page, failureMessage, unexpectedStatus, legacy = false) => {
|
|
14714
14417
|
try {
|
|
14715
14418
|
const history = await deps.chatService.getChatHistory(chatId, legacy && page.limit !== void 0 ? {} : page);
|
|
@@ -15514,7 +15217,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
15514
15217
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
15515
15218
|
const sessions = await Promise.all(
|
|
15516
15219
|
logFiles.map(async (filename) => {
|
|
15517
|
-
const filePath =
|
|
15220
|
+
const filePath = join32(LOG_DIR, filename);
|
|
15518
15221
|
const fileStat = await stat7(filePath);
|
|
15519
15222
|
const sessionId = filename.replace(/\.log$/, "");
|
|
15520
15223
|
return {
|
|
@@ -15549,7 +15252,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
15549
15252
|
}
|
|
15550
15253
|
let content;
|
|
15551
15254
|
try {
|
|
15552
|
-
content = await
|
|
15255
|
+
content = await readFile23(filePath, "utf-8");
|
|
15553
15256
|
} catch {
|
|
15554
15257
|
return c.json(jsonError("Log session not found"), 404);
|
|
15555
15258
|
}
|
|
@@ -15620,17 +15323,17 @@ var HeartbeatService = class _HeartbeatService {
|
|
|
15620
15323
|
var heartbeatService = new HeartbeatService();
|
|
15621
15324
|
|
|
15622
15325
|
// src/services/workspace-sdk-service.ts
|
|
15623
|
-
import { cp, mkdir as
|
|
15624
|
-
import { dirname as
|
|
15625
|
-
import { fileURLToPath as
|
|
15326
|
+
import { cp, mkdir as mkdir20 } from "fs/promises";
|
|
15327
|
+
import { dirname as dirname11, resolve as resolve4 } from "path";
|
|
15328
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15626
15329
|
async function installWorkspaceSdk() {
|
|
15627
|
-
const source = resolve4(
|
|
15330
|
+
const source = resolve4(dirname11(fileURLToPath4(import.meta.url)), "../../workspace-sdk");
|
|
15628
15331
|
const targets = [
|
|
15629
15332
|
resolve4(ENGINE_ENV.HOME_DIR, "node_modules/@replicas/sdk"),
|
|
15630
15333
|
"/tmp/node_modules/@replicas/sdk"
|
|
15631
15334
|
];
|
|
15632
15335
|
await Promise.all(targets.map(async (target) => {
|
|
15633
|
-
await
|
|
15336
|
+
await mkdir20(target, { recursive: true });
|
|
15634
15337
|
await cp(source, target, { recursive: true, force: true });
|
|
15635
15338
|
}));
|
|
15636
15339
|
}
|
|
@@ -15658,7 +15361,7 @@ async function timeStartupStep(name, fn) {
|
|
|
15658
15361
|
async function waitForInitializationGate() {
|
|
15659
15362
|
if (!ENGINE_ENV.REPLICAS_ENGINE_DEFER_INITIALIZATION) return;
|
|
15660
15363
|
const deadline = Date.now() + ENGINE_INIT_GATE_TIMEOUT_MS;
|
|
15661
|
-
while (!
|
|
15364
|
+
while (!existsSync13(SANDBOX_PATHS.ENGINE_INIT_GATE)) {
|
|
15662
15365
|
if (Date.now() >= deadline) {
|
|
15663
15366
|
throw new Error("Timed out waiting for deferred engine initialization gate");
|
|
15664
15367
|
}
|