@kaelio/ktx 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/assets/python/{kaelio_ktx-0.8.0-py3-none-any.whl → kaelio_ktx-0.10.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 +42 -2
  8. package/dist/cli-runtime.d.ts +3 -0
  9. package/dist/cli-runtime.js +94 -3
  10. package/dist/commands/setup-commands.js +3 -4
  11. package/dist/connection-recovery.d.ts +34 -0
  12. package/dist/connection-recovery.js +82 -0
  13. package/dist/connection.js +26 -2
  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/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
  36. package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
  37. package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
  38. package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
  39. package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
  40. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +30 -0
  41. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +194 -0
  42. package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
  43. package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
  44. package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
  45. package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
  46. package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
  47. package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
  48. package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
  49. package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
  50. package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
  51. package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
  52. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
  53. package/dist/context/ingest/final-gate-repair.d.ts +1 -0
  54. package/dist/context/ingest/final-gate-repair.js +1 -0
  55. package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
  56. package/dist/context/ingest/ingest-bundle.runner.js +127 -53
  57. package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
  58. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
  59. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
  60. package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
  61. package/dist/context/ingest/local-adapters.js +21 -4
  62. package/dist/context/ingest/local-bundle-runtime.js +13 -5
  63. package/dist/context/ingest/local-ingest.d.ts +1 -0
  64. package/dist/context/ingest/local-ingest.js +13 -3
  65. package/dist/context/ingest/memory-flow/events.js +1 -1
  66. package/dist/context/ingest/memory-flow/schema.js +8 -3
  67. package/dist/context/ingest/memory-flow/types.d.ts +7 -3
  68. package/dist/context/ingest/ports.d.ts +3 -5
  69. package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
  70. package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
  71. package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
  72. package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
  73. package/dist/context/ingest/types.d.ts +1 -0
  74. package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
  75. package/dist/context/llm/ai-sdk-runtime.js +152 -16
  76. package/dist/context/llm/claude-code-runtime.d.ts +6 -4
  77. package/dist/context/llm/claude-code-runtime.js +127 -48
  78. package/dist/context/llm/codex-exec-events.d.ts +20 -0
  79. package/dist/context/llm/codex-exec-events.js +155 -0
  80. package/dist/context/llm/codex-isolation.d.ts +3 -0
  81. package/dist/context/llm/codex-isolation.js +5 -0
  82. package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
  83. package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
  84. package/dist/context/llm/codex-models.d.ts +2 -0
  85. package/dist/context/llm/codex-models.js +17 -0
  86. package/dist/context/llm/codex-runtime-config.d.ts +16 -0
  87. package/dist/context/llm/codex-runtime-config.js +19 -0
  88. package/dist/context/llm/codex-runtime.d.ts +37 -0
  89. package/dist/context/llm/codex-runtime.js +347 -0
  90. package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
  91. package/dist/context/llm/codex-sdk-runner.js +63 -0
  92. package/dist/context/llm/local-config.d.ts +16 -4
  93. package/dist/context/llm/local-config.js +18 -2
  94. package/dist/context/llm/rate-limit-governor.d.ts +103 -0
  95. package/dist/context/llm/rate-limit-governor.js +285 -0
  96. package/dist/context/llm/runtime-port.d.ts +3 -6
  97. package/dist/context/mcp/context-tools.js +43 -13
  98. package/dist/context/project/config.d.ts +14 -0
  99. package/dist/context/project/config.js +37 -2
  100. package/dist/context/scan/types.d.ts +15 -2
  101. package/dist/context/scan/types.js +12 -0
  102. package/dist/context/sl/description-normalization.js +4 -14
  103. package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
  104. package/dist/context/sql-analysis/ports.d.ts +12 -2
  105. package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
  106. package/dist/context-build-view.d.ts +13 -0
  107. package/dist/context-build-view.js +63 -32
  108. package/dist/demo-metrics.d.ts +0 -2
  109. package/dist/demo-metrics.js +1 -11
  110. package/dist/ingest.d.ts +1 -0
  111. package/dist/ingest.js +32 -3
  112. package/dist/io/buffered-command-io.d.ts +11 -0
  113. package/dist/io/buffered-command-io.js +28 -0
  114. package/dist/io/symbols.d.ts +2 -0
  115. package/dist/io/symbols.js +2 -0
  116. package/dist/llm/types.d.ts +1 -1
  117. package/dist/local-adapters.d.ts +10 -2
  118. package/dist/local-adapters.js +19 -3
  119. package/dist/memory-flow-hud.js +8 -16
  120. package/dist/next-steps.js +1 -2
  121. package/dist/progress-port-adapter.d.ts +6 -0
  122. package/dist/progress-port-adapter.js +18 -0
  123. package/dist/public-ingest.d.ts +20 -1
  124. package/dist/public-ingest.js +228 -42
  125. package/dist/reveal-password-prompt.d.ts +24 -0
  126. package/dist/reveal-password-prompt.js +78 -0
  127. package/dist/scan.js +21 -3
  128. package/dist/setup-context.d.ts +2 -0
  129. package/dist/setup-context.js +133 -27
  130. package/dist/setup-databases.d.ts +18 -1
  131. package/dist/setup-databases.js +378 -249
  132. package/dist/setup-demo-tour.js +1 -0
  133. package/dist/setup-embeddings.js +1 -1
  134. package/dist/setup-models.d.ts +11 -15
  135. package/dist/setup-models.js +140 -276
  136. package/dist/setup-prompts.js +3 -2
  137. package/dist/setup-ready-menu.d.ts +16 -2
  138. package/dist/setup-ready-menu.js +37 -5
  139. package/dist/setup-sources.js +115 -35
  140. package/dist/setup.d.ts +1 -1
  141. package/dist/setup.js +23 -11
  142. package/dist/sl.d.ts +2 -2
  143. package/dist/sl.js +20 -4
  144. package/dist/sql.js +18 -2
  145. package/dist/star-prompt/cache.d.ts +16 -0
  146. package/dist/star-prompt/cache.js +45 -0
  147. package/dist/star-prompt/star-count.d.ts +7 -0
  148. package/dist/star-prompt/star-count.js +66 -0
  149. package/dist/star-prompt/star-line.d.ts +12 -0
  150. package/dist/star-prompt/star-line.js +26 -0
  151. package/dist/status-project.d.ts +11 -0
  152. package/dist/status-project.js +50 -1
  153. package/dist/telemetry/command-hook.d.ts +1 -0
  154. package/dist/telemetry/command-hook.js +3 -1
  155. package/dist/telemetry/emitter.d.ts +10 -0
  156. package/dist/telemetry/emitter.js +31 -0
  157. package/dist/telemetry/events.d.ts +35 -6
  158. package/dist/telemetry/events.js +25 -2
  159. package/dist/telemetry/exception.d.ts +18 -0
  160. package/dist/telemetry/exception.js +162 -0
  161. package/dist/telemetry/identity.d.ts +0 -1
  162. package/dist/telemetry/identity.js +6 -6
  163. package/dist/telemetry/index.d.ts +15 -2
  164. package/dist/telemetry/index.js +15 -3
  165. package/dist/telemetry/redaction-secrets.d.ts +11 -0
  166. package/dist/telemetry/redaction-secrets.js +92 -0
  167. package/dist/telemetry/scrubber.d.ts +10 -0
  168. package/dist/telemetry/scrubber.js +20 -0
  169. package/dist/update-check/cache.d.ts +21 -0
  170. package/dist/update-check/cache.js +38 -0
  171. package/dist/update-check/channel.d.ts +15 -0
  172. package/dist/update-check/channel.js +30 -0
  173. package/dist/update-check/registry.d.ts +1 -0
  174. package/dist/update-check/registry.js +45 -0
  175. package/dist/update-check/update-check.d.ts +43 -0
  176. package/dist/update-check/update-check.js +116 -0
  177. package/package.json +12 -4
  178. package/dist/context/connections/local-query-executor.d.ts +0 -6
  179. package/dist/context/connections/local-query-executor.js +0 -39
  180. package/dist/context/connections/postgres-query-executor.d.ts +0 -25
  181. package/dist/context/connections/postgres-query-executor.js +0 -53
  182. package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
  183. package/dist/context/connections/sqlite-query-executor.js +0 -74
@@ -0,0 +1,116 @@
1
+ import { cyan, dim } from '../clack.js';
2
+ import { resolveOutputMode } from '../io/mode.js';
3
+ import { readUpdateCheckCache, writeUpdateCheckCache } from './cache.js';
4
+ import { decideUpdate, inferUpdateChannel } from './channel.js';
5
+ import { fetchDistTags as defaultFetchDistTags } from './registry.js';
6
+ const DAY_MS = 24 * 60 * 60 * 1000;
7
+ function truthy(value) {
8
+ return value !== undefined && value !== '' && value !== '0' && value !== 'false';
9
+ }
10
+ function commandRequestsJson(options) {
11
+ return options?.json === true || options?.output === 'json' || options?.format === 'json';
12
+ }
13
+ /** @internal */
14
+ export function shouldSuppressUpdateCheck(args) {
15
+ const env = args.env ?? process.env;
16
+ if (truthy(env.KTX_NO_UPDATE_CHECK) || truthy(env.NO_UPDATE_NOTIFIER) || truthy(env.DO_NOT_TRACK)) {
17
+ return true;
18
+ }
19
+ if (commandRequestsJson(args.commandOptions) || truthy(env.CI) || args.io.stdout.isTTY !== true) {
20
+ return true;
21
+ }
22
+ try {
23
+ const mode = resolveOutputMode({
24
+ json: false,
25
+ io: args.io,
26
+ env,
27
+ });
28
+ return mode !== 'pretty';
29
+ }
30
+ catch {
31
+ return true;
32
+ }
33
+ }
34
+ /** @internal */
35
+ export function renderUpdateNotice(args) {
36
+ const command = args.channel === 'next' ? 'npm i -g @kaelio/ktx@next' : 'npm i -g @kaelio/ktx';
37
+ return `${cyan('↑', args.env)} Update available: ktx ${args.installedVersion} → ${args.targetVersion}\n ${dim(command, args.env)}\n`;
38
+ }
39
+ function timestampMs(value) {
40
+ if (!value) {
41
+ return null;
42
+ }
43
+ const parsed = Date.parse(value);
44
+ return Number.isNaN(parsed) ? null : parsed;
45
+ }
46
+ function elapsedAtLeast(value, now, intervalMs) {
47
+ const previous = timestampMs(value);
48
+ if (previous === null) {
49
+ return true;
50
+ }
51
+ return now.getTime() - previous >= intervalMs;
52
+ }
53
+ function shouldRefreshCache(cache, installedVersion, now) {
54
+ if (!cache || cache.installedVersion !== installedVersion) {
55
+ return true;
56
+ }
57
+ return elapsedAtLeast(cache.checkedAt, now, DAY_MS);
58
+ }
59
+ async function refreshUpdateCache(args) {
60
+ const distTags = await args.fetchDistTags();
61
+ const decision = decideUpdate(args.installedVersion, distTags);
62
+ if (decision.status === 'skip') {
63
+ return;
64
+ }
65
+ await writeUpdateCheckCache({
66
+ checkedAt: args.now.toISOString(),
67
+ channel: decision.channel,
68
+ installedVersion: args.installedVersion,
69
+ latestForChannel: decision.target,
70
+ ...(args.cache?.installedVersion === args.installedVersion && args.cache.channel === decision.channel
71
+ ? { lastNoticeAt: args.cache.lastNoticeAt }
72
+ : {}),
73
+ }, { homeDir: args.homeDir });
74
+ }
75
+ export async function prepareUpdateCheckNotice(options) {
76
+ const env = options.env ?? process.env;
77
+ const now = (options.now ?? (() => new Date()))();
78
+ const fetchDistTags = options.fetchDistTags ?? defaultFetchDistTags;
79
+ if (shouldSuppressUpdateCheck({
80
+ commandOptions: options.commandOptions,
81
+ env,
82
+ io: options.io,
83
+ })) {
84
+ return { notice: null };
85
+ }
86
+ if (!inferUpdateChannel(options.installedVersion)) {
87
+ return { notice: null };
88
+ }
89
+ let cache = await readUpdateCheckCache({ homeDir: options.homeDir });
90
+ let notice = null;
91
+ if (cache?.installedVersion === options.installedVersion) {
92
+ const decision = decideUpdate(options.installedVersion, {
93
+ [cache.channel]: cache.latestForChannel,
94
+ });
95
+ if (decision.status === 'available' && elapsedAtLeast(cache.lastNoticeAt, now, DAY_MS)) {
96
+ notice = renderUpdateNotice({
97
+ channel: decision.channel,
98
+ env,
99
+ installedVersion: options.installedVersion,
100
+ targetVersion: decision.target,
101
+ });
102
+ cache = { ...cache, lastNoticeAt: now.toISOString() };
103
+ await writeUpdateCheckCache(cache, { homeDir: options.homeDir });
104
+ }
105
+ }
106
+ if (shouldRefreshCache(cache, options.installedVersion, now)) {
107
+ void refreshUpdateCache({
108
+ cache,
109
+ fetchDistTags,
110
+ homeDir: options.homeDir,
111
+ installedVersion: options.installedVersion,
112
+ now,
113
+ }).catch(() => { });
114
+ }
115
+ return { notice };
116
+ }
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@kaelio/ktx",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Standalone ktx context layer for data agents",
5
+ "author": {
6
+ "name": "Kaelio",
7
+ "url": "https://www.kaelio.com"
8
+ },
5
9
  "type": "module",
6
10
  "engines": {
7
11
  "node": ">=22.0.0"
@@ -32,6 +36,7 @@
32
36
  "@ai-sdk/devtools": "0.0.18",
33
37
  "@ai-sdk/google-vertex": "^4.0.134",
34
38
  "@anthropic-ai/claude-agent-sdk": "0.3.146",
39
+ "@clack/core": "1.3.1",
35
40
  "@clack/prompts": "1.4.0",
36
41
  "@clickhouse/client": "^1.18.5",
37
42
  "@commander-js/extra-typings": "14.0.0",
@@ -41,6 +46,7 @@
41
46
  "@looker/sdk-rtl": "^21.6.5",
42
47
  "@modelcontextprotocol/sdk": "^1.29.0",
43
48
  "@notionhq/client": "^5.22.0",
49
+ "@openai/codex-sdk": "^0.133.0",
44
50
  "ai": "^6.0.188",
45
51
  "better-sqlite3": "^12.10.0",
46
52
  "commander": "14.0.3",
@@ -56,6 +62,7 @@
56
62
  "pg": "^8.21.0",
57
63
  "posthog-node": "^5.34.9",
58
64
  "react": "^19.2.6",
65
+ "semver": "^7.8.1",
59
66
  "simple-git": "3.36.0",
60
67
  "snowflake-sdk": "^2.4.2",
61
68
  "yaml": "^2.9.0",
@@ -69,6 +76,7 @@
69
76
  "@types/node": "^25.9.1",
70
77
  "@types/pg": "^8.20.0",
71
78
  "@types/react": "^19.2.15",
79
+ "@types/semver": "^7.7.1",
72
80
  "@vitest/coverage-v8": "^4.1.7",
73
81
  "ajv": "8.20.0",
74
82
  "ink-testing-library": "^4.0.0",
@@ -78,13 +86,13 @@
78
86
  "license": "Apache-2.0",
79
87
  "repository": {
80
88
  "type": "git",
81
- "url": "https://github.com/Kaelio/ktx-ai-data-agents-context",
89
+ "url": "https://github.com/Kaelio/ktx",
82
90
  "directory": "packages/cli"
83
91
  },
84
92
  "bugs": {
85
- "url": "https://github.com/Kaelio/ktx-ai-data-agents-context/issues"
93
+ "url": "https://github.com/Kaelio/ktx/issues"
86
94
  },
87
- "homepage": "https://github.com/Kaelio/ktx-ai-data-agents-context#readme",
95
+ "homepage": "https://github.com/Kaelio/ktx#readme",
88
96
  "scripts": {
89
97
  "assets:demo": "node scripts/build-demo-assets.mjs",
90
98
  "build": "tsc -p tsconfig.json && node dist/telemetry/schema-writer.js src/telemetry/events.schema.json ../../python/ktx-daemon/src/ktx_daemon/telemetry/events.schema.json && node scripts/copy-runtime-assets.mjs && node ../../scripts/prepare-cli-bin.mjs",
@@ -1,6 +0,0 @@
1
- import type { KtxSqlQueryExecutorPort } from './query-executor.js';
2
- export interface DefaultLocalQueryExecutorOptions {
3
- postgres?: KtxSqlQueryExecutorPort;
4
- sqlite?: KtxSqlQueryExecutorPort;
5
- }
6
- export declare function createDefaultLocalQueryExecutor(options?: DefaultLocalQueryExecutorOptions): KtxSqlQueryExecutorPort;
@@ -1,39 +0,0 @@
1
- import { driverRegistrations, getDriverRegistration } from './drivers.js';
2
- import { createPostgresQueryExecutor } from './postgres-query-executor.js';
3
- import { createSqliteQueryExecutor } from './sqlite-query-executor.js';
4
- function driverFor(input) {
5
- return String(input.connection?.driver ?? '').toLowerCase();
6
- }
7
- function localExecutorMap(options) {
8
- const wiredExecutors = {
9
- postgres: options.postgres ?? createPostgresQueryExecutor(),
10
- sqlite: options.sqlite ?? createSqliteQueryExecutor(),
11
- };
12
- const executors = {};
13
- for (const registration of Object.values(driverRegistrations)) {
14
- if (!registration.hasLocalQueryExecutor)
15
- continue;
16
- const executor = wiredExecutors[registration.driver];
17
- if (executor) {
18
- executors[registration.driver] = executor;
19
- }
20
- }
21
- return executors;
22
- }
23
- export function createDefaultLocalQueryExecutor(options = {}) {
24
- const executors = localExecutorMap(options);
25
- return {
26
- async execute(input) {
27
- const driver = driverFor(input);
28
- const registration = getDriverRegistration(driver);
29
- if (!registration?.hasLocalQueryExecutor) {
30
- throw new Error(`No local query executor is configured for driver "${input.connection?.driver ?? 'unknown'}".`);
31
- }
32
- const executor = executors[registration.driver];
33
- if (!executor) {
34
- throw new Error(`Local query executor flag is enabled for driver "${registration.driver}", but no executor factory is wired.`);
35
- }
36
- return executor.execute(input);
37
- },
38
- };
39
- }
@@ -1,25 +0,0 @@
1
- import { type ClientConfig } from 'pg';
2
- import type { KtxSqlQueryExecutorPort } from './query-executor.js';
3
- interface PgClientLike {
4
- connect(): Promise<unknown>;
5
- query(input: string | {
6
- text: string;
7
- rowMode: 'array';
8
- }): Promise<{
9
- fields: Array<{
10
- name: string;
11
- }>;
12
- rows: unknown[][];
13
- command: string;
14
- rowCount: number | null;
15
- }>;
16
- end(): Promise<void>;
17
- }
18
- interface PostgresQueryExecutorOptions {
19
- statementTimeoutMs?: number;
20
- queryTimeoutMs?: number;
21
- connectionTimeoutMs?: number;
22
- clientFactory?: (config: ClientConfig) => PgClientLike;
23
- }
24
- export declare function createPostgresQueryExecutor(options?: PostgresQueryExecutorOptions): KtxSqlQueryExecutorPort;
25
- export {};
@@ -1,53 +0,0 @@
1
- import { Client } from 'pg';
2
- import { limitSqlForExecution } from './read-only-sql.js';
3
- function connectionDriver(input) {
4
- return String(input.connection?.driver ?? '').toLowerCase();
5
- }
6
- function createDefaultClient(config) {
7
- return new Client(config);
8
- }
9
- export function createPostgresQueryExecutor(options = {}) {
10
- const clientFactory = options.clientFactory ?? createDefaultClient;
11
- return {
12
- async execute(input) {
13
- const driver = connectionDriver(input);
14
- const connection = input.connection;
15
- if (driver !== 'postgres') {
16
- throw new Error(`Local Postgres execution cannot run driver "${connection?.driver ?? 'unknown'}".`);
17
- }
18
- if (typeof connection?.url !== 'string' || connection.url.trim().length === 0) {
19
- throw new Error(`Local Postgres execution requires connections.${input.connectionId}.url.`);
20
- }
21
- const client = clientFactory({
22
- connectionString: connection.url,
23
- statement_timeout: options.statementTimeoutMs ?? 30_000,
24
- query_timeout: options.queryTimeoutMs ?? 35_000,
25
- connectionTimeoutMillis: options.connectionTimeoutMs ?? 5_000,
26
- application_name: 'ktx-local-query',
27
- });
28
- await client.connect();
29
- try {
30
- await client.query('BEGIN READ ONLY');
31
- const result = await client.query({
32
- text: limitSqlForExecution(input.sql, input.maxRows),
33
- rowMode: 'array',
34
- });
35
- await client.query('COMMIT');
36
- return {
37
- headers: result.fields.map((field) => field.name),
38
- rows: result.rows,
39
- totalRows: result.rows.length,
40
- command: result.command,
41
- rowCount: result.rowCount,
42
- };
43
- }
44
- catch (error) {
45
- await client.query('ROLLBACK').catch(() => undefined);
46
- throw error;
47
- }
48
- finally {
49
- await client.end();
50
- }
51
- },
52
- };
53
- }
@@ -1,4 +0,0 @@
1
- import type { KtxSqlQueryExecutionInput, KtxSqlQueryExecutorPort } from './query-executor.js';
2
- /** @internal */
3
- export declare function sqliteDatabasePathFromConnection(input: KtxSqlQueryExecutionInput): string;
4
- export declare function createSqliteQueryExecutor(): KtxSqlQueryExecutorPort;
@@ -1,74 +0,0 @@
1
- import { isAbsolute, resolve } from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- import Database from 'better-sqlite3';
4
- import { readFileSync } from 'node:fs';
5
- import { homedir } from 'node:os';
6
- import { normalizeQueryRows } from './query-executor.js';
7
- import { limitSqlForExecution } from './read-only-sql.js';
8
- function connectionDriver(input) {
9
- return String(input.connection?.driver ?? '').toLowerCase();
10
- }
11
- function stringConfigValue(connection, key) {
12
- const value = connection?.[key];
13
- return typeof value === 'string' && value.trim().length > 0 ? resolveStringReference(key, value.trim()) : undefined;
14
- }
15
- function resolveStringReference(key, value) {
16
- if (value.startsWith('env:')) {
17
- return process.env[value.slice('env:'.length)] ?? '';
18
- }
19
- if (key !== 'url' && value.startsWith('file:')) {
20
- const rawPath = value.slice('file:'.length);
21
- const path = rawPath.startsWith('~') ? resolve(homedir(), rawPath.slice(1)) : rawPath;
22
- return readFileSync(path, 'utf-8').trim();
23
- }
24
- return value;
25
- }
26
- function sqlitePathFromUrl(url) {
27
- if (url.startsWith('file:')) {
28
- return fileURLToPath(url);
29
- }
30
- if (url.startsWith('sqlite:')) {
31
- const parsed = new URL(url);
32
- if (parsed.pathname.length > 0) {
33
- return decodeURIComponent(parsed.pathname);
34
- }
35
- }
36
- return url;
37
- }
38
- /** @internal */
39
- export function sqliteDatabasePathFromConnection(input) {
40
- const driver = connectionDriver(input);
41
- if (driver !== 'sqlite') {
42
- throw new Error(`Local SQLite execution cannot run driver "${input.connection?.driver ?? 'unknown'}".`);
43
- }
44
- const pathValue = stringConfigValue(input.connection, 'path');
45
- const urlValue = stringConfigValue(input.connection, 'url');
46
- if (!pathValue && !urlValue) {
47
- throw new Error(`Local SQLite execution requires connections.${input.connectionId}.path or connections.${input.connectionId}.url.`);
48
- }
49
- const candidate = pathValue ?? sqlitePathFromUrl(urlValue);
50
- return isAbsolute(candidate) ? candidate : resolve(input.projectDir ?? process.cwd(), candidate);
51
- }
52
- export function createSqliteQueryExecutor() {
53
- return {
54
- async execute(input) {
55
- const sql = limitSqlForExecution(input.sql, input.maxRows);
56
- const dbPath = sqliteDatabasePathFromConnection(input);
57
- const db = new Database(dbPath, { readonly: true, fileMustExist: true });
58
- try {
59
- const statement = db.prepare(sql);
60
- const rows = statement.all();
61
- return {
62
- headers: statement.columns().map((column) => column.name),
63
- rows: normalizeQueryRows(rows),
64
- totalRows: rows.length,
65
- command: 'SELECT',
66
- rowCount: rows.length,
67
- };
68
- }
69
- finally {
70
- db.close();
71
- }
72
- },
73
- };
74
- }