@kaelio/ktx 0.9.0 → 0.11.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.9.0-py3-none-any.whl → kaelio_ktx-0.11.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 +46 -2
- package/dist/cli-runtime.d.ts +5 -0
- package/dist/cli-runtime.js +50 -0
- package/dist/commands/setup-commands.js +2 -3
- package/dist/community-cta.d.ts +11 -0
- package/dist/community-cta.js +19 -0
- package/dist/connection.js +23 -1
- 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/core/git-env.d.ts +12 -1
- package/dist/context/core/git-env.js +17 -2
- package/dist/context/core/git.service.js +15 -7
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +1 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +6 -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/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-bundle-runtime.js +11 -4
- 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-runtime.d.ts +3 -3
- package/dist/context/llm/codex-runtime.js +90 -47
- package/dist/context/llm/local-config.d.ts +15 -5
- package/dist/context/llm/local-config.js +6 -1
- 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 +12 -0
- package/dist/context/project/config.js +35 -0
- 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/tools/context-candidate-mark.tool.d.ts +2 -2
- package/dist/context-build-view.d.ts +13 -0
- package/dist/context-build-view.js +60 -1
- 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/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/io/tty.d.ts +9 -0
- package/dist/io/tty.js +5 -0
- package/dist/links.d.ts +1 -0
- package/dist/links.js +1 -0
- package/dist/memory-flow-hud.js +8 -16
- package/dist/public-ingest.js +50 -15
- package/dist/reveal-password-prompt.d.ts +24 -0
- package/dist/reveal-password-prompt.js +78 -0
- package/dist/scan.js +18 -2
- package/dist/setup-agents.js +1 -5
- package/dist/setup-databases.d.ts +1 -0
- package/dist/setup-databases.js +23 -3
- package/dist/setup-demo-tour.js +1 -0
- package/dist/setup-embeddings.js +1 -1
- package/dist/setup-models.d.ts +1 -14
- package/dist/setup-models.js +116 -340
- package/dist/setup-prompts.js +4 -7
- package/dist/setup-sources.js +7 -7
- package/dist/setup.d.ts +26 -1
- package/dist/setup.js +78 -7
- 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/telemetry/command-hook.d.ts +24 -0
- package/dist/telemetry/command-hook.js +37 -3
- package/dist/telemetry/emitter.d.ts +10 -0
- package/dist/telemetry/emitter.js +31 -0
- package/dist/telemetry/events.d.ts +24 -0
- package/dist/telemetry/events.js +15 -0
- package/dist/telemetry/exception.d.ts +18 -0
- package/dist/telemetry/exception.js +162 -0
- package/dist/telemetry/index.d.ts +4 -3
- package/dist/telemetry/index.js +3 -2
- package/dist/telemetry/redaction-secrets.d.ts +11 -0
- package/dist/telemetry/redaction-secrets.js +92 -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 +8 -1
- 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/clack.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { KtxCliIo } from './cli-runtime.js';
|
|
2
|
+
export interface CliStyleEnv {
|
|
3
|
+
NO_COLOR?: string;
|
|
4
|
+
TERM?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function dim(text: string, env?: CliStyleEnv): string;
|
|
7
|
+
export declare function cyan(text: string, env?: CliStyleEnv): string;
|
|
2
8
|
export interface RailBufferedSource {
|
|
3
9
|
stdoutText(): string;
|
|
4
10
|
stderrText(): string;
|
package/dist/clack.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { cancel, confirm, isCancel, log, spinner } from '@clack/prompts';
|
|
2
2
|
const ESC = String.fromCharCode(0x1b);
|
|
3
|
+
function ansiEnabled(env = process.env) {
|
|
4
|
+
return !env.NO_COLOR && env.TERM !== 'dumb';
|
|
5
|
+
}
|
|
6
|
+
function ansiColor(text, open, close, env) {
|
|
7
|
+
if (!ansiEnabled(env)) {
|
|
8
|
+
return text;
|
|
9
|
+
}
|
|
10
|
+
return `${ESC}[${open}m${text}${ESC}[${close}m`;
|
|
11
|
+
}
|
|
12
|
+
export function dim(text, env) {
|
|
13
|
+
return ansiColor(text, 2, 22, env);
|
|
14
|
+
}
|
|
15
|
+
export function cyan(text, env) {
|
|
16
|
+
return ansiColor(text, 36, 39, env);
|
|
17
|
+
}
|
|
3
18
|
export function errorMessage(error) {
|
|
4
19
|
return error instanceof Error ? error.message : String(error);
|
|
5
20
|
}
|
|
@@ -24,10 +39,10 @@ export function createClackSpinner() {
|
|
|
24
39
|
return spinner();
|
|
25
40
|
}
|
|
26
41
|
function magenta(text) {
|
|
27
|
-
return
|
|
42
|
+
return ansiColor(text, 35, 39);
|
|
28
43
|
}
|
|
29
44
|
function red(text) {
|
|
30
|
-
return
|
|
45
|
+
return ansiColor(text, 31, 39);
|
|
31
46
|
}
|
|
32
47
|
export function createStaticCliSpinner(io) {
|
|
33
48
|
return {
|
package/dist/cli-program.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command, type CommandUnknownOpts } from '@commander-js/extra-typings';
|
|
2
2
|
import type { KtxCliDeps, KtxCliIo, KtxCliPackageInfo } from './cli-runtime.js';
|
|
3
|
+
import { type PrepareUpdateCheckNoticeOptions } from './update-check/update-check.js';
|
|
3
4
|
export interface KtxCliCommandContext {
|
|
4
5
|
io: KtxCliIo;
|
|
5
6
|
deps: KtxCliDeps;
|
|
@@ -23,6 +24,7 @@ interface KtxCommanderProgramOptions {
|
|
|
23
24
|
force: boolean;
|
|
24
25
|
}, io: KtxCliIo) => Promise<number>;
|
|
25
26
|
}
|
|
27
|
+
type KtxCliUpdateCheckOptions = Pick<PrepareUpdateCheckNoticeOptions, 'env' | 'fetchDistTags' | 'homeDir' | 'now'>;
|
|
26
28
|
export interface BuildKtxProgramOptions {
|
|
27
29
|
io: KtxCliIo;
|
|
28
30
|
deps: KtxCliDeps;
|
|
@@ -34,6 +36,7 @@ export interface BuildKtxProgramOptions {
|
|
|
34
36
|
setExitCode?: (code: number) => void;
|
|
35
37
|
argv?: string[];
|
|
36
38
|
setTelemetryModule?: (telemetry: typeof import('./telemetry/index.js')) => void;
|
|
39
|
+
updateCheck?: KtxCliUpdateCheckOptions;
|
|
37
40
|
}
|
|
38
41
|
export interface CommandWithGlobalOptions {
|
|
39
42
|
opts: () => object;
|
package/dist/cli-program.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { Command, InvalidArgumentError } from '@commander-js/extra-typings';
|
|
4
|
+
import { SLACK_HELP_FOOTER, writeErrorCommunityHint } from './community-cta.js';
|
|
4
5
|
import { registerCompletionCommands } from './commands/completion-commands.js';
|
|
5
6
|
import { registerConnectionCommands } from './commands/connection-commands.js';
|
|
6
7
|
import { registerIngestCommands } from './commands/ingest-commands.js';
|
|
@@ -14,6 +15,7 @@ import { registerAdminCommands } from './admin.js';
|
|
|
14
15
|
import { renderMissingProjectMessage } from './doctor.js';
|
|
15
16
|
import { findNearestKtxProjectDir, resolveKtxProjectDir } from './project-resolver.js';
|
|
16
17
|
import { profileMark, profileSpan } from './startup-profile.js';
|
|
18
|
+
import { prepareUpdateCheckNotice } from './update-check/update-check.js';
|
|
17
19
|
profileMark('module:cli-program');
|
|
18
20
|
const PROJECT_AWARE_ROOT_COMMANDS = new Set(['setup', 'connection', 'ingest', 'wiki', 'sl', 'sql', 'status', 'mcp']);
|
|
19
21
|
const PROJECT_INDEPENDENT_ADMIN_COMMANDS = new Set(['runtime', 'schema']);
|
|
@@ -167,6 +169,7 @@ function createBaseProgram(info, io) {
|
|
|
167
169
|
.helpOption('-h, --help', 'Show this help text')
|
|
168
170
|
.configureHelp({ showGlobalOptions: true })
|
|
169
171
|
.showHelpAfterError()
|
|
172
|
+
.addHelpText('after', `\n${SLACK_HELP_FOOTER}`)
|
|
170
173
|
.exitOverride()
|
|
171
174
|
.configureOutput({
|
|
172
175
|
writeOut: (chunk) => io.stdout.write(chunk),
|
|
@@ -319,16 +322,27 @@ export function collectCommandFlagsPresent(command) {
|
|
|
319
322
|
}
|
|
320
323
|
export function buildKtxProgram(options) {
|
|
321
324
|
const program = createBaseProgram(options.packageInfo, options.io);
|
|
325
|
+
let pendingUpdateNotice = null;
|
|
322
326
|
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
323
327
|
// The hidden completion command must stay silent and side-effect free: skip
|
|
324
|
-
// the telemetry notice, command span, and
|
|
328
|
+
// the telemetry notice, command span, project checks, and update checks entirely.
|
|
325
329
|
if (commandPath(actionCommand).includes('__complete')) {
|
|
326
330
|
return;
|
|
327
331
|
}
|
|
332
|
+
const commandNode = actionCommand;
|
|
333
|
+
const updateCheck = await prepareUpdateCheckNotice({
|
|
334
|
+
io: options.io,
|
|
335
|
+
env: options.updateCheck?.env,
|
|
336
|
+
fetchDistTags: options.updateCheck?.fetchDistTags,
|
|
337
|
+
homeDir: options.updateCheck?.homeDir,
|
|
338
|
+
installedVersion: options.packageInfo.version,
|
|
339
|
+
now: options.updateCheck?.now,
|
|
340
|
+
commandOptions: commandOptions(commandNode),
|
|
341
|
+
});
|
|
342
|
+
pendingUpdateNotice = updateCheck.notice;
|
|
328
343
|
const telemetry = await import('./telemetry/index.js');
|
|
329
344
|
options.setTelemetryModule?.(telemetry);
|
|
330
345
|
await telemetry.showTelemetryNoticeIfNeeded(options.io, options.packageInfo);
|
|
331
|
-
const commandNode = actionCommand;
|
|
332
346
|
const path = commandPath(commandNode);
|
|
333
347
|
const projectDir = resolveCommandProjectDir(commandNode);
|
|
334
348
|
const hasProject = ktxYamlExists(projectDir);
|
|
@@ -344,6 +358,12 @@ export function buildKtxProgram(options) {
|
|
|
344
358
|
writeProjectDir(options.io, commandNode);
|
|
345
359
|
ensureProjectAvailable(options.io, commandNode);
|
|
346
360
|
});
|
|
361
|
+
program.hook('postAction', () => {
|
|
362
|
+
if (pendingUpdateNotice) {
|
|
363
|
+
options.io.stderr.write(pendingUpdateNotice);
|
|
364
|
+
pendingUpdateNotice = null;
|
|
365
|
+
}
|
|
366
|
+
});
|
|
347
367
|
const context = {
|
|
348
368
|
io: options.io,
|
|
349
369
|
deps: options.deps,
|
|
@@ -407,7 +427,15 @@ export async function runCommanderKtxCli(argv, io, deps, info, options) {
|
|
|
407
427
|
return await runBareInteractiveCommand(program, io, context);
|
|
408
428
|
}
|
|
409
429
|
catch (error) {
|
|
430
|
+
const telemetry = await import('./telemetry/index.js');
|
|
431
|
+
await telemetry.reportException({
|
|
432
|
+
error,
|
|
433
|
+
context: { source: 'bare-interactive', handled: true, fatal: false },
|
|
434
|
+
packageInfo: info,
|
|
435
|
+
io,
|
|
436
|
+
});
|
|
410
437
|
io.stderr.write(`${formatCliError(error)}\n`);
|
|
438
|
+
writeErrorCommunityHint(io, 'error');
|
|
411
439
|
return 1;
|
|
412
440
|
}
|
|
413
441
|
}
|
|
@@ -433,6 +461,7 @@ export async function runCommanderKtxCli(argv, io, deps, info, options) {
|
|
|
433
461
|
}
|
|
434
462
|
else {
|
|
435
463
|
io.stderr.write(`${formatCliError(error)}\n`);
|
|
464
|
+
writeErrorCommunityHint(io, 'error');
|
|
436
465
|
exitCode = 1;
|
|
437
466
|
}
|
|
438
467
|
}
|
|
@@ -443,6 +472,21 @@ export async function runCommanderKtxCli(argv, io, deps, info, options) {
|
|
|
443
472
|
outcome: commandOutcomeForParseResult(parseError, exitCode),
|
|
444
473
|
error: parseError,
|
|
445
474
|
});
|
|
475
|
+
if (parseError &&
|
|
476
|
+
!isCommanderExit(parseError) &&
|
|
477
|
+
!isKtxProjectMissingAbortError(parseError)) {
|
|
478
|
+
await telemetryModule.reportException({
|
|
479
|
+
error: parseError,
|
|
480
|
+
context: {
|
|
481
|
+
source: completed?.commandPath.join(' ') ?? 'commander parseAsync',
|
|
482
|
+
handled: true,
|
|
483
|
+
fatal: false,
|
|
484
|
+
},
|
|
485
|
+
projectDir: completed?.projectGroupAttached ? completed.projectDir : undefined,
|
|
486
|
+
packageInfo: info,
|
|
487
|
+
io,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
446
490
|
await telemetryModule.emitCompletedCommand({ completed, packageInfo: info, io });
|
|
447
491
|
await telemetryModule.shutdownTelemetryEmitter();
|
|
448
492
|
}
|
package/dist/cli-runtime.d.ts
CHANGED
|
@@ -47,4 +47,9 @@ export declare function runInitForCommander(args: {
|
|
|
47
47
|
projectDir: string;
|
|
48
48
|
force: boolean;
|
|
49
49
|
}, io: KtxCliIo): Promise<number>;
|
|
50
|
+
/** @internal */
|
|
51
|
+
export declare function createGlobalExceptionReporter(io: KtxCliIo, info: KtxCliPackageInfo): (source: "uncaughtException" | "unhandledRejection", error: unknown) => Promise<void>;
|
|
52
|
+
/** @internal */
|
|
53
|
+
export declare function writeGlobalExceptionToStderr(io: KtxCliIo, error: unknown): void;
|
|
54
|
+
export declare function installGlobalExceptionHandlers(io: KtxCliIo, info: KtxCliPackageInfo): () => void;
|
|
50
55
|
export declare function runKtxCli(argv?: string[], io?: KtxCliIo, deps?: KtxCliDeps): Promise<number>;
|
package/dist/cli-runtime.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { profileMark, profileSpan } from './startup-profile.js';
|
|
3
3
|
import { assertCliVersion } from './release-version.js';
|
|
4
|
+
import { writeErrorCommunityHint } from './community-cta.js';
|
|
4
5
|
profileMark('module:cli-runtime');
|
|
5
6
|
const requirePackageJson = createRequire(import.meta.url);
|
|
6
7
|
export function getKtxCliPackageInfo() {
|
|
@@ -74,6 +75,53 @@ function installTelemetrySignalFlush(io, info) {
|
|
|
74
75
|
process.off('SIGTERM', onSigterm);
|
|
75
76
|
};
|
|
76
77
|
}
|
|
78
|
+
/** @internal */
|
|
79
|
+
export function createGlobalExceptionReporter(io, info) {
|
|
80
|
+
return async (source, error) => {
|
|
81
|
+
const { reportException, shutdownTelemetryEmitter } = await import('./telemetry/index.js');
|
|
82
|
+
await reportException({
|
|
83
|
+
error,
|
|
84
|
+
context: { source, handled: false, fatal: true },
|
|
85
|
+
io,
|
|
86
|
+
packageInfo: info,
|
|
87
|
+
immediate: true,
|
|
88
|
+
});
|
|
89
|
+
await shutdownTelemetryEmitter();
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** @internal */
|
|
93
|
+
export function writeGlobalExceptionToStderr(io, error) {
|
|
94
|
+
if (error instanceof Error && error.stack) {
|
|
95
|
+
io.stderr.write(`${error.stack}\n`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
io.stderr.write(`${String(error)}\n`);
|
|
99
|
+
}
|
|
100
|
+
writeErrorCommunityHint(io, 'crash');
|
|
101
|
+
}
|
|
102
|
+
export function installGlobalExceptionHandlers(io, info) {
|
|
103
|
+
const report = createGlobalExceptionReporter(io, info);
|
|
104
|
+
const handle = (source, error) => {
|
|
105
|
+
void (async () => {
|
|
106
|
+
try {
|
|
107
|
+
await report(source, error);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Best-effort: preserve Node's process termination behavior.
|
|
111
|
+
}
|
|
112
|
+
writeGlobalExceptionToStderr(io, error);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
})();
|
|
115
|
+
};
|
|
116
|
+
const onUncaught = (error) => handle('uncaughtException', error);
|
|
117
|
+
const onUnhandled = (reason) => handle('unhandledRejection', reason);
|
|
118
|
+
process.on('uncaughtException', onUncaught);
|
|
119
|
+
process.on('unhandledRejection', onUnhandled);
|
|
120
|
+
return () => {
|
|
121
|
+
process.off('uncaughtException', onUncaught);
|
|
122
|
+
process.off('unhandledRejection', onUnhandled);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
77
125
|
export async function runKtxCli(argv = process.argv.slice(2), io = process, deps = {}) {
|
|
78
126
|
const info = getKtxCliPackageInfo();
|
|
79
127
|
profileMark('runtime:runKtxCli');
|
|
@@ -81,12 +129,14 @@ export async function runKtxCli(argv = process.argv.slice(2), io = process, deps
|
|
|
81
129
|
// Real-process entry only: flush telemetry if interrupted. Test/programmatic
|
|
82
130
|
// callers pass their own `io`, so they never install process-level handlers.
|
|
83
131
|
const removeSignalFlush = io === process ? installTelemetrySignalFlush(io, info) : undefined;
|
|
132
|
+
const removeGlobalExceptionHandlers = io === process ? installGlobalExceptionHandlers(io, info) : undefined;
|
|
84
133
|
try {
|
|
85
134
|
return await runCommanderKtxCli(argv, io, deps, info, {
|
|
86
135
|
runInit: runInitForCommander,
|
|
87
136
|
});
|
|
88
137
|
}
|
|
89
138
|
finally {
|
|
139
|
+
removeGlobalExceptionHandlers?.();
|
|
90
140
|
removeSignalFlush?.();
|
|
91
141
|
}
|
|
92
142
|
}
|
|
@@ -89,7 +89,6 @@ function shouldShowSetupEntryMenu(options, command) {
|
|
|
89
89
|
'llmBackend',
|
|
90
90
|
'anthropicApiKeyEnv',
|
|
91
91
|
'anthropicApiKeyFile',
|
|
92
|
-
'llmModel',
|
|
93
92
|
'vertexProject',
|
|
94
93
|
'vertexLocation',
|
|
95
94
|
'skipLlm',
|
|
@@ -145,7 +144,6 @@ export function registerSetupCommands(program, context) {
|
|
|
145
144
|
.addOption(new Option('--llm-backend <backend>', 'LLM backend').argParser(llmBackend).hideHelp())
|
|
146
145
|
.addOption(new Option('--anthropic-api-key-env <name>', 'Environment variable containing the Anthropic API key').hideHelp())
|
|
147
146
|
.addOption(new Option('--anthropic-api-key-file <path>', 'File containing the Anthropic API key').hideHelp())
|
|
148
|
-
.addOption(new Option('--llm-model <model>', 'LLM model ID or backend model alias').hideHelp())
|
|
149
147
|
.addOption(new Option('--vertex-project <project>', 'Google Vertex AI project ID, env:NAME, or file:/path').hideHelp())
|
|
150
148
|
.addOption(new Option('--vertex-location <location>', 'Google Vertex AI location, env:NAME, or file:/path').hideHelp())
|
|
151
149
|
.addOption(new Option('--skip-llm', 'Leave LLM setup incomplete for now').hideHelp().default(false))
|
|
@@ -272,6 +270,7 @@ export function registerSetupCommands(program, context) {
|
|
|
272
270
|
return;
|
|
273
271
|
}
|
|
274
272
|
const resolvedAgentScope = options.local ? 'local' : options.global ? 'global' : 'project';
|
|
273
|
+
const debugEnabled = (command.optsWithGlobals ? command.optsWithGlobals() : command.opts()).debug === true;
|
|
275
274
|
await runSetupArgs(context, {
|
|
276
275
|
command: 'run',
|
|
277
276
|
projectDir: resolveCommandProjectDir(command),
|
|
@@ -281,12 +280,12 @@ export function registerSetupCommands(program, context) {
|
|
|
281
280
|
agentScope: resolvedAgentScope,
|
|
282
281
|
skipAgents: options.skipAgents === true,
|
|
283
282
|
inputMode: options.input === false ? 'disabled' : 'auto',
|
|
283
|
+
...(debugEnabled ? { debug: true } : {}),
|
|
284
284
|
yes: options.yes === true,
|
|
285
285
|
cliVersion: context.packageInfo.version,
|
|
286
286
|
...(options.llmBackend ? { llmBackend: options.llmBackend } : {}),
|
|
287
287
|
...(options.anthropicApiKeyEnv ? { anthropicApiKeyEnv: options.anthropicApiKeyEnv } : {}),
|
|
288
288
|
...(options.anthropicApiKeyFile ? { anthropicApiKeyFile: options.anthropicApiKeyFile } : {}),
|
|
289
|
-
...(options.llmModel ? { llmModel: options.llmModel } : {}),
|
|
290
289
|
...(options.vertexProject ? { vertexProject: options.vertexProject } : {}),
|
|
291
290
|
...(options.vertexLocation ? { vertexLocation: options.vertexLocation } : {}),
|
|
292
291
|
skipLlm: options.skipLlm === true,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { KtxCliIo } from './cli-runtime.js';
|
|
2
|
+
type ErrorCtaVariant = 'error' | 'crash';
|
|
3
|
+
/** @internal */
|
|
4
|
+
export declare const SLACK_HELP_FOOTER = "Community & support: https://ktx.sh/slack";
|
|
5
|
+
/** @internal */
|
|
6
|
+
export declare const SLACK_SETUP_NOTE: {
|
|
7
|
+
readonly title: "Community";
|
|
8
|
+
readonly body: "Questions or feedback? Join the ktx Slack: https://ktx.sh/slack";
|
|
9
|
+
};
|
|
10
|
+
export declare function writeErrorCommunityHint(io: KtxCliIo, variant: ErrorCtaVariant): void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isWritableTtyOutput } from './io/tty.js';
|
|
2
|
+
import { dim } from './io/symbols.js';
|
|
3
|
+
import { SLACK_URL } from './links.js';
|
|
4
|
+
/** @internal */
|
|
5
|
+
export const SLACK_HELP_FOOTER = `Community & support: ${SLACK_URL}`;
|
|
6
|
+
/** @internal */
|
|
7
|
+
export const SLACK_SETUP_NOTE = {
|
|
8
|
+
title: 'Community',
|
|
9
|
+
body: `Questions or feedback? Join the ktx Slack: ${SLACK_URL}`,
|
|
10
|
+
};
|
|
11
|
+
export function writeErrorCommunityHint(io, variant) {
|
|
12
|
+
if (!isWritableTtyOutput(io.stderr)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const line = variant === 'crash'
|
|
16
|
+
? `This may be a bug - report it or ask in the ktx community: ${SLACK_URL}`
|
|
17
|
+
: `Stuck? The ktx community can help: ${SLACK_URL}`;
|
|
18
|
+
io.stderr.write(`${dim(line)}\n`);
|
|
19
|
+
}
|
package/dist/connection.js
CHANGED
|
@@ -12,7 +12,8 @@ import { bold, dim, green, red, SYMBOLS } from './io/symbols.js';
|
|
|
12
12
|
import { createKtxCliScanConnector } from './local-scan-connectors.js';
|
|
13
13
|
import { profileMark } from './startup-profile.js';
|
|
14
14
|
import { isDemoConnection } from './telemetry/demo-detect.js';
|
|
15
|
-
import { emitTelemetryEvent } from './telemetry/index.js';
|
|
15
|
+
import { emitTelemetryEvent, reportException } from './telemetry/index.js';
|
|
16
|
+
import { collectTelemetryRedactionSecrets } from './telemetry/redaction-secrets.js';
|
|
16
17
|
import { formatErrorDetail, scrubErrorClass } from './telemetry/scrubber.js';
|
|
17
18
|
profileMark('module:connection');
|
|
18
19
|
const SUPPORTED_TEST_DRIVERS = [
|
|
@@ -44,6 +45,12 @@ async function testNativeConnection(project, connectionId, createScanConnector)
|
|
|
44
45
|
}
|
|
45
46
|
const result = await connector.testConnection();
|
|
46
47
|
if (!result.success) {
|
|
48
|
+
// Re-throw the driver's original error so connection_test telemetry records
|
|
49
|
+
// its real class (e.g. ConnectionError) and code (e.g. ELOGIN) instead of
|
|
50
|
+
// collapsing every native failure to a generic Error with no code.
|
|
51
|
+
if (result.cause instanceof Error) {
|
|
52
|
+
throw result.cause;
|
|
53
|
+
}
|
|
47
54
|
throw new Error(result.error ?? 'connection test failed');
|
|
48
55
|
}
|
|
49
56
|
return { driver: connector.driver };
|
|
@@ -182,6 +189,21 @@ async function emitConnectionTest(input) {
|
|
|
182
189
|
...(errorDetail ? { errorDetail } : {}),
|
|
183
190
|
},
|
|
184
191
|
});
|
|
192
|
+
if (input.error) {
|
|
193
|
+
await reportException({
|
|
194
|
+
error: input.error,
|
|
195
|
+
context: { source: 'connection test', handled: true, fatal: false },
|
|
196
|
+
projectDir: input.project.projectDir,
|
|
197
|
+
io: input.io,
|
|
198
|
+
redactionSecrets: await collectTelemetryRedactionSecrets({
|
|
199
|
+
project: input.project,
|
|
200
|
+
connectionId: input.connectionId,
|
|
201
|
+
includeLlm: false,
|
|
202
|
+
includeEmbeddings: false,
|
|
203
|
+
env: process.env,
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
185
207
|
}
|
|
186
208
|
function visualWidth(text) {
|
|
187
209
|
// styleText wraps content in ANSI escape sequences; strip them before measuring.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TableField } from '@google-cloud/bigquery';
|
|
2
|
-
import { type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
2
|
+
import { type KtxConnectorTestResult, type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
3
3
|
export interface KtxBigQueryConnectionConfig {
|
|
4
4
|
driver?: string;
|
|
5
5
|
dataset_id?: string;
|
|
@@ -121,10 +121,7 @@ export declare class KtxBigQueryScanConnector implements KtxScanConnector {
|
|
|
121
121
|
private readonly dialect;
|
|
122
122
|
private client;
|
|
123
123
|
constructor(options: KtxBigQueryScanConnectorOptions);
|
|
124
|
-
testConnection(): Promise<
|
|
125
|
-
success: boolean;
|
|
126
|
-
error?: string;
|
|
127
|
-
}>;
|
|
124
|
+
testConnection(): Promise<KtxConnectorTestResult>;
|
|
128
125
|
introspect(input: KtxScanInput, _ctx: KtxScanContext): Promise<KtxSchemaSnapshot>;
|
|
129
126
|
sampleTable(input: KtxTableSampleInput, _ctx: KtxScanContext): Promise<KtxTableSampleResult & {
|
|
130
127
|
headerTypes?: string[];
|
|
@@ -4,7 +4,7 @@ import { getDialectForDriver } from '../../context/connections/dialects.js';
|
|
|
4
4
|
import { assertReadOnlySql, limitSqlForExecution } from '../../context/connections/read-only-sql.js';
|
|
5
5
|
import { tryConstraintQuery } from '../../context/scan/constraint-discovery.js';
|
|
6
6
|
import { scopedTableNames } from '../../context/scan/table-ref.js';
|
|
7
|
-
import { createKtxConnectorCapabilities, } from '../../context/scan/types.js';
|
|
7
|
+
import { connectorTestFailure, createKtxConnectorCapabilities, } from '../../context/scan/types.js';
|
|
8
8
|
import { readFileSync } from 'node:fs';
|
|
9
9
|
import { homedir } from 'node:os';
|
|
10
10
|
import { resolve } from 'node:path';
|
|
@@ -185,7 +185,7 @@ export class KtxBigQueryScanConnector {
|
|
|
185
185
|
return { success: true };
|
|
186
186
|
}
|
|
187
187
|
catch (error) {
|
|
188
|
-
return
|
|
188
|
+
return connectorTestFailure(error);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
async introspect(input, _ctx) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createClient } from '@clickhouse/client';
|
|
2
|
-
import { type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableRef, type KtxTableSampleInput, type KtxTableListEntry, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
2
|
+
import { type KtxConnectorTestResult, type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableRef, type KtxTableSampleInput, type KtxTableListEntry, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
3
3
|
export interface KtxClickHouseConnectionConfig {
|
|
4
4
|
driver?: string;
|
|
5
5
|
host?: string;
|
|
@@ -94,10 +94,7 @@ export declare class KtxClickHouseScanConnector implements KtxScanConnector {
|
|
|
94
94
|
private client;
|
|
95
95
|
private resolvedEndpoint;
|
|
96
96
|
constructor(options: KtxClickHouseScanConnectorOptions);
|
|
97
|
-
testConnection(): Promise<
|
|
98
|
-
success: boolean;
|
|
99
|
-
error?: string;
|
|
100
|
-
}>;
|
|
97
|
+
testConnection(): Promise<KtxConnectorTestResult>;
|
|
101
98
|
introspect(input: KtxScanInput, _ctx: KtxScanContext): Promise<KtxSchemaSnapshot>;
|
|
102
99
|
private emptySnapshot;
|
|
103
100
|
sampleTable(input: KtxTableSampleInput, _ctx: KtxScanContext): Promise<KtxTableSampleResult>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createClient } from '@clickhouse/client';
|
|
2
2
|
import { getDialectForDriver } from '../../context/connections/dialects.js';
|
|
3
3
|
import { assertReadOnlySql, limitSqlForExecution } from '../../context/connections/read-only-sql.js';
|
|
4
|
-
import { createKtxConnectorCapabilities } from '../../context/scan/types.js';
|
|
4
|
+
import { connectorTestFailure, createKtxConnectorCapabilities } from '../../context/scan/types.js';
|
|
5
5
|
import { scopedTableNames } from '../../context/scan/table-ref.js';
|
|
6
6
|
import { readFileSync } from 'node:fs';
|
|
7
7
|
import { Agent as HttpsAgent } from 'node:https';
|
|
@@ -162,7 +162,7 @@ export class KtxClickHouseScanConnector {
|
|
|
162
162
|
return { success: true };
|
|
163
163
|
}
|
|
164
164
|
catch (error) {
|
|
165
|
-
return
|
|
165
|
+
return connectorTestFailure(error);
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
async introspect(input, _ctx) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FieldPacket, type RowDataPacket } from 'mysql2/promise';
|
|
2
|
-
import { type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
2
|
+
import { type KtxConnectorTestResult, type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
3
3
|
export interface KtxMysqlConnectionConfig {
|
|
4
4
|
driver?: string;
|
|
5
5
|
host?: string;
|
|
@@ -71,6 +71,9 @@ export interface KtxMysqlColumnDistinctValuesResult {
|
|
|
71
71
|
values: string[] | null;
|
|
72
72
|
cardinality: number;
|
|
73
73
|
}
|
|
74
|
+
export interface KtxMysqlColumnStatisticsResult {
|
|
75
|
+
cardinalityByColumn: Map<string, number>;
|
|
76
|
+
}
|
|
74
77
|
/** @internal */
|
|
75
78
|
export declare function prepareMysqlReadOnlyQuery(sql: string, params?: Record<string, unknown>): {
|
|
76
79
|
sql: string;
|
|
@@ -97,15 +100,13 @@ export declare class KtxMysqlScanConnector implements KtxScanConnector {
|
|
|
97
100
|
private pool;
|
|
98
101
|
private resolvedEndpoint;
|
|
99
102
|
constructor(options: KtxMysqlScanConnectorOptions);
|
|
100
|
-
testConnection(): Promise<
|
|
101
|
-
success: boolean;
|
|
102
|
-
error?: string;
|
|
103
|
-
}>;
|
|
103
|
+
testConnection(): Promise<KtxConnectorTestResult>;
|
|
104
104
|
introspect(input: KtxScanInput, _ctx: KtxScanContext): Promise<KtxSchemaSnapshot>;
|
|
105
105
|
private emptySnapshot;
|
|
106
106
|
sampleTable(input: KtxTableSampleInput, _ctx: KtxScanContext): Promise<KtxTableSampleResult>;
|
|
107
107
|
sampleColumn(input: KtxColumnSampleInput, _ctx: KtxScanContext): Promise<KtxColumnSampleResult>;
|
|
108
|
-
columnStats(
|
|
108
|
+
columnStats(input: KtxColumnStatsInput, _ctx: KtxScanContext): Promise<KtxColumnStatsResult | null>;
|
|
109
|
+
getColumnStatistics(table: KtxTableRef): Promise<KtxMysqlColumnStatisticsResult | null>;
|
|
109
110
|
executeReadOnly(input: KtxMysqlReadOnlyQueryInput, _ctx: KtxScanContext): Promise<KtxQueryResult>;
|
|
110
111
|
getColumnDistinctValues(table: KtxTableRef, columnName: string, options: KtxMysqlColumnDistinctValuesOptions): Promise<KtxMysqlColumnDistinctValuesResult | null>;
|
|
111
112
|
getTableRowCount(tableName: string): Promise<number>;
|
|
@@ -6,7 +6,7 @@ import { getDialectForDriver } from '../../context/connections/dialects.js';
|
|
|
6
6
|
import { assertReadOnlySql, limitSqlForExecution } from '../../context/connections/read-only-sql.js';
|
|
7
7
|
import { constraintDiscoveryWarning, tryConstraintQuery, } from '../../context/scan/constraint-discovery.js';
|
|
8
8
|
import { scopedTableNames } from '../../context/scan/table-ref.js';
|
|
9
|
-
import { createKtxConnectorCapabilities, } from '../../context/scan/types.js';
|
|
9
|
+
import { connectorTestFailure, createKtxConnectorCapabilities, } from '../../context/scan/types.js';
|
|
10
10
|
class DefaultMysqlPoolFactory {
|
|
11
11
|
createPool(config) {
|
|
12
12
|
return mysql.createPool(config);
|
|
@@ -185,7 +185,7 @@ export class KtxMysqlScanConnector {
|
|
|
185
185
|
capabilities = createKtxConnectorCapabilities({
|
|
186
186
|
tableSampling: true,
|
|
187
187
|
columnSampling: true,
|
|
188
|
-
columnStats:
|
|
188
|
+
columnStats: true,
|
|
189
189
|
readOnlySql: true,
|
|
190
190
|
nestedAnalysis: true,
|
|
191
191
|
formalForeignKeys: true,
|
|
@@ -219,7 +219,7 @@ export class KtxMysqlScanConnector {
|
|
|
219
219
|
return { success: true };
|
|
220
220
|
}
|
|
221
221
|
catch (error) {
|
|
222
|
-
return
|
|
222
|
+
return connectorTestFailure(error);
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
async introspect(input, _ctx) {
|
|
@@ -324,8 +324,28 @@ export class KtxMysqlScanConnector {
|
|
|
324
324
|
const values = result.rows.filter((row) => row.length > 0 && row[0] !== null).map((row) => row[0]);
|
|
325
325
|
return { values, nullCount: null, distinctCount: null };
|
|
326
326
|
}
|
|
327
|
-
async columnStats(
|
|
328
|
-
|
|
327
|
+
async columnStats(input, _ctx) {
|
|
328
|
+
const stats = await this.getColumnStatistics(input.table);
|
|
329
|
+
const value = stats?.cardinalityByColumn.get(input.column);
|
|
330
|
+
return value === undefined
|
|
331
|
+
? null
|
|
332
|
+
: { min: null, max: null, average: null, nullCount: null, distinctCount: value };
|
|
333
|
+
}
|
|
334
|
+
async getColumnStatistics(table) {
|
|
335
|
+
const schema = table.db ?? this.poolConfig.database;
|
|
336
|
+
const sql = this.dialect.generateColumnStatisticsQuery(schema, table.name);
|
|
337
|
+
if (!sql) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
const rows = await this.queryRaw(sql);
|
|
341
|
+
const cardinalityByColumn = new Map();
|
|
342
|
+
for (const row of rows) {
|
|
343
|
+
const cardinality = Number(row.estimated_cardinality);
|
|
344
|
+
if (Number.isFinite(cardinality) && cardinality >= 0) {
|
|
345
|
+
cardinalityByColumn.set(row.column_name, cardinality);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return cardinalityByColumn.size > 0 ? { cardinalityByColumn } : null;
|
|
329
349
|
}
|
|
330
350
|
async executeReadOnly(input, _ctx) {
|
|
331
351
|
this.assertConnection(input.connectionId);
|
|
@@ -25,7 +25,7 @@ export declare class KtxMysqlDialect implements KtxDialect {
|
|
|
25
25
|
getSampleValueAggregation(innerSql: string): string;
|
|
26
26
|
generateCardinalitySampleQuery(tableName: string, columnName: string, sampleSize: number): string;
|
|
27
27
|
generateDistinctValuesQuery(tableName: string, columnName: string, limit: number): string;
|
|
28
|
-
generateColumnStatisticsQuery(
|
|
28
|
+
generateColumnStatisticsQuery(schemaName: string, tableName: string): string | null;
|
|
29
29
|
generateRandomizedCardinalitySampleQuery(tableName: string, columnName: string, sampleSize: number): string;
|
|
30
30
|
}
|
|
31
31
|
export {};
|
|
@@ -135,8 +135,18 @@ export class KtxMysqlDialect {
|
|
|
135
135
|
LIMIT ${limit}
|
|
136
136
|
`;
|
|
137
137
|
}
|
|
138
|
-
generateColumnStatisticsQuery(
|
|
139
|
-
return
|
|
138
|
+
generateColumnStatisticsQuery(schemaName, tableName) {
|
|
139
|
+
return `
|
|
140
|
+
SELECT
|
|
141
|
+
COLUMN_NAME AS column_name,
|
|
142
|
+
MAX(CARDINALITY) AS estimated_cardinality
|
|
143
|
+
FROM INFORMATION_SCHEMA.STATISTICS
|
|
144
|
+
WHERE TABLE_SCHEMA = '${schemaName.replace(/'/g, "''")}'
|
|
145
|
+
AND TABLE_NAME = '${tableName.replace(/'/g, "''")}'
|
|
146
|
+
AND CARDINALITY IS NOT NULL
|
|
147
|
+
AND SEQ_IN_INDEX = 1
|
|
148
|
+
GROUP BY COLUMN_NAME
|
|
149
|
+
`;
|
|
140
150
|
}
|
|
141
151
|
generateRandomizedCardinalitySampleQuery(tableName, columnName, sampleSize) {
|
|
142
152
|
return `
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
1
|
+
import { type KtxConnectorTestResult, type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
2
2
|
export interface KtxPostgresConnectionConfig {
|
|
3
3
|
driver?: string;
|
|
4
4
|
host?: string;
|
|
@@ -117,10 +117,7 @@ export declare class KtxPostgresScanConnector implements KtxScanConnector {
|
|
|
117
117
|
private lastIdlePoolError;
|
|
118
118
|
private resolvedEndpoint;
|
|
119
119
|
constructor(options: KtxPostgresScanConnectorOptions);
|
|
120
|
-
testConnection(): Promise<
|
|
121
|
-
success: boolean;
|
|
122
|
-
error?: string;
|
|
123
|
-
}>;
|
|
120
|
+
testConnection(): Promise<KtxConnectorTestResult>;
|
|
124
121
|
introspect(input: KtxScanInput, _ctx: KtxScanContext): Promise<KtxSchemaSnapshot>;
|
|
125
122
|
sampleTable(input: KtxTableSampleInput, _ctx: KtxScanContext): Promise<KtxPostgresTableSampleResult>;
|
|
126
123
|
sampleColumn(input: KtxColumnSampleInput, _ctx: KtxScanContext): Promise<KtxColumnSampleResult>;
|
|
@@ -5,7 +5,7 @@ import { getDialectForDriver } from '../../context/connections/dialects.js';
|
|
|
5
5
|
import { assertReadOnlySql, limitSqlForExecution } from '../../context/connections/read-only-sql.js';
|
|
6
6
|
import { tryConstraintQuery } from '../../context/scan/constraint-discovery.js';
|
|
7
7
|
import { scopedTableNames } from '../../context/scan/table-ref.js';
|
|
8
|
-
import { createKtxConnectorCapabilities, } from '../../context/scan/types.js';
|
|
8
|
+
import { connectorTestFailure, createKtxConnectorCapabilities, } from '../../context/scan/types.js';
|
|
9
9
|
import { Pool } from 'pg';
|
|
10
10
|
const PG_OID_TYPE_MAP = {
|
|
11
11
|
16: 'boolean',
|
|
@@ -231,7 +231,7 @@ export class KtxPostgresScanConnector {
|
|
|
231
231
|
return { success: true };
|
|
232
232
|
}
|
|
233
233
|
catch (error) {
|
|
234
|
-
return
|
|
234
|
+
return connectorTestFailure(error);
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async introspect(input, _ctx) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
1
|
+
import { type KtxConnectorTestResult, type KtxColumnSampleInput, type KtxColumnSampleResult, type KtxColumnStatsInput, type KtxColumnStatsResult, type KtxQueryResult, type KtxReadOnlyQueryInput, type KtxScanConnector, type KtxScanContext, type KtxScanInput, type KtxSchemaSnapshot, type KtxTableListEntry, type KtxTableRef, type KtxTableSampleInput, type KtxTableSampleResult } from '../../context/scan/types.js';
|
|
2
2
|
export interface KtxSnowflakeConnectionConfig {
|
|
3
3
|
driver?: string;
|
|
4
4
|
authMethod?: 'password' | 'rsa';
|
|
@@ -117,10 +117,7 @@ export declare class KtxSnowflakeScanConnector implements KtxScanConnector {
|
|
|
117
117
|
private readonly now;
|
|
118
118
|
private driverInstance;
|
|
119
119
|
constructor(options: KtxSnowflakeScanConnectorOptions);
|
|
120
|
-
testConnection(): Promise<
|
|
121
|
-
success: boolean;
|
|
122
|
-
error?: string;
|
|
123
|
-
}>;
|
|
120
|
+
testConnection(): Promise<KtxConnectorTestResult>;
|
|
124
121
|
introspect(input: KtxScanInput, _ctx: KtxScanContext): Promise<KtxSchemaSnapshot>;
|
|
125
122
|
sampleTable(input: KtxTableSampleInput, _ctx: KtxScanContext): Promise<KtxTableSampleResult>;
|
|
126
123
|
sampleColumn(input: KtxColumnSampleInput, _ctx: KtxScanContext): Promise<KtxColumnSampleResult>;
|