@kaelio/ktx 0.9.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.9.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 +44 -0
- package/dist/commands/setup-commands.js +2 -3
- package/dist/connection.js +23 -1
- 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/query-history-filter-picker.d.ts +1 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +6 -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/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-bundle-runtime.js +11 -4
- 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-runtime.d.ts +3 -3
- package/dist/context/llm/codex-runtime.js +90 -47
- package/dist/context/llm/local-config.d.ts +15 -5
- package/dist/context/llm/local-config.js +6 -1
- 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 +12 -0
- package/dist/context/project/config.js +35 -0
- 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/tools/context-candidate-mark.tool.d.ts +2 -2
- package/dist/context-build-view.d.ts +13 -0
- package/dist/context-build-view.js +60 -1
- 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/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/memory-flow-hud.js +8 -16
- package/dist/public-ingest.js +50 -15
- package/dist/reveal-password-prompt.d.ts +24 -0
- package/dist/reveal-password-prompt.js +78 -0
- package/dist/scan.js +18 -2
- package/dist/setup-databases.d.ts +1 -0
- package/dist/setup-databases.js +23 -3
- package/dist/setup-demo-tour.js +1 -0
- package/dist/setup-embeddings.js +1 -1
- package/dist/setup-models.d.ts +1 -14
- package/dist/setup-models.js +116 -340
- package/dist/setup-prompts.js +3 -2
- package/dist/setup-sources.js +7 -7
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +1 -1
- 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/telemetry/emitter.d.ts +10 -0
- package/dist/telemetry/emitter.js +31 -0
- package/dist/telemetry/events.d.ts +24 -0
- package/dist/telemetry/events.js +15 -0
- package/dist/telemetry/exception.d.ts +18 -0
- package/dist/telemetry/exception.js +162 -0
- package/dist/telemetry/index.d.ts +3 -2
- package/dist/telemetry/index.js +2 -1
- package/dist/telemetry/redaction-secrets.d.ts +11 -0
- package/dist/telemetry/redaction-secrets.js +92 -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 +8 -1
- 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
package/dist/setup-prompts.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { autocomplete, autocompleteMultiselect, cancel, confirm, intro, isCancel, log, multiselect, note,
|
|
1
|
+
import { autocomplete, autocompleteMultiselect, cancel, confirm, intro, isCancel, log, multiselect, note, select, text, } from '@clack/prompts';
|
|
2
2
|
import { withMenuOptionsSpacing, withTextInputNavigation } from './prompt-navigation.js';
|
|
3
|
+
import { revealPassword } from './reveal-password-prompt.js';
|
|
3
4
|
import { withSetupInterruptConfirmation } from './setup-interrupt.js';
|
|
4
5
|
const DEFAULT_SETUP_CANCEL_MESSAGE = 'Setup cancelled.';
|
|
5
6
|
export function createKtxSetupPromptAdapter(options) {
|
|
@@ -89,7 +90,7 @@ export function createKtxSetupPromptAdapter(options) {
|
|
|
89
90
|
return isCancel(value) ? undefined : String(value);
|
|
90
91
|
},
|
|
91
92
|
async password(promptOptions) {
|
|
92
|
-
const value = await withSetupInterruptConfirmation(() =>
|
|
93
|
+
const value = await withSetupInterruptConfirmation(() => revealPassword({ ...promptOptions, message: withTextInputNavigation(promptOptions.message) }));
|
|
93
94
|
return isCancel(value) ? undefined : String(value);
|
|
94
95
|
},
|
|
95
96
|
cancel(message) {
|
package/dist/setup-sources.js
CHANGED
|
@@ -29,11 +29,11 @@ import { createKtxSetupPromptAdapter, } from './setup-prompts.js';
|
|
|
29
29
|
const DEFAULT_NOTION_MAX_KNOWLEDGE_CREATES_PER_RUN = 25;
|
|
30
30
|
const SOURCE_OPTIONS = [
|
|
31
31
|
{ value: 'dbt', label: 'dbt' },
|
|
32
|
-
{ value: 'metricflow', label: 'MetricFlow' },
|
|
33
32
|
{ value: 'metabase', label: 'Metabase' },
|
|
33
|
+
{ value: 'notion', label: 'Notion' },
|
|
34
|
+
{ value: 'metricflow', label: 'MetricFlow' },
|
|
34
35
|
{ value: 'looker', label: 'Looker' },
|
|
35
36
|
{ value: 'lookml', label: 'LookML' },
|
|
36
|
-
{ value: 'notion', label: 'Notion' },
|
|
37
37
|
];
|
|
38
38
|
const SOURCE_LABELS = Object.fromEntries(SOURCE_OPTIONS.map((option) => [option.value, option.label]));
|
|
39
39
|
const PRIMARY_SOURCE_DRIVERS = new Set([
|
|
@@ -146,8 +146,8 @@ async function chooseSourceCredentialRef(input) {
|
|
|
146
146
|
message: `How should KTX find your ${input.label}?`,
|
|
147
147
|
options: [
|
|
148
148
|
...(input.existingRef ? [{ value: 'keep', label: 'Keep existing credential' }] : []),
|
|
149
|
-
{ value: 'env', label: `Use ${input.envName} from the environment` },
|
|
150
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` },
|
|
151
151
|
{ value: 'back', label: 'Back' },
|
|
152
152
|
],
|
|
153
153
|
});
|
|
@@ -179,8 +179,8 @@ async function chooseGitAuthCredentialRef(input) {
|
|
|
179
179
|
message: `${label} repo requires authentication.`,
|
|
180
180
|
options: [
|
|
181
181
|
...(input.existingRef ? [{ value: 'keep', label: 'Keep existing credential' }] : []),
|
|
182
|
-
{ value: 'env', label: 'Use GITHUB_TOKEN from the environment' },
|
|
183
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' },
|
|
184
184
|
{ value: 'skip', label: 'Skip — try without authentication' },
|
|
185
185
|
{ value: 'back', label: 'Back' },
|
|
186
186
|
],
|
|
@@ -800,8 +800,8 @@ async function promptForInteractiveSource(args, source, prompts, io, deps, defau
|
|
|
800
800
|
const selectedLocation = await prompts.select({
|
|
801
801
|
message: `${source} source location`,
|
|
802
802
|
options: [
|
|
803
|
-
{ value: 'path', label: 'Local path' },
|
|
804
803
|
{ value: 'git', label: 'Git URL' },
|
|
804
|
+
{ value: 'path', label: 'Local path' },
|
|
805
805
|
{ value: 'back', label: 'Back' },
|
|
806
806
|
],
|
|
807
807
|
});
|
|
@@ -1101,8 +1101,8 @@ async function promptForInteractiveSource(args, source, prompts, io, deps, defau
|
|
|
1101
1101
|
const crawlMode = await prompts.select({
|
|
1102
1102
|
message: 'Which Notion pages should KTX ingest?',
|
|
1103
1103
|
options: [
|
|
1104
|
-
{ value: 'selected_roots', label: 'Specific pages and their subpages (choose them in a picker)' },
|
|
1105
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)' },
|
|
1106
1106
|
{ value: 'back', label: 'Back' },
|
|
1107
1107
|
],
|
|
1108
1108
|
});
|
|
@@ -1667,7 +1667,7 @@ export async function runKtxSetupSourcesStep(args, io, deps = {}) {
|
|
|
1667
1667
|
const addMore = await prompts.select({
|
|
1668
1668
|
message: `${readyConnectionIds.length} context source${readyConnectionIds.length > 1 ? 's' : ''} configured (${readyConnectionIds.join(', ')}). Add another?`,
|
|
1669
1669
|
options: [
|
|
1670
|
-
{ value: 'done', label: 'Done
|
|
1670
|
+
{ value: 'done', label: 'Done adding context sources' },
|
|
1671
1671
|
{ value: 'edit', label: 'Edit an existing context source' },
|
|
1672
1672
|
{ value: 'add', label: 'Add another context source' },
|
|
1673
1673
|
],
|
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
|
@@ -443,7 +443,6 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
443
443
|
...(args.llmBackend ? { llmBackend: args.llmBackend } : {}),
|
|
444
444
|
...(args.anthropicApiKeyEnv ? { anthropicApiKeyEnv: args.anthropicApiKeyEnv } : {}),
|
|
445
445
|
...(args.anthropicApiKeyFile ? { anthropicApiKeyFile: args.anthropicApiKeyFile } : {}),
|
|
446
|
-
...(args.llmModel ? { llmModel: args.llmModel } : {}),
|
|
447
446
|
...(args.vertexProject ? { vertexProject: args.vertexProject } : {}),
|
|
448
447
|
...(args.vertexLocation ? { vertexLocation: args.vertexLocation } : {}),
|
|
449
448
|
forcePrompt: forcePromptSteps.has('models') || runOnly === 'models',
|
|
@@ -473,6 +472,7 @@ async function runKtxSetupInner(args, io, deps = {}) {
|
|
|
473
472
|
const databaseResult = await databasesRunner({
|
|
474
473
|
projectDir: projectResult.projectDir,
|
|
475
474
|
inputMode: args.inputMode,
|
|
475
|
+
...(args.debug !== undefined ? { debug: args.debug } : {}),
|
|
476
476
|
yes: args.yes,
|
|
477
477
|
cliVersion: args.cliVersion,
|
|
478
478
|
runtimeInstallPolicy: setupRuntimeInstallPolicy(args),
|
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
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { request as httpsRequest } from 'node:https';
|
|
2
|
+
import { URL } from 'node:url';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const GITHUB_REPO_URL = new URL('https://api.github.com/repos/Kaelio/ktx');
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
6
|
+
const githubRepoSchema = z.object({
|
|
7
|
+
stargazers_count: z.number().int().nonnegative(),
|
|
8
|
+
});
|
|
9
|
+
function parseStarCount(raw) {
|
|
10
|
+
return githubRepoSchema.parse(JSON.parse(raw)).stargazers_count;
|
|
11
|
+
}
|
|
12
|
+
export function fetchGitHubStarCount(options = {}) {
|
|
13
|
+
const requestImpl = options.request ?? httpsRequest;
|
|
14
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
let settled = false;
|
|
17
|
+
const finish = (count) => {
|
|
18
|
+
if (settled) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
settled = true;
|
|
22
|
+
resolve(count);
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
const request = requestImpl(GITHUB_REPO_URL, {
|
|
26
|
+
method: 'GET',
|
|
27
|
+
headers: {
|
|
28
|
+
accept: 'application/vnd.github+json',
|
|
29
|
+
'user-agent': 'ktx-star-prompt',
|
|
30
|
+
},
|
|
31
|
+
}, (response) => {
|
|
32
|
+
const chunks = [];
|
|
33
|
+
response.on('data', (chunk) => {
|
|
34
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
35
|
+
});
|
|
36
|
+
response.on('end', () => {
|
|
37
|
+
const statusCode = response.statusCode ?? 0;
|
|
38
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
39
|
+
finish(null);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
finish(parseStarCount(Buffer.concat(chunks).toString('utf8')));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
finish(null);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
request.on('socket', (socket) => {
|
|
51
|
+
socket.unref();
|
|
52
|
+
});
|
|
53
|
+
request.on('error', () => {
|
|
54
|
+
finish(null);
|
|
55
|
+
});
|
|
56
|
+
request.setTimeout(timeoutMs, () => {
|
|
57
|
+
request.destroy(new Error('GitHub star count request timed out'));
|
|
58
|
+
finish(null);
|
|
59
|
+
});
|
|
60
|
+
request.end();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
finish(null);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface StarPromptSymbols {
|
|
2
|
+
star: string;
|
|
3
|
+
middot: string;
|
|
4
|
+
rightArrow: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RenderStarPromptLineOptions {
|
|
7
|
+
columns: number;
|
|
8
|
+
count?: number | null;
|
|
9
|
+
symbols?: StarPromptSymbols;
|
|
10
|
+
}
|
|
11
|
+
export declare function renderStarPromptLine(options: RenderStarPromptLineOptions): string;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SYMBOLS } from '../io/symbols.js';
|
|
2
|
+
const STAR_PROMPT_URL = 'github.com/Kaelio/ktx';
|
|
3
|
+
const STAR_PROMPT_TEXT = 'This takes a few minutes - mind giving ktx a star while you wait?';
|
|
4
|
+
function usableColumns(columns) {
|
|
5
|
+
return Number.isFinite(columns) && columns > 0 ? Math.floor(columns) : 80;
|
|
6
|
+
}
|
|
7
|
+
function starCountSegment(count, symbols) {
|
|
8
|
+
if (typeof count !== 'number' || !Number.isFinite(count)) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
const formatted = new Intl.NumberFormat('en-US').format(count);
|
|
12
|
+
return ` ${symbols.middot} ${formatted} ${symbols.star}`;
|
|
13
|
+
}
|
|
14
|
+
export function renderStarPromptLine(options) {
|
|
15
|
+
const symbols = options.symbols ?? SYMBOLS;
|
|
16
|
+
const columns = usableColumns(options.columns);
|
|
17
|
+
const base = ` ${symbols.star} ${STAR_PROMPT_TEXT} ${STAR_PROMPT_URL}`;
|
|
18
|
+
const withCount = `${base}${starCountSegment(options.count, symbols)}`;
|
|
19
|
+
if (withCount.length <= columns) {
|
|
20
|
+
return withCount;
|
|
21
|
+
}
|
|
22
|
+
if (base.length <= columns) {
|
|
23
|
+
return base;
|
|
24
|
+
}
|
|
25
|
+
return ` ${symbols.star} Star ktx ${symbols.rightArrow} ${STAR_PROMPT_URL}`;
|
|
26
|
+
}
|
|
@@ -15,6 +15,16 @@ export declare function trackTelemetryEvent(input: {
|
|
|
15
15
|
projectApiKey?: string;
|
|
16
16
|
host?: string;
|
|
17
17
|
}): Promise<void>;
|
|
18
|
+
export declare function trackTelemetryException(input: {
|
|
19
|
+
error: Error;
|
|
20
|
+
distinctId: string;
|
|
21
|
+
properties: Record<string, unknown>;
|
|
22
|
+
env?: TelemetryEmitterEnv;
|
|
23
|
+
stderr: TelemetrySink;
|
|
24
|
+
projectApiKey?: string;
|
|
25
|
+
host?: string;
|
|
26
|
+
immediate?: boolean;
|
|
27
|
+
}): Promise<void>;
|
|
18
28
|
export declare function shutdownTelemetryEmitter(): Promise<void>;
|
|
19
29
|
/** @internal */
|
|
20
30
|
export declare function __resetTelemetryEmitterForTests(): void;
|
|
@@ -56,6 +56,37 @@ export async function trackTelemetryEvent(input) {
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
function writeDebugExceptionPayload(input) {
|
|
60
|
+
input.stderr.write(`[telemetry-exception] ${JSON.stringify({
|
|
61
|
+
distinctId: input.distinctId,
|
|
62
|
+
message: input.error.message,
|
|
63
|
+
name: input.error.name,
|
|
64
|
+
properties: input.properties,
|
|
65
|
+
})}\n`);
|
|
66
|
+
}
|
|
67
|
+
export async function trackTelemetryException(input) {
|
|
68
|
+
const env = input.env ?? process.env;
|
|
69
|
+
if (debugEnabled(env)) {
|
|
70
|
+
writeDebugExceptionPayload(input);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const projectApiKey = telemetryProjectApiKey(input.projectApiKey);
|
|
74
|
+
const host = telemetryHost(env, input.host);
|
|
75
|
+
const client = await getPostHogClient(projectApiKey, host);
|
|
76
|
+
if (!client) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
if (input.immediate) {
|
|
81
|
+
await client.captureExceptionImmediate(input.error, input.distinctId, input.properties);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
client.captureException(input.error, input.distinctId, input.properties);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
59
90
|
export async function shutdownTelemetryEmitter() {
|
|
60
91
|
const client = await clientPromise;
|
|
61
92
|
if (!client) {
|
|
@@ -386,6 +386,26 @@ export declare const telemetryEventSchemas: {
|
|
|
386
386
|
errorClass: z.ZodOptional<z.ZodString>;
|
|
387
387
|
durationMs: z.ZodNumber;
|
|
388
388
|
}, z.core.$strict>;
|
|
389
|
+
readonly query_history_filter_completed: z.ZodObject<{
|
|
390
|
+
cliVersion: z.ZodString;
|
|
391
|
+
nodeVersion: z.ZodString;
|
|
392
|
+
osPlatform: z.ZodString;
|
|
393
|
+
osRelease: z.ZodString;
|
|
394
|
+
arch: z.ZodString;
|
|
395
|
+
runtime: z.ZodEnum<{
|
|
396
|
+
node: "node";
|
|
397
|
+
"daemon-py": "daemon-py";
|
|
398
|
+
}>;
|
|
399
|
+
isCi: z.ZodBoolean;
|
|
400
|
+
dialect: z.ZodString;
|
|
401
|
+
consideredRoleCount: z.ZodNumber;
|
|
402
|
+
excludedRoleCount: z.ZodNumber;
|
|
403
|
+
parseFailedCount: z.ZodNumber;
|
|
404
|
+
outcome: z.ZodEnum<{
|
|
405
|
+
ok: "ok";
|
|
406
|
+
error: "error";
|
|
407
|
+
}>;
|
|
408
|
+
}, z.core.$strict>;
|
|
389
409
|
};
|
|
390
410
|
/** @internal */
|
|
391
411
|
export declare const telemetryEventCatalog: readonly [{
|
|
@@ -456,6 +476,10 @@ export declare const telemetryEventCatalog: readonly [{
|
|
|
456
476
|
readonly name: "sql_gen_completed";
|
|
457
477
|
readonly description: "Emitted after daemon SQL generation completes.";
|
|
458
478
|
readonly fields: readonly ["outcome", "dialect", "errorClass", "durationMs"];
|
|
479
|
+
}, {
|
|
480
|
+
readonly name: "query_history_filter_completed";
|
|
481
|
+
readonly description: "Emitted after the setup query-history service-account filter picker runs.";
|
|
482
|
+
readonly fields: readonly ["dialect", "consideredRoleCount", "excludedRoleCount", "parseFailedCount", "outcome"];
|
|
459
483
|
}];
|
|
460
484
|
export type TelemetryEventName = keyof typeof telemetryEventSchemas;
|
|
461
485
|
export type TelemetryCommonEnvelope = z.infer<typeof telemetryCommonEnvelopeSchema>;
|
package/dist/telemetry/events.js
CHANGED
|
@@ -185,6 +185,15 @@ const sqlGenCompletedSchema = telemetryCommonEnvelopeSchema
|
|
|
185
185
|
durationMs: z.number().nonnegative(),
|
|
186
186
|
})
|
|
187
187
|
.strict();
|
|
188
|
+
const queryHistoryFilterCompletedSchema = telemetryCommonEnvelopeSchema
|
|
189
|
+
.extend({
|
|
190
|
+
dialect: z.string(),
|
|
191
|
+
consideredRoleCount: z.number().int().nonnegative(),
|
|
192
|
+
excludedRoleCount: z.number().int().nonnegative(),
|
|
193
|
+
parseFailedCount: z.number().int().nonnegative(),
|
|
194
|
+
outcome: outcomeSchema,
|
|
195
|
+
})
|
|
196
|
+
.strict();
|
|
188
197
|
/** @internal */
|
|
189
198
|
export const telemetryEventSchemas = {
|
|
190
199
|
install_first_run: installFirstRunSchema,
|
|
@@ -204,6 +213,7 @@ export const telemetryEventSchemas = {
|
|
|
204
213
|
daemon_stopped: daemonStoppedSchema,
|
|
205
214
|
sl_plan_completed: slPlanCompletedSchema,
|
|
206
215
|
sql_gen_completed: sqlGenCompletedSchema,
|
|
216
|
+
query_history_filter_completed: queryHistoryFilterCompletedSchema,
|
|
207
217
|
};
|
|
208
218
|
/** @internal */
|
|
209
219
|
export const telemetryEventCatalog = [
|
|
@@ -338,6 +348,11 @@ export const telemetryEventCatalog = [
|
|
|
338
348
|
description: 'Emitted after daemon SQL generation completes.',
|
|
339
349
|
fields: ['outcome', 'dialect', 'errorClass', 'durationMs'],
|
|
340
350
|
},
|
|
351
|
+
{
|
|
352
|
+
name: 'query_history_filter_completed',
|
|
353
|
+
description: 'Emitted after the setup query-history service-account filter picker runs.',
|
|
354
|
+
fields: ['dialect', 'consideredRoleCount', 'excludedRoleCount', 'parseFailedCount', 'outcome'],
|
|
355
|
+
},
|
|
341
356
|
];
|
|
342
357
|
export function buildCommonEnvelope(input) {
|
|
343
358
|
return {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type KtxCliIo, type KtxCliPackageInfo } from '../cli-runtime.js';
|
|
2
|
+
export interface ExceptionContext {
|
|
3
|
+
source: string;
|
|
4
|
+
handled: boolean;
|
|
5
|
+
fatal: boolean;
|
|
6
|
+
extra?: Record<string, string | number | boolean>;
|
|
7
|
+
}
|
|
8
|
+
export declare function reportException(input: {
|
|
9
|
+
error: unknown;
|
|
10
|
+
context: ExceptionContext;
|
|
11
|
+
io: KtxCliIo;
|
|
12
|
+
packageInfo?: KtxCliPackageInfo;
|
|
13
|
+
projectDir?: string;
|
|
14
|
+
immediate?: boolean;
|
|
15
|
+
redactionSecrets?: ReadonlyArray<string>;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
/** @internal */
|
|
18
|
+
export declare function __resetTelemetryExceptionStateForTests(): void;
|