owlrunkit 0.15.0 → 0.16.0
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 +100 -0
- package/README.md +274 -66
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +223 -117
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/ready-for-commit-request.json +2 -2
- package/package.json +2 -2
- package/packages/attest/distribution-manifest-v1.json +10 -0
- package/packages/attest/src/quick.mjs +10 -0
- package/registry-release-policy-v1.json +8 -2
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/assurance-router.mjs +119 -0
- package/scripts/runkit-contract/core-contract.mjs +250 -115
- package/scripts/runkit-contract/core-successor.mjs +2043 -0
- package/scripts/runkit-contract/deployment-workflow.mjs +2040 -0
- package/scripts/runkit-contract/finalize.mjs +242 -21
- package/scripts/runkit-contract/fleet-discovery.mjs +456 -0
- package/scripts/runkit-contract/formal-workflow.mjs +850 -0
- package/scripts/runkit-contract/human-status.mjs +553 -0
- package/scripts/runkit-contract/inspect-presentation.mjs +25 -8
- package/scripts/runkit-contract/install-codex-skill.mjs +73 -30
- package/scripts/runkit-contract/lease-lifecycle.mjs +8 -2
- package/scripts/runkit-contract/lifecycle-orchestration.mjs +135 -12
- package/scripts/runkit-contract/onboarding-doctor.mjs +43 -90
- package/scripts/runkit-contract/owner-authority-trust.mjs +230 -0
- package/scripts/runkit-contract/owner-deployment-decision.mjs +385 -0
- package/scripts/runkit-contract/profile-onboarding.mjs +841 -13
- package/scripts/runkit-contract/project-cli-resolver.mjs +701 -0
- package/scripts/runkit-contract/project-control-parser.mjs +830 -0
- package/scripts/runkit-contract/project-control-state.mjs +28 -0
- package/scripts/runkit-contract/provenance-common.mjs +19 -2
- package/scripts/runkit-contract/ready-for-commit.mjs +330 -18
- package/scripts/runkit-contract/remote-deployment.mjs +1596 -0
- package/scripts/runkit-contract/remote-process-adapter.mjs +177 -0
- package/scripts/runkit-contract/runkit-bootstrap.mjs +131 -0
- package/scripts/runkit-contract/runkit-cli.mjs +1097 -21
- package/scripts/runkit-contract/schemas/assurance-route-v1.schema.json +26 -0
- package/scripts/runkit-contract/schemas/core-successor-batch-receipt-v1.schema.json +126 -0
- package/scripts/runkit-contract/schemas/core-successor-plan-v1.schema.json +155 -0
- package/scripts/runkit-contract/schemas/core-successor-project-success-receipt-v1.schema.json +72 -0
- package/scripts/runkit-contract/schemas/deployment-execute-request-v1.schema.json +63 -0
- package/scripts/runkit-contract/schemas/deployment-lineage-v1.schema.json +292 -0
- package/scripts/runkit-contract/schemas/deployment-prepare-receipt-v1.schema.json +140 -0
- package/scripts/runkit-contract/schemas/fleet-discovery-v1.schema.json +31 -0
- package/scripts/runkit-contract/schemas/fleet-manifest-v1.schema.json +20 -0
- package/scripts/runkit-contract/schemas/fleet-root-registry-v1.schema.json +34 -0
- package/scripts/runkit-contract/schemas/human-status-v1.schema.json +102 -0
- package/scripts/runkit-contract/schemas/owner-authority-trust-v1.schema.json +66 -0
- package/scripts/runkit-contract/schemas/owner-deployment-authority-v1.schema.json +46 -0
- package/scripts/runkit-contract/schemas/owner-deployment-authority-v2.schema.json +94 -0
- package/scripts/runkit-contract/schemas/owner-deployment-decision-v1.schema.json +102 -0
- package/scripts/runkit-contract/schemas/owner-migration-authority-v1.schema.json +33 -0
- package/scripts/runkit-contract/schemas/owner-migration-authority-v2.schema.json +67 -0
- package/scripts/runkit-contract/schemas/profile-apply-receipt-v1.schema.json +32 -0
- package/scripts/runkit-contract/schemas/profile-detection-v2.schema.json +145 -0
- package/scripts/runkit-contract/schemas/ready-for-commit-v2.schema.json +37 -0
- package/scripts/runkit-contract/schemas/remote-deployment-manifest-v1.schema.json +379 -0
- package/scripts/runkit-contract/schemas/remote-deployment-result-v1.schema.json +153 -0
- package/scripts/runkit-contract/schemas/remote-deployment-stage-journal-v1.schema.json +183 -0
- package/scripts/runkit-contract/schemas/remote-target-v1.schema.json +32 -0
- package/scripts/runkit-contract/schemas/source-candidate-v2.schema.json +152 -0
- package/scripts/runkit-contract/schemas/verification-receipt-v2.schema.json +13 -0
- package/scripts/runkit-contract/snapshot.mjs +1 -1
- package/scripts/runkit-contract/source-candidate.mjs +1382 -0
- package/scripts/runkit-contract/ssh-remote-adapter.mjs +1184 -0
- package/scripts/runkit-contract/sync-core016-release-identity.mjs +161 -0
- package/scripts/runkit-contract/templates/assurance-request.json +13 -0
- package/scripts/runkit-contract/templates/core-successor-owner-authority.json +15 -0
- package/scripts/runkit-contract/templates/deployment-execute-request.json +39 -0
- package/scripts/runkit-contract/templates/deployment-owner-authority.json +23 -0
- package/scripts/runkit-contract/templates/deployment-owner-decision.json +26 -0
- package/scripts/runkit-contract/templates/fleet-manifest.json +8 -0
- package/scripts/runkit-contract/templates/fleet-root-registry.json +8 -0
- package/scripts/runkit-contract/templates/owner-authority-trust.json +16 -0
- package/scripts/runkit-contract/templates/ready-for-commit-request.json +2 -2
- package/scripts/runkit-contract/templates/remote-deployment-manifest.json +108 -0
- package/scripts/runkit-contract/verification-receipt-gate.mjs +28 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
lstatSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
realpathSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
|
|
10
|
+
import { REMOTE_DEPLOYMENT_STAGES } from "./remote-deployment.mjs";
|
|
11
|
+
|
|
12
|
+
const SHA256 = /^[a-f0-9]{64}$/u;
|
|
13
|
+
const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
14
|
+
const SAFE_ENV_KEYS = [
|
|
15
|
+
"HOME",
|
|
16
|
+
"LANG",
|
|
17
|
+
"LC_ALL",
|
|
18
|
+
"PATH",
|
|
19
|
+
"SSH_AUTH_SOCK",
|
|
20
|
+
"SystemRoot",
|
|
21
|
+
"TMPDIR",
|
|
22
|
+
"USER",
|
|
23
|
+
"WINDIR",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function sha256(value) {
|
|
27
|
+
return createHash("sha256").update(value).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function constrainedEnvironment() {
|
|
31
|
+
return Object.fromEntries(
|
|
32
|
+
SAFE_ENV_KEYS
|
|
33
|
+
.filter((key) => typeof process.env[key] === "string")
|
|
34
|
+
.map((key) => [key, process.env[key]]),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function validateIdentity(identity) {
|
|
39
|
+
if (
|
|
40
|
+
identity === null
|
|
41
|
+
|| typeof identity !== "object"
|
|
42
|
+
|| Array.isArray(identity)
|
|
43
|
+
|| Object.keys(identity).some((key) => ![
|
|
44
|
+
"adapterId",
|
|
45
|
+
"executable",
|
|
46
|
+
"sha256",
|
|
47
|
+
"version",
|
|
48
|
+
].includes(key))
|
|
49
|
+
|| !IDENTIFIER.test(identity.adapterId ?? "")
|
|
50
|
+
|| typeof identity.version !== "string"
|
|
51
|
+
|| identity.version.length === 0
|
|
52
|
+
|| typeof identity.executable !== "string"
|
|
53
|
+
|| !path.isAbsolute(identity.executable)
|
|
54
|
+
|| !SHA256.test(identity.sha256 ?? "")
|
|
55
|
+
) {
|
|
56
|
+
throw new Error("Remote process adapter identity is invalid.");
|
|
57
|
+
}
|
|
58
|
+
const stat = lstatSync(identity.executable);
|
|
59
|
+
const executable = realpathSync(identity.executable);
|
|
60
|
+
const resolvedStat = lstatSync(executable);
|
|
61
|
+
if (
|
|
62
|
+
stat.isSymbolicLink()
|
|
63
|
+
|| !stat.isFile()
|
|
64
|
+
|| resolvedStat.isSymbolicLink()
|
|
65
|
+
|| !resolvedStat.isFile()
|
|
66
|
+
) {
|
|
67
|
+
throw new Error("Remote process adapter executable must be a regular file.");
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
...structuredClone(identity),
|
|
71
|
+
executable,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function validateWorkspaceRoot(workspaceRoot) {
|
|
76
|
+
if (typeof workspaceRoot !== "string" || workspaceRoot.length === 0) {
|
|
77
|
+
throw new Error("Remote process adapter workspaceRoot is required.");
|
|
78
|
+
}
|
|
79
|
+
const requested = path.resolve(workspaceRoot);
|
|
80
|
+
const stat = lstatSync(requested);
|
|
81
|
+
if (
|
|
82
|
+
stat.isSymbolicLink()
|
|
83
|
+
|| !stat.isDirectory()
|
|
84
|
+
) {
|
|
85
|
+
throw new Error("Remote process adapter workspaceRoot must be a real directory.");
|
|
86
|
+
}
|
|
87
|
+
return realpathSync(requested);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function failure(failureCode, evidence) {
|
|
91
|
+
return {
|
|
92
|
+
status: "failed",
|
|
93
|
+
failureCode,
|
|
94
|
+
evidenceSha256: sha256(evidence),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function indeterminate(failureCode, evidence) {
|
|
99
|
+
return {
|
|
100
|
+
...failure(failureCode, evidence),
|
|
101
|
+
status: "indeterminate",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function createRemoteProcessAdapterV1({
|
|
106
|
+
identity,
|
|
107
|
+
workspaceRoot,
|
|
108
|
+
timeoutMs = 600_000,
|
|
109
|
+
maxOutputBytes = 1_048_576,
|
|
110
|
+
spawn = spawnSync,
|
|
111
|
+
} = {}) {
|
|
112
|
+
const normalizedIdentity = validateIdentity(identity);
|
|
113
|
+
const normalizedWorkspaceRoot = validateWorkspaceRoot(workspaceRoot);
|
|
114
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 3_600_000) {
|
|
115
|
+
throw new Error("Remote process adapter timeoutMs is invalid.");
|
|
116
|
+
}
|
|
117
|
+
if (
|
|
118
|
+
!Number.isSafeInteger(maxOutputBytes)
|
|
119
|
+
|| maxOutputBytes < 1_024
|
|
120
|
+
|| maxOutputBytes > 16_777_216
|
|
121
|
+
) {
|
|
122
|
+
throw new Error("Remote process adapter maxOutputBytes is invalid.");
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
identity: normalizedIdentity,
|
|
126
|
+
async runStage(input) {
|
|
127
|
+
if (!REMOTE_DEPLOYMENT_STAGES.includes(input?.stage)) {
|
|
128
|
+
return failure("adapter_stage_invalid", JSON.stringify(input ?? null));
|
|
129
|
+
}
|
|
130
|
+
const executableBytes = readFileSync(normalizedIdentity.executable);
|
|
131
|
+
if (sha256(executableBytes) !== normalizedIdentity.sha256) {
|
|
132
|
+
throw new Error("Remote process adapter executable hash mismatch.");
|
|
133
|
+
}
|
|
134
|
+
const completed = spawn(
|
|
135
|
+
normalizedIdentity.executable,
|
|
136
|
+
["--owlrunkit-remote-stage", input.stage],
|
|
137
|
+
{
|
|
138
|
+
encoding: "utf8",
|
|
139
|
+
cwd: normalizedWorkspaceRoot,
|
|
140
|
+
env: constrainedEnvironment(),
|
|
141
|
+
input: `${JSON.stringify(input)}\n`,
|
|
142
|
+
maxBuffer: maxOutputBytes,
|
|
143
|
+
timeout: timeoutMs,
|
|
144
|
+
windowsHide: true,
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
if (completed.error) {
|
|
148
|
+
return indeterminate(
|
|
149
|
+
completed.error.code === "ETIMEDOUT"
|
|
150
|
+
? "adapter_process_timeout"
|
|
151
|
+
: "adapter_process_failed",
|
|
152
|
+
String(completed.error),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (completed.status !== 0) {
|
|
156
|
+
return indeterminate(
|
|
157
|
+
`adapter_process_exit_${Number.isInteger(completed.status)
|
|
158
|
+
? completed.status
|
|
159
|
+
: "unknown"}`,
|
|
160
|
+
`${completed.stdout ?? ""}\n${completed.stderr ?? ""}`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const output = JSON.parse(completed.stdout);
|
|
165
|
+
if (output === null || typeof output !== "object" || Array.isArray(output)) {
|
|
166
|
+
throw new Error("adapter output is not an object");
|
|
167
|
+
}
|
|
168
|
+
return output;
|
|
169
|
+
} catch {
|
|
170
|
+
return indeterminate(
|
|
171
|
+
"adapter_output_invalid",
|
|
172
|
+
`${completed.stdout ?? ""}\n${completed.stderr ?? ""}`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import {
|
|
5
|
+
existsSync,
|
|
6
|
+
realpathSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
import { resolveProjectCli } from "./project-cli-resolver.mjs";
|
|
12
|
+
|
|
13
|
+
const CURRENT_PACKAGE_COMMANDS = new Set([
|
|
14
|
+
"adopt",
|
|
15
|
+
"core-successor",
|
|
16
|
+
"fleet",
|
|
17
|
+
"init",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function comparablePath(value) {
|
|
21
|
+
const resolved = path.resolve(value);
|
|
22
|
+
return existsSync(resolved) ? realpathSync(resolved) : resolved;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function workspaceFromArgv(argv, cwd) {
|
|
26
|
+
const index = argv.indexOf("--workspace");
|
|
27
|
+
if (index < 0) return path.resolve(cwd);
|
|
28
|
+
const value = argv[index + 1];
|
|
29
|
+
if (typeof value !== "string" || value.length === 0 || value.startsWith("--")) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return path.resolve(cwd, value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function selectProjectCliInvocationV1({
|
|
36
|
+
argv,
|
|
37
|
+
cwd = process.cwd(),
|
|
38
|
+
selfCliPath = fileURLToPath(import.meta.url),
|
|
39
|
+
} = {}) {
|
|
40
|
+
if (!Array.isArray(argv)) throw new Error("argv is required.");
|
|
41
|
+
const workspaceRoot = workspaceFromArgv(argv, cwd);
|
|
42
|
+
const separatorIndex = argv.indexOf("--");
|
|
43
|
+
const runkitArgv = separatorIndex < 0
|
|
44
|
+
? argv
|
|
45
|
+
: argv.slice(0, separatorIndex);
|
|
46
|
+
if (
|
|
47
|
+
workspaceRoot === null
|
|
48
|
+
|| argv.length === 0
|
|
49
|
+
|| runkitArgv.includes("--help")
|
|
50
|
+
|| runkitArgv.includes("-h")
|
|
51
|
+
|| argv[0] === "--version"
|
|
52
|
+
|| argv[0] === "-v"
|
|
53
|
+
|| CURRENT_PACKAGE_COMMANDS.has(argv[0])
|
|
54
|
+
) {
|
|
55
|
+
return {
|
|
56
|
+
mode: "current_package",
|
|
57
|
+
workspaceRoot,
|
|
58
|
+
commandArgv: null,
|
|
59
|
+
binding: null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const binding = resolveProjectCli({ workspaceRoot });
|
|
63
|
+
if (
|
|
64
|
+
binding.status === "bound"
|
|
65
|
+
&& comparablePath(binding.cliPath) !== comparablePath(selfCliPath)
|
|
66
|
+
) {
|
|
67
|
+
return {
|
|
68
|
+
mode: "project_local",
|
|
69
|
+
workspaceRoot,
|
|
70
|
+
commandArgv: [...binding.argvPrefix, ...argv],
|
|
71
|
+
binding,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (binding.status !== "bound") {
|
|
75
|
+
return {
|
|
76
|
+
mode: "project_local_blocked",
|
|
77
|
+
workspaceRoot,
|
|
78
|
+
commandArgv: null,
|
|
79
|
+
binding,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
mode: "current_package",
|
|
84
|
+
workspaceRoot,
|
|
85
|
+
commandArgv: null,
|
|
86
|
+
binding,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function runBootstrapV1({
|
|
91
|
+
argv = process.argv.slice(2),
|
|
92
|
+
cwd = process.cwd(),
|
|
93
|
+
selfCliPath = fileURLToPath(import.meta.url),
|
|
94
|
+
spawn = spawnSync,
|
|
95
|
+
} = {}) {
|
|
96
|
+
const selected = selectProjectCliInvocationV1({
|
|
97
|
+
argv,
|
|
98
|
+
cwd,
|
|
99
|
+
selfCliPath,
|
|
100
|
+
});
|
|
101
|
+
if (selected.mode === "project_local_blocked") {
|
|
102
|
+
throw new Error([
|
|
103
|
+
`project_local_cli_unavailable:${selected.binding.issueCodes.join(",")}.`,
|
|
104
|
+
"The current/global package did not execute this project command.",
|
|
105
|
+
"Repair the exact dependency, lockfile, and local install binding,",
|
|
106
|
+
`or use the bootstrap recovery path: owlrunkit init --workspace ${selected.workspaceRoot}`,
|
|
107
|
+
].join(" "));
|
|
108
|
+
}
|
|
109
|
+
const commandArgv = selected.commandArgv ?? [
|
|
110
|
+
process.execPath,
|
|
111
|
+
path.join(path.dirname(selfCliPath), "runkit-cli.mjs"),
|
|
112
|
+
...argv,
|
|
113
|
+
];
|
|
114
|
+
const completed = spawn(commandArgv[0], commandArgv.slice(1), {
|
|
115
|
+
cwd,
|
|
116
|
+
env: process.env,
|
|
117
|
+
stdio: "inherit",
|
|
118
|
+
});
|
|
119
|
+
if (completed.error) throw completed.error;
|
|
120
|
+
return Number.isInteger(completed.status) ? completed.status : 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const direct = comparablePath(process.argv[1] ?? "") === comparablePath(fileURLToPath(import.meta.url));
|
|
124
|
+
if (direct) {
|
|
125
|
+
try {
|
|
126
|
+
process.exitCode = runBootstrapV1();
|
|
127
|
+
} catch (error) {
|
|
128
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
129
|
+
process.exitCode = 1;
|
|
130
|
+
}
|
|
131
|
+
}
|