@ricsam/r5d-worker 0.0.23 → 0.0.25
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 +239 -27
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +237 -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;
|
|
@@ -2089,7 +2268,11 @@ async function openPty(input) {
|
|
|
2089
2268
|
});
|
|
2090
2269
|
}
|
|
2091
2270
|
});
|
|
2092
|
-
activePtys.set(input.message.ptyId,
|
|
2271
|
+
activePtys.set(input.message.ptyId, {
|
|
2272
|
+
...ptyProcess,
|
|
2273
|
+
projectId: input.projectId,
|
|
2274
|
+
branchName: input.message.branchName
|
|
2275
|
+
});
|
|
2093
2276
|
} catch (error) {
|
|
2094
2277
|
sendWorkerMessage(input.ws, {
|
|
2095
2278
|
type: "pty_error",
|
|
@@ -2172,6 +2355,7 @@ async function startWorker(options) {
|
|
|
2172
2355
|
import_node_fs.default.mkdirSync(artifactRoot, { recursive: true });
|
|
2173
2356
|
import_node_fs.default.mkdirSync(planRoot, { recursive: true });
|
|
2174
2357
|
const manifestByProjectId = /* @__PURE__ */ new Map();
|
|
2358
|
+
let repositorySyncQueue = Promise.resolve();
|
|
2175
2359
|
const ws = new WebSocket(websocketUrl(baseUrl, label), {
|
|
2176
2360
|
headers: {
|
|
2177
2361
|
Authorization: `Bearer ${token}`
|
|
@@ -2229,42 +2413,64 @@ async function startWorker(options) {
|
|
|
2229
2413
|
}
|
|
2230
2414
|
process.stdout.write(`[r5d-worker] project manifest: ${message.projects.length} projects
|
|
2231
2415
|
`);
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
if (message.type === "sync_projects") {
|
|
2419
|
+
let syncResult = { status: "completed", results: [], blockedBy: [] };
|
|
2420
|
+
const executeSync = async () => {
|
|
2421
|
+
syncResult = await syncManifestProjectsFromInternal({
|
|
2422
|
+
baseUrl,
|
|
2423
|
+
token,
|
|
2424
|
+
projectsRoot,
|
|
2425
|
+
syncRoot,
|
|
2426
|
+
manifests: [...manifestByProjectId.values()],
|
|
2427
|
+
projectIds: message.projectIds
|
|
2428
|
+
});
|
|
2429
|
+
for (const result of syncResult.results) {
|
|
2430
|
+
if (result.status !== "synced") continue;
|
|
2236
2431
|
try {
|
|
2237
|
-
|
|
2238
|
-
projectId: project.projectId,
|
|
2432
|
+
await syncProjectPlans({
|
|
2239
2433
|
baseUrl,
|
|
2240
2434
|
token,
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
manifest: project
|
|
2435
|
+
projectId: result.projectId,
|
|
2436
|
+
branchName: result.branchName,
|
|
2437
|
+
planRoot
|
|
2245
2438
|
});
|
|
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
2439
|
} catch (error) {
|
|
2261
2440
|
process.stderr.write(
|
|
2262
|
-
`[r5d-worker]
|
|
2441
|
+
`[r5d-worker] plan sync skipped for ${result.projectId}/${result.branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2263
2442
|
`
|
|
2264
2443
|
);
|
|
2265
2444
|
}
|
|
2266
2445
|
}
|
|
2446
|
+
};
|
|
2447
|
+
const queued = repositorySyncQueue.then(executeSync, executeSync);
|
|
2448
|
+
repositorySyncQueue = queued.then(
|
|
2449
|
+
() => void 0,
|
|
2450
|
+
() => void 0
|
|
2451
|
+
);
|
|
2452
|
+
try {
|
|
2453
|
+
await queued;
|
|
2454
|
+
} catch (error) {
|
|
2455
|
+
syncResult = {
|
|
2456
|
+
status: "partial",
|
|
2457
|
+
results: [
|
|
2458
|
+
{
|
|
2459
|
+
projectId: "worker",
|
|
2460
|
+
projectPath: "worker",
|
|
2461
|
+
branchName: "all",
|
|
2462
|
+
status: "failed",
|
|
2463
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2464
|
+
}
|
|
2465
|
+
],
|
|
2466
|
+
blockedBy: []
|
|
2467
|
+
};
|
|
2267
2468
|
}
|
|
2469
|
+
sendWorkerMessage(ws, {
|
|
2470
|
+
type: "sync_projects_result",
|
|
2471
|
+
requestId: message.requestId,
|
|
2472
|
+
result: syncResult
|
|
2473
|
+
});
|
|
2268
2474
|
return;
|
|
2269
2475
|
}
|
|
2270
2476
|
if (message.type === "ping") {
|
|
@@ -2291,6 +2497,7 @@ async function startWorker(options) {
|
|
|
2291
2497
|
return;
|
|
2292
2498
|
}
|
|
2293
2499
|
if (message.type === "pty_open") {
|
|
2500
|
+
await repositorySyncQueue;
|
|
2294
2501
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2295
2502
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2296
2503
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${message.projectId}/${message.branchName}
|
|
@@ -2311,6 +2518,7 @@ async function startWorker(options) {
|
|
|
2311
2518
|
return;
|
|
2312
2519
|
}
|
|
2313
2520
|
if (message.type === "exec") {
|
|
2521
|
+
await repositorySyncQueue;
|
|
2314
2522
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2315
2523
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2316
2524
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
@@ -2330,6 +2538,7 @@ async function startWorker(options) {
|
|
|
2330
2538
|
return;
|
|
2331
2539
|
}
|
|
2332
2540
|
if (message.type === "exec_start") {
|
|
2541
|
+
await repositorySyncQueue;
|
|
2333
2542
|
sendWorkerMessage(ws, {
|
|
2334
2543
|
type: "exec_accepted",
|
|
2335
2544
|
requestId: message.requestId,
|
|
@@ -2371,6 +2580,7 @@ async function startWorker(options) {
|
|
|
2371
2580
|
}
|
|
2372
2581
|
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
2582
|
try {
|
|
2583
|
+
await repositorySyncQueue;
|
|
2374
2584
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2375
2585
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2376
2586
|
const result = await executeOperation({
|
|
@@ -2481,11 +2691,13 @@ if (isCliEntrypoint()) {
|
|
|
2481
2691
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2482
2692
|
0 && (module.exports = {
|
|
2483
2693
|
ensureVisibleGitCheckout,
|
|
2694
|
+
findRepositorySyncBlockers,
|
|
2484
2695
|
isArtifactEnvPath,
|
|
2485
2696
|
prepareArtifactEnvForShell,
|
|
2486
2697
|
preparePlanEnvForShell,
|
|
2487
2698
|
resolveHostShell,
|
|
2488
2699
|
resolveProjectFilePath,
|
|
2700
|
+
syncManifestProjectsFromInternal,
|
|
2489
2701
|
syncProjectPlans,
|
|
2490
2702
|
syncSessionArtifacts
|
|
2491
2703
|
});
|
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;
|
|
@@ -2048,7 +2225,11 @@ async function openPty(input) {
|
|
|
2048
2225
|
});
|
|
2049
2226
|
}
|
|
2050
2227
|
});
|
|
2051
|
-
activePtys.set(input.message.ptyId,
|
|
2228
|
+
activePtys.set(input.message.ptyId, {
|
|
2229
|
+
...ptyProcess,
|
|
2230
|
+
projectId: input.projectId,
|
|
2231
|
+
branchName: input.message.branchName
|
|
2232
|
+
});
|
|
2052
2233
|
} catch (error) {
|
|
2053
2234
|
sendWorkerMessage(input.ws, {
|
|
2054
2235
|
type: "pty_error",
|
|
@@ -2131,6 +2312,7 @@ async function startWorker(options) {
|
|
|
2131
2312
|
fs.mkdirSync(artifactRoot, { recursive: true });
|
|
2132
2313
|
fs.mkdirSync(planRoot, { recursive: true });
|
|
2133
2314
|
const manifestByProjectId = /* @__PURE__ */ new Map();
|
|
2315
|
+
let repositorySyncQueue = Promise.resolve();
|
|
2134
2316
|
const ws = new WebSocket(websocketUrl(baseUrl, label), {
|
|
2135
2317
|
headers: {
|
|
2136
2318
|
Authorization: `Bearer ${token}`
|
|
@@ -2188,42 +2370,64 @@ async function startWorker(options) {
|
|
|
2188
2370
|
}
|
|
2189
2371
|
process.stdout.write(`[r5d-worker] project manifest: ${message.projects.length} projects
|
|
2190
2372
|
`);
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2373
|
+
return;
|
|
2374
|
+
}
|
|
2375
|
+
if (message.type === "sync_projects") {
|
|
2376
|
+
let syncResult = { status: "completed", results: [], blockedBy: [] };
|
|
2377
|
+
const executeSync = async () => {
|
|
2378
|
+
syncResult = await syncManifestProjectsFromInternal({
|
|
2379
|
+
baseUrl,
|
|
2380
|
+
token,
|
|
2381
|
+
projectsRoot,
|
|
2382
|
+
syncRoot,
|
|
2383
|
+
manifests: [...manifestByProjectId.values()],
|
|
2384
|
+
projectIds: message.projectIds
|
|
2385
|
+
});
|
|
2386
|
+
for (const result of syncResult.results) {
|
|
2387
|
+
if (result.status !== "synced") continue;
|
|
2195
2388
|
try {
|
|
2196
|
-
|
|
2197
|
-
projectId: project.projectId,
|
|
2389
|
+
await syncProjectPlans({
|
|
2198
2390
|
baseUrl,
|
|
2199
2391
|
token,
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
manifest: project
|
|
2392
|
+
projectId: result.projectId,
|
|
2393
|
+
branchName: result.branchName,
|
|
2394
|
+
planRoot
|
|
2204
2395
|
});
|
|
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
2396
|
} catch (error) {
|
|
2220
2397
|
process.stderr.write(
|
|
2221
|
-
`[r5d-worker]
|
|
2398
|
+
`[r5d-worker] plan sync skipped for ${result.projectId}/${result.branchName}: ${error instanceof Error ? error.message : String(error)}
|
|
2222
2399
|
`
|
|
2223
2400
|
);
|
|
2224
2401
|
}
|
|
2225
2402
|
}
|
|
2403
|
+
};
|
|
2404
|
+
const queued = repositorySyncQueue.then(executeSync, executeSync);
|
|
2405
|
+
repositorySyncQueue = queued.then(
|
|
2406
|
+
() => void 0,
|
|
2407
|
+
() => void 0
|
|
2408
|
+
);
|
|
2409
|
+
try {
|
|
2410
|
+
await queued;
|
|
2411
|
+
} catch (error) {
|
|
2412
|
+
syncResult = {
|
|
2413
|
+
status: "partial",
|
|
2414
|
+
results: [
|
|
2415
|
+
{
|
|
2416
|
+
projectId: "worker",
|
|
2417
|
+
projectPath: "worker",
|
|
2418
|
+
branchName: "all",
|
|
2419
|
+
status: "failed",
|
|
2420
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2421
|
+
}
|
|
2422
|
+
],
|
|
2423
|
+
blockedBy: []
|
|
2424
|
+
};
|
|
2226
2425
|
}
|
|
2426
|
+
sendWorkerMessage(ws, {
|
|
2427
|
+
type: "sync_projects_result",
|
|
2428
|
+
requestId: message.requestId,
|
|
2429
|
+
result: syncResult
|
|
2430
|
+
});
|
|
2227
2431
|
return;
|
|
2228
2432
|
}
|
|
2229
2433
|
if (message.type === "ping") {
|
|
@@ -2250,6 +2454,7 @@ async function startWorker(options) {
|
|
|
2250
2454
|
return;
|
|
2251
2455
|
}
|
|
2252
2456
|
if (message.type === "pty_open") {
|
|
2457
|
+
await repositorySyncQueue;
|
|
2253
2458
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2254
2459
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2255
2460
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${message.projectId}/${message.branchName}
|
|
@@ -2270,6 +2475,7 @@ async function startWorker(options) {
|
|
|
2270
2475
|
return;
|
|
2271
2476
|
}
|
|
2272
2477
|
if (message.type === "exec") {
|
|
2478
|
+
await repositorySyncQueue;
|
|
2273
2479
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2274
2480
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2275
2481
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
@@ -2289,6 +2495,7 @@ async function startWorker(options) {
|
|
|
2289
2495
|
return;
|
|
2290
2496
|
}
|
|
2291
2497
|
if (message.type === "exec_start") {
|
|
2498
|
+
await repositorySyncQueue;
|
|
2292
2499
|
sendWorkerMessage(ws, {
|
|
2293
2500
|
type: "exec_accepted",
|
|
2294
2501
|
requestId: message.requestId,
|
|
@@ -2330,6 +2537,7 @@ async function startWorker(options) {
|
|
|
2330
2537
|
}
|
|
2331
2538
|
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
2539
|
try {
|
|
2540
|
+
await repositorySyncQueue;
|
|
2333
2541
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2334
2542
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
2335
2543
|
const result = await executeOperation({
|
|
@@ -2439,11 +2647,13 @@ if (isCliEntrypoint()) {
|
|
|
2439
2647
|
}
|
|
2440
2648
|
export {
|
|
2441
2649
|
ensureVisibleGitCheckout,
|
|
2650
|
+
findRepositorySyncBlockers,
|
|
2442
2651
|
isArtifactEnvPath,
|
|
2443
2652
|
prepareArtifactEnvForShell,
|
|
2444
2653
|
preparePlanEnvForShell,
|
|
2445
2654
|
resolveHostShell,
|
|
2446
2655
|
resolveProjectFilePath,
|
|
2656
|
+
syncManifestProjectsFromInternal,
|
|
2447
2657
|
syncProjectPlans,
|
|
2448
2658
|
syncSessionArtifacts
|
|
2449
2659
|
};
|
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[];
|