owlrunkit 0.13.0 → 0.15.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 +41 -0
- package/README.md +66 -4
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +79 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +163 -2
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +1 -1
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +10 -0
- package/packages/attest/src/quick.mjs +10 -0
- package/registry-release-policy-v1.json +1 -1
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +145 -4
- package/scripts/runkit-contract/install-codex-skill.mjs +505 -11
- package/scripts/runkit-contract/lifecycle-orchestration.mjs +119 -4
- package/scripts/runkit-contract/onboarding-doctor.mjs +805 -0
- package/scripts/runkit-contract/profile-onboarding.mjs +510 -0
- package/scripts/runkit-contract/registry-adoption-gate.mjs +121 -7
- package/scripts/runkit-contract/registry-adoption.mjs +198 -0
- package/scripts/runkit-contract/resume-execution.mjs +8 -2
- package/scripts/runkit-contract/runkit-cli.mjs +216 -6
- package/scripts/runkit-contract/schemas/adoption-evidence-v1.schema.json +63 -0
- package/scripts/runkit-contract/schemas/verification-finalize-attempt-v1.schema.json +52 -0
- package/scripts/runkit-contract/schemas/verification-preflight-v1.schema.json +52 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
} from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
OFFICIAL_NPM_REGISTRY,
|
|
11
|
+
evaluateRegistryAdoption,
|
|
12
|
+
} from "./registry-adoption-gate.mjs";
|
|
13
|
+
import { CORE_VERSION } from "./core-contract.mjs";
|
|
14
|
+
import {
|
|
15
|
+
readLocalInstallBinding,
|
|
16
|
+
readRegistryExact,
|
|
17
|
+
resolveBoundedWorkspaceRoot,
|
|
18
|
+
} from "./onboarding-doctor.mjs";
|
|
19
|
+
import { writeJsonExclusiveAtomically } from "./provenance-common.mjs";
|
|
20
|
+
|
|
21
|
+
const PACKAGE_NAME = "owlrunkit";
|
|
22
|
+
const CANDIDATE_VERSION = CORE_VERSION;
|
|
23
|
+
const EXACT_SPEC = `${PACKAGE_NAME}@${CANDIDATE_VERSION}`;
|
|
24
|
+
|
|
25
|
+
const POLICY = Object.freeze({
|
|
26
|
+
schemaVersion: "OwlCodaRunKitRegistryReleasePolicyV1",
|
|
27
|
+
status: "registry_verified",
|
|
28
|
+
packageName: PACKAGE_NAME,
|
|
29
|
+
candidateVersion: CANDIDATE_VERSION,
|
|
30
|
+
registry: OFFICIAL_NPM_REGISTRY,
|
|
31
|
+
registryRequiredBeforeAdoption: true,
|
|
32
|
+
exactVersionRequired: true,
|
|
33
|
+
requiredRegistryBindings: [
|
|
34
|
+
"packageName",
|
|
35
|
+
"version",
|
|
36
|
+
"shasum",
|
|
37
|
+
"integrity",
|
|
38
|
+
"tarballUrl",
|
|
39
|
+
],
|
|
40
|
+
forbiddenAdoptionSources: [
|
|
41
|
+
"directory",
|
|
42
|
+
"file",
|
|
43
|
+
"git",
|
|
44
|
+
"local_tarball",
|
|
45
|
+
"symlink",
|
|
46
|
+
"workspace",
|
|
47
|
+
],
|
|
48
|
+
currentPublishedRelease: null,
|
|
49
|
+
registryRelease: null,
|
|
50
|
+
registryEvidenceMustBeExternal: true,
|
|
51
|
+
registryEvidenceSchemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
|
|
52
|
+
embeddedAttestComponentVersion: "0.2.0",
|
|
53
|
+
standaloneAttestPublicationRequired: false,
|
|
54
|
+
signatureRequired: false,
|
|
55
|
+
githubActionRequired: false,
|
|
56
|
+
foreignProjectWriteAuthorized: false,
|
|
57
|
+
authorizationGranted: false,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function ineligible(issueCodes) {
|
|
61
|
+
return {
|
|
62
|
+
schemaVersion: "OwlCodaRunKitRegistryAdoptionResultV1",
|
|
63
|
+
status: "ineligible",
|
|
64
|
+
decision: "INELIGIBLE",
|
|
65
|
+
issueCodes,
|
|
66
|
+
packageName: PACKAGE_NAME,
|
|
67
|
+
version: CANDIDATE_VERSION,
|
|
68
|
+
authorizationGranted: false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function assertRealDirectory(directory, label) {
|
|
73
|
+
const stat = lstatSync(directory);
|
|
74
|
+
if (stat.isSymbolicLink() || !stat.isDirectory() || realpathSync(directory) !== path.resolve(directory)) {
|
|
75
|
+
throw new Error(`${label} must be a real directory without symlink ancestors.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function adoptionRoot(workspaceRoot) {
|
|
80
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
81
|
+
const owlcodaRoot = path.join(root, ".owlcoda");
|
|
82
|
+
const runtimeRoot = path.join(owlcodaRoot, "runkit");
|
|
83
|
+
assertRealDirectory(owlcodaRoot, ".owlcoda");
|
|
84
|
+
assertRealDirectory(runtimeRoot, "RunKit runtime");
|
|
85
|
+
const target = path.join(runtimeRoot, "adoption");
|
|
86
|
+
if (!existsSync(target)) mkdirSync(target);
|
|
87
|
+
assertRealDirectory(target, "RunKit adoption directory");
|
|
88
|
+
return target;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function runRegistryAdoption({
|
|
92
|
+
workspaceRoot,
|
|
93
|
+
exactSpec,
|
|
94
|
+
registryClient,
|
|
95
|
+
timeoutMs = 1_500,
|
|
96
|
+
} = {}) {
|
|
97
|
+
if (exactSpec !== EXACT_SPEC) return ineligible(["exact_registry_spec_required"]);
|
|
98
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
99
|
+
const local = readLocalInstallBinding({ workspaceRoot: root });
|
|
100
|
+
if (local.status !== "bound") {
|
|
101
|
+
return ineligible(local.issueCodes ?? ["registry_install_binding_mismatch"]);
|
|
102
|
+
}
|
|
103
|
+
const observedRelease = registryClient
|
|
104
|
+
? await registryClient.readExact({
|
|
105
|
+
registry: OFFICIAL_NPM_REGISTRY,
|
|
106
|
+
packageName: PACKAGE_NAME,
|
|
107
|
+
version: CANDIDATE_VERSION,
|
|
108
|
+
})
|
|
109
|
+
: await readRegistryExact({
|
|
110
|
+
registryUrl: OFFICIAL_NPM_REGISTRY,
|
|
111
|
+
packageName: PACKAGE_NAME,
|
|
112
|
+
version: CANDIDATE_VERSION,
|
|
113
|
+
timeoutMs,
|
|
114
|
+
});
|
|
115
|
+
const release = observedRelease.status === "registry_verified"
|
|
116
|
+
? {
|
|
117
|
+
schemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
|
|
118
|
+
...observedRelease,
|
|
119
|
+
}
|
|
120
|
+
: observedRelease;
|
|
121
|
+
if (release.status !== "registry_verified") {
|
|
122
|
+
return ineligible(release.issueCodes ?? ["registry_release_not_verified"]);
|
|
123
|
+
}
|
|
124
|
+
const install = {
|
|
125
|
+
kind: "registry",
|
|
126
|
+
requestedSpec: EXACT_SPEC,
|
|
127
|
+
packageName: local.packageName,
|
|
128
|
+
version: local.version,
|
|
129
|
+
shasum: release.shasum,
|
|
130
|
+
integrity: local.integrity,
|
|
131
|
+
resolved: local.resolved,
|
|
132
|
+
};
|
|
133
|
+
const decision = evaluateRegistryAdoption({
|
|
134
|
+
policy: POLICY,
|
|
135
|
+
releaseEvidence: release,
|
|
136
|
+
install,
|
|
137
|
+
});
|
|
138
|
+
if (decision.decision !== "ELIGIBLE") return ineligible(decision.issueCodes);
|
|
139
|
+
|
|
140
|
+
const relativeEvidencePath = `.owlcoda/runkit/adoption/${PACKAGE_NAME}-${CANDIDATE_VERSION}.json`;
|
|
141
|
+
const evidencePath = path.join(adoptionRoot(root), `${PACKAGE_NAME}-${CANDIDATE_VERSION}.json`);
|
|
142
|
+
const evidence = {
|
|
143
|
+
schemaVersion: "OwlCodaRunKitRegistryAdoptionEvidenceV1",
|
|
144
|
+
status: "adopted",
|
|
145
|
+
decision: "ELIGIBLE",
|
|
146
|
+
packageName: PACKAGE_NAME,
|
|
147
|
+
version: CANDIDATE_VERSION,
|
|
148
|
+
exactSpec: EXACT_SPEC,
|
|
149
|
+
registryRelease: {
|
|
150
|
+
registry: release.registry,
|
|
151
|
+
packageName: release.packageName,
|
|
152
|
+
version: release.version,
|
|
153
|
+
shasum: release.shasum,
|
|
154
|
+
integrity: release.integrity,
|
|
155
|
+
tarballUrl: release.tarballUrl,
|
|
156
|
+
},
|
|
157
|
+
installedBinding: {
|
|
158
|
+
requestedSpec: local.requestedSpec,
|
|
159
|
+
packageName: local.packageName,
|
|
160
|
+
version: local.version,
|
|
161
|
+
resolved: local.resolved,
|
|
162
|
+
integrity: local.integrity,
|
|
163
|
+
},
|
|
164
|
+
authority: {
|
|
165
|
+
git: false,
|
|
166
|
+
release: false,
|
|
167
|
+
foreignProjectWrite: false,
|
|
168
|
+
},
|
|
169
|
+
authorizationGranted: false,
|
|
170
|
+
};
|
|
171
|
+
writeJsonExclusiveAtomically(evidencePath, evidence);
|
|
172
|
+
return {
|
|
173
|
+
schemaVersion: "OwlCodaRunKitRegistryAdoptionResultV1",
|
|
174
|
+
status: "adopted",
|
|
175
|
+
decision: "ELIGIBLE",
|
|
176
|
+
evidencePath: relativeEvidencePath,
|
|
177
|
+
issueCodes: [],
|
|
178
|
+
packageName: PACKAGE_NAME,
|
|
179
|
+
version: CANDIDATE_VERSION,
|
|
180
|
+
authorizationGranted: false,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function formatAdoptionHuman(result) {
|
|
185
|
+
if (result.status === "adopted") {
|
|
186
|
+
return [
|
|
187
|
+
`Adopted ${result.packageName}@${result.version}`,
|
|
188
|
+
`Evidence: ${result.evidencePath}`,
|
|
189
|
+
"Git and release authority remain false.",
|
|
190
|
+
"",
|
|
191
|
+
].join("\n");
|
|
192
|
+
}
|
|
193
|
+
return [
|
|
194
|
+
`Adoption blocked: ${result.issueCodes.join(", ")}`,
|
|
195
|
+
"No adoption evidence was written.",
|
|
196
|
+
"",
|
|
197
|
+
].join("\n");
|
|
198
|
+
}
|
|
@@ -94,6 +94,11 @@ function assertRegularArtifact(filePath, label) {
|
|
|
94
94
|
if (!stat.isFile()) throw new Error(`${label} must be a regular file.`);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function readRegularJsonArtifact(filePath, label) {
|
|
98
|
+
assertRegularArtifact(filePath, label);
|
|
99
|
+
return readJson(filePath);
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
function acquireResumeLock(workspaceRoot) {
|
|
98
103
|
const runtimeRoot = path.join(workspaceRoot, RUNTIME_ROOT);
|
|
99
104
|
assertSafeDirectory(workspaceRoot, runtimeRoot, "RunKit runtime root");
|
|
@@ -299,7 +304,7 @@ function validatedParentCloseout(workspaceRoot, sourceRoot, sourceRunId) {
|
|
|
299
304
|
throw new Error("Parent closeout payload is invalid.");
|
|
300
305
|
}
|
|
301
306
|
if (payload.decision === "accepted") validateAcceptedParentEvidence(sourceRoot, sourceRunId, payload.verification);
|
|
302
|
-
const pin =
|
|
307
|
+
const pin = readRegularJsonArtifact(path.join(sourceRoot, "engine-pin.json"), "Parent engine pin");
|
|
303
308
|
const pinGate = validateExecutionPin({ expected: pin, actual: closeout.artifact.core });
|
|
304
309
|
if (pinGate.status !== "valid") throw new Error(`Parent closeout does not match its engine pin: ${pinGate.issues.join("; ")}`);
|
|
305
310
|
return {
|
|
@@ -392,7 +397,7 @@ function appendSameExecution({ workspaceRoot, sourceRunId, sourceRoot, request,
|
|
|
392
397
|
throw new Error("Unclosed execution resume requires continuationRunId=null.");
|
|
393
398
|
}
|
|
394
399
|
assertSoleActiveRun(workspaceRoot, sourceRunId);
|
|
395
|
-
const pin =
|
|
400
|
+
const pin = readRegularJsonArtifact(path.join(sourceRoot, "engine-pin.json"), "Resume engine pin");
|
|
396
401
|
const pinGate = validateExecutionPin({ expected: pin, actual: currentCoreIdentity() });
|
|
397
402
|
if (pinGate.status !== "valid") throw new Error(`Resume engine pin is stale: ${pinGate.issues.join("; ")}`);
|
|
398
403
|
assertNoActiveWriterLease(sourceRoot);
|
|
@@ -507,6 +512,7 @@ function createContinuation({ workspaceRoot, sourceRunId, sourceRoot, request, g
|
|
|
507
512
|
runId: targetRunId,
|
|
508
513
|
state: "planned",
|
|
509
514
|
enginePin,
|
|
515
|
+
goalContractSha256: sha256(goal.bytes),
|
|
510
516
|
continuation: {
|
|
511
517
|
parentRunId: sourceRunId,
|
|
512
518
|
resumeId: request.resumeId,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import path from "node:path";
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
|
+
CORE_VERSION,
|
|
18
19
|
RUNTIME_ROOT,
|
|
19
20
|
acceptedCloseoutVerificationIssues,
|
|
20
21
|
createCoreArtifact,
|
|
@@ -66,6 +67,120 @@ import {
|
|
|
66
67
|
exportOfflineReceipt,
|
|
67
68
|
importOfflineReceipt,
|
|
68
69
|
} from "./offline-store.mjs";
|
|
70
|
+
import {
|
|
71
|
+
detectProfiles,
|
|
72
|
+
resolveProfilesImpact,
|
|
73
|
+
validateProfiles,
|
|
74
|
+
} from "./profile-onboarding.mjs";
|
|
75
|
+
import {
|
|
76
|
+
formatDoctorHuman,
|
|
77
|
+
readAdoptionReadiness,
|
|
78
|
+
readProfileLauncherReadiness,
|
|
79
|
+
runDoctor,
|
|
80
|
+
} from "./onboarding-doctor.mjs";
|
|
81
|
+
import {
|
|
82
|
+
formatAdoptionHuman,
|
|
83
|
+
runRegistryAdoption,
|
|
84
|
+
} from "./registry-adoption.mjs";
|
|
85
|
+
import { OFFICIAL_NPM_REGISTRY } from "./registry-adoption-gate.mjs";
|
|
86
|
+
|
|
87
|
+
const TOP_LEVEL_HELP = `OwlRunKit ${CORE_VERSION}
|
|
88
|
+
|
|
89
|
+
Usage:
|
|
90
|
+
owlrunkit <command> [options]
|
|
91
|
+
|
|
92
|
+
Start here:
|
|
93
|
+
owlrunkit doctor --workspace .
|
|
94
|
+
owlrunkit profiles detect --workspace . --dry-run
|
|
95
|
+
owlrunkit adopt --workspace . --exact owlrunkit@${CORE_VERSION}
|
|
96
|
+
|
|
97
|
+
Quick Verification
|
|
98
|
+
owlrunkit quick-verify --workspace . -- <command> [args...]
|
|
99
|
+
Low-risk, source-bound reusable evidence without writer leases or acceptance.
|
|
100
|
+
|
|
101
|
+
Formal Delivery
|
|
102
|
+
owlrunkit start --workspace . --run-id <id> --goal <file> \\
|
|
103
|
+
--work-item <id> --owned-path <path>
|
|
104
|
+
Use for multi-writer, long-running, migration, integration, release, funds,
|
|
105
|
+
production, or formal acceptance work.
|
|
106
|
+
|
|
107
|
+
Other onboarding commands:
|
|
108
|
+
owlrunkit profiles validate --workspace .
|
|
109
|
+
owlrunkit profiles impact --workspace . --changed <path>
|
|
110
|
+
owlrunkit inspect --workspace . --json
|
|
111
|
+
|
|
112
|
+
Quick receipts cannot become Formal acceptance.
|
|
113
|
+
Formal accepted does not grant Git or release authority.
|
|
114
|
+
Those always require separate explicit authorization.
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
function withHumanOutput(result, humanOutput) {
|
|
118
|
+
Object.defineProperty(result, "humanOutput", {
|
|
119
|
+
configurable: false,
|
|
120
|
+
enumerable: false,
|
|
121
|
+
value: humanOutput,
|
|
122
|
+
writable: false,
|
|
123
|
+
});
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function profilesReadiness(root) {
|
|
128
|
+
if (!root) return { valid: false, launcherWarnings: [] };
|
|
129
|
+
try {
|
|
130
|
+
const validation = validateProfiles({ workspaceRoot: root });
|
|
131
|
+
const launchers = readProfileLauncherReadiness({ workspaceRoot: root });
|
|
132
|
+
return {
|
|
133
|
+
valid: validation.valid === true && launchers.ready,
|
|
134
|
+
launcherWarnings: launchers.launcherWarnings,
|
|
135
|
+
};
|
|
136
|
+
} catch {
|
|
137
|
+
return { valid: false, launcherWarnings: [] };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function onboardingProjection(root, recovery = null) {
|
|
142
|
+
const profiles = profilesReadiness(root);
|
|
143
|
+
const adoption = root
|
|
144
|
+
? readAdoptionReadiness({ workspaceRoot: root })
|
|
145
|
+
: { status: "missing" };
|
|
146
|
+
const profilesReady = profiles.valid;
|
|
147
|
+
const adoptionReady = adoption.status === "ok";
|
|
148
|
+
if (profilesReady && adoptionReady) {
|
|
149
|
+
return {
|
|
150
|
+
nextAllowedAction: recovery?.nextAllowedAction ?? "plan_new_execution",
|
|
151
|
+
completed: true,
|
|
152
|
+
steps: [],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const steps = [
|
|
156
|
+
{
|
|
157
|
+
id: "doctor",
|
|
158
|
+
command: "owlrunkit doctor --workspace .",
|
|
159
|
+
},
|
|
160
|
+
...(!profilesReady
|
|
161
|
+
? profiles.launcherWarnings.length > 0
|
|
162
|
+
? [{
|
|
163
|
+
id: "profile_launchers_review",
|
|
164
|
+
command: "owlrunkit doctor --workspace .",
|
|
165
|
+
}]
|
|
166
|
+
: [{
|
|
167
|
+
id: "profiles_detect",
|
|
168
|
+
command: "owlrunkit profiles detect --workspace . --dry-run",
|
|
169
|
+
}]
|
|
170
|
+
: []),
|
|
171
|
+
...(!adoptionReady
|
|
172
|
+
? [{
|
|
173
|
+
id: "adopt_or_start",
|
|
174
|
+
command: `owlrunkit adopt --workspace . --exact owlrunkit@${CORE_VERSION} or owlrunkit start`,
|
|
175
|
+
}]
|
|
176
|
+
: []),
|
|
177
|
+
];
|
|
178
|
+
return {
|
|
179
|
+
nextAllowedAction: "run_onboarding_diagnostics",
|
|
180
|
+
completed: false,
|
|
181
|
+
steps,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
69
184
|
|
|
70
185
|
function parseOptions(values, { multi = [], boolean = [] } = {}) {
|
|
71
186
|
const options = {};
|
|
@@ -592,7 +707,26 @@ function closeoutGateRejected(runId, decision, issues) {
|
|
|
592
707
|
}
|
|
593
708
|
|
|
594
709
|
async function initialize(options) {
|
|
595
|
-
|
|
710
|
+
const root = workspace(options);
|
|
711
|
+
const initialized = await initializeProjectRunKit({ workspaceRoot: root });
|
|
712
|
+
const result = {
|
|
713
|
+
...initialized,
|
|
714
|
+
onboarding: onboardingProjection(root),
|
|
715
|
+
nextAllowedAction: "run_onboarding_diagnostics",
|
|
716
|
+
};
|
|
717
|
+
if (options.json === true) return result;
|
|
718
|
+
return withHumanOutput(
|
|
719
|
+
result,
|
|
720
|
+
[
|
|
721
|
+
`Initialized OwlRunKit ${result.core.coreVersion} in ${result.runtimeRoot}`,
|
|
722
|
+
"",
|
|
723
|
+
"Next:",
|
|
724
|
+
"1. owlrunkit doctor --workspace .",
|
|
725
|
+
"2. owlrunkit profiles detect --workspace . --dry-run",
|
|
726
|
+
`3. owlrunkit adopt --workspace . --exact owlrunkit@${CORE_VERSION} or owlrunkit start`,
|
|
727
|
+
"",
|
|
728
|
+
].join("\n"),
|
|
729
|
+
);
|
|
596
730
|
}
|
|
597
731
|
|
|
598
732
|
function plan(options) {
|
|
@@ -844,7 +978,7 @@ function inspect(options) {
|
|
|
844
978
|
execution: executions.find(execution => execution.runId === requestedRunId),
|
|
845
979
|
}
|
|
846
980
|
: { mode: "summary" };
|
|
847
|
-
return { ...result, summary, view };
|
|
981
|
+
return { ...result, summary, view, onboarding: onboardingProjection(root, recovery) };
|
|
848
982
|
}
|
|
849
983
|
|
|
850
984
|
function closeout(options) {
|
|
@@ -1025,29 +1159,95 @@ function requestCommand(options, handler) {
|
|
|
1025
1159
|
export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
1026
1160
|
try {
|
|
1027
1161
|
const [command, ...rest] = argv;
|
|
1162
|
+
if (command === "--help" || command === "-h") {
|
|
1163
|
+
return withHumanOutput({
|
|
1164
|
+
status: "help",
|
|
1165
|
+
exitCode: 0,
|
|
1166
|
+
authorizationGranted: false,
|
|
1167
|
+
}, TOP_LEVEL_HELP);
|
|
1168
|
+
}
|
|
1169
|
+
if (command === "--version" || command === "-v") {
|
|
1170
|
+
return withHumanOutput({
|
|
1171
|
+
status: "version",
|
|
1172
|
+
exitCode: 0,
|
|
1173
|
+
version: CORE_VERSION,
|
|
1174
|
+
authorizationGranted: false,
|
|
1175
|
+
}, `owlrunkit ${CORE_VERSION}\n`);
|
|
1176
|
+
}
|
|
1028
1177
|
const separatorIndex = rest.indexOf("--");
|
|
1029
1178
|
if (separatorIndex >= 0 && command !== "verify" && command !== "quick-verify") {
|
|
1030
1179
|
throw new Error("Only runkit verify accepts an exact command after --.");
|
|
1031
1180
|
}
|
|
1032
1181
|
const optionValues = separatorIndex >= 0 ? rest.slice(0, separatorIndex) : rest;
|
|
1033
1182
|
const commandArgv = separatorIndex >= 0 ? rest.slice(separatorIndex + 1) : [];
|
|
1034
|
-
const nested = new Set(["lease", "delivery"]).has(command);
|
|
1183
|
+
const nested = new Set(["lease", "delivery", "profiles"]).has(command);
|
|
1035
1184
|
const [action, ...nestedOptions] = nested ? optionValues : [null, ...optionValues];
|
|
1036
1185
|
const options = parseOptions(nested ? nestedOptions : optionValues, {
|
|
1037
|
-
multi:
|
|
1186
|
+
multi: command === "profiles" && action === "impact"
|
|
1187
|
+
? ["changed"]
|
|
1188
|
+
: (command === "lease" && action === "acquire") || command === "start"
|
|
1189
|
+
? ["owned-path"]
|
|
1190
|
+
: [],
|
|
1038
1191
|
boolean: command === "inspect"
|
|
1039
1192
|
? ["json", "verbose", "history"]
|
|
1193
|
+
: command === "init"
|
|
1194
|
+
? ["json"]
|
|
1195
|
+
: command === "doctor" || command === "adopt"
|
|
1196
|
+
? ["json"]
|
|
1197
|
+
: command === "profiles"
|
|
1198
|
+
? ["json", "dry-run"]
|
|
1040
1199
|
: new Set(["quick-verify", "quick-attest", "quick-metrics"]).has(command)
|
|
1041
1200
|
? ["json", "local"]
|
|
1042
1201
|
: [],
|
|
1043
1202
|
});
|
|
1044
1203
|
if (command === "init") return await initialize(options);
|
|
1204
|
+
if (command === "doctor") {
|
|
1205
|
+
const timeoutMs = options["timeout-ms"] === undefined
|
|
1206
|
+
? 1_500
|
|
1207
|
+
: Number.parseInt(options["timeout-ms"], 10);
|
|
1208
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 25 || timeoutMs > 10_000) {
|
|
1209
|
+
throw new Error("--timeout-ms must be an integer from 25 to 10000.");
|
|
1210
|
+
}
|
|
1211
|
+
const report = await runDoctor({
|
|
1212
|
+
workspaceRoot: requireOption(options, "workspace"),
|
|
1213
|
+
registryUrl: options.registry ?? OFFICIAL_NPM_REGISTRY,
|
|
1214
|
+
timeoutMs,
|
|
1215
|
+
});
|
|
1216
|
+
report.exitCode = report.status === "ready" ? 0 : 2;
|
|
1217
|
+
return options.json === true
|
|
1218
|
+
? report
|
|
1219
|
+
: withHumanOutput(report, formatDoctorHuman(report));
|
|
1220
|
+
}
|
|
1221
|
+
if (command === "adopt") {
|
|
1222
|
+
const result = await runRegistryAdoption({
|
|
1223
|
+
workspaceRoot: requireOption(options, "workspace"),
|
|
1224
|
+
exactSpec: requireOption(options, "exact"),
|
|
1225
|
+
});
|
|
1226
|
+
result.exitCode = result.status === "adopted" ? 0 : 2;
|
|
1227
|
+
return options.json === true
|
|
1228
|
+
? result
|
|
1229
|
+
: withHumanOutput(result, formatAdoptionHuman(result));
|
|
1230
|
+
}
|
|
1045
1231
|
if (command === "start") return startExecution(options);
|
|
1046
1232
|
if (command === "plan") {
|
|
1047
1233
|
const root = workspace(options);
|
|
1048
1234
|
return withControlTransaction(root, () => plan(options));
|
|
1049
1235
|
}
|
|
1050
1236
|
if (command === "inspect") return inspect(options);
|
|
1237
|
+
if (command === "profiles") {
|
|
1238
|
+
const root = requireOption(options, "workspace");
|
|
1239
|
+
if (action === "detect") {
|
|
1240
|
+
if (options["dry-run"] !== true) throw new Error("profiles detect requires --dry-run.");
|
|
1241
|
+
return detectProfiles({ workspaceRoot: root });
|
|
1242
|
+
}
|
|
1243
|
+
if (action === "validate") return validateProfiles({ workspaceRoot: root });
|
|
1244
|
+
if (action === "impact") {
|
|
1245
|
+
const changedPaths = options.changed ?? [];
|
|
1246
|
+
if (changedPaths.length === 0) throw new Error("profiles impact requires --changed.");
|
|
1247
|
+
return resolveProfilesImpact({ workspaceRoot: root, changedPaths });
|
|
1248
|
+
}
|
|
1249
|
+
throw new Error("Usage: owlrunkit profiles <detect|validate|impact> [options]");
|
|
1250
|
+
}
|
|
1051
1251
|
if (command === "lease") {
|
|
1052
1252
|
const root = workspace(options);
|
|
1053
1253
|
const runId = safeRunId(requireOption(options, "run-id"));
|
|
@@ -1184,7 +1384,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1184
1384
|
return withControlTransaction(root, () => closeout(options));
|
|
1185
1385
|
}
|
|
1186
1386
|
if (command === "finish") return finishExecution(options);
|
|
1187
|
-
throw new Error("Usage:
|
|
1387
|
+
throw new Error("Usage: owlrunkit <init|doctor|adopt|profiles|start|verify|repair|offline-export|offline-import|quick-verify|quick-attest|quick-metrics|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
|
|
1188
1388
|
} catch (error) {
|
|
1189
1389
|
return {
|
|
1190
1390
|
status: "invalid_input",
|
|
@@ -1209,6 +1409,16 @@ if (isDirectExecution(import.meta.url)) {
|
|
|
1209
1409
|
? parseOptions(argv.slice(1), { boolean: ["json", "verbose", "history"] })
|
|
1210
1410
|
: null;
|
|
1211
1411
|
const humanInspect = inspectOptions !== null && inspectOptions.json !== true;
|
|
1212
|
-
|
|
1412
|
+
const onboardingHumanCommand = new Set(["init", "doctor", "adopt", "profiles"]).has(argv[0]);
|
|
1413
|
+
const interactiveOnboarding = onboardingHumanCommand
|
|
1414
|
+
&& !argv.includes("--json")
|
|
1415
|
+
&& process.stdout.isTTY === true;
|
|
1416
|
+
process.stdout.write(
|
|
1417
|
+
typeof result.humanOutput === "string" && (!onboardingHumanCommand || interactiveOnboarding)
|
|
1418
|
+
? result.humanOutput
|
|
1419
|
+
: humanInspect
|
|
1420
|
+
? formatInspectHuman(result)
|
|
1421
|
+
: `${JSON.stringify(result)}\n`,
|
|
1422
|
+
);
|
|
1213
1423
|
process.exitCode = result.exitCode;
|
|
1214
1424
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://owlcoda.dev/schemas/runkit/registry-adoption-evidence-v1.json",
|
|
4
|
+
"title": "OwlCoda RunKit Registry Adoption Evidence V1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"status",
|
|
10
|
+
"decision",
|
|
11
|
+
"packageName",
|
|
12
|
+
"version",
|
|
13
|
+
"exactSpec",
|
|
14
|
+
"registryRelease",
|
|
15
|
+
"installedBinding",
|
|
16
|
+
"authority",
|
|
17
|
+
"authorizationGranted"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"schemaVersion": { "const": "OwlCodaRunKitRegistryAdoptionEvidenceV1" },
|
|
21
|
+
"status": { "const": "adopted" },
|
|
22
|
+
"decision": { "const": "ELIGIBLE" },
|
|
23
|
+
"packageName": { "const": "owlrunkit" },
|
|
24
|
+
"version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
|
|
25
|
+
"exactSpec": { "type": "string", "pattern": "^owlrunkit@[0-9]+\\.[0-9]+\\.[0-9]+$" },
|
|
26
|
+
"registryRelease": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"required": ["registry", "packageName", "version", "shasum", "integrity", "tarballUrl"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"registry": { "const": "https://registry.npmjs.org" },
|
|
32
|
+
"packageName": { "const": "owlrunkit" },
|
|
33
|
+
"version": { "type": "string" },
|
|
34
|
+
"shasum": { "type": "string", "pattern": "^[a-f0-9]{40}$" },
|
|
35
|
+
"integrity": { "type": "string", "pattern": "^sha512-" },
|
|
36
|
+
"tarballUrl": { "type": "string", "pattern": "^https://registry\\.npmjs\\.org/owlrunkit/-/owlrunkit-[0-9]+\\.[0-9]+\\.[0-9]+\\.tgz$" }
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"installedBinding": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"required": ["requestedSpec", "packageName", "version", "resolved", "integrity"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"requestedSpec": { "type": "string" },
|
|
45
|
+
"packageName": { "const": "owlrunkit" },
|
|
46
|
+
"version": { "type": "string" },
|
|
47
|
+
"resolved": { "type": "string" },
|
|
48
|
+
"integrity": { "type": "string" }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"authority": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"required": ["git", "release", "foreignProjectWrite"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"git": { "const": false },
|
|
57
|
+
"release": { "const": false },
|
|
58
|
+
"foreignProjectWrite": { "const": false }
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"authorizationGranted": { "const": false }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://owlcoda.dev/schemas/runkit/verification-finalize-attempt-v1.json",
|
|
4
|
+
"title": "OwlCoda RunKit Verification Finalize Attempt V1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"runId",
|
|
10
|
+
"verificationId",
|
|
11
|
+
"status",
|
|
12
|
+
"gateStatus",
|
|
13
|
+
"finalizeRequestPath",
|
|
14
|
+
"finalizeRequestSha256",
|
|
15
|
+
"result",
|
|
16
|
+
"authorizationGranted"
|
|
17
|
+
],
|
|
18
|
+
"properties": {
|
|
19
|
+
"schemaVersion": {
|
|
20
|
+
"const": "OwlCodaRunKitVerificationFinalizeAttemptV1"
|
|
21
|
+
},
|
|
22
|
+
"runId": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
|
|
25
|
+
},
|
|
26
|
+
"verificationId": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
|
|
29
|
+
},
|
|
30
|
+
"status": {
|
|
31
|
+
"const": "rejected"
|
|
32
|
+
},
|
|
33
|
+
"gateStatus": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1
|
|
36
|
+
},
|
|
37
|
+
"finalizeRequestPath": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"minLength": 1
|
|
40
|
+
},
|
|
41
|
+
"finalizeRequestSha256": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
44
|
+
},
|
|
45
|
+
"result": {
|
|
46
|
+
"type": "object"
|
|
47
|
+
},
|
|
48
|
+
"authorizationGranted": {
|
|
49
|
+
"const": false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://owlcoda.dev/schemas/runkit/verification-preflight-v1.json",
|
|
4
|
+
"title": "OwlCoda RunKit Verification Preflight V1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"runId",
|
|
10
|
+
"verificationId",
|
|
11
|
+
"status",
|
|
12
|
+
"issueCode",
|
|
13
|
+
"executable",
|
|
14
|
+
"commandArgvSha256",
|
|
15
|
+
"authorizationGranted"
|
|
16
|
+
],
|
|
17
|
+
"properties": {
|
|
18
|
+
"schemaVersion": {
|
|
19
|
+
"const": "OwlCodaRunKitVerificationPreflightV1"
|
|
20
|
+
},
|
|
21
|
+
"runId": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
|
|
24
|
+
},
|
|
25
|
+
"verificationId": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
|
|
28
|
+
},
|
|
29
|
+
"status": {
|
|
30
|
+
"const": "rejected"
|
|
31
|
+
},
|
|
32
|
+
"issueCode": {
|
|
33
|
+
"enum": [
|
|
34
|
+
"verify_executable_absolute_path_required",
|
|
35
|
+
"verify_executable_missing",
|
|
36
|
+
"verify_executable_not_regular_file",
|
|
37
|
+
"verify_executable_symlink_rejected"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"executable": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1
|
|
43
|
+
},
|
|
44
|
+
"commandArgvSha256": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
47
|
+
},
|
|
48
|
+
"authorizationGranted": {
|
|
49
|
+
"const": false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|