replicas-engine 0.1.362 → 0.1.364
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/README.md +1 -0
- package/dist/src/index.js +298 -39
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -121,9 +121,9 @@ var EXT_TO_LANGUAGE = {
|
|
|
121
121
|
function detectLanguageByPath(filePath) {
|
|
122
122
|
const dot = filePath.lastIndexOf(".");
|
|
123
123
|
if (dot === -1) {
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
126
|
-
if (
|
|
124
|
+
const basename3 = filePath.split("/").pop() ?? "";
|
|
125
|
+
if (basename3 === "Dockerfile") return "dockerfile";
|
|
126
|
+
if (basename3 === "Makefile") return "makefile";
|
|
127
127
|
return null;
|
|
128
128
|
}
|
|
129
129
|
const ext = filePath.slice(dot).toLowerCase();
|
|
@@ -295,7 +295,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
295
295
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
296
296
|
|
|
297
297
|
// ../shared/src/e2b.ts
|
|
298
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-
|
|
298
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-28-v2";
|
|
299
299
|
|
|
300
300
|
// ../shared/src/runtime-env.ts
|
|
301
301
|
function parsePosixEnvFile(content) {
|
|
@@ -427,6 +427,56 @@ function normalizeRepositoryUrl(url, options = {}) {
|
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
// ../shared/src/slash-commands.ts
|
|
430
|
+
function normalizeSlashCommandName(name) {
|
|
431
|
+
const command = name.trim().replace(/^\/+/, "");
|
|
432
|
+
if (!command || /\s/.test(command)) return null;
|
|
433
|
+
return `/${command}`;
|
|
434
|
+
}
|
|
435
|
+
function createProviderSlashCommand(provider, name, description, argumentHint) {
|
|
436
|
+
const command = normalizeSlashCommandName(name);
|
|
437
|
+
if (!command) return null;
|
|
438
|
+
const trimmedDescription = description?.trim();
|
|
439
|
+
const trimmedArgumentHint = argumentHint?.trim();
|
|
440
|
+
return {
|
|
441
|
+
command,
|
|
442
|
+
description: trimmedDescription || `Run ${command}.`,
|
|
443
|
+
...trimmedArgumentHint ? { argumentHint: trimmedArgumentHint } : {},
|
|
444
|
+
providers: [provider]
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
var SLASH_COMMANDS = [
|
|
448
|
+
{
|
|
449
|
+
command: "/plan",
|
|
450
|
+
description: "Switch to plan mode and optionally send a prompt.",
|
|
451
|
+
argumentHint: "[prompt]",
|
|
452
|
+
providers: ["claude", "codex", "cursor", "opencode", "relay"]
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
command: "/fast",
|
|
456
|
+
description: "Use the fast service tier for future turns.",
|
|
457
|
+
providers: ["claude", "codex"]
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
command: "/goal",
|
|
461
|
+
description: "Set or clear a task goal.",
|
|
462
|
+
argumentHint: "<objective | clear>",
|
|
463
|
+
providers: ["codex"]
|
|
464
|
+
}
|
|
465
|
+
];
|
|
466
|
+
function mergeSlashCommands(...commandGroups) {
|
|
467
|
+
const seen = /* @__PURE__ */ new Set();
|
|
468
|
+
const commands = [];
|
|
469
|
+
for (const command of commandGroups.flat()) {
|
|
470
|
+
const key = command.command.toLowerCase();
|
|
471
|
+
if (seen.has(key)) continue;
|
|
472
|
+
seen.add(key);
|
|
473
|
+
commands.push(command);
|
|
474
|
+
}
|
|
475
|
+
return commands;
|
|
476
|
+
}
|
|
477
|
+
function getSlashCommandsForProvider(provider, commands = SLASH_COMMANDS) {
|
|
478
|
+
return commands.filter((command) => command.providers.includes(provider));
|
|
479
|
+
}
|
|
430
480
|
var MAX_CODEX_GOAL_OBJECTIVE_CHARS = 4e3;
|
|
431
481
|
function parseGoalCommand(message) {
|
|
432
482
|
const match = message.trim().match(/^\/goal(?:\s+([\s\S]*))?$/i);
|
|
@@ -5257,7 +5307,7 @@ async function registerDesktopPreview() {
|
|
|
5257
5307
|
|
|
5258
5308
|
// src/services/chat/chat-service.ts
|
|
5259
5309
|
import { existsSync as existsSync7 } from "fs";
|
|
5260
|
-
import { appendFile as appendFile3, copyFile, mkdir as mkdir13, readFile as
|
|
5310
|
+
import { appendFile as appendFile3, copyFile, mkdir as mkdir13, readFile as readFile14, rename as rename2, rm } from "fs/promises";
|
|
5261
5311
|
import { homedir as homedir14 } from "os";
|
|
5262
5312
|
import { join as join19 } from "path";
|
|
5263
5313
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -6579,6 +6629,16 @@ var MAX_MIDTURN_CONTINUE_RETRIES = 2;
|
|
|
6579
6629
|
var TRANSIENT_RETRY_DELAYS_MS = [1e3, 2500];
|
|
6580
6630
|
var CLAUDE_TRANSIENT_HTTP_STATUSES = [408, 500, 502, 503, 504, 529];
|
|
6581
6631
|
var CLAUDE_MIDTURN_CONTINUE_PROMPT = "Your previous turn was interrupted by a transient network error before it could finish. Continue from exactly where you left off. Do not repeat any tool calls, commits, messages, or other actions you have already completed \u2014 first check what is already done, then do only the remaining work.";
|
|
6632
|
+
function toSlashCommands(command) {
|
|
6633
|
+
const names = [command.name, ...command.aliases ?? []];
|
|
6634
|
+
return names.flatMap((name) => {
|
|
6635
|
+
const result = createProviderSlashCommand("claude", name, command.description, command.argumentHint);
|
|
6636
|
+
return result ? [result] : [];
|
|
6637
|
+
});
|
|
6638
|
+
}
|
|
6639
|
+
function normalizeClaudeSlashCommands(commands) {
|
|
6640
|
+
return mergeSlashCommands(...commands.map(toSlashCommands));
|
|
6641
|
+
}
|
|
6582
6642
|
var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
6583
6643
|
historyFilePath;
|
|
6584
6644
|
historyFile;
|
|
@@ -6602,6 +6662,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
6602
6662
|
disallowedToolsOverride;
|
|
6603
6663
|
/** Active tool-input requests keyed by requestId; resolved when the user selects an option. */
|
|
6604
6664
|
pendingToolInputs = /* @__PURE__ */ new Map();
|
|
6665
|
+
supportedSlashCommands = [];
|
|
6605
6666
|
authRetrying = false;
|
|
6606
6667
|
constructor(options) {
|
|
6607
6668
|
super(options);
|
|
@@ -6640,6 +6701,21 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
6640
6701
|
isAuthRetrying() {
|
|
6641
6702
|
return this.authRetrying;
|
|
6642
6703
|
}
|
|
6704
|
+
async listSlashCommands() {
|
|
6705
|
+
await this.initialized;
|
|
6706
|
+
if (!this.activeQuery || this.isProcessing()) {
|
|
6707
|
+
return this.supportedSlashCommands;
|
|
6708
|
+
}
|
|
6709
|
+
try {
|
|
6710
|
+
const commands = await this.activeQuery?.supportedCommands();
|
|
6711
|
+
if (commands) {
|
|
6712
|
+
this.supportedSlashCommands = normalizeClaudeSlashCommands(commands);
|
|
6713
|
+
}
|
|
6714
|
+
} catch (error) {
|
|
6715
|
+
console.warn("[ClaudeManager] Failed to load slash commands:", error);
|
|
6716
|
+
}
|
|
6717
|
+
return this.supportedSlashCommands;
|
|
6718
|
+
}
|
|
6643
6719
|
setAuthRetrying(value) {
|
|
6644
6720
|
if (this.authRetrying === value) return;
|
|
6645
6721
|
this.authRetrying = value;
|
|
@@ -7390,6 +7466,9 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
7390
7466
|
this.handlePartialAssistantMessage(message);
|
|
7391
7467
|
return;
|
|
7392
7468
|
}
|
|
7469
|
+
if (message.type === "system" && message.subtype === "commands_changed") {
|
|
7470
|
+
this.supportedSlashCommands = normalizeClaudeSlashCommands(message.commands);
|
|
7471
|
+
}
|
|
7393
7472
|
this.trackNativeCompaction(message);
|
|
7394
7473
|
await this.recordEvent(message);
|
|
7395
7474
|
}
|
|
@@ -7612,7 +7691,7 @@ var AspClient = class {
|
|
|
7612
7691
|
// src/managers/codex-asp/app-server-process.ts
|
|
7613
7692
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
7614
7693
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
7615
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
7694
|
+
var ENGINE_PACKAGE_VERSION = "0.1.364";
|
|
7616
7695
|
var INITIALIZE_METHOD = "initialize";
|
|
7617
7696
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
7618
7697
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -7867,6 +7946,7 @@ var TURN_START_METHOD = "turn/start";
|
|
|
7867
7946
|
var TURN_INTERRUPT_METHOD = "turn/interrupt";
|
|
7868
7947
|
var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
|
|
7869
7948
|
var MODEL_LIST_METHOD = "model/list";
|
|
7949
|
+
var SKILLS_LIST_METHOD = "skills/list";
|
|
7870
7950
|
var MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS;
|
|
7871
7951
|
function codexApprovalPolicyOverrides() {
|
|
7872
7952
|
if (!ENGINE_ENV.REPLICAS_DISABLE_GH_PR_MERGE) {
|
|
@@ -8420,6 +8500,20 @@ var TranscriptUpdateCoalescer = class {
|
|
|
8420
8500
|
};
|
|
8421
8501
|
|
|
8422
8502
|
// src/managers/codex-asp/codex-asp-manager.ts
|
|
8503
|
+
var CODEX_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
8504
|
+
function skillToSlashCommand(skill) {
|
|
8505
|
+
if (!skill.enabled) return null;
|
|
8506
|
+
const description = skill.interface?.shortDescription ?? skill.shortDescription ?? skill.description;
|
|
8507
|
+
return createProviderSlashCommand("codex", skill.name, description);
|
|
8508
|
+
}
|
|
8509
|
+
function skillsListToSlashCommands(response) {
|
|
8510
|
+
return mergeSlashCommands(
|
|
8511
|
+
...response.data.map((entry) => entry.skills.flatMap((skill) => {
|
|
8512
|
+
const command = skillToSlashCommand(skill);
|
|
8513
|
+
return command ? [command] : [];
|
|
8514
|
+
}))
|
|
8515
|
+
);
|
|
8516
|
+
}
|
|
8423
8517
|
var CodexAspManager = class extends CodingAgentManager {
|
|
8424
8518
|
currentThreadId = null;
|
|
8425
8519
|
activeTurnId = null;
|
|
@@ -8437,6 +8531,8 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8437
8531
|
skillRegistriesApplied = false;
|
|
8438
8532
|
modelServiceTierCache = null;
|
|
8439
8533
|
activeServiceTier;
|
|
8534
|
+
slashCommandsCache = null;
|
|
8535
|
+
slashCommandsRequest = null;
|
|
8440
8536
|
constructor(options) {
|
|
8441
8537
|
super(options);
|
|
8442
8538
|
this.historyFile = options.historyFilePath ? new CodexHistoryFile(options.historyFilePath) : null;
|
|
@@ -8515,6 +8611,34 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
8515
8611
|
getGoal() {
|
|
8516
8612
|
return this.currentGoal;
|
|
8517
8613
|
}
|
|
8614
|
+
async listSlashCommands() {
|
|
8615
|
+
await this.initialized;
|
|
8616
|
+
const now = Date.now();
|
|
8617
|
+
if (this.slashCommandsCache && this.slashCommandsCache.expiresAt > now) {
|
|
8618
|
+
return this.slashCommandsCache.commands;
|
|
8619
|
+
}
|
|
8620
|
+
if (!this.slashCommandsRequest) {
|
|
8621
|
+
this.slashCommandsRequest = (async () => {
|
|
8622
|
+
try {
|
|
8623
|
+
const host = await getCodexAspHost();
|
|
8624
|
+
await this.applySkillRegistries(host);
|
|
8625
|
+
const response = await host.client.request(
|
|
8626
|
+
SKILLS_LIST_METHOD,
|
|
8627
|
+
{ cwds: [this.workingDirectory] }
|
|
8628
|
+
);
|
|
8629
|
+
const commands = skillsListToSlashCommands(response);
|
|
8630
|
+
this.slashCommandsCache = { commands, expiresAt: Date.now() + CODEX_SLASH_COMMANDS_CACHE_MS };
|
|
8631
|
+
return commands;
|
|
8632
|
+
} catch (error) {
|
|
8633
|
+
console.warn("[CodexAspManager] Failed to load slash commands:", error);
|
|
8634
|
+
return this.slashCommandsCache?.commands ?? [];
|
|
8635
|
+
} finally {
|
|
8636
|
+
this.slashCommandsRequest = null;
|
|
8637
|
+
}
|
|
8638
|
+
})();
|
|
8639
|
+
}
|
|
8640
|
+
return this.slashCommandsRequest;
|
|
8641
|
+
}
|
|
8518
8642
|
async clearGoal() {
|
|
8519
8643
|
await this.initialized;
|
|
8520
8644
|
if (!this.currentThreadId) {
|
|
@@ -9381,9 +9505,11 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
9381
9505
|
};
|
|
9382
9506
|
|
|
9383
9507
|
// src/managers/cursor-manager.ts
|
|
9384
|
-
import { mkdir as mkdir11 } from "fs/promises";
|
|
9385
|
-
import { dirname as dirname5, join as join15 } from "path";
|
|
9508
|
+
import { mkdir as mkdir11, readFile as readFile10, readdir as readdir4 } from "fs/promises";
|
|
9509
|
+
import { basename, dirname as dirname5, extname, join as join15 } from "path";
|
|
9510
|
+
import { parse as parseYaml2 } from "yaml";
|
|
9386
9511
|
import { Agent as CursorAgent } from "@cursor/sdk";
|
|
9512
|
+
var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
9387
9513
|
var CURSOR_COMPOSER_CONTEXT_WINDOW = 2e5;
|
|
9388
9514
|
var CURSOR_CATEGORY_COLORS = {
|
|
9389
9515
|
input: "#3eeba3",
|
|
@@ -9394,12 +9520,45 @@ var CURSOR_CATEGORY_COLORS = {
|
|
|
9394
9520
|
function finiteNumber(value) {
|
|
9395
9521
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
9396
9522
|
}
|
|
9523
|
+
function extractCursorCommandDescription(content) {
|
|
9524
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
9525
|
+
if (!match) return void 0;
|
|
9526
|
+
try {
|
|
9527
|
+
const parsed = parseYaml2(match[1]);
|
|
9528
|
+
if (isRecord4(parsed) && typeof parsed.description === "string") return parsed.description;
|
|
9529
|
+
} catch {
|
|
9530
|
+
}
|
|
9531
|
+
return void 0;
|
|
9532
|
+
}
|
|
9533
|
+
async function listCursorCommandsInDirectory(directory) {
|
|
9534
|
+
let entries;
|
|
9535
|
+
try {
|
|
9536
|
+
entries = await readdir4(directory, { withFileTypes: true });
|
|
9537
|
+
} catch (error) {
|
|
9538
|
+
if (isRecord4(error) && error.code === "ENOENT") return [];
|
|
9539
|
+
console.warn("[CursorManager] Failed to read slash command directory:", error);
|
|
9540
|
+
return [];
|
|
9541
|
+
}
|
|
9542
|
+
const commands = await Promise.all(entries.filter((entry) => entry.isFile() && extname(entry.name) === ".md").map(async (entry) => {
|
|
9543
|
+
const name = basename(entry.name, ".md");
|
|
9544
|
+
let description;
|
|
9545
|
+
try {
|
|
9546
|
+
description = extractCursorCommandDescription(await readFile10(join15(directory, entry.name), "utf8"));
|
|
9547
|
+
} catch (error) {
|
|
9548
|
+
console.warn("[CursorManager] Failed to read slash command file:", error);
|
|
9549
|
+
}
|
|
9550
|
+
return createProviderSlashCommand("cursor", name, description);
|
|
9551
|
+
}));
|
|
9552
|
+
return commands.filter((command) => Boolean(command));
|
|
9553
|
+
}
|
|
9397
9554
|
var CursorManager = class extends CodingAgentManager {
|
|
9398
9555
|
agent = null;
|
|
9399
9556
|
activeRun = null;
|
|
9400
9557
|
activeModel = null;
|
|
9401
9558
|
historyFilePath;
|
|
9402
9559
|
historyFile;
|
|
9560
|
+
slashCommandsCache = null;
|
|
9561
|
+
slashCommandsRequest = null;
|
|
9403
9562
|
constructor(options) {
|
|
9404
9563
|
super(options);
|
|
9405
9564
|
this.historyFilePath = options.historyFilePath ?? join15(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
|
|
@@ -9421,6 +9580,26 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
9421
9580
|
goal: null
|
|
9422
9581
|
};
|
|
9423
9582
|
}
|
|
9583
|
+
async listSlashCommands() {
|
|
9584
|
+
await this.initialized;
|
|
9585
|
+
const now = Date.now();
|
|
9586
|
+
if (this.slashCommandsCache && this.slashCommandsCache.expiresAt > now) {
|
|
9587
|
+
return this.slashCommandsCache.commands;
|
|
9588
|
+
}
|
|
9589
|
+
this.slashCommandsRequest ??= (async () => {
|
|
9590
|
+
try {
|
|
9591
|
+
const commands = mergeSlashCommands(
|
|
9592
|
+
await listCursorCommandsInDirectory(join15(this.workingDirectory, ".cursor", "commands")),
|
|
9593
|
+
await listCursorCommandsInDirectory(join15(ENGINE_ENV.HOME_DIR, ".cursor", "commands"))
|
|
9594
|
+
);
|
|
9595
|
+
this.slashCommandsCache = { commands, expiresAt: Date.now() + CURSOR_SLASH_COMMANDS_CACHE_MS };
|
|
9596
|
+
return commands;
|
|
9597
|
+
} finally {
|
|
9598
|
+
this.slashCommandsRequest = null;
|
|
9599
|
+
}
|
|
9600
|
+
})();
|
|
9601
|
+
return this.slashCommandsRequest;
|
|
9602
|
+
}
|
|
9424
9603
|
async ensureAgent(request) {
|
|
9425
9604
|
if (this.agent) return this.agent;
|
|
9426
9605
|
const apiKey = ENGINE_ENV.CURSOR_API_KEY;
|
|
@@ -9560,7 +9739,7 @@ var CursorManager = class extends CodingAgentManager {
|
|
|
9560
9739
|
};
|
|
9561
9740
|
|
|
9562
9741
|
// src/managers/opencode-manager.ts
|
|
9563
|
-
import { mkdir as mkdir12, readFile as
|
|
9742
|
+
import { mkdir as mkdir12, readFile as readFile11 } from "fs/promises";
|
|
9564
9743
|
import { delimiter, dirname as dirname6, join as join16 } from "path";
|
|
9565
9744
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
9566
9745
|
import { fileURLToPath } from "url";
|
|
@@ -9573,6 +9752,7 @@ var OPENCODE_SHIM_DIR = dirname6(fileURLToPath(new URL("../../scripts/opencode",
|
|
|
9573
9752
|
var OPENCODE_CONFIG_PATH = join16(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
|
|
9574
9753
|
var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
9575
9754
|
var OPENCODE_WORKSPACE_PERMISSION = "allow";
|
|
9755
|
+
var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
9576
9756
|
var OPENCODE_VARIANT_CANDIDATES_BY_THINKING_LEVEL = {
|
|
9577
9757
|
low: ["low"],
|
|
9578
9758
|
medium: ["medium"],
|
|
@@ -9604,6 +9784,25 @@ async function opencodeConfig(model) {
|
|
|
9604
9784
|
function getConfiguredOpencodeModels(model) {
|
|
9605
9785
|
return [.../* @__PURE__ */ new Set([model, ...AGENT_MODELS.opencode])];
|
|
9606
9786
|
}
|
|
9787
|
+
function opencodeCommandToSlashCommand(command) {
|
|
9788
|
+
return createProviderSlashCommand("opencode", command.name, command.description);
|
|
9789
|
+
}
|
|
9790
|
+
function opencodeSkillToSlashCommand(skill) {
|
|
9791
|
+
if (!skill.slash) return null;
|
|
9792
|
+
return createProviderSlashCommand("opencode", skill.name, skill.description);
|
|
9793
|
+
}
|
|
9794
|
+
function opencodeCommandListToSlashCommands(response) {
|
|
9795
|
+
return mergeSlashCommands(response.data.flatMap((command) => {
|
|
9796
|
+
const result = opencodeCommandToSlashCommand(command);
|
|
9797
|
+
return result ? [result] : [];
|
|
9798
|
+
}));
|
|
9799
|
+
}
|
|
9800
|
+
function opencodeSkillListToSlashCommands(response) {
|
|
9801
|
+
return mergeSlashCommands(response.data.flatMap((skill) => {
|
|
9802
|
+
const result = opencodeSkillToSlashCommand(skill);
|
|
9803
|
+
return result ? [result] : [];
|
|
9804
|
+
}));
|
|
9805
|
+
}
|
|
9607
9806
|
function isOpencodePart(value) {
|
|
9608
9807
|
return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
9609
9808
|
}
|
|
@@ -9627,7 +9826,7 @@ function isOpencodeMcpEntry(value) {
|
|
|
9627
9826
|
async function readProvisionedOpencodeMcpConfig() {
|
|
9628
9827
|
let raw;
|
|
9629
9828
|
try {
|
|
9630
|
-
raw = await
|
|
9829
|
+
raw = await readFile11(OPENCODE_CONFIG_PATH, "utf8");
|
|
9631
9830
|
} catch (error) {
|
|
9632
9831
|
if (isRecord4(error) && error.code === "ENOENT") return void 0;
|
|
9633
9832
|
console.error("[OpencodeManager] Failed to read Opencode config:", error);
|
|
@@ -9699,6 +9898,8 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9699
9898
|
reasoningParts = /* @__PURE__ */ new Map();
|
|
9700
9899
|
nonAssistantMessageIds = /* @__PURE__ */ new Set();
|
|
9701
9900
|
modelVariants = /* @__PURE__ */ new Map();
|
|
9901
|
+
slashCommandsCache = null;
|
|
9902
|
+
slashCommandsRequest = null;
|
|
9702
9903
|
constructor(options) {
|
|
9703
9904
|
super(options);
|
|
9704
9905
|
this.sessionId = options.initialSessionId;
|
|
@@ -9731,6 +9932,35 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9731
9932
|
goal: null
|
|
9732
9933
|
};
|
|
9733
9934
|
}
|
|
9935
|
+
async listSlashCommands() {
|
|
9936
|
+
await this.initialized;
|
|
9937
|
+
const now = Date.now();
|
|
9938
|
+
if (this.slashCommandsCache && this.slashCommandsCache.expiresAt > now) {
|
|
9939
|
+
return this.slashCommandsCache.commands;
|
|
9940
|
+
}
|
|
9941
|
+
this.slashCommandsRequest ??= (async () => {
|
|
9942
|
+
try {
|
|
9943
|
+
const client = await this.ensureClient(DEFAULT_OPENCODE_MODEL);
|
|
9944
|
+
const location = { directory: this.workingDirectory };
|
|
9945
|
+
const [commandResponse, skillResponse] = await Promise.all([
|
|
9946
|
+
client.v2.command.list({ location }, { throwOnError: true }),
|
|
9947
|
+
client.v2.skill.list({ location }, { throwOnError: true })
|
|
9948
|
+
]);
|
|
9949
|
+
const commands = mergeSlashCommands(
|
|
9950
|
+
opencodeCommandListToSlashCommands(commandResponse.data),
|
|
9951
|
+
opencodeSkillListToSlashCommands(skillResponse.data)
|
|
9952
|
+
);
|
|
9953
|
+
this.slashCommandsCache = { commands, expiresAt: Date.now() + OPENCODE_SLASH_COMMANDS_CACHE_MS };
|
|
9954
|
+
return commands;
|
|
9955
|
+
} catch (error) {
|
|
9956
|
+
console.warn("[OpencodeManager] Failed to load slash commands:", error);
|
|
9957
|
+
return this.slashCommandsCache?.commands ?? [];
|
|
9958
|
+
} finally {
|
|
9959
|
+
this.slashCommandsRequest = null;
|
|
9960
|
+
}
|
|
9961
|
+
})();
|
|
9962
|
+
return this.slashCommandsRequest;
|
|
9963
|
+
}
|
|
9734
9964
|
async ensureClient(model) {
|
|
9735
9965
|
if (this.client && this.configuredModels.has(model)) return this.client;
|
|
9736
9966
|
if (!ENGINE_ENV.OPENROUTER_API_KEY) {
|
|
@@ -10545,6 +10775,9 @@ var RelayManager = class {
|
|
|
10545
10775
|
async getHistory() {
|
|
10546
10776
|
return this.inner.getHistory();
|
|
10547
10777
|
}
|
|
10778
|
+
async listSlashCommands() {
|
|
10779
|
+
return this.inner.listSlashCommands?.() ?? [];
|
|
10780
|
+
}
|
|
10548
10781
|
isProcessing() {
|
|
10549
10782
|
return this.inner.isProcessing();
|
|
10550
10783
|
}
|
|
@@ -10604,7 +10837,7 @@ var KeepAliveService = class _KeepAliveService {
|
|
|
10604
10837
|
var keepAliveService = new KeepAliveService();
|
|
10605
10838
|
|
|
10606
10839
|
// src/services/canvas-service.ts
|
|
10607
|
-
import { readdir as
|
|
10840
|
+
import { readdir as readdir5, readFile as readFile12, stat as stat3 } from "fs/promises";
|
|
10608
10841
|
import { homedir as homedir12 } from "os";
|
|
10609
10842
|
import { join as join17 } from "path";
|
|
10610
10843
|
var CANVAS_DIRECTORIES = [
|
|
@@ -10617,7 +10850,7 @@ var CanvasService = class {
|
|
|
10617
10850
|
for (const directory of CANVAS_DIRECTORIES) {
|
|
10618
10851
|
let entries;
|
|
10619
10852
|
try {
|
|
10620
|
-
entries = await
|
|
10853
|
+
entries = await readdir5(directory, { withFileTypes: true });
|
|
10621
10854
|
} catch {
|
|
10622
10855
|
continue;
|
|
10623
10856
|
}
|
|
@@ -10664,7 +10897,7 @@ var CanvasService = class {
|
|
|
10664
10897
|
};
|
|
10665
10898
|
}
|
|
10666
10899
|
try {
|
|
10667
|
-
const bytes = await
|
|
10900
|
+
const bytes = await readFile12(filePath);
|
|
10668
10901
|
return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
|
|
10669
10902
|
} catch {
|
|
10670
10903
|
continue;
|
|
@@ -10779,8 +11012,8 @@ async function reconcileCanvasItems(filenames) {
|
|
|
10779
11012
|
}
|
|
10780
11013
|
|
|
10781
11014
|
// src/services/upload-chat-transcripts.ts
|
|
10782
|
-
import { readdir as
|
|
10783
|
-
import { basename, join as join18 } from "path";
|
|
11015
|
+
import { readdir as readdir6, readFile as readFile13 } from "fs/promises";
|
|
11016
|
+
import { basename as basename2, join as join18 } from "path";
|
|
10784
11017
|
import { homedir as homedir13 } from "os";
|
|
10785
11018
|
var ENGINE_DIR2 = join18(homedir13(), ".replicas", "engine");
|
|
10786
11019
|
var HISTORY_DIRS = [
|
|
@@ -10795,13 +11028,13 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
|
|
|
10795
11028
|
for (const dir of HISTORY_DIRS) {
|
|
10796
11029
|
let entries;
|
|
10797
11030
|
try {
|
|
10798
|
-
entries = await
|
|
11031
|
+
entries = await readdir6(dir);
|
|
10799
11032
|
} catch {
|
|
10800
11033
|
continue;
|
|
10801
11034
|
}
|
|
10802
11035
|
for (const entry of entries) {
|
|
10803
11036
|
if (!entry.endsWith(".jsonl")) continue;
|
|
10804
|
-
const chatId =
|
|
11037
|
+
const chatId = basename2(entry, ".jsonl");
|
|
10805
11038
|
tasks.push(
|
|
10806
11039
|
uploadChatTranscript(chatId, join18(dir, entry), chatsById.get(chatId)).then(() => {
|
|
10807
11040
|
flushed++;
|
|
@@ -10816,7 +11049,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
|
|
|
10816
11049
|
return { flushed, failed };
|
|
10817
11050
|
}
|
|
10818
11051
|
async function uploadChatTranscript(chatId, filePath, chat) {
|
|
10819
|
-
const bytes = await
|
|
11052
|
+
const bytes = await readFile13(filePath);
|
|
10820
11053
|
if (bytes.byteLength === 0) return;
|
|
10821
11054
|
const form = new FormData();
|
|
10822
11055
|
form.append("chat_id", chatId);
|
|
@@ -11046,6 +11279,21 @@ var ChatService = class {
|
|
|
11046
11279
|
const chat = this.chats.get(chatId);
|
|
11047
11280
|
return chat ? this.toSummary(chat) : null;
|
|
11048
11281
|
}
|
|
11282
|
+
async listSlashCommands(chatId) {
|
|
11283
|
+
const chat = this.requireChat(chatId);
|
|
11284
|
+
const provider = chat.persisted.provider;
|
|
11285
|
+
const discovered = await chat.provider.listSlashCommands?.() ?? [];
|
|
11286
|
+
const providerCommands = discovered.map((command) => ({
|
|
11287
|
+
...command,
|
|
11288
|
+
providers: [provider]
|
|
11289
|
+
}));
|
|
11290
|
+
return {
|
|
11291
|
+
commands: mergeSlashCommands(
|
|
11292
|
+
getSlashCommandsForProvider(provider),
|
|
11293
|
+
providerCommands
|
|
11294
|
+
)
|
|
11295
|
+
};
|
|
11296
|
+
}
|
|
11049
11297
|
async createChat(request) {
|
|
11050
11298
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11051
11299
|
const title = request.title?.trim() || `${request.provider} chat`;
|
|
@@ -11121,7 +11369,7 @@ var ChatService = class {
|
|
|
11121
11369
|
}
|
|
11122
11370
|
async readSenders(chatId) {
|
|
11123
11371
|
try {
|
|
11124
|
-
const content = await
|
|
11372
|
+
const content = await readFile14(this.senderFilePath(chatId), "utf-8");
|
|
11125
11373
|
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
11126
11374
|
const senders = [];
|
|
11127
11375
|
for (const line of lines) {
|
|
@@ -11531,7 +11779,7 @@ var ChatService = class {
|
|
|
11531
11779
|
}
|
|
11532
11780
|
async loadChats() {
|
|
11533
11781
|
try {
|
|
11534
|
-
const content = await
|
|
11782
|
+
const content = await readFile14(CHATS_FILE, "utf-8");
|
|
11535
11783
|
return parsePersistedChatsContent(content);
|
|
11536
11784
|
} catch (error) {
|
|
11537
11785
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -11546,7 +11794,7 @@ var ChatService = class {
|
|
|
11546
11794
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
11547
11795
|
}
|
|
11548
11796
|
try {
|
|
11549
|
-
const backupContent = await
|
|
11797
|
+
const backupContent = await readFile14(CHATS_BACKUP_FILE, "utf-8");
|
|
11550
11798
|
return parsePersistedChatsContent(backupContent);
|
|
11551
11799
|
} catch (backupError) {
|
|
11552
11800
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -11633,8 +11881,8 @@ var ChatService = class {
|
|
|
11633
11881
|
|
|
11634
11882
|
// src/services/repo-file-service.ts
|
|
11635
11883
|
import { execFile as execFile2 } from "child_process";
|
|
11636
|
-
import { readFile as
|
|
11637
|
-
import { join as join20, resolve as resolve2, extname } from "path";
|
|
11884
|
+
import { readFile as readFile15, realpath, stat as stat4 } from "fs/promises";
|
|
11885
|
+
import { join as join20, resolve as resolve2, extname as extname2 } from "path";
|
|
11638
11886
|
var CACHE_TTL_MS = 3e4;
|
|
11639
11887
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
11640
11888
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -11642,7 +11890,7 @@ var DEFAULT_LIMIT = 50;
|
|
|
11642
11890
|
var MAX_LIMIT = 5e3;
|
|
11643
11891
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".next", "dist", "build", "out", "vendor", ".turbo", "__pycache__"]);
|
|
11644
11892
|
function isBinaryExtension(filePath) {
|
|
11645
|
-
const ext =
|
|
11893
|
+
const ext = extname2(filePath).toLowerCase();
|
|
11646
11894
|
const binaryExts = /* @__PURE__ */ new Set([
|
|
11647
11895
|
".png",
|
|
11648
11896
|
".jpg",
|
|
@@ -11694,11 +11942,11 @@ function scoreMatch(query2, filePath) {
|
|
|
11694
11942
|
const lowerQuery = query2.toLowerCase();
|
|
11695
11943
|
const lowerPath = filePath.toLowerCase();
|
|
11696
11944
|
const segments = lowerPath.split("/");
|
|
11697
|
-
const
|
|
11698
|
-
if (
|
|
11699
|
-
if (
|
|
11945
|
+
const basename3 = segments[segments.length - 1] ?? "";
|
|
11946
|
+
if (basename3 === lowerQuery) return 100;
|
|
11947
|
+
if (basename3.startsWith(lowerQuery)) return 90;
|
|
11700
11948
|
if (segments.some((seg) => seg === lowerQuery)) return 80;
|
|
11701
|
-
if (
|
|
11949
|
+
if (basename3.includes(lowerQuery)) return 70;
|
|
11702
11950
|
if (segments.some((seg) => seg.includes(lowerQuery))) return 60;
|
|
11703
11951
|
if (lowerPath.includes(lowerQuery)) return 50;
|
|
11704
11952
|
return 0;
|
|
@@ -11823,7 +12071,7 @@ var RepoFileService = class {
|
|
|
11823
12071
|
tooLarge: true
|
|
11824
12072
|
};
|
|
11825
12073
|
}
|
|
11826
|
-
const content = await
|
|
12074
|
+
const content = await readFile15(fullPath, "utf-8");
|
|
11827
12075
|
return {
|
|
11828
12076
|
repoName,
|
|
11829
12077
|
path: filePath,
|
|
@@ -11901,17 +12149,17 @@ var RepoFileService = class {
|
|
|
11901
12149
|
// src/v1-routes.ts
|
|
11902
12150
|
import { Hono } from "hono";
|
|
11903
12151
|
import { z as z2 } from "zod";
|
|
11904
|
-
import { readdir as
|
|
12152
|
+
import { readdir as readdir8, stat as stat5, readFile as readFile18 } from "fs/promises";
|
|
11905
12153
|
import { join as join23, resolve as resolve3 } from "path";
|
|
11906
12154
|
|
|
11907
12155
|
// src/services/warm-hooks-service.ts
|
|
11908
12156
|
import { spawn as spawn4 } from "child_process";
|
|
11909
|
-
import { readFile as
|
|
12157
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
11910
12158
|
import { existsSync as existsSync8 } from "fs";
|
|
11911
12159
|
import { join as join22 } from "path";
|
|
11912
12160
|
|
|
11913
12161
|
// src/services/warm-hook-logs-service.ts
|
|
11914
|
-
import { mkdir as mkdir14, readFile as
|
|
12162
|
+
import { mkdir as mkdir14, readFile as readFile16, writeFile as writeFile6, readdir as readdir7, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
|
|
11915
12163
|
import { homedir as homedir15 } from "os";
|
|
11916
12164
|
import { join as join21 } from "path";
|
|
11917
12165
|
var LOGS_DIR2 = join21(homedir15(), ".replicas", "warm-hook-logs");
|
|
@@ -11958,7 +12206,7 @@ var WarmHookLogsService = class {
|
|
|
11958
12206
|
async getAllLogs() {
|
|
11959
12207
|
let files;
|
|
11960
12208
|
try {
|
|
11961
|
-
files = await
|
|
12209
|
+
files = await readdir7(LOGS_DIR2);
|
|
11962
12210
|
} catch (err) {
|
|
11963
12211
|
if (err.code === "ENOENT") {
|
|
11964
12212
|
return [];
|
|
@@ -11971,7 +12219,7 @@ var WarmHookLogsService = class {
|
|
|
11971
12219
|
continue;
|
|
11972
12220
|
}
|
|
11973
12221
|
try {
|
|
11974
|
-
const raw = await
|
|
12222
|
+
const raw = await readFile16(join21(LOGS_DIR2, file), "utf-8");
|
|
11975
12223
|
const stored = JSON.parse(raw);
|
|
11976
12224
|
logs.push(withPreview2(stored));
|
|
11977
12225
|
} catch {
|
|
@@ -12000,7 +12248,7 @@ var WarmHookLogsService = class {
|
|
|
12000
12248
|
}
|
|
12001
12249
|
async getCurrentRunLog() {
|
|
12002
12250
|
try {
|
|
12003
|
-
return await
|
|
12251
|
+
return await readFile16(CURRENT_RUN_LOG, "utf-8");
|
|
12004
12252
|
} catch (err) {
|
|
12005
12253
|
if (err.code === "ENOENT") return null;
|
|
12006
12254
|
throw err;
|
|
@@ -12009,7 +12257,7 @@ var WarmHookLogsService = class {
|
|
|
12009
12257
|
async getFullOutput(hookType, hookName) {
|
|
12010
12258
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
12011
12259
|
try {
|
|
12012
|
-
const raw = await
|
|
12260
|
+
const raw = await readFile16(join21(LOGS_DIR2, filename), "utf-8");
|
|
12013
12261
|
const stored = JSON.parse(raw);
|
|
12014
12262
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
12015
12263
|
return null;
|
|
@@ -12033,7 +12281,7 @@ async function readRepoWarmHook(repoPath) {
|
|
|
12033
12281
|
continue;
|
|
12034
12282
|
}
|
|
12035
12283
|
try {
|
|
12036
|
-
const raw = await
|
|
12284
|
+
const raw = await readFile17(configPath, "utf-8");
|
|
12037
12285
|
const config = parseReplicasConfigString(raw, filename);
|
|
12038
12286
|
if (!config.warmHook) {
|
|
12039
12287
|
return null;
|
|
@@ -12468,6 +12716,17 @@ function createV1Routes(deps) {
|
|
|
12468
12716
|
return c.json(jsonError("Failed to load chat history", error instanceof Error ? error.message : "Unknown error"), 404);
|
|
12469
12717
|
}
|
|
12470
12718
|
});
|
|
12719
|
+
app2.get("/chats/:chatId/slash-commands", async (c) => {
|
|
12720
|
+
try {
|
|
12721
|
+
const response = await deps.chatService.listSlashCommands(c.req.param("chatId"));
|
|
12722
|
+
return c.json(response);
|
|
12723
|
+
} catch (error) {
|
|
12724
|
+
if (error instanceof ChatNotFoundError) {
|
|
12725
|
+
return c.json(jsonError("Failed to load slash commands", error.message), 404);
|
|
12726
|
+
}
|
|
12727
|
+
return c.json(jsonError("Failed to load slash commands", error instanceof Error ? error.message : "Unknown error"), 500);
|
|
12728
|
+
}
|
|
12729
|
+
});
|
|
12471
12730
|
app2.post("/chats/:chatId/messages", async (c) => {
|
|
12472
12731
|
try {
|
|
12473
12732
|
const body = sendMessageSchema.parse(await c.req.json());
|
|
@@ -12979,7 +13238,7 @@ function createV1Routes(deps) {
|
|
|
12979
13238
|
});
|
|
12980
13239
|
app2.get("/logs", async (c) => {
|
|
12981
13240
|
try {
|
|
12982
|
-
const files = await
|
|
13241
|
+
const files = await readdir8(LOG_DIR).catch(() => []);
|
|
12983
13242
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
12984
13243
|
const sessions = await Promise.all(
|
|
12985
13244
|
logFiles.map(async (filename) => {
|
|
@@ -13020,7 +13279,7 @@ function createV1Routes(deps) {
|
|
|
13020
13279
|
const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
|
|
13021
13280
|
let content;
|
|
13022
13281
|
try {
|
|
13023
|
-
content = await
|
|
13282
|
+
content = await readFile18(filePath, "utf-8");
|
|
13024
13283
|
} catch {
|
|
13025
13284
|
return c.json(jsonError("Log session not found"), 404);
|
|
13026
13285
|
}
|