replicas-engine 0.1.257 → 0.1.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.js +329 -227
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -3,21 +3,11 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { serve } from "@hono/node-server";
|
|
5
5
|
import { Hono as Hono2 } from "hono";
|
|
6
|
-
import { execSync as execSync2 } from "child_process";
|
|
7
6
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8
7
|
|
|
9
|
-
// src/
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
// src/engine-env.ts
|
|
14
|
-
import { homedir as homedir2 } from "os";
|
|
15
|
-
import { join as join2 } from "path";
|
|
16
|
-
|
|
17
|
-
// src/runtime-env-loader.ts
|
|
18
|
-
import { readFileSync } from "fs";
|
|
19
|
-
import { homedir } from "os";
|
|
20
|
-
import { join } from "path";
|
|
8
|
+
// src/utils/exec.ts
|
|
9
|
+
import { exec, execFile } from "child_process";
|
|
10
|
+
import { promisify } from "util";
|
|
21
11
|
|
|
22
12
|
// ../shared/src/type-guards.ts
|
|
23
13
|
function isRecord(value) {
|
|
@@ -137,7 +127,7 @@ function detectLanguageByPath(filePath) {
|
|
|
137
127
|
|
|
138
128
|
// ../shared/src/context-usage.ts
|
|
139
129
|
var CODEX_CATEGORY_COLORS = {
|
|
140
|
-
input: "#
|
|
130
|
+
input: "#3eeba3",
|
|
141
131
|
output: "#56b6c2"
|
|
142
132
|
};
|
|
143
133
|
function clampPercentage(value) {
|
|
@@ -296,7 +286,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
296
286
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
297
287
|
|
|
298
288
|
// ../shared/src/e2b.ts
|
|
299
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-
|
|
289
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-04-v1";
|
|
300
290
|
|
|
301
291
|
// ../shared/src/runtime-env.ts
|
|
302
292
|
function parsePosixEnvFile(content) {
|
|
@@ -357,6 +347,25 @@ function parsePosixEnvFile(content) {
|
|
|
357
347
|
// ../shared/src/git.ts
|
|
358
348
|
var GIT_PARTIAL_CLONE_FILTER = "blob:none";
|
|
359
349
|
|
|
350
|
+
// ../shared/src/in-flight.ts
|
|
351
|
+
var InFlightMap = class {
|
|
352
|
+
pending = /* @__PURE__ */ new Map();
|
|
353
|
+
run(key, producer) {
|
|
354
|
+
const existing = this.pending.get(key);
|
|
355
|
+
if (existing) return existing;
|
|
356
|
+
const promise = (async () => producer())().finally(() => {
|
|
357
|
+
if (this.pending.get(key) === promise) {
|
|
358
|
+
this.pending.delete(key);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
this.pending.set(key, promise);
|
|
362
|
+
return promise;
|
|
363
|
+
}
|
|
364
|
+
get(key) {
|
|
365
|
+
return this.pending.get(key);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
360
369
|
// ../shared/src/default-skills/replicas-agent/abilities/computer.ts
|
|
361
370
|
var SECTION = `### Computer use (Linux desktop control)
|
|
362
371
|
Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - via the \`replicas computer\` CLI. Every Replicas workspace boots with Xvfb / openbox / x11vnc / noVNC pre-installed and the live noVNC viewer is automatically published as an authenticated preview, so a \`Desktop\` tab is always available in the dashboard. Use \`replicas computer info\` to get the viewer URL when you want to share it (e.g. point a user on Slack at the live stream).
|
|
@@ -1634,6 +1643,7 @@ var DEFAULT_DEFAULT_SKILLS = {
|
|
|
1634
1643
|
};
|
|
1635
1644
|
|
|
1636
1645
|
// ../shared/src/prompts.ts
|
|
1646
|
+
var MAX_CODEX_GOAL_OBJECTIVE_CHARS = 4e3;
|
|
1637
1647
|
function parseGoalCommand(message) {
|
|
1638
1648
|
const match = message.trim().match(/^\/goal(?:\s+([\s\S]*))?$/i);
|
|
1639
1649
|
const value = match?.[1]?.trim();
|
|
@@ -1641,6 +1651,13 @@ function parseGoalCommand(message) {
|
|
|
1641
1651
|
if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
|
|
1642
1652
|
return { type: "set", objective: value };
|
|
1643
1653
|
}
|
|
1654
|
+
function getGoalCommandObjectiveValidationError(message) {
|
|
1655
|
+
const command = parseGoalCommand(message);
|
|
1656
|
+
if (command?.type !== "set") return null;
|
|
1657
|
+
const length = command.objective.length;
|
|
1658
|
+
if (length <= MAX_CODEX_GOAL_OBJECTIVE_CHARS) return null;
|
|
1659
|
+
return `Goal objective must be at most ${MAX_CODEX_GOAL_OBJECTIVE_CHARS} characters (${length} provided). Shorten the /goal objective, or send the extra details as a regular message after setting the goal.`;
|
|
1660
|
+
}
|
|
1644
1661
|
|
|
1645
1662
|
// ../shared/src/replicas-config.ts
|
|
1646
1663
|
import { parse as parseYaml } from "yaml";
|
|
@@ -2240,7 +2257,23 @@ function isGitHubUrl(url) {
|
|
|
2240
2257
|
return GITHUB_HOST_RE.test(url);
|
|
2241
2258
|
}
|
|
2242
2259
|
|
|
2260
|
+
// src/utils/exec.ts
|
|
2261
|
+
var execAsync = promisify(exec);
|
|
2262
|
+
var execFileAsync = promisify(execFile);
|
|
2263
|
+
var SUBPROCESS_MAX_BUFFER = HOOK_EXEC_MAX_BUFFER_BYTES;
|
|
2264
|
+
|
|
2265
|
+
// src/managers/github-token-manager.ts
|
|
2266
|
+
import { promises as fs } from "fs";
|
|
2267
|
+
import path from "path";
|
|
2268
|
+
|
|
2269
|
+
// src/engine-env.ts
|
|
2270
|
+
import { homedir as homedir2 } from "os";
|
|
2271
|
+
import { join as join2 } from "path";
|
|
2272
|
+
|
|
2243
2273
|
// src/runtime-env-loader.ts
|
|
2274
|
+
import { readFileSync } from "fs";
|
|
2275
|
+
import { homedir } from "os";
|
|
2276
|
+
import { join } from "path";
|
|
2244
2277
|
function loadRuntimeEnvFile() {
|
|
2245
2278
|
let content;
|
|
2246
2279
|
try {
|
|
@@ -2736,9 +2769,9 @@ var CodexTokenManager = class extends BaseRefreshManager {
|
|
|
2736
2769
|
var codexTokenManager = new CodexTokenManager();
|
|
2737
2770
|
|
|
2738
2771
|
// src/git/service.ts
|
|
2739
|
-
import { readdir, stat } from "fs/promises";
|
|
2740
|
-
import { existsSync as existsSync2,
|
|
2741
|
-
import {
|
|
2772
|
+
import { readdir, readFile as readFile2, stat } from "fs/promises";
|
|
2773
|
+
import { existsSync as existsSync2, unlinkSync } from "fs";
|
|
2774
|
+
import { spawn } from "child_process";
|
|
2742
2775
|
import { join as join5 } from "path";
|
|
2743
2776
|
|
|
2744
2777
|
// src/utils/state.ts
|
|
@@ -2869,15 +2902,15 @@ async function saveRepoState(repoName, state, fallbackState) {
|
|
|
2869
2902
|
}
|
|
2870
2903
|
|
|
2871
2904
|
// src/git/commands.ts
|
|
2872
|
-
import { execFileSync } from "child_process";
|
|
2873
2905
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2874
2906
|
import { join as join4 } from "path";
|
|
2875
|
-
function runGitCommand(args, cwd) {
|
|
2876
|
-
|
|
2907
|
+
async function runGitCommand(args, cwd, options = {}) {
|
|
2908
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
2877
2909
|
cwd,
|
|
2878
2910
|
encoding: "utf-8",
|
|
2879
|
-
|
|
2880
|
-
})
|
|
2911
|
+
maxBuffer: options.maxBuffer ?? SUBPROCESS_MAX_BUFFER
|
|
2912
|
+
});
|
|
2913
|
+
return stdout.trim();
|
|
2881
2914
|
}
|
|
2882
2915
|
function readRepoHeadBranch(repoPath) {
|
|
2883
2916
|
try {
|
|
@@ -2888,17 +2921,17 @@ function readRepoHeadBranch(repoPath) {
|
|
|
2888
2921
|
return null;
|
|
2889
2922
|
}
|
|
2890
2923
|
}
|
|
2891
|
-
function branchExists(branchName, cwd) {
|
|
2924
|
+
async function branchExists(branchName, cwd) {
|
|
2892
2925
|
try {
|
|
2893
|
-
runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
2926
|
+
await runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
2894
2927
|
return true;
|
|
2895
2928
|
} catch {
|
|
2896
2929
|
return false;
|
|
2897
2930
|
}
|
|
2898
2931
|
}
|
|
2899
|
-
function getCurrentBranch(cwd) {
|
|
2932
|
+
async function getCurrentBranch(cwd) {
|
|
2900
2933
|
try {
|
|
2901
|
-
return runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
2934
|
+
return await runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
2902
2935
|
} catch (error) {
|
|
2903
2936
|
console.error("Error getting current branch:", error);
|
|
2904
2937
|
return null;
|
|
@@ -2906,6 +2939,7 @@ function getCurrentBranch(cwd) {
|
|
|
2906
2939
|
}
|
|
2907
2940
|
|
|
2908
2941
|
// src/git/service.ts
|
|
2942
|
+
var FULL_DIFF_MAX_BUFFER = 50 * 1024 * 1024;
|
|
2909
2943
|
function appendUniqueUrl(urls, url) {
|
|
2910
2944
|
return urls.includes(url) ? urls : [...urls, url];
|
|
2911
2945
|
}
|
|
@@ -2915,6 +2949,10 @@ var GitService = class {
|
|
|
2915
2949
|
// No invalidation on purpose — `git remote set-url` mid-session is rare and
|
|
2916
2950
|
// the worst case is `gh pr view` stays skipped until the next engine restart.
|
|
2917
2951
|
originIsGitHubCache = /* @__PURE__ */ new Map();
|
|
2952
|
+
// Broadcaster + UI /repos requests fan in here every ~2s; one shared run.
|
|
2953
|
+
listReposInFlight = new InFlightMap();
|
|
2954
|
+
diffStatsInFlight = new InFlightMap();
|
|
2955
|
+
fullDiffInFlight = new InFlightMap();
|
|
2918
2956
|
getWorkspaceRoot() {
|
|
2919
2957
|
return ENGINE_ENV.WORKSPACE_ROOT;
|
|
2920
2958
|
}
|
|
@@ -2940,7 +2978,7 @@ var GitService = class {
|
|
|
2940
2978
|
repos.push({
|
|
2941
2979
|
name: entry,
|
|
2942
2980
|
path: fullPath,
|
|
2943
|
-
defaultBranch: this.resolveDefaultBranch(fullPath)
|
|
2981
|
+
defaultBranch: await this.resolveDefaultBranch(fullPath)
|
|
2944
2982
|
});
|
|
2945
2983
|
} catch {
|
|
2946
2984
|
}
|
|
@@ -2949,13 +2987,21 @@ var GitService = class {
|
|
|
2949
2987
|
}
|
|
2950
2988
|
async listRepos(options) {
|
|
2951
2989
|
const includeDiffs = options?.includeDiffs === true;
|
|
2990
|
+
const key = includeDiffs ? "diffs" : "base";
|
|
2991
|
+
return this.listReposInFlight.run(key, () => this.computeListRepos(includeDiffs));
|
|
2992
|
+
}
|
|
2993
|
+
async computeListRepos(includeDiffs) {
|
|
2952
2994
|
const repos = await this.listRepositories();
|
|
2953
2995
|
const states = [];
|
|
2954
2996
|
for (const repo of repos) {
|
|
2955
2997
|
try {
|
|
2956
|
-
const persistedState = await
|
|
2957
|
-
|
|
2958
|
-
|
|
2998
|
+
const [persistedState, currentBranchRaw, gitDiff] = await Promise.all([
|
|
2999
|
+
loadRepoState(repo.name),
|
|
3000
|
+
getCurrentBranch(repo.path),
|
|
3001
|
+
this.getGitDiffStats(repo.path, repo.defaultBranch)
|
|
3002
|
+
]);
|
|
3003
|
+
const currentBranch = currentBranchRaw ?? repo.defaultBranch;
|
|
3004
|
+
const fullDiff = includeDiffs && gitDiff ? await this.getFullGitDiff(repo.path, repo.defaultBranch) : void 0;
|
|
2959
3005
|
states.push({
|
|
2960
3006
|
name: repo.name,
|
|
2961
3007
|
path: repo.path,
|
|
@@ -2963,7 +3009,7 @@ var GitService = class {
|
|
|
2963
3009
|
currentBranch,
|
|
2964
3010
|
prUrls: persistedState?.prUrls ?? [],
|
|
2965
3011
|
// fullDiff may be empty if the diff subprocess fails.
|
|
2966
|
-
gitDiff: includeDiffs && gitDiff ? { ...gitDiff, fullDiff:
|
|
3012
|
+
gitDiff: includeDiffs && gitDiff ? { ...gitDiff, fullDiff: fullDiff ?? "" } : gitDiff,
|
|
2967
3013
|
startHooksCompleted: persistedState?.startHooksCompleted ?? false
|
|
2968
3014
|
});
|
|
2969
3015
|
} catch {
|
|
@@ -2977,7 +3023,7 @@ var GitService = class {
|
|
|
2977
3023
|
for (const repo of repos) {
|
|
2978
3024
|
try {
|
|
2979
3025
|
const persistedState = await loadRepoState(repo.name);
|
|
2980
|
-
const currentBranch = getCurrentBranch(repo.path) ?? repo.defaultBranch;
|
|
3026
|
+
const currentBranch = await getCurrentBranch(repo.path) ?? repo.defaultBranch;
|
|
2981
3027
|
const startHooksCompleted = persistedState?.startHooksCompleted ?? false;
|
|
2982
3028
|
const observed = observedBranchesByRepo?.get(repo.name);
|
|
2983
3029
|
states.push(
|
|
@@ -3042,12 +3088,12 @@ var GitService = class {
|
|
|
3042
3088
|
const persistedState = await loadRepoState(repo.name);
|
|
3043
3089
|
const persistedBranch = persistedState?.currentBranch;
|
|
3044
3090
|
if (!skipNetworkRefresh) {
|
|
3045
|
-
runGitCommand(["fetch", "--all", "--prune", `--filter=${GIT_PARTIAL_CLONE_FILTER}`], repo.path);
|
|
3091
|
+
await runGitCommand(["fetch", "--all", "--prune", `--filter=${GIT_PARTIAL_CLONE_FILTER}`], repo.path);
|
|
3046
3092
|
}
|
|
3047
|
-
if (persistedBranch && branchExists(persistedBranch, repo.path)) {
|
|
3048
|
-
const currentBranch = getCurrentBranch(repo.path);
|
|
3093
|
+
if (persistedBranch && await branchExists(persistedBranch, repo.path)) {
|
|
3094
|
+
const currentBranch = await getCurrentBranch(repo.path);
|
|
3049
3095
|
if (currentBranch !== persistedBranch) {
|
|
3050
|
-
runGitCommand(["checkout", persistedBranch], repo.path);
|
|
3096
|
+
await runGitCommand(["checkout", persistedBranch], repo.path);
|
|
3051
3097
|
}
|
|
3052
3098
|
results.push({
|
|
3053
3099
|
name: repo.name,
|
|
@@ -3057,15 +3103,15 @@ var GitService = class {
|
|
|
3057
3103
|
});
|
|
3058
3104
|
continue;
|
|
3059
3105
|
}
|
|
3060
|
-
runGitCommand(["checkout", repo.defaultBranch], repo.path);
|
|
3106
|
+
await runGitCommand(["checkout", repo.defaultBranch], repo.path);
|
|
3061
3107
|
if (!skipNetworkRefresh) {
|
|
3062
3108
|
try {
|
|
3063
|
-
runGitCommand(["pull", "--rebase", "--autostash"], repo.path);
|
|
3109
|
+
await runGitCommand(["pull", "--rebase", "--autostash"], repo.path);
|
|
3064
3110
|
} catch {
|
|
3065
3111
|
}
|
|
3066
3112
|
}
|
|
3067
|
-
const branchName = this.findAvailableBranchName(workspaceName, repo.path);
|
|
3068
|
-
runGitCommand(["checkout", "-b", branchName], repo.path);
|
|
3113
|
+
const branchName = await this.findAvailableBranchName(workspaceName, repo.path);
|
|
3114
|
+
await runGitCommand(["checkout", "-b", branchName], repo.path);
|
|
3069
3115
|
await saveRepoState(repo.name, { currentBranch: branchName }, baselineState);
|
|
3070
3116
|
results.push({
|
|
3071
3117
|
name: repo.name,
|
|
@@ -3103,109 +3149,149 @@ var GitService = class {
|
|
|
3103
3149
|
return true;
|
|
3104
3150
|
}
|
|
3105
3151
|
getGitDiffStats(repoPath, defaultBranch) {
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3152
|
+
return this.diffStatsInFlight.run(repoPath, async () => {
|
|
3153
|
+
try {
|
|
3154
|
+
const diffBase = await this.getDiffBase(repoPath, defaultBranch);
|
|
3155
|
+
const shortstat = await runGitCommand(["diff", diffBase, "--shortstat", "-M"], repoPath);
|
|
3156
|
+
let added = 0;
|
|
3157
|
+
let removed = 0;
|
|
3158
|
+
const addedMatch = shortstat.match(/(\d+) insertion/);
|
|
3159
|
+
const removedMatch = shortstat.match(/(\d+) deletion/);
|
|
3160
|
+
if (addedMatch) {
|
|
3161
|
+
added = parseInt(addedMatch[1], 10);
|
|
3162
|
+
}
|
|
3163
|
+
if (removedMatch) {
|
|
3164
|
+
removed = parseInt(removedMatch[1], 10);
|
|
3165
|
+
}
|
|
3166
|
+
added += await this.countUntrackedAddedLines(repoPath);
|
|
3167
|
+
return {
|
|
3168
|
+
added,
|
|
3169
|
+
removed
|
|
3170
|
+
};
|
|
3171
|
+
} catch (error) {
|
|
3172
|
+
console.error("Error getting git diff:", error);
|
|
3173
|
+
return null;
|
|
3174
|
+
}
|
|
3175
|
+
});
|
|
3128
3176
|
}
|
|
3129
|
-
listUntrackedPaths(repoPath) {
|
|
3130
|
-
const
|
|
3177
|
+
async listUntrackedPaths(repoPath) {
|
|
3178
|
+
const { stdout } = await execFileAsync("git", ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
3131
3179
|
cwd: repoPath,
|
|
3132
3180
|
encoding: "utf-8",
|
|
3133
|
-
|
|
3181
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3134
3182
|
});
|
|
3135
|
-
return
|
|
3183
|
+
return stdout.split("\0").filter(Boolean);
|
|
3136
3184
|
}
|
|
3137
|
-
getUntrackedDiff(repoPath) {
|
|
3138
|
-
const paths = this.listUntrackedPaths(repoPath);
|
|
3185
|
+
async getUntrackedDiff(repoPath) {
|
|
3186
|
+
const paths = await this.listUntrackedPaths(repoPath);
|
|
3139
3187
|
if (paths.length === 0) return "";
|
|
3140
3188
|
try {
|
|
3141
|
-
|
|
3142
|
-
const result = spawnSync("git", ["diff", "--", ...paths], {
|
|
3189
|
+
await execFileAsync("git", ["add", "--intent-to-add", "--", ...paths], {
|
|
3143
3190
|
cwd: repoPath,
|
|
3144
|
-
|
|
3145
|
-
maxBuffer: 50 * 1024 * 1024
|
|
3191
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3146
3192
|
});
|
|
3147
|
-
return
|
|
3193
|
+
return await this.readUntrackedDiff(repoPath, paths);
|
|
3148
3194
|
} finally {
|
|
3149
3195
|
try {
|
|
3150
|
-
|
|
3196
|
+
await execFileAsync("git", ["reset", "--", ...paths], {
|
|
3197
|
+
cwd: repoPath,
|
|
3198
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3199
|
+
});
|
|
3151
3200
|
} catch {
|
|
3152
3201
|
}
|
|
3153
3202
|
}
|
|
3154
3203
|
}
|
|
3155
|
-
|
|
3204
|
+
readUntrackedDiff(repoPath, paths) {
|
|
3205
|
+
return new Promise((resolve3) => {
|
|
3206
|
+
const child = spawn("git", ["diff", "--", ...paths], {
|
|
3207
|
+
cwd: repoPath,
|
|
3208
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3209
|
+
});
|
|
3210
|
+
const chunks = [];
|
|
3211
|
+
let total = 0;
|
|
3212
|
+
let truncated = false;
|
|
3213
|
+
child.stdout.on("data", (chunk) => {
|
|
3214
|
+
if (truncated) return;
|
|
3215
|
+
total += chunk.length;
|
|
3216
|
+
if (total > FULL_DIFF_MAX_BUFFER) {
|
|
3217
|
+
truncated = true;
|
|
3218
|
+
chunks.push(chunk.subarray(0, Math.max(0, FULL_DIFF_MAX_BUFFER - (total - chunk.length))));
|
|
3219
|
+
child.kill("SIGTERM");
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
chunks.push(chunk);
|
|
3223
|
+
});
|
|
3224
|
+
child.stderr.on("data", () => {
|
|
3225
|
+
});
|
|
3226
|
+
child.on("error", () => resolve3(""));
|
|
3227
|
+
child.on("close", (code) => {
|
|
3228
|
+
if (code === 0 || code === 1 || truncated) {
|
|
3229
|
+
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
3230
|
+
} else {
|
|
3231
|
+
resolve3("");
|
|
3232
|
+
}
|
|
3233
|
+
});
|
|
3234
|
+
});
|
|
3235
|
+
}
|
|
3236
|
+
async countUntrackedAddedLines(repoPath) {
|
|
3156
3237
|
try {
|
|
3157
|
-
|
|
3238
|
+
const paths = await this.listUntrackedPaths(repoPath);
|
|
3239
|
+
let total = 0;
|
|
3240
|
+
for (const path4 of paths) {
|
|
3158
3241
|
try {
|
|
3159
|
-
const contents =
|
|
3242
|
+
const contents = await readFile2(join5(repoPath, path4));
|
|
3160
3243
|
if (contents.length === 0 || contents.includes(0)) {
|
|
3161
|
-
|
|
3244
|
+
continue;
|
|
3162
3245
|
}
|
|
3163
3246
|
let lines = 0;
|
|
3164
3247
|
for (const byte of contents) {
|
|
3165
3248
|
if (byte === 10) lines += 1;
|
|
3166
3249
|
}
|
|
3167
|
-
|
|
3250
|
+
total += lines + (contents[contents.length - 1] === 10 ? 0 : 1);
|
|
3168
3251
|
} catch {
|
|
3169
|
-
return total;
|
|
3170
3252
|
}
|
|
3171
|
-
}
|
|
3253
|
+
}
|
|
3254
|
+
return total;
|
|
3172
3255
|
} catch (error) {
|
|
3173
3256
|
console.error("Error counting untracked added lines:", error);
|
|
3174
3257
|
return 0;
|
|
3175
3258
|
}
|
|
3176
3259
|
}
|
|
3177
3260
|
getFullGitDiff(repoPath, defaultBranch) {
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3261
|
+
return this.fullDiffInFlight.run(repoPath, async () => {
|
|
3262
|
+
try {
|
|
3263
|
+
const diffBase = await this.getDiffBase(repoPath, defaultBranch);
|
|
3264
|
+
const { stdout: trackedDiff } = await execFileAsync("git", ["diff", diffBase, "-M", "-C"], {
|
|
3265
|
+
cwd: repoPath,
|
|
3266
|
+
encoding: "utf-8",
|
|
3267
|
+
maxBuffer: FULL_DIFF_MAX_BUFFER
|
|
3268
|
+
});
|
|
3269
|
+
const untrackedDiff = await this.getUntrackedAsDiff(repoPath);
|
|
3270
|
+
return trackedDiff + untrackedDiff;
|
|
3271
|
+
} catch {
|
|
3272
|
+
return "";
|
|
3273
|
+
}
|
|
3274
|
+
});
|
|
3189
3275
|
}
|
|
3190
|
-
getUntrackedAsDiff(repoPath) {
|
|
3276
|
+
async getUntrackedAsDiff(repoPath) {
|
|
3191
3277
|
try {
|
|
3192
|
-
return this.getUntrackedDiff(repoPath);
|
|
3278
|
+
return await this.getUntrackedDiff(repoPath);
|
|
3193
3279
|
} catch (error) {
|
|
3194
3280
|
console.error("Error building untracked diff:", error);
|
|
3195
3281
|
return "";
|
|
3196
3282
|
}
|
|
3197
3283
|
}
|
|
3198
|
-
getDiffBase(repoPath, defaultBranch) {
|
|
3284
|
+
async getDiffBase(repoPath, defaultBranch) {
|
|
3199
3285
|
const baseBranch = `origin/${defaultBranch}`;
|
|
3200
3286
|
try {
|
|
3201
|
-
return runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3287
|
+
return await runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3202
3288
|
} catch {
|
|
3203
3289
|
return baseBranch;
|
|
3204
3290
|
}
|
|
3205
3291
|
}
|
|
3206
3292
|
async getPullRequestUrl(repoName, repoPath, currentBranchArg, persistedRepoStateArg) {
|
|
3207
3293
|
try {
|
|
3208
|
-
const currentBranch = currentBranchArg ?? getCurrentBranch(repoPath);
|
|
3294
|
+
const currentBranch = currentBranchArg ?? await getCurrentBranch(repoPath);
|
|
3209
3295
|
if (!currentBranch) {
|
|
3210
3296
|
return { status: "not_found" };
|
|
3211
3297
|
}
|
|
@@ -3215,7 +3301,7 @@ var GitService = class {
|
|
|
3215
3301
|
}
|
|
3216
3302
|
const persistedRepoState = persistedRepoStateArg ?? await loadRepoState(repoName);
|
|
3217
3303
|
this.cachedPrByRepo.delete(repoName);
|
|
3218
|
-
const result = this.lookupPrOnRemote(repoName, repoPath, currentBranch);
|
|
3304
|
+
const result = await this.lookupPrOnRemote(repoName, repoPath, currentBranch);
|
|
3219
3305
|
if (result.status === "found") {
|
|
3220
3306
|
this.cachedPrByRepo.set(repoName, { prUrl: result.url, currentBranch });
|
|
3221
3307
|
if (persistedRepoState && !persistedRepoState.prUrls.includes(result.url)) {
|
|
@@ -3232,28 +3318,29 @@ var GitService = class {
|
|
|
3232
3318
|
return { status: "error" };
|
|
3233
3319
|
}
|
|
3234
3320
|
}
|
|
3235
|
-
lookupPrOnRemote(repoName, repoPath, branch) {
|
|
3236
|
-
if (!this.originIsGitHub(repoPath)) {
|
|
3321
|
+
async lookupPrOnRemote(repoName, repoPath, branch) {
|
|
3322
|
+
if (!await this.originIsGitHub(repoPath)) {
|
|
3237
3323
|
return { status: "not_found" };
|
|
3238
3324
|
}
|
|
3239
3325
|
try {
|
|
3240
|
-
const
|
|
3326
|
+
const { stdout } = await execFileAsync("git", ["ls-remote", "--heads", "origin", branch], {
|
|
3241
3327
|
cwd: repoPath,
|
|
3242
3328
|
encoding: "utf-8",
|
|
3243
|
-
|
|
3244
|
-
})
|
|
3245
|
-
if (!
|
|
3329
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3330
|
+
});
|
|
3331
|
+
if (!stdout.trim()) {
|
|
3246
3332
|
return { status: "not_found" };
|
|
3247
3333
|
}
|
|
3248
3334
|
} catch {
|
|
3249
3335
|
return { status: "error" };
|
|
3250
3336
|
}
|
|
3251
3337
|
try {
|
|
3252
|
-
const
|
|
3338
|
+
const { stdout } = await execFileAsync("gh", ["pr", "view", branch, "--json", "url", "--jq", ".url"], {
|
|
3253
3339
|
cwd: repoPath,
|
|
3254
3340
|
encoding: "utf-8",
|
|
3255
|
-
|
|
3256
|
-
})
|
|
3341
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3342
|
+
});
|
|
3343
|
+
const prInfo = stdout.trim();
|
|
3257
3344
|
return prInfo ? { status: "found", url: prInfo } : { status: "not_found" };
|
|
3258
3345
|
} catch (error) {
|
|
3259
3346
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3261,31 +3348,31 @@ var GitService = class {
|
|
|
3261
3348
|
return { status: "error" };
|
|
3262
3349
|
}
|
|
3263
3350
|
}
|
|
3264
|
-
originIsGitHub(repoPath) {
|
|
3351
|
+
async originIsGitHub(repoPath) {
|
|
3265
3352
|
const cached = this.originIsGitHubCache.get(repoPath);
|
|
3266
3353
|
if (cached !== void 0) {
|
|
3267
3354
|
return cached;
|
|
3268
3355
|
}
|
|
3269
3356
|
let isGitHub = false;
|
|
3270
3357
|
try {
|
|
3271
|
-
const
|
|
3358
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
|
|
3272
3359
|
cwd: repoPath,
|
|
3273
3360
|
encoding: "utf-8",
|
|
3274
|
-
|
|
3275
|
-
})
|
|
3276
|
-
isGitHub = isGitHubUrl(
|
|
3361
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3362
|
+
});
|
|
3363
|
+
isGitHub = isGitHubUrl(stdout.trim());
|
|
3277
3364
|
} catch {
|
|
3278
3365
|
isGitHub = false;
|
|
3279
3366
|
}
|
|
3280
3367
|
this.originIsGitHubCache.set(repoPath, isGitHub);
|
|
3281
3368
|
return isGitHub;
|
|
3282
3369
|
}
|
|
3283
|
-
resolveDefaultBranch(repoPath) {
|
|
3370
|
+
async resolveDefaultBranch(repoPath) {
|
|
3284
3371
|
const cached = this.defaultBranchCache.get(repoPath);
|
|
3285
3372
|
if (cached) {
|
|
3286
3373
|
return cached;
|
|
3287
3374
|
}
|
|
3288
|
-
const fromSymbolicRef = this.resolveDefaultBranchFromSymbolicRef(repoPath);
|
|
3375
|
+
const fromSymbolicRef = await this.resolveDefaultBranchFromSymbolicRef(repoPath);
|
|
3289
3376
|
if (fromSymbolicRef) {
|
|
3290
3377
|
this.defaultBranchCache.set(repoPath, fromSymbolicRef);
|
|
3291
3378
|
return fromSymbolicRef;
|
|
@@ -3294,18 +3381,18 @@ var GitService = class {
|
|
|
3294
3381
|
this.defaultBranchCache.set(repoPath, fallback);
|
|
3295
3382
|
return fallback;
|
|
3296
3383
|
}
|
|
3297
|
-
resolveDefaultBranchFromSymbolicRef(repoPath) {
|
|
3384
|
+
async resolveDefaultBranchFromSymbolicRef(repoPath) {
|
|
3298
3385
|
try {
|
|
3299
|
-
const output = runGitCommand(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoPath);
|
|
3386
|
+
const output = await runGitCommand(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoPath);
|
|
3300
3387
|
const match = output.match(/^origin\/(.+)$/);
|
|
3301
3388
|
return match ? match[1] : null;
|
|
3302
3389
|
} catch {
|
|
3303
3390
|
return null;
|
|
3304
3391
|
}
|
|
3305
3392
|
}
|
|
3306
|
-
findAvailableBranchName(baseName, cwd) {
|
|
3393
|
+
async findAvailableBranchName(baseName, cwd) {
|
|
3307
3394
|
const sanitizedBaseName = this.sanitizeBranchName(baseName);
|
|
3308
|
-
if (!branchExists(sanitizedBaseName, cwd)) {
|
|
3395
|
+
if (!await branchExists(sanitizedBaseName, cwd)) {
|
|
3309
3396
|
return sanitizedBaseName;
|
|
3310
3397
|
}
|
|
3311
3398
|
return `${sanitizedBaseName}-${Date.now()}`;
|
|
@@ -3323,7 +3410,7 @@ var GitService = class {
|
|
|
3323
3410
|
if (observedBranches) {
|
|
3324
3411
|
for (const branch of observedBranches) {
|
|
3325
3412
|
if (branch === currentBranch) continue;
|
|
3326
|
-
const branchResult = this.lookupPrOnRemote(repo.name, repo.path, branch);
|
|
3413
|
+
const branchResult = await this.lookupPrOnRemote(repo.name, repo.path, branch);
|
|
3327
3414
|
if (branchResult.status === "found") {
|
|
3328
3415
|
prUrls = appendUniqueUrl(prUrls, branchResult.url);
|
|
3329
3416
|
}
|
|
@@ -3335,7 +3422,7 @@ var GitService = class {
|
|
|
3335
3422
|
defaultBranch: repo.defaultBranch,
|
|
3336
3423
|
currentBranch,
|
|
3337
3424
|
prUrls,
|
|
3338
|
-
gitDiff: this.getGitDiffStats(repo.path, repo.defaultBranch),
|
|
3425
|
+
gitDiff: await this.getGitDiffStats(repo.path, repo.defaultBranch),
|
|
3339
3426
|
startHooksCompleted
|
|
3340
3427
|
};
|
|
3341
3428
|
await saveRepoState(repo.name, state, state);
|
|
@@ -3423,20 +3510,17 @@ var EngineLogger = class {
|
|
|
3423
3510
|
var engineLogger = new EngineLogger();
|
|
3424
3511
|
|
|
3425
3512
|
// src/services/replicas-config-service.ts
|
|
3426
|
-
import { readFile as
|
|
3513
|
+
import { readFile as readFile5, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
|
|
3427
3514
|
import { existsSync as existsSync4 } from "fs";
|
|
3428
3515
|
import { join as join9 } from "path";
|
|
3429
3516
|
import { homedir as homedir7 } from "os";
|
|
3430
|
-
import { spawn } from "child_process";
|
|
3517
|
+
import { spawn as spawn2 } from "child_process";
|
|
3431
3518
|
|
|
3432
3519
|
// src/services/environment-details-service.ts
|
|
3433
|
-
import { mkdir as mkdir3, readFile as
|
|
3520
|
+
import { mkdir as mkdir3, readFile as readFile3 } from "fs/promises";
|
|
3434
3521
|
import { existsSync as existsSync3 } from "fs";
|
|
3435
3522
|
import { homedir as homedir5 } from "os";
|
|
3436
3523
|
import { join as join7 } from "path";
|
|
3437
|
-
import { execFile } from "child_process";
|
|
3438
|
-
import { promisify } from "util";
|
|
3439
|
-
var execFileAsync = promisify(execFile);
|
|
3440
3524
|
var REPLICAS_DIR = join7(homedir5(), ".replicas");
|
|
3441
3525
|
var DETAILS_FILE = join7(REPLICAS_DIR, "environment-details.json");
|
|
3442
3526
|
var CLAUDE_CREDENTIALS_PATH = join7(homedir5(), ".claude", ".credentials.json");
|
|
@@ -3509,7 +3593,7 @@ async function readDetails() {
|
|
|
3509
3593
|
if (!existsSync3(DETAILS_FILE)) {
|
|
3510
3594
|
return createDefaultDetails();
|
|
3511
3595
|
}
|
|
3512
|
-
const raw = await
|
|
3596
|
+
const raw = await readFile3(DETAILS_FILE, "utf-8");
|
|
3513
3597
|
const parsed = JSON.parse(raw);
|
|
3514
3598
|
return { ...createDefaultDetails(), ...parsed };
|
|
3515
3599
|
} catch {
|
|
@@ -3603,7 +3687,7 @@ var EnvironmentDetailsService = class {
|
|
|
3603
3687
|
var environmentDetailsService = new EnvironmentDetailsService();
|
|
3604
3688
|
|
|
3605
3689
|
// src/services/start-hook-logs-service.ts
|
|
3606
|
-
import { mkdir as mkdir4, readFile as
|
|
3690
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
|
|
3607
3691
|
import { homedir as homedir6 } from "os";
|
|
3608
3692
|
import { join as join8 } from "path";
|
|
3609
3693
|
|
|
@@ -3680,7 +3764,7 @@ var StartHookLogsService = class {
|
|
|
3680
3764
|
continue;
|
|
3681
3765
|
}
|
|
3682
3766
|
try {
|
|
3683
|
-
const raw = await
|
|
3767
|
+
const raw = await readFile4(join8(LOGS_DIR, file), "utf-8");
|
|
3684
3768
|
const stored = normalizeStored(JSON.parse(raw));
|
|
3685
3769
|
if (stored) {
|
|
3686
3770
|
logs.push(withPreview(stored));
|
|
@@ -3699,7 +3783,7 @@ var StartHookLogsService = class {
|
|
|
3699
3783
|
async getFullOutput(hookType, hookName) {
|
|
3700
3784
|
const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
3701
3785
|
try {
|
|
3702
|
-
const raw = await
|
|
3786
|
+
const raw = await readFile4(join8(LOGS_DIR, filename), "utf-8");
|
|
3703
3787
|
const stored = normalizeStored(JSON.parse(raw));
|
|
3704
3788
|
if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
3705
3789
|
return null;
|
|
@@ -3731,7 +3815,7 @@ async function readReplicasConfigFromDir(dirPath) {
|
|
|
3731
3815
|
if (!existsSync4(configPath)) {
|
|
3732
3816
|
continue;
|
|
3733
3817
|
}
|
|
3734
|
-
const data = await
|
|
3818
|
+
const data = await readFile5(configPath, "utf-8");
|
|
3735
3819
|
const config = parseReplicasConfigString(data, filename);
|
|
3736
3820
|
return { config, filename };
|
|
3737
3821
|
}
|
|
@@ -3833,7 +3917,7 @@ var ReplicasConfigService = class {
|
|
|
3833
3917
|
emit(`$ ${params.label}
|
|
3834
3918
|
`);
|
|
3835
3919
|
return new Promise((resolve3) => {
|
|
3836
|
-
const proc =
|
|
3920
|
+
const proc = spawn2("bash", ["-lc", params.command], {
|
|
3837
3921
|
cwd: params.cwd,
|
|
3838
3922
|
env: process.env,
|
|
3839
3923
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4166,7 +4250,7 @@ var EventService = class {
|
|
|
4166
4250
|
var eventService = new EventService();
|
|
4167
4251
|
|
|
4168
4252
|
// src/services/preview-service.ts
|
|
4169
|
-
import { mkdir as mkdir7, readFile as
|
|
4253
|
+
import { mkdir as mkdir7, readFile as readFile6 } from "fs/promises";
|
|
4170
4254
|
import { existsSync as existsSync5 } from "fs";
|
|
4171
4255
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4172
4256
|
import { homedir as homedir9 } from "os";
|
|
@@ -4177,7 +4261,7 @@ async function readPreviewsFile() {
|
|
|
4177
4261
|
if (!existsSync5(PREVIEW_PORTS_FILE)) {
|
|
4178
4262
|
return { previews: [] };
|
|
4179
4263
|
}
|
|
4180
|
-
const raw = await
|
|
4264
|
+
const raw = await readFile6(PREVIEW_PORTS_FILE, "utf-8");
|
|
4181
4265
|
return JSON.parse(raw);
|
|
4182
4266
|
} catch {
|
|
4183
4267
|
return { previews: [] };
|
|
@@ -4282,7 +4366,7 @@ async function registerDesktopPreview() {
|
|
|
4282
4366
|
|
|
4283
4367
|
// src/services/chat/chat-service.ts
|
|
4284
4368
|
import { existsSync as existsSync8 } from "fs";
|
|
4285
|
-
import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as
|
|
4369
|
+
import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as readFile9, rename as rename2, rm } from "fs/promises";
|
|
4286
4370
|
import { homedir as homedir13 } from "os";
|
|
4287
4371
|
import { join as join15 } from "path";
|
|
4288
4372
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -4297,7 +4381,7 @@ import { mkdir as mkdir9, appendFile as appendFile4 } from "fs/promises";
|
|
|
4297
4381
|
import { homedir as homedir11 } from "os";
|
|
4298
4382
|
|
|
4299
4383
|
// src/utils/jsonl-reader.ts
|
|
4300
|
-
import { readFile as
|
|
4384
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
4301
4385
|
function isJsonlEvent(value) {
|
|
4302
4386
|
if (!isRecord4(value)) {
|
|
4303
4387
|
return false;
|
|
@@ -4319,7 +4403,7 @@ function parseJsonlEvents(lines) {
|
|
|
4319
4403
|
}
|
|
4320
4404
|
async function readJSONL(filePath) {
|
|
4321
4405
|
try {
|
|
4322
|
-
const content = await
|
|
4406
|
+
const content = await readFile7(filePath, "utf-8");
|
|
4323
4407
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
4324
4408
|
return parseJsonlEvents(lines);
|
|
4325
4409
|
} catch (error) {
|
|
@@ -6070,7 +6154,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
6070
6154
|
};
|
|
6071
6155
|
|
|
6072
6156
|
// src/managers/codex-asp/app-server-process.ts
|
|
6073
|
-
import { spawn as
|
|
6157
|
+
import { spawn as spawn3 } from "child_process";
|
|
6074
6158
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
6075
6159
|
|
|
6076
6160
|
// src/managers/codex-asp/asp-client.ts
|
|
@@ -6265,7 +6349,7 @@ var AspClient = class {
|
|
|
6265
6349
|
// src/managers/codex-asp/app-server-process.ts
|
|
6266
6350
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
6267
6351
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
6268
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6352
|
+
var ENGINE_PACKAGE_VERSION = "0.1.259";
|
|
6269
6353
|
var INITIALIZE_METHOD = "initialize";
|
|
6270
6354
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6271
6355
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -6292,7 +6376,7 @@ var AppServerProcess = class {
|
|
|
6292
6376
|
return { client: this.client };
|
|
6293
6377
|
}
|
|
6294
6378
|
this.shuttingDown = false;
|
|
6295
|
-
const child =
|
|
6379
|
+
const child = spawn3(this.binary, this.args, {
|
|
6296
6380
|
cwd: this.cwd,
|
|
6297
6381
|
env: this.env,
|
|
6298
6382
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6532,7 +6616,7 @@ function isCodexAuthError(error) {
|
|
|
6532
6616
|
}
|
|
6533
6617
|
|
|
6534
6618
|
// src/managers/codex-asp/mappers.ts
|
|
6535
|
-
import { existsSync as existsSync6, readFileSync as
|
|
6619
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
6536
6620
|
var localImageCache = /* @__PURE__ */ new Map();
|
|
6537
6621
|
var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
|
|
6538
6622
|
var THREAD_START_METHOD = "thread/start";
|
|
@@ -6613,7 +6697,7 @@ function userImageForLocalPath(path4) {
|
|
|
6613
6697
|
const image = {
|
|
6614
6698
|
type: "image",
|
|
6615
6699
|
mediaType: inferMediaType(path4),
|
|
6616
|
-
data:
|
|
6700
|
+
data: readFileSync3(path4).toString("base64")
|
|
6617
6701
|
};
|
|
6618
6702
|
if (image.data.length > 0) localImageCache.set(path4, image);
|
|
6619
6703
|
return image;
|
|
@@ -7885,7 +7969,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7885
7969
|
|
|
7886
7970
|
// src/managers/codex-manager.ts
|
|
7887
7971
|
import { Codex } from "@openai/codex-sdk";
|
|
7888
|
-
import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir10, readFile as
|
|
7972
|
+
import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir10, readFile as readFile8 } from "fs/promises";
|
|
7889
7973
|
import { existsSync as existsSync7 } from "fs";
|
|
7890
7974
|
import { join as join14 } from "path";
|
|
7891
7975
|
import { homedir as homedir12 } from "os";
|
|
@@ -7933,7 +8017,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
7933
8017
|
try {
|
|
7934
8018
|
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
7935
8019
|
if (!sessionFile) return;
|
|
7936
|
-
const content = await
|
|
8020
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
7937
8021
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
7938
8022
|
let latest = null;
|
|
7939
8023
|
for (const line of lines) {
|
|
@@ -7973,7 +8057,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
7973
8057
|
let config = {};
|
|
7974
8058
|
if (existsSync7(CODEX_CONFIG_PATH)) {
|
|
7975
8059
|
try {
|
|
7976
|
-
const existingContent = await
|
|
8060
|
+
const existingContent = await readFile8(CODEX_CONFIG_PATH, "utf-8");
|
|
7977
8061
|
const parsed = parseToml(existingContent);
|
|
7978
8062
|
if (isRecord4(parsed)) {
|
|
7979
8063
|
config = parsed;
|
|
@@ -8216,7 +8300,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
8216
8300
|
const seenLines = /* @__PURE__ */ new Set();
|
|
8217
8301
|
const seedSeenLines = async () => {
|
|
8218
8302
|
try {
|
|
8219
|
-
const content = await
|
|
8303
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
8220
8304
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
8221
8305
|
let latest = null;
|
|
8222
8306
|
for (const line of lines) {
|
|
@@ -8238,7 +8322,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
8238
8322
|
const pump = async () => {
|
|
8239
8323
|
let emitted = 0;
|
|
8240
8324
|
try {
|
|
8241
|
-
const content = await
|
|
8325
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
8242
8326
|
const lines = content.split("\n");
|
|
8243
8327
|
const completeLines = content.endsWith("\n") ? lines : lines.slice(0, -1);
|
|
8244
8328
|
for (const line of completeLines) {
|
|
@@ -9101,7 +9185,7 @@ var ChatService = class {
|
|
|
9101
9185
|
}
|
|
9102
9186
|
async readSenders(chatId) {
|
|
9103
9187
|
try {
|
|
9104
|
-
const content = await
|
|
9188
|
+
const content = await readFile9(this.senderFilePath(chatId), "utf-8");
|
|
9105
9189
|
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
9106
9190
|
const senders = [];
|
|
9107
9191
|
for (const line of lines) {
|
|
@@ -9458,7 +9542,7 @@ var ChatService = class {
|
|
|
9458
9542
|
}
|
|
9459
9543
|
async loadChats() {
|
|
9460
9544
|
try {
|
|
9461
|
-
const content = await
|
|
9545
|
+
const content = await readFile9(CHATS_FILE, "utf-8");
|
|
9462
9546
|
return parsePersistedChatsContent(content);
|
|
9463
9547
|
} catch (error) {
|
|
9464
9548
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -9473,7 +9557,7 @@ var ChatService = class {
|
|
|
9473
9557
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
9474
9558
|
}
|
|
9475
9559
|
try {
|
|
9476
|
-
const backupContent = await
|
|
9560
|
+
const backupContent = await readFile9(CHATS_BACKUP_FILE, "utf-8");
|
|
9477
9561
|
return parsePersistedChatsContent(backupContent);
|
|
9478
9562
|
} catch (backupError) {
|
|
9479
9563
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -9548,7 +9632,7 @@ var ChatService = class {
|
|
|
9548
9632
|
|
|
9549
9633
|
// src/services/repo-file-service.ts
|
|
9550
9634
|
import { execFile as execFile2 } from "child_process";
|
|
9551
|
-
import { readFile as
|
|
9635
|
+
import { readFile as readFile10, realpath, stat as stat3 } from "fs/promises";
|
|
9552
9636
|
import { join as join16, resolve, extname } from "path";
|
|
9553
9637
|
var CACHE_TTL_MS = 3e4;
|
|
9554
9638
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
@@ -9738,7 +9822,7 @@ var RepoFileService = class {
|
|
|
9738
9822
|
tooLarge: true
|
|
9739
9823
|
};
|
|
9740
9824
|
}
|
|
9741
|
-
const content = await
|
|
9825
|
+
const content = await readFile10(fullPath, "utf-8");
|
|
9742
9826
|
return {
|
|
9743
9827
|
repoName,
|
|
9744
9828
|
path: filePath,
|
|
@@ -9820,11 +9904,11 @@ var RepoFileService = class {
|
|
|
9820
9904
|
// src/v1-routes.ts
|
|
9821
9905
|
import { Hono } from "hono";
|
|
9822
9906
|
import { z as z2 } from "zod";
|
|
9823
|
-
import { readdir as readdir6, stat as stat4, readFile as
|
|
9907
|
+
import { readdir as readdir6, stat as stat4, readFile as readFile14 } from "fs/promises";
|
|
9824
9908
|
import { join as join20, resolve as resolve2 } from "path";
|
|
9825
9909
|
|
|
9826
9910
|
// src/services/plan-service.ts
|
|
9827
|
-
import { readdir as readdir4, readFile as
|
|
9911
|
+
import { readdir as readdir4, readFile as readFile11 } from "fs/promises";
|
|
9828
9912
|
import { homedir as homedir14 } from "os";
|
|
9829
9913
|
import { basename, join as join17 } from "path";
|
|
9830
9914
|
var PLAN_DIRECTORIES = [
|
|
@@ -9865,7 +9949,7 @@ var PlanService = class {
|
|
|
9865
9949
|
for (const directory of PLAN_DIRECTORIES) {
|
|
9866
9950
|
const filePath = join17(directory, safeFilename);
|
|
9867
9951
|
try {
|
|
9868
|
-
const content = await
|
|
9952
|
+
const content = await readFile11(filePath, "utf-8");
|
|
9869
9953
|
return { filename: safeFilename, content };
|
|
9870
9954
|
} catch {
|
|
9871
9955
|
}
|
|
@@ -9876,13 +9960,13 @@ var PlanService = class {
|
|
|
9876
9960
|
var planService = new PlanService();
|
|
9877
9961
|
|
|
9878
9962
|
// src/services/warm-hooks-service.ts
|
|
9879
|
-
import { spawn as
|
|
9880
|
-
import { readFile as
|
|
9963
|
+
import { spawn as spawn4 } from "child_process";
|
|
9964
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
9881
9965
|
import { existsSync as existsSync9 } from "fs";
|
|
9882
9966
|
import { join as join19 } from "path";
|
|
9883
9967
|
|
|
9884
9968
|
// src/services/warm-hook-logs-service.ts
|
|
9885
|
-
import { mkdir as mkdir12, readFile as
|
|
9969
|
+
import { mkdir as mkdir12, readFile as readFile12, writeFile as writeFile7, readdir as readdir5, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
|
|
9886
9970
|
import { homedir as homedir15 } from "os";
|
|
9887
9971
|
import { join as join18 } from "path";
|
|
9888
9972
|
var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
|
|
@@ -9942,7 +10026,7 @@ var WarmHookLogsService = class {
|
|
|
9942
10026
|
continue;
|
|
9943
10027
|
}
|
|
9944
10028
|
try {
|
|
9945
|
-
const raw = await
|
|
10029
|
+
const raw = await readFile12(join18(LOGS_DIR2, file), "utf-8");
|
|
9946
10030
|
const stored = JSON.parse(raw);
|
|
9947
10031
|
logs.push(withPreview2(stored));
|
|
9948
10032
|
} catch {
|
|
@@ -9971,7 +10055,7 @@ var WarmHookLogsService = class {
|
|
|
9971
10055
|
}
|
|
9972
10056
|
async getCurrentRunLog() {
|
|
9973
10057
|
try {
|
|
9974
|
-
return await
|
|
10058
|
+
return await readFile12(CURRENT_RUN_LOG, "utf-8");
|
|
9975
10059
|
} catch (err) {
|
|
9976
10060
|
if (err.code === "ENOENT") return null;
|
|
9977
10061
|
throw err;
|
|
@@ -9980,7 +10064,7 @@ var WarmHookLogsService = class {
|
|
|
9980
10064
|
async getFullOutput(hookType, hookName) {
|
|
9981
10065
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
9982
10066
|
try {
|
|
9983
|
-
const raw = await
|
|
10067
|
+
const raw = await readFile12(join18(LOGS_DIR2, filename), "utf-8");
|
|
9984
10068
|
const stored = JSON.parse(raw);
|
|
9985
10069
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
9986
10070
|
return null;
|
|
@@ -10004,7 +10088,7 @@ async function readRepoWarmHook(repoPath) {
|
|
|
10004
10088
|
continue;
|
|
10005
10089
|
}
|
|
10006
10090
|
try {
|
|
10007
|
-
const raw = await
|
|
10091
|
+
const raw = await readFile13(configPath, "utf-8");
|
|
10008
10092
|
const config = parseReplicasConfigString(raw, filename);
|
|
10009
10093
|
if (!config.warmHook) {
|
|
10010
10094
|
return null;
|
|
@@ -10051,7 +10135,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
10051
10135
|
params.onChunk(`$ ${params.label}
|
|
10052
10136
|
`);
|
|
10053
10137
|
return new Promise((resolve3) => {
|
|
10054
|
-
const proc =
|
|
10138
|
+
const proc = spawn4("bash", ["-lc", params.content], {
|
|
10055
10139
|
cwd: params.cwd,
|
|
10056
10140
|
env: process.env,
|
|
10057
10141
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -10440,6 +10524,10 @@ function createV1Routes(deps) {
|
|
|
10440
10524
|
app2.post("/chats/:chatId/messages", async (c) => {
|
|
10441
10525
|
try {
|
|
10442
10526
|
const body = sendMessageSchema.parse(await c.req.json());
|
|
10527
|
+
const goalValidationError = getGoalCommandObjectiveValidationError(body.message);
|
|
10528
|
+
if (goalValidationError) {
|
|
10529
|
+
return c.json(jsonError(goalValidationError), 400);
|
|
10530
|
+
}
|
|
10443
10531
|
const result = await deps.chatService.sendMessage(c.req.param("chatId"), body);
|
|
10444
10532
|
return c.json(result);
|
|
10445
10533
|
} catch (error) {
|
|
@@ -10934,7 +11022,7 @@ function createV1Routes(deps) {
|
|
|
10934
11022
|
const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
|
|
10935
11023
|
let content;
|
|
10936
11024
|
try {
|
|
10937
|
-
content = await
|
|
11025
|
+
content = await readFile14(filePath, "utf-8");
|
|
10938
11026
|
} catch {
|
|
10939
11027
|
return c.json(jsonError("Log session not found"), 404);
|
|
10940
11028
|
}
|
|
@@ -10974,10 +11062,10 @@ process.on("unhandledRejection", (reason) => {
|
|
|
10974
11062
|
engineLogger.flush().finally(() => process.exit(1));
|
|
10975
11063
|
});
|
|
10976
11064
|
await eventService.initialize();
|
|
10977
|
-
function checkActiveSSHSessions() {
|
|
11065
|
+
async function checkActiveSSHSessions() {
|
|
10978
11066
|
try {
|
|
10979
|
-
const
|
|
10980
|
-
const sessionCount = parseInt(
|
|
11067
|
+
const { stdout } = await execAsync('who | grep -v "^$" | wc -l', { encoding: "utf-8" });
|
|
11068
|
+
const sessionCount = parseInt(stdout.trim(), 10);
|
|
10981
11069
|
return sessionCount > 0;
|
|
10982
11070
|
} catch {
|
|
10983
11071
|
return false;
|
|
@@ -11010,10 +11098,13 @@ app.get("/health", (c) => {
|
|
|
11010
11098
|
app.use("*", authMiddleware);
|
|
11011
11099
|
app.get("/status", async (c) => {
|
|
11012
11100
|
try {
|
|
11013
|
-
const repos = await
|
|
11101
|
+
const [repos, hasActiveSSHSessions] = await Promise.all([
|
|
11102
|
+
gitService.listRepos(),
|
|
11103
|
+
checkActiveSSHSessions()
|
|
11104
|
+
]);
|
|
11014
11105
|
const status = {
|
|
11015
11106
|
uptimeSeconds: Math.floor((Date.now() - bootTimeMs) / 1e3),
|
|
11016
|
-
hasActiveSSHSessions
|
|
11107
|
+
hasActiveSSHSessions,
|
|
11017
11108
|
activeSseClients: eventService.getSubscriberCount(),
|
|
11018
11109
|
chatsTotal: chatService.listChats().length,
|
|
11019
11110
|
chatsProcessing: chatService.getProcessingCount(),
|
|
@@ -11043,8 +11134,16 @@ function startStatusBroadcaster() {
|
|
|
11043
11134
|
let previousHookStatus = "";
|
|
11044
11135
|
let previousEngineStatus = "";
|
|
11045
11136
|
let lastHooksRunning = replicasConfigService.areHooksRunning();
|
|
11046
|
-
|
|
11047
|
-
|
|
11137
|
+
const BROADCAST_INTERVAL_MS = 2e3;
|
|
11138
|
+
const scheduleNext = () => {
|
|
11139
|
+
setTimeout(tick, BROADCAST_INTERVAL_MS);
|
|
11140
|
+
};
|
|
11141
|
+
const tick = async () => {
|
|
11142
|
+
try {
|
|
11143
|
+
const [repos, hasActiveSSHSessions] = await Promise.all([
|
|
11144
|
+
gitService.listRepos(),
|
|
11145
|
+
checkActiveSSHSessions()
|
|
11146
|
+
]);
|
|
11048
11147
|
const serialized = JSON.stringify(repos);
|
|
11049
11148
|
if (serialized !== previousRepoStatus) {
|
|
11050
11149
|
previousRepoStatus = serialized;
|
|
@@ -11058,7 +11157,7 @@ function startStatusBroadcaster() {
|
|
|
11058
11157
|
}
|
|
11059
11158
|
const engineStatus = {
|
|
11060
11159
|
uptimeSeconds: Math.floor((Date.now() - bootTimeMs) / 1e3),
|
|
11061
|
-
hasActiveSSHSessions
|
|
11160
|
+
hasActiveSSHSessions,
|
|
11062
11161
|
activeSseClients: eventService.getSubscriberCount(),
|
|
11063
11162
|
chatsTotal: chatService.listChats().length,
|
|
11064
11163
|
chatsProcessing: chatService.getProcessingCount(),
|
|
@@ -11076,67 +11175,70 @@ function startStatusBroadcaster() {
|
|
|
11076
11175
|
}).catch(() => {
|
|
11077
11176
|
});
|
|
11078
11177
|
}
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
}
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
}
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
}
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11178
|
+
const hooksRunning = replicasConfigService.areHooksRunning();
|
|
11179
|
+
const hooksCompleted = replicasConfigService.areHooksCompleted();
|
|
11180
|
+
const hooksFailed = replicasConfigService.didHooksFail();
|
|
11181
|
+
const hookSnapshot = JSON.stringify({
|
|
11182
|
+
running: hooksRunning,
|
|
11183
|
+
completed: hooksCompleted,
|
|
11184
|
+
failed: hooksFailed
|
|
11185
|
+
});
|
|
11186
|
+
if (hookSnapshot !== previousHookStatus) {
|
|
11187
|
+
previousHookStatus = hookSnapshot;
|
|
11188
|
+
if (!lastHooksRunning && hooksRunning) {
|
|
11189
|
+
eventService.publish({
|
|
11190
|
+
id: randomUUID6(),
|
|
11191
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11192
|
+
type: "hooks.started",
|
|
11193
|
+
payload: { running: true, completed: false }
|
|
11194
|
+
}).catch(() => {
|
|
11195
|
+
});
|
|
11196
|
+
}
|
|
11197
|
+
if (hooksRunning) {
|
|
11198
|
+
eventService.publish({
|
|
11199
|
+
id: randomUUID6(),
|
|
11200
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11201
|
+
type: "hooks.progress",
|
|
11202
|
+
payload: { running: true, completed: false }
|
|
11203
|
+
}).catch(() => {
|
|
11204
|
+
});
|
|
11205
|
+
}
|
|
11206
|
+
if (lastHooksRunning && !hooksRunning && hooksCompleted && !hooksFailed) {
|
|
11207
|
+
eventService.publish({
|
|
11208
|
+
id: randomUUID6(),
|
|
11209
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11210
|
+
type: "hooks.completed",
|
|
11211
|
+
payload: { running: false, completed: true }
|
|
11212
|
+
}).catch(() => {
|
|
11213
|
+
});
|
|
11214
|
+
}
|
|
11215
|
+
if (lastHooksRunning && !hooksRunning && hooksFailed) {
|
|
11216
|
+
eventService.publish({
|
|
11217
|
+
id: randomUUID6(),
|
|
11218
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11219
|
+
type: "hooks.failed",
|
|
11220
|
+
payload: { running: false, completed: hooksCompleted }
|
|
11221
|
+
}).catch(() => {
|
|
11222
|
+
});
|
|
11223
|
+
}
|
|
11119
11224
|
eventService.publish({
|
|
11120
11225
|
id: randomUUID6(),
|
|
11121
11226
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11122
|
-
type: "hooks.
|
|
11123
|
-
payload: {
|
|
11227
|
+
type: "hooks.status",
|
|
11228
|
+
payload: {
|
|
11229
|
+
running: hooksRunning,
|
|
11230
|
+
completed: hooksCompleted
|
|
11231
|
+
}
|
|
11124
11232
|
}).catch(() => {
|
|
11125
11233
|
});
|
|
11234
|
+
lastHooksRunning = hooksRunning;
|
|
11126
11235
|
}
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
type: "hooks.status",
|
|
11131
|
-
payload: {
|
|
11132
|
-
running: hooksRunning,
|
|
11133
|
-
completed: hooksCompleted
|
|
11134
|
-
}
|
|
11135
|
-
}).catch(() => {
|
|
11136
|
-
});
|
|
11137
|
-
lastHooksRunning = hooksRunning;
|
|
11236
|
+
} catch {
|
|
11237
|
+
} finally {
|
|
11238
|
+
scheduleNext();
|
|
11138
11239
|
}
|
|
11139
|
-
}
|
|
11240
|
+
};
|
|
11241
|
+
scheduleNext();
|
|
11140
11242
|
}
|
|
11141
11243
|
serve(
|
|
11142
11244
|
{
|