@kaelio/ktx 0.11.0 → 0.13.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.13.0-py3-none-any.whl +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/admin.js +1 -1
- package/dist/clack.d.ts +16 -0
- package/dist/clack.js +37 -6
- package/dist/claude-code-prompt-caching.js +1 -1
- package/dist/cli-program.js +3 -3
- package/dist/cli-runtime.js +2 -2
- package/dist/commands/connection-commands.js +1 -1
- package/dist/commands/ingest-commands.js +4 -4
- package/dist/commands/mcp-commands.js +12 -12
- package/dist/commands/runtime-commands.js +4 -4
- package/dist/commands/setup-commands.js +19 -5
- package/dist/commands/sl-commands.js +1 -1
- package/dist/commands/sql-commands.js +1 -1
- package/dist/commands/status-commands.js +1 -1
- package/dist/connection.js +15 -3
- package/dist/connectors/bigquery/connector.js +1 -14
- package/dist/connectors/clickhouse/connector.js +2 -16
- package/dist/connectors/duckdb/federated-attach.d.ts +7 -0
- package/dist/connectors/duckdb/federated-attach.js +86 -0
- package/dist/connectors/duckdb/federated-executor.d.ts +5 -0
- package/dist/connectors/duckdb/federated-executor.js +59 -0
- package/dist/connectors/mysql/connector.js +2 -16
- package/dist/connectors/postgres/connector.js +1 -14
- package/dist/connectors/shared/string-reference.d.ts +6 -0
- package/dist/connectors/shared/string-reference.js +19 -0
- package/dist/connectors/snowflake/connector.d.ts +1 -1
- package/dist/connectors/snowflake/connector.js +1 -14
- package/dist/connectors/sqlite/connector.js +2 -25
- package/dist/connectors/sqlserver/connector.js +4 -17
- package/dist/context/connections/connection-type.d.ts +1 -1
- package/dist/context/connections/federation.d.ts +33 -0
- package/dist/context/connections/federation.js +51 -0
- package/dist/context/connections/local-warehouse-descriptor.d.ts +2 -0
- package/dist/context/connections/project-sql-executor.d.ts +18 -0
- package/dist/context/connections/project-sql-executor.js +39 -0
- package/dist/context/connections/query-executor.d.ts +2 -2
- package/dist/context/connections/read-only-sql.d.ts +1 -0
- package/dist/context/connections/read-only-sql.js +119 -4
- package/dist/context/connections/resolve-connection.d.ts +12 -0
- package/dist/context/connections/resolve-connection.js +37 -0
- package/dist/context/core/git-env.d.ts +4 -0
- package/dist/context/core/git-env.js +5 -1
- package/dist/context/core/git.service.d.ts +23 -0
- package/dist/context/core/git.service.js +71 -8
- package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
- package/dist/context/ingest/adapters/live-database/manifest.d.ts +3 -0
- package/dist/context/ingest/adapters/live-database/manifest.js +19 -11
- package/dist/context/ingest/adapters/looker/client.js +7 -2
- package/dist/context/ingest/adapters/looker/factory.d.ts +8 -1
- package/dist/context/ingest/adapters/looker/factory.js +9 -0
- package/dist/context/ingest/adapters/looker/mapping.js +1 -1
- package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.js +1 -1
- package/dist/context/ingest/adapters/metabase/local-metabase.adapter.js +1 -1
- package/dist/context/ingest/adapters/metabase/mapping.js +6 -6
- package/dist/context/ingest/artifact-gates.d.ts +2 -6
- package/dist/context/ingest/artifact-gates.js +5 -47
- package/dist/context/ingest/constrained-repair.d.ts +55 -0
- package/dist/context/ingest/constrained-repair.js +167 -0
- package/dist/context/ingest/final-gate-repair.d.ts +9 -11
- package/dist/context/ingest/final-gate-repair.js +40 -128
- package/dist/context/ingest/finalization-scope.d.ts +1 -1
- package/dist/context/ingest/finalization-scope.js +15 -15
- package/dist/context/ingest/ingest-bundle.runner.d.ts +1 -0
- package/dist/context/ingest/ingest-bundle.runner.js +101 -67
- package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +6 -13
- package/dist/context/ingest/isolated-diff/patch-integrator.js +32 -109
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +8 -9
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +63 -141
- package/dist/context/ingest/local-bundle-runtime.d.ts +2 -0
- package/dist/context/ingest/local-bundle-runtime.js +9 -10
- package/dist/context/ingest/local-ingest.d.ts +2 -0
- package/dist/context/ingest/local-ingest.js +2 -0
- package/dist/context/ingest/memory-flow/view-model.js +1 -1
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -6
- package/dist/context/ingest/stages/stage-3-work-units.js +2 -1
- package/dist/context/ingest/stages/validate-wu-sources.d.ts +7 -1
- package/dist/context/ingest/stages/validate-wu-sources.js +109 -4
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.d.ts +2 -0
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.js +1 -1
- package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +3 -3
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.d.ts +3 -1
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +15 -1
- package/dist/context/llm/ai-sdk-runtime.js +2 -2
- package/dist/context/llm/claude-code-runtime.js +19 -3
- package/dist/context/llm/local-config.js +1 -1
- package/dist/context/llm/runtime-tools.js +2 -2
- package/dist/context/mcp/context-tools.js +33 -8
- package/dist/context/mcp/local-project-ports.js +63 -89
- package/dist/context/mcp/types.d.ts +2 -0
- package/dist/context/memory/local-memory.js +4 -1
- package/dist/context/memory/memory-agent.service.js +1 -1
- package/dist/context/project/config.d.ts +11 -4
- package/dist/context/project/config.js +85 -30
- package/dist/context/project/driver-schemas.js +1 -1
- package/dist/context/project/mappings-yaml-schema.js +2 -2
- package/dist/context/project/project.js +12 -4
- package/dist/context/scan/description-generation.js +4 -4
- package/dist/context/scan/local-enrichment-artifacts.js +33 -4
- package/dist/context/scan/local-scan.js +2 -2
- package/dist/context/scan/local-structural-artifacts.js +5 -5
- package/dist/context/scan/relationship-benchmark-report.js +1 -1
- package/dist/context/scan/relationship-discovery.js +3 -3
- package/dist/context/scan/relationship-llm-proposal.js +3 -3
- package/dist/context/sl/local-query.js +31 -44
- package/dist/context/sl/local-sl.d.ts +0 -8
- package/dist/context/sl/local-sl.js +71 -70
- package/dist/context/sl/semantic-layer.service.d.ts +25 -8
- package/dist/context/sl/semantic-layer.service.js +109 -56
- package/dist/context/sl/source-files.d.ts +48 -0
- package/dist/context/sl/source-files.js +138 -0
- package/dist/context/sl/tools/base-semantic-layer.tool.d.ts +2 -2
- package/dist/context/sl/tools/base-semantic-layer.tool.js +2 -7
- package/dist/context/sl/tools/sl-edit-source.tool.js +10 -8
- package/dist/context/sl/tools/sl-warehouse-validation.js +55 -27
- package/dist/context/sl/tools/sl-write-source.tool.js +12 -9
- package/dist/context/sql-analysis/dialect.d.ts +2 -0
- package/dist/context/sql-analysis/dialect.js +20 -0
- package/dist/context/tools/base-tool.d.ts +6 -19
- package/dist/context/tools/base-tool.js +0 -14
- package/dist/context-build-view.js +5 -5
- package/dist/database-tree-picker.js +18 -3
- package/dist/demo-assets.js +0 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +31 -23
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +44 -0
- package/dist/ingest-query-executor.d.ts +2 -0
- package/dist/ingest-query-executor.js +8 -22
- package/dist/ingest.d.ts +1 -1
- package/dist/ingest.js +8 -2
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/io/tty.d.ts +8 -0
- package/dist/io/tty.js +16 -0
- package/dist/llm/embedding-health.js +1 -1
- package/dist/llm/embedding-provider.js +3 -3
- package/dist/llm/model-provider.js +1 -1
- package/dist/local-adapters.d.ts +1 -0
- package/dist/local-adapters.js +2 -2
- package/dist/local-scan-connectors.js +1 -1
- package/dist/managed-local-embeddings.js +17 -8
- package/dist/managed-mcp-daemon.js +3 -3
- package/dist/managed-python-command.d.ts +7 -0
- package/dist/managed-python-command.js +34 -8
- package/dist/managed-python-daemon.js +2 -2
- package/dist/managed-python-http.js +3 -3
- package/dist/managed-python-runtime.d.ts +30 -1
- package/dist/managed-python-runtime.js +134 -18
- package/dist/managed-uv-release.d.ts +7 -0
- package/dist/managed-uv-release.js +11 -0
- package/dist/mcp-http-server.js +4 -4
- package/dist/mcp-server-factory.js +3 -3
- package/dist/mcp-stdio-server.js +1 -1
- package/dist/memory-flow-hud.js +2 -2
- package/dist/next-steps.js +2 -2
- package/dist/prompt-navigation.d.ts +17 -0
- package/dist/prompt-navigation.js +49 -3
- package/dist/prompts/memory_agent_bundle_ingest_work_unit.md +2 -2
- package/dist/prompts/memory_agent_external_ingest.md +2 -2
- package/dist/public-ingest-copy.js +1 -1
- package/dist/public-ingest.js +3 -3
- package/dist/release-version.js +1 -1
- package/dist/runtime-requirements.js +1 -1
- package/dist/runtime.js +9 -9
- package/dist/scan.js +1 -1
- package/dist/setup-agents.d.ts +21 -15
- package/dist/setup-agents.js +143 -66
- package/dist/setup-banner.d.ts +20 -0
- package/dist/setup-banner.js +39 -0
- package/dist/setup-context.js +24 -15
- package/dist/setup-databases.d.ts +3 -0
- package/dist/setup-databases.js +47 -59
- package/dist/setup-demo-tour.js +12 -8
- package/dist/setup-embeddings.js +9 -9
- package/dist/setup-interrupt.js +1 -1
- package/dist/setup-models.d.ts +4 -1
- package/dist/setup-models.js +54 -28
- package/dist/setup-project.js +29 -5
- package/dist/setup-prompts.js +16 -1
- package/dist/setup-ready-menu.js +1 -1
- package/dist/setup-sources.js +28 -12
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +14 -13
- package/dist/skills/analytics/SKILL.md +3 -3
- package/dist/skills/dbt_ingest/SKILL.md +3 -3
- package/dist/skills/looker_ingest/SKILL.md +3 -3
- package/dist/skills/lookml_ingest/SKILL.md +7 -7
- package/dist/skills/metabase_ingest/SKILL.md +4 -4
- package/dist/skills/metricflow_ingest/SKILL.md +15 -15
- package/dist/skills/notion_synthesize/SKILL.md +1 -1
- package/dist/skills/sl/SKILL.md +3 -3
- package/dist/skills/sl_capture/SKILL.md +1 -1
- package/dist/skills/wiki_capture/SKILL.md +1 -1
- package/dist/source-mapping.js +1 -1
- package/dist/sql.d.ts +2 -0
- package/dist/sql.js +35 -53
- package/dist/startup-profile.js +1 -1
- package/dist/status-project.d.ts +0 -2
- package/dist/status-project.js +4 -6
- package/dist/telemetry/events.d.ts +3 -2
- package/dist/telemetry/events.js +11 -1
- package/dist/telemetry/exception.js +14 -0
- package/dist/text-ingest.js +1 -1
- package/dist/tree-picker-tui.d.ts +0 -1
- package/dist/tree-picker-tui.js +2 -3
- package/package.json +2 -1
- package/assets/python/kaelio_ktx-0.11.0-py3-none-any.whl +0 -0
package/dist/setup-context.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join, resolve } from 'node:path';
|
|
|
4
4
|
import { loadKtxProject } from './context/project/project.js';
|
|
5
5
|
import { markKtxSetupStateStepComplete, readKtxSetupState } from './context/project/setup-config.js';
|
|
6
6
|
import { serializeKtxProjectConfig } from './context/project/config.js';
|
|
7
|
-
import { errorMessage, writePrefixedLines } from './clack.js';
|
|
7
|
+
import { createCliSpinner, errorMessage, writePrefixedLines } from './clack.js';
|
|
8
8
|
import { formatErrorDetail } from './telemetry/scrubber.js';
|
|
9
9
|
import { buildPublicIngestPlan } from './public-ingest.js';
|
|
10
10
|
import { runKtxConnection } from './connection.js';
|
|
@@ -189,13 +189,22 @@ async function defaultGateTestConnection(projectDir, connectionId, io) {
|
|
|
189
189
|
* test's output is captured in a buffer and discarded so raw error text never
|
|
190
190
|
* reaches the user — callers surface only the connection id and connector type.
|
|
191
191
|
*/
|
|
192
|
-
async function testRequiredConnections(projectDir, project, targets, testConnection) {
|
|
192
|
+
async function testRequiredConnections(projectDir, project, targets, testConnection, io) {
|
|
193
193
|
const failures = [];
|
|
194
|
-
|
|
194
|
+
const connectionIds = requiredConnectionIds(targets);
|
|
195
|
+
for (const [index, connectionId] of connectionIds.entries()) {
|
|
196
|
+
const driver = connectorTypeLabel(project, connectionId);
|
|
197
|
+
const counter = connectionIds.length > 1 ? ` (${index + 1}/${connectionIds.length})` : '';
|
|
198
|
+
const spinner = createCliSpinner(io);
|
|
199
|
+
spinner.start(`Testing connection ${connectionId}${counter}…`);
|
|
195
200
|
const buffered = createBufferedCommandIo();
|
|
196
201
|
const exitCode = await testConnection(projectDir, connectionId, buffered);
|
|
197
|
-
if (exitCode
|
|
198
|
-
|
|
202
|
+
if (exitCode === 0) {
|
|
203
|
+
spinner.stop(`Connection ${connectionId} (${driver}) is reachable`);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
spinner.error(`Connection ${connectionId} (${driver}) is not reachable`);
|
|
207
|
+
failures.push({ connectionId, driver });
|
|
199
208
|
}
|
|
200
209
|
}
|
|
201
210
|
return failures.length === 0 ? { ok: true } : { ok: false, failures };
|
|
@@ -213,7 +222,7 @@ async function prepareBuildTargets(args, io) {
|
|
|
213
222
|
if (args.allowEmpty === true) {
|
|
214
223
|
return { kind: 'result', result: { status: 'skipped', projectDir: args.projectDir } };
|
|
215
224
|
}
|
|
216
|
-
io.stderr.write('No databases or context sources are configured for a
|
|
225
|
+
io.stderr.write('No databases or context sources are configured for a ktx context build.\n');
|
|
217
226
|
return { kind: 'result', result: { status: 'failed', projectDir: args.projectDir } };
|
|
218
227
|
}
|
|
219
228
|
const preflightPlan = buildPublicIngestPlan(project, { projectDir: project.projectDir, all: true });
|
|
@@ -228,12 +237,12 @@ async function prepareBuildTargets(args, io) {
|
|
|
228
237
|
return { kind: 'ready', project, targets };
|
|
229
238
|
}
|
|
230
239
|
function writeConnectionGateFailureLines(io, projectDir, failures) {
|
|
231
|
-
io.stderr.write('
|
|
240
|
+
io.stderr.write('ktx cannot build context: a required connection failed its live test.\n\n');
|
|
232
241
|
io.stderr.write('Failed connections:\n');
|
|
233
242
|
for (const failure of failures) {
|
|
234
243
|
io.stderr.write(` ${failure.connectionId} (${failure.driver})\n`);
|
|
235
244
|
}
|
|
236
|
-
io.stderr.write('\nEach connection must be reachable before
|
|
245
|
+
io.stderr.write('\nEach connection must be reachable before ktx builds context.\n');
|
|
237
246
|
io.stderr.write(`Run \`ktx connection test <id> --project-dir ${resolve(projectDir)}\` to see the error, fix the connection, then retry.\n`);
|
|
238
247
|
}
|
|
239
248
|
function connectionGateFailureReason(failures) {
|
|
@@ -391,7 +400,7 @@ async function markContextComplete(projectDir) {
|
|
|
391
400
|
await markKtxSetupStateStepComplete(projectDir, 'context');
|
|
392
401
|
}
|
|
393
402
|
function writeMissingCapabilities(missing, io) {
|
|
394
|
-
io.stderr.write('
|
|
403
|
+
io.stderr.write('ktx cannot build agent-ready context yet.\n\n');
|
|
395
404
|
io.stderr.write('Missing:\n');
|
|
396
405
|
for (const item of missing) {
|
|
397
406
|
io.stderr.write(` ${item}\n`);
|
|
@@ -404,7 +413,7 @@ function writeSkippedContext(io) {
|
|
|
404
413
|
io.stdout.write('\nLeaving context unbuilt for now.\n');
|
|
405
414
|
}
|
|
406
415
|
function writeSuccess(readiness, targets, io) {
|
|
407
|
-
io.stdout.write('\
|
|
416
|
+
io.stdout.write('\nktx context is ready for agents.\n\n');
|
|
408
417
|
io.stdout.write('Databases:\n');
|
|
409
418
|
if (targets.primarySourceConnectionIds.length === 0) {
|
|
410
419
|
io.stdout.write(' none\n');
|
|
@@ -428,7 +437,7 @@ function writeSuccess(readiness, targets, io) {
|
|
|
428
437
|
io.stdout.write(` Semantic search: ${readiness.semanticSearchReady ? 'ready' : 'not ready'}\n`);
|
|
429
438
|
}
|
|
430
439
|
function writeExistingContextSuccess(readiness, io) {
|
|
431
|
-
io.stdout.write('\
|
|
440
|
+
io.stdout.write('\nktx context is ready for agents.\n\n');
|
|
432
441
|
io.stdout.write('Existing context artifacts were found from setup ingest.\n\n');
|
|
433
442
|
io.stdout.write('Verification:\n');
|
|
434
443
|
io.stdout.write(` Agent context: ${readiness.agentContextReady ? 'ready' : 'not ready'}\n`);
|
|
@@ -436,8 +445,8 @@ function writeExistingContextSuccess(readiness, io) {
|
|
|
436
445
|
}
|
|
437
446
|
async function promptForBuild(prompts) {
|
|
438
447
|
return (await prompts.select({
|
|
439
|
-
message: 'Build
|
|
440
|
-
'
|
|
448
|
+
message: 'Build ktx context for agents?\n\n' +
|
|
449
|
+
'ktx is fully configured and ready to build context. This may take a few minutes to a few hours.',
|
|
441
450
|
options: [
|
|
442
451
|
{ value: 'build', label: 'Build context now (recommended)' },
|
|
443
452
|
{ value: 'skip', label: 'Leave context unbuilt and exit setup' },
|
|
@@ -517,7 +526,7 @@ async function runBuild(args, io, deps, project, targets) {
|
|
|
517
526
|
failureReason: readiness.details.join(' '),
|
|
518
527
|
...(lastSourceProgress ? { sourceProgress: lastSourceProgress } : {}),
|
|
519
528
|
});
|
|
520
|
-
io.stderr.write('
|
|
529
|
+
io.stderr.write('ktx context build did not pass agent-readiness verification.\n');
|
|
521
530
|
for (const detail of readiness.details) {
|
|
522
531
|
io.stderr.write(` ${detail}\n`);
|
|
523
532
|
}
|
|
@@ -607,7 +616,7 @@ export async function runKtxSetupContextStep(args, io, deps = {}) {
|
|
|
607
616
|
// error text.
|
|
608
617
|
const testConnection = deps.testConnection ?? defaultGateTestConnection;
|
|
609
618
|
while (true) {
|
|
610
|
-
const gate = await testRequiredConnections(args.projectDir, project, targets, testConnection);
|
|
619
|
+
const gate = await testRequiredConnections(args.projectDir, project, targets, testConnection, io);
|
|
611
620
|
if (gate.ok) {
|
|
612
621
|
return await runBuild(args, io, deps, project, targets);
|
|
613
622
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { KtxLlmRuntimePort } from './context/llm/runtime-port.js';
|
|
2
2
|
import { type ProposeQueryHistoryServiceAccountFiltersInput, type QueryHistoryFilterProposal } from './context/ingest/adapters/historic-sql/query-history-filter-picker.js';
|
|
3
3
|
import { type HistoricSqlReadinessProbe } from './context/ingest/historic-sql-probes.js';
|
|
4
|
+
import { type KtxProjectConnectionConfig } from './context/project/config.js';
|
|
4
5
|
import { loadKtxProject } from './context/project/project.js';
|
|
5
6
|
import type { KtxTableListEntry } from './context/scan/types.js';
|
|
6
7
|
import { type KtxCliIo } from './cli-runtime.js';
|
|
@@ -90,6 +91,8 @@ export interface KtxSetupDatabasesDeps {
|
|
|
90
91
|
createQueryHistoryLlmRuntime?: (projectDir: string, project: Awaited<ReturnType<typeof loadKtxProject>>) => KtxLlmRuntimePort | null;
|
|
91
92
|
}
|
|
92
93
|
/** @internal */
|
|
94
|
+
export declare function federationNoticeFor(connections: Record<string, KtxProjectConnectionConfig>, projectDir: string): string | null;
|
|
95
|
+
/** @internal */
|
|
93
96
|
export declare function managedDaemonOptionsForSetupQueryHistoryPicker(input: {
|
|
94
97
|
projectDir: string;
|
|
95
98
|
args: Pick<KtxSetupDatabasesArgs, 'cliVersion' | 'runtimeInstallPolicy' | 'inputMode'>;
|
package/dist/setup-databases.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFile, writeFile } from 'node:fs/promises';
|
|
|
3
3
|
import { delimiter, dirname, join } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
|
+
import { deriveFederatedConnection, FEDERATED_CONNECTION_ID } from './context/connections/federation.js';
|
|
6
7
|
import { getDriverRegistration } from './context/connections/drivers.js';
|
|
7
8
|
import { createLocalKtxLlmRuntimeFromConfig } from './context/llm/local-config.js';
|
|
8
9
|
import { queryHistoryDialectForConnection } from './context/ingest/adapters/historic-sql/connection-dialect.js';
|
|
@@ -119,7 +120,7 @@ function driverLabel(driver) {
|
|
|
119
120
|
return DRIVER_LABELS[driver];
|
|
120
121
|
}
|
|
121
122
|
function connectionNamePrompt(label) {
|
|
122
|
-
return `Name this ${label} connection\
|
|
123
|
+
return `Name this ${label} connection\nktx will use this short name in commands and config. You can rename it now.`;
|
|
123
124
|
}
|
|
124
125
|
function missingConnectionDetailsPrompt(label, canReturnToDriverSelection) {
|
|
125
126
|
const backDestination = canReturnToDriverSelection ? 'database selection' : 'the previous setup step';
|
|
@@ -163,12 +164,6 @@ function numberConfigField(connection, field) {
|
|
|
163
164
|
const value = connection?.[field];
|
|
164
165
|
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
165
166
|
}
|
|
166
|
-
function historicSqlConfigRecord(connection) {
|
|
167
|
-
const historicSql = connection?.historicSql;
|
|
168
|
-
return historicSql && typeof historicSql === 'object' && !Array.isArray(historicSql)
|
|
169
|
-
? historicSql
|
|
170
|
-
: null;
|
|
171
|
-
}
|
|
172
167
|
function contextRecord(connection) {
|
|
173
168
|
const context = connection?.context;
|
|
174
169
|
return context && typeof context === 'object' && !Array.isArray(context) ? context : {};
|
|
@@ -179,28 +174,15 @@ function queryHistoryConfigRecord(connection) {
|
|
|
179
174
|
? queryHistory
|
|
180
175
|
: null;
|
|
181
176
|
}
|
|
182
|
-
function stripLegacyHistoricSql(connection) {
|
|
183
|
-
const { historicSql: _historicSql, ...rest } = connection;
|
|
184
|
-
return rest;
|
|
185
|
-
}
|
|
186
177
|
function withQueryHistoryConfig(connection, queryHistory) {
|
|
187
178
|
return {
|
|
188
|
-
...
|
|
179
|
+
...connection,
|
|
189
180
|
context: {
|
|
190
181
|
...contextRecord(connection),
|
|
191
182
|
queryHistory,
|
|
192
183
|
},
|
|
193
184
|
};
|
|
194
185
|
}
|
|
195
|
-
function migrateLegacyHistoricSqlConnection(connection) {
|
|
196
|
-
const existingQueryHistory = queryHistoryConfigRecord(connection);
|
|
197
|
-
const legacy = historicSqlConfigRecord(connection);
|
|
198
|
-
if (existingQueryHistory || !legacy) {
|
|
199
|
-
return existingQueryHistory ? stripLegacyHistoricSql(connection) : connection;
|
|
200
|
-
}
|
|
201
|
-
const { dialect: _dialect, ...queryHistory } = legacy;
|
|
202
|
-
return withQueryHistoryConfig(connection, queryHistory);
|
|
203
|
-
}
|
|
204
186
|
function setupHistoricSqlProbeResult(outcome) {
|
|
205
187
|
if (!outcome) {
|
|
206
188
|
return { ok: true, lines: [] };
|
|
@@ -864,6 +846,21 @@ async function writeConnectionConfig(input) {
|
|
|
864
846
|
if (queryHistory?.enabled === true) {
|
|
865
847
|
await ensureHistoricSqlIngestDefaults(input.projectDir);
|
|
866
848
|
}
|
|
849
|
+
if (input.io) {
|
|
850
|
+
const federationNotice = federationNoticeFor(config.connections, input.projectDir);
|
|
851
|
+
if (federationNotice) {
|
|
852
|
+
writeSetupSection(input.io, 'Federated connection available', [federationNotice]);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
/** @internal */
|
|
857
|
+
export function federationNoticeFor(connections, projectDir) {
|
|
858
|
+
const descriptor = deriveFederatedConnection(connections, projectDir);
|
|
859
|
+
if (!descriptor) {
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
const names = descriptor.members.map((m) => m.connectionId).join(', ');
|
|
863
|
+
return `Detected ${descriptor.members.length} attach-compatible databases (${names}). Run a cross-database join as read-only SQL against \`${FEDERATED_CONNECTION_ID}\` (ktx sql -c ${FEDERATED_CONNECTION_ID} "SELECT ..."), using catalog-qualified table names.`;
|
|
867
864
|
}
|
|
868
865
|
async function disableConnectionQueryHistory(projectDir, connectionId) {
|
|
869
866
|
const project = await loadKtxProject({ projectDir });
|
|
@@ -871,7 +868,7 @@ async function disableConnectionQueryHistory(projectDir, connectionId) {
|
|
|
871
868
|
if (!connection) {
|
|
872
869
|
return;
|
|
873
870
|
}
|
|
874
|
-
const existing = queryHistoryConfigRecord(connection) ??
|
|
871
|
+
const existing = queryHistoryConfigRecord(connection) ?? {};
|
|
875
872
|
await writeConnectionConfig({
|
|
876
873
|
projectDir,
|
|
877
874
|
connectionId,
|
|
@@ -1004,32 +1001,29 @@ async function maybeConfigureDatabaseScope(input) {
|
|
|
1004
1001
|
}
|
|
1005
1002
|
const cliSchemas = input.args.databaseSchemas;
|
|
1006
1003
|
if (input.args.inputMode === 'disabled') {
|
|
1007
|
-
if (spec) {
|
|
1008
|
-
|
|
1009
|
-
if (scopeToWrite.length === 0) {
|
|
1010
|
-
try {
|
|
1011
|
-
scopeToWrite = unique(await (input.deps.listSchemas ?? defaultListSchemas)(input.projectDir, input.connectionId));
|
|
1012
|
-
}
|
|
1013
|
-
catch (error) {
|
|
1014
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
1015
|
-
input.io.stderr.write(`Could not discover ${spec.promptLabel.toLowerCase()} for ${input.connectionId}; ${detail}\n`);
|
|
1016
|
-
return okValidateResult();
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
if (scopeToWrite.length > 0) {
|
|
1020
|
-
await writeScopeConfig({
|
|
1021
|
-
projectDir: input.projectDir,
|
|
1022
|
-
connectionId: input.connectionId,
|
|
1023
|
-
values: scopeToWrite,
|
|
1024
|
-
spec,
|
|
1025
|
-
});
|
|
1026
|
-
const capitalNounPlural = spec.nounPlural[0].toUpperCase() + spec.nounPlural.slice(1);
|
|
1027
|
-
writeSetupSection(input.io, `${capitalNounPlural} saved for ${input.connectionId}`, [
|
|
1028
|
-
`✓ ${scopeToWrite.join(', ')}`,
|
|
1029
|
-
]);
|
|
1030
|
-
}
|
|
1004
|
+
if (!spec) {
|
|
1005
|
+
return okValidateResult();
|
|
1031
1006
|
}
|
|
1032
|
-
|
|
1007
|
+
if (cliSchemas.length > 0) {
|
|
1008
|
+
await writeScopeConfig({
|
|
1009
|
+
projectDir: input.projectDir,
|
|
1010
|
+
connectionId: input.connectionId,
|
|
1011
|
+
values: cliSchemas,
|
|
1012
|
+
spec,
|
|
1013
|
+
});
|
|
1014
|
+
const capitalNounPlural = spec.nounPlural[0].toUpperCase() + spec.nounPlural.slice(1);
|
|
1015
|
+
writeSetupSection(input.io, `${capitalNounPlural} saved for ${input.connectionId}`, [
|
|
1016
|
+
`✓ ${cliSchemas.join(', ')}`,
|
|
1017
|
+
]);
|
|
1018
|
+
return okValidateResult();
|
|
1019
|
+
}
|
|
1020
|
+
if (existingScope.length > 0) {
|
|
1021
|
+
return okValidateResult();
|
|
1022
|
+
}
|
|
1023
|
+
writePrefixedLines((chunk) => input.io.stderr.write(chunk), `No ${spec.nounPlural} configured for ${input.connectionId}. ` +
|
|
1024
|
+
`Pass --database-schema <${spec.noun}> (repeatable) or set ` +
|
|
1025
|
+
`connections.${input.connectionId}.${spec.configArrayField} in ktx.yaml.`);
|
|
1026
|
+
return failedValidateResult();
|
|
1033
1027
|
}
|
|
1034
1028
|
if (spec && cliSchemas.length > 0) {
|
|
1035
1029
|
await writeScopeConfig({
|
|
@@ -1158,20 +1152,14 @@ async function ensureHistoricSqlIngestDefaults(projectDir) {
|
|
|
1158
1152
|
}
|
|
1159
1153
|
async function markDatabasesComplete(projectDir, connectionIds) {
|
|
1160
1154
|
const project = await loadKtxProject({ projectDir });
|
|
1161
|
-
const config = setKtxSetupDatabaseConnectionIds(
|
|
1162
|
-
...project.config,
|
|
1163
|
-
connections: Object.fromEntries(Object.entries(project.config.connections).map(([connectionId, connection]) => [
|
|
1164
|
-
connectionId,
|
|
1165
|
-
migrateLegacyHistoricSqlConnection(connection),
|
|
1166
|
-
])),
|
|
1167
|
-
}, unique(connectionIds));
|
|
1155
|
+
const config = setKtxSetupDatabaseConnectionIds(project.config, unique(connectionIds));
|
|
1168
1156
|
await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8');
|
|
1169
1157
|
await markKtxSetupStateStepComplete(projectDir, 'databases');
|
|
1170
1158
|
}
|
|
1171
1159
|
async function maybeRunHistoricSqlSetupProbe(input) {
|
|
1172
1160
|
const project = await loadKtxProject({ projectDir: input.projectDir });
|
|
1173
1161
|
const connection = project.config.connections[input.connectionId];
|
|
1174
|
-
const queryHistory = queryHistoryConfigRecord(connection)
|
|
1162
|
+
const queryHistory = queryHistoryConfigRecord(connection);
|
|
1175
1163
|
if (queryHistory?.enabled !== true) {
|
|
1176
1164
|
return true;
|
|
1177
1165
|
}
|
|
@@ -1494,13 +1482,13 @@ async function chooseDrivers(args, io, prompts, options) {
|
|
|
1494
1482
|
return [];
|
|
1495
1483
|
}
|
|
1496
1484
|
if (args.inputMode === 'disabled') {
|
|
1497
|
-
io.stderr.write('
|
|
1485
|
+
io.stderr.write('ktx cannot work without a database. Pass --database or --database-connection-id, or pass --skip-databases to leave setup incomplete.\n');
|
|
1498
1486
|
return 'missing-input';
|
|
1499
1487
|
}
|
|
1500
1488
|
const initialValues = unique(options?.initialDrivers ?? []);
|
|
1501
1489
|
createKtxSetupUiAdapter().note(`Get demo credentials: ${KTX_DEMO_START_URL}`, '🎁 Need a warehouse to play with?', io);
|
|
1502
1490
|
const choices = await prompts.multiselect({
|
|
1503
|
-
message: withMultiselectNavigation('Which databases should
|
|
1491
|
+
message: withMultiselectNavigation('Which databases should ktx connect to?'),
|
|
1504
1492
|
options: [...DRIVER_OPTIONS],
|
|
1505
1493
|
...(initialValues.length > 0 ? { initialValues } : {}),
|
|
1506
1494
|
required: true,
|
|
@@ -1722,7 +1710,7 @@ async function runPrimarySourceFullEdit(input) {
|
|
|
1722
1710
|
}
|
|
1723
1711
|
export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
|
|
1724
1712
|
if (args.skipDatabases) {
|
|
1725
|
-
io.stdout.write('│ Database setup skipped.
|
|
1713
|
+
io.stdout.write('│ Database setup skipped. ktx cannot work until you add a database.\n');
|
|
1726
1714
|
return { status: 'skipped', projectDir: args.projectDir };
|
|
1727
1715
|
}
|
|
1728
1716
|
const prompts = deps.prompts ?? createPromptAdapter();
|
|
@@ -1836,7 +1824,7 @@ export async function runKtxSetupDatabasesStep(args, io, deps = {}) {
|
|
|
1836
1824
|
return { status: 'missing-input', projectDir: args.projectDir };
|
|
1837
1825
|
if (drivers.length === 0) {
|
|
1838
1826
|
await markDatabasesComplete(args.projectDir, []);
|
|
1839
|
-
io.stdout.write('│
|
|
1827
|
+
io.stdout.write('│ ktx cannot work without a database.\n');
|
|
1840
1828
|
return { status: 'skipped', projectDir: args.projectDir };
|
|
1841
1829
|
}
|
|
1842
1830
|
let returnToDriverSelection = false;
|
package/dist/setup-demo-tour.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createStaticCliSpinner, runWithCliSpinner } from './clack.js';
|
|
1
2
|
import { createRepainter, renderContextBuildView } from './context-build-view.js';
|
|
2
3
|
import { defaultDemoProjectDir, ensureSeededDemoProject } from './demo-assets.js';
|
|
3
4
|
import { runKtxSetupAgentsStep } from './setup-agents.js';
|
|
@@ -48,7 +49,7 @@ function createTargetState(target) {
|
|
|
48
49
|
export function renderDemoBanner(projectDir) {
|
|
49
50
|
const lines = [
|
|
50
51
|
'',
|
|
51
|
-
`┌ ${cyan('Demo mode')} — data has been pre-processed and
|
|
52
|
+
`┌ ${cyan('Demo mode')} — data has been pre-processed and ktx context is already built.`,
|
|
52
53
|
'│ This walkthrough illustrates the setup steps. Selections are pre-filled and read-only.',
|
|
53
54
|
];
|
|
54
55
|
if (projectDir) {
|
|
@@ -73,7 +74,7 @@ export function renderDemoAgentTransition() {
|
|
|
73
74
|
const lines = [
|
|
74
75
|
'┌ Demo project is ready — let\'s connect your agent',
|
|
75
76
|
'│',
|
|
76
|
-
'│ Your
|
|
77
|
+
'│ Your ktx context has been built with demo data.',
|
|
77
78
|
'│ Select an agent to start using it.',
|
|
78
79
|
'└',
|
|
79
80
|
];
|
|
@@ -83,17 +84,17 @@ export function renderDemoAgentTransition() {
|
|
|
83
84
|
export function renderDemoCompletionSummary(projectDir, agentInstalled) {
|
|
84
85
|
const lines = [
|
|
85
86
|
'',
|
|
86
|
-
`${cyan('★')}
|
|
87
|
+
`${cyan('★')} ktx demo is ready`,
|
|
87
88
|
'',
|
|
88
89
|
];
|
|
89
90
|
if (agentInstalled) {
|
|
90
|
-
lines.push(' Your agent is connected to a demo
|
|
91
|
+
lines.push(' Your agent is connected to a demo ktx project.');
|
|
91
92
|
}
|
|
92
93
|
else {
|
|
93
94
|
lines.push(' Demo project created. Connect an agent to start using it:');
|
|
94
95
|
lines.push(` $ ${cyan(`ktx setup --agents --project-dir ${projectDir}`)}`);
|
|
95
96
|
}
|
|
96
|
-
lines.push('', ` ${dim('⚠')} This project is in a temporary directory and will be`, ' cleaned up by your system. To set up
|
|
97
|
+
lines.push('', ` ${dim('⚠')} This project is in a temporary directory and will be`, ' cleaned up by your system. To set up ktx with your own', ' data, run: ktx setup', '', ` Project: ${projectDir}`);
|
|
97
98
|
return lines.join('\n');
|
|
98
99
|
}
|
|
99
100
|
// ---------------------------------------------------------------------------
|
|
@@ -171,9 +172,9 @@ export function buildDemoReplayTimeline() {
|
|
|
171
172
|
function renderDemoContextCompletionSummary() {
|
|
172
173
|
const lines = [
|
|
173
174
|
'',
|
|
174
|
-
`${cyan('★')}
|
|
175
|
+
`${cyan('★')} ktx finished building context`,
|
|
175
176
|
'',
|
|
176
|
-
'
|
|
177
|
+
' ktx created:',
|
|
177
178
|
` ${cyan('📊')} 46 semantic layer definitions`,
|
|
178
179
|
` ${cyan('📝')} 28 wiki pages`,
|
|
179
180
|
'',
|
|
@@ -249,7 +250,10 @@ export async function runDemoTour(args, io, deps = {}) {
|
|
|
249
250
|
const waitNav = deps.waitForNavigation ?? waitForDemoNavigation;
|
|
250
251
|
const ensureProject = deps.ensureProject ?? ensureSeededDemoProject;
|
|
251
252
|
const projectDir = defaultDemoProjectDir();
|
|
252
|
-
|
|
253
|
+
// Static (stderr-only) spinner: the demo navigation below reads stdin in raw mode,
|
|
254
|
+
// and an animated clack spinner would leave stdin dirty so the first keypress wait
|
|
255
|
+
// sees a stray key and skips the intro.
|
|
256
|
+
await runWithCliSpinner(createStaticCliSpinner(io), { start: 'Preparing demo project…', success: 'Demo project ready', failure: 'Could not prepare demo project' }, () => ensureProject({ projectDir, force: false, io, cliVersion: args.cliVersion }));
|
|
253
257
|
io.stdout.write(renderDemoBanner(projectDir) + '\n');
|
|
254
258
|
io.stdout.write(`\n│ ${dim('Press Enter to continue, Escape to go back')}\n└\n`);
|
|
255
259
|
const introDirection = await waitNav();
|
package/dist/setup-embeddings.js
CHANGED
|
@@ -4,7 +4,7 @@ import { serializeKtxProjectConfig } from './context/project/config.js';
|
|
|
4
4
|
import { loadKtxProject } from './context/project/project.js';
|
|
5
5
|
import { markKtxSetupStateStepComplete, readKtxSetupState } from './context/project/setup-config.js';
|
|
6
6
|
import { runKtxEmbeddingHealthCheck } from './llm/embedding-health.js';
|
|
7
|
-
import {
|
|
7
|
+
import { createCliSpinner, errorMessage, writePrefixedLines } from './clack.js';
|
|
8
8
|
import { ensureManagedLocalEmbeddingsDaemon, managedLocalEmbeddingHealthConfig, } from './managed-local-embeddings.js';
|
|
9
9
|
import { ManagedPythonDaemonStartError } from './managed-python-daemon.js';
|
|
10
10
|
import { withTextInputNavigation } from './prompt-navigation.js';
|
|
@@ -20,7 +20,7 @@ const DEFAULTS = {
|
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
22
|
const LOCAL_EMBEDDING_BACKEND = 'sentence-transformers';
|
|
23
|
-
const EMBEDDING_OPTION_PROMPT_CONTEXT = '
|
|
23
|
+
const EMBEDDING_OPTION_PROMPT_CONTEXT = 'ktx uses embeddings for semantic search over semantic-layer sources, wiki context, schema metadata, ' +
|
|
24
24
|
'and relationship evidence.';
|
|
25
25
|
const LOCAL_EMBEDDING_HEALTH_TIMEOUT_MS = 120_000;
|
|
26
26
|
const LOCAL_EMBEDDING_STDERR_TAIL_LINES = 40;
|
|
@@ -136,7 +136,7 @@ async function chooseCredentialRef(backend, args, io, deps) {
|
|
|
136
136
|
const defaultEnv = DEFAULTS[backend].envName ?? 'EMBEDDING_API_KEY';
|
|
137
137
|
const prompts = deps.prompts ?? createPromptAdapter();
|
|
138
138
|
const choice = await prompts.select({
|
|
139
|
-
message: `How should
|
|
139
|
+
message: `How should ktx find your ${embeddingBackendDisplayName(backend)} embedding API key?`,
|
|
140
140
|
options: [
|
|
141
141
|
{ value: 'paste', label: 'Paste a key and save it as a local secret file' },
|
|
142
142
|
{ value: 'env', label: `Use ${defaultEnv} from the environment` },
|
|
@@ -148,7 +148,7 @@ async function chooseCredentialRef(backend, args, io, deps) {
|
|
|
148
148
|
}
|
|
149
149
|
if (choice === 'paste') {
|
|
150
150
|
io.stdout.write(`│ ${[
|
|
151
|
-
`
|
|
151
|
+
`ktx will save the key in .ktx/secrets/${backend}-api-key with local file permissions,`,
|
|
152
152
|
'then write a file: reference in ktx.yaml.',
|
|
153
153
|
].join(' ')}\n`);
|
|
154
154
|
const value = await prompts.password({ message: withTextInputNavigation(`${backend} embedding API key`) });
|
|
@@ -181,7 +181,7 @@ async function chooseEmbeddingBackend(args, deps) {
|
|
|
181
181
|
return LOCAL_EMBEDDING_BACKEND;
|
|
182
182
|
}
|
|
183
183
|
const choice = await (deps.prompts ?? createPromptAdapter()).select({
|
|
184
|
-
message: `Which embedding option should
|
|
184
|
+
message: `Which embedding option should ktx use?\n\n${EMBEDDING_OPTION_PROMPT_CONTEXT}`,
|
|
185
185
|
options: [
|
|
186
186
|
{ value: 'sentence-transformers', label: 'Local sentence-transformers embeddings' },
|
|
187
187
|
{ value: 'openai', label: 'OpenAI embeddings', hint: 'recommended' },
|
|
@@ -211,19 +211,19 @@ async function readLocalEmbeddingDaemonStderrTail(stderrLog) {
|
|
|
211
211
|
function localEmbeddingSetupMessage(message, stderrTail = []) {
|
|
212
212
|
const lines = [
|
|
213
213
|
`Local embedding health check failed: ${message}`,
|
|
214
|
-
'Local embeddings use the
|
|
214
|
+
'Local embeddings use the ktx-managed Python runtime.',
|
|
215
215
|
'Prepare the runtime with: ktx admin runtime start --feature local-embeddings',
|
|
216
216
|
'Use --yes with setup to install and start the runtime without prompting.',
|
|
217
217
|
'The first run may download Python packages and the all-MiniLM-L6-v2 model.',
|
|
218
218
|
];
|
|
219
219
|
if (stderrTail.length > 0) {
|
|
220
|
-
lines.push('Recent
|
|
220
|
+
lines.push('Recent ktx daemon stderr:', ...stderrTail);
|
|
221
221
|
}
|
|
222
222
|
return lines.join('\n');
|
|
223
223
|
}
|
|
224
224
|
async function promptAfterLocalEmbeddingFailure(deps) {
|
|
225
225
|
const choice = await (deps.prompts ?? createPromptAdapter()).select({
|
|
226
|
-
message: 'Local embeddings are not reachable. Start the local
|
|
226
|
+
message: 'Local embeddings are not reachable. Start the local ktx daemon, then retry.',
|
|
227
227
|
options: [
|
|
228
228
|
{ value: 'retry', label: 'Retry' },
|
|
229
229
|
{ value: 'openai', label: 'Use OpenAI embeddings' },
|
|
@@ -329,7 +329,7 @@ export async function runKtxSetupEmbeddingsStep(args, io, deps = {}) {
|
|
|
329
329
|
dimensions,
|
|
330
330
|
credentialValue,
|
|
331
331
|
});
|
|
332
|
-
const healthSpinner = (deps.spinner ?? (() =>
|
|
332
|
+
const healthSpinner = (deps.spinner ?? (() => createCliSpinner(io)))();
|
|
333
333
|
const progress = startHealthCheckProgress(healthSpinner, healthCheckStartText(selectedBackend, model, dimensions));
|
|
334
334
|
let health;
|
|
335
335
|
try {
|
package/dist/setup-interrupt.js
CHANGED
|
@@ -2,7 +2,7 @@ import { stdin } from 'node:process';
|
|
|
2
2
|
import { cancel, confirm, isCancel as isClackCancel } from '@clack/prompts';
|
|
3
3
|
export class KtxSetupExitError extends Error {
|
|
4
4
|
constructor() {
|
|
5
|
-
super('
|
|
5
|
+
super('ktx setup exit requested');
|
|
6
6
|
this.name = 'KtxSetupExitError';
|
|
7
7
|
}
|
|
8
8
|
}
|
package/dist/setup-models.d.ts
CHANGED
|
@@ -32,7 +32,10 @@ export type KtxSetupModelResult = {
|
|
|
32
32
|
status: 'failed';
|
|
33
33
|
projectDir: string;
|
|
34
34
|
};
|
|
35
|
-
|
|
35
|
+
declare const KTX_SETUP_LLM_BACKENDS: readonly ["claude-code", "codex", "anthropic", "vertex"];
|
|
36
|
+
export type KtxSetupLlmBackend = (typeof KTX_SETUP_LLM_BACKENDS)[number];
|
|
37
|
+
/** Validates a raw CLI or prompt value against the setup-selectable LLM backends. */
|
|
38
|
+
export declare function isKtxSetupLlmBackend(value: string): value is KtxSetupLlmBackend;
|
|
36
39
|
/** @internal */
|
|
37
40
|
export interface KtxSetupModelPromptAdapter {
|
|
38
41
|
select(options: {
|