replicas-engine 0.1.257 → 0.1.258
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 +316 -226
- 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) {
|
|
@@ -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-03-
|
|
289
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-03-v3";
|
|
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).
|
|
@@ -2240,7 +2249,23 @@ function isGitHubUrl(url) {
|
|
|
2240
2249
|
return GITHUB_HOST_RE.test(url);
|
|
2241
2250
|
}
|
|
2242
2251
|
|
|
2252
|
+
// src/utils/exec.ts
|
|
2253
|
+
var execAsync = promisify(exec);
|
|
2254
|
+
var execFileAsync = promisify(execFile);
|
|
2255
|
+
var SUBPROCESS_MAX_BUFFER = HOOK_EXEC_MAX_BUFFER_BYTES;
|
|
2256
|
+
|
|
2257
|
+
// src/managers/github-token-manager.ts
|
|
2258
|
+
import { promises as fs } from "fs";
|
|
2259
|
+
import path from "path";
|
|
2260
|
+
|
|
2261
|
+
// src/engine-env.ts
|
|
2262
|
+
import { homedir as homedir2 } from "os";
|
|
2263
|
+
import { join as join2 } from "path";
|
|
2264
|
+
|
|
2243
2265
|
// src/runtime-env-loader.ts
|
|
2266
|
+
import { readFileSync } from "fs";
|
|
2267
|
+
import { homedir } from "os";
|
|
2268
|
+
import { join } from "path";
|
|
2244
2269
|
function loadRuntimeEnvFile() {
|
|
2245
2270
|
let content;
|
|
2246
2271
|
try {
|
|
@@ -2736,9 +2761,9 @@ var CodexTokenManager = class extends BaseRefreshManager {
|
|
|
2736
2761
|
var codexTokenManager = new CodexTokenManager();
|
|
2737
2762
|
|
|
2738
2763
|
// src/git/service.ts
|
|
2739
|
-
import { readdir, stat } from "fs/promises";
|
|
2740
|
-
import { existsSync as existsSync2,
|
|
2741
|
-
import {
|
|
2764
|
+
import { readdir, readFile as readFile2, stat } from "fs/promises";
|
|
2765
|
+
import { existsSync as existsSync2, unlinkSync } from "fs";
|
|
2766
|
+
import { spawn } from "child_process";
|
|
2742
2767
|
import { join as join5 } from "path";
|
|
2743
2768
|
|
|
2744
2769
|
// src/utils/state.ts
|
|
@@ -2869,15 +2894,15 @@ async function saveRepoState(repoName, state, fallbackState) {
|
|
|
2869
2894
|
}
|
|
2870
2895
|
|
|
2871
2896
|
// src/git/commands.ts
|
|
2872
|
-
import { execFileSync } from "child_process";
|
|
2873
2897
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2874
2898
|
import { join as join4 } from "path";
|
|
2875
|
-
function runGitCommand(args, cwd) {
|
|
2876
|
-
|
|
2899
|
+
async function runGitCommand(args, cwd, options = {}) {
|
|
2900
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
2877
2901
|
cwd,
|
|
2878
2902
|
encoding: "utf-8",
|
|
2879
|
-
|
|
2880
|
-
})
|
|
2903
|
+
maxBuffer: options.maxBuffer ?? SUBPROCESS_MAX_BUFFER
|
|
2904
|
+
});
|
|
2905
|
+
return stdout.trim();
|
|
2881
2906
|
}
|
|
2882
2907
|
function readRepoHeadBranch(repoPath) {
|
|
2883
2908
|
try {
|
|
@@ -2888,17 +2913,17 @@ function readRepoHeadBranch(repoPath) {
|
|
|
2888
2913
|
return null;
|
|
2889
2914
|
}
|
|
2890
2915
|
}
|
|
2891
|
-
function branchExists(branchName, cwd) {
|
|
2916
|
+
async function branchExists(branchName, cwd) {
|
|
2892
2917
|
try {
|
|
2893
|
-
runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
2918
|
+
await runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
2894
2919
|
return true;
|
|
2895
2920
|
} catch {
|
|
2896
2921
|
return false;
|
|
2897
2922
|
}
|
|
2898
2923
|
}
|
|
2899
|
-
function getCurrentBranch(cwd) {
|
|
2924
|
+
async function getCurrentBranch(cwd) {
|
|
2900
2925
|
try {
|
|
2901
|
-
return runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
2926
|
+
return await runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
2902
2927
|
} catch (error) {
|
|
2903
2928
|
console.error("Error getting current branch:", error);
|
|
2904
2929
|
return null;
|
|
@@ -2906,6 +2931,7 @@ function getCurrentBranch(cwd) {
|
|
|
2906
2931
|
}
|
|
2907
2932
|
|
|
2908
2933
|
// src/git/service.ts
|
|
2934
|
+
var FULL_DIFF_MAX_BUFFER = 50 * 1024 * 1024;
|
|
2909
2935
|
function appendUniqueUrl(urls, url) {
|
|
2910
2936
|
return urls.includes(url) ? urls : [...urls, url];
|
|
2911
2937
|
}
|
|
@@ -2915,6 +2941,10 @@ var GitService = class {
|
|
|
2915
2941
|
// No invalidation on purpose — `git remote set-url` mid-session is rare and
|
|
2916
2942
|
// the worst case is `gh pr view` stays skipped until the next engine restart.
|
|
2917
2943
|
originIsGitHubCache = /* @__PURE__ */ new Map();
|
|
2944
|
+
// Broadcaster + UI /repos requests fan in here every ~2s; one shared run.
|
|
2945
|
+
listReposInFlight = new InFlightMap();
|
|
2946
|
+
diffStatsInFlight = new InFlightMap();
|
|
2947
|
+
fullDiffInFlight = new InFlightMap();
|
|
2918
2948
|
getWorkspaceRoot() {
|
|
2919
2949
|
return ENGINE_ENV.WORKSPACE_ROOT;
|
|
2920
2950
|
}
|
|
@@ -2940,7 +2970,7 @@ var GitService = class {
|
|
|
2940
2970
|
repos.push({
|
|
2941
2971
|
name: entry,
|
|
2942
2972
|
path: fullPath,
|
|
2943
|
-
defaultBranch: this.resolveDefaultBranch(fullPath)
|
|
2973
|
+
defaultBranch: await this.resolveDefaultBranch(fullPath)
|
|
2944
2974
|
});
|
|
2945
2975
|
} catch {
|
|
2946
2976
|
}
|
|
@@ -2949,13 +2979,21 @@ var GitService = class {
|
|
|
2949
2979
|
}
|
|
2950
2980
|
async listRepos(options) {
|
|
2951
2981
|
const includeDiffs = options?.includeDiffs === true;
|
|
2982
|
+
const key = includeDiffs ? "diffs" : "base";
|
|
2983
|
+
return this.listReposInFlight.run(key, () => this.computeListRepos(includeDiffs));
|
|
2984
|
+
}
|
|
2985
|
+
async computeListRepos(includeDiffs) {
|
|
2952
2986
|
const repos = await this.listRepositories();
|
|
2953
2987
|
const states = [];
|
|
2954
2988
|
for (const repo of repos) {
|
|
2955
2989
|
try {
|
|
2956
|
-
const persistedState = await
|
|
2957
|
-
|
|
2958
|
-
|
|
2990
|
+
const [persistedState, currentBranchRaw, gitDiff] = await Promise.all([
|
|
2991
|
+
loadRepoState(repo.name),
|
|
2992
|
+
getCurrentBranch(repo.path),
|
|
2993
|
+
this.getGitDiffStats(repo.path, repo.defaultBranch)
|
|
2994
|
+
]);
|
|
2995
|
+
const currentBranch = currentBranchRaw ?? repo.defaultBranch;
|
|
2996
|
+
const fullDiff = includeDiffs && gitDiff ? await this.getFullGitDiff(repo.path, repo.defaultBranch) : void 0;
|
|
2959
2997
|
states.push({
|
|
2960
2998
|
name: repo.name,
|
|
2961
2999
|
path: repo.path,
|
|
@@ -2963,7 +3001,7 @@ var GitService = class {
|
|
|
2963
3001
|
currentBranch,
|
|
2964
3002
|
prUrls: persistedState?.prUrls ?? [],
|
|
2965
3003
|
// fullDiff may be empty if the diff subprocess fails.
|
|
2966
|
-
gitDiff: includeDiffs && gitDiff ? { ...gitDiff, fullDiff:
|
|
3004
|
+
gitDiff: includeDiffs && gitDiff ? { ...gitDiff, fullDiff: fullDiff ?? "" } : gitDiff,
|
|
2967
3005
|
startHooksCompleted: persistedState?.startHooksCompleted ?? false
|
|
2968
3006
|
});
|
|
2969
3007
|
} catch {
|
|
@@ -2977,7 +3015,7 @@ var GitService = class {
|
|
|
2977
3015
|
for (const repo of repos) {
|
|
2978
3016
|
try {
|
|
2979
3017
|
const persistedState = await loadRepoState(repo.name);
|
|
2980
|
-
const currentBranch = getCurrentBranch(repo.path) ?? repo.defaultBranch;
|
|
3018
|
+
const currentBranch = await getCurrentBranch(repo.path) ?? repo.defaultBranch;
|
|
2981
3019
|
const startHooksCompleted = persistedState?.startHooksCompleted ?? false;
|
|
2982
3020
|
const observed = observedBranchesByRepo?.get(repo.name);
|
|
2983
3021
|
states.push(
|
|
@@ -3042,12 +3080,12 @@ var GitService = class {
|
|
|
3042
3080
|
const persistedState = await loadRepoState(repo.name);
|
|
3043
3081
|
const persistedBranch = persistedState?.currentBranch;
|
|
3044
3082
|
if (!skipNetworkRefresh) {
|
|
3045
|
-
runGitCommand(["fetch", "--all", "--prune", `--filter=${GIT_PARTIAL_CLONE_FILTER}`], repo.path);
|
|
3083
|
+
await runGitCommand(["fetch", "--all", "--prune", `--filter=${GIT_PARTIAL_CLONE_FILTER}`], repo.path);
|
|
3046
3084
|
}
|
|
3047
|
-
if (persistedBranch && branchExists(persistedBranch, repo.path)) {
|
|
3048
|
-
const currentBranch = getCurrentBranch(repo.path);
|
|
3085
|
+
if (persistedBranch && await branchExists(persistedBranch, repo.path)) {
|
|
3086
|
+
const currentBranch = await getCurrentBranch(repo.path);
|
|
3049
3087
|
if (currentBranch !== persistedBranch) {
|
|
3050
|
-
runGitCommand(["checkout", persistedBranch], repo.path);
|
|
3088
|
+
await runGitCommand(["checkout", persistedBranch], repo.path);
|
|
3051
3089
|
}
|
|
3052
3090
|
results.push({
|
|
3053
3091
|
name: repo.name,
|
|
@@ -3057,15 +3095,15 @@ var GitService = class {
|
|
|
3057
3095
|
});
|
|
3058
3096
|
continue;
|
|
3059
3097
|
}
|
|
3060
|
-
runGitCommand(["checkout", repo.defaultBranch], repo.path);
|
|
3098
|
+
await runGitCommand(["checkout", repo.defaultBranch], repo.path);
|
|
3061
3099
|
if (!skipNetworkRefresh) {
|
|
3062
3100
|
try {
|
|
3063
|
-
runGitCommand(["pull", "--rebase", "--autostash"], repo.path);
|
|
3101
|
+
await runGitCommand(["pull", "--rebase", "--autostash"], repo.path);
|
|
3064
3102
|
} catch {
|
|
3065
3103
|
}
|
|
3066
3104
|
}
|
|
3067
|
-
const branchName = this.findAvailableBranchName(workspaceName, repo.path);
|
|
3068
|
-
runGitCommand(["checkout", "-b", branchName], repo.path);
|
|
3105
|
+
const branchName = await this.findAvailableBranchName(workspaceName, repo.path);
|
|
3106
|
+
await runGitCommand(["checkout", "-b", branchName], repo.path);
|
|
3069
3107
|
await saveRepoState(repo.name, { currentBranch: branchName }, baselineState);
|
|
3070
3108
|
results.push({
|
|
3071
3109
|
name: repo.name,
|
|
@@ -3103,109 +3141,149 @@ var GitService = class {
|
|
|
3103
3141
|
return true;
|
|
3104
3142
|
}
|
|
3105
3143
|
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
|
-
|
|
3144
|
+
return this.diffStatsInFlight.run(repoPath, async () => {
|
|
3145
|
+
try {
|
|
3146
|
+
const diffBase = await this.getDiffBase(repoPath, defaultBranch);
|
|
3147
|
+
const shortstat = await runGitCommand(["diff", diffBase, "--shortstat", "-M"], repoPath);
|
|
3148
|
+
let added = 0;
|
|
3149
|
+
let removed = 0;
|
|
3150
|
+
const addedMatch = shortstat.match(/(\d+) insertion/);
|
|
3151
|
+
const removedMatch = shortstat.match(/(\d+) deletion/);
|
|
3152
|
+
if (addedMatch) {
|
|
3153
|
+
added = parseInt(addedMatch[1], 10);
|
|
3154
|
+
}
|
|
3155
|
+
if (removedMatch) {
|
|
3156
|
+
removed = parseInt(removedMatch[1], 10);
|
|
3157
|
+
}
|
|
3158
|
+
added += await this.countUntrackedAddedLines(repoPath);
|
|
3159
|
+
return {
|
|
3160
|
+
added,
|
|
3161
|
+
removed
|
|
3162
|
+
};
|
|
3163
|
+
} catch (error) {
|
|
3164
|
+
console.error("Error getting git diff:", error);
|
|
3165
|
+
return null;
|
|
3166
|
+
}
|
|
3167
|
+
});
|
|
3128
3168
|
}
|
|
3129
|
-
listUntrackedPaths(repoPath) {
|
|
3130
|
-
const
|
|
3169
|
+
async listUntrackedPaths(repoPath) {
|
|
3170
|
+
const { stdout } = await execFileAsync("git", ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
3131
3171
|
cwd: repoPath,
|
|
3132
3172
|
encoding: "utf-8",
|
|
3133
|
-
|
|
3173
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3134
3174
|
});
|
|
3135
|
-
return
|
|
3175
|
+
return stdout.split("\0").filter(Boolean);
|
|
3136
3176
|
}
|
|
3137
|
-
getUntrackedDiff(repoPath) {
|
|
3138
|
-
const paths = this.listUntrackedPaths(repoPath);
|
|
3177
|
+
async getUntrackedDiff(repoPath) {
|
|
3178
|
+
const paths = await this.listUntrackedPaths(repoPath);
|
|
3139
3179
|
if (paths.length === 0) return "";
|
|
3140
3180
|
try {
|
|
3141
|
-
|
|
3142
|
-
const result = spawnSync("git", ["diff", "--", ...paths], {
|
|
3181
|
+
await execFileAsync("git", ["add", "--intent-to-add", "--", ...paths], {
|
|
3143
3182
|
cwd: repoPath,
|
|
3144
|
-
|
|
3145
|
-
maxBuffer: 50 * 1024 * 1024
|
|
3183
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3146
3184
|
});
|
|
3147
|
-
return
|
|
3185
|
+
return await this.readUntrackedDiff(repoPath, paths);
|
|
3148
3186
|
} finally {
|
|
3149
3187
|
try {
|
|
3150
|
-
|
|
3188
|
+
await execFileAsync("git", ["reset", "--", ...paths], {
|
|
3189
|
+
cwd: repoPath,
|
|
3190
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3191
|
+
});
|
|
3151
3192
|
} catch {
|
|
3152
3193
|
}
|
|
3153
3194
|
}
|
|
3154
3195
|
}
|
|
3155
|
-
|
|
3196
|
+
readUntrackedDiff(repoPath, paths) {
|
|
3197
|
+
return new Promise((resolve3) => {
|
|
3198
|
+
const child = spawn("git", ["diff", "--", ...paths], {
|
|
3199
|
+
cwd: repoPath,
|
|
3200
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3201
|
+
});
|
|
3202
|
+
const chunks = [];
|
|
3203
|
+
let total = 0;
|
|
3204
|
+
let truncated = false;
|
|
3205
|
+
child.stdout.on("data", (chunk) => {
|
|
3206
|
+
if (truncated) return;
|
|
3207
|
+
total += chunk.length;
|
|
3208
|
+
if (total > FULL_DIFF_MAX_BUFFER) {
|
|
3209
|
+
truncated = true;
|
|
3210
|
+
chunks.push(chunk.subarray(0, Math.max(0, FULL_DIFF_MAX_BUFFER - (total - chunk.length))));
|
|
3211
|
+
child.kill("SIGTERM");
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
chunks.push(chunk);
|
|
3215
|
+
});
|
|
3216
|
+
child.stderr.on("data", () => {
|
|
3217
|
+
});
|
|
3218
|
+
child.on("error", () => resolve3(""));
|
|
3219
|
+
child.on("close", (code) => {
|
|
3220
|
+
if (code === 0 || code === 1 || truncated) {
|
|
3221
|
+
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
3222
|
+
} else {
|
|
3223
|
+
resolve3("");
|
|
3224
|
+
}
|
|
3225
|
+
});
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3228
|
+
async countUntrackedAddedLines(repoPath) {
|
|
3156
3229
|
try {
|
|
3157
|
-
|
|
3230
|
+
const paths = await this.listUntrackedPaths(repoPath);
|
|
3231
|
+
let total = 0;
|
|
3232
|
+
for (const path4 of paths) {
|
|
3158
3233
|
try {
|
|
3159
|
-
const contents =
|
|
3234
|
+
const contents = await readFile2(join5(repoPath, path4));
|
|
3160
3235
|
if (contents.length === 0 || contents.includes(0)) {
|
|
3161
|
-
|
|
3236
|
+
continue;
|
|
3162
3237
|
}
|
|
3163
3238
|
let lines = 0;
|
|
3164
3239
|
for (const byte of contents) {
|
|
3165
3240
|
if (byte === 10) lines += 1;
|
|
3166
3241
|
}
|
|
3167
|
-
|
|
3242
|
+
total += lines + (contents[contents.length - 1] === 10 ? 0 : 1);
|
|
3168
3243
|
} catch {
|
|
3169
|
-
return total;
|
|
3170
3244
|
}
|
|
3171
|
-
}
|
|
3245
|
+
}
|
|
3246
|
+
return total;
|
|
3172
3247
|
} catch (error) {
|
|
3173
3248
|
console.error("Error counting untracked added lines:", error);
|
|
3174
3249
|
return 0;
|
|
3175
3250
|
}
|
|
3176
3251
|
}
|
|
3177
3252
|
getFullGitDiff(repoPath, defaultBranch) {
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3253
|
+
return this.fullDiffInFlight.run(repoPath, async () => {
|
|
3254
|
+
try {
|
|
3255
|
+
const diffBase = await this.getDiffBase(repoPath, defaultBranch);
|
|
3256
|
+
const { stdout: trackedDiff } = await execFileAsync("git", ["diff", diffBase, "-M", "-C"], {
|
|
3257
|
+
cwd: repoPath,
|
|
3258
|
+
encoding: "utf-8",
|
|
3259
|
+
maxBuffer: FULL_DIFF_MAX_BUFFER
|
|
3260
|
+
});
|
|
3261
|
+
const untrackedDiff = await this.getUntrackedAsDiff(repoPath);
|
|
3262
|
+
return trackedDiff + untrackedDiff;
|
|
3263
|
+
} catch {
|
|
3264
|
+
return "";
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3189
3267
|
}
|
|
3190
|
-
getUntrackedAsDiff(repoPath) {
|
|
3268
|
+
async getUntrackedAsDiff(repoPath) {
|
|
3191
3269
|
try {
|
|
3192
|
-
return this.getUntrackedDiff(repoPath);
|
|
3270
|
+
return await this.getUntrackedDiff(repoPath);
|
|
3193
3271
|
} catch (error) {
|
|
3194
3272
|
console.error("Error building untracked diff:", error);
|
|
3195
3273
|
return "";
|
|
3196
3274
|
}
|
|
3197
3275
|
}
|
|
3198
|
-
getDiffBase(repoPath, defaultBranch) {
|
|
3276
|
+
async getDiffBase(repoPath, defaultBranch) {
|
|
3199
3277
|
const baseBranch = `origin/${defaultBranch}`;
|
|
3200
3278
|
try {
|
|
3201
|
-
return runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3279
|
+
return await runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3202
3280
|
} catch {
|
|
3203
3281
|
return baseBranch;
|
|
3204
3282
|
}
|
|
3205
3283
|
}
|
|
3206
3284
|
async getPullRequestUrl(repoName, repoPath, currentBranchArg, persistedRepoStateArg) {
|
|
3207
3285
|
try {
|
|
3208
|
-
const currentBranch = currentBranchArg ?? getCurrentBranch(repoPath);
|
|
3286
|
+
const currentBranch = currentBranchArg ?? await getCurrentBranch(repoPath);
|
|
3209
3287
|
if (!currentBranch) {
|
|
3210
3288
|
return { status: "not_found" };
|
|
3211
3289
|
}
|
|
@@ -3215,7 +3293,7 @@ var GitService = class {
|
|
|
3215
3293
|
}
|
|
3216
3294
|
const persistedRepoState = persistedRepoStateArg ?? await loadRepoState(repoName);
|
|
3217
3295
|
this.cachedPrByRepo.delete(repoName);
|
|
3218
|
-
const result = this.lookupPrOnRemote(repoName, repoPath, currentBranch);
|
|
3296
|
+
const result = await this.lookupPrOnRemote(repoName, repoPath, currentBranch);
|
|
3219
3297
|
if (result.status === "found") {
|
|
3220
3298
|
this.cachedPrByRepo.set(repoName, { prUrl: result.url, currentBranch });
|
|
3221
3299
|
if (persistedRepoState && !persistedRepoState.prUrls.includes(result.url)) {
|
|
@@ -3232,28 +3310,29 @@ var GitService = class {
|
|
|
3232
3310
|
return { status: "error" };
|
|
3233
3311
|
}
|
|
3234
3312
|
}
|
|
3235
|
-
lookupPrOnRemote(repoName, repoPath, branch) {
|
|
3236
|
-
if (!this.originIsGitHub(repoPath)) {
|
|
3313
|
+
async lookupPrOnRemote(repoName, repoPath, branch) {
|
|
3314
|
+
if (!await this.originIsGitHub(repoPath)) {
|
|
3237
3315
|
return { status: "not_found" };
|
|
3238
3316
|
}
|
|
3239
3317
|
try {
|
|
3240
|
-
const
|
|
3318
|
+
const { stdout } = await execFileAsync("git", ["ls-remote", "--heads", "origin", branch], {
|
|
3241
3319
|
cwd: repoPath,
|
|
3242
3320
|
encoding: "utf-8",
|
|
3243
|
-
|
|
3244
|
-
})
|
|
3245
|
-
if (!
|
|
3321
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3322
|
+
});
|
|
3323
|
+
if (!stdout.trim()) {
|
|
3246
3324
|
return { status: "not_found" };
|
|
3247
3325
|
}
|
|
3248
3326
|
} catch {
|
|
3249
3327
|
return { status: "error" };
|
|
3250
3328
|
}
|
|
3251
3329
|
try {
|
|
3252
|
-
const
|
|
3330
|
+
const { stdout } = await execFileAsync("gh", ["pr", "view", branch, "--json", "url", "--jq", ".url"], {
|
|
3253
3331
|
cwd: repoPath,
|
|
3254
3332
|
encoding: "utf-8",
|
|
3255
|
-
|
|
3256
|
-
})
|
|
3333
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3334
|
+
});
|
|
3335
|
+
const prInfo = stdout.trim();
|
|
3257
3336
|
return prInfo ? { status: "found", url: prInfo } : { status: "not_found" };
|
|
3258
3337
|
} catch (error) {
|
|
3259
3338
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3261,31 +3340,31 @@ var GitService = class {
|
|
|
3261
3340
|
return { status: "error" };
|
|
3262
3341
|
}
|
|
3263
3342
|
}
|
|
3264
|
-
originIsGitHub(repoPath) {
|
|
3343
|
+
async originIsGitHub(repoPath) {
|
|
3265
3344
|
const cached = this.originIsGitHubCache.get(repoPath);
|
|
3266
3345
|
if (cached !== void 0) {
|
|
3267
3346
|
return cached;
|
|
3268
3347
|
}
|
|
3269
3348
|
let isGitHub = false;
|
|
3270
3349
|
try {
|
|
3271
|
-
const
|
|
3350
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
|
|
3272
3351
|
cwd: repoPath,
|
|
3273
3352
|
encoding: "utf-8",
|
|
3274
|
-
|
|
3275
|
-
})
|
|
3276
|
-
isGitHub = isGitHubUrl(
|
|
3353
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3354
|
+
});
|
|
3355
|
+
isGitHub = isGitHubUrl(stdout.trim());
|
|
3277
3356
|
} catch {
|
|
3278
3357
|
isGitHub = false;
|
|
3279
3358
|
}
|
|
3280
3359
|
this.originIsGitHubCache.set(repoPath, isGitHub);
|
|
3281
3360
|
return isGitHub;
|
|
3282
3361
|
}
|
|
3283
|
-
resolveDefaultBranch(repoPath) {
|
|
3362
|
+
async resolveDefaultBranch(repoPath) {
|
|
3284
3363
|
const cached = this.defaultBranchCache.get(repoPath);
|
|
3285
3364
|
if (cached) {
|
|
3286
3365
|
return cached;
|
|
3287
3366
|
}
|
|
3288
|
-
const fromSymbolicRef = this.resolveDefaultBranchFromSymbolicRef(repoPath);
|
|
3367
|
+
const fromSymbolicRef = await this.resolveDefaultBranchFromSymbolicRef(repoPath);
|
|
3289
3368
|
if (fromSymbolicRef) {
|
|
3290
3369
|
this.defaultBranchCache.set(repoPath, fromSymbolicRef);
|
|
3291
3370
|
return fromSymbolicRef;
|
|
@@ -3294,18 +3373,18 @@ var GitService = class {
|
|
|
3294
3373
|
this.defaultBranchCache.set(repoPath, fallback);
|
|
3295
3374
|
return fallback;
|
|
3296
3375
|
}
|
|
3297
|
-
resolveDefaultBranchFromSymbolicRef(repoPath) {
|
|
3376
|
+
async resolveDefaultBranchFromSymbolicRef(repoPath) {
|
|
3298
3377
|
try {
|
|
3299
|
-
const output = runGitCommand(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoPath);
|
|
3378
|
+
const output = await runGitCommand(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoPath);
|
|
3300
3379
|
const match = output.match(/^origin\/(.+)$/);
|
|
3301
3380
|
return match ? match[1] : null;
|
|
3302
3381
|
} catch {
|
|
3303
3382
|
return null;
|
|
3304
3383
|
}
|
|
3305
3384
|
}
|
|
3306
|
-
findAvailableBranchName(baseName, cwd) {
|
|
3385
|
+
async findAvailableBranchName(baseName, cwd) {
|
|
3307
3386
|
const sanitizedBaseName = this.sanitizeBranchName(baseName);
|
|
3308
|
-
if (!branchExists(sanitizedBaseName, cwd)) {
|
|
3387
|
+
if (!await branchExists(sanitizedBaseName, cwd)) {
|
|
3309
3388
|
return sanitizedBaseName;
|
|
3310
3389
|
}
|
|
3311
3390
|
return `${sanitizedBaseName}-${Date.now()}`;
|
|
@@ -3323,7 +3402,7 @@ var GitService = class {
|
|
|
3323
3402
|
if (observedBranches) {
|
|
3324
3403
|
for (const branch of observedBranches) {
|
|
3325
3404
|
if (branch === currentBranch) continue;
|
|
3326
|
-
const branchResult = this.lookupPrOnRemote(repo.name, repo.path, branch);
|
|
3405
|
+
const branchResult = await this.lookupPrOnRemote(repo.name, repo.path, branch);
|
|
3327
3406
|
if (branchResult.status === "found") {
|
|
3328
3407
|
prUrls = appendUniqueUrl(prUrls, branchResult.url);
|
|
3329
3408
|
}
|
|
@@ -3335,7 +3414,7 @@ var GitService = class {
|
|
|
3335
3414
|
defaultBranch: repo.defaultBranch,
|
|
3336
3415
|
currentBranch,
|
|
3337
3416
|
prUrls,
|
|
3338
|
-
gitDiff: this.getGitDiffStats(repo.path, repo.defaultBranch),
|
|
3417
|
+
gitDiff: await this.getGitDiffStats(repo.path, repo.defaultBranch),
|
|
3339
3418
|
startHooksCompleted
|
|
3340
3419
|
};
|
|
3341
3420
|
await saveRepoState(repo.name, state, state);
|
|
@@ -3423,20 +3502,17 @@ var EngineLogger = class {
|
|
|
3423
3502
|
var engineLogger = new EngineLogger();
|
|
3424
3503
|
|
|
3425
3504
|
// src/services/replicas-config-service.ts
|
|
3426
|
-
import { readFile as
|
|
3505
|
+
import { readFile as readFile5, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
|
|
3427
3506
|
import { existsSync as existsSync4 } from "fs";
|
|
3428
3507
|
import { join as join9 } from "path";
|
|
3429
3508
|
import { homedir as homedir7 } from "os";
|
|
3430
|
-
import { spawn } from "child_process";
|
|
3509
|
+
import { spawn as spawn2 } from "child_process";
|
|
3431
3510
|
|
|
3432
3511
|
// src/services/environment-details-service.ts
|
|
3433
|
-
import { mkdir as mkdir3, readFile as
|
|
3512
|
+
import { mkdir as mkdir3, readFile as readFile3 } from "fs/promises";
|
|
3434
3513
|
import { existsSync as existsSync3 } from "fs";
|
|
3435
3514
|
import { homedir as homedir5 } from "os";
|
|
3436
3515
|
import { join as join7 } from "path";
|
|
3437
|
-
import { execFile } from "child_process";
|
|
3438
|
-
import { promisify } from "util";
|
|
3439
|
-
var execFileAsync = promisify(execFile);
|
|
3440
3516
|
var REPLICAS_DIR = join7(homedir5(), ".replicas");
|
|
3441
3517
|
var DETAILS_FILE = join7(REPLICAS_DIR, "environment-details.json");
|
|
3442
3518
|
var CLAUDE_CREDENTIALS_PATH = join7(homedir5(), ".claude", ".credentials.json");
|
|
@@ -3509,7 +3585,7 @@ async function readDetails() {
|
|
|
3509
3585
|
if (!existsSync3(DETAILS_FILE)) {
|
|
3510
3586
|
return createDefaultDetails();
|
|
3511
3587
|
}
|
|
3512
|
-
const raw = await
|
|
3588
|
+
const raw = await readFile3(DETAILS_FILE, "utf-8");
|
|
3513
3589
|
const parsed = JSON.parse(raw);
|
|
3514
3590
|
return { ...createDefaultDetails(), ...parsed };
|
|
3515
3591
|
} catch {
|
|
@@ -3603,7 +3679,7 @@ var EnvironmentDetailsService = class {
|
|
|
3603
3679
|
var environmentDetailsService = new EnvironmentDetailsService();
|
|
3604
3680
|
|
|
3605
3681
|
// src/services/start-hook-logs-service.ts
|
|
3606
|
-
import { mkdir as mkdir4, readFile as
|
|
3682
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
|
|
3607
3683
|
import { homedir as homedir6 } from "os";
|
|
3608
3684
|
import { join as join8 } from "path";
|
|
3609
3685
|
|
|
@@ -3680,7 +3756,7 @@ var StartHookLogsService = class {
|
|
|
3680
3756
|
continue;
|
|
3681
3757
|
}
|
|
3682
3758
|
try {
|
|
3683
|
-
const raw = await
|
|
3759
|
+
const raw = await readFile4(join8(LOGS_DIR, file), "utf-8");
|
|
3684
3760
|
const stored = normalizeStored(JSON.parse(raw));
|
|
3685
3761
|
if (stored) {
|
|
3686
3762
|
logs.push(withPreview(stored));
|
|
@@ -3699,7 +3775,7 @@ var StartHookLogsService = class {
|
|
|
3699
3775
|
async getFullOutput(hookType, hookName) {
|
|
3700
3776
|
const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
3701
3777
|
try {
|
|
3702
|
-
const raw = await
|
|
3778
|
+
const raw = await readFile4(join8(LOGS_DIR, filename), "utf-8");
|
|
3703
3779
|
const stored = normalizeStored(JSON.parse(raw));
|
|
3704
3780
|
if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
3705
3781
|
return null;
|
|
@@ -3731,7 +3807,7 @@ async function readReplicasConfigFromDir(dirPath) {
|
|
|
3731
3807
|
if (!existsSync4(configPath)) {
|
|
3732
3808
|
continue;
|
|
3733
3809
|
}
|
|
3734
|
-
const data = await
|
|
3810
|
+
const data = await readFile5(configPath, "utf-8");
|
|
3735
3811
|
const config = parseReplicasConfigString(data, filename);
|
|
3736
3812
|
return { config, filename };
|
|
3737
3813
|
}
|
|
@@ -3833,7 +3909,7 @@ var ReplicasConfigService = class {
|
|
|
3833
3909
|
emit(`$ ${params.label}
|
|
3834
3910
|
`);
|
|
3835
3911
|
return new Promise((resolve3) => {
|
|
3836
|
-
const proc =
|
|
3912
|
+
const proc = spawn2("bash", ["-lc", params.command], {
|
|
3837
3913
|
cwd: params.cwd,
|
|
3838
3914
|
env: process.env,
|
|
3839
3915
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4166,7 +4242,7 @@ var EventService = class {
|
|
|
4166
4242
|
var eventService = new EventService();
|
|
4167
4243
|
|
|
4168
4244
|
// src/services/preview-service.ts
|
|
4169
|
-
import { mkdir as mkdir7, readFile as
|
|
4245
|
+
import { mkdir as mkdir7, readFile as readFile6 } from "fs/promises";
|
|
4170
4246
|
import { existsSync as existsSync5 } from "fs";
|
|
4171
4247
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4172
4248
|
import { homedir as homedir9 } from "os";
|
|
@@ -4177,7 +4253,7 @@ async function readPreviewsFile() {
|
|
|
4177
4253
|
if (!existsSync5(PREVIEW_PORTS_FILE)) {
|
|
4178
4254
|
return { previews: [] };
|
|
4179
4255
|
}
|
|
4180
|
-
const raw = await
|
|
4256
|
+
const raw = await readFile6(PREVIEW_PORTS_FILE, "utf-8");
|
|
4181
4257
|
return JSON.parse(raw);
|
|
4182
4258
|
} catch {
|
|
4183
4259
|
return { previews: [] };
|
|
@@ -4282,7 +4358,7 @@ async function registerDesktopPreview() {
|
|
|
4282
4358
|
|
|
4283
4359
|
// src/services/chat/chat-service.ts
|
|
4284
4360
|
import { existsSync as existsSync8 } from "fs";
|
|
4285
|
-
import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as
|
|
4361
|
+
import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as readFile9, rename as rename2, rm } from "fs/promises";
|
|
4286
4362
|
import { homedir as homedir13 } from "os";
|
|
4287
4363
|
import { join as join15 } from "path";
|
|
4288
4364
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -4297,7 +4373,7 @@ import { mkdir as mkdir9, appendFile as appendFile4 } from "fs/promises";
|
|
|
4297
4373
|
import { homedir as homedir11 } from "os";
|
|
4298
4374
|
|
|
4299
4375
|
// src/utils/jsonl-reader.ts
|
|
4300
|
-
import { readFile as
|
|
4376
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
4301
4377
|
function isJsonlEvent(value) {
|
|
4302
4378
|
if (!isRecord4(value)) {
|
|
4303
4379
|
return false;
|
|
@@ -4319,7 +4395,7 @@ function parseJsonlEvents(lines) {
|
|
|
4319
4395
|
}
|
|
4320
4396
|
async function readJSONL(filePath) {
|
|
4321
4397
|
try {
|
|
4322
|
-
const content = await
|
|
4398
|
+
const content = await readFile7(filePath, "utf-8");
|
|
4323
4399
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
4324
4400
|
return parseJsonlEvents(lines);
|
|
4325
4401
|
} catch (error) {
|
|
@@ -6070,7 +6146,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
6070
6146
|
};
|
|
6071
6147
|
|
|
6072
6148
|
// src/managers/codex-asp/app-server-process.ts
|
|
6073
|
-
import { spawn as
|
|
6149
|
+
import { spawn as spawn3 } from "child_process";
|
|
6074
6150
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
6075
6151
|
|
|
6076
6152
|
// src/managers/codex-asp/asp-client.ts
|
|
@@ -6265,7 +6341,7 @@ var AspClient = class {
|
|
|
6265
6341
|
// src/managers/codex-asp/app-server-process.ts
|
|
6266
6342
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
6267
6343
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
6268
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6344
|
+
var ENGINE_PACKAGE_VERSION = "0.1.258";
|
|
6269
6345
|
var INITIALIZE_METHOD = "initialize";
|
|
6270
6346
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6271
6347
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -6292,7 +6368,7 @@ var AppServerProcess = class {
|
|
|
6292
6368
|
return { client: this.client };
|
|
6293
6369
|
}
|
|
6294
6370
|
this.shuttingDown = false;
|
|
6295
|
-
const child =
|
|
6371
|
+
const child = spawn3(this.binary, this.args, {
|
|
6296
6372
|
cwd: this.cwd,
|
|
6297
6373
|
env: this.env,
|
|
6298
6374
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6532,7 +6608,7 @@ function isCodexAuthError(error) {
|
|
|
6532
6608
|
}
|
|
6533
6609
|
|
|
6534
6610
|
// src/managers/codex-asp/mappers.ts
|
|
6535
|
-
import { existsSync as existsSync6, readFileSync as
|
|
6611
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
6536
6612
|
var localImageCache = /* @__PURE__ */ new Map();
|
|
6537
6613
|
var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
|
|
6538
6614
|
var THREAD_START_METHOD = "thread/start";
|
|
@@ -6613,7 +6689,7 @@ function userImageForLocalPath(path4) {
|
|
|
6613
6689
|
const image = {
|
|
6614
6690
|
type: "image",
|
|
6615
6691
|
mediaType: inferMediaType(path4),
|
|
6616
|
-
data:
|
|
6692
|
+
data: readFileSync3(path4).toString("base64")
|
|
6617
6693
|
};
|
|
6618
6694
|
if (image.data.length > 0) localImageCache.set(path4, image);
|
|
6619
6695
|
return image;
|
|
@@ -7885,7 +7961,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7885
7961
|
|
|
7886
7962
|
// src/managers/codex-manager.ts
|
|
7887
7963
|
import { Codex } from "@openai/codex-sdk";
|
|
7888
|
-
import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir10, readFile as
|
|
7964
|
+
import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir10, readFile as readFile8 } from "fs/promises";
|
|
7889
7965
|
import { existsSync as existsSync7 } from "fs";
|
|
7890
7966
|
import { join as join14 } from "path";
|
|
7891
7967
|
import { homedir as homedir12 } from "os";
|
|
@@ -7933,7 +8009,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
7933
8009
|
try {
|
|
7934
8010
|
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
7935
8011
|
if (!sessionFile) return;
|
|
7936
|
-
const content = await
|
|
8012
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
7937
8013
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
7938
8014
|
let latest = null;
|
|
7939
8015
|
for (const line of lines) {
|
|
@@ -7973,7 +8049,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
7973
8049
|
let config = {};
|
|
7974
8050
|
if (existsSync7(CODEX_CONFIG_PATH)) {
|
|
7975
8051
|
try {
|
|
7976
|
-
const existingContent = await
|
|
8052
|
+
const existingContent = await readFile8(CODEX_CONFIG_PATH, "utf-8");
|
|
7977
8053
|
const parsed = parseToml(existingContent);
|
|
7978
8054
|
if (isRecord4(parsed)) {
|
|
7979
8055
|
config = parsed;
|
|
@@ -8216,7 +8292,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
8216
8292
|
const seenLines = /* @__PURE__ */ new Set();
|
|
8217
8293
|
const seedSeenLines = async () => {
|
|
8218
8294
|
try {
|
|
8219
|
-
const content = await
|
|
8295
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
8220
8296
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
8221
8297
|
let latest = null;
|
|
8222
8298
|
for (const line of lines) {
|
|
@@ -8238,7 +8314,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
8238
8314
|
const pump = async () => {
|
|
8239
8315
|
let emitted = 0;
|
|
8240
8316
|
try {
|
|
8241
|
-
const content = await
|
|
8317
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
8242
8318
|
const lines = content.split("\n");
|
|
8243
8319
|
const completeLines = content.endsWith("\n") ? lines : lines.slice(0, -1);
|
|
8244
8320
|
for (const line of completeLines) {
|
|
@@ -9101,7 +9177,7 @@ var ChatService = class {
|
|
|
9101
9177
|
}
|
|
9102
9178
|
async readSenders(chatId) {
|
|
9103
9179
|
try {
|
|
9104
|
-
const content = await
|
|
9180
|
+
const content = await readFile9(this.senderFilePath(chatId), "utf-8");
|
|
9105
9181
|
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
9106
9182
|
const senders = [];
|
|
9107
9183
|
for (const line of lines) {
|
|
@@ -9458,7 +9534,7 @@ var ChatService = class {
|
|
|
9458
9534
|
}
|
|
9459
9535
|
async loadChats() {
|
|
9460
9536
|
try {
|
|
9461
|
-
const content = await
|
|
9537
|
+
const content = await readFile9(CHATS_FILE, "utf-8");
|
|
9462
9538
|
return parsePersistedChatsContent(content);
|
|
9463
9539
|
} catch (error) {
|
|
9464
9540
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -9473,7 +9549,7 @@ var ChatService = class {
|
|
|
9473
9549
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
9474
9550
|
}
|
|
9475
9551
|
try {
|
|
9476
|
-
const backupContent = await
|
|
9552
|
+
const backupContent = await readFile9(CHATS_BACKUP_FILE, "utf-8");
|
|
9477
9553
|
return parsePersistedChatsContent(backupContent);
|
|
9478
9554
|
} catch (backupError) {
|
|
9479
9555
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -9548,7 +9624,7 @@ var ChatService = class {
|
|
|
9548
9624
|
|
|
9549
9625
|
// src/services/repo-file-service.ts
|
|
9550
9626
|
import { execFile as execFile2 } from "child_process";
|
|
9551
|
-
import { readFile as
|
|
9627
|
+
import { readFile as readFile10, realpath, stat as stat3 } from "fs/promises";
|
|
9552
9628
|
import { join as join16, resolve, extname } from "path";
|
|
9553
9629
|
var CACHE_TTL_MS = 3e4;
|
|
9554
9630
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
@@ -9738,7 +9814,7 @@ var RepoFileService = class {
|
|
|
9738
9814
|
tooLarge: true
|
|
9739
9815
|
};
|
|
9740
9816
|
}
|
|
9741
|
-
const content = await
|
|
9817
|
+
const content = await readFile10(fullPath, "utf-8");
|
|
9742
9818
|
return {
|
|
9743
9819
|
repoName,
|
|
9744
9820
|
path: filePath,
|
|
@@ -9820,11 +9896,11 @@ var RepoFileService = class {
|
|
|
9820
9896
|
// src/v1-routes.ts
|
|
9821
9897
|
import { Hono } from "hono";
|
|
9822
9898
|
import { z as z2 } from "zod";
|
|
9823
|
-
import { readdir as readdir6, stat as stat4, readFile as
|
|
9899
|
+
import { readdir as readdir6, stat as stat4, readFile as readFile14 } from "fs/promises";
|
|
9824
9900
|
import { join as join20, resolve as resolve2 } from "path";
|
|
9825
9901
|
|
|
9826
9902
|
// src/services/plan-service.ts
|
|
9827
|
-
import { readdir as readdir4, readFile as
|
|
9903
|
+
import { readdir as readdir4, readFile as readFile11 } from "fs/promises";
|
|
9828
9904
|
import { homedir as homedir14 } from "os";
|
|
9829
9905
|
import { basename, join as join17 } from "path";
|
|
9830
9906
|
var PLAN_DIRECTORIES = [
|
|
@@ -9865,7 +9941,7 @@ var PlanService = class {
|
|
|
9865
9941
|
for (const directory of PLAN_DIRECTORIES) {
|
|
9866
9942
|
const filePath = join17(directory, safeFilename);
|
|
9867
9943
|
try {
|
|
9868
|
-
const content = await
|
|
9944
|
+
const content = await readFile11(filePath, "utf-8");
|
|
9869
9945
|
return { filename: safeFilename, content };
|
|
9870
9946
|
} catch {
|
|
9871
9947
|
}
|
|
@@ -9876,13 +9952,13 @@ var PlanService = class {
|
|
|
9876
9952
|
var planService = new PlanService();
|
|
9877
9953
|
|
|
9878
9954
|
// src/services/warm-hooks-service.ts
|
|
9879
|
-
import { spawn as
|
|
9880
|
-
import { readFile as
|
|
9955
|
+
import { spawn as spawn4 } from "child_process";
|
|
9956
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
9881
9957
|
import { existsSync as existsSync9 } from "fs";
|
|
9882
9958
|
import { join as join19 } from "path";
|
|
9883
9959
|
|
|
9884
9960
|
// src/services/warm-hook-logs-service.ts
|
|
9885
|
-
import { mkdir as mkdir12, readFile as
|
|
9961
|
+
import { mkdir as mkdir12, readFile as readFile12, writeFile as writeFile7, readdir as readdir5, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
|
|
9886
9962
|
import { homedir as homedir15 } from "os";
|
|
9887
9963
|
import { join as join18 } from "path";
|
|
9888
9964
|
var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
|
|
@@ -9942,7 +10018,7 @@ var WarmHookLogsService = class {
|
|
|
9942
10018
|
continue;
|
|
9943
10019
|
}
|
|
9944
10020
|
try {
|
|
9945
|
-
const raw = await
|
|
10021
|
+
const raw = await readFile12(join18(LOGS_DIR2, file), "utf-8");
|
|
9946
10022
|
const stored = JSON.parse(raw);
|
|
9947
10023
|
logs.push(withPreview2(stored));
|
|
9948
10024
|
} catch {
|
|
@@ -9971,7 +10047,7 @@ var WarmHookLogsService = class {
|
|
|
9971
10047
|
}
|
|
9972
10048
|
async getCurrentRunLog() {
|
|
9973
10049
|
try {
|
|
9974
|
-
return await
|
|
10050
|
+
return await readFile12(CURRENT_RUN_LOG, "utf-8");
|
|
9975
10051
|
} catch (err) {
|
|
9976
10052
|
if (err.code === "ENOENT") return null;
|
|
9977
10053
|
throw err;
|
|
@@ -9980,7 +10056,7 @@ var WarmHookLogsService = class {
|
|
|
9980
10056
|
async getFullOutput(hookType, hookName) {
|
|
9981
10057
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
9982
10058
|
try {
|
|
9983
|
-
const raw = await
|
|
10059
|
+
const raw = await readFile12(join18(LOGS_DIR2, filename), "utf-8");
|
|
9984
10060
|
const stored = JSON.parse(raw);
|
|
9985
10061
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
9986
10062
|
return null;
|
|
@@ -10004,7 +10080,7 @@ async function readRepoWarmHook(repoPath) {
|
|
|
10004
10080
|
continue;
|
|
10005
10081
|
}
|
|
10006
10082
|
try {
|
|
10007
|
-
const raw = await
|
|
10083
|
+
const raw = await readFile13(configPath, "utf-8");
|
|
10008
10084
|
const config = parseReplicasConfigString(raw, filename);
|
|
10009
10085
|
if (!config.warmHook) {
|
|
10010
10086
|
return null;
|
|
@@ -10051,7 +10127,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
10051
10127
|
params.onChunk(`$ ${params.label}
|
|
10052
10128
|
`);
|
|
10053
10129
|
return new Promise((resolve3) => {
|
|
10054
|
-
const proc =
|
|
10130
|
+
const proc = spawn4("bash", ["-lc", params.content], {
|
|
10055
10131
|
cwd: params.cwd,
|
|
10056
10132
|
env: process.env,
|
|
10057
10133
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -10934,7 +11010,7 @@ function createV1Routes(deps) {
|
|
|
10934
11010
|
const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
|
|
10935
11011
|
let content;
|
|
10936
11012
|
try {
|
|
10937
|
-
content = await
|
|
11013
|
+
content = await readFile14(filePath, "utf-8");
|
|
10938
11014
|
} catch {
|
|
10939
11015
|
return c.json(jsonError("Log session not found"), 404);
|
|
10940
11016
|
}
|
|
@@ -10974,10 +11050,10 @@ process.on("unhandledRejection", (reason) => {
|
|
|
10974
11050
|
engineLogger.flush().finally(() => process.exit(1));
|
|
10975
11051
|
});
|
|
10976
11052
|
await eventService.initialize();
|
|
10977
|
-
function checkActiveSSHSessions() {
|
|
11053
|
+
async function checkActiveSSHSessions() {
|
|
10978
11054
|
try {
|
|
10979
|
-
const
|
|
10980
|
-
const sessionCount = parseInt(
|
|
11055
|
+
const { stdout } = await execAsync('who | grep -v "^$" | wc -l', { encoding: "utf-8" });
|
|
11056
|
+
const sessionCount = parseInt(stdout.trim(), 10);
|
|
10981
11057
|
return sessionCount > 0;
|
|
10982
11058
|
} catch {
|
|
10983
11059
|
return false;
|
|
@@ -11010,10 +11086,13 @@ app.get("/health", (c) => {
|
|
|
11010
11086
|
app.use("*", authMiddleware);
|
|
11011
11087
|
app.get("/status", async (c) => {
|
|
11012
11088
|
try {
|
|
11013
|
-
const repos = await
|
|
11089
|
+
const [repos, hasActiveSSHSessions] = await Promise.all([
|
|
11090
|
+
gitService.listRepos(),
|
|
11091
|
+
checkActiveSSHSessions()
|
|
11092
|
+
]);
|
|
11014
11093
|
const status = {
|
|
11015
11094
|
uptimeSeconds: Math.floor((Date.now() - bootTimeMs) / 1e3),
|
|
11016
|
-
hasActiveSSHSessions
|
|
11095
|
+
hasActiveSSHSessions,
|
|
11017
11096
|
activeSseClients: eventService.getSubscriberCount(),
|
|
11018
11097
|
chatsTotal: chatService.listChats().length,
|
|
11019
11098
|
chatsProcessing: chatService.getProcessingCount(),
|
|
@@ -11043,8 +11122,16 @@ function startStatusBroadcaster() {
|
|
|
11043
11122
|
let previousHookStatus = "";
|
|
11044
11123
|
let previousEngineStatus = "";
|
|
11045
11124
|
let lastHooksRunning = replicasConfigService.areHooksRunning();
|
|
11046
|
-
|
|
11047
|
-
|
|
11125
|
+
const BROADCAST_INTERVAL_MS = 2e3;
|
|
11126
|
+
const scheduleNext = () => {
|
|
11127
|
+
setTimeout(tick, BROADCAST_INTERVAL_MS);
|
|
11128
|
+
};
|
|
11129
|
+
const tick = async () => {
|
|
11130
|
+
try {
|
|
11131
|
+
const [repos, hasActiveSSHSessions] = await Promise.all([
|
|
11132
|
+
gitService.listRepos(),
|
|
11133
|
+
checkActiveSSHSessions()
|
|
11134
|
+
]);
|
|
11048
11135
|
const serialized = JSON.stringify(repos);
|
|
11049
11136
|
if (serialized !== previousRepoStatus) {
|
|
11050
11137
|
previousRepoStatus = serialized;
|
|
@@ -11058,7 +11145,7 @@ function startStatusBroadcaster() {
|
|
|
11058
11145
|
}
|
|
11059
11146
|
const engineStatus = {
|
|
11060
11147
|
uptimeSeconds: Math.floor((Date.now() - bootTimeMs) / 1e3),
|
|
11061
|
-
hasActiveSSHSessions
|
|
11148
|
+
hasActiveSSHSessions,
|
|
11062
11149
|
activeSseClients: eventService.getSubscriberCount(),
|
|
11063
11150
|
chatsTotal: chatService.listChats().length,
|
|
11064
11151
|
chatsProcessing: chatService.getProcessingCount(),
|
|
@@ -11076,67 +11163,70 @@ function startStatusBroadcaster() {
|
|
|
11076
11163
|
}).catch(() => {
|
|
11077
11164
|
});
|
|
11078
11165
|
}
|
|
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
|
-
|
|
11166
|
+
const hooksRunning = replicasConfigService.areHooksRunning();
|
|
11167
|
+
const hooksCompleted = replicasConfigService.areHooksCompleted();
|
|
11168
|
+
const hooksFailed = replicasConfigService.didHooksFail();
|
|
11169
|
+
const hookSnapshot = JSON.stringify({
|
|
11170
|
+
running: hooksRunning,
|
|
11171
|
+
completed: hooksCompleted,
|
|
11172
|
+
failed: hooksFailed
|
|
11173
|
+
});
|
|
11174
|
+
if (hookSnapshot !== previousHookStatus) {
|
|
11175
|
+
previousHookStatus = hookSnapshot;
|
|
11176
|
+
if (!lastHooksRunning && hooksRunning) {
|
|
11177
|
+
eventService.publish({
|
|
11178
|
+
id: randomUUID6(),
|
|
11179
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11180
|
+
type: "hooks.started",
|
|
11181
|
+
payload: { running: true, completed: false }
|
|
11182
|
+
}).catch(() => {
|
|
11183
|
+
});
|
|
11184
|
+
}
|
|
11185
|
+
if (hooksRunning) {
|
|
11186
|
+
eventService.publish({
|
|
11187
|
+
id: randomUUID6(),
|
|
11188
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11189
|
+
type: "hooks.progress",
|
|
11190
|
+
payload: { running: true, completed: false }
|
|
11191
|
+
}).catch(() => {
|
|
11192
|
+
});
|
|
11193
|
+
}
|
|
11194
|
+
if (lastHooksRunning && !hooksRunning && hooksCompleted && !hooksFailed) {
|
|
11195
|
+
eventService.publish({
|
|
11196
|
+
id: randomUUID6(),
|
|
11197
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11198
|
+
type: "hooks.completed",
|
|
11199
|
+
payload: { running: false, completed: true }
|
|
11200
|
+
}).catch(() => {
|
|
11201
|
+
});
|
|
11202
|
+
}
|
|
11203
|
+
if (lastHooksRunning && !hooksRunning && hooksFailed) {
|
|
11204
|
+
eventService.publish({
|
|
11205
|
+
id: randomUUID6(),
|
|
11206
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11207
|
+
type: "hooks.failed",
|
|
11208
|
+
payload: { running: false, completed: hooksCompleted }
|
|
11209
|
+
}).catch(() => {
|
|
11210
|
+
});
|
|
11211
|
+
}
|
|
11119
11212
|
eventService.publish({
|
|
11120
11213
|
id: randomUUID6(),
|
|
11121
11214
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11122
|
-
type: "hooks.
|
|
11123
|
-
payload: {
|
|
11215
|
+
type: "hooks.status",
|
|
11216
|
+
payload: {
|
|
11217
|
+
running: hooksRunning,
|
|
11218
|
+
completed: hooksCompleted
|
|
11219
|
+
}
|
|
11124
11220
|
}).catch(() => {
|
|
11125
11221
|
});
|
|
11222
|
+
lastHooksRunning = hooksRunning;
|
|
11126
11223
|
}
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
type: "hooks.status",
|
|
11131
|
-
payload: {
|
|
11132
|
-
running: hooksRunning,
|
|
11133
|
-
completed: hooksCompleted
|
|
11134
|
-
}
|
|
11135
|
-
}).catch(() => {
|
|
11136
|
-
});
|
|
11137
|
-
lastHooksRunning = hooksRunning;
|
|
11224
|
+
} catch {
|
|
11225
|
+
} finally {
|
|
11226
|
+
scheduleNext();
|
|
11138
11227
|
}
|
|
11139
|
-
}
|
|
11228
|
+
};
|
|
11229
|
+
scheduleNext();
|
|
11140
11230
|
}
|
|
11141
11231
|
serve(
|
|
11142
11232
|
{
|