@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/setup.test.js
CHANGED
|
@@ -8,7 +8,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
8
8
|
import { localFakeBundleReport, persistLocalBundleReport } from './ingest.test-utils.js';
|
|
9
9
|
import { contextBuildCommands, writeKtxSetupContextState } from './setup-context.js';
|
|
10
10
|
import { runDemoTour } from './setup-demo-tour.js';
|
|
11
|
-
import { formatKtxSetupStatus, readKtxSetupStatus, runKtxSetup } from './setup.js';
|
|
11
|
+
import { formatKtxSetupCompletionSummary, formatKtxSetupStatus, readKtxSetupStatus, runKtxSetup } from './setup.js';
|
|
12
12
|
vi.mock('./setup-demo-tour.js', () => ({
|
|
13
13
|
runDemoTour: vi.fn(async () => 0),
|
|
14
14
|
}));
|
|
@@ -33,6 +33,41 @@ function makeIo() {
|
|
|
33
33
|
stderr: () => stderr,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
function runtimeReady(projectDir) {
|
|
37
|
+
return { status: 'ready', projectDir, requirements: { features: ['core'], requirements: [] } };
|
|
38
|
+
}
|
|
39
|
+
async function writeReadyRuntime(rootDir, cliVersion = '0.2.0') {
|
|
40
|
+
const runtimeRoot = join(rootDir, '.runtime');
|
|
41
|
+
const versionDir = join(runtimeRoot, cliVersion);
|
|
42
|
+
const pythonPath = join(versionDir, '.venv', 'bin', 'python');
|
|
43
|
+
const daemonPath = join(versionDir, '.venv', 'bin', 'ktx-daemon');
|
|
44
|
+
await mkdir(join(versionDir, '.venv', 'bin'), { recursive: true });
|
|
45
|
+
await writeFile(pythonPath, '', 'utf-8');
|
|
46
|
+
await writeFile(daemonPath, '', 'utf-8');
|
|
47
|
+
await writeFile(join(versionDir, 'manifest.json'), `${JSON.stringify({
|
|
48
|
+
schemaVersion: 1,
|
|
49
|
+
cliVersion,
|
|
50
|
+
installedAt: '2026-05-09T10:02:00.000Z',
|
|
51
|
+
asset: {
|
|
52
|
+
schemaVersion: 1,
|
|
53
|
+
distributionName: 'kaelio-ktx',
|
|
54
|
+
normalizedName: 'kaelio_ktx',
|
|
55
|
+
version: '0.1.0',
|
|
56
|
+
wheel: {
|
|
57
|
+
file: 'kaelio_ktx-0.1.0-py3-none-any.whl',
|
|
58
|
+
sha256: '0'.repeat(64),
|
|
59
|
+
bytes: 0,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
features: ['core'],
|
|
63
|
+
python: {
|
|
64
|
+
executable: pythonPath,
|
|
65
|
+
daemonExecutable: daemonPath,
|
|
66
|
+
},
|
|
67
|
+
installLog: join(versionDir, 'install.log'),
|
|
68
|
+
}, null, 2)}\n`, 'utf-8');
|
|
69
|
+
return runtimeRoot;
|
|
70
|
+
}
|
|
36
71
|
describe('setup status', () => {
|
|
37
72
|
let tempDir;
|
|
38
73
|
beforeEach(async () => {
|
|
@@ -302,6 +337,98 @@ describe('setup status', () => {
|
|
|
302
337
|
expect(rendered).toContain('KTX context built: no');
|
|
303
338
|
expect(rendered).not.toContain('No KTX project found.');
|
|
304
339
|
});
|
|
340
|
+
it('formats a concise ready summary for completed agent setup', () => {
|
|
341
|
+
const rendered = formatKtxSetupCompletionSummary({
|
|
342
|
+
project: { path: tempDir, ready: true },
|
|
343
|
+
llm: { ready: true, model: 'sonnet' },
|
|
344
|
+
embeddings: { ready: true, model: 'text-embedding-3-small' },
|
|
345
|
+
databases: [{ connectionId: 'postgres-warehouse', ready: true }],
|
|
346
|
+
sources: [{ connectionId: 'dbt-main', type: 'dbt', ready: true }],
|
|
347
|
+
runtime: { required: true, ready: true, features: ['core'] },
|
|
348
|
+
context: { ready: true, status: 'completed' },
|
|
349
|
+
agents: [
|
|
350
|
+
{ target: 'claude-code', scope: 'project', ready: true },
|
|
351
|
+
{ target: 'claude-desktop', scope: 'global', ready: true },
|
|
352
|
+
],
|
|
353
|
+
}, {
|
|
354
|
+
agentNextActions: [
|
|
355
|
+
'1. Start MCP',
|
|
356
|
+
' Run this command before using Claude Code:',
|
|
357
|
+
'',
|
|
358
|
+
' RUN:',
|
|
359
|
+
` ktx mcp start --project-dir ${tempDir}`,
|
|
360
|
+
'',
|
|
361
|
+
' If you need to stop MCP later:',
|
|
362
|
+
` ktx mcp stop --project-dir ${tempDir}`,
|
|
363
|
+
'',
|
|
364
|
+
'2. Open Claude Code',
|
|
365
|
+
' Open Claude Code from the KTX project directory:',
|
|
366
|
+
'',
|
|
367
|
+
' RUN:',
|
|
368
|
+
` cd '${tempDir}'`,
|
|
369
|
+
' claude',
|
|
370
|
+
].join('\n'),
|
|
371
|
+
});
|
|
372
|
+
expect(rendered).toContain(`Project\n ${tempDir}`);
|
|
373
|
+
expect(rendered).toContain('Context\n built');
|
|
374
|
+
expect(rendered).toContain('Agents configured\n Claude Code, Claude Desktop');
|
|
375
|
+
expect(rendered).toContain('REQUIRED BEFORE USING AGENTS\n\n 1. Start MCP');
|
|
376
|
+
expect(rendered).toContain(' Run this command before using Claude Code:');
|
|
377
|
+
expect(rendered).toContain(' RUN:');
|
|
378
|
+
expect(rendered).toContain(' If you need to stop MCP later:');
|
|
379
|
+
expect(rendered).toContain(`ktx mcp stop --project-dir ${tempDir}`);
|
|
380
|
+
expect(rendered).toContain('After that, try\n Ask your agent: "Use KTX to show me the available tables."');
|
|
381
|
+
expect(rendered).not.toContain('Verify');
|
|
382
|
+
expect(rendered).not.toContain('Project ready: yes');
|
|
383
|
+
expect(rendered).not.toContain('What you can do next');
|
|
384
|
+
});
|
|
385
|
+
it('prints agent next actions inside the final ready summary during full setup', async () => {
|
|
386
|
+
const testIo = makeIo();
|
|
387
|
+
await expect(runKtxSetup({
|
|
388
|
+
command: 'run',
|
|
389
|
+
projectDir: tempDir,
|
|
390
|
+
mode: 'new',
|
|
391
|
+
agents: false,
|
|
392
|
+
target: 'claude-code',
|
|
393
|
+
skipAgents: false,
|
|
394
|
+
inputMode: 'disabled',
|
|
395
|
+
yes: true,
|
|
396
|
+
cliVersion: '0.2.0',
|
|
397
|
+
skipLlm: true,
|
|
398
|
+
skipEmbeddings: true,
|
|
399
|
+
skipDatabases: true,
|
|
400
|
+
skipSources: true,
|
|
401
|
+
databaseSchemas: [],
|
|
402
|
+
}, testIo.io, {
|
|
403
|
+
runtime: async () => runtimeReady(tempDir),
|
|
404
|
+
context: async () => {
|
|
405
|
+
await writeKtxSetupContextState(tempDir, {
|
|
406
|
+
runId: 'setup-context-local-test',
|
|
407
|
+
status: 'completed',
|
|
408
|
+
primarySourceConnectionIds: [],
|
|
409
|
+
contextSourceConnectionIds: [],
|
|
410
|
+
reportIds: [],
|
|
411
|
+
artifactPaths: [],
|
|
412
|
+
retryableFailedTargets: [],
|
|
413
|
+
commands: contextBuildCommands(tempDir, 'setup-context-local-test'),
|
|
414
|
+
});
|
|
415
|
+
await writeKtxSetupState(tempDir, { completed_steps: ['project', 'context'] });
|
|
416
|
+
return { status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' };
|
|
417
|
+
},
|
|
418
|
+
})).resolves.toBe(0);
|
|
419
|
+
const output = testIo.stdout();
|
|
420
|
+
expect(output).toContain('Claude Code · Project scope');
|
|
421
|
+
expect(output).toContain(join(tempDir, '.mcp.json'));
|
|
422
|
+
expect(output).toContain('Requires MCP to be started.');
|
|
423
|
+
expect(output).toContain('Analytics skill installed.');
|
|
424
|
+
expect(output).not.toContain('Agent integration complete');
|
|
425
|
+
expect(output).toContain('Finish KTX agent setup');
|
|
426
|
+
expect(output).not.toContain('KTX project ready');
|
|
427
|
+
expect(output).toContain('REQUIRED BEFORE USING AGENTS');
|
|
428
|
+
expect(output).toContain('Run this command before using Claude Code:');
|
|
429
|
+
expect(output).toContain(`ktx mcp start --project-dir ${tempDir}`);
|
|
430
|
+
expect(output).not.toContain('Finish agent setup');
|
|
431
|
+
});
|
|
305
432
|
it('prints the setup shell intro for auto-created run mode', async () => {
|
|
306
433
|
const testIo = makeIo();
|
|
307
434
|
await expect(runKtxSetup({
|
|
@@ -805,7 +932,7 @@ describe('setup status', () => {
|
|
|
805
932
|
runtimeInstallPolicy: 'never',
|
|
806
933
|
}), io.io);
|
|
807
934
|
});
|
|
808
|
-
it('
|
|
935
|
+
it('prompts before installing the managed runtime by default during setup', async () => {
|
|
809
936
|
const io = makeIo();
|
|
810
937
|
const embeddings = vi.fn(async () => ({ status: 'ready', projectDir: tempDir }));
|
|
811
938
|
const context = vi.fn(async () => ({ status: 'failed', projectDir: tempDir }));
|
|
@@ -830,11 +957,11 @@ describe('setup status', () => {
|
|
|
830
957
|
})).resolves.toBe(1);
|
|
831
958
|
expect(embeddings).toHaveBeenCalledWith(expect.objectContaining({
|
|
832
959
|
cliVersion: '0.2.0',
|
|
833
|
-
runtimeInstallPolicy: '
|
|
960
|
+
runtimeInstallPolicy: 'prompt',
|
|
834
961
|
}), io.io);
|
|
835
962
|
expect(context).toHaveBeenCalledWith(expect.objectContaining({
|
|
836
963
|
cliVersion: '0.2.0',
|
|
837
|
-
runtimeInstallPolicy: '
|
|
964
|
+
runtimeInstallPolicy: 'prompt',
|
|
838
965
|
}), io.io);
|
|
839
966
|
});
|
|
840
967
|
it('lets Back from embedding setup return to the model step instead of exiting', async () => {
|
|
@@ -1164,6 +1291,10 @@ describe('setup status', () => {
|
|
|
1164
1291
|
calls.push('sources');
|
|
1165
1292
|
return { status: 'skipped', projectDir: tempDir };
|
|
1166
1293
|
},
|
|
1294
|
+
runtime: async () => {
|
|
1295
|
+
calls.push('runtime');
|
|
1296
|
+
return runtimeReady(tempDir);
|
|
1297
|
+
},
|
|
1167
1298
|
context: async () => {
|
|
1168
1299
|
calls.push('context');
|
|
1169
1300
|
return { status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' };
|
|
@@ -1177,7 +1308,7 @@ describe('setup status', () => {
|
|
|
1177
1308
|
};
|
|
1178
1309
|
},
|
|
1179
1310
|
})).resolves.toBe(0);
|
|
1180
|
-
expect(calls).toEqual(['model', 'embeddings', 'databases', 'sources', 'context', 'agents']);
|
|
1311
|
+
expect(calls).toEqual(['model', 'embeddings', 'databases', 'sources', 'runtime', 'context', 'agents']);
|
|
1181
1312
|
});
|
|
1182
1313
|
it('commits setup config changes written by later setup steps', async () => {
|
|
1183
1314
|
const io = makeIo();
|
|
@@ -1205,6 +1336,7 @@ describe('setup status', () => {
|
|
|
1205
1336
|
return { status: 'skipped', projectDir: tempDir };
|
|
1206
1337
|
},
|
|
1207
1338
|
sources: async () => ({ status: 'skipped', projectDir: tempDir }),
|
|
1339
|
+
runtime: async () => runtimeReady(tempDir),
|
|
1208
1340
|
context: async () => ({ status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' }),
|
|
1209
1341
|
agents: async () => ({
|
|
1210
1342
|
status: 'ready',
|
|
@@ -1217,7 +1349,7 @@ describe('setup status', () => {
|
|
|
1217
1349
|
const committedConfig = await execFileAsync('git', ['-C', tempDir, 'show', 'HEAD:ktx.yaml']);
|
|
1218
1350
|
expect(committedConfig.stdout).toContain('warehouse:');
|
|
1219
1351
|
});
|
|
1220
|
-
it('runs agent setup
|
|
1352
|
+
it('runs agent setup without runtime or context in --agents mode', async () => {
|
|
1221
1353
|
const calls = [];
|
|
1222
1354
|
const io = makeIo();
|
|
1223
1355
|
await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8');
|
|
@@ -1242,9 +1374,13 @@ describe('setup status', () => {
|
|
|
1242
1374
|
embeddings: async () => ({ status: 'skipped', projectDir: tempDir }),
|
|
1243
1375
|
databases: async () => ({ status: 'skipped', projectDir: tempDir }),
|
|
1244
1376
|
sources: async () => ({ status: 'skipped', projectDir: tempDir }),
|
|
1377
|
+
runtime: async () => {
|
|
1378
|
+
calls.push('runtime');
|
|
1379
|
+
throw new Error('runtime should not run');
|
|
1380
|
+
},
|
|
1245
1381
|
context: async () => {
|
|
1246
1382
|
calls.push('context');
|
|
1247
|
-
|
|
1383
|
+
throw new Error('context should not run');
|
|
1248
1384
|
},
|
|
1249
1385
|
agents: async () => {
|
|
1250
1386
|
calls.push('agents');
|
|
@@ -1255,10 +1391,12 @@ describe('setup status', () => {
|
|
|
1255
1391
|
};
|
|
1256
1392
|
},
|
|
1257
1393
|
})).resolves.toBe(0);
|
|
1258
|
-
expect(calls).toEqual(['
|
|
1394
|
+
expect(calls).toEqual(['agents']);
|
|
1259
1395
|
});
|
|
1260
|
-
it('
|
|
1396
|
+
it('installs agents when non-interactive --agents finds context incomplete', async () => {
|
|
1261
1397
|
const io = makeIo();
|
|
1398
|
+
const runtime = vi.fn(async () => runtimeReady(tempDir));
|
|
1399
|
+
const context = vi.fn(async () => ({ status: 'skipped', projectDir: tempDir }));
|
|
1262
1400
|
const agents = vi.fn(async () => ({
|
|
1263
1401
|
status: 'ready',
|
|
1264
1402
|
projectDir: tempDir,
|
|
@@ -1282,11 +1420,14 @@ describe('setup status', () => {
|
|
|
1282
1420
|
skipAgents: false,
|
|
1283
1421
|
databaseSchemas: [],
|
|
1284
1422
|
}, io.io, {
|
|
1285
|
-
|
|
1423
|
+
runtime,
|
|
1424
|
+
context,
|
|
1286
1425
|
agents,
|
|
1287
|
-
})).resolves.toBe(
|
|
1288
|
-
expect(
|
|
1289
|
-
expect(
|
|
1426
|
+
})).resolves.toBe(0);
|
|
1427
|
+
expect(runtime).not.toHaveBeenCalled();
|
|
1428
|
+
expect(context).not.toHaveBeenCalled();
|
|
1429
|
+
expect(agents).toHaveBeenCalledTimes(1);
|
|
1430
|
+
expect(io.stderr()).not.toContain('KTX context is not ready for agents.');
|
|
1290
1431
|
});
|
|
1291
1432
|
it('routes a ready project menu selection to agent setup', async () => {
|
|
1292
1433
|
const calls = [];
|
|
@@ -1309,7 +1450,7 @@ describe('setup status', () => {
|
|
|
1309
1450
|
'',
|
|
1310
1451
|
].join('\n'), 'utf-8');
|
|
1311
1452
|
await writeKtxSetupState(tempDir, {
|
|
1312
|
-
completed_steps: ['project', 'llm', 'embeddings', 'sources', 'context', 'agents'],
|
|
1453
|
+
completed_steps: ['project', 'llm', 'embeddings', 'sources', 'runtime', 'context', 'agents'],
|
|
1313
1454
|
});
|
|
1314
1455
|
await writeFile(join(tempDir, '.ktx/agents/install-manifest.json'), JSON.stringify({
|
|
1315
1456
|
version: 1,
|
|
@@ -1331,47 +1472,63 @@ describe('setup status', () => {
|
|
|
1331
1472
|
retryableFailedTargets: [],
|
|
1332
1473
|
commands: contextBuildCommands(tempDir, 'setup-context-local-ready'),
|
|
1333
1474
|
});
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1475
|
+
const previousRuntimeRoot = process.env.KTX_RUNTIME_ROOT;
|
|
1476
|
+
process.env.KTX_RUNTIME_ROOT = await writeReadyRuntime(tempDir);
|
|
1477
|
+
try {
|
|
1478
|
+
await expect(runKtxSetup({
|
|
1479
|
+
command: 'run',
|
|
1480
|
+
projectDir: tempDir,
|
|
1481
|
+
mode: 'existing',
|
|
1482
|
+
agents: false,
|
|
1483
|
+
inputMode: 'auto',
|
|
1484
|
+
yes: false,
|
|
1485
|
+
cliVersion: '0.2.0',
|
|
1486
|
+
skipLlm: false,
|
|
1487
|
+
skipEmbeddings: false,
|
|
1488
|
+
skipDatabases: false,
|
|
1489
|
+
skipSources: false,
|
|
1490
|
+
skipAgents: false,
|
|
1491
|
+
databaseSchemas: [],
|
|
1492
|
+
}, io.io, {
|
|
1493
|
+
readyMenuDeps: { prompts: { select: vi.fn(async () => 'agents'), cancel: vi.fn() } },
|
|
1494
|
+
model: async (args) => {
|
|
1495
|
+
expect(args.skipLlm).toBe(true);
|
|
1496
|
+
return { status: 'skipped', projectDir: tempDir };
|
|
1497
|
+
},
|
|
1498
|
+
embeddings: async (args) => {
|
|
1499
|
+
expect(args.skipEmbeddings).toBe(true);
|
|
1500
|
+
return { status: 'skipped', projectDir: tempDir };
|
|
1501
|
+
},
|
|
1502
|
+
databases: async (args) => {
|
|
1503
|
+
expect(args.skipDatabases).toBe(true);
|
|
1504
|
+
return { status: 'skipped', projectDir: tempDir };
|
|
1505
|
+
},
|
|
1506
|
+
sources: async (args) => {
|
|
1507
|
+
expect(args.skipSources).toBe(true);
|
|
1508
|
+
return { status: 'skipped', projectDir: tempDir };
|
|
1509
|
+
},
|
|
1510
|
+
runtime: async () => {
|
|
1511
|
+
calls.push('runtime');
|
|
1512
|
+
return runtimeReady(tempDir);
|
|
1513
|
+
},
|
|
1514
|
+
agents: async () => {
|
|
1515
|
+
calls.push('agents');
|
|
1516
|
+
return {
|
|
1517
|
+
status: 'ready',
|
|
1518
|
+
projectDir: tempDir,
|
|
1519
|
+
installs: [{ target: 'codex', scope: 'project', mode: 'mcp-cli' }],
|
|
1520
|
+
};
|
|
1521
|
+
},
|
|
1522
|
+
})).resolves.toBe(0);
|
|
1523
|
+
}
|
|
1524
|
+
finally {
|
|
1525
|
+
if (previousRuntimeRoot === undefined) {
|
|
1526
|
+
delete process.env.KTX_RUNTIME_ROOT;
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
process.env.KTX_RUNTIME_ROOT = previousRuntimeRoot;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1375
1532
|
expect(calls).toEqual(['agents']);
|
|
1376
1533
|
});
|
|
1377
1534
|
it('skips to agent setup when context is ready but agents are not configured', async () => {
|
|
@@ -1442,6 +1599,10 @@ describe('setup status', () => {
|
|
|
1442
1599
|
expect(args.skipSources).toBe(true);
|
|
1443
1600
|
return { status: 'skipped', projectDir: tempDir };
|
|
1444
1601
|
},
|
|
1602
|
+
runtime: async () => {
|
|
1603
|
+
calls.push('runtime');
|
|
1604
|
+
return runtimeReady(tempDir);
|
|
1605
|
+
},
|
|
1445
1606
|
agents: async () => {
|
|
1446
1607
|
calls.push('agents');
|
|
1447
1608
|
return {
|
|
@@ -1454,8 +1615,9 @@ describe('setup status', () => {
|
|
|
1454
1615
|
expect(readyMenuSelect).not.toHaveBeenCalled();
|
|
1455
1616
|
expect(calls).toEqual(['agents']);
|
|
1456
1617
|
});
|
|
1457
|
-
it('runs only project resolution
|
|
1618
|
+
it('runs only project resolution and agent setup in --agents mode', async () => {
|
|
1458
1619
|
const io = makeIo();
|
|
1620
|
+
const runtime = vi.fn(async () => runtimeReady(tempDir));
|
|
1459
1621
|
const context = vi.fn(async () => ({ status: 'ready', projectDir: tempDir, runId: 'setup-context-local-test' }));
|
|
1460
1622
|
const agents = vi.fn(async () => ({
|
|
1461
1623
|
status: 'ready',
|
|
@@ -1482,10 +1644,12 @@ describe('setup status', () => {
|
|
|
1482
1644
|
model: async () => {
|
|
1483
1645
|
throw new Error('model should not run');
|
|
1484
1646
|
},
|
|
1647
|
+
runtime,
|
|
1485
1648
|
context,
|
|
1486
1649
|
agents,
|
|
1487
1650
|
})).resolves.toBe(0);
|
|
1488
|
-
expect(
|
|
1651
|
+
expect(runtime).not.toHaveBeenCalled();
|
|
1652
|
+
expect(context).not.toHaveBeenCalled();
|
|
1489
1653
|
expect(agents).toHaveBeenCalledTimes(1);
|
|
1490
1654
|
});
|
|
1491
1655
|
it('does not run embedding setup when the model step fails', async () => {
|
package/dist/sl.js
CHANGED
|
@@ -15,7 +15,7 @@ function slSearchEmbeddingService(project, deps) {
|
|
|
15
15
|
}
|
|
16
16
|
async function printSlSources(input) {
|
|
17
17
|
const { resolveOutputMode } = await import('./io/mode.js');
|
|
18
|
-
const { printList } = await import('./io/print-list.js');
|
|
18
|
+
const { createRankBadgeFormatter, printList } = await import('./io/print-list.js');
|
|
19
19
|
const mode = resolveOutputMode({ explicit: input.output, json: input.json, io: input.io });
|
|
20
20
|
if (input.command === 'sl search') {
|
|
21
21
|
const searchColumns = [
|
|
@@ -24,7 +24,7 @@ async function printSlSources(input) {
|
|
|
24
24
|
label: 'SCORE',
|
|
25
25
|
plain: 'score=',
|
|
26
26
|
role: 'badge',
|
|
27
|
-
prettyFormat: (
|
|
27
|
+
prettyFormat: createRankBadgeFormatter(input.rows),
|
|
28
28
|
dim: true,
|
|
29
29
|
},
|
|
30
30
|
{ key: 'connectionId', label: 'CONNECTION', plain: '' },
|
package/dist/sl.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
4
5
|
import Database from 'better-sqlite3';
|
|
5
6
|
import { initKtxProject } from '@ktx/context/project';
|
|
6
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
@@ -72,6 +73,15 @@ describe('runKtxSl', () => {
|
|
|
72
73
|
meta: { command: 'sl search' },
|
|
73
74
|
});
|
|
74
75
|
});
|
|
76
|
+
it('prints semantic-layer search rank badges in pretty output', async () => {
|
|
77
|
+
const projectDir = join(tempDir, 'rank-project');
|
|
78
|
+
await seedSlSource({ projectDir });
|
|
79
|
+
const searchIo = makeIo();
|
|
80
|
+
await expect(runKtxSl({ command: 'search', projectDir, connectionId: 'warehouse', query: 'order', output: 'pretty' }, searchIo.io)).resolves.toBe(0);
|
|
81
|
+
const stdout = stripVTControlCharacters(searchIo.stdout());
|
|
82
|
+
expect(stdout).toMatch(/#1\s+orders/);
|
|
83
|
+
expect(stdout).not.toContain('%');
|
|
84
|
+
});
|
|
75
85
|
it('prints semantic-layer list and search as public JSON envelopes', async () => {
|
|
76
86
|
const projectDir = join(tempDir, 'project');
|
|
77
87
|
await seedSlSource({
|
package/dist/source-mapping.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { localConnectionToWarehouseDescriptor } from '@ktx/context/connections';
|
|
2
|
-
import { DEFAULT_METABASE_CLIENT_CONFIG, DefaultLookerConnectionClientFactory, DefaultMetabaseConnectionClientFactory, KtxYamlMetabaseSourceStateReader, LocalLookerRuntimeStore, LocalMetabaseDiscoveryCache, computeLookerMappingDrift, computeMetabaseMappingDrift, discoverLookerConnections, discoverMetabaseDatabases, lookerCredentialsFromLocalConnection, metabaseRuntimeConfigFromLocalConnection, seedLocalMappingStateFromKtxYaml, validateLookerMappings, validateMappingPhysicalMatch, } from '@ktx/context/ingest';
|
|
2
|
+
import { DEFAULT_METABASE_CLIENT_CONFIG, DefaultLookerConnectionClientFactory, DefaultMetabaseConnectionClientFactory, KtxYamlMetabaseSourceStateReader, LocalLookerRuntimeStore, LocalMetabaseDiscoveryCache, computeLookerMappingDrift, computeMetabaseMappingDrift, discoverLookerConnections, discoverMetabaseDatabases, lookerCredentialsFromLocalConnection, metabaseRuntimeConfigFromLocalConnection, planMetabaseFanoutChildren, seedLocalMappingStateFromKtxYaml, validateLookerMappings, validateMappingPhysicalMatch, } from '@ktx/context/ingest';
|
|
3
3
|
import { ktxLocalStateDbPath, loadKtxProject } from '@ktx/context/project';
|
|
4
4
|
import { profileMark } from './startup-profile.js';
|
|
5
5
|
profileMark('module:source-mapping');
|
|
@@ -126,6 +126,14 @@ export async function runKtxSourceMapping(args, io = process, deps = {}) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
const rows = await store.listDatabaseMappings(args.connectionId);
|
|
129
|
+
planMetabaseFanoutChildren({
|
|
130
|
+
metabaseConnectionId: args.connectionId,
|
|
131
|
+
mappings: rows.map((row) => ({
|
|
132
|
+
metabaseDatabaseId: row.metabaseDatabaseId,
|
|
133
|
+
targetConnectionId: row.targetConnectionId,
|
|
134
|
+
syncEnabled: row.syncEnabled,
|
|
135
|
+
})),
|
|
136
|
+
});
|
|
129
137
|
const failures = rows.flatMap((row) => {
|
|
130
138
|
if (!row.targetConnectionId) {
|
|
131
139
|
return [];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { runKtxSourceMapping } from './source-mapping.js';
|
|
6
|
+
function makeIo() {
|
|
7
|
+
let stdout = '';
|
|
8
|
+
let stderr = '';
|
|
9
|
+
return {
|
|
10
|
+
io: {
|
|
11
|
+
stdout: {
|
|
12
|
+
write: (chunk) => {
|
|
13
|
+
stdout += chunk;
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
stderr: {
|
|
17
|
+
write: (chunk) => {
|
|
18
|
+
stderr += chunk;
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
stdout: () => stdout,
|
|
23
|
+
stderr: () => stderr,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
describe('source mapping commands', () => {
|
|
27
|
+
let tempDir;
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
tempDir = await mkdtemp(join(tmpdir(), 'ktx-source-mapping-'));
|
|
30
|
+
});
|
|
31
|
+
afterEach(async () => {
|
|
32
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
33
|
+
});
|
|
34
|
+
async function writeConfig(metabaseMappings) {
|
|
35
|
+
await writeFile(join(tempDir, 'ktx.yaml'), [
|
|
36
|
+
'connections:',
|
|
37
|
+
' warehouse:',
|
|
38
|
+
' driver: postgres',
|
|
39
|
+
' url: env:DATABASE_URL',
|
|
40
|
+
' metabase:',
|
|
41
|
+
' driver: metabase',
|
|
42
|
+
' api_url: https://metabase.example.com',
|
|
43
|
+
...metabaseMappings,
|
|
44
|
+
'',
|
|
45
|
+
].join('\n'), 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
it('fails Metabase validation when no sync-enabled target mapping exists', async () => {
|
|
48
|
+
await writeConfig([]);
|
|
49
|
+
const io = makeIo();
|
|
50
|
+
await expect(runKtxSourceMapping({ command: 'validate', projectDir: tempDir, connectionId: 'metabase' }, io.io)).resolves.toBe(1);
|
|
51
|
+
expect(io.stderr()).toContain('no sync-enabled mappings with a target connection for Metabase connection metabase');
|
|
52
|
+
});
|
|
53
|
+
it('passes Metabase validation when a sync-enabled target mapping exists', async () => {
|
|
54
|
+
await writeConfig([
|
|
55
|
+
' mappings:',
|
|
56
|
+
' databaseMappings:',
|
|
57
|
+
' "3": warehouse',
|
|
58
|
+
' syncEnabled:',
|
|
59
|
+
' "3": true',
|
|
60
|
+
]);
|
|
61
|
+
const io = makeIo();
|
|
62
|
+
await expect(runKtxSourceMapping({ command: 'validate', projectDir: tempDir, connectionId: 'metabase' }, io.io)).resolves.toBe(0);
|
|
63
|
+
expect(io.stdout()).toContain('Mapping validation passed: metabase');
|
|
64
|
+
});
|
|
65
|
+
});
|
package/dist/sql.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { loadKtxProject } from '@ktx/context/project';
|
|
2
|
+
import type { SqlAnalysisPort } from '@ktx/context/sql-analysis';
|
|
3
|
+
import type { KtxCliIo } from './cli-runtime.js';
|
|
4
|
+
import { createKtxCliScanConnector } from './local-scan-connectors.js';
|
|
5
|
+
type KtxSqlOutputMode = 'pretty' | 'plain' | 'json';
|
|
6
|
+
export type KtxSqlArgs = {
|
|
7
|
+
command: 'execute';
|
|
8
|
+
projectDir: string;
|
|
9
|
+
connectionId: string;
|
|
10
|
+
sql: string;
|
|
11
|
+
maxRows: number;
|
|
12
|
+
output?: KtxSqlOutputMode;
|
|
13
|
+
json?: boolean;
|
|
14
|
+
cliVersion: string;
|
|
15
|
+
};
|
|
16
|
+
export interface KtxSqlDeps {
|
|
17
|
+
loadProject?: typeof loadKtxProject;
|
|
18
|
+
createSqlAnalysis?: () => SqlAnalysisPort;
|
|
19
|
+
createScanConnector?: typeof createKtxCliScanConnector;
|
|
20
|
+
}
|
|
21
|
+
export declare function runKtxSql(args: KtxSqlArgs, io?: KtxCliIo, deps?: KtxSqlDeps): Promise<number>;
|
|
22
|
+
export {};
|