@runa-ai/runa-cli 0.7.1 → 0.7.2

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 (136) hide show
  1. package/dist/{build-HUDIP6KU.js → build-HQMSVN6N.js} +3 -3
  2. package/dist/{check-LOMVIRHX.js → check-PCSQPYDM.js} +2 -2
  3. package/dist/{chunk-QM53IQHM.js → chunk-2QX7T24B.js} +1 -1
  4. package/dist/{chunk-XDCHRVE3.js → chunk-4XHZQRRK.js} +2 -2
  5. package/dist/{chunk-7B5C6U2K.js → chunk-A6A7JIRD.js} +35 -2
  6. package/dist/{chunk-Z4Z5DNW4.js → chunk-B3POLMII.js} +12 -0
  7. package/dist/chunk-CKRLVEIO.js +119 -0
  8. package/dist/{chunk-HD74F6W2.js → chunk-FWMGC5FP.js} +1 -0
  9. package/dist/{chunk-H2AHNI75.js → chunk-LCK2LGVR.js} +1 -1
  10. package/dist/{chunk-FHG3ILE4.js → chunk-OBYZDT2E.js} +38 -8
  11. package/dist/{chunk-AFY3TX4I.js → chunk-PMXE5XOJ.js} +1 -1
  12. package/dist/{chunk-VM3IWOT5.js → chunk-QSEF4T3Y.js} +13 -5
  13. package/dist/{chunk-NPSRD26F.js → chunk-UHDAYPHH.js} +1 -1
  14. package/dist/{chunk-2APB25TT.js → chunk-VSH3IXDQ.js} +7 -3
  15. package/dist/{chunk-644FVGIQ.js → chunk-WPMR7RQ4.js} +9 -2
  16. package/dist/{chunk-EMB6IZFT.js → chunk-XVNDDHAF.js} +20 -1
  17. package/dist/{risk-detector-plpgsql-HWKS4OLR.js → chunk-Y5ANTCKE.js} +3 -412
  18. package/dist/{chunk-CCW3PLQY.js → chunk-Z7A4BEWF.js} +1 -1
  19. package/dist/{ci-XY6IKEDC.js → ci-Z4525QW6.js} +2150 -488
  20. package/dist/{cli-UZA4RBNQ.js → cli-Q2XIQDRS.js} +72 -54
  21. package/dist/commands/ci/commands/ci-prod-db-operations.d.ts +6 -4
  22. package/dist/commands/ci/commands/ci-prod-types.d.ts +3 -0
  23. package/dist/commands/ci/commands/ci-prod-workflow.d.ts +1 -1
  24. package/dist/commands/ci/commands/ci-resolvers.d.ts +1 -1
  25. package/dist/commands/ci/commands/ci-supabase-local.d.ts +4 -0
  26. package/dist/commands/ci/machine/actors/build/build-and-playwright.d.ts +1 -1
  27. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +11 -1
  28. package/dist/commands/ci/machine/actors/db/production-preview.d.ts +22 -4
  29. package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +8 -1
  30. package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +1 -0
  31. package/dist/commands/ci/machine/actors/finalize/index.d.ts +0 -1
  32. package/dist/commands/ci/machine/actors/index.d.ts +1 -1
  33. package/dist/commands/ci/machine/actors/setup/local.d.ts +2 -0
  34. package/dist/commands/ci/machine/actors/setup/pr-common.d.ts +3 -0
  35. package/dist/commands/ci/machine/actors/setup/pr-local.d.ts +2 -0
  36. package/dist/commands/ci/machine/commands/machine-runner.d.ts +5 -1
  37. package/dist/commands/ci/machine/commands/step-telemetry.d.ts +16 -0
  38. package/dist/commands/ci/machine/contract.d.ts +40 -0
  39. package/dist/commands/ci/machine/formatters/github-comment-types.d.ts +7 -2
  40. package/dist/commands/ci/machine/formatters/github-comment.d.ts +2 -1
  41. package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts +2 -1
  42. package/dist/commands/ci/machine/formatters/sections/index.d.ts +1 -1
  43. package/dist/commands/ci/machine/formatters/summary.d.ts +4 -4
  44. package/dist/commands/ci/machine/guards.d.ts +4 -0
  45. package/dist/commands/ci/machine/helpers.d.ts +25 -0
  46. package/dist/commands/ci/machine/machine-state-helpers.d.ts +1 -1
  47. package/dist/commands/ci/machine/machine.d.ts +15 -8
  48. package/dist/commands/ci/machine/types.d.ts +9 -0
  49. package/dist/commands/ci/utils/ci-diagnostics.d.ts +67 -0
  50. package/dist/commands/ci/utils/ci-summary.d.ts +118 -0
  51. package/dist/commands/ci/utils/db-url-utils.d.ts +4 -77
  52. package/dist/commands/ci/utils/github-api.d.ts +14 -0
  53. package/dist/commands/db/apply/contract.d.ts +73 -0
  54. package/dist/commands/db/apply/helpers/alter-statement-parsers.d.ts +95 -0
  55. package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts +0 -61
  56. package/dist/commands/db/apply/helpers/function-plan-false-positive-filter.d.ts +36 -0
  57. package/dist/commands/db/apply/helpers/hazard-handler.d.ts +4 -4
  58. package/dist/commands/db/apply/helpers/index.d.ts +14 -5
  59. package/dist/commands/db/apply/helpers/partition-acl-cleaner.d.ts +3 -1
  60. package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +69 -6
  61. package/dist/commands/db/apply/helpers/plan-ast.d.ts +56 -0
  62. package/dist/commands/db/apply/helpers/plan-check-filter.d.ts +26 -0
  63. package/dist/commands/db/apply/helpers/plan-drop-protection.d.ts +43 -0
  64. package/dist/commands/db/apply/helpers/plan-ordering.d.ts +6 -0
  65. package/dist/commands/db/apply/helpers/plan-statement-parser.d.ts +39 -0
  66. package/dist/commands/db/apply/helpers/plan-validator.d.ts +8 -40
  67. package/dist/commands/db/apply/helpers/retry-logic.d.ts +1 -10
  68. package/dist/commands/db/apply/helpers/temp-db-bootstrap.d.ts +18 -0
  69. package/dist/commands/db/apply/helpers/temp-db-dsn.d.ts +14 -0
  70. package/dist/commands/db/apply/machine.d.ts +56 -32
  71. package/dist/commands/db/commands/db-apply-error.d.ts +5 -0
  72. package/dist/commands/db/commands/db-apply.d.ts +2 -0
  73. package/dist/commands/db/commands/db-sync/directory-placement-check.d.ts +4 -0
  74. package/dist/commands/db/commands/db-sync/error-classifier.d.ts +1 -1
  75. package/dist/commands/db/commands/db-sync/plan-boundary-reconciliation.d.ts +3 -0
  76. package/dist/commands/db/commands/db-sync/precheck-helpers.d.ts +18 -0
  77. package/dist/commands/db/commands/db-sync/production-precheck.d.ts +15 -0
  78. package/dist/commands/db/commands/db-sync/risk-scan-collectors.d.ts +11 -0
  79. package/dist/commands/db/commands/db-sync.d.ts +11 -5
  80. package/dist/commands/db/sync/contract.d.ts +80 -0
  81. package/dist/commands/db/sync/machine.d.ts +60 -1
  82. package/dist/commands/db/types.d.ts +5 -0
  83. package/dist/commands/db/utils/boundary-policy/rule-compiler.d.ts +2 -1
  84. package/dist/commands/db/utils/boundary-policy/types.d.ts +21 -0
  85. package/dist/commands/db/utils/boundary-policy-runtime.d.ts +12 -3
  86. package/dist/commands/db/utils/boundary-policy.d.ts +1 -1
  87. package/dist/commands/db/utils/db-target.d.ts +5 -3
  88. package/dist/commands/db/utils/declarative-dependency-collectors.d.ts +6 -0
  89. package/dist/commands/db/utils/declarative-dependency-contract.d.ts +78 -0
  90. package/dist/commands/db/utils/declarative-dependency-sql-utils.d.ts +49 -0
  91. package/dist/commands/db/utils/declarative-dependency-warning-governance.d.ts +24 -0
  92. package/dist/commands/db/utils/preflight-check.d.ts +1 -1
  93. package/dist/commands/db/utils/preflight-checks/declarative-dependency-checks.d.ts +4 -0
  94. package/dist/commands/db/utils/preflight-checks/idempotent-risk-checks.d.ts +4 -0
  95. package/dist/commands/db/utils/preflight-checks/schema-boundary-checks.d.ts +4 -0
  96. package/dist/commands/db/utils/preflight-checks/schema-risk-policy.d.ts +4 -0
  97. package/dist/commands/db/utils/preflight-checks/supabase-checks.d.ts +12 -0
  98. package/dist/commands/db/utils/psql.d.ts +23 -0
  99. package/dist/commands/db/utils/sql-table-extractor.d.ts +42 -1
  100. package/dist/commands/env/commands/setup/types.d.ts +1 -0
  101. package/dist/commands/env/constants/local-supabase.d.ts +4 -1
  102. package/dist/commands/observability.d.ts +72 -0
  103. package/dist/commands/observability.helpers.d.ts +25 -0
  104. package/dist/commands/template-check/contract.d.ts +3 -3
  105. package/dist/commands/template-check/machine.d.ts +1 -1
  106. package/dist/commands/workflow/commands/deploy-production.d.ts +0 -1
  107. package/dist/constants/versions.d.ts +1 -1
  108. package/dist/{db-Q3GF7JWP.js → db-BPQ2TEQM.js} +14361 -11076
  109. package/dist/{dev-5YXNPTCJ.js → dev-MLRKIP7F.js} +5 -5
  110. package/dist/{doctor-MZLOA53G.js → doctor-ROSWSMLH.js} +2 -2
  111. package/dist/{env-GMB3THRG.js → env-WNHJVLOT.js} +37 -20
  112. package/dist/{env-HMMRSYCI.js → env-XPPACZM4.js} +2 -2
  113. package/dist/{env-files-2UIUYLLR.js → env-files-HRNUGZ5O.js} +1 -1
  114. package/dist/{error-handler-HEXBRNVV.js → error-handler-YRQWRDEF.js} +17 -0
  115. package/dist/{hotfix-NDTPY2T4.js → hotfix-Z5EGVSMH.js} +4 -4
  116. package/dist/index.js +4 -4
  117. package/dist/{init-U4VCRHTD.js → init-S2ATHLJ6.js} +1 -1
  118. package/dist/{inject-test-attrs-P44BVTQS.js → inject-test-attrs-XN4I2AOR.js} +2 -2
  119. package/dist/internal/machines/index.d.ts +1 -1
  120. package/dist/internal/machines/snapshot-helpers.d.ts +6 -0
  121. package/dist/{manifest-TMFLESHW.js → manifest-EGCAZ4TK.js} +1 -1
  122. package/dist/observability-CJA5UFIC.js +721 -0
  123. package/dist/{risk-detector-4U6ZJ2G5.js → risk-detector-VO5HJR4R.js} +1 -1
  124. package/dist/{risk-detector-core-TK4OAI3N.js → risk-detector-core-7WZJZ5ZI.js} +61 -3
  125. package/dist/risk-detector-plpgsql-ULV7NLDB.js +638 -0
  126. package/dist/{template-check-FFJVDLBF.js → template-check-BDFMT6ZO.js} +1 -1
  127. package/dist/{upgrade-7TWORWBV.js → upgrade-BDUWBRT5.js} +1 -1
  128. package/dist/utils/db-url-utils.d.ts +81 -0
  129. package/dist/validators/risk-detector-plpgsql.d.ts +3 -1
  130. package/dist/{vuln-check-6CMNPSBR.js → vuln-check-66RXX3TO.js} +1 -1
  131. package/dist/{vuln-checker-EJJTNDNE.js → vuln-checker-FFOGOJPT.js} +1 -1
  132. package/dist/{watch-PNTKZYFB.js → watch-ITYW57SL.js} +1 -1
  133. package/dist/{workflow-H75N4BXX.js → workflow-UZIZ2JUS.js} +2 -3
  134. package/package.json +3 -3
  135. package/dist/chunk-AKZAN4BC.js +0 -90
  136. package/dist/commands/ci/machine/actors/finalize/summary.d.ts +0 -32
@@ -0,0 +1,721 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';
3
+ import { emitJsonSuccess } from './chunk-KE6QJBZG.js';
4
+ import './chunk-WJXC4MVY.js';
5
+ import { getOutputFormatFromEnv } from './chunk-HKUWEGUX.js';
6
+ import { loadRunaConfig } from './chunk-5NKWR4FF.js';
7
+ import './chunk-JMJP4A47.js';
8
+ import { init_esm_shims } from './chunk-VRXHCR5K.js';
9
+ import { createObservabilityClient, ObservabilityIngestOutputSchema } from '@runa-ai/runa/observability';
10
+ import { createCLILogger, CLIError } from '@runa-ai/runa/cli-runtime';
11
+ import { Command } from 'commander';
12
+ import { basename, join } from 'path';
13
+ import { z } from 'zod';
14
+ import { execa } from 'execa';
15
+ import { createHash } from 'crypto';
16
+ import { mkdirSync, writeFileSync, existsSync, readFileSync, readdirSync } from 'fs';
17
+
18
+ createRequire(import.meta.url);
19
+
20
+ // src/commands/observability.ts
21
+ init_esm_shims();
22
+
23
+ // src/cli/exec.ts
24
+ init_esm_shims();
25
+ function pipeChildToStderr(child) {
26
+ if (child.stdout) {
27
+ child.stdout.on("data", (chunk) => {
28
+ process.stderr.write(chunk);
29
+ });
30
+ }
31
+ if (child.stderr) {
32
+ child.stderr.on("data", (chunk) => {
33
+ process.stderr.write(chunk);
34
+ });
35
+ }
36
+ }
37
+ async function runExeca(file, args, options = {}) {
38
+ const isJson = getOutputFormatFromEnv() === "json";
39
+ const env = { DOTENVX_QUIET: "1", ...options.env };
40
+ if (!isJson) {
41
+ await execa(file, args, { ...options, env });
42
+ return;
43
+ }
44
+ const child = execa(file, args, {
45
+ ...options,
46
+ env,
47
+ stdio: ["ignore", "pipe", "pipe"]
48
+ });
49
+ pipeChildToStderr(child);
50
+ await child;
51
+ }
52
+
53
+ // src/commands/observability.helpers.ts
54
+ init_esm_shims();
55
+ var OBSERVABILITY_SECRET_SYNC_MARKER_PATH = "supabase/.temp/observability-function-secret-sync.json";
56
+ function detectSupabaseProjectLink(targetDir) {
57
+ return resolveSupabaseProjectRef(targetDir) !== null;
58
+ }
59
+ function fileExists(targetDir, relativePath) {
60
+ return existsSync(join(targetDir, relativePath));
61
+ }
62
+ function readTextFile(targetDir, relativePath) {
63
+ const filePath = join(targetDir, relativePath);
64
+ if (!existsSync(filePath)) {
65
+ return null;
66
+ }
67
+ return readFileSync(filePath, "utf-8");
68
+ }
69
+ function readJsonFile(targetDir, relativePath) {
70
+ const raw = readTextFile(targetDir, relativePath);
71
+ if (!raw) {
72
+ return null;
73
+ }
74
+ try {
75
+ return JSON.parse(raw);
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+ function resolveSupabaseProjectRef(targetDir) {
81
+ const fromEnv = process.env.SUPABASE_PROJECT_REF?.trim();
82
+ if (fromEnv) {
83
+ return fromEnv;
84
+ }
85
+ const candidates = [".supabase/project-ref", "supabase/.temp/project-ref"];
86
+ for (const candidate of candidates) {
87
+ const value = readTextFile(targetDir, candidate)?.trim();
88
+ if (value) {
89
+ return value;
90
+ }
91
+ }
92
+ return null;
93
+ }
94
+ function hashObservabilityFunctionSecret(secret) {
95
+ return createHash("sha256").update(secret).digest("hex");
96
+ }
97
+ function readObservabilityFunctionSecretSyncMarker(targetDir) {
98
+ const marker = readJsonFile(
99
+ targetDir,
100
+ OBSERVABILITY_SECRET_SYNC_MARKER_PATH
101
+ );
102
+ if (!marker || marker.version !== 1) {
103
+ return null;
104
+ }
105
+ if (!marker.projectRef || !marker.secretHash || !marker.syncedAt) {
106
+ return null;
107
+ }
108
+ return marker;
109
+ }
110
+ function writeObservabilityFunctionSecretSyncMarker(targetDir, input) {
111
+ const markerPath = join(targetDir, OBSERVABILITY_SECRET_SYNC_MARKER_PATH);
112
+ mkdirSync(join(targetDir, "supabase/.temp"), { recursive: true });
113
+ const marker = {
114
+ version: 1,
115
+ projectRef: input.projectRef,
116
+ secretHash: hashObservabilityFunctionSecret(input.secret),
117
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
118
+ };
119
+ writeFileSync(markerPath, `${JSON.stringify(marker, null, 2)}
120
+ `, "utf-8");
121
+ }
122
+ function collectRouteFiles(rootDir) {
123
+ if (!existsSync(rootDir)) {
124
+ return [];
125
+ }
126
+ const entries = readdirSync(rootDir, { withFileTypes: true });
127
+ const routeFiles = [];
128
+ for (const entry of entries) {
129
+ const absolutePath = join(rootDir, entry.name);
130
+ if (entry.isDirectory()) {
131
+ routeFiles.push(...collectRouteFiles(absolutePath));
132
+ continue;
133
+ }
134
+ if (entry.isFile() && entry.name === "route.ts") {
135
+ routeFiles.push(absolutePath);
136
+ }
137
+ }
138
+ return routeFiles;
139
+ }
140
+ function createStatusItem(name, status, message) {
141
+ return { name, status, message };
142
+ }
143
+ var BLOCKING_WARNING_CHECK_NAMES = /* @__PURE__ */ new Set([
144
+ "runtime-surface",
145
+ "route-instrumentation",
146
+ "function-deploy",
147
+ "function-secret",
148
+ "scope-fallback"
149
+ ]);
150
+ function resolveObservabilityTenantMode(targetDir) {
151
+ try {
152
+ const config = loadRunaConfig(targetDir);
153
+ const tenantMode2 = config.observability?.tenantMode;
154
+ if (tenantMode2 === "single-tenant" || tenantMode2 === "multi-tenant") {
155
+ return tenantMode2;
156
+ }
157
+ } catch {
158
+ }
159
+ const runaConfig = readTextFile(targetDir, "runa.config.ts");
160
+ if (!runaConfig) {
161
+ return "auto";
162
+ }
163
+ const tenantModeMatch = runaConfig.match(
164
+ /tenantMode\s*:\s*['"](auto|single-tenant|multi-tenant)['"]/
165
+ );
166
+ const tenantMode = tenantModeMatch?.[1];
167
+ if (tenantMode === "single-tenant" || tenantMode === "multi-tenant") {
168
+ return tenantMode;
169
+ }
170
+ const directTenantModeConstantMatch = runaConfig.match(
171
+ /(?:const|let|var)\s+tenantMode\s*=\s*['"](auto|single-tenant|multi-tenant)['"]/
172
+ );
173
+ const directTenantModeConstant = directTenantModeConstantMatch?.[1];
174
+ if (directTenantModeConstant === "single-tenant" || directTenantModeConstant === "multi-tenant") {
175
+ return directTenantModeConstant;
176
+ }
177
+ const tenantModeReferenceMatch = runaConfig.match(/tenantMode\s*:\s*([A-Za-z_$][A-Za-z0-9_$]*)/);
178
+ const tenantModeReference = tenantModeReferenceMatch?.[1];
179
+ if (tenantModeReference) {
180
+ const tenantModeConstantMatch = runaConfig.match(
181
+ new RegExp(
182
+ String.raw`(?:const|let|var)\s+${tenantModeReference}\s*=\s*['"](auto|single-tenant|multi-tenant)['"]`
183
+ )
184
+ );
185
+ const referencedTenantMode = tenantModeConstantMatch?.[1];
186
+ if (referencedTenantMode === "single-tenant" || referencedTenantMode === "multi-tenant") {
187
+ return referencedTenantMode;
188
+ }
189
+ }
190
+ return "auto";
191
+ }
192
+ function createRuntimeStatusItem(targetDir) {
193
+ const appRoot = "apps/web";
194
+ const hasAppRoot = fileExists(targetDir, appRoot);
195
+ const hasIngestHelper = fileExists(targetDir, "apps/web/lib/observability/ingest.ts");
196
+ const hasWrappersHelper = fileExists(targetDir, "apps/web/lib/observability/wrappers.ts");
197
+ if (!hasAppRoot) {
198
+ return createStatusItem(
199
+ "runtime-surface",
200
+ "ok",
201
+ "Headless observability is ready; an application UI surface is optional"
202
+ );
203
+ }
204
+ if (hasIngestHelper && hasWrappersHelper) {
205
+ return createStatusItem(
206
+ "runtime-surface",
207
+ "ok",
208
+ "Application runtime helpers for observability are present"
209
+ );
210
+ }
211
+ return createStatusItem(
212
+ "runtime-surface",
213
+ "warning",
214
+ "apps/web is present but observability runtime helpers were not detected"
215
+ );
216
+ }
217
+ function createTenantModeStatusItem(targetDir) {
218
+ const tenantMode = resolveObservabilityTenantMode(targetDir);
219
+ if (tenantMode === "single-tenant") {
220
+ return createStatusItem(
221
+ "tenant-mode",
222
+ "ok",
223
+ "Observability tenant mode is single-tenant; project-scoped fallback is expected"
224
+ );
225
+ }
226
+ if (tenantMode === "multi-tenant") {
227
+ return createStatusItem(
228
+ "tenant-mode",
229
+ "ok",
230
+ "Observability tenant mode is multi-tenant; tenant-aware scope resolution is required for background jobs"
231
+ );
232
+ }
233
+ return createStatusItem(
234
+ "tenant-mode",
235
+ "ok",
236
+ "Observability tenant mode is auto; authenticated requests use user-scoped fallback and background jobs may still require a scopeResolver"
237
+ );
238
+ }
239
+ function createRouteInstrumentationStatusItem(targetDir) {
240
+ const appRouteRoot = join(targetDir, "apps/web/app");
241
+ const routeFiles = collectRouteFiles(appRouteRoot);
242
+ if (routeFiles.length === 0) {
243
+ return createStatusItem(
244
+ "route-instrumentation",
245
+ "ok",
246
+ "No Next.js route handlers were detected in apps/web/app"
247
+ );
248
+ }
249
+ const unobservedRoutes = routeFiles.filter((routeFile) => {
250
+ const content = readFileSync(routeFile, "utf-8");
251
+ return !content.includes("withObservedRoute(");
252
+ });
253
+ if (unobservedRoutes.length === 0) {
254
+ return createStatusItem(
255
+ "route-instrumentation",
256
+ "ok",
257
+ `All detected route handlers use withObservedRoute (${routeFiles.length}/${routeFiles.length})`
258
+ );
259
+ }
260
+ return createStatusItem(
261
+ "route-instrumentation",
262
+ "warning",
263
+ `${unobservedRoutes.length}/${routeFiles.length} route handler(s) are not wrapped with withObservedRoute`
264
+ );
265
+ }
266
+ function createFunctionDeployStatusItem(targetDir) {
267
+ if (detectSupabaseProjectLink(targetDir)) {
268
+ return createStatusItem(
269
+ "function-deploy",
270
+ "ok",
271
+ "Supabase project link was detected for observability function deployment"
272
+ );
273
+ }
274
+ return createStatusItem(
275
+ "function-deploy",
276
+ "warning",
277
+ "No Supabase project link was detected; function deploy will require SUPABASE_PROJECT_REF + SUPABASE_ACCESS_TOKEN or supabase link"
278
+ );
279
+ }
280
+ function createFunctionSecretStatusItem(targetDir) {
281
+ const explicitFunctionSecret = process.env.OBSERVABILITY_FUNCTION_SECRET?.trim();
282
+ const serviceRoleSecret = process.env.SUPABASE_SERVICE_ROLE_KEY?.trim();
283
+ if (serviceRoleSecret) {
284
+ return createStatusItem(
285
+ "function-secret",
286
+ "ok",
287
+ "Supabase service-role key is available in the current environment and can authorize observability functions"
288
+ );
289
+ }
290
+ if (explicitFunctionSecret) {
291
+ const projectRef = resolveSupabaseProjectRef(targetDir);
292
+ if (!projectRef) {
293
+ return createStatusItem(
294
+ "function-secret",
295
+ "ok",
296
+ "Observability function secret is available in the current environment"
297
+ );
298
+ }
299
+ const marker = readObservabilityFunctionSecretSyncMarker(targetDir);
300
+ const expectedHash = hashObservabilityFunctionSecret(explicitFunctionSecret);
301
+ if (marker && marker.projectRef === projectRef && marker.secretHash === expectedHash) {
302
+ return createStatusItem(
303
+ "function-secret",
304
+ "ok",
305
+ `Observability function secret was synced for project ${projectRef}`
306
+ );
307
+ }
308
+ return createStatusItem(
309
+ "function-secret",
310
+ "warning",
311
+ "OBSERVABILITY_FUNCTION_SECRET is present locally but remote sync is not verified; run `runa observability enable` to sync it to Supabase Functions"
312
+ );
313
+ }
314
+ const hasAppRoot = fileExists(targetDir, "apps/web");
315
+ return createStatusItem(
316
+ "function-secret",
317
+ "warning",
318
+ hasAppRoot ? "No observability function secret was detected in the current environment; runtime emits will fail until OBSERVABILITY_FUNCTION_SECRET or SUPABASE_SERVICE_ROLE_KEY is set" : "No observability function secret was detected in the current environment; CLI emit/dispatch will fail until OBSERVABILITY_FUNCTION_SECRET or SUPABASE_SERVICE_ROLE_KEY is set"
319
+ );
320
+ }
321
+ function createScopeStatusItem(targetDir) {
322
+ const publicSchema = readTextFile(targetDir, "supabase/schemas/declarative/00_public.sql");
323
+ const observabilitySchema = readTextFile(
324
+ targetDir,
325
+ "supabase/schemas/declarative/85_observability.sql"
326
+ );
327
+ const tenantMode = resolveObservabilityTenantMode(targetDir);
328
+ const hasScopeResolver = publicSchema?.includes(
329
+ "CREATE OR REPLACE FUNCTION public.runa_get_scope_id()"
330
+ );
331
+ const hasAuthUidResolver = publicSchema?.includes(
332
+ "CREATE OR REPLACE FUNCTION public.runa_auth_uid()"
333
+ );
334
+ const usesProjectGlobalFallback = observabilitySchema?.includes("'project:global'") ?? false;
335
+ const usesUserFallback = observabilitySchema?.includes("'user:' ||") ?? false;
336
+ if (tenantMode === "single-tenant") {
337
+ return createStatusItem(
338
+ "scope-fallback",
339
+ "ok",
340
+ "Single-tenant mode is declared in runa.config.ts; project-scoped observability fallback is acceptable"
341
+ );
342
+ }
343
+ if (!hasScopeResolver) {
344
+ return createStatusItem(
345
+ "scope-fallback",
346
+ tenantMode === "multi-tenant" ? "error" : "warning",
347
+ tenantMode === "multi-tenant" ? "Multi-tenant mode is declared but no runa_get_scope_id() helper was detected; add a scope_id claim resolver before enabling observability" : "No runa_get_scope_id() helper was detected; add a scope_id claim resolver or treat the repo as intentionally single-tenant"
348
+ );
349
+ }
350
+ if (hasAuthUidResolver && usesUserFallback) {
351
+ return createStatusItem(
352
+ "scope-fallback",
353
+ "ok",
354
+ "Observability falls back to user-scoped visibility when scope_id is absent"
355
+ );
356
+ }
357
+ if (usesProjectGlobalFallback) {
358
+ return createStatusItem(
359
+ "scope-fallback",
360
+ tenantMode === "multi-tenant" ? "error" : "warning",
361
+ tenantMode === "multi-tenant" ? "Multi-tenant mode is declared but observability still falls back to a project-global scope; add scope_id claims or a worker scopeResolver for tenant-aware jobs" : "When scope_id is absent in the JWT, observability falls back to a project-global scope; add scope_id claims or a worker scopeResolver for tenant-aware jobs"
362
+ );
363
+ }
364
+ return createStatusItem(
365
+ "scope-fallback",
366
+ "ok",
367
+ "Observability scope resolution does not rely on the project-global fallback"
368
+ );
369
+ }
370
+ function buildObservabilityChecks(targetDir) {
371
+ const schemaFile = "supabase/schemas/declarative/85_observability.sql";
372
+ const configToml = readTextFile(targetDir, "supabase/config.toml");
373
+ const extensionsSql = readTextFile(targetDir, "supabase/schemas/idempotent/00_extensions.sql");
374
+ const checks = [
375
+ createStatusItem(
376
+ "schema",
377
+ fileExists(targetDir, schemaFile) ? "ok" : "error",
378
+ fileExists(targetDir, schemaFile) ? "Observability schema file is present" : "Missing supabase/schemas/declarative/85_observability.sql"
379
+ ),
380
+ createStatusItem(
381
+ "config",
382
+ configToml?.includes('"observability"') ? "ok" : "error",
383
+ configToml?.includes('"observability"') ? "Supabase API exposes the observability schema" : "supabase/config.toml does not expose the observability schema"
384
+ ),
385
+ createStatusItem(
386
+ "extensions",
387
+ extensionsSql?.includes("pg_cron") ? "ok" : "warning",
388
+ extensionsSql?.includes("pg_cron") ? "pg_cron bootstrap is configured" : "pg_cron bootstrap was not detected in 00_extensions.sql"
389
+ )
390
+ ];
391
+ for (const functionName of [
392
+ "observability-ingest",
393
+ "observability-checkin",
394
+ "observability-dispatch"
395
+ ]) {
396
+ checks.push(
397
+ createStatusItem(
398
+ functionName,
399
+ fileExists(targetDir, `supabase/functions/${functionName}/index.ts`) ? "ok" : "error",
400
+ fileExists(targetDir, `supabase/functions/${functionName}/index.ts`) ? `${functionName} Edge Function is present` : `Missing supabase/functions/${functionName}/index.ts`
401
+ )
402
+ );
403
+ }
404
+ checks.push(createRuntimeStatusItem(targetDir));
405
+ checks.push(createTenantModeStatusItem(targetDir));
406
+ checks.push(createRouteInstrumentationStatusItem(targetDir));
407
+ checks.push(createFunctionDeployStatusItem(targetDir));
408
+ checks.push(createFunctionSecretStatusItem(targetDir));
409
+ checks.push(createScopeStatusItem(targetDir));
410
+ return checks;
411
+ }
412
+ function isBlockingObservabilityCheck(check) {
413
+ if (check.status === "error") {
414
+ return true;
415
+ }
416
+ return check.status === "warning" && BLOCKING_WARNING_CHECK_NAMES.has(check.name);
417
+ }
418
+ function getObservabilityEnableBlockingChecks(targetDir, checks) {
419
+ const linkedProjectDetected = detectSupabaseProjectLink(targetDir);
420
+ const explicitFunctionSecret = process.env.OBSERVABILITY_FUNCTION_SECRET?.trim();
421
+ const projectRef = resolveSupabaseProjectRef(targetDir);
422
+ return checks.filter((check) => {
423
+ if (check.name === "function-secret" && check.status === "warning" && linkedProjectDetected && explicitFunctionSecret && projectRef) {
424
+ return false;
425
+ }
426
+ return isBlockingObservabilityCheck(check);
427
+ });
428
+ }
429
+ function isObservabilityReady(checks) {
430
+ return checks.every((check) => !isBlockingObservabilityCheck(check));
431
+ }
432
+
433
+ // src/commands/observability.ts
434
+ var logger = createCLILogger("observability");
435
+ var ObservabilityStatusInputSchema = z.object({
436
+ cwd: z.string().min(1).optional()
437
+ }).strict();
438
+ var ObservabilityStatusItemSchema = z.object({
439
+ name: z.string(),
440
+ status: z.enum(["ok", "warning", "error"]),
441
+ message: z.string()
442
+ });
443
+ var ObservabilityStatusOutputSchema = z.object({
444
+ ready: z.boolean(),
445
+ targetDir: z.string(),
446
+ checks: z.array(ObservabilityStatusItemSchema)
447
+ });
448
+ var ObservabilityEnableInputSchema = z.object({
449
+ cwd: z.string().min(1).optional(),
450
+ check: z.boolean().optional(),
451
+ skipFunctionDeploy: z.boolean().optional()
452
+ }).strict();
453
+ var ObservabilityEnableOutputSchema = z.object({
454
+ targetDir: z.string(),
455
+ checks: z.array(ObservabilityStatusItemSchema),
456
+ dbSyncApplied: z.boolean(),
457
+ functionSecretSynced: z.boolean(),
458
+ functionsDeployed: z.array(z.string()),
459
+ skippedFunctions: z.array(z.string()),
460
+ linkedProjectDetected: z.boolean()
461
+ });
462
+ var ObservabilityEmitTestEventInputSchema = z.object({
463
+ baseUrl: z.string().url().optional(),
464
+ accessToken: z.string().min(1).optional(),
465
+ functionSecret: z.string().min(1).optional(),
466
+ scopeKey: z.string().min(1).optional(),
467
+ serviceName: z.string().min(1).optional(),
468
+ environment: z.string().min(1).optional()
469
+ }).strict();
470
+ var ObservabilityEmitTestEventOutputSchema = z.object({
471
+ eventId: z.string().uuid(),
472
+ issueId: z.string().uuid().nullable(),
473
+ resolvedScopeKey: z.string().min(1)
474
+ });
475
+ var ObservabilityDispatchInputSchema = z.object({
476
+ baseUrl: z.string().url().optional(),
477
+ accessToken: z.string().min(1).optional(),
478
+ functionSecret: z.string().min(1).optional(),
479
+ limit: z.coerce.number().int().positive().max(100).optional()
480
+ }).strict();
481
+ var ObservabilityDispatchOutputSchema = z.object({
482
+ processed: z.number().int().nonnegative(),
483
+ delivered: z.number().int().nonnegative(),
484
+ failed: z.number().int().nonnegative()
485
+ });
486
+ function resolveBaseUrl(explicitValue) {
487
+ return explicitValue || process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.LOCAL_SUPABASE_URL;
488
+ }
489
+ function resolveAccessToken(explicitValue) {
490
+ return explicitValue || process.env.SUPABASE_ANON_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY;
491
+ }
492
+ function resolveFunctionSecret(explicitValue) {
493
+ return explicitValue || process.env.OBSERVABILITY_FUNCTION_SECRET || process.env.SUPABASE_SERVICE_ROLE_KEY;
494
+ }
495
+ function resolveRequestTimeoutMs() {
496
+ const raw = process.env.OBSERVABILITY_TIMEOUT_MS;
497
+ const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
498
+ if (Number.isFinite(parsed) && parsed > 0) {
499
+ return parsed;
500
+ }
501
+ return 1500;
502
+ }
503
+ function printStatusChecks(checks) {
504
+ if (getOutputFormatFromEnv() === "json") {
505
+ return;
506
+ }
507
+ logger.section("Observability Status");
508
+ for (const check of checks) {
509
+ const icon = check.status === "ok" ? "\u2713" : check.status === "warning" ? "\u26A0" : "\u2717";
510
+ logger.info(` ${icon} ${check.name.padEnd(24)} ${check.message}`);
511
+ }
512
+ }
513
+ async function runObservabilityStatus(command, options) {
514
+ const targetDir = options.cwd || process.cwd();
515
+ const checks = buildObservabilityChecks(targetDir);
516
+ const output = {
517
+ ready: isObservabilityReady(checks),
518
+ targetDir,
519
+ checks
520
+ };
521
+ printStatusChecks(checks);
522
+ emitJsonSuccess(command, ObservabilityStatusOutputSchema, output);
523
+ if (!output.ready) {
524
+ process.exitCode = 1;
525
+ if (getOutputFormatFromEnv() !== "json") {
526
+ const blockingMessages = checks.filter(isBlockingObservabilityCheck).map((check) => check.message);
527
+ throw new CLIError(
528
+ "Observability readiness check failed",
529
+ "OBSERVABILITY_STATUS_FAILED",
530
+ blockingMessages
531
+ );
532
+ }
533
+ }
534
+ }
535
+ async function runObservabilityEnable(command, options) {
536
+ const targetDir = options.cwd || process.cwd();
537
+ const checks = buildObservabilityChecks(targetDir);
538
+ const blockingChecks = getObservabilityEnableBlockingChecks(targetDir, checks);
539
+ const linkedProjectDetected = detectSupabaseProjectLink(targetDir);
540
+ const functions = ["observability-ingest", "observability-checkin", "observability-dispatch"];
541
+ printStatusChecks(checks);
542
+ if (blockingChecks.length > 0) {
543
+ throw new CLIError(
544
+ "Observability enable prerequisites are not satisfied",
545
+ "OBSERVABILITY_ENABLE_BLOCKED",
546
+ blockingChecks.map((check) => check.message)
547
+ );
548
+ }
549
+ const output = {
550
+ targetDir,
551
+ checks,
552
+ dbSyncApplied: false,
553
+ functionSecretSynced: false,
554
+ functionsDeployed: [],
555
+ skippedFunctions: [],
556
+ linkedProjectDetected
557
+ };
558
+ if (options.check) {
559
+ emitJsonSuccess(command, ObservabilityEnableOutputSchema, output);
560
+ return;
561
+ }
562
+ logger.section("Observability Enable");
563
+ logger.info("Applying observability schema via runa db sync...");
564
+ await runExeca("runa", ["db", "sync", "--auto-approve"], {
565
+ cwd: targetDir,
566
+ stdio: "inherit"
567
+ });
568
+ output.dbSyncApplied = true;
569
+ const explicitFunctionSecret = process.env.OBSERVABILITY_FUNCTION_SECRET?.trim();
570
+ const projectRef = resolveSupabaseProjectRef(targetDir);
571
+ if (linkedProjectDetected && explicitFunctionSecret && projectRef) {
572
+ logger.info(`Syncing OBSERVABILITY_FUNCTION_SECRET to Supabase project ${projectRef}...`);
573
+ await runExeca(
574
+ "supabase",
575
+ ["secrets", "set", `OBSERVABILITY_FUNCTION_SECRET=${explicitFunctionSecret}`],
576
+ {
577
+ cwd: targetDir,
578
+ stdio: "inherit"
579
+ }
580
+ );
581
+ writeObservabilityFunctionSecretSyncMarker(targetDir, {
582
+ projectRef,
583
+ secret: explicitFunctionSecret
584
+ });
585
+ output.functionSecretSynced = true;
586
+ }
587
+ if (options.skipFunctionDeploy) {
588
+ output.skippedFunctions = functions;
589
+ } else if (!linkedProjectDetected) {
590
+ output.skippedFunctions = functions;
591
+ logger.warn("Supabase project link was not detected; skipping Edge Function deploy");
592
+ logger.info("Link the project and run:");
593
+ for (const functionName of functions) {
594
+ logger.info(` supabase functions deploy ${functionName}`);
595
+ }
596
+ } else {
597
+ logger.info("Deploying observability Edge Functions...");
598
+ for (const functionName of functions) {
599
+ await runExeca("supabase", ["functions", "deploy", functionName], {
600
+ cwd: targetDir,
601
+ stdio: "inherit"
602
+ });
603
+ output.functionsDeployed.push(functionName);
604
+ }
605
+ }
606
+ if (getOutputFormatFromEnv() !== "json") {
607
+ logger.success("Observability enable completed");
608
+ }
609
+ emitJsonSuccess(command, ObservabilityEnableOutputSchema, output);
610
+ }
611
+ async function runObservabilityEmitTestEvent(command, options) {
612
+ const baseUrl = resolveBaseUrl(options.baseUrl);
613
+ const accessToken = resolveAccessToken(options.accessToken);
614
+ const functionSecret = resolveFunctionSecret(options.functionSecret);
615
+ if (!baseUrl || !accessToken || !functionSecret) {
616
+ throw new CLIError(
617
+ "Observability emit requires Supabase URL, access token, and function secret",
618
+ "OBSERVABILITY_EMIT_CONFIG_MISSING",
619
+ [
620
+ "Provide --base-url, --access-token, and --function-secret",
621
+ "Or set SUPABASE_URL/NEXT_PUBLIC_SUPABASE_URL, SUPABASE_ANON_KEY/NEXT_PUBLIC_SUPABASE_ANON_KEY, and OBSERVABILITY_FUNCTION_SECRET/SUPABASE_SERVICE_ROLE_KEY"
622
+ ]
623
+ );
624
+ }
625
+ const client = createObservabilityClient({
626
+ baseUrl,
627
+ accessToken,
628
+ functionSecret,
629
+ timeoutMs: resolveRequestTimeoutMs()
630
+ });
631
+ const output = await client.captureEvent({
632
+ kind: "log",
633
+ category: "diagnostic",
634
+ level: "warn",
635
+ message: "runa observability emit-test-event",
636
+ scopeKey: options.scopeKey,
637
+ serviceName: options.serviceName || basename(process.cwd()) || "runa-cli",
638
+ environment: options.environment || process.env.VERCEL_ENV || process.env.NODE_ENV || "development",
639
+ source: "cli",
640
+ payload: {
641
+ command: "runa observability emit-test-event",
642
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
643
+ }
644
+ });
645
+ if (getOutputFormatFromEnv() !== "json") {
646
+ logger.section("Observability Emit");
647
+ logger.success(`Emitted test event ${output.eventId} (${output.resolvedScopeKey})`);
648
+ }
649
+ emitJsonSuccess(
650
+ command,
651
+ ObservabilityEmitTestEventOutputSchema,
652
+ ObservabilityIngestOutputSchema.parse(output)
653
+ );
654
+ }
655
+ async function runObservabilityDispatch(command, options) {
656
+ const baseUrl = resolveBaseUrl(options.baseUrl);
657
+ const accessToken = resolveAccessToken(options.accessToken);
658
+ const functionSecret = resolveFunctionSecret(options.functionSecret);
659
+ if (!baseUrl || !accessToken || !functionSecret) {
660
+ throw new CLIError(
661
+ "Observability dispatch requires Supabase URL, access token, and function secret",
662
+ "OBSERVABILITY_DISPATCH_CONFIG_MISSING",
663
+ [
664
+ "Provide --base-url, --access-token, and --function-secret",
665
+ "Or set SUPABASE_URL/NEXT_PUBLIC_SUPABASE_URL, SUPABASE_ANON_KEY/NEXT_PUBLIC_SUPABASE_ANON_KEY, and OBSERVABILITY_FUNCTION_SECRET/SUPABASE_SERVICE_ROLE_KEY"
666
+ ]
667
+ );
668
+ }
669
+ const response = await fetch(
670
+ `${baseUrl.replace(/\/+$/, "")}/functions/v1/observability-dispatch`,
671
+ {
672
+ method: "POST",
673
+ headers: {
674
+ Authorization: `Bearer ${accessToken}`,
675
+ apikey: accessToken,
676
+ "Content-Type": "application/json",
677
+ "x-observability-secret": functionSecret
678
+ },
679
+ body: JSON.stringify({
680
+ limit: Number(options.limit || "25")
681
+ }),
682
+ signal: AbortSignal.timeout(resolveRequestTimeoutMs())
683
+ }
684
+ );
685
+ if (!response.ok) {
686
+ throw new CLIError(
687
+ `Observability dispatch failed: ${response.status} ${response.statusText}`,
688
+ "OBSERVABILITY_DISPATCH_FAILED",
689
+ ["Verify the observability-dispatch Edge Function is deployed"]
690
+ );
691
+ }
692
+ const output = ObservabilityDispatchOutputSchema.parse(await response.json());
693
+ if (getOutputFormatFromEnv() !== "json") {
694
+ logger.section("Observability Dispatch");
695
+ logger.success(
696
+ `Processed ${output.processed} notification(s), delivered ${output.delivered}, failed ${output.failed}`
697
+ );
698
+ }
699
+ emitJsonSuccess(command, ObservabilityDispatchOutputSchema, output);
700
+ }
701
+ var observabilityStatusCommand = new Command("status").description("Check observability readiness for the current project").option("--cwd <path>", "Working directory (default: current directory)").action(
702
+ async (options) => runObservabilityStatus(observabilityStatusCommand, options)
703
+ );
704
+ var observabilityCheckCommand = new Command("check").description("Alias for `runa observability status`").option("--cwd <path>", "Working directory (default: current directory)").action(
705
+ async (options) => runObservabilityStatus(observabilityCheckCommand, options)
706
+ );
707
+ var observabilityDoctorCommand = new Command("doctor").description("Deprecated alias for `runa observability status`").option("--cwd <path>", "Working directory (default: current directory)").action(
708
+ async (options) => runObservabilityStatus(observabilityDoctorCommand, options)
709
+ );
710
+ var observabilityEnableCommand = new Command("enable").description("Apply observability schema and deploy observability Edge Functions when possible").option("--cwd <path>", "Working directory (default: current directory)").option("--check", "Validate prerequisites without mutating the project").option("--skip-function-deploy", "Skip Supabase Edge Function deployment").action(
711
+ async (options) => runObservabilityEnable(observabilityEnableCommand, options)
712
+ );
713
+ var observabilityEmitTestEventCommand = new Command("emit-test-event").description("Emit a test observability event through the configured Supabase Edge Function").option("--base-url <url>", "Supabase base URL").option("--access-token <token>", "Supabase anon or service-role key").option("--function-secret <secret>", "Observability function shared secret").option("--scope-key <scopeKey>", "Override scope key").option("--service-name <serviceName>", "Override service name").option("--environment <environment>", "Override environment").action(
714
+ async (options) => runObservabilityEmitTestEvent(observabilityEmitTestEventCommand, options)
715
+ );
716
+ var observabilityDispatchCommand = new Command("dispatch").description("Dispatch pending observability notifications through the configured Edge Function").option("--base-url <url>", "Supabase base URL").option("--access-token <token>", "Supabase anon or service-role key").option("--function-secret <secret>", "Observability function shared secret").option("--limit <count>", "Maximum number of pending notifications to process", "25").action(
717
+ async (options) => runObservabilityDispatch(observabilityDispatchCommand, options)
718
+ );
719
+ var observabilityCommand = new Command("observability").description("Check and exercise the observability control plane").addCommand(observabilityEnableCommand).addCommand(observabilityStatusCommand).addCommand(observabilityCheckCommand).addCommand(observabilityDoctorCommand).addCommand(observabilityEmitTestEventCommand).addCommand(observabilityDispatchCommand);
720
+
721
+ export { ObservabilityDispatchInputSchema, ObservabilityDispatchOutputSchema, ObservabilityEmitTestEventInputSchema, ObservabilityEmitTestEventOutputSchema, ObservabilityEnableInputSchema, ObservabilityEnableOutputSchema, ObservabilityStatusInputSchema, ObservabilityStatusOutputSchema, observabilityCheckCommand, observabilityCommand, observabilityDispatchCommand, observabilityDoctorCommand, observabilityEmitTestEventCommand, observabilityEnableCommand, observabilityStatusCommand };