replicas-engine 0.1.256 → 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 +344 -238
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -3,22 +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 { readFile as readFile14 } from "fs/promises";
|
|
7
|
-
import { execSync as execSync2 } from "child_process";
|
|
8
6
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
9
7
|
|
|
10
|
-
// src/
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
// src/engine-env.ts
|
|
15
|
-
import { homedir as homedir2 } from "os";
|
|
16
|
-
import { join as join2 } from "path";
|
|
17
|
-
|
|
18
|
-
// src/runtime-env-loader.ts
|
|
19
|
-
import { readFileSync } from "fs";
|
|
20
|
-
import { homedir } from "os";
|
|
21
|
-
import { join } from "path";
|
|
8
|
+
// src/utils/exec.ts
|
|
9
|
+
import { exec, execFile } from "child_process";
|
|
10
|
+
import { promisify } from "util";
|
|
22
11
|
|
|
23
12
|
// ../shared/src/type-guards.ts
|
|
24
13
|
function isRecord(value) {
|
|
@@ -297,7 +286,7 @@ var WORKSPACE_SIZES = ["small", "large"];
|
|
|
297
286
|
var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
|
|
298
287
|
|
|
299
288
|
// ../shared/src/e2b.ts
|
|
300
|
-
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-03-
|
|
289
|
+
var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-03-v3";
|
|
301
290
|
|
|
302
291
|
// ../shared/src/runtime-env.ts
|
|
303
292
|
function parsePosixEnvFile(content) {
|
|
@@ -358,6 +347,25 @@ function parsePosixEnvFile(content) {
|
|
|
358
347
|
// ../shared/src/git.ts
|
|
359
348
|
var GIT_PARTIAL_CLONE_FILTER = "blob:none";
|
|
360
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
|
+
|
|
361
369
|
// ../shared/src/default-skills/replicas-agent/abilities/computer.ts
|
|
362
370
|
var SECTION = `### Computer use (Linux desktop control)
|
|
363
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).
|
|
@@ -2234,7 +2242,30 @@ var MEDIA_KIND = {
|
|
|
2234
2242
|
};
|
|
2235
2243
|
var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
|
|
2236
2244
|
|
|
2245
|
+
// ../shared/src/github/url.ts
|
|
2246
|
+
var GITHUB_HOST_RE = /(^|@|\/\/)github\.com[:/]/;
|
|
2247
|
+
function isGitHubUrl(url) {
|
|
2248
|
+
if (!url) return false;
|
|
2249
|
+
return GITHUB_HOST_RE.test(url);
|
|
2250
|
+
}
|
|
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
|
+
|
|
2237
2265
|
// src/runtime-env-loader.ts
|
|
2266
|
+
import { readFileSync } from "fs";
|
|
2267
|
+
import { homedir } from "os";
|
|
2268
|
+
import { join } from "path";
|
|
2238
2269
|
function loadRuntimeEnvFile() {
|
|
2239
2270
|
let content;
|
|
2240
2271
|
try {
|
|
@@ -2730,9 +2761,9 @@ var CodexTokenManager = class extends BaseRefreshManager {
|
|
|
2730
2761
|
var codexTokenManager = new CodexTokenManager();
|
|
2731
2762
|
|
|
2732
2763
|
// src/git/service.ts
|
|
2733
|
-
import { readdir, stat } from "fs/promises";
|
|
2734
|
-
import { existsSync as existsSync2,
|
|
2735
|
-
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";
|
|
2736
2767
|
import { join as join5 } from "path";
|
|
2737
2768
|
|
|
2738
2769
|
// src/utils/state.ts
|
|
@@ -2863,15 +2894,15 @@ async function saveRepoState(repoName, state, fallbackState) {
|
|
|
2863
2894
|
}
|
|
2864
2895
|
|
|
2865
2896
|
// src/git/commands.ts
|
|
2866
|
-
import { execFileSync } from "child_process";
|
|
2867
2897
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2868
2898
|
import { join as join4 } from "path";
|
|
2869
|
-
function runGitCommand(args, cwd) {
|
|
2870
|
-
|
|
2899
|
+
async function runGitCommand(args, cwd, options = {}) {
|
|
2900
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
2871
2901
|
cwd,
|
|
2872
2902
|
encoding: "utf-8",
|
|
2873
|
-
|
|
2874
|
-
})
|
|
2903
|
+
maxBuffer: options.maxBuffer ?? SUBPROCESS_MAX_BUFFER
|
|
2904
|
+
});
|
|
2905
|
+
return stdout.trim();
|
|
2875
2906
|
}
|
|
2876
2907
|
function readRepoHeadBranch(repoPath) {
|
|
2877
2908
|
try {
|
|
@@ -2882,17 +2913,17 @@ function readRepoHeadBranch(repoPath) {
|
|
|
2882
2913
|
return null;
|
|
2883
2914
|
}
|
|
2884
2915
|
}
|
|
2885
|
-
function branchExists(branchName, cwd) {
|
|
2916
|
+
async function branchExists(branchName, cwd) {
|
|
2886
2917
|
try {
|
|
2887
|
-
runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
2918
|
+
await runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
2888
2919
|
return true;
|
|
2889
2920
|
} catch {
|
|
2890
2921
|
return false;
|
|
2891
2922
|
}
|
|
2892
2923
|
}
|
|
2893
|
-
function getCurrentBranch(cwd) {
|
|
2924
|
+
async function getCurrentBranch(cwd) {
|
|
2894
2925
|
try {
|
|
2895
|
-
return runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
2926
|
+
return await runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
2896
2927
|
} catch (error) {
|
|
2897
2928
|
console.error("Error getting current branch:", error);
|
|
2898
2929
|
return null;
|
|
@@ -2900,12 +2931,20 @@ function getCurrentBranch(cwd) {
|
|
|
2900
2931
|
}
|
|
2901
2932
|
|
|
2902
2933
|
// src/git/service.ts
|
|
2934
|
+
var FULL_DIFF_MAX_BUFFER = 50 * 1024 * 1024;
|
|
2903
2935
|
function appendUniqueUrl(urls, url) {
|
|
2904
2936
|
return urls.includes(url) ? urls : [...urls, url];
|
|
2905
2937
|
}
|
|
2906
2938
|
var GitService = class {
|
|
2907
2939
|
defaultBranchCache = /* @__PURE__ */ new Map();
|
|
2908
2940
|
cachedPrByRepo = /* @__PURE__ */ new Map();
|
|
2941
|
+
// No invalidation on purpose — `git remote set-url` mid-session is rare and
|
|
2942
|
+
// the worst case is `gh pr view` stays skipped until the next engine restart.
|
|
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();
|
|
2909
2948
|
getWorkspaceRoot() {
|
|
2910
2949
|
return ENGINE_ENV.WORKSPACE_ROOT;
|
|
2911
2950
|
}
|
|
@@ -2931,7 +2970,7 @@ var GitService = class {
|
|
|
2931
2970
|
repos.push({
|
|
2932
2971
|
name: entry,
|
|
2933
2972
|
path: fullPath,
|
|
2934
|
-
defaultBranch: this.resolveDefaultBranch(fullPath)
|
|
2973
|
+
defaultBranch: await this.resolveDefaultBranch(fullPath)
|
|
2935
2974
|
});
|
|
2936
2975
|
} catch {
|
|
2937
2976
|
}
|
|
@@ -2940,13 +2979,21 @@ var GitService = class {
|
|
|
2940
2979
|
}
|
|
2941
2980
|
async listRepos(options) {
|
|
2942
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) {
|
|
2943
2986
|
const repos = await this.listRepositories();
|
|
2944
2987
|
const states = [];
|
|
2945
2988
|
for (const repo of repos) {
|
|
2946
2989
|
try {
|
|
2947
|
-
const persistedState = await
|
|
2948
|
-
|
|
2949
|
-
|
|
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;
|
|
2950
2997
|
states.push({
|
|
2951
2998
|
name: repo.name,
|
|
2952
2999
|
path: repo.path,
|
|
@@ -2954,7 +3001,7 @@ var GitService = class {
|
|
|
2954
3001
|
currentBranch,
|
|
2955
3002
|
prUrls: persistedState?.prUrls ?? [],
|
|
2956
3003
|
// fullDiff may be empty if the diff subprocess fails.
|
|
2957
|
-
gitDiff: includeDiffs && gitDiff ? { ...gitDiff, fullDiff:
|
|
3004
|
+
gitDiff: includeDiffs && gitDiff ? { ...gitDiff, fullDiff: fullDiff ?? "" } : gitDiff,
|
|
2958
3005
|
startHooksCompleted: persistedState?.startHooksCompleted ?? false
|
|
2959
3006
|
});
|
|
2960
3007
|
} catch {
|
|
@@ -2968,7 +3015,7 @@ var GitService = class {
|
|
|
2968
3015
|
for (const repo of repos) {
|
|
2969
3016
|
try {
|
|
2970
3017
|
const persistedState = await loadRepoState(repo.name);
|
|
2971
|
-
const currentBranch = getCurrentBranch(repo.path) ?? repo.defaultBranch;
|
|
3018
|
+
const currentBranch = await getCurrentBranch(repo.path) ?? repo.defaultBranch;
|
|
2972
3019
|
const startHooksCompleted = persistedState?.startHooksCompleted ?? false;
|
|
2973
3020
|
const observed = observedBranchesByRepo?.get(repo.name);
|
|
2974
3021
|
states.push(
|
|
@@ -3033,12 +3080,12 @@ var GitService = class {
|
|
|
3033
3080
|
const persistedState = await loadRepoState(repo.name);
|
|
3034
3081
|
const persistedBranch = persistedState?.currentBranch;
|
|
3035
3082
|
if (!skipNetworkRefresh) {
|
|
3036
|
-
runGitCommand(["fetch", "--all", "--prune", `--filter=${GIT_PARTIAL_CLONE_FILTER}`], repo.path);
|
|
3083
|
+
await runGitCommand(["fetch", "--all", "--prune", `--filter=${GIT_PARTIAL_CLONE_FILTER}`], repo.path);
|
|
3037
3084
|
}
|
|
3038
|
-
if (persistedBranch && branchExists(persistedBranch, repo.path)) {
|
|
3039
|
-
const currentBranch = getCurrentBranch(repo.path);
|
|
3085
|
+
if (persistedBranch && await branchExists(persistedBranch, repo.path)) {
|
|
3086
|
+
const currentBranch = await getCurrentBranch(repo.path);
|
|
3040
3087
|
if (currentBranch !== persistedBranch) {
|
|
3041
|
-
runGitCommand(["checkout", persistedBranch], repo.path);
|
|
3088
|
+
await runGitCommand(["checkout", persistedBranch], repo.path);
|
|
3042
3089
|
}
|
|
3043
3090
|
results.push({
|
|
3044
3091
|
name: repo.name,
|
|
@@ -3048,15 +3095,15 @@ var GitService = class {
|
|
|
3048
3095
|
});
|
|
3049
3096
|
continue;
|
|
3050
3097
|
}
|
|
3051
|
-
runGitCommand(["checkout", repo.defaultBranch], repo.path);
|
|
3098
|
+
await runGitCommand(["checkout", repo.defaultBranch], repo.path);
|
|
3052
3099
|
if (!skipNetworkRefresh) {
|
|
3053
3100
|
try {
|
|
3054
|
-
runGitCommand(["pull", "--rebase", "--autostash"], repo.path);
|
|
3101
|
+
await runGitCommand(["pull", "--rebase", "--autostash"], repo.path);
|
|
3055
3102
|
} catch {
|
|
3056
3103
|
}
|
|
3057
3104
|
}
|
|
3058
|
-
const branchName = this.findAvailableBranchName(workspaceName, repo.path);
|
|
3059
|
-
runGitCommand(["checkout", "-b", branchName], repo.path);
|
|
3105
|
+
const branchName = await this.findAvailableBranchName(workspaceName, repo.path);
|
|
3106
|
+
await runGitCommand(["checkout", "-b", branchName], repo.path);
|
|
3060
3107
|
await saveRepoState(repo.name, { currentBranch: branchName }, baselineState);
|
|
3061
3108
|
results.push({
|
|
3062
3109
|
name: repo.name,
|
|
@@ -3094,109 +3141,149 @@ var GitService = class {
|
|
|
3094
3141
|
return true;
|
|
3095
3142
|
}
|
|
3096
3143
|
getGitDiffStats(repoPath, defaultBranch) {
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
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
|
+
});
|
|
3119
3168
|
}
|
|
3120
|
-
listUntrackedPaths(repoPath) {
|
|
3121
|
-
const
|
|
3169
|
+
async listUntrackedPaths(repoPath) {
|
|
3170
|
+
const { stdout } = await execFileAsync("git", ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
3122
3171
|
cwd: repoPath,
|
|
3123
3172
|
encoding: "utf-8",
|
|
3124
|
-
|
|
3173
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3125
3174
|
});
|
|
3126
|
-
return
|
|
3175
|
+
return stdout.split("\0").filter(Boolean);
|
|
3127
3176
|
}
|
|
3128
|
-
getUntrackedDiff(repoPath) {
|
|
3129
|
-
const paths = this.listUntrackedPaths(repoPath);
|
|
3177
|
+
async getUntrackedDiff(repoPath) {
|
|
3178
|
+
const paths = await this.listUntrackedPaths(repoPath);
|
|
3130
3179
|
if (paths.length === 0) return "";
|
|
3131
3180
|
try {
|
|
3132
|
-
|
|
3133
|
-
const result = spawnSync("git", ["diff", "--", ...paths], {
|
|
3181
|
+
await execFileAsync("git", ["add", "--intent-to-add", "--", ...paths], {
|
|
3134
3182
|
cwd: repoPath,
|
|
3135
|
-
|
|
3136
|
-
maxBuffer: 50 * 1024 * 1024
|
|
3183
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3137
3184
|
});
|
|
3138
|
-
return
|
|
3185
|
+
return await this.readUntrackedDiff(repoPath, paths);
|
|
3139
3186
|
} finally {
|
|
3140
3187
|
try {
|
|
3141
|
-
|
|
3188
|
+
await execFileAsync("git", ["reset", "--", ...paths], {
|
|
3189
|
+
cwd: repoPath,
|
|
3190
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3191
|
+
});
|
|
3142
3192
|
} catch {
|
|
3143
3193
|
}
|
|
3144
3194
|
}
|
|
3145
3195
|
}
|
|
3146
|
-
|
|
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) {
|
|
3147
3229
|
try {
|
|
3148
|
-
|
|
3230
|
+
const paths = await this.listUntrackedPaths(repoPath);
|
|
3231
|
+
let total = 0;
|
|
3232
|
+
for (const path4 of paths) {
|
|
3149
3233
|
try {
|
|
3150
|
-
const contents =
|
|
3234
|
+
const contents = await readFile2(join5(repoPath, path4));
|
|
3151
3235
|
if (contents.length === 0 || contents.includes(0)) {
|
|
3152
|
-
|
|
3236
|
+
continue;
|
|
3153
3237
|
}
|
|
3154
3238
|
let lines = 0;
|
|
3155
3239
|
for (const byte of contents) {
|
|
3156
3240
|
if (byte === 10) lines += 1;
|
|
3157
3241
|
}
|
|
3158
|
-
|
|
3242
|
+
total += lines + (contents[contents.length - 1] === 10 ? 0 : 1);
|
|
3159
3243
|
} catch {
|
|
3160
|
-
return total;
|
|
3161
3244
|
}
|
|
3162
|
-
}
|
|
3245
|
+
}
|
|
3246
|
+
return total;
|
|
3163
3247
|
} catch (error) {
|
|
3164
3248
|
console.error("Error counting untracked added lines:", error);
|
|
3165
3249
|
return 0;
|
|
3166
3250
|
}
|
|
3167
3251
|
}
|
|
3168
3252
|
getFullGitDiff(repoPath, defaultBranch) {
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
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
|
+
});
|
|
3180
3267
|
}
|
|
3181
|
-
getUntrackedAsDiff(repoPath) {
|
|
3268
|
+
async getUntrackedAsDiff(repoPath) {
|
|
3182
3269
|
try {
|
|
3183
|
-
return this.getUntrackedDiff(repoPath);
|
|
3270
|
+
return await this.getUntrackedDiff(repoPath);
|
|
3184
3271
|
} catch (error) {
|
|
3185
3272
|
console.error("Error building untracked diff:", error);
|
|
3186
3273
|
return "";
|
|
3187
3274
|
}
|
|
3188
3275
|
}
|
|
3189
|
-
getDiffBase(repoPath, defaultBranch) {
|
|
3276
|
+
async getDiffBase(repoPath, defaultBranch) {
|
|
3190
3277
|
const baseBranch = `origin/${defaultBranch}`;
|
|
3191
3278
|
try {
|
|
3192
|
-
return runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3279
|
+
return await runGitCommand(["merge-base", baseBranch, "HEAD"], repoPath);
|
|
3193
3280
|
} catch {
|
|
3194
3281
|
return baseBranch;
|
|
3195
3282
|
}
|
|
3196
3283
|
}
|
|
3197
3284
|
async getPullRequestUrl(repoName, repoPath, currentBranchArg, persistedRepoStateArg) {
|
|
3198
3285
|
try {
|
|
3199
|
-
const currentBranch = currentBranchArg ?? getCurrentBranch(repoPath);
|
|
3286
|
+
const currentBranch = currentBranchArg ?? await getCurrentBranch(repoPath);
|
|
3200
3287
|
if (!currentBranch) {
|
|
3201
3288
|
return { status: "not_found" };
|
|
3202
3289
|
}
|
|
@@ -3206,7 +3293,7 @@ var GitService = class {
|
|
|
3206
3293
|
}
|
|
3207
3294
|
const persistedRepoState = persistedRepoStateArg ?? await loadRepoState(repoName);
|
|
3208
3295
|
this.cachedPrByRepo.delete(repoName);
|
|
3209
|
-
const result = this.lookupPrOnRemote(repoName, repoPath, currentBranch);
|
|
3296
|
+
const result = await this.lookupPrOnRemote(repoName, repoPath, currentBranch);
|
|
3210
3297
|
if (result.status === "found") {
|
|
3211
3298
|
this.cachedPrByRepo.set(repoName, { prUrl: result.url, currentBranch });
|
|
3212
3299
|
if (persistedRepoState && !persistedRepoState.prUrls.includes(result.url)) {
|
|
@@ -3223,25 +3310,29 @@ var GitService = class {
|
|
|
3223
3310
|
return { status: "error" };
|
|
3224
3311
|
}
|
|
3225
3312
|
}
|
|
3226
|
-
lookupPrOnRemote(repoName, repoPath, branch) {
|
|
3313
|
+
async lookupPrOnRemote(repoName, repoPath, branch) {
|
|
3314
|
+
if (!await this.originIsGitHub(repoPath)) {
|
|
3315
|
+
return { status: "not_found" };
|
|
3316
|
+
}
|
|
3227
3317
|
try {
|
|
3228
|
-
const
|
|
3318
|
+
const { stdout } = await execFileAsync("git", ["ls-remote", "--heads", "origin", branch], {
|
|
3229
3319
|
cwd: repoPath,
|
|
3230
3320
|
encoding: "utf-8",
|
|
3231
|
-
|
|
3232
|
-
})
|
|
3233
|
-
if (!
|
|
3321
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3322
|
+
});
|
|
3323
|
+
if (!stdout.trim()) {
|
|
3234
3324
|
return { status: "not_found" };
|
|
3235
3325
|
}
|
|
3236
3326
|
} catch {
|
|
3237
3327
|
return { status: "error" };
|
|
3238
3328
|
}
|
|
3239
3329
|
try {
|
|
3240
|
-
const
|
|
3330
|
+
const { stdout } = await execFileAsync("gh", ["pr", "view", branch, "--json", "url", "--jq", ".url"], {
|
|
3241
3331
|
cwd: repoPath,
|
|
3242
3332
|
encoding: "utf-8",
|
|
3243
|
-
|
|
3244
|
-
})
|
|
3333
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3334
|
+
});
|
|
3335
|
+
const prInfo = stdout.trim();
|
|
3245
3336
|
return prInfo ? { status: "found", url: prInfo } : { status: "not_found" };
|
|
3246
3337
|
} catch (error) {
|
|
3247
3338
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3249,12 +3340,31 @@ var GitService = class {
|
|
|
3249
3340
|
return { status: "error" };
|
|
3250
3341
|
}
|
|
3251
3342
|
}
|
|
3252
|
-
|
|
3343
|
+
async originIsGitHub(repoPath) {
|
|
3344
|
+
const cached = this.originIsGitHubCache.get(repoPath);
|
|
3345
|
+
if (cached !== void 0) {
|
|
3346
|
+
return cached;
|
|
3347
|
+
}
|
|
3348
|
+
let isGitHub = false;
|
|
3349
|
+
try {
|
|
3350
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
|
|
3351
|
+
cwd: repoPath,
|
|
3352
|
+
encoding: "utf-8",
|
|
3353
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER
|
|
3354
|
+
});
|
|
3355
|
+
isGitHub = isGitHubUrl(stdout.trim());
|
|
3356
|
+
} catch {
|
|
3357
|
+
isGitHub = false;
|
|
3358
|
+
}
|
|
3359
|
+
this.originIsGitHubCache.set(repoPath, isGitHub);
|
|
3360
|
+
return isGitHub;
|
|
3361
|
+
}
|
|
3362
|
+
async resolveDefaultBranch(repoPath) {
|
|
3253
3363
|
const cached = this.defaultBranchCache.get(repoPath);
|
|
3254
3364
|
if (cached) {
|
|
3255
3365
|
return cached;
|
|
3256
3366
|
}
|
|
3257
|
-
const fromSymbolicRef = this.resolveDefaultBranchFromSymbolicRef(repoPath);
|
|
3367
|
+
const fromSymbolicRef = await this.resolveDefaultBranchFromSymbolicRef(repoPath);
|
|
3258
3368
|
if (fromSymbolicRef) {
|
|
3259
3369
|
this.defaultBranchCache.set(repoPath, fromSymbolicRef);
|
|
3260
3370
|
return fromSymbolicRef;
|
|
@@ -3263,18 +3373,18 @@ var GitService = class {
|
|
|
3263
3373
|
this.defaultBranchCache.set(repoPath, fallback);
|
|
3264
3374
|
return fallback;
|
|
3265
3375
|
}
|
|
3266
|
-
resolveDefaultBranchFromSymbolicRef(repoPath) {
|
|
3376
|
+
async resolveDefaultBranchFromSymbolicRef(repoPath) {
|
|
3267
3377
|
try {
|
|
3268
|
-
const output = runGitCommand(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoPath);
|
|
3378
|
+
const output = await runGitCommand(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoPath);
|
|
3269
3379
|
const match = output.match(/^origin\/(.+)$/);
|
|
3270
3380
|
return match ? match[1] : null;
|
|
3271
3381
|
} catch {
|
|
3272
3382
|
return null;
|
|
3273
3383
|
}
|
|
3274
3384
|
}
|
|
3275
|
-
findAvailableBranchName(baseName, cwd) {
|
|
3385
|
+
async findAvailableBranchName(baseName, cwd) {
|
|
3276
3386
|
const sanitizedBaseName = this.sanitizeBranchName(baseName);
|
|
3277
|
-
if (!branchExists(sanitizedBaseName, cwd)) {
|
|
3387
|
+
if (!await branchExists(sanitizedBaseName, cwd)) {
|
|
3278
3388
|
return sanitizedBaseName;
|
|
3279
3389
|
}
|
|
3280
3390
|
return `${sanitizedBaseName}-${Date.now()}`;
|
|
@@ -3292,7 +3402,7 @@ var GitService = class {
|
|
|
3292
3402
|
if (observedBranches) {
|
|
3293
3403
|
for (const branch of observedBranches) {
|
|
3294
3404
|
if (branch === currentBranch) continue;
|
|
3295
|
-
const branchResult = this.lookupPrOnRemote(repo.name, repo.path, branch);
|
|
3405
|
+
const branchResult = await this.lookupPrOnRemote(repo.name, repo.path, branch);
|
|
3296
3406
|
if (branchResult.status === "found") {
|
|
3297
3407
|
prUrls = appendUniqueUrl(prUrls, branchResult.url);
|
|
3298
3408
|
}
|
|
@@ -3304,7 +3414,7 @@ var GitService = class {
|
|
|
3304
3414
|
defaultBranch: repo.defaultBranch,
|
|
3305
3415
|
currentBranch,
|
|
3306
3416
|
prUrls,
|
|
3307
|
-
gitDiff: this.getGitDiffStats(repo.path, repo.defaultBranch),
|
|
3417
|
+
gitDiff: await this.getGitDiffStats(repo.path, repo.defaultBranch),
|
|
3308
3418
|
startHooksCompleted
|
|
3309
3419
|
};
|
|
3310
3420
|
await saveRepoState(repo.name, state, state);
|
|
@@ -3392,20 +3502,17 @@ var EngineLogger = class {
|
|
|
3392
3502
|
var engineLogger = new EngineLogger();
|
|
3393
3503
|
|
|
3394
3504
|
// src/services/replicas-config-service.ts
|
|
3395
|
-
import { readFile as
|
|
3505
|
+
import { readFile as readFile5, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
|
|
3396
3506
|
import { existsSync as existsSync4 } from "fs";
|
|
3397
3507
|
import { join as join9 } from "path";
|
|
3398
3508
|
import { homedir as homedir7 } from "os";
|
|
3399
|
-
import { spawn } from "child_process";
|
|
3509
|
+
import { spawn as spawn2 } from "child_process";
|
|
3400
3510
|
|
|
3401
3511
|
// src/services/environment-details-service.ts
|
|
3402
|
-
import { mkdir as mkdir3, readFile as
|
|
3512
|
+
import { mkdir as mkdir3, readFile as readFile3 } from "fs/promises";
|
|
3403
3513
|
import { existsSync as existsSync3 } from "fs";
|
|
3404
3514
|
import { homedir as homedir5 } from "os";
|
|
3405
3515
|
import { join as join7 } from "path";
|
|
3406
|
-
import { execFile } from "child_process";
|
|
3407
|
-
import { promisify } from "util";
|
|
3408
|
-
var execFileAsync = promisify(execFile);
|
|
3409
3516
|
var REPLICAS_DIR = join7(homedir5(), ".replicas");
|
|
3410
3517
|
var DETAILS_FILE = join7(REPLICAS_DIR, "environment-details.json");
|
|
3411
3518
|
var CLAUDE_CREDENTIALS_PATH = join7(homedir5(), ".claude", ".credentials.json");
|
|
@@ -3478,7 +3585,7 @@ async function readDetails() {
|
|
|
3478
3585
|
if (!existsSync3(DETAILS_FILE)) {
|
|
3479
3586
|
return createDefaultDetails();
|
|
3480
3587
|
}
|
|
3481
|
-
const raw = await
|
|
3588
|
+
const raw = await readFile3(DETAILS_FILE, "utf-8");
|
|
3482
3589
|
const parsed = JSON.parse(raw);
|
|
3483
3590
|
return { ...createDefaultDetails(), ...parsed };
|
|
3484
3591
|
} catch {
|
|
@@ -3572,7 +3679,7 @@ var EnvironmentDetailsService = class {
|
|
|
3572
3679
|
var environmentDetailsService = new EnvironmentDetailsService();
|
|
3573
3680
|
|
|
3574
3681
|
// src/services/start-hook-logs-service.ts
|
|
3575
|
-
import { mkdir as mkdir4, readFile as
|
|
3682
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
|
|
3576
3683
|
import { homedir as homedir6 } from "os";
|
|
3577
3684
|
import { join as join8 } from "path";
|
|
3578
3685
|
|
|
@@ -3649,7 +3756,7 @@ var StartHookLogsService = class {
|
|
|
3649
3756
|
continue;
|
|
3650
3757
|
}
|
|
3651
3758
|
try {
|
|
3652
|
-
const raw = await
|
|
3759
|
+
const raw = await readFile4(join8(LOGS_DIR, file), "utf-8");
|
|
3653
3760
|
const stored = normalizeStored(JSON.parse(raw));
|
|
3654
3761
|
if (stored) {
|
|
3655
3762
|
logs.push(withPreview(stored));
|
|
@@ -3668,7 +3775,7 @@ var StartHookLogsService = class {
|
|
|
3668
3775
|
async getFullOutput(hookType, hookName) {
|
|
3669
3776
|
const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
3670
3777
|
try {
|
|
3671
|
-
const raw = await
|
|
3778
|
+
const raw = await readFile4(join8(LOGS_DIR, filename), "utf-8");
|
|
3672
3779
|
const stored = normalizeStored(JSON.parse(raw));
|
|
3673
3780
|
if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
3674
3781
|
return null;
|
|
@@ -3700,7 +3807,7 @@ async function readReplicasConfigFromDir(dirPath) {
|
|
|
3700
3807
|
if (!existsSync4(configPath)) {
|
|
3701
3808
|
continue;
|
|
3702
3809
|
}
|
|
3703
|
-
const data = await
|
|
3810
|
+
const data = await readFile5(configPath, "utf-8");
|
|
3704
3811
|
const config = parseReplicasConfigString(data, filename);
|
|
3705
3812
|
return { config, filename };
|
|
3706
3813
|
}
|
|
@@ -3802,7 +3909,7 @@ var ReplicasConfigService = class {
|
|
|
3802
3909
|
emit(`$ ${params.label}
|
|
3803
3910
|
`);
|
|
3804
3911
|
return new Promise((resolve3) => {
|
|
3805
|
-
const proc =
|
|
3912
|
+
const proc = spawn2("bash", ["-lc", params.command], {
|
|
3806
3913
|
cwd: params.cwd,
|
|
3807
3914
|
env: process.env,
|
|
3808
3915
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4135,7 +4242,7 @@ var EventService = class {
|
|
|
4135
4242
|
var eventService = new EventService();
|
|
4136
4243
|
|
|
4137
4244
|
// src/services/preview-service.ts
|
|
4138
|
-
import { mkdir as mkdir7, readFile as
|
|
4245
|
+
import { mkdir as mkdir7, readFile as readFile6 } from "fs/promises";
|
|
4139
4246
|
import { existsSync as existsSync5 } from "fs";
|
|
4140
4247
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4141
4248
|
import { homedir as homedir9 } from "os";
|
|
@@ -4146,7 +4253,7 @@ async function readPreviewsFile() {
|
|
|
4146
4253
|
if (!existsSync5(PREVIEW_PORTS_FILE)) {
|
|
4147
4254
|
return { previews: [] };
|
|
4148
4255
|
}
|
|
4149
|
-
const raw = await
|
|
4256
|
+
const raw = await readFile6(PREVIEW_PORTS_FILE, "utf-8");
|
|
4150
4257
|
return JSON.parse(raw);
|
|
4151
4258
|
} catch {
|
|
4152
4259
|
return { previews: [] };
|
|
@@ -4251,7 +4358,7 @@ async function registerDesktopPreview() {
|
|
|
4251
4358
|
|
|
4252
4359
|
// src/services/chat/chat-service.ts
|
|
4253
4360
|
import { existsSync as existsSync8 } from "fs";
|
|
4254
|
-
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";
|
|
4255
4362
|
import { homedir as homedir13 } from "os";
|
|
4256
4363
|
import { join as join15 } from "path";
|
|
4257
4364
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -4266,7 +4373,7 @@ import { mkdir as mkdir9, appendFile as appendFile4 } from "fs/promises";
|
|
|
4266
4373
|
import { homedir as homedir11 } from "os";
|
|
4267
4374
|
|
|
4268
4375
|
// src/utils/jsonl-reader.ts
|
|
4269
|
-
import { readFile as
|
|
4376
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
4270
4377
|
function isJsonlEvent(value) {
|
|
4271
4378
|
if (!isRecord4(value)) {
|
|
4272
4379
|
return false;
|
|
@@ -4288,7 +4395,7 @@ function parseJsonlEvents(lines) {
|
|
|
4288
4395
|
}
|
|
4289
4396
|
async function readJSONL(filePath) {
|
|
4290
4397
|
try {
|
|
4291
|
-
const content = await
|
|
4398
|
+
const content = await readFile7(filePath, "utf-8");
|
|
4292
4399
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
4293
4400
|
return parseJsonlEvents(lines);
|
|
4294
4401
|
} catch (error) {
|
|
@@ -6039,7 +6146,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
6039
6146
|
};
|
|
6040
6147
|
|
|
6041
6148
|
// src/managers/codex-asp/app-server-process.ts
|
|
6042
|
-
import { spawn as
|
|
6149
|
+
import { spawn as spawn3 } from "child_process";
|
|
6043
6150
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
6044
6151
|
|
|
6045
6152
|
// src/managers/codex-asp/asp-client.ts
|
|
@@ -6234,7 +6341,7 @@ var AspClient = class {
|
|
|
6234
6341
|
// src/managers/codex-asp/app-server-process.ts
|
|
6235
6342
|
var DEFAULT_CODEX_BINARY = "codex";
|
|
6236
6343
|
var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
6237
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6344
|
+
var ENGINE_PACKAGE_VERSION = "0.1.258";
|
|
6238
6345
|
var INITIALIZE_METHOD = "initialize";
|
|
6239
6346
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6240
6347
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -6261,7 +6368,7 @@ var AppServerProcess = class {
|
|
|
6261
6368
|
return { client: this.client };
|
|
6262
6369
|
}
|
|
6263
6370
|
this.shuttingDown = false;
|
|
6264
|
-
const child =
|
|
6371
|
+
const child = spawn3(this.binary, this.args, {
|
|
6265
6372
|
cwd: this.cwd,
|
|
6266
6373
|
env: this.env,
|
|
6267
6374
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6501,7 +6608,7 @@ function isCodexAuthError(error) {
|
|
|
6501
6608
|
}
|
|
6502
6609
|
|
|
6503
6610
|
// src/managers/codex-asp/mappers.ts
|
|
6504
|
-
import { existsSync as existsSync6, readFileSync as
|
|
6611
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
6505
6612
|
var localImageCache = /* @__PURE__ */ new Map();
|
|
6506
6613
|
var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
|
|
6507
6614
|
var THREAD_START_METHOD = "thread/start";
|
|
@@ -6582,7 +6689,7 @@ function userImageForLocalPath(path4) {
|
|
|
6582
6689
|
const image = {
|
|
6583
6690
|
type: "image",
|
|
6584
6691
|
mediaType: inferMediaType(path4),
|
|
6585
|
-
data:
|
|
6692
|
+
data: readFileSync3(path4).toString("base64")
|
|
6586
6693
|
};
|
|
6587
6694
|
if (image.data.length > 0) localImageCache.set(path4, image);
|
|
6588
6695
|
return image;
|
|
@@ -7854,7 +7961,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7854
7961
|
|
|
7855
7962
|
// src/managers/codex-manager.ts
|
|
7856
7963
|
import { Codex } from "@openai/codex-sdk";
|
|
7857
|
-
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";
|
|
7858
7965
|
import { existsSync as existsSync7 } from "fs";
|
|
7859
7966
|
import { join as join14 } from "path";
|
|
7860
7967
|
import { homedir as homedir12 } from "os";
|
|
@@ -7902,7 +8009,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
7902
8009
|
try {
|
|
7903
8010
|
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
7904
8011
|
if (!sessionFile) return;
|
|
7905
|
-
const content = await
|
|
8012
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
7906
8013
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
7907
8014
|
let latest = null;
|
|
7908
8015
|
for (const line of lines) {
|
|
@@ -7942,7 +8049,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
7942
8049
|
let config = {};
|
|
7943
8050
|
if (existsSync7(CODEX_CONFIG_PATH)) {
|
|
7944
8051
|
try {
|
|
7945
|
-
const existingContent = await
|
|
8052
|
+
const existingContent = await readFile8(CODEX_CONFIG_PATH, "utf-8");
|
|
7946
8053
|
const parsed = parseToml(existingContent);
|
|
7947
8054
|
if (isRecord4(parsed)) {
|
|
7948
8055
|
config = parsed;
|
|
@@ -8185,7 +8292,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
8185
8292
|
const seenLines = /* @__PURE__ */ new Set();
|
|
8186
8293
|
const seedSeenLines = async () => {
|
|
8187
8294
|
try {
|
|
8188
|
-
const content = await
|
|
8295
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
8189
8296
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
8190
8297
|
let latest = null;
|
|
8191
8298
|
for (const line of lines) {
|
|
@@ -8207,7 +8314,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
8207
8314
|
const pump = async () => {
|
|
8208
8315
|
let emitted = 0;
|
|
8209
8316
|
try {
|
|
8210
|
-
const content = await
|
|
8317
|
+
const content = await readFile8(sessionFile, "utf-8");
|
|
8211
8318
|
const lines = content.split("\n");
|
|
8212
8319
|
const completeLines = content.endsWith("\n") ? lines : lines.slice(0, -1);
|
|
8213
8320
|
for (const line of completeLines) {
|
|
@@ -9070,7 +9177,7 @@ var ChatService = class {
|
|
|
9070
9177
|
}
|
|
9071
9178
|
async readSenders(chatId) {
|
|
9072
9179
|
try {
|
|
9073
|
-
const content = await
|
|
9180
|
+
const content = await readFile9(this.senderFilePath(chatId), "utf-8");
|
|
9074
9181
|
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
9075
9182
|
const senders = [];
|
|
9076
9183
|
for (const line of lines) {
|
|
@@ -9427,7 +9534,7 @@ var ChatService = class {
|
|
|
9427
9534
|
}
|
|
9428
9535
|
async loadChats() {
|
|
9429
9536
|
try {
|
|
9430
|
-
const content = await
|
|
9537
|
+
const content = await readFile9(CHATS_FILE, "utf-8");
|
|
9431
9538
|
return parsePersistedChatsContent(content);
|
|
9432
9539
|
} catch (error) {
|
|
9433
9540
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -9442,7 +9549,7 @@ var ChatService = class {
|
|
|
9442
9549
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
9443
9550
|
}
|
|
9444
9551
|
try {
|
|
9445
|
-
const backupContent = await
|
|
9552
|
+
const backupContent = await readFile9(CHATS_BACKUP_FILE, "utf-8");
|
|
9446
9553
|
return parsePersistedChatsContent(backupContent);
|
|
9447
9554
|
} catch (backupError) {
|
|
9448
9555
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -9517,7 +9624,7 @@ var ChatService = class {
|
|
|
9517
9624
|
|
|
9518
9625
|
// src/services/repo-file-service.ts
|
|
9519
9626
|
import { execFile as execFile2 } from "child_process";
|
|
9520
|
-
import { readFile as
|
|
9627
|
+
import { readFile as readFile10, realpath, stat as stat3 } from "fs/promises";
|
|
9521
9628
|
import { join as join16, resolve, extname } from "path";
|
|
9522
9629
|
var CACHE_TTL_MS = 3e4;
|
|
9523
9630
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
@@ -9707,7 +9814,7 @@ var RepoFileService = class {
|
|
|
9707
9814
|
tooLarge: true
|
|
9708
9815
|
};
|
|
9709
9816
|
}
|
|
9710
|
-
const content = await
|
|
9817
|
+
const content = await readFile10(fullPath, "utf-8");
|
|
9711
9818
|
return {
|
|
9712
9819
|
repoName,
|
|
9713
9820
|
path: filePath,
|
|
@@ -9789,11 +9896,11 @@ var RepoFileService = class {
|
|
|
9789
9896
|
// src/v1-routes.ts
|
|
9790
9897
|
import { Hono } from "hono";
|
|
9791
9898
|
import { z as z2 } from "zod";
|
|
9792
|
-
import { readdir as readdir6, stat as stat4, readFile as
|
|
9899
|
+
import { readdir as readdir6, stat as stat4, readFile as readFile14 } from "fs/promises";
|
|
9793
9900
|
import { join as join20, resolve as resolve2 } from "path";
|
|
9794
9901
|
|
|
9795
9902
|
// src/services/plan-service.ts
|
|
9796
|
-
import { readdir as readdir4, readFile as
|
|
9903
|
+
import { readdir as readdir4, readFile as readFile11 } from "fs/promises";
|
|
9797
9904
|
import { homedir as homedir14 } from "os";
|
|
9798
9905
|
import { basename, join as join17 } from "path";
|
|
9799
9906
|
var PLAN_DIRECTORIES = [
|
|
@@ -9834,7 +9941,7 @@ var PlanService = class {
|
|
|
9834
9941
|
for (const directory of PLAN_DIRECTORIES) {
|
|
9835
9942
|
const filePath = join17(directory, safeFilename);
|
|
9836
9943
|
try {
|
|
9837
|
-
const content = await
|
|
9944
|
+
const content = await readFile11(filePath, "utf-8");
|
|
9838
9945
|
return { filename: safeFilename, content };
|
|
9839
9946
|
} catch {
|
|
9840
9947
|
}
|
|
@@ -9845,13 +9952,13 @@ var PlanService = class {
|
|
|
9845
9952
|
var planService = new PlanService();
|
|
9846
9953
|
|
|
9847
9954
|
// src/services/warm-hooks-service.ts
|
|
9848
|
-
import { spawn as
|
|
9849
|
-
import { readFile as
|
|
9955
|
+
import { spawn as spawn4 } from "child_process";
|
|
9956
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
9850
9957
|
import { existsSync as existsSync9 } from "fs";
|
|
9851
9958
|
import { join as join19 } from "path";
|
|
9852
9959
|
|
|
9853
9960
|
// src/services/warm-hook-logs-service.ts
|
|
9854
|
-
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";
|
|
9855
9962
|
import { homedir as homedir15 } from "os";
|
|
9856
9963
|
import { join as join18 } from "path";
|
|
9857
9964
|
var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
|
|
@@ -9911,7 +10018,7 @@ var WarmHookLogsService = class {
|
|
|
9911
10018
|
continue;
|
|
9912
10019
|
}
|
|
9913
10020
|
try {
|
|
9914
|
-
const raw = await
|
|
10021
|
+
const raw = await readFile12(join18(LOGS_DIR2, file), "utf-8");
|
|
9915
10022
|
const stored = JSON.parse(raw);
|
|
9916
10023
|
logs.push(withPreview2(stored));
|
|
9917
10024
|
} catch {
|
|
@@ -9940,7 +10047,7 @@ var WarmHookLogsService = class {
|
|
|
9940
10047
|
}
|
|
9941
10048
|
async getCurrentRunLog() {
|
|
9942
10049
|
try {
|
|
9943
|
-
return await
|
|
10050
|
+
return await readFile12(CURRENT_RUN_LOG, "utf-8");
|
|
9944
10051
|
} catch (err) {
|
|
9945
10052
|
if (err.code === "ENOENT") return null;
|
|
9946
10053
|
throw err;
|
|
@@ -9949,7 +10056,7 @@ var WarmHookLogsService = class {
|
|
|
9949
10056
|
async getFullOutput(hookType, hookName) {
|
|
9950
10057
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
9951
10058
|
try {
|
|
9952
|
-
const raw = await
|
|
10059
|
+
const raw = await readFile12(join18(LOGS_DIR2, filename), "utf-8");
|
|
9953
10060
|
const stored = JSON.parse(raw);
|
|
9954
10061
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
9955
10062
|
return null;
|
|
@@ -9973,7 +10080,7 @@ async function readRepoWarmHook(repoPath) {
|
|
|
9973
10080
|
continue;
|
|
9974
10081
|
}
|
|
9975
10082
|
try {
|
|
9976
|
-
const raw = await
|
|
10083
|
+
const raw = await readFile13(configPath, "utf-8");
|
|
9977
10084
|
const config = parseReplicasConfigString(raw, filename);
|
|
9978
10085
|
if (!config.warmHook) {
|
|
9979
10086
|
return null;
|
|
@@ -10020,7 +10127,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
10020
10127
|
params.onChunk(`$ ${params.label}
|
|
10021
10128
|
`);
|
|
10022
10129
|
return new Promise((resolve3) => {
|
|
10023
|
-
const proc =
|
|
10130
|
+
const proc = spawn4("bash", ["-lc", params.content], {
|
|
10024
10131
|
cwd: params.cwd,
|
|
10025
10132
|
env: process.env,
|
|
10026
10133
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -10903,7 +11010,7 @@ function createV1Routes(deps) {
|
|
|
10903
11010
|
const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
|
|
10904
11011
|
let content;
|
|
10905
11012
|
try {
|
|
10906
|
-
content = await
|
|
11013
|
+
content = await readFile14(filePath, "utf-8");
|
|
10907
11014
|
} catch {
|
|
10908
11015
|
return c.json(jsonError("Log session not found"), 404);
|
|
10909
11016
|
}
|
|
@@ -10943,12 +11050,10 @@ process.on("unhandledRejection", (reason) => {
|
|
|
10943
11050
|
engineLogger.flush().finally(() => process.exit(1));
|
|
10944
11051
|
});
|
|
10945
11052
|
await eventService.initialize();
|
|
10946
|
-
|
|
10947
|
-
var COMPLETION_MESSAGE = "========= REPLICAS WORKSPACE INITIALIZATION COMPLETE ==========";
|
|
10948
|
-
function checkActiveSSHSessions() {
|
|
11053
|
+
async function checkActiveSSHSessions() {
|
|
10949
11054
|
try {
|
|
10950
|
-
const
|
|
10951
|
-
const sessionCount = parseInt(
|
|
11055
|
+
const { stdout } = await execAsync('who | grep -v "^$" | wc -l', { encoding: "utf-8" });
|
|
11056
|
+
const sessionCount = parseInt(stdout.trim(), 10);
|
|
10952
11057
|
return sessionCount > 0;
|
|
10953
11058
|
} catch {
|
|
10954
11059
|
return false;
|
|
@@ -10972,32 +11077,22 @@ var authMiddleware = async (c, next) => {
|
|
|
10972
11077
|
await next();
|
|
10973
11078
|
};
|
|
10974
11079
|
var chatService = new ChatService(gitService.getWorkspaceRoot());
|
|
10975
|
-
app.get("/health",
|
|
11080
|
+
app.get("/health", (c) => {
|
|
10976
11081
|
if (!engineReady) {
|
|
10977
11082
|
return c.json({ status: "initializing", timestamp: (/* @__PURE__ */ new Date()).toISOString() }, 503);
|
|
10978
11083
|
}
|
|
10979
|
-
|
|
10980
|
-
const logContent = await readFile14("/var/log/cloud-init-output.log", "utf-8");
|
|
10981
|
-
let status;
|
|
10982
|
-
if (logContent.includes(COMPLETION_MESSAGE)) {
|
|
10983
|
-
status = "active";
|
|
10984
|
-
} else if (logContent.includes(READY_MESSAGE)) {
|
|
10985
|
-
status = "ready";
|
|
10986
|
-
} else {
|
|
10987
|
-
status = "initializing";
|
|
10988
|
-
}
|
|
10989
|
-
return c.json({ status, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
10990
|
-
} catch {
|
|
10991
|
-
return c.json({ status: "initializing", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
10992
|
-
}
|
|
11084
|
+
return c.json({ status: "active", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
10993
11085
|
});
|
|
10994
11086
|
app.use("*", authMiddleware);
|
|
10995
11087
|
app.get("/status", async (c) => {
|
|
10996
11088
|
try {
|
|
10997
|
-
const repos = await
|
|
11089
|
+
const [repos, hasActiveSSHSessions] = await Promise.all([
|
|
11090
|
+
gitService.listRepos(),
|
|
11091
|
+
checkActiveSSHSessions()
|
|
11092
|
+
]);
|
|
10998
11093
|
const status = {
|
|
10999
11094
|
uptimeSeconds: Math.floor((Date.now() - bootTimeMs) / 1e3),
|
|
11000
|
-
hasActiveSSHSessions
|
|
11095
|
+
hasActiveSSHSessions,
|
|
11001
11096
|
activeSseClients: eventService.getSubscriberCount(),
|
|
11002
11097
|
chatsTotal: chatService.listChats().length,
|
|
11003
11098
|
chatsProcessing: chatService.getProcessingCount(),
|
|
@@ -11027,8 +11122,16 @@ function startStatusBroadcaster() {
|
|
|
11027
11122
|
let previousHookStatus = "";
|
|
11028
11123
|
let previousEngineStatus = "";
|
|
11029
11124
|
let lastHooksRunning = replicasConfigService.areHooksRunning();
|
|
11030
|
-
|
|
11031
|
-
|
|
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
|
+
]);
|
|
11032
11135
|
const serialized = JSON.stringify(repos);
|
|
11033
11136
|
if (serialized !== previousRepoStatus) {
|
|
11034
11137
|
previousRepoStatus = serialized;
|
|
@@ -11042,7 +11145,7 @@ function startStatusBroadcaster() {
|
|
|
11042
11145
|
}
|
|
11043
11146
|
const engineStatus = {
|
|
11044
11147
|
uptimeSeconds: Math.floor((Date.now() - bootTimeMs) / 1e3),
|
|
11045
|
-
hasActiveSSHSessions
|
|
11148
|
+
hasActiveSSHSessions,
|
|
11046
11149
|
activeSseClients: eventService.getSubscriberCount(),
|
|
11047
11150
|
chatsTotal: chatService.listChats().length,
|
|
11048
11151
|
chatsProcessing: chatService.getProcessingCount(),
|
|
@@ -11060,67 +11163,70 @@ function startStatusBroadcaster() {
|
|
|
11060
11163
|
}).catch(() => {
|
|
11061
11164
|
});
|
|
11062
11165
|
}
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
}
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
}
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
}
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
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
|
+
}
|
|
11103
11212
|
eventService.publish({
|
|
11104
11213
|
id: randomUUID6(),
|
|
11105
11214
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11106
|
-
type: "hooks.
|
|
11107
|
-
payload: {
|
|
11215
|
+
type: "hooks.status",
|
|
11216
|
+
payload: {
|
|
11217
|
+
running: hooksRunning,
|
|
11218
|
+
completed: hooksCompleted
|
|
11219
|
+
}
|
|
11108
11220
|
}).catch(() => {
|
|
11109
11221
|
});
|
|
11222
|
+
lastHooksRunning = hooksRunning;
|
|
11110
11223
|
}
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
type: "hooks.status",
|
|
11115
|
-
payload: {
|
|
11116
|
-
running: hooksRunning,
|
|
11117
|
-
completed: hooksCompleted
|
|
11118
|
-
}
|
|
11119
|
-
}).catch(() => {
|
|
11120
|
-
});
|
|
11121
|
-
lastHooksRunning = hooksRunning;
|
|
11224
|
+
} catch {
|
|
11225
|
+
} finally {
|
|
11226
|
+
scheduleNext();
|
|
11122
11227
|
}
|
|
11123
|
-
}
|
|
11228
|
+
};
|
|
11229
|
+
scheduleNext();
|
|
11124
11230
|
}
|
|
11125
11231
|
serve(
|
|
11126
11232
|
{
|