@ricsam/r5d-worker 0.0.58 → 0.0.60
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 +569 -191
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-convergence.cjs +256 -0
- package/dist/cjs/workspace-incident-state.cjs +32 -3
- package/dist/cjs/workspace-manifest-admission.cjs +64 -0
- package/dist/cjs/workspace-sync.cjs +129 -16
- package/dist/mjs/main.mjs +579 -197
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-convergence.mjs +213 -0
- package/dist/mjs/workspace-incident-state.mjs +31 -3
- package/dist/mjs/workspace-manifest-admission.mjs +30 -0
- package/dist/mjs/workspace-sync.mjs +126 -15
- package/dist/types/workspace-convergence.d.ts +82 -0
- package/dist/types/workspace-incident-state.d.ts +15 -1
- package/dist/types/workspace-manifest-admission.d.ts +24 -0
- package/dist/types/workspace-sync.d.ts +23 -6
- package/package.json +1 -1
- package/dist/cjs/workspace-sync-fast-path.cjs +0 -108
- package/dist/mjs/workspace-sync-fast-path.mjs +0 -81
- package/dist/types/workspace-sync-fast-path.d.ts +0 -69
package/dist/cjs/package.json
CHANGED
|
@@ -0,0 +1,256 @@
|
|
|
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 workspace_convergence_exports = {};
|
|
30
|
+
__export(workspace_convergence_exports, {
|
|
31
|
+
ExplicitWorkspacePublicationQueue: () => ExplicitWorkspacePublicationQueue,
|
|
32
|
+
WORKSPACE_IDLE_SAFETY_SCAN_MS: () => WORKSPACE_IDLE_SAFETY_SCAN_MS,
|
|
33
|
+
WORKSPACE_PUBLICATION_QUIET_MS: () => WORKSPACE_PUBLICATION_QUIET_MS,
|
|
34
|
+
WorkspaceConvergenceState: () => WorkspaceConvergenceState,
|
|
35
|
+
WorkspaceManifestRevisionFence: () => WorkspaceManifestRevisionFence,
|
|
36
|
+
processWorkspaceConvergenceState: () => processWorkspaceConvergenceState,
|
|
37
|
+
readWorkspaceConvergenceState: () => readWorkspaceConvergenceState,
|
|
38
|
+
waitForWorkspacePublicationOnShutdown: () => waitForWorkspacePublicationOnShutdown,
|
|
39
|
+
workspacePublicationIncidentAction: () => workspacePublicationIncidentAction,
|
|
40
|
+
workspacePublicationTelemetry: () => workspacePublicationTelemetry
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(workspace_convergence_exports);
|
|
43
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
44
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
45
|
+
const WORKSPACE_PUBLICATION_QUIET_MS = 5e3;
|
|
46
|
+
const WORKSPACE_IDLE_SAFETY_SCAN_MS = 6e4;
|
|
47
|
+
const WORKSPACE_MANIFEST_REVISION_PATTERN = /^[0-9a-f]{64}$/;
|
|
48
|
+
class WorkspaceManifestRevisionFence {
|
|
49
|
+
sequence = 0;
|
|
50
|
+
requestedRevision = null;
|
|
51
|
+
completedRevision = null;
|
|
52
|
+
begin(revision) {
|
|
53
|
+
if (!WORKSPACE_MANIFEST_REVISION_PATTERN.test(revision)) {
|
|
54
|
+
throw new Error("Workspace manifest revision must be a lowercase SHA-256 hash");
|
|
55
|
+
}
|
|
56
|
+
this.sequence += 1;
|
|
57
|
+
this.requestedRevision = revision;
|
|
58
|
+
this.completedRevision = null;
|
|
59
|
+
return { sequence: this.sequence, revision };
|
|
60
|
+
}
|
|
61
|
+
isCurrent(token) {
|
|
62
|
+
return token.sequence === this.sequence && token.revision === this.requestedRevision;
|
|
63
|
+
}
|
|
64
|
+
complete(token) {
|
|
65
|
+
if (!this.isCurrent(token)) return false;
|
|
66
|
+
this.completedRevision = token.revision;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
readyRevision() {
|
|
70
|
+
return this.completedRevision === this.requestedRevision ? this.completedRevision : null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function workspacePublicationTelemetry(input) {
|
|
74
|
+
return {
|
|
75
|
+
totalMs: Math.max(0, input.finishedAt - input.requestedAt),
|
|
76
|
+
queueMs: Math.max(0, input.prepareStartedAt - input.requestedAt),
|
|
77
|
+
prepareMs: Math.max(0, input.synchronizeStartedAt - input.prepareStartedAt),
|
|
78
|
+
synchronizeMs: Math.max(0, input.finishedAt - input.synchronizeStartedAt)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function workspacePublicationIncidentAction(requestId) {
|
|
82
|
+
return requestId ? "request_terminal_authority" : "defer_automatic";
|
|
83
|
+
}
|
|
84
|
+
class ExplicitWorkspacePublicationQueue {
|
|
85
|
+
scheduled;
|
|
86
|
+
queued = [];
|
|
87
|
+
schedule(publication, pendingRequestId) {
|
|
88
|
+
if (pendingRequestId === publication.requestId || this.scheduled?.requestId === publication.requestId || this.queued.some((queued) => queued.requestId === publication.requestId)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
if (pendingRequestId || this.scheduled) {
|
|
92
|
+
this.queued.push(publication);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
this.scheduled = publication;
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
hasScheduled() {
|
|
99
|
+
return this.scheduled !== void 0;
|
|
100
|
+
}
|
|
101
|
+
consumeScheduled(requestId) {
|
|
102
|
+
if (this.scheduled?.requestId === requestId) this.scheduled = void 0;
|
|
103
|
+
}
|
|
104
|
+
enqueue(publication, pendingRequestId) {
|
|
105
|
+
if (pendingRequestId === publication.requestId || this.scheduled?.requestId === publication.requestId || this.queued.some((queued) => queued.requestId === publication.requestId)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.queued.push(publication);
|
|
109
|
+
}
|
|
110
|
+
next() {
|
|
111
|
+
return this.queued.shift();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const EMPTY_STATE = {
|
|
115
|
+
localHead: null,
|
|
116
|
+
desiredCanonicalHead: null,
|
|
117
|
+
dirtyGeneration: 0,
|
|
118
|
+
publishedGeneration: 0
|
|
119
|
+
};
|
|
120
|
+
function validHead(value) {
|
|
121
|
+
return value === null || typeof value === "string" && /^[0-9a-f]{40,64}$/.test(value);
|
|
122
|
+
}
|
|
123
|
+
function validGeneration(value) {
|
|
124
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
125
|
+
}
|
|
126
|
+
function readWorkspaceConvergenceState(statePath) {
|
|
127
|
+
try {
|
|
128
|
+
const parsed = JSON.parse(import_node_fs.default.readFileSync(statePath, "utf8"));
|
|
129
|
+
if (!validHead(parsed.localHead) || !validHead(parsed.desiredCanonicalHead) || !validGeneration(parsed.dirtyGeneration) || !validGeneration(parsed.publishedGeneration) || parsed.publishedGeneration > parsed.dirtyGeneration) {
|
|
130
|
+
return { ...EMPTY_STATE };
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
localHead: parsed.localHead,
|
|
134
|
+
desiredCanonicalHead: parsed.desiredCanonicalHead,
|
|
135
|
+
dirtyGeneration: parsed.dirtyGeneration,
|
|
136
|
+
publishedGeneration: parsed.publishedGeneration
|
|
137
|
+
};
|
|
138
|
+
} catch {
|
|
139
|
+
return { ...EMPTY_STATE };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
class WorkspaceConvergenceState {
|
|
143
|
+
constructor(statePath) {
|
|
144
|
+
this.statePath = statePath;
|
|
145
|
+
this.state = readWorkspaceConvergenceState(statePath);
|
|
146
|
+
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
147
|
+
}
|
|
148
|
+
statePath;
|
|
149
|
+
state;
|
|
150
|
+
publicationState;
|
|
151
|
+
snapshot() {
|
|
152
|
+
return { ...this.state, publicationState: this.publicationState };
|
|
153
|
+
}
|
|
154
|
+
setLocalHead(localHead) {
|
|
155
|
+
if (!validHead(localHead)) throw new Error("Invalid local workspace head");
|
|
156
|
+
this.state.localHead = localHead;
|
|
157
|
+
this.persist();
|
|
158
|
+
}
|
|
159
|
+
observeCanonicalHead(canonicalHead) {
|
|
160
|
+
if (!validHead(canonicalHead)) throw new Error("Invalid canonical workspace head");
|
|
161
|
+
if (this.state.desiredCanonicalHead === canonicalHead) return false;
|
|
162
|
+
this.state.desiredCanonicalHead = canonicalHead;
|
|
163
|
+
this.persist();
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
markDirty() {
|
|
167
|
+
this.state.dirtyGeneration += 1;
|
|
168
|
+
if (this.publicationState === "idle") this.publicationState = "scheduled";
|
|
169
|
+
this.persist();
|
|
170
|
+
return this.state.dirtyGeneration;
|
|
171
|
+
}
|
|
172
|
+
schedule() {
|
|
173
|
+
if (this.publicationState === "idle") this.publicationState = "scheduled";
|
|
174
|
+
}
|
|
175
|
+
beginPreparing() {
|
|
176
|
+
if (this.publicationState === "preparing" || this.publicationState === "submitting") {
|
|
177
|
+
throw new Error("A workspace publication is already in flight");
|
|
178
|
+
}
|
|
179
|
+
this.publicationState = "preparing";
|
|
180
|
+
}
|
|
181
|
+
beginSubmitting() {
|
|
182
|
+
if (this.publicationState !== "preparing") throw new Error("Workspace publication authority was not prepared");
|
|
183
|
+
this.publicationState = "submitting";
|
|
184
|
+
}
|
|
185
|
+
defer() {
|
|
186
|
+
this.publicationState = "scheduled";
|
|
187
|
+
}
|
|
188
|
+
block() {
|
|
189
|
+
this.publicationState = "blocked";
|
|
190
|
+
}
|
|
191
|
+
releaseBlock() {
|
|
192
|
+
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
193
|
+
}
|
|
194
|
+
observeIncidentState(blocked) {
|
|
195
|
+
if (this.publicationState === "preparing" || this.publicationState === "submitting") return;
|
|
196
|
+
if (blocked) this.block();
|
|
197
|
+
else this.releaseBlock();
|
|
198
|
+
}
|
|
199
|
+
resetTransientPublicationState() {
|
|
200
|
+
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
201
|
+
}
|
|
202
|
+
complete(input) {
|
|
203
|
+
if (!validGeneration(input.generation) || input.generation > this.state.dirtyGeneration) {
|
|
204
|
+
throw new Error("Invalid completed workspace generation");
|
|
205
|
+
}
|
|
206
|
+
if (!validHead(input.canonicalHead)) throw new Error("Invalid completed canonical workspace head");
|
|
207
|
+
if (input.published) {
|
|
208
|
+
this.state.publishedGeneration = Math.max(this.state.publishedGeneration, input.generation);
|
|
209
|
+
this.state.localHead = input.canonicalHead;
|
|
210
|
+
this.state.desiredCanonicalHead = input.canonicalHead;
|
|
211
|
+
}
|
|
212
|
+
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
213
|
+
this.persist();
|
|
214
|
+
}
|
|
215
|
+
persist() {
|
|
216
|
+
import_node_fs.default.mkdirSync(import_node_path.default.dirname(this.statePath), { recursive: true });
|
|
217
|
+
const temporaryPath = `${this.statePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
218
|
+
import_node_fs.default.writeFileSync(temporaryPath, `${JSON.stringify(this.state)}
|
|
219
|
+
`, { mode: 384 });
|
|
220
|
+
import_node_fs.default.renameSync(temporaryPath, this.statePath);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const PROCESS_WORKSPACE_CONVERGENCE_STATES = /* @__PURE__ */ new Map();
|
|
224
|
+
function processWorkspaceConvergenceState(statePath) {
|
|
225
|
+
const key = import_node_path.default.resolve(statePath);
|
|
226
|
+
const existing = PROCESS_WORKSPACE_CONVERGENCE_STATES.get(key);
|
|
227
|
+
if (existing) return existing;
|
|
228
|
+
const created = new WorkspaceConvergenceState(key);
|
|
229
|
+
PROCESS_WORKSPACE_CONVERGENCE_STATES.set(key, created);
|
|
230
|
+
return created;
|
|
231
|
+
}
|
|
232
|
+
async function waitForWorkspacePublicationOnShutdown(input) {
|
|
233
|
+
input.forcePublication();
|
|
234
|
+
const deadline = Date.now() + (input.timeoutMs ?? 1e4);
|
|
235
|
+
const pollMs = input.pollMs ?? 50;
|
|
236
|
+
while (Date.now() < deadline) {
|
|
237
|
+
const snapshot = input.snapshot();
|
|
238
|
+
if (snapshot.publicationState === "blocked") return "blocked";
|
|
239
|
+
if (snapshot.publicationState === "idle" && snapshot.publishedGeneration >= snapshot.dirtyGeneration) return "settled";
|
|
240
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
241
|
+
}
|
|
242
|
+
return "timed_out";
|
|
243
|
+
}
|
|
244
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
245
|
+
0 && (module.exports = {
|
|
246
|
+
ExplicitWorkspacePublicationQueue,
|
|
247
|
+
WORKSPACE_IDLE_SAFETY_SCAN_MS,
|
|
248
|
+
WORKSPACE_PUBLICATION_QUIET_MS,
|
|
249
|
+
WorkspaceConvergenceState,
|
|
250
|
+
WorkspaceManifestRevisionFence,
|
|
251
|
+
processWorkspaceConvergenceState,
|
|
252
|
+
readWorkspaceConvergenceState,
|
|
253
|
+
waitForWorkspacePublicationOnShutdown,
|
|
254
|
+
workspacePublicationIncidentAction,
|
|
255
|
+
workspacePublicationTelemetry
|
|
256
|
+
});
|
|
@@ -18,22 +18,51 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var workspace_incident_state_exports = {};
|
|
20
20
|
__export(workspace_incident_state_exports, {
|
|
21
|
+
WorkspaceIncidentOrderingFence: () => WorkspaceIncidentOrderingFence,
|
|
21
22
|
applyWorkspaceIncidentUpdate: () => applyWorkspaceIncidentUpdate,
|
|
22
23
|
releasePendingWorkspaceHead: () => releasePendingWorkspaceHead
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(workspace_incident_state_exports);
|
|
26
|
+
const DEFAULT_TERMINAL_INCIDENT_HISTORY_LIMIT = 32;
|
|
27
|
+
function isTerminalWorkspaceIncidentUpdate(update) {
|
|
28
|
+
return update.incidentId !== null && (update.status === "resolved" || update.status === "confirmed" || update.status === "reset");
|
|
29
|
+
}
|
|
30
|
+
class WorkspaceIncidentOrderingFence {
|
|
31
|
+
constructor(historyLimit = DEFAULT_TERMINAL_INCIDENT_HISTORY_LIMIT) {
|
|
32
|
+
this.historyLimit = historyLimit;
|
|
33
|
+
if (!Number.isSafeInteger(historyLimit) || historyLimit < 1) {
|
|
34
|
+
throw new Error("Workspace incident history limit must be a positive integer");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
historyLimit;
|
|
38
|
+
terminalIncidentIds = /* @__PURE__ */ new Set();
|
|
39
|
+
terminalIncidentOrder = [];
|
|
40
|
+
observe(update) {
|
|
41
|
+
if (!isTerminalWorkspaceIncidentUpdate(update) || this.terminalIncidentIds.has(update.incidentId)) return;
|
|
42
|
+
this.terminalIncidentIds.add(update.incidentId);
|
|
43
|
+
this.terminalIncidentOrder.push(update.incidentId);
|
|
44
|
+
while (this.terminalIncidentOrder.length > this.historyLimit) {
|
|
45
|
+
const expired = this.terminalIncidentOrder.shift();
|
|
46
|
+
if (expired) this.terminalIncidentIds.delete(expired);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
permitsBlockedResponse(incidentId) {
|
|
50
|
+
if (!incidentId) return false;
|
|
51
|
+
return !this.terminalIncidentIds.has(incidentId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
25
54
|
function applyWorkspaceIncidentUpdate(currentIncidentId, update) {
|
|
26
55
|
if (update.status === "remediating" || update.status === "waiting_for_worker") return update.incidentId;
|
|
27
56
|
if (currentIncidentId && update.incidentId && update.incidentId !== currentIncidentId) return currentIncidentId;
|
|
28
57
|
return null;
|
|
29
58
|
}
|
|
30
59
|
function releasePendingWorkspaceHead(previousIncidentId, currentIncidentId, terminalIncidentId) {
|
|
31
|
-
if (!previousIncidentId || currentIncidentId) return {
|
|
32
|
-
|
|
33
|
-
return { syncHead: null };
|
|
60
|
+
if (!previousIncidentId || currentIncidentId) return { fetchAuthoritativeHead: false };
|
|
61
|
+
return { fetchAuthoritativeHead: terminalIncidentId === previousIncidentId };
|
|
34
62
|
}
|
|
35
63
|
// Annotate the CommonJS export names for ESM import in node:
|
|
36
64
|
0 && (module.exports = {
|
|
65
|
+
WorkspaceIncidentOrderingFence,
|
|
37
66
|
applyWorkspaceIncidentUpdate,
|
|
38
67
|
releasePendingWorkspaceHead
|
|
39
68
|
});
|
|
@@ -0,0 +1,64 @@
|
|
|
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 workspace_manifest_admission_exports = {};
|
|
30
|
+
__export(workspace_manifest_admission_exports, {
|
|
31
|
+
inspectWorkspaceManifestFilesystem: () => inspectWorkspaceManifestFilesystem
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(workspace_manifest_admission_exports);
|
|
34
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
35
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
36
|
+
var import_managed_paths = require("./managed-paths.cjs");
|
|
37
|
+
function hasNormalGitDirectory(checkoutPath) {
|
|
38
|
+
const gitPath = import_node_path.default.join(checkoutPath, ".git");
|
|
39
|
+
return import_node_fs.default.existsSync(gitPath) && import_node_fs.default.statSync(gitPath).isDirectory();
|
|
40
|
+
}
|
|
41
|
+
function inspectWorkspaceManifestFilesystem(input) {
|
|
42
|
+
const firstBootstrap = !hasNormalGitDirectory(input.workspaceShadowRoot);
|
|
43
|
+
const missingCheckouts = [];
|
|
44
|
+
const migratedProjectIds = [];
|
|
45
|
+
for (const project of input.projects) {
|
|
46
|
+
const projectRoot = (0, import_managed_paths.managedProjectRoot)(input.projectsRoot, project.checkoutPathSegments);
|
|
47
|
+
const legacyRoot = import_node_path.default.join(input.projectsRoot, (0, import_managed_paths.legacyProjectSlug)(project.projectPath, project.projectId));
|
|
48
|
+
if (import_node_fs.default.existsSync(legacyRoot)) migratedProjectIds.push(project.projectId);
|
|
49
|
+
for (const branchName of project.branches) {
|
|
50
|
+
if (!hasNormalGitDirectory(import_node_path.default.join(projectRoot, branchName))) {
|
|
51
|
+
missingCheckouts.push({ projectId: project.projectId, branchName });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
firstBootstrap,
|
|
57
|
+
missingCheckouts,
|
|
58
|
+
migratedProjectIds
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
62
|
+
0 && (module.exports = {
|
|
63
|
+
inspectWorkspaceManifestFilesystem
|
|
64
|
+
});
|
|
@@ -32,11 +32,13 @@ __export(workspace_sync_exports, {
|
|
|
32
32
|
WORKSPACE_BRANCH: () => WORKSPACE_BRANCH,
|
|
33
33
|
WORKSPACE_INTENT_REF_PREFIX: () => WORKSPACE_INTENT_REF_PREFIX,
|
|
34
34
|
WORKSPACE_NATIVE_PLANS_RELATIVE_PATH: () => WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
|
|
35
|
-
|
|
35
|
+
WORKSPACE_PUBLICATION_REF_PREFIX: () => WORKSPACE_PUBLICATION_REF_PREFIX,
|
|
36
36
|
WorkspaceSyncSingleFlight: () => WorkspaceSyncSingleFlight,
|
|
37
37
|
assertCanonicalCheckoutIndexMatchesWorktree: () => assertCanonicalCheckoutIndexMatchesWorktree,
|
|
38
38
|
assertWorkspaceQuarantineRef: () => assertWorkspaceQuarantineRef,
|
|
39
39
|
calculateWorkspaceDiffFingerprint: () => calculateWorkspaceDiffFingerprint,
|
|
40
|
+
calculateWorkspaceLocalDiffFingerprint: () => calculateWorkspaceLocalDiffFingerprint,
|
|
41
|
+
convergeWorkspaceHead: () => convergeWorkspaceHead,
|
|
40
42
|
encodeWorkspaceBranch: () => encodeWorkspaceBranch,
|
|
41
43
|
mirrorShadowWorkspaceToVisible: () => mirrorShadowWorkspaceToVisible,
|
|
42
44
|
mirrorVisibleWorkspaceToShadow: () => mirrorVisibleWorkspaceToShadow,
|
|
@@ -54,8 +56,8 @@ var import_managed_paths = require("./managed-paths.cjs");
|
|
|
54
56
|
var import_workspace_mutation_gate = require("./workspace-mutation-gate.cjs");
|
|
55
57
|
const WORKSPACE_BRANCH = "main";
|
|
56
58
|
const WORKSPACE_INTENT_REF_PREFIX = "refs/r5d/workspace-intents/";
|
|
59
|
+
const WORKSPACE_PUBLICATION_REF_PREFIX = "refs/r5d/workspace-publications/";
|
|
57
60
|
const MAX_WORKSPACE_SYNC_DIFF_BYTES = 5 * 1024 * 1024;
|
|
58
|
-
const WORKSPACE_PERIODIC_SCAN_INTERVAL_MS = 5e3;
|
|
59
61
|
const MAX_REPORTED_WORKSPACE_PATHS = 2e3;
|
|
60
62
|
const MAX_REPORTED_WORKSPACE_STATUS_BYTES = 256 * 1024;
|
|
61
63
|
const MAX_MIRROR_COMPARISON_CACHE_ENTRIES = 2e5;
|
|
@@ -1754,9 +1756,10 @@ function commitMessage(input) {
|
|
|
1754
1756
|
});
|
|
1755
1757
|
}
|
|
1756
1758
|
function assertWorkspaceQuarantineRef(quarantineRef) {
|
|
1757
|
-
const
|
|
1758
|
-
|
|
1759
|
-
|
|
1759
|
+
const prefix = quarantineRef.startsWith(WORKSPACE_PUBLICATION_REF_PREFIX) ? WORKSPACE_PUBLICATION_REF_PREFIX : WORKSPACE_INTENT_REF_PREFIX;
|
|
1760
|
+
const suffix = quarantineRef.slice(prefix.length);
|
|
1761
|
+
if (!quarantineRef.startsWith(prefix) || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(suffix)) {
|
|
1762
|
+
throw new Error(`Workspace candidate ref must be below ${WORKSPACE_PUBLICATION_REF_PREFIX} or ${WORKSPACE_INTENT_REF_PREFIX}`);
|
|
1760
1763
|
}
|
|
1761
1764
|
const result = Bun.spawnSync(["git", "check-ref-format", quarantineRef], {
|
|
1762
1765
|
stdout: "pipe",
|
|
@@ -1876,6 +1879,36 @@ function preserveWorkspaceConflictCandidate(input, observed, options) {
|
|
|
1876
1879
|
};
|
|
1877
1880
|
}
|
|
1878
1881
|
}
|
|
1882
|
+
function preserveLargeDiffCandidate(input, observed, error) {
|
|
1883
|
+
try {
|
|
1884
|
+
if (stagedPaths(input).length > 0) {
|
|
1885
|
+
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit blocked workspace candidate");
|
|
1886
|
+
}
|
|
1887
|
+
const candidateHead = revParse(input, "HEAD");
|
|
1888
|
+
if (!candidateHead) throw new Error("the blocked workspace candidate does not have a commit");
|
|
1889
|
+
assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
|
|
1890
|
+
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
1891
|
+
if (upload.exitCode !== 0) {
|
|
1892
|
+
throw new Error(upload.stderr || upload.stdout || "blocked workspace candidate upload failed");
|
|
1893
|
+
}
|
|
1894
|
+
return {
|
|
1895
|
+
...observed,
|
|
1896
|
+
outcome: "large_diff_blocked",
|
|
1897
|
+
expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
|
|
1898
|
+
candidateHead,
|
|
1899
|
+
quarantineRef: input.quarantineRef,
|
|
1900
|
+
error,
|
|
1901
|
+
gitStatus: gitStatus(input)
|
|
1902
|
+
};
|
|
1903
|
+
} catch (candidateError) {
|
|
1904
|
+
return {
|
|
1905
|
+
...observed,
|
|
1906
|
+
outcome: "failed",
|
|
1907
|
+
error: `Workspace safety guard blocked publication but could not preserve a durable candidate: ${candidateError instanceof Error ? candidateError.message : String(candidateError)}`,
|
|
1908
|
+
gitStatus: gitStatus(input)
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1879
1912
|
async function synchronizeWorkspace(rawInput) {
|
|
1880
1913
|
let input = { ...rawInput, attemptId: rawInput.attemptId ?? crypto.randomUUID() };
|
|
1881
1914
|
try {
|
|
@@ -1887,6 +1920,15 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1887
1920
|
let hydrationPreimages = input.skipVisibleMirror ? hydrationPreimagesFromShadowRevision(input, shadowWasCreated ? null : revParse(input, "HEAD")) : /* @__PURE__ */ new Map();
|
|
1888
1921
|
const remoteHeadAtStart = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
1889
1922
|
const result = baseResult(input, remoteHeadAtStart);
|
|
1923
|
+
if (input.expectedCanonicalHead !== void 0 && remoteHeadAtStart !== input.expectedCanonicalHead) {
|
|
1924
|
+
return {
|
|
1925
|
+
...result,
|
|
1926
|
+
outcome: "failed",
|
|
1927
|
+
expectedHead: input.expectedCanonicalHead,
|
|
1928
|
+
publishedHead: remoteHeadAtStart ?? void 0,
|
|
1929
|
+
error: `canonical_head_advanced: expected ${input.expectedCanonicalHead ?? "unborn"}, fetched ${remoteHeadAtStart ?? "unborn"}`
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1890
1932
|
const inboundConflictsAtStart = input.skipVisibleMirror && !isCanonicalRemediationSync && remoteHeadAtStart ? visibleHydrationConflictPaths(input, hydrationPreimages, `origin/${WORKSPACE_BRANCH}`) : [];
|
|
1891
1933
|
if (input.resetToCanonical) {
|
|
1892
1934
|
if (input.trigger.canonicalCheckoutOnly) {
|
|
@@ -1935,18 +1977,14 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1935
1977
|
const destructiveReductions = destructiveCheckoutReductions(input, baseRevision);
|
|
1936
1978
|
if (destructiveReductions.length > 0 && !input.confirmedLargeDiff) {
|
|
1937
1979
|
const summary = destructiveReductions.map(({ root, trackedBefore, trackedAfter }) => `${root} (${trackedBefore} -> ${trackedAfter} tracked files)`).join(", ");
|
|
1938
|
-
return {
|
|
1939
|
-
...observed,
|
|
1940
|
-
outcome: "large_diff_blocked",
|
|
1941
|
-
error: `Workspace safety guard blocked a destructive checkout reduction: ${summary}`
|
|
1942
|
-
};
|
|
1980
|
+
return preserveLargeDiffCandidate(input, observed, `Workspace safety guard blocked a destructive checkout reduction: ${summary}`);
|
|
1943
1981
|
}
|
|
1944
1982
|
if (paths.length > 0 && diffSizeBytes > MAX_WORKSPACE_SYNC_DIFF_BYTES && !input.confirmedLargeDiff && !input.trigger.canonicalCheckoutOnly) {
|
|
1945
|
-
return
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1983
|
+
return preserveLargeDiffCandidate(
|
|
1984
|
+
input,
|
|
1985
|
+
observed,
|
|
1986
|
+
`Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`
|
|
1987
|
+
);
|
|
1950
1988
|
}
|
|
1951
1989
|
if (stagedWorkingPaths.length > 0) {
|
|
1952
1990
|
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit workspace changes");
|
|
@@ -2111,6 +2149,79 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2111
2149
|
};
|
|
2112
2150
|
}
|
|
2113
2151
|
}
|
|
2152
|
+
async function convergeWorkspaceHead(rawInput, options) {
|
|
2153
|
+
const input = {
|
|
2154
|
+
...rawInput,
|
|
2155
|
+
projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
|
|
2156
|
+
};
|
|
2157
|
+
if (input.trigger.canonicalCheckoutOnly) {
|
|
2158
|
+
throw new Error("Ordinary inbound convergence cannot target a canonical resolver checkout");
|
|
2159
|
+
}
|
|
2160
|
+
ensureShadowWorkspace(input);
|
|
2161
|
+
const fetchedLocalHead = revParse(input, "HEAD");
|
|
2162
|
+
const fetchedCanonicalHead = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
2163
|
+
if (!options.hydrateVisible) {
|
|
2164
|
+
return {
|
|
2165
|
+
localHead: fetchedLocalHead,
|
|
2166
|
+
canonicalHead: fetchedCanonicalHead,
|
|
2167
|
+
localChanges: false,
|
|
2168
|
+
hydrated: false,
|
|
2169
|
+
hydrationConflicts: []
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
resetUncommittedShadowSnapshot(input);
|
|
2173
|
+
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
2174
|
+
const localHead = fetchedLocalHead;
|
|
2175
|
+
const canonicalHead = fetchedCanonicalHead;
|
|
2176
|
+
const projection = mirrorVisibleWorkspaceToShadow(input);
|
|
2177
|
+
runGit(
|
|
2178
|
+
input,
|
|
2179
|
+
input.shadowRoot,
|
|
2180
|
+
["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
|
|
2181
|
+
"stage inbound workspace observation"
|
|
2182
|
+
);
|
|
2183
|
+
forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
|
|
2184
|
+
const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write inbound workspace observation tree");
|
|
2185
|
+
const headTree = localHead ? revParse(input, `${localHead}^{tree}`) : null;
|
|
2186
|
+
const localChanges = stagedTree !== headTree;
|
|
2187
|
+
if (!options.hydrateVisible || localChanges || !canonicalHead || localHead === canonicalHead) {
|
|
2188
|
+
resetUncommittedShadowSnapshot(input);
|
|
2189
|
+
return { localHead, canonicalHead, localChanges, hydrated: false, hydrationConflicts: [] };
|
|
2190
|
+
}
|
|
2191
|
+
const preimages = projection.visiblePreimages ?? hydrationPreimagesFromShadowRevision(input, localHead);
|
|
2192
|
+
const hydrationConflicts = localHead ? hydrationConflictPaths(input, preimages, localHead, canonicalHead) : [];
|
|
2193
|
+
if (hydrationConflicts.length > 0) {
|
|
2194
|
+
resetUncommittedShadowSnapshot(input);
|
|
2195
|
+
return { localHead, canonicalHead, localChanges: true, hydrated: false, hydrationConflicts };
|
|
2196
|
+
}
|
|
2197
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", canonicalHead], "fast-forward workspace to available canonical head");
|
|
2198
|
+
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean fast-forwarded workspace");
|
|
2199
|
+
mirrorShadowWorkspaceToVisible(input, projection.opaqueRoots, preimages);
|
|
2200
|
+
return { localHead: canonicalHead, canonicalHead, localChanges: false, hydrated: true, hydrationConflicts: [] };
|
|
2201
|
+
}
|
|
2202
|
+
function calculateWorkspaceLocalDiffFingerprint(rawInput) {
|
|
2203
|
+
const input = {
|
|
2204
|
+
...rawInput,
|
|
2205
|
+
projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
|
|
2206
|
+
};
|
|
2207
|
+
if (!import_node_fs.default.existsSync(import_node_path.default.join(input.shadowRoot, ".git"))) {
|
|
2208
|
+
throw new Error("Cannot inspect local workspace changes before first bootstrap");
|
|
2209
|
+
}
|
|
2210
|
+
resetUncommittedShadowSnapshot(input);
|
|
2211
|
+
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
2212
|
+
const projection = mirrorVisibleWorkspaceToShadow(input);
|
|
2213
|
+
runGit(
|
|
2214
|
+
input,
|
|
2215
|
+
input.shadowRoot,
|
|
2216
|
+
["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
|
|
2217
|
+
"stage local workspace fingerprint"
|
|
2218
|
+
);
|
|
2219
|
+
forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
|
|
2220
|
+
const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write local workspace fingerprint tree");
|
|
2221
|
+
const headTree = revParse(input, "HEAD^{tree}");
|
|
2222
|
+
resetUncommittedShadowSnapshot(input);
|
|
2223
|
+
return stagedTree === headTree ? (0, import_node_crypto.createHash)("sha256").update("").digest("hex") : (0, import_node_crypto.createHash)("sha256").update(stagedTree).digest("hex");
|
|
2224
|
+
}
|
|
2114
2225
|
function calculateWorkspaceDiffFingerprint(rawInput) {
|
|
2115
2226
|
const input = {
|
|
2116
2227
|
...rawInput,
|
|
@@ -2183,11 +2294,13 @@ class WorkspaceSyncSingleFlight {
|
|
|
2183
2294
|
WORKSPACE_BRANCH,
|
|
2184
2295
|
WORKSPACE_INTENT_REF_PREFIX,
|
|
2185
2296
|
WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
|
|
2186
|
-
|
|
2297
|
+
WORKSPACE_PUBLICATION_REF_PREFIX,
|
|
2187
2298
|
WorkspaceSyncSingleFlight,
|
|
2188
2299
|
assertCanonicalCheckoutIndexMatchesWorktree,
|
|
2189
2300
|
assertWorkspaceQuarantineRef,
|
|
2190
2301
|
calculateWorkspaceDiffFingerprint,
|
|
2302
|
+
calculateWorkspaceLocalDiffFingerprint,
|
|
2303
|
+
convergeWorkspaceHead,
|
|
2191
2304
|
encodeWorkspaceBranch,
|
|
2192
2305
|
mirrorShadowWorkspaceToVisible,
|
|
2193
2306
|
mirrorVisibleWorkspaceToShadow,
|