@kaelio/ktx 0.1.0-rc.5 → 0.1.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 (180) hide show
  1. package/assets/python/kaelio_ktx-0.1.0-py3-none-any.whl +0 -0
  2. package/assets/python/manifest.json +2 -2
  3. package/dist/clack.d.ts +6 -0
  4. package/dist/clack.js +23 -0
  5. package/dist/cli-program.js +5 -2
  6. package/dist/cli-program.test.js +7 -1
  7. package/dist/cli-runtime.d.ts +4 -0
  8. package/dist/cli-runtime.js +8 -1
  9. package/dist/command-schemas.d.ts +1 -1
  10. package/dist/commands/ingest-commands.js +1 -0
  11. package/dist/commands/knowledge-commands.js +5 -0
  12. package/dist/commands/mcp-commands.js +11 -3
  13. package/dist/commands/mcp-commands.test.js +30 -1
  14. package/dist/commands/sql-commands.d.ts +3 -0
  15. package/dist/commands/sql-commands.js +43 -0
  16. package/dist/commands/sql-commands.test.d.ts +1 -0
  17. package/dist/commands/sql-commands.test.js +68 -0
  18. package/dist/context-build-view.js +5 -1
  19. package/dist/dev.test.js +27 -0
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +1 -0
  22. package/dist/index.test.js +56 -21
  23. package/dist/ingest.js +123 -18
  24. package/dist/ingest.test.js +206 -0
  25. package/dist/io/print-list.d.ts +2 -1
  26. package/dist/io/print-list.js +7 -0
  27. package/dist/io/print-list.test.js +13 -11
  28. package/dist/io/symbols.d.ts +2 -2
  29. package/dist/knowledge.d.ts +1 -0
  30. package/dist/knowledge.js +34 -16
  31. package/dist/knowledge.test.js +27 -0
  32. package/dist/managed-python-command.d.ts +2 -0
  33. package/dist/managed-python-command.js +17 -9
  34. package/dist/managed-python-command.test.js +59 -4
  35. package/dist/next-steps.js +1 -1
  36. package/dist/next-steps.test.js +2 -0
  37. package/dist/print-command-tree.js +7 -1
  38. package/dist/public-ingest.d.ts +9 -1
  39. package/dist/public-ingest.js +50 -7
  40. package/dist/public-ingest.test.js +69 -2
  41. package/dist/release-version.d.ts +5 -0
  42. package/dist/release-version.js +44 -0
  43. package/dist/runtime-requirements.d.ts +23 -0
  44. package/dist/runtime-requirements.js +99 -0
  45. package/dist/runtime-requirements.test.d.ts +1 -0
  46. package/dist/runtime-requirements.test.js +63 -0
  47. package/dist/setup-agents.d.ts +11 -3
  48. package/dist/setup-agents.js +397 -134
  49. package/dist/setup-agents.test.js +359 -61
  50. package/dist/setup-embeddings.js +3 -6
  51. package/dist/setup-embeddings.test.js +18 -2
  52. package/dist/setup-models.js +2 -2
  53. package/dist/setup-models.test.js +5 -3
  54. package/dist/setup-ready-menu.d.ts +1 -1
  55. package/dist/setup-ready-menu.js +2 -0
  56. package/dist/setup-ready-menu.test.js +3 -0
  57. package/dist/setup-runtime.d.ts +45 -0
  58. package/dist/setup-runtime.js +47 -0
  59. package/dist/setup-runtime.test.d.ts +1 -0
  60. package/dist/setup-runtime.test.js +110 -0
  61. package/dist/setup-sources-notion.test.d.ts +1 -0
  62. package/dist/setup-sources-notion.test.js +107 -0
  63. package/dist/setup-sources.js +5 -2
  64. package/dist/setup.d.ts +19 -1
  65. package/dist/setup.js +104 -29
  66. package/dist/setup.test.js +221 -57
  67. package/dist/sl.js +2 -2
  68. package/dist/sl.test.js +10 -0
  69. package/dist/source-mapping.js +9 -1
  70. package/dist/source-mapping.test.d.ts +1 -0
  71. package/dist/source-mapping.test.js +65 -0
  72. package/dist/sql.d.ts +22 -0
  73. package/dist/sql.js +125 -0
  74. package/dist/sql.test.d.ts +1 -0
  75. package/dist/sql.test.js +226 -0
  76. package/node_modules/@ktx/connector-clickhouse/dist/package-exports.test.js +1 -1
  77. package/node_modules/@ktx/context/dist/connections/connection-type.d.ts +4 -4
  78. package/node_modules/@ktx/context/dist/core/git.service.d.ts +3 -0
  79. package/node_modules/@ktx/context/dist/core/git.service.js +47 -1
  80. package/node_modules/@ktx/context/dist/core/git.service.patch.test.d.ts +1 -0
  81. package/node_modules/@ktx/context/dist/core/git.service.patch.test.js +40 -0
  82. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/types.d.ts +5 -5
  83. package/node_modules/@ktx/context/dist/ingest/adapters/looker/looker.adapter.d.ts +2 -2
  84. package/node_modules/@ktx/context/dist/ingest/adapters/looker/tools/looker-query-to-sl.tool.d.ts +2 -2
  85. package/node_modules/@ktx/context/dist/ingest/adapters/looker/types.d.ts +16 -16
  86. package/node_modules/@ktx/context/dist/ingest/adapters/lookml/pull-config.d.ts +1 -1
  87. package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.js +16 -0
  88. package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.test.js +41 -0
  89. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.d.ts +2 -1
  90. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.js +40 -0
  91. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.test.js +116 -1
  92. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.d.ts +29 -0
  93. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.js +40 -0
  94. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/pull-config.d.ts +1 -1
  95. package/node_modules/@ktx/context/dist/ingest/artifact-gates.d.ts +25 -0
  96. package/node_modules/@ktx/context/dist/ingest/artifact-gates.js +149 -0
  97. package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.d.ts +1 -0
  98. package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.js +167 -0
  99. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.d.ts +29 -0
  100. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.js +178 -0
  101. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.d.ts +1 -0
  102. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.js +109 -0
  103. package/node_modules/@ktx/context/dist/ingest/index.d.ts +8 -1
  104. package/node_modules/@ktx/context/dist/ingest/index.js +7 -0
  105. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.d.ts +18 -2
  106. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.d.ts +1 -0
  107. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.js +1761 -0
  108. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.js +1695 -901
  109. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.test.js +135 -118
  110. package/node_modules/@ktx/context/dist/ingest/ingest-trace.d.ts +50 -0
  111. package/node_modules/@ktx/context/dist/ingest/ingest-trace.js +88 -0
  112. package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.d.ts +1 -0
  113. package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.js +76 -0
  114. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.d.ts +16 -0
  115. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.js +78 -0
  116. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.d.ts +1 -0
  117. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.js +76 -0
  118. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.d.ts +58 -0
  119. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.js +223 -0
  120. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.d.ts +1 -0
  121. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.js +369 -0
  122. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.d.ts +23 -0
  123. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.js +190 -0
  124. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.d.ts +1 -0
  125. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.js +101 -0
  126. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.d.ts +15 -0
  127. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.js +61 -0
  128. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.d.ts +1 -0
  129. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.js +137 -0
  130. package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +7 -0
  131. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +54 -10
  132. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +65 -0
  133. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +23 -5
  134. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.js +17 -0
  135. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.test.js +1 -0
  136. package/node_modules/@ktx/context/dist/ingest/memory-flow/types.d.ts +6 -0
  137. package/node_modules/@ktx/context/dist/ingest/parsed-target-table.d.ts +1 -1
  138. package/node_modules/@ktx/context/dist/ingest/ports.d.ts +3 -0
  139. package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +32 -7
  140. package/node_modules/@ktx/context/dist/ingest/report-snapshot.js +25 -0
  141. package/node_modules/@ktx/context/dist/ingest/report-snapshot.test.js +124 -0
  142. package/node_modules/@ktx/context/dist/ingest/reports.d.ts +23 -0
  143. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.d.ts +11 -0
  144. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.js +26 -0
  145. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.d.ts +1 -0
  146. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.js +25 -0
  147. package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.d.ts +4 -0
  148. package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.js +4 -0
  149. package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.test.js +29 -0
  150. package/node_modules/@ktx/context/dist/ingest/tools/emit-unmapped-fallback.tool.d.ts +1 -1
  151. package/node_modules/@ktx/context/dist/ingest/types.d.ts +24 -0
  152. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.d.ts +24 -0
  153. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.js +111 -0
  154. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.d.ts +1 -0
  155. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.js +138 -0
  156. package/node_modules/@ktx/context/dist/llm/claude-code-runtime.js +19 -2
  157. package/node_modules/@ktx/context/dist/llm/claude-code-runtime.test.js +33 -0
  158. package/node_modules/@ktx/context/dist/project/setup-config.d.ts +1 -1
  159. package/node_modules/@ktx/context/dist/project/setup-config.js +10 -1
  160. package/node_modules/@ktx/context/dist/project/setup-config.test.js +3 -2
  161. package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.js +5 -1
  162. package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.test.js +15 -0
  163. package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.js +5 -1
  164. package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.test.js +22 -0
  165. package/node_modules/@ktx/context/dist/tools/action-target-connection.d.ts +9 -0
  166. package/node_modules/@ktx/context/dist/tools/action-target-connection.js +14 -0
  167. package/node_modules/@ktx/context/dist/tools/context-candidate-write.tool.d.ts +4 -4
  168. package/node_modules/@ktx/context/dist/tools/index.d.ts +1 -0
  169. package/node_modules/@ktx/context/dist/tools/index.js +1 -0
  170. package/node_modules/@ktx/context/dist/wiki/local-knowledge.js +4 -1
  171. package/node_modules/@ktx/context/dist/wiki/local-knowledge.test.js +44 -0
  172. package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.js +3 -48
  173. package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.test.js +28 -0
  174. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.d.ts +17 -0
  175. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.js +79 -0
  176. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.d.ts +1 -0
  177. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.js +64 -0
  178. package/node_modules/@ktx/context/prompts/memory_agent_bundle_ingest_work_unit.md +23 -4
  179. package/node_modules/@ktx/context/skills/ingest_triage/SKILL.md +7 -3
  180. package/package.json +4 -4
@@ -145,9 +145,25 @@ describe('setup embeddings step', () => {
145
145
  expect(config.scan.enrichment.embeddings).toMatchObject(config.ingest.embeddings);
146
146
  expect(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8')).not.toContain('completed_steps:');
147
147
  expect((await readKtxSetupState(tempDir)).completed_steps).toContain('embeddings');
148
- expect(spinnerEvents).toContainEqual('start:Testing local sentence-transformers embeddings (all-MiniLM-L6-v2, 384 dimensions). First run may take up to 60 seconds.');
148
+ expect(spinnerEvents).toContainEqual('start:Testing local embeddings (all-MiniLM-L6-v2)');
149
149
  expect(io.stdout()).toContain('Embeddings ready: yes');
150
150
  });
151
+ it('uses a short non-animated local embeddings health-check status by default', async () => {
152
+ const io = makeIo();
153
+ const healthCheck = vi.fn(async () => ({ ok: true }));
154
+ const prompts = makePromptAdapter({ selectValues: ['sentence-transformers'] });
155
+ const result = await runKtxSetupEmbeddingsStep({
156
+ projectDir: tempDir,
157
+ inputMode: 'auto',
158
+ cliVersion: '0.2.0',
159
+ runtimeInstallPolicy: 'auto',
160
+ skipEmbeddings: false,
161
+ }, io.io, { prompts, env: {}, healthCheck, ensureLocalEmbeddings: vi.fn(async () => managedDaemon()) });
162
+ expect(result.status).toBe('ready');
163
+ expect(io.stderr()).toContain('Testing local embeddings (all-MiniLM-L6-v2)');
164
+ expect(io.stderr()).not.toContain('First run may take up to 60 seconds');
165
+ expect(io.stderr().match(/Testing local embeddings/g)).toHaveLength(1);
166
+ });
151
167
  it('shows live progress while local sentence-transformers embeddings are being tested', async () => {
152
168
  const io = makeIo();
153
169
  const prompts = makePromptAdapter({ selectValues: ['sentence-transformers'] });
@@ -170,7 +186,7 @@ describe('setup embeddings step', () => {
170
186
  skipEmbeddings: false,
171
187
  }, io.io, { prompts, env: {}, healthCheck, ensureLocalEmbeddings: vi.fn(async () => managedDaemon()), spinner });
172
188
  await vi.waitFor(() => {
173
- expect(spinnerEvents).toContainEqual('start:Testing local sentence-transformers embeddings (all-MiniLM-L6-v2, 384 dimensions). First run may take up to 60 seconds.');
189
+ expect(spinnerEvents).toContainEqual('start:Testing local embeddings (all-MiniLM-L6-v2)');
174
190
  });
175
191
  expect(resolveHealthCheck).toBeDefined();
176
192
  resolveHealthCheck?.({ ok: true });
@@ -335,9 +335,9 @@ async function chooseBackend(args, io, deps) {
335
335
  const choice = await prompts.select({
336
336
  message: 'Which LLM provider should KTX use?',
337
337
  options: [
338
- { value: 'anthropic', label: 'Anthropic API' },
338
+ { value: 'claude-code', label: 'Claude subscription (Pro/Max)' },
339
+ { value: 'anthropic', label: 'Anthropic API key' },
339
340
  { value: 'vertex', label: 'Google Vertex AI for Anthropic Claude' },
340
- { value: 'claude-code', label: 'Local Claude Code session' },
341
341
  { value: 'back', label: 'Back' },
342
342
  ],
343
343
  });
@@ -120,16 +120,18 @@ describe('setup Anthropic model step', () => {
120
120
  ],
121
121
  }));
122
122
  });
123
- it('offers Vertex AI as an Anthropic model provider option', async () => {
123
+ it('offers Anthropic provider paths in the preferred order', async () => {
124
124
  const prompts = makePromptAdapter({ providerChoice: 'back' });
125
125
  const result = await runKtxSetupAnthropicModelStep({ projectDir: tempDir, inputMode: 'auto', skipLlm: false }, makeIo().io, { prompts, env: {} });
126
126
  expect(result.status).toBe('back');
127
127
  expect(prompts.select).toHaveBeenCalledWith(expect.objectContaining({
128
128
  message: expect.stringContaining('Which LLM provider should KTX use?'),
129
- options: expect.arrayContaining([
129
+ options: [
130
+ { value: 'claude-code', label: 'Claude subscription (Pro/Max)' },
131
+ { value: 'anthropic', label: 'Anthropic API key' },
130
132
  { value: 'vertex', label: 'Google Vertex AI for Anthropic Claude' },
131
133
  { value: 'back', label: 'Back' },
132
- ]),
134
+ ],
133
135
  }));
134
136
  });
135
137
  it('configures Claude Code backend and validates local auth', async () => {
@@ -1,6 +1,6 @@
1
1
  import { type KtxSetupPromptOption } from './setup-prompts.js';
2
2
  import type { KtxSetupStatus } from './setup.js';
3
- export type KtxSetupReadyAction = 'models' | 'embeddings' | 'databases' | 'sources' | 'context' | 'agents' | 'exit';
3
+ export type KtxSetupReadyAction = 'models' | 'embeddings' | 'databases' | 'sources' | 'runtime' | 'context' | 'agents' | 'exit';
4
4
  export interface KtxSetupReadyMenuPromptAdapter {
5
5
  select(options: {
6
6
  message: string;
@@ -5,6 +5,7 @@ export function isKtxPreAgentSetupReady(status) {
5
5
  status.embeddings.ready &&
6
6
  status.databases.every((database) => database.ready) &&
7
7
  status.sources.every((source) => source.ready) &&
8
+ status.runtime.ready &&
8
9
  status.context.ready);
9
10
  }
10
11
  export function isKtxSetupReady(status) {
@@ -22,6 +23,7 @@ export async function runKtxSetupReadyChangeMenu(status, deps = {}) {
22
23
  { value: 'embeddings', label: 'Embeddings' },
23
24
  { value: 'databases', label: 'Databases' },
24
25
  { value: 'sources', label: 'Context sources' },
26
+ ...(status.runtime.required ? [{ value: 'runtime', label: 'Runtime' }] : []),
25
27
  { value: 'context', label: 'Rebuild KTX context' },
26
28
  { value: 'agents', label: 'Agent integration' },
27
29
  { value: 'exit', label: 'Exit' },
@@ -6,6 +6,7 @@ const readyStatus = {
6
6
  embeddings: { backend: 'openai', ready: true, model: 'text-embedding-3-small', dimensions: 1536 },
7
7
  databases: [{ connectionId: 'warehouse', ready: true }],
8
8
  sources: [],
9
+ runtime: { required: false, ready: true, features: [] },
9
10
  context: { ready: true, status: 'completed' },
10
11
  agents: [{ target: 'codex', scope: 'project', ready: true }],
11
12
  };
@@ -13,6 +14,7 @@ describe('setup ready menu', () => {
13
14
  it('recognizes a ready setup only when required sections are ready', () => {
14
15
  expect(isKtxSetupReady(readyStatus)).toBe(true);
15
16
  expect(isKtxSetupReady({ ...readyStatus, embeddings: { ready: false } })).toBe(false);
17
+ expect(isKtxSetupReady({ ...readyStatus, runtime: { required: true, ready: false, features: ['core'] } })).toBe(false);
16
18
  expect(isKtxSetupReady({ ...readyStatus, context: { ready: false, status: 'not_started' } })).toBe(false);
17
19
  expect(isKtxSetupReady({ ...readyStatus, agents: [] })).toBe(false);
18
20
  });
@@ -20,6 +22,7 @@ describe('setup ready menu', () => {
20
22
  expect(isKtxPreAgentSetupReady(readyStatus)).toBe(true);
21
23
  expect(isKtxPreAgentSetupReady({ ...readyStatus, agents: [] })).toBe(true);
22
24
  expect(isKtxPreAgentSetupReady({ ...readyStatus, embeddings: { ready: false } })).toBe(false);
25
+ expect(isKtxPreAgentSetupReady({ ...readyStatus, runtime: { required: true, ready: false, features: ['core'] } })).toBe(false);
23
26
  expect(isKtxPreAgentSetupReady({ ...readyStatus, context: { ready: false, status: 'not_started' } })).toBe(false);
24
27
  });
25
28
  it('maps ready-project menu choices to setup sections', async () => {
@@ -0,0 +1,45 @@
1
+ import { type KtxLocalProject } from '@ktx/context/project';
2
+ import type { KtxCliIo } from './cli-runtime.js';
3
+ import { type ManagedLocalEmbeddingsDaemon } from './managed-local-embeddings.js';
4
+ import { type KtxManagedPythonInstallPolicy, type ManagedPythonCommandRuntime } from './managed-python-command.js';
5
+ import type { KtxRuntimeFeature } from './managed-python-runtime.js';
6
+ import { type KtxRuntimeRequirements } from './runtime-requirements.js';
7
+ export interface KtxSetupRuntimeArgs {
8
+ projectDir: string;
9
+ inputMode: 'auto' | 'disabled';
10
+ cliVersion: string;
11
+ runtimeInstallPolicy: KtxManagedPythonInstallPolicy;
12
+ databaseIntrospectionFallback?: boolean;
13
+ }
14
+ export type KtxSetupRuntimeResult = {
15
+ status: 'ready';
16
+ projectDir: string;
17
+ requirements: KtxRuntimeRequirements;
18
+ } | {
19
+ status: 'skipped';
20
+ projectDir: string;
21
+ requirements: KtxRuntimeRequirements;
22
+ } | {
23
+ status: 'failed';
24
+ projectDir: string;
25
+ requirements: KtxRuntimeRequirements;
26
+ };
27
+ export interface KtxSetupRuntimeDeps {
28
+ env?: NodeJS.ProcessEnv;
29
+ loadProject?: (options: {
30
+ projectDir: string;
31
+ }) => Promise<Pick<KtxLocalProject, 'config'>>;
32
+ ensureRuntime?: (options: {
33
+ cliVersion: string;
34
+ installPolicy: KtxManagedPythonInstallPolicy;
35
+ io: KtxCliIo;
36
+ feature: KtxRuntimeFeature;
37
+ }) => Promise<ManagedPythonCommandRuntime>;
38
+ ensureLocalEmbeddings?: (options: {
39
+ cliVersion: string;
40
+ projectDir: string;
41
+ installPolicy: KtxManagedPythonInstallPolicy;
42
+ io: KtxCliIo;
43
+ }) => Promise<ManagedLocalEmbeddingsDaemon>;
44
+ }
45
+ export declare function runKtxSetupRuntimeStep(args: KtxSetupRuntimeArgs, io: KtxCliIo, deps?: KtxSetupRuntimeDeps): Promise<KtxSetupRuntimeResult>;
@@ -0,0 +1,47 @@
1
+ import { loadKtxProject, markKtxSetupStateStepComplete, } from '@ktx/context/project';
2
+ import { ensureManagedLocalEmbeddingsDaemon, } from './managed-local-embeddings.js';
3
+ import { ensureManagedPythonCommandRuntime, } from './managed-python-command.js';
4
+ import { resolveProjectRuntimeRequirements, } from './runtime-requirements.js';
5
+ function formatRuntimeFeature(feature) {
6
+ return feature === 'local-embeddings' ? 'local embeddings' : 'core';
7
+ }
8
+ export async function runKtxSetupRuntimeStep(args, io, deps = {}) {
9
+ const loadProjectForRuntime = deps.loadProject ?? loadKtxProject;
10
+ const project = await loadProjectForRuntime({ projectDir: args.projectDir });
11
+ const requirements = resolveProjectRuntimeRequirements(project.config, {
12
+ databaseIntrospectionFallback: args.databaseIntrospectionFallback,
13
+ env: deps.env ?? process.env,
14
+ });
15
+ if (requirements.features.length === 0) {
16
+ io.stdout.write('│ Runtime setup skipped.\n');
17
+ return { status: 'skipped', projectDir: args.projectDir, requirements };
18
+ }
19
+ const ensureRuntime = deps.ensureRuntime ?? ensureManagedPythonCommandRuntime;
20
+ const ensureLocalEmbeddings = deps.ensureLocalEmbeddings ?? ensureManagedLocalEmbeddingsDaemon;
21
+ try {
22
+ for (const feature of requirements.features) {
23
+ if (feature === 'local-embeddings') {
24
+ await ensureLocalEmbeddings({
25
+ cliVersion: args.cliVersion,
26
+ projectDir: args.projectDir,
27
+ installPolicy: args.runtimeInstallPolicy,
28
+ io,
29
+ });
30
+ continue;
31
+ }
32
+ await ensureRuntime({
33
+ cliVersion: args.cliVersion,
34
+ installPolicy: args.runtimeInstallPolicy,
35
+ io,
36
+ feature,
37
+ });
38
+ }
39
+ }
40
+ catch (error) {
41
+ io.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
42
+ return { status: 'failed', projectDir: args.projectDir, requirements };
43
+ }
44
+ await markKtxSetupStateStepComplete(args.projectDir, 'runtime');
45
+ io.stdout.write(`│ Runtime ready: yes (${requirements.features.map(formatRuntimeFeature).join(', ')})\n`);
46
+ return { status: 'ready', projectDir: args.projectDir, requirements };
47
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,110 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { MANAGED_SENTENCE_TRANSFORMERS_BASE_URL } from '@ktx/context';
5
+ import { buildDefaultKtxProjectConfig, readKtxSetupState } from '@ktx/context/project';
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { runKtxSetupRuntimeStep } from './setup-runtime.js';
8
+ function makeIo() {
9
+ let stdout = '';
10
+ let stderr = '';
11
+ return {
12
+ io: {
13
+ stdout: {
14
+ write: (chunk) => {
15
+ stdout += chunk;
16
+ },
17
+ },
18
+ stderr: {
19
+ write: (chunk) => {
20
+ stderr += chunk;
21
+ },
22
+ },
23
+ },
24
+ stdout: () => stdout,
25
+ stderr: () => stderr,
26
+ };
27
+ }
28
+ function projectConfig(config) {
29
+ return vi.fn(async () => ({ config }));
30
+ }
31
+ describe('runKtxSetupRuntimeStep', () => {
32
+ let tempDir;
33
+ beforeEach(async () => {
34
+ tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-runtime-'));
35
+ });
36
+ afterEach(async () => {
37
+ await rm(tempDir, { recursive: true, force: true });
38
+ });
39
+ it('skips runtime setup when the project has no direct runtime requirements', async () => {
40
+ const io = makeIo();
41
+ const ensureRuntime = vi.fn();
42
+ await expect(runKtxSetupRuntimeStep({
43
+ projectDir: tempDir,
44
+ inputMode: 'auto',
45
+ cliVersion: '0.2.0',
46
+ runtimeInstallPolicy: 'prompt',
47
+ }, io.io, {
48
+ loadProject: projectConfig(buildDefaultKtxProjectConfig()),
49
+ ensureRuntime,
50
+ env: {},
51
+ })).resolves.toMatchObject({ status: 'skipped' });
52
+ expect(ensureRuntime).not.toHaveBeenCalled();
53
+ expect((await readKtxSetupState(tempDir)).completed_steps).not.toContain('runtime');
54
+ expect(io.stdout()).toContain('Runtime setup skipped.');
55
+ });
56
+ it('fails fast when required runtime features cannot be installed in no-input mode', async () => {
57
+ const io = makeIo();
58
+ const ensureRuntime = vi.fn(async () => {
59
+ throw new Error('KTX Python runtime is required for this command. Run: ktx dev runtime install --yes');
60
+ });
61
+ await expect(runKtxSetupRuntimeStep({
62
+ projectDir: tempDir,
63
+ inputMode: 'disabled',
64
+ cliVersion: '0.2.0',
65
+ runtimeInstallPolicy: 'never',
66
+ databaseIntrospectionFallback: true,
67
+ }, io.io, {
68
+ loadProject: projectConfig(buildDefaultKtxProjectConfig()),
69
+ ensureRuntime,
70
+ env: {},
71
+ })).resolves.toMatchObject({ status: 'failed' });
72
+ expect(ensureRuntime).toHaveBeenCalledWith(expect.objectContaining({ installPolicy: 'never' }));
73
+ expect((await readKtxSetupState(tempDir)).completed_steps).not.toContain('runtime');
74
+ expect(io.stderr()).toContain('ktx dev runtime install --yes');
75
+ });
76
+ it('starts the managed local embeddings daemon for configured sentence-transformers embeddings', async () => {
77
+ const io = makeIo();
78
+ const ensureLocalEmbeddings = vi.fn(async () => ({
79
+ baseUrl: 'http://127.0.0.1:61234',
80
+ env: { KTX_MANAGED_SENTENCE_TRANSFORMERS_BASE_URL: 'http://127.0.0.1:61234' },
81
+ }));
82
+ const config = {
83
+ ...buildDefaultKtxProjectConfig(),
84
+ ingest: {
85
+ ...buildDefaultKtxProjectConfig().ingest,
86
+ embeddings: {
87
+ backend: 'sentence-transformers',
88
+ model: 'all-MiniLM-L6-v2',
89
+ dimensions: 384,
90
+ sentenceTransformers: { base_url: MANAGED_SENTENCE_TRANSFORMERS_BASE_URL },
91
+ },
92
+ },
93
+ };
94
+ await expect(runKtxSetupRuntimeStep({
95
+ projectDir: tempDir,
96
+ inputMode: 'auto',
97
+ cliVersion: '0.2.0',
98
+ runtimeInstallPolicy: 'auto',
99
+ }, io.io, {
100
+ loadProject: projectConfig(config),
101
+ ensureLocalEmbeddings,
102
+ env: {},
103
+ })).resolves.toMatchObject({ status: 'ready' });
104
+ expect(ensureLocalEmbeddings).toHaveBeenCalledWith(expect.objectContaining({
105
+ projectDir: tempDir,
106
+ installPolicy: 'auto',
107
+ }));
108
+ expect(io.stdout()).toContain('Runtime ready: yes (local embeddings)');
109
+ });
110
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,107 @@
1
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { initKtxProject, parseKtxProjectConfig, serializeKtxProjectConfig, } from '@ktx/context/project';
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
+ import { runKtxSetupSourcesStep, } from './setup-sources.js';
7
+ const notionMocks = vi.hoisted(() => ({
8
+ tokens: [],
9
+ retrieveBotUser: vi.fn(async () => ({ name: 'Docs Bot' })),
10
+ retrievePage: vi.fn(async () => ({ id: 'page-1' })),
11
+ }));
12
+ vi.mock('@ktx/context/ingest', async (importOriginal) => {
13
+ const actual = await importOriginal();
14
+ return {
15
+ ...actual,
16
+ NotionClient: vi.fn().mockImplementation(function NotionClient(token) {
17
+ notionMocks.tokens.push(token);
18
+ return {
19
+ retrieveBotUser: notionMocks.retrieveBotUser,
20
+ retrievePage: notionMocks.retrievePage,
21
+ };
22
+ }),
23
+ };
24
+ });
25
+ function makeIo() {
26
+ let stdout = '';
27
+ let stderr = '';
28
+ return {
29
+ io: {
30
+ stdout: {
31
+ isTTY: true,
32
+ write: (chunk) => {
33
+ stdout += chunk;
34
+ },
35
+ },
36
+ stderr: {
37
+ write: (chunk) => {
38
+ stderr += chunk;
39
+ },
40
+ },
41
+ },
42
+ stdout: () => stdout,
43
+ stderr: () => stderr,
44
+ };
45
+ }
46
+ function prompts(values) {
47
+ const multiselectValues = [...(values.multiselect ?? [])];
48
+ const selectValues = [...(values.select ?? [])];
49
+ return {
50
+ multiselect: vi.fn(async () => multiselectValues.shift() ?? []),
51
+ select: vi.fn(async () => selectValues.shift() ?? 'back'),
52
+ text: vi.fn(async () => ''),
53
+ password: vi.fn(async () => undefined),
54
+ cancel: vi.fn(),
55
+ log: vi.fn(),
56
+ };
57
+ }
58
+ describe('setup sources Notion validation', () => {
59
+ let tempDir;
60
+ let projectDir;
61
+ beforeEach(async () => {
62
+ notionMocks.tokens.length = 0;
63
+ notionMocks.retrieveBotUser.mockClear();
64
+ notionMocks.retrievePage.mockClear();
65
+ tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-sources-notion-'));
66
+ projectDir = join(tempDir, 'project');
67
+ await initKtxProject({ projectDir });
68
+ });
69
+ afterEach(async () => {
70
+ await rm(tempDir, { recursive: true, force: true });
71
+ });
72
+ async function readConfig() {
73
+ return parseKtxProjectConfig(await readFile(join(projectDir, 'ktx.yaml'), 'utf-8'));
74
+ }
75
+ async function writeConfigConnection(connectionId, connection) {
76
+ const config = await readConfig();
77
+ await writeFile(join(projectDir, 'ktx.yaml'), serializeKtxProjectConfig({
78
+ ...config,
79
+ connections: {
80
+ ...config.connections,
81
+ warehouse: { driver: 'postgres', url: 'env:DATABASE_URL' },
82
+ [connectionId]: connection,
83
+ },
84
+ setup: {
85
+ ...config.setup,
86
+ database_connection_ids: ['warehouse'],
87
+ },
88
+ }), 'utf-8');
89
+ }
90
+ it('validates an existing Notion source that uses an inline auth token', async () => {
91
+ await writeConfigConnection('notion', {
92
+ driver: 'notion',
93
+ auth_token: 'ntn_inline_token',
94
+ crawl_mode: 'all_accessible',
95
+ });
96
+ const io = makeIo();
97
+ await expect(runKtxSetupSourcesStep({ projectDir, inputMode: 'auto', runInitialSourceIngest: false, skipSources: false }, io.io, {
98
+ prompts: prompts({
99
+ multiselect: [['notion']],
100
+ select: ['existing:notion'],
101
+ }),
102
+ })).resolves.toEqual({ status: 'ready', projectDir, connectionIds: ['notion'] });
103
+ expect(notionMocks.tokens).toEqual(['ntn_inline_token']);
104
+ expect(notionMocks.retrieveBotUser).toHaveBeenCalledOnce();
105
+ expect(io.stderr()).toBe('');
106
+ });
107
+ });
@@ -2,7 +2,7 @@ import { mkdtemp, readdir, readFile, writeFile } from 'node:fs/promises';
2
2
  import { tmpdir } from 'node:os';
3
3
  import { join, relative, resolve } from 'node:path';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
- import { localConnectionTypeForConfig, resolveNotionAuthToken } from '@ktx/context/connections';
5
+ import { localConnectionTypeForConfig, resolveNotionConnectionAuthToken, } from '@ktx/context/connections';
6
6
  import { resolveKtxConfigReference } from '@ktx/context/core';
7
7
  import { cloneOrPull, DEFAULT_METABASE_CLIENT_CONFIG, discoverMetabaseDatabases, loadDbtSchemaFiles, loadProjectInfo, MetabaseClient, NotionClient, parseLookmlStagedDir, parseMetricflowFiles, testRepoConnection, } from '@ktx/context/ingest';
8
8
  import { loadKtxProject, markKtxSetupStateStepComplete, serializeKtxProjectConfig, } from '@ktx/context/project';
@@ -467,7 +467,10 @@ async function defaultValidateLookml(connection) {
467
467
  return count > 0 ? { ok: true, detail: `lookmlFiles=${count}` } : { ok: false, message: 'No LookML files found' };
468
468
  }
469
469
  async function defaultValidateNotion(connection) {
470
- const token = await resolveNotionAuthToken(String(connection.auth_token_ref));
470
+ const token = await resolveNotionConnectionAuthToken({
471
+ auth_token: stringField(connection.auth_token) ?? null,
472
+ auth_token_ref: stringField(connection.auth_token_ref) ?? null,
473
+ });
471
474
  const client = new NotionClient(token);
472
475
  await client.retrieveBotUser();
473
476
  const roots = Array.isArray(connection.root_page_ids)
package/dist/setup.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { KtxCliIo } from './cli-runtime.js';
2
+ import { readManagedPythonRuntimeStatus } from './managed-python-runtime.js';
2
3
  import { type KtxAgentScope, type KtxAgentTarget, type KtxSetupAgentsDeps, runKtxSetupAgentsStep } from './setup-agents.js';
3
4
  import { type KtxSetupDatabaseDriver, type KtxSetupDatabasesDeps, runKtxSetupDatabasesStep } from './setup-databases.js';
4
5
  import { type KtxSetupEmbeddingsDeps, runKtxSetupEmbeddingsStep } from './setup-embeddings.js';
@@ -6,6 +7,7 @@ import { type KtxSetupLlmBackend, type KtxSetupModelDeps, runKtxSetupAnthropicMo
6
7
  import { type KtxSetupProjectDeps } from './setup-project.js';
7
8
  import { type KtxSetupReadyMenuDeps } from './setup-ready-menu.js';
8
9
  import { type KtxSetupSourcesDeps, type KtxSetupSourceType, runKtxSetupSourcesStep } from './setup-sources.js';
10
+ import { type KtxSetupRuntimeDeps, type KtxSetupRuntimeResult, runKtxSetupRuntimeStep } from './setup-runtime.js';
9
11
  import { type KtxSetupPromptOption, type KtxSetupUiAdapter } from './setup-prompts.js';
10
12
  import { type KtxSetupContextDeps, type KtxSetupContextResult, runKtxSetupContextStep, type KtxSetupContextStatusSummary } from './setup-context.js';
11
13
  export interface KtxSetupStatus {
@@ -34,6 +36,12 @@ export interface KtxSetupStatus {
34
36
  type: string;
35
37
  ready: boolean;
36
38
  }>;
39
+ runtime: {
40
+ required: boolean;
41
+ ready: boolean;
42
+ features: string[];
43
+ detail?: string;
44
+ };
37
45
  context: KtxSetupContextStatusSummary;
38
46
  agents: Array<{
39
47
  target: string;
@@ -108,6 +116,8 @@ export interface KtxSetupDeps {
108
116
  databasesDeps?: KtxSetupDatabasesDeps;
109
117
  sources?: (args: Parameters<typeof runKtxSetupSourcesStep>[0], io: KtxCliIo) => Promise<Awaited<ReturnType<typeof runKtxSetupSourcesStep>>>;
110
118
  sourcesDeps?: KtxSetupSourcesDeps;
119
+ runtime?: (args: Parameters<typeof runKtxSetupRuntimeStep>[0], io: KtxCliIo) => Promise<KtxSetupRuntimeResult>;
120
+ runtimeDeps?: KtxSetupRuntimeDeps;
111
121
  agents?: (args: Parameters<typeof runKtxSetupAgentsStep>[0], io: KtxCliIo) => Promise<Awaited<ReturnType<typeof runKtxSetupAgentsStep>>>;
112
122
  agentsDeps?: KtxSetupAgentsDeps;
113
123
  context?: (args: Parameters<typeof runKtxSetupContextStep>[0], io: KtxCliIo) => Promise<KtxSetupContextResult>;
@@ -126,6 +136,14 @@ export interface KtxSetupEntryMenuPromptAdapter {
126
136
  export interface KtxSetupEntryMenuDeps {
127
137
  prompts?: KtxSetupEntryMenuPromptAdapter;
128
138
  }
129
- export declare function readKtxSetupStatus(projectDir: string): Promise<KtxSetupStatus>;
139
+ export interface ReadKtxSetupStatusOptions {
140
+ cliVersion?: string;
141
+ env?: NodeJS.ProcessEnv;
142
+ readRuntimeStatus?: typeof readManagedPythonRuntimeStatus;
143
+ }
144
+ export declare function readKtxSetupStatus(projectDir: string, options?: ReadKtxSetupStatusOptions): Promise<KtxSetupStatus>;
130
145
  export declare function formatKtxSetupStatus(status: KtxSetupStatus): string;
146
+ export declare function formatKtxSetupCompletionSummary(status: KtxSetupStatus, options?: {
147
+ agentNextActions?: string;
148
+ }): string;
131
149
  export declare function runKtxSetup(args: KtxSetupArgs, io: KtxCliIo, deps?: KtxSetupDeps): Promise<number>;