@ricsam/r5d-worker 0.0.78 → 0.0.80
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 +6 -0
- package/dist/cjs/main.cjs +1550 -335
- package/dist/cjs/managed-paths.cjs +101 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/project-workspace-state.cjs +184 -5
- package/dist/cjs/project-worktrees.cjs +676 -58
- package/dist/cjs/registry-auth.cjs +310 -0
- package/dist/cjs/repository-transition-policy.cjs +49 -0
- package/dist/cjs/supervisor.cjs +62 -11
- package/dist/cjs/working-tree-mirror.cjs +196 -36
- package/dist/cjs/workspace-automatic-sync-policy.cjs +69 -0
- package/dist/cjs/workspace-branch-incarnation-policy.cjs +37 -0
- package/dist/cjs/workspace-command-sync-policy.cjs +18 -0
- package/dist/cjs/workspace-git-sync.cjs +1037 -54
- package/dist/cjs/workspace-mount-boundary.cjs +71 -0
- package/dist/cjs/workspace-path-move.cjs +195 -0
- package/dist/cjs/workspace-preserve-only-policy.cjs +36 -0
- package/dist/cjs/workspace-project-config-policy.cjs +46 -0
- package/dist/cjs/workspace-publication-evidence.cjs +46 -0
- package/dist/mjs/main.mjs +1584 -341
- package/dist/mjs/managed-paths.mjs +100 -1
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/project-workspace-state.mjs +181 -5
- package/dist/mjs/project-worktrees.mjs +667 -57
- package/dist/mjs/registry-auth.mjs +269 -0
- package/dist/mjs/repository-transition-policy.mjs +22 -0
- package/dist/mjs/supervisor.mjs +61 -11
- package/dist/mjs/working-tree-mirror.mjs +195 -36
- package/dist/mjs/workspace-automatic-sync-policy.mjs +40 -0
- package/dist/mjs/workspace-branch-incarnation-policy.mjs +13 -0
- package/dist/mjs/workspace-command-sync-policy.mjs +17 -0
- package/dist/mjs/workspace-git-sync.mjs +1032 -54
- package/dist/mjs/workspace-mount-boundary.mjs +37 -0
- package/dist/mjs/workspace-path-move.mjs +160 -0
- package/dist/mjs/workspace-preserve-only-policy.mjs +11 -0
- package/dist/mjs/workspace-project-config-policy.mjs +21 -0
- package/dist/mjs/workspace-publication-evidence.mjs +21 -0
- package/dist/types/credential-authority-lock-fixture.d.ts +1 -0
- package/dist/types/main.d.ts +175 -1
- package/dist/types/managed-paths.d.ts +11 -0
- package/dist/types/project-workspace-state.d.ts +45 -1
- package/dist/types/project-worktrees.d.ts +119 -2
- package/dist/types/registry-auth.d.ts +47 -0
- package/dist/types/repository-transition-policy.d.ts +26 -0
- package/dist/types/supervisor-daemonized-fixture.d.ts +1 -0
- package/dist/types/supervisor-signal-fixture.d.ts +1 -0
- package/dist/types/supervisor.d.ts +6 -4
- package/dist/types/working-tree-mirror.d.ts +14 -0
- package/dist/types/workspace-automatic-sync-policy.d.ts +37 -0
- package/dist/types/workspace-branch-incarnation-policy.d.ts +14 -0
- package/dist/types/workspace-command-sync-policy.d.ts +9 -0
- package/dist/types/workspace-git-sync.d.ts +33 -3
- package/dist/types/workspace-mount-boundary.d.ts +10 -0
- package/dist/types/workspace-path-move.d.ts +21 -0
- package/dist/types/workspace-preserve-only-policy.d.ts +15 -0
- package/dist/types/workspace-project-config-policy.d.ts +34 -0
- package/dist/types/workspace-publication-evidence.d.ts +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var registry_auth_exports = {};
|
|
30
|
+
__export(registry_auth_exports, {
|
|
31
|
+
APP_MANAGED_GITHUB_REGISTRY: () => APP_MANAGED_GITHUB_REGISTRY,
|
|
32
|
+
RegistryAuthConfigurationError: () => RegistryAuthConfigurationError,
|
|
33
|
+
configureGitHubRegistryAuthFiles: () => configureGitHubRegistryAuthFiles,
|
|
34
|
+
planGitHubRegistryAuthFiles: () => planGitHubRegistryAuthFiles,
|
|
35
|
+
preparePrivateAuthFileGeneration: () => preparePrivateAuthFileGeneration,
|
|
36
|
+
registryAuthHasAppManagedGitHubCredential: () => registryAuthHasAppManagedGitHubCredential,
|
|
37
|
+
registryAuthTestHarness: () => registryAuthTestHarness,
|
|
38
|
+
updateGitHubRegistryAuthFile: () => updateGitHubRegistryAuthFile
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(registry_auth_exports);
|
|
41
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
42
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
43
|
+
const APP_MANAGED_GITHUB_REGISTRY = "ghcr.io";
|
|
44
|
+
class RegistryAuthConfigurationError extends Error {
|
|
45
|
+
constructor(cause) {
|
|
46
|
+
super("Could not authoritatively configure GitHub registry authentication", { cause });
|
|
47
|
+
this.name = "RegistryAuthConfigurationError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function isJsonObject(value) {
|
|
51
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
function lstatIfExists(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
return import_node_fs.default.lstatSync(filePath);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (error.code === "ENOENT") return null;
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function readRegistryAuthConfig(filePath) {
|
|
62
|
+
const stat = lstatIfExists(filePath);
|
|
63
|
+
if (!stat) return null;
|
|
64
|
+
if (stat.isSymbolicLink() || !stat.isFile()) throw new Error(`Registry auth config must be a regular file: ${filePath}`);
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = JSON.parse(import_node_fs.default.readFileSync(filePath, "utf8"));
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new Error(`Registry auth config is not valid JSON: ${filePath}`, { cause: error });
|
|
70
|
+
}
|
|
71
|
+
if (!isJsonObject(parsed)) throw new Error(`Registry auth config must contain a JSON object: ${filePath}`);
|
|
72
|
+
return parsed;
|
|
73
|
+
}
|
|
74
|
+
function registryAuthHasAppManagedGitHubCredential(filePath) {
|
|
75
|
+
const config = readRegistryAuthConfig(filePath);
|
|
76
|
+
if (!config) return false;
|
|
77
|
+
if (config.auths === void 0) return false;
|
|
78
|
+
if (!isJsonObject(config.auths)) throw new Error(`Registry auth config has an invalid auths object: ${filePath}`);
|
|
79
|
+
return Object.prototype.hasOwnProperty.call(config.auths, APP_MANAGED_GITHUB_REGISTRY);
|
|
80
|
+
}
|
|
81
|
+
function fsyncDirectory(directory, dependencies = {
|
|
82
|
+
platform: process.platform,
|
|
83
|
+
open: (target) => import_node_fs.default.openSync(target, "r"),
|
|
84
|
+
fsync: import_node_fs.default.fsyncSync,
|
|
85
|
+
close: import_node_fs.default.closeSync
|
|
86
|
+
}) {
|
|
87
|
+
if (dependencies.platform === "win32") return;
|
|
88
|
+
const descriptor = dependencies.open(directory);
|
|
89
|
+
try {
|
|
90
|
+
dependencies.fsync(descriptor);
|
|
91
|
+
} finally {
|
|
92
|
+
dependencies.close(descriptor);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function ensurePrivateParentDirectory(filePath) {
|
|
96
|
+
const directory = import_node_path.default.dirname(filePath);
|
|
97
|
+
let existingAncestor = directory;
|
|
98
|
+
while (!lstatIfExists(existingAncestor)) {
|
|
99
|
+
const parent = import_node_path.default.dirname(existingAncestor);
|
|
100
|
+
if (parent === existingAncestor) throw new Error(`Registry auth parent has no existing ancestor: ${directory}`);
|
|
101
|
+
existingAncestor = parent;
|
|
102
|
+
}
|
|
103
|
+
const ancestorStat = lstatIfExists(existingAncestor);
|
|
104
|
+
if (!ancestorStat || ancestorStat.isSymbolicLink() || !ancestorStat.isDirectory()) {
|
|
105
|
+
throw new Error(`Registry auth parent must descend from a real directory: ${directory}`);
|
|
106
|
+
}
|
|
107
|
+
import_node_fs.default.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
108
|
+
let current = directory;
|
|
109
|
+
while (true) {
|
|
110
|
+
const stat = lstatIfExists(current);
|
|
111
|
+
if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
112
|
+
throw new Error(`Registry auth parent must be a real directory: ${current}`);
|
|
113
|
+
}
|
|
114
|
+
fsyncDirectory(current);
|
|
115
|
+
if (current === existingAncestor) break;
|
|
116
|
+
current = import_node_path.default.dirname(current);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function stagePrivateFile(filePath, content) {
|
|
120
|
+
ensurePrivateParentDirectory(filePath);
|
|
121
|
+
const temporaryPath = import_node_path.default.join(import_node_path.default.dirname(filePath), `.${import_node_path.default.basename(filePath)}.r5d-auth.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
122
|
+
let descriptor;
|
|
123
|
+
try {
|
|
124
|
+
descriptor = import_node_fs.default.openSync(temporaryPath, "wx", 384);
|
|
125
|
+
import_node_fs.default.writeFileSync(descriptor, content, "utf8");
|
|
126
|
+
import_node_fs.default.fsyncSync(descriptor);
|
|
127
|
+
import_node_fs.default.closeSync(descriptor);
|
|
128
|
+
descriptor = void 0;
|
|
129
|
+
import_node_fs.default.chmodSync(temporaryPath, 384);
|
|
130
|
+
return { filePath, temporaryPath };
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (descriptor !== void 0) import_node_fs.default.closeSync(descriptor);
|
|
133
|
+
import_node_fs.default.rmSync(temporaryPath, { force: true });
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function commitStagedPrivateFile(staged, beforeRename) {
|
|
138
|
+
beforeRename?.();
|
|
139
|
+
import_node_fs.default.renameSync(staged.temporaryPath, staged.filePath);
|
|
140
|
+
import_node_fs.default.chmodSync(staged.filePath, 384);
|
|
141
|
+
fsyncDirectory(import_node_path.default.dirname(staged.filePath));
|
|
142
|
+
}
|
|
143
|
+
function discardStagedPrivateFile(staged) {
|
|
144
|
+
import_node_fs.default.rmSync(staged.temporaryPath, { force: true });
|
|
145
|
+
}
|
|
146
|
+
function cleanupAbandonedStagedFiles(filePath) {
|
|
147
|
+
const directory = import_node_path.default.dirname(filePath);
|
|
148
|
+
const prefix = `.${import_node_path.default.basename(filePath)}.r5d-auth.`;
|
|
149
|
+
let entries;
|
|
150
|
+
try {
|
|
151
|
+
entries = import_node_fs.default.readdirSync(directory);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error.code === "ENOENT") return;
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
let removed = false;
|
|
157
|
+
for (const name of entries) {
|
|
158
|
+
if (!name.startsWith(prefix) || !name.endsWith(".tmp")) continue;
|
|
159
|
+
const candidate = import_node_path.default.join(directory, name);
|
|
160
|
+
const stat = import_node_fs.default.lstatSync(candidate);
|
|
161
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
162
|
+
throw new Error(`Registry auth staging artifact must be a regular file: ${candidate}`);
|
|
163
|
+
}
|
|
164
|
+
import_node_fs.default.rmSync(candidate);
|
|
165
|
+
removed = true;
|
|
166
|
+
}
|
|
167
|
+
if (removed) fsyncDirectory(directory);
|
|
168
|
+
}
|
|
169
|
+
function atomicReplacePrivateFile(filePath, content, beforeRename) {
|
|
170
|
+
const staged = stagePrivateFile(filePath, content);
|
|
171
|
+
try {
|
|
172
|
+
commitStagedPrivateFile(staged, beforeRename);
|
|
173
|
+
} finally {
|
|
174
|
+
discardStagedPrivateFile(staged);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function preparePrivateAuthFileGeneration(updates) {
|
|
178
|
+
try {
|
|
179
|
+
const desiredByPath = /* @__PURE__ */ new Map();
|
|
180
|
+
for (const update of updates) {
|
|
181
|
+
const filePath = import_node_path.default.resolve(update.filePath);
|
|
182
|
+
const existing = desiredByPath.get(filePath);
|
|
183
|
+
const desired = { content: update.content, allowSymlinkRemoval: update.allowSymlinkRemoval === true };
|
|
184
|
+
if (existing && (existing.content !== desired.content || existing.allowSymlinkRemoval !== desired.allowSymlinkRemoval)) {
|
|
185
|
+
throw new Error(`Conflicting desired auth content for ${filePath}`);
|
|
186
|
+
}
|
|
187
|
+
desiredByPath.set(filePath, desired);
|
|
188
|
+
}
|
|
189
|
+
const changes = [];
|
|
190
|
+
const stagedFiles = [];
|
|
191
|
+
try {
|
|
192
|
+
for (const [filePath, desired] of desiredByPath) {
|
|
193
|
+
const { content, allowSymlinkRemoval } = desired;
|
|
194
|
+
cleanupAbandonedStagedFiles(filePath);
|
|
195
|
+
const status = lstatIfExists(filePath);
|
|
196
|
+
if (status && (status.isSymbolicLink() ? !(content === null && allowSymlinkRemoval) : !status.isFile())) {
|
|
197
|
+
throw new Error(`Private auth target must be a regular file: ${filePath}`);
|
|
198
|
+
}
|
|
199
|
+
const unchanged = content === null ? !status : Boolean(status && import_node_fs.default.readFileSync(filePath, "utf8") === content && (status.mode & 511) === 384);
|
|
200
|
+
if (unchanged) continue;
|
|
201
|
+
if (content === null) {
|
|
202
|
+
changes.push({ filePath, content });
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const staged = stagePrivateFile(filePath, content);
|
|
206
|
+
stagedFiles.push(staged);
|
|
207
|
+
changes.push({ filePath, content, staged });
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
for (const staged of stagedFiles) discardStagedPrivateFile(staged);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
let finished = false;
|
|
214
|
+
const discard = () => {
|
|
215
|
+
for (const staged of stagedFiles) discardStagedPrivateFile(staged);
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
changed: changes.length > 0,
|
|
219
|
+
commit(beforeMutation) {
|
|
220
|
+
if (finished) throw new Error("Private auth generation has already been finalized");
|
|
221
|
+
finished = true;
|
|
222
|
+
try {
|
|
223
|
+
changes.forEach((change, index) => {
|
|
224
|
+
beforeMutation?.(change.filePath, index);
|
|
225
|
+
if (change.content === null) {
|
|
226
|
+
import_node_fs.default.unlinkSync(change.filePath);
|
|
227
|
+
fsyncDirectory(import_node_path.default.dirname(change.filePath));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (!change.staged) throw new Error(`Private auth replacement was not staged: ${change.filePath}`);
|
|
231
|
+
commitStagedPrivateFile(change.staged);
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
throw error instanceof RegistryAuthConfigurationError ? error : new RegistryAuthConfigurationError(error);
|
|
235
|
+
} finally {
|
|
236
|
+
discard();
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
discard() {
|
|
240
|
+
if (finished) return;
|
|
241
|
+
finished = true;
|
|
242
|
+
discard();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
} catch (error) {
|
|
246
|
+
throw error instanceof RegistryAuthConfigurationError ? error : new RegistryAuthConfigurationError(error);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function registryAuthFileContent(filePath, credential) {
|
|
250
|
+
if (credential && credential.registry !== APP_MANAGED_GITHUB_REGISTRY) {
|
|
251
|
+
throw new Error(`Unsupported app-managed registry: ${credential.registry}`);
|
|
252
|
+
}
|
|
253
|
+
const existing = readRegistryAuthConfig(filePath);
|
|
254
|
+
if (!existing && !credential) return null;
|
|
255
|
+
const config = { ...existing ?? {} };
|
|
256
|
+
const existingAuths = config.auths;
|
|
257
|
+
if (existingAuths !== void 0 && !isJsonObject(existingAuths)) {
|
|
258
|
+
throw new Error(`Registry auth config has an invalid auths object: ${filePath}`);
|
|
259
|
+
}
|
|
260
|
+
const auths = { ...existingAuths ?? {} };
|
|
261
|
+
delete auths[APP_MANAGED_GITHUB_REGISTRY];
|
|
262
|
+
if (credential) {
|
|
263
|
+
auths[APP_MANAGED_GITHUB_REGISTRY] = {
|
|
264
|
+
username: credential.username,
|
|
265
|
+
password: credential.token,
|
|
266
|
+
auth: Buffer.from(`${credential.username}:${credential.token}`).toString("base64")
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
config.auths = auths;
|
|
270
|
+
return `${JSON.stringify(config, null, 2)}
|
|
271
|
+
`;
|
|
272
|
+
}
|
|
273
|
+
function configureGitHubRegistryAuthFilesWithHook(filePaths, credential, beforeRename) {
|
|
274
|
+
try {
|
|
275
|
+
const prepared = preparePrivateAuthFileGeneration(planGitHubRegistryAuthFiles(filePaths, credential));
|
|
276
|
+
prepared.commit(beforeRename);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
throw error instanceof RegistryAuthConfigurationError ? error : new RegistryAuthConfigurationError(error);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function planGitHubRegistryAuthFiles(filePaths, credential) {
|
|
282
|
+
const uniquePaths = [...new Set(filePaths.map((filePath) => import_node_path.default.resolve(filePath)))];
|
|
283
|
+
return uniquePaths.map((filePath) => ({ filePath, content: registryAuthFileContent(filePath, credential) }));
|
|
284
|
+
}
|
|
285
|
+
function updateGitHubRegistryAuthFile(filePath, credential) {
|
|
286
|
+
configureGitHubRegistryAuthFilesWithHook([filePath], credential);
|
|
287
|
+
}
|
|
288
|
+
function configureGitHubRegistryAuthFiles(filePaths, credential) {
|
|
289
|
+
configureGitHubRegistryAuthFilesWithHook(filePaths, credential);
|
|
290
|
+
}
|
|
291
|
+
const registryAuthTestHarness = {
|
|
292
|
+
fsyncDirectory,
|
|
293
|
+
atomicReplacePrivateFile(filePath, content, beforeRename) {
|
|
294
|
+
atomicReplacePrivateFile(filePath, content, beforeRename);
|
|
295
|
+
},
|
|
296
|
+
configureFiles(filePaths, credential, beforeRename) {
|
|
297
|
+
configureGitHubRegistryAuthFilesWithHook(filePaths, credential, beforeRename);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
301
|
+
0 && (module.exports = {
|
|
302
|
+
APP_MANAGED_GITHUB_REGISTRY,
|
|
303
|
+
RegistryAuthConfigurationError,
|
|
304
|
+
configureGitHubRegistryAuthFiles,
|
|
305
|
+
planGitHubRegistryAuthFiles,
|
|
306
|
+
preparePrivateAuthFileGeneration,
|
|
307
|
+
registryAuthHasAppManagedGitHubCredential,
|
|
308
|
+
registryAuthTestHarness,
|
|
309
|
+
updateGitHubRegistryAuthFile
|
|
310
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var repository_transition_policy_exports = {};
|
|
20
|
+
__export(repository_transition_policy_exports, {
|
|
21
|
+
assertRepositoryExecutionEnabled: () => assertRepositoryExecutionEnabled,
|
|
22
|
+
assertRepositoryTransitionState: () => assertRepositoryTransitionState,
|
|
23
|
+
enabledRepositoryTransitionRequiresReseed: () => enabledRepositoryTransitionRequiresReseed,
|
|
24
|
+
repositoryMirrorWritesAllowed: () => repositoryMirrorWritesAllowed
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(repository_transition_policy_exports);
|
|
27
|
+
function assertRepositoryTransitionState(project) {
|
|
28
|
+
if (project.repositoryTransitionId !== null && (typeof project.repositoryTransitionId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(project.repositoryTransitionId)) || typeof project.executionDisabled !== "boolean" || typeof project.mirrorWritesDisabled !== "boolean") {
|
|
29
|
+
throw new Error(`Project ${project.projectId} has invalid repository transition state`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function assertRepositoryExecutionEnabled(project) {
|
|
33
|
+
if (project.executionDisabled) {
|
|
34
|
+
throw new Error(`Project ${project.projectId} execution is disabled while its repository connection is transitioning`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function repositoryMirrorWritesAllowed(project) {
|
|
38
|
+
return project?.executionDisabled !== true && project?.mirrorWritesDisabled !== true;
|
|
39
|
+
}
|
|
40
|
+
function enabledRepositoryTransitionRequiresReseed(input) {
|
|
41
|
+
return !input.project.executionDisabled && (!input.lastEnabled.known || input.lastEnabled.transitionId !== input.project.repositoryTransitionId);
|
|
42
|
+
}
|
|
43
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
44
|
+
0 && (module.exports = {
|
|
45
|
+
assertRepositoryExecutionEnabled,
|
|
46
|
+
assertRepositoryTransitionState,
|
|
47
|
+
enabledRepositoryTransitionRequiresReseed,
|
|
48
|
+
repositoryMirrorWritesAllowed
|
|
49
|
+
});
|
package/dist/cjs/supervisor.cjs
CHANGED
|
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var supervisor_exports = {};
|
|
20
20
|
__export(supervisor_exports, {
|
|
21
|
+
WORKER_CREDENTIAL_RESTART_EXIT_CODE: () => WORKER_CREDENTIAL_RESTART_EXIT_CODE,
|
|
21
22
|
WORKER_RECONNECT_DELAY_MS: () => WORKER_RECONNECT_DELAY_MS,
|
|
22
23
|
WORKER_RECONNECT_EXIT_CODE: () => WORKER_RECONNECT_EXIT_CODE,
|
|
23
24
|
WORKER_RELOAD_EXIT_CODE: () => WORKER_RELOAD_EXIT_CODE,
|
|
@@ -26,20 +27,33 @@ __export(supervisor_exports, {
|
|
|
26
27
|
superviseWorkerRuntime: () => superviseWorkerRuntime
|
|
27
28
|
});
|
|
28
29
|
module.exports = __toCommonJS(supervisor_exports);
|
|
30
|
+
var import_node_os = require("node:os");
|
|
31
|
+
var import_process_tree = require("./process-tree.cjs");
|
|
29
32
|
const WORKER_RUNTIME_ENV = "R5D_WORKER_INTERNAL_RUNTIME";
|
|
30
33
|
const WORKER_RELOAD_EXIT_CODE = 75;
|
|
31
34
|
const WORKER_RECONNECT_EXIT_CODE = 76;
|
|
35
|
+
const WORKER_CREDENTIAL_RESTART_EXIT_CODE = 77;
|
|
32
36
|
const WORKER_RECONNECT_DELAY_MS = 2e3;
|
|
37
|
+
const SUPERVISOR_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
33
38
|
function defaultRuntimeSpawner(argv, options) {
|
|
34
39
|
return Bun.spawn(argv, options);
|
|
35
40
|
}
|
|
36
41
|
function defaultRuntimeDelay(delayMs) {
|
|
37
42
|
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
38
43
|
}
|
|
44
|
+
async function defaultRuntimeReaper(runtime) {
|
|
45
|
+
await (0, import_process_tree.terminateProcessTree)(runtime);
|
|
46
|
+
}
|
|
39
47
|
function isRetryableWorkerServerStatus(status) {
|
|
40
48
|
return status === 408 || status === 429 || status >= 500;
|
|
41
49
|
}
|
|
42
|
-
|
|
50
|
+
function signalExitCode(signal) {
|
|
51
|
+
return 128 + (osSignalNumber(signal) ?? 1);
|
|
52
|
+
}
|
|
53
|
+
function osSignalNumber(signal) {
|
|
54
|
+
return import_node_os.constants.signals[signal];
|
|
55
|
+
}
|
|
56
|
+
async function superviseWorkerRuntime(argv, env = process.env, spawnRuntime = defaultRuntimeSpawner, waitBeforeReconnect = defaultRuntimeDelay, reapCredentialRuntime = defaultRuntimeReaper) {
|
|
43
57
|
if (!process.argv[1]) {
|
|
44
58
|
throw new Error("Cannot locate the r5d-worker entrypoint");
|
|
45
59
|
}
|
|
@@ -48,27 +62,64 @@ async function superviseWorkerRuntime(argv, env = process.env, spawnRuntime = de
|
|
|
48
62
|
stdin: "inherit",
|
|
49
63
|
stdout: "inherit",
|
|
50
64
|
stderr: "inherit",
|
|
65
|
+
detached: true,
|
|
51
66
|
env: {
|
|
52
67
|
...env,
|
|
53
68
|
[WORKER_RUNTIME_ENV]: "1"
|
|
54
69
|
}
|
|
55
70
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
let receivedSignal;
|
|
72
|
+
let resolveSignal;
|
|
73
|
+
const signalReceived = new Promise((resolve) => {
|
|
74
|
+
resolveSignal = resolve;
|
|
75
|
+
});
|
|
76
|
+
const handlers = SUPERVISOR_SIGNALS.map((signal) => {
|
|
77
|
+
const handler = () => {
|
|
78
|
+
if (receivedSignal) return;
|
|
79
|
+
receivedSignal = signal;
|
|
80
|
+
resolveSignal?.(signal);
|
|
81
|
+
};
|
|
82
|
+
process.on(signal, handler);
|
|
83
|
+
return { signal, handler };
|
|
84
|
+
});
|
|
85
|
+
try {
|
|
86
|
+
const outcome = await Promise.race([
|
|
87
|
+
runtime.exited.then((exitCode2) => ({ type: "exit", exitCode: exitCode2 })),
|
|
88
|
+
signalReceived.then((signal) => ({ type: "signal", signal }))
|
|
89
|
+
]);
|
|
90
|
+
try {
|
|
91
|
+
await reapCredentialRuntime(runtime);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
process.stderr.write(
|
|
94
|
+
`[r5d-worker] runtime restart/exit aborted because process-group reaping failed: ${error instanceof Error ? error.message : String(error)}
|
|
95
|
+
`
|
|
96
|
+
);
|
|
97
|
+
return 1;
|
|
98
|
+
}
|
|
99
|
+
if (outcome.type === "signal" || receivedSignal) {
|
|
100
|
+
return signalExitCode(outcome.type === "signal" ? outcome.signal : receivedSignal);
|
|
101
|
+
}
|
|
102
|
+
const { exitCode } = outcome;
|
|
103
|
+
if (exitCode === WORKER_RELOAD_EXIT_CODE) {
|
|
104
|
+
process.stdout.write("[r5d-worker] activating updated worker runtime\n");
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (exitCode === WORKER_RECONNECT_EXIT_CODE) {
|
|
108
|
+
process.stderr.write(`[r5d-worker] reconnecting in ${WORKER_RECONNECT_DELAY_MS}ms
|
|
63
109
|
`);
|
|
64
|
-
|
|
65
|
-
|
|
110
|
+
await waitBeforeReconnect(WORKER_RECONNECT_DELAY_MS);
|
|
111
|
+
if (receivedSignal) return signalExitCode(receivedSignal);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
return exitCode;
|
|
115
|
+
} finally {
|
|
116
|
+
for (const { signal, handler } of handlers) process.off(signal, handler);
|
|
66
117
|
}
|
|
67
|
-
return exitCode;
|
|
68
118
|
}
|
|
69
119
|
}
|
|
70
120
|
// Annotate the CommonJS export names for ESM import in node:
|
|
71
121
|
0 && (module.exports = {
|
|
122
|
+
WORKER_CREDENTIAL_RESTART_EXIT_CODE,
|
|
72
123
|
WORKER_RECONNECT_DELAY_MS,
|
|
73
124
|
WORKER_RECONNECT_EXIT_CODE,
|
|
74
125
|
WORKER_RELOAD_EXIT_CODE,
|