owlrunkit 0.13.0 → 0.14.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.
@@ -66,6 +66,80 @@ import {
66
66
  exportOfflineReceipt,
67
67
  importOfflineReceipt,
68
68
  } from "./offline-store.mjs";
69
+ import {
70
+ detectProfiles,
71
+ resolveProfilesImpact,
72
+ validateProfiles,
73
+ } from "./profile-onboarding.mjs";
74
+ import {
75
+ formatDoctorHuman,
76
+ runDoctor,
77
+ } from "./onboarding-doctor.mjs";
78
+ import {
79
+ formatAdoptionHuman,
80
+ runRegistryAdoption,
81
+ } from "./registry-adoption.mjs";
82
+ import { OFFICIAL_NPM_REGISTRY } from "./registry-adoption-gate.mjs";
83
+
84
+ const TOP_LEVEL_HELP = `OwlRunKit 0.14.0
85
+
86
+ Usage:
87
+ owlrunkit <command> [options]
88
+
89
+ Start here:
90
+ owlrunkit doctor --workspace .
91
+ owlrunkit profiles detect --workspace . --dry-run
92
+ owlrunkit adopt --workspace . --exact owlrunkit@0.14.0
93
+
94
+ Quick Verification
95
+ owlrunkit quick-verify --workspace . -- <command> [args...]
96
+ Low-risk, source-bound reusable evidence without writer leases or acceptance.
97
+
98
+ Formal Delivery
99
+ owlrunkit start --workspace . --run-id <id> --goal <file> \\
100
+ --work-item <id> --owned-path <path>
101
+ Use for multi-writer, long-running, migration, integration, release, funds,
102
+ production, or formal acceptance work.
103
+
104
+ Other onboarding commands:
105
+ owlrunkit profiles validate --workspace .
106
+ owlrunkit profiles impact --workspace . --changed <path>
107
+ owlrunkit inspect --workspace . --json
108
+
109
+ Quick receipts cannot become Formal acceptance.
110
+ Formal accepted does not grant Git or release authority.
111
+ Those always require separate explicit authorization.
112
+ `;
113
+
114
+ function withHumanOutput(result, humanOutput) {
115
+ Object.defineProperty(result, "humanOutput", {
116
+ configurable: false,
117
+ enumerable: false,
118
+ value: humanOutput,
119
+ writable: false,
120
+ });
121
+ return result;
122
+ }
123
+
124
+ function onboardingProjection() {
125
+ return {
126
+ nextAllowedAction: "run_onboarding_diagnostics",
127
+ steps: [
128
+ {
129
+ id: "doctor",
130
+ command: "owlrunkit doctor --workspace .",
131
+ },
132
+ {
133
+ id: "profiles_detect",
134
+ command: "owlrunkit profiles detect --workspace . --dry-run",
135
+ },
136
+ {
137
+ id: "adopt_or_start",
138
+ command: "owlrunkit adopt --workspace . --exact owlrunkit@0.14.0 or owlrunkit start",
139
+ },
140
+ ],
141
+ };
142
+ }
69
143
 
70
144
  function parseOptions(values, { multi = [], boolean = [] } = {}) {
71
145
  const options = {};
@@ -592,7 +666,25 @@ function closeoutGateRejected(runId, decision, issues) {
592
666
  }
593
667
 
594
668
  async function initialize(options) {
595
- return initializeProjectRunKit({ workspaceRoot: workspace(options) });
669
+ const initialized = await initializeProjectRunKit({ workspaceRoot: workspace(options) });
670
+ const result = {
671
+ ...initialized,
672
+ onboarding: onboardingProjection(),
673
+ nextAllowedAction: "run_onboarding_diagnostics",
674
+ };
675
+ if (options.json === true) return result;
676
+ return withHumanOutput(
677
+ result,
678
+ [
679
+ `Initialized OwlRunKit ${result.core.coreVersion} in ${result.runtimeRoot}`,
680
+ "",
681
+ "Next:",
682
+ "1. owlrunkit doctor --workspace .",
683
+ "2. owlrunkit profiles detect --workspace . --dry-run",
684
+ "3. owlrunkit adopt --workspace . --exact owlrunkit@0.14.0 or owlrunkit start",
685
+ "",
686
+ ].join("\n"),
687
+ );
596
688
  }
597
689
 
598
690
  function plan(options) {
@@ -844,7 +936,7 @@ function inspect(options) {
844
936
  execution: executions.find(execution => execution.runId === requestedRunId),
845
937
  }
846
938
  : { mode: "summary" };
847
- return { ...result, summary, view };
939
+ return { ...result, summary, view, onboarding: onboardingProjection() };
848
940
  }
849
941
 
850
942
  function closeout(options) {
@@ -1025,29 +1117,95 @@ function requestCommand(options, handler) {
1025
1117
  export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
1026
1118
  try {
1027
1119
  const [command, ...rest] = argv;
1120
+ if (command === "--help" || command === "-h") {
1121
+ return withHumanOutput({
1122
+ status: "help",
1123
+ exitCode: 0,
1124
+ authorizationGranted: false,
1125
+ }, TOP_LEVEL_HELP);
1126
+ }
1127
+ if (command === "--version" || command === "-v") {
1128
+ return withHumanOutput({
1129
+ status: "version",
1130
+ exitCode: 0,
1131
+ version: "0.14.0",
1132
+ authorizationGranted: false,
1133
+ }, "owlrunkit 0.14.0\n");
1134
+ }
1028
1135
  const separatorIndex = rest.indexOf("--");
1029
1136
  if (separatorIndex >= 0 && command !== "verify" && command !== "quick-verify") {
1030
1137
  throw new Error("Only runkit verify accepts an exact command after --.");
1031
1138
  }
1032
1139
  const optionValues = separatorIndex >= 0 ? rest.slice(0, separatorIndex) : rest;
1033
1140
  const commandArgv = separatorIndex >= 0 ? rest.slice(separatorIndex + 1) : [];
1034
- const nested = new Set(["lease", "delivery"]).has(command);
1141
+ const nested = new Set(["lease", "delivery", "profiles"]).has(command);
1035
1142
  const [action, ...nestedOptions] = nested ? optionValues : [null, ...optionValues];
1036
1143
  const options = parseOptions(nested ? nestedOptions : optionValues, {
1037
- multi: (command === "lease" && action === "acquire") || command === "start" ? ["owned-path"] : [],
1144
+ multi: command === "profiles" && action === "impact"
1145
+ ? ["changed"]
1146
+ : (command === "lease" && action === "acquire") || command === "start"
1147
+ ? ["owned-path"]
1148
+ : [],
1038
1149
  boolean: command === "inspect"
1039
1150
  ? ["json", "verbose", "history"]
1151
+ : command === "init"
1152
+ ? ["json"]
1153
+ : command === "doctor" || command === "adopt"
1154
+ ? ["json"]
1155
+ : command === "profiles"
1156
+ ? ["json", "dry-run"]
1040
1157
  : new Set(["quick-verify", "quick-attest", "quick-metrics"]).has(command)
1041
1158
  ? ["json", "local"]
1042
1159
  : [],
1043
1160
  });
1044
1161
  if (command === "init") return await initialize(options);
1162
+ if (command === "doctor") {
1163
+ const timeoutMs = options["timeout-ms"] === undefined
1164
+ ? 1_500
1165
+ : Number.parseInt(options["timeout-ms"], 10);
1166
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 25 || timeoutMs > 10_000) {
1167
+ throw new Error("--timeout-ms must be an integer from 25 to 10000.");
1168
+ }
1169
+ const report = await runDoctor({
1170
+ workspaceRoot: requireOption(options, "workspace"),
1171
+ registryUrl: options.registry ?? OFFICIAL_NPM_REGISTRY,
1172
+ timeoutMs,
1173
+ });
1174
+ report.exitCode = report.status === "ready" ? 0 : 2;
1175
+ return options.json === true
1176
+ ? report
1177
+ : withHumanOutput(report, formatDoctorHuman(report));
1178
+ }
1179
+ if (command === "adopt") {
1180
+ const result = await runRegistryAdoption({
1181
+ workspaceRoot: requireOption(options, "workspace"),
1182
+ exactSpec: requireOption(options, "exact"),
1183
+ });
1184
+ result.exitCode = result.status === "adopted" ? 0 : 2;
1185
+ return options.json === true
1186
+ ? result
1187
+ : withHumanOutput(result, formatAdoptionHuman(result));
1188
+ }
1045
1189
  if (command === "start") return startExecution(options);
1046
1190
  if (command === "plan") {
1047
1191
  const root = workspace(options);
1048
1192
  return withControlTransaction(root, () => plan(options));
1049
1193
  }
1050
1194
  if (command === "inspect") return inspect(options);
1195
+ if (command === "profiles") {
1196
+ const root = requireOption(options, "workspace");
1197
+ if (action === "detect") {
1198
+ if (options["dry-run"] !== true) throw new Error("profiles detect requires --dry-run.");
1199
+ return detectProfiles({ workspaceRoot: root });
1200
+ }
1201
+ if (action === "validate") return validateProfiles({ workspaceRoot: root });
1202
+ if (action === "impact") {
1203
+ const changedPaths = options.changed ?? [];
1204
+ if (changedPaths.length === 0) throw new Error("profiles impact requires --changed.");
1205
+ return resolveProfilesImpact({ workspaceRoot: root, changedPaths });
1206
+ }
1207
+ throw new Error("Usage: owlrunkit profiles <detect|validate|impact> [options]");
1208
+ }
1051
1209
  if (command === "lease") {
1052
1210
  const root = workspace(options);
1053
1211
  const runId = safeRunId(requireOption(options, "run-id"));
@@ -1184,7 +1342,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
1184
1342
  return withControlTransaction(root, () => closeout(options));
1185
1343
  }
1186
1344
  if (command === "finish") return finishExecution(options);
1187
- throw new Error("Usage: runkit-cli.mjs <init|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]");
1345
+ 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
1346
  } catch (error) {
1189
1347
  return {
1190
1348
  status: "invalid_input",
@@ -1209,6 +1367,16 @@ if (isDirectExecution(import.meta.url)) {
1209
1367
  ? parseOptions(argv.slice(1), { boolean: ["json", "verbose", "history"] })
1210
1368
  : null;
1211
1369
  const humanInspect = inspectOptions !== null && inspectOptions.json !== true;
1212
- process.stdout.write(humanInspect ? formatInspectHuman(result) : `${JSON.stringify(result)}\n`);
1370
+ const onboardingHumanCommand = new Set(["init", "doctor", "adopt", "profiles"]).has(argv[0]);
1371
+ const interactiveOnboarding = onboardingHumanCommand
1372
+ && !argv.includes("--json")
1373
+ && process.stdout.isTTY === true;
1374
+ process.stdout.write(
1375
+ typeof result.humanOutput === "string" && (!onboardingHumanCommand || interactiveOnboarding)
1376
+ ? result.humanOutput
1377
+ : humanInspect
1378
+ ? formatInspectHuman(result)
1379
+ : `${JSON.stringify(result)}\n`,
1380
+ );
1213
1381
  process.exitCode = result.exitCode;
1214
1382
  }
@@ -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
+ }