@kaelio/ktx 0.7.0 → 0.9.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.7.0-py3-none-any.whl → kaelio_ktx-0.9.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli-program.js +7 -0
- package/dist/cli-runtime.js +50 -3
- package/dist/command-schemas.d.ts +1 -1
- package/dist/command-tree.js +5 -1
- package/dist/commands/completion-commands.d.ts +3 -0
- package/dist/commands/completion-commands.js +38 -0
- package/dist/commands/ingest-commands.js +0 -4
- package/dist/commands/knowledge-commands.js +15 -2
- package/dist/commands/setup-commands.js +3 -3
- package/dist/commands/sl-commands.js +19 -7
- package/dist/completion/complete-engine.d.ts +19 -0
- package/dist/completion/complete-engine.js +128 -0
- package/dist/completion/completion-scripts.d.ts +1 -0
- package/dist/completion/completion-scripts.js +36 -0
- package/dist/completion/dynamic-candidates.d.ts +6 -0
- package/dist/completion/dynamic-candidates.js +98 -0
- package/dist/connection-drivers.d.ts +3 -0
- package/dist/connection-drivers.js +17 -0
- package/dist/connection-recovery.d.ts +34 -0
- package/dist/connection-recovery.js +82 -0
- package/dist/connection.js +3 -1
- 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 +29 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +190 -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-evidence/sqlite-context-evidence-store.d.ts +1 -1
- package/dist/context/ingest/ingest-bundle.runner.d.ts +8 -0
- package/dist/context/ingest/ingest-bundle.runner.js +72 -15
- package/dist/context/ingest/ingest-profile.d.ts +102 -0
- package/dist/context/ingest/ingest-profile.js +306 -0
- package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
- package/dist/context/ingest/isolated-diff/work-unit-executor.js +25 -2
- package/dist/context/ingest/local-adapters.js +21 -4
- package/dist/context/ingest/local-bundle-runtime.js +4 -2
- package/dist/context/ingest/local-ingest.d.ts +1 -1
- package/dist/context/ingest/local-ingest.js +6 -4
- package/dist/context/ingest/memory-flow/events.js +2 -1
- package/dist/context/ingest/ports.d.ts +2 -0
- package/dist/context/ingest/reports.d.ts +3 -0
- package/dist/context/ingest/reports.js +10 -0
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +3 -1
- package/dist/context/ingest/stages/stage-3-work-units.js +2 -0
- package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +2 -1
- package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
- package/dist/context/ingest/tools/tool-call-logger.d.ts +6 -0
- package/dist/context/ingest/tools/tool-call-logger.js +36 -1
- package/dist/context/llm/ai-sdk-runtime.js +32 -3
- package/dist/context/llm/claude-code-runtime.js +35 -2
- 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 +304 -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 +2 -0
- package/dist/context/llm/local-config.js +12 -1
- package/dist/context/llm/runtime-port.d.ts +25 -0
- package/dist/context/mcp/context-tools.d.ts +2 -1
- package/dist/context/mcp/context-tools.js +82 -15
- package/dist/context/mcp/server.js +4 -0
- package/dist/context/mcp/types.d.ts +15 -1
- package/dist/context/project/config.d.ts +3 -0
- package/dist/context/project/config.js +6 -2
- package/dist/context/project/driver-schemas.js +1 -1
- package/dist/context/search/discover.js +4 -3
- package/dist/context/sl/local-sl.d.ts +15 -0
- package/dist/context/sl/local-sl.js +30 -0
- 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/wiki/local-knowledge.d.ts +10 -0
- package/dist/context/wiki/local-knowledge.js +22 -0
- package/dist/context-build-view.d.ts +0 -3
- package/dist/context-build-view.js +5 -39
- package/dist/ingest.js +7 -10
- package/dist/io/buffered-command-io.d.ts +11 -0
- package/dist/io/buffered-command-io.js +28 -0
- package/dist/knowledge.d.ts +5 -0
- package/dist/knowledge.js +10 -1
- 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/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-copy.js +1 -1
- package/dist/public-ingest.d.ts +20 -8
- package/dist/public-ingest.js +198 -61
- package/dist/scan.js +3 -1
- package/dist/setup-context.d.ts +2 -0
- package/dist/setup-context.js +138 -64
- package/dist/setup-databases.d.ts +17 -1
- package/dist/setup-databases.js +366 -326
- package/dist/setup-models.d.ts +10 -1
- package/dist/setup-models.js +90 -2
- package/dist/setup-ready-menu.d.ts +16 -2
- package/dist/setup-ready-menu.js +37 -5
- package/dist/setup-sources.js +141 -33
- package/dist/setup.js +24 -12
- package/dist/skills/analytics/SKILL.md +6 -1
- package/dist/sl.d.ts +6 -1
- package/dist/sl.js +32 -8
- 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.js +1 -1
- package/dist/telemetry/events.d.ts +15 -9
- package/dist/telemetry/events.js +17 -5
- package/dist/telemetry/identity.d.ts +1 -2
- package/dist/telemetry/identity.js +13 -10
- package/dist/telemetry/index.d.ts +13 -1
- package/dist/telemetry/index.js +18 -3
- package/dist/telemetry/scrubber.d.ts +10 -0
- package/dist/telemetry/scrubber.js +20 -0
- package/package.json +20 -19
- package/dist/ingest-depth.d.ts +0 -8
- package/dist/ingest-depth.js +0 -56
- package/dist/setup-database-context-depth.d.ts +0 -23
- package/dist/setup-database-context-depth.js +0 -84
package/dist/public-ingest.d.ts
CHANGED
|
@@ -2,14 +2,13 @@ import { type KtxLocalProject } from './context/project/project.js';
|
|
|
2
2
|
import type { KtxProgressPort } from './context/scan/types.js';
|
|
3
3
|
import type { KtxCliIo } from './index.js';
|
|
4
4
|
import type { KtxIngestArgs, KtxIngestDeps, KtxIngestProgressUpdate } from './ingest.js';
|
|
5
|
-
import { type KtxDatabaseContextDepth } from './ingest-depth.js';
|
|
6
5
|
import { type KtxManagedPythonInstallPolicy, type ManagedPythonCommandRuntime } from './managed-python-command.js';
|
|
7
6
|
import type { KtxRuntimeFeature } from './managed-python-runtime.js';
|
|
8
7
|
import type { KtxScanArgs, KtxScanDeps } from './scan.js';
|
|
8
|
+
import type { KtxTableRef } from './context/scan/types.js';
|
|
9
9
|
type KtxPublicIngestStepName = 'database-schema' | 'query-history' | 'source-ingest' | 'memory-update';
|
|
10
10
|
type KtxPublicIngestStepStatus = 'done' | 'skipped' | 'failed' | 'not-run';
|
|
11
11
|
type KtxPublicIngestInputMode = 'auto' | 'disabled';
|
|
12
|
-
type KtxPublicIngestDepth = KtxDatabaseContextDepth;
|
|
13
12
|
type KtxPublicIngestQueryHistoryFlag = 'default' | 'enabled' | 'disabled';
|
|
14
13
|
type HistoricSqlDialect = 'postgres' | 'bigquery' | 'snowflake';
|
|
15
14
|
export type KtxPublicIngestArgs = {
|
|
@@ -19,7 +18,6 @@ export type KtxPublicIngestArgs = {
|
|
|
19
18
|
all: boolean;
|
|
20
19
|
json: boolean;
|
|
21
20
|
inputMode: KtxPublicIngestInputMode;
|
|
22
|
-
depth?: KtxPublicIngestDepth;
|
|
23
21
|
queryHistory?: KtxPublicIngestQueryHistoryFlag;
|
|
24
22
|
queryHistoryWindowDays?: number;
|
|
25
23
|
scanMode?: Extract<KtxScanArgs, {
|
|
@@ -37,7 +35,6 @@ export interface KtxPublicIngestPlanTarget {
|
|
|
37
35
|
sourceDir?: string;
|
|
38
36
|
debugCommand: string;
|
|
39
37
|
steps: KtxPublicIngestStepName[];
|
|
40
|
-
databaseDepth?: KtxPublicIngestDepth;
|
|
41
38
|
detectRelationships?: boolean;
|
|
42
39
|
preflightFailure?: string;
|
|
43
40
|
queryHistory?: {
|
|
@@ -46,7 +43,6 @@ export interface KtxPublicIngestPlanTarget {
|
|
|
46
43
|
windowDays?: number;
|
|
47
44
|
pullConfig?: Record<string, unknown>;
|
|
48
45
|
unsupported?: boolean;
|
|
49
|
-
skippedStoredByFast?: boolean;
|
|
50
46
|
};
|
|
51
47
|
}
|
|
52
48
|
export interface KtxPublicIngestPlan {
|
|
@@ -94,7 +90,6 @@ interface KtxPublicContextBuildArgs {
|
|
|
94
90
|
inputMode: 'auto' | 'disabled';
|
|
95
91
|
targetConnectionId?: string;
|
|
96
92
|
all?: boolean;
|
|
97
|
-
depth?: KtxPublicIngestDepth;
|
|
98
93
|
queryHistory?: KtxPublicIngestQueryHistoryFlag;
|
|
99
94
|
queryHistoryWindowDays?: number;
|
|
100
95
|
scanMode?: Extract<KtxScanArgs, {
|
|
@@ -104,19 +99,36 @@ interface KtxPublicContextBuildArgs {
|
|
|
104
99
|
cliVersion?: string;
|
|
105
100
|
runtimeInstallPolicy?: KtxManagedPythonInstallPolicy;
|
|
106
101
|
}
|
|
102
|
+
export declare function publicProgressMessage(message: string, target: KtxPublicIngestPlanTarget): string;
|
|
103
|
+
/** @internal */
|
|
104
|
+
export declare function queryHistoryPullConfig(input: {
|
|
105
|
+
stored: Record<string, unknown>;
|
|
106
|
+
dialect: HistoricSqlDialect;
|
|
107
|
+
windowDays?: number;
|
|
108
|
+
enabledTables?: KtxTableRef[];
|
|
109
|
+
enabledSchemas?: string[];
|
|
110
|
+
modeledTableCatalog?: KtxTableRef[];
|
|
111
|
+
scopeFloorWarnings?: string[];
|
|
112
|
+
}): Record<string, unknown>;
|
|
107
113
|
export declare function buildPublicIngestPlan(project: KtxPublicIngestProject, args: {
|
|
108
114
|
projectDir: string;
|
|
109
115
|
targetConnectionId?: string;
|
|
110
116
|
all: boolean;
|
|
111
|
-
depth?: KtxPublicIngestDepth;
|
|
112
117
|
queryHistory?: KtxPublicIngestQueryHistoryFlag;
|
|
113
118
|
queryHistoryWindowDays?: number;
|
|
114
119
|
scanMode?: Extract<KtxScanArgs, {
|
|
115
120
|
command: 'run';
|
|
116
121
|
}>['mode'];
|
|
117
122
|
}): KtxPublicIngestPlan;
|
|
123
|
+
/**
|
|
124
|
+
* Run one ingest target through its scan/ingest steps. The single per-target
|
|
125
|
+
* chokepoint reached by every entrypoint — standalone `ktx ingest` (plain/json
|
|
126
|
+
* and foreground) and `ktx setup` (via `runContextBuild`). The exported
|
|
127
|
+
* `executePublicIngestTarget` wraps this and emits the `ingest_completed`
|
|
128
|
+
* telemetry event exactly once, so every path is counted.
|
|
129
|
+
*/
|
|
118
130
|
export declare function executePublicIngestTarget(target: KtxPublicIngestPlanTarget, args: Extract<KtxPublicIngestArgs, {
|
|
119
131
|
command: 'run';
|
|
120
|
-
}>, io: KtxCliIo, deps: KtxPublicIngestDeps): Promise<KtxPublicIngestTargetResult>;
|
|
132
|
+
}>, io: KtxCliIo, deps: KtxPublicIngestDeps, project: KtxPublicIngestProject): Promise<KtxPublicIngestTargetResult>;
|
|
121
133
|
export declare function runKtxPublicIngest(args: KtxPublicIngestArgs, io: KtxCliIo, deps?: KtxPublicIngestDeps): Promise<number>;
|
|
122
134
|
export {};
|
package/dist/public-ingest.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
|
2
2
|
import { loadKtxProject } from './context/project/project.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isDatabaseDriver, normalizeConnectionDriver } from './connection-drivers.js';
|
|
4
|
+
import { resolveQueryHistoryScopeFloor } from './context/ingest/adapters/historic-sql/scope-floor.js';
|
|
4
5
|
import { ensureManagedPythonCommandRuntime, } from './managed-python-command.js';
|
|
5
|
-
import { publicIngestOutputLine } from './public-ingest-copy.js';
|
|
6
|
+
import { publicDatabaseIngestMessage, publicIngestOutputLine, publicQueryHistoryMessage, } from './public-ingest-copy.js';
|
|
7
|
+
import { createAggregateProgressPort } from './progress-port-adapter.js';
|
|
6
8
|
import { resolvePublicIngestRuntimeRequirements } from './runtime-requirements.js';
|
|
7
9
|
import { profileMark } from './startup-profile.js';
|
|
8
10
|
import { isDemoConnection } from './telemetry/demo-detect.js';
|
|
9
11
|
import { emitProjectStackSnapshot, emitTelemetryEvent } from './telemetry/index.js';
|
|
12
|
+
import { formatErrorDetail } from './telemetry/scrubber.js';
|
|
10
13
|
profileMark('module:public-ingest');
|
|
11
14
|
const sourceAdapterByDriver = new Map([
|
|
12
15
|
['metabase', 'metabase'],
|
|
@@ -17,6 +20,16 @@ const sourceAdapterByDriver = new Map([
|
|
|
17
20
|
['dbt', 'dbt'],
|
|
18
21
|
['lookml', 'lookml'],
|
|
19
22
|
]);
|
|
23
|
+
export function publicProgressMessage(message, target) {
|
|
24
|
+
let current = message;
|
|
25
|
+
if (target.operation === 'database-ingest') {
|
|
26
|
+
current = publicDatabaseIngestMessage(current);
|
|
27
|
+
}
|
|
28
|
+
if (target.steps.includes('query-history')) {
|
|
29
|
+
current = publicQueryHistoryMessage(current, target.connectionId);
|
|
30
|
+
}
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
20
33
|
const queryHistoryDialectByDriver = new Map([
|
|
21
34
|
['postgres', 'postgres'],
|
|
22
35
|
['bigquery', 'bigquery'],
|
|
@@ -25,7 +38,6 @@ const queryHistoryDialectByDriver = new Map([
|
|
|
25
38
|
function createWarningAccumulator() {
|
|
26
39
|
return {
|
|
27
40
|
warnings: [],
|
|
28
|
-
ignoredDepthForSources: [],
|
|
29
41
|
ignoredQueryHistoryForSources: [],
|
|
30
42
|
unsupportedQueryHistoryForDatabases: [],
|
|
31
43
|
};
|
|
@@ -71,12 +83,6 @@ function finalizeWarnings(accumulator, args) {
|
|
|
71
83
|
...accumulator.warnings,
|
|
72
84
|
...unsupportedQueryHistoryWarnings(accumulator.unsupportedQueryHistoryForDatabases, args.all),
|
|
73
85
|
];
|
|
74
|
-
const depthOption = args.depth ? `--${args.depth}` : null;
|
|
75
|
-
if (depthOption) {
|
|
76
|
-
const warning = sourceIgnoredWarning(depthOption, accumulator.ignoredDepthForSources, args.all);
|
|
77
|
-
if (warning)
|
|
78
|
-
warnings.push(warning);
|
|
79
|
-
}
|
|
80
86
|
if (args.queryHistory === 'enabled' || args.queryHistoryWindowDays !== undefined) {
|
|
81
87
|
const warning = sourceIgnoredWarning('--query-history', accumulator.ignoredQueryHistoryForSources, args.all);
|
|
82
88
|
if (warning)
|
|
@@ -106,20 +112,20 @@ function storedQueryHistory(connection) {
|
|
|
106
112
|
function positiveInteger(value) {
|
|
107
113
|
return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
108
114
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
const tables = raw.filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
115
|
-
return tables.length > 0 ? tables : undefined;
|
|
116
|
-
}
|
|
117
|
-
function queryHistoryPullConfig(input) {
|
|
118
|
-
const { enabled: _enabled, dialect: _dialect, ...storedConfig } = input.stored;
|
|
115
|
+
/** @internal */
|
|
116
|
+
export function queryHistoryPullConfig(input) {
|
|
117
|
+
const { enabled: _enabled, dialect: _dialect, enabledTables: _enabledTables, enabledSchemas: _enabledSchemas, scopeFloorWarnings: _scopeFloorWarnings, ...storedConfig } = input.stored;
|
|
119
118
|
return {
|
|
120
119
|
...storedConfig,
|
|
121
120
|
dialect: input.dialect,
|
|
122
|
-
...(input.enabledTables ? { enabledTables: input.enabledTables } : {}),
|
|
121
|
+
...(input.enabledTables && input.enabledTables.length > 0 ? { enabledTables: input.enabledTables } : {}),
|
|
122
|
+
...(input.enabledSchemas && input.enabledSchemas.length > 0 ? { enabledSchemas: input.enabledSchemas } : {}),
|
|
123
|
+
...(input.modeledTableCatalog && input.modeledTableCatalog.length > 0
|
|
124
|
+
? { modeledTableCatalog: input.modeledTableCatalog }
|
|
125
|
+
: {}),
|
|
126
|
+
...(input.scopeFloorWarnings && input.scopeFloorWarnings.length > 0
|
|
127
|
+
? { scopeFloorWarnings: input.scopeFloorWarnings }
|
|
128
|
+
: {}),
|
|
123
129
|
...(input.windowDays !== undefined ? { windowDays: input.windowDays } : {}),
|
|
124
130
|
};
|
|
125
131
|
}
|
|
@@ -135,7 +141,6 @@ function resolveDatabaseTargetOptions(input) {
|
|
|
135
141
|
const windowOverrideRequested = input.args.queryHistoryWindowDays !== undefined;
|
|
136
142
|
const requestedQh = explicitQueryHistory === 'enabled' ||
|
|
137
143
|
(explicitQueryHistory !== 'disabled' && (windowOverrideRequested || storedEnabled));
|
|
138
|
-
let depth = input.args.depth ?? databaseContextDepth(input.connection) ?? 'fast';
|
|
139
144
|
const queryHistory = {
|
|
140
145
|
enabled: false,
|
|
141
146
|
...(input.args.queryHistoryWindowDays !== undefined
|
|
@@ -151,18 +156,12 @@ function resolveDatabaseTargetOptions(input) {
|
|
|
151
156
|
reason: explicitQueryHistory === 'enabled' || input.args.queryHistoryWindowDays !== undefined ? 'explicit' : 'stored',
|
|
152
157
|
});
|
|
153
158
|
return {
|
|
154
|
-
databaseDepth: depth,
|
|
155
159
|
queryHistory: { ...queryHistory, unsupported: true },
|
|
156
160
|
steps: ['database-schema'],
|
|
157
161
|
};
|
|
158
162
|
}
|
|
159
163
|
if (requestedQh && dialect) {
|
|
160
|
-
if (depth === 'fast') {
|
|
161
|
-
input.warnings.warnings.push(`--query-history requires deep ingest; running ${input.connectionId} with --deep.`);
|
|
162
|
-
}
|
|
163
|
-
depth = 'deep';
|
|
164
164
|
return {
|
|
165
|
-
databaseDepth: depth,
|
|
166
165
|
queryHistory: {
|
|
167
166
|
...queryHistory,
|
|
168
167
|
enabled: true,
|
|
@@ -171,34 +170,66 @@ function resolveDatabaseTargetOptions(input) {
|
|
|
171
170
|
stored: storedQh,
|
|
172
171
|
dialect,
|
|
173
172
|
windowDays: queryHistory.windowDays,
|
|
174
|
-
enabledTables: enabledTablesForConnection(input.connection),
|
|
175
173
|
}),
|
|
176
174
|
},
|
|
177
175
|
steps: ['database-schema', 'query-history'],
|
|
178
176
|
};
|
|
179
177
|
}
|
|
180
|
-
if (input.args.depth === 'fast' && explicitQueryHistory !== 'enabled' && storedEnabled) {
|
|
181
|
-
input.warnings.warnings.push(`${input.connectionId} has query history enabled in ktx.yaml, but --fast skips query-history processing.`);
|
|
182
|
-
return {
|
|
183
|
-
databaseDepth: 'fast',
|
|
184
|
-
queryHistory: { ...queryHistory, skippedStoredByFast: true },
|
|
185
|
-
steps: ['database-schema'],
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
178
|
return {
|
|
189
|
-
databaseDepth: depth,
|
|
190
179
|
queryHistory,
|
|
191
180
|
steps: ['database-schema'],
|
|
192
181
|
};
|
|
193
182
|
}
|
|
183
|
+
async function resolvedQueryHistoryPullConfigForTarget(target, project) {
|
|
184
|
+
if (target.operation !== 'database-ingest' || target.queryHistory?.enabled !== true || !target.queryHistory.dialect) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const connection = project.config.connections[target.connectionId];
|
|
188
|
+
if (!connection) {
|
|
189
|
+
return (target.queryHistory.pullConfig ??
|
|
190
|
+
queryHistoryPullConfig({
|
|
191
|
+
stored: {},
|
|
192
|
+
dialect: target.queryHistory.dialect,
|
|
193
|
+
windowDays: target.queryHistory.windowDays,
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
const stored = storedQueryHistory(connection);
|
|
197
|
+
const scopeFloor = await resolveQueryHistoryScopeFloor({
|
|
198
|
+
projectDir: project.projectDir,
|
|
199
|
+
connectionId: target.connectionId,
|
|
200
|
+
driver: target.driver,
|
|
201
|
+
connection: connection,
|
|
202
|
+
storedQueryHistory: stored,
|
|
203
|
+
});
|
|
204
|
+
return queryHistoryPullConfig({
|
|
205
|
+
stored,
|
|
206
|
+
dialect: target.queryHistory.dialect,
|
|
207
|
+
windowDays: target.queryHistory.windowDays,
|
|
208
|
+
enabledTables: scopeFloor.enabledTables,
|
|
209
|
+
enabledSchemas: scopeFloor.enabledSchemas,
|
|
210
|
+
modeledTableCatalog: scopeFloor.modeledTableCatalog,
|
|
211
|
+
scopeFloorWarnings: scopeFloor.warnings,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function enrichmentReadinessGaps(config) {
|
|
215
|
+
const gaps = [];
|
|
216
|
+
if (config.llm.provider.backend === 'none' || !config.llm.models.default) {
|
|
217
|
+
gaps.push('model configuration');
|
|
218
|
+
}
|
|
219
|
+
if (config.scan.enrichment.mode !== 'llm') {
|
|
220
|
+
gaps.push('scan enrichment mode');
|
|
221
|
+
}
|
|
222
|
+
const embeddings = config.scan.enrichment.embeddings;
|
|
223
|
+
if (!embeddings || embeddings.backend === 'none' || !embeddings.model || embeddings.dimensions <= 0) {
|
|
224
|
+
gaps.push('scan embeddings');
|
|
225
|
+
}
|
|
226
|
+
return gaps;
|
|
227
|
+
}
|
|
194
228
|
function targetForConnection(connectionId, connection, projectConfig, args, warnings) {
|
|
195
229
|
const driver = normalizeConnectionDriver(connection);
|
|
196
230
|
const adapter = sourceAdapterByDriver.get(driver);
|
|
197
231
|
const sourceDir = sourceDirForConnection(connection);
|
|
198
232
|
if (adapter) {
|
|
199
|
-
if (args.depth) {
|
|
200
|
-
warnings.ignoredDepthForSources.push(connectionId);
|
|
201
|
-
}
|
|
202
233
|
if (args.queryHistory === 'enabled' || args.queryHistoryWindowDays !== undefined) {
|
|
203
234
|
warnings.ignoredQueryHistoryForSources.push(connectionId);
|
|
204
235
|
}
|
|
@@ -214,16 +245,16 @@ function targetForConnection(connectionId, connection, projectConfig, args, warn
|
|
|
214
245
|
}
|
|
215
246
|
if (isDatabaseDriver(driver)) {
|
|
216
247
|
const options = resolveDatabaseTargetOptions({ connectionId, driver, connection, args, warnings });
|
|
217
|
-
const gaps =
|
|
248
|
+
const gaps = enrichmentReadinessGaps(projectConfig);
|
|
218
249
|
return {
|
|
219
250
|
connectionId,
|
|
220
251
|
driver,
|
|
221
252
|
operation: 'database-ingest',
|
|
222
253
|
debugCommand: `ktx ingest ${connectionId} --debug`,
|
|
223
|
-
detectRelationships:
|
|
254
|
+
detectRelationships: projectConfig.scan.relationships.enabled,
|
|
224
255
|
...(gaps.length > 0
|
|
225
256
|
? {
|
|
226
|
-
preflightFailure: `${connectionId}
|
|
257
|
+
preflightFailure: `${connectionId} cannot be ingested: enrichment is not configured (${gaps.join(', ')}). Run ktx setup to configure a model and embeddings.`,
|
|
227
258
|
}
|
|
228
259
|
: {}),
|
|
229
260
|
...options,
|
|
@@ -281,12 +312,11 @@ function defaultSteps(target) {
|
|
|
281
312
|
}
|
|
282
313
|
function retryCommandForTarget(target, args) {
|
|
283
314
|
const projectPart = ` --project-dir ${args.projectDir}`;
|
|
284
|
-
const depthPart = target.databaseDepth ? ` --${target.databaseDepth}` : '';
|
|
285
315
|
const queryHistoryPart = target.queryHistory?.enabled === true ? ' --query-history' : '';
|
|
286
316
|
const windowPart = target.queryHistory?.enabled === true && target.queryHistory.windowDays !== undefined
|
|
287
317
|
? ` --query-history-window-days ${target.queryHistory.windowDays}`
|
|
288
318
|
: '';
|
|
289
|
-
return `ktx ingest ${target.connectionId}${projectPart}${
|
|
319
|
+
return `ktx ingest ${target.connectionId}${projectPart}${queryHistoryPart}${windowPart}`;
|
|
290
320
|
}
|
|
291
321
|
function trimTrailingPeriod(value) {
|
|
292
322
|
return value.endsWith('.') ? value.slice(0, -1) : value;
|
|
@@ -361,6 +391,9 @@ function rowsBucket() {
|
|
|
361
391
|
}
|
|
362
392
|
async function emitIngestCompleted(input) {
|
|
363
393
|
const failed = resultFailed(input.result);
|
|
394
|
+
const failureDetail = failed
|
|
395
|
+
? formatErrorDetail(input.result.steps.find((step) => step.status === 'failed')?.detail)
|
|
396
|
+
: undefined;
|
|
364
397
|
await emitTelemetryEvent({
|
|
365
398
|
name: 'ingest_completed',
|
|
366
399
|
projectDir: input.args.projectDir,
|
|
@@ -374,6 +407,7 @@ async function emitIngestCompleted(input) {
|
|
|
374
407
|
rowsBucket: rowsBucket(),
|
|
375
408
|
durationMs: Math.max(0, performance.now() - input.startedAt),
|
|
376
409
|
outcome: failed ? 'error' : 'ok',
|
|
410
|
+
...(failureDetail ? { errorDetail: failureDetail } : {}),
|
|
377
411
|
},
|
|
378
412
|
});
|
|
379
413
|
}
|
|
@@ -440,6 +474,65 @@ function createCapturedPublicIngestIo() {
|
|
|
440
474
|
},
|
|
441
475
|
};
|
|
442
476
|
}
|
|
477
|
+
function isCapturedPublicIngestIo(io) {
|
|
478
|
+
return typeof io.capturedOutput === 'function';
|
|
479
|
+
}
|
|
480
|
+
const PLAIN_PUBLIC_INGEST_PHASE_LABELS = {
|
|
481
|
+
'database-schema': 'database schema',
|
|
482
|
+
'query-history': 'query history',
|
|
483
|
+
'source-ingest': 'source ingest',
|
|
484
|
+
};
|
|
485
|
+
function firstSummaryLine(summary) {
|
|
486
|
+
if (!summary)
|
|
487
|
+
return undefined;
|
|
488
|
+
return summary.split(/\r?\n/).find((line) => line.trim().length > 0)?.trim();
|
|
489
|
+
}
|
|
490
|
+
function plainPhaseHeader(options, phaseKey) {
|
|
491
|
+
const prefix = options.total > 1 ? `[${options.index + 1}/${options.total}] ` : '';
|
|
492
|
+
return `${prefix}${options.target.connectionId} · ${PLAIN_PUBLIC_INGEST_PHASE_LABELS[phaseKey]}`;
|
|
493
|
+
}
|
|
494
|
+
function plainPhaseEndLine(status, summary) {
|
|
495
|
+
const firstLine = firstSummaryLine(summary);
|
|
496
|
+
return firstLine ? ` ${status} · ${firstLine}` : ` ${status}`;
|
|
497
|
+
}
|
|
498
|
+
function createPlainPublicIngestProgress(io, options) {
|
|
499
|
+
let currentPhase = null;
|
|
500
|
+
const startedPhases = new Set();
|
|
501
|
+
const lastPercentByPhase = new Map();
|
|
502
|
+
const startPhase = (phaseKey) => {
|
|
503
|
+
currentPhase = phaseKey;
|
|
504
|
+
startedPhases.add(phaseKey);
|
|
505
|
+
lastPercentByPhase.set(phaseKey, -1);
|
|
506
|
+
io.stderr.write(`${plainPhaseHeader(options, phaseKey)}\n`);
|
|
507
|
+
};
|
|
508
|
+
const ensurePhaseStarted = (phaseKey) => {
|
|
509
|
+
if (!startedPhases.has(phaseKey)) {
|
|
510
|
+
startPhase(phaseKey);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
currentPhase = phaseKey;
|
|
514
|
+
};
|
|
515
|
+
const emitProgress = (update) => {
|
|
516
|
+
if (currentPhase === null)
|
|
517
|
+
return;
|
|
518
|
+
const rounded = Math.max(0, Math.min(100, Math.round(update.percent)));
|
|
519
|
+
const lastPercent = lastPercentByPhase.get(currentPhase) ?? -1;
|
|
520
|
+
if (rounded <= lastPercent)
|
|
521
|
+
return;
|
|
522
|
+
lastPercentByPhase.set(currentPhase, rounded);
|
|
523
|
+
io.stderr.write(` [${rounded}%] ${publicProgressMessage(update.message, options.target)}\n`);
|
|
524
|
+
};
|
|
525
|
+
return {
|
|
526
|
+
onPhaseStart: startPhase,
|
|
527
|
+
onPhaseEnd(phaseKey, status, summary) {
|
|
528
|
+
ensurePhaseStarted(phaseKey);
|
|
529
|
+
io.stderr.write(`${plainPhaseEndLine(status, summary)}\n`);
|
|
530
|
+
currentPhase = null;
|
|
531
|
+
},
|
|
532
|
+
scanProgress: createAggregateProgressPort(emitProgress),
|
|
533
|
+
ingestProgress: emitProgress,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
443
536
|
const INTERNAL_STATUS_LINE_RE = /^(Report|Run|Job|Status|Adapter|Connection|Sync|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/;
|
|
444
537
|
const ACTIONABLE_FAILURE_LINE_RE = /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/;
|
|
445
538
|
const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed KTX command\.?$/;
|
|
@@ -470,7 +563,23 @@ function capturedFailureMessage(output) {
|
|
|
470
563
|
.filter((line) => line.startsWith('In a source checkout, build the local runtime assets with:'));
|
|
471
564
|
return [firstLine, ...followupLines].join('\n');
|
|
472
565
|
}
|
|
473
|
-
|
|
566
|
+
/**
|
|
567
|
+
* Run one ingest target through its scan/ingest steps. The single per-target
|
|
568
|
+
* chokepoint reached by every entrypoint — standalone `ktx ingest` (plain/json
|
|
569
|
+
* and foreground) and `ktx setup` (via `runContextBuild`). The exported
|
|
570
|
+
* `executePublicIngestTarget` wraps this and emits the `ingest_completed`
|
|
571
|
+
* telemetry event exactly once, so every path is counted.
|
|
572
|
+
*/
|
|
573
|
+
export async function executePublicIngestTarget(target, args, io, deps, project) {
|
|
574
|
+
const startedAt = performance.now();
|
|
575
|
+
const result = await runIngestTargetSteps(target, args, io, deps, project);
|
|
576
|
+
// `io` may be a capture buffer for the scan/ingest step output; the telemetry
|
|
577
|
+
// debug echo belongs on the real user-facing stream, which callers expose as
|
|
578
|
+
// `deps.runtimeIo` (falling back to `io` when the step io is already real).
|
|
579
|
+
await emitIngestCompleted({ args, project, target, result, startedAt, io: deps.runtimeIo ?? io });
|
|
580
|
+
return result;
|
|
581
|
+
}
|
|
582
|
+
async function runIngestTargetSteps(target, args, io, deps, project) {
|
|
474
583
|
if (target.preflightFailure) {
|
|
475
584
|
if (target.operation === 'database-ingest') {
|
|
476
585
|
deps.onPhaseEnd?.('database-schema', 'failed', target.preflightFailure);
|
|
@@ -488,7 +597,7 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
488
597
|
? {
|
|
489
598
|
...step,
|
|
490
599
|
status: 'failed',
|
|
491
|
-
detail: target.preflightFailure
|
|
600
|
+
detail: `${target.connectionId} failed: ${target.preflightFailure}`,
|
|
492
601
|
}
|
|
493
602
|
: step),
|
|
494
603
|
};
|
|
@@ -499,14 +608,18 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
499
608
|
command: 'run',
|
|
500
609
|
projectDir: args.projectDir,
|
|
501
610
|
connectionId: target.connectionId,
|
|
502
|
-
mode:
|
|
611
|
+
mode: 'enriched',
|
|
503
612
|
detectRelationships: target.detectRelationships === true,
|
|
504
613
|
dryRun: false,
|
|
505
614
|
...(args.cliVersion ? { cliVersion: args.cliVersion } : {}),
|
|
506
615
|
...(args.runtimeInstallPolicy ? { runtimeInstallPolicy: args.runtimeInstallPolicy } : {}),
|
|
507
616
|
};
|
|
508
617
|
const runScan = deps.runScan ?? runKtxScan;
|
|
509
|
-
const capturedScanIo = deps.scanProgress
|
|
618
|
+
const capturedScanIo = deps.scanProgress
|
|
619
|
+
? isCapturedPublicIngestIo(io)
|
|
620
|
+
? io
|
|
621
|
+
: null
|
|
622
|
+
: createCapturedPublicIngestIo();
|
|
510
623
|
const scanIo = capturedScanIo ?? io;
|
|
511
624
|
const scanDeps = {
|
|
512
625
|
...(deps.scanProgress ? { progress: deps.scanProgress } : {}),
|
|
@@ -525,6 +638,10 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
525
638
|
if (target.queryHistory?.enabled === true) {
|
|
526
639
|
const { runKtxIngest } = await import('./ingest.js');
|
|
527
640
|
const runIngest = deps.runIngest ?? runKtxIngest;
|
|
641
|
+
const historicSqlPullConfigOverride = (await resolvedQueryHistoryPullConfigForTarget(target, project)) ?? {
|
|
642
|
+
dialect: target.queryHistory.dialect,
|
|
643
|
+
...(target.queryHistory.windowDays !== undefined ? { windowDays: target.queryHistory.windowDays } : {}),
|
|
644
|
+
};
|
|
528
645
|
const ingestArgs = {
|
|
529
646
|
command: 'run',
|
|
530
647
|
projectDir: args.projectDir,
|
|
@@ -535,12 +652,14 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
535
652
|
...(args.cliVersion ? { cliVersion: args.cliVersion } : {}),
|
|
536
653
|
...(args.runtimeInstallPolicy ? { runtimeInstallPolicy: args.runtimeInstallPolicy } : {}),
|
|
537
654
|
allowImplicitAdapter: true,
|
|
538
|
-
historicSqlPullConfigOverride
|
|
539
|
-
dialect: target.queryHistory.dialect,
|
|
540
|
-
...(target.queryHistory.windowDays !== undefined ? { windowDays: target.queryHistory.windowDays } : {}),
|
|
541
|
-
},
|
|
655
|
+
historicSqlPullConfigOverride,
|
|
542
656
|
};
|
|
543
|
-
|
|
657
|
+
// Query history runs after the schema scan has already written its report
|
|
658
|
+
// into the shared target io, so it needs a phase-local capture. Reusing
|
|
659
|
+
// `io` here would let leftover scan text (e.g. "Mode: enriched") surface as
|
|
660
|
+
// the query-history failure detail. Only skip capture when progress is
|
|
661
|
+
// active and the caller manages its own buffer (io is not a capture).
|
|
662
|
+
const capturedIngestIo = deps.ingestProgress && !isCapturedPublicIngestIo(io) ? null : createCapturedPublicIngestIo();
|
|
544
663
|
const ingestIo = capturedIngestIo ?? io;
|
|
545
664
|
const ingestDeps = {
|
|
546
665
|
...(deps.ingestProgress ? { progress: deps.ingestProgress } : {}),
|
|
@@ -577,7 +696,11 @@ export async function executePublicIngestTarget(target, args, io, deps) {
|
|
|
577
696
|
allowImplicitAdapter: true,
|
|
578
697
|
};
|
|
579
698
|
const runIngest = deps.runIngest ?? runKtxIngest;
|
|
580
|
-
const capturedIngestIo = deps.ingestProgress
|
|
699
|
+
const capturedIngestIo = deps.ingestProgress
|
|
700
|
+
? isCapturedPublicIngestIo(io)
|
|
701
|
+
? io
|
|
702
|
+
: null
|
|
703
|
+
: createCapturedPublicIngestIo();
|
|
581
704
|
const ingestIo = capturedIngestIo ?? io;
|
|
582
705
|
const ingestDeps = {
|
|
583
706
|
...(deps.ingestProgress ? { progress: deps.ingestProgress } : {}),
|
|
@@ -622,7 +745,6 @@ export async function runKtxPublicIngest(args, io, deps = {}) {
|
|
|
622
745
|
all: args.all,
|
|
623
746
|
entrypoint: 'ingest',
|
|
624
747
|
inputMode: args.inputMode,
|
|
625
|
-
...(args.depth ? { depth: args.depth } : {}),
|
|
626
748
|
...(args.queryHistory ? { queryHistory: args.queryHistory } : {}),
|
|
627
749
|
...(args.queryHistoryWindowDays !== undefined ? { queryHistoryWindowDays: args.queryHistoryWindowDays } : {}),
|
|
628
750
|
...(args.scanMode ? { scanMode: args.scanMode } : {}),
|
|
@@ -642,11 +764,26 @@ export async function runKtxPublicIngest(args, io, deps = {}) {
|
|
|
642
764
|
io.stderr.write(`Warning: ${warning}\n`);
|
|
643
765
|
}
|
|
644
766
|
}
|
|
645
|
-
for (const target of plan.targets) {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
767
|
+
for (const [index, target] of plan.targets.entries()) {
|
|
768
|
+
if (args.json) {
|
|
769
|
+
results.push(await executePublicIngestTarget(target, args, io, deps, project));
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
const capture = createCapturedPublicIngestIo();
|
|
773
|
+
const progress = createPlainPublicIngestProgress(io, {
|
|
774
|
+
target,
|
|
775
|
+
index,
|
|
776
|
+
total: plan.targets.length,
|
|
777
|
+
});
|
|
778
|
+
const targetDeps = {
|
|
779
|
+
...deps,
|
|
780
|
+
scanProgress: progress.scanProgress,
|
|
781
|
+
ingestProgress: progress.ingestProgress,
|
|
782
|
+
onPhaseStart: progress.onPhaseStart,
|
|
783
|
+
onPhaseEnd: progress.onPhaseEnd,
|
|
784
|
+
runtimeIo: deps.runtimeIo ?? io,
|
|
785
|
+
};
|
|
786
|
+
results.push(await executePublicIngestTarget(target, args, capture, targetDeps, project));
|
|
650
787
|
}
|
|
651
788
|
if (args.json) {
|
|
652
789
|
io.stdout.write(`${JSON.stringify({ plan, results }, null, 2)}\n`);
|
package/dist/scan.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createKtxCliLocalIngestAdapters } from './local-adapters.js';
|
|
|
6
6
|
import { createKtxCliScanConnector } from './local-scan-connectors.js';
|
|
7
7
|
import { profileMark } from './startup-profile.js';
|
|
8
8
|
import { emitTelemetryEvent } from './telemetry/index.js';
|
|
9
|
-
import { scrubErrorClass } from './telemetry/scrubber.js';
|
|
9
|
+
import { formatErrorDetail, scrubErrorClass } from './telemetry/scrubber.js';
|
|
10
10
|
profileMark('module:scan');
|
|
11
11
|
function shouldUseStyledOutput(io) {
|
|
12
12
|
return io.stdout.isTTY === true && !process.env.NO_COLOR && process.env.TERM !== 'dumb' && !process.env.CI;
|
|
@@ -306,6 +306,7 @@ export async function runKtxScan(args, io = process, deps = {}) {
|
|
|
306
306
|
}
|
|
307
307
|
catch (error) {
|
|
308
308
|
const errorClass = scrubErrorClass(error);
|
|
309
|
+
const errorDetail = formatErrorDetail(error);
|
|
309
310
|
await emitTelemetryEvent({
|
|
310
311
|
name: 'scan_completed',
|
|
311
312
|
projectDir: args.projectDir,
|
|
@@ -319,6 +320,7 @@ export async function runKtxScan(args, io = process, deps = {}) {
|
|
|
319
320
|
durationMs: Math.max(0, performance.now() - startedAt),
|
|
320
321
|
outcome: 'error',
|
|
321
322
|
...(errorClass ? { errorClass } : {}),
|
|
323
|
+
...(errorDetail ? { errorDetail } : {}),
|
|
322
324
|
},
|
|
323
325
|
});
|
|
324
326
|
io.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
package/dist/setup-context.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export type KtxSetupContextResult = {
|
|
|
54
54
|
} | {
|
|
55
55
|
status: 'failed';
|
|
56
56
|
projectDir: string;
|
|
57
|
+
errorDetail?: string;
|
|
57
58
|
};
|
|
58
59
|
export interface KtxSetupContextStepArgs {
|
|
59
60
|
projectDir: string;
|
|
@@ -77,6 +78,7 @@ export interface KtxSetupContextDeps {
|
|
|
77
78
|
now?: () => Date;
|
|
78
79
|
runContextBuild?: typeof runContextBuild;
|
|
79
80
|
verifyContextReady?: (projectDir: string) => Promise<KtxSetupContextReadiness>;
|
|
81
|
+
testConnection?: (projectDir: string, connectionId: string, io: KtxCliIo) => Promise<number>;
|
|
80
82
|
}
|
|
81
83
|
/** @internal */
|
|
82
84
|
export declare function contextBuildCommands(projectDir: string): KtxSetupContextCommands;
|