@kaelio/ktx 0.1.0-rc.5 → 0.1.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.1.0-py3-none-any.whl +0 -0
- package/assets/python/manifest.json +2 -2
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +23 -0
- package/dist/cli-program.js +5 -2
- package/dist/cli-program.test.js +7 -1
- package/dist/cli-runtime.d.ts +4 -0
- package/dist/cli-runtime.js +8 -1
- package/dist/command-schemas.d.ts +1 -1
- package/dist/commands/ingest-commands.js +1 -0
- package/dist/commands/knowledge-commands.js +5 -0
- package/dist/commands/mcp-commands.js +11 -3
- package/dist/commands/mcp-commands.test.js +30 -1
- package/dist/commands/sql-commands.d.ts +3 -0
- package/dist/commands/sql-commands.js +43 -0
- package/dist/commands/sql-commands.test.d.ts +1 -0
- package/dist/commands/sql-commands.test.js +68 -0
- package/dist/context-build-view.js +5 -1
- package/dist/dev.test.js +27 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.test.js +56 -21
- package/dist/ingest.js +123 -18
- package/dist/ingest.test.js +206 -0
- package/dist/io/print-list.d.ts +2 -1
- package/dist/io/print-list.js +7 -0
- package/dist/io/print-list.test.js +13 -11
- package/dist/io/symbols.d.ts +2 -2
- package/dist/knowledge.d.ts +1 -0
- package/dist/knowledge.js +34 -16
- package/dist/knowledge.test.js +27 -0
- package/dist/managed-python-command.d.ts +2 -0
- package/dist/managed-python-command.js +17 -9
- package/dist/managed-python-command.test.js +59 -4
- package/dist/next-steps.js +1 -1
- package/dist/next-steps.test.js +2 -0
- package/dist/print-command-tree.js +7 -1
- package/dist/public-ingest.d.ts +9 -1
- package/dist/public-ingest.js +50 -7
- package/dist/public-ingest.test.js +69 -2
- package/dist/release-version.d.ts +5 -0
- package/dist/release-version.js +44 -0
- package/dist/runtime-requirements.d.ts +23 -0
- package/dist/runtime-requirements.js +99 -0
- package/dist/runtime-requirements.test.d.ts +1 -0
- package/dist/runtime-requirements.test.js +63 -0
- package/dist/setup-agents.d.ts +11 -3
- package/dist/setup-agents.js +397 -134
- package/dist/setup-agents.test.js +359 -61
- package/dist/setup-embeddings.js +3 -6
- package/dist/setup-embeddings.test.js +18 -2
- package/dist/setup-models.js +2 -2
- package/dist/setup-models.test.js +5 -3
- package/dist/setup-ready-menu.d.ts +1 -1
- package/dist/setup-ready-menu.js +2 -0
- package/dist/setup-ready-menu.test.js +3 -0
- package/dist/setup-runtime.d.ts +45 -0
- package/dist/setup-runtime.js +47 -0
- package/dist/setup-runtime.test.d.ts +1 -0
- package/dist/setup-runtime.test.js +110 -0
- package/dist/setup-sources-notion.test.d.ts +1 -0
- package/dist/setup-sources-notion.test.js +107 -0
- package/dist/setup-sources.js +5 -2
- package/dist/setup.d.ts +19 -1
- package/dist/setup.js +104 -29
- package/dist/setup.test.js +221 -57
- package/dist/sl.js +2 -2
- package/dist/sl.test.js +10 -0
- package/dist/source-mapping.js +9 -1
- package/dist/source-mapping.test.d.ts +1 -0
- package/dist/source-mapping.test.js +65 -0
- package/dist/sql.d.ts +22 -0
- package/dist/sql.js +125 -0
- package/dist/sql.test.d.ts +1 -0
- package/dist/sql.test.js +226 -0
- package/node_modules/@ktx/connector-clickhouse/dist/package-exports.test.js +1 -1
- package/node_modules/@ktx/context/dist/connections/connection-type.d.ts +4 -4
- package/node_modules/@ktx/context/dist/core/git.service.d.ts +3 -0
- package/node_modules/@ktx/context/dist/core/git.service.js +47 -1
- package/node_modules/@ktx/context/dist/core/git.service.patch.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/core/git.service.patch.test.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/types.d.ts +5 -5
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/looker.adapter.d.ts +2 -2
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/tools/looker-query-to-sl.tool.d.ts +2 -2
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/types.d.ts +16 -16
- package/node_modules/@ktx/context/dist/ingest/adapters/lookml/pull-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.js +16 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.test.js +41 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.d.ts +2 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.test.js +116 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.d.ts +29 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/pull-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.d.ts +25 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.js +149 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.js +167 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.d.ts +29 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.js +178 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.js +109 -0
- package/node_modules/@ktx/context/dist/ingest/index.d.ts +8 -1
- package/node_modules/@ktx/context/dist/ingest/index.js +7 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.d.ts +18 -2
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.js +1761 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.js +1695 -901
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.test.js +135 -118
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.d.ts +50 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.js +88 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.js +76 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.d.ts +16 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.js +78 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.js +76 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.d.ts +58 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.js +223 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.js +369 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.d.ts +23 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.js +190 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.js +101 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.d.ts +15 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.js +61 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.js +137 -0
- package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +7 -0
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +54 -10
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +65 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +23 -5
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.js +17 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.test.js +1 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/types.d.ts +6 -0
- package/node_modules/@ktx/context/dist/ingest/parsed-target-table.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/ports.d.ts +3 -0
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +32 -7
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.js +25 -0
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.test.js +124 -0
- package/node_modules/@ktx/context/dist/ingest/reports.d.ts +23 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.d.ts +11 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.js +26 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.js +25 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.d.ts +4 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.js +4 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.test.js +29 -0
- package/node_modules/@ktx/context/dist/ingest/tools/emit-unmapped-fallback.tool.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/types.d.ts +24 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.d.ts +24 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.js +111 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.js +138 -0
- package/node_modules/@ktx/context/dist/llm/claude-code-runtime.js +19 -2
- package/node_modules/@ktx/context/dist/llm/claude-code-runtime.test.js +33 -0
- package/node_modules/@ktx/context/dist/project/setup-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/project/setup-config.js +10 -1
- package/node_modules/@ktx/context/dist/project/setup-config.test.js +3 -2
- package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.js +5 -1
- package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.test.js +15 -0
- package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.js +5 -1
- package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.test.js +22 -0
- package/node_modules/@ktx/context/dist/tools/action-target-connection.d.ts +9 -0
- package/node_modules/@ktx/context/dist/tools/action-target-connection.js +14 -0
- package/node_modules/@ktx/context/dist/tools/context-candidate-write.tool.d.ts +4 -4
- package/node_modules/@ktx/context/dist/tools/index.d.ts +1 -0
- package/node_modules/@ktx/context/dist/tools/index.js +1 -0
- package/node_modules/@ktx/context/dist/wiki/local-knowledge.js +4 -1
- package/node_modules/@ktx/context/dist/wiki/local-knowledge.test.js +44 -0
- package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.js +3 -48
- package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.test.js +28 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.d.ts +17 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.js +79 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.js +64 -0
- package/node_modules/@ktx/context/prompts/memory_agent_bundle_ingest_work_unit.md +23 -4
- package/node_modules/@ktx/context/skills/ingest_triage/SKILL.md +7 -3
- package/package.json +4 -4
package/dist/sql.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { loadKtxProject } from '@ktx/context/project';
|
|
2
|
+
import { createKtxCliScanConnector } from './local-scan-connectors.js';
|
|
3
|
+
import { createManagedDaemonSqlAnalysisPort } from './managed-python-http.js';
|
|
4
|
+
import { profileMark } from './startup-profile.js';
|
|
5
|
+
profileMark('module:sql');
|
|
6
|
+
function sqlAnalysisDialectForDriver(driver) {
|
|
7
|
+
const normalized = String(driver ?? '').trim().toLowerCase();
|
|
8
|
+
const map = {
|
|
9
|
+
postgres: 'postgres',
|
|
10
|
+
postgresql: 'postgres',
|
|
11
|
+
bigquery: 'bigquery',
|
|
12
|
+
snowflake: 'snowflake',
|
|
13
|
+
mysql: 'mysql',
|
|
14
|
+
sqlserver: 'tsql',
|
|
15
|
+
mssql: 'tsql',
|
|
16
|
+
sqlite: 'sqlite',
|
|
17
|
+
sqlite3: 'sqlite',
|
|
18
|
+
clickhouse: 'clickhouse',
|
|
19
|
+
redshift: 'redshift',
|
|
20
|
+
};
|
|
21
|
+
return map[normalized] ?? 'postgres';
|
|
22
|
+
}
|
|
23
|
+
function resolveOutputMode(args) {
|
|
24
|
+
if (args.json === true)
|
|
25
|
+
return 'json';
|
|
26
|
+
return args.output ?? 'pretty';
|
|
27
|
+
}
|
|
28
|
+
function formatValue(value) {
|
|
29
|
+
if (value === null || value === undefined)
|
|
30
|
+
return '';
|
|
31
|
+
if (typeof value === 'string')
|
|
32
|
+
return value;
|
|
33
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint')
|
|
34
|
+
return String(value);
|
|
35
|
+
return JSON.stringify(value);
|
|
36
|
+
}
|
|
37
|
+
function printJson(output, io) {
|
|
38
|
+
io.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
39
|
+
}
|
|
40
|
+
function printPlain(output, io) {
|
|
41
|
+
io.stdout.write(`${output.headers.join('\t')}\n`);
|
|
42
|
+
for (const row of output.rows) {
|
|
43
|
+
io.stdout.write(`${row.map(formatValue).join('\t')}\n`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function printPretty(output, io) {
|
|
47
|
+
const rows = output.rows.map((row) => row.map(formatValue));
|
|
48
|
+
const widths = output.headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0)));
|
|
49
|
+
const renderRow = (cells) => cells.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join(' ').trimEnd();
|
|
50
|
+
if (output.headers.length > 0) {
|
|
51
|
+
io.stdout.write(`${renderRow(output.headers)}\n`);
|
|
52
|
+
io.stdout.write(`${renderRow(widths.map((width) => '-'.repeat(width)))}\n`);
|
|
53
|
+
}
|
|
54
|
+
for (const row of rows) {
|
|
55
|
+
io.stdout.write(`${renderRow(row)}\n`);
|
|
56
|
+
}
|
|
57
|
+
io.stdout.write(`\n${output.rowCount} ${output.rowCount === 1 ? 'row' : 'rows'}\n`);
|
|
58
|
+
}
|
|
59
|
+
function printSqlResult(output, mode, io) {
|
|
60
|
+
if (mode === 'json') {
|
|
61
|
+
printJson(output, io);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (mode === 'plain') {
|
|
65
|
+
printPlain(output, io);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
printPretty(output, io);
|
|
69
|
+
}
|
|
70
|
+
async function cleanupConnector(connector) {
|
|
71
|
+
if (connector?.cleanup) {
|
|
72
|
+
await connector.cleanup();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function resultOutput(connectionId, result) {
|
|
76
|
+
return {
|
|
77
|
+
connectionId,
|
|
78
|
+
headers: result.headers,
|
|
79
|
+
...(result.headerTypes ? { headerTypes: result.headerTypes } : {}),
|
|
80
|
+
rows: result.rows,
|
|
81
|
+
rowCount: result.rowCount ?? result.rows.length,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export async function runKtxSql(args, io = process, deps = {}) {
|
|
85
|
+
try {
|
|
86
|
+
const project = await (deps.loadProject ?? loadKtxProject)({ projectDir: args.projectDir });
|
|
87
|
+
const connection = project.config.connections[args.connectionId];
|
|
88
|
+
if (!connection) {
|
|
89
|
+
throw new Error(`Connection "${args.connectionId}" is not configured in ktx.yaml`);
|
|
90
|
+
}
|
|
91
|
+
const sqlAnalysis = deps.createSqlAnalysis ??
|
|
92
|
+
(() => createManagedDaemonSqlAnalysisPort({
|
|
93
|
+
cliVersion: args.cliVersion,
|
|
94
|
+
projectDir: args.projectDir,
|
|
95
|
+
installPolicy: 'auto',
|
|
96
|
+
io,
|
|
97
|
+
}));
|
|
98
|
+
const validation = await sqlAnalysis().validateReadOnly(args.sql, sqlAnalysisDialectForDriver(connection.driver));
|
|
99
|
+
if (!validation.ok) {
|
|
100
|
+
throw new Error(validation.error ?? 'SQL is not read-only.');
|
|
101
|
+
}
|
|
102
|
+
const createScanConnector = deps.createScanConnector ?? createKtxCliScanConnector;
|
|
103
|
+
let connector = null;
|
|
104
|
+
try {
|
|
105
|
+
connector = await createScanConnector(project, args.connectionId);
|
|
106
|
+
if (!connector.capabilities.readOnlySql || !connector.executeReadOnly) {
|
|
107
|
+
throw new Error(`Connection "${args.connectionId}" does not support read-only SQL execution.`);
|
|
108
|
+
}
|
|
109
|
+
const result = await connector.executeReadOnly({
|
|
110
|
+
connectionId: args.connectionId,
|
|
111
|
+
sql: args.sql,
|
|
112
|
+
maxRows: args.maxRows,
|
|
113
|
+
}, { runId: 'cli-sql' });
|
|
114
|
+
printSqlResult(resultOutput(args.connectionId, result), resolveOutputMode(args), io);
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await cleanupConnector(connector);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
io.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/sql.test.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { initKtxProject, parseKtxProjectConfig, serializeKtxProjectConfig } from '@ktx/context/project';
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
|
+
import { runKtxSql } from './sql.js';
|
|
7
|
+
function makeIo() {
|
|
8
|
+
let stdout = '';
|
|
9
|
+
let stderr = '';
|
|
10
|
+
return {
|
|
11
|
+
io: {
|
|
12
|
+
stdout: {
|
|
13
|
+
write: (chunk) => {
|
|
14
|
+
stdout += chunk;
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
stderr: {
|
|
18
|
+
write: (chunk) => {
|
|
19
|
+
stderr += chunk;
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
stdout: () => stdout,
|
|
24
|
+
stderr: () => stderr,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function makeSqlAnalysis(result) {
|
|
28
|
+
return {
|
|
29
|
+
analyzeForFingerprint: vi.fn(),
|
|
30
|
+
analyzeBatch: vi.fn(),
|
|
31
|
+
validateReadOnly: vi.fn(async () => result),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function makeConnector(overrides = {}) {
|
|
35
|
+
return {
|
|
36
|
+
id: 'sqlite:warehouse',
|
|
37
|
+
driver: 'sqlite',
|
|
38
|
+
capabilities: {
|
|
39
|
+
structuralIntrospection: true,
|
|
40
|
+
tableSampling: true,
|
|
41
|
+
columnSampling: true,
|
|
42
|
+
columnStats: true,
|
|
43
|
+
readOnlySql: true,
|
|
44
|
+
nestedAnalysis: false,
|
|
45
|
+
eventStreamDiscovery: false,
|
|
46
|
+
formalForeignKeys: true,
|
|
47
|
+
estimatedRowCounts: true,
|
|
48
|
+
},
|
|
49
|
+
introspect: vi.fn(),
|
|
50
|
+
executeReadOnly: vi.fn(async () => ({
|
|
51
|
+
headers: ['id', 'status'],
|
|
52
|
+
headerTypes: ['integer', 'text'],
|
|
53
|
+
rows: [
|
|
54
|
+
[1, 'paid'],
|
|
55
|
+
[2, 'open'],
|
|
56
|
+
],
|
|
57
|
+
totalRows: 2,
|
|
58
|
+
rowCount: 2,
|
|
59
|
+
})),
|
|
60
|
+
cleanup: vi.fn(async () => undefined),
|
|
61
|
+
...overrides,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
describe('runKtxSql', () => {
|
|
65
|
+
let tempDir;
|
|
66
|
+
beforeEach(async () => {
|
|
67
|
+
tempDir = await mkdtemp(join(tmpdir(), 'ktx-cli-sql-'));
|
|
68
|
+
});
|
|
69
|
+
afterEach(async () => {
|
|
70
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
71
|
+
});
|
|
72
|
+
async function writeConnections(projectDir, connections) {
|
|
73
|
+
const config = parseKtxProjectConfig(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8'));
|
|
74
|
+
await writeFile(join(projectDir, 'ktx.yaml'), serializeKtxProjectConfig({ ...config, connections }), 'utf-8');
|
|
75
|
+
}
|
|
76
|
+
it('validates SQL, executes through the scan connector, and prints a pretty table', async () => {
|
|
77
|
+
const projectDir = join(tempDir, 'project');
|
|
78
|
+
await initKtxProject({ projectDir });
|
|
79
|
+
await writeConnections(projectDir, { warehouse: { driver: 'sqlite', path: 'warehouse.db' } });
|
|
80
|
+
const sqlAnalysis = makeSqlAnalysis({ ok: true, error: null });
|
|
81
|
+
const connector = makeConnector();
|
|
82
|
+
const createScanConnector = vi.fn(async () => connector);
|
|
83
|
+
const io = makeIo();
|
|
84
|
+
await expect(runKtxSql({
|
|
85
|
+
command: 'execute',
|
|
86
|
+
projectDir,
|
|
87
|
+
connectionId: 'warehouse',
|
|
88
|
+
sql: 'select id, status from orders',
|
|
89
|
+
maxRows: 1000,
|
|
90
|
+
output: 'pretty',
|
|
91
|
+
json: false,
|
|
92
|
+
cliVersion: '0.0.0-test',
|
|
93
|
+
}, io.io, {
|
|
94
|
+
createSqlAnalysis: () => sqlAnalysis,
|
|
95
|
+
createScanConnector,
|
|
96
|
+
})).resolves.toBe(0);
|
|
97
|
+
expect(sqlAnalysis.validateReadOnly).toHaveBeenCalledWith('select id, status from orders', 'sqlite');
|
|
98
|
+
expect(createScanConnector).toHaveBeenCalledWith(expect.objectContaining({ projectDir }), 'warehouse');
|
|
99
|
+
expect(connector.executeReadOnly).toHaveBeenCalledWith({ connectionId: 'warehouse', sql: 'select id, status from orders', maxRows: 1000 }, { runId: 'cli-sql' });
|
|
100
|
+
expect(connector.cleanup).toHaveBeenCalledTimes(1);
|
|
101
|
+
expect(io.stdout()).toContain('id status');
|
|
102
|
+
expect(io.stdout()).toContain('1 paid');
|
|
103
|
+
expect(io.stdout()).toContain('2 open');
|
|
104
|
+
expect(io.stdout()).toContain('2 rows');
|
|
105
|
+
expect(io.stderr()).toBe('');
|
|
106
|
+
});
|
|
107
|
+
it('prints JSON output', async () => {
|
|
108
|
+
const projectDir = join(tempDir, 'project');
|
|
109
|
+
await initKtxProject({ projectDir });
|
|
110
|
+
await writeConnections(projectDir, { warehouse: { driver: 'sqlite', path: 'warehouse.db' } });
|
|
111
|
+
const io = makeIo();
|
|
112
|
+
await expect(runKtxSql({
|
|
113
|
+
command: 'execute',
|
|
114
|
+
projectDir,
|
|
115
|
+
connectionId: 'warehouse',
|
|
116
|
+
sql: 'select id from orders',
|
|
117
|
+
maxRows: 10,
|
|
118
|
+
output: undefined,
|
|
119
|
+
json: true,
|
|
120
|
+
cliVersion: '0.0.0-test',
|
|
121
|
+
}, io.io, {
|
|
122
|
+
createSqlAnalysis: () => makeSqlAnalysis({ ok: true, error: null }),
|
|
123
|
+
createScanConnector: vi.fn(async () => makeConnector()),
|
|
124
|
+
})).resolves.toBe(0);
|
|
125
|
+
expect(JSON.parse(io.stdout())).toEqual({
|
|
126
|
+
connectionId: 'warehouse',
|
|
127
|
+
headers: ['id', 'status'],
|
|
128
|
+
headerTypes: ['integer', 'text'],
|
|
129
|
+
rows: [
|
|
130
|
+
[1, 'paid'],
|
|
131
|
+
[2, 'open'],
|
|
132
|
+
],
|
|
133
|
+
rowCount: 2,
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
it('prints plain TSV output', async () => {
|
|
137
|
+
const projectDir = join(tempDir, 'project');
|
|
138
|
+
await initKtxProject({ projectDir });
|
|
139
|
+
await writeConnections(projectDir, { warehouse: { driver: 'sqlite', path: 'warehouse.db' } });
|
|
140
|
+
const io = makeIo();
|
|
141
|
+
await expect(runKtxSql({
|
|
142
|
+
command: 'execute',
|
|
143
|
+
projectDir,
|
|
144
|
+
connectionId: 'warehouse',
|
|
145
|
+
sql: 'select id from orders',
|
|
146
|
+
maxRows: 10,
|
|
147
|
+
output: 'plain',
|
|
148
|
+
json: false,
|
|
149
|
+
cliVersion: '0.0.0-test',
|
|
150
|
+
}, io.io, {
|
|
151
|
+
createSqlAnalysis: () => makeSqlAnalysis({ ok: true, error: null }),
|
|
152
|
+
createScanConnector: vi.fn(async () => makeConnector()),
|
|
153
|
+
})).resolves.toBe(0);
|
|
154
|
+
expect(io.stdout()).toBe('id\tstatus\n1\tpaid\n2\topen\n');
|
|
155
|
+
expect(io.stderr()).toBe('');
|
|
156
|
+
});
|
|
157
|
+
it('rejects non-read-only SQL before executing connector SQL', async () => {
|
|
158
|
+
const projectDir = join(tempDir, 'project');
|
|
159
|
+
await initKtxProject({ projectDir });
|
|
160
|
+
await writeConnections(projectDir, { warehouse: { driver: 'sqlite', path: 'warehouse.db' } });
|
|
161
|
+
const connector = makeConnector();
|
|
162
|
+
const io = makeIo();
|
|
163
|
+
await expect(runKtxSql({
|
|
164
|
+
command: 'execute',
|
|
165
|
+
projectDir,
|
|
166
|
+
connectionId: 'warehouse',
|
|
167
|
+
sql: 'delete from orders',
|
|
168
|
+
maxRows: 1000,
|
|
169
|
+
output: 'pretty',
|
|
170
|
+
json: false,
|
|
171
|
+
cliVersion: '0.0.0-test',
|
|
172
|
+
}, io.io, {
|
|
173
|
+
createSqlAnalysis: () => makeSqlAnalysis({ ok: false, error: 'SQL contains read/write operation: Delete' }),
|
|
174
|
+
createScanConnector: vi.fn(async () => connector),
|
|
175
|
+
})).resolves.toBe(1);
|
|
176
|
+
expect(connector.executeReadOnly).not.toHaveBeenCalled();
|
|
177
|
+
expect(connector.cleanup).not.toHaveBeenCalled();
|
|
178
|
+
expect(io.stderr()).toContain('SQL contains read/write operation: Delete');
|
|
179
|
+
});
|
|
180
|
+
it('rejects missing connections', async () => {
|
|
181
|
+
const projectDir = join(tempDir, 'project');
|
|
182
|
+
await initKtxProject({ projectDir });
|
|
183
|
+
const io = makeIo();
|
|
184
|
+
await expect(runKtxSql({
|
|
185
|
+
command: 'execute',
|
|
186
|
+
projectDir,
|
|
187
|
+
connectionId: 'warehouse',
|
|
188
|
+
sql: 'select 1',
|
|
189
|
+
maxRows: 1000,
|
|
190
|
+
output: 'pretty',
|
|
191
|
+
json: false,
|
|
192
|
+
cliVersion: '0.0.0-test',
|
|
193
|
+
}, io.io, {
|
|
194
|
+
createSqlAnalysis: () => makeSqlAnalysis({ ok: true, error: null }),
|
|
195
|
+
})).resolves.toBe(1);
|
|
196
|
+
expect(io.stderr()).toContain('Connection "warehouse" is not configured in ktx.yaml');
|
|
197
|
+
});
|
|
198
|
+
it('rejects connectors without read-only SQL support and still cleans up', async () => {
|
|
199
|
+
const projectDir = join(tempDir, 'project');
|
|
200
|
+
await initKtxProject({ projectDir });
|
|
201
|
+
await writeConnections(projectDir, { warehouse: { driver: 'sqlite', path: 'warehouse.db' } });
|
|
202
|
+
const connector = makeConnector({
|
|
203
|
+
capabilities: {
|
|
204
|
+
...makeConnector().capabilities,
|
|
205
|
+
readOnlySql: false,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const io = makeIo();
|
|
209
|
+
await expect(runKtxSql({
|
|
210
|
+
command: 'execute',
|
|
211
|
+
projectDir,
|
|
212
|
+
connectionId: 'warehouse',
|
|
213
|
+
sql: 'select 1',
|
|
214
|
+
maxRows: 1000,
|
|
215
|
+
output: 'pretty',
|
|
216
|
+
json: false,
|
|
217
|
+
cliVersion: '0.0.0-test',
|
|
218
|
+
}, io.io, {
|
|
219
|
+
createSqlAnalysis: () => makeSqlAnalysis({ ok: true, error: null }),
|
|
220
|
+
createScanConnector: vi.fn(async () => connector),
|
|
221
|
+
})).resolves.toBe(1);
|
|
222
|
+
expect(connector.executeReadOnly).not.toHaveBeenCalled();
|
|
223
|
+
expect(connector.cleanup).toHaveBeenCalledTimes(1);
|
|
224
|
+
expect(io.stderr()).toContain('Connection "warehouse" does not support read-only SQL execution.');
|
|
225
|
+
});
|
|
226
|
+
});
|
|
@@ -6,5 +6,5 @@ describe('@ktx/connector-clickhouse package exports', () => {
|
|
|
6
6
|
expect(connector.KtxClickHouseScanConnector).toBeTypeOf('function');
|
|
7
7
|
expect(connector.clickHouseClientConfigFromConfig).toBeTypeOf('function');
|
|
8
8
|
expect(connector.createClickHouseLiveDatabaseIntrospection).toBeTypeOf('function');
|
|
9
|
-
});
|
|
9
|
+
}, 20_000);
|
|
10
10
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const connectionTypeSchema: z.ZodEnum<{
|
|
3
|
-
|
|
3
|
+
POSTGRESQL: "POSTGRESQL";
|
|
4
4
|
BIGQUERY: "BIGQUERY";
|
|
5
|
+
SNOWFLAKE: "SNOWFLAKE";
|
|
5
6
|
MYSQL: "MYSQL";
|
|
6
|
-
CLICKHOUSE: "CLICKHOUSE";
|
|
7
|
-
POSTGRESQL: "POSTGRESQL";
|
|
8
|
-
SQLITE: "SQLITE";
|
|
9
7
|
SQLSERVER: "SQLSERVER";
|
|
8
|
+
SQLITE: "SQLITE";
|
|
9
|
+
CLICKHOUSE: "CLICKHOUSE";
|
|
10
10
|
CENTRALREACH: "CENTRALREACH";
|
|
11
11
|
EPIC: "EPIC";
|
|
12
12
|
CERNER: "CERNER";
|
|
@@ -153,6 +153,9 @@ export declare class GitService {
|
|
|
153
153
|
* unmerged paths in the index.
|
|
154
154
|
*/
|
|
155
155
|
assertWorktreeClean(): Promise<void>;
|
|
156
|
+
writeBinaryNoRenamePatch(from: string, to: string, patchPath: string): Promise<void>;
|
|
157
|
+
applyPatchFile3WayIndex(patchPath: string): Promise<void>;
|
|
158
|
+
commitStaged(commitMessage: string, author: string, authorEmail: string): Promise<GitCommitInfo>;
|
|
156
159
|
private fileExists;
|
|
157
160
|
/**
|
|
158
161
|
* Create a new worktree at `path` with a new branch `branch` pointing at `startSha`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
3
|
import { noopLogger, resolveConfigDir } from './config.js';
|
|
4
4
|
import { createSimpleGit } from './git-env.js';
|
|
5
5
|
function mergeErrorMessage(error) {
|
|
@@ -596,6 +596,52 @@ export class GitService {
|
|
|
596
596
|
throw new Error(`Worktree has ${unmerged.length} unmerged path(s): ${unmerged.slice(0, 5).join(', ')}; refusing to proceed`);
|
|
597
597
|
}
|
|
598
598
|
}
|
|
599
|
+
async writeBinaryNoRenamePatch(from, to, patchPath) {
|
|
600
|
+
await this.withMutationQueue(async () => {
|
|
601
|
+
const patch = await this.git.raw(['diff', '--binary', '--no-renames', `${from}..${to}`]);
|
|
602
|
+
await fs.mkdir(dirname(patchPath), { recursive: true });
|
|
603
|
+
await fs.writeFile(patchPath, patch, 'utf-8');
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
async applyPatchFile3WayIndex(patchPath) {
|
|
607
|
+
await this.withMutationQueue(async () => {
|
|
608
|
+
await this.git.raw(['apply', '--3way', '--index', patchPath]);
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
async commitStaged(commitMessage, author, authorEmail) {
|
|
612
|
+
return this.withMutationQueue(async () => {
|
|
613
|
+
const stagedChanges = await this.git.diff(['--cached', '--name-only']);
|
|
614
|
+
if (!stagedChanges.trim()) {
|
|
615
|
+
const head = (await this.git.revparse(['HEAD'])).trim();
|
|
616
|
+
const log = await this.git.log({ maxCount: 1 });
|
|
617
|
+
const latest = log.latest;
|
|
618
|
+
return {
|
|
619
|
+
commitHash: head,
|
|
620
|
+
shortHash: head.substring(0, 8),
|
|
621
|
+
message: latest?.message ?? '',
|
|
622
|
+
author: latest?.author_name ?? '',
|
|
623
|
+
authorEmail: latest?.author_email ?? '',
|
|
624
|
+
timestamp: latest?.date ?? new Date(0).toISOString(),
|
|
625
|
+
committedDate: latest?.date ? new Date(latest.date).toISOString() : new Date(0).toISOString(),
|
|
626
|
+
created: false,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
await this.git.commit(commitMessage, { '--author': `${author} <${authorEmail}>` });
|
|
630
|
+
const head = (await this.git.revparse(['HEAD'])).trim();
|
|
631
|
+
const log = await this.git.log({ maxCount: 1 });
|
|
632
|
+
const latest = log.latest;
|
|
633
|
+
return {
|
|
634
|
+
commitHash: head,
|
|
635
|
+
shortHash: head.substring(0, 8),
|
|
636
|
+
message: latest?.message ?? commitMessage,
|
|
637
|
+
author: latest?.author_name ?? author,
|
|
638
|
+
authorEmail: latest?.author_email ?? authorEmail,
|
|
639
|
+
timestamp: latest?.date ?? new Date().toISOString(),
|
|
640
|
+
committedDate: latest?.date ? new Date(latest.date).toISOString() : new Date().toISOString(),
|
|
641
|
+
created: true,
|
|
642
|
+
};
|
|
643
|
+
});
|
|
644
|
+
}
|
|
599
645
|
async fileExists(path) {
|
|
600
646
|
try {
|
|
601
647
|
await fs.access(path);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { GitService } from './git.service.js';
|
|
6
|
+
async function makeGit() {
|
|
7
|
+
const homeDir = await mkdtemp(join(tmpdir(), 'ktx-git-patch-'));
|
|
8
|
+
const configDir = join(homeDir, 'config');
|
|
9
|
+
const git = new GitService({
|
|
10
|
+
storage: { configDir, homeDir },
|
|
11
|
+
git: {
|
|
12
|
+
userName: 'System User',
|
|
13
|
+
userEmail: 'system@example.com',
|
|
14
|
+
bootstrapMessage: 'init',
|
|
15
|
+
bootstrapAuthor: 'system',
|
|
16
|
+
bootstrapAuthorEmail: 'system@example.com',
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
await git.onModuleInit();
|
|
20
|
+
return { homeDir, configDir, git };
|
|
21
|
+
}
|
|
22
|
+
describe('GitService patch helpers', () => {
|
|
23
|
+
it('collects binary-safe no-rename patches and applies them with --3way --index', async () => {
|
|
24
|
+
const { homeDir, configDir, git } = await makeGit();
|
|
25
|
+
await mkdir(join(configDir, 'wiki/global'), { recursive: true });
|
|
26
|
+
await writeFile(join(configDir, 'wiki/global/page.md'), 'old\n');
|
|
27
|
+
await git.commitFiles(['wiki/global/page.md'], 'add page', 'System User', 'system@example.com');
|
|
28
|
+
const base = await git.revParseHead();
|
|
29
|
+
await writeFile(join(configDir, 'wiki/global/page.md'), 'new\n');
|
|
30
|
+
await git.commitFiles(['wiki/global/page.md'], 'edit page', 'System User', 'system@example.com');
|
|
31
|
+
const patchPath = join(homeDir, 'proposal.patch');
|
|
32
|
+
await git.writeBinaryNoRenamePatch(base, 'HEAD', patchPath);
|
|
33
|
+
const targetDir = join(homeDir, 'target');
|
|
34
|
+
await git.addWorktree(targetDir, 'target', base);
|
|
35
|
+
const targetGit = git.forWorktree(targetDir);
|
|
36
|
+
await targetGit.applyPatchFile3WayIndex(patchPath);
|
|
37
|
+
await targetGit.commitStaged('apply proposal', 'System User', 'system@example.com');
|
|
38
|
+
await expect(readFile(join(targetDir, 'wiki/global/page.md'), 'utf-8')).resolves.toBe('new\n');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -3,8 +3,8 @@ import type { SqlAnalysisPort } from '../../../sql-analysis/index.js';
|
|
|
3
3
|
export declare const HISTORIC_SQL_SOURCE_KEY: "historic-sql";
|
|
4
4
|
declare const historicSqlDialectSchema: z.ZodEnum<{
|
|
5
5
|
postgres: "postgres";
|
|
6
|
-
snowflake: "snowflake";
|
|
7
6
|
bigquery: "bigquery";
|
|
7
|
+
snowflake: "snowflake";
|
|
8
8
|
}>;
|
|
9
9
|
export type HistoricSqlDialect = z.infer<typeof historicSqlDialectSchema>;
|
|
10
10
|
export declare const historicSqlUnifiedPullConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -35,8 +35,8 @@ export declare const historicSqlUnifiedPullConfigSchema: z.ZodDiscriminatedUnion
|
|
|
35
35
|
redactionPatterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
36
36
|
staleArchiveAfterDays: z.ZodDefault<z.ZodNumber>;
|
|
37
37
|
dialect: z.ZodEnum<{
|
|
38
|
-
snowflake: "snowflake";
|
|
39
38
|
bigquery: "bigquery";
|
|
39
|
+
snowflake: "snowflake";
|
|
40
40
|
}>;
|
|
41
41
|
windowDays: z.ZodDefault<z.ZodNumber>;
|
|
42
42
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -74,8 +74,8 @@ export declare const aggregatedTemplateSchema: z.ZodObject<{
|
|
|
74
74
|
canonicalSql: z.ZodString;
|
|
75
75
|
dialect: z.ZodEnum<{
|
|
76
76
|
postgres: "postgres";
|
|
77
|
-
snowflake: "snowflake";
|
|
78
77
|
bigquery: "bigquery";
|
|
78
|
+
snowflake: "snowflake";
|
|
79
79
|
}>;
|
|
80
80
|
stats: z.ZodObject<{
|
|
81
81
|
executions: z.ZodNumber;
|
|
@@ -126,8 +126,8 @@ export declare const stagedPatternsInputSchema: z.ZodObject<{
|
|
|
126
126
|
distinctUsersBucket: z.ZodString;
|
|
127
127
|
dialect: z.ZodEnum<{
|
|
128
128
|
postgres: "postgres";
|
|
129
|
-
snowflake: "snowflake";
|
|
130
129
|
bigquery: "bigquery";
|
|
130
|
+
snowflake: "snowflake";
|
|
131
131
|
}>;
|
|
132
132
|
}, z.core.$strip>>;
|
|
133
133
|
}, z.core.$strip>;
|
|
@@ -137,8 +137,8 @@ export declare const stagedManifestSchema: z.ZodObject<{
|
|
|
137
137
|
connectionId: z.ZodString;
|
|
138
138
|
dialect: z.ZodEnum<{
|
|
139
139
|
postgres: "postgres";
|
|
140
|
-
snowflake: "snowflake";
|
|
141
140
|
bigquery: "bigquery";
|
|
141
|
+
snowflake: "snowflake";
|
|
142
142
|
}>;
|
|
143
143
|
fetchedAt: z.ZodISODateTime;
|
|
144
144
|
windowStart: z.ZodISODateTime;
|
|
@@ -34,7 +34,7 @@ export declare class LookerSourceAdapter implements SourceAdapter {
|
|
|
34
34
|
statusCode: number | null;
|
|
35
35
|
message: string;
|
|
36
36
|
retryRecommended: boolean;
|
|
37
|
-
kind?: "lookml_connection_mismatch" | "
|
|
37
|
+
kind?: "lookml_connection_mismatch" | "looker_template_unresolved" | "derived_table_not_supported" | "unmapped_looker_connection" | "unparseable_sql_table_name" | undefined;
|
|
38
38
|
details?: Record<string, unknown> | undefined;
|
|
39
39
|
}[];
|
|
40
40
|
warnings: {
|
|
@@ -45,7 +45,7 @@ export declare class LookerSourceAdapter implements SourceAdapter {
|
|
|
45
45
|
statusCode: number | null;
|
|
46
46
|
message: string;
|
|
47
47
|
retryRecommended: boolean;
|
|
48
|
-
kind?: "lookml_connection_mismatch" | "
|
|
48
|
+
kind?: "lookml_connection_mismatch" | "looker_template_unresolved" | "derived_table_not_supported" | "unmapped_looker_connection" | "unparseable_sql_table_name" | undefined;
|
|
49
49
|
details?: Record<string, unknown> | undefined;
|
|
50
50
|
}[];
|
|
51
51
|
} | null>;
|
package/node_modules/@ktx/context/dist/ingest/adapters/looker/tools/looker-query-to-sl.tool.d.ts
CHANGED
|
@@ -21,9 +21,9 @@ export declare const lookerQueryToSlInputSchema: z.ZodObject<{
|
|
|
21
21
|
}, z.core.$strip>, z.ZodObject<{
|
|
22
22
|
ok: z.ZodLiteral<false>;
|
|
23
23
|
reason: z.ZodEnum<{
|
|
24
|
+
no_connection_mapping: "no_connection_mapping";
|
|
24
25
|
looker_template_unresolved: "looker_template_unresolved";
|
|
25
26
|
derived_table_not_supported: "derived_table_not_supported";
|
|
26
|
-
no_connection_mapping: "no_connection_mapping";
|
|
27
27
|
no_physical_table: "no_physical_table";
|
|
28
28
|
multiple_table_references: "multiple_table_references";
|
|
29
29
|
unsupported_dialect: "unsupported_dialect";
|
|
@@ -89,7 +89,7 @@ export declare function createLookerQueryToSlTool(): import("ai").Tool<{
|
|
|
89
89
|
canonicalTable: string;
|
|
90
90
|
} | {
|
|
91
91
|
ok: false;
|
|
92
|
-
reason: "
|
|
92
|
+
reason: "no_connection_mapping" | "looker_template_unresolved" | "derived_table_not_supported" | "no_physical_table" | "multiple_table_references" | "unsupported_dialect" | "parse_error";
|
|
93
93
|
detail?: string | undefined;
|
|
94
94
|
} | null;
|
|
95
95
|
id?: string | undefined;
|