codex-orchestrator 0.1.46 → 0.1.48
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/CHANGELOG.md +34 -0
- package/dist/src/config/schema.d.ts +5 -1
- package/dist/src/config/schema.d.ts.map +1 -1
- package/dist/src/config/schema.js +16 -0
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/runner/command-utils.d.ts.map +1 -1
- package/dist/src/runner/command-utils.js +1 -0
- package/dist/src/runner/command-utils.js.map +1 -1
- package/dist/src/runner/completion-report.d.ts +6 -1
- package/dist/src/runner/completion-report.d.ts.map +1 -1
- package/dist/src/runner/completion-report.js +26 -1
- package/dist/src/runner/completion-report.js.map +1 -1
- package/dist/src/runner/handoff-evidence.d.ts.map +1 -1
- package/dist/src/runner/handoff-evidence.js +13 -8
- package/dist/src/runner/handoff-evidence.js.map +1 -1
- package/dist/src/runner/local-execution-session.d.ts.map +1 -1
- package/dist/src/runner/local-execution-session.js +24 -7
- package/dist/src/runner/local-execution-session.js.map +1 -1
- package/dist/src/runner/mission-activation.d.ts +29 -0
- package/dist/src/runner/mission-activation.d.ts.map +1 -0
- package/dist/src/runner/mission-activation.js +54 -0
- package/dist/src/runner/mission-activation.js.map +1 -0
- package/dist/src/runner/mission-agent-broker.d.ts +53 -0
- package/dist/src/runner/mission-agent-broker.d.ts.map +1 -0
- package/dist/src/runner/mission-agent-broker.js +182 -0
- package/dist/src/runner/mission-agent-broker.js.map +1 -0
- package/dist/src/runner/mission-cancellation.d.ts +77 -0
- package/dist/src/runner/mission-cancellation.d.ts.map +1 -0
- package/dist/src/runner/mission-cancellation.js +180 -0
- package/dist/src/runner/mission-cancellation.js.map +1 -0
- package/dist/src/runner/mission-canonical-path.d.ts +34 -0
- package/dist/src/runner/mission-canonical-path.d.ts.map +1 -0
- package/dist/src/runner/mission-canonical-path.js +121 -0
- package/dist/src/runner/mission-canonical-path.js.map +1 -0
- package/dist/src/runner/mission-capability-kernel.d.ts +34 -0
- package/dist/src/runner/mission-capability-kernel.d.ts.map +1 -0
- package/dist/src/runner/mission-capability-kernel.js +158 -0
- package/dist/src/runner/mission-capability-kernel.js.map +1 -0
- package/dist/src/runner/mission-check-policy.d.ts +17 -0
- package/dist/src/runner/mission-check-policy.d.ts.map +1 -0
- package/dist/src/runner/mission-check-policy.js +31 -0
- package/dist/src/runner/mission-check-policy.js.map +1 -0
- package/dist/src/runner/mission-coordinator-lock.d.ts +16 -0
- package/dist/src/runner/mission-coordinator-lock.d.ts.map +1 -0
- package/dist/src/runner/mission-coordinator-lock.js +170 -0
- package/dist/src/runner/mission-coordinator-lock.js.map +1 -0
- package/dist/src/runner/mission-deployment.d.ts +21 -0
- package/dist/src/runner/mission-deployment.d.ts.map +1 -0
- package/dist/src/runner/mission-deployment.js +108 -0
- package/dist/src/runner/mission-deployment.js.map +1 -0
- package/dist/src/runner/mission-evaluation.d.ts +61 -0
- package/dist/src/runner/mission-evaluation.d.ts.map +1 -0
- package/dist/src/runner/mission-evaluation.js +183 -0
- package/dist/src/runner/mission-evaluation.js.map +1 -0
- package/dist/src/runner/mission-executor-probe.d.ts +10 -0
- package/dist/src/runner/mission-executor-probe.d.ts.map +1 -0
- package/dist/src/runner/mission-executor-probe.js +180 -0
- package/dist/src/runner/mission-executor-probe.js.map +1 -0
- package/dist/src/runner/mission-git-contracts.d.ts +75 -0
- package/dist/src/runner/mission-git-contracts.d.ts.map +1 -0
- package/dist/src/runner/mission-git-contracts.js +201 -0
- package/dist/src/runner/mission-git-contracts.js.map +1 -0
- package/dist/src/runner/mission-git-runtime-owner-ref.d.ts +13 -0
- package/dist/src/runner/mission-git-runtime-owner-ref.d.ts.map +1 -0
- package/dist/src/runner/mission-git-runtime-owner-ref.js +111 -0
- package/dist/src/runner/mission-git-runtime-owner-ref.js.map +1 -0
- package/dist/src/runner/mission-git-transaction.d.ts +97 -0
- package/dist/src/runner/mission-git-transaction.d.ts.map +1 -0
- package/dist/src/runner/mission-git-transaction.js +1009 -0
- package/dist/src/runner/mission-git-transaction.js.map +1 -0
- package/dist/src/runner/mission-identifiers.d.ts +31 -0
- package/dist/src/runner/mission-identifiers.d.ts.map +1 -0
- package/dist/src/runner/mission-identifiers.js +75 -0
- package/dist/src/runner/mission-identifiers.js.map +1 -0
- package/dist/src/runner/mission-input-snapshot.d.ts +21 -0
- package/dist/src/runner/mission-input-snapshot.d.ts.map +1 -0
- package/dist/src/runner/mission-input-snapshot.js +63 -0
- package/dist/src/runner/mission-input-snapshot.js.map +1 -0
- package/dist/src/runner/mission-ownership.d.ts +38 -0
- package/dist/src/runner/mission-ownership.d.ts.map +1 -0
- package/dist/src/runner/mission-ownership.js +88 -0
- package/dist/src/runner/mission-ownership.js.map +1 -0
- package/dist/src/runner/mission-patch-audit.d.ts +21 -0
- package/dist/src/runner/mission-patch-audit.d.ts.map +1 -0
- package/dist/src/runner/mission-patch-audit.js +186 -0
- package/dist/src/runner/mission-patch-audit.js.map +1 -0
- package/dist/src/runner/mission-path-language.d.ts +6 -0
- package/dist/src/runner/mission-path-language.d.ts.map +1 -0
- package/dist/src/runner/mission-path-language.js +106 -0
- package/dist/src/runner/mission-path-language.js.map +1 -0
- package/dist/src/runner/mission-permit-authority.d.ts +26 -0
- package/dist/src/runner/mission-permit-authority.d.ts.map +1 -0
- package/dist/src/runner/mission-permit-authority.js +145 -0
- package/dist/src/runner/mission-permit-authority.js.map +1 -0
- package/dist/src/runner/mission-plan-parent-coordinator.d.ts +87 -0
- package/dist/src/runner/mission-plan-parent-coordinator.d.ts.map +1 -0
- package/dist/src/runner/mission-plan-parent-coordinator.js +412 -0
- package/dist/src/runner/mission-plan-parent-coordinator.js.map +1 -0
- package/dist/src/runner/mission-plan-parent.d.ts +154 -0
- package/dist/src/runner/mission-plan-parent.d.ts.map +1 -0
- package/dist/src/runner/mission-plan-parent.js +583 -0
- package/dist/src/runner/mission-plan-parent.js.map +1 -0
- package/dist/src/runner/mission-process-executor.d.ts +25 -0
- package/dist/src/runner/mission-process-executor.d.ts.map +1 -0
- package/dist/src/runner/mission-process-executor.js +182 -0
- package/dist/src/runner/mission-process-executor.js.map +1 -0
- package/dist/src/runner/mission-quarantine-patch-executor.d.ts +29 -0
- package/dist/src/runner/mission-quarantine-patch-executor.d.ts.map +1 -0
- package/dist/src/runner/mission-quarantine-patch-executor.js +121 -0
- package/dist/src/runner/mission-quarantine-patch-executor.js.map +1 -0
- package/dist/src/runner/mission-repository-executor.d.ts +54 -0
- package/dist/src/runner/mission-repository-executor.d.ts.map +1 -0
- package/dist/src/runner/mission-repository-executor.js +192 -0
- package/dist/src/runner/mission-repository-executor.js.map +1 -0
- package/dist/src/runner/mission-repository-observer.d.ts +8 -0
- package/dist/src/runner/mission-repository-observer.d.ts.map +1 -0
- package/dist/src/runner/mission-repository-observer.js +24 -0
- package/dist/src/runner/mission-repository-observer.js.map +1 -0
- package/dist/src/runner/mission-reservations.d.ts +15 -0
- package/dist/src/runner/mission-reservations.d.ts.map +1 -0
- package/dist/src/runner/mission-reservations.js +116 -0
- package/dist/src/runner/mission-reservations.js.map +1 -0
- package/dist/src/runner/mission-resolution-loop.d.ts +172 -0
- package/dist/src/runner/mission-resolution-loop.d.ts.map +1 -0
- package/dist/src/runner/mission-resolution-loop.js +318 -0
- package/dist/src/runner/mission-resolution-loop.js.map +1 -0
- package/dist/src/runner/mission-runtime-owner.d.ts +36 -0
- package/dist/src/runner/mission-runtime-owner.d.ts.map +1 -0
- package/dist/src/runner/mission-runtime-owner.js +174 -0
- package/dist/src/runner/mission-runtime-owner.js.map +1 -0
- package/dist/src/runner/mission-safe-executor.d.ts +96 -0
- package/dist/src/runner/mission-safe-executor.d.ts.map +1 -0
- package/dist/src/runner/mission-safe-executor.js +288 -0
- package/dist/src/runner/mission-safe-executor.js.map +1 -0
- package/dist/src/runner/mission-sandbox.d.ts +16 -0
- package/dist/src/runner/mission-sandbox.d.ts.map +1 -0
- package/dist/src/runner/mission-sandbox.js +91 -0
- package/dist/src/runner/mission-sandbox.js.map +1 -0
- package/dist/src/runner/mission-scheduler.d.ts +57 -0
- package/dist/src/runner/mission-scheduler.d.ts.map +1 -0
- package/dist/src/runner/mission-scheduler.js +205 -0
- package/dist/src/runner/mission-scheduler.js.map +1 -0
- package/dist/src/runner/mission-scope-expansion.d.ts +47 -0
- package/dist/src/runner/mission-scope-expansion.d.ts.map +1 -0
- package/dist/src/runner/mission-scope-expansion.js +147 -0
- package/dist/src/runner/mission-scope-expansion.js.map +1 -0
- package/dist/src/runner/mission-state-machine.d.ts +118 -0
- package/dist/src/runner/mission-state-machine.d.ts.map +1 -0
- package/dist/src/runner/mission-state-machine.js +316 -0
- package/dist/src/runner/mission-state-machine.js.map +1 -0
- package/dist/src/runner/mission-state-store.d.ts +70 -0
- package/dist/src/runner/mission-state-store.d.ts.map +1 -0
- package/dist/src/runner/mission-state-store.js +856 -0
- package/dist/src/runner/mission-state-store.js.map +1 -0
- package/dist/src/runner/prompt.d.ts.map +1 -1
- package/dist/src/runner/prompt.js +5 -2
- package/dist/src/runner/prompt.js.map +1 -1
- package/dist/src/runner/review-gates.js +41 -1
- package/dist/src/runner/review-gates.js.map +1 -1
- package/dist/src/runner/rework-policy.d.ts +2 -1
- package/dist/src/runner/rework-policy.d.ts.map +1 -1
- package/dist/src/runner/rework-policy.js +3 -0
- package/dist/src/runner/rework-policy.js.map +1 -1
- package/dist/src/setup/project-config.d.ts.map +1 -1
- package/dist/src/setup/project-config.js +5 -0
- package/dist/src/setup/project-config.js.map +1 -1
- package/docs/deep-dive.md +39 -17
- package/package.json +1 -1
- package/prompts/workflows/issue-tree-orchestration.md +1 -1
- package/prompts/workflows/scoped-implementation.md +2 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type MissionSandboxBackend = 'macos-sandbox' | 'linux-bwrap';
|
|
2
|
+
export type MissionCapability = 'git-status' | 'read-file' | 'validate-patch';
|
|
3
|
+
export declare const missionGitStatusArgv: readonly ["/usr/bin/git", "-c", "core.fsmonitor=false", "-c", "core.untrackedCache=false", "-c", "core.hooksPath=/dev/null", "status", "--porcelain=v1", "--untracked-files=all"];
|
|
4
|
+
export interface MissionCapabilityProbeInput {
|
|
5
|
+
platform: NodeJS.Platform;
|
|
6
|
+
commands: ReadonlySet<string>;
|
|
7
|
+
}
|
|
8
|
+
export interface MissionCapabilityProbeResult {
|
|
9
|
+
prerequisitesAvailable: boolean;
|
|
10
|
+
backend?: MissionSandboxBackend;
|
|
11
|
+
missing: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface MissionCapabilityRequest {
|
|
14
|
+
missionId: string;
|
|
15
|
+
actionKey: string;
|
|
16
|
+
capability: MissionCapability;
|
|
17
|
+
argv: string[];
|
|
18
|
+
requestedPaths: string[];
|
|
19
|
+
grantedPaths: string[];
|
|
20
|
+
inputSnapshot: string;
|
|
21
|
+
fencingEpoch: number;
|
|
22
|
+
expiresAt: string;
|
|
23
|
+
readPath?: string;
|
|
24
|
+
maxReadBytes?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface MissionCapabilityPermit extends MissionCapabilityRequest {
|
|
27
|
+
network: 'deny';
|
|
28
|
+
workspace: 'read-only';
|
|
29
|
+
}
|
|
30
|
+
export declare function missionCapabilityPermitFingerprint(permit: MissionCapabilityPermit): string;
|
|
31
|
+
export declare function discoverMissionExecutorPrerequisites(input: MissionCapabilityProbeInput): MissionCapabilityProbeResult;
|
|
32
|
+
export declare function authorizeMissionCapability(request: MissionCapabilityRequest): MissionCapabilityPermit;
|
|
33
|
+
export declare function scrubMissionExecutorEnv(source: NodeJS.ProcessEnv, allowedKeys: string[]): Record<string, string>;
|
|
34
|
+
//# sourceMappingURL=mission-capability-kernel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-capability-kernel.d.ts","sourceRoot":"","sources":["../../../src/runner/mission-capability-kernel.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG,aAAa,CAAC;AACpE,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE9E,eAAO,MAAM,oBAAoB,mLAMvB,CAAC;AAEX,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC1B,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,4BAA4B;IAC3C,sBAAsB,EAAE,OAAO,CAAC;IAChC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,WAAW,CAAC;CACxB;AAED,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,uBAAuB,GAAG,MAAM,CAkB1F;AAOD,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,2BAA2B,GACjC,4BAA4B,CA4B9B;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,wBAAwB,GAChC,uBAAuB,CAoDzB;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,UAAU,EACzB,WAAW,EAAE,MAAM,EAAE,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxB"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { uniqueSortedPaths } from '../path-policy.js';
|
|
4
|
+
import { assertMissionPathPattern, scopePatternContainedBy } from './mission-path-language.js';
|
|
5
|
+
export const missionGitStatusArgv = [
|
|
6
|
+
'/usr/bin/git',
|
|
7
|
+
'-c', 'core.fsmonitor=false',
|
|
8
|
+
'-c', 'core.untrackedCache=false',
|
|
9
|
+
'-c', 'core.hooksPath=/dev/null',
|
|
10
|
+
'status', '--porcelain=v1', '--untracked-files=all',
|
|
11
|
+
];
|
|
12
|
+
export function missionCapabilityPermitFingerprint(permit) {
|
|
13
|
+
const canonical = JSON.stringify([
|
|
14
|
+
'mission-capability-permit-v1',
|
|
15
|
+
permit.missionId,
|
|
16
|
+
permit.actionKey,
|
|
17
|
+
permit.capability,
|
|
18
|
+
permit.argv,
|
|
19
|
+
permit.requestedPaths,
|
|
20
|
+
permit.grantedPaths,
|
|
21
|
+
permit.inputSnapshot,
|
|
22
|
+
permit.fencingEpoch,
|
|
23
|
+
permit.expiresAt,
|
|
24
|
+
permit.readPath ?? null,
|
|
25
|
+
permit.maxReadBytes ?? null,
|
|
26
|
+
permit.network,
|
|
27
|
+
permit.workspace,
|
|
28
|
+
]);
|
|
29
|
+
return `sha256:${createHash('sha256').update(canonical).digest('hex')}`;
|
|
30
|
+
}
|
|
31
|
+
const secretPath = /(^|\/)(?:\.env(?:\..*)?|\.git(?:\/|$)|id_(?:rsa|ed25519)(?:\.pub)?$)/iu;
|
|
32
|
+
const safeEnvironmentKeys = new Set([
|
|
33
|
+
'CI', 'LANG', 'LC_ALL', 'LC_CTYPE', 'NO_COLOR', 'PATH', 'TERM', 'TZ',
|
|
34
|
+
]);
|
|
35
|
+
export function discoverMissionExecutorPrerequisites(input) {
|
|
36
|
+
if (input.platform === 'darwin') {
|
|
37
|
+
const missing = [
|
|
38
|
+
input.commands.has('/usr/bin/sandbox-exec') ? undefined : 'sandbox-exec',
|
|
39
|
+
input.commands.has('/usr/bin/git') ? undefined : 'git',
|
|
40
|
+
].filter((value) => value !== undefined);
|
|
41
|
+
return {
|
|
42
|
+
prerequisitesAvailable: missing.length === 0,
|
|
43
|
+
backend: missing.length === 0 ? 'macos-sandbox' : undefined,
|
|
44
|
+
missing,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (input.platform === 'linux') {
|
|
48
|
+
const hasBwrap = [...input.commands].some((command) => basename(command) === 'bwrap');
|
|
49
|
+
const hasGit = [...input.commands].some((command) => basename(command) === 'git');
|
|
50
|
+
const missing = [hasBwrap ? undefined : 'bwrap', hasGit ? undefined : 'git']
|
|
51
|
+
.filter((value) => value !== undefined);
|
|
52
|
+
return {
|
|
53
|
+
prerequisitesAvailable: missing.length === 0,
|
|
54
|
+
backend: missing.length === 0 ? 'linux-bwrap' : undefined,
|
|
55
|
+
missing,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
prerequisitesAvailable: false,
|
|
60
|
+
backend: undefined,
|
|
61
|
+
missing: [`unsupported-platform:${input.platform}`],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function authorizeMissionCapability(request) {
|
|
65
|
+
requireText(request.missionId, 'missionId');
|
|
66
|
+
requireText(request.actionKey, 'actionKey');
|
|
67
|
+
requireText(request.inputSnapshot, 'inputSnapshot');
|
|
68
|
+
if (!Number.isSafeInteger(request.fencingEpoch) || request.fencingEpoch <= 0) {
|
|
69
|
+
throw new Error('Mission capability fencingEpoch must be a positive integer.');
|
|
70
|
+
}
|
|
71
|
+
if (!Number.isFinite(Date.parse(request.expiresAt))) {
|
|
72
|
+
throw new Error('Mission capability expiresAt must be an ISO timestamp.');
|
|
73
|
+
}
|
|
74
|
+
assertAllowedArgv(request.capability, request.argv);
|
|
75
|
+
const grantedPaths = normalizeScope(request.grantedPaths, 'granted');
|
|
76
|
+
const requestedPaths = normalizeScope(request.requestedPaths, 'requested');
|
|
77
|
+
if (request.capability === 'read-file') {
|
|
78
|
+
if (typeof request.readPath !== 'string' || request.readPath.includes('*')
|
|
79
|
+
|| requestedPaths.length !== 1 || requestedPaths[0] !== request.readPath
|
|
80
|
+
|| !Number.isSafeInteger(request.maxReadBytes) || request.maxReadBytes <= 0
|
|
81
|
+
|| request.maxReadBytes > 1024 * 1024) {
|
|
82
|
+
throw new Error('Mission read-file requires one concrete readPath and maxReadBytes up to 1048576.');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (request.readPath !== undefined || request.maxReadBytes !== undefined) {
|
|
86
|
+
throw new Error(`Mission capability ${request.capability} forbids read-file inputs.`);
|
|
87
|
+
}
|
|
88
|
+
if (request.capability === 'git-status'
|
|
89
|
+
&& (requestedPaths.length !== 1 || requestedPaths[0] !== '**')) {
|
|
90
|
+
throw new Error('Mission capability git-status requires explicit whole-repository scope.');
|
|
91
|
+
}
|
|
92
|
+
for (const path of requestedPaths) {
|
|
93
|
+
if (secretPath.test(path)) {
|
|
94
|
+
throw new Error(`Mission capability requested secret path: ${path}.`);
|
|
95
|
+
}
|
|
96
|
+
if (!grantedPaths.some((grant) => scopePatternContainedBy(path, grant))) {
|
|
97
|
+
throw new Error(`Mission capability path is outside granted scope: ${path}.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
missionId: request.missionId,
|
|
102
|
+
actionKey: request.actionKey,
|
|
103
|
+
capability: request.capability,
|
|
104
|
+
argv: [...request.argv],
|
|
105
|
+
requestedPaths,
|
|
106
|
+
grantedPaths,
|
|
107
|
+
inputSnapshot: request.inputSnapshot,
|
|
108
|
+
fencingEpoch: request.fencingEpoch,
|
|
109
|
+
expiresAt: request.expiresAt,
|
|
110
|
+
...(request.capability === 'read-file' ? {
|
|
111
|
+
readPath: request.readPath,
|
|
112
|
+
maxReadBytes: request.maxReadBytes,
|
|
113
|
+
} : {}),
|
|
114
|
+
network: 'deny',
|
|
115
|
+
workspace: 'read-only',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export function scrubMissionExecutorEnv(source, allowedKeys) {
|
|
119
|
+
const result = {};
|
|
120
|
+
for (const key of [...new Set(allowedKeys)].sort()) {
|
|
121
|
+
if (!safeEnvironmentKeys.has(key)) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const value = source[key];
|
|
125
|
+
if (value !== undefined) {
|
|
126
|
+
result[key] = value;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
function assertAllowedArgv(capability, argv) {
|
|
132
|
+
const expected = {
|
|
133
|
+
'git-status': [[]],
|
|
134
|
+
'read-file': [[]],
|
|
135
|
+
'validate-patch': [[]],
|
|
136
|
+
};
|
|
137
|
+
const normalized = argv.map((value, index) => index === 0 ? basename(value) : value);
|
|
138
|
+
if (!expected[capability].some((candidate) => arraysEqual(candidate, normalized))) {
|
|
139
|
+
throw new Error(`Mission capability ${capability} requires allowlisted argv.`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function normalizeScope(paths, kind) {
|
|
143
|
+
const normalized = uniqueSortedPaths(paths);
|
|
144
|
+
if (normalized.length === 0) {
|
|
145
|
+
throw new Error(`Mission capability ${kind} scope must contain repository-relative globs.`);
|
|
146
|
+
}
|
|
147
|
+
normalized.forEach((path) => assertMissionPathPattern(path, `Mission capability ${kind} scope`));
|
|
148
|
+
return normalized;
|
|
149
|
+
}
|
|
150
|
+
function arraysEqual(left, right) {
|
|
151
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
152
|
+
}
|
|
153
|
+
function requireText(value, field) {
|
|
154
|
+
if (value.trim().length === 0) {
|
|
155
|
+
throw new Error(`Mission capability ${field} must be non-empty.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=mission-capability-kernel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-capability-kernel.js","sourceRoot":"","sources":["../../../src/runner/mission-capability-kernel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type MissionCapability } from './mission-capability-kernel.js';
|
|
2
|
+
export interface RunnerOwnedMissionCheck {
|
|
3
|
+
executable: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
capability: MissionCapability;
|
|
6
|
+
}
|
|
7
|
+
export type MissionCheckClassification = {
|
|
8
|
+
kind: 'legacy-shell';
|
|
9
|
+
reason: 'string-command-has-no-enforced-argv-contract';
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'safe-runner-argv';
|
|
12
|
+
executable: string;
|
|
13
|
+
args: string[];
|
|
14
|
+
capability: MissionCapability;
|
|
15
|
+
};
|
|
16
|
+
export declare function classifyMissionCheck(check: string | RunnerOwnedMissionCheck): MissionCheckClassification;
|
|
17
|
+
//# sourceMappingURL=mission-check-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-check-policy.d.ts","sourceRoot":"","sources":["../../../src/runner/mission-check-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAExC,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,8CAA8C,CAAA;CAAE,GAChF;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEN,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,GAAG,uBAAuB,GACtC,0BAA0B,CA4B5B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { authorizeMissionCapability, missionGitStatusArgv, } from './mission-capability-kernel.js';
|
|
2
|
+
export function classifyMissionCheck(check) {
|
|
3
|
+
if (typeof check === 'string') {
|
|
4
|
+
return {
|
|
5
|
+
kind: 'legacy-shell',
|
|
6
|
+
reason: 'string-command-has-no-enforced-argv-contract',
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
authorizeMissionCapability({
|
|
10
|
+
missionId: 'classification-probe',
|
|
11
|
+
actionKey: 'classification-probe',
|
|
12
|
+
capability: check.capability,
|
|
13
|
+
argv: [],
|
|
14
|
+
requestedPaths: ['**'],
|
|
15
|
+
grantedPaths: ['**'],
|
|
16
|
+
inputSnapshot: 'classification-probe',
|
|
17
|
+
fencingEpoch: 1,
|
|
18
|
+
expiresAt: '2099-01-01T00:00:00.000Z',
|
|
19
|
+
});
|
|
20
|
+
if (check.capability !== 'git-status'
|
|
21
|
+
|| [check.executable, ...check.args].join('\0') !== missionGitStatusArgv.join('\0')) {
|
|
22
|
+
throw new Error(`Mission capability ${check.capability} requires allowlisted argv.`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
kind: 'safe-runner-argv',
|
|
26
|
+
executable: check.executable,
|
|
27
|
+
args: [...check.args],
|
|
28
|
+
capability: check.capability,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=mission-check-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-check-policy.js","sourceRoot":"","sources":["../../../src/runner/mission-check-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,GAErB,MAAM,gCAAgC,CAAC;AAiBxC,MAAM,UAAU,oBAAoB,CAClC,KAAuC;IAEvC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,8CAA8C;SACvD,CAAC;IACJ,CAAC;IACD,0BAA0B,CAAC;QACzB,SAAS,EAAE,sBAAsB;QACjC,SAAS,EAAE,sBAAsB;QACjC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,IAAI,EAAE,EAAE;QACR,cAAc,EAAE,CAAC,IAAI,CAAC;QACtB,YAAY,EAAE,CAAC,IAAI,CAAC;QACpB,aAAa,EAAE,sBAAsB;QACrC,YAAY,EAAE,CAAC;QACf,SAAS,EAAE,0BAA0B;KACtC,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,UAAU,KAAK,YAAY;WAChC,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,CAAC,UAAU,6BAA6B,CAAC,CAAC;IACvF,CAAC;IACD,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;QACrB,UAAU,EAAE,KAAK,CAAC,UAAU;KAC7B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface MissionCoordinatorLockInput {
|
|
2
|
+
targetRoot: string;
|
|
3
|
+
stateDir: string;
|
|
4
|
+
hostId: string;
|
|
5
|
+
bootNonce: string;
|
|
6
|
+
pid?: number;
|
|
7
|
+
now?: Date;
|
|
8
|
+
isProcessAlive?: (pid: number) => boolean;
|
|
9
|
+
waitTimeoutMs?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface MissionCoordinatorLock {
|
|
12
|
+
metadataPath: string;
|
|
13
|
+
release(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function acquireMissionCoordinatorLock(input: MissionCoordinatorLockInput): Promise<MissionCoordinatorLock>;
|
|
16
|
+
//# sourceMappingURL=mission-coordinator-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-coordinator-lock.d.ts","sourceRoot":"","sources":["../../../src/runner/mission-coordinator-lock.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAWD,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,sBAAsB,CAAC,CAwFjC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
export async function acquireMissionCoordinatorLock(input) {
|
|
5
|
+
const lockDirectory = join(input.targetRoot, input.stateDir, 'mission-coordinator.lock');
|
|
6
|
+
const metadataPath = join(lockDirectory, 'owner.json');
|
|
7
|
+
const token = randomUUID();
|
|
8
|
+
const owner = {
|
|
9
|
+
version: 1,
|
|
10
|
+
token,
|
|
11
|
+
hostId: requireText(input.hostId, 'hostId'),
|
|
12
|
+
bootNonce: requireText(input.bootNonce, 'bootNonce'),
|
|
13
|
+
pid: input.pid ?? process.pid,
|
|
14
|
+
acquiredAt: (input.now ?? new Date()).toISOString(),
|
|
15
|
+
};
|
|
16
|
+
if (!Number.isSafeInteger(owner.pid) || owner.pid <= 0) {
|
|
17
|
+
throw new Error('Mission coordinator pid must be a positive integer.');
|
|
18
|
+
}
|
|
19
|
+
const alive = input.isProcessAlive ?? defaultProcessAlive;
|
|
20
|
+
let reclaimGuardPath;
|
|
21
|
+
const waitTimeoutMs = input.waitTimeoutMs ?? 0;
|
|
22
|
+
if (!Number.isSafeInteger(waitTimeoutMs) || waitTimeoutMs < 0) {
|
|
23
|
+
throw new Error('Mission coordinator waitTimeoutMs must be a non-negative integer.');
|
|
24
|
+
}
|
|
25
|
+
const deadline = Date.now() + waitTimeoutMs;
|
|
26
|
+
await mkdir(join(input.targetRoot, input.stateDir), { recursive: true });
|
|
27
|
+
while (true) {
|
|
28
|
+
const candidateDirectory = `${lockDirectory}.candidate.${token}.${randomUUID()}`;
|
|
29
|
+
try {
|
|
30
|
+
await mkdir(candidateDirectory);
|
|
31
|
+
try {
|
|
32
|
+
await writeFile(join(candidateDirectory, 'owner.json'), `${JSON.stringify(owner)}\n`, {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
flag: 'wx',
|
|
35
|
+
mode: 0o600,
|
|
36
|
+
});
|
|
37
|
+
await rename(candidateDirectory, lockDirectory);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
await rm(candidateDirectory, { recursive: true, force: true });
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
if (reclaimGuardPath) {
|
|
44
|
+
await rm(reclaimGuardPath, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
metadataPath,
|
|
48
|
+
release: () => releaseLock(lockDirectory, metadataPath, token),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (!isCode(error, 'EEXIST') && !isCode(error, 'ENOTEMPTY')) {
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
let existing;
|
|
56
|
+
try {
|
|
57
|
+
existing = await readLockOwner(metadataPath);
|
|
58
|
+
}
|
|
59
|
+
catch (readError) {
|
|
60
|
+
if (isCode(readError, 'ENOENT')) {
|
|
61
|
+
await delay(5);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
throw readError;
|
|
65
|
+
}
|
|
66
|
+
if (existing.hostId !== owner.hostId) {
|
|
67
|
+
throw new Error(`Mission coordinator lock belongs to a different host: ${existing.hostId}.`);
|
|
68
|
+
}
|
|
69
|
+
const ownerStillAlive = existing.pid === owner.pid
|
|
70
|
+
? existing.bootNonce === owner.bootNonce
|
|
71
|
+
: alive(existing.pid);
|
|
72
|
+
if (ownerStillAlive) {
|
|
73
|
+
if (Date.now() < deadline) {
|
|
74
|
+
await delay(10);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`Mission coordinator lock is already owned by pid ${existing.pid}.`);
|
|
78
|
+
}
|
|
79
|
+
const stalePath = `${lockDirectory}.stale.${existing.token}`;
|
|
80
|
+
try {
|
|
81
|
+
await rename(lockDirectory, stalePath);
|
|
82
|
+
}
|
|
83
|
+
catch (renameError) {
|
|
84
|
+
if (isCode(renameError, 'ENOENT')) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (isCode(renameError, 'EEXIST') || isCode(renameError, 'ENOTEMPTY')) {
|
|
88
|
+
throw new Error(`Mission coordinator stale reclaim guard already exists for token ${existing.token}.`);
|
|
89
|
+
}
|
|
90
|
+
throw renameError;
|
|
91
|
+
}
|
|
92
|
+
reclaimGuardPath = stalePath;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function releaseLock(lockDirectory, metadataPath, token) {
|
|
97
|
+
let current;
|
|
98
|
+
try {
|
|
99
|
+
current = await readLockOwner(metadataPath);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (isCode(error, 'ENOENT')) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
if (current.token !== token) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const releasePath = `${lockDirectory}.release.${token}`;
|
|
111
|
+
try {
|
|
112
|
+
await rename(lockDirectory, releasePath);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (isCode(error, 'ENOENT')) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
await rm(releasePath, { recursive: true, force: true });
|
|
121
|
+
}
|
|
122
|
+
async function readLockOwner(path) {
|
|
123
|
+
let parsed;
|
|
124
|
+
try {
|
|
125
|
+
parsed = JSON.parse(await readFile(path, 'utf8'));
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (isCode(error, 'ENOENT')) {
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
throw new Error('Mission coordinator lock metadata is invalid and cannot be reclaimed safely.');
|
|
132
|
+
}
|
|
133
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
134
|
+
throw new Error('Mission coordinator lock metadata is invalid and cannot be reclaimed safely.');
|
|
135
|
+
}
|
|
136
|
+
const record = parsed;
|
|
137
|
+
const exact = ['version', 'token', 'hostId', 'bootNonce', 'pid', 'acquiredAt'];
|
|
138
|
+
if (Object.keys(record).length !== exact.length || exact.some((key) => !(key in record))
|
|
139
|
+
|| record.version !== 1
|
|
140
|
+
|| typeof record.token !== 'string' || record.token.length === 0
|
|
141
|
+
|| typeof record.hostId !== 'string' || record.hostId.length === 0
|
|
142
|
+
|| typeof record.bootNonce !== 'string' || record.bootNonce.length === 0
|
|
143
|
+
|| !Number.isSafeInteger(record.pid) || record.pid <= 0
|
|
144
|
+
|| typeof record.acquiredAt !== 'string' || !Number.isFinite(Date.parse(record.acquiredAt))) {
|
|
145
|
+
throw new Error('Mission coordinator lock metadata is invalid and cannot be reclaimed safely.');
|
|
146
|
+
}
|
|
147
|
+
return record;
|
|
148
|
+
}
|
|
149
|
+
function defaultProcessAlive(pid) {
|
|
150
|
+
try {
|
|
151
|
+
process.kill(pid, 0);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return error instanceof Error && 'code' in error && error.code === 'EPERM';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function requireText(value, field) {
|
|
159
|
+
if (value.trim().length === 0) {
|
|
160
|
+
throw new Error(`Mission coordinator ${field} must be non-empty.`);
|
|
161
|
+
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
function isCode(error, code) {
|
|
165
|
+
return error instanceof Error && 'code' in error && error.code === code;
|
|
166
|
+
}
|
|
167
|
+
function delay(ms) {
|
|
168
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=mission-coordinator-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-coordinator-lock.js","sourceRoot":"","sources":["../../../src/runner/mission-coordinator-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface MissionDeploymentRecord {
|
|
2
|
+
version: 1;
|
|
3
|
+
repository: string;
|
|
4
|
+
deploymentId: string;
|
|
5
|
+
hostId: string;
|
|
6
|
+
serviceId: string;
|
|
7
|
+
githubAppInstallationId: string;
|
|
8
|
+
credentialGeneration: string;
|
|
9
|
+
compatibilityEpoch: number;
|
|
10
|
+
priorCredentialRevokedAt: string;
|
|
11
|
+
priorTokenExpiresAt: string;
|
|
12
|
+
takeoverGraceUntil: string;
|
|
13
|
+
takeoverNotBefore: string;
|
|
14
|
+
approvedByCommit: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function canonicalMissionDeploymentRecord(record: MissionDeploymentRecord): string;
|
|
17
|
+
export declare function parseMissionDeploymentRecord(content: string): MissionDeploymentRecord;
|
|
18
|
+
export declare function missionDeploymentRecordHash(record: MissionDeploymentRecord): string;
|
|
19
|
+
export declare function takeoverNotBefore(record: Pick<MissionDeploymentRecord, 'priorTokenExpiresAt' | 'takeoverGraceUntil'>): string;
|
|
20
|
+
export declare function assertMissionDeploymentRecord(value: unknown): asserts value is MissionDeploymentRecord;
|
|
21
|
+
//# sourceMappingURL=mission-deployment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-deployment.d.ts","sourceRoot":"","sources":["../../../src/runner/mission-deployment.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB,EAAE,MAAM,CAAC;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,wBAAwB,EAAE,MAAM,CAAC;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAkBD,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,uBAAuB,GAAG,MAAM,CAGxF;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB,CAarF;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,uBAAuB,GAAG,MAAM,CAInF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAC9C,uBAAuB,EACvB,qBAAqB,GAAG,oBAAoB,CAC3C,GAAG,MAAM,CAIT;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,uBAAuB,CA8CtG"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
const deploymentFields = [
|
|
3
|
+
'version',
|
|
4
|
+
'repository',
|
|
5
|
+
'deploymentId',
|
|
6
|
+
'hostId',
|
|
7
|
+
'serviceId',
|
|
8
|
+
'githubAppInstallationId',
|
|
9
|
+
'credentialGeneration',
|
|
10
|
+
'compatibilityEpoch',
|
|
11
|
+
'priorCredentialRevokedAt',
|
|
12
|
+
'priorTokenExpiresAt',
|
|
13
|
+
'takeoverGraceUntil',
|
|
14
|
+
'takeoverNotBefore',
|
|
15
|
+
'approvedByCommit',
|
|
16
|
+
];
|
|
17
|
+
export function canonicalMissionDeploymentRecord(record) {
|
|
18
|
+
assertMissionDeploymentRecord(record);
|
|
19
|
+
return JSON.stringify(Object.fromEntries(deploymentFields.map((field) => [field, record[field]])));
|
|
20
|
+
}
|
|
21
|
+
export function parseMissionDeploymentRecord(content) {
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(content);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error('Invalid Mission deployment record: expected JSON.');
|
|
28
|
+
}
|
|
29
|
+
assertMissionDeploymentRecord(parsed);
|
|
30
|
+
const canonical = canonicalMissionDeploymentRecord(parsed);
|
|
31
|
+
if (content !== canonical) {
|
|
32
|
+
throw new Error('Invalid Mission deployment record: file must use canonical JSON.');
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
export function missionDeploymentRecordHash(record) {
|
|
37
|
+
return `sha256:${createHash('sha256')
|
|
38
|
+
.update(Buffer.from(canonicalMissionDeploymentRecord(record), 'utf8'))
|
|
39
|
+
.digest('hex')}`;
|
|
40
|
+
}
|
|
41
|
+
export function takeoverNotBefore(record) {
|
|
42
|
+
const tokenExpiry = parseExactTimestamp(record.priorTokenExpiresAt, 'priorTokenExpiresAt');
|
|
43
|
+
const grace = parseExactTimestamp(record.takeoverGraceUntil, 'takeoverGraceUntil');
|
|
44
|
+
return new Date(Math.max(tokenExpiry, grace)).toISOString();
|
|
45
|
+
}
|
|
46
|
+
export function assertMissionDeploymentRecord(value) {
|
|
47
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
48
|
+
throw new Error('Invalid Mission deployment record: root must be an object.');
|
|
49
|
+
}
|
|
50
|
+
const record = value;
|
|
51
|
+
const allowed = new Set(deploymentFields);
|
|
52
|
+
for (const key of Object.keys(record)) {
|
|
53
|
+
if (!allowed.has(key)) {
|
|
54
|
+
throw new Error(`Invalid Mission deployment record: unexpected field ${key}.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const field of deploymentFields) {
|
|
58
|
+
if (!(field in record)) {
|
|
59
|
+
throw new Error(`Invalid Mission deployment record: missing field ${field}.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (record.version !== 1) {
|
|
63
|
+
throw new Error('Invalid Mission deployment record: version must be 1.');
|
|
64
|
+
}
|
|
65
|
+
for (const field of [
|
|
66
|
+
'repository',
|
|
67
|
+
'deploymentId',
|
|
68
|
+
'hostId',
|
|
69
|
+
'serviceId',
|
|
70
|
+
'githubAppInstallationId',
|
|
71
|
+
'credentialGeneration',
|
|
72
|
+
]) {
|
|
73
|
+
requireNonEmpty(record[field], field);
|
|
74
|
+
}
|
|
75
|
+
if (!Number.isSafeInteger(record.compatibilityEpoch) || record.compatibilityEpoch <= 0) {
|
|
76
|
+
throw new Error('Invalid Mission deployment record: compatibilityEpoch must be a positive integer.');
|
|
77
|
+
}
|
|
78
|
+
for (const field of [
|
|
79
|
+
'priorCredentialRevokedAt',
|
|
80
|
+
'priorTokenExpiresAt',
|
|
81
|
+
'takeoverGraceUntil',
|
|
82
|
+
'takeoverNotBefore',
|
|
83
|
+
]) {
|
|
84
|
+
parseExactTimestamp(record[field], field);
|
|
85
|
+
}
|
|
86
|
+
if (record.takeoverNotBefore !== takeoverNotBefore(record)) {
|
|
87
|
+
throw new Error('Invalid Mission deployment record: takeoverNotBefore must equal the later token expiry or grace boundary.');
|
|
88
|
+
}
|
|
89
|
+
if (typeof record.approvedByCommit !== 'string' || !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u.test(record.approvedByCommit)) {
|
|
90
|
+
throw new Error('Invalid Mission deployment record: approvedByCommit must be a Git object ID.');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function parseExactTimestamp(value, field) {
|
|
94
|
+
if (typeof value !== 'string') {
|
|
95
|
+
throw new Error(`Invalid Mission deployment record: ${field} must be an ISO timestamp.`);
|
|
96
|
+
}
|
|
97
|
+
const parsed = Date.parse(value);
|
|
98
|
+
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
99
|
+
throw new Error(`Invalid Mission deployment record: ${field} must be an exact UTC ISO timestamp.`);
|
|
100
|
+
}
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
function requireNonEmpty(value, field) {
|
|
104
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
105
|
+
throw new Error(`Invalid Mission deployment record: ${field} must be non-empty.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=mission-deployment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission-deployment.js","sourceRoot":"","sources":["../../../src/runner/mission-deployment.ts"],"names":[],"mappings":"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"}
|