@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.
Files changed (143) hide show
  1. package/assets/python/{kaelio_ktx-0.9.0-py3-none-any.whl → kaelio_ktx-0.11.0-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/clack.d.ts +6 -0
  5. package/dist/clack.js +17 -2
  6. package/dist/cli-program.d.ts +3 -0
  7. package/dist/cli-program.js +46 -2
  8. package/dist/cli-runtime.d.ts +5 -0
  9. package/dist/cli-runtime.js +50 -0
  10. package/dist/commands/setup-commands.js +2 -3
  11. package/dist/community-cta.d.ts +11 -0
  12. package/dist/community-cta.js +19 -0
  13. package/dist/connection.js +23 -1
  14. package/dist/connectors/bigquery/connector.d.ts +2 -5
  15. package/dist/connectors/bigquery/connector.js +2 -2
  16. package/dist/connectors/clickhouse/connector.d.ts +2 -5
  17. package/dist/connectors/clickhouse/connector.js +2 -2
  18. package/dist/connectors/mysql/connector.d.ts +7 -6
  19. package/dist/connectors/mysql/connector.js +25 -5
  20. package/dist/connectors/mysql/dialect.d.ts +1 -1
  21. package/dist/connectors/mysql/dialect.js +12 -2
  22. package/dist/connectors/postgres/connector.d.ts +2 -5
  23. package/dist/connectors/postgres/connector.js +2 -2
  24. package/dist/connectors/snowflake/connector.d.ts +2 -5
  25. package/dist/connectors/snowflake/connector.js +2 -2
  26. package/dist/connectors/sqlite/connector.d.ts +2 -5
  27. package/dist/connectors/sqlite/connector.js +2 -2
  28. package/dist/connectors/sqlserver/connector.d.ts +2 -5
  29. package/dist/connectors/sqlserver/connector.js +2 -2
  30. package/dist/context/connections/drivers.d.ts +0 -1
  31. package/dist/context/connections/drivers.js +0 -7
  32. package/dist/context/connections/query-executor.d.ts +2 -1
  33. package/dist/context/core/abort.d.ts +9 -0
  34. package/dist/context/core/abort.js +36 -0
  35. package/dist/context/core/git-env.d.ts +12 -1
  36. package/dist/context/core/git-env.js +17 -2
  37. package/dist/context/core/git.service.js +15 -7
  38. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +1 -0
  39. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +6 -2
  40. package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
  41. package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
  42. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
  43. package/dist/context/ingest/final-gate-repair.d.ts +1 -0
  44. package/dist/context/ingest/final-gate-repair.js +1 -0
  45. package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
  46. package/dist/context/ingest/ingest-bundle.runner.js +127 -53
  47. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
  48. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
  49. package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
  50. package/dist/context/ingest/local-bundle-runtime.js +11 -4
  51. package/dist/context/ingest/local-ingest.d.ts +1 -0
  52. package/dist/context/ingest/local-ingest.js +13 -3
  53. package/dist/context/ingest/memory-flow/events.js +1 -1
  54. package/dist/context/ingest/memory-flow/schema.js +8 -3
  55. package/dist/context/ingest/memory-flow/types.d.ts +7 -3
  56. package/dist/context/ingest/ports.d.ts +3 -5
  57. package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
  58. package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
  59. package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
  60. package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
  61. package/dist/context/ingest/types.d.ts +1 -0
  62. package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
  63. package/dist/context/llm/ai-sdk-runtime.js +152 -16
  64. package/dist/context/llm/claude-code-runtime.d.ts +6 -4
  65. package/dist/context/llm/claude-code-runtime.js +127 -48
  66. package/dist/context/llm/codex-runtime.d.ts +3 -3
  67. package/dist/context/llm/codex-runtime.js +90 -47
  68. package/dist/context/llm/local-config.d.ts +15 -5
  69. package/dist/context/llm/local-config.js +6 -1
  70. package/dist/context/llm/rate-limit-governor.d.ts +103 -0
  71. package/dist/context/llm/rate-limit-governor.js +285 -0
  72. package/dist/context/llm/runtime-port.d.ts +3 -6
  73. package/dist/context/mcp/context-tools.js +43 -13
  74. package/dist/context/project/config.d.ts +12 -0
  75. package/dist/context/project/config.js +35 -0
  76. package/dist/context/scan/types.d.ts +15 -2
  77. package/dist/context/scan/types.js +12 -0
  78. package/dist/context/sl/description-normalization.js +4 -14
  79. package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
  80. package/dist/context-build-view.d.ts +13 -0
  81. package/dist/context-build-view.js +60 -1
  82. package/dist/demo-metrics.d.ts +0 -2
  83. package/dist/demo-metrics.js +1 -11
  84. package/dist/ingest.d.ts +1 -0
  85. package/dist/ingest.js +32 -3
  86. package/dist/io/symbols.d.ts +2 -0
  87. package/dist/io/symbols.js +2 -0
  88. package/dist/io/tty.d.ts +9 -0
  89. package/dist/io/tty.js +5 -0
  90. package/dist/links.d.ts +1 -0
  91. package/dist/links.js +1 -0
  92. package/dist/memory-flow-hud.js +8 -16
  93. package/dist/public-ingest.js +50 -15
  94. package/dist/reveal-password-prompt.d.ts +24 -0
  95. package/dist/reveal-password-prompt.js +78 -0
  96. package/dist/scan.js +18 -2
  97. package/dist/setup-agents.js +1 -5
  98. package/dist/setup-databases.d.ts +1 -0
  99. package/dist/setup-databases.js +23 -3
  100. package/dist/setup-demo-tour.js +1 -0
  101. package/dist/setup-embeddings.js +1 -1
  102. package/dist/setup-models.d.ts +1 -14
  103. package/dist/setup-models.js +116 -340
  104. package/dist/setup-prompts.js +4 -7
  105. package/dist/setup-sources.js +7 -7
  106. package/dist/setup.d.ts +26 -1
  107. package/dist/setup.js +78 -7
  108. package/dist/sl.d.ts +2 -2
  109. package/dist/sl.js +20 -4
  110. package/dist/sql.js +18 -2
  111. package/dist/star-prompt/cache.d.ts +16 -0
  112. package/dist/star-prompt/cache.js +45 -0
  113. package/dist/star-prompt/star-count.d.ts +7 -0
  114. package/dist/star-prompt/star-count.js +66 -0
  115. package/dist/star-prompt/star-line.d.ts +12 -0
  116. package/dist/star-prompt/star-line.js +26 -0
  117. package/dist/telemetry/command-hook.d.ts +24 -0
  118. package/dist/telemetry/command-hook.js +37 -3
  119. package/dist/telemetry/emitter.d.ts +10 -0
  120. package/dist/telemetry/emitter.js +31 -0
  121. package/dist/telemetry/events.d.ts +24 -0
  122. package/dist/telemetry/events.js +15 -0
  123. package/dist/telemetry/exception.d.ts +18 -0
  124. package/dist/telemetry/exception.js +162 -0
  125. package/dist/telemetry/index.d.ts +4 -3
  126. package/dist/telemetry/index.js +3 -2
  127. package/dist/telemetry/redaction-secrets.d.ts +11 -0
  128. package/dist/telemetry/redaction-secrets.js +92 -0
  129. package/dist/update-check/cache.d.ts +21 -0
  130. package/dist/update-check/cache.js +38 -0
  131. package/dist/update-check/channel.d.ts +15 -0
  132. package/dist/update-check/channel.js +30 -0
  133. package/dist/update-check/registry.d.ts +1 -0
  134. package/dist/update-check/registry.js +45 -0
  135. package/dist/update-check/update-check.d.ts +43 -0
  136. package/dist/update-check/update-check.js +116 -0
  137. package/package.json +8 -1
  138. package/dist/context/connections/local-query-executor.d.ts +0 -6
  139. package/dist/context/connections/local-query-executor.js +0 -39
  140. package/dist/context/connections/postgres-query-executor.d.ts +0 -25
  141. package/dist/context/connections/postgres-query-executor.js +0 -53
  142. package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
  143. 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>;
@@ -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;
@@ -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>;