@runa-ai/runa-cli 0.9.0 → 0.10.1

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 (38) hide show
  1. package/dist/{chunk-ZWDWFMOX.js → chunk-HWR5NUUZ.js} +24 -3
  2. package/dist/{chunk-JQXOVCOP.js → chunk-NIS77243.js} +8 -5
  3. package/dist/{chunk-URWDB7YL.js → chunk-O3M7A73M.js} +58 -2
  4. package/dist/{chunk-YRNQEJQW.js → chunk-XRLIZKB2.js} +80 -12
  5. package/dist/{chunk-IEKYTCYA.js → chunk-YTQS2O4H.js} +59 -0
  6. package/dist/{chunk-GKBE7EIE.js → chunk-ZPE52NEK.js} +1 -1
  7. package/dist/{ci-S5KSBECX.js → ci-3HZWUQFN.js} +4 -4
  8. package/dist/{cli-TJZCAMB2.js → cli-RES5QRC2.js} +13 -13
  9. package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +6 -0
  10. package/dist/commands/db/commands/db-sync/production-precheck.d.ts +0 -8
  11. package/dist/commands/db/sync/schema-guardrail-graph-guidance.d.ts +18 -1
  12. package/dist/commands/db/sync/schema-guardrail-graph-nodes.d.ts +1 -1
  13. package/dist/commands/db/sync/schema-guardrail-graph-sql-helpers.d.ts +1 -1
  14. package/dist/commands/db/sync/schema-guardrail-types.d.ts +4 -2
  15. package/dist/commands/db/utils/changed-files-detector.d.ts +21 -0
  16. package/dist/commands/db/utils/schema-sync.d.ts +12 -0
  17. package/dist/commands/db/utils/sql-boundary-parser.d.ts +13 -0
  18. package/dist/commands/db/utils/sql-file-collector.d.ts +2 -0
  19. package/dist/constants/versions.d.ts +9 -0
  20. package/dist/{db-D2OLJDYW.js → db-PRGL7PBX.js} +587 -76
  21. package/dist/{dev-LGSMDFJN.js → dev-QR55VDNZ.js} +1 -1
  22. package/dist/{error-handler-YRQWRDEF.js → error-handler-XUQOP4TU.js} +1 -2
  23. package/dist/{hotfix-RJIAPLAM.js → hotfix-JYHDY2M6.js} +1 -2
  24. package/dist/index.js +4 -4
  25. package/dist/{init-2O6ODG5Z.js → init-4UAWYY75.js} +1 -1
  26. package/dist/{license-OB7GVJQ2.js → license-M6ODBV4X.js} +140 -154
  27. package/dist/pg-schema-diff-helpers-JZO4GAQG.js +7 -0
  28. package/dist/{template-check-BDFMT6ZO.js → template-check-VNNQQXCX.js} +10 -0
  29. package/dist/{upgrade-QZKEI3NJ.js → upgrade-LBO3Z3J7.js} +1 -1
  30. package/dist/utils/license/index.d.ts +15 -24
  31. package/dist/utils/license/types.d.ts +3 -4
  32. package/dist/utils/template-access.d.ts +20 -0
  33. package/dist/utils/template-fetcher.d.ts +10 -7
  34. package/dist/{vuln-check-5NUTETPW.js → vuln-check-5JJ2YAJW.js} +1 -1
  35. package/dist/{vuln-checker-UV342N66.js → vuln-checker-JF5234BL.js} +1 -1
  36. package/package.json +3 -3
  37. package/dist/chunk-ZZOXM6Q4.js +0 -8
  38. package/dist/pg-schema-diff-helpers-7377FS2D.js +0 -7
@@ -2,10 +2,10 @@
2
2
  import { createRequire } from 'module';
3
3
  import { blankDollarQuotedBodies, stripSqlComments, psqlSyncQuery, parsePostgresUrl, buildPsqlArgs, buildPsqlEnv } from './chunk-A6A7JIRD.js';
4
4
  import { init_esm_shims } from './chunk-VRXHCR5K.js';
5
+ import { existsSync, readdirSync, readFileSync } from 'fs';
5
6
  import path from 'path';
6
7
  import { execFileSync, spawnSync, spawn } from 'child_process';
7
8
  import { createCLILogger } from '@runa-ai/runa';
8
- import { existsSync, readdirSync, readFileSync } from 'fs';
9
9
 
10
10
  createRequire(import.meta.url);
11
11
 
@@ -473,6 +473,7 @@ function collectWarningsForStatement(statement, file, line, knownFunctionNames)
473
473
 
474
474
  // src/commands/db/utils/sql-boundary-parser.ts
475
475
  init_esm_shims();
476
+ var ALLOW_DYNAMIC_SQL_ANNOTATION = /--\s*runa:allow-dynamic-sql\b/;
476
477
  var DOLLAR_QUOTE_PATTERN = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/;
477
478
  function countNewlines2(text) {
478
479
  let count = 0;
@@ -1204,6 +1205,20 @@ function detectMissingExtensionType(errorOutput) {
1204
1205
  suggestedExtensions: [...extensions]
1205
1206
  };
1206
1207
  }
1208
+ function detectExtensionFilePath() {
1209
+ const defaultPath = "supabase/schemas/idempotent/00_extensions.sql";
1210
+ const idempotentDir = path.join(process.cwd(), "supabase", "schemas", "idempotent");
1211
+ if (!existsSync(idempotentDir)) return defaultPath;
1212
+ try {
1213
+ const files = readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
1214
+ const extensionFile = files.find((f) => /extension/i.test(f));
1215
+ if (extensionFile) {
1216
+ return path.posix.join("supabase", "schemas", "idempotent", extensionFile);
1217
+ }
1218
+ } catch {
1219
+ }
1220
+ return defaultPath;
1221
+ }
1207
1222
  function formatExtensionErrorHint(detection) {
1208
1223
  if (!detection.detected) return "";
1209
1224
  const lines = [];
@@ -1214,8 +1229,14 @@ function formatExtensionErrorHint(detection) {
1214
1229
  lines.push(` type "${type}" \u2192 extension: ${ext ?? "unknown"}`);
1215
1230
  }
1216
1231
  if (detection.suggestedExtensions.length > 0) {
1232
+ const extensionFile = detectExtensionFilePath();
1233
+ const fileExists = existsSync(path.join(process.cwd(), extensionFile));
1217
1234
  lines.push("");
1218
- lines.push("Fix 1: Add to supabase/schemas/idempotent/00_extensions.sql:");
1235
+ if (fileExists) {
1236
+ lines.push(`Fix 1: Add to ${extensionFile}:`);
1237
+ } else {
1238
+ lines.push(`Fix 1: Create ${extensionFile} (or add to your existing extension file):`);
1239
+ }
1219
1240
  for (const ext of detection.suggestedExtensions) {
1220
1241
  lines.push(` CREATE EXTENSION IF NOT EXISTS ${ext};`);
1221
1242
  }
@@ -1511,4 +1532,4 @@ function executePgSchemaDiffPlan(dbUrl, schemasDir, includeSchemas, verbose, opt
1511
1532
  throw new Error("pg-schema-diff plan failed after all retries");
1512
1533
  }
1513
1534
 
1514
- export { DB_APPLY_CHECK_MODE_CONTRACT_NOTE, FUNCTION_DEFINITION_RE, HAZARD_REGEX, MAX_DETAILED_DECLARATIVE_WARNINGS, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, PUBLIC_SCHEMA, STATEMENT_IDX_REGEX, analyzeDeclarativeDependencyContract, blankQuotedStrings, buildIdleConnectionCleanupSql, collectSqlFiles, countNewlines, detectDropTableStatements, detectMissingExtensionType, detectMissingQualifiedFunction, detectPartitionPrivilegeError, detectPgSchemaDiffVersion, executePgSchemaDiffPlan, extractDdlObject, extractFirstDollarBody, formatDeclarativeDependencyBoundaryHint, formatDeclarativeDependencyViolation, formatDeclarativeDependencyWarning, formatExtensionErrorHint, formatPartitionPrivilegeHint, freeConnectionSlotsForPgSchemaDiff, hasUnparsedHazardHints, isNonDdlMaintenanceStatement, isNonSchemaOperation, parseSqlFilename, sanitizeExecutableCode, sanitizeExecutableCodePreserveStrings, shouldReviewUnknownDeclarativeDdl, shouldReviewUnknownIdempotentDdl, splitSqlStatements, startConnectionCleanupDaemon, stopConnectionCleanupDaemon, summarizeDeclarativeDependencyWarnings, verifyDatabaseConnection, verifyPgSchemaDiffBinary };
1535
+ export { ALLOW_DYNAMIC_SQL_ANNOTATION, DB_APPLY_CHECK_MODE_CONTRACT_NOTE, FUNCTION_DEFINITION_RE, HAZARD_REGEX, MAX_DETAILED_DECLARATIVE_WARNINGS, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, PUBLIC_SCHEMA, STATEMENT_IDX_REGEX, analyzeDeclarativeDependencyContract, blankQuotedStrings, buildIdleConnectionCleanupSql, collectSqlFiles, countNewlines, detectDropTableStatements, detectExtensionFilePath, detectMissingExtensionType, detectMissingQualifiedFunction, detectPartitionPrivilegeError, detectPgSchemaDiffVersion, executePgSchemaDiffPlan, extractDdlObject, extractFirstDollarBody, formatDeclarativeDependencyBoundaryHint, formatDeclarativeDependencyViolation, formatDeclarativeDependencyWarning, formatExtensionErrorHint, formatPartitionPrivilegeHint, freeConnectionSlotsForPgSchemaDiff, hasUnparsedHazardHints, isNonDdlMaintenanceStatement, isNonSchemaOperation, parseSqlFilename, sanitizeExecutableCode, sanitizeExecutableCodePreserveStrings, shouldReviewUnknownDeclarativeDdl, shouldReviewUnknownIdempotentDdl, splitSqlStatements, startConnectionCleanupDaemon, stopConnectionCleanupDaemon, summarizeDeclarativeDependencyWarnings, verifyDatabaseConnection, verifyPgSchemaDiffBinary };
@@ -132,12 +132,12 @@ var ERROR_CATALOG = {
132
132
  LICENSE_UNAUTHORIZED: {
133
133
  code: "ERR_RUNA_LICENSE_UNAUTHORIZED",
134
134
  exitCode: EXIT_CODES.AUTH_ERROR,
135
- title: "CI access not authorized",
136
- template: 'Organization "{owner}" is not authorized to use runa CI',
135
+ title: "Legacy CI access policy denied command",
136
+ template: 'Legacy CI access policy denied runa execution for "{owner}"',
137
137
  suggestions: [
138
- "Install GitHub App: https://github.com/apps/runa-app",
139
- "Contact runa admin for approval",
140
- "Local development is not affected"
138
+ "Upgrade to the latest @runa-ai/runa-cli",
139
+ "Public runa CLI/SDK/plugin commands should not require CI allowlist approval",
140
+ "Template operations use separate repository-access checks"
141
141
  ],
142
142
  docUrl: "https://runa.dev/docs/errors/license-unauthorized"
143
143
  },
@@ -571,4 +571,7 @@ function interpolateTemplate(template, params) {
571
571
  });
572
572
  }
573
573
 
574
+ // src/errors/index.ts
575
+ init_esm_shims();
576
+
574
577
  export { ERROR_CATALOG, createError };
@@ -231,6 +231,32 @@ function extractTablesFromIdempotentSql(idempotentDir, projectRoot = process.cwd
231
231
  }
232
232
  return [...new Set(tables)].sort();
233
233
  }
234
+ function extractDynamicTablePatternsFromIdempotentSql(idempotentDir, projectRoot = process.cwd()) {
235
+ const fullPath = path.resolve(projectRoot, idempotentDir);
236
+ if (!existsSync(fullPath)) {
237
+ return [];
238
+ }
239
+ const patterns = [];
240
+ const prefixInFormat = /EXECUTE\s+format\s*\(\s*'CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"?([a-zA-Z_][a-zA-Z0-9_]*)"?\.)?([a-zA-Z_][a-zA-Z0-9_]*)%[sI]/gi;
241
+ try {
242
+ const files = readdirSync(fullPath).filter((f) => f.endsWith(".sql"));
243
+ for (const file of files) {
244
+ const filePath = path.join(fullPath, file);
245
+ const content = readFileSync(filePath, "utf-8");
246
+ const cleaned = content.replace(/--.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
247
+ for (const match of cleaned.matchAll(prefixInFormat)) {
248
+ const schema = match[1] || "public";
249
+ const prefix = match[2];
250
+ if (prefix && prefix.length >= 2) {
251
+ patterns.push(`${schema}.${prefix}*`);
252
+ }
253
+ }
254
+ }
255
+ } catch {
256
+ return [];
257
+ }
258
+ return [...new Set(patterns)].sort();
259
+ }
234
260
 
235
261
  // src/commands/db/utils/table-registry.ts
236
262
  init_esm_shims();
@@ -1686,8 +1712,16 @@ function resolveSourceConfig(projectRoot, options) {
1686
1712
  }
1687
1713
  } catch {
1688
1714
  }
1715
+ const resolvedIdempotentDir = isAbsolute(idempotentSqlDir) ? idempotentSqlDir : join(projectRoot, idempotentSqlDir);
1716
+ const dynamicPatterns = extractDynamicTablePatternsFromIdempotentSql(
1717
+ resolvedIdempotentDir,
1718
+ projectRoot
1719
+ );
1720
+ for (const pattern of dynamicPatterns) {
1721
+ exclusions.add(pattern);
1722
+ }
1689
1723
  return {
1690
- idempotentSqlDir: isAbsolute(idempotentSqlDir) ? idempotentSqlDir : join(projectRoot, idempotentSqlDir),
1724
+ idempotentSqlDir: resolvedIdempotentDir,
1691
1725
  excludeFromOrphanDetection: [...exclusions].sort((a, b) => a.localeCompare(b))
1692
1726
  };
1693
1727
  }
@@ -1918,9 +1952,31 @@ async function generateTablesManifest(projectRoot, options = {}) {
1918
1952
  }
1919
1953
  writeFileSync(outputPath, `${JSON.stringify(manifest, null, 2)}
1920
1954
  `);
1955
+ validateManifestSourceFiles(manifest, projectRoot);
1921
1956
  logManifestSummary(manifest, mappingResult.conflicts);
1922
1957
  return manifest;
1923
1958
  }
1959
+ function validateManifestSourceFiles(manifest, projectRoot) {
1960
+ const staleEntries = [];
1961
+ for (const table of manifest.tables) {
1962
+ if (!table.sourceFile) continue;
1963
+ const absolutePath = join(projectRoot, table.sourceFile);
1964
+ if (!existsSync(absolutePath)) {
1965
+ staleEntries.push({ table: table.qualifiedName, sourceFile: table.sourceFile });
1966
+ }
1967
+ }
1968
+ if (staleEntries.length === 0) return;
1969
+ console.warn(
1970
+ `[tables-manifest] warn: ${staleEntries.length} table(s) reference non-existent sourceFile:`
1971
+ );
1972
+ for (const entry of staleEntries.slice(0, 5)) {
1973
+ console.warn(` ${entry.table} \u2192 ${entry.sourceFile} (file not found)`);
1974
+ }
1975
+ if (staleEntries.length > 5) {
1976
+ console.warn(` ... and ${staleEntries.length - 5} more`);
1977
+ }
1978
+ console.warn(" \u2192 If files were moved, re-run `runa db sync` to update sourceFile references.");
1979
+ }
1924
1980
  function logManifestSummary(manifest, conflicts) {
1925
1981
  const tableCount = manifest.tables.length;
1926
1982
  const schemas = [...new Set(manifest.tables.map((t) => t.schema))];
@@ -1975,4 +2031,4 @@ function logManifestSummary(manifest, conflicts) {
1975
2031
  console.log(" Path: .runa/manifests/tables.json\n");
1976
2032
  }
1977
2033
 
1978
- export { buildTablePatternMatcher, diffSchema, extractSchemaTablesAndEnums, extractTablesFromIdempotentSql, fetchDbTablesAndEnums, generateTablesManifest, getSqlParserUtils };
2034
+ export { buildTablePatternMatcher, diffSchema, extractDynamicTablePatternsFromIdempotentSql, extractSchemaTablesAndEnums, extractTablesFromIdempotentSql, fetchDbTablesAndEnums, generateTablesManifest, getSqlParserUtils };
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'module';
3
3
  import { resolveDatabaseUrl, resolveDatabaseTarget } from './chunk-WGRVAGSR.js';
4
- import { verifyPgSchemaDiffBinary, verifyDatabaseConnection, freeConnectionSlotsForPgSchemaDiff, executePgSchemaDiffPlan, detectDropTableStatements, analyzeDeclarativeDependencyContract, DB_APPLY_CHECK_MODE_CONTRACT_NOTE, formatDeclarativeDependencyViolation, summarizeDeclarativeDependencyWarnings, MAX_DETAILED_DECLARATIVE_WARNINGS, formatDeclarativeDependencyWarning, detectPgSchemaDiffVersion, STATEMENT_IDX_REGEX, hasUnparsedHazardHints, collectSqlFiles, splitSqlStatements, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, HAZARD_REGEX, PUBLIC_SCHEMA, FUNCTION_DEFINITION_RE } from './chunk-ZWDWFMOX.js';
4
+ import { verifyPgSchemaDiffBinary, verifyDatabaseConnection, freeConnectionSlotsForPgSchemaDiff, executePgSchemaDiffPlan, detectDropTableStatements, analyzeDeclarativeDependencyContract, DB_APPLY_CHECK_MODE_CONTRACT_NOTE, formatDeclarativeDependencyViolation, summarizeDeclarativeDependencyWarnings, MAX_DETAILED_DECLARATIVE_WARNINGS, formatDeclarativeDependencyWarning, detectPgSchemaDiffVersion, STATEMENT_IDX_REGEX, hasUnparsedHazardHints, collectSqlFiles, splitSqlStatements, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, HAZARD_REGEX, PUBLIC_SCHEMA, FUNCTION_DEFINITION_RE } from './chunk-HWR5NUUZ.js';
5
5
  import { splitPlpgsqlStatementsWithOffsets, extractExecuteExpressions, extractStaticSqlFromExpression } from './chunk-Y5ANTCKE.js';
6
6
  import { loadEnvFiles } from './chunk-IWVXI5O4.js';
7
- import { CLI_VERSION } from './chunk-GKBE7EIE.js';
8
- import { generateTablesManifest } from './chunk-URWDB7YL.js';
7
+ import { CLI_VERSION } from './chunk-ZPE52NEK.js';
8
+ import { generateTablesManifest } from './chunk-O3M7A73M.js';
9
9
  import { parsePostgresUrl, buildPsqlEnv, buildPsqlArgs, stripSqlComments, psqlSyncQuery, blankDollarQuotedBodies, psqlSyncBatch, psqlSyncFile } from './chunk-A6A7JIRD.js';
10
10
  import { init_local_supabase, buildLocalDatabaseUrl } from './chunk-QSEF4T3Y.js';
11
11
  import { emitJsonSuccess } from './chunk-KE6QJBZG.js';
@@ -2309,7 +2309,7 @@ function extractQualifiedNameAfterKeyword(sql, keywordPattern) {
2309
2309
  const rest = sql.slice(keywordMatch.index + keywordMatch[0].length);
2310
2310
  const nameMatch = rest.match(/^\s*["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?/);
2311
2311
  if (!nameMatch) return null;
2312
- return `${nameMatch[1].replace(/"/g, "").toLowerCase()}.${nameMatch[2].replace(/"/g, "").toLowerCase()}`;
2312
+ return `${nameMatch[1]?.replace(/"/g, "").toLowerCase()}.${nameMatch[2]?.replace(/"/g, "").toLowerCase()}`;
2313
2313
  }
2314
2314
  function extractFkReferences(sql) {
2315
2315
  const refs = /* @__PURE__ */ new Set();
@@ -2317,7 +2317,7 @@ function extractFkReferences(sql) {
2317
2317
  /REFERENCES\s+["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?\s*\(/gi
2318
2318
  )) {
2319
2319
  refs.add(
2320
- `${match[1].replace(/"/g, "").toLowerCase()}.${match[2].replace(/"/g, "").toLowerCase()}`
2320
+ `${match[1]?.replace(/"/g, "").toLowerCase()}.${match[2]?.replace(/"/g, "").toLowerCase()}`
2321
2321
  );
2322
2322
  }
2323
2323
  return [...refs];
@@ -2327,12 +2327,12 @@ function extractRelationRefsFromBody(sql) {
2327
2327
  for (const m of sql.matchAll(
2328
2328
  /\bFROM\s+["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?/gi
2329
2329
  )) {
2330
- refs.add(`${m[1].replace(/"/g, "").toLowerCase()}.${m[2].replace(/"/g, "").toLowerCase()}`);
2330
+ refs.add(`${m[1]?.replace(/"/g, "").toLowerCase()}.${m[2]?.replace(/"/g, "").toLowerCase()}`);
2331
2331
  }
2332
2332
  for (const m of sql.matchAll(
2333
2333
  /["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?\s*%ROWTYPE/gi
2334
2334
  )) {
2335
- refs.add(`${m[1].replace(/"/g, "").toLowerCase()}.${m[2].replace(/"/g, "").toLowerCase()}`);
2335
+ refs.add(`${m[1]?.replace(/"/g, "").toLowerCase()}.${m[2]?.replace(/"/g, "").toLowerCase()}`);
2336
2336
  }
2337
2337
  return [...refs];
2338
2338
  }
@@ -2404,7 +2404,7 @@ function analyzeWithRegexFallback(strippedSql) {
2404
2404
  const match = strippedSql.match(
2405
2405
  /(?:CREATE|ALTER|DROP)\s+POLICY\s+.*?\bON\s+["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?/i
2406
2406
  );
2407
- const key = match ? `${match[1].replace(/"/g, "").toLowerCase()}.${match[2].replace(/"/g, "").toLowerCase()}` : null;
2407
+ const key = match ? `${match[1]?.replace(/"/g, "").toLowerCase()}.${match[2]?.replace(/"/g, "").toLowerCase()}` : null;
2408
2408
  return {
2409
2409
  ...buildBaseStatementAnalysis(strippedSql, "relation"),
2410
2410
  targetKey: key,
@@ -2565,7 +2565,7 @@ function stabilizeAnalyzedPlanOrder(analyzedPlan) {
2565
2565
  const groupedStatements = /* @__PURE__ */ new Set();
2566
2566
  const deferredTriggerStatements = [];
2567
2567
  const createdFunctionKeys = new Set(
2568
- analyzedPlan.statements.filter((s) => s.objectKind === "function" && s.targetFunctionKey).map((s) => s.targetFunctionKey.toLowerCase())
2568
+ analyzedPlan.statements.filter((s) => s.objectKind === "function" && s.targetFunctionKey).map((s) => s.targetFunctionKey?.toLowerCase())
2569
2569
  );
2570
2570
  for (const statement of analyzedPlan.statements) {
2571
2571
  if (statement.phase === 50 && statement.functionDependencies.some((fn) => createdFunctionKeys.has(fn.toLowerCase()))) {
@@ -3322,7 +3322,30 @@ async function createShadowDbWithExtensions(config) {
3322
3322
  // Extensions like PostGIS can take time
3323
3323
  });
3324
3324
  if (extResult.status !== 0) {
3325
- logger7.warn(`Some extensions failed to install: ${extResult.stderr}`);
3325
+ const stderr = extResult.stderr?.trim() ?? "";
3326
+ logger7.error(`Shadow DB extension installation failed: ${stderr}`);
3327
+ logger7.error("Configured extensions may not be available in the target PostgreSQL server.");
3328
+ logger7.error("Check that the extensions are installed on the server:");
3329
+ for (const ext of extensions) {
3330
+ logger7.error(` SELECT * FROM pg_available_extensions WHERE name = '${ext}';`);
3331
+ }
3332
+ logger7.error(
3333
+ "If running locally with Supabase, ensure extensions are enabled in the dashboard."
3334
+ );
3335
+ }
3336
+ const verifyResult = psqlSyncQuery({
3337
+ databaseUrl: shadowDsn,
3338
+ sql: `SELECT extname FROM pg_extension WHERE extname = ANY(ARRAY[${extensions.map((e) => `'${e}'`).join(",")}])`,
3339
+ timeout: 1e4
3340
+ });
3341
+ if (verifyResult.status === 0) {
3342
+ const installed = (verifyResult.stdout ?? "").split("\n").map((l) => l.trim()).filter(Boolean);
3343
+ const missing = extensions.filter((ext) => !installed.includes(ext));
3344
+ if (missing.length > 0) {
3345
+ logger7.warn(
3346
+ `Shadow DB missing extensions: ${missing.join(", ")}. pg-schema-diff may fail on extension-defined types (geometry, vector, etc.).`
3347
+ );
3348
+ }
3326
3349
  }
3327
3350
  bootstrapShadowDbSchemaPrerequisites(sourceDbUrl, shadowDsn);
3328
3351
  logger7.debug(`Shadow database ready with extensions: ${extensions.join(", ")}`);
@@ -7203,11 +7226,54 @@ async function applyWithRetry(params) {
7203
7226
  retryWaitMs: result.totalWaitMs
7204
7227
  };
7205
7228
  }
7229
+ function autoDetectRequiredExtensions(targetDir) {
7230
+ const EXTENSION_TYPE_KEYWORDS = {
7231
+ geometry: "postgis",
7232
+ geography: "postgis",
7233
+ box2d: "postgis",
7234
+ box3d: "postgis",
7235
+ raster: "postgis_raster",
7236
+ vector: "vector",
7237
+ halfvec: "vector",
7238
+ sparsevec: "vector",
7239
+ citext: "citext",
7240
+ hstore: "hstore",
7241
+ ltree: "ltree"
7242
+ };
7243
+ const declarativeDir = join(targetDir, "supabase", "schemas", "declarative");
7244
+ if (!existsSync(declarativeDir)) return [];
7245
+ const detected = /* @__PURE__ */ new Set();
7246
+ try {
7247
+ const files = readdirSync(declarativeDir).filter((f) => f.endsWith(".sql"));
7248
+ for (const file of files) {
7249
+ const content = readFileSync(join(declarativeDir, file), "utf-8").toLowerCase();
7250
+ for (const [typeName, extName] of Object.entries(EXTENSION_TYPE_KEYWORDS)) {
7251
+ if (content.includes(typeName)) {
7252
+ detected.add(extName);
7253
+ }
7254
+ }
7255
+ }
7256
+ } catch {
7257
+ }
7258
+ return [...detected].sort();
7259
+ }
7260
+ function resolveEffectiveShadowExtensions(configured, targetDir, verbose) {
7261
+ if (configured && configured.length > 0) return configured;
7262
+ const autoDetected = autoDetectRequiredExtensions(targetDir);
7263
+ if (autoDetected.length > 0 && verbose) {
7264
+ logger13.debug(`Auto-detected shadow DB extensions from SQL: ${autoDetected.join(", ")}`);
7265
+ }
7266
+ return autoDetected.length > 0 ? autoDetected : void 0;
7267
+ }
7206
7268
  function loadPgSchemaDiffConfigState(targetDir, verbose) {
7207
7269
  try {
7208
7270
  const config = loadRunaConfig(targetDir);
7209
7271
  return {
7210
- shadowExtensions: config.database?.pgSchemaDiff?.shadowDbExtensions,
7272
+ shadowExtensions: resolveEffectiveShadowExtensions(
7273
+ config.database?.pgSchemaDiff?.shadowDbExtensions,
7274
+ targetDir,
7275
+ verbose
7276
+ ),
7211
7277
  configExclusions: config.database?.pgSchemaDiff?.excludeFromOrphanDetection ? [...config.database.pgSchemaDiff.excludeFromOrphanDetection] : void 0,
7212
7278
  schemaCheckExclusions: config.database?.pgSchemaDiff?.excludeFromSchemaCheck ? [...config.database.pgSchemaDiff.excludeFromSchemaCheck] : void 0
7213
7279
  };
@@ -7215,7 +7281,9 @@ function loadPgSchemaDiffConfigState(targetDir, verbose) {
7215
7281
  if (verbose) {
7216
7282
  logger13.debug("Could not load runa.config.ts - continuing without extension support");
7217
7283
  }
7218
- return {};
7284
+ return {
7285
+ shadowExtensions: resolveEffectiveShadowExtensions(void 0, targetDir, verbose)
7286
+ };
7219
7287
  }
7220
7288
  }
7221
7289
  function createPrefilterState(schemasDir, verbose, configExclusions) {
@@ -19,6 +19,64 @@ init_esm_shims();
19
19
  var COMPATIBLE_TEMPLATES_VERSION = "0.7.3";
20
20
  var TEMPLATES_PACKAGE_NAME = "@r06-dev/runa-templates";
21
21
  var GITHUB_PACKAGES_REGISTRY = "https://npm.pkg.github.com";
22
+ var TEMPLATES_SOURCE_REPOSITORY = "r06-dev/runa";
23
+ var TEMPLATES_SOURCE_REPOSITORY_API_URL = `https://api.github.com/repos/${TEMPLATES_SOURCE_REPOSITORY}`;
24
+
25
+ // src/utils/template-access.ts
26
+ init_esm_shims();
27
+ var TEMPLATE_ACCESS_TIMEOUT_MS = 5e3;
28
+ var TEMPLATE_ACCESS_USER_AGENT = "runa-cli/template-access-check";
29
+ async function hasRepoAccessWithToken(token) {
30
+ const controller = new AbortController();
31
+ const timeoutId = setTimeout(() => controller.abort(), TEMPLATE_ACCESS_TIMEOUT_MS);
32
+ try {
33
+ const response = await fetch(TEMPLATES_SOURCE_REPOSITORY_API_URL, {
34
+ method: "GET",
35
+ headers: {
36
+ Authorization: `Bearer ${token}`,
37
+ Accept: "application/vnd.github+json",
38
+ "User-Agent": TEMPLATE_ACCESS_USER_AGENT
39
+ },
40
+ signal: controller.signal
41
+ });
42
+ return response.ok;
43
+ } catch {
44
+ return false;
45
+ } finally {
46
+ clearTimeout(timeoutId);
47
+ }
48
+ }
49
+ async function hasRepoAccessWithGh() {
50
+ try {
51
+ const result = await secureGh(["api", `repos/${TEMPLATES_SOURCE_REPOSITORY}`, "--silent"], {
52
+ reject: false,
53
+ timeout: TEMPLATE_ACCESS_TIMEOUT_MS,
54
+ stdio: "pipe"
55
+ });
56
+ return result.exitCode === 0;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+ async function verifyTemplateRepoAccess() {
62
+ const explicitToken = process.env.NODE_AUTH_TOKEN?.trim();
63
+ if (explicitToken && await hasRepoAccessWithToken(explicitToken)) {
64
+ return;
65
+ }
66
+ if (await hasRepoAccessWithGh()) {
67
+ return;
68
+ }
69
+ throw new CLIError(
70
+ `Template operations require access to ${TEMPLATES_SOURCE_REPOSITORY}.`,
71
+ "TEMPLATE_REPO_ACCESS_REQUIRED",
72
+ [
73
+ `Use a GitHub account that can access https://github.com/${TEMPLATES_SOURCE_REPOSITORY}`,
74
+ "Authenticate with GitHub CLI: gh auth login",
75
+ "Or export NODE_AUTH_TOKEN from an authorized account",
76
+ "Normal runa CLI/SDK/plugin commands do not require this access"
77
+ ]
78
+ );
79
+ }
22
80
 
23
81
  // src/utils/template-fetcher.ts
24
82
  var SAFE_VERSION_PATTERN = /^[a-zA-Z0-9._-]+$/;
@@ -273,6 +331,7 @@ async function fetchTemplates(options = {}) {
273
331
  const version = options.version ?? COMPATIBLE_TEMPLATES_VERSION;
274
332
  const fresh = options.fresh ?? false;
275
333
  const verbose = options.verbose ?? false;
334
+ await verifyTemplateRepoAccess();
276
335
  const workspaceResult = getWorkspaceTemplatesResult(resolveWorkspaceTemplates(), verbose);
277
336
  if (workspaceResult) {
278
337
  return workspaceResult;
@@ -6,7 +6,7 @@ createRequire(import.meta.url);
6
6
 
7
7
  // src/version.ts
8
8
  init_esm_shims();
9
- var CLI_VERSION = "0.9.0";
9
+ var CLI_VERSION = "0.10.1";
10
10
  var HAS_ADMIN_COMMAND = false;
11
11
 
12
12
  export { CLI_VERSION, HAS_ADMIN_COMMAND };
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'module';
3
- import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, isIdempotentRoleHazard, detectAppSchemas, formatSchemasForSql, getDbPlanArtifactPath, runDbApply } from './chunk-YRNQEJQW.js';
3
+ import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, isIdempotentRoleHazard, detectAppSchemas, formatSchemasForSql, getDbPlanArtifactPath, runDbApply } from './chunk-XRLIZKB2.js';
4
4
  import './chunk-WGRVAGSR.js';
5
- import './chunk-ZWDWFMOX.js';
5
+ import './chunk-HWR5NUUZ.js';
6
6
  import './chunk-UHDAYPHH.js';
7
7
  import './chunk-Y5ANTCKE.js';
8
8
  import './chunk-IWVXI5O4.js';
9
- import './chunk-GKBE7EIE.js';
9
+ import './chunk-ZPE52NEK.js';
10
10
  import './chunk-B7C7CLW2.js';
11
11
  import './chunk-QDF7QXBL.js';
12
12
  import { getSnapshotStateName, getSnapshotStatePaths, isSnapshotComplete } from './chunk-XVNDDHAF.js';
13
13
  import { createInitialSummary, resolveMode, appendGithubStepSummary, buildCiProdApplyStepSummaryMarkdown, setSummaryErrorFromUnknown, writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput, requireCiAutoApprove, resolveProdApplyInputs, parseIntOr, classifyCiProdApplyError, addGithubMask } from './chunk-EXR4J2JT.js';
14
- import './chunk-URWDB7YL.js';
14
+ import './chunk-O3M7A73M.js';
15
15
  import { parsePostgresUrl, buildPsqlArgs, buildPsqlEnv, psqlSyncQuery } from './chunk-A6A7JIRD.js';
16
16
  import { ensureRunaTmpDir, runLogged } from './chunk-ELXXQIGW.js';
17
17
  import { createMachineStateChangeLogger } from './chunk-5FT3F36G.js';
@@ -2,7 +2,7 @@
2
2
  import { createRequire } from 'module';
3
3
  import { enableNonInteractiveMode } from './chunk-6Y3LAUGL.js';
4
4
  import { getRequestedCommandNameFromArgv } from './chunk-UWWSAPDR.js';
5
- import { CLI_VERSION, HAS_ADMIN_COMMAND } from './chunk-GKBE7EIE.js';
5
+ import { CLI_VERSION, HAS_ADMIN_COMMAND } from './chunk-ZPE52NEK.js';
6
6
  import { emitDefaultSuccessIfNeeded } from './chunk-WJXC4MVY.js';
7
7
  import { parseOutputFormat, setOutputFormat, getOutputFormatFromEnv } from './chunk-HKUWEGUX.js';
8
8
  import { init_esm_shims } from './chunk-VRXHCR5K.js';
@@ -145,7 +145,7 @@ function isTestCommand(requested) {
145
145
  async function registerProjectLifecycleCommands(program, requested, loadAllCommands) {
146
146
  if (!loadAllCommands && requested) {
147
147
  if (requested === "init") {
148
- const { initCommand: initCommand2 } = await import('./init-2O6ODG5Z.js');
148
+ const { initCommand: initCommand2 } = await import('./init-4UAWYY75.js');
149
149
  program.addCommand(initCommand2);
150
150
  return;
151
151
  }
@@ -155,7 +155,7 @@ async function registerProjectLifecycleCommands(program, requested, loadAllComma
155
155
  return;
156
156
  }
157
157
  if (requested === "upgrade") {
158
- const { upgradeCommand: upgradeCommand2 } = await import('./upgrade-QZKEI3NJ.js');
158
+ const { upgradeCommand: upgradeCommand2 } = await import('./upgrade-LBO3Z3J7.js');
159
159
  program.addCommand(upgradeCommand2);
160
160
  return;
161
161
  }
@@ -170,7 +170,7 @@ async function registerProjectLifecycleCommands(program, requested, loadAllComma
170
170
  return;
171
171
  }
172
172
  if (requested === "dev") {
173
- const { devCommand: devCommand2 } = await import('./dev-LGSMDFJN.js');
173
+ const { devCommand: devCommand2 } = await import('./dev-QR55VDNZ.js');
174
174
  program.addCommand(devCommand2);
175
175
  return;
176
176
  }
@@ -183,12 +183,12 @@ async function registerProjectLifecycleCommands(program, requested, loadAllComma
183
183
  { buildCommand },
184
184
  { devCommand }
185
185
  ] = await Promise.all([
186
- import('./init-2O6ODG5Z.js'),
186
+ import('./init-4UAWYY75.js'),
187
187
  import('./prepare-32DOVHTE.js'),
188
- import('./upgrade-QZKEI3NJ.js'),
188
+ import('./upgrade-LBO3Z3J7.js'),
189
189
  import('./validate-CAAW4Y44.js'),
190
190
  import('./build-P2A6345N.js'),
191
- import('./dev-LGSMDFJN.js')
191
+ import('./dev-QR55VDNZ.js')
192
192
  ]);
193
193
  program.addCommand(initCommand);
194
194
  program.addCommand(prepareCommand);
@@ -462,11 +462,11 @@ async function registerFocusedStatusUtilityCommand(program, requested) {
462
462
  return false;
463
463
  }
464
464
  async function registerCiCommand(program) {
465
- const { ciCommand } = await import('./ci-S5KSBECX.js');
465
+ const { ciCommand } = await import('./ci-3HZWUQFN.js');
466
466
  program.addCommand(ciCommand);
467
467
  }
468
468
  async function registerDbCommand(program) {
469
- const { dbCommand } = await import('./db-D2OLJDYW.js');
469
+ const { dbCommand } = await import('./db-PRGL7PBX.js');
470
470
  program.addCommand(dbCommand);
471
471
  }
472
472
  async function registerServicesCommand(program) {
@@ -478,7 +478,7 @@ async function registerEnvCommand(program) {
478
478
  program.addCommand(envCommand);
479
479
  }
480
480
  async function registerHotfixCommand(program) {
481
- const { hotfixCommand } = await import('./hotfix-RJIAPLAM.js');
481
+ const { hotfixCommand } = await import('./hotfix-JYHDY2M6.js');
482
482
  program.addCommand(hotfixCommand);
483
483
  }
484
484
  async function registerSdkCommand(program) {
@@ -498,11 +498,11 @@ async function registerWorkflowCommand(program) {
498
498
  program.addCommand(workflowCommand);
499
499
  }
500
500
  async function registerVulnCheckCommand(program) {
501
- const { vulnCheckCommand } = await import('./vuln-check-5NUTETPW.js');
501
+ const { vulnCheckCommand } = await import('./vuln-check-5JJ2YAJW.js');
502
502
  program.addCommand(vulnCheckCommand);
503
503
  }
504
504
  async function registerTemplateCheckCommand(program) {
505
- const { templateCheckCommand } = await import('./template-check-BDFMT6ZO.js');
505
+ const { templateCheckCommand } = await import('./template-check-VNNQQXCX.js');
506
506
  program.addCommand(templateCheckCommand);
507
507
  }
508
508
  async function registerSessionCommands(program) {
@@ -708,7 +708,7 @@ async function executeProgram(program) {
708
708
  if (!isHelpOrVersion && shouldRunRuntimeBootstrap(argv)) {
709
709
  const [{ loadEnvFiles }, { enforceLicenseInCI }] = await Promise.all([
710
710
  import('./env-files-ONBC47I6.js'),
711
- import('./license-OB7GVJQ2.js')
711
+ import('./license-M6ODBV4X.js')
712
712
  ]);
713
713
  loadEnvFiles({
714
714
  silent: true
@@ -42,8 +42,14 @@ export interface MissingExtensionDetection {
42
42
  * and map them to the required PostgreSQL extensions.
43
43
  */
44
44
  export declare function detectMissingExtensionType(errorOutput: string): MissingExtensionDetection;
45
+ /**
46
+ * Detect the actual extension management SQL file in the project.
47
+ * Falls back to the default path if no extension file is found.
48
+ */
49
+ export declare function detectExtensionFilePath(): string;
45
50
  /**
46
51
  * Format actionable hint for missing extension type errors.
52
+ * Auto-detects the actual extension file path in the project.
47
53
  */
48
54
  export declare function formatExtensionErrorHint(detection: MissingExtensionDetection): string;
49
55
  export interface PartitionPrivilegeDetection {
@@ -1,11 +1,3 @@
1
- /**
2
- * AI HINT: Production apply precheck orchestration
3
- *
4
- * Purpose: Run full production precheck pipeline: local risk checks + plan boundary reconciliation.
5
- * Collects findings, applies strict mode, logs results, and throws on blockers.
6
- *
7
- * Used by: db-sync.ts (maybeRunProductionApplyPrecheck)
8
- */
9
1
  import { type createCLILogger } from '@runa-ai/runa';
10
2
  import type { DbApplyOutput } from '../../apply/contract.js';
11
3
  import type { RunaDbEnv } from '../../utils/db-target.js';
@@ -4,12 +4,29 @@
4
4
  * Purpose: Generate guidance warnings for schema boundary violations,
5
5
  * security definer issues, trigger ownership, and managed schema access
6
6
  */
7
- import type { SchemaGuardrailReport, SchemaGraphFunctionClaim, SchemaGraphFileNode, SchemaGraphSchemaNode } from './schema-guardrail-types.js';
7
+ import type { BoundaryGuidanceWarning, SchemaGuardrailReport, SchemaGraphFunctionClaim, SchemaGraphFileNode, SchemaGraphSchemaNode, SchemaGraphTableNode, LoadedSqlSources } from './schema-guardrail-types.js';
8
8
  declare function buildBoundaryGuidanceWarnings(params: {
9
9
  fileNodes: SchemaGraphFileNode[];
10
10
  schemaNodes: SchemaGraphSchemaNode[];
11
11
  functionClaims: SchemaGraphFunctionClaim[];
12
12
  ownerFileByTable: Map<string, string>;
13
13
  }): SchemaGuardrailReport['boundaryGuidanceWarnings'];
14
+ /**
15
+ * Detect trigger dispatch coverage gaps.
16
+ *
17
+ * When a trigger calls a dispatch function (e.g., check_org_id_matches_parent)
18
+ * with literal arguments (e.g., 'parent_table'), and the function body has
19
+ * CASE/WHEN branches that don't cover those arguments, report a warning.
20
+ */
21
+ export declare function buildTriggerDispatchGapWarnings(params: {
22
+ tableNodes: Map<string, SchemaGraphTableNode>;
23
+ sources: LoadedSqlSources;
24
+ }): BoundaryGuidanceWarning[];
25
+ /**
26
+ * Detect index names that appear in both declarative and idempotent layers.
27
+ * This is typically redundant — declarative indexes are pg-schema-diff managed,
28
+ * and idempotent IF NOT EXISTS copies create silent redundancy.
29
+ */
30
+ export declare function buildCrossLayerDuplicateIndexWarnings(sources: LoadedSqlSources): BoundaryGuidanceWarning[];
14
31
  export { buildBoundaryGuidanceWarnings };
15
32
  //# sourceMappingURL=schema-guardrail-graph-guidance.d.ts.map
@@ -5,7 +5,7 @@
5
5
  * Includes: Trigger extraction, declarative file parsing,
6
6
  * table/FK/dependency/file node construction
7
7
  */
8
- import { type SqlFile } from '../utils/declarative-dependency-sql-utils.js';
8
+ import type { SqlFile } from '../utils/declarative-dependency-sql-utils.js';
9
9
  import { type DeclarativeFileRecord, type DefinedFunctionMetadata, type ManagedBoundaryMetadata, type IdempotentTouchMetadata } from './schema-guardrail-graph-types.js';
10
10
  import type { SchemaGuardrailConfig, SchemaGraphFileDependencyEdge, SchemaGraphFileNode, SchemaGraphFunctionClaim, SchemaGraphPolicyClaim, SchemaGraphPolicyRef, SchemaGraphTableNode, SchemaGraphTriggerRef, SchemaGuardrailReport } from './schema-guardrail-types.js';
11
11
  declare function parseCreateTriggerStatement(statement: string): {
@@ -4,7 +4,7 @@
4
4
  * Purpose: SQL string sanitization, pattern matching, and metadata extraction
5
5
  * for idempotent file analysis
6
6
  */
7
- import { type SqlFile } from '../utils/declarative-dependency-sql-utils.js';
7
+ import type { SqlFile } from '../utils/declarative-dependency-sql-utils.js';
8
8
  import type { IdempotentTouchMetadata } from './schema-guardrail-graph-types.js';
9
9
  declare function normalizeSqlIdentifier(value: string): string;
10
10
  declare function consumeSingleQuotedLiteral(params: {
@@ -3,8 +3,8 @@ export type SchemaManagedBlockKind = 'file-header' | 'table-header';
3
3
  export type SchemaGuardrailMode = 'check' | 'sync';
4
4
  export type SchemaGraphFileLayer = 'declarative' | 'idempotent';
5
5
  export type SchemaGraphFileAuthoringRole = 'declarative-owner' | 'operational';
6
- export type BoundaryGuidanceWarningKind = 'schema' | 'function' | 'policy' | 'security_definer' | 'trigger_function' | 'managed_boundary';
7
- export type LocalBlindSpotBlockerKind = 'cross-schema-rls' | 'dynamic-sql' | 'extension-placement';
6
+ export type BoundaryGuidanceWarningKind = 'schema' | 'function' | 'policy' | 'security_definer' | 'trigger_function' | 'trigger_dispatch_gap' | 'managed_boundary';
7
+ export type LocalBlindSpotBlockerKind = 'cross-schema-rls' | 'dynamic-sql' | 'dynamic-sql-infra' | 'extension-placement';
8
8
  export type SchemaGuardrailPhaseId = 'load_sources' | 'build_static_graph' | 'validate_ownership' | 'compare_generated_headers' | 'refresh_generated_headers' | 'handoff_db_sync' | 'runtime_reconcile' | 'publish_report';
9
9
  export type SchemaGuardrailFailureCode = 'source_load_failed' | 'duplicate_table_owner' | 'duplicate_function_owner' | 'policy_ownership_conflict' | 'raw_cross_schema_rls_blocked' | 'dynamic_sql_blocked' | 'extension_placement_blocked' | 'stale_generated_header' | 'generated_header_validation_failed' | 'generated_header_rewrite_failed' | 'static_graph_build_failed' | 'critical_runtime_graph_contradiction' | 'sync_apply_failed';
10
10
  export interface SchemaGraphFileNode {
@@ -58,6 +58,8 @@ export interface SchemaGraphTriggerRef {
58
58
  timing: 'BEFORE' | 'AFTER' | 'INSTEAD OF';
59
59
  event: string;
60
60
  functionName?: string;
61
+ /** Literal string arguments passed to the trigger function (e.g., 'parent_table') */
62
+ functionArgs?: string[];
61
63
  }
62
64
  export interface SchemaGraphTableNode {
63
65
  schema: string;