@ricsam/r5d-worker 0.0.24 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/main.cjs +241 -27
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +239 -27
- package/dist/mjs/package.json +1 -1
- package/dist/types/main.d.ts +51 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The worker uses the existing `r5dctl` config file and accepts `R5D_WORKER_TOKEN`
|
|
|
15
15
|
|
|
16
16
|
Web shells and agent shell commands receive a server-issued `r5dctl` credential automatically. The credential is scoped to the signed-in user and revoked when the shell or command finishes, so commands such as `r5dctl auth status` do not require a separate login on the worker host.
|
|
17
17
|
|
|
18
|
-
Visible branch checkouts are the user/agent workbench.
|
|
18
|
+
Visible branch checkouts are the user/agent workbench. Their `origin` remains the connected GitHub repository for manual Git work. r5d internal sync git metadata lives separately under `~/.r5d/sync`; when a worker connects, every idle registered branch is force-synchronized from that internal remote. The same destructive sync can be requested for a live worker from User Settings.
|
|
19
19
|
|
|
20
20
|
Worker labels are mandatory and unique per user. Choose labels that describe host capabilities, such as `macos`, `linux`, `ios`, or `ec2-build`.
|
|
21
21
|
|
package/dist/cjs/main.cjs
CHANGED
|
@@ -30,11 +30,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
var main_exports = {};
|
|
31
31
|
__export(main_exports, {
|
|
32
32
|
ensureVisibleGitCheckout: () => ensureVisibleGitCheckout,
|
|
33
|
+
findRepositorySyncBlockers: () => findRepositorySyncBlockers,
|
|
33
34
|
isArtifactEnvPath: () => isArtifactEnvPath,
|
|
34
35
|
prepareArtifactEnvForShell: () => prepareArtifactEnvForShell,
|
|
35
36
|
preparePlanEnvForShell: () => preparePlanEnvForShell,
|
|
36
37
|
resolveHostShell: () => resolveHostShell,
|
|
37
38
|
resolveProjectFilePath: () => resolveProjectFilePath,
|
|
39
|
+
syncManifestProjectsFromInternal: () => syncManifestProjectsFromInternal,
|
|
38
40
|
syncProjectPlans: () => syncProjectPlans,
|
|
39
41
|
syncSessionArtifacts: () => syncSessionArtifacts
|
|
40
42
|
});
|
|
@@ -627,6 +629,27 @@ function runGit(args, options = {}) {
|
|
|
627
629
|
}
|
|
628
630
|
return result.stdout.toString().trim();
|
|
629
631
|
}
|
|
632
|
+
async function runGitAsync(args, options = {}) {
|
|
633
|
+
const command = ["git", ...gitAuthArgs(options.auth), ...args];
|
|
634
|
+
const subprocess = Bun.spawn(command, {
|
|
635
|
+
cwd: options.cwd,
|
|
636
|
+
stdout: "pipe",
|
|
637
|
+
stderr: "pipe",
|
|
638
|
+
env: {
|
|
639
|
+
...process.env,
|
|
640
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
644
|
+
collectStream(subprocess.stdout),
|
|
645
|
+
collectStream(subprocess.stderr),
|
|
646
|
+
subprocess.exited
|
|
647
|
+
]);
|
|
648
|
+
if (exitCode !== 0) {
|
|
649
|
+
throw new Error(`git ${args.join(" ")} failed: ${stderr.trim() || stdout.trim() || `exit ${exitCode}`}`);
|
|
650
|
+
}
|
|
651
|
+
return stdout.trim();
|
|
652
|
+
}
|
|
630
653
|
function tryGit(args, options = {}) {
|
|
631
654
|
try {
|
|
632
655
|
runGit(args, options);
|
|
@@ -635,9 +658,20 @@ function tryGit(args, options = {}) {
|
|
|
635
658
|
return false;
|
|
636
659
|
}
|
|
637
660
|
}
|
|
661
|
+
async function tryGitAsync(args, options = {}) {
|
|
662
|
+
try {
|
|
663
|
+
await runGitAsync(args, options);
|
|
664
|
+
return true;
|
|
665
|
+
} catch {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
638
669
|
function runInternalGit(context, args) {
|
|
639
670
|
return runGit(["--git-dir", context.gitDir, "--work-tree", context.workTree, ...args], { auth: context.auth });
|
|
640
671
|
}
|
|
672
|
+
function runInternalGitAsync(context, args) {
|
|
673
|
+
return runGitAsync(["--git-dir", context.gitDir, "--work-tree", context.workTree, ...args], { auth: context.auth });
|
|
674
|
+
}
|
|
641
675
|
function runInternalGitDir(context, args) {
|
|
642
676
|
return runGit(["--git-dir", context.gitDir, ...args], { auth: context.auth });
|
|
643
677
|
}
|
|
@@ -754,6 +788,13 @@ function ensureOriginRemote(branchPath, remoteUrl) {
|
|
|
754
788
|
runGit(["remote", "add", "origin", remoteUrl], { cwd: branchPath });
|
|
755
789
|
}
|
|
756
790
|
}
|
|
791
|
+
async function ensureOriginRemoteAsync(branchPath, remoteUrl) {
|
|
792
|
+
if (await tryGitAsync(["remote", "get-url", "origin"], { cwd: branchPath })) {
|
|
793
|
+
await runGitAsync(["remote", "set-url", "origin", remoteUrl], { cwd: branchPath });
|
|
794
|
+
} else {
|
|
795
|
+
await runGitAsync(["remote", "add", "origin", remoteUrl], { cwd: branchPath });
|
|
796
|
+
}
|
|
797
|
+
}
|
|
757
798
|
function shellQuote(value) {
|
|
758
799
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
759
800
|
}
|
|
@@ -926,6 +967,9 @@ function ensureVisibleGitCheckout(input) {
|
|
|
926
967
|
checkoutVisibleBranch({ branchPath, branchName: input.branchName, defaultBranch: input.defaultBranch });
|
|
927
968
|
return branchPath;
|
|
928
969
|
}
|
|
970
|
+
if (input.reconcileExistingOrigin) {
|
|
971
|
+
ensureOriginRemote(branchPath, input.githubRemoteUrl);
|
|
972
|
+
}
|
|
929
973
|
configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
|
|
930
974
|
(0, import_git_identity.configureVisibleGitIdentity)(branchPath, visibleGitIdentity, runGit);
|
|
931
975
|
return branchPath;
|
|
@@ -972,7 +1016,8 @@ function ensureBranchWorkspace(input) {
|
|
|
972
1016
|
githubRemoteUrl,
|
|
973
1017
|
githubAuth,
|
|
974
1018
|
githubAuthHeader: input.manifest?.repoAuthHeader ?? null,
|
|
975
|
-
defaultBranch
|
|
1019
|
+
defaultBranch,
|
|
1020
|
+
reconcileExistingOrigin: Boolean(input.manifest?.repoHttpUrl)
|
|
976
1021
|
});
|
|
977
1022
|
const internalGit = ensureInternalSyncGit({
|
|
978
1023
|
projectId: input.projectId,
|
|
@@ -985,6 +1030,140 @@ function ensureBranchWorkspace(input) {
|
|
|
985
1030
|
});
|
|
986
1031
|
return { branchPath, internalGit };
|
|
987
1032
|
}
|
|
1033
|
+
async function ensureVisibleGitCheckoutForRepositorySync(input) {
|
|
1034
|
+
validateBranchName(input.branchName);
|
|
1035
|
+
import_node_fs.default.mkdirSync(input.projectRoot, { recursive: true });
|
|
1036
|
+
const branchPath = import_node_path.default.join(input.projectRoot, input.branchName);
|
|
1037
|
+
const createdCheckout = !hasNormalVisibleGitDir(branchPath);
|
|
1038
|
+
if (createdCheckout) {
|
|
1039
|
+
import_node_fs.default.rmSync(branchPath, { recursive: true, force: true });
|
|
1040
|
+
import_node_fs.default.mkdirSync(import_node_path.default.dirname(branchPath), { recursive: true });
|
|
1041
|
+
if (!await tryGitAsync(["clone", "--origin", "origin", input.githubRemoteUrl, branchPath], { auth: input.githubAuth })) {
|
|
1042
|
+
import_node_fs.default.mkdirSync(branchPath, { recursive: true });
|
|
1043
|
+
await runGitAsync(["init"], { cwd: branchPath });
|
|
1044
|
+
}
|
|
1045
|
+
await ensureOriginRemoteAsync(branchPath, input.githubRemoteUrl);
|
|
1046
|
+
configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
|
|
1047
|
+
(0, import_git_identity.configureVisibleGitIdentity)(branchPath, visibleGitIdentity, runGit);
|
|
1048
|
+
await tryGitAsync(["fetch", "origin", "--prune"], { cwd: branchPath, auth: input.githubAuth });
|
|
1049
|
+
checkoutVisibleBranch({ branchPath, branchName: input.branchName, defaultBranch: input.defaultBranch });
|
|
1050
|
+
return branchPath;
|
|
1051
|
+
}
|
|
1052
|
+
if (input.reconcileExistingOrigin) {
|
|
1053
|
+
await ensureOriginRemoteAsync(branchPath, input.githubRemoteUrl);
|
|
1054
|
+
}
|
|
1055
|
+
configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
|
|
1056
|
+
(0, import_git_identity.configureVisibleGitIdentity)(branchPath, visibleGitIdentity, runGit);
|
|
1057
|
+
return branchPath;
|
|
1058
|
+
}
|
|
1059
|
+
async function forceSyncManifestBranchFromInternal(input) {
|
|
1060
|
+
const githubRemoteUrl = githubRemoteUrlFor(input.baseUrl, input.projectId, input.manifest);
|
|
1061
|
+
const githubAuth = gitAuthForRemote(githubRemoteUrl, input.manifest.repoAuthHeader);
|
|
1062
|
+
const branchPath = await ensureVisibleGitCheckoutForRepositorySync({
|
|
1063
|
+
projectRoot: input.projectRoot,
|
|
1064
|
+
branchName: input.branchName,
|
|
1065
|
+
githubRemoteUrl,
|
|
1066
|
+
githubAuth,
|
|
1067
|
+
githubAuthHeader: input.manifest.repoAuthHeader,
|
|
1068
|
+
defaultBranch: input.manifest.defaultBranch || "main",
|
|
1069
|
+
reconcileExistingOrigin: Boolean(input.manifest.repoHttpUrl)
|
|
1070
|
+
});
|
|
1071
|
+
const gitDir = internalGitDirFor(input.syncRoot, input.projectId, input.branchName);
|
|
1072
|
+
import_node_fs.default.mkdirSync(import_node_path.default.dirname(gitDir), { recursive: true });
|
|
1073
|
+
if (!import_node_fs.default.existsSync(gitDir)) {
|
|
1074
|
+
await runGitAsync(["init", "--bare", gitDir]);
|
|
1075
|
+
}
|
|
1076
|
+
const context = {
|
|
1077
|
+
gitDir,
|
|
1078
|
+
workTree: branchPath,
|
|
1079
|
+
auth: internalGitAuth(input.baseUrl, input.token)
|
|
1080
|
+
};
|
|
1081
|
+
configureInternalRepo(context, internalRemoteUrlFor(input.baseUrl, input.projectId, input.manifest));
|
|
1082
|
+
await runInternalGitAsync(context, ["fetch", R5D_REMOTE_NAME, "--prune", `+refs/heads/*:refs/remotes/${R5D_REMOTE_NAME}/*`]);
|
|
1083
|
+
const branchTarget = `refs/remotes/${R5D_REMOTE_NAME}/${input.branchName}`;
|
|
1084
|
+
const mainTarget = `refs/remotes/${R5D_REMOTE_NAME}/main`;
|
|
1085
|
+
const target = tryInternalGit(context, ["show-ref", "--verify", "--quiet", branchTarget]) ? branchTarget : tryInternalGit(context, ["show-ref", "--verify", "--quiet", mainTarget]) ? mainTarget : null;
|
|
1086
|
+
if (!target) {
|
|
1087
|
+
throw new Error(`Internal remote has neither ${input.branchName} nor main`);
|
|
1088
|
+
}
|
|
1089
|
+
await runInternalGitAsync(context, ["reset", "--hard", target]);
|
|
1090
|
+
await runInternalGitAsync(context, ["clean", "-fd", "--", ".", ":(exclude).git"]);
|
|
1091
|
+
return getInternalCommitHash(context);
|
|
1092
|
+
}
|
|
1093
|
+
function findRepositorySyncBlockers(input) {
|
|
1094
|
+
const targetByKey = new Map(input.targets.map((target) => [`${target.projectId}:${target.branchName}`, target]));
|
|
1095
|
+
const blockedBy = [];
|
|
1096
|
+
for (const process2 of input.processes) {
|
|
1097
|
+
const target = targetByKey.get(`${process2.projectId}:${process2.branchName}`);
|
|
1098
|
+
if (target) blockedBy.push({ ...target, kind: "process", id: process2.id });
|
|
1099
|
+
}
|
|
1100
|
+
for (const shell of input.shells) {
|
|
1101
|
+
const target = targetByKey.get(`${shell.projectId}:${shell.branchName}`);
|
|
1102
|
+
if (target) blockedBy.push({ ...target, kind: "shell", id: shell.id });
|
|
1103
|
+
}
|
|
1104
|
+
return blockedBy;
|
|
1105
|
+
}
|
|
1106
|
+
async function syncManifestProjectsFromInternal(input) {
|
|
1107
|
+
const requestedProjectIds = input.projectIds ? new Set(input.projectIds) : null;
|
|
1108
|
+
const targets = input.manifests.filter((manifest) => !requestedProjectIds || requestedProjectIds.has(manifest.projectId)).flatMap((manifest) => {
|
|
1109
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
1110
|
+
return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
|
|
1111
|
+
});
|
|
1112
|
+
const blockedBy = findRepositorySyncBlockers({
|
|
1113
|
+
targets: targets.map(({ manifest, branchName }) => ({
|
|
1114
|
+
projectId: manifest.projectId,
|
|
1115
|
+
projectPath: manifest.projectPath,
|
|
1116
|
+
branchName
|
|
1117
|
+
})),
|
|
1118
|
+
processes: [...activeProcesses].map(([id, active]) => ({
|
|
1119
|
+
id,
|
|
1120
|
+
projectId: active.projectId,
|
|
1121
|
+
branchName: active.branchName
|
|
1122
|
+
})),
|
|
1123
|
+
shells: [...activePtys].map(([id, active]) => ({
|
|
1124
|
+
id,
|
|
1125
|
+
projectId: active.projectId,
|
|
1126
|
+
branchName: active.branchName
|
|
1127
|
+
}))
|
|
1128
|
+
});
|
|
1129
|
+
if (blockedBy.length > 0) {
|
|
1130
|
+
return { status: "blocked", results: [], blockedBy };
|
|
1131
|
+
}
|
|
1132
|
+
const results = [];
|
|
1133
|
+
for (const { manifest, branchName } of targets) {
|
|
1134
|
+
try {
|
|
1135
|
+
const commitHash = await forceSyncManifestBranchFromInternal({
|
|
1136
|
+
projectId: manifest.projectId,
|
|
1137
|
+
baseUrl: input.baseUrl,
|
|
1138
|
+
token: input.token,
|
|
1139
|
+
projectRoot: import_node_path.default.join(input.projectsRoot, manifest.repoSlug || manifest.projectId),
|
|
1140
|
+
syncRoot: input.syncRoot,
|
|
1141
|
+
branchName,
|
|
1142
|
+
manifest
|
|
1143
|
+
});
|
|
1144
|
+
results.push({
|
|
1145
|
+
projectId: manifest.projectId,
|
|
1146
|
+
projectPath: manifest.projectPath,
|
|
1147
|
+
branchName,
|
|
1148
|
+
status: "synced",
|
|
1149
|
+
commitHash
|
|
1150
|
+
});
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
results.push({
|
|
1153
|
+
projectId: manifest.projectId,
|
|
1154
|
+
projectPath: manifest.projectPath,
|
|
1155
|
+
branchName,
|
|
1156
|
+
status: "failed",
|
|
1157
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
status: results.some((result) => result.status === "failed") ? "partial" : "completed",
|
|
1163
|
+
results,
|
|
1164
|
+
blockedBy: []
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
988
1167
|
function resolveCommandCwd(branchPath, cwd) {
|
|
989
1168
|
if (!cwd) {
|
|
990
1169
|
return branchPath;
|
|
@@ -1747,6 +1926,7 @@ async function executeStreamingCommand(input) {
|
|
|
1747
1926
|
projectId: input.projectId,
|
|
1748
1927
|
sessionId: input.message.sessionId,
|
|
1749
1928
|
branchName: input.message.branchName,
|
|
1929
|
+
credentialId: input.message.credentialId,
|
|
1750
1930
|
argv: input.message.argv,
|
|
1751
1931
|
command: input.message.command,
|
|
1752
1932
|
cwd: input.message.cwd,
|
|
@@ -1820,6 +2000,7 @@ function buildActiveProcessReports() {
|
|
|
1820
2000
|
projectId: active.projectId,
|
|
1821
2001
|
sessionId: active.sessionId,
|
|
1822
2002
|
branchName: active.branchName,
|
|
2003
|
+
...active.credentialId ? { credentialId: active.credentialId } : {},
|
|
1823
2004
|
argv: active.argv,
|
|
1824
2005
|
command: active.command,
|
|
1825
2006
|
...active.cwd ? { cwd: active.cwd } : {},
|
|
@@ -2089,7 +2270,11 @@ async function openPty(input) {
|
|
|
2089
2270
|
});
|
|
2090
2271
|
}
|
|
2091
2272
|
});
|
|
2092
|
-
activePtys.set(input.message.ptyId,
|
|
2273
|
+
activePtys.set(input.message.ptyId, {
|
|
2274
|
+
...ptyProcess,
|
|
2275
|
+
projectId: input.projectId,
|
|
2276
|
+
branchName: input.message.branchName
|
|
2277
|
+
});
|
|
2093
2278
|
} catch (error) {
|
|
2094
2279
|
sendWorkerMessage(input.ws, {
|
|
2095
2280
|
type: "pty_error",
|
|
@@ -2172,6 +2357,7 @@ async function startWorker(options) {
|
|
|
2172
2357
|
import_node_fs.default.mkdirSync(artifactRoot, { recursive: true });
|
|
2173
2358
|
import_node_fs.default.mkdirSync(planRoot, { recursive: true });
|
|
2174
2359
|
const manifestByProjectId = /* @__PURE__ */ new Map();
|
|
2360
|
+
let repositorySyncQueue = Promise.resolve();
|
|
2175
2361
|
const ws = new WebSocket(websocketUrl(baseUrl, label), {
|
|
2176
2362
|
headers: {
|
|
2177
2363
|
Authorization: `Bearer ${token}`
|
|
@@ -2229,42 +2415,64 @@ async function startWorker(options) {
|
|
|
2229
2415
|
}
|
|
2230
2416
|
process.stdout.write(`[r5d-worker] project manifest: ${message.projects.length} projects
|
|
2231
2417
|
`);
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2418
|
+
return;
|
|
2419
|
+
}
|
|
2420
|
+
if (message.type === "sync_projects") {
|
|
2421
|
+
let syncResult = { status: "completed", results: [], blockedBy: [] };
|
|
2422
|
+
const executeSync = async () => {
|
|
2423
|
+
syncResult = await syncManifestProjectsFromInternal({
|
|
2424
|
+
baseUrl,
|
|
2425
|
+
token,
|
|
2426
|
+
projectsRoot,
|
|
2427
|
+
syncRoot,
|
|
2428
|
+
manifests: [...manifestByProjectId.values()],
|
|
2429
|
+
projectIds: message.projectIds
|
|
2430
|
+
});
|
|
2431
|
+
for (const result of syncResult.results) {
|
|
2432
|
+
if (result.status !== "synced") continue;
|
|
2236
2433
|
try {
|
|
2237
|
-
|
|
2238
|
-
projectId: project.projectId,
|
|
2434
|
+
await syncProjectPlans({
|
|
2239
2435
|
baseUrl,
|
|
2240
2436
|
token,
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
manifest: project
|
|
2437
|
+
projectId: result.projectId,
|
|
2438
|
+
branchName: result.branchName,
|
|
2439
|
+
planRoot
|
|
2245
2440
|
});
|
|
2246
|
-
try {
|
|
2247
|
-
await syncProjectPlans({
|
|
2248
|
-
baseUrl,
|
|
2249
|
-
token,
|
|
2250
|
-
projectId: project.projectId,
|
|
2251
|
-
branchName,
|
|
2252
|
-
planRoot
|
|
2253
|
-
});
|
|
2254
|
-
} catch (error) {
|
|
2255
|
-
process.stderr.write(
|
|
2256
|
-
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2257
|
-
`
|
|
2258
|
-
);
|
|
2259
|
-
}
|
|
2260
2441
|
} catch (error) {
|
|
2261
2442
|
process.stderr.write(
|
|
2262
|
-
`[r5d-worker]
|
|
2443
|
+
`[r5d-worker] plan sync skipped for ${result.projectId}/${result.branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2263
2444
|
`
|
|
2264
2445
|
);
|
|
2265
2446
|
}
|
|
2266
2447
|
}
|
|
2448
|
+
};
|
|
2449
|
+
const queued = repositorySyncQueue.then(executeSync, executeSync);
|
|
2450
|
+
repositorySyncQueue = queued.then(
|
|
2451
|
+
() => void 0,
|
|
2452
|
+
() => void 0
|
|
2453
|
+
);
|
|
2454
|
+
try {
|
|
2455
|
+
await queued;
|
|
2456
|
+
} catch (error) {
|
|
2457
|
+
syncResult = {
|
|
2458
|
+
status: "partial",
|
|
2459
|
+
results: [
|
|
2460
|
+
{
|
|
2461
|
+
projectId: "worker",
|
|
2462
|
+
projectPath: "worker",
|
|
2463
|
+
branchName: "all",
|
|
2464
|
+
status: "failed",
|
|
2465
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2466
|
+
}
|
|
2467
|
+
],
|
|
2468
|
+
blockedBy: []
|
|
2469
|
+
};
|
|
2267
2470
|
}
|
|
2471
|
+
sendWorkerMessage(ws, {
|
|
2472
|
+
type: "sync_projects_result",
|
|
2473
|
+
requestId: message.requestId,
|
|
2474
|
+
result: syncResult
|
|
2475
|
+
});
|
|
2268
2476
|
return;
|
|
2269
2477
|
}
|
|
2270
2478
|
if (message.type === "ping") {
|
|
@@ -2291,6 +2499,7 @@ async function startWorker(options) {
|
|
|
2291
2499
|
return;
|
|
2292
2500
|
}
|
|
2293
2501
|
if (message.type === "pty_open") {
|
|
2502
|
+
await repositorySyncQueue;
|
|
2294
2503
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2295
2504
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2296
2505
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${message.projectId}/${message.branchName}
|
|
@@ -2311,6 +2520,7 @@ async function startWorker(options) {
|
|
|
2311
2520
|
return;
|
|
2312
2521
|
}
|
|
2313
2522
|
if (message.type === "exec") {
|
|
2523
|
+
await repositorySyncQueue;
|
|
2314
2524
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2315
2525
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2316
2526
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
@@ -2330,6 +2540,7 @@ async function startWorker(options) {
|
|
|
2330
2540
|
return;
|
|
2331
2541
|
}
|
|
2332
2542
|
if (message.type === "exec_start") {
|
|
2543
|
+
await repositorySyncQueue;
|
|
2333
2544
|
sendWorkerMessage(ws, {
|
|
2334
2545
|
type: "exec_accepted",
|
|
2335
2546
|
requestId: message.requestId,
|
|
@@ -2371,6 +2582,7 @@ async function startWorker(options) {
|
|
|
2371
2582
|
}
|
|
2372
2583
|
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "sync" || message.type === "confirm_large_diff" || message.type === "pull_branch") {
|
|
2373
2584
|
try {
|
|
2585
|
+
await repositorySyncQueue;
|
|
2374
2586
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2375
2587
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2376
2588
|
const result = await executeOperation({
|
|
@@ -2481,11 +2693,13 @@ if (isCliEntrypoint()) {
|
|
|
2481
2693
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2482
2694
|
0 && (module.exports = {
|
|
2483
2695
|
ensureVisibleGitCheckout,
|
|
2696
|
+
findRepositorySyncBlockers,
|
|
2484
2697
|
isArtifactEnvPath,
|
|
2485
2698
|
prepareArtifactEnvForShell,
|
|
2486
2699
|
preparePlanEnvForShell,
|
|
2487
2700
|
resolveHostShell,
|
|
2488
2701
|
resolveProjectFilePath,
|
|
2702
|
+
syncManifestProjectsFromInternal,
|
|
2489
2703
|
syncProjectPlans,
|
|
2490
2704
|
syncSessionArtifacts
|
|
2491
2705
|
});
|
package/dist/cjs/package.json
CHANGED
package/dist/mjs/main.mjs
CHANGED
|
@@ -586,6 +586,27 @@ function runGit(args, options = {}) {
|
|
|
586
586
|
}
|
|
587
587
|
return result.stdout.toString().trim();
|
|
588
588
|
}
|
|
589
|
+
async function runGitAsync(args, options = {}) {
|
|
590
|
+
const command = ["git", ...gitAuthArgs(options.auth), ...args];
|
|
591
|
+
const subprocess = Bun.spawn(command, {
|
|
592
|
+
cwd: options.cwd,
|
|
593
|
+
stdout: "pipe",
|
|
594
|
+
stderr: "pipe",
|
|
595
|
+
env: {
|
|
596
|
+
...process.env,
|
|
597
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
601
|
+
collectStream(subprocess.stdout),
|
|
602
|
+
collectStream(subprocess.stderr),
|
|
603
|
+
subprocess.exited
|
|
604
|
+
]);
|
|
605
|
+
if (exitCode !== 0) {
|
|
606
|
+
throw new Error(`git ${args.join(" ")} failed: ${stderr.trim() || stdout.trim() || `exit ${exitCode}`}`);
|
|
607
|
+
}
|
|
608
|
+
return stdout.trim();
|
|
609
|
+
}
|
|
589
610
|
function tryGit(args, options = {}) {
|
|
590
611
|
try {
|
|
591
612
|
runGit(args, options);
|
|
@@ -594,9 +615,20 @@ function tryGit(args, options = {}) {
|
|
|
594
615
|
return false;
|
|
595
616
|
}
|
|
596
617
|
}
|
|
618
|
+
async function tryGitAsync(args, options = {}) {
|
|
619
|
+
try {
|
|
620
|
+
await runGitAsync(args, options);
|
|
621
|
+
return true;
|
|
622
|
+
} catch {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
597
626
|
function runInternalGit(context, args) {
|
|
598
627
|
return runGit(["--git-dir", context.gitDir, "--work-tree", context.workTree, ...args], { auth: context.auth });
|
|
599
628
|
}
|
|
629
|
+
function runInternalGitAsync(context, args) {
|
|
630
|
+
return runGitAsync(["--git-dir", context.gitDir, "--work-tree", context.workTree, ...args], { auth: context.auth });
|
|
631
|
+
}
|
|
600
632
|
function runInternalGitDir(context, args) {
|
|
601
633
|
return runGit(["--git-dir", context.gitDir, ...args], { auth: context.auth });
|
|
602
634
|
}
|
|
@@ -713,6 +745,13 @@ function ensureOriginRemote(branchPath, remoteUrl) {
|
|
|
713
745
|
runGit(["remote", "add", "origin", remoteUrl], { cwd: branchPath });
|
|
714
746
|
}
|
|
715
747
|
}
|
|
748
|
+
async function ensureOriginRemoteAsync(branchPath, remoteUrl) {
|
|
749
|
+
if (await tryGitAsync(["remote", "get-url", "origin"], { cwd: branchPath })) {
|
|
750
|
+
await runGitAsync(["remote", "set-url", "origin", remoteUrl], { cwd: branchPath });
|
|
751
|
+
} else {
|
|
752
|
+
await runGitAsync(["remote", "add", "origin", remoteUrl], { cwd: branchPath });
|
|
753
|
+
}
|
|
754
|
+
}
|
|
716
755
|
function shellQuote(value) {
|
|
717
756
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
718
757
|
}
|
|
@@ -885,6 +924,9 @@ function ensureVisibleGitCheckout(input) {
|
|
|
885
924
|
checkoutVisibleBranch({ branchPath, branchName: input.branchName, defaultBranch: input.defaultBranch });
|
|
886
925
|
return branchPath;
|
|
887
926
|
}
|
|
927
|
+
if (input.reconcileExistingOrigin) {
|
|
928
|
+
ensureOriginRemote(branchPath, input.githubRemoteUrl);
|
|
929
|
+
}
|
|
888
930
|
configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
|
|
889
931
|
configureVisibleGitIdentity(branchPath, visibleGitIdentity, runGit);
|
|
890
932
|
return branchPath;
|
|
@@ -931,7 +973,8 @@ function ensureBranchWorkspace(input) {
|
|
|
931
973
|
githubRemoteUrl,
|
|
932
974
|
githubAuth,
|
|
933
975
|
githubAuthHeader: input.manifest?.repoAuthHeader ?? null,
|
|
934
|
-
defaultBranch
|
|
976
|
+
defaultBranch,
|
|
977
|
+
reconcileExistingOrigin: Boolean(input.manifest?.repoHttpUrl)
|
|
935
978
|
});
|
|
936
979
|
const internalGit = ensureInternalSyncGit({
|
|
937
980
|
projectId: input.projectId,
|
|
@@ -944,6 +987,140 @@ function ensureBranchWorkspace(input) {
|
|
|
944
987
|
});
|
|
945
988
|
return { branchPath, internalGit };
|
|
946
989
|
}
|
|
990
|
+
async function ensureVisibleGitCheckoutForRepositorySync(input) {
|
|
991
|
+
validateBranchName(input.branchName);
|
|
992
|
+
fs.mkdirSync(input.projectRoot, { recursive: true });
|
|
993
|
+
const branchPath = path.join(input.projectRoot, input.branchName);
|
|
994
|
+
const createdCheckout = !hasNormalVisibleGitDir(branchPath);
|
|
995
|
+
if (createdCheckout) {
|
|
996
|
+
fs.rmSync(branchPath, { recursive: true, force: true });
|
|
997
|
+
fs.mkdirSync(path.dirname(branchPath), { recursive: true });
|
|
998
|
+
if (!await tryGitAsync(["clone", "--origin", "origin", input.githubRemoteUrl, branchPath], { auth: input.githubAuth })) {
|
|
999
|
+
fs.mkdirSync(branchPath, { recursive: true });
|
|
1000
|
+
await runGitAsync(["init"], { cwd: branchPath });
|
|
1001
|
+
}
|
|
1002
|
+
await ensureOriginRemoteAsync(branchPath, input.githubRemoteUrl);
|
|
1003
|
+
configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
|
|
1004
|
+
configureVisibleGitIdentity(branchPath, visibleGitIdentity, runGit);
|
|
1005
|
+
await tryGitAsync(["fetch", "origin", "--prune"], { cwd: branchPath, auth: input.githubAuth });
|
|
1006
|
+
checkoutVisibleBranch({ branchPath, branchName: input.branchName, defaultBranch: input.defaultBranch });
|
|
1007
|
+
return branchPath;
|
|
1008
|
+
}
|
|
1009
|
+
if (input.reconcileExistingOrigin) {
|
|
1010
|
+
await ensureOriginRemoteAsync(branchPath, input.githubRemoteUrl);
|
|
1011
|
+
}
|
|
1012
|
+
configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
|
|
1013
|
+
configureVisibleGitIdentity(branchPath, visibleGitIdentity, runGit);
|
|
1014
|
+
return branchPath;
|
|
1015
|
+
}
|
|
1016
|
+
async function forceSyncManifestBranchFromInternal(input) {
|
|
1017
|
+
const githubRemoteUrl = githubRemoteUrlFor(input.baseUrl, input.projectId, input.manifest);
|
|
1018
|
+
const githubAuth = gitAuthForRemote(githubRemoteUrl, input.manifest.repoAuthHeader);
|
|
1019
|
+
const branchPath = await ensureVisibleGitCheckoutForRepositorySync({
|
|
1020
|
+
projectRoot: input.projectRoot,
|
|
1021
|
+
branchName: input.branchName,
|
|
1022
|
+
githubRemoteUrl,
|
|
1023
|
+
githubAuth,
|
|
1024
|
+
githubAuthHeader: input.manifest.repoAuthHeader,
|
|
1025
|
+
defaultBranch: input.manifest.defaultBranch || "main",
|
|
1026
|
+
reconcileExistingOrigin: Boolean(input.manifest.repoHttpUrl)
|
|
1027
|
+
});
|
|
1028
|
+
const gitDir = internalGitDirFor(input.syncRoot, input.projectId, input.branchName);
|
|
1029
|
+
fs.mkdirSync(path.dirname(gitDir), { recursive: true });
|
|
1030
|
+
if (!fs.existsSync(gitDir)) {
|
|
1031
|
+
await runGitAsync(["init", "--bare", gitDir]);
|
|
1032
|
+
}
|
|
1033
|
+
const context = {
|
|
1034
|
+
gitDir,
|
|
1035
|
+
workTree: branchPath,
|
|
1036
|
+
auth: internalGitAuth(input.baseUrl, input.token)
|
|
1037
|
+
};
|
|
1038
|
+
configureInternalRepo(context, internalRemoteUrlFor(input.baseUrl, input.projectId, input.manifest));
|
|
1039
|
+
await runInternalGitAsync(context, ["fetch", R5D_REMOTE_NAME, "--prune", `+refs/heads/*:refs/remotes/${R5D_REMOTE_NAME}/*`]);
|
|
1040
|
+
const branchTarget = `refs/remotes/${R5D_REMOTE_NAME}/${input.branchName}`;
|
|
1041
|
+
const mainTarget = `refs/remotes/${R5D_REMOTE_NAME}/main`;
|
|
1042
|
+
const target = tryInternalGit(context, ["show-ref", "--verify", "--quiet", branchTarget]) ? branchTarget : tryInternalGit(context, ["show-ref", "--verify", "--quiet", mainTarget]) ? mainTarget : null;
|
|
1043
|
+
if (!target) {
|
|
1044
|
+
throw new Error(`Internal remote has neither ${input.branchName} nor main`);
|
|
1045
|
+
}
|
|
1046
|
+
await runInternalGitAsync(context, ["reset", "--hard", target]);
|
|
1047
|
+
await runInternalGitAsync(context, ["clean", "-fd", "--", ".", ":(exclude).git"]);
|
|
1048
|
+
return getInternalCommitHash(context);
|
|
1049
|
+
}
|
|
1050
|
+
function findRepositorySyncBlockers(input) {
|
|
1051
|
+
const targetByKey = new Map(input.targets.map((target) => [`${target.projectId}:${target.branchName}`, target]));
|
|
1052
|
+
const blockedBy = [];
|
|
1053
|
+
for (const process2 of input.processes) {
|
|
1054
|
+
const target = targetByKey.get(`${process2.projectId}:${process2.branchName}`);
|
|
1055
|
+
if (target) blockedBy.push({ ...target, kind: "process", id: process2.id });
|
|
1056
|
+
}
|
|
1057
|
+
for (const shell of input.shells) {
|
|
1058
|
+
const target = targetByKey.get(`${shell.projectId}:${shell.branchName}`);
|
|
1059
|
+
if (target) blockedBy.push({ ...target, kind: "shell", id: shell.id });
|
|
1060
|
+
}
|
|
1061
|
+
return blockedBy;
|
|
1062
|
+
}
|
|
1063
|
+
async function syncManifestProjectsFromInternal(input) {
|
|
1064
|
+
const requestedProjectIds = input.projectIds ? new Set(input.projectIds) : null;
|
|
1065
|
+
const targets = input.manifests.filter((manifest) => !requestedProjectIds || requestedProjectIds.has(manifest.projectId)).flatMap((manifest) => {
|
|
1066
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
1067
|
+
return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
|
|
1068
|
+
});
|
|
1069
|
+
const blockedBy = findRepositorySyncBlockers({
|
|
1070
|
+
targets: targets.map(({ manifest, branchName }) => ({
|
|
1071
|
+
projectId: manifest.projectId,
|
|
1072
|
+
projectPath: manifest.projectPath,
|
|
1073
|
+
branchName
|
|
1074
|
+
})),
|
|
1075
|
+
processes: [...activeProcesses].map(([id, active]) => ({
|
|
1076
|
+
id,
|
|
1077
|
+
projectId: active.projectId,
|
|
1078
|
+
branchName: active.branchName
|
|
1079
|
+
})),
|
|
1080
|
+
shells: [...activePtys].map(([id, active]) => ({
|
|
1081
|
+
id,
|
|
1082
|
+
projectId: active.projectId,
|
|
1083
|
+
branchName: active.branchName
|
|
1084
|
+
}))
|
|
1085
|
+
});
|
|
1086
|
+
if (blockedBy.length > 0) {
|
|
1087
|
+
return { status: "blocked", results: [], blockedBy };
|
|
1088
|
+
}
|
|
1089
|
+
const results = [];
|
|
1090
|
+
for (const { manifest, branchName } of targets) {
|
|
1091
|
+
try {
|
|
1092
|
+
const commitHash = await forceSyncManifestBranchFromInternal({
|
|
1093
|
+
projectId: manifest.projectId,
|
|
1094
|
+
baseUrl: input.baseUrl,
|
|
1095
|
+
token: input.token,
|
|
1096
|
+
projectRoot: path.join(input.projectsRoot, manifest.repoSlug || manifest.projectId),
|
|
1097
|
+
syncRoot: input.syncRoot,
|
|
1098
|
+
branchName,
|
|
1099
|
+
manifest
|
|
1100
|
+
});
|
|
1101
|
+
results.push({
|
|
1102
|
+
projectId: manifest.projectId,
|
|
1103
|
+
projectPath: manifest.projectPath,
|
|
1104
|
+
branchName,
|
|
1105
|
+
status: "synced",
|
|
1106
|
+
commitHash
|
|
1107
|
+
});
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
results.push({
|
|
1110
|
+
projectId: manifest.projectId,
|
|
1111
|
+
projectPath: manifest.projectPath,
|
|
1112
|
+
branchName,
|
|
1113
|
+
status: "failed",
|
|
1114
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return {
|
|
1119
|
+
status: results.some((result) => result.status === "failed") ? "partial" : "completed",
|
|
1120
|
+
results,
|
|
1121
|
+
blockedBy: []
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
947
1124
|
function resolveCommandCwd(branchPath, cwd) {
|
|
948
1125
|
if (!cwd) {
|
|
949
1126
|
return branchPath;
|
|
@@ -1706,6 +1883,7 @@ async function executeStreamingCommand(input) {
|
|
|
1706
1883
|
projectId: input.projectId,
|
|
1707
1884
|
sessionId: input.message.sessionId,
|
|
1708
1885
|
branchName: input.message.branchName,
|
|
1886
|
+
credentialId: input.message.credentialId,
|
|
1709
1887
|
argv: input.message.argv,
|
|
1710
1888
|
command: input.message.command,
|
|
1711
1889
|
cwd: input.message.cwd,
|
|
@@ -1779,6 +1957,7 @@ function buildActiveProcessReports() {
|
|
|
1779
1957
|
projectId: active.projectId,
|
|
1780
1958
|
sessionId: active.sessionId,
|
|
1781
1959
|
branchName: active.branchName,
|
|
1960
|
+
...active.credentialId ? { credentialId: active.credentialId } : {},
|
|
1782
1961
|
argv: active.argv,
|
|
1783
1962
|
command: active.command,
|
|
1784
1963
|
...active.cwd ? { cwd: active.cwd } : {},
|
|
@@ -2048,7 +2227,11 @@ async function openPty(input) {
|
|
|
2048
2227
|
});
|
|
2049
2228
|
}
|
|
2050
2229
|
});
|
|
2051
|
-
activePtys.set(input.message.ptyId,
|
|
2230
|
+
activePtys.set(input.message.ptyId, {
|
|
2231
|
+
...ptyProcess,
|
|
2232
|
+
projectId: input.projectId,
|
|
2233
|
+
branchName: input.message.branchName
|
|
2234
|
+
});
|
|
2052
2235
|
} catch (error) {
|
|
2053
2236
|
sendWorkerMessage(input.ws, {
|
|
2054
2237
|
type: "pty_error",
|
|
@@ -2131,6 +2314,7 @@ async function startWorker(options) {
|
|
|
2131
2314
|
fs.mkdirSync(artifactRoot, { recursive: true });
|
|
2132
2315
|
fs.mkdirSync(planRoot, { recursive: true });
|
|
2133
2316
|
const manifestByProjectId = /* @__PURE__ */ new Map();
|
|
2317
|
+
let repositorySyncQueue = Promise.resolve();
|
|
2134
2318
|
const ws = new WebSocket(websocketUrl(baseUrl, label), {
|
|
2135
2319
|
headers: {
|
|
2136
2320
|
Authorization: `Bearer ${token}`
|
|
@@ -2188,42 +2372,64 @@ async function startWorker(options) {
|
|
|
2188
2372
|
}
|
|
2189
2373
|
process.stdout.write(`[r5d-worker] project manifest: ${message.projects.length} projects
|
|
2190
2374
|
`);
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
if (message.type === "sync_projects") {
|
|
2378
|
+
let syncResult = { status: "completed", results: [], blockedBy: [] };
|
|
2379
|
+
const executeSync = async () => {
|
|
2380
|
+
syncResult = await syncManifestProjectsFromInternal({
|
|
2381
|
+
baseUrl,
|
|
2382
|
+
token,
|
|
2383
|
+
projectsRoot,
|
|
2384
|
+
syncRoot,
|
|
2385
|
+
manifests: [...manifestByProjectId.values()],
|
|
2386
|
+
projectIds: message.projectIds
|
|
2387
|
+
});
|
|
2388
|
+
for (const result of syncResult.results) {
|
|
2389
|
+
if (result.status !== "synced") continue;
|
|
2195
2390
|
try {
|
|
2196
|
-
|
|
2197
|
-
projectId: project.projectId,
|
|
2391
|
+
await syncProjectPlans({
|
|
2198
2392
|
baseUrl,
|
|
2199
2393
|
token,
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
manifest: project
|
|
2394
|
+
projectId: result.projectId,
|
|
2395
|
+
branchName: result.branchName,
|
|
2396
|
+
planRoot
|
|
2204
2397
|
});
|
|
2205
|
-
try {
|
|
2206
|
-
await syncProjectPlans({
|
|
2207
|
-
baseUrl,
|
|
2208
|
-
token,
|
|
2209
|
-
projectId: project.projectId,
|
|
2210
|
-
branchName,
|
|
2211
|
-
planRoot
|
|
2212
|
-
});
|
|
2213
|
-
} catch (error) {
|
|
2214
|
-
process.stderr.write(
|
|
2215
|
-
`[r5d-worker] plan sync skipped for ${project.projectId}/${branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2216
|
-
`
|
|
2217
|
-
);
|
|
2218
|
-
}
|
|
2219
2398
|
} catch (error) {
|
|
2220
2399
|
process.stderr.write(
|
|
2221
|
-
`[r5d-worker]
|
|
2400
|
+
`[r5d-worker] plan sync skipped for ${result.projectId}/${result.branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2222
2401
|
`
|
|
2223
2402
|
);
|
|
2224
2403
|
}
|
|
2225
2404
|
}
|
|
2405
|
+
};
|
|
2406
|
+
const queued = repositorySyncQueue.then(executeSync, executeSync);
|
|
2407
|
+
repositorySyncQueue = queued.then(
|
|
2408
|
+
() => void 0,
|
|
2409
|
+
() => void 0
|
|
2410
|
+
);
|
|
2411
|
+
try {
|
|
2412
|
+
await queued;
|
|
2413
|
+
} catch (error) {
|
|
2414
|
+
syncResult = {
|
|
2415
|
+
status: "partial",
|
|
2416
|
+
results: [
|
|
2417
|
+
{
|
|
2418
|
+
projectId: "worker",
|
|
2419
|
+
projectPath: "worker",
|
|
2420
|
+
branchName: "all",
|
|
2421
|
+
status: "failed",
|
|
2422
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2423
|
+
}
|
|
2424
|
+
],
|
|
2425
|
+
blockedBy: []
|
|
2426
|
+
};
|
|
2226
2427
|
}
|
|
2428
|
+
sendWorkerMessage(ws, {
|
|
2429
|
+
type: "sync_projects_result",
|
|
2430
|
+
requestId: message.requestId,
|
|
2431
|
+
result: syncResult
|
|
2432
|
+
});
|
|
2227
2433
|
return;
|
|
2228
2434
|
}
|
|
2229
2435
|
if (message.type === "ping") {
|
|
@@ -2250,6 +2456,7 @@ async function startWorker(options) {
|
|
|
2250
2456
|
return;
|
|
2251
2457
|
}
|
|
2252
2458
|
if (message.type === "pty_open") {
|
|
2459
|
+
await repositorySyncQueue;
|
|
2253
2460
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2254
2461
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2255
2462
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${message.projectId}/${message.branchName}
|
|
@@ -2270,6 +2477,7 @@ async function startWorker(options) {
|
|
|
2270
2477
|
return;
|
|
2271
2478
|
}
|
|
2272
2479
|
if (message.type === "exec") {
|
|
2480
|
+
await repositorySyncQueue;
|
|
2273
2481
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2274
2482
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2275
2483
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
@@ -2289,6 +2497,7 @@ async function startWorker(options) {
|
|
|
2289
2497
|
return;
|
|
2290
2498
|
}
|
|
2291
2499
|
if (message.type === "exec_start") {
|
|
2500
|
+
await repositorySyncQueue;
|
|
2292
2501
|
sendWorkerMessage(ws, {
|
|
2293
2502
|
type: "exec_accepted",
|
|
2294
2503
|
requestId: message.requestId,
|
|
@@ -2330,6 +2539,7 @@ async function startWorker(options) {
|
|
|
2330
2539
|
}
|
|
2331
2540
|
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "sync" || message.type === "confirm_large_diff" || message.type === "pull_branch") {
|
|
2332
2541
|
try {
|
|
2542
|
+
await repositorySyncQueue;
|
|
2333
2543
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2334
2544
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2335
2545
|
const result = await executeOperation({
|
|
@@ -2439,11 +2649,13 @@ if (isCliEntrypoint()) {
|
|
|
2439
2649
|
}
|
|
2440
2650
|
export {
|
|
2441
2651
|
ensureVisibleGitCheckout,
|
|
2652
|
+
findRepositorySyncBlockers,
|
|
2442
2653
|
isArtifactEnvPath,
|
|
2443
2654
|
prepareArtifactEnvForShell,
|
|
2444
2655
|
preparePlanEnvForShell,
|
|
2445
2656
|
resolveHostShell,
|
|
2446
2657
|
resolveProjectFilePath,
|
|
2658
|
+
syncManifestProjectsFromInternal,
|
|
2447
2659
|
syncProjectPlans,
|
|
2448
2660
|
syncSessionArtifacts
|
|
2449
2661
|
};
|
package/dist/mjs/package.json
CHANGED
package/dist/types/main.d.ts
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
type WorkerProjectManifestEntry = {
|
|
3
|
+
projectId: string;
|
|
4
|
+
repoSlug: string;
|
|
5
|
+
projectPath: string;
|
|
6
|
+
repoHttpUrl: string | null;
|
|
7
|
+
repoAuthHeader: string | null;
|
|
8
|
+
defaultBranch: string;
|
|
9
|
+
branches: string[];
|
|
10
|
+
internalRemoteUrl: string;
|
|
11
|
+
};
|
|
12
|
+
type WorkerRepositorySyncTarget = {
|
|
13
|
+
projectId: string;
|
|
14
|
+
projectPath: string;
|
|
15
|
+
branchName: string;
|
|
16
|
+
};
|
|
17
|
+
type WorkerRepositorySyncResult = {
|
|
18
|
+
status: "completed" | "partial" | "blocked";
|
|
19
|
+
results: Array<WorkerRepositorySyncTarget & ({
|
|
20
|
+
status: "synced";
|
|
21
|
+
commitHash: string;
|
|
22
|
+
} | {
|
|
23
|
+
status: "failed";
|
|
24
|
+
error: string;
|
|
25
|
+
})>;
|
|
26
|
+
blockedBy: Array<WorkerRepositorySyncTarget & {
|
|
27
|
+
kind: "process" | "shell";
|
|
28
|
+
id: string;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
2
31
|
export declare function isArtifactEnvPath(filePath: string): boolean;
|
|
3
32
|
export declare function syncSessionArtifacts(input: {
|
|
4
33
|
baseUrl: string;
|
|
@@ -47,7 +76,29 @@ export declare function ensureVisibleGitCheckout(input: {
|
|
|
47
76
|
githubAuth?: GitAuth;
|
|
48
77
|
githubAuthHeader?: string | null;
|
|
49
78
|
defaultBranch: string;
|
|
79
|
+
reconcileExistingOrigin?: boolean;
|
|
50
80
|
}): string;
|
|
81
|
+
export declare function findRepositorySyncBlockers(input: {
|
|
82
|
+
targets: WorkerRepositorySyncTarget[];
|
|
83
|
+
processes: Array<{
|
|
84
|
+
id: string;
|
|
85
|
+
projectId: string;
|
|
86
|
+
branchName: string;
|
|
87
|
+
}>;
|
|
88
|
+
shells: Array<{
|
|
89
|
+
id: string;
|
|
90
|
+
projectId: string;
|
|
91
|
+
branchName: string;
|
|
92
|
+
}>;
|
|
93
|
+
}): WorkerRepositorySyncResult["blockedBy"];
|
|
94
|
+
export declare function syncManifestProjectsFromInternal(input: {
|
|
95
|
+
baseUrl: string;
|
|
96
|
+
token: string;
|
|
97
|
+
projectsRoot: string;
|
|
98
|
+
syncRoot: string;
|
|
99
|
+
manifests: WorkerProjectManifestEntry[];
|
|
100
|
+
projectIds?: string[];
|
|
101
|
+
}): Promise<WorkerRepositorySyncResult>;
|
|
51
102
|
export declare function resolveHostShell(command?: string, platform?: NodeJS.Platform): {
|
|
52
103
|
file: string;
|
|
53
104
|
args: string[];
|