@kaelio/ktx 0.8.0 → 0.10.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.8.0-py3-none-any.whl → kaelio_ktx-0.10.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +17 -2
- package/dist/cli-program.d.ts +3 -0
- package/dist/cli-program.js +42 -2
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.js +94 -3
- package/dist/commands/setup-commands.js +3 -4
- package/dist/connection-recovery.d.ts +34 -0
- package/dist/connection-recovery.js +82 -0
- package/dist/connection.js +26 -2
- package/dist/connectors/bigquery/connector.d.ts +2 -5
- package/dist/connectors/bigquery/connector.js +2 -2
- package/dist/connectors/clickhouse/connector.d.ts +2 -5
- package/dist/connectors/clickhouse/connector.js +2 -2
- package/dist/connectors/mysql/connector.d.ts +7 -6
- package/dist/connectors/mysql/connector.js +25 -5
- package/dist/connectors/mysql/dialect.d.ts +1 -1
- package/dist/connectors/mysql/dialect.js +12 -2
- package/dist/connectors/postgres/connector.d.ts +2 -5
- package/dist/connectors/postgres/connector.js +2 -2
- package/dist/connectors/snowflake/connector.d.ts +2 -5
- package/dist/connectors/snowflake/connector.js +2 -2
- package/dist/connectors/sqlite/connector.d.ts +2 -5
- package/dist/connectors/sqlite/connector.js +2 -2
- package/dist/connectors/sqlserver/connector.d.ts +2 -5
- package/dist/connectors/sqlserver/connector.js +2 -2
- package/dist/context/connections/drivers.d.ts +0 -1
- package/dist/context/connections/drivers.js +0 -7
- package/dist/context/connections/query-executor.d.ts +2 -1
- package/dist/context/core/abort.d.ts +9 -0
- package/dist/context/core/abort.js +36 -0
- package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
- package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
- package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +30 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +194 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
- package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
- package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
- package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
- package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
- package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
- package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
- package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
- package/dist/context/ingest/final-gate-repair.d.ts +1 -0
- package/dist/context/ingest/final-gate-repair.js +1 -0
- package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
- package/dist/context/ingest/ingest-bundle.runner.js +127 -53
- package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
- package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
- package/dist/context/ingest/local-adapters.js +21 -4
- package/dist/context/ingest/local-bundle-runtime.js +13 -5
- package/dist/context/ingest/local-ingest.d.ts +1 -0
- package/dist/context/ingest/local-ingest.js +13 -3
- package/dist/context/ingest/memory-flow/events.js +1 -1
- package/dist/context/ingest/memory-flow/schema.js +8 -3
- package/dist/context/ingest/memory-flow/types.d.ts +7 -3
- package/dist/context/ingest/ports.d.ts +3 -5
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
- package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
- package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
- package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
- package/dist/context/ingest/types.d.ts +1 -0
- package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
- package/dist/context/llm/ai-sdk-runtime.js +152 -16
- package/dist/context/llm/claude-code-runtime.d.ts +6 -4
- package/dist/context/llm/claude-code-runtime.js +127 -48
- package/dist/context/llm/codex-exec-events.d.ts +20 -0
- package/dist/context/llm/codex-exec-events.js +155 -0
- package/dist/context/llm/codex-isolation.d.ts +3 -0
- package/dist/context/llm/codex-isolation.js +5 -0
- package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
- package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
- package/dist/context/llm/codex-models.d.ts +2 -0
- package/dist/context/llm/codex-models.js +17 -0
- package/dist/context/llm/codex-runtime-config.d.ts +16 -0
- package/dist/context/llm/codex-runtime-config.js +19 -0
- package/dist/context/llm/codex-runtime.d.ts +37 -0
- package/dist/context/llm/codex-runtime.js +347 -0
- package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
- package/dist/context/llm/codex-sdk-runner.js +63 -0
- package/dist/context/llm/local-config.d.ts +16 -4
- package/dist/context/llm/local-config.js +18 -2
- package/dist/context/llm/rate-limit-governor.d.ts +103 -0
- package/dist/context/llm/rate-limit-governor.js +285 -0
- package/dist/context/llm/runtime-port.d.ts +3 -6
- package/dist/context/mcp/context-tools.js +43 -13
- package/dist/context/project/config.d.ts +14 -0
- package/dist/context/project/config.js +37 -2
- package/dist/context/scan/types.d.ts +15 -2
- package/dist/context/scan/types.js +12 -0
- package/dist/context/sl/description-normalization.js +4 -14
- package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
- package/dist/context/sql-analysis/ports.d.ts +12 -2
- package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
- package/dist/context-build-view.d.ts +13 -0
- package/dist/context-build-view.js +63 -32
- package/dist/demo-metrics.d.ts +0 -2
- package/dist/demo-metrics.js +1 -11
- package/dist/ingest.d.ts +1 -0
- package/dist/ingest.js +32 -3
- package/dist/io/buffered-command-io.d.ts +11 -0
- package/dist/io/buffered-command-io.js +28 -0
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/llm/types.d.ts +1 -1
- package/dist/local-adapters.d.ts +10 -2
- package/dist/local-adapters.js +19 -3
- package/dist/memory-flow-hud.js +8 -16
- package/dist/next-steps.js +1 -2
- package/dist/progress-port-adapter.d.ts +6 -0
- package/dist/progress-port-adapter.js +18 -0
- package/dist/public-ingest.d.ts +20 -1
- package/dist/public-ingest.js +228 -42
- package/dist/reveal-password-prompt.d.ts +24 -0
- package/dist/reveal-password-prompt.js +78 -0
- package/dist/scan.js +21 -3
- package/dist/setup-context.d.ts +2 -0
- package/dist/setup-context.js +133 -27
- package/dist/setup-databases.d.ts +18 -1
- package/dist/setup-databases.js +378 -249
- package/dist/setup-demo-tour.js +1 -0
- package/dist/setup-embeddings.js +1 -1
- package/dist/setup-models.d.ts +11 -15
- package/dist/setup-models.js +140 -276
- package/dist/setup-prompts.js +3 -2
- package/dist/setup-ready-menu.d.ts +16 -2
- package/dist/setup-ready-menu.js +37 -5
- package/dist/setup-sources.js +115 -35
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +23 -11
- package/dist/sl.d.ts +2 -2
- package/dist/sl.js +20 -4
- package/dist/sql.js +18 -2
- package/dist/star-prompt/cache.d.ts +16 -0
- package/dist/star-prompt/cache.js +45 -0
- package/dist/star-prompt/star-count.d.ts +7 -0
- package/dist/star-prompt/star-count.js +66 -0
- package/dist/star-prompt/star-line.d.ts +12 -0
- package/dist/star-prompt/star-line.js +26 -0
- package/dist/status-project.d.ts +11 -0
- package/dist/status-project.js +50 -1
- package/dist/telemetry/command-hook.d.ts +1 -0
- package/dist/telemetry/command-hook.js +3 -1
- package/dist/telemetry/emitter.d.ts +10 -0
- package/dist/telemetry/emitter.js +31 -0
- package/dist/telemetry/events.d.ts +35 -6
- package/dist/telemetry/events.js +25 -2
- package/dist/telemetry/exception.d.ts +18 -0
- package/dist/telemetry/exception.js +162 -0
- package/dist/telemetry/identity.d.ts +0 -1
- package/dist/telemetry/identity.js +6 -6
- package/dist/telemetry/index.d.ts +15 -2
- package/dist/telemetry/index.js +15 -3
- package/dist/telemetry/redaction-secrets.d.ts +11 -0
- package/dist/telemetry/redaction-secrets.js +92 -0
- package/dist/telemetry/scrubber.d.ts +10 -0
- package/dist/telemetry/scrubber.js +20 -0
- package/dist/update-check/cache.d.ts +21 -0
- package/dist/update-check/cache.js +38 -0
- package/dist/update-check/channel.d.ts +15 -0
- package/dist/update-check/channel.js +30 -0
- package/dist/update-check/registry.d.ts +1 -0
- package/dist/update-check/registry.js +45 -0
- package/dist/update-check/update-check.d.ts +43 -0
- package/dist/update-check/update-check.js +116 -0
- package/package.json +12 -4
- package/dist/context/connections/local-query-executor.d.ts +0 -6
- package/dist/context/connections/local-query-executor.js +0 -39
- package/dist/context/connections/postgres-query-executor.d.ts +0 -25
- package/dist/context/connections/postgres-query-executor.js +0 -53
- package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
- package/dist/context/connections/sqlite-query-executor.js +0 -74
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type KtxSetupPromptOption } from './setup-prompts.js';
|
|
2
2
|
import type { KtxSetupStatus } from './setup.js';
|
|
3
3
|
export type KtxSetupReadyAction = 'models' | 'embeddings' | 'databases' | 'sources' | 'runtime' | 'context' | 'agents' | 'exit';
|
|
4
|
+
/**
|
|
5
|
+
* Where a project stands once its `ktx.yaml` exists. Single source of truth for the
|
|
6
|
+
* end-of-setup interception: each state maps to exactly one obvious next action.
|
|
7
|
+
*/
|
|
8
|
+
export type KtxSetupCompletion = 'incomplete' | 'needs-context' | 'needs-agents' | 'ready';
|
|
4
9
|
interface KtxSetupReadyMenuPromptAdapter {
|
|
5
10
|
select(options: {
|
|
6
11
|
message: string;
|
|
@@ -11,8 +16,17 @@ interface KtxSetupReadyMenuPromptAdapter {
|
|
|
11
16
|
export interface KtxSetupReadyMenuDeps {
|
|
12
17
|
prompts?: KtxSetupReadyMenuPromptAdapter;
|
|
13
18
|
}
|
|
14
|
-
export declare function
|
|
15
|
-
export declare function
|
|
19
|
+
export declare function setupHasContextTargets(status: KtxSetupStatus): boolean;
|
|
20
|
+
export declare function classifyKtxSetupCompletion(status: KtxSetupStatus): KtxSetupCompletion;
|
|
21
|
+
/**
|
|
22
|
+
* Shown when a returning user re-runs `ktx setup` on a fully-ready project. Leads with
|
|
23
|
+
* "you're done" (the readiness note is printed by the caller first) and keeps the
|
|
24
|
+
* section editor one explicit step away rather than defaulting into it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function runKtxSetupReadyMenu(status: KtxSetupStatus, deps?: KtxSetupReadyMenuDeps): Promise<{
|
|
27
|
+
action: KtxSetupReadyAction;
|
|
28
|
+
}>;
|
|
29
|
+
/** @internal Reached only through {@link runKtxSetupReadyMenu}; exported for unit tests. */
|
|
16
30
|
export declare function runKtxSetupReadyChangeMenu(status: KtxSetupStatus, deps?: KtxSetupReadyMenuDeps): Promise<{
|
|
17
31
|
action: KtxSetupReadyAction;
|
|
18
32
|
}>;
|
package/dist/setup-ready-menu.js
CHANGED
|
@@ -1,23 +1,55 @@
|
|
|
1
1
|
import { createKtxSetupPromptAdapter, } from './setup-prompts.js';
|
|
2
|
-
export function
|
|
2
|
+
export function setupHasContextTargets(status) {
|
|
3
|
+
return status.databases.length > 0 || status.sources.length > 0;
|
|
4
|
+
}
|
|
5
|
+
function setupConfigReady(status) {
|
|
3
6
|
return (status.project.ready &&
|
|
4
7
|
status.llm.ready &&
|
|
5
8
|
status.embeddings.ready &&
|
|
6
9
|
status.databases.every((database) => database.ready) &&
|
|
7
10
|
status.sources.every((source) => source.ready) &&
|
|
8
11
|
status.runtime.ready &&
|
|
9
|
-
status
|
|
12
|
+
setupHasContextTargets(status));
|
|
10
13
|
}
|
|
11
|
-
export function
|
|
12
|
-
|
|
14
|
+
export function classifyKtxSetupCompletion(status) {
|
|
15
|
+
if (!setupConfigReady(status)) {
|
|
16
|
+
return 'incomplete';
|
|
17
|
+
}
|
|
18
|
+
if (!status.context.ready) {
|
|
19
|
+
return 'needs-context';
|
|
20
|
+
}
|
|
21
|
+
if (!status.agents.some((agent) => agent.ready)) {
|
|
22
|
+
return 'needs-agents';
|
|
23
|
+
}
|
|
24
|
+
return 'ready';
|
|
13
25
|
}
|
|
14
26
|
function createPromptAdapter() {
|
|
15
27
|
return createKtxSetupPromptAdapter({ selectCancelValue: 'exit' });
|
|
16
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Shown when a returning user re-runs `ktx setup` on a fully-ready project. Leads with
|
|
31
|
+
* "you're done" (the readiness note is printed by the caller first) and keeps the
|
|
32
|
+
* section editor one explicit step away rather than defaulting into it.
|
|
33
|
+
*/
|
|
34
|
+
export async function runKtxSetupReadyMenu(status, deps = {}) {
|
|
35
|
+
const prompts = deps.prompts ?? createPromptAdapter();
|
|
36
|
+
const choice = await prompts.select({
|
|
37
|
+
message: 'Anything else?',
|
|
38
|
+
options: [
|
|
39
|
+
{ value: 'done', label: "Done — I'll start using ktx" },
|
|
40
|
+
{ value: 'change', label: 'Change a setting' },
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
if (choice !== 'change') {
|
|
44
|
+
return { action: 'exit' };
|
|
45
|
+
}
|
|
46
|
+
return runKtxSetupReadyChangeMenu(status, { prompts });
|
|
47
|
+
}
|
|
48
|
+
/** @internal Reached only through {@link runKtxSetupReadyMenu}; exported for unit tests. */
|
|
17
49
|
export async function runKtxSetupReadyChangeMenu(status, deps = {}) {
|
|
18
50
|
const prompts = deps.prompts ?? createPromptAdapter();
|
|
19
51
|
const action = (await prompts.select({
|
|
20
|
-
message:
|
|
52
|
+
message: 'What would you like to change?',
|
|
21
53
|
options: [
|
|
22
54
|
{ value: 'models', label: 'Models' },
|
|
23
55
|
{ value: 'embeddings', label: 'Embeddings' },
|
package/dist/setup-sources.js
CHANGED
|
@@ -19,6 +19,7 @@ import { markKtxSetupStateStepComplete } from './context/project/setup-config.js
|
|
|
19
19
|
import { errorMessage, writePrefixedLines } from './clack.js';
|
|
20
20
|
import { pickNotionRootPages } from './notion-page-picker.js';
|
|
21
21
|
import { runKtxSourceMapping } from './source-mapping.js';
|
|
22
|
+
import { runConnectionSetupWithRecovery, } from './connection-recovery.js';
|
|
22
23
|
import { withMultiselectNavigation, withTextInputNavigation } from './prompt-navigation.js';
|
|
23
24
|
import { runKtxPublicIngest } from './public-ingest.js';
|
|
24
25
|
import { writeProjectLocalSecretReference } from './setup-secrets.js';
|
|
@@ -28,11 +29,11 @@ import { createKtxSetupPromptAdapter, } from './setup-prompts.js';
|
|
|
28
29
|
const DEFAULT_NOTION_MAX_KNOWLEDGE_CREATES_PER_RUN = 25;
|
|
29
30
|
const SOURCE_OPTIONS = [
|
|
30
31
|
{ value: 'dbt', label: 'dbt' },
|
|
31
|
-
{ value: 'metricflow', label: 'MetricFlow' },
|
|
32
32
|
{ value: 'metabase', label: 'Metabase' },
|
|
33
|
+
{ value: 'notion', label: 'Notion' },
|
|
34
|
+
{ value: 'metricflow', label: 'MetricFlow' },
|
|
33
35
|
{ value: 'looker', label: 'Looker' },
|
|
34
36
|
{ value: 'lookml', label: 'LookML' },
|
|
35
|
-
{ value: 'notion', label: 'Notion' },
|
|
36
37
|
];
|
|
37
38
|
const SOURCE_LABELS = Object.fromEntries(SOURCE_OPTIONS.map((option) => [option.value, option.label]));
|
|
38
39
|
const PRIMARY_SOURCE_DRIVERS = new Set([
|
|
@@ -145,8 +146,8 @@ async function chooseSourceCredentialRef(input) {
|
|
|
145
146
|
message: `How should KTX find your ${input.label}?`,
|
|
146
147
|
options: [
|
|
147
148
|
...(input.existingRef ? [{ value: 'keep', label: 'Keep existing credential' }] : []),
|
|
148
|
-
{ value: 'env', label: `Use ${input.envName} from the environment` },
|
|
149
149
|
{ value: 'paste', label: 'Paste a key and save it as a local secret file' },
|
|
150
|
+
{ value: 'env', label: `Use ${input.envName} from the environment` },
|
|
150
151
|
{ value: 'back', label: 'Back' },
|
|
151
152
|
],
|
|
152
153
|
});
|
|
@@ -178,8 +179,8 @@ async function chooseGitAuthCredentialRef(input) {
|
|
|
178
179
|
message: `${label} repo requires authentication.`,
|
|
179
180
|
options: [
|
|
180
181
|
...(input.existingRef ? [{ value: 'keep', label: 'Keep existing credential' }] : []),
|
|
181
|
-
{ value: 'env', label: 'Use GITHUB_TOKEN from the environment' },
|
|
182
182
|
{ value: 'paste', label: 'Paste a token and save it as a local secret file' },
|
|
183
|
+
{ value: 'env', label: 'Use GITHUB_TOKEN from the environment' },
|
|
183
184
|
{ value: 'skip', label: 'Skip — try without authentication' },
|
|
184
185
|
{ value: 'back', label: 'Back' },
|
|
185
186
|
],
|
|
@@ -799,8 +800,8 @@ async function promptForInteractiveSource(args, source, prompts, io, deps, defau
|
|
|
799
800
|
const selectedLocation = await prompts.select({
|
|
800
801
|
message: `${source} source location`,
|
|
801
802
|
options: [
|
|
802
|
-
{ value: 'path', label: 'Local path' },
|
|
803
803
|
{ value: 'git', label: 'Git URL' },
|
|
804
|
+
{ value: 'path', label: 'Local path' },
|
|
804
805
|
{ value: 'back', label: 'Back' },
|
|
805
806
|
],
|
|
806
807
|
});
|
|
@@ -1100,8 +1101,8 @@ async function promptForInteractiveSource(args, source, prompts, io, deps, defau
|
|
|
1100
1101
|
const crawlMode = await prompts.select({
|
|
1101
1102
|
message: 'Which Notion pages should KTX ingest?',
|
|
1102
1103
|
options: [
|
|
1103
|
-
{ value: 'selected_roots', label: 'Specific pages and their subpages (choose them in a picker)' },
|
|
1104
1104
|
{ value: 'all_accessible', label: 'All pages the integration can access' },
|
|
1105
|
+
{ value: 'selected_roots', label: 'Specific pages and their subpages (choose them in a picker)' },
|
|
1105
1106
|
{ value: 'back', label: 'Back' },
|
|
1106
1107
|
],
|
|
1107
1108
|
});
|
|
@@ -1434,56 +1435,125 @@ async function validateSource(source, args, deps) {
|
|
|
1434
1435
|
}
|
|
1435
1436
|
return await (deps.validateNotion ?? defaultValidateNotion)(args.connection);
|
|
1436
1437
|
}
|
|
1437
|
-
async function
|
|
1438
|
-
const
|
|
1438
|
+
async function createSourceSetupRollback(projectDir) {
|
|
1439
|
+
const project = await loadKtxProject({ projectDir });
|
|
1440
|
+
const previousConfig = project.config;
|
|
1441
|
+
const configPath = project.configPath;
|
|
1442
|
+
return async () => {
|
|
1443
|
+
await writeFile(configPath, serializeKtxProjectConfig(previousConfig), 'utf-8');
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
function sourceConnectionId(input) {
|
|
1447
|
+
return input.sourceChoice.kind === 'existing' || input.sourceChoice.kind === 'edited'
|
|
1439
1448
|
? input.sourceChoice.connectionId
|
|
1440
|
-
: input.sourceChoice.
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
const
|
|
1444
|
-
? input.sourceChoice.connection
|
|
1445
|
-
: buildConnection(input.source, input.sourceChoice.args);
|
|
1446
|
-
const rollback = input.sourceChoice.kind === 'existing'
|
|
1447
|
-
? undefined
|
|
1448
|
-
: await writeSourceConnection(input.args.projectDir, connectionId, connection, sourceAdapter(input.source), input.io);
|
|
1449
|
-
if (input.sourceChoice.kind === 'existing') {
|
|
1450
|
-
await ensureSourceAdapterEnabled(input.args.projectDir, input.source);
|
|
1451
|
-
}
|
|
1452
|
-
const validation = await validateSource(input.source, { projectDir: input.args.projectDir, connectionId, connection }, input.deps);
|
|
1449
|
+
: (input.sourceChoice.args.sourceConnectionId ?? `${input.source}-main`);
|
|
1450
|
+
}
|
|
1451
|
+
async function validateSourceConnectionAndMapping(input) {
|
|
1452
|
+
const validation = await validateSource(input.source, { projectDir: input.args.projectDir, connectionId: input.connectionId, connection: input.connection }, input.deps);
|
|
1453
1453
|
if (!validation.ok) {
|
|
1454
|
-
await rollback?.();
|
|
1455
1454
|
input.io.stderr.write(`${validation.message}\n`);
|
|
1456
1455
|
return { status: 'failed' };
|
|
1457
1456
|
}
|
|
1458
1457
|
if (input.source === 'metabase' || input.source === 'looker') {
|
|
1459
|
-
input.prompts.log?.(`Validating ${sourceLabel(input.source)} mapping
|
|
1460
|
-
const mappingCode = await (input.deps.runMapping ?? defaultRunMapping)(input.args.projectDir, connectionId, createSetupPrefixedIo(input.io));
|
|
1458
|
+
input.prompts.log?.(`Validating ${sourceLabel(input.source)} mapping...`);
|
|
1459
|
+
const mappingCode = await (input.deps.runMapping ?? defaultRunMapping)(input.args.projectDir, input.connectionId, createSetupPrefixedIo(input.io));
|
|
1461
1460
|
if (mappingCode !== 0) {
|
|
1462
|
-
await rollback?.();
|
|
1463
1461
|
return { status: 'failed' };
|
|
1464
1462
|
}
|
|
1465
1463
|
}
|
|
1464
|
+
return { status: 'ok' };
|
|
1465
|
+
}
|
|
1466
|
+
async function saveValidateAndMaybeBuildSource(input) {
|
|
1467
|
+
let latestChoice = input.sourceChoice;
|
|
1468
|
+
let latestConnectionId = sourceConnectionId({ source: input.source, sourceChoice: latestChoice });
|
|
1469
|
+
let latestConnection = latestChoice.kind === 'existing'
|
|
1470
|
+
? latestChoice.connection
|
|
1471
|
+
: buildConnection(input.source, latestChoice.args);
|
|
1472
|
+
let configureCount = 0;
|
|
1473
|
+
let rollbackAfterConfigure;
|
|
1474
|
+
const outcome = await runConnectionSetupWithRecovery({
|
|
1475
|
+
label: latestConnectionId,
|
|
1476
|
+
interactive: input.args.inputMode !== 'disabled',
|
|
1477
|
+
allowSkip: true,
|
|
1478
|
+
io: input.io,
|
|
1479
|
+
prompts: input.prompts,
|
|
1480
|
+
snapshot: async () => {
|
|
1481
|
+
rollbackAfterConfigure = await createSourceSetupRollback(input.args.projectDir);
|
|
1482
|
+
return rollbackAfterConfigure;
|
|
1483
|
+
},
|
|
1484
|
+
configure: async () => {
|
|
1485
|
+
configureCount += 1;
|
|
1486
|
+
if (latestChoice.kind === 'existing' && configureCount === 1) {
|
|
1487
|
+
await ensureSourceAdapterEnabled(input.args.projectDir, input.source);
|
|
1488
|
+
return 'configured';
|
|
1489
|
+
}
|
|
1490
|
+
const project = await loadKtxProject({ projectDir: input.args.projectDir });
|
|
1491
|
+
const currentConnection = project.config.connections[latestConnectionId] ?? latestConnection;
|
|
1492
|
+
const useAlreadyPromptedArgs = configureCount === 1 && latestChoice.kind !== 'existing';
|
|
1493
|
+
const sourceArgs = useAlreadyPromptedArgs && latestChoice.kind !== 'existing'
|
|
1494
|
+
? latestChoice.args
|
|
1495
|
+
: input.args.inputMode === 'disabled'
|
|
1496
|
+
? sourceArgsFromExistingConnection({
|
|
1497
|
+
args: input.args,
|
|
1498
|
+
source: input.source,
|
|
1499
|
+
connectionId: latestConnectionId,
|
|
1500
|
+
connection: currentConnection,
|
|
1501
|
+
})
|
|
1502
|
+
: await promptForInteractiveSource(sourceArgsFromExistingConnection({
|
|
1503
|
+
args: input.args,
|
|
1504
|
+
source: input.source,
|
|
1505
|
+
connectionId: latestConnectionId,
|
|
1506
|
+
connection: currentConnection,
|
|
1507
|
+
}), input.source, input.prompts, input.io, {
|
|
1508
|
+
pickNotionRootPages: input.deps.pickNotionRootPages,
|
|
1509
|
+
discoverMetabaseDatabases: input.deps.discoverMetabaseDatabases,
|
|
1510
|
+
}, latestConnectionId, input.deps.testGitRepo, input.deps.discoverMetabaseDatabases);
|
|
1511
|
+
if (sourceArgs === 'back') {
|
|
1512
|
+
return 'back';
|
|
1513
|
+
}
|
|
1514
|
+
latestConnectionId = sourceArgs.sourceConnectionId ?? latestConnectionId;
|
|
1515
|
+
latestConnection = buildConnection(input.source, sourceArgs);
|
|
1516
|
+
latestChoice =
|
|
1517
|
+
latestChoice.kind === 'new'
|
|
1518
|
+
? { kind: 'new', args: sourceArgs }
|
|
1519
|
+
: { kind: 'edited', connectionId: latestConnectionId, args: sourceArgs };
|
|
1520
|
+
await writeSourceConnection(input.args.projectDir, latestConnectionId, latestConnection, sourceAdapter(input.source), input.io);
|
|
1521
|
+
return 'configured';
|
|
1522
|
+
},
|
|
1523
|
+
validate: () => validateSourceConnectionAndMapping({
|
|
1524
|
+
args: input.args,
|
|
1525
|
+
source: input.source,
|
|
1526
|
+
connectionId: latestConnectionId,
|
|
1527
|
+
connection: latestConnection,
|
|
1528
|
+
prompts: input.prompts,
|
|
1529
|
+
io: input.io,
|
|
1530
|
+
deps: input.deps,
|
|
1531
|
+
}),
|
|
1532
|
+
});
|
|
1533
|
+
if (outcome !== 'ready') {
|
|
1534
|
+
return { status: outcome };
|
|
1535
|
+
}
|
|
1466
1536
|
if (input.args.runInitialSourceIngest) {
|
|
1467
1537
|
const ingestResult = await runInitialSourceIngestWithRecovery({
|
|
1468
1538
|
args: input.args,
|
|
1469
|
-
connectionId,
|
|
1539
|
+
connectionId: latestConnectionId,
|
|
1470
1540
|
io: input.io,
|
|
1471
1541
|
prompts: input.prompts,
|
|
1472
1542
|
deps: input.deps,
|
|
1473
1543
|
});
|
|
1474
1544
|
if (ingestResult === 'failed') {
|
|
1475
|
-
await
|
|
1545
|
+
await rollbackAfterConfigure?.();
|
|
1476
1546
|
return { status: 'failed' };
|
|
1477
1547
|
}
|
|
1478
1548
|
if (ingestResult === 'back') {
|
|
1479
|
-
await
|
|
1549
|
+
await rollbackAfterConfigure?.();
|
|
1480
1550
|
return { status: 'back' };
|
|
1481
1551
|
}
|
|
1482
1552
|
}
|
|
1483
1553
|
else {
|
|
1484
|
-
input.io.stdout.write(`│ Context source ${
|
|
1554
|
+
input.io.stdout.write(`│ Context source ${latestConnectionId} saved. It will be built during the context build step.\n`);
|
|
1485
1555
|
}
|
|
1486
|
-
return { status: 'ready', connectionId };
|
|
1556
|
+
return { status: 'ready', connectionId: latestConnectionId };
|
|
1487
1557
|
}
|
|
1488
1558
|
export async function runKtxSetupSourcesStep(args, io, deps = {}) {
|
|
1489
1559
|
try {
|
|
@@ -1579,8 +1649,13 @@ export async function runKtxSetupSourcesStep(args, io, deps = {}) {
|
|
|
1579
1649
|
returnToSourceSelection = true;
|
|
1580
1650
|
break;
|
|
1581
1651
|
}
|
|
1582
|
-
if (
|
|
1583
|
-
|
|
1652
|
+
if (choiceResult.status === 'skip') {
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
if (choiceResult.status === 'ready') {
|
|
1656
|
+
if (!readyConnectionIds.includes(choiceResult.connectionId)) {
|
|
1657
|
+
readyConnectionIds.push(choiceResult.connectionId);
|
|
1658
|
+
}
|
|
1584
1659
|
}
|
|
1585
1660
|
}
|
|
1586
1661
|
if (returnToSourceSelection) {
|
|
@@ -1592,7 +1667,7 @@ export async function runKtxSetupSourcesStep(args, io, deps = {}) {
|
|
|
1592
1667
|
const addMore = await prompts.select({
|
|
1593
1668
|
message: `${readyConnectionIds.length} context source${readyConnectionIds.length > 1 ? 's' : ''} configured (${readyConnectionIds.join(', ')}). Add another?`,
|
|
1594
1669
|
options: [
|
|
1595
|
-
{ value: 'done', label: 'Done
|
|
1670
|
+
{ value: 'done', label: 'Done adding context sources' },
|
|
1596
1671
|
{ value: 'edit', label: 'Edit an existing context source' },
|
|
1597
1672
|
{ value: 'add', label: 'Add another context source' },
|
|
1598
1673
|
],
|
|
@@ -1640,8 +1715,13 @@ export async function runKtxSetupSourcesStep(args, io, deps = {}) {
|
|
|
1640
1715
|
if (choiceResult.status === 'back') {
|
|
1641
1716
|
continue;
|
|
1642
1717
|
}
|
|
1643
|
-
if (
|
|
1644
|
-
|
|
1718
|
+
if (choiceResult.status === 'skip') {
|
|
1719
|
+
continue;
|
|
1720
|
+
}
|
|
1721
|
+
if (choiceResult.status === 'ready') {
|
|
1722
|
+
if (!readyConnectionIds.includes(choiceResult.connectionId)) {
|
|
1723
|
+
readyConnectionIds.push(choiceResult.connectionId);
|
|
1724
|
+
}
|
|
1645
1725
|
}
|
|
1646
1726
|
continue;
|
|
1647
1727
|
}
|
package/dist/setup.d.ts
CHANGED
|
@@ -58,12 +58,12 @@ export type KtxSetupArgs = {
|
|
|
58
58
|
agentScope?: KtxAgentScope;
|
|
59
59
|
skipAgents?: boolean;
|
|
60
60
|
inputMode: 'auto' | 'disabled';
|
|
61
|
+
debug?: boolean;
|
|
61
62
|
yes: boolean;
|
|
62
63
|
cliVersion: string;
|
|
63
64
|
llmBackend?: KtxSetupLlmBackend;
|
|
64
65
|
anthropicApiKeyEnv?: string;
|
|
65
66
|
anthropicApiKeyFile?: string;
|
|
66
|
-
llmModel?: string;
|
|
67
67
|
vertexProject?: string;
|
|
68
68
|
vertexLocation?: string;
|
|
69
69
|
skipLlm: boolean;
|
package/dist/setup.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ktxLocalStateDbPath } from './context/project/local-state-db.js';
|
|
|
6
6
|
import { loadKtxProject } from './context/project/project.js';
|
|
7
7
|
import { readKtxSetupState } from './context/project/setup-config.js';
|
|
8
8
|
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
|
9
|
-
import { formatSetupNextStepLines } from './next-steps.js';
|
|
9
|
+
import { formatNextStepLines, formatSetupNextStepLines } from './next-steps.js';
|
|
10
10
|
import { runtimeInstallPolicyFromFlags } from './managed-python-command.js';
|
|
11
11
|
import { readManagedPythonRuntimeStatus } from './managed-python-runtime.js';
|
|
12
12
|
import { resolveProjectRuntimeRequirements } from './runtime-requirements.js';
|
|
@@ -16,7 +16,7 @@ import { runKtxSetupDatabasesStep, } from './setup-databases.js';
|
|
|
16
16
|
import { runKtxSetupEmbeddingsStep } from './setup-embeddings.js';
|
|
17
17
|
import { isKtxSetupLlmConfigReady, runKtxSetupAnthropicModelStep, } from './setup-models.js';
|
|
18
18
|
import { runKtxSetupProjectStep } from './setup-project.js';
|
|
19
|
-
import {
|
|
19
|
+
import { classifyKtxSetupCompletion, runKtxSetupReadyMenu, setupHasContextTargets, } from './setup-ready-menu.js';
|
|
20
20
|
import { runKtxSetupSourcesStep } from './setup-sources.js';
|
|
21
21
|
import { runKtxSetupRuntimeStep, } from './setup-runtime.js';
|
|
22
22
|
import { createKtxSetupPromptAdapter, createKtxSetupUiAdapter, } from './setup-prompts.js';
|
|
@@ -47,6 +47,7 @@ async function recordSetupStep(input) {
|
|
|
47
47
|
step: input.step,
|
|
48
48
|
outcome: setupTelemetryOutcome(input.status),
|
|
49
49
|
durationMs: Math.max(0, performance.now() - input.startedAt),
|
|
50
|
+
...(input.errorDetail ? { errorDetail: input.errorDetail } : {}),
|
|
50
51
|
},
|
|
51
52
|
});
|
|
52
53
|
}
|
|
@@ -286,9 +287,6 @@ function setupStatusReady(status) {
|
|
|
286
287
|
status.sources.every((source) => source.ready) &&
|
|
287
288
|
status.runtime.ready);
|
|
288
289
|
}
|
|
289
|
-
function setupHasContextTargets(status) {
|
|
290
|
-
return status.databases.length > 0 || status.sources.length > 0;
|
|
291
|
-
}
|
|
292
290
|
function setupContextReady(status) {
|
|
293
291
|
return status.context.ready;
|
|
294
292
|
}
|
|
@@ -371,14 +369,22 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
371
369
|
const currentStatus = await readKtxSetupStatus(projectResult.projectDir, { cliVersion: args.cliVersion });
|
|
372
370
|
let readyAction;
|
|
373
371
|
if (args.inputMode !== 'disabled' && !agentsRequested) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
372
|
+
const completion = classifyKtxSetupCompletion(currentStatus);
|
|
373
|
+
if (completion === 'ready') {
|
|
374
|
+
setupUi.note(formatNextStepLines().join('\n'), 'ktx is ready', io);
|
|
375
|
+
const choice = (await runKtxSetupReadyMenu(currentStatus, deps.readyMenuDeps)).action;
|
|
376
|
+
if (choice === 'exit')
|
|
377
377
|
return 0;
|
|
378
|
+
readyAction = choice;
|
|
379
|
+
}
|
|
380
|
+
else if (completion === 'needs-context') {
|
|
381
|
+
// Config is done; skip the re-walk and land straight on the build prompt.
|
|
382
|
+
readyAction = 'context';
|
|
378
383
|
}
|
|
379
|
-
else if (
|
|
384
|
+
else if (completion === 'needs-agents') {
|
|
380
385
|
readyAction = 'agents';
|
|
381
386
|
}
|
|
387
|
+
// 'incomplete' → readyAction stays undefined → run the full setup walk.
|
|
382
388
|
}
|
|
383
389
|
const runOnly = readyAction;
|
|
384
390
|
const agentOnlySetup = agentsRequested || runOnly === 'agents';
|
|
@@ -437,7 +443,6 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
437
443
|
...(args.llmBackend ? { llmBackend: args.llmBackend } : {}),
|
|
438
444
|
...(args.anthropicApiKeyEnv ? { anthropicApiKeyEnv: args.anthropicApiKeyEnv } : {}),
|
|
439
445
|
...(args.anthropicApiKeyFile ? { anthropicApiKeyFile: args.anthropicApiKeyFile } : {}),
|
|
440
|
-
...(args.llmModel ? { llmModel: args.llmModel } : {}),
|
|
441
446
|
...(args.vertexProject ? { vertexProject: args.vertexProject } : {}),
|
|
442
447
|
...(args.vertexLocation ? { vertexLocation: args.vertexLocation } : {}),
|
|
443
448
|
forcePrompt: forcePromptSteps.has('models') || runOnly === 'models',
|
|
@@ -467,6 +472,10 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
467
472
|
const databaseResult = await databasesRunner({
|
|
468
473
|
projectDir: projectResult.projectDir,
|
|
469
474
|
inputMode: args.inputMode,
|
|
475
|
+
...(args.debug !== undefined ? { debug: args.debug } : {}),
|
|
476
|
+
yes: args.yes,
|
|
477
|
+
cliVersion: args.cliVersion,
|
|
478
|
+
runtimeInstallPolicy: setupRuntimeInstallPolicy(args),
|
|
470
479
|
...(args.databaseDrivers ? { databaseDrivers: args.databaseDrivers } : {}),
|
|
471
480
|
...(args.databaseConnectionIds ? { databaseConnectionIds: args.databaseConnectionIds } : {}),
|
|
472
481
|
...(args.databaseConnectionId ? { databaseConnectionId: args.databaseConnectionId } : {}),
|
|
@@ -565,6 +574,7 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
565
574
|
startedAt: stepStartedAt,
|
|
566
575
|
io,
|
|
567
576
|
cliVersion: args.cliVersion,
|
|
577
|
+
...(stepResult.errorDetail ? { errorDetail: stepResult.errorDetail } : {}),
|
|
568
578
|
});
|
|
569
579
|
if (stepResult.status === 'failed') {
|
|
570
580
|
return 1;
|
|
@@ -589,7 +599,9 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
589
599
|
}
|
|
590
600
|
if (step === 'context' && stepResult.status !== 'ready') {
|
|
591
601
|
if (shouldRunAgents && args.skipAgents !== true) {
|
|
592
|
-
|
|
602
|
+
// Context isn't built, so skip agent install — but still reach the
|
|
603
|
+
// completion screen, which states readiness and points at `ktx ingest`.
|
|
604
|
+
break setupLoop;
|
|
593
605
|
}
|
|
594
606
|
}
|
|
595
607
|
forcePromptSteps.delete(step);
|
package/dist/sl.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { KtxCliIo } from './cli-runtime.js';
|
|
2
2
|
import type { KtxSqlQueryExecutorPort } from './context/connections/query-executor.js';
|
|
3
3
|
import type { KtxSemanticLayerComputePort } from './context/daemon/semantic-layer-compute.js';
|
|
4
|
-
import { loadKtxProject } from './context/project/project.js';
|
|
4
|
+
import { loadKtxProject, type KtxLocalProject } from './context/project/project.js';
|
|
5
5
|
import { searchLocalSlSources as defaultSearchLocalSlSources } from './context/sl/local-sl.js';
|
|
6
6
|
import type { SemanticLayerQueryInput } from './context/sl/types.js';
|
|
7
7
|
import { resolveProjectEmbeddingProvider } from './embedding-resolution.js';
|
|
@@ -57,7 +57,7 @@ interface KtxSlDeps {
|
|
|
57
57
|
io: KtxSlIo;
|
|
58
58
|
projectDir?: string;
|
|
59
59
|
}) => Promise<KtxSemanticLayerComputePort>;
|
|
60
|
-
createQueryExecutor?: () => KtxSqlQueryExecutorPort;
|
|
60
|
+
createQueryExecutor?: (project: KtxLocalProject) => KtxSqlQueryExecutorPort;
|
|
61
61
|
}
|
|
62
62
|
export declare function runKtxSl(args: KtxSlArgs, io?: KtxSlIo, deps?: KtxSlDeps): Promise<number>;
|
|
63
63
|
export {};
|
package/dist/sl.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { createDefaultLocalQueryExecutor } from './context/connections/local-query-executor.js';
|
|
3
2
|
import { KtxIngestEmbeddingPortAdapter } from './context/llm/embedding-port.js';
|
|
4
3
|
import { loadKtxProject } from './context/project/project.js';
|
|
5
4
|
import { compileLocalSlQuery } from './context/sl/local-query.js';
|
|
6
5
|
import { listLocalSlSources, resolveLocalSlSource, searchLocalSlSources as defaultSearchLocalSlSources, validateLocalSlSource, } from './context/sl/local-sl.js';
|
|
7
6
|
import { resolveProjectEmbeddingProvider, } from './embedding-resolution.js';
|
|
7
|
+
import { createKtxCliIngestQueryExecutor } from './ingest-query-executor.js';
|
|
8
8
|
import { createManagedPythonSemanticLayerComputePort, } from './managed-python-command.js';
|
|
9
9
|
import { profileMark } from './startup-profile.js';
|
|
10
|
-
import { emitTelemetryEvent } from './telemetry/index.js';
|
|
10
|
+
import { emitTelemetryEvent, reportException } from './telemetry/index.js';
|
|
11
|
+
import { collectTelemetryRedactionSecrets } from './telemetry/redaction-secrets.js';
|
|
11
12
|
import { scrubErrorClass } from './telemetry/scrubber.js';
|
|
12
13
|
profileMark('module:sl');
|
|
13
14
|
function resolutionToEmbeddingPort(resolution) {
|
|
@@ -91,8 +92,9 @@ function ambiguousSourceMessage(sourceName, connectionIds) {
|
|
|
91
92
|
export async function runKtxSl(args, io = process, deps = {}) {
|
|
92
93
|
const startedAt = performance.now();
|
|
93
94
|
let queryForTelemetry;
|
|
95
|
+
let project;
|
|
94
96
|
try {
|
|
95
|
-
|
|
97
|
+
project = await (deps.loadProject ?? loadKtxProject)({ projectDir: args.projectDir });
|
|
96
98
|
if (args.command === 'list') {
|
|
97
99
|
const sources = await listLocalSlSources(project, { connectionId: args.connectionId });
|
|
98
100
|
await printSlSources({
|
|
@@ -204,7 +206,7 @@ export async function runKtxSl(args, io = process, deps = {}) {
|
|
|
204
206
|
io,
|
|
205
207
|
projectDir: args.projectDir,
|
|
206
208
|
});
|
|
207
|
-
const queryExecutor = args.execute ? (deps.createQueryExecutor ??
|
|
209
|
+
const queryExecutor = args.execute ? (deps.createQueryExecutor ?? createKtxCliIngestQueryExecutor)(project) : undefined;
|
|
208
210
|
const result = await compileLocalSlQuery(project, {
|
|
209
211
|
connectionId: args.connectionId,
|
|
210
212
|
query,
|
|
@@ -237,6 +239,20 @@ export async function runKtxSl(args, io = process, deps = {}) {
|
|
|
237
239
|
throw new Error(`Unsupported sl command: ${JSON.stringify(_exhaustive)}`);
|
|
238
240
|
}
|
|
239
241
|
catch (error) {
|
|
242
|
+
await reportException({
|
|
243
|
+
error,
|
|
244
|
+
context: { source: `sl ${args.command}`, handled: true, fatal: false },
|
|
245
|
+
projectDir: args.projectDir,
|
|
246
|
+
io,
|
|
247
|
+
redactionSecrets: await collectTelemetryRedactionSecrets({
|
|
248
|
+
project,
|
|
249
|
+
projectDir: args.projectDir,
|
|
250
|
+
connectionId: args.connectionId,
|
|
251
|
+
includeLlm: args.command === 'query',
|
|
252
|
+
includeEmbeddings: args.command === 'search' || args.command === 'query',
|
|
253
|
+
env: process.env,
|
|
254
|
+
}),
|
|
255
|
+
});
|
|
240
256
|
if (args.command === 'validate') {
|
|
241
257
|
const errorClass = scrubErrorClass(error);
|
|
242
258
|
await emitTelemetryEvent({
|
package/dist/sql.js
CHANGED
|
@@ -4,7 +4,8 @@ import { createKtxCliScanConnector } from './local-scan-connectors.js';
|
|
|
4
4
|
import { createManagedDaemonSqlAnalysisPort } from './managed-python-http.js';
|
|
5
5
|
import { profileMark } from './startup-profile.js';
|
|
6
6
|
import { isDemoConnection } from './telemetry/demo-detect.js';
|
|
7
|
-
import { emitTelemetryEvent } from './telemetry/index.js';
|
|
7
|
+
import { emitTelemetryEvent, reportException } from './telemetry/index.js';
|
|
8
|
+
import { collectTelemetryRedactionSecrets } from './telemetry/redaction-secrets.js';
|
|
8
9
|
import { scrubErrorClass } from './telemetry/scrubber.js';
|
|
9
10
|
profileMark('module:sql');
|
|
10
11
|
function sqlAnalysisDialectForDriver(driver) {
|
|
@@ -96,8 +97,9 @@ export async function runKtxSql(args, io = process, deps = {}) {
|
|
|
96
97
|
const startedAt = performance.now();
|
|
97
98
|
let driver = 'unknown';
|
|
98
99
|
let demoConnection = false;
|
|
100
|
+
let project;
|
|
99
101
|
try {
|
|
100
|
-
|
|
102
|
+
project = await (deps.loadProject ?? loadKtxProject)({ projectDir: args.projectDir });
|
|
101
103
|
const connection = project.config.connections[args.connectionId];
|
|
102
104
|
if (!connection) {
|
|
103
105
|
throw new Error(`Connection "${args.connectionId}" is not configured in ktx.yaml`);
|
|
@@ -167,6 +169,20 @@ export async function runKtxSql(args, io = process, deps = {}) {
|
|
|
167
169
|
...(errorClass ? { errorClass } : {}),
|
|
168
170
|
},
|
|
169
171
|
});
|
|
172
|
+
await reportException({
|
|
173
|
+
error,
|
|
174
|
+
context: { source: 'sql run', handled: true, fatal: false },
|
|
175
|
+
projectDir: args.projectDir,
|
|
176
|
+
io,
|
|
177
|
+
redactionSecrets: await collectTelemetryRedactionSecrets({
|
|
178
|
+
project,
|
|
179
|
+
projectDir: args.projectDir,
|
|
180
|
+
connectionId: args.connectionId,
|
|
181
|
+
includeLlm: false,
|
|
182
|
+
includeEmbeddings: false,
|
|
183
|
+
env: process.env,
|
|
184
|
+
}),
|
|
185
|
+
});
|
|
170
186
|
io.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
171
187
|
return 1;
|
|
172
188
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const starCountCacheSchema: z.ZodObject<{
|
|
3
|
+
count: z.ZodNumber;
|
|
4
|
+
fetchedAt: z.ZodString;
|
|
5
|
+
}, z.core.$strict>;
|
|
6
|
+
export type StarCountCache = z.infer<typeof starCountCacheSchema>;
|
|
7
|
+
/** @internal */
|
|
8
|
+
export declare function starCountCachePath(homeDir?: string): string;
|
|
9
|
+
export declare function readStarCountCache(options?: {
|
|
10
|
+
homeDir?: string;
|
|
11
|
+
}): StarCountCache | null;
|
|
12
|
+
export declare function writeStarCountCache(value: StarCountCache, options?: {
|
|
13
|
+
homeDir?: string;
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
export declare function isFreshStarCountCache(cache: StarCountCache | null, now: Date, ttlMs: number): boolean;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
const starCountCacheSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
count: z.number().int().nonnegative(),
|
|
9
|
+
fetchedAt: z.string(),
|
|
10
|
+
})
|
|
11
|
+
.strict();
|
|
12
|
+
/** @internal */
|
|
13
|
+
export function starCountCachePath(homeDir = homedir()) {
|
|
14
|
+
return join(homeDir, '.ktx', 'star-count.json');
|
|
15
|
+
}
|
|
16
|
+
export function readStarCountCache(options = {}) {
|
|
17
|
+
try {
|
|
18
|
+
return starCountCacheSchema.parse(JSON.parse(readFileSync(starCountCachePath(options.homeDir), 'utf-8')));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function writeStarCountCache(value, options = {}) {
|
|
25
|
+
try {
|
|
26
|
+
const path = starCountCachePath(options.homeDir);
|
|
27
|
+
await mkdir(dirname(path), { recursive: true });
|
|
28
|
+
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
29
|
+
writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
|
|
30
|
+
renameSync(tempPath, path);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function isFreshStarCountCache(cache, now, ttlMs) {
|
|
37
|
+
if (!cache) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const fetchedAtMs = Date.parse(cache.fetchedAt);
|
|
41
|
+
if (Number.isNaN(fetchedAtMs)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return now.getTime() - fetchedAtMs < ttlMs;
|
|
45
|
+
}
|