@rehearsal-db/core 0.1.0-beta.13 → 0.1.0-beta.15

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.
Files changed (169) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +4 -1
  3. package/dist/package.json +100 -0
  4. package/dist/scripts/install/create_root_config.d.mts +3 -0
  5. package/dist/scripts/install/create_root_config.mjs +13 -0
  6. package/dist/scripts/runtime/manage_database.d.mts +6 -0
  7. package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
  8. package/dist/src/application/session.d.mts +96 -0
  9. package/dist/src/application/session.mjs +380 -0
  10. package/dist/src/baseline/artifact.d.mts +119 -0
  11. package/dist/src/baseline/artifact.mjs +522 -0
  12. package/dist/src/baseline/builder.d.mts +19 -0
  13. package/dist/src/baseline/builder.mjs +142 -0
  14. package/dist/src/baseline/input_discovery.d.mts +13 -0
  15. package/dist/src/baseline/input_discovery.mjs +149 -0
  16. package/dist/src/baseline/policy_review.d.mts +56 -0
  17. package/dist/src/baseline/policy_review.mjs +73 -0
  18. package/dist/src/baseline/preparation.d.mts +63 -0
  19. package/dist/src/baseline/preparation.mjs +295 -0
  20. package/dist/src/baseline/privacy_engine.d.mts +162 -0
  21. package/dist/src/baseline/privacy_engine.mjs +1162 -0
  22. package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
  23. package/dist/src/baseline/privacy_structured_json.mjs +464 -0
  24. package/dist/src/baseline/sanitization_policy.d.mts +69 -0
  25. package/dist/src/baseline/sanitization_policy.mjs +227 -0
  26. package/dist/src/baseline/schema_snapshot.d.mts +19 -0
  27. package/dist/src/baseline/schema_snapshot.mjs +79 -0
  28. package/dist/src/cli/arguments.d.mts +43 -0
  29. package/dist/src/cli/arguments.mjs +135 -0
  30. package/dist/src/cli/guided.d.mts +51 -0
  31. package/dist/src/cli/guided.mjs +713 -0
  32. package/dist/src/cli/init_command.d.mts +21 -0
  33. package/dist/src/cli/init_command.mjs +90 -0
  34. package/dist/src/cli/project_commands.d.mts +157 -0
  35. package/dist/src/cli/project_commands.mjs +183 -0
  36. package/dist/src/cli/rehearsal.d.mts +7 -0
  37. package/dist/src/cli/rehearsal.mjs +706 -0
  38. package/dist/src/cli/renderers.d.mts +299 -0
  39. package/dist/src/cli/renderers.mjs +554 -0
  40. package/dist/src/cli/runtime_commands.d.mts +265 -0
  41. package/dist/src/cli/runtime_commands.mjs +599 -0
  42. package/dist/src/cli/source_commands.d.mts +432 -0
  43. package/dist/src/cli/source_commands.mjs +299 -0
  44. package/dist/src/cli/terminal.d.mts +59 -0
  45. package/dist/src/cli/terminal.mjs +230 -0
  46. package/dist/src/identity/claim.d.mts +144 -0
  47. package/dist/src/identity/claim.mjs +947 -0
  48. package/dist/src/identity/provider_configuration.d.mts +37 -0
  49. package/dist/src/identity/provider_configuration.mjs +132 -0
  50. package/dist/src/identity/storage.d.mts +21 -0
  51. package/dist/src/identity/storage.mjs +122 -0
  52. package/dist/src/index.d.mts +4 -0
  53. package/dist/src/index.mjs +3 -0
  54. package/dist/src/project/configuration.d.mts +181 -0
  55. package/dist/src/project/configuration.mjs +958 -0
  56. package/dist/src/project/install_scaffold.d.mts +42 -0
  57. package/dist/src/project/install_scaffold.mjs +263 -0
  58. package/dist/src/project/setup.d.mts +87 -0
  59. package/dist/src/project/setup.mjs +414 -0
  60. package/dist/src/project/support_report.d.mts +88 -0
  61. package/dist/src/project/support_report.mjs +83 -0
  62. package/dist/src/runtime/cleanup.d.mts +85 -0
  63. package/dist/src/runtime/cleanup.mjs +311 -0
  64. package/dist/src/runtime/migration_history.d.mts +51 -0
  65. package/dist/src/runtime/migration_history.mjs +170 -0
  66. package/dist/src/runtime/plan.d.mts +134 -0
  67. package/dist/src/runtime/plan.mjs +636 -0
  68. package/dist/src/runtime/policy.d.mts +66 -0
  69. package/dist/src/runtime/policy.mjs +369 -0
  70. package/dist/src/runtime/restore.d.mts +22 -0
  71. package/dist/src/runtime/restore.mjs +276 -0
  72. package/dist/src/runtime/service_environment.d.mts +11 -0
  73. package/dist/src/runtime/service_environment.mjs +58 -0
  74. package/dist/src/runtime/topology.d.mts +51 -0
  75. package/dist/src/runtime/topology.mjs +128 -0
  76. package/dist/src/shared/diagnostics.d.mts +97 -0
  77. package/dist/src/shared/diagnostics.mjs +170 -0
  78. package/dist/src/shared/human_output.d.mts +2 -0
  79. package/{src → dist/src}/shared/human_output.mjs +1 -3
  80. package/dist/src/shared/node_runtime.d.mts +14 -0
  81. package/dist/src/shared/node_runtime.mjs +25 -0
  82. package/dist/src/shared/operation_guard.d.mts +22 -0
  83. package/dist/src/shared/operation_guard.mjs +150 -0
  84. package/dist/src/shared/process_environment.d.mts +15 -0
  85. package/dist/src/shared/process_environment.mjs +50 -0
  86. package/dist/src/source/access.d.mts +165 -0
  87. package/dist/src/source/access.mjs +602 -0
  88. package/dist/src/source/asset_transfer.d.mts +27 -0
  89. package/dist/src/source/asset_transfer.mjs +202 -0
  90. package/dist/src/source/baseline.d.mts +66 -0
  91. package/dist/src/source/baseline.mjs +372 -0
  92. package/dist/src/source/external_reader.d.mts +42 -0
  93. package/dist/src/source/external_reader.mjs +278 -0
  94. package/dist/src/source/postgresql_access.d.mts +86 -0
  95. package/dist/src/source/postgresql_access.mjs +454 -0
  96. package/dist/src/source/privacy_paths.d.mts +10 -0
  97. package/dist/src/source/privacy_paths.mjs +27 -0
  98. package/dist/src/targets/driver.d.mts +2 -0
  99. package/dist/src/targets/driver.mjs +13 -0
  100. package/dist/src/targets/postgresql.d.mts +5 -0
  101. package/dist/src/targets/postgresql.mjs +641 -0
  102. package/dist/src/targets/supabase.d.mts +8 -0
  103. package/dist/src/targets/supabase.mjs +659 -0
  104. package/dist/src/targets/supabase_environment.d.mts +49 -0
  105. package/dist/src/targets/supabase_environment.mjs +162 -0
  106. package/dist/src/targets/target.d.mts +33 -0
  107. package/dist/src/targets/target.mjs +39 -0
  108. package/docs/README.md +1 -0
  109. package/docs/architecture.md +17 -9
  110. package/docs/configuration.md +49 -8
  111. package/docs/getting-started.md +28 -0
  112. package/docs/index.md +1 -0
  113. package/docs/production-source.md +43 -7
  114. package/docs/releasing.md +5 -3
  115. package/docs/roadmap.md +3 -2
  116. package/docs/sanitization.md +415 -14
  117. package/docs/standalone-workflow.md +12 -6
  118. package/docs/troubleshooting.md +5 -1
  119. package/docs/tutorial.md +5 -0
  120. package/docs/typescript.md +87 -0
  121. package/package.json +35 -29
  122. package/scripts/install/create_root_config.mjs +0 -36
  123. package/src/README.md +0 -17
  124. package/src/application/session.mjs +0 -435
  125. package/src/baseline/artifact.mjs +0 -684
  126. package/src/baseline/builder.mjs +0 -164
  127. package/src/baseline/input_discovery.mjs +0 -155
  128. package/src/baseline/policy_review.mjs +0 -92
  129. package/src/baseline/preparation.mjs +0 -323
  130. package/src/baseline/privacy_engine.mjs +0 -952
  131. package/src/baseline/sanitization_policy.mjs +0 -299
  132. package/src/baseline/schema_snapshot.mjs +0 -113
  133. package/src/cli/arguments.mjs +0 -126
  134. package/src/cli/guided.mjs +0 -814
  135. package/src/cli/init_command.mjs +0 -94
  136. package/src/cli/rehearsal.mjs +0 -970
  137. package/src/cli/renderers.mjs +0 -626
  138. package/src/cli/runtime_commands.mjs +0 -707
  139. package/src/cli/source_commands.mjs +0 -347
  140. package/src/cli/terminal.mjs +0 -275
  141. package/src/identity/claim.mjs +0 -1163
  142. package/src/identity/provider_configuration.mjs +0 -177
  143. package/src/identity/storage.mjs +0 -165
  144. package/src/project/configuration.mjs +0 -1337
  145. package/src/project/install_scaffold.mjs +0 -291
  146. package/src/project/setup.mjs +0 -466
  147. package/src/project/support_report.mjs +0 -105
  148. package/src/runtime/cleanup.mjs +0 -381
  149. package/src/runtime/migration_history.mjs +0 -220
  150. package/src/runtime/plan.mjs +0 -722
  151. package/src/runtime/policy.mjs +0 -438
  152. package/src/runtime/restore.mjs +0 -337
  153. package/src/runtime/service_environment.mjs +0 -82
  154. package/src/runtime/topology.mjs +0 -177
  155. package/src/shared/diagnostics.mjs +0 -207
  156. package/src/shared/node_runtime.mjs +0 -31
  157. package/src/shared/operation_guard.mjs +0 -162
  158. package/src/shared/process_environment.mjs +0 -68
  159. package/src/source/access.mjs +0 -753
  160. package/src/source/asset_transfer.mjs +0 -207
  161. package/src/source/baseline.mjs +0 -482
  162. package/src/source/external_reader.mjs +0 -255
  163. package/src/source/postgresql_access.mjs +0 -581
  164. package/src/source/privacy_paths.mjs +0 -38
  165. package/src/targets/postgresql.mjs +0 -808
  166. package/src/targets/supabase.mjs +0 -840
  167. package/src/targets/supabase_environment.mjs +0 -229
  168. package/src/targets/target.mjs +0 -66
  169. /package/{src/project → types}/configuration.d.mts +0 -0
@@ -0,0 +1,21 @@
1
+ /** Preview or create the project-root Rehearsal configuration. */
2
+ interface InitFlags {
3
+ readonly configPath: string | undefined;
4
+ readonly write: boolean;
5
+ }
6
+ interface InitOptions {
7
+ readonly projectRoot: string;
8
+ readonly flags: InitFlags;
9
+ }
10
+ export declare const runRehearsalInit: ({ projectRoot, flags }: InitOptions) => Promise<{
11
+ mode: string;
12
+ destination: string;
13
+ detected: import("../project/configuration.mjs").DetectedProject;
14
+ source: string;
15
+ availableOptions: {
16
+ path: string;
17
+ summary: string;
18
+ }[];
19
+ nextAction: string;
20
+ }>;
21
+ export {};
@@ -0,0 +1,90 @@
1
+ /** Preview or create the project-root Rehearsal configuration. */
2
+ import { writeFile } from "node:fs/promises";
3
+ import { join, relative } from "node:path";
4
+ import { findRehearsalConfigPath, inspectDetectedProject, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "../project/configuration.mjs";
5
+ const findExistingConfig = async ({ projectRoot, configPath, }) => {
6
+ try {
7
+ return await findRehearsalConfigPath({
8
+ projectRoot,
9
+ ...(configPath === undefined ? {} : { configPath }),
10
+ });
11
+ }
12
+ catch (error) {
13
+ if (String(error instanceof Error ? error.message : error).startsWith("No Rehearsal configuration")) {
14
+ return null;
15
+ }
16
+ throw error;
17
+ }
18
+ };
19
+ export const runRehearsalInit = async ({ projectRoot, flags }) => {
20
+ const detected = await inspectDetectedProject({ projectRoot });
21
+ const destination = join(projectRoot, "rehearsal.config.mjs");
22
+ const existingPath = await findExistingConfig({
23
+ projectRoot,
24
+ configPath: flags.configPath,
25
+ });
26
+ if (existingPath) {
27
+ if (flags.write) {
28
+ throw new Error(`A Rehearsal configuration already exists at ${relative(projectRoot, existingPath)}; Rehearsal will not overwrite it.`);
29
+ }
30
+ const loaded = await loadRehearsalConfig({
31
+ projectRoot,
32
+ ...(flags.configPath === undefined
33
+ ? {}
34
+ : { configPath: flags.configPath }),
35
+ });
36
+ let source;
37
+ if (loaded.config.supabase) {
38
+ const { api, database, studio } = loaded.config.runtime.ports;
39
+ if (api === undefined || studio === undefined) {
40
+ throw new Error("The normalized Supabase configuration is missing its API or Studio port.");
41
+ }
42
+ source = renderDetectedConfig(detected, {
43
+ applicationUrl: loaded.config.runtime.applicationUrl,
44
+ ports: { api, database, studio },
45
+ });
46
+ }
47
+ else {
48
+ source = renderDetectedPostgresqlConfig(detected, {
49
+ applicationUrl: loaded.config.runtime.applicationUrl,
50
+ databasePort: loaded.config.runtime.ports.database,
51
+ });
52
+ }
53
+ const usesLegacyAuthentication = Boolean(loaded.config.supabase?.serviceEnvironmentFile ||
54
+ loaded.config.safety.authenticationProviders.length);
55
+ const availableOptions = loaded.config.supabase && !loaded.config.supabase.authentication
56
+ ? [
57
+ {
58
+ path: "supabase.authentication",
59
+ summary: usesLegacyAuthentication
60
+ ? "A declarative Google/GitHub replacement is available; replace the legacy provider fields together after review."
61
+ : "Configure local Google or GitHub sign-in with credentials kept outside tracked files.",
62
+ },
63
+ ]
64
+ : [];
65
+ return {
66
+ mode: "current-template",
67
+ destination: relative(projectRoot, existingPath),
68
+ detected,
69
+ source,
70
+ availableOptions,
71
+ nextAction: "Your existing configuration was not changed. Compare it with this installed-release template to discover new optional keys.",
72
+ };
73
+ }
74
+ const source = detected.hasSupabaseConfig
75
+ ? renderDetectedConfig(detected)
76
+ : renderDetectedPostgresqlConfig(detected);
77
+ if (flags.write) {
78
+ await writeFile(destination, source, { flag: "wx", mode: 0o600 });
79
+ }
80
+ return {
81
+ mode: flags.write ? "written" : "preview",
82
+ destination: relative(projectRoot, destination),
83
+ detected,
84
+ source,
85
+ availableOptions: [],
86
+ nextAction: flags.write
87
+ ? "Review the generated safety settings and run rehearsal doctor."
88
+ : "Review this preview, then rerun rehearsal init --write to create it.",
89
+ };
90
+ };
@@ -0,0 +1,157 @@
1
+ /** Project-scoped CLI orchestration that is shared by direct and guided commands. */
2
+ import { summarizeBaselinePreparation } from "../baseline/preparation.mjs";
3
+ import type { RehearsalConfigPathOptions } from "../project/configuration.mjs";
4
+ import type { RehearsalCleanupPlan } from "../runtime/cleanup.mjs";
5
+ import type { RehearsalCliFlags } from "./arguments.mjs";
6
+ type CleanupStackPlan = Readonly<{
7
+ digest: string;
8
+ targets: readonly Readonly<{
9
+ name: string;
10
+ configPath: string;
11
+ plan: RehearsalCleanupPlan;
12
+ }>[];
13
+ }>;
14
+ type RunManager = (input: {
15
+ action: "discard";
16
+ flags: RehearsalCliFlags;
17
+ configPath?: string;
18
+ targetName?: string;
19
+ }) => unknown;
20
+ interface ProjectCommandInput {
21
+ readonly flags: RehearsalCliFlags;
22
+ readonly planOptions: RehearsalConfigPathOptions;
23
+ }
24
+ export declare const createProjectCommands: ({ projectRoot, runManager, }: {
25
+ projectRoot: string;
26
+ runManager: RunManager;
27
+ }) => Readonly<{
28
+ loadIdentityClaimPlan: ({ flags, planOptions, }: ProjectCommandInput) => Promise<{
29
+ loaded: Readonly<{
30
+ config: Readonly<{
31
+ schemaVersion: 1;
32
+ project: Readonly<{
33
+ name: string;
34
+ }>;
35
+ supabase: Readonly<{
36
+ workdir: string;
37
+ migrationDirectory: string;
38
+ rehearsalConfig: string;
39
+ runtimeWorkdir: string;
40
+ authentication: Readonly<{
41
+ enableLocalSignup: true;
42
+ environmentFile: string;
43
+ providers: readonly Readonly<{
44
+ name: "github" | "google";
45
+ clientIdEnvironmentVariable: string;
46
+ clientSecretEnvironmentVariable: string;
47
+ skipNonceCheck: boolean;
48
+ emailOptional: boolean;
49
+ }>[];
50
+ }> | null;
51
+ serviceEnvironmentFile: string | null;
52
+ serviceEnvironmentVariables: readonly string[];
53
+ }> | null;
54
+ postgresql: Readonly<{
55
+ migrationDirectory: string;
56
+ runtimeWorkdir: string;
57
+ image: string;
58
+ database: string;
59
+ user: string;
60
+ }> | null;
61
+ baseline: Readonly<{
62
+ artifactDirectory: string;
63
+ sanitizationPolicy: string;
64
+ }>;
65
+ containerRuntime: Readonly<{
66
+ autoStartColima: boolean;
67
+ }>;
68
+ cleanup: Readonly<{
69
+ retainBaselineGenerations: number;
70
+ }>;
71
+ preparation: Readonly<{
72
+ sourcePolicy: string;
73
+ privacyKey: string;
74
+ batchRows: number;
75
+ maximumRows: number;
76
+ maximumBytes: number;
77
+ diskHeadroomBytes: number;
78
+ }> | null;
79
+ runtimePolicy: string | null;
80
+ identityPolicy: string | null;
81
+ dependentTargets: readonly Readonly<{
82
+ name: string;
83
+ configPath: string;
84
+ prepareCommand: string | null;
85
+ }>[];
86
+ application: Readonly<{
87
+ startCommand: string;
88
+ proofCommand: string;
89
+ environmentFile: string;
90
+ environmentVariables: Readonly<Record<string, string>>;
91
+ readiness: Readonly<{
92
+ url: string;
93
+ expectedStatus: number;
94
+ timeoutSeconds: number;
95
+ }> | null;
96
+ httpProofs: readonly Readonly<{
97
+ name: string;
98
+ kind: "positive" | "negative";
99
+ url: string;
100
+ method: string;
101
+ expectedStatus: number;
102
+ json: Readonly<{
103
+ path: readonly string[];
104
+ equals?: unknown;
105
+ notEquals?: unknown;
106
+ minimumItems?: number;
107
+ }> | null;
108
+ }>[];
109
+ runtimeAdapter: string | null;
110
+ }>;
111
+ runtime: Readonly<{
112
+ target: "postgresql" | "supabase";
113
+ applicationUrl: string;
114
+ projectId: string;
115
+ ports: Readonly<{
116
+ api?: number;
117
+ database: number;
118
+ studio?: number;
119
+ }>;
120
+ }>;
121
+ safety: Readonly<{
122
+ allowedHosts: readonly string[];
123
+ blockedEnvironmentVariables: readonly string[];
124
+ authenticationProviders: readonly string[];
125
+ hostedAccess: "disabled";
126
+ outboundNetwork: "deny";
127
+ }>;
128
+ verification: Readonly<{
129
+ commands: readonly string[];
130
+ }>;
131
+ }>;
132
+ configPath: string;
133
+ projectRoot: string;
134
+ paths: Readonly<{
135
+ artifactDirectory: string;
136
+ applicationEnvironment: string;
137
+ migrationDirectory: string;
138
+ rehearsalConfig: string | null;
139
+ runtimeWorkdir: string;
140
+ serviceEnvironment: string | null;
141
+ sanitizationPolicy: string;
142
+ sourcePolicy: string | null;
143
+ privacyKey: string | null;
144
+ runtimePolicy: string | null;
145
+ identityPolicy: string | null;
146
+ supabaseWorkdir: string | null;
147
+ runtimeAdapter: string | null;
148
+ }>;
149
+ }>;
150
+ plan: import("../identity/claim.mjs").IdentityClaimPlan;
151
+ }>;
152
+ planRuntimeStackCleanup: ({ flags, planOptions, }: ProjectCommandInput) => Promise<RehearsalCleanupPlan | CleanupStackPlan>;
153
+ runBaselinePreparation: ({ flags, planOptions, }: ProjectCommandInput) => Promise<ReturnType<typeof summarizeBaselinePreparation>>;
154
+ runCleanup: ({ flags, planOptions, }: ProjectCommandInput) => Promise<unknown>;
155
+ runSetup: ({ flags, planOptions, }: ProjectCommandInput) => Promise<unknown>;
156
+ }>;
157
+ export {};
@@ -0,0 +1,183 @@
1
+ /** Project-scoped CLI orchestration that is shared by direct and guided commands. */
2
+ import { createHash } from "node:crypto";
3
+ import { readFile } from "node:fs/promises";
4
+ import { applyBaselinePreparation, planBaselinePreparation, summarizeBaselinePreparation, } from "../baseline/preparation.mjs";
5
+ import { createIdentityClaimPlan, validateIdentityPolicy, } from "../identity/claim.mjs";
6
+ import { loadRehearsalConfig } from "../project/configuration.mjs";
7
+ import { applyRehearsalSetup, planRehearsalSetup, summarizeRehearsalSetup, } from "../project/setup.mjs";
8
+ import { applyRehearsalCleanup, planRehearsalCleanup, } from "../runtime/cleanup.mjs";
9
+ import { runRehearsalDoctor } from "../runtime/plan.mjs";
10
+ import { loadRuntimeTopology } from "../runtime/topology.mjs";
11
+ const isCleanupStackPlan = (plan) => "targets" in plan;
12
+ export const createProjectCommands = ({ projectRoot, runManager, }) => {
13
+ const runSetup = async ({ flags, planOptions, }) => {
14
+ const setupTarget = flags.target === "supabase" || flags.target === "postgresql"
15
+ ? flags.target
16
+ : undefined;
17
+ if (flags.target !== undefined && setupTarget === undefined) {
18
+ throw new Error("--target must be supabase or postgresql.");
19
+ }
20
+ const plan = flags.setupPlan ??
21
+ (await planRehearsalSetup({
22
+ projectRoot,
23
+ ...(planOptions.configPath === undefined
24
+ ? {}
25
+ : { configPath: planOptions.configPath }),
26
+ ...(setupTarget === undefined ? {} : { target: setupTarget }),
27
+ }));
28
+ if (flags.write)
29
+ await applyRehearsalSetup(plan);
30
+ const result = summarizeRehearsalSetup(plan, {
31
+ mode: flags.write ? "written" : "preview",
32
+ });
33
+ if (!flags.write)
34
+ return result;
35
+ const readiness = await runRehearsalDoctor(planOptions);
36
+ const needsBaselinePolicy = readiness.checks.some((check) => check.id === "sanitization-policy" && check.status === "fail");
37
+ return {
38
+ ...result,
39
+ readiness,
40
+ nextAction: [
41
+ `Review ${result.configurationPath}.`,
42
+ readiness.state === "READY"
43
+ ? "Next: run rehearsal explain to review the migration plan."
44
+ : needsBaselinePolicy
45
+ ? flags.guided
46
+ ? "Next: choose Prepare the script to create a reviewable policy draft."
47
+ : "Next: run rehearsal again and choose Prepare a reviewable baseline policy draft."
48
+ : "Next: complete the remaining readiness items shown above.",
49
+ ].join("\n"),
50
+ };
51
+ };
52
+ const runBaselinePreparation = async ({ flags, planOptions, }) => {
53
+ if (!flags.recordsPath || !flags.ledgerPath) {
54
+ throw new Error("baseline prepare requires --records and --ledger input paths.");
55
+ }
56
+ const plan = flags.preparationPlan ??
57
+ (await planBaselinePreparation({
58
+ ...planOptions,
59
+ recordsPath: flags.recordsPath,
60
+ ledgerPath: flags.ledgerPath,
61
+ }));
62
+ if (flags.write)
63
+ await applyBaselinePreparation(plan);
64
+ const result = summarizeBaselinePreparation(plan, {
65
+ mode: flags.write ? "written" : "preview",
66
+ });
67
+ return flags.guided && flags.write
68
+ ? {
69
+ ...result,
70
+ nextAction: "Next: choose Review the script to classify every column interactively.",
71
+ }
72
+ : result;
73
+ };
74
+ const planRuntimeStackCleanup = async ({ flags, planOptions, }) => {
75
+ const topology = await loadRuntimeTopology(planOptions);
76
+ const targets = [];
77
+ for (const target of topology.targets) {
78
+ targets.push({
79
+ name: target.name,
80
+ configPath: target.configPath,
81
+ plan: await planRehearsalCleanup({
82
+ projectRoot: topology.projectRoot,
83
+ configPath: target.configPath,
84
+ includeRuntime: flags.includeRuntime,
85
+ includeImages: flags.includeImages && target.primary,
86
+ }),
87
+ });
88
+ }
89
+ if (targets.length === 1)
90
+ return targets[0].plan;
91
+ return {
92
+ digest: createHash("sha256")
93
+ .update(JSON.stringify(targets.map((target) => ({
94
+ name: target.name,
95
+ digest: target.plan.digest,
96
+ }))))
97
+ .digest("hex"),
98
+ targets,
99
+ };
100
+ };
101
+ const runCleanup = async ({ flags, planOptions, }) => {
102
+ const plan = flags.cleanupPlan ??
103
+ (await planRuntimeStackCleanup({ flags, planOptions }));
104
+ if (!flags.write)
105
+ return { mode: "preview", plan };
106
+ if (isCleanupStackPlan(plan)) {
107
+ if (flags.cleanupConfirmation !== plan.digest) {
108
+ throw new Error("Cleanup requires the exact --confirm-cleanup digest from a fresh preview. Nothing was removed.");
109
+ }
110
+ for (const target of plan.targets) {
111
+ const refreshed = await planRehearsalCleanup({
112
+ projectRoot,
113
+ configPath: target.configPath,
114
+ includeRuntime: target.plan.runtime.included,
115
+ includeImages: target.plan.images.requested,
116
+ });
117
+ if (refreshed.digest !== target.plan.digest) {
118
+ throw new Error(`Cleanup targets changed for ${target.name} after the preview. Nothing was removed; preview the cleanup again.`);
119
+ }
120
+ }
121
+ const appliedTargets = [];
122
+ for (const target of plan.targets) {
123
+ appliedTargets.push({
124
+ name: target.name,
125
+ applied: await applyRehearsalCleanup({
126
+ plan: target.plan,
127
+ confirmation: target.plan.digest,
128
+ projectRoot,
129
+ configPath: target.configPath,
130
+ removeRuntime: async () => {
131
+ runManager({
132
+ action: "discard",
133
+ flags,
134
+ configPath: target.configPath,
135
+ targetName: target.name,
136
+ });
137
+ },
138
+ }),
139
+ });
140
+ }
141
+ return {
142
+ mode: "written",
143
+ plan,
144
+ applied: { targets: appliedTargets },
145
+ };
146
+ }
147
+ const applied = await applyRehearsalCleanup({
148
+ plan,
149
+ ...(flags.cleanupConfirmation === undefined
150
+ ? {}
151
+ : { confirmation: flags.cleanupConfirmation }),
152
+ ...planOptions,
153
+ removeRuntime: async () => {
154
+ runManager({ action: "discard", flags });
155
+ },
156
+ });
157
+ return { mode: "written", plan, applied };
158
+ };
159
+ const loadIdentityClaimPlan = async ({ flags, planOptions, }) => {
160
+ const loaded = await loadRehearsalConfig(planOptions);
161
+ if (!loaded.paths.identityPolicy) {
162
+ throw new Error("No config.identityPolicy is configured.");
163
+ }
164
+ if (!flags.identityName) {
165
+ throw new Error("Choose a declared identity with --identity=<name>.");
166
+ }
167
+ const policy = validateIdentityPolicy(JSON.parse(await readFile(loaded.paths.identityPolicy, "utf8")));
168
+ return {
169
+ loaded,
170
+ plan: createIdentityClaimPlan({
171
+ policy,
172
+ name: flags.identityName,
173
+ }),
174
+ };
175
+ };
176
+ return Object.freeze({
177
+ loadIdentityClaimPlan,
178
+ planRuntimeStackCleanup,
179
+ runBaselinePreparation,
180
+ runCleanup,
181
+ runSetup,
182
+ });
183
+ };
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Purpose: Run the versioned, project-configured Rehearsal developer CLI for
4
+ * initialization, readiness, planning, inspection, and verified local execution.
5
+ * Run: `npm run rehearsal -- doctor` or `npm run rehearsal -- explain`.
6
+ */
7
+ export {};