@kaelio/ktx 0.10.0 → 0.12.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.10.0-py3-none-any.whl → kaelio_ktx-0.12.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 +7 -3
- package/dist/cli-runtime.d.ts +2 -0
- package/dist/cli-runtime.js +14 -8
- 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 +6 -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/community-cta.d.ts +11 -0
- package/dist/community-cta.js +19 -0
- package/dist/connection.js +1 -1
- package/dist/connectors/clickhouse/connector.js +1 -1
- package/dist/connectors/mysql/connector.js +1 -1
- package/dist/connectors/snowflake/connector.d.ts +1 -1
- package/dist/connectors/sqlite/connector.js +2 -25
- package/dist/connectors/sqlserver/connector.js +3 -3
- package/dist/context/connections/connection-type.d.ts +1 -1
- package/dist/context/connections/read-only-sql.d.ts +1 -0
- package/dist/context/connections/read-only-sql.js +116 -2
- 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.d.ts +23 -0
- package/dist/context/core/git.service.js +86 -15
- package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
- 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 +1 -1
- 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 +7 -7
- package/dist/context/mcp/local-project-ports.js +23 -54
- 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 +2 -1
- 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 +3 -33
- package/dist/context/sl/local-sl.d.ts +0 -8
- package/dist/context/sl/local-sl.js +44 -69
- 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 +46 -0
- package/dist/context/sl/source-files.js +131 -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.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 +17 -0
- package/dist/io/tty.js +21 -0
- package/dist/links.d.ts +1 -0
- package/dist/links.js +1 -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.js +22 -35
- 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.js +31 -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 -5
- package/dist/setup-ready-menu.js +1 -1
- package/dist/setup-sources.js +27 -7
- package/dist/setup.d.ts +25 -0
- package/dist/setup.js +90 -19
- 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/startup-profile.js +1 -1
- package/dist/status-project.d.ts +0 -2
- package/dist/status-project.js +4 -6
- package/dist/telemetry/command-hook.d.ts +24 -0
- package/dist/telemetry/command-hook.js +37 -3
- package/dist/telemetry/events.d.ts +1 -1
- package/dist/telemetry/exception.js +14 -0
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.js +2 -2
- 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 +1 -1
|
@@ -2,8 +2,8 @@ import YAML from 'yaml';
|
|
|
2
2
|
import { SYSTEM_GIT_AUTHOR } from '../../../context/tools/authors.js';
|
|
3
3
|
import { sourceOverlaySchema } from '../schemas.js';
|
|
4
4
|
import { SemanticLayerService } from '../semantic-layer.service.js';
|
|
5
|
+
import { resolveSlSourceFile, slSourceFilePath } from '../source-files.js';
|
|
5
6
|
import { sourceDefinitionSchema } from './base-semantic-layer.tool.js';
|
|
6
|
-
const slSourcePath = (connectionId, sourceName) => `semantic-layer/${connectionId}/${sourceName}.yaml`;
|
|
7
7
|
function resolveDialect(warehouse) {
|
|
8
8
|
if (!warehouse) {
|
|
9
9
|
return null;
|
|
@@ -33,32 +33,28 @@ function wrapWithSingleRowQuery(sql, dialect) {
|
|
|
33
33
|
export async function validateSingleSource(deps, connectionId, sourceName) {
|
|
34
34
|
const errors = [];
|
|
35
35
|
const warnings = [];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
content = result.content;
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
errors.push(`${sourceName}.yaml: file not found`);
|
|
36
|
+
const file = await deps.semanticLayerService.readSourceFile(connectionId, sourceName);
|
|
37
|
+
if (!file) {
|
|
38
|
+
errors.push(`${sourceName}: no standalone or overlay file found`);
|
|
43
39
|
return { errors, warnings };
|
|
44
40
|
}
|
|
45
41
|
let parsed;
|
|
46
42
|
try {
|
|
47
|
-
parsed = YAML.parse(content);
|
|
43
|
+
parsed = YAML.parse(file.content);
|
|
48
44
|
}
|
|
49
45
|
catch (e) {
|
|
50
|
-
errors.push(`${sourceName}
|
|
46
|
+
errors.push(`${sourceName}: invalid YAML — ${e instanceof Error ? e.message : String(e)}`);
|
|
51
47
|
return { errors, warnings };
|
|
52
48
|
}
|
|
53
49
|
if (!parsed || typeof parsed !== 'object') {
|
|
54
|
-
errors.push(`${sourceName}
|
|
50
|
+
errors.push(`${sourceName}: top-level content is not an object`);
|
|
55
51
|
return { errors, warnings };
|
|
56
52
|
}
|
|
57
53
|
const isOverlay = !parsed.table && !parsed.sql;
|
|
58
54
|
if (!isOverlay) {
|
|
59
55
|
const isManifestBacked = await deps.semanticLayerService.isManifestBacked(connectionId, sourceName);
|
|
60
56
|
if (isManifestBacked) {
|
|
61
|
-
errors.push(`${sourceName}
|
|
57
|
+
errors.push(`${sourceName}: standalone source shadows an existing manifest entry — ` +
|
|
62
58
|
`writing it as-is drops the manifest's columns and joins. ` +
|
|
63
59
|
`Remove "sql:", "table:", "grain:", and base-table "columns:" and keep only ` +
|
|
64
60
|
`"name:" plus overlay fields such as "measures:", "segments:", "descriptions:", ` +
|
|
@@ -71,16 +67,16 @@ export async function validateSingleSource(deps, connectionId, sourceName) {
|
|
|
71
67
|
const result = schema.safeParse(parsed);
|
|
72
68
|
if (!result.success) {
|
|
73
69
|
const issues = result.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ');
|
|
74
|
-
errors.push(`${sourceName}
|
|
70
|
+
errors.push(`${sourceName}: schema — ${issues}`);
|
|
75
71
|
const errorPaths = new Set(result.error.issues.map((i) => String(i.path[0])));
|
|
76
72
|
if (errorPaths.has('joins')) {
|
|
77
|
-
warnings.push(`${sourceName}
|
|
73
|
+
warnings.push(`${sourceName}: hint — join format: {to, on: 'local_col = TARGET.col', relationship: 'many_to_one|one_to_many|one_to_one'}`);
|
|
78
74
|
}
|
|
79
75
|
if (errorPaths.has('columns')) {
|
|
80
|
-
warnings.push(`${sourceName}
|
|
76
|
+
warnings.push(`${sourceName}: hint — overlay columns must be computed: {name, expr, type}. Use column_overrides for manifest column descriptions or metadata.`);
|
|
81
77
|
}
|
|
82
78
|
if (errorPaths.has('measures')) {
|
|
83
|
-
warnings.push(`${sourceName}
|
|
79
|
+
warnings.push(`${sourceName}: hint — measure format: {name, expr, description (optional), filter (optional)}`);
|
|
84
80
|
}
|
|
85
81
|
return { errors, warnings };
|
|
86
82
|
}
|
|
@@ -93,7 +89,7 @@ export async function validateSingleSource(deps, connectionId, sourceName) {
|
|
|
93
89
|
const seenMeasures = new Set();
|
|
94
90
|
for (const m of measures) {
|
|
95
91
|
if (seenMeasures.has(m.name)) {
|
|
96
|
-
errors.push(`${sourceName}
|
|
92
|
+
errors.push(`${sourceName}: duplicate measure name "${m.name}"`);
|
|
97
93
|
}
|
|
98
94
|
seenMeasures.add(m.name);
|
|
99
95
|
}
|
|
@@ -125,7 +121,7 @@ export async function validateSingleSource(deps, connectionId, sourceName) {
|
|
|
125
121
|
const actual = new Set((probe.headers ?? []).map((h) => h.toLowerCase()));
|
|
126
122
|
const missing = sourceColumns.map((c) => c.name).filter((n) => !actual.has(n.toLowerCase()));
|
|
127
123
|
if (missing.length > 0) {
|
|
128
|
-
errors.push(`${sourceName}
|
|
124
|
+
errors.push(`${sourceName}: declared columns absent from sql result — ${missing.join(', ')} (warehouse returned: ${[...actual].slice(0, 10).join(', ')}${actual.size > 10 ? ', …' : ''})`);
|
|
129
125
|
}
|
|
130
126
|
}
|
|
131
127
|
catch (e) {
|
|
@@ -151,7 +147,7 @@ function formatProbeError(args) {
|
|
|
151
147
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
152
148
|
const refColumns = sourceColumns.filter((c) => referencesColumn(probeSql, c.name));
|
|
153
149
|
const lines = [
|
|
154
|
-
measureName ? `${sourceName}
|
|
150
|
+
measureName ? `${sourceName}: measure "${measureName}" ${headline}.` : `${sourceName}: ${headline}.`,
|
|
155
151
|
];
|
|
156
152
|
if (warehouse) {
|
|
157
153
|
lines.push(` Warehouse: ${warehouse}`);
|
|
@@ -179,7 +175,7 @@ async function probeOverlayMeasures(deps, connectionId, sourceName, warehouse) {
|
|
|
179
175
|
composed = all.find((s) => s.name === sourceName);
|
|
180
176
|
}
|
|
181
177
|
catch (e) {
|
|
182
|
-
errors.push(`${sourceName}
|
|
178
|
+
errors.push(`${sourceName}: failed to load composed source for probe — ${e instanceof Error ? e.message : String(e)}`);
|
|
183
179
|
return errors;
|
|
184
180
|
}
|
|
185
181
|
if (!composed?.table || composed.measures.length === 0) {
|
|
@@ -214,22 +210,54 @@ async function probeOverlayMeasures(deps, connectionId, sourceName, warehouse) {
|
|
|
214
210
|
}
|
|
215
211
|
return errors;
|
|
216
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* A read-only view of the config repo at one commit, shaped for
|
|
215
|
+
* `resolveSlSourceFile` so name→file resolution runs against history exactly as
|
|
216
|
+
* it does against the working tree — one resolver, two backing stores. Used to
|
|
217
|
+
* recover the path a source occupied at `preHead` after the live file is gone.
|
|
218
|
+
*/
|
|
219
|
+
function gitCommitFileStore(git, commitHash) {
|
|
220
|
+
return {
|
|
221
|
+
async listFiles(path) {
|
|
222
|
+
return { files: await git.listFilesAtCommit(path, commitHash) };
|
|
223
|
+
},
|
|
224
|
+
async readFile(path) {
|
|
225
|
+
return { content: await git.getFileAtCommit(path, commitHash) };
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
217
229
|
/**
|
|
218
230
|
* Restore `sourceName` to the content it had at `preHead`, or delete it if it didn't
|
|
219
231
|
* exist then. Used by sl_rollback (agent-driven) and the pre-squash revert gate
|
|
220
232
|
* (automatic). Returns a short human-readable description of what happened.
|
|
221
233
|
*/
|
|
222
234
|
export async function revertSourceToPreHead(deps, connectionId, preHead, sourceName) {
|
|
223
|
-
|
|
235
|
+
// Find the file that defines this source. While it is still on disk
|
|
236
|
+
// (invalid-but-present) the live resolver finds it by its in-file `name:`.
|
|
237
|
+
// Once the session deleted it, the path is gone too — and humans rename files
|
|
238
|
+
// freely, so it is NOT the writer-derived filename. Recover it from history by
|
|
239
|
+
// resolving the name against the preHead commit instead of guessing.
|
|
240
|
+
const live = await resolveSlSourceFile(deps.configService, connectionId, sourceName);
|
|
241
|
+
let relPath;
|
|
224
242
|
let preContent = null;
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
243
|
+
if (live) {
|
|
244
|
+
relPath = live.path;
|
|
245
|
+
if (preHead) {
|
|
246
|
+
try {
|
|
247
|
+
preContent = await deps.gitService.getFileAtCommit(relPath, preHead);
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
preContent = null;
|
|
251
|
+
}
|
|
231
252
|
}
|
|
232
253
|
}
|
|
254
|
+
else {
|
|
255
|
+
const atPreHead = preHead
|
|
256
|
+
? await resolveSlSourceFile(gitCommitFileStore(deps.gitService, preHead), connectionId, sourceName)
|
|
257
|
+
: null;
|
|
258
|
+
relPath = atPreHead?.path ?? slSourceFilePath(connectionId, sourceName);
|
|
259
|
+
preContent = atPreHead?.content ?? null;
|
|
260
|
+
}
|
|
233
261
|
if (preContent !== null) {
|
|
234
262
|
await deps.configService.writeFile(relPath, preContent, SYSTEM_GIT_AUTHOR.name, SYSTEM_GIT_AUTHOR.email, `Revert SL source to pre-session state: ${sourceName}`, { skipLock: true });
|
|
235
263
|
return 'restored to pre-session content';
|
|
@@ -12,8 +12,10 @@ const slWriteSourceInputSchema = z.object({
|
|
|
12
12
|
connectionId: slToolConnectionIdSchema.describe('Data source connection ID'),
|
|
13
13
|
sourceName: z
|
|
14
14
|
.string()
|
|
15
|
-
.
|
|
16
|
-
.describe(
|
|
15
|
+
.min(1)
|
|
16
|
+
.describe("Name of the source to create, edit, or delete. Must equal the source's `name:`. Use the verbatim " +
|
|
17
|
+
'warehouse identifier when overlaying a manifest source (e.g. SIGNED_UP); snake_case is recommended ' +
|
|
18
|
+
'for new standalone sources.'),
|
|
17
19
|
source: sourceInputSchema
|
|
18
20
|
.optional()
|
|
19
21
|
.describe('Source definition (standalone with table/sql) or overlay (measures, column_overrides, computed columns, etc.)'),
|
|
@@ -122,6 +124,12 @@ Do NOT join back to a table that the SQL already aggregates from if the grain co
|
|
|
122
124
|
if (!input.source) {
|
|
123
125
|
return this.buildOutput(false, ['Provide `source` to create or rewrite. For targeted edits, use sl_edit_source.'], sourceName);
|
|
124
126
|
}
|
|
127
|
+
// The in-file `name:` is the source's identity; the file is written under
|
|
128
|
+
// source.name while the orphan/shadow checks key on sourceName — a mismatch
|
|
129
|
+
// would validate one source and save another.
|
|
130
|
+
if (input.source.name !== sourceName) {
|
|
131
|
+
return this.buildOutput(false, [`source.name "${input.source.name}" does not match sourceName "${sourceName}" — they must be identical.`], sourceName);
|
|
132
|
+
}
|
|
125
133
|
return this.writeFullSource(connectionId, input.source, sourceName, author, authorEmail, context, semanticLayerService, skipIndex, rawPathValidation.rawPaths);
|
|
126
134
|
}
|
|
127
135
|
async writeFullSource(connectionId, source, sourceName, author, authorEmail, context, semanticLayerService, skipIndex, rawPaths) {
|
|
@@ -183,13 +191,8 @@ Do NOT join back to a table that the SQL already aggregates from if the grain co
|
|
|
183
191
|
}
|
|
184
192
|
}
|
|
185
193
|
async readSourceYamlFromService(service, connectionId, sourceName) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return content;
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
194
|
+
const file = await service.readSourceFile(connectionId, sourceName);
|
|
195
|
+
return file?.content ?? null;
|
|
193
196
|
}
|
|
194
197
|
async rejectOrphanOverlay(semanticLayerService, connectionId, sourceName, content) {
|
|
195
198
|
let parsed;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// One mapping from ktx connection identity to the sqlglot dialect name used by
|
|
2
|
+
// the Python daemon (SQL analysis, read-only validation) and semantic-layer
|
|
3
|
+
// compute. Keys cover both vocabularies that name a connection's engine:
|
|
4
|
+
// ktx.yaml driver names ("postgres", "sqlserver") and the local connection-type
|
|
5
|
+
// spellings exposed by KtxConnectionInfo.connectionType ("POSTGRESQL").
|
|
6
|
+
const SQLGLOT_DIALECTS = {
|
|
7
|
+
postgres: 'postgres',
|
|
8
|
+
postgresql: 'postgres',
|
|
9
|
+
bigquery: 'bigquery',
|
|
10
|
+
snowflake: 'snowflake',
|
|
11
|
+
mysql: 'mysql',
|
|
12
|
+
sqlserver: 'tsql',
|
|
13
|
+
sqlite: 'sqlite',
|
|
14
|
+
duckdb: 'duckdb',
|
|
15
|
+
clickhouse: 'clickhouse',
|
|
16
|
+
databricks: 'databricks',
|
|
17
|
+
};
|
|
18
|
+
export function sqlAnalysisDialectForDriver(driver) {
|
|
19
|
+
return SQLGLOT_DIALECTS[(driver ?? '').toLowerCase()] ?? 'postgres';
|
|
20
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Tool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
import { type KtxLogger } from '../../context/core/config.js';
|
|
3
4
|
import type { KtxRuntimeToolDescriptor } from '../llm/runtime-port.js';
|
|
4
5
|
import type { IngestToolMetadata, ToolSession } from './tool-session.js';
|
|
@@ -56,30 +57,16 @@ interface MethodologyEntry {
|
|
|
56
57
|
/**
|
|
57
58
|
* SECURITY: All tools require authentication. userId must always be provided in ToolContext.
|
|
58
59
|
*/
|
|
59
|
-
export declare abstract class BaseTool<TInput extends
|
|
60
|
+
export declare abstract class BaseTool<TInput extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> {
|
|
60
61
|
protected readonly logger: KtxLogger;
|
|
61
62
|
abstract readonly name: string;
|
|
62
63
|
constructor(logger?: KtxLogger);
|
|
63
64
|
abstract get description(): string;
|
|
64
65
|
abstract get inputSchema(): TInput;
|
|
65
|
-
abstract call(input: z.infer<TInput>, context: ToolContext): Promise<
|
|
66
|
-
|
|
67
|
-
type: 'object';
|
|
68
|
-
properties: Record<string, any>;
|
|
69
|
-
required?: string[];
|
|
70
|
-
};
|
|
71
|
-
toAnthropicFormat(): {
|
|
72
|
-
name: string;
|
|
73
|
-
description: string;
|
|
74
|
-
input_schema: {
|
|
75
|
-
type: 'object';
|
|
76
|
-
properties: Record<string, any>;
|
|
77
|
-
required?: string[];
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
toAiSdkTool(context: ToolContext): any;
|
|
66
|
+
abstract call(input: z.infer<TInput>, context: ToolContext): Promise<unknown>;
|
|
67
|
+
toAiSdkTool(context: ToolContext): Tool;
|
|
81
68
|
toRuntimeTool(context: ToolContext): KtxRuntimeToolDescriptor;
|
|
82
|
-
parseInput(input: Record<string,
|
|
69
|
+
parseInput(input: Record<string, unknown>): z.infer<TInput>;
|
|
83
70
|
protected getCurrentUserQuery(context: ToolContext): string | null;
|
|
84
71
|
}
|
|
85
72
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { tool } from 'ai';
|
|
2
|
-
import { z } from 'zod';
|
|
3
2
|
import { noopLogger } from '../../context/core/config.js';
|
|
4
3
|
import { normalizeKtxRuntimeToolOutput } from '../llm/runtime-tools.js';
|
|
5
4
|
/**
|
|
@@ -10,19 +9,6 @@ export class BaseTool {
|
|
|
10
9
|
constructor(logger = noopLogger) {
|
|
11
10
|
this.logger = logger;
|
|
12
11
|
}
|
|
13
|
-
getParametersSchema() {
|
|
14
|
-
const jsonSchema = z.toJSONSchema(this.inputSchema, {
|
|
15
|
-
target: 'draft-7',
|
|
16
|
-
});
|
|
17
|
-
return jsonSchema;
|
|
18
|
-
}
|
|
19
|
-
toAnthropicFormat() {
|
|
20
|
-
return {
|
|
21
|
-
name: this.name,
|
|
22
|
-
description: this.description,
|
|
23
|
-
input_schema: this.getParametersSchema(),
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
12
|
toAiSdkTool(context) {
|
|
27
13
|
const toolName = this.name;
|
|
28
14
|
const logger = this.logger;
|
|
@@ -260,7 +260,7 @@ export function renderContextBuildView(state, options = {}) {
|
|
|
260
260
|
const totalCount = allTargets.length;
|
|
261
261
|
const hasActive = allTargets.some((t) => t.status === 'running' || t.status === 'queued');
|
|
262
262
|
const allDone = totalCount > 0 && !hasActive;
|
|
263
|
-
const headerParts = [options.title ?? 'Building
|
|
263
|
+
const headerParts = [options.title ?? 'Building ktx context'];
|
|
264
264
|
if (totalCount > 0) {
|
|
265
265
|
const progressParts = [`${doneCount}/${totalCount}`];
|
|
266
266
|
if (state.totalElapsedMs > 0)
|
|
@@ -550,7 +550,7 @@ function failedStepDetail(result) {
|
|
|
550
550
|
return result.steps.find((step) => step.status === 'failed')?.detail ?? null;
|
|
551
551
|
}
|
|
552
552
|
const INTERNAL_FAILURE_LINE_RE = /^(Report|Run|Job|Status|Adapter|Connection|Sync|Mode|Dry run|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/;
|
|
553
|
-
const ACTIONABLE_FAILURE_LINE_RE = /^(Missing bundled Python runtime manifest|
|
|
553
|
+
const ACTIONABLE_FAILURE_LINE_RE = /^(Missing bundled Python runtime manifest|ktx Python runtime is required|ktx daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/;
|
|
554
554
|
function trimErrorPrefix(line) {
|
|
555
555
|
return line.replace(/^Error:\s*/, '');
|
|
556
556
|
}
|
|
@@ -559,7 +559,7 @@ function firstCapturedFailureLine(output) {
|
|
|
559
559
|
.split(/\r?\n/)
|
|
560
560
|
.map((candidate) => candidate.trim())
|
|
561
561
|
.filter((candidate) => candidate.length > 0)
|
|
562
|
-
.filter((candidate) => !candidate.startsWith('
|
|
562
|
+
.filter((candidate) => !candidate.startsWith('ktx scan completed'))
|
|
563
563
|
.filter((candidate) => !INTERNAL_FAILURE_LINE_RE.test(candidate));
|
|
564
564
|
const line = lines.find((candidate) => ACTIONABLE_FAILURE_LINE_RE.test(candidate)) ?? lines.at(-1) ?? null;
|
|
565
565
|
return line ? trimErrorPrefix(line) : null;
|
|
@@ -584,7 +584,7 @@ function failureTextForTarget(input) {
|
|
|
584
584
|
const code = networkErrorCode(input.error, input.capturedOutput);
|
|
585
585
|
if (code && isLocalSqlAnalysisConnectionRefused({ capturedOutput: input.capturedOutput, fallback: input.fallback })) {
|
|
586
586
|
return [
|
|
587
|
-
`
|
|
587
|
+
`ktx could not reach the local SQL analysis runtime while processing query history for ${input.target.connectionId}.`,
|
|
588
588
|
`Reason: ${NETWORK_ERROR_REASONS[code]} (${code}).`,
|
|
589
589
|
`Retry: ${retryCommand({
|
|
590
590
|
projectDir: input.projectDir,
|
|
@@ -598,7 +598,7 @@ function failureTextForTarget(input) {
|
|
|
598
598
|
if (code) {
|
|
599
599
|
const operation = input.target.operation === 'database-ingest' ? 'reading schema for' : 'ingesting';
|
|
600
600
|
return [
|
|
601
|
-
`
|
|
601
|
+
`ktx lost its connection to ${friendlyDriverName(input.target.driver)} while ${operation} ${input.target.connectionId}.`,
|
|
602
602
|
`Reason: ${NETWORK_ERROR_REASONS[code]} (${code}).`,
|
|
603
603
|
`Retry: ${retryCommand({
|
|
604
604
|
projectDir: input.projectDir,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { parseDottedTableEntry } from './context/scan/enabled-tables.js';
|
|
2
|
+
import { createStaticCliSpinner } from './clack.js';
|
|
2
3
|
import { profileMark } from './startup-profile.js';
|
|
4
|
+
import { withSearchableMultiselectNavigation } from './prompt-navigation.js';
|
|
3
5
|
import { buildInitialState, buildPickerTree, } from './tree-picker-state.js';
|
|
4
6
|
import { renderTreePickerTui, } from './tree-picker-tui.js';
|
|
5
7
|
profileMark('module:database-tree-picker');
|
|
@@ -167,7 +169,7 @@ export async function pickDatabaseScope(args, io, render = defaultRenderer) {
|
|
|
167
169
|
let selectedSchemas = initialStageOneSchemas(args);
|
|
168
170
|
while (true) {
|
|
169
171
|
const pickedSchemas = await args.prompts.autocompleteMultiselect({
|
|
170
|
-
message: `Choose ${args.schemaNounPlural} to enable for ${args.connectionId}
|
|
172
|
+
message: withSearchableMultiselectNavigation(`Choose ${args.schemaNounPlural} to enable for ${args.connectionId}`),
|
|
171
173
|
placeholder: `Search ${args.schemaNounPlural}`,
|
|
172
174
|
options: schemaOptions(args),
|
|
173
175
|
initialValues: selectedSchemas,
|
|
@@ -178,7 +180,7 @@ export async function pickDatabaseScope(args, io, render = defaultRenderer) {
|
|
|
178
180
|
}
|
|
179
181
|
selectedSchemas = pickedSchemas;
|
|
180
182
|
if (selectedSchemas.length === 0) {
|
|
181
|
-
io.stderr.write(`Nothing selected - type to
|
|
183
|
+
io.stderr.write(`Nothing selected - type to search, or Escape to skip ${args.schemaNoun} scope.\n`);
|
|
182
184
|
continue;
|
|
183
185
|
}
|
|
184
186
|
const selectedNoun = selectedSchemas.length === 1 ? args.schemaNoun : args.schemaNounPlural;
|
|
@@ -193,7 +195,20 @@ export async function pickDatabaseScope(args, io, render = defaultRenderer) {
|
|
|
193
195
|
if (action === 'back') {
|
|
194
196
|
continue;
|
|
195
197
|
}
|
|
196
|
-
|
|
198
|
+
// Static (stderr-only) spinner: the stage-two table picker below is a raw-mode
|
|
199
|
+
// Ink TUI, and an animated clack spinner would leave stdin dirty so Ink reads a
|
|
200
|
+
// stray Escape and exits immediately.
|
|
201
|
+
const tablesSpinner = createStaticCliSpinner(io);
|
|
202
|
+
tablesSpinner.start(`Listing tables in ${selectedSchemas.length} ${selectedNoun}…`);
|
|
203
|
+
let discovered;
|
|
204
|
+
try {
|
|
205
|
+
discovered = await args.listTablesForSchemas(selectedSchemas);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
tablesSpinner.error('Could not list tables');
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
tablesSpinner.stop(`Found ${discovered.length} ${discovered.length === 1 ? 'table' : 'tables'}`);
|
|
197
212
|
if (action === 'save' && args.existing.enabledTables.length === 0) {
|
|
198
213
|
return {
|
|
199
214
|
kind: 'selected',
|
package/dist/demo-assets.js
CHANGED
package/dist/doctor.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ interface RenderOptions {
|
|
|
67
67
|
}
|
|
68
68
|
export declare function formatDoctorReport(report: DoctorReport, options?: Partial<RenderOptions>): string;
|
|
69
69
|
export declare function renderInvalidConfigMessage(projectDir: string, issues: KtxConfigIssue[], outputMode: KtxDoctorOutputMode, io: KtxDoctorIo): void;
|
|
70
|
-
export declare function renderValidConfigMessage(projectDir: string, outputMode: KtxDoctorOutputMode, io: KtxDoctorIo): void;
|
|
70
|
+
export declare function renderValidConfigMessage(projectDir: string, outputMode: KtxDoctorOutputMode, io: KtxDoctorIo, warnings?: KtxConfigIssue[]): void;
|
|
71
71
|
export declare function renderMissingProjectMessage(projectDir: string, outputMode: KtxDoctorOutputMode, io: KtxDoctorIo): void;
|
|
72
72
|
export declare function runKtxDoctor(args: KtxDoctorArgs, io?: KtxDoctorIo, deps?: KtxDoctorDeps): Promise<number>;
|
|
73
73
|
export {};
|
package/dist/doctor.js
CHANGED
|
@@ -4,6 +4,7 @@ import { access, readFile } from 'node:fs/promises';
|
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
|
+
import { shouldUseColorOutput } from './io/tty.js';
|
|
7
8
|
import { KTX_NEXT_STEP_DIRECT_COMMANDS } from './next-steps.js';
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
9
10
|
function workspaceRootDir() {
|
|
@@ -121,12 +122,6 @@ const GROUP_LABEL = {
|
|
|
121
122
|
search: 'Semantic search',
|
|
122
123
|
history: 'Query history',
|
|
123
124
|
};
|
|
124
|
-
function shouldUseColor(io) {
|
|
125
|
-
if (io.stdout.isTTY !== true)
|
|
126
|
-
return false;
|
|
127
|
-
const env = process.env;
|
|
128
|
-
return !env.NO_COLOR && env.TERM !== 'dumb' && !env.CI;
|
|
129
|
-
}
|
|
130
125
|
function styleStatus(useColor, status, text) {
|
|
131
126
|
if (!useColor)
|
|
132
127
|
return text;
|
|
@@ -322,17 +317,20 @@ export function renderInvalidConfigMessage(projectDir, issues, outputMode, io) {
|
|
|
322
317
|
}, null, 2)}\n`);
|
|
323
318
|
return;
|
|
324
319
|
}
|
|
325
|
-
const useColor =
|
|
320
|
+
const useColor = shouldUseColorOutput(io.stdout);
|
|
326
321
|
const dim = (text) => styleDim(useColor, text);
|
|
327
322
|
const bold = (text) => styleBold(useColor, text);
|
|
328
323
|
const status = (s, text) => styleStatus(useColor, s, text);
|
|
329
324
|
const abbreviated = abbreviateHome(projectDir) ?? projectDir;
|
|
325
|
+
const errorCount = issues.filter((issue) => issue.severity === 'error').length;
|
|
326
|
+
const warningCount = issues.length - errorCount;
|
|
330
327
|
const lines = [];
|
|
331
|
-
lines.push(`${bold('
|
|
328
|
+
lines.push(`${bold('ktx status')} ${dim('·')} ${abbreviated}`);
|
|
332
329
|
lines.push('');
|
|
333
|
-
lines.push(` ${status('fail', '✗')} ${bold('Config')} ktx.yaml has ${
|
|
330
|
+
lines.push(` ${status('fail', '✗')} ${bold('Config')} ktx.yaml has ${errorCount} schema issue${errorCount === 1 ? '' : 's'}${warningCount > 0 ? ` · ${warningCount} ignored field${warningCount === 1 ? '' : 's'}` : ''}`);
|
|
334
331
|
for (const issue of issues) {
|
|
335
|
-
|
|
332
|
+
const glyph = issue.severity === 'error' ? status('fail', '✗') : status('warn', '⚠');
|
|
333
|
+
lines.push(` ${glyph} ${issue.message}`);
|
|
336
334
|
if (issue.fix) {
|
|
337
335
|
lines.push(` ${dim(`→ ${issue.fix}`)}`);
|
|
338
336
|
}
|
|
@@ -342,23 +340,35 @@ export function renderInvalidConfigMessage(projectDir, issues, outputMode, io) {
|
|
|
342
340
|
lines.push('');
|
|
343
341
|
io.stdout.write(lines.join('\n'));
|
|
344
342
|
}
|
|
345
|
-
export function renderValidConfigMessage(projectDir, outputMode, io) {
|
|
343
|
+
export function renderValidConfigMessage(projectDir, outputMode, io, warnings = []) {
|
|
346
344
|
if (outputMode === 'json') {
|
|
347
345
|
io.stdout.write(`${JSON.stringify({
|
|
348
346
|
ok: true,
|
|
349
347
|
projectDir,
|
|
348
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
350
349
|
}, null, 2)}\n`);
|
|
351
350
|
return;
|
|
352
351
|
}
|
|
353
|
-
const useColor =
|
|
352
|
+
const useColor = shouldUseColorOutput(io.stdout);
|
|
354
353
|
const dim = (text) => styleDim(useColor, text);
|
|
355
354
|
const bold = (text) => styleBold(useColor, text);
|
|
356
355
|
const status = (s, text) => styleStatus(useColor, s, text);
|
|
357
356
|
const abbreviated = abbreviateHome(projectDir) ?? projectDir;
|
|
358
357
|
const lines = [];
|
|
359
|
-
lines.push(`${bold('
|
|
358
|
+
lines.push(`${bold('ktx status')} ${dim('·')} ${abbreviated}`);
|
|
360
359
|
lines.push('');
|
|
361
|
-
|
|
360
|
+
if (warnings.length > 0) {
|
|
361
|
+
lines.push(` ${status('warn', '⚠')} ${bold('Config')} ktx.yaml schema valid · ${warnings.length} ignored field${warnings.length === 1 ? '' : 's'}`);
|
|
362
|
+
for (const warning of warnings) {
|
|
363
|
+
lines.push(` ${status('warn', '⚠')} ${warning.message}`);
|
|
364
|
+
if (warning.fix) {
|
|
365
|
+
lines.push(` ${dim(`→ ${warning.fix}`)}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
lines.push(` ${status('pass', '✓')} ${bold('Config')} ${dim('ktx.yaml schema valid')}`);
|
|
371
|
+
}
|
|
362
372
|
lines.push('');
|
|
363
373
|
io.stdout.write(lines.join('\n'));
|
|
364
374
|
}
|
|
@@ -371,15 +381,15 @@ export function renderMissingProjectMessage(projectDir, outputMode, io) {
|
|
|
371
381
|
}, null, 2)}\n`);
|
|
372
382
|
return;
|
|
373
383
|
}
|
|
374
|
-
const useColor =
|
|
384
|
+
const useColor = shouldUseColorOutput(io.stdout);
|
|
375
385
|
const dim = (text) => styleDim(useColor, text);
|
|
376
386
|
const bold = (text) => styleBold(useColor, text);
|
|
377
387
|
const abbreviated = abbreviateHome(projectDir) ?? projectDir;
|
|
378
388
|
const envProjectDir = process.env.KTX_PROJECT_DIR;
|
|
379
389
|
const lines = [];
|
|
380
|
-
lines.push(`${bold('
|
|
390
|
+
lines.push(`${bold('ktx status')} ${dim('·')} ${abbreviated}`);
|
|
381
391
|
lines.push('');
|
|
382
|
-
lines.push(` No
|
|
392
|
+
lines.push(` No ktx project here yet. ${dim('(ktx.yaml not found)')}`);
|
|
383
393
|
lines.push('');
|
|
384
394
|
lines.push(` Run ${bold('ktx setup')} to create one.`);
|
|
385
395
|
if (envProjectDir !== undefined) {
|
|
@@ -402,14 +412,13 @@ export async function runKtxDoctor(args, io = process, deps = {}) {
|
|
|
402
412
|
return 1;
|
|
403
413
|
}
|
|
404
414
|
const { validateKtxProjectConfig } = await import('./context/project/config.js');
|
|
405
|
-
;
|
|
406
415
|
const rawConfig = await readFile(configPath, 'utf-8');
|
|
407
416
|
const validation = validateKtxProjectConfig(rawConfig);
|
|
408
417
|
if (!validation.ok) {
|
|
409
418
|
renderInvalidConfigMessage(args.projectDir, validation.issues, args.outputMode, io);
|
|
410
419
|
return 1;
|
|
411
420
|
}
|
|
412
|
-
renderValidConfigMessage(args.projectDir, args.outputMode, io);
|
|
421
|
+
renderValidConfigMessage(args.projectDir, args.outputMode, io, validation.issues);
|
|
413
422
|
return 0;
|
|
414
423
|
}
|
|
415
424
|
if (args.command === 'project') {
|
|
@@ -420,7 +429,6 @@ export async function runKtxDoctor(args, io = process, deps = {}) {
|
|
|
420
429
|
}
|
|
421
430
|
const { loadKtxProject } = await import('./context/project/project.js');
|
|
422
431
|
const { validateKtxProjectConfig } = await import('./context/project/config.js');
|
|
423
|
-
;
|
|
424
432
|
const { buildProjectStatus, renderProjectStatus } = await import('./status-project.js');
|
|
425
433
|
const rawConfig = await readFile(configPath, 'utf-8');
|
|
426
434
|
const validation = validateKtxProjectConfig(rawConfig);
|
|
@@ -445,7 +453,7 @@ export async function runKtxDoctor(args, io = process, deps = {}) {
|
|
|
445
453
|
else {
|
|
446
454
|
io.stdout.write(renderProjectStatus(projectStatus, {
|
|
447
455
|
verbose,
|
|
448
|
-
useColor:
|
|
456
|
+
useColor: shouldUseColorOutput(io.stdout),
|
|
449
457
|
durationMs: Date.now() - startedAt,
|
|
450
458
|
toolchainChecks,
|
|
451
459
|
}));
|
|
@@ -453,10 +461,10 @@ export async function runKtxDoctor(args, io = process, deps = {}) {
|
|
|
453
461
|
return projectStatus.verdict === 'blocked' ? 1 : 0;
|
|
454
462
|
}
|
|
455
463
|
const setupChecks = await runSetupChecks();
|
|
456
|
-
const report = { title: '
|
|
464
|
+
const report = { title: 'ktx status', checks: setupChecks };
|
|
457
465
|
const renderOptions = {
|
|
458
466
|
verbose: args.verbose ?? false,
|
|
459
|
-
useColor:
|
|
467
|
+
useColor: shouldUseColorOutput(io.stdout),
|
|
460
468
|
durationMs: Date.now() - startedAt,
|
|
461
469
|
command: args.command,
|
|
462
470
|
};
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marks an error as an expected operational outcome that ktx surfaces to its
|
|
3
|
+
* caller (a connected agent or the CLI user) rather than an unexpected ktx
|
|
4
|
+
* fault. Examples: invalid agent input, a warehouse rejecting a query, or a
|
|
5
|
+
* validation guard rejecting a request.
|
|
6
|
+
*
|
|
7
|
+
* `reportException` skips PostHog Error Tracking for these so the bug stream
|
|
8
|
+
* stays free of routine, caller-driven failures. The failure is still surfaced
|
|
9
|
+
* to the caller (as a tool-error result or CLI error) and still recorded by the
|
|
10
|
+
* outcome-tagged telemetry events, so no diagnostic signal is lost.
|
|
11
|
+
*/
|
|
12
|
+
export declare class KtxExpectedError extends Error {
|
|
13
|
+
constructor(message: string, options?: ErrorOptions);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A query was rejected at the warehouse/driver boundary — the warehouse refused
|
|
17
|
+
* to compile or run it, or a read-only guard rejected it. Reuses the underlying
|
|
18
|
+
* error's message so the caller still sees the original warehouse diagnostics,
|
|
19
|
+
* and keeps the driver error as `cause`.
|
|
20
|
+
*/
|
|
21
|
+
export declare class KtxQueryError extends KtxExpectedError {
|
|
22
|
+
constructor(message: string, options?: ErrorOptions);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* True for the native JavaScript error types that signal a programming fault — a
|
|
26
|
+
* bug in ktx code rather than an operational outcome. These are universal
|
|
27
|
+
* language invariants (a `TypeError` never means "the warehouse rejected the
|
|
28
|
+
* query"), so callers can use this to keep genuine faults out of the
|
|
29
|
+
* expected-error classification and let them reach Error Tracking unchanged.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isNativeProgrammingFault(error: unknown): boolean;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marks an error as an expected operational outcome that ktx surfaces to its
|
|
3
|
+
* caller (a connected agent or the CLI user) rather than an unexpected ktx
|
|
4
|
+
* fault. Examples: invalid agent input, a warehouse rejecting a query, or a
|
|
5
|
+
* validation guard rejecting a request.
|
|
6
|
+
*
|
|
7
|
+
* `reportException` skips PostHog Error Tracking for these so the bug stream
|
|
8
|
+
* stays free of routine, caller-driven failures. The failure is still surfaced
|
|
9
|
+
* to the caller (as a tool-error result or CLI error) and still recorded by the
|
|
10
|
+
* outcome-tagged telemetry events, so no diagnostic signal is lost.
|
|
11
|
+
*/
|
|
12
|
+
export class KtxExpectedError extends Error {
|
|
13
|
+
constructor(message, options) {
|
|
14
|
+
super(message, options);
|
|
15
|
+
this.name = 'KtxExpectedError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A query was rejected at the warehouse/driver boundary — the warehouse refused
|
|
20
|
+
* to compile or run it, or a read-only guard rejected it. Reuses the underlying
|
|
21
|
+
* error's message so the caller still sees the original warehouse diagnostics,
|
|
22
|
+
* and keeps the driver error as `cause`.
|
|
23
|
+
*/
|
|
24
|
+
export class KtxQueryError extends KtxExpectedError {
|
|
25
|
+
constructor(message, options) {
|
|
26
|
+
super(message, options);
|
|
27
|
+
this.name = 'KtxQueryError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* True for the native JavaScript error types that signal a programming fault — a
|
|
32
|
+
* bug in ktx code rather than an operational outcome. These are universal
|
|
33
|
+
* language invariants (a `TypeError` never means "the warehouse rejected the
|
|
34
|
+
* query"), so callers can use this to keep genuine faults out of the
|
|
35
|
+
* expected-error classification and let them reach Error Tracking unchanged.
|
|
36
|
+
*/
|
|
37
|
+
export function isNativeProgrammingFault(error) {
|
|
38
|
+
return (error instanceof TypeError ||
|
|
39
|
+
error instanceof RangeError ||
|
|
40
|
+
error instanceof ReferenceError ||
|
|
41
|
+
error instanceof SyntaxError ||
|
|
42
|
+
error instanceof EvalError ||
|
|
43
|
+
error instanceof URIError);
|
|
44
|
+
}
|