replicas-engine 0.1.702 → 0.1.703
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/{chunk-UNES5UCP.js → chunk-LPJ6WYD4.js} +2 -544
- package/dist/src/chunk-RVDZXLBY.js +287 -0
- package/dist/src/chunk-U3XQBTVU.js +551 -0
- package/dist/src/command-protection-hook.js +48 -0
- package/dist/src/deepseek-command-protection-plugin.js +23 -0
- package/dist/src/headless-agent.js +6 -4
- package/dist/src/index.js +386 -777
- package/package.json +3 -1
- package/scripts/deepseek/replicas.cordis.patch.yml +4 -0
- package/scripts/opencode-command-protection-plugin.ts +69 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
REPLICAS_RUNTIME_ENV_ALIASES,
|
|
4
|
+
agentCredentialSnapshotSchema,
|
|
5
|
+
extractCommandProtectionCommandText,
|
|
6
|
+
findGitCommitSignals,
|
|
7
|
+
findPrMergeSignals,
|
|
8
|
+
isRecord,
|
|
9
|
+
isValidAgentProvider,
|
|
10
|
+
parsePosixEnvFile,
|
|
11
|
+
readReplicasRuntimeEnv
|
|
12
|
+
} from "./chunk-LPJ6WYD4.js";
|
|
13
|
+
|
|
14
|
+
// src/utils/type-guards.ts
|
|
15
|
+
function isRecord2(value) {
|
|
16
|
+
return typeof value === "object" && value !== null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/engine-env.ts
|
|
20
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
21
|
+
import { homedir as homedir2 } from "os";
|
|
22
|
+
import { join as join2 } from "path";
|
|
23
|
+
|
|
24
|
+
// src/runtime-env-loader.ts
|
|
25
|
+
import { readFileSync } from "fs";
|
|
26
|
+
import { homedir } from "os";
|
|
27
|
+
import { join } from "path";
|
|
28
|
+
function loadRuntimeEnvFile() {
|
|
29
|
+
let content;
|
|
30
|
+
try {
|
|
31
|
+
content = readFileSync(join(homedir(), ".replicas", "runtime-env.sh"), "utf-8");
|
|
32
|
+
} catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
for (const [key, value] of Object.entries(parsePosixEnvFile(content))) {
|
|
36
|
+
process.env[key] = value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/engine-env.ts
|
|
41
|
+
var SANDBOX_IMAGE_VERSION_FILE = "/usr/local/lib/replicas-sandbox-image-version";
|
|
42
|
+
function readEnv(name) {
|
|
43
|
+
const value = process.env[name]?.trim();
|
|
44
|
+
return value ? value : void 0;
|
|
45
|
+
}
|
|
46
|
+
function readSandboxImageVersion() {
|
|
47
|
+
const environmentVersion = readEnv("REPLICAS_SANDBOX_IMAGE_VERSION");
|
|
48
|
+
if (environmentVersion) return environmentVersion;
|
|
49
|
+
try {
|
|
50
|
+
return readFileSync2(SANDBOX_IMAGE_VERSION_FILE, "utf8").trim() || "development";
|
|
51
|
+
} catch {
|
|
52
|
+
return "development";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function parsePort(value) {
|
|
56
|
+
if (!value) {
|
|
57
|
+
return 3737;
|
|
58
|
+
}
|
|
59
|
+
const parsed = Number(value);
|
|
60
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
61
|
+
throw new Error("Invalid engine environment: REPLICAS_ENGINE_PORT must be a positive integer");
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
function requireDefined(value, name) {
|
|
66
|
+
if (value === void 0 || value === null) {
|
|
67
|
+
throw new Error(`Invalid engine environment: ${name} is required`);
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function requireValidURL(value, name) {
|
|
72
|
+
try {
|
|
73
|
+
new URL(value);
|
|
74
|
+
return value;
|
|
75
|
+
} catch {
|
|
76
|
+
throw new Error(`Invalid engine environment: ${name} must be a valid URL`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function parseClaudeAuthMethod(value) {
|
|
80
|
+
if (value === "oauth" || value === "api_key" || value === "bedrock" || value === "foundry") {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
function parseCodexAuthMethod(value) {
|
|
86
|
+
if (value === "oauth" || value === "api_key" || value === "foundry") {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
function parseAgentCredentialSnapshots(value) {
|
|
92
|
+
if (!value) return {};
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(value);
|
|
95
|
+
if (!isRecord2(parsed)) return {};
|
|
96
|
+
const snapshots = {};
|
|
97
|
+
for (const [provider, snapshot] of Object.entries(parsed)) {
|
|
98
|
+
if (!isValidAgentProvider(provider)) continue;
|
|
99
|
+
const parsedSnapshot = agentCredentialSnapshotSchema.safeParse(snapshot);
|
|
100
|
+
if (parsedSnapshot.success) snapshots[provider] = parsedSnapshot.data;
|
|
101
|
+
}
|
|
102
|
+
return snapshots;
|
|
103
|
+
} catch {
|
|
104
|
+
return {};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var IS_WARMING_MODE = process.argv.includes("--warming");
|
|
108
|
+
function loadEngineEnv() {
|
|
109
|
+
loadRuntimeEnvFile();
|
|
110
|
+
const HOME_DIR = homedir2();
|
|
111
|
+
const env = {
|
|
112
|
+
// Defined: always available
|
|
113
|
+
REPLICAS_ENGINE_SECRET: requireDefined(readEnv("REPLICAS_ENGINE_SECRET"), "REPLICAS_ENGINE_SECRET"),
|
|
114
|
+
REPLICAS_ENGINE_PORT: parsePort(readEnv("REPLICAS_ENGINE_PORT")),
|
|
115
|
+
REPLICAS_MONOLITH_URL: requireValidURL(
|
|
116
|
+
requireDefined(readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.monolithUrl), "REPLICAS_MONOLITH_URL"),
|
|
117
|
+
"REPLICAS_MONOLITH_URL"
|
|
118
|
+
),
|
|
119
|
+
HOME_DIR,
|
|
120
|
+
WORKSPACE_ROOT: join2(HOME_DIR, "workspaces"),
|
|
121
|
+
REPLICAS_SANDBOX_IMAGE_VERSION: readSandboxImageVersion(),
|
|
122
|
+
// Runtime: may not be set during warming
|
|
123
|
+
REPLICAS_WORKSPACE_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.workspaceId),
|
|
124
|
+
REPLICAS_LINEAR_SESSION_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearSessionId),
|
|
125
|
+
REPLICAS_LINEAR_ACCESS_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearAccessToken),
|
|
126
|
+
REPLICAS_SLACK_BOT_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackBotToken),
|
|
127
|
+
REPLICAS_SLACK_CHANNEL_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackChannelId),
|
|
128
|
+
REPLICAS_SLACK_THREAD_TS: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackThreadTs),
|
|
129
|
+
ANTHROPIC_API_KEY: readEnv("ANTHROPIC_API_KEY"),
|
|
130
|
+
OPENAI_API_KEY: readEnv("OPENAI_API_KEY"),
|
|
131
|
+
CURSOR_API_KEY: readEnv("CURSOR_API_KEY"),
|
|
132
|
+
AI_GATEWAY_API_KEY: readEnv("AI_GATEWAY_API_KEY"),
|
|
133
|
+
CLAUDE_CODE_USE_BEDROCK: readEnv("CLAUDE_CODE_USE_BEDROCK"),
|
|
134
|
+
AWS_ACCESS_KEY_ID: readEnv("AWS_ACCESS_KEY_ID"),
|
|
135
|
+
AWS_SECRET_ACCESS_KEY: readEnv("AWS_SECRET_ACCESS_KEY"),
|
|
136
|
+
AWS_REGION: readEnv("AWS_REGION"),
|
|
137
|
+
ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION: readEnv("ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION"),
|
|
138
|
+
REPLICAS_CLAUDE_AUTH_METHOD: parseClaudeAuthMethod(readEnv("REPLICAS_CLAUDE_AUTH_METHOD")),
|
|
139
|
+
REPLICAS_CODEX_AUTH_METHOD: parseCodexAuthMethod(readEnv("REPLICAS_CODEX_AUTH_METHOD")),
|
|
140
|
+
REPLICAS_AGENT_CREDENTIALS: parseAgentCredentialSnapshots(readEnv("REPLICAS_AGENT_CREDENTIALS")),
|
|
141
|
+
REPLICAS_ENV_SYSTEM_PROMPT: readEnv("REPLICAS_ENV_SYSTEM_PROMPT"),
|
|
142
|
+
REPLICAS_ENV_START_HOOK: readEnv("REPLICAS_ENV_START_HOOK"),
|
|
143
|
+
REPLICAS_DISABLE_AUTO_START_HOOKS: readEnv("REPLICAS_DISABLE_AUTO_START_HOOKS")?.toLowerCase() === "true",
|
|
144
|
+
REPLICAS_ENGINE_DEFER_INITIALIZATION: readEnv("REPLICAS_ENGINE_DEFER_INITIALIZATION")?.toLowerCase() === "true"
|
|
145
|
+
};
|
|
146
|
+
if (!IS_WARMING_MODE && !env.REPLICAS_WORKSPACE_ID) {
|
|
147
|
+
console.error("REPLICAS_WORKSPACE_ID is not set \u2014 this is required in normal (non-warming) mode");
|
|
148
|
+
}
|
|
149
|
+
return env;
|
|
150
|
+
}
|
|
151
|
+
var ENGINE_ENV = loadEngineEnv();
|
|
152
|
+
function setAgentCredentialSnapshot(provider, snapshot) {
|
|
153
|
+
ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS = {
|
|
154
|
+
...ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS,
|
|
155
|
+
[provider]: snapshot
|
|
156
|
+
};
|
|
157
|
+
process.env.REPLICAS_AGENT_CREDENTIALS = JSON.stringify(ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/services/monolith-service.ts
|
|
161
|
+
async function monolithRequest(path, init = {}) {
|
|
162
|
+
if (!ENGINE_ENV.REPLICAS_WORKSPACE_ID) {
|
|
163
|
+
throw new Error("REPLICAS_WORKSPACE_ID is not set; cannot call monolith");
|
|
164
|
+
}
|
|
165
|
+
const headers = {
|
|
166
|
+
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
167
|
+
"X-Workspace-Id": ENGINE_ENV.REPLICAS_WORKSPACE_ID
|
|
168
|
+
};
|
|
169
|
+
if (!(init.body instanceof FormData)) headers["Content-Type"] = "application/json";
|
|
170
|
+
return fetch(`${ENGINE_ENV.REPLICAS_MONOLITH_URL}${path}`, {
|
|
171
|
+
method: init.method ?? "POST",
|
|
172
|
+
headers,
|
|
173
|
+
body: init.body === void 0 ? void 0 : init.body instanceof FormData ? init.body : JSON.stringify(init.body),
|
|
174
|
+
signal: init.signal
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
var MonolithService = class {
|
|
178
|
+
async getRelaySubagentProviders() {
|
|
179
|
+
try {
|
|
180
|
+
const response = await monolithRequest("/v1/engine/relay-subagent-providers", { method: "GET" });
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const body = await response.json();
|
|
185
|
+
return isRecord(body) && Array.isArray(body.providers) && body.providers.every((provider) => typeof provider === "string" && isValidAgentProvider(provider)) ? body.providers : null;
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async getOpenRouterModels() {
|
|
191
|
+
try {
|
|
192
|
+
const response = await monolithRequest("/v1/engine/openrouter-models", { method: "GET" });
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const body = await response.json();
|
|
197
|
+
return isRecord(body) && Array.isArray(body.models) && body.models.every((model) => typeof model === "string") ? body.models : null;
|
|
198
|
+
} catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async sendEvent(event) {
|
|
203
|
+
if (!ENGINE_ENV.REPLICAS_WORKSPACE_ID) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const response = await monolithRequest("/v1/engine/webhook", { body: event });
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
const errorText = await response.text();
|
|
210
|
+
console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error("[MonolithService] Failed to send event:", error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
var monolithService = new MonolithService();
|
|
218
|
+
|
|
219
|
+
// src/services/command-protection-service.ts
|
|
220
|
+
var DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE = "Blocked by Replicas command protection.";
|
|
221
|
+
function asCommandProtectionResponse(value) {
|
|
222
|
+
if (typeof value !== "object" || value === null) return null;
|
|
223
|
+
const candidate = value;
|
|
224
|
+
return typeof candidate.allowed === "boolean" && typeof candidate.enabled === "boolean" ? candidate : null;
|
|
225
|
+
}
|
|
226
|
+
function failClosed(reason, matchedSignals) {
|
|
227
|
+
return {
|
|
228
|
+
allowed: false,
|
|
229
|
+
enabled: true,
|
|
230
|
+
reason,
|
|
231
|
+
matchedSignals
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function evaluateCommandProtection(request, signal) {
|
|
235
|
+
const matchedSignals = [...findGitCommitSignals(request), ...findPrMergeSignals(request)];
|
|
236
|
+
if (matchedSignals.length === 0) {
|
|
237
|
+
return { allowed: true, enabled: true };
|
|
238
|
+
}
|
|
239
|
+
let response;
|
|
240
|
+
try {
|
|
241
|
+
response = await monolithRequest("/v1/engine/command-protection/evaluate", {
|
|
242
|
+
body: request,
|
|
243
|
+
signal
|
|
244
|
+
});
|
|
245
|
+
} catch (error) {
|
|
246
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
247
|
+
return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
|
|
248
|
+
}
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
const detail = await response.text().catch(() => "");
|
|
251
|
+
return failClosed(`Command protection failed closed: ${response.status} ${detail}`, matchedSignals);
|
|
252
|
+
}
|
|
253
|
+
let payload;
|
|
254
|
+
try {
|
|
255
|
+
payload = await response.json();
|
|
256
|
+
} catch (error) {
|
|
257
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
258
|
+
return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
|
|
259
|
+
}
|
|
260
|
+
return asCommandProtectionResponse(payload) ?? failClosed("Command protection failed closed: invalid response from policy service", matchedSignals);
|
|
261
|
+
}
|
|
262
|
+
function extractToolCommand(input) {
|
|
263
|
+
return extractCommandProtectionCommandText({ toolInput: input }, { stringifyFallback: false });
|
|
264
|
+
}
|
|
265
|
+
function reportCommandProtectionBlock(options) {
|
|
266
|
+
const message = options.result.reason || DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE;
|
|
267
|
+
console.warn(`[${options.managerName}] blocked ${options.action} by command protection: ${message}`);
|
|
268
|
+
options.recordHistoryEvent(options.eventType, {
|
|
269
|
+
type: "error",
|
|
270
|
+
message,
|
|
271
|
+
...options.context,
|
|
272
|
+
command: options.command
|
|
273
|
+
}, options.historyFile);
|
|
274
|
+
return message;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export {
|
|
278
|
+
isRecord2 as isRecord,
|
|
279
|
+
IS_WARMING_MODE,
|
|
280
|
+
ENGINE_ENV,
|
|
281
|
+
setAgentCredentialSnapshot,
|
|
282
|
+
monolithRequest,
|
|
283
|
+
monolithService,
|
|
284
|
+
evaluateCommandProtection,
|
|
285
|
+
extractToolCommand,
|
|
286
|
+
reportCommandProtectionBlock
|
|
287
|
+
};
|