@kaelio/ktx 0.8.0 → 0.10.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.
Files changed (183) hide show
  1. package/assets/python/{kaelio_ktx-0.8.0-py3-none-any.whl → kaelio_ktx-0.10.0-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/clack.d.ts +6 -0
  5. package/dist/clack.js +17 -2
  6. package/dist/cli-program.d.ts +3 -0
  7. package/dist/cli-program.js +42 -2
  8. package/dist/cli-runtime.d.ts +3 -0
  9. package/dist/cli-runtime.js +94 -3
  10. package/dist/commands/setup-commands.js +3 -4
  11. package/dist/connection-recovery.d.ts +34 -0
  12. package/dist/connection-recovery.js +82 -0
  13. package/dist/connection.js +26 -2
  14. package/dist/connectors/bigquery/connector.d.ts +2 -5
  15. package/dist/connectors/bigquery/connector.js +2 -2
  16. package/dist/connectors/clickhouse/connector.d.ts +2 -5
  17. package/dist/connectors/clickhouse/connector.js +2 -2
  18. package/dist/connectors/mysql/connector.d.ts +7 -6
  19. package/dist/connectors/mysql/connector.js +25 -5
  20. package/dist/connectors/mysql/dialect.d.ts +1 -1
  21. package/dist/connectors/mysql/dialect.js +12 -2
  22. package/dist/connectors/postgres/connector.d.ts +2 -5
  23. package/dist/connectors/postgres/connector.js +2 -2
  24. package/dist/connectors/snowflake/connector.d.ts +2 -5
  25. package/dist/connectors/snowflake/connector.js +2 -2
  26. package/dist/connectors/sqlite/connector.d.ts +2 -5
  27. package/dist/connectors/sqlite/connector.js +2 -2
  28. package/dist/connectors/sqlserver/connector.d.ts +2 -5
  29. package/dist/connectors/sqlserver/connector.js +2 -2
  30. package/dist/context/connections/drivers.d.ts +0 -1
  31. package/dist/context/connections/drivers.js +0 -7
  32. package/dist/context/connections/query-executor.d.ts +2 -1
  33. package/dist/context/core/abort.d.ts +9 -0
  34. package/dist/context/core/abort.js +36 -0
  35. package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
  36. package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
  37. package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
  38. package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
  39. package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
  40. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +30 -0
  41. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +194 -0
  42. package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
  43. package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
  44. package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
  45. package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
  46. package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
  47. package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
  48. package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
  49. package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
  50. package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
  51. package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
  52. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
  53. package/dist/context/ingest/final-gate-repair.d.ts +1 -0
  54. package/dist/context/ingest/final-gate-repair.js +1 -0
  55. package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
  56. package/dist/context/ingest/ingest-bundle.runner.js +127 -53
  57. package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
  58. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
  59. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
  60. package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
  61. package/dist/context/ingest/local-adapters.js +21 -4
  62. package/dist/context/ingest/local-bundle-runtime.js +13 -5
  63. package/dist/context/ingest/local-ingest.d.ts +1 -0
  64. package/dist/context/ingest/local-ingest.js +13 -3
  65. package/dist/context/ingest/memory-flow/events.js +1 -1
  66. package/dist/context/ingest/memory-flow/schema.js +8 -3
  67. package/dist/context/ingest/memory-flow/types.d.ts +7 -3
  68. package/dist/context/ingest/ports.d.ts +3 -5
  69. package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
  70. package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
  71. package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
  72. package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
  73. package/dist/context/ingest/types.d.ts +1 -0
  74. package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
  75. package/dist/context/llm/ai-sdk-runtime.js +152 -16
  76. package/dist/context/llm/claude-code-runtime.d.ts +6 -4
  77. package/dist/context/llm/claude-code-runtime.js +127 -48
  78. package/dist/context/llm/codex-exec-events.d.ts +20 -0
  79. package/dist/context/llm/codex-exec-events.js +155 -0
  80. package/dist/context/llm/codex-isolation.d.ts +3 -0
  81. package/dist/context/llm/codex-isolation.js +5 -0
  82. package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
  83. package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
  84. package/dist/context/llm/codex-models.d.ts +2 -0
  85. package/dist/context/llm/codex-models.js +17 -0
  86. package/dist/context/llm/codex-runtime-config.d.ts +16 -0
  87. package/dist/context/llm/codex-runtime-config.js +19 -0
  88. package/dist/context/llm/codex-runtime.d.ts +37 -0
  89. package/dist/context/llm/codex-runtime.js +347 -0
  90. package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
  91. package/dist/context/llm/codex-sdk-runner.js +63 -0
  92. package/dist/context/llm/local-config.d.ts +16 -4
  93. package/dist/context/llm/local-config.js +18 -2
  94. package/dist/context/llm/rate-limit-governor.d.ts +103 -0
  95. package/dist/context/llm/rate-limit-governor.js +285 -0
  96. package/dist/context/llm/runtime-port.d.ts +3 -6
  97. package/dist/context/mcp/context-tools.js +43 -13
  98. package/dist/context/project/config.d.ts +14 -0
  99. package/dist/context/project/config.js +37 -2
  100. package/dist/context/scan/types.d.ts +15 -2
  101. package/dist/context/scan/types.js +12 -0
  102. package/dist/context/sl/description-normalization.js +4 -14
  103. package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
  104. package/dist/context/sql-analysis/ports.d.ts +12 -2
  105. package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
  106. package/dist/context-build-view.d.ts +13 -0
  107. package/dist/context-build-view.js +63 -32
  108. package/dist/demo-metrics.d.ts +0 -2
  109. package/dist/demo-metrics.js +1 -11
  110. package/dist/ingest.d.ts +1 -0
  111. package/dist/ingest.js +32 -3
  112. package/dist/io/buffered-command-io.d.ts +11 -0
  113. package/dist/io/buffered-command-io.js +28 -0
  114. package/dist/io/symbols.d.ts +2 -0
  115. package/dist/io/symbols.js +2 -0
  116. package/dist/llm/types.d.ts +1 -1
  117. package/dist/local-adapters.d.ts +10 -2
  118. package/dist/local-adapters.js +19 -3
  119. package/dist/memory-flow-hud.js +8 -16
  120. package/dist/next-steps.js +1 -2
  121. package/dist/progress-port-adapter.d.ts +6 -0
  122. package/dist/progress-port-adapter.js +18 -0
  123. package/dist/public-ingest.d.ts +20 -1
  124. package/dist/public-ingest.js +228 -42
  125. package/dist/reveal-password-prompt.d.ts +24 -0
  126. package/dist/reveal-password-prompt.js +78 -0
  127. package/dist/scan.js +21 -3
  128. package/dist/setup-context.d.ts +2 -0
  129. package/dist/setup-context.js +133 -27
  130. package/dist/setup-databases.d.ts +18 -1
  131. package/dist/setup-databases.js +378 -249
  132. package/dist/setup-demo-tour.js +1 -0
  133. package/dist/setup-embeddings.js +1 -1
  134. package/dist/setup-models.d.ts +11 -15
  135. package/dist/setup-models.js +140 -276
  136. package/dist/setup-prompts.js +3 -2
  137. package/dist/setup-ready-menu.d.ts +16 -2
  138. package/dist/setup-ready-menu.js +37 -5
  139. package/dist/setup-sources.js +115 -35
  140. package/dist/setup.d.ts +1 -1
  141. package/dist/setup.js +23 -11
  142. package/dist/sl.d.ts +2 -2
  143. package/dist/sl.js +20 -4
  144. package/dist/sql.js +18 -2
  145. package/dist/star-prompt/cache.d.ts +16 -0
  146. package/dist/star-prompt/cache.js +45 -0
  147. package/dist/star-prompt/star-count.d.ts +7 -0
  148. package/dist/star-prompt/star-count.js +66 -0
  149. package/dist/star-prompt/star-line.d.ts +12 -0
  150. package/dist/star-prompt/star-line.js +26 -0
  151. package/dist/status-project.d.ts +11 -0
  152. package/dist/status-project.js +50 -1
  153. package/dist/telemetry/command-hook.d.ts +1 -0
  154. package/dist/telemetry/command-hook.js +3 -1
  155. package/dist/telemetry/emitter.d.ts +10 -0
  156. package/dist/telemetry/emitter.js +31 -0
  157. package/dist/telemetry/events.d.ts +35 -6
  158. package/dist/telemetry/events.js +25 -2
  159. package/dist/telemetry/exception.d.ts +18 -0
  160. package/dist/telemetry/exception.js +162 -0
  161. package/dist/telemetry/identity.d.ts +0 -1
  162. package/dist/telemetry/identity.js +6 -6
  163. package/dist/telemetry/index.d.ts +15 -2
  164. package/dist/telemetry/index.js +15 -3
  165. package/dist/telemetry/redaction-secrets.d.ts +11 -0
  166. package/dist/telemetry/redaction-secrets.js +92 -0
  167. package/dist/telemetry/scrubber.d.ts +10 -0
  168. package/dist/telemetry/scrubber.js +20 -0
  169. package/dist/update-check/cache.d.ts +21 -0
  170. package/dist/update-check/cache.js +38 -0
  171. package/dist/update-check/channel.d.ts +15 -0
  172. package/dist/update-check/channel.js +30 -0
  173. package/dist/update-check/registry.d.ts +1 -0
  174. package/dist/update-check/registry.js +45 -0
  175. package/dist/update-check/update-check.d.ts +43 -0
  176. package/dist/update-check/update-check.js +116 -0
  177. package/package.json +12 -4
  178. package/dist/context/connections/local-query-executor.d.ts +0 -6
  179. package/dist/context/connections/local-query-executor.js +0 -39
  180. package/dist/context/connections/postgres-query-executor.d.ts +0 -25
  181. package/dist/context/connections/postgres-query-executor.js +0 -53
  182. package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
  183. package/dist/context/connections/sqlite-query-executor.js +0 -74
@@ -4,15 +4,23 @@ import { delimiter, dirname, join } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { promisify } from 'node:util';
6
6
  import { getDriverRegistration } from './context/connections/drivers.js';
7
+ import { createLocalKtxLlmRuntimeFromConfig } from './context/llm/local-config.js';
7
8
  import { queryHistoryDialectForConnection } from './context/ingest/adapters/historic-sql/connection-dialect.js';
9
+ import { proposeQueryHistoryServiceAccountFilters, } from './context/ingest/adapters/historic-sql/query-history-filter-picker.js';
10
+ import { resolveQueryHistoryScopeFloor } from './context/ingest/adapters/historic-sql/scope-floor.js';
8
11
  import { runHistoricSqlReadinessProbe, } from './context/ingest/historic-sql-probes.js';
9
12
  import { serializeKtxProjectConfig } from './context/project/config.js';
10
13
  import { loadKtxProject } from './context/project/project.js';
11
14
  import { markKtxSetupStateStepComplete, setKtxSetupDatabaseConnectionIds } from './context/project/setup-config.js';
15
+ import { getKtxCliPackageInfo } from './cli-runtime.js';
12
16
  import { errorMessage, flushPrefixedBufferedCommandOutput, writePrefixedLines, } from './clack.js';
13
17
  import { runKtxConnection } from './connection.js';
18
+ import { createBufferedCommandIo } from './io/buffered-command-io.js';
19
+ import { runConnectionSetupWithRecovery, } from './connection-recovery.js';
14
20
  import { pickDatabaseScope as defaultPickDatabaseScope, } from './database-tree-picker.js';
15
21
  import { withMultiselectNavigation, withTextInputNavigation } from './prompt-navigation.js';
22
+ import { createKtxCliHistoricSqlRuntime } from './local-adapters.js';
23
+ import { queryHistoryPullConfig } from './public-ingest.js';
16
24
  import { runKtxScan } from './scan.js';
17
25
  import { writeProjectLocalSecretReference } from './setup-secrets.js';
18
26
  import { isDemoConnection } from './telemetry/demo-detect.js';
@@ -671,10 +679,13 @@ async function maybeApplyHistoricSqlConfig(input) {
671
679
  if (!enabled) {
672
680
  return withQueryHistoryConfig(input.connection, { ...existing, enabled: false });
673
681
  }
682
+ const existingFilters = existing.filters && typeof existing.filters === 'object' && !Array.isArray(existing.filters)
683
+ ? existing.filters
684
+ : {};
674
685
  const common = {
675
686
  ...existing,
676
687
  enabled: true,
677
- filters: historicSqlFiltersForSetup(input.args.queryHistoryServiceAccountPatterns),
688
+ filters: historicSqlFiltersForSetup(input.args.queryHistoryServiceAccountPatterns, existingFilters),
678
689
  };
679
690
  if (dialect === 'postgres') {
680
691
  return withQueryHistoryConfig(input.connection, {
@@ -688,9 +699,10 @@ async function maybeApplyHistoricSqlConfig(input) {
688
699
  redactionPatterns: input.args.queryHistoryRedactionPatterns ?? [],
689
700
  });
690
701
  }
691
- function historicSqlFiltersForSetup(patterns) {
702
+ function historicSqlFiltersForSetup(patterns, existingFilters = {}) {
692
703
  const serviceAccountPatterns = patterns ?? [];
693
704
  return {
705
+ ...existingFilters,
694
706
  dropTrivialProbes: true,
695
707
  ...(serviceAccountPatterns.length > 0
696
708
  ? {
@@ -715,29 +727,6 @@ async function defaultScanConnection(projectDir, connectionId, io) {
715
727
  dryRun: false,
716
728
  }, io);
717
729
  }
718
- function createBufferedCommandIo() {
719
- let stdout = '';
720
- let stderr = '';
721
- return {
722
- stdout: {
723
- isTTY: false,
724
- write(chunk) {
725
- stdout += chunk;
726
- },
727
- },
728
- stderr: {
729
- write(chunk) {
730
- stderr += chunk;
731
- },
732
- },
733
- stdoutText() {
734
- return stdout;
735
- },
736
- stderrText() {
737
- return stderr;
738
- },
739
- };
740
- }
741
730
  function envWithCurrentNodeFirst(env = process.env) {
742
731
  return {
743
732
  ...env,
@@ -889,6 +878,27 @@ async function disableConnectionQueryHistory(projectDir, connectionId) {
889
878
  connection: withQueryHistoryConfig(connection, { ...existing, enabled: false }),
890
879
  });
891
880
  }
881
+ function okValidateResult() {
882
+ return { status: 'ok' };
883
+ }
884
+ function backValidateResult() {
885
+ return { status: 'back' };
886
+ }
887
+ function failedValidateResult() {
888
+ return { status: 'failed' };
889
+ }
890
+ function queryHistoryUnavailableResult(projectDir, connectionId) {
891
+ return {
892
+ status: 'failed',
893
+ extraActions: [
894
+ {
895
+ value: 'disable-query-history',
896
+ label: 'Disable query history and retry',
897
+ run: () => disableConnectionQueryHistory(projectDir, connectionId),
898
+ },
899
+ ],
900
+ };
901
+ }
892
902
  async function createConnectionConfigRollback(projectDir, connectionId) {
893
903
  const project = await loadKtxProject({ projectDir });
894
904
  const previousConnection = project.config.connections[connectionId];
@@ -983,14 +993,14 @@ async function maybeConfigureDatabaseScope(input) {
983
993
  const connection = project.config.connections[input.connectionId];
984
994
  const driver = normalizeDriver(connection?.driver);
985
995
  if (!driver || driver === 'sqlite')
986
- return 'ready';
996
+ return okValidateResult();
987
997
  const spec = SCOPE_DISCOVERY_SPECS[driver];
988
998
  const existingTables = connection?.enabled_tables;
989
999
  const hasExistingTables = Array.isArray(existingTables) && existingTables.length > 0;
990
1000
  const existingScope = spec ? configuredScopeValues(connection, spec) : [];
991
1001
  const hasExistingScope = !spec || existingScope.length > 0;
992
1002
  if (hasExistingTables && hasExistingScope && input.forcePrompt !== true) {
993
- return 'ready';
1003
+ return okValidateResult();
994
1004
  }
995
1005
  const cliSchemas = input.args.databaseSchemas;
996
1006
  if (input.args.inputMode === 'disabled') {
@@ -1003,7 +1013,7 @@ async function maybeConfigureDatabaseScope(input) {
1003
1013
  catch (error) {
1004
1014
  const detail = error instanceof Error ? error.message : String(error);
1005
1015
  input.io.stderr.write(`Could not discover ${spec.promptLabel.toLowerCase()} for ${input.connectionId}; ${detail}\n`);
1006
- return 'ready';
1016
+ return okValidateResult();
1007
1017
  }
1008
1018
  }
1009
1019
  if (scopeToWrite.length > 0) {
@@ -1019,7 +1029,7 @@ async function maybeConfigureDatabaseScope(input) {
1019
1029
  ]);
1020
1030
  }
1021
1031
  }
1022
- return 'ready';
1032
+ return okValidateResult();
1023
1033
  }
1024
1034
  if (spec && cliSchemas.length > 0) {
1025
1035
  await writeScopeConfig({
@@ -1050,7 +1060,7 @@ async function maybeConfigureDatabaseScope(input) {
1050
1060
  spec,
1051
1061
  });
1052
1062
  if (typed === undefined)
1053
- return 'back';
1063
+ return backValidateResult();
1054
1064
  effectiveCliSchemas = typed;
1055
1065
  listedSchemas = typed;
1056
1066
  if (typed.length > 0) {
@@ -1065,7 +1075,7 @@ async function maybeConfigureDatabaseScope(input) {
1065
1075
  }
1066
1076
  const schemas = unique(listedSchemas);
1067
1077
  if (spec && schemas.length === 0) {
1068
- return 'ready';
1078
+ return okValidateResult();
1069
1079
  }
1070
1080
  const schemaSuggestion = effectiveCliSchemas.length > 0
1071
1081
  ? { excluded: new Set(), suggested: new Set(effectiveCliSchemas) }
@@ -1093,10 +1103,10 @@ async function maybeConfigureDatabaseScope(input) {
1093
1103
  writePrefixedLines((chunk) => input.io.stderr.write(chunk), input.forcePrompt === true
1094
1104
  ? `Could not discover tables for ${input.connectionId}; edit was not saved. ${detail}`
1095
1105
  : `Could not discover tables for ${input.connectionId}; continuing without table filter. ${detail}`);
1096
- return input.forcePrompt === true ? 'failed' : 'ready';
1106
+ return input.forcePrompt === true ? failedValidateResult() : okValidateResult();
1097
1107
  }
1098
1108
  if (pickResult.kind === 'back') {
1099
- return 'back';
1109
+ return backValidateResult();
1100
1110
  }
1101
1111
  const enabledTables = pickResult.enabledTables;
1102
1112
  const activeSchemas = pickResult.activeSchemas;
@@ -1111,7 +1121,7 @@ async function maybeConfigureDatabaseScope(input) {
1111
1121
  const refreshedProject = await loadKtxProject({ projectDir: input.projectDir });
1112
1122
  const currentConnection = refreshedProject.config.connections[input.connectionId];
1113
1123
  if (!currentConnection)
1114
- return 'ready';
1124
+ return okValidateResult();
1115
1125
  await writeConnectionConfig({
1116
1126
  projectDir: input.projectDir,
1117
1127
  connectionId: input.connectionId,
@@ -1127,7 +1137,7 @@ async function maybeConfigureDatabaseScope(input) {
1127
1137
  writeSetupSection(input.io, `Tables enabled for ${input.connectionId}`, [
1128
1138
  `✓ ${enabledTables.length} tables enabled`,
1129
1139
  ]);
1130
- return 'ready';
1140
+ return okValidateResult();
1131
1141
  }
1132
1142
  async function ensureHistoricSqlIngestDefaults(projectDir) {
1133
1143
  const project = await loadKtxProject({ projectDir });
@@ -1188,6 +1198,178 @@ async function maybeRunHistoricSqlSetupProbe(input) {
1188
1198
  }
1189
1199
  return result.ok;
1190
1200
  }
1201
+ function hasServiceAccountsBlock(connection) {
1202
+ const queryHistory = queryHistoryConfigRecord(connection);
1203
+ const filters = queryHistory?.filters;
1204
+ if (!filters || typeof filters !== 'object' || Array.isArray(filters)) {
1205
+ return false;
1206
+ }
1207
+ return 'serviceAccounts' in filters;
1208
+ }
1209
+ function printQueryHistoryFilterProposal(io, proposal, debug = false) {
1210
+ if (debug && proposal.parseFailedTemplateIds.length > 0) {
1211
+ io.stderr.write(`[debug] query-history filter picker could not parse ${proposal.parseFailedTemplateIds.length} template(s): ${proposal.parseFailedTemplateIds.join(', ')}\n`);
1212
+ }
1213
+ if (proposal.excludedRoles.length === 0) {
1214
+ if (proposal.skipped?.reason === 'no-llm') {
1215
+ io.stdout.write('│ Query-history filter picker skipped: no LLM is configured.\n');
1216
+ }
1217
+ else if (proposal.skipped?.reason === 'no-daemon') {
1218
+ io.stdout.write('│ Query-history filter picker skipped: SQL analysis is unavailable.\n');
1219
+ }
1220
+ else if (proposal.skipped?.reason === 'no-in-scope-history') {
1221
+ io.stdout.write('│ Query-history filter picker found no in-scope service-account exclusions.\n');
1222
+ }
1223
+ if (proposal.parseFailedTemplateIds.length > 0) {
1224
+ const count = proposal.parseFailedTemplateIds.length;
1225
+ io.stdout.write(`│ Skipped ${count} query template${count === 1 ? '' : 's'} ktx could not parse (run with --debug to list them).\n`);
1226
+ }
1227
+ for (const warning of proposal.warnings) {
1228
+ io.stdout.write(`│ ! ${warning}\n`);
1229
+ }
1230
+ return;
1231
+ }
1232
+ io.stdout.write('│ Proposed query-history service-account filters:\n');
1233
+ for (const excluded of proposal.excludedRoles) {
1234
+ io.stdout.write(`│ - ${excluded.role}: ${excluded.reason}\n`);
1235
+ }
1236
+ }
1237
+ async function shouldApplyQueryHistoryFilterProposal(input) {
1238
+ if (input.proposal.excludedRoles.length === 0 || input.proposal.skipped?.reason === 'user-block-present') {
1239
+ return false;
1240
+ }
1241
+ if (input.args.yes === true || input.args.inputMode === 'disabled') {
1242
+ return true;
1243
+ }
1244
+ const choice = await input.prompts.select({
1245
+ message: `Apply ${input.proposal.excludedRoles.length} derived query-history service-account exclusion${input.proposal.excludedRoles.length === 1 ? '' : 's'}?`,
1246
+ options: [
1247
+ { value: 'apply', label: 'Apply derived filters (recommended)' },
1248
+ { value: 'skip', label: 'Leave query history filters unchanged' },
1249
+ ],
1250
+ });
1251
+ return choice === 'apply';
1252
+ }
1253
+ function createSetupQueryHistoryLlmRuntime(input) {
1254
+ try {
1255
+ return (input.deps.createQueryHistoryLlmRuntime?.(input.projectDir, input.project) ??
1256
+ createLocalKtxLlmRuntimeFromConfig(input.project.config.llm, {
1257
+ projectDir: input.projectDir,
1258
+ }));
1259
+ }
1260
+ catch {
1261
+ return null;
1262
+ }
1263
+ }
1264
+ /** @internal */
1265
+ export function managedDaemonOptionsForSetupQueryHistoryPicker(input) {
1266
+ return {
1267
+ cliVersion: input.args.cliVersion ?? getKtxCliPackageInfo().version,
1268
+ projectDir: input.projectDir,
1269
+ installPolicy: input.args.runtimeInstallPolicy ?? (input.args.inputMode === 'disabled' ? 'never' : 'prompt'),
1270
+ io: input.io,
1271
+ };
1272
+ }
1273
+ async function maybeProposeQueryHistoryFilters(input) {
1274
+ const project = await loadKtxProject({ projectDir: input.projectDir });
1275
+ const connection = project.config.connections[input.connectionId];
1276
+ const queryHistory = queryHistoryConfigRecord(connection);
1277
+ if (!connection || queryHistory?.enabled !== true) {
1278
+ return;
1279
+ }
1280
+ const dialect = queryHistoryDialectForConnection(connection);
1281
+ if (!dialect) {
1282
+ return;
1283
+ }
1284
+ const picker = input.deps.queryHistoryFilterPicker ?? proposeQueryHistoryServiceAccountFilters;
1285
+ const llmRuntime = createSetupQueryHistoryLlmRuntime({
1286
+ projectDir: input.projectDir,
1287
+ project,
1288
+ deps: input.deps,
1289
+ });
1290
+ if (!llmRuntime && !input.deps.queryHistoryFilterPicker) {
1291
+ printQueryHistoryFilterProposal(input.io, {
1292
+ excludedRoles: [],
1293
+ consideredRoleCount: 0,
1294
+ skipped: { reason: 'no-llm' },
1295
+ warnings: [],
1296
+ parseFailedTemplateIds: [],
1297
+ }, input.args.debug === true);
1298
+ return;
1299
+ }
1300
+ const runtime = createKtxCliHistoricSqlRuntime(project, input.connectionId, {
1301
+ managedDaemon: managedDaemonOptionsForSetupQueryHistoryPicker({
1302
+ projectDir: input.projectDir,
1303
+ args: input.args,
1304
+ io: input.io,
1305
+ }),
1306
+ });
1307
+ if (!runtime) {
1308
+ return;
1309
+ }
1310
+ const userServiceAccountsPresent = hasServiceAccountsBlock(connection);
1311
+ const scopeFloor = await resolveQueryHistoryScopeFloor({
1312
+ projectDir: input.projectDir,
1313
+ connectionId: input.connectionId,
1314
+ driver: String(connection.driver ?? ''),
1315
+ connection: connection,
1316
+ storedQueryHistory: queryHistory,
1317
+ });
1318
+ const pullConfig = queryHistoryPullConfig({
1319
+ stored: queryHistory,
1320
+ dialect,
1321
+ enabledTables: scopeFloor.enabledTables,
1322
+ enabledSchemas: scopeFloor.enabledSchemas,
1323
+ modeledTableCatalog: scopeFloor.modeledTableCatalog,
1324
+ scopeFloorWarnings: scopeFloor.warnings,
1325
+ });
1326
+ const proposal = await picker({
1327
+ connectionId: input.connectionId,
1328
+ dialect,
1329
+ queryClient: runtime.queryClient,
1330
+ reader: runtime.reader,
1331
+ sqlAnalysis: runtime.sqlAnalysis,
1332
+ llmRuntime,
1333
+ pullConfig,
1334
+ userServiceAccountsPresent,
1335
+ });
1336
+ printQueryHistoryFilterProposal(input.io, proposal, input.args.debug === true);
1337
+ await emitTelemetryEvent({
1338
+ name: 'query_history_filter_completed',
1339
+ projectDir: input.projectDir,
1340
+ io: input.io,
1341
+ fields: {
1342
+ dialect,
1343
+ consideredRoleCount: proposal.consideredRoleCount,
1344
+ excludedRoleCount: proposal.excludedRoles.length,
1345
+ parseFailedCount: proposal.parseFailedTemplateIds.length,
1346
+ outcome: 'ok',
1347
+ },
1348
+ });
1349
+ if (proposal.skipped?.reason === 'user-block-present') {
1350
+ input.io.stdout.write('│ Existing query-history service-account filters left unchanged.\n');
1351
+ return;
1352
+ }
1353
+ if (!(await shouldApplyQueryHistoryFilterProposal({ args: input.args, prompts: input.prompts, proposal }))) {
1354
+ return;
1355
+ }
1356
+ await writeConnectionConfig({
1357
+ projectDir: input.projectDir,
1358
+ connectionId: input.connectionId,
1359
+ connection: withQueryHistoryConfig(connection, {
1360
+ ...queryHistory,
1361
+ filters: {
1362
+ ...(queryHistory.filters && typeof queryHistory.filters === 'object' && !Array.isArray(queryHistory.filters)
1363
+ ? queryHistory.filters
1364
+ : {}),
1365
+ serviceAccounts: {
1366
+ mode: 'exclude',
1367
+ patterns: proposal.excludedRoles.map((role) => role.pattern),
1368
+ },
1369
+ },
1370
+ }),
1371
+ });
1372
+ }
1191
1373
  async function applyHistoricSqlConfigToExistingConnection(input) {
1192
1374
  if (input.args.inputMode === 'disabled' &&
1193
1375
  input.args.enableQueryHistory !== true &&
@@ -1225,7 +1407,7 @@ async function validateAndScanConnection(input) {
1225
1407
  if (testCode !== 0) {
1226
1408
  flushPrefixedBufferedCommandOutput(input.io, testIo);
1227
1409
  writePrefixedLines((chunk) => input.io.stderr.write(chunk), `Connection test failed for ${input.connectionId}.`);
1228
- return 'failed';
1410
+ return failedValidateResult();
1229
1411
  }
1230
1412
  const testOutput = testIo.stdoutText();
1231
1413
  const outputDriver = normalizeDriver(readOutputValue(testOutput, 'Driver'));
@@ -1233,7 +1415,7 @@ async function validateAndScanConnection(input) {
1233
1415
  const testLines = ['✓ Connection test passed', `Driver: ${driverDisplay}`];
1234
1416
  writeSetupSection(input.io, `Testing ${input.connectionId}`, testLines);
1235
1417
  const scopeStatus = await maybeConfigureDatabaseScope({ ...input, forcePrompt: input.forceScopeAndTables });
1236
- if (scopeStatus !== 'ready') {
1418
+ if (scopeStatus.status !== 'ok') {
1237
1419
  return scopeStatus;
1238
1420
  }
1239
1421
  const queryHistoryAvailable = await maybeRunHistoricSqlSetupProbe({
@@ -1282,15 +1464,27 @@ async function validateAndScanConnection(input) {
1282
1464
  ].join('\n'));
1283
1465
  }
1284
1466
  if (scanCode !== 0) {
1285
- return queryHistoryAvailable ? 'failed' : 'failed-query-history-unavailable';
1467
+ return queryHistoryAvailable
1468
+ ? failedValidateResult()
1469
+ : queryHistoryUnavailableResult(input.projectDir, input.connectionId);
1286
1470
  }
1287
1471
  }
1288
1472
  const scanOutput = scanIo.stdoutText();
1289
1473
  writeSetupSection(input.io, `Schema context complete for ${input.connectionId}`, [`Changes: ${summarizeScanChanges(scanOutput)}`]);
1474
+ if (queryHistoryAvailable) {
1475
+ await maybeProposeQueryHistoryFilters({
1476
+ projectDir: input.projectDir,
1477
+ connectionId: input.connectionId,
1478
+ io: input.io,
1479
+ deps: input.deps,
1480
+ args: input.args,
1481
+ prompts: input.prompts,
1482
+ });
1483
+ }
1290
1484
  writeSetupSection(input.io, 'Database ready', [
1291
1485
  `${input.connectionId} · ${driverDisplay} · schema context complete`,
1292
1486
  ]);
1293
- return 'ready';
1487
+ return okValidateResult();
1294
1488
  }
1295
1489
  async function chooseDrivers(args, io, prompts, options) {
1296
1490
  if (args.databaseDrivers && args.databaseDrivers.length > 0) {
@@ -1394,64 +1588,137 @@ async function choosePrimarySourceToEdit(input) {
1394
1588
  });
1395
1589
  return choice === 'back' ? 'back' : choice;
1396
1590
  }
1397
- async function runPrimarySourceFullEdit(input) {
1591
+ async function configureDatabaseConnection(input) {
1398
1592
  const project = await loadKtxProject({ projectDir: input.projectDir });
1399
- const existing = project.config.connections[input.connectionId];
1400
- const driver = normalizeDriver(existing?.driver);
1401
- if (!existing || !driver) {
1402
- writePrefixedLines((chunk) => input.io.stderr.write(chunk), `Connection "${input.connectionId}" is not a configured database.`);
1403
- return 'failed';
1404
- }
1405
- const rollback = await createConnectionConfigRollback(input.projectDir, input.connectionId);
1406
- const replacement = await buildConnectionConfig({
1407
- driver,
1593
+ const latestConnection = project.config.connections[input.connectionId];
1594
+ let connection = await buildConnectionConfig({
1595
+ driver: input.driver,
1408
1596
  connectionId: input.connectionId,
1409
1597
  args: input.args,
1410
1598
  prompts: input.prompts,
1411
- existingConnection: existing,
1599
+ existingConnection: latestConnection,
1412
1600
  });
1413
- if (replacement === 'back') {
1414
- await rollback();
1601
+ while (!connection && input.args.inputMode !== 'disabled') {
1602
+ const action = await input.prompts.select(missingConnectionDetailsPrompt(driverLabel(input.driver), input.canReturnToDriverSelection));
1603
+ if (action === 'back') {
1604
+ return 'back';
1605
+ }
1606
+ connection = await buildConnectionConfig({
1607
+ driver: input.driver,
1608
+ connectionId: input.connectionId,
1609
+ args: input.args,
1610
+ prompts: input.prompts,
1611
+ existingConnection: latestConnection,
1612
+ });
1613
+ }
1614
+ if (connection === 'back') {
1415
1615
  return 'back';
1416
1616
  }
1417
- if (!replacement) {
1418
- await rollback();
1419
- return 'failed';
1617
+ if (!connection) {
1618
+ input.io.stderr.write(`Missing connection details for ${driverLabel(input.driver)}.\n`);
1619
+ return 'cancelled';
1420
1620
  }
1421
1621
  const withHistoricSql = await maybeApplyHistoricSqlConfig({
1422
- connection: replacement,
1423
- driver,
1622
+ connection,
1623
+ driver: input.driver,
1424
1624
  args: input.args,
1425
1625
  prompts: input.prompts,
1426
1626
  });
1427
1627
  if (withHistoricSql === 'back') {
1428
- await rollback();
1429
1628
  return 'back';
1430
1629
  }
1431
1630
  await writeConnectionConfig({
1432
1631
  projectDir: input.projectDir,
1433
1632
  connectionId: input.connectionId,
1434
- connection: withExistingPrimaryEditPromptDefaults({
1435
- previous: existing,
1436
- next: withHistoricSql,
1437
- driver,
1438
- }),
1633
+ connection: input.editBaseline
1634
+ ? withExistingPrimaryEditPromptDefaults({
1635
+ previous: input.editBaseline,
1636
+ next: withHistoricSql,
1637
+ driver: input.driver,
1638
+ })
1639
+ : withHistoricSql,
1439
1640
  io: input.io,
1440
1641
  });
1441
- const validated = await validateAndScanConnection({
1642
+ return 'configured';
1643
+ }
1644
+ async function runDatabaseConnectionSetupWithRecovery(input) {
1645
+ let configureCalls = 0;
1646
+ // `configureDatabaseConnection` returns 'cancelled' only when required
1647
+ // connection details are absent in non-interactive mode. The recovery
1648
+ // primitive collapses that into 'failed', so we track it here to restore the
1649
+ // distinct 'missing-input' outcome the surrounding step reports for
1650
+ // incomplete flags (vs. a real connection/probe failure).
1651
+ let sawMissingInput = false;
1652
+ const outcome = await runConnectionSetupWithRecovery({
1653
+ label: input.connectionId,
1654
+ interactive: input.interactive ?? input.args.inputMode !== 'disabled',
1655
+ allowSkip: input.allowSkip,
1656
+ io: input.io,
1657
+ prompts: input.prompts,
1658
+ snapshot: () => createConnectionConfigRollback(input.projectDir, input.connectionId),
1659
+ configure: async () => {
1660
+ configureCalls += 1;
1661
+ if (input.reuseExistingOnFirstConfigure && configureCalls === 1) {
1662
+ const historicSqlResult = await applyHistoricSqlConfigToExistingConnection({
1663
+ projectDir: input.projectDir,
1664
+ connectionId: input.connectionId,
1665
+ args: input.args,
1666
+ prompts: input.prompts,
1667
+ });
1668
+ return historicSqlResult === 'back' ? 'back' : 'configured';
1669
+ }
1670
+ const configured = await configureDatabaseConnection({
1671
+ projectDir: input.projectDir,
1672
+ connectionId: input.connectionId,
1673
+ driver: input.driver,
1674
+ args: input.args,
1675
+ prompts: input.prompts,
1676
+ io: input.io,
1677
+ canReturnToDriverSelection: input.canReturnToDriverSelection,
1678
+ editBaseline: input.editBaseline,
1679
+ });
1680
+ if (configured === 'cancelled') {
1681
+ sawMissingInput = true;
1682
+ }
1683
+ return configured;
1684
+ },
1685
+ validate: () => validateAndScanConnection({
1686
+ projectDir: input.projectDir,
1687
+ connectionId: input.connectionId,
1688
+ io: input.io,
1689
+ deps: input.deps,
1690
+ args: input.args,
1691
+ prompts: input.prompts,
1692
+ forceScopeAndTables: input.forceScopeAndTables,
1693
+ }),
1694
+ });
1695
+ if (outcome === 'failed' && sawMissingInput) {
1696
+ return 'missing-input';
1697
+ }
1698
+ return outcome;
1699
+ }
1700
+ async function runPrimarySourceFullEdit(input) {
1701
+ const project = await loadKtxProject({ projectDir: input.projectDir });
1702
+ const existing = project.config.connections[input.connectionId];
1703
+ const driver = normalizeDriver(existing?.driver);
1704
+ if (!existing || !driver) {
1705
+ writePrefixedLines((chunk) => input.io.stderr.write(chunk), `Connection "${input.connectionId}" is not a configured database.`);
1706
+ return 'failed';
1707
+ }
1708
+ const outcome = await runDatabaseConnectionSetupWithRecovery({
1442
1709
  projectDir: input.projectDir,
1443
1710
  connectionId: input.connectionId,
1444
- io: input.io,
1445
- deps: input.deps,
1711
+ driver,
1446
1712
  args: input.args,
1447
1713
  prompts: input.prompts,
1714
+ io: input.io,
1715
+ deps: input.deps,
1716
+ canReturnToDriverSelection: true,
1717
+ allowSkip: false,
1448
1718
  forceScopeAndTables: true,
1719
+ editBaseline: existing,
1449
1720
  });
1450
- if (validated !== 'ready') {
1451
- await rollback();
1452
- return validated === 'failed-query-history-unavailable' ? 'failed' : validated;
1453
- }
1454
- return 'ready';
1721
+ return outcome === 'skip' ? 'back' : outcome;
1455
1722
  }
1456
1723
  export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
1457
1724
  if (args.skipDatabases) {
@@ -1462,29 +1729,37 @@ export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
1462
1729
  if (args.databaseConnectionIds && args.databaseConnectionIds.length > 0) {
1463
1730
  const selectedConnectionIds = [];
1464
1731
  for (const connectionId of unique(args.databaseConnectionIds)) {
1465
- const historicSqlResult = await applyHistoricSqlConfigToExistingConnection({
1732
+ const project = await loadKtxProject({ projectDir: args.projectDir });
1733
+ const driver = normalizeDriver(project.config.connections[connectionId]?.driver);
1734
+ if (!driver) {
1735
+ writePrefixedLines((chunk) => io.stderr.write(chunk), `Connection "${connectionId}" is not configured.`);
1736
+ return { status: 'failed', projectDir: args.projectDir };
1737
+ }
1738
+ const setupOutcome = await runDatabaseConnectionSetupWithRecovery({
1466
1739
  projectDir: args.projectDir,
1467
1740
  connectionId,
1741
+ driver,
1468
1742
  args,
1469
1743
  prompts,
1470
- });
1471
- if (historicSqlResult === 'back')
1472
- return { status: 'back', projectDir: args.projectDir };
1473
- const setupStatus = await validateAndScanConnection({
1474
- projectDir: args.projectDir,
1475
- connectionId,
1476
1744
  io,
1477
1745
  deps,
1478
- args,
1479
- prompts,
1746
+ canReturnToDriverSelection: false,
1747
+ allowSkip: false,
1748
+ interactive: false,
1749
+ reuseExistingOnFirstConfigure: true,
1480
1750
  });
1481
- if (setupStatus === 'back') {
1751
+ if (setupOutcome === 'back') {
1482
1752
  return { status: 'back', projectDir: args.projectDir };
1483
1753
  }
1484
- if (setupStatus === 'failed') {
1754
+ if (setupOutcome === 'missing-input') {
1755
+ return { status: 'missing-input', projectDir: args.projectDir };
1756
+ }
1757
+ if (setupOutcome === 'failed') {
1485
1758
  return { status: 'failed', projectDir: args.projectDir };
1486
1759
  }
1487
- selectedConnectionIds.push(connectionId);
1760
+ if (setupOutcome === 'ready') {
1761
+ selectedConnectionIds.push(connectionId);
1762
+ }
1488
1763
  }
1489
1764
  await markDatabasesComplete(args.projectDir, selectedConnectionIds);
1490
1765
  return { status: 'ready', projectDir: args.projectDir, connectionIds: selectedConnectionIds };
@@ -1533,6 +1808,9 @@ export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
1533
1808
  showConfiguredPrimaryMenu = true;
1534
1809
  continue;
1535
1810
  }
1811
+ if (editResult === 'missing-input') {
1812
+ return { status: 'missing-input', projectDir: args.projectDir };
1813
+ }
1536
1814
  if (editResult === 'failed') {
1537
1815
  return { status: 'failed', projectDir: args.projectDir };
1538
1816
  }
@@ -1587,7 +1865,6 @@ export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
1587
1865
  io.stderr.write('Missing database connection id: pass --database-connection-id.\n');
1588
1866
  return { status: 'missing-input', projectDir: args.projectDir };
1589
1867
  }
1590
- let connectionAlreadyValidated = false;
1591
1868
  if (connectionChoice.kind === 'edit') {
1592
1869
  const editResult = await runPrimarySourceFullEdit({
1593
1870
  projectDir: args.projectDir,
@@ -1603,192 +1880,44 @@ export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
1603
1880
  returnToDriverSelection = true;
1604
1881
  break;
1605
1882
  }
1606
- if (editResult === 'failed') {
1607
- return { status: 'failed', projectDir: args.projectDir };
1608
- }
1609
- connectionAlreadyValidated = true;
1610
- }
1611
- else if (connectionChoice.kind === 'new') {
1612
- let connection = await buildConnectionConfig({
1613
- driver,
1614
- connectionId: connectionChoice.connectionId,
1615
- args,
1616
- prompts,
1617
- });
1618
- if (connection === 'back') {
1619
- if (!canReturnToDriverSelection)
1620
- return { status: 'back', projectDir: args.projectDir };
1621
- returnToDriverSelection = true;
1622
- break;
1623
- }
1624
- while (!connection && args.inputMode !== 'disabled') {
1625
- const label = driverLabel(driver);
1626
- const action = await prompts.select(missingConnectionDetailsPrompt(label, canReturnToDriverSelection));
1627
- if (action === 'back') {
1628
- if (!canReturnToDriverSelection)
1629
- return { status: 'back', projectDir: args.projectDir };
1630
- returnToDriverSelection = true;
1631
- break;
1632
- }
1633
- connection = await buildConnectionConfig({
1634
- driver,
1635
- connectionId: connectionChoice.connectionId,
1636
- args,
1637
- prompts,
1638
- });
1639
- if (connection === 'back') {
1640
- if (!canReturnToDriverSelection)
1641
- return { status: 'back', projectDir: args.projectDir };
1642
- returnToDriverSelection = true;
1643
- break;
1644
- }
1645
- }
1646
- if (returnToDriverSelection) {
1647
- break;
1648
- }
1649
- if (connection === 'back') {
1650
- break;
1651
- }
1652
- if (!connection) {
1653
- io.stderr.write(`Missing connection details for ${driverLabel(driver)}.\n`);
1883
+ if (editResult === 'missing-input') {
1654
1884
  return { status: 'missing-input', projectDir: args.projectDir };
1655
1885
  }
1656
- const withHistoricSql = await maybeApplyHistoricSqlConfig({ connection, driver, args, prompts });
1657
- if (withHistoricSql === 'back') {
1658
- if (!canReturnToDriverSelection)
1659
- return { status: 'back', projectDir: args.projectDir };
1660
- returnToDriverSelection = true;
1661
- break;
1886
+ if (editResult === 'failed') {
1887
+ return { status: 'failed', projectDir: args.projectDir };
1662
1888
  }
1663
- await writeConnectionConfig({
1664
- projectDir: args.projectDir,
1665
- connectionId: connectionChoice.connectionId,
1666
- connection: withHistoricSql,
1667
- io,
1668
- });
1669
1889
  }
1670
1890
  else {
1671
- const existing = project.config.connections[connectionChoice.connectionId];
1672
- const withHistoricSql = await maybeApplyHistoricSqlConfig({ connection: existing, driver, args, prompts });
1673
- if (withHistoricSql === 'back') {
1674
- if (!canReturnToDriverSelection)
1675
- return { status: 'back', projectDir: args.projectDir };
1676
- returnToDriverSelection = true;
1677
- break;
1678
- }
1679
- await writeConnectionConfig({
1680
- projectDir: args.projectDir,
1681
- connectionId: connectionChoice.connectionId,
1682
- connection: withHistoricSql,
1683
- io,
1684
- });
1685
- }
1686
- let connectionSkipped = false;
1687
- let setupStatus = connectionAlreadyValidated
1688
- ? 'ready'
1689
- : await validateAndScanConnection({
1891
+ const setupOutcome = await runDatabaseConnectionSetupWithRecovery({
1690
1892
  projectDir: args.projectDir,
1691
1893
  connectionId: connectionChoice.connectionId,
1692
- io,
1693
- deps,
1894
+ driver,
1694
1895
  args,
1695
1896
  prompts,
1897
+ io,
1898
+ deps,
1899
+ canReturnToDriverSelection,
1900
+ allowSkip: true,
1901
+ reuseExistingOnFirstConfigure: connectionChoice.kind === 'existing',
1696
1902
  });
1697
- while (!connectionAlreadyValidated && setupStatus !== 'ready') {
1698
- if (setupStatus === 'back') {
1699
- if (!canReturnToDriverSelection)
1700
- return { status: 'back', projectDir: args.projectDir };
1701
- returnToDriverSelection = true;
1702
- break;
1703
- }
1704
- if (args.inputMode === 'disabled')
1705
- return { status: 'failed', projectDir: args.projectDir };
1706
- const failureOptions = [
1707
- { value: 'retry', label: 'Retry connection test' },
1708
- { value: 're-enter', label: 'Re-enter connection details' },
1709
- ...(setupStatus === 'failed-query-history-unavailable'
1710
- ? [{ value: 'disable-query-history', label: 'Disable query history and retry' }]
1711
- : []),
1712
- { value: 'skip', label: 'Skip this database' },
1713
- { value: 'back', label: 'Back' },
1714
- ];
1715
- const action = await prompts.select({
1716
- message: `Database setup failed for ${connectionChoice.connectionId}`,
1717
- options: failureOptions,
1718
- });
1719
- if (action === 'back') {
1903
+ if (setupOutcome === 'back') {
1720
1904
  if (!canReturnToDriverSelection)
1721
1905
  return { status: 'back', projectDir: args.projectDir };
1722
1906
  returnToDriverSelection = true;
1723
1907
  break;
1724
1908
  }
1725
- if (action === 'skip') {
1726
- connectionSkipped = true;
1727
- break;
1728
- }
1729
- if (action === 'retry') {
1730
- setupStatus = await validateAndScanConnection({
1731
- projectDir: args.projectDir,
1732
- connectionId: connectionChoice.connectionId,
1733
- io,
1734
- deps,
1735
- args,
1736
- prompts,
1737
- });
1909
+ if (setupOutcome === 'missing-input') {
1910
+ return { status: 'missing-input', projectDir: args.projectDir };
1738
1911
  }
1739
- else if (action === 'disable-query-history') {
1740
- await disableConnectionQueryHistory(args.projectDir, connectionChoice.connectionId);
1741
- setupStatus = await validateAndScanConnection({
1742
- projectDir: args.projectDir,
1743
- connectionId: connectionChoice.connectionId,
1744
- io,
1745
- deps,
1746
- args,
1747
- prompts,
1748
- });
1912
+ if (setupOutcome === 'failed') {
1913
+ return { status: 'failed', projectDir: args.projectDir };
1749
1914
  }
1750
- else if (action === 're-enter') {
1751
- const connection = await buildConnectionConfig({
1752
- driver,
1753
- connectionId: connectionChoice.connectionId,
1754
- args,
1755
- prompts,
1756
- });
1757
- if (connection === 'back') {
1758
- if (!canReturnToDriverSelection)
1759
- return { status: 'back', projectDir: args.projectDir };
1760
- returnToDriverSelection = true;
1761
- break;
1762
- }
1763
- if (!connection)
1764
- continue;
1765
- const withHistoricSql = await maybeApplyHistoricSqlConfig({ connection, driver, args, prompts });
1766
- if (withHistoricSql === 'back') {
1767
- if (!canReturnToDriverSelection)
1768
- return { status: 'back', projectDir: args.projectDir };
1769
- returnToDriverSelection = true;
1770
- break;
1771
- }
1772
- await writeConnectionConfig({
1773
- projectDir: args.projectDir,
1774
- connectionId: connectionChoice.connectionId,
1775
- connection: withHistoricSql,
1776
- io,
1777
- });
1778
- setupStatus = await validateAndScanConnection({
1779
- projectDir: args.projectDir,
1780
- connectionId: connectionChoice.connectionId,
1781
- io,
1782
- deps,
1783
- args,
1784
- prompts,
1785
- });
1915
+ if (setupOutcome === 'skip') {
1916
+ continue;
1786
1917
  }
1787
1918
  }
1788
1919
  if (returnToDriverSelection)
1789
1920
  break;
1790
- if (connectionSkipped)
1791
- continue;
1792
1921
  pushUniqueConnectionId(selectedConnectionIds, connectionChoice.connectionId);
1793
1922
  }
1794
1923
  if (returnToDriverSelection) {