@kaelio/ktx 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/python/{kaelio_ktx-0.13.1-py3-none-any.whl → kaelio_ktx-0.15.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli-program.js +1 -1
- package/dist/commands/ingest-commands.d.ts +9 -0
- package/dist/commands/ingest-commands.js +37 -1
- package/dist/commands/knowledge-commands.js +3 -0
- package/dist/commands/setup-commands.js +15 -1
- package/dist/connection-drivers.d.ts +2 -0
- package/dist/connection-drivers.js +7 -3
- package/dist/connection.d.ts +3 -0
- package/dist/connection.js +27 -0
- package/dist/connectors/bigquery/connector.d.ts +17 -6
- package/dist/connectors/bigquery/connector.js +152 -76
- package/dist/connectors/bigquery/dialect.d.ts +2 -2
- package/dist/connectors/clickhouse/connector.d.ts +1 -0
- package/dist/connectors/clickhouse/connector.js +45 -16
- package/dist/connectors/clickhouse/dialect.d.ts +2 -2
- package/dist/connectors/mongodb/connector.d.ts +69 -0
- package/dist/connectors/mongodb/connector.js +307 -0
- package/dist/connectors/mongodb/dialect.d.ts +17 -0
- package/dist/connectors/mongodb/dialect.js +50 -0
- package/dist/connectors/mongodb/live-database-introspection.d.ts +10 -0
- package/dist/connectors/mongodb/live-database-introspection.js +24 -0
- package/dist/connectors/mongodb/schema-inference.d.ts +20 -0
- package/dist/connectors/mongodb/schema-inference.js +110 -0
- package/dist/connectors/mysql/connector.d.ts +1 -0
- package/dist/connectors/mysql/connector.js +18 -2
- package/dist/connectors/mysql/dialect.d.ts +2 -2
- package/dist/connectors/postgres/connector.d.ts +1 -0
- package/dist/connectors/postgres/connector.js +20 -3
- package/dist/connectors/postgres/dialect.d.ts +2 -2
- package/dist/connectors/snowflake/connector.d.ts +1 -0
- package/dist/connectors/snowflake/connector.js +27 -3
- package/dist/connectors/snowflake/dialect.d.ts +2 -2
- package/dist/connectors/sqlite/connector.d.ts +11 -1
- package/dist/connectors/sqlite/connector.js +120 -17
- package/dist/connectors/sqlite/dialect.d.ts +2 -2
- package/dist/connectors/sqlite/read-query-child.d.ts +1 -0
- package/dist/connectors/sqlite/read-query-child.js +23 -0
- package/dist/connectors/sqlserver/connector.d.ts +2 -0
- package/dist/connectors/sqlserver/connector.js +20 -3
- package/dist/connectors/sqlserver/dialect.d.ts +2 -2
- package/dist/context/cache/content-result-cache.d.ts +36 -0
- package/dist/context/cache/content-result-cache.js +14 -0
- package/dist/context/cache/sqlite-content-result-cache.d.ts +18 -0
- package/dist/context/cache/sqlite-content-result-cache.js +223 -0
- package/dist/context/connections/bigquery-identifiers.d.ts +1 -0
- package/dist/context/connections/bigquery-identifiers.js +7 -0
- package/dist/context/connections/configured-connections.d.ts +9 -0
- package/dist/context/connections/configured-connections.js +18 -0
- package/dist/context/connections/dialects.d.ts +30 -4
- package/dist/context/connections/dialects.js +38 -11
- package/dist/context/connections/drivers.js +21 -0
- package/dist/context/connections/gdrive-config.d.ts +19 -0
- package/dist/context/connections/gdrive-config.js +57 -0
- package/dist/context/connections/query-deadline.d.ts +31 -0
- package/dist/context/connections/query-deadline.js +37 -0
- package/dist/context/ingest/adapters/gdrive/chunk.d.ts +3 -0
- package/dist/context/ingest/adapters/gdrive/chunk.js +74 -0
- package/dist/context/ingest/adapters/gdrive/detect.d.ts +1 -0
- package/dist/context/ingest/adapters/gdrive/detect.js +20 -0
- package/dist/context/ingest/adapters/gdrive/fetch.d.ts +6 -0
- package/dist/context/ingest/adapters/gdrive/fetch.js +95 -0
- package/dist/context/ingest/adapters/gdrive/gdrive-client.d.ts +30 -0
- package/dist/context/ingest/adapters/gdrive/gdrive-client.js +132 -0
- package/dist/context/ingest/adapters/gdrive/gdrive.adapter.d.ts +11 -0
- package/dist/context/ingest/adapters/gdrive/gdrive.adapter.js +27 -0
- package/dist/context/ingest/adapters/gdrive/normalize.d.ts +2 -0
- package/dist/context/ingest/adapters/gdrive/normalize.js +256 -0
- package/dist/context/ingest/adapters/gdrive/types.d.ts +153 -0
- package/dist/context/ingest/adapters/gdrive/types.js +36 -0
- package/dist/context/ingest/adapters/live-database/daemon-introspection.js +24 -0
- package/dist/context/ingest/adapters/live-database/fetch-report.d.ts +8 -0
- package/dist/context/ingest/adapters/live-database/fetch-report.js +37 -0
- package/dist/context/ingest/adapters/live-database/live-database.adapter.d.ts +2 -1
- package/dist/context/ingest/adapters/live-database/live-database.adapter.js +9 -2
- package/dist/context/ingest/adapters/live-database/manifest.d.ts +2 -0
- package/dist/context/ingest/adapters/live-database/manifest.js +8 -4
- package/dist/context/ingest/adapters/live-database/scan-outcome.d.ts +17 -0
- package/dist/context/ingest/adapters/live-database/scan-outcome.js +40 -0
- package/dist/context/ingest/adapters/live-database/stage.js +5 -5
- package/dist/context/ingest/adapters/metabase/types.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/types.js +1 -1
- package/dist/context/ingest/adapters/sigma/chunk.d.ts +6 -0
- package/dist/context/ingest/adapters/sigma/chunk.js +119 -0
- package/dist/context/ingest/adapters/sigma/client-port.d.ts +45 -0
- package/dist/context/ingest/adapters/sigma/client-port.js +1 -0
- package/dist/context/ingest/adapters/sigma/client.d.ts +33 -0
- package/dist/context/ingest/adapters/sigma/client.js +176 -0
- package/dist/context/ingest/adapters/sigma/detect.d.ts +1 -0
- package/dist/context/ingest/adapters/sigma/detect.js +23 -0
- package/dist/context/ingest/adapters/sigma/fetch.d.ts +14 -0
- package/dist/context/ingest/adapters/sigma/fetch.js +191 -0
- package/dist/context/ingest/adapters/sigma/local-sigma.adapter.d.ts +17 -0
- package/dist/context/ingest/adapters/sigma/local-sigma.adapter.js +41 -0
- package/dist/context/ingest/adapters/sigma/project.d.ts +8 -0
- package/dist/context/ingest/adapters/sigma/project.js +186 -0
- package/dist/context/ingest/adapters/sigma/sigma.adapter.d.ts +18 -0
- package/dist/context/ingest/adapters/sigma/sigma.adapter.js +43 -0
- package/dist/context/ingest/adapters/sigma/types.d.ts +80 -0
- package/dist/context/ingest/adapters/sigma/types.js +82 -0
- package/dist/context/ingest/artifact-gates.d.ts +32 -1
- package/dist/context/ingest/artifact-gates.js +85 -18
- package/dist/context/ingest/final-gate-prune.d.ts +35 -0
- package/dist/context/ingest/final-gate-prune.js +229 -0
- package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
- package/dist/context/ingest/ingest-bundle.runner.js +459 -240
- package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +0 -11
- package/dist/context/ingest/isolated-diff/patch-integrator.js +0 -44
- package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
- package/dist/context/ingest/isolated-diff/work-unit-executor.js +13 -4
- package/dist/context/ingest/local-adapters.d.ts +2 -1
- package/dist/context/ingest/local-adapters.js +28 -0
- package/dist/context/ingest/local-bundle-runtime.js +7 -2
- package/dist/context/ingest/local-ingest.js +1 -1
- package/dist/context/ingest/local-stage-ingest.d.ts +3 -1
- package/dist/context/ingest/local-stage-ingest.js +3 -1
- package/dist/context/ingest/ports.d.ts +3 -0
- package/dist/context/ingest/report-snapshot.js +13 -3
- package/dist/context/ingest/reports.d.ts +3 -3
- package/dist/context/ingest/reports.js +3 -1
- package/dist/context/ingest/stages/build-wu-context.d.ts +1 -0
- package/dist/context/ingest/stages/build-wu-context.js +2 -1
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -1
- package/dist/context/ingest/stages/stage-3-work-units.js +4 -10
- package/dist/context/ingest/stages/validate-wu-sources.d.ts +12 -0
- package/dist/context/ingest/stages/validate-wu-sources.js +23 -8
- package/dist/context/ingest/tools/read-raw-file.tool.js +10 -5
- package/dist/context/ingest/tools/read-raw-span.tool.js +10 -5
- package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +1 -1
- package/dist/context/ingest/tools/warehouse-verification/entity-details.tool.js +1 -1
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +1 -1
- package/dist/context/ingest/types.d.ts +3 -0
- package/dist/context/ingest/wiki-body-refs.d.ts +28 -0
- package/dist/context/ingest/wiki-body-refs.js +37 -8
- package/dist/context/ingest/wiki-sl-ref-repair.d.ts +1 -0
- package/dist/context/ingest/wiki-sl-ref-repair.js +4 -0
- package/dist/context/ingest/work-unit-cache.d.ts +67 -0
- package/dist/context/ingest/work-unit-cache.js +230 -0
- package/dist/context/llm/ai-sdk-runtime.d.ts +1 -0
- package/dist/context/llm/ai-sdk-runtime.js +5 -0
- package/dist/context/llm/claude-code-runtime.d.ts +4 -1
- package/dist/context/llm/claude-code-runtime.js +38 -8
- package/dist/context/llm/codex-runtime.d.ts +4 -1
- package/dist/context/llm/codex-runtime.js +48 -35
- package/dist/context/llm/runtime-port.d.ts +26 -0
- package/dist/context/llm/subprocess-generate-object-child.d.ts +1 -0
- package/dist/context/llm/subprocess-generate-object-child.js +34 -0
- package/dist/context/llm/subprocess-generate-object.d.ts +42 -0
- package/dist/context/llm/subprocess-generate-object.js +122 -0
- package/dist/context/mcp/context-tools.d.ts +2 -0
- package/dist/context/mcp/context-tools.js +111 -17
- package/dist/context/mcp/local-project-ports.d.ts +7 -0
- package/dist/context/mcp/local-project-ports.js +29 -0
- package/dist/context/mcp/logger.d.ts +24 -0
- package/dist/context/mcp/logger.js +49 -0
- package/dist/context/mcp/server.js +2 -0
- package/dist/context/mcp/types.d.ts +17 -0
- package/dist/context/memory/memory-agent.service.js +1 -1
- package/dist/context/project/config.d.ts +72 -0
- package/dist/context/project/config.js +5 -0
- package/dist/context/project/driver-schemas.d.ts +35 -0
- package/dist/context/project/driver-schemas.js +89 -1
- package/dist/context/project/project.js +1 -1
- package/dist/context/project/setup-config.js +1 -0
- package/dist/context/scan/description-generation.d.ts +4 -0
- package/dist/context/scan/description-generation.js +100 -13
- package/dist/context/scan/enabled-tables.d.ts +5 -0
- package/dist/context/scan/enabled-tables.js +13 -1
- package/dist/context/scan/enrichment-state.d.ts +50 -7
- package/dist/context/scan/enrichment-state.js +30 -13
- package/dist/context/scan/local-enrichment-artifacts.d.ts +41 -0
- package/dist/context/scan/local-enrichment-artifacts.js +155 -32
- package/dist/context/scan/local-enrichment.d.ts +39 -4
- package/dist/context/scan/local-enrichment.js +345 -92
- package/dist/context/scan/local-scan.d.ts +4 -1
- package/dist/context/scan/local-scan.js +54 -13
- package/dist/context/scan/local-structural-artifacts.d.ts +3 -1
- package/dist/context/scan/local-structural-artifacts.js +5 -0
- package/dist/context/scan/object-introspection.d.ts +21 -0
- package/dist/context/scan/object-introspection.js +35 -0
- package/dist/context/scan/relationship-benchmarks.js +5 -3
- package/dist/context/scan/relationship-composite-candidates.d.ts +6 -3
- package/dist/context/scan/relationship-composite-candidates.js +13 -1
- package/dist/context/scan/relationship-detection-budget.d.ts +35 -0
- package/dist/context/scan/relationship-detection-budget.js +53 -0
- package/dist/context/scan/relationship-diagnostics.d.ts +5 -0
- package/dist/context/scan/relationship-diagnostics.js +2 -0
- package/dist/context/scan/relationship-discovery.d.ts +9 -3
- package/dist/context/scan/relationship-discovery.js +27 -4
- package/dist/context/scan/relationship-llm-proposal.js +36 -27
- package/dist/context/scan/relationship-profiling.d.ts +7 -3
- package/dist/context/scan/relationship-profiling.js +53 -41
- package/dist/context/scan/relationship-validation.d.ts +6 -4
- package/dist/context/scan/relationship-validation.js +46 -32
- package/dist/context/scan/sqlite-local-enrichment-state-store.d.ts +8 -1
- package/dist/context/scan/sqlite-local-enrichment-state-store.js +71 -159
- package/dist/context/scan/types.d.ts +5 -3
- package/dist/context/scan/types.js +2 -0
- package/dist/context/sl/local-query.js +5 -0
- package/dist/context/sl/pglite-sl-search-prototype.js +1 -1
- package/dist/context/sl/semantic-layer.service.js +1 -1
- package/dist/context/sl/source-files.d.ts +5 -0
- package/dist/context/sl/source-files.js +17 -11
- package/dist/context/sl/tools/connection-id-schema.js +1 -1
- package/dist/context/sql-analysis/dialect-notes.d.ts +9 -0
- package/dist/context/sql-analysis/dialect-notes.js +40 -0
- package/dist/context/sql-analysis/dialects/bigquery.md +13 -0
- package/dist/context/sql-analysis/dialects/clickhouse.md +9 -0
- package/dist/context/sql-analysis/dialects/mysql.md +9 -0
- package/dist/context/sql-analysis/dialects/postgres.md +10 -0
- package/dist/context/sql-analysis/dialects/snowflake.md +10 -0
- package/dist/context/sql-analysis/dialects/sqlite.md +11 -0
- package/dist/context/sql-analysis/dialects/tsql.md +10 -0
- package/dist/context/wiki/keys.js +1 -1
- package/dist/context/wiki/knowledge-wiki.service.d.ts +4 -4
- package/dist/context/wiki/knowledge-wiki.service.js +2 -1
- package/dist/context/wiki/local-knowledge.d.ts +13 -0
- package/dist/context/wiki/local-knowledge.js +50 -6
- package/dist/context/wiki/sqlite-knowledge-index.d.ts +2 -0
- package/dist/context/wiki/sqlite-knowledge-index.js +21 -5
- package/dist/context/wiki/tools/wiki-write.tool.d.ts +2 -0
- package/dist/context/wiki/tools/wiki-write.tool.js +27 -0
- package/dist/context/wiki/types.d.ts +6 -0
- package/dist/context-build-view.d.ts +3 -0
- package/dist/context-build-view.js +1 -0
- package/dist/knowledge.d.ts +2 -0
- package/dist/knowledge.js +12 -1
- package/dist/local-adapters.js +11 -0
- package/dist/mcp-http-server.js +15 -1
- package/dist/mcp-server-factory.d.ts +2 -0
- package/dist/mcp-server-factory.js +15 -1
- package/dist/mcp-stdio-server.js +10 -2
- package/dist/notion-page-picker.js +1 -1
- package/dist/prompts/memory_agent_external_ingest.md +2 -0
- package/dist/public-ingest.d.ts +3 -1
- package/dist/public-ingest.js +4 -0
- package/dist/scan.d.ts +3 -1
- package/dist/scan.js +7 -0
- package/dist/setup-databases.d.ts +1 -1
- package/dist/setup-databases.js +43 -1
- package/dist/setup-sources.d.ts +6 -1
- package/dist/setup-sources.js +208 -48
- package/dist/setup.d.ts +3 -0
- package/dist/setup.js +6 -1
- package/dist/skills/analytics/SKILL.md +200 -2
- package/dist/skills/gdrive_synthesize/SKILL.md +97 -0
- package/dist/skills/sigma_ingest/SKILL.md +189 -0
- package/dist/skills/wiki_capture/SKILL.md +24 -0
- package/dist/sql.js +4 -0
- package/dist/status-project.d.ts +4 -0
- package/dist/status-project.js +54 -2
- package/dist/telemetry/events.d.ts +2 -2
- package/dist/text-ingest.d.ts +4 -0
- package/dist/text-ingest.js +58 -29
- package/dist/verbatim-ingest.d.ts +46 -0
- package/dist/verbatim-ingest.js +193 -0
- package/package.json +5 -1
- package/dist/context/ingest/final-gate-repair.d.ts +0 -28
- package/dist/context/ingest/final-gate-repair.js +0 -91
|
@@ -27,6 +27,10 @@ const wikiWriteInputSchema = z.object({
|
|
|
27
27
|
tags: z.array(z.string()).optional(),
|
|
28
28
|
refs: z.array(z.string()).optional(),
|
|
29
29
|
sl_refs: z.array(z.string()).optional(),
|
|
30
|
+
connections: z
|
|
31
|
+
.union([z.string(), z.array(z.string())])
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('Connection ids this page applies to. Set [connectionId] on database-specific pages (with a connection-distinctive key); omit or leave empty for org-wide content. REPLACE semantics like tags.'),
|
|
30
34
|
source: z.string().optional(),
|
|
31
35
|
intent: z.string().optional(),
|
|
32
36
|
tables: z.array(z.string()).optional(),
|
|
@@ -124,6 +128,28 @@ Keys must be flat file names, not directory paths. Use tags/source frontmatter f
|
|
|
124
128
|
const resolvedTags = input.tags === undefined ? existingFm?.tags : input.tags;
|
|
125
129
|
const resolvedRefs = input.refs === undefined ? existingFm?.refs : input.refs;
|
|
126
130
|
const resolvedSlRefs = input.sl_refs === undefined ? existingFm?.sl_refs : input.sl_refs;
|
|
131
|
+
const incomingConnections = input.connections === undefined
|
|
132
|
+
? undefined
|
|
133
|
+
: typeof input.connections === 'string'
|
|
134
|
+
? [input.connections]
|
|
135
|
+
: input.connections;
|
|
136
|
+
const resolvedConnections = incomingConnections === undefined ? existingFm?.connections : incomingConnections;
|
|
137
|
+
// Data-loss guard: page keys are a flat global namespace, so a write whose
|
|
138
|
+
// incoming connection scope is disjoint from an existing same-key page would
|
|
139
|
+
// silently overwrite a different connection's page. Surface it instead.
|
|
140
|
+
const existingConnections = existingFm?.connections ?? [];
|
|
141
|
+
if (existing &&
|
|
142
|
+
incomingConnections !== undefined &&
|
|
143
|
+
incomingConnections.length > 0 &&
|
|
144
|
+
existingConnections.length > 0 &&
|
|
145
|
+
!incomingConnections.some((id) => existingConnections.includes(id))) {
|
|
146
|
+
return {
|
|
147
|
+
markdown: `Error: page "${input.key}" already exists scoped to a different connection ` +
|
|
148
|
+
`(connections: ${existingConnections.join(', ')}); writing it for ${incomingConnections.join(', ')} ` +
|
|
149
|
+
`would overwrite that page. Use a connection-distinctive key (e.g. "${input.key}_${incomingConnections[0]}").`,
|
|
150
|
+
structured: { success: false, key: input.key },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
127
153
|
let finalContent;
|
|
128
154
|
const finalFm = {
|
|
129
155
|
summary: input.summary,
|
|
@@ -132,6 +158,7 @@ Keys must be flat file names, not directory paths. Use tags/source frontmatter f
|
|
|
132
158
|
tags: resolvedTags,
|
|
133
159
|
refs: resolvedRefs,
|
|
134
160
|
sl_refs: resolvedSlRefs,
|
|
161
|
+
connections: resolvedConnections,
|
|
135
162
|
source: input.source === undefined ? existingFm?.source : input.source,
|
|
136
163
|
intent: input.intent === undefined ? existingFm?.intent : input.intent,
|
|
137
164
|
tables: input.tables === undefined ? existingFm?.tables : input.tables,
|
|
@@ -14,6 +14,12 @@ export interface WikiFrontmatter {
|
|
|
14
14
|
tags?: string[];
|
|
15
15
|
refs?: string[];
|
|
16
16
|
sl_refs?: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Connection ids this page applies to. Absent or empty ⇒ unscoped: the page
|
|
19
|
+
* applies to all connections. Additive metadata, orthogonal to GLOBAL/USER
|
|
20
|
+
* scope; it does not namespace page keys.
|
|
21
|
+
*/
|
|
22
|
+
connections?: string[];
|
|
17
23
|
usage_mode: 'always' | 'auto' | 'never';
|
|
18
24
|
sort_order?: number;
|
|
19
25
|
source?: string;
|
|
@@ -48,6 +48,9 @@ export interface ContextBuildArgs {
|
|
|
48
48
|
scanMode?: Extract<KtxPublicIngestArgs, {
|
|
49
49
|
command: 'run';
|
|
50
50
|
}>['scanMode'];
|
|
51
|
+
stages?: Extract<KtxPublicIngestArgs, {
|
|
52
|
+
command: 'run';
|
|
53
|
+
}>['stages'];
|
|
51
54
|
detectRelationships?: boolean;
|
|
52
55
|
cliVersion?: string;
|
|
53
56
|
runtimeInstallPolicy?: KtxManagedPythonInstallPolicy;
|
|
@@ -750,6 +750,7 @@ export async function runContextBuild(project, args, io, deps = {}) {
|
|
|
750
750
|
...(args.queryHistory ? { queryHistory: args.queryHistory } : {}),
|
|
751
751
|
...(args.queryHistoryWindowDays !== undefined ? { queryHistoryWindowDays: args.queryHistoryWindowDays } : {}),
|
|
752
752
|
...(args.scanMode ? { scanMode: args.scanMode } : {}),
|
|
753
|
+
...(args.stages ? { stages: args.stages } : {}),
|
|
753
754
|
...(args.detectRelationships !== undefined ? { detectRelationships: args.detectRelationships } : {}),
|
|
754
755
|
...(args.cliVersion ? { cliVersion: args.cliVersion } : {}),
|
|
755
756
|
...(args.runtimeInstallPolicy ? { runtimeInstallPolicy: args.runtimeInstallPolicy } : {}),
|
package/dist/knowledge.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type KtxKnowledgeArgs = {
|
|
|
5
5
|
command: 'list';
|
|
6
6
|
projectDir: string;
|
|
7
7
|
userId: string;
|
|
8
|
+
connectionId?: string;
|
|
8
9
|
output?: string;
|
|
9
10
|
json?: boolean;
|
|
10
11
|
cliVersion: string;
|
|
@@ -13,6 +14,7 @@ export type KtxKnowledgeArgs = {
|
|
|
13
14
|
projectDir: string;
|
|
14
15
|
query: string;
|
|
15
16
|
userId: string;
|
|
17
|
+
connectionId?: string;
|
|
16
18
|
output?: string;
|
|
17
19
|
json?: boolean;
|
|
18
20
|
limit?: number;
|
package/dist/knowledge.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { KtxIngestEmbeddingPortAdapter } from './context/llm/embedding-port.js';
|
|
2
2
|
import { loadKtxProject } from './context/project/project.js';
|
|
3
|
+
import { assertConfiguredConnectionId } from './context/connections/configured-connections.js';
|
|
3
4
|
import { listLocalKnowledgePages, readLocalKnowledgePage, searchLocalKnowledgePages as defaultSearchLocalKnowledgePages, } from './context/wiki/local-knowledge.js';
|
|
4
5
|
import { resolveProjectEmbeddingProvider, } from './embedding-resolution.js';
|
|
5
6
|
import { resolveOutputMode } from './io/mode.js';
|
|
@@ -57,7 +58,13 @@ export async function runKtxKnowledge(args, io = process, deps = {}) {
|
|
|
57
58
|
try {
|
|
58
59
|
const project = await loadKtxProject({ projectDir: args.projectDir });
|
|
59
60
|
if (args.command === 'list') {
|
|
60
|
-
const
|
|
61
|
+
const connectionId = args.connectionId === undefined
|
|
62
|
+
? undefined
|
|
63
|
+
: assertConfiguredConnectionId(project.config.connections, args.connectionId);
|
|
64
|
+
const pages = await listLocalKnowledgePages(project, {
|
|
65
|
+
userId: args.userId,
|
|
66
|
+
...(connectionId !== undefined ? { connectionId } : {}),
|
|
67
|
+
});
|
|
61
68
|
const mode = resolveOutputMode({ explicit: args.output, json: args.json, io });
|
|
62
69
|
printList({
|
|
63
70
|
rows: pages,
|
|
@@ -82,6 +89,9 @@ export async function runKtxKnowledge(args, io = process, deps = {}) {
|
|
|
82
89
|
return 0;
|
|
83
90
|
}
|
|
84
91
|
if (args.command === 'search') {
|
|
92
|
+
const connectionId = args.connectionId === undefined
|
|
93
|
+
? undefined
|
|
94
|
+
: assertConfiguredConnectionId(project.config.connections, args.connectionId);
|
|
85
95
|
const embeddingService = await wikiSearchEmbeddingService(project, deps, { cliVersion: args.cliVersion }, io);
|
|
86
96
|
const search = deps.searchLocalKnowledgePages ?? defaultSearchLocalKnowledgePages;
|
|
87
97
|
const results = await search(project, {
|
|
@@ -89,6 +99,7 @@ export async function runKtxKnowledge(args, io = process, deps = {}) {
|
|
|
89
99
|
userId: args.userId,
|
|
90
100
|
embeddingService,
|
|
91
101
|
limit: args.limit,
|
|
102
|
+
...(connectionId !== undefined ? { connectionId } : {}),
|
|
92
103
|
});
|
|
93
104
|
await emitTelemetryEvent({
|
|
94
105
|
name: 'wiki_query_completed',
|
package/dist/local-adapters.js
CHANGED
|
@@ -92,6 +92,17 @@ function createKtxCliLiveDatabaseIntrospection(project, options = {}) {
|
|
|
92
92
|
return {
|
|
93
93
|
async extractSchema(connectionId, options) {
|
|
94
94
|
const connection = project.config.connections[connectionId];
|
|
95
|
+
if (String(connection?.driver ?? '').toLowerCase() === 'mongodb') {
|
|
96
|
+
const { createMongoDbLiveDatabaseIntrospection } = await import('./connectors/mongodb/live-database-introspection.js');
|
|
97
|
+
const { isKtxMongoDbConnectionConfig } = await import('./connectors/mongodb/connector.js');
|
|
98
|
+
if (!isKtxMongoDbConnectionConfig(connection)) {
|
|
99
|
+
return daemon.extractSchema(connectionId, options);
|
|
100
|
+
}
|
|
101
|
+
const mongodb = createMongoDbLiveDatabaseIntrospection({
|
|
102
|
+
connections: project.config.connections,
|
|
103
|
+
});
|
|
104
|
+
return mongodb.extractSchema(connectionId, options);
|
|
105
|
+
}
|
|
95
106
|
if (isKtxPostgresConnectionConfig(connection)) {
|
|
96
107
|
return postgres.extractSchema(connectionId, options);
|
|
97
108
|
}
|
package/dist/mcp-http-server.js
CHANGED
|
@@ -4,6 +4,7 @@ import { loadKtxProject } from './context/project/project.js';
|
|
|
4
4
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
5
|
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
6
6
|
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
|
7
|
+
import { createMcpLogger, serializeMcpError } from './context/mcp/logger.js';
|
|
7
8
|
import { createKtxMcpServerFactory } from './mcp-server-factory.js';
|
|
8
9
|
const DEFAULT_ALLOWED_HOSTS = ['localhost', '127.0.0.1', '::1'];
|
|
9
10
|
function isLoopbackHost(host) {
|
|
@@ -121,12 +122,16 @@ export async function runKtxMcpHttpServer(options) {
|
|
|
121
122
|
const project = options.createMcpServer === undefined
|
|
122
123
|
? await (options.loadProject ?? loadKtxProject)({ projectDir: options.projectDir })
|
|
123
124
|
: undefined;
|
|
125
|
+
// One logger per process, shared by the tool layer (via the factory) and the
|
|
126
|
+
// transport lifecycle below. Falls back to a no-op sink for programmatic callers.
|
|
127
|
+
const logger = createMcpLogger(options.io ?? { stdout: { write() { } }, stderr: { write() { } } });
|
|
124
128
|
const createMcpServer = options.createMcpServer ??
|
|
125
129
|
(await createKtxMcpServerFactory({
|
|
126
130
|
project: project,
|
|
127
131
|
projectDir: options.projectDir,
|
|
128
132
|
cliVersion: options.cliVersion ?? getKtxCliPackageInfo().version,
|
|
129
133
|
io: options.io,
|
|
134
|
+
logger,
|
|
130
135
|
}));
|
|
131
136
|
const sessions = new Map();
|
|
132
137
|
async function newTransport() {
|
|
@@ -135,6 +140,7 @@ export async function runKtxMcpHttpServer(options) {
|
|
|
135
140
|
sessionIdGenerator: () => randomUUID(),
|
|
136
141
|
onsessioninitialized: (sessionId) => {
|
|
137
142
|
sessions.set(sessionId, transport);
|
|
143
|
+
logger.info({ sessionId }, 'session.open');
|
|
138
144
|
},
|
|
139
145
|
onsessionclosed: (sessionId) => {
|
|
140
146
|
sessions.delete(sessionId);
|
|
@@ -143,14 +149,21 @@ export async function runKtxMcpHttpServer(options) {
|
|
|
143
149
|
allowedOrigins: config.allowedOrigins,
|
|
144
150
|
enableDnsRebindingProtection: true,
|
|
145
151
|
});
|
|
152
|
+
// onclose is the universal session-end signal (clean DELETE and dropped connection both
|
|
153
|
+
// close the transport), so session.close is logged here rather than in onsessionclosed.
|
|
146
154
|
transport.onclose = () => {
|
|
147
155
|
if (transport.sessionId) {
|
|
148
156
|
sessions.delete(transport.sessionId);
|
|
157
|
+
logger.info({ sessionId: transport.sessionId }, 'session.close');
|
|
149
158
|
}
|
|
150
159
|
};
|
|
160
|
+
transport.onerror = (error) => {
|
|
161
|
+
logger.error({ ...(transport.sessionId ? { sessionId: transport.sessionId } : {}), err: serializeMcpError(error) }, 'transport.error');
|
|
162
|
+
};
|
|
151
163
|
await createMcpServer().connect(transport);
|
|
152
164
|
return transport;
|
|
153
165
|
}
|
|
166
|
+
const startedAt = performance.now();
|
|
154
167
|
const server = createServer(async (req, res) => {
|
|
155
168
|
const path = requestPath(req);
|
|
156
169
|
const auth = isMcpRequestAuthorized({ path, headers: req.headers }, config);
|
|
@@ -160,7 +173,8 @@ export async function runKtxMcpHttpServer(options) {
|
|
|
160
173
|
}
|
|
161
174
|
if (path === '/health' && req.method === 'GET') {
|
|
162
175
|
const port = listenerPort(server, config.port);
|
|
163
|
-
|
|
176
|
+
const uptimeMs = Math.round(performance.now() - startedAt);
|
|
177
|
+
writeJson(res, 200, { status: 'ok', projectDir: options.projectDir, port, uptimeMs });
|
|
164
178
|
return;
|
|
165
179
|
}
|
|
166
180
|
if (path !== '/mcp' || !['POST', 'GET', 'DELETE'].includes(req.method ?? '')) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { KtxMcpLogger } from './context/mcp/logger.js';
|
|
1
2
|
import type { KtxLocalProject } from './context/project/project.js';
|
|
2
3
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
4
|
import type { KtxCliIo } from './cli-runtime.js';
|
|
@@ -6,4 +7,5 @@ export declare function createKtxMcpServerFactory(input: {
|
|
|
6
7
|
projectDir: string;
|
|
7
8
|
cliVersion: string;
|
|
8
9
|
io?: KtxCliIo;
|
|
10
|
+
logger?: KtxMcpLogger;
|
|
9
11
|
}): Promise<() => McpServer>;
|
|
@@ -2,6 +2,7 @@ import { KtxIngestEmbeddingPortAdapter } from './context/llm/embedding-port.js';
|
|
|
2
2
|
import { createDefaultKtxMcpServer } from './context/mcp/server.js';
|
|
3
3
|
import { createLocalProjectMcpContextPorts } from './context/mcp/local-project-ports.js';
|
|
4
4
|
import { createLocalProjectMemoryIngest } from './context/memory/local-memory.js';
|
|
5
|
+
import { assertConfiguredConnectionId } from './context/connections/configured-connections.js';
|
|
5
6
|
import { resolveProjectEmbeddingProvider } from './embedding-resolution.js';
|
|
6
7
|
import { createKtxCliIngestQueryExecutor } from './ingest-query-executor.js';
|
|
7
8
|
import { createKtxCliScanConnector } from './local-scan-connectors.js';
|
|
@@ -47,11 +48,23 @@ export async function createKtxMcpServerFactory(input) {
|
|
|
47
48
|
});
|
|
48
49
|
let memoryIngest;
|
|
49
50
|
try {
|
|
50
|
-
|
|
51
|
+
const baseMemoryIngest = createLocalProjectMemoryIngest(input.project, {
|
|
51
52
|
semanticLayerCompute,
|
|
52
53
|
queryExecutor,
|
|
53
54
|
embeddingProvider,
|
|
54
55
|
});
|
|
56
|
+
// Validate the explicit connectionId argument here so a typo is rejected with the
|
|
57
|
+
// configured ids before the ingest run starts; persisted page scope is validated
|
|
58
|
+
// separately (warn-only) and must not fail.
|
|
59
|
+
memoryIngest = {
|
|
60
|
+
ingest: (ingestInput) => {
|
|
61
|
+
if (ingestInput.connectionId !== undefined) {
|
|
62
|
+
assertConfiguredConnectionId(input.project.config.connections, ingestInput.connectionId);
|
|
63
|
+
}
|
|
64
|
+
return baseMemoryIngest.ingest(ingestInput);
|
|
65
|
+
},
|
|
66
|
+
status: (runId) => baseMemoryIngest.status(runId),
|
|
67
|
+
};
|
|
55
68
|
}
|
|
56
69
|
catch (error) {
|
|
57
70
|
io.stderr.write(`ktx MCP memory_ingest disabled: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -62,6 +75,7 @@ export async function createKtxMcpServerFactory(input) {
|
|
|
62
75
|
userContext: { userId: 'local' },
|
|
63
76
|
projectDir: input.projectDir,
|
|
64
77
|
io,
|
|
78
|
+
...(input.logger ? { logger: input.logger } : {}),
|
|
65
79
|
contextTools: {
|
|
66
80
|
...contextTools,
|
|
67
81
|
...(memoryIngest ? { memoryIngest } : {}),
|
package/dist/mcp-stdio-server.js
CHANGED
|
@@ -2,6 +2,7 @@ import process from 'node:process';
|
|
|
2
2
|
import { loadKtxProject } from './context/project/project.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
|
5
|
+
import { createMcpLogger, serializeMcpError } from './context/mcp/logger.js';
|
|
5
6
|
import { createKtxMcpServerFactory } from './mcp-server-factory.js';
|
|
6
7
|
export async function runKtxMcpStdioServer(options) {
|
|
7
8
|
const project = options.createMcpServer === undefined
|
|
@@ -11,12 +12,15 @@ export async function runKtxMcpStdioServer(options) {
|
|
|
11
12
|
stdout: { write() { } },
|
|
12
13
|
stderr: options.io?.stderr ?? process.stderr,
|
|
13
14
|
};
|
|
15
|
+
// stdout is reserved for JSON-RPC, so the logger writes to stderr only.
|
|
16
|
+
const logger = createMcpLogger(protocolIo);
|
|
14
17
|
const createMcpServer = options.createMcpServer ??
|
|
15
18
|
(await createKtxMcpServerFactory({
|
|
16
19
|
project: project,
|
|
17
20
|
projectDir: options.projectDir,
|
|
18
21
|
cliVersion: options.cliVersion ?? getKtxCliPackageInfo().version,
|
|
19
22
|
io: protocolIo,
|
|
23
|
+
logger,
|
|
20
24
|
}));
|
|
21
25
|
const stdin = options.stdin ?? process.stdin;
|
|
22
26
|
const transport = new StdioServerTransport(stdin, options.stdout);
|
|
@@ -35,13 +39,17 @@ export async function runKtxMcpStdioServer(options) {
|
|
|
35
39
|
settle(() => reject(error instanceof Error ? error : new Error(String(error))));
|
|
36
40
|
});
|
|
37
41
|
};
|
|
38
|
-
transport.onclose = () =>
|
|
42
|
+
transport.onclose = () => {
|
|
43
|
+
logger.info({}, 'session.close');
|
|
44
|
+
settle(resolve);
|
|
45
|
+
};
|
|
39
46
|
transport.onerror = (error) => {
|
|
40
|
-
|
|
47
|
+
logger.error({ err: serializeMcpError(error) }, 'transport.error');
|
|
41
48
|
settle(() => reject(error));
|
|
42
49
|
};
|
|
43
50
|
stdin.once('end', closeTransport);
|
|
44
51
|
stdin.once('close', closeTransport);
|
|
52
|
+
logger.info({}, 'session.open');
|
|
45
53
|
createMcpServer().connect(transport).catch((error) => {
|
|
46
54
|
settle(() => reject(error instanceof Error ? error : new Error(String(error))));
|
|
47
55
|
});
|
|
@@ -7,7 +7,7 @@ profileMark('module:notion-page-picker');
|
|
|
7
7
|
const NOTION_PICKER_PAGE_CAP = 5000;
|
|
8
8
|
const NOTION_SCRIPTED_MODE_HINT = 'Notion picker requires a TTY. Use --no-input --notion-root-page-id <UUID> for scripted mode.';
|
|
9
9
|
function assertSafeNotionPickerConnectionId(connectionId) {
|
|
10
|
-
if (!/^[a-zA-Z0-
|
|
10
|
+
if (!/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/.test(connectionId)) {
|
|
11
11
|
throw new Error(`Unsafe connection id: ${connectionId}`);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -19,6 +19,8 @@ A single artifact typically produces multiple actions: one SL source per table/v
|
|
|
19
19
|
|
|
20
20
|
<scope>
|
|
21
21
|
All wiki writes go to the GLOBAL scope - they will be visible to every user of this ktx project. Phrase wiki pages as objective business knowledge, not personal preference. The `wiki_write` tool handles scope selection automatically for external ingest.
|
|
22
|
+
|
|
23
|
+
When a `connectionId` is shown in the prompt context, tag database-specific pages with `connections: [<that id>]` and give them connection-distinctive keys (`orders_sales_db`, not `orders`) so same-concept pages from other databases do not collide or pollute each other's searches. Leave `connections` empty for org-wide knowledge that applies across every database. See the `wiki_capture` skill's "Connection scoping" section.
|
|
22
24
|
</scope>
|
|
23
25
|
|
|
24
26
|
<do_not>
|
package/dist/public-ingest.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { KtxIngestArgs, KtxIngestDeps, KtxIngestProgressUpdate } from './in
|
|
|
5
5
|
import { type KtxManagedPythonInstallPolicy, type ManagedPythonCommandRuntime } from './managed-python-command.js';
|
|
6
6
|
import type { KtxRuntimeFeature } from './managed-python-runtime.js';
|
|
7
7
|
import type { KtxScanArgs, KtxScanDeps } from './scan.js';
|
|
8
|
-
import type { KtxTableRef } from './context/scan/types.js';
|
|
8
|
+
import type { KtxScanEnrichmentStage, KtxTableRef } from './context/scan/types.js';
|
|
9
9
|
type KtxPublicIngestStepName = 'database-schema' | 'query-history' | 'source-ingest' | 'memory-update';
|
|
10
10
|
type KtxPublicIngestStepStatus = 'done' | 'skipped' | 'failed' | 'not-run';
|
|
11
11
|
type KtxPublicIngestInputMode = 'auto' | 'disabled';
|
|
@@ -23,6 +23,7 @@ export type KtxPublicIngestArgs = {
|
|
|
23
23
|
scanMode?: Extract<KtxScanArgs, {
|
|
24
24
|
command: 'run';
|
|
25
25
|
}>['mode'];
|
|
26
|
+
stages?: KtxScanEnrichmentStage[];
|
|
26
27
|
detectRelationships?: boolean;
|
|
27
28
|
cliVersion?: string;
|
|
28
29
|
runtimeInstallPolicy?: KtxManagedPythonInstallPolicy;
|
|
@@ -95,6 +96,7 @@ interface KtxPublicContextBuildArgs {
|
|
|
95
96
|
scanMode?: Extract<KtxScanArgs, {
|
|
96
97
|
command: 'run';
|
|
97
98
|
}>['mode'];
|
|
99
|
+
stages?: KtxScanEnrichmentStage[];
|
|
98
100
|
detectRelationships?: boolean;
|
|
99
101
|
cliVersion?: string;
|
|
100
102
|
runtimeInstallPolicy?: KtxManagedPythonInstallPolicy;
|
package/dist/public-ingest.js
CHANGED
|
@@ -17,9 +17,11 @@ const sourceAdapterByDriver = new Map([
|
|
|
17
17
|
['local_metabase', 'metabase'],
|
|
18
18
|
['looker', 'looker'],
|
|
19
19
|
['notion', 'notion'],
|
|
20
|
+
['gdrive', 'gdrive'],
|
|
20
21
|
['metricflow', 'metricflow'],
|
|
21
22
|
['dbt', 'dbt'],
|
|
22
23
|
['lookml', 'lookml'],
|
|
24
|
+
['sigma', 'sigma'],
|
|
23
25
|
]);
|
|
24
26
|
export function publicProgressMessage(message, target) {
|
|
25
27
|
let current = message;
|
|
@@ -612,6 +614,7 @@ async function runIngestTargetSteps(target, args, io, deps, project) {
|
|
|
612
614
|
mode: 'enriched',
|
|
613
615
|
detectRelationships: target.detectRelationships === true,
|
|
614
616
|
dryRun: false,
|
|
617
|
+
...(args.stages ? { stages: args.stages } : {}),
|
|
615
618
|
...(args.cliVersion ? { cliVersion: args.cliVersion } : {}),
|
|
616
619
|
...(args.runtimeInstallPolicy ? { runtimeInstallPolicy: args.runtimeInstallPolicy } : {}),
|
|
617
620
|
};
|
|
@@ -764,6 +767,7 @@ export async function runKtxPublicIngest(args, io, deps = {}) {
|
|
|
764
767
|
...(args.queryHistory ? { queryHistory: args.queryHistory } : {}),
|
|
765
768
|
...(args.queryHistoryWindowDays !== undefined ? { queryHistoryWindowDays: args.queryHistoryWindowDays } : {}),
|
|
766
769
|
...(args.scanMode ? { scanMode: args.scanMode } : {}),
|
|
770
|
+
...(args.stages ? { stages: args.stages } : {}),
|
|
767
771
|
...(args.detectRelationships !== undefined ? { detectRelationships: args.detectRelationships } : {}),
|
|
768
772
|
...(args.cliVersion ? { cliVersion: args.cliVersion } : {}),
|
|
769
773
|
...(args.runtimeInstallPolicy ? { runtimeInstallPolicy: args.runtimeInstallPolicy } : {}),
|
package/dist/scan.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KtxProgressPort, KtxScanMode } from './context/scan/types.js';
|
|
1
|
+
import type { KtxProgressPort, KtxScanEnrichmentStage, KtxScanMode } from './context/scan/types.js';
|
|
2
2
|
import { runLocalScan } from './context/scan/local-scan.js';
|
|
3
3
|
import { resolveProjectEmbeddingProvider } from './embedding-resolution.js';
|
|
4
4
|
import type { KtxCliIo } from './index.js';
|
|
@@ -11,6 +11,8 @@ export interface KtxScanArgs {
|
|
|
11
11
|
mode: KtxScanMode;
|
|
12
12
|
detectRelationships: boolean;
|
|
13
13
|
dryRun: boolean;
|
|
14
|
+
/** Enrichment stages to (re)run; omit to run all eligible stages. */
|
|
15
|
+
stages?: KtxScanEnrichmentStage[];
|
|
14
16
|
databaseIntrospectionUrl?: string;
|
|
15
17
|
cliVersion?: string;
|
|
16
18
|
runtimeInstallPolicy?: KtxManagedPythonInstallPolicy;
|
package/dist/scan.js
CHANGED
|
@@ -134,8 +134,14 @@ function describeWarningGroup(code, count) {
|
|
|
134
134
|
return `${count} LLM relationship ${plural(count, 'proposal')} failed.`;
|
|
135
135
|
case 'scan_enrichment_backend_not_configured':
|
|
136
136
|
return 'Scan enrichment backend is not configured; AI stages were skipped.';
|
|
137
|
+
case 'enrichment_stage_skipped':
|
|
138
|
+
return `${count} requested ${plural(count, 'enrichment stage')} could not run (prerequisite missing).`;
|
|
139
|
+
case 'enrichment_stage_stale':
|
|
140
|
+
return `${count} enrichment ${plural(count, 'stage')} are stale after a selective run; re-run them to refresh.`;
|
|
137
141
|
case 'credential_redacted':
|
|
138
142
|
return `${count} ${plural(count, 'credential')} were redacted from scan output.`;
|
|
143
|
+
case 'object_introspection_failed':
|
|
144
|
+
return `${count} ${plural(count, 'object')} skipped during introspection (broken or inaccessible objects were excluded; the rest were ingested).`;
|
|
139
145
|
default:
|
|
140
146
|
return `${count} ${plural(count, 'warning')} (${code})`;
|
|
141
147
|
}
|
|
@@ -272,6 +278,7 @@ export async function runKtxScan(args, io = process, deps = {}) {
|
|
|
272
278
|
connectionId: args.connectionId,
|
|
273
279
|
mode: args.mode,
|
|
274
280
|
detectRelationships: args.detectRelationships,
|
|
281
|
+
...(args.stages ? { stages: args.stages } : {}),
|
|
275
282
|
dryRun: args.dryRun,
|
|
276
283
|
trigger: 'cli',
|
|
277
284
|
databaseIntrospectionUrl: args.databaseIntrospectionUrl,
|
|
@@ -9,7 +9,7 @@ import { type DatabaseScopePickResult, type PickDatabaseScopeArgs } from './data
|
|
|
9
9
|
import type { KtxManagedPythonInstallPolicy } from './managed-python-command.js';
|
|
10
10
|
import type { ManagedPythonCoreDaemonOptions } from './managed-python-http.js';
|
|
11
11
|
import { type KtxSetupPromptOption } from './setup-prompts.js';
|
|
12
|
-
export type KtxSetupDatabaseDriver = 'sqlite' | 'postgres' | 'mysql' | 'clickhouse' | 'sqlserver' | 'bigquery' | 'snowflake';
|
|
12
|
+
export type KtxSetupDatabaseDriver = 'sqlite' | 'postgres' | 'mysql' | 'clickhouse' | 'sqlserver' | 'bigquery' | 'snowflake' | 'mongodb';
|
|
13
13
|
export interface KtxSetupDatabasesArgs {
|
|
14
14
|
projectDir: string;
|
|
15
15
|
inputMode: 'auto' | 'disabled';
|
package/dist/setup-databases.js
CHANGED
|
@@ -37,6 +37,7 @@ const DRIVER_OPTIONS = [
|
|
|
37
37
|
{ value: 'mysql', label: 'MySQL' },
|
|
38
38
|
{ value: 'clickhouse', label: 'ClickHouse' },
|
|
39
39
|
{ value: 'sqlserver', label: 'SQL Server' },
|
|
40
|
+
{ value: 'mongodb', label: 'MongoDB' },
|
|
40
41
|
{ value: 'sqlite', label: 'SQLite' },
|
|
41
42
|
];
|
|
42
43
|
const DRIVER_LABELS = Object.fromEntries(DRIVER_OPTIONS.map((option) => [option.value, option.label]));
|
|
@@ -53,6 +54,7 @@ const DEFAULT_CONNECTION_IDS = {
|
|
|
53
54
|
sqlserver: 'sqlserver-warehouse',
|
|
54
55
|
bigquery: 'bigquery-warehouse',
|
|
55
56
|
snowflake: 'snowflake-warehouse',
|
|
57
|
+
mongodb: 'mongodb-source',
|
|
56
58
|
};
|
|
57
59
|
const SUGGESTED_SCOPE_PATTERN = /^(mart|prod|analytics|core|dim|fact|gold)(_|$)/i;
|
|
58
60
|
const EXCLUDED_SCOPE_PATTERN = /^(information_schema|pg_catalog|pg_toast|_airbyte_|mysql$|performance_schema$|sys$)/i;
|
|
@@ -85,6 +87,13 @@ const SCOPE_DISCOVERY_SPECS = {
|
|
|
85
87
|
configArrayField: 'databases',
|
|
86
88
|
suggest: defaultSuggest,
|
|
87
89
|
},
|
|
90
|
+
mongodb: {
|
|
91
|
+
noun: 'database',
|
|
92
|
+
nounPlural: 'databases',
|
|
93
|
+
promptLabel: 'MongoDB databases',
|
|
94
|
+
configArrayField: 'databases',
|
|
95
|
+
suggest: defaultSuggest,
|
|
96
|
+
},
|
|
88
97
|
sqlserver: {
|
|
89
98
|
noun: 'schema',
|
|
90
99
|
nounPlural: 'schemas',
|
|
@@ -152,7 +161,7 @@ function unique(values) {
|
|
|
152
161
|
return [...new Set(values.filter((value) => value.trim().length > 0))];
|
|
153
162
|
}
|
|
154
163
|
function assertSafeDatabaseConnectionId(connectionId) {
|
|
155
|
-
if (!/^[a-zA-Z0-
|
|
164
|
+
if (!/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/.test(connectionId)) {
|
|
156
165
|
throw new Error(`Unsafe connection id: ${connectionId}`);
|
|
157
166
|
}
|
|
158
167
|
}
|
|
@@ -496,6 +505,31 @@ async function buildUrlConnectionConfig(input) {
|
|
|
496
505
|
return result;
|
|
497
506
|
}
|
|
498
507
|
}
|
|
508
|
+
async function buildMongoConnectionConfig(input) {
|
|
509
|
+
if (input.args.inputMode === 'disabled' && !input.args.databaseUrl)
|
|
510
|
+
return null;
|
|
511
|
+
const rawUrl = input.args.databaseUrl ??
|
|
512
|
+
(await promptText(input.prompts, 'MongoDB connection URL\nFor example mongodb+srv://user:pass@cluster.example.net/app.', // pragma: allowlist secret
|
|
513
|
+
stringConfigField(input.existingConnection, 'url')));
|
|
514
|
+
if (rawUrl === undefined)
|
|
515
|
+
return 'back';
|
|
516
|
+
if (!rawUrl)
|
|
517
|
+
return null;
|
|
518
|
+
const url = normalizeInputReference(rawUrl);
|
|
519
|
+
const scope = scriptedScopeConfigForDriver('mongodb', input.args.databaseSchemas);
|
|
520
|
+
if (url.startsWith('env:') || url.startsWith('file:')) {
|
|
521
|
+
return { driver: 'mongodb', url, ...scope };
|
|
522
|
+
}
|
|
523
|
+
if (urlHasCredentials(url)) {
|
|
524
|
+
const ref = await writeProjectLocalSecretReference({
|
|
525
|
+
projectDir: input.args.projectDir,
|
|
526
|
+
fileName: `${input.connectionId}-url`,
|
|
527
|
+
value: url,
|
|
528
|
+
});
|
|
529
|
+
return { driver: 'mongodb', url: ref, ...scope };
|
|
530
|
+
}
|
|
531
|
+
return { driver: 'mongodb', url, ...scope };
|
|
532
|
+
}
|
|
499
533
|
async function buildConnectionConfig(input) {
|
|
500
534
|
const { driver, args, prompts } = input;
|
|
501
535
|
if (driver === 'sqlite') {
|
|
@@ -516,6 +550,14 @@ async function buildConnectionConfig(input) {
|
|
|
516
550
|
existingConnection: input.existingConnection,
|
|
517
551
|
});
|
|
518
552
|
}
|
|
553
|
+
if (driver === 'mongodb') {
|
|
554
|
+
return await buildMongoConnectionConfig({
|
|
555
|
+
connectionId: input.connectionId,
|
|
556
|
+
args,
|
|
557
|
+
prompts,
|
|
558
|
+
existingConnection: input.existingConnection,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
519
561
|
if (driver === 'bigquery') {
|
|
520
562
|
const credentialsPath = await promptText(prompts, 'Path to service account JSON file', displayFileReference(stringConfigField(input.existingConnection, 'credentials_json')));
|
|
521
563
|
if (credentialsPath === undefined)
|
package/dist/setup-sources.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type KtxProjectConnectionConfig } from './context/project/config.js';
|
|
|
3
3
|
import type { KtxCliIo } from './cli-runtime.js';
|
|
4
4
|
import { pickNotionRootPages } from './notion-page-picker.js';
|
|
5
5
|
import { type KtxSetupPromptOption } from './setup-prompts.js';
|
|
6
|
-
export type KtxSetupSourceType = 'dbt' | 'metricflow' | 'metabase' | 'looker' | 'lookml' | 'notion';
|
|
6
|
+
export type KtxSetupSourceType = 'dbt' | 'metricflow' | 'metabase' | 'looker' | 'lookml' | 'notion' | 'sigma' | 'gdrive';
|
|
7
7
|
export interface KtxSetupSourcesArgs {
|
|
8
8
|
projectDir: string;
|
|
9
9
|
inputMode: 'auto' | 'disabled';
|
|
@@ -25,6 +25,9 @@ export interface KtxSetupSourcesArgs {
|
|
|
25
25
|
metabaseDatabaseId?: number;
|
|
26
26
|
notionCrawlMode?: 'all_accessible' | 'selected_roots';
|
|
27
27
|
notionRootPageIds?: string[];
|
|
28
|
+
gdriveServiceAccountKeyRef?: string;
|
|
29
|
+
gdriveFolderId?: string;
|
|
30
|
+
gdriveRecursive?: boolean;
|
|
28
31
|
runInitialSourceIngest: boolean;
|
|
29
32
|
skipSources: boolean;
|
|
30
33
|
}
|
|
@@ -96,6 +99,8 @@ export interface KtxSetupSourcesDeps {
|
|
|
96
99
|
validateLooker?: (projectDir: string, connectionId: string) => Promise<SourceValidationResult>;
|
|
97
100
|
validateLookml?: (connection: KtxProjectConnectionConfig) => Promise<SourceValidationResult>;
|
|
98
101
|
validateNotion?: (connection: KtxProjectConnectionConfig) => Promise<SourceValidationResult>;
|
|
102
|
+
validateSigma?: (connection: KtxProjectConnectionConfig) => Promise<SourceValidationResult>;
|
|
103
|
+
validateGdrive?: (connection: KtxProjectConnectionConfig) => Promise<SourceValidationResult>;
|
|
99
104
|
pickNotionRootPages?: typeof pickNotionRootPages;
|
|
100
105
|
discoverMetabaseDatabases?: (args: {
|
|
101
106
|
sourceUrl: string;
|