@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.
- package/assets/python/{kaelio_ktx-0.8.0-py3-none-any.whl → kaelio_ktx-0.10.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +17 -2
- package/dist/cli-program.d.ts +3 -0
- package/dist/cli-program.js +42 -2
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.js +94 -3
- package/dist/commands/setup-commands.js +3 -4
- package/dist/connection-recovery.d.ts +34 -0
- package/dist/connection-recovery.js +82 -0
- package/dist/connection.js +26 -2
- package/dist/connectors/bigquery/connector.d.ts +2 -5
- package/dist/connectors/bigquery/connector.js +2 -2
- package/dist/connectors/clickhouse/connector.d.ts +2 -5
- package/dist/connectors/clickhouse/connector.js +2 -2
- package/dist/connectors/mysql/connector.d.ts +7 -6
- package/dist/connectors/mysql/connector.js +25 -5
- package/dist/connectors/mysql/dialect.d.ts +1 -1
- package/dist/connectors/mysql/dialect.js +12 -2
- package/dist/connectors/postgres/connector.d.ts +2 -5
- package/dist/connectors/postgres/connector.js +2 -2
- package/dist/connectors/snowflake/connector.d.ts +2 -5
- package/dist/connectors/snowflake/connector.js +2 -2
- package/dist/connectors/sqlite/connector.d.ts +2 -5
- package/dist/connectors/sqlite/connector.js +2 -2
- package/dist/connectors/sqlserver/connector.d.ts +2 -5
- package/dist/connectors/sqlserver/connector.js +2 -2
- package/dist/context/connections/drivers.d.ts +0 -1
- package/dist/context/connections/drivers.js +0 -7
- package/dist/context/connections/query-executor.d.ts +2 -1
- package/dist/context/core/abort.d.ts +9 -0
- package/dist/context/core/abort.js +36 -0
- package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
- package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
- package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +30 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +194 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
- package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
- package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
- package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
- package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
- package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
- package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
- package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
- package/dist/context/ingest/final-gate-repair.d.ts +1 -0
- package/dist/context/ingest/final-gate-repair.js +1 -0
- package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
- package/dist/context/ingest/ingest-bundle.runner.js +127 -53
- package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
- package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
- package/dist/context/ingest/local-adapters.js +21 -4
- package/dist/context/ingest/local-bundle-runtime.js +13 -5
- package/dist/context/ingest/local-ingest.d.ts +1 -0
- package/dist/context/ingest/local-ingest.js +13 -3
- package/dist/context/ingest/memory-flow/events.js +1 -1
- package/dist/context/ingest/memory-flow/schema.js +8 -3
- package/dist/context/ingest/memory-flow/types.d.ts +7 -3
- package/dist/context/ingest/ports.d.ts +3 -5
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
- package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
- package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
- package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
- package/dist/context/ingest/types.d.ts +1 -0
- package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
- package/dist/context/llm/ai-sdk-runtime.js +152 -16
- package/dist/context/llm/claude-code-runtime.d.ts +6 -4
- package/dist/context/llm/claude-code-runtime.js +127 -48
- package/dist/context/llm/codex-exec-events.d.ts +20 -0
- package/dist/context/llm/codex-exec-events.js +155 -0
- package/dist/context/llm/codex-isolation.d.ts +3 -0
- package/dist/context/llm/codex-isolation.js +5 -0
- package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
- package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
- package/dist/context/llm/codex-models.d.ts +2 -0
- package/dist/context/llm/codex-models.js +17 -0
- package/dist/context/llm/codex-runtime-config.d.ts +16 -0
- package/dist/context/llm/codex-runtime-config.js +19 -0
- package/dist/context/llm/codex-runtime.d.ts +37 -0
- package/dist/context/llm/codex-runtime.js +347 -0
- package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
- package/dist/context/llm/codex-sdk-runner.js +63 -0
- package/dist/context/llm/local-config.d.ts +16 -4
- package/dist/context/llm/local-config.js +18 -2
- package/dist/context/llm/rate-limit-governor.d.ts +103 -0
- package/dist/context/llm/rate-limit-governor.js +285 -0
- package/dist/context/llm/runtime-port.d.ts +3 -6
- package/dist/context/mcp/context-tools.js +43 -13
- package/dist/context/project/config.d.ts +14 -0
- package/dist/context/project/config.js +37 -2
- package/dist/context/scan/types.d.ts +15 -2
- package/dist/context/scan/types.js +12 -0
- package/dist/context/sl/description-normalization.js +4 -14
- package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
- package/dist/context/sql-analysis/ports.d.ts +12 -2
- package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
- package/dist/context-build-view.d.ts +13 -0
- package/dist/context-build-view.js +63 -32
- package/dist/demo-metrics.d.ts +0 -2
- package/dist/demo-metrics.js +1 -11
- package/dist/ingest.d.ts +1 -0
- package/dist/ingest.js +32 -3
- package/dist/io/buffered-command-io.d.ts +11 -0
- package/dist/io/buffered-command-io.js +28 -0
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/llm/types.d.ts +1 -1
- package/dist/local-adapters.d.ts +10 -2
- package/dist/local-adapters.js +19 -3
- package/dist/memory-flow-hud.js +8 -16
- package/dist/next-steps.js +1 -2
- package/dist/progress-port-adapter.d.ts +6 -0
- package/dist/progress-port-adapter.js +18 -0
- package/dist/public-ingest.d.ts +20 -1
- package/dist/public-ingest.js +228 -42
- package/dist/reveal-password-prompt.d.ts +24 -0
- package/dist/reveal-password-prompt.js +78 -0
- package/dist/scan.js +21 -3
- package/dist/setup-context.d.ts +2 -0
- package/dist/setup-context.js +133 -27
- package/dist/setup-databases.d.ts +18 -1
- package/dist/setup-databases.js +378 -249
- package/dist/setup-demo-tour.js +1 -0
- package/dist/setup-embeddings.js +1 -1
- package/dist/setup-models.d.ts +11 -15
- package/dist/setup-models.js +140 -276
- package/dist/setup-prompts.js +3 -2
- package/dist/setup-ready-menu.d.ts +16 -2
- package/dist/setup-ready-menu.js +37 -5
- package/dist/setup-sources.js +115 -35
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +23 -11
- package/dist/sl.d.ts +2 -2
- package/dist/sl.js +20 -4
- package/dist/sql.js +18 -2
- package/dist/star-prompt/cache.d.ts +16 -0
- package/dist/star-prompt/cache.js +45 -0
- package/dist/star-prompt/star-count.d.ts +7 -0
- package/dist/star-prompt/star-count.js +66 -0
- package/dist/star-prompt/star-line.d.ts +12 -0
- package/dist/star-prompt/star-line.js +26 -0
- package/dist/status-project.d.ts +11 -0
- package/dist/status-project.js +50 -1
- package/dist/telemetry/command-hook.d.ts +1 -0
- package/dist/telemetry/command-hook.js +3 -1
- package/dist/telemetry/emitter.d.ts +10 -0
- package/dist/telemetry/emitter.js +31 -0
- package/dist/telemetry/events.d.ts +35 -6
- package/dist/telemetry/events.js +25 -2
- package/dist/telemetry/exception.d.ts +18 -0
- package/dist/telemetry/exception.js +162 -0
- package/dist/telemetry/identity.d.ts +0 -1
- package/dist/telemetry/identity.js +6 -6
- package/dist/telemetry/index.d.ts +15 -2
- package/dist/telemetry/index.js +15 -3
- package/dist/telemetry/redaction-secrets.d.ts +11 -0
- package/dist/telemetry/redaction-secrets.js +92 -0
- package/dist/telemetry/scrubber.d.ts +10 -0
- package/dist/telemetry/scrubber.js +20 -0
- package/dist/update-check/cache.d.ts +21 -0
- package/dist/update-check/cache.js +38 -0
- package/dist/update-check/channel.d.ts +15 -0
- package/dist/update-check/channel.js +30 -0
- package/dist/update-check/registry.d.ts +1 -0
- package/dist/update-check/registry.js +45 -0
- package/dist/update-check/update-check.d.ts +43 -0
- package/dist/update-check/update-check.js +116 -0
- package/package.json +12 -4
- package/dist/context/connections/local-query-executor.d.ts +0 -6
- package/dist/context/connections/local-query-executor.js +0 -39
- package/dist/context/connections/postgres-query-executor.d.ts +0 -25
- package/dist/context/connections/postgres-query-executor.js +0 -53
- package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
- package/dist/context/connections/sqlite-query-executor.js +0 -74
package/dist/setup-databases.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ?
|
|
1106
|
+
return input.forcePrompt === true ? failedValidateResult() : okValidateResult();
|
|
1097
1107
|
}
|
|
1098
1108
|
if (pickResult.kind === 'back') {
|
|
1099
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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 !== '
|
|
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
|
|
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
|
|
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
|
|
1591
|
+
async function configureDatabaseConnection(input) {
|
|
1398
1592
|
const project = await loadKtxProject({ projectDir: input.projectDir });
|
|
1399
|
-
const
|
|
1400
|
-
|
|
1401
|
-
|
|
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:
|
|
1599
|
+
existingConnection: latestConnection,
|
|
1412
1600
|
});
|
|
1413
|
-
|
|
1414
|
-
await
|
|
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 (!
|
|
1418
|
-
|
|
1419
|
-
return '
|
|
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
|
|
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:
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1479
|
-
|
|
1746
|
+
canReturnToDriverSelection: false,
|
|
1747
|
+
allowSkip: false,
|
|
1748
|
+
interactive: false,
|
|
1749
|
+
reuseExistingOnFirstConfigure: true,
|
|
1480
1750
|
});
|
|
1481
|
-
if (
|
|
1751
|
+
if (setupOutcome === 'back') {
|
|
1482
1752
|
return { status: 'back', projectDir: args.projectDir };
|
|
1483
1753
|
}
|
|
1484
|
-
if (
|
|
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
|
-
|
|
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 === '
|
|
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
|
-
|
|
1657
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1726
|
-
|
|
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
|
-
|
|
1740
|
-
|
|
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
|
-
|
|
1751
|
-
|
|
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) {
|