@kaelio/ktx 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/python/kaelio_ktx-0.13.0-py3-none-any.whl +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/admin.js +1 -1
- package/dist/clack.d.ts +16 -0
- package/dist/clack.js +37 -6
- package/dist/claude-code-prompt-caching.js +1 -1
- package/dist/cli-program.js +3 -3
- package/dist/cli-runtime.js +2 -2
- package/dist/commands/connection-commands.js +1 -1
- package/dist/commands/ingest-commands.js +4 -4
- package/dist/commands/mcp-commands.js +12 -12
- package/dist/commands/runtime-commands.js +4 -4
- package/dist/commands/setup-commands.js +19 -5
- package/dist/commands/sl-commands.js +1 -1
- package/dist/commands/sql-commands.js +1 -1
- package/dist/commands/status-commands.js +1 -1
- package/dist/connection.js +15 -3
- package/dist/connectors/bigquery/connector.js +1 -14
- package/dist/connectors/clickhouse/connector.js +2 -16
- package/dist/connectors/duckdb/federated-attach.d.ts +7 -0
- package/dist/connectors/duckdb/federated-attach.js +86 -0
- package/dist/connectors/duckdb/federated-executor.d.ts +5 -0
- package/dist/connectors/duckdb/federated-executor.js +59 -0
- package/dist/connectors/mysql/connector.js +2 -16
- package/dist/connectors/postgres/connector.js +1 -14
- package/dist/connectors/shared/string-reference.d.ts +6 -0
- package/dist/connectors/shared/string-reference.js +19 -0
- package/dist/connectors/snowflake/connector.d.ts +1 -1
- package/dist/connectors/snowflake/connector.js +1 -14
- package/dist/connectors/sqlite/connector.js +2 -25
- package/dist/connectors/sqlserver/connector.js +4 -17
- package/dist/context/connections/connection-type.d.ts +1 -1
- package/dist/context/connections/federation.d.ts +33 -0
- package/dist/context/connections/federation.js +51 -0
- package/dist/context/connections/local-warehouse-descriptor.d.ts +2 -0
- package/dist/context/connections/project-sql-executor.d.ts +18 -0
- package/dist/context/connections/project-sql-executor.js +39 -0
- package/dist/context/connections/query-executor.d.ts +2 -2
- package/dist/context/connections/read-only-sql.d.ts +1 -0
- package/dist/context/connections/read-only-sql.js +119 -4
- package/dist/context/connections/resolve-connection.d.ts +12 -0
- package/dist/context/connections/resolve-connection.js +37 -0
- package/dist/context/core/git-env.d.ts +4 -0
- package/dist/context/core/git-env.js +5 -1
- package/dist/context/core/git.service.d.ts +23 -0
- package/dist/context/core/git.service.js +71 -8
- package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
- package/dist/context/ingest/adapters/live-database/manifest.d.ts +3 -0
- package/dist/context/ingest/adapters/live-database/manifest.js +19 -11
- package/dist/context/ingest/adapters/looker/client.js +7 -2
- package/dist/context/ingest/adapters/looker/factory.d.ts +8 -1
- package/dist/context/ingest/adapters/looker/factory.js +9 -0
- package/dist/context/ingest/adapters/looker/mapping.js +1 -1
- package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.js +1 -1
- package/dist/context/ingest/adapters/metabase/local-metabase.adapter.js +1 -1
- package/dist/context/ingest/adapters/metabase/mapping.js +6 -6
- package/dist/context/ingest/artifact-gates.d.ts +2 -6
- package/dist/context/ingest/artifact-gates.js +5 -47
- package/dist/context/ingest/constrained-repair.d.ts +55 -0
- package/dist/context/ingest/constrained-repair.js +167 -0
- package/dist/context/ingest/final-gate-repair.d.ts +9 -11
- package/dist/context/ingest/final-gate-repair.js +40 -128
- package/dist/context/ingest/finalization-scope.d.ts +1 -1
- package/dist/context/ingest/finalization-scope.js +15 -15
- package/dist/context/ingest/ingest-bundle.runner.d.ts +1 -0
- package/dist/context/ingest/ingest-bundle.runner.js +101 -67
- package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +6 -13
- package/dist/context/ingest/isolated-diff/patch-integrator.js +32 -109
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +8 -9
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +63 -141
- package/dist/context/ingest/local-bundle-runtime.d.ts +2 -0
- package/dist/context/ingest/local-bundle-runtime.js +9 -10
- package/dist/context/ingest/local-ingest.d.ts +2 -0
- package/dist/context/ingest/local-ingest.js +2 -0
- package/dist/context/ingest/memory-flow/view-model.js +1 -1
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -6
- package/dist/context/ingest/stages/stage-3-work-units.js +2 -1
- package/dist/context/ingest/stages/validate-wu-sources.d.ts +7 -1
- package/dist/context/ingest/stages/validate-wu-sources.js +109 -4
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.d.ts +2 -0
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.js +1 -1
- package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +3 -3
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.d.ts +3 -1
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +15 -1
- package/dist/context/llm/ai-sdk-runtime.js +2 -2
- package/dist/context/llm/claude-code-runtime.js +19 -3
- package/dist/context/llm/local-config.js +1 -1
- package/dist/context/llm/runtime-tools.js +2 -2
- package/dist/context/mcp/context-tools.js +33 -8
- package/dist/context/mcp/local-project-ports.js +63 -89
- package/dist/context/mcp/types.d.ts +2 -0
- package/dist/context/memory/local-memory.js +4 -1
- package/dist/context/memory/memory-agent.service.js +1 -1
- package/dist/context/project/config.d.ts +11 -4
- package/dist/context/project/config.js +85 -30
- package/dist/context/project/driver-schemas.js +1 -1
- package/dist/context/project/mappings-yaml-schema.js +2 -2
- package/dist/context/project/project.js +12 -4
- package/dist/context/scan/description-generation.js +4 -4
- package/dist/context/scan/local-enrichment-artifacts.js +33 -4
- package/dist/context/scan/local-scan.js +2 -2
- package/dist/context/scan/local-structural-artifacts.js +5 -5
- package/dist/context/scan/relationship-benchmark-report.js +1 -1
- package/dist/context/scan/relationship-discovery.js +3 -3
- package/dist/context/scan/relationship-llm-proposal.js +3 -3
- package/dist/context/sl/local-query.js +31 -44
- package/dist/context/sl/local-sl.d.ts +0 -8
- package/dist/context/sl/local-sl.js +71 -70
- package/dist/context/sl/semantic-layer.service.d.ts +25 -8
- package/dist/context/sl/semantic-layer.service.js +109 -56
- package/dist/context/sl/source-files.d.ts +48 -0
- package/dist/context/sl/source-files.js +138 -0
- package/dist/context/sl/tools/base-semantic-layer.tool.d.ts +2 -2
- package/dist/context/sl/tools/base-semantic-layer.tool.js +2 -7
- package/dist/context/sl/tools/sl-edit-source.tool.js +10 -8
- package/dist/context/sl/tools/sl-warehouse-validation.js +55 -27
- package/dist/context/sl/tools/sl-write-source.tool.js +12 -9
- package/dist/context/sql-analysis/dialect.d.ts +2 -0
- package/dist/context/sql-analysis/dialect.js +20 -0
- package/dist/context/tools/base-tool.d.ts +6 -19
- package/dist/context/tools/base-tool.js +0 -14
- package/dist/context-build-view.js +5 -5
- package/dist/database-tree-picker.js +18 -3
- package/dist/demo-assets.js +0 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +31 -23
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +44 -0
- package/dist/ingest-query-executor.d.ts +2 -0
- package/dist/ingest-query-executor.js +8 -22
- package/dist/ingest.d.ts +1 -1
- package/dist/ingest.js +8 -2
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/io/tty.d.ts +8 -0
- package/dist/io/tty.js +16 -0
- package/dist/llm/embedding-health.js +1 -1
- package/dist/llm/embedding-provider.js +3 -3
- package/dist/llm/model-provider.js +1 -1
- package/dist/local-adapters.d.ts +1 -0
- package/dist/local-adapters.js +2 -2
- package/dist/local-scan-connectors.js +1 -1
- package/dist/managed-local-embeddings.js +17 -8
- package/dist/managed-mcp-daemon.js +3 -3
- package/dist/managed-python-command.d.ts +7 -0
- package/dist/managed-python-command.js +34 -8
- package/dist/managed-python-daemon.js +2 -2
- package/dist/managed-python-http.js +3 -3
- package/dist/managed-python-runtime.d.ts +30 -1
- package/dist/managed-python-runtime.js +134 -18
- package/dist/managed-uv-release.d.ts +7 -0
- package/dist/managed-uv-release.js +11 -0
- package/dist/mcp-http-server.js +4 -4
- package/dist/mcp-server-factory.js +3 -3
- package/dist/mcp-stdio-server.js +1 -1
- package/dist/memory-flow-hud.js +2 -2
- package/dist/next-steps.js +2 -2
- package/dist/prompt-navigation.d.ts +17 -0
- package/dist/prompt-navigation.js +49 -3
- package/dist/prompts/memory_agent_bundle_ingest_work_unit.md +2 -2
- package/dist/prompts/memory_agent_external_ingest.md +2 -2
- package/dist/public-ingest-copy.js +1 -1
- package/dist/public-ingest.js +3 -3
- package/dist/release-version.js +1 -1
- package/dist/runtime-requirements.js +1 -1
- package/dist/runtime.js +9 -9
- package/dist/scan.js +1 -1
- package/dist/setup-agents.d.ts +21 -15
- package/dist/setup-agents.js +143 -66
- package/dist/setup-banner.d.ts +20 -0
- package/dist/setup-banner.js +39 -0
- package/dist/setup-context.js +24 -15
- package/dist/setup-databases.d.ts +3 -0
- package/dist/setup-databases.js +47 -59
- package/dist/setup-demo-tour.js +12 -8
- package/dist/setup-embeddings.js +9 -9
- package/dist/setup-interrupt.js +1 -1
- package/dist/setup-models.d.ts +4 -1
- package/dist/setup-models.js +54 -28
- package/dist/setup-project.js +29 -5
- package/dist/setup-prompts.js +16 -1
- package/dist/setup-ready-menu.js +1 -1
- package/dist/setup-sources.js +28 -12
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +14 -13
- package/dist/skills/analytics/SKILL.md +3 -3
- package/dist/skills/dbt_ingest/SKILL.md +3 -3
- package/dist/skills/looker_ingest/SKILL.md +3 -3
- package/dist/skills/lookml_ingest/SKILL.md +7 -7
- package/dist/skills/metabase_ingest/SKILL.md +4 -4
- package/dist/skills/metricflow_ingest/SKILL.md +15 -15
- package/dist/skills/notion_synthesize/SKILL.md +1 -1
- package/dist/skills/sl/SKILL.md +3 -3
- package/dist/skills/sl_capture/SKILL.md +1 -1
- package/dist/skills/wiki_capture/SKILL.md +1 -1
- package/dist/source-mapping.js +1 -1
- package/dist/sql.d.ts +2 -0
- package/dist/sql.js +35 -53
- package/dist/startup-profile.js +1 -1
- package/dist/status-project.d.ts +0 -2
- package/dist/status-project.js +4 -6
- package/dist/telemetry/events.d.ts +3 -2
- package/dist/telemetry/events.js +11 -1
- package/dist/telemetry/exception.js +14 -0
- package/dist/text-ingest.js +1 -1
- package/dist/tree-picker-tui.d.ts +0 -1
- package/dist/tree-picker-tui.js +2 -3
- package/package.json +2 -1
- package/assets/python/kaelio_ktx-0.11.0-py3-none-any.whl +0 -0
|
@@ -12,7 +12,7 @@ function normalizedBaseUrl(baseUrl) {
|
|
|
12
12
|
function parseJsonObject(raw, path) {
|
|
13
13
|
const parsed = JSON.parse(raw);
|
|
14
14
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
15
|
-
throw new Error(`
|
|
15
|
+
throw new Error(`ktx daemon HTTP ${path} returned non-object JSON`);
|
|
16
16
|
}
|
|
17
17
|
return parsed;
|
|
18
18
|
}
|
|
@@ -35,7 +35,7 @@ async function postManagedDaemonJson(baseUrl, path, payload) {
|
|
|
35
35
|
const text = Buffer.concat(chunks).toString('utf8');
|
|
36
36
|
const statusCode = response.statusCode ?? 0;
|
|
37
37
|
if (statusCode < 200 || statusCode >= 300) {
|
|
38
|
-
reject(new Error(`
|
|
38
|
+
reject(new Error(`ktx daemon HTTP ${path} failed with ${statusCode}: ${text}`));
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
try {
|
|
@@ -72,7 +72,7 @@ export function createManagedPythonDaemonBaseUrlResolver(options) {
|
|
|
72
72
|
force: false,
|
|
73
73
|
});
|
|
74
74
|
const verb = daemon.status === 'started' ? 'Started' : 'Using existing';
|
|
75
|
-
writePrefixedLines((chunk) => options.io.stderr.write(chunk), `${verb}
|
|
75
|
+
writePrefixedLines((chunk) => options.io.stderr.write(chunk), `${verb} ktx daemon: ${daemon.baseUrl}`);
|
|
76
76
|
cachedBaseUrl = daemon.baseUrl;
|
|
77
77
|
return cachedBaseUrl;
|
|
78
78
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { type ManagedUvArtifact, type ManagedUvPlatformKey } from './managed-uv-release.js';
|
|
2
3
|
export declare const runtimeFeatureSchema: z.ZodEnum<{
|
|
3
4
|
core: "core";
|
|
4
5
|
"local-embeddings": "local-embeddings";
|
|
@@ -92,6 +93,7 @@ export interface ManagedPythonRuntimeInstallOptions extends ManagedPythonRuntime
|
|
|
92
93
|
features: KtxRuntimeFeature[];
|
|
93
94
|
force?: boolean;
|
|
94
95
|
exec?: ManagedPythonRuntimeExec;
|
|
96
|
+
fetchUvArtifact?: ManagedUvFetchArtifact;
|
|
95
97
|
}
|
|
96
98
|
export interface ManagedPythonRuntimeInstallResult {
|
|
97
99
|
status: 'ready' | 'installed';
|
|
@@ -113,8 +115,22 @@ export interface ManagedPythonRuntimeDoctorCheck {
|
|
|
113
115
|
detail: string;
|
|
114
116
|
fix?: string;
|
|
115
117
|
}
|
|
118
|
+
export type ManagedUvFetchArtifact = (url: string) => Promise<Uint8Array>;
|
|
116
119
|
/** @internal */
|
|
117
|
-
export
|
|
120
|
+
export interface ManagedUvRelease {
|
|
121
|
+
version: string;
|
|
122
|
+
artifacts: Partial<Record<ManagedUvPlatformKey, ManagedUvArtifact>>;
|
|
123
|
+
}
|
|
124
|
+
/** @internal */
|
|
125
|
+
export interface EnsureManagedUvOptions {
|
|
126
|
+
platform?: NodeJS.Platform;
|
|
127
|
+
arch?: string;
|
|
128
|
+
env?: NodeJS.ProcessEnv;
|
|
129
|
+
homeDir?: string;
|
|
130
|
+
runtimeRoot?: string;
|
|
131
|
+
fetchArtifact?: ManagedUvFetchArtifact;
|
|
132
|
+
release?: ManagedUvRelease;
|
|
133
|
+
}
|
|
118
134
|
/** @internal */
|
|
119
135
|
export declare function managedPythonRuntimeLayout(options: ManagedPythonRuntimeLayoutOptions): ManagedPythonRuntimeLayout;
|
|
120
136
|
export declare function managedPythonDaemonLayout(options: ManagedPythonDaemonLayoutOptions): ManagedPythonDaemonLayout;
|
|
@@ -122,9 +138,22 @@ export declare function managedPythonDaemonLayout(options: ManagedPythonDaemonLa
|
|
|
122
138
|
export declare function verifyRuntimeAsset(input: {
|
|
123
139
|
assetDir: string;
|
|
124
140
|
}): Promise<ManagedRuntimeAsset>;
|
|
141
|
+
/** @internal */
|
|
142
|
+
export declare function managedUvPath(options?: EnsureManagedUvOptions): string;
|
|
143
|
+
/**
|
|
144
|
+
* ktx provisions its own pinned uv under the runtime root; uv on PATH is never
|
|
145
|
+
* consulted, so runtime installs behave identically on every machine. All
|
|
146
|
+
* failures here are environment outcomes (offline host, intercepting proxy,
|
|
147
|
+
* unsupported platform) and stay out of Error Tracking via KtxExpectedError —
|
|
148
|
+
* except a pin/layout mismatch inside a checksum-verified archive, which is a
|
|
149
|
+
* ktx release fault and must reach Error Tracking.
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
export declare function ensureManagedUv(options?: EnsureManagedUvOptions): Promise<string>;
|
|
125
153
|
export declare function installManagedPythonRuntime(options: ManagedPythonRuntimeInstallOptions): Promise<ManagedPythonRuntimeInstallResult>;
|
|
126
154
|
export declare function readManagedPythonRuntimeStatus(options: ManagedPythonRuntimeLayoutOptions): Promise<ManagedPythonRuntimeStatus>;
|
|
127
155
|
export declare function doctorManagedPythonRuntime(options: ManagedPythonRuntimeLayoutOptions & {
|
|
128
156
|
exec?: ManagedPythonRuntimeExec;
|
|
157
|
+
fetchUvArtifact?: ManagedUvFetchArtifact;
|
|
129
158
|
}): Promise<ManagedPythonRuntimeDoctorCheck[]>;
|
|
130
159
|
export {};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import { access, appendFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { access, appendFile, chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
import { basename, join } from 'node:path';
|
|
5
|
+
import { basename, dirname, join } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
|
-
import { strFromU8, unzipSync } from 'fflate';
|
|
8
|
+
import { gunzipSync, strFromU8, unzipSync } from 'fflate';
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
+
import { KtxExpectedError } from './errors.js';
|
|
11
|
+
import { MANAGED_UV_ARTIFACTS, MANAGED_UV_VERSION, } from './managed-uv-release.js';
|
|
10
12
|
const execFileAsync = promisify(execFile);
|
|
11
13
|
export const runtimeFeatureSchema = z.enum(['core', 'local-embeddings']);
|
|
12
14
|
const runtimeAssetManifestSchema = z.object({
|
|
@@ -32,8 +34,10 @@ const installedRuntimeManifestSchema = z.object({
|
|
|
32
34
|
}),
|
|
33
35
|
installLog: z.string().min(1),
|
|
34
36
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
const PINNED_UV_RELEASE = {
|
|
38
|
+
version: MANAGED_UV_VERSION,
|
|
39
|
+
artifacts: MANAGED_UV_ARTIFACTS,
|
|
40
|
+
};
|
|
37
41
|
function defaultAssetDir() {
|
|
38
42
|
return fileURLToPath(new URL('../assets/python/', import.meta.url));
|
|
39
43
|
}
|
|
@@ -144,7 +148,7 @@ export async function verifyRuntimeAsset(input) {
|
|
|
144
148
|
throw new Error([
|
|
145
149
|
`Missing bundled Python runtime manifest: ${manifestPath}`,
|
|
146
150
|
'In a source checkout, build the local runtime assets with: pnpm run artifacts:build',
|
|
147
|
-
'Then retry the runtime-backed
|
|
151
|
+
'Then retry the runtime-backed ktx command.',
|
|
148
152
|
].join('\n'));
|
|
149
153
|
}
|
|
150
154
|
throw error;
|
|
@@ -221,13 +225,123 @@ async function runLogged(input) {
|
|
|
221
225
|
function managedRuntimeUvEnv(baseEnv) {
|
|
222
226
|
return { ...baseEnv, UV_NO_CONFIG: '1' };
|
|
223
227
|
}
|
|
224
|
-
|
|
228
|
+
function managedUvBinaryName(platform) {
|
|
229
|
+
return platform === 'win32' ? 'uv.exe' : 'uv';
|
|
230
|
+
}
|
|
231
|
+
/** @internal */
|
|
232
|
+
export function managedUvPath(options = {}) {
|
|
233
|
+
const platform = options.platform ?? process.platform;
|
|
234
|
+
const env = options.env ?? process.env;
|
|
235
|
+
const homeDir = options.homeDir ?? homedir();
|
|
236
|
+
const runtimeRoot = options.runtimeRoot ?? runtimeRootFor({ env, homeDir });
|
|
237
|
+
const version = (options.release ?? PINNED_UV_RELEASE).version;
|
|
238
|
+
return join(runtimeRoot, 'uv', version, managedUvBinaryName(platform));
|
|
239
|
+
}
|
|
240
|
+
async function defaultFetchUvArtifact(url) {
|
|
241
|
+
const response = await fetch(url);
|
|
242
|
+
if (!response.ok) {
|
|
243
|
+
throw new Error(`HTTP ${response.status}`);
|
|
244
|
+
}
|
|
245
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
246
|
+
}
|
|
247
|
+
function readTarField(block, start, length) {
|
|
248
|
+
const field = block.subarray(start, start + length);
|
|
249
|
+
const end = field.indexOf(0);
|
|
250
|
+
return strFromU8(end < 0 ? field : field.subarray(0, end));
|
|
251
|
+
}
|
|
252
|
+
function findTarEntry(archive, matches) {
|
|
253
|
+
let offset = 0;
|
|
254
|
+
while (offset + 512 <= archive.length) {
|
|
255
|
+
const block = archive.subarray(offset, offset + 512);
|
|
256
|
+
const name = readTarField(block, 0, 100);
|
|
257
|
+
if (!name) {
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
const size = Number.parseInt(readTarField(block, 124, 12).trim() || '0', 8);
|
|
261
|
+
if (matches(name)) {
|
|
262
|
+
return archive.subarray(offset + 512, offset + 512 + size);
|
|
263
|
+
}
|
|
264
|
+
offset += 512 + Math.ceil(size / 512) * 512;
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
function extractUvFromArchive(input) {
|
|
269
|
+
const entry = input.file.endsWith('.zip')
|
|
270
|
+
? unzipSync(input.contents)[input.binaryName]
|
|
271
|
+
: findTarEntry(gunzipSync(input.contents), (name) => name === input.binaryName || name.endsWith(`/${input.binaryName}`));
|
|
272
|
+
if (!entry) {
|
|
273
|
+
throw new Error(`uv archive ${input.file} is missing the ${input.binaryName} binary`);
|
|
274
|
+
}
|
|
275
|
+
return entry;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* ktx provisions its own pinned uv under the runtime root; uv on PATH is never
|
|
279
|
+
* consulted, so runtime installs behave identically on every machine. All
|
|
280
|
+
* failures here are environment outcomes (offline host, intercepting proxy,
|
|
281
|
+
* unsupported platform) and stay out of Error Tracking via KtxExpectedError —
|
|
282
|
+
* except a pin/layout mismatch inside a checksum-verified archive, which is a
|
|
283
|
+
* ktx release fault and must reach Error Tracking.
|
|
284
|
+
* @internal
|
|
285
|
+
*/
|
|
286
|
+
export async function ensureManagedUv(options = {}) {
|
|
287
|
+
const platform = options.platform ?? process.platform;
|
|
288
|
+
const arch = options.arch ?? process.arch;
|
|
289
|
+
const release = options.release ?? PINNED_UV_RELEASE;
|
|
290
|
+
const binaryName = managedUvBinaryName(platform);
|
|
291
|
+
const uvPath = managedUvPath(options);
|
|
292
|
+
if (await pathExists(uvPath)) {
|
|
293
|
+
return uvPath;
|
|
294
|
+
}
|
|
295
|
+
const artifact = release.artifacts[`${platform}-${arch}`];
|
|
296
|
+
if (!artifact) {
|
|
297
|
+
throw new KtxExpectedError(`ktx does not bundle uv for ${platform}-${arch}. Place a uv ${release.version} binary at ${uvPath} and retry: ktx admin runtime install --yes`);
|
|
298
|
+
}
|
|
299
|
+
const url = `https://github.com/astral-sh/uv/releases/download/${release.version}/${artifact.file}`;
|
|
300
|
+
let contents;
|
|
225
301
|
try {
|
|
226
|
-
|
|
227
|
-
return result.stdout.trim() || 'uv available';
|
|
302
|
+
contents = await (options.fetchArtifact ?? defaultFetchUvArtifact)(url);
|
|
228
303
|
}
|
|
229
|
-
catch {
|
|
230
|
-
throw new
|
|
304
|
+
catch (error) {
|
|
305
|
+
throw new KtxExpectedError(`ktx could not download uv ${release.version} (required to install the ktx Python runtime). ` +
|
|
306
|
+
'Check network access to github.com and retry: ktx admin runtime install --yes. ' +
|
|
307
|
+
`Air-gapped hosts: place the uv binary at ${uvPath}.`, { cause: error });
|
|
308
|
+
}
|
|
309
|
+
const sha256 = createHash('sha256').update(contents).digest('hex');
|
|
310
|
+
if (sha256 !== artifact.sha256) {
|
|
311
|
+
throw new KtxExpectedError(`Downloaded uv ${release.version} failed checksum verification (a proxy or captive portal may have altered the download). Retry: ktx admin runtime install --yes`);
|
|
312
|
+
}
|
|
313
|
+
const binary = extractUvFromArchive({ file: artifact.file, contents, binaryName });
|
|
314
|
+
await mkdir(dirname(uvPath), { recursive: true });
|
|
315
|
+
const stagedPath = `${uvPath}.${process.pid}.download`;
|
|
316
|
+
await writeFile(stagedPath, binary);
|
|
317
|
+
await chmod(stagedPath, 0o755);
|
|
318
|
+
try {
|
|
319
|
+
await rename(stagedPath, uvPath);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
// On Windows a concurrent install may have won the rename; the binary at
|
|
323
|
+
// uvPath is checksum-pinned identical, so reuse it.
|
|
324
|
+
await rm(stagedPath, { force: true });
|
|
325
|
+
if (!(await pathExists(uvPath))) {
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return uvPath;
|
|
330
|
+
}
|
|
331
|
+
async function ensureUv(input) {
|
|
332
|
+
const uvPath = await ensureManagedUv({
|
|
333
|
+
platform: input.options.platform,
|
|
334
|
+
env: input.options.env,
|
|
335
|
+
homeDir: input.options.homeDir,
|
|
336
|
+
runtimeRoot: input.options.runtimeRoot,
|
|
337
|
+
fetchArtifact: input.options.fetchUvArtifact,
|
|
338
|
+
});
|
|
339
|
+
try {
|
|
340
|
+
const result = await input.exec(uvPath, ['--version'], { env: input.uvEnv });
|
|
341
|
+
return { uvPath, version: result.stdout.trim() || `uv ${MANAGED_UV_VERSION}` };
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
throw new KtxExpectedError(`Managed uv at ${uvPath} failed to run. Delete it and retry: ktx admin runtime install --yes`, { cause: error });
|
|
231
345
|
}
|
|
232
346
|
}
|
|
233
347
|
export async function installManagedPythonRuntime(options) {
|
|
@@ -246,21 +360,23 @@ export async function installManagedPythonRuntime(options) {
|
|
|
246
360
|
(await pathExists(existing.python.daemonExecutable))) {
|
|
247
361
|
return { status: 'ready', layout, asset, manifest: existing };
|
|
248
362
|
}
|
|
363
|
+
// uv is acquired before the version dir is wiped, so a failed acquisition
|
|
364
|
+
// never destroys a previously installed runtime.
|
|
365
|
+
const { uvPath } = await ensureUv({ exec, uvEnv, options });
|
|
249
366
|
await rm(layout.versionDir, { recursive: true, force: true });
|
|
250
367
|
await mkdir(layout.versionDir, { recursive: true });
|
|
251
368
|
await writeFile(layout.installLogPath, '');
|
|
252
|
-
await ensureUv(exec, uvEnv);
|
|
253
369
|
await runLogged({
|
|
254
370
|
exec,
|
|
255
371
|
logPath: layout.installLogPath,
|
|
256
|
-
command:
|
|
372
|
+
command: uvPath,
|
|
257
373
|
args: ['python', 'install', asset.requiresPython.minimumVersion],
|
|
258
374
|
env: uvEnv,
|
|
259
375
|
});
|
|
260
376
|
await runLogged({
|
|
261
377
|
exec,
|
|
262
378
|
logPath: layout.installLogPath,
|
|
263
|
-
command:
|
|
379
|
+
command: uvPath,
|
|
264
380
|
args: ['venv', '--python', asset.requiresPython.minimumVersion, layout.venvDir],
|
|
265
381
|
env: uvEnv,
|
|
266
382
|
});
|
|
@@ -268,7 +384,7 @@ export async function installManagedPythonRuntime(options) {
|
|
|
268
384
|
await runLogged({
|
|
269
385
|
exec,
|
|
270
386
|
logPath: layout.installLogPath,
|
|
271
|
-
command:
|
|
387
|
+
command: uvPath,
|
|
272
388
|
args: ['pip', 'install', '--python', layout.pythonPath, wheelSpec],
|
|
273
389
|
env: uvEnv,
|
|
274
390
|
});
|
|
@@ -326,15 +442,15 @@ export async function doctorManagedPythonRuntime(options) {
|
|
|
326
442
|
const exec = options.exec ?? defaultExec;
|
|
327
443
|
const checks = [];
|
|
328
444
|
try {
|
|
329
|
-
const
|
|
330
|
-
checks.push(check('pass', { id: 'uv', label: 'uv', detail: version }));
|
|
445
|
+
const uv = await ensureUv({ exec, uvEnv: managedRuntimeUvEnv(options.env ?? process.env), options });
|
|
446
|
+
checks.push(check('pass', { id: 'uv', label: 'uv', detail: `${uv.version} (managed: ${uv.uvPath})` }));
|
|
331
447
|
}
|
|
332
448
|
catch (error) {
|
|
333
449
|
checks.push(check('fail', {
|
|
334
450
|
id: 'uv',
|
|
335
451
|
label: 'uv',
|
|
336
452
|
detail: error instanceof Error ? error.message : String(error),
|
|
337
|
-
fix: '
|
|
453
|
+
fix: 'Check network access to github.com and run: ktx admin runtime install --yes',
|
|
338
454
|
}));
|
|
339
455
|
}
|
|
340
456
|
try {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type ManagedUvPlatformKey = 'darwin-arm64' | 'darwin-x64' | 'linux-arm64' | 'linux-x64' | 'win32-arm64' | 'win32-x64';
|
|
2
|
+
export interface ManagedUvArtifact {
|
|
3
|
+
file: string;
|
|
4
|
+
sha256: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const MANAGED_UV_VERSION = "0.11.21";
|
|
7
|
+
export declare const MANAGED_UV_ARTIFACTS: Record<ManagedUvPlatformKey, ManagedUvArtifact>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Generated by scripts/refresh-uv-manifest.mjs. Do not edit by hand.
|
|
2
|
+
// Regenerate with: node scripts/refresh-uv-manifest.mjs [<uv-version>]
|
|
3
|
+
export const MANAGED_UV_VERSION = '0.11.21';
|
|
4
|
+
export const MANAGED_UV_ARTIFACTS = {
|
|
5
|
+
'darwin-arm64': { file: 'uv-aarch64-apple-darwin.tar.gz', sha256: '1f921d491ba5ffeea774eb04d6681ecee379101341cbb1500394993b541bf3f4' }, // pragma: allowlist secret
|
|
6
|
+
'darwin-x64': { file: 'uv-x86_64-apple-darwin.tar.gz', sha256: 'f3c8e5708a84b920c18b691214d54d2b0da6b984789caae95d47c95120cb7765' }, // pragma: allowlist secret
|
|
7
|
+
'linux-arm64': { file: 'uv-aarch64-unknown-linux-musl.tar.gz', sha256: 'e71badaed2a2c3a404a0a00974b51c7ed5f5bc7be947916846005b739c68a5a2' }, // pragma: allowlist secret
|
|
8
|
+
'linux-x64': { file: 'uv-x86_64-unknown-linux-musl.tar.gz', sha256: '9dadff5b9e7b1d2d011e41852a1cbca713d9d5d88194f2eb6bd240fa4fb0a719' }, // pragma: allowlist secret
|
|
9
|
+
'win32-arm64': { file: 'uv-aarch64-pc-windows-msvc.zip', sha256: '74e443f8004022dde57a1bd0d10c097830f9ea8feb4ec927db52cd5d805c2f48' }, // pragma: allowlist secret
|
|
10
|
+
'win32-x64': { file: 'uv-x86_64-pc-windows-msvc.zip', sha256: 'ace861f360c6de2babedc1607d0f454b6b09a820dbc8182dc15af927e4df9589' }, // pragma: allowlist secret
|
|
11
|
+
};
|
package/dist/mcp-http-server.js
CHANGED
|
@@ -38,7 +38,7 @@ function fullOrigin(value) {
|
|
|
38
38
|
}
|
|
39
39
|
export function buildMcpSecurityConfig(input) {
|
|
40
40
|
if (!isLoopbackHost(input.host) && !input.token) {
|
|
41
|
-
throw new Error(`Binding
|
|
41
|
+
throw new Error(`Binding ktx MCP to ${input.host} requires --token or KTX_MCP_TOKEN`);
|
|
42
42
|
}
|
|
43
43
|
const allowedHostSet = new Set(DEFAULT_ALLOWED_HOSTS);
|
|
44
44
|
if (!isLoopbackHost(input.host)) {
|
|
@@ -63,16 +63,16 @@ function headerValue(headers, name) {
|
|
|
63
63
|
export function isMcpRequestAuthorized(request, config) {
|
|
64
64
|
const host = headerValue(request.headers, 'host');
|
|
65
65
|
if (!host || !config.allowedHosts.includes(normalizeHostHeader(host))) {
|
|
66
|
-
return { ok: false, status: 403, message: 'Host header is not allowed for
|
|
66
|
+
return { ok: false, status: 403, message: 'Host header is not allowed for ktx MCP.' };
|
|
67
67
|
}
|
|
68
68
|
const origin = headerValue(request.headers, 'origin');
|
|
69
69
|
if (origin && !config.allowedOrigins.includes(origin)) {
|
|
70
|
-
return { ok: false, status: 403, message: 'Origin header is not allowed for
|
|
70
|
+
return { ok: false, status: 403, message: 'Origin header is not allowed for ktx MCP.' };
|
|
71
71
|
}
|
|
72
72
|
if (request.path === '/mcp' && config.token) {
|
|
73
73
|
const auth = headerValue(request.headers, 'authorization');
|
|
74
74
|
if (auth !== `Bearer ${config.token}`) {
|
|
75
|
-
return { ok: false, status: 401, message: 'Missing or invalid
|
|
75
|
+
return { ok: false, status: 401, message: 'Missing or invalid ktx MCP bearer token.' };
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
return { ok: true };
|
|
@@ -5,7 +5,7 @@ import { createLocalProjectMemoryIngest } from './context/memory/local-memory.js
|
|
|
5
5
|
import { resolveProjectEmbeddingProvider } from './embedding-resolution.js';
|
|
6
6
|
import { createKtxCliIngestQueryExecutor } from './ingest-query-executor.js';
|
|
7
7
|
import { createKtxCliScanConnector } from './local-scan-connectors.js';
|
|
8
|
-
import {
|
|
8
|
+
import { createLazyManagedPythonSemanticLayerComputePort } from './managed-python-command.js';
|
|
9
9
|
import { createManagedDaemonSqlAnalysisPort } from './managed-python-http.js';
|
|
10
10
|
function noopMcpIo() {
|
|
11
11
|
return {
|
|
@@ -16,7 +16,7 @@ function noopMcpIo() {
|
|
|
16
16
|
export async function createKtxMcpServerFactory(input) {
|
|
17
17
|
const io = input.io ?? noopMcpIo();
|
|
18
18
|
const queryExecutor = createKtxCliIngestQueryExecutor(input.project);
|
|
19
|
-
const semanticLayerCompute =
|
|
19
|
+
const semanticLayerCompute = createLazyManagedPythonSemanticLayerComputePort({
|
|
20
20
|
cliVersion: input.cliVersion,
|
|
21
21
|
installPolicy: 'auto',
|
|
22
22
|
io,
|
|
@@ -54,7 +54,7 @@ export async function createKtxMcpServerFactory(input) {
|
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
catch (error) {
|
|
57
|
-
io.stderr.write(`
|
|
57
|
+
io.stderr.write(`ktx MCP memory_ingest disabled: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
58
58
|
}
|
|
59
59
|
return () => createDefaultKtxMcpServer({
|
|
60
60
|
name: 'ktx',
|
package/dist/mcp-stdio-server.js
CHANGED
|
@@ -37,7 +37,7 @@ export async function runKtxMcpStdioServer(options) {
|
|
|
37
37
|
};
|
|
38
38
|
transport.onclose = () => settle(resolve);
|
|
39
39
|
transport.onerror = (error) => {
|
|
40
|
-
options.io?.stderr.write(`
|
|
40
|
+
options.io?.stderr.write(`ktx MCP stdio transport error: ${error.message}\n`);
|
|
41
41
|
settle(() => reject(error));
|
|
42
42
|
};
|
|
43
43
|
stdin.once('end', closeTransport);
|
package/dist/memory-flow-hud.js
CHANGED
|
@@ -198,7 +198,7 @@ export function ActivityFeed(props) {
|
|
|
198
198
|
const barWidth = Math.min(40, props.width - 20);
|
|
199
199
|
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [!diffEvent && !workStarted && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Connecting to ", src.type.toLowerCase(), "..."] })), diffEvent && (_jsxs(Text, { color: props.theme.complete, children: ["\u2713 Connected \u2014 found ", src.sourceCount, " ", src.itemNounPlural, " to ingest"] })), diffEvent && isIncremental && (_jsx(Text, { color: props.theme.complete, children: "\u2713 Compared with last sync \u2014 only re-analyzing what changed" })), diffEvent && !planEvent && !workStarted && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Grouping related ", src.itemNounPlural, " together for deeper analysis..."] })), planEvent && (_jsxs(Text, { color: props.theme.complete, children: ["\u2713 Grouped into ", planEvent.chunkCount, " business area", planEvent.chunkCount === 1 ? '' : 's'] })), workStarted && !allWorkDone && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Ingesting \u2014 ", finishedAreas, "/", totalChunks || '?', " business area", totalChunks === 1 ? '' : 's', " done"] }), _jsxs(Text, { color: props.theme.muted, children: [' ', src.ingestDescription] }), totalChunks > 0 && (_jsxs(Text, { color: props.theme.active, children: [' ', progressBarOverall(finishedAreas, actives.length, totalChunks, barWidth, props.frame)] }))] })), insights.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: props.theme.text, children: " Created so far:" }), insights.map((insight, idx) => (_jsxs(Text, { color: props.theme.muted, children: [' ', insight.icon, " ", insight.text] }, `result-${idx}`)))] })), isReconciling && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Deduplicating \u2014 removing overlaps between business areas and checking for conflicts..."] })), reconEvent && (_jsxs(Text, { color: props.theme.complete, children: ["\u2713 Deduplicated", reconEvent.conflictCount > 0
|
|
200
200
|
? ` — ${reconEvent.conflictCount} conflict${reconEvent.conflictCount === 1 ? '' : 's'} resolved`
|
|
201
|
-
: ' — no conflicts'] })), isSaving && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Saving to context layer..."] })), savedEvent && (_jsx(Text, { color: props.theme.complete, children: "\u2713 Saved \u2014 your agents can now use the
|
|
201
|
+
: ' — no conflicts'] })), isSaving && (_jsxs(Text, { color: props.theme.active, children: [spinner(props.frame), " Saving to context layer..."] })), savedEvent && (_jsx(Text, { color: props.theme.complete, children: "\u2713 Saved \u2014 your agents can now use the ktx context layer" })), props.showCompletion && (isDone || isError) && (_jsx(CompletionSummary, { input: props.input, theme: props.theme, frame: props.completionFrame, holdComplete: props.holdComplete }))] }));
|
|
202
202
|
}
|
|
203
203
|
function CompletionSummary(props) {
|
|
204
204
|
const saved = [...props.input.events].reverse().find((e) => e.type === 'saved');
|
|
@@ -207,5 +207,5 @@ function CompletionSummary(props) {
|
|
|
207
207
|
const isError = props.input.status === 'error';
|
|
208
208
|
const sl = counterValue(slCount, props.frame);
|
|
209
209
|
const wiki = counterValue(wikiCount, props.frame);
|
|
210
|
-
return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: isError ? (_jsx(Text, { bold: true, color: props.theme.failed, children: "\u2717 Something went wrong \u2014 review the errors above." })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: props.theme.border, children: '─'.repeat(60) }), _jsx(Text, { bold: true, color: props.theme.complete, children: "\u2605
|
|
210
|
+
return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: isError ? (_jsx(Text, { bold: true, color: props.theme.failed, children: "\u2717 Something went wrong \u2014 review the errors above." })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: props.theme.border, children: '─'.repeat(60) }), _jsx(Text, { bold: true, color: props.theme.complete, children: "\u2605 ktx finished ingesting your data" }), (sl > 0 || wiki > 0) && (_jsxs(_Fragment, { children: [_jsx(Text, {}), _jsx(Text, { color: props.theme.text, children: "ktx created:" }), sl > 0 && (_jsxs(Text, { color: props.theme.active, children: [' ', "\uD83D\uDCCA ", sl, " query definition", sl === 1 ? '' : 's', " \u2014 so agents can write accurate SQL for your data"] })), wiki > 0 && (_jsxs(Text, { color: props.theme.complete, children: [' ', "\uD83D\uDCDD ", wiki, " wiki page", wiki === 1 ? '' : 's', " \u2014 so agents understand your business context"] }))] })), _jsx(Text, {}), _jsx(Text, { color: props.theme.text, children: "What to do next:" }), formatNextStepLines().map((line) => (_jsx(Text, { color: props.theme.active, children: line }, line))), props.holdComplete && (_jsxs(_Fragment, { children: [_jsx(Text, {}), _jsx(Text, { color: props.theme.muted, children: "Press q to exit" })] }))] })) }));
|
|
211
211
|
}
|
package/dist/next-steps.js
CHANGED
|
@@ -31,7 +31,7 @@ function commandLines(commands, indent) {
|
|
|
31
31
|
}
|
|
32
32
|
export function formatNextStepLines(indent = ' ') {
|
|
33
33
|
return [
|
|
34
|
-
`${indent}
|
|
34
|
+
`${indent}ktx context is ready for agents. Open your coding agent from the ktx project directory and ask a data question.`,
|
|
35
35
|
`${indent}Verify with:`,
|
|
36
36
|
...commandLines(KTX_NEXT_STEP_DIRECT_COMMANDS, indent),
|
|
37
37
|
];
|
|
@@ -59,7 +59,7 @@ export function formatSetupNextStepLines(state, indent = ' ') {
|
|
|
59
59
|
}
|
|
60
60
|
if (!state.agentIntegrationReady) {
|
|
61
61
|
return [
|
|
62
|
-
`${indent}
|
|
62
|
+
`${indent}ktx context is built. Install agent rules when you want your coding agent to use it.`,
|
|
63
63
|
`${indent}$ ${'ktx setup --agents'.padEnd(KTX_NEXT_STEP_COMMAND_WIDTH)} Install CLI-based agent rules`,
|
|
64
64
|
`${indent}$ ${'ktx status'.padEnd(KTX_NEXT_STEP_COMMAND_WIDTH)} Check setup and context readiness`,
|
|
65
65
|
];
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
+
export declare const MULTISELECT_NAVIGATION_FRAGMENTS: {
|
|
3
|
+
readonly move: "Up/Down to move";
|
|
4
|
+
readonly expand: "Right/Left to expand or collapse";
|
|
5
|
+
readonly select: "Tab to select or unselect";
|
|
6
|
+
readonly search: "Type to search";
|
|
7
|
+
readonly confirm: "Enter to confirm";
|
|
8
|
+
readonly back: "Escape to go back";
|
|
9
|
+
readonly backSearchableTree: "Escape to clear search or go back";
|
|
10
|
+
readonly exit: "Ctrl+C to exit";
|
|
11
|
+
};
|
|
12
|
+
/** @internal */
|
|
13
|
+
export declare const FLAT_MULTISELECT_NAVIGATION_HINT: string;
|
|
14
|
+
/** @internal */
|
|
15
|
+
export declare const SEARCHABLE_MULTISELECT_NAVIGATION_HINT: string;
|
|
16
|
+
export declare const TREE_PICKER_NAVIGATION_HINT: string;
|
|
17
|
+
/** @internal */
|
|
2
18
|
export declare function withMenuOptionSpacing(message: string): string;
|
|
3
19
|
export declare function withMenuOptionsSpacing<T extends {
|
|
4
20
|
message: string;
|
|
5
21
|
}>(options: T): T;
|
|
6
22
|
export declare function withMultiselectNavigation(message: string): string;
|
|
23
|
+
export declare function withSearchableMultiselectNavigation(message: string): string;
|
|
7
24
|
export declare function withTextInputNavigation(message: string): string;
|
|
@@ -1,4 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
/** @internal */
|
|
2
|
+
export const MULTISELECT_NAVIGATION_FRAGMENTS = {
|
|
3
|
+
move: 'Up/Down to move',
|
|
4
|
+
expand: 'Right/Left to expand or collapse',
|
|
5
|
+
select: 'Tab to select or unselect',
|
|
6
|
+
search: 'Type to search',
|
|
7
|
+
confirm: 'Enter to confirm',
|
|
8
|
+
back: 'Escape to go back',
|
|
9
|
+
backSearchableTree: 'Escape to clear search or go back',
|
|
10
|
+
exit: 'Ctrl+C to exit',
|
|
11
|
+
};
|
|
12
|
+
function composeNavigationHint(fragments) {
|
|
13
|
+
return `${fragments.join(', ')}.`;
|
|
14
|
+
}
|
|
15
|
+
const fragment = MULTISELECT_NAVIGATION_FRAGMENTS;
|
|
16
|
+
/** @internal */
|
|
17
|
+
export const FLAT_MULTISELECT_NAVIGATION_HINT = composeNavigationHint([
|
|
18
|
+
fragment.move,
|
|
19
|
+
fragment.select,
|
|
20
|
+
fragment.confirm,
|
|
21
|
+
fragment.back,
|
|
22
|
+
fragment.exit,
|
|
23
|
+
]);
|
|
24
|
+
/** @internal */
|
|
25
|
+
export const SEARCHABLE_MULTISELECT_NAVIGATION_HINT = composeNavigationHint([
|
|
26
|
+
fragment.move,
|
|
27
|
+
fragment.select,
|
|
28
|
+
fragment.search,
|
|
29
|
+
fragment.confirm,
|
|
30
|
+
fragment.back,
|
|
31
|
+
fragment.exit,
|
|
32
|
+
]);
|
|
33
|
+
export const TREE_PICKER_NAVIGATION_HINT = composeNavigationHint([
|
|
34
|
+
fragment.move,
|
|
35
|
+
fragment.expand,
|
|
36
|
+
fragment.select,
|
|
37
|
+
fragment.search,
|
|
38
|
+
fragment.confirm,
|
|
39
|
+
fragment.backSearchableTree,
|
|
40
|
+
fragment.exit,
|
|
41
|
+
]);
|
|
2
42
|
const TEXT_INPUT_NAVIGATION_HINT = 'Press Escape to go back.';
|
|
3
43
|
function removeTrailingBlankLines(message) {
|
|
4
44
|
return message.replace(/\n+$/, '');
|
|
@@ -45,10 +85,16 @@ export function withMenuOptionsSpacing(options) {
|
|
|
45
85
|
return { ...options, message: withMenuOptionSpacing(options.message) };
|
|
46
86
|
}
|
|
47
87
|
export function withMultiselectNavigation(message) {
|
|
48
|
-
if (message.includes(
|
|
88
|
+
if (message.includes(FLAT_MULTISELECT_NAVIGATION_HINT)) {
|
|
89
|
+
return message;
|
|
90
|
+
}
|
|
91
|
+
return `${message}\n${FLAT_MULTISELECT_NAVIGATION_HINT}`;
|
|
92
|
+
}
|
|
93
|
+
export function withSearchableMultiselectNavigation(message) {
|
|
94
|
+
if (message.includes(SEARCHABLE_MULTISELECT_NAVIGATION_HINT)) {
|
|
49
95
|
return message;
|
|
50
96
|
}
|
|
51
|
-
return `${message}\n${
|
|
97
|
+
return `${message}\n${SEARCHABLE_MULTISELECT_NAVIGATION_HINT}`;
|
|
52
98
|
}
|
|
53
99
|
export function withTextInputNavigation(message) {
|
|
54
100
|
const messageWithoutHint = removeTrailingBlankLines(message)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
You are processing ONE WorkUnit of a multi-file ingest bundle. The WorkUnit
|
|
3
3
|
gives you a slice of raw source files (LookML views, dbt/MetricFlow YAMLs,
|
|
4
4
|
Metabase card JSONs, Notion pages, or similar) and you must translate that
|
|
5
|
-
slice into
|
|
5
|
+
slice into ktx semantic-layer sources and/or knowledge wiki pages, in one pass.
|
|
6
6
|
You run in an isolated WorkUnit worktree. Deterministic projection output,
|
|
7
7
|
existing project memory, and listed dependency paths are visible; sibling
|
|
8
8
|
WorkUnit edits from this same job are not visible until the runner integrates
|
|
@@ -10,7 +10,7 @@ accepted patches.
|
|
|
10
10
|
</role>
|
|
11
11
|
|
|
12
12
|
<stance>
|
|
13
|
-
Assertive. The bundle was explicitly submitted for ingest. Default to capturing everything the raw files declare that maps cleanly to
|
|
13
|
+
Assertive. The bundle was explicitly submitted for ingest. Default to capturing everything the raw files declare that maps cleanly to ktx: one SL source per table/view, one wiki page per non-obvious business rule or alias. Do not abandon a WorkUnit because "some content overlaps with another WU"; use `ingest_triage` to reconcile, do not skip.
|
|
14
14
|
</stance>
|
|
15
15
|
|
|
16
16
|
<workflow>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<role>
|
|
2
|
-
You are ingesting an external technical artifact (a LookML view, dbt model, schema description, business glossary, or other reference document) into
|
|
2
|
+
You are ingesting an external technical artifact (a LookML view, dbt model, schema description, business glossary, or other reference document) into ktx organizational memory. The user has explicitly submitted this content for bulk ingest. Assume it is intentional and worth capturing.
|
|
3
3
|
</role>
|
|
4
4
|
|
|
5
5
|
<stance>
|
|
@@ -18,7 +18,7 @@ A single artifact typically produces multiple actions: one SL source per table/v
|
|
|
18
18
|
</workflow>
|
|
19
19
|
|
|
20
20
|
<scope>
|
|
21
|
-
All wiki writes go to the GLOBAL scope - they will be visible to every user of this
|
|
21
|
+
All wiki writes go to the GLOBAL scope - they will be visible to every user of this ktx project. Phrase wiki pages as objective business knowledge, not personal preference. The `wiki_write` tool handles scope selection automatically for external ingest.
|
|
22
22
|
</scope>
|
|
23
23
|
|
|
24
24
|
<do_not>
|
|
@@ -7,7 +7,7 @@ const DATABASE_INGEST_REPLACEMENTS = [
|
|
|
7
7
|
[/\bWriting schema artifacts\b/gi, 'Writing schema context'],
|
|
8
8
|
[/\bEnriching schema metadata\b/gi, 'Building enriched schema context'],
|
|
9
9
|
[
|
|
10
|
-
/\
|
|
10
|
+
/\bktx scan enrichment failed after structural scan completed\b/gi,
|
|
11
11
|
'Database enrichment failed after schema context completed',
|
|
12
12
|
],
|
|
13
13
|
[/\bstructural scan\b/gi, 'schema context'],
|
package/dist/public-ingest.js
CHANGED
|
@@ -535,8 +535,8 @@ function createPlainPublicIngestProgress(io, options) {
|
|
|
535
535
|
};
|
|
536
536
|
}
|
|
537
537
|
const INTERNAL_STATUS_LINE_RE = /^(Report|Run|Job|Status|Adapter|Connection|Sync|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/;
|
|
538
|
-
const ACTIONABLE_FAILURE_LINE_RE = /^(Missing bundled Python runtime manifest|
|
|
539
|
-
const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed
|
|
538
|
+
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)/;
|
|
539
|
+
const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed ktx command\.?$/;
|
|
540
540
|
function trimErrorPrefix(line) {
|
|
541
541
|
return line.replace(/^Error:\s*/, '');
|
|
542
542
|
}
|
|
@@ -545,7 +545,7 @@ function capturedFailureMessage(output) {
|
|
|
545
545
|
.split(/\r?\n/)
|
|
546
546
|
.map((line) => line.trim())
|
|
547
547
|
.filter((line) => line.length > 0)
|
|
548
|
-
.filter((line) => !line.startsWith('
|
|
548
|
+
.filter((line) => !line.startsWith('ktx scan completed'))
|
|
549
549
|
.filter((line) => !INTERNAL_STATUS_LINE_RE.test(line))
|
|
550
550
|
.map(publicIngestOutputLine);
|
|
551
551
|
const actionableIndex = lines.findIndex((line) => ACTIONABLE_FAILURE_LINE_RE.test(line));
|
package/dist/release-version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const semverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
2
2
|
export function assertCliVersion(value, source) {
|
|
3
3
|
if (typeof value !== 'string' || !semverPattern.test(value)) {
|
|
4
|
-
throw new Error(`Invalid
|
|
4
|
+
throw new Error(`Invalid ktx CLI version in ${source}`);
|
|
5
5
|
}
|
|
6
6
|
return value;
|
|
7
7
|
}
|
|
@@ -44,7 +44,7 @@ export function resolveProjectRuntimeRequirements(config, options = {}) {
|
|
|
44
44
|
requirements.push({
|
|
45
45
|
feature: 'core',
|
|
46
46
|
reason: 'database-introspection',
|
|
47
|
-
detail: 'Database introspection fallback uses the
|
|
47
|
+
detail: 'Database introspection fallback uses the ktx daemon.',
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
for (const [connectionId, connection] of Object.entries(config.connections)) {
|