@kaelio/ktx 0.9.0 → 0.11.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.11.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 +46 -2
- package/dist/cli-runtime.d.ts +5 -0
- package/dist/cli-runtime.js +50 -0
- package/dist/commands/setup-commands.js +2 -3
- package/dist/community-cta.d.ts +11 -0
- package/dist/community-cta.js +19 -0
- 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/core/git-env.d.ts +12 -1
- package/dist/context/core/git-env.js +17 -2
- package/dist/context/core/git.service.js +15 -7
- 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/io/tty.d.ts +9 -0
- package/dist/io/tty.js +5 -0
- package/dist/links.d.ts +1 -0
- package/dist/links.js +1 -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-agents.js +1 -5
- 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 +4 -7
- package/dist/setup-sources.js +7 -7
- package/dist/setup.d.ts +26 -1
- package/dist/setup.js +78 -7
- 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/command-hook.d.ts +24 -0
- package/dist/telemetry/command-hook.js +37 -3
- 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 +4 -3
- package/dist/telemetry/index.js +3 -2
- 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
|
@@ -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;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import { getKtxCliPackageInfo } from '../cli-runtime.js';
|
|
3
|
+
import { buildCommonEnvelope } from './events.js';
|
|
4
|
+
import { trackTelemetryException } from './emitter.js';
|
|
5
|
+
import { computeTelemetryProjectId, loadTelemetryIdentity } from './identity.js';
|
|
6
|
+
const reportedObjects = new WeakSet();
|
|
7
|
+
const recentHandledPrimitives = [];
|
|
8
|
+
const RECENT_PRIMITIVE_LIMIT = 128;
|
|
9
|
+
function primitiveKey(value) {
|
|
10
|
+
return `${typeof value}:${String(value)}`;
|
|
11
|
+
}
|
|
12
|
+
function rememberHandledPrimitive(value) {
|
|
13
|
+
recentHandledPrimitives.push(primitiveKey(value));
|
|
14
|
+
if (recentHandledPrimitives.length > RECENT_PRIMITIVE_LIMIT) {
|
|
15
|
+
recentHandledPrimitives.splice(0, recentHandledPrimitives.length - RECENT_PRIMITIVE_LIMIT);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function consumeHandledPrimitive(value) {
|
|
19
|
+
const key = primitiveKey(value);
|
|
20
|
+
const index = recentHandledPrimitives.indexOf(key);
|
|
21
|
+
if (index < 0) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
recentHandledPrimitives.splice(index, 1);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
function shouldSkipAsAlreadyReported(error, handled) {
|
|
28
|
+
if ((typeof error === 'object' || typeof error === 'function') && error !== null) {
|
|
29
|
+
if (reportedObjects.has(error)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
reportedObjects.add(error);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (handled) {
|
|
36
|
+
rememberHandledPrimitive(error);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return consumeHandledPrimitive(error);
|
|
40
|
+
}
|
|
41
|
+
function escapeRegExp(value) {
|
|
42
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
43
|
+
}
|
|
44
|
+
function redactStaticPatterns(value) {
|
|
45
|
+
return value
|
|
46
|
+
.replace(/([a-z][a-z0-9+.-]*:\/\/[^:\s/@]+:)([^@\s/]+)(@)/gi, '$1[redacted]$3')
|
|
47
|
+
.replace(/\b(password|pwd)=([^;&\s]+)/gi, '$1=[redacted]')
|
|
48
|
+
.replace(/\bAuthorization\s*:\s*[^\r\n,;]+/gi, 'Authorization: [redacted]')
|
|
49
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]')
|
|
50
|
+
.replace(/\b(api[_-]?key)\s*[:=]\s*([^\s,;]+)/gi, '$1=[redacted]')
|
|
51
|
+
.replace(/\b(KTX_[A-Z0-9_]*|[A-Z0-9_]*(?:TOKEN|SECRET))\s*[:=]\s*([^\s,;]+)/g, '$1=[redacted]')
|
|
52
|
+
.replace(/([?&](?:X-Amz-Signature|X-Goog-Signature|sig)=)[^&\s]+/gi, '$1[redacted]');
|
|
53
|
+
}
|
|
54
|
+
function redactText(value, secrets) {
|
|
55
|
+
let redacted = value;
|
|
56
|
+
for (const secret of secrets) {
|
|
57
|
+
if (secret) {
|
|
58
|
+
redacted = redacted.replace(new RegExp(escapeRegExp(secret), 'g'), '[redacted]');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return redactStaticPatterns(redacted);
|
|
62
|
+
}
|
|
63
|
+
const FORBIDDEN_EXTRA_PROPERTY_KEYS = new Set([
|
|
64
|
+
'argv',
|
|
65
|
+
'args',
|
|
66
|
+
'env',
|
|
67
|
+
'environment',
|
|
68
|
+
'sql',
|
|
69
|
+
'query',
|
|
70
|
+
'prompt',
|
|
71
|
+
'mcparguments',
|
|
72
|
+
'mcpargs',
|
|
73
|
+
'tablename',
|
|
74
|
+
'schemaname',
|
|
75
|
+
'columnname',
|
|
76
|
+
'databaseurl',
|
|
77
|
+
'connectionstring',
|
|
78
|
+
'url',
|
|
79
|
+
'password',
|
|
80
|
+
'token',
|
|
81
|
+
'apikey',
|
|
82
|
+
'api_key',
|
|
83
|
+
'authorization',
|
|
84
|
+
]);
|
|
85
|
+
function safeExtraProperties(extra) {
|
|
86
|
+
const safe = {};
|
|
87
|
+
for (const [key, value] of Object.entries(extra ?? {})) {
|
|
88
|
+
if (!FORBIDDEN_EXTRA_PROPERTY_KEYS.has(key.replace(/[^a-z0-9_]/gi, '').toLowerCase())) {
|
|
89
|
+
safe[key] = value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return safe;
|
|
93
|
+
}
|
|
94
|
+
function toMessage(error) {
|
|
95
|
+
if (error instanceof Error) {
|
|
96
|
+
return error.message;
|
|
97
|
+
}
|
|
98
|
+
if (typeof error === 'string') {
|
|
99
|
+
return error;
|
|
100
|
+
}
|
|
101
|
+
return inspect(error, { depth: 4, breakLength: 120 });
|
|
102
|
+
}
|
|
103
|
+
function sanitizedError(error, secrets) {
|
|
104
|
+
if (error instanceof Error) {
|
|
105
|
+
const cause = 'cause' in error ? error.cause : undefined;
|
|
106
|
+
const clone = new Error(redactText(error.message, secrets), {
|
|
107
|
+
...(cause !== undefined ? { cause: sanitizedError(cause, secrets) } : {}),
|
|
108
|
+
});
|
|
109
|
+
clone.name = error.name;
|
|
110
|
+
if (error.stack) {
|
|
111
|
+
clone.stack = redactText(error.stack, secrets);
|
|
112
|
+
}
|
|
113
|
+
return clone;
|
|
114
|
+
}
|
|
115
|
+
return new Error(redactText(toMessage(error), secrets));
|
|
116
|
+
}
|
|
117
|
+
export async function reportException(input) {
|
|
118
|
+
try {
|
|
119
|
+
if (shouldSkipAsAlreadyReported(input.error, input.context.handled)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const debug = process.env.KTX_TELEMETRY_DEBUG === '1';
|
|
123
|
+
const identity = await loadTelemetryIdentity({
|
|
124
|
+
stderr: input.io.stderr,
|
|
125
|
+
env: process.env,
|
|
126
|
+
});
|
|
127
|
+
if ((!identity.enabled || !identity.installId) && !debug) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const packageInfo = input.packageInfo ?? getKtxCliPackageInfo();
|
|
131
|
+
const installId = identity.installId ?? 'debug';
|
|
132
|
+
const projectId = input.projectDir ? computeTelemetryProjectId(installId, input.projectDir) : undefined;
|
|
133
|
+
const safeError = sanitizedError(input.error, input.redactionSecrets ?? []);
|
|
134
|
+
const properties = {
|
|
135
|
+
...buildCommonEnvelope({
|
|
136
|
+
cliVersion: packageInfo.version,
|
|
137
|
+
isCi: Boolean(process.env.CI),
|
|
138
|
+
}),
|
|
139
|
+
source: input.context.source,
|
|
140
|
+
handled: input.context.handled,
|
|
141
|
+
fatal: input.context.fatal,
|
|
142
|
+
...(projectId ? { projectId } : {}),
|
|
143
|
+
...safeExtraProperties(input.context.extra),
|
|
144
|
+
};
|
|
145
|
+
delete properties.$groups;
|
|
146
|
+
await trackTelemetryException({
|
|
147
|
+
error: safeError,
|
|
148
|
+
distinctId: installId,
|
|
149
|
+
properties,
|
|
150
|
+
env: process.env,
|
|
151
|
+
stderr: input.io.stderr,
|
|
152
|
+
immediate: input.immediate,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** @internal */
|
|
160
|
+
export function __resetTelemetryExceptionStateForTests() {
|
|
161
|
+
recentHandledPrimitives.length = 0;
|
|
162
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type KtxCliIo, type KtxCliPackageInfo } from '../cli-runtime.js';
|
|
2
|
-
import { beginCommandSpan, completeCommandSpan, type CommandOutcome, type CompletedCommandSpan } from './command-hook.js';
|
|
2
|
+
import { annotateCommandOutcome, beginCommandSpan, completeCommandSpan, type CommandOutcome, type CompletedCommandSpan } from './command-hook.js';
|
|
3
3
|
import { shutdownTelemetryEmitter } from './emitter.js';
|
|
4
|
+
import { reportException, type ExceptionContext } from './exception.js';
|
|
4
5
|
import { type TelemetryCommonEnvelope, type TelemetryEventName, type TelemetryEventProperties } from './events.js';
|
|
5
|
-
export { beginCommandSpan, completeCommandSpan, shutdownTelemetryEmitter };
|
|
6
|
-
export type { CommandOutcome, CompletedCommandSpan };
|
|
6
|
+
export { annotateCommandOutcome, beginCommandSpan, completeCommandSpan, reportException, shutdownTelemetryEmitter };
|
|
7
|
+
export type { CommandOutcome, CompletedCommandSpan, ExceptionContext };
|
|
7
8
|
export declare function showTelemetryNoticeIfNeeded(io: KtxCliIo, packageInfo: KtxCliPackageInfo): Promise<void>;
|
|
8
9
|
type TelemetryEventFields<Name extends TelemetryEventName> = Omit<TelemetryEventProperties<Name>, keyof TelemetryCommonEnvelope>;
|
|
9
10
|
export declare function shouldEmitMcpTelemetry(): boolean;
|
package/dist/telemetry/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getKtxCliPackageInfo } from '../cli-runtime.js';
|
|
2
2
|
import { loadKtxProject } from '../context/project/project.js';
|
|
3
|
-
import { beginCommandSpan, completeCommandSpan, } from './command-hook.js';
|
|
3
|
+
import { annotateCommandOutcome, beginCommandSpan, completeCommandSpan, } from './command-hook.js';
|
|
4
4
|
import { shutdownTelemetryEmitter, trackTelemetryEvent } from './emitter.js';
|
|
5
|
+
import { reportException } from './exception.js';
|
|
5
6
|
import { buildCommonEnvelope, buildTelemetryEvent, } from './events.js';
|
|
6
7
|
import { computeTelemetryProjectId, loadTelemetryIdentity } from './identity.js';
|
|
7
8
|
import { buildProjectStackSnapshotFields } from './project-snapshot.js';
|
|
8
|
-
export { beginCommandSpan, completeCommandSpan, shutdownTelemetryEmitter };
|
|
9
|
+
export { annotateCommandOutcome, beginCommandSpan, completeCommandSpan, reportException, shutdownTelemetryEmitter };
|
|
9
10
|
export async function showTelemetryNoticeIfNeeded(io, packageInfo) {
|
|
10
11
|
const identity = await loadTelemetryIdentity({
|
|
11
12
|
stderr: io.stderr,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type KtxLocalProject } from '../context/project/project.js';
|
|
2
|
+
type TelemetryRedactionProject = Pick<KtxLocalProject, 'config' | 'projectDir'>;
|
|
3
|
+
export declare function collectTelemetryRedactionSecrets(input: {
|
|
4
|
+
project?: TelemetryRedactionProject;
|
|
5
|
+
projectDir?: string;
|
|
6
|
+
connectionId?: string;
|
|
7
|
+
includeLlm?: boolean;
|
|
8
|
+
includeEmbeddings?: boolean;
|
|
9
|
+
env?: NodeJS.ProcessEnv;
|
|
10
|
+
}): Promise<string[]>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { resolveKtxConfigReference } from '../context/core/config-reference.js';
|
|
2
|
+
import { loadKtxProject } from '../context/project/project.js';
|
|
3
|
+
const SENSITIVE_KEY = /(password|secret|token|api[_-]?key|auth[_-]?token|auth_token_ref|private[_-]?key|passphrase|credential|authorization|url)$/i;
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function addSecret(values, value) {
|
|
8
|
+
const trimmed = value?.trim();
|
|
9
|
+
if (trimmed && !values.includes(trimmed)) {
|
|
10
|
+
values.push(trimmed);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function tryResolve(value, env) {
|
|
14
|
+
try {
|
|
15
|
+
return resolveKtxConfigReference(value, env);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function addUrlCredentials(values, value) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = new URL(value);
|
|
24
|
+
addSecret(values, parsed.password ? decodeURIComponent(parsed.password) : undefined);
|
|
25
|
+
addSecret(values, parsed.username ? decodeURIComponent(parsed.username) : undefined);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function collectFromRecord(input, env, values) {
|
|
32
|
+
if (Array.isArray(input)) {
|
|
33
|
+
for (const item of input) {
|
|
34
|
+
collectFromRecord(item, env, values);
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!isRecord(input)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const [key, raw] of Object.entries(input)) {
|
|
42
|
+
if (isRecord(raw) || Array.isArray(raw)) {
|
|
43
|
+
collectFromRecord(raw, env, values);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (typeof raw !== 'string' || !SENSITIVE_KEY.test(key)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const resolved = tryResolve(raw, env);
|
|
50
|
+
addSecret(values, resolved);
|
|
51
|
+
if (resolved) {
|
|
52
|
+
addUrlCredentials(values, resolved);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function collectLlmSecrets(project, env, values) {
|
|
57
|
+
collectFromRecord(project.config.llm.provider, env, values);
|
|
58
|
+
}
|
|
59
|
+
function collectEmbeddingSecrets(project, env, values) {
|
|
60
|
+
collectFromRecord(project.config.ingest.embeddings, env, values);
|
|
61
|
+
collectFromRecord(project.config.scan.enrichment.embeddings, env, values);
|
|
62
|
+
}
|
|
63
|
+
function collectConnectionSecrets(project, connectionId, env, values) {
|
|
64
|
+
if (!connectionId) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
collectFromRecord(project.config.connections[connectionId], env, values);
|
|
68
|
+
}
|
|
69
|
+
export async function collectTelemetryRedactionSecrets(input) {
|
|
70
|
+
const env = input.env ?? process.env;
|
|
71
|
+
let project = input.project;
|
|
72
|
+
if (!project && input.projectDir) {
|
|
73
|
+
try {
|
|
74
|
+
project = await loadKtxProject({ projectDir: input.projectDir });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
project = undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!project) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const values = [];
|
|
84
|
+
if (input.includeLlm) {
|
|
85
|
+
collectLlmSecrets(project, env, values);
|
|
86
|
+
}
|
|
87
|
+
if (input.includeEmbeddings) {
|
|
88
|
+
collectEmbeddingSecrets(project, env, values);
|
|
89
|
+
}
|
|
90
|
+
collectConnectionSecrets(project, input.connectionId, env, values);
|
|
91
|
+
return values;
|
|
92
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const updateCheckCacheSchema: z.ZodObject<{
|
|
3
|
+
checkedAt: z.ZodString;
|
|
4
|
+
channel: z.ZodEnum<{
|
|
5
|
+
next: "next";
|
|
6
|
+
latest: "latest";
|
|
7
|
+
}>;
|
|
8
|
+
installedVersion: z.ZodString;
|
|
9
|
+
latestForChannel: z.ZodString;
|
|
10
|
+
lastNoticeAt: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export type UpdateCheckCache = z.infer<typeof updateCheckCacheSchema>;
|
|
13
|
+
/** @internal */
|
|
14
|
+
export declare function updateCheckCachePath(homeDir?: string): string;
|
|
15
|
+
export declare function readUpdateCheckCache(options?: {
|
|
16
|
+
homeDir?: string;
|
|
17
|
+
}): Promise<UpdateCheckCache | null>;
|
|
18
|
+
export declare function writeUpdateCheckCache(value: UpdateCheckCache, options?: {
|
|
19
|
+
homeDir?: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
const updateCheckCacheSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
checkedAt: z.string(),
|
|
9
|
+
channel: z.enum(['latest', 'next']),
|
|
10
|
+
installedVersion: z.string(),
|
|
11
|
+
latestForChannel: z.string(),
|
|
12
|
+
lastNoticeAt: z.string().optional(),
|
|
13
|
+
})
|
|
14
|
+
.strict();
|
|
15
|
+
/** @internal */
|
|
16
|
+
export function updateCheckCachePath(homeDir = homedir()) {
|
|
17
|
+
return join(homeDir, '.ktx', 'update-check.json');
|
|
18
|
+
}
|
|
19
|
+
export async function readUpdateCheckCache(options = {}) {
|
|
20
|
+
try {
|
|
21
|
+
return updateCheckCacheSchema.parse(JSON.parse(await readFile(updateCheckCachePath(options.homeDir), 'utf-8')));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function writeUpdateCheckCache(value, options = {}) {
|
|
28
|
+
try {
|
|
29
|
+
const path = updateCheckCachePath(options.homeDir);
|
|
30
|
+
await mkdir(dirname(path), { recursive: true });
|
|
31
|
+
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
32
|
+
writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
|
|
33
|
+
renameSync(tempPath, path);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type UpdateChannel = 'latest' | 'next';
|
|
2
|
+
export type UpdateDecision = {
|
|
3
|
+
status: 'skip';
|
|
4
|
+
} | {
|
|
5
|
+
status: 'upToDate';
|
|
6
|
+
channel: UpdateChannel;
|
|
7
|
+
target: string;
|
|
8
|
+
} | {
|
|
9
|
+
status: 'available';
|
|
10
|
+
channel: UpdateChannel;
|
|
11
|
+
target: string;
|
|
12
|
+
};
|
|
13
|
+
/** @internal */
|
|
14
|
+
export declare function inferUpdateChannel(installed: string): UpdateChannel | null;
|
|
15
|
+
export declare function decideUpdate(installed: string, distTags: Record<string, string>): UpdateDecision;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
/** @internal */
|
|
3
|
+
export function inferUpdateChannel(installed) {
|
|
4
|
+
const parsed = semver.parse(installed);
|
|
5
|
+
if (!parsed || installed === '0.0.0') {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const [prereleaseId] = parsed.prerelease;
|
|
9
|
+
if (prereleaseId === undefined) {
|
|
10
|
+
return 'latest';
|
|
11
|
+
}
|
|
12
|
+
if (prereleaseId === 'rc') {
|
|
13
|
+
return 'next';
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
export function decideUpdate(installed, distTags) {
|
|
18
|
+
const channel = inferUpdateChannel(installed);
|
|
19
|
+
if (!channel || !semver.valid(installed)) {
|
|
20
|
+
return { status: 'skip' };
|
|
21
|
+
}
|
|
22
|
+
const target = distTags[channel];
|
|
23
|
+
if (!target || !semver.valid(target)) {
|
|
24
|
+
return { status: 'skip' };
|
|
25
|
+
}
|
|
26
|
+
if (semver.gt(target, installed)) {
|
|
27
|
+
return { status: 'available', channel, target };
|
|
28
|
+
}
|
|
29
|
+
return { status: 'upToDate', channel, target };
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function fetchDistTags(): Promise<Record<string, string>>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { request as httpsRequest } from 'node:https';
|
|
2
|
+
import { URL } from 'node:url';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const DIST_TAGS_URL = new URL('https://registry.npmjs.org/-/package/@kaelio/ktx/dist-tags');
|
|
5
|
+
const distTagsSchema = z.record(z.string(), z.string());
|
|
6
|
+
function parseDistTags(raw) {
|
|
7
|
+
return distTagsSchema.parse(JSON.parse(raw));
|
|
8
|
+
}
|
|
9
|
+
export function fetchDistTags() {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const request = httpsRequest(DIST_TAGS_URL, {
|
|
12
|
+
method: 'GET',
|
|
13
|
+
headers: {
|
|
14
|
+
accept: 'application/json',
|
|
15
|
+
},
|
|
16
|
+
}, (response) => {
|
|
17
|
+
const chunks = [];
|
|
18
|
+
response.on('data', (chunk) => {
|
|
19
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
20
|
+
});
|
|
21
|
+
response.on('end', () => {
|
|
22
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
23
|
+
const statusCode = response.statusCode ?? 0;
|
|
24
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
25
|
+
reject(new Error(`npm dist-tags request failed with ${statusCode}: ${text}`));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
resolve(parseDistTags(text));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
reject(error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
request.on('socket', (socket) => {
|
|
37
|
+
socket.unref();
|
|
38
|
+
});
|
|
39
|
+
request.on('error', reject);
|
|
40
|
+
request.setTimeout(5000, () => {
|
|
41
|
+
request.destroy(new Error('npm dist-tags request timed out'));
|
|
42
|
+
});
|
|
43
|
+
request.end();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { KtxCliIo } from '../cli-runtime.js';
|
|
2
|
+
import { type CliStyleEnv } from '../clack.js';
|
|
3
|
+
import { type UpdateChannel } from './channel.js';
|
|
4
|
+
/** @internal */
|
|
5
|
+
export interface UpdateCheckEnv extends NodeJS.ProcessEnv, CliStyleEnv {
|
|
6
|
+
CI?: string;
|
|
7
|
+
DO_NOT_TRACK?: string;
|
|
8
|
+
KTX_NO_UPDATE_CHECK?: string;
|
|
9
|
+
KTX_OUTPUT?: string;
|
|
10
|
+
NO_UPDATE_NOTIFIER?: string;
|
|
11
|
+
}
|
|
12
|
+
/** @internal */
|
|
13
|
+
export interface UpdateCheckCommandOptions {
|
|
14
|
+
format?: unknown;
|
|
15
|
+
json?: unknown;
|
|
16
|
+
output?: unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface PrepareUpdateCheckNoticeOptions {
|
|
19
|
+
commandOptions?: UpdateCheckCommandOptions;
|
|
20
|
+
env?: UpdateCheckEnv;
|
|
21
|
+
fetchDistTags?: () => Promise<Record<string, string>>;
|
|
22
|
+
homeDir?: string;
|
|
23
|
+
installedVersion: string;
|
|
24
|
+
io: KtxCliIo;
|
|
25
|
+
now?: () => Date;
|
|
26
|
+
}
|
|
27
|
+
export interface PreparedUpdateCheckNotice {
|
|
28
|
+
notice: string | null;
|
|
29
|
+
}
|
|
30
|
+
/** @internal */
|
|
31
|
+
export declare function shouldSuppressUpdateCheck(args: {
|
|
32
|
+
commandOptions?: UpdateCheckCommandOptions;
|
|
33
|
+
env?: UpdateCheckEnv;
|
|
34
|
+
io: KtxCliIo;
|
|
35
|
+
}): boolean;
|
|
36
|
+
/** @internal */
|
|
37
|
+
export declare function renderUpdateNotice(args: {
|
|
38
|
+
channel: UpdateChannel;
|
|
39
|
+
env?: CliStyleEnv;
|
|
40
|
+
installedVersion: string;
|
|
41
|
+
targetVersion: string;
|
|
42
|
+
}): string;
|
|
43
|
+
export declare function prepareUpdateCheckNotice(options: PrepareUpdateCheckNoticeOptions): Promise<PreparedUpdateCheckNotice>;
|