@loucompanion/forge-bridge 0.1.3-dev.8777774c67bc → 0.1.3-dev.88e93f53db5b
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 +51 -0
- package/dist/bridge/bridge.base.d.ts +4 -1
- package/dist/bridge/bridge.service.d.ts +50 -3
- package/dist/bridge/bridge.service.js +730 -87
- package/dist/bridge/bridge.types.d.ts +63 -1
- package/dist/cleanup/cleanup.service.d.ts +14 -2
- package/dist/cleanup/cleanup.service.js +496 -3
- package/dist/cleanup/cleanup.types.d.ts +10 -0
- package/dist/cleanup/index.d.ts +1 -1
- package/dist/cleanup/index.js +1 -1
- package/dist/cleanup/internals.d.ts +2 -2
- package/dist/cleanup/internals.js +1 -1
- package/dist/cli/cli.help.js +37 -3
- package/dist/cli/cli.service.d.ts +30 -2
- package/dist/cli/cli.service.js +191 -27
- package/dist/cli/local-config.d.ts +4 -0
- package/dist/cli/local-config.js +41 -1
- package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
- package/dist/runtime/aspnet-runtime-prebuild.js +340 -0
- package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
- package/dist/runtime/attempt-node-runtime-launcher.js +105 -0
- package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
- package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
- package/dist/runtime/attempt-process-supervisor.d.ts +97 -0
- package/dist/runtime/attempt-process-supervisor.js +384 -0
- package/dist/runtime/generated-output-reset.d.ts +10 -0
- package/dist/runtime/generated-output-reset.js +103 -0
- package/dist/runtime/host-listener-inspector.d.ts +46 -0
- package/dist/runtime/host-listener-inspector.js +349 -0
- package/dist/runtime/host-port-broker.d.ts +76 -0
- package/dist/runtime/host-port-broker.js +333 -0
- package/dist/runtime/host-runtime-contract.d.ts +47 -0
- package/dist/runtime/host-runtime-contract.js +700 -0
- package/dist/runtime/host-runtime-lease.d.ts +194 -0
- package/dist/runtime/host-runtime-lease.js +2138 -0
- package/dist/runtime/host-runtime-promotion.d.ts +23 -0
- package/dist/runtime/host-runtime-promotion.js +159 -0
- package/dist/runtime/memory-capacity.d.ts +68 -0
- package/dist/runtime/memory-capacity.js +121 -0
- package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
- package/dist/runtime/node-host-runtime-adapter.js +284 -0
- package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
- package/dist/runtime/playwright-host-runtime-adapter.js +225 -0
- package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
- package/dist/runtime/runtime-isolation-lease.js +158 -0
- package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
- package/dist/runtime/workspace-resource-estimator.js +174 -0
- package/dist/sandbox/image-policy.d.ts +3 -1
- package/dist/sandbox/image-policy.js +4 -2
- package/dist/sandbox/preflight.d.ts +49 -1
- package/dist/sandbox/preflight.js +313 -31
- package/dist/sandbox/process.js +125 -40
- package/dist/sandbox/repository-lock.js +41 -3
- package/dist/sandbox/runtime.d.ts +85 -4
- package/dist/sandbox/runtime.js +806 -126
- package/dist/sandbox/sandbox.types.d.ts +10 -0
- package/dist/sandbox/smoke.d.ts +76 -0
- package/dist/sandbox/smoke.js +380 -0
- package/dist/sandbox/worker-supervisor.js +1 -1
- package/dist/sandbox/worker.js +2 -5
- package/dist/sandbox/workspace.service.d.ts +1 -0
- package/dist/sandbox/workspace.service.js +28 -5
- package/dist/session/managed-attempt-tools.d.ts +18 -0
- package/dist/session/managed-attempt-tools.js +854 -0
- package/dist/session/managed-build-coordinator.d.ts +5 -0
- package/dist/session/managed-build-coordinator.js +105 -0
- package/dist/session/managed-build-receipt.d.ts +21 -0
- package/dist/session/managed-build-receipt.js +335 -0
- package/dist/session/phase-completion.artifacts.d.ts +17 -0
- package/dist/session/phase-completion.artifacts.js +233 -24
- package/dist/session/phase-completion.d.ts +11 -2
- package/dist/session/phase-completion.helpers.d.ts +5 -0
- package/dist/session/phase-completion.helpers.js +51 -10
- package/dist/session/phase-completion.js +12 -3
- package/dist/session/phase-completion.prompts.d.ts +3 -3
- package/dist/session/phase-completion.prompts.js +21 -6
- package/dist/session/phase-completion.runtime.d.ts +13 -1
- package/dist/session/phase-completion.runtime.js +189 -8
- package/dist/session/phase-completion.schemas.js +108 -3
- package/dist/session/phase-completion.validation.d.ts +4 -1
- package/dist/session/phase-completion.validation.js +151 -21
- package/dist/session/provider-cli/ci-repair-readiness.d.ts +14 -0
- package/dist/session/provider-cli/ci-repair-readiness.js +105 -0
- package/dist/session/provider-cli/provider-cli.artifacts.d.ts +3 -2
- package/dist/session/provider-cli/provider-cli.artifacts.js +65 -7
- package/dist/session/provider-cli/provider-cli.deterministic.js +47 -1
- package/dist/session/provider-cli/provider-cli.process.d.ts +23 -0
- package/dist/session/provider-cli/provider-cli.process.js +420 -32
- package/dist/session/provider-cli/provider-cli.prompts.d.ts +4 -1
- package/dist/session/provider-cli/provider-cli.prompts.js +99 -19
- package/dist/session/provider-cli/provider-cli.qa-sidecars.js +1 -0
- package/dist/session/provider-cli/provider-cli.runtime.d.ts +1 -1
- package/dist/session/provider-cli/provider-cli.runtime.js +6 -3
- package/dist/session/provider-cli/provider-cli.service.d.ts +20 -2
- package/dist/session/provider-cli/provider-cli.service.js +464 -102
- package/dist/session/provider-cli/provider-cli.types.d.ts +5 -0
- package/dist/session/provider-cli/provider-output-state.d.ts +2 -0
- package/dist/session/provider-cli/provider-output-state.js +91 -16
- package/dist/session/provider-cli/provider-usage.d.ts +7 -0
- package/dist/session/provider-cli/provider-usage.js +28 -5
- package/dist/session/session-redaction.d.ts +3 -0
- package/dist/session/session-redaction.js +38 -0
- package/dist/session/session.base.d.ts +6 -1
- package/dist/session/session.service.d.ts +8 -2
- package/dist/session/session.service.js +522 -41
- package/dist/session/session.types.d.ts +70 -0
- package/dist/session/session.types.js +76 -4
- package/dist/shared/format/format-bytes.d.ts +1 -0
- package/dist/shared/format/format-bytes.js +9 -0
- package/dist/shared/path/path.d.ts +1 -0
- package/dist/shared/path/path.js +21 -1
- package/dist/shared/process/host-process-recovery.d.ts +12 -0
- package/dist/shared/process/host-process-recovery.js +78 -0
- package/dist/shared/process/host-process-supervisor.d.ts +59 -0
- package/dist/shared/process/host-process-supervisor.js +330 -0
- package/dist/shared/process/process.d.ts +1 -0
- package/dist/shared/process/process.js +1 -0
- package/dist/shared/redaction/redaction.js +12 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/artifact-injector.d.ts +3 -2
- package/dist/worktree/artifact-injector.js +43 -4
- package/dist/worktree/dependency-warmer.d.ts +28 -1
- package/dist/worktree/dependency-warmer.js +308 -9
- package/dist/worktree/index.d.ts +2 -1
- package/dist/worktree/index.js +1 -0
- package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
- package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
- package/dist/worktree/worktree-paths.d.ts +4 -0
- package/dist/worktree/worktree-paths.js +30 -0
- package/dist/worktree/worktree.base.d.ts +3 -0
- package/dist/worktree/worktree.service.d.ts +9 -2
- package/dist/worktree/worktree.service.js +217 -48
- package/dist/worktree/worktree.types.d.ts +5 -1
- package/package.json +5 -3
- package/runtime/windows-job-supervisor.ps1 +444 -0
- package/runtime/windows-listener-owner.ps1 +33 -0
|
@@ -7,12 +7,14 @@ export interface RunOnceDispatchPacket {
|
|
|
7
7
|
readonly session: DispatchSession;
|
|
8
8
|
readonly lease: DispatchLease;
|
|
9
9
|
readonly execution: DispatchExecution;
|
|
10
|
+
readonly runtimeIntent: DispatchRuntimeIntent;
|
|
10
11
|
readonly serverLocal: DispatchServerLocalConfig;
|
|
11
12
|
readonly sandbox?: DispatchSandboxContext | null;
|
|
12
13
|
readonly pullRequest?: DispatchPullRequestContext | null;
|
|
13
14
|
readonly apiKeySecretGrant?: ApiKeySecretGrant | null;
|
|
14
15
|
readonly apiKeySecretGrantHandle?: ApiKeySecretGrantHandle | null;
|
|
15
16
|
readonly artifacts: readonly DispatchResolvedArtifact[];
|
|
17
|
+
readonly priorPhaseArtifacts?: readonly DispatchPriorPhaseArtifact[];
|
|
16
18
|
readonly warmPathOverrides?: readonly string[];
|
|
17
19
|
readonly qaBrowser?: DispatchQaBrowserContext | null;
|
|
18
20
|
readonly qaScope?: DispatchQaScope | null;
|
|
@@ -24,6 +26,10 @@ export interface RunOnceDispatchPacket {
|
|
|
24
26
|
readonly designerRefs?: DispatchDesignerRefHandoffContext | null;
|
|
25
27
|
readonly draft?: DispatchDraftPrompt | null;
|
|
26
28
|
}
|
|
29
|
+
export interface DispatchRuntimeIntent {
|
|
30
|
+
readonly executionClass: "analysis_only" | "managed_runtime";
|
|
31
|
+
readonly applicationRuntimeRequired: boolean;
|
|
32
|
+
}
|
|
27
33
|
export interface DispatchSandboxContext {
|
|
28
34
|
readonly dispatchAttemptId: string;
|
|
29
35
|
readonly executionLeaseId: string;
|
|
@@ -48,6 +54,9 @@ export interface DispatchPublishGrantHandle {
|
|
|
48
54
|
export interface DispatchSandboxResourceRequest {
|
|
49
55
|
readonly sandboxSlots: number;
|
|
50
56
|
readonly cpuMillis: number;
|
|
57
|
+
readonly workerMemoryLimitBytes: number;
|
|
58
|
+
readonly serviceMemoryLimitBytes: number;
|
|
59
|
+
readonly runtimeOverheadBytes: number;
|
|
51
60
|
readonly memoryBytes: number;
|
|
52
61
|
readonly workspaceBytes: number;
|
|
53
62
|
readonly pids: number;
|
|
@@ -101,6 +110,7 @@ export interface DispatchQaAppLaunchTarget {
|
|
|
101
110
|
readonly command?: string | null;
|
|
102
111
|
readonly working_directory?: string | null;
|
|
103
112
|
readonly ready_url?: string | null;
|
|
113
|
+
readonly managed_runtime_port?: string | null;
|
|
104
114
|
readonly routes: readonly string[];
|
|
105
115
|
readonly notes?: string | null;
|
|
106
116
|
}
|
|
@@ -189,6 +199,19 @@ export interface DispatchStoryQaDependencyContext {
|
|
|
189
199
|
readonly fromWorkItemId: string;
|
|
190
200
|
readonly toWorkItemId: string;
|
|
191
201
|
}
|
|
202
|
+
export interface DispatchPriorPhaseArtifact {
|
|
203
|
+
readonly workItemId: string;
|
|
204
|
+
readonly artifactId: string;
|
|
205
|
+
readonly cycleRunId: string;
|
|
206
|
+
readonly phase: "implementation" | "self_review" | "validation";
|
|
207
|
+
readonly status: string;
|
|
208
|
+
readonly producedAt: string;
|
|
209
|
+
readonly sourcePath: string;
|
|
210
|
+
readonly verdictJson: string;
|
|
211
|
+
readonly report: string;
|
|
212
|
+
readonly verdictContentHash: string;
|
|
213
|
+
readonly reportContentHash: string;
|
|
214
|
+
}
|
|
192
215
|
export interface DispatchDesignerRefHandoffContext {
|
|
193
216
|
readonly schemaVersion: number;
|
|
194
217
|
readonly kind: string;
|
|
@@ -285,6 +308,8 @@ export interface DispatchSession {
|
|
|
285
308
|
readonly id: string;
|
|
286
309
|
readonly model: string;
|
|
287
310
|
readonly lane: string;
|
|
311
|
+
readonly resumedFromSessionId?: string | null;
|
|
312
|
+
readonly resumeProviderThreadId?: string | null;
|
|
288
313
|
}
|
|
289
314
|
export interface DispatchLease {
|
|
290
315
|
readonly id: string;
|
|
@@ -353,6 +378,19 @@ export interface RunnerDispatchArtifact {
|
|
|
353
378
|
readonly producedBy: string;
|
|
354
379
|
readonly status: string;
|
|
355
380
|
readonly phase?: string | null;
|
|
381
|
+
/**
|
|
382
|
+
* QA sidecars are transported with the terminal result because managed
|
|
383
|
+
* worktrees may be released before the API mirrors the result. The API
|
|
384
|
+
* validates these bytes against the canonical evidence metadata and stores
|
|
385
|
+
* only that metadata.
|
|
386
|
+
*/
|
|
387
|
+
readonly sidecars?: readonly RunnerDispatchSidecar[] | null;
|
|
388
|
+
}
|
|
389
|
+
export interface RunnerDispatchSidecar {
|
|
390
|
+
readonly path: string;
|
|
391
|
+
readonly contentBase64: string;
|
|
392
|
+
readonly contentHash: string;
|
|
393
|
+
readonly bytes: number;
|
|
356
394
|
}
|
|
357
395
|
export interface RunOnceResultPacket {
|
|
358
396
|
readonly schemaVersion: 1;
|
|
@@ -368,4 +406,36 @@ export interface RunnerEventLine {
|
|
|
368
406
|
readonly event: RunnerDispatchEvent;
|
|
369
407
|
}
|
|
370
408
|
export declare function parseRunOnceDispatchPacket(value: unknown): RunOnceDispatchPacket;
|
|
409
|
+
export interface HostRuntimeReadinessProbeRequest {
|
|
410
|
+
readonly requestId: string;
|
|
411
|
+
readonly fencingToken: string;
|
|
412
|
+
readonly userId: string;
|
|
413
|
+
readonly projectId: string;
|
|
414
|
+
readonly repoId: string;
|
|
415
|
+
readonly runnerId: string;
|
|
416
|
+
readonly runnerInstanceId: string;
|
|
417
|
+
readonly attempt: number;
|
|
418
|
+
readonly repoName: string;
|
|
419
|
+
readonly remoteUrl: string;
|
|
420
|
+
readonly defaultBranch: string;
|
|
421
|
+
readonly cloneOnDemand: boolean;
|
|
422
|
+
readonly runnerRepoLocalPath: string;
|
|
423
|
+
readonly revision: string;
|
|
424
|
+
readonly expiresAt: string;
|
|
425
|
+
readonly repoRoot: string;
|
|
426
|
+
readonly cleanupWorktrees: boolean;
|
|
427
|
+
}
|
|
428
|
+
export interface HostRuntimeReadinessProbeResult {
|
|
429
|
+
readonly status: "parallel_ready" | "serialized" | "failed";
|
|
430
|
+
readonly runtimeContractFingerprint?: string | null;
|
|
431
|
+
readonly runtimeContractSource?: string | null;
|
|
432
|
+
readonly reasonCode?: string | null;
|
|
433
|
+
readonly reasonMessage?: string | null;
|
|
434
|
+
readonly qaAppLaunchTarget?: HostRuntimeReadinessQaLaunchTarget | null;
|
|
435
|
+
readonly checkedAt: string;
|
|
436
|
+
}
|
|
437
|
+
export interface HostRuntimeReadinessQaLaunchTarget {
|
|
438
|
+
readonly managedRuntimePort: string;
|
|
439
|
+
readonly routes: readonly string[];
|
|
440
|
+
}
|
|
371
441
|
export declare function failureResult(summary: string, events?: readonly RunnerDispatchEvent[]): RunOnceResultPacket;
|
|
@@ -14,12 +14,14 @@ export function parseRunOnceDispatchPacket(value) {
|
|
|
14
14
|
session: parseSession(packet.session),
|
|
15
15
|
lease: parseLease(packet.lease),
|
|
16
16
|
execution: parseExecution(packet.execution),
|
|
17
|
+
runtimeIntent: parseRuntimeIntent(packet.runtimeIntent),
|
|
17
18
|
serverLocal: parseServerLocal(packet.serverLocal),
|
|
18
19
|
sandbox: parseSandbox(packet.sandbox, schemaVersion),
|
|
19
20
|
pullRequest: parsePullRequest(packet.pullRequest),
|
|
20
21
|
apiKeySecretGrant: parseApiKeySecretGrant(packet.apiKeySecretGrant),
|
|
21
22
|
apiKeySecretGrantHandle: parseApiKeySecretGrantHandle(packet.apiKeySecretGrantHandle),
|
|
22
23
|
artifacts: parseArtifacts(packet.artifacts),
|
|
24
|
+
priorPhaseArtifacts: parsePriorPhaseArtifacts(packet.priorPhaseArtifacts),
|
|
23
25
|
warmPathOverrides: parseWarmPathOverrides(packet.warmPathOverrides),
|
|
24
26
|
qaBrowser: parseQaBrowser(packet.qaBrowser),
|
|
25
27
|
qaScope: parseQaScope(packet.qaScope ?? packet.qa_scope),
|
|
@@ -32,6 +34,21 @@ export function parseRunOnceDispatchPacket(value) {
|
|
|
32
34
|
draft: parseDraftPrompt(packet.draft)
|
|
33
35
|
};
|
|
34
36
|
}
|
|
37
|
+
function parseRuntimeIntent(value) {
|
|
38
|
+
if (value === undefined || value === null) {
|
|
39
|
+
return { executionClass: "managed_runtime", applicationRuntimeRequired: true };
|
|
40
|
+
}
|
|
41
|
+
const intent = asObject(value, "runtimeIntent");
|
|
42
|
+
const executionClass = requiredString(intent, "executionClass").trim().toLowerCase();
|
|
43
|
+
if (executionClass !== "analysis_only" && executionClass !== "managed_runtime") {
|
|
44
|
+
throw new Error("runtimeIntent.executionClass must be analysis_only or managed_runtime.");
|
|
45
|
+
}
|
|
46
|
+
const applicationRuntimeRequired = requiredBoolean(intent, "applicationRuntimeRequired");
|
|
47
|
+
if ((executionClass === "analysis_only") === applicationRuntimeRequired) {
|
|
48
|
+
throw new Error("runtimeIntent execution class and application runtime requirement are inconsistent.");
|
|
49
|
+
}
|
|
50
|
+
return { executionClass, applicationRuntimeRequired };
|
|
51
|
+
}
|
|
35
52
|
function parseSandbox(value, schemaVersion) {
|
|
36
53
|
if (value === undefined || value === null) {
|
|
37
54
|
if (schemaVersion === 2) {
|
|
@@ -93,17 +110,38 @@ function parseSandboxResourceRequest(value) {
|
|
|
93
110
|
return {
|
|
94
111
|
sandboxSlots: 1,
|
|
95
112
|
cpuMillis: 2000,
|
|
96
|
-
|
|
113
|
+
workerMemoryLimitBytes: 2 * 1024 ** 3,
|
|
114
|
+
serviceMemoryLimitBytes: 0,
|
|
115
|
+
runtimeOverheadBytes: 128 * 1024 ** 2,
|
|
116
|
+
memoryBytes: 2 * 1024 ** 3 + 128 * 1024 ** 2,
|
|
97
117
|
workspaceBytes: 10 * 1024 ** 3,
|
|
98
118
|
pids: 512,
|
|
99
119
|
buildSlots: 1
|
|
100
120
|
};
|
|
101
121
|
}
|
|
102
122
|
const resources = asObject(value, "sandbox.requestedResources");
|
|
123
|
+
const memoryBytes = boundedInteger(resources, "memoryBytes", 128 * 1024 ** 2, 512 * 1024 ** 3);
|
|
124
|
+
const hasMemoryBreakdown = ["workerMemoryLimitBytes", "serviceMemoryLimitBytes", "runtimeOverheadBytes"]
|
|
125
|
+
.some(key => resources[key] !== undefined && resources[key] !== null);
|
|
126
|
+
const workerMemoryLimitBytes = hasMemoryBreakdown
|
|
127
|
+
? boundedInteger(resources, "workerMemoryLimitBytes", 128 * 1024 ** 2, 512 * 1024 ** 3)
|
|
128
|
+
: memoryBytes;
|
|
129
|
+
const serviceMemoryLimitBytes = hasMemoryBreakdown
|
|
130
|
+
? boundedInteger(resources, "serviceMemoryLimitBytes", 0, 512 * 1024 ** 3)
|
|
131
|
+
: 0;
|
|
132
|
+
const runtimeOverheadBytes = hasMemoryBreakdown
|
|
133
|
+
? boundedInteger(resources, "runtimeOverheadBytes", 0, 64 * 1024 ** 3)
|
|
134
|
+
: 0;
|
|
135
|
+
if (workerMemoryLimitBytes + serviceMemoryLimitBytes + runtimeOverheadBytes !== memoryBytes) {
|
|
136
|
+
throw new Error("sandbox.requestedResources memory breakdown must equal memoryBytes.");
|
|
137
|
+
}
|
|
103
138
|
return {
|
|
104
139
|
sandboxSlots: boundedInteger(resources, "sandboxSlots", 1, 4),
|
|
105
140
|
cpuMillis: boundedInteger(resources, "cpuMillis", 100, 64_000),
|
|
106
|
-
|
|
141
|
+
workerMemoryLimitBytes,
|
|
142
|
+
serviceMemoryLimitBytes,
|
|
143
|
+
runtimeOverheadBytes,
|
|
144
|
+
memoryBytes,
|
|
107
145
|
workspaceBytes: boundedInteger(resources, "workspaceBytes", 1024 ** 3, 10 * 1024 ** 4),
|
|
108
146
|
pids: boundedInteger(resources, "pids", 32, 65_536),
|
|
109
147
|
buildSlots: boundedInteger(resources, "buildSlots", 0, 4)
|
|
@@ -111,7 +149,7 @@ function parseSandboxResourceRequest(value) {
|
|
|
111
149
|
}
|
|
112
150
|
function parseSandboxNetworkPolicy(value) {
|
|
113
151
|
if (value === undefined || value === null)
|
|
114
|
-
return { mode: "
|
|
152
|
+
return { mode: "offline", allowedHosts: [] };
|
|
115
153
|
const policy = asObject(value, "sandbox.networkPolicy");
|
|
116
154
|
const mode = requiredString(policy, "mode");
|
|
117
155
|
if (mode !== "offline" && mode !== "public_allowlist" && mode !== "open_public") {
|
|
@@ -128,6 +166,9 @@ function parseSandboxNetworkPolicy(value) {
|
|
|
128
166
|
if (mode !== "public_allowlist" && allowedHosts.length > 0) {
|
|
129
167
|
throw new Error("sandbox.networkPolicy.allowedHosts requires public_allowlist mode.");
|
|
130
168
|
}
|
|
169
|
+
if (mode === "public_allowlist" && allowedHosts.length === 0) {
|
|
170
|
+
return { mode: "offline", allowedHosts: [] };
|
|
171
|
+
}
|
|
131
172
|
return { mode, allowedHosts };
|
|
132
173
|
}
|
|
133
174
|
export function failureResult(summary, events = []) {
|
|
@@ -182,7 +223,9 @@ function parseSession(value) {
|
|
|
182
223
|
return {
|
|
183
224
|
id: requiredString(session, "id"),
|
|
184
225
|
model: requiredString(session, "model"),
|
|
185
|
-
lane: requiredString(session, "lane")
|
|
226
|
+
lane: requiredString(session, "lane"),
|
|
227
|
+
resumedFromSessionId: optionalString(session, "resumedFromSessionId"),
|
|
228
|
+
resumeProviderThreadId: optionalString(session, "resumeProviderThreadId")
|
|
186
229
|
};
|
|
187
230
|
}
|
|
188
231
|
function parseLease(value) {
|
|
@@ -381,6 +424,7 @@ function parseQaAppLaunchTarget(value) {
|
|
|
381
424
|
command: optionalString(launchTarget, "command"),
|
|
382
425
|
working_directory: optionalString(launchTarget, "working_directory"),
|
|
383
426
|
ready_url: optionalString(launchTarget, "ready_url"),
|
|
427
|
+
managed_runtime_port: optionalString(launchTarget, "managed_runtime_port"),
|
|
384
428
|
routes: Array.isArray(routes)
|
|
385
429
|
? routes.map((entry, index) => {
|
|
386
430
|
if (typeof entry !== "string") {
|
|
@@ -659,6 +703,34 @@ function parseArtifacts(value) {
|
|
|
659
703
|
};
|
|
660
704
|
});
|
|
661
705
|
}
|
|
706
|
+
function parsePriorPhaseArtifacts(value) {
|
|
707
|
+
if (value === undefined || value === null) {
|
|
708
|
+
return [];
|
|
709
|
+
}
|
|
710
|
+
if (!Array.isArray(value)) {
|
|
711
|
+
throw new Error("priorPhaseArtifacts must be an array when provided.");
|
|
712
|
+
}
|
|
713
|
+
return value.map((entry, index) => {
|
|
714
|
+
const artifact = asObject(entry, `priorPhaseArtifacts[${index}]`);
|
|
715
|
+
const phase = requiredString(artifact, "phase").trim().toLowerCase().replace(/-/g, "_");
|
|
716
|
+
if (phase !== "implementation" && phase !== "self_review" && phase !== "validation") {
|
|
717
|
+
throw new Error(`priorPhaseArtifacts[${index}].phase must be implementation, self_review, or validation.`);
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
workItemId: requiredString(artifact, "workItemId"),
|
|
721
|
+
artifactId: requiredString(artifact, "artifactId"),
|
|
722
|
+
cycleRunId: requiredString(artifact, "cycleRunId"),
|
|
723
|
+
phase,
|
|
724
|
+
status: requiredString(artifact, "status"),
|
|
725
|
+
producedAt: requiredString(artifact, "producedAt"),
|
|
726
|
+
sourcePath: requiredString(artifact, "sourcePath"),
|
|
727
|
+
verdictJson: requiredString(artifact, "verdictJson"),
|
|
728
|
+
report: optionalString(artifact, "report") ?? "",
|
|
729
|
+
verdictContentHash: requiredString(artifact, "verdictContentHash").toLowerCase(),
|
|
730
|
+
reportContentHash: requiredString(artifact, "reportContentHash").toLowerCase()
|
|
731
|
+
};
|
|
732
|
+
});
|
|
733
|
+
}
|
|
662
734
|
function asObject(value, label) {
|
|
663
735
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
664
736
|
throw new Error(`${label} must be an object.`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatBytes(bytes: number): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function formatBytes(bytes) {
|
|
2
|
+
if (!Number.isFinite(bytes) || bytes <= 0)
|
|
3
|
+
return "0 B";
|
|
4
|
+
const units = ["B", "KiB", "MiB", "GiB", "TiB"];
|
|
5
|
+
const power = Math.max(0, Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024))));
|
|
6
|
+
const value = bytes / 1024 ** power;
|
|
7
|
+
const precision = power === 0 || value >= 100 ? 0 : 1;
|
|
8
|
+
return `${value.toFixed(precision)} ${units[power]}`;
|
|
9
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function isSubPath(root: string, candidate: string): boolean;
|
|
2
2
|
export declare function resolveExistingDirectoryPath(inputPath: string): Promise<string>;
|
|
3
|
+
export declare function resolvePathIdentity(inputPath: string): Promise<string>;
|
|
3
4
|
export declare function ensureDirectory(inputPath: string): Promise<string>;
|
|
4
5
|
export declare function safeSegment(value: string, fallback: string): string;
|
package/dist/shared/path/path.js
CHANGED
|
@@ -4,7 +4,9 @@ export function isSubPath(root, candidate) {
|
|
|
4
4
|
const normalizedRoot = path.resolve(root);
|
|
5
5
|
const normalizedCandidate = path.resolve(candidate);
|
|
6
6
|
const relative = path.relative(normalizedRoot, normalizedCandidate);
|
|
7
|
-
return relative === "" || (
|
|
7
|
+
return relative === "" || (relative !== ".."
|
|
8
|
+
&& !relative.startsWith(`..${path.sep}`)
|
|
9
|
+
&& !path.isAbsolute(relative));
|
|
8
10
|
}
|
|
9
11
|
export async function resolveExistingDirectoryPath(inputPath) {
|
|
10
12
|
const stat = await fs.stat(inputPath);
|
|
@@ -13,6 +15,24 @@ export async function resolveExistingDirectoryPath(inputPath) {
|
|
|
13
15
|
}
|
|
14
16
|
return await fs.realpath(inputPath);
|
|
15
17
|
}
|
|
18
|
+
export async function resolvePathIdentity(inputPath) {
|
|
19
|
+
let existingAncestor = path.resolve(inputPath);
|
|
20
|
+
const missingSegments = [];
|
|
21
|
+
while (true) {
|
|
22
|
+
try {
|
|
23
|
+
return path.join(await fs.realpath(existingAncestor), ...missingSegments);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error.code !== "ENOENT")
|
|
27
|
+
throw error;
|
|
28
|
+
const parent = path.dirname(existingAncestor);
|
|
29
|
+
if (parent === existingAncestor)
|
|
30
|
+
throw error;
|
|
31
|
+
missingSegments.unshift(path.basename(existingAncestor));
|
|
32
|
+
existingAncestor = parent;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
16
36
|
export async function ensureDirectory(inputPath) {
|
|
17
37
|
await fs.mkdir(inputPath, { recursive: true });
|
|
18
38
|
return await resolveExistingDirectoryPath(inputPath);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HostProcessSupervisorMetadata } from "./host-process-supervisor.js";
|
|
2
|
+
export type HostProcessRecoveryOutcome = "absent" | "terminated" | "pid_reused" | "unverifiable";
|
|
3
|
+
export interface HostProcessRecovery {
|
|
4
|
+
recover(metadata: HostProcessSupervisorMetadata): Promise<HostProcessRecoveryOutcome>;
|
|
5
|
+
}
|
|
6
|
+
/** Exact, birth-marker-fenced recovery for a process tree left by an old bridge. */
|
|
7
|
+
export declare class ExactHostProcessRecovery implements HostProcessRecovery {
|
|
8
|
+
private readonly platform;
|
|
9
|
+
private readonly graceMs;
|
|
10
|
+
constructor(platform?: NodeJS.Platform, graceMs?: number);
|
|
11
|
+
recover(metadata: HostProcessSupervisorMetadata): Promise<HostProcessRecoveryOutcome>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { captureHostProcessIdentity } from "./host-process-supervisor.js";
|
|
2
|
+
/** Exact, birth-marker-fenced recovery for a process tree left by an old bridge. */
|
|
3
|
+
export class ExactHostProcessRecovery {
|
|
4
|
+
platform;
|
|
5
|
+
graceMs;
|
|
6
|
+
constructor(platform = process.platform, graceMs = 2_000) {
|
|
7
|
+
this.platform = platform;
|
|
8
|
+
this.graceMs = graceMs;
|
|
9
|
+
}
|
|
10
|
+
async recover(metadata) {
|
|
11
|
+
if (!processExists(metadata.rootPid))
|
|
12
|
+
return "absent";
|
|
13
|
+
if (!metadata.rootIdentity)
|
|
14
|
+
return "unverifiable";
|
|
15
|
+
const currentIdentity = captureHostProcessIdentity(metadata.rootPid, this.platform);
|
|
16
|
+
if (!currentIdentity)
|
|
17
|
+
return processExists(metadata.rootPid) ? "unverifiable" : "absent";
|
|
18
|
+
if (currentIdentity !== metadata.rootIdentity)
|
|
19
|
+
return "pid_reused";
|
|
20
|
+
if (this.platform === "win32") {
|
|
21
|
+
signalExactProcess(metadata.rootPid, "SIGTERM");
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
signalExactProcessGroup(metadata.rootPid, "SIGTERM");
|
|
25
|
+
}
|
|
26
|
+
await waitForExit(metadata.rootPid, this.graceMs);
|
|
27
|
+
if (processExists(metadata.rootPid)) {
|
|
28
|
+
if (captureHostProcessIdentity(metadata.rootPid, this.platform) !== metadata.rootIdentity) {
|
|
29
|
+
return "pid_reused";
|
|
30
|
+
}
|
|
31
|
+
if (this.platform === "win32") {
|
|
32
|
+
signalExactProcess(metadata.rootPid, "SIGKILL");
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
signalExactProcessGroup(metadata.rootPid, "SIGKILL");
|
|
36
|
+
}
|
|
37
|
+
await waitForExit(metadata.rootPid, this.graceMs);
|
|
38
|
+
}
|
|
39
|
+
if (processExists(metadata.rootPid)
|
|
40
|
+
&& captureHostProcessIdentity(metadata.rootPid, this.platform) === metadata.rootIdentity) {
|
|
41
|
+
throw new Error("host_process_recovery_degraded: exact supervised process tree did not stop.");
|
|
42
|
+
}
|
|
43
|
+
return "terminated";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function processExists(pid) {
|
|
47
|
+
try {
|
|
48
|
+
process.kill(pid, 0);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return error.code === "EPERM";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function signalExactProcess(pid, signal) {
|
|
56
|
+
try {
|
|
57
|
+
process.kill(pid, signal);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error.code !== "ESRCH")
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function signalExactProcessGroup(pid, signal) {
|
|
65
|
+
try {
|
|
66
|
+
process.kill(-pid, signal);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.code !== "ESRCH")
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function waitForExit(pid, timeoutMs) {
|
|
74
|
+
const deadline = Date.now() + timeoutMs;
|
|
75
|
+
while (Date.now() < deadline && processExists(pid)) {
|
|
76
|
+
await new Promise(resolve => setTimeout(resolve, 25));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
export type HostProcessSupervisorStrategy = "windows_job_object" | "unix_process_group";
|
|
3
|
+
export interface HostProcessSupervisionRequest {
|
|
4
|
+
readonly command: string;
|
|
5
|
+
readonly args: readonly string[];
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
8
|
+
}
|
|
9
|
+
export interface HostProcessSupervisedLaunch {
|
|
10
|
+
readonly command: string;
|
|
11
|
+
readonly args: readonly string[];
|
|
12
|
+
readonly detached: boolean;
|
|
13
|
+
readonly supervisorId: string;
|
|
14
|
+
readonly strategy: HostProcessSupervisorStrategy;
|
|
15
|
+
readonly metadataPath: string;
|
|
16
|
+
readonly memoryTelemetryPath?: string;
|
|
17
|
+
readonly payloadPath?: string;
|
|
18
|
+
readonly payloadSha256?: string;
|
|
19
|
+
readonly readyPath?: string;
|
|
20
|
+
readonly startGrantPath?: string;
|
|
21
|
+
readonly commandDigest: string;
|
|
22
|
+
}
|
|
23
|
+
export interface HostProcessProviderReady {
|
|
24
|
+
readonly schemaVersion: 1;
|
|
25
|
+
readonly supervisorId: string;
|
|
26
|
+
readonly rootPid?: number;
|
|
27
|
+
readonly rootIdentity?: string | null;
|
|
28
|
+
readonly providerPid: number;
|
|
29
|
+
readonly providerCreatedAt: string;
|
|
30
|
+
readonly providerIdentity?: string | null;
|
|
31
|
+
readonly startAttempt: number;
|
|
32
|
+
}
|
|
33
|
+
export interface HostProcessSupervisorMetadata {
|
|
34
|
+
readonly schemaVersion: 1;
|
|
35
|
+
readonly supervisorId: string;
|
|
36
|
+
readonly strategy: HostProcessSupervisorStrategy;
|
|
37
|
+
readonly rootPid: number;
|
|
38
|
+
readonly rootCreatedAt: string;
|
|
39
|
+
readonly rootIdentity?: string | null;
|
|
40
|
+
readonly commandDigest: string;
|
|
41
|
+
readonly worktreePath: string;
|
|
42
|
+
readonly runtimeLeaseId: string;
|
|
43
|
+
readonly dispatchAttemptId: string;
|
|
44
|
+
readonly fencingToken: string;
|
|
45
|
+
readonly bridgePid: number;
|
|
46
|
+
readonly providerPid?: number;
|
|
47
|
+
readonly providerCreatedAt?: string;
|
|
48
|
+
readonly providerIdentity?: string | null;
|
|
49
|
+
readonly providerStartAttempt?: number;
|
|
50
|
+
}
|
|
51
|
+
export declare function resolveHostProcessSupervisedLaunch(request: HostProcessSupervisionRequest, resolvedCommand: string, resolvedArgs: readonly string[], platform?: NodeJS.Platform): HostProcessSupervisedLaunch | null;
|
|
52
|
+
export declare function persistHostProcessSupervisorMetadata(launch: HostProcessSupervisedLaunch, request: HostProcessSupervisionRequest, rootPid: number, rootCreatedAt: string, providerReady?: HostProcessProviderReady): HostProcessSupervisorMetadata;
|
|
53
|
+
/**
|
|
54
|
+
* Captures an OS-issued process birth marker. Recovery compares this marker
|
|
55
|
+
* before signalling a persisted PID so PID reuse can never redirect cleanup
|
|
56
|
+
* at an unrelated process.
|
|
57
|
+
*/
|
|
58
|
+
export declare function captureHostProcessIdentity(pid: number, platform?: NodeJS.Platform): string | null;
|
|
59
|
+
export declare function terminateHostProcessTree(child: ChildProcess, launch: HostProcessSupervisedLaunch | null, graceMs?: number): Promise<void>;
|