@cosmicdrift/kumiko-cli 0.389.0 → 0.390.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-cli",
3
- "version": "0.389.0",
3
+ "version": "0.390.0",
4
4
  "description": "CLI for Kumiko apps. `bunx @cosmicdrift/kumiko-cli new app <name>` scaffolds a runnable workspace; `add feature <name>` mounts a feature; `check` runs boot validation plus the guard suites with the step list derived from kumiko.json; `agent lint`, `project` and `consumer` operate on an existing app; `handbuch build` renders the HTML handbuch.",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -36,18 +36,19 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@cosmicdrift/kumiko-dev-server": "0.389.0",
40
- "@cosmicdrift/kumiko-guards": "0.389.0",
41
- "@cosmicdrift/kumiko-server-runtime": "0.389.0",
42
- "@cosmicdrift/kumiko-testing": "0.389.0",
39
+ "@cosmicdrift/kumiko-dev-server": "0.390.0",
40
+ "@cosmicdrift/kumiko-feature-ast": "0.390.0",
41
+ "@cosmicdrift/kumiko-guards": "0.390.0",
42
+ "@cosmicdrift/kumiko-server-runtime": "0.390.0",
43
+ "@cosmicdrift/kumiko-testing": "0.390.0",
43
44
  "marked": "^18.0.3",
44
45
  "zod": "^4.4.3"
45
46
  },
46
47
  "peerDependencies": {
47
48
  "sharp": "^0.35.5",
48
- "@cosmicdrift/kumiko-bundled-features": "0.389.0",
49
- "@cosmicdrift/kumiko-framework": "0.389.0",
50
- "@cosmicdrift/kumiko-renderer": "0.389.0"
49
+ "@cosmicdrift/kumiko-bundled-features": "0.390.0",
50
+ "@cosmicdrift/kumiko-framework": "0.390.0",
51
+ "@cosmicdrift/kumiko-renderer": "0.390.0"
51
52
  },
52
53
  "peerDependenciesMeta": {
53
54
  "sharp": {
@@ -64,9 +65,9 @@
64
65
  }
65
66
  },
66
67
  "devDependencies": {
67
- "@cosmicdrift/kumiko-bundled-features": "0.389.0",
68
- "@cosmicdrift/kumiko-framework": "0.389.0",
69
- "@cosmicdrift/kumiko-renderer": "0.389.0",
68
+ "@cosmicdrift/kumiko-bundled-features": "0.390.0",
69
+ "@cosmicdrift/kumiko-framework": "0.390.0",
70
+ "@cosmicdrift/kumiko-renderer": "0.390.0",
70
71
  "sharp": "^0.35.5"
71
72
  },
72
73
  "publishConfig": {
@@ -39,13 +39,13 @@ async function runCodemod(ctx: CliCommandContext): Promise<number> {
39
39
 
40
40
  const { resolveCodemodScript } = await import("@cosmicdrift/kumiko-framework/upgrade-cli");
41
41
  const scriptField = `scripts/codemod/${name}.ts`;
42
- // The project's own install wins; the framework next to this CLI is the fallback.
42
+ // The project's own install wins; the feature-ast package next to this CLI is the fallback.
43
43
  const script =
44
44
  resolveCodemodScript(ctx.cwd, scriptField) ??
45
45
  resolveCodemodScript(import.meta.dir, scriptField);
46
46
  if (script === null) {
47
47
  ctx.out.err(
48
- " Codemod script not found. Install @cosmicdrift/kumiko-framework in this project.",
48
+ " Codemod script not found. Install @cosmicdrift/kumiko-feature-ast in this project.",
49
49
  );
50
50
  return 1;
51
51
  }
@@ -62,30 +62,44 @@ export const consumerCommand: CliCommand = {
62
62
  SEARCH_CONSUMER_NAME,
63
63
  ...registry.getAllMultiStreamProjections().keys(),
64
64
  ];
65
+ const { runInSystemRowLevelSecurityScope } = await import(
66
+ "@cosmicdrift/kumiko-framework/internal/row-level-security"
67
+ );
65
68
  const { db, close } = createDbConnection({ url: databaseUrl });
66
69
 
67
70
  try {
68
71
  await createEventConsumerStateTable(db);
69
- switch (sub) {
70
- case "list":
71
- return await listConsumers(ctx, db, registeredConsumerNames, listConsumersWithState);
72
- case "status":
73
- return await showConsumerStatus(ctx, db, arg, registeredConsumerNames, getConsumerState);
74
- case "restart":
75
- return await transitionOne(ctx, "restart", arg, "restarted", (name) =>
76
- restartConsumer(db, name),
77
- );
78
- case "disable":
79
- return await transitionOne(ctx, "disable", arg, "disabled", (name) =>
80
- disableConsumer(db, name),
81
- );
82
- case "enable":
83
- return await transitionOne(ctx, "enable", arg, "enabled", (name) =>
84
- enableConsumer(db, name),
85
- );
86
- case "skip":
87
- return await skipOne(ctx, db, arg, skipPoisonEvent);
88
- }
72
+ return await runInSystemRowLevelSecurityScope(
73
+ "operator consumer tool skips poison events, which reads the events of every tenant",
74
+ async () => {
75
+ switch (sub) {
76
+ case "list":
77
+ return await listConsumers(ctx, db, registeredConsumerNames, listConsumersWithState);
78
+ case "status":
79
+ return await showConsumerStatus(
80
+ ctx,
81
+ db,
82
+ arg,
83
+ registeredConsumerNames,
84
+ getConsumerState,
85
+ );
86
+ case "restart":
87
+ return await transitionOne(ctx, "restart", arg, "restarted", (name) =>
88
+ restartConsumer(db, name),
89
+ );
90
+ case "disable":
91
+ return await transitionOne(ctx, "disable", arg, "disabled", (name) =>
92
+ disableConsumer(db, name),
93
+ );
94
+ case "enable":
95
+ return await transitionOne(ctx, "enable", arg, "enabled", (name) =>
96
+ enableConsumer(db, name),
97
+ );
98
+ case "skip":
99
+ return await skipOne(ctx, db, arg, skipPoisonEvent);
100
+ }
101
+ },
102
+ );
89
103
  } catch (e) {
90
104
  ctx.out.err("");
91
105
  ctx.out.err(` ✗ ${e instanceof Error ? e.message : String(e)}`);
@@ -223,11 +223,18 @@ export async function runBackfillPii(ctx: CliCommandContext): Promise<number> {
223
223
  crypto.configurePiiSubjectKms(wiring.kms);
224
224
  crypto.configureBlindIndexKey(wiring.blindIndexKey);
225
225
  const registry = createRegistry(config.features);
226
+ const { runInSystemRowLevelSecurityScope } = await import(
227
+ "@cosmicdrift/kumiko-framework/internal/row-level-security"
228
+ );
226
229
  const { db, close } = createDbConnection({ url: databaseUrl, maxConnections: 4 });
227
230
  try {
228
- return args.mode === "rebuild"
229
- ? await rebuildPiiProjections(ctx, db, registry, crypto)
230
- : await backfillPii(ctx, db, registry, args);
231
+ return await runInSystemRowLevelSecurityScope(
232
+ "operator PII backfill rewrites events and projections of every tenant",
233
+ () =>
234
+ args.mode === "rebuild"
235
+ ? rebuildPiiProjections(ctx, db, registry, crypto)
236
+ : backfillPii(ctx, db, registry, args),
237
+ );
231
238
  } finally {
232
239
  await close();
233
240
  }
@@ -68,6 +68,9 @@ export const waitlistMigrateOp = {
68
68
 
69
69
  const dryRun = ctx.argv.includes("--dry-run");
70
70
  const { createDbConnection } = await import("@cosmicdrift/kumiko-framework/db");
71
+ const { runInSystemRowLevelSecurityScope } = await import(
72
+ "@cosmicdrift/kumiko-framework/internal/row-level-security"
73
+ );
71
74
  const { db, close } = createDbConnection({ url: checked.databaseUrl });
72
75
  let wiring: Awaited<ReturnType<typeof wireKmsFromEnv>> | undefined;
73
76
  try {
@@ -80,12 +83,16 @@ export const waitlistMigrateOp = {
80
83
  allowPlaintextPii: flagValue(ctx.argv, "--allow-plaintext-pii"),
81
84
  consequence: "The import would store personal data as plaintext in the event log.",
82
85
  });
83
- const report = await migrateLegacyWaitlist(db, {
84
- from,
85
- dryRun,
86
- sourceTable: flagValue(ctx.argv, "--source-table") ?? DEFAULT_LEGACY_SOURCE_TABLE[from],
87
- defaultLocale: flagValue(ctx.argv, "--locale") ?? DEFAULT_IMPORT_LOCALE,
88
- });
86
+ const report = await runInSystemRowLevelSecurityScope(
87
+ "operator waitlist migration imports signups into the platform tenant and archives legacy streams",
88
+ () =>
89
+ migrateLegacyWaitlist(db, {
90
+ from,
91
+ dryRun,
92
+ sourceTable: flagValue(ctx.argv, "--source-table") ?? DEFAULT_LEGACY_SOURCE_TABLE[from],
93
+ defaultLocale: flagValue(ctx.argv, "--locale") ?? DEFAULT_IMPORT_LOCALE,
94
+ }),
95
+ );
89
96
  ctx.out.log("");
90
97
  ctx.out.log(
91
98
  ` ${dryRun ? "[dry-run] " : ""}waitlist-migrate --from ${from}: read ${report.read}, ${dryRun ? "would create" : "created"} ${report.created}, already present ${report.skipped}, unreadable ${report.unreadable}, legacy streams ${dryRun ? "would archive" : "archived"} ${report.archivedStreams}`,
@@ -67,6 +67,9 @@ export const projectCommand: CliCommand = {
67
67
  } = await import("@cosmicdrift/kumiko-framework/pipeline");
68
68
 
69
69
  const registry = createRegistry(config.features);
70
+ const { runInSystemRowLevelSecurityScope } = await import(
71
+ "@cosmicdrift/kumiko-framework/internal/row-level-security"
72
+ );
70
73
  const { db, close } = createDbConnection({ url: databaseUrl });
71
74
  let wiring: Awaited<ReturnType<typeof wireKmsFromEnv>> | undefined;
72
75
 
@@ -80,14 +83,19 @@ export const projectCommand: CliCommand = {
80
83
  });
81
84
  }
82
85
  await createProjectionStateTable(db);
83
- switch (sub) {
84
- case "list":
85
- return await listProjections(ctx, db, registry, listProjectionsWithState);
86
- case "status":
87
- return await showProjectionStatus(ctx, db, arg, registry, getProjectionState);
88
- case "rebuild":
89
- return await rebuildOne(ctx, db, arg, registry, rebuildProjection);
90
- }
86
+ return await runInSystemRowLevelSecurityScope(
87
+ "operator projection tool reads and rebuilds projections of every tenant",
88
+ async () => {
89
+ switch (sub) {
90
+ case "list":
91
+ return await listProjections(ctx, db, registry, listProjectionsWithState);
92
+ case "status":
93
+ return await showProjectionStatus(ctx, db, arg, registry, getProjectionState);
94
+ case "rebuild":
95
+ return await rebuildOne(ctx, db, arg, registry, rebuildProjection);
96
+ }
97
+ },
98
+ );
91
99
  } catch (e) {
92
100
  ctx.out.err("");
93
101
  ctx.out.err(` ✗ ${e instanceof Error ? e.message : String(e)}`);