@duckcodeailabs/dql-cli 1.4.1 → 1.4.4
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/apps-api.d.ts +77 -0
- package/apps-api.d.ts.map +1 -0
- package/apps-api.js +612 -0
- package/apps-api.js.map +1 -0
- package/apps-api.test.d.ts +2 -0
- package/apps-api.test.d.ts.map +1 -0
- package/apps-api.test.js +111 -0
- package/apps-api.test.js.map +1 -0
- package/{dist/assets/dql-notebook/assets/index-BJ7MV8Gv.js → assets/dql-notebook/assets/index-DUTeFz5j.js} +145 -134
- package/{dist/assets → assets}/dql-notebook/index.html +1 -1
- package/{dist/commands → commands}/agent.d.ts.map +1 -1
- package/{dist/commands → commands}/agent.js +50 -2
- package/commands/agent.js.map +1 -0
- package/governance-runtime.d.ts +15 -0
- package/governance-runtime.d.ts.map +1 -0
- package/governance-runtime.js +50 -0
- package/governance-runtime.js.map +1 -0
- package/{dist/llm → llm}/index.d.ts.map +1 -1
- package/llm/index.js +19 -0
- package/llm/index.js.map +1 -0
- package/llm/providers/dql-agent-provider.d.ts +5 -0
- package/llm/providers/dql-agent-provider.d.ts.map +1 -0
- package/llm/providers/dql-agent-provider.js +99 -0
- package/llm/providers/dql-agent-provider.js.map +1 -0
- package/{dist/llm → llm}/types.d.ts +1 -1
- package/llm/types.d.ts.map +1 -0
- package/local-runtime.d.ts.map +1 -0
- package/{dist/local-runtime.js → local-runtime.js} +557 -36
- package/local-runtime.js.map +1 -0
- package/metricflow.d.ts +35 -0
- package/metricflow.d.ts.map +1 -0
- package/metricflow.js +122 -0
- package/metricflow.js.map +1 -0
- package/metricflow.test.d.ts +2 -0
- package/metricflow.test.d.ts.map +1 -0
- package/metricflow.test.js +54 -0
- package/metricflow.test.js.map +1 -0
- package/package.json +20 -30
- package/{dist/schedule → schedule}/runner.d.ts +3 -0
- package/schedule/runner.d.ts.map +1 -0
- package/schedule/runner.js +221 -0
- package/schedule/runner.js.map +1 -0
- package/{dist/schedule → schedule}/service.d.ts +2 -1
- package/schedule/service.d.ts.map +1 -0
- package/{dist/schedule → schedule}/service.js +39 -1
- package/schedule/service.js.map +1 -0
- package/{dist/schedule → schedule}/types.d.ts +6 -0
- package/schedule/types.d.ts.map +1 -0
- package/{dist/semantic-import.d.ts → semantic-import.d.ts} +9 -1
- package/semantic-import.d.ts.map +1 -0
- package/{dist/semantic-import.js → semantic-import.js} +269 -3
- package/semantic-import.js.map +1 -0
- package/LICENSE +0 -123
- package/README.md +0 -71
- package/dist/apps-api.d.ts +0 -16
- package/dist/apps-api.d.ts.map +0 -1
- package/dist/apps-api.js +0 -249
- package/dist/apps-api.js.map +0 -1
- package/dist/commands/agent.js.map +0 -1
- package/dist/llm/index.js +0 -16
- package/dist/llm/index.js.map +0 -1
- package/dist/llm/types.d.ts.map +0 -1
- package/dist/local-runtime.d.ts.map +0 -1
- package/dist/local-runtime.js.map +0 -1
- package/dist/schedule/runner.d.ts.map +0 -1
- package/dist/schedule/runner.js +0 -109
- package/dist/schedule/runner.js.map +0 -1
- package/dist/schedule/service.d.ts.map +0 -1
- package/dist/schedule/service.js.map +0 -1
- package/dist/schedule/types.d.ts.map +0 -1
- package/dist/semantic-import.d.ts.map +0 -1
- package/dist/semantic-import.js.map +0 -1
- /package/{dist/args.d.ts → args.d.ts} +0 -0
- /package/{dist/args.d.ts.map → args.d.ts.map} +0 -0
- /package/{dist/args.js → args.js} +0 -0
- /package/{dist/args.js.map → args.js.map} +0 -0
- /package/{dist/args.test.d.ts → args.test.d.ts} +0 -0
- /package/{dist/args.test.d.ts.map → args.test.d.ts.map} +0 -0
- /package/{dist/args.test.js → args.test.js} +0 -0
- /package/{dist/args.test.js.map → args.test.js.map} +0 -0
- /package/{dist/assets → assets}/dql-notebook/assets/codemirror-DJYUkPr1.js +0 -0
- /package/{dist/assets → assets}/dql-notebook/assets/index-DrhoZmtv.css +0 -0
- /package/{dist/assets → assets}/dql-notebook/assets/react-CRB3T2We.js +0 -0
- /package/{dist/assets → assets}/notebook-browser/app.js +0 -0
- /package/{dist/assets → assets}/notebook-browser/index.html +0 -0
- /package/{dist/assets → assets}/notebook-browser/styles.css +0 -0
- /package/{dist/block-templates.d.ts → block-templates.d.ts} +0 -0
- /package/{dist/block-templates.d.ts.map → block-templates.d.ts.map} +0 -0
- /package/{dist/block-templates.js → block-templates.js} +0 -0
- /package/{dist/block-templates.js.map → block-templates.js.map} +0 -0
- /package/{dist/commands → commands}/agent.d.ts +0 -0
- /package/{dist/commands → commands}/app.d.ts +0 -0
- /package/{dist/commands → commands}/app.d.ts.map +0 -0
- /package/{dist/commands → commands}/app.js +0 -0
- /package/{dist/commands → commands}/app.js.map +0 -0
- /package/{dist/commands → commands}/build.d.ts +0 -0
- /package/{dist/commands → commands}/build.d.ts.map +0 -0
- /package/{dist/commands → commands}/build.js +0 -0
- /package/{dist/commands → commands}/build.js.map +0 -0
- /package/{dist/commands → commands}/build.test.d.ts +0 -0
- /package/{dist/commands → commands}/build.test.d.ts.map +0 -0
- /package/{dist/commands → commands}/build.test.js +0 -0
- /package/{dist/commands → commands}/build.test.js.map +0 -0
- /package/{dist/commands → commands}/certify.d.ts +0 -0
- /package/{dist/commands → commands}/certify.d.ts.map +0 -0
- /package/{dist/commands → commands}/certify.js +0 -0
- /package/{dist/commands → commands}/certify.js.map +0 -0
- /package/{dist/commands → commands}/compile.d.ts +0 -0
- /package/{dist/commands → commands}/compile.d.ts.map +0 -0
- /package/{dist/commands → commands}/compile.js +0 -0
- /package/{dist/commands → commands}/compile.js.map +0 -0
- /package/{dist/commands → commands}/compile.test.d.ts +0 -0
- /package/{dist/commands → commands}/compile.test.d.ts.map +0 -0
- /package/{dist/commands → commands}/compile.test.js +0 -0
- /package/{dist/commands → commands}/compile.test.js.map +0 -0
- /package/{dist/commands → commands}/diff.d.ts +0 -0
- /package/{dist/commands → commands}/diff.d.ts.map +0 -0
- /package/{dist/commands → commands}/diff.js +0 -0
- /package/{dist/commands → commands}/diff.js.map +0 -0
- /package/{dist/commands → commands}/doctor.d.ts +0 -0
- /package/{dist/commands → commands}/doctor.d.ts.map +0 -0
- /package/{dist/commands → commands}/doctor.js +0 -0
- /package/{dist/commands → commands}/doctor.js.map +0 -0
- /package/{dist/commands → commands}/doctor.test.d.ts +0 -0
- /package/{dist/commands → commands}/doctor.test.d.ts.map +0 -0
- /package/{dist/commands → commands}/doctor.test.js +0 -0
- /package/{dist/commands → commands}/doctor.test.js.map +0 -0
- /package/{dist/commands → commands}/fmt.d.ts +0 -0
- /package/{dist/commands → commands}/fmt.d.ts.map +0 -0
- /package/{dist/commands → commands}/fmt.js +0 -0
- /package/{dist/commands → commands}/fmt.js.map +0 -0
- /package/{dist/commands → commands}/info.d.ts +0 -0
- /package/{dist/commands → commands}/info.d.ts.map +0 -0
- /package/{dist/commands → commands}/info.js +0 -0
- /package/{dist/commands → commands}/info.js.map +0 -0
- /package/{dist/commands → commands}/init.d.ts +0 -0
- /package/{dist/commands → commands}/init.d.ts.map +0 -0
- /package/{dist/commands → commands}/init.js +0 -0
- /package/{dist/commands → commands}/init.js.map +0 -0
- /package/{dist/commands → commands}/init.test.d.ts +0 -0
- /package/{dist/commands → commands}/init.test.d.ts.map +0 -0
- /package/{dist/commands → commands}/init.test.js +0 -0
- /package/{dist/commands → commands}/init.test.js.map +0 -0
- /package/{dist/commands → commands}/lineage.d.ts +0 -0
- /package/{dist/commands → commands}/lineage.d.ts.map +0 -0
- /package/{dist/commands → commands}/lineage.js +0 -0
- /package/{dist/commands → commands}/lineage.js.map +0 -0
- /package/{dist/commands → commands}/mcp.d.ts +0 -0
- /package/{dist/commands → commands}/mcp.d.ts.map +0 -0
- /package/{dist/commands → commands}/mcp.js +0 -0
- /package/{dist/commands → commands}/mcp.js.map +0 -0
- /package/{dist/commands → commands}/migrate.d.ts +0 -0
- /package/{dist/commands → commands}/migrate.d.ts.map +0 -0
- /package/{dist/commands → commands}/migrate.js +0 -0
- /package/{dist/commands → commands}/migrate.js.map +0 -0
- /package/{dist/commands → commands}/new.d.ts +0 -0
- /package/{dist/commands → commands}/new.d.ts.map +0 -0
- /package/{dist/commands → commands}/new.js +0 -0
- /package/{dist/commands → commands}/new.js.map +0 -0
- /package/{dist/commands → commands}/new.test.d.ts +0 -0
- /package/{dist/commands → commands}/new.test.d.ts.map +0 -0
- /package/{dist/commands → commands}/new.test.js +0 -0
- /package/{dist/commands → commands}/new.test.js.map +0 -0
- /package/{dist/commands → commands}/notebook.d.ts +0 -0
- /package/{dist/commands → commands}/notebook.d.ts.map +0 -0
- /package/{dist/commands → commands}/notebook.js +0 -0
- /package/{dist/commands → commands}/notebook.js.map +0 -0
- /package/{dist/commands → commands}/parse.d.ts +0 -0
- /package/{dist/commands → commands}/parse.d.ts.map +0 -0
- /package/{dist/commands → commands}/parse.js +0 -0
- /package/{dist/commands → commands}/parse.js.map +0 -0
- /package/{dist/commands → commands}/preview.d.ts +0 -0
- /package/{dist/commands → commands}/preview.d.ts.map +0 -0
- /package/{dist/commands → commands}/preview.js +0 -0
- /package/{dist/commands → commands}/preview.js.map +0 -0
- /package/{dist/commands → commands}/schedule.d.ts +0 -0
- /package/{dist/commands → commands}/schedule.d.ts.map +0 -0
- /package/{dist/commands → commands}/schedule.js +0 -0
- /package/{dist/commands → commands}/schedule.js.map +0 -0
- /package/{dist/commands → commands}/semantic.d.ts +0 -0
- /package/{dist/commands → commands}/semantic.d.ts.map +0 -0
- /package/{dist/commands → commands}/semantic.js +0 -0
- /package/{dist/commands → commands}/semantic.js.map +0 -0
- /package/{dist/commands → commands}/serve.d.ts +0 -0
- /package/{dist/commands → commands}/serve.d.ts.map +0 -0
- /package/{dist/commands → commands}/serve.js +0 -0
- /package/{dist/commands → commands}/serve.js.map +0 -0
- /package/{dist/commands → commands}/slack.d.ts +0 -0
- /package/{dist/commands → commands}/slack.d.ts.map +0 -0
- /package/{dist/commands → commands}/slack.js +0 -0
- /package/{dist/commands → commands}/slack.js.map +0 -0
- /package/{dist/commands → commands}/sync.d.ts +0 -0
- /package/{dist/commands → commands}/sync.d.ts.map +0 -0
- /package/{dist/commands → commands}/sync.js +0 -0
- /package/{dist/commands → commands}/sync.js.map +0 -0
- /package/{dist/commands → commands}/sync.test.d.ts +0 -0
- /package/{dist/commands → commands}/sync.test.d.ts.map +0 -0
- /package/{dist/commands → commands}/sync.test.js +0 -0
- /package/{dist/commands → commands}/sync.test.js.map +0 -0
- /package/{dist/commands → commands}/test.d.ts +0 -0
- /package/{dist/commands → commands}/test.d.ts.map +0 -0
- /package/{dist/commands → commands}/test.js +0 -0
- /package/{dist/commands → commands}/test.js.map +0 -0
- /package/{dist/commands → commands}/validate.d.ts +0 -0
- /package/{dist/commands → commands}/validate.d.ts.map +0 -0
- /package/{dist/commands → commands}/validate.js +0 -0
- /package/{dist/commands → commands}/validate.js.map +0 -0
- /package/{dist/commands → commands}/verify.d.ts +0 -0
- /package/{dist/commands → commands}/verify.d.ts.map +0 -0
- /package/{dist/commands → commands}/verify.js +0 -0
- /package/{dist/commands → commands}/verify.js.map +0 -0
- /package/{dist/digest.d.ts → digest.d.ts} +0 -0
- /package/{dist/digest.d.ts.map → digest.d.ts.map} +0 -0
- /package/{dist/digest.js → digest.js} +0 -0
- /package/{dist/digest.js.map → digest.js.map} +0 -0
- /package/{dist/git-service.d.ts → git-service.d.ts} +0 -0
- /package/{dist/git-service.d.ts.map → git-service.d.ts.map} +0 -0
- /package/{dist/git-service.js → git-service.js} +0 -0
- /package/{dist/git-service.js.map → git-service.js.map} +0 -0
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/index.d.ts.map → index.d.ts.map} +0 -0
- /package/{dist/index.js → index.js} +0 -0
- /package/{dist/index.js.map → index.js.map} +0 -0
- /package/{dist/llm → llm}/index.d.ts +0 -0
- /package/{dist/llm → llm}/providers/claude-agent-sdk.d.ts +0 -0
- /package/{dist/llm → llm}/providers/claude-agent-sdk.d.ts.map +0 -0
- /package/{dist/llm → llm}/providers/claude-agent-sdk.js +0 -0
- /package/{dist/llm → llm}/providers/claude-agent-sdk.js.map +0 -0
- /package/{dist/llm → llm}/providers/claude-code.d.ts +0 -0
- /package/{dist/llm → llm}/providers/claude-code.d.ts.map +0 -0
- /package/{dist/llm → llm}/providers/claude-code.js +0 -0
- /package/{dist/llm → llm}/providers/claude-code.js.map +0 -0
- /package/{dist/llm → llm}/tools.d.ts +0 -0
- /package/{dist/llm → llm}/tools.d.ts.map +0 -0
- /package/{dist/llm → llm}/tools.js +0 -0
- /package/{dist/llm → llm}/tools.js.map +0 -0
- /package/{dist/llm → llm}/types.js +0 -0
- /package/{dist/llm → llm}/types.js.map +0 -0
- /package/{dist/local-runtime.d.ts → local-runtime.d.ts} +0 -0
- /package/{dist/local-runtime.test.d.ts → local-runtime.test.d.ts} +0 -0
- /package/{dist/local-runtime.test.d.ts.map → local-runtime.test.d.ts.map} +0 -0
- /package/{dist/local-runtime.test.js → local-runtime.test.js} +0 -0
- /package/{dist/local-runtime.test.js.map → local-runtime.test.js.map} +0 -0
- /package/{dist/open-browser.d.ts → open-browser.d.ts} +0 -0
- /package/{dist/open-browser.d.ts.map → open-browser.d.ts.map} +0 -0
- /package/{dist/open-browser.js → open-browser.js} +0 -0
- /package/{dist/open-browser.js.map → open-browser.js.map} +0 -0
- /package/{dist/schedule → schedule}/alerts.d.ts +0 -0
- /package/{dist/schedule → schedule}/alerts.d.ts.map +0 -0
- /package/{dist/schedule → schedule}/alerts.js +0 -0
- /package/{dist/schedule → schedule}/alerts.js.map +0 -0
- /package/{dist/schedule → schedule}/discovery.d.ts +0 -0
- /package/{dist/schedule → schedule}/discovery.d.ts.map +0 -0
- /package/{dist/schedule → schedule}/discovery.js +0 -0
- /package/{dist/schedule → schedule}/discovery.js.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/email.d.ts +0 -0
- /package/{dist/schedule → schedule}/notifiers/email.d.ts.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/email.js +0 -0
- /package/{dist/schedule → schedule}/notifiers/email.js.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/file.d.ts +0 -0
- /package/{dist/schedule → schedule}/notifiers/file.d.ts.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/file.js +0 -0
- /package/{dist/schedule → schedule}/notifiers/file.js.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/index.d.ts +0 -0
- /package/{dist/schedule → schedule}/notifiers/index.d.ts.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/index.js +0 -0
- /package/{dist/schedule → schedule}/notifiers/index.js.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/slack.d.ts +0 -0
- /package/{dist/schedule → schedule}/notifiers/slack.d.ts.map +0 -0
- /package/{dist/schedule → schedule}/notifiers/slack.js +0 -0
- /package/{dist/schedule → schedule}/notifiers/slack.js.map +0 -0
- /package/{dist/schedule → schedule}/runs.d.ts +0 -0
- /package/{dist/schedule → schedule}/runs.d.ts.map +0 -0
- /package/{dist/schedule → schedule}/runs.js +0 -0
- /package/{dist/schedule → schedule}/runs.js.map +0 -0
- /package/{dist/schedule → schedule}/types.js +0 -0
- /package/{dist/schedule → schedule}/types.js.map +0 -0
- /package/{dist/semantic-import.test.d.ts → semantic-import.test.d.ts} +0 -0
- /package/{dist/semantic-import.test.d.ts.map → semantic-import.test.d.ts.map} +0 -0
- /package/{dist/semantic-import.test.js → semantic-import.test.js} +0 -0
- /package/{dist/semantic-import.test.js.map → semantic-import.test.js.map} +0 -0
|
@@ -3,12 +3,14 @@ import { createServer } from 'node:http';
|
|
|
3
3
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, watch, writeFileSync } from 'node:fs';
|
|
4
4
|
import { dirname, extname, join, normalize, relative, resolve } from 'node:path';
|
|
5
5
|
import { buildExecutionPlan, createWelcomeNotebook, deserializeNotebook, getConnectorFormSchemas, hasSemanticRefs, resolveSemanticRefs, } from '@duckcodeailabs/dql-notebook';
|
|
6
|
-
import { loadSemanticLayerFromDir, resolveSemanticLayerAsync, Parser, buildLineageGraph, buildManifest, analyzeImpact, buildTrustChain, detectDomainFlows, getDomainTrustOverview, queryLineage, queryCompleteLineagePaths, LineageGraph, canonicalize, canonicalizeNotebook, diffDQL, diffNotebook, } from '@duckcodeailabs/dql-core';
|
|
6
|
+
import { loadSemanticLayerFromDir, resolveSemanticLayerAsync, Parser, buildLineageGraph, buildManifest, findAppDocuments, findDashboardsForApp, isBlockIdRef, loadAppDocument, loadDashboardDocument, analyzeImpact, buildTrustChain, detectDomainFlows, getDomainTrustOverview, queryLineage, queryCompleteLineagePaths, LineageGraph, canonicalize, canonicalizeNotebook, diffDQL, diffNotebook, } from '@duckcodeailabs/dql-core';
|
|
7
7
|
import { load as loadYaml } from 'js-yaml';
|
|
8
8
|
import { listBlockTemplates } from './block-templates.js';
|
|
9
9
|
import { getRunner as getLLMRunner } from './llm/index.js';
|
|
10
10
|
import { handleAppsApi } from './apps-api.js';
|
|
11
|
+
import { DQLAccessDeniedError, activePersonaAppId, assertAppAccess, loadRuntimeApp, runtimeVariables, } from './governance-runtime.js';
|
|
11
12
|
import { buildSemanticObjectDetail, buildSemanticTree, computeSyncDiff, loadSemanticImportManifest, performSemanticImport, previewSemanticImport, syncSemanticImport, } from './semantic-import.js';
|
|
13
|
+
import { MetricFlowUnavailableError, compileMetricFlowQuery, hasDbtSemanticManifest, } from './metricflow.js';
|
|
12
14
|
export async function startLocalServer(opts) {
|
|
13
15
|
const { rootDir, executor, connection: rawConnection, preferredPort, projectRoot = process.cwd() } = opts;
|
|
14
16
|
const bindHost = opts.host ?? process.env.DQL_HOST ?? '127.0.0.1';
|
|
@@ -138,6 +140,117 @@ export async function startLocalServer(opts) {
|
|
|
138
140
|
res.end(serializeJSON({ status: 'ok' }));
|
|
139
141
|
return;
|
|
140
142
|
}
|
|
143
|
+
if (req.method === 'GET' && path === '/api/settings/env-status') {
|
|
144
|
+
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
145
|
+
res.end(serializeJSON({ groups: collectSettingsEnvStatus() }));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const appDashRun = path.match(/^\/api\/apps\/([^/]+)\/dashboards\/([^/]+)\/run$/);
|
|
149
|
+
if (req.method === 'POST' && appDashRun) {
|
|
150
|
+
try {
|
|
151
|
+
const appId = decodeURIComponent(appDashRun[1]);
|
|
152
|
+
const dashboardId = decodeURIComponent(appDashRun[2]);
|
|
153
|
+
const body = await readJSON(req).catch(() => ({}));
|
|
154
|
+
const loaded = loadAppDashboard(projectRoot, appId, dashboardId);
|
|
155
|
+
if (!loaded) {
|
|
156
|
+
res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
157
|
+
res.end(serializeJSON({ error: `Dashboard "${dashboardId}" not found in app "${appId}"` }));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const manifest = buildManifest({ projectRoot });
|
|
161
|
+
const variables = body.variables && typeof body.variables === 'object'
|
|
162
|
+
? body.variables
|
|
163
|
+
: {};
|
|
164
|
+
const tiles = [];
|
|
165
|
+
for (const item of loaded.dashboard.layout.items) {
|
|
166
|
+
const block = resolveDashboardItemBlock(item, manifest);
|
|
167
|
+
if (!block) {
|
|
168
|
+
tiles.push({
|
|
169
|
+
tileId: item.i,
|
|
170
|
+
status: 'unresolved',
|
|
171
|
+
blockRef: isBlockIdRef(item.block) ? item.block.blockId : item.block.ref,
|
|
172
|
+
error: 'Block reference could not be resolved',
|
|
173
|
+
});
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
assertAppAccess({
|
|
178
|
+
app: loaded.app,
|
|
179
|
+
domain: block.domain ?? loaded.dashboard.metadata.domain ?? loaded.app.domain,
|
|
180
|
+
level: 'execute',
|
|
181
|
+
});
|
|
182
|
+
const absBlockPath = join(projectRoot, block.filePath);
|
|
183
|
+
const source = readFileSync(absBlockPath, 'utf-8');
|
|
184
|
+
const semanticCompose = semanticLayer
|
|
185
|
+
? composeSemanticBlockSql(source, semanticLayer, {
|
|
186
|
+
driver: connection.driver,
|
|
187
|
+
projectRoot,
|
|
188
|
+
projectConfig,
|
|
189
|
+
detectedProvider: semanticDetectedProvider,
|
|
190
|
+
})
|
|
191
|
+
: null;
|
|
192
|
+
const plan = buildExecutionPlan({ id: item.i, type: 'dql', source, title: item.title ?? block.name }, { semanticLayer, driver: connection.driver });
|
|
193
|
+
if (!plan && !semanticCompose?.sql) {
|
|
194
|
+
tiles.push({
|
|
195
|
+
tileId: item.i,
|
|
196
|
+
status: 'error',
|
|
197
|
+
blockId: block.name,
|
|
198
|
+
error: semanticCompose?.diagnostics.find((diagnostic) => diagnostic.severity === 'error')?.message ?? 'Block produced no executable plan',
|
|
199
|
+
});
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const prepared = prepareLocalExecution(semanticCompose?.sql ?? plan.sql, isConnectionConfig(body.connection) ? body.connection : connection, projectRoot, projectConfig);
|
|
203
|
+
const result = await executor.executeQuery(prepared.sql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...variables }), prepared.connection);
|
|
204
|
+
tiles.push({
|
|
205
|
+
tileId: item.i,
|
|
206
|
+
status: 'ok',
|
|
207
|
+
blockId: block.name,
|
|
208
|
+
blockPath: block.filePath,
|
|
209
|
+
certificationStatus: block.status ?? null,
|
|
210
|
+
title: item.title ?? block.name,
|
|
211
|
+
viz: item.viz,
|
|
212
|
+
chartConfig: plan?.chartConfig ?? { chart: item.viz.type },
|
|
213
|
+
result: normalizeQueryResult(result),
|
|
214
|
+
citation: {
|
|
215
|
+
kind: 'block',
|
|
216
|
+
name: block.name,
|
|
217
|
+
path: block.filePath,
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
if (err instanceof DQLAccessDeniedError) {
|
|
223
|
+
tiles.push({
|
|
224
|
+
tileId: item.i,
|
|
225
|
+
status: 'unauthorized',
|
|
226
|
+
blockId: block.name,
|
|
227
|
+
error: err.message,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
tiles.push({
|
|
232
|
+
tileId: item.i,
|
|
233
|
+
status: 'error',
|
|
234
|
+
blockId: block.name,
|
|
235
|
+
error: err instanceof Error ? err.message : String(err),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
241
|
+
res.end(serializeJSON({
|
|
242
|
+
appId,
|
|
243
|
+
dashboardId,
|
|
244
|
+
persona: activePersonaAppId() ? { appId: activePersonaAppId() } : null,
|
|
245
|
+
tiles,
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
250
|
+
res.end(serializeJSON({ error: err instanceof Error ? err.message : String(err) }));
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
141
254
|
// Apps, dashboards, persona — see apps-api.ts. Returns true if handled.
|
|
142
255
|
if (path.startsWith('/api/apps') || path === '/api/persona') {
|
|
143
256
|
try {
|
|
@@ -218,6 +331,11 @@ export async function startLocalServer(opts) {
|
|
|
218
331
|
res.end(serializeJSON({ path: `notebooks/${slug}.dqlnb`, content }));
|
|
219
332
|
}
|
|
220
333
|
catch (error) {
|
|
334
|
+
if (error instanceof DQLAccessDeniedError) {
|
|
335
|
+
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
336
|
+
res.end(serializeJSON({ error: error.message, code: 'unauthorized' }));
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
221
339
|
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
222
340
|
res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
|
|
223
341
|
}
|
|
@@ -249,6 +367,11 @@ export async function startLocalServer(opts) {
|
|
|
249
367
|
res.end(serializeJSON({ ok: true }));
|
|
250
368
|
}
|
|
251
369
|
catch (error) {
|
|
370
|
+
if (error instanceof DQLAccessDeniedError) {
|
|
371
|
+
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
372
|
+
res.end(serializeJSON({ error: error.message, code: 'unauthorized' }));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
252
375
|
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
253
376
|
res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
|
|
254
377
|
}
|
|
@@ -584,6 +707,11 @@ export async function startLocalServer(opts) {
|
|
|
584
707
|
res.end(serializeJSON({ blocks }));
|
|
585
708
|
}
|
|
586
709
|
catch (error) {
|
|
710
|
+
if (error instanceof DQLAccessDeniedError) {
|
|
711
|
+
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
712
|
+
res.end(serializeJSON({ error: error.message, code: 'unauthorized' }));
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
587
715
|
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
588
716
|
res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
|
|
589
717
|
}
|
|
@@ -636,8 +764,12 @@ export async function startLocalServer(opts) {
|
|
|
636
764
|
res.end(serializeJSON({ apps }));
|
|
637
765
|
}
|
|
638
766
|
catch (error) {
|
|
639
|
-
|
|
640
|
-
res.
|
|
767
|
+
const status = error instanceof DQLAccessDeniedError ? 403 : 500;
|
|
768
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
769
|
+
res.end(serializeJSON({
|
|
770
|
+
error: error instanceof Error ? error.message : String(error),
|
|
771
|
+
...(status === 403 ? { code: 'unauthorized' } : {}),
|
|
772
|
+
}));
|
|
641
773
|
}
|
|
642
774
|
return;
|
|
643
775
|
}
|
|
@@ -918,7 +1050,13 @@ export async function startLocalServer(opts) {
|
|
|
918
1050
|
}
|
|
919
1051
|
}
|
|
920
1052
|
const semanticCompose = semanticLayer
|
|
921
|
-
? composeSemanticBlockSql(source, semanticLayer, {
|
|
1053
|
+
? composeSemanticBlockSql(source, semanticLayer, {
|
|
1054
|
+
driver: targetConnection.driver,
|
|
1055
|
+
tableMapping,
|
|
1056
|
+
projectRoot,
|
|
1057
|
+
projectConfig,
|
|
1058
|
+
detectedProvider: semanticDetectedProvider,
|
|
1059
|
+
})
|
|
922
1060
|
: null;
|
|
923
1061
|
const validation = validateBlockStudioSource(source, semanticLayer);
|
|
924
1062
|
const executableSql = semanticCompose?.sql ?? validation.executableSql;
|
|
@@ -930,13 +1068,14 @@ export async function startLocalServer(opts) {
|
|
|
930
1068
|
res.end(serializeJSON({ error: message, diagnostics: validation.diagnostics }));
|
|
931
1069
|
return;
|
|
932
1070
|
}
|
|
933
|
-
const
|
|
934
|
-
const
|
|
1071
|
+
const plan = buildExecutionPlan({ id: 'block-studio', type: 'dql', source, title: 'Block Studio' }, { semanticLayer, driver: targetConnection.driver });
|
|
1072
|
+
const sql = resolveProjectRelativeSqlPaths(semanticCompose?.sql ?? plan?.sql ?? executableSql, projectRoot, projectConfig.dataDir);
|
|
1073
|
+
const result = await executor.executeQuery(sql, plan?.sqlParams ?? [], runtimeVariables(plan?.variables ?? {}), targetConnection);
|
|
935
1074
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
936
1075
|
res.end(serializeJSON({
|
|
937
|
-
sql: executableSql,
|
|
1076
|
+
sql: plan?.sql ?? executableSql,
|
|
938
1077
|
result: normalizeQueryResult(result),
|
|
939
|
-
chartConfig: validation.chartConfig ?? null,
|
|
1078
|
+
chartConfig: plan?.chartConfig ?? validation.chartConfig ?? null,
|
|
940
1079
|
}));
|
|
941
1080
|
}
|
|
942
1081
|
catch (error) {
|
|
@@ -1062,8 +1201,13 @@ export async function startLocalServer(opts) {
|
|
|
1062
1201
|
provider: projectConfig.semanticLayer?.provider ?? semanticDetectedProvider ?? null,
|
|
1063
1202
|
errors: semanticLayerErrors,
|
|
1064
1203
|
metrics: [],
|
|
1204
|
+
measures: [],
|
|
1065
1205
|
dimensions: [],
|
|
1206
|
+
timeDimensions: [],
|
|
1207
|
+
entities: [],
|
|
1066
1208
|
hierarchies: [],
|
|
1209
|
+
semanticModels: [],
|
|
1210
|
+
savedQueries: [],
|
|
1067
1211
|
domains: [],
|
|
1068
1212
|
tags: [],
|
|
1069
1213
|
favorites: userPrefs.favorites,
|
|
@@ -1082,6 +1226,25 @@ export async function startLocalServer(opts) {
|
|
|
1082
1226
|
table: m.table,
|
|
1083
1227
|
tags: m.tags ?? [],
|
|
1084
1228
|
owner: m.owner ?? null,
|
|
1229
|
+
metricType: m.metricType ?? null,
|
|
1230
|
+
typeParams: m.typeParams ?? null,
|
|
1231
|
+
filter: m.filter ?? null,
|
|
1232
|
+
source: m.source ?? null,
|
|
1233
|
+
}));
|
|
1234
|
+
const measures = semanticLayer.listMeasures().map((m) => ({
|
|
1235
|
+
name: m.name,
|
|
1236
|
+
label: m.label,
|
|
1237
|
+
description: m.description,
|
|
1238
|
+
domain: m.domain,
|
|
1239
|
+
agg: m.agg,
|
|
1240
|
+
expr: m.expr ?? null,
|
|
1241
|
+
table: m.table,
|
|
1242
|
+
cube: m.cube ?? null,
|
|
1243
|
+
aggTimeDimension: m.aggTimeDimension ?? null,
|
|
1244
|
+
nonAdditiveDimension: m.nonAdditiveDimension ?? null,
|
|
1245
|
+
tags: m.tags ?? [],
|
|
1246
|
+
owner: m.owner ?? null,
|
|
1247
|
+
source: m.source ?? null,
|
|
1085
1248
|
}));
|
|
1086
1249
|
const dimensions = semanticLayer.listDimensions().map((d) => ({
|
|
1087
1250
|
name: d.name,
|
|
@@ -1093,6 +1256,40 @@ export async function startLocalServer(opts) {
|
|
|
1093
1256
|
table: d.table,
|
|
1094
1257
|
tags: d.tags ?? [],
|
|
1095
1258
|
owner: d.owner ?? null,
|
|
1259
|
+
cube: d.cube ?? null,
|
|
1260
|
+
isTimeDimension: d.isTimeDimension ?? false,
|
|
1261
|
+
typeParams: d.typeParams ?? null,
|
|
1262
|
+
source: d.source ?? null,
|
|
1263
|
+
}));
|
|
1264
|
+
const timeDimensions = semanticLayer.listTimeDimensions().map((d) => ({
|
|
1265
|
+
name: d.name,
|
|
1266
|
+
label: d.label,
|
|
1267
|
+
description: d.description,
|
|
1268
|
+
domain: d.domain,
|
|
1269
|
+
sql: d.sql,
|
|
1270
|
+
type: d.type,
|
|
1271
|
+
table: d.table,
|
|
1272
|
+
cube: d.cube ?? null,
|
|
1273
|
+
granularities: d.granularities ?? [],
|
|
1274
|
+
primaryTime: d.primaryTime ?? false,
|
|
1275
|
+
tags: d.tags ?? [],
|
|
1276
|
+
owner: d.owner ?? null,
|
|
1277
|
+
typeParams: d.typeParams ?? null,
|
|
1278
|
+
source: d.source ?? null,
|
|
1279
|
+
}));
|
|
1280
|
+
const entities = semanticLayer.listEntities().map((e) => ({
|
|
1281
|
+
name: e.name,
|
|
1282
|
+
label: e.label,
|
|
1283
|
+
description: e.description,
|
|
1284
|
+
domain: e.domain,
|
|
1285
|
+
type: e.type,
|
|
1286
|
+
expr: e.expr ?? null,
|
|
1287
|
+
table: e.table,
|
|
1288
|
+
cube: e.cube ?? null,
|
|
1289
|
+
role: e.role ?? null,
|
|
1290
|
+
tags: e.tags ?? [],
|
|
1291
|
+
owner: e.owner ?? null,
|
|
1292
|
+
source: e.source ?? null,
|
|
1096
1293
|
}));
|
|
1097
1294
|
const hierarchies = semanticLayer.listHierarchies().map((h) => ({
|
|
1098
1295
|
name: h.name,
|
|
@@ -1101,14 +1298,61 @@ export async function startLocalServer(opts) {
|
|
|
1101
1298
|
domain: h.domain,
|
|
1102
1299
|
levels: h.levels.map((l) => ({ name: l.name, label: l.label })),
|
|
1103
1300
|
}));
|
|
1301
|
+
const semanticModels = semanticLayer.listSemanticModels().map((m) => ({
|
|
1302
|
+
name: m.name,
|
|
1303
|
+
label: m.label,
|
|
1304
|
+
description: m.description,
|
|
1305
|
+
domain: m.domain,
|
|
1306
|
+
model: m.model ?? null,
|
|
1307
|
+
table: m.table,
|
|
1308
|
+
entities: m.entities,
|
|
1309
|
+
measures: m.measures,
|
|
1310
|
+
dimensions: m.dimensions,
|
|
1311
|
+
timeDimensions: m.timeDimensions,
|
|
1312
|
+
tags: m.tags ?? [],
|
|
1313
|
+
owner: m.owner ?? null,
|
|
1314
|
+
source: m.source ?? null,
|
|
1315
|
+
}));
|
|
1316
|
+
const savedQueries = semanticLayer.listSavedQueries().map((q) => ({
|
|
1317
|
+
name: q.name,
|
|
1318
|
+
label: q.label,
|
|
1319
|
+
description: q.description,
|
|
1320
|
+
domain: q.domain,
|
|
1321
|
+
metrics: q.metrics,
|
|
1322
|
+
dimensions: q.dimensions,
|
|
1323
|
+
timeDimension: q.timeDimension ?? null,
|
|
1324
|
+
granularity: q.granularity ?? null,
|
|
1325
|
+
filters: q.filters ?? null,
|
|
1326
|
+
tags: q.tags ?? [],
|
|
1327
|
+
owner: q.owner ?? null,
|
|
1328
|
+
source: q.source ?? null,
|
|
1329
|
+
}));
|
|
1330
|
+
const provider = projectConfig.semanticLayer?.provider ?? semanticDetectedProvider ?? 'dql';
|
|
1331
|
+
const dbtExecutionReady = provider === 'dbt'
|
|
1332
|
+
? hasDbtSemanticManifest(projectRoot, projectConfig.semanticLayer?.projectPath)
|
|
1333
|
+
: false;
|
|
1104
1334
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1105
1335
|
res.end(serializeJSON({
|
|
1106
1336
|
available: true,
|
|
1107
|
-
provider
|
|
1337
|
+
provider,
|
|
1338
|
+
execution: provider === 'dbt'
|
|
1339
|
+
? {
|
|
1340
|
+
engine: 'metricflow',
|
|
1341
|
+
ready: dbtExecutionReady,
|
|
1342
|
+
setup: dbtExecutionReady
|
|
1343
|
+
? null
|
|
1344
|
+
: 'Run `dbt parse` or `dbt build` so target/semantic_manifest.json exists, and install MetricFlow so `mf` is on PATH.',
|
|
1345
|
+
}
|
|
1346
|
+
: { engine: 'native', ready: true, setup: null },
|
|
1108
1347
|
errors: semanticLayerErrors,
|
|
1109
1348
|
metrics,
|
|
1349
|
+
measures,
|
|
1110
1350
|
dimensions,
|
|
1351
|
+
timeDimensions,
|
|
1352
|
+
entities,
|
|
1111
1353
|
hierarchies,
|
|
1354
|
+
semanticModels,
|
|
1355
|
+
savedQueries,
|
|
1112
1356
|
domains: semanticLayer.listDomains(),
|
|
1113
1357
|
tags: semanticLayer.listTags(),
|
|
1114
1358
|
favorites: userPrefs.favorites,
|
|
@@ -1329,7 +1573,7 @@ export async function startLocalServer(opts) {
|
|
|
1329
1573
|
if (req.method === 'GET' && path === '/api/semantic-layer/search') {
|
|
1330
1574
|
if (!semanticLayer) {
|
|
1331
1575
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1332
|
-
res.end(serializeJSON({ metrics: [], dimensions: [], hierarchies: [] }));
|
|
1576
|
+
res.end(serializeJSON({ metrics: [], measures: [], dimensions: [], timeDimensions: [], entities: [], hierarchies: [], semanticModels: [], savedQueries: [] }));
|
|
1333
1577
|
return;
|
|
1334
1578
|
}
|
|
1335
1579
|
const q = url.searchParams.get('q') ?? '';
|
|
@@ -1339,7 +1583,7 @@ export async function startLocalServer(opts) {
|
|
|
1339
1583
|
const results = semanticLayer.searchAdvanced(q, {
|
|
1340
1584
|
domains: domain ? [domain] : undefined,
|
|
1341
1585
|
tags: tag ? [tag] : undefined,
|
|
1342
|
-
types:
|
|
1586
|
+
types: ['metric', 'measure', 'dimension', 'hierarchy', 'entity', 'semantic_model', 'saved_query'].includes(type) ? [type] : undefined,
|
|
1343
1587
|
});
|
|
1344
1588
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1345
1589
|
res.end(serializeJSON({
|
|
@@ -1354,6 +1598,18 @@ export async function startLocalServer(opts) {
|
|
|
1354
1598
|
tags: m.tags ?? [],
|
|
1355
1599
|
owner: m.owner ?? null,
|
|
1356
1600
|
})),
|
|
1601
|
+
measures: results.measures.map((m) => ({
|
|
1602
|
+
name: m.name,
|
|
1603
|
+
label: m.label,
|
|
1604
|
+
description: m.description,
|
|
1605
|
+
domain: m.domain,
|
|
1606
|
+
agg: m.agg,
|
|
1607
|
+
expr: m.expr,
|
|
1608
|
+
table: m.table,
|
|
1609
|
+
cube: m.cube,
|
|
1610
|
+
tags: m.tags ?? [],
|
|
1611
|
+
owner: m.owner ?? null,
|
|
1612
|
+
})),
|
|
1357
1613
|
dimensions: results.dimensions.map((d) => ({
|
|
1358
1614
|
name: d.name,
|
|
1359
1615
|
label: d.label,
|
|
@@ -1365,6 +1621,27 @@ export async function startLocalServer(opts) {
|
|
|
1365
1621
|
tags: d.tags ?? [],
|
|
1366
1622
|
owner: d.owner ?? null,
|
|
1367
1623
|
})),
|
|
1624
|
+
timeDimensions: semanticLayer.listTimeDimensions().filter((d) => results.dimensions.some((dim) => dim.name === d.name)).map((d) => ({
|
|
1625
|
+
name: d.name,
|
|
1626
|
+
label: d.label,
|
|
1627
|
+
description: d.description,
|
|
1628
|
+
domain: d.domain,
|
|
1629
|
+
sql: d.sql,
|
|
1630
|
+
type: d.type,
|
|
1631
|
+
table: d.table,
|
|
1632
|
+
tags: d.tags ?? [],
|
|
1633
|
+
owner: d.owner ?? null,
|
|
1634
|
+
})),
|
|
1635
|
+
entities: results.entities.map((e) => ({
|
|
1636
|
+
name: e.name,
|
|
1637
|
+
label: e.label,
|
|
1638
|
+
description: e.description,
|
|
1639
|
+
domain: e.domain,
|
|
1640
|
+
type: e.type,
|
|
1641
|
+
table: e.table,
|
|
1642
|
+
tags: e.tags ?? [],
|
|
1643
|
+
owner: e.owner ?? null,
|
|
1644
|
+
})),
|
|
1368
1645
|
hierarchies: results.hierarchies.map((h) => ({
|
|
1369
1646
|
name: h.name,
|
|
1370
1647
|
label: h.label,
|
|
@@ -1372,6 +1649,24 @@ export async function startLocalServer(opts) {
|
|
|
1372
1649
|
domain: h.domain,
|
|
1373
1650
|
levels: h.levels.map((l) => ({ name: l.name, label: l.label })),
|
|
1374
1651
|
})),
|
|
1652
|
+
semanticModels: results.semanticModels.map((m) => ({
|
|
1653
|
+
name: m.name,
|
|
1654
|
+
label: m.label,
|
|
1655
|
+
description: m.description,
|
|
1656
|
+
domain: m.domain,
|
|
1657
|
+
table: m.table,
|
|
1658
|
+
measures: m.measures,
|
|
1659
|
+
dimensions: m.dimensions,
|
|
1660
|
+
timeDimensions: m.timeDimensions,
|
|
1661
|
+
})),
|
|
1662
|
+
savedQueries: results.savedQueries.map((q) => ({
|
|
1663
|
+
name: q.name,
|
|
1664
|
+
label: q.label,
|
|
1665
|
+
description: q.description,
|
|
1666
|
+
domain: q.domain,
|
|
1667
|
+
metrics: q.metrics,
|
|
1668
|
+
dimensions: q.dimensions,
|
|
1669
|
+
})),
|
|
1375
1670
|
}));
|
|
1376
1671
|
return;
|
|
1377
1672
|
}
|
|
@@ -1538,7 +1833,7 @@ export async function startLocalServer(opts) {
|
|
|
1538
1833
|
return;
|
|
1539
1834
|
}
|
|
1540
1835
|
const { provider, messages, upstream } = body;
|
|
1541
|
-
const runner = provider
|
|
1836
|
+
const runner = isLLMProviderId(provider) ? getLLMRunner(provider) : null;
|
|
1542
1837
|
if (!runner) {
|
|
1543
1838
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1544
1839
|
res.end(serializeJSON({ error: `Unknown provider: ${provider}` }));
|
|
@@ -1587,7 +1882,10 @@ export async function startLocalServer(opts) {
|
|
|
1587
1882
|
return;
|
|
1588
1883
|
}
|
|
1589
1884
|
const prepared = prepareLocalExecution(semantic.sql, isConnectionConfig(body.connection) ? body.connection : connection, projectRoot, projectConfig);
|
|
1590
|
-
const
|
|
1885
|
+
const app = loadRuntimeApp(projectRoot, typeof body.appId === 'string' ? body.appId : activePersonaAppId());
|
|
1886
|
+
const domain = typeof body.domain === 'string' ? body.domain : app?.domain;
|
|
1887
|
+
assertAppAccess({ app, domain, level: 'execute' });
|
|
1888
|
+
const result = await executor.executeQuery(prepared.sql, Array.isArray(body.sqlParams) ? body.sqlParams : [], runtimeVariables(body.variables && typeof body.variables === 'object' ? body.variables : {}), prepared.connection);
|
|
1591
1889
|
const payload = serializeJSON(normalizeQueryResult(result, semantic.semanticRefs));
|
|
1592
1890
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1593
1891
|
res.end(payload);
|
|
@@ -1597,6 +1895,16 @@ export async function startLocalServer(opts) {
|
|
|
1597
1895
|
res.end();
|
|
1598
1896
|
return;
|
|
1599
1897
|
}
|
|
1898
|
+
if (error instanceof DQLAccessDeniedError) {
|
|
1899
|
+
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1900
|
+
res.end(serializeJSON({
|
|
1901
|
+
columns: [],
|
|
1902
|
+
rows: [],
|
|
1903
|
+
error: error.message,
|
|
1904
|
+
code: 'unauthorized',
|
|
1905
|
+
}));
|
|
1906
|
+
return;
|
|
1907
|
+
}
|
|
1600
1908
|
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1601
1909
|
res.end(serializeJSON({
|
|
1602
1910
|
columns: [],
|
|
@@ -1615,7 +1923,7 @@ export async function startLocalServer(opts) {
|
|
|
1615
1923
|
return;
|
|
1616
1924
|
}
|
|
1617
1925
|
const body = await readJSON(req);
|
|
1618
|
-
const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy } = body;
|
|
1926
|
+
const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
|
|
1619
1927
|
// Resolve which connection to use — request can override default
|
|
1620
1928
|
const targetConnection = isConnectionConfig(body.connection) ? body.connection : connection;
|
|
1621
1929
|
const driver = targetConnection.driver;
|
|
@@ -1649,7 +1957,22 @@ export async function startLocalServer(opts) {
|
|
|
1649
1957
|
catch {
|
|
1650
1958
|
// Non-fatal: proceed without table mapping
|
|
1651
1959
|
}
|
|
1652
|
-
const composed =
|
|
1960
|
+
const composed = composeRuntimeSemanticQuery({
|
|
1961
|
+
metrics,
|
|
1962
|
+
dimensions,
|
|
1963
|
+
filters,
|
|
1964
|
+
limit,
|
|
1965
|
+
timeDimension,
|
|
1966
|
+
orderBy,
|
|
1967
|
+
savedQuery,
|
|
1968
|
+
engine,
|
|
1969
|
+
}, semanticLayer, {
|
|
1970
|
+
projectRoot,
|
|
1971
|
+
projectConfig,
|
|
1972
|
+
detectedProvider: semanticDetectedProvider,
|
|
1973
|
+
driver,
|
|
1974
|
+
tableMapping,
|
|
1975
|
+
});
|
|
1653
1976
|
if (!composed) {
|
|
1654
1977
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1655
1978
|
res.end(serializeJSON({ error: `Could not compose query for metrics: [${metrics.join(', ')}]` }));
|
|
@@ -1663,12 +1986,25 @@ export async function startLocalServer(opts) {
|
|
|
1663
1986
|
sql: composed.sql,
|
|
1664
1987
|
tables: composed.tables,
|
|
1665
1988
|
joins: composed.joins,
|
|
1989
|
+
engine: composed.engine,
|
|
1666
1990
|
result: normalizeQueryResult(result),
|
|
1667
1991
|
}));
|
|
1668
1992
|
}
|
|
1669
1993
|
catch (error) {
|
|
1670
|
-
|
|
1671
|
-
|
|
1994
|
+
if (error instanceof DQLAccessDeniedError) {
|
|
1995
|
+
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1996
|
+
res.end(serializeJSON({ error: error.message, code: 'unauthorized' }));
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
const status = error instanceof MetricFlowUnavailableError ? 400 : 500;
|
|
2000
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
2001
|
+
res.end(serializeJSON({
|
|
2002
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2003
|
+
code: error instanceof MetricFlowUnavailableError ? 'metricflow_unavailable' : undefined,
|
|
2004
|
+
hint: error instanceof MetricFlowUnavailableError
|
|
2005
|
+
? 'Install dbt Semantic Layer dependencies, run dbt parse/build to create target/semantic_manifest.json, then retry.'
|
|
2006
|
+
: undefined,
|
|
2007
|
+
}));
|
|
1672
2008
|
}
|
|
1673
2009
|
return;
|
|
1674
2010
|
}
|
|
@@ -1680,7 +2016,7 @@ export async function startLocalServer(opts) {
|
|
|
1680
2016
|
return;
|
|
1681
2017
|
}
|
|
1682
2018
|
const body = await readJSON(req);
|
|
1683
|
-
const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy } = body;
|
|
2019
|
+
const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
|
|
1684
2020
|
const targetConnection = isConnectionConfig(body.connection) ? body.connection : connection;
|
|
1685
2021
|
const driver = targetConnection.driver;
|
|
1686
2022
|
let tableMapping;
|
|
@@ -1707,7 +2043,22 @@ export async function startLocalServer(opts) {
|
|
|
1707
2043
|
catch {
|
|
1708
2044
|
tableMapping = undefined;
|
|
1709
2045
|
}
|
|
1710
|
-
const composed =
|
|
2046
|
+
const composed = composeRuntimeSemanticQuery({
|
|
2047
|
+
metrics,
|
|
2048
|
+
dimensions,
|
|
2049
|
+
filters,
|
|
2050
|
+
limit,
|
|
2051
|
+
timeDimension,
|
|
2052
|
+
orderBy,
|
|
2053
|
+
savedQuery,
|
|
2054
|
+
engine,
|
|
2055
|
+
}, semanticLayer, {
|
|
2056
|
+
projectRoot,
|
|
2057
|
+
projectConfig,
|
|
2058
|
+
detectedProvider: semanticDetectedProvider,
|
|
2059
|
+
driver,
|
|
2060
|
+
tableMapping,
|
|
2061
|
+
});
|
|
1711
2062
|
if (!composed) {
|
|
1712
2063
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1713
2064
|
res.end(serializeJSON({ error: 'Could not compose semantic block preview SQL.' }));
|
|
@@ -1720,12 +2071,20 @@ export async function startLocalServer(opts) {
|
|
|
1720
2071
|
sql: composed.sql,
|
|
1721
2072
|
joins: composed.joins,
|
|
1722
2073
|
tables: composed.tables,
|
|
2074
|
+
engine: composed.engine,
|
|
1723
2075
|
result: normalizeQueryResult(result),
|
|
1724
2076
|
}));
|
|
1725
2077
|
}
|
|
1726
2078
|
catch (error) {
|
|
1727
|
-
|
|
1728
|
-
res.
|
|
2079
|
+
const status = error instanceof MetricFlowUnavailableError ? 400 : 500;
|
|
2080
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
2081
|
+
res.end(serializeJSON({
|
|
2082
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2083
|
+
code: error instanceof MetricFlowUnavailableError ? 'metricflow_unavailable' : undefined,
|
|
2084
|
+
hint: error instanceof MetricFlowUnavailableError
|
|
2085
|
+
? 'Install dbt Semantic Layer dependencies, run dbt parse/build to create target/semantic_manifest.json, then retry.'
|
|
2086
|
+
: undefined,
|
|
2087
|
+
}));
|
|
1729
2088
|
}
|
|
1730
2089
|
return;
|
|
1731
2090
|
}
|
|
@@ -1737,18 +2096,23 @@ export async function startLocalServer(opts) {
|
|
|
1737
2096
|
return;
|
|
1738
2097
|
}
|
|
1739
2098
|
const body = await readJSON(req);
|
|
1740
|
-
const { name, domain, description, owner, tags, metrics = [], dimensions = [], timeDimension, filters = [], chart = 'table', blockType = 'semantic', } = body;
|
|
2099
|
+
const { name, domain, description, owner, tags, metrics = [], dimensions = [], timeDimension, filters = [], chart = 'table', blockType = 'semantic', engine, } = body;
|
|
1741
2100
|
if (!name || metrics.length === 0) {
|
|
1742
2101
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1743
2102
|
res.end(serializeJSON({ error: 'name and at least one metric are required.' }));
|
|
1744
2103
|
return;
|
|
1745
2104
|
}
|
|
1746
2105
|
const targetConnection = isConnectionConfig(body.connection) ? body.connection : connection;
|
|
1747
|
-
const composed =
|
|
2106
|
+
const composed = composeRuntimeSemanticQuery({
|
|
1748
2107
|
metrics,
|
|
1749
2108
|
dimensions,
|
|
1750
2109
|
filters,
|
|
1751
2110
|
timeDimension,
|
|
2111
|
+
engine,
|
|
2112
|
+
}, semanticLayer, {
|
|
2113
|
+
projectRoot,
|
|
2114
|
+
projectConfig,
|
|
2115
|
+
detectedProvider: semanticDetectedProvider,
|
|
1752
2116
|
driver: targetConnection.driver,
|
|
1753
2117
|
});
|
|
1754
2118
|
if (!composed) {
|
|
@@ -1780,8 +2144,15 @@ export async function startLocalServer(opts) {
|
|
|
1780
2144
|
res.end(serializeJSON({ error: 'Block already exists' }));
|
|
1781
2145
|
return;
|
|
1782
2146
|
}
|
|
1783
|
-
|
|
1784
|
-
res.
|
|
2147
|
+
const status = error instanceof MetricFlowUnavailableError ? 400 : 500;
|
|
2148
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
2149
|
+
res.end(serializeJSON({
|
|
2150
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2151
|
+
code: error instanceof MetricFlowUnavailableError ? 'metricflow_unavailable' : undefined,
|
|
2152
|
+
hint: error instanceof MetricFlowUnavailableError
|
|
2153
|
+
? 'Install dbt Semantic Layer dependencies, run dbt parse/build to create target/semantic_manifest.json, then retry.'
|
|
2154
|
+
: undefined,
|
|
2155
|
+
}));
|
|
1785
2156
|
}
|
|
1786
2157
|
return;
|
|
1787
2158
|
}
|
|
@@ -2041,7 +2412,9 @@ export async function startLocalServer(opts) {
|
|
|
2041
2412
|
return;
|
|
2042
2413
|
}
|
|
2043
2414
|
const prepared = prepareLocalExecution(plan.sql, isConnectionConfig(body.connection) ? body.connection : connection, projectRoot, projectConfig);
|
|
2044
|
-
const
|
|
2415
|
+
const app = loadRuntimeApp(projectRoot, typeof body.appId === 'string' ? body.appId : activePersonaAppId());
|
|
2416
|
+
assertAppAccess({ app, domain: app?.domain, level: 'execute' });
|
|
2417
|
+
const rawResult = await executor.executeQuery(prepared.sql, plan.sqlParams, runtimeVariables(plan.variables), prepared.connection);
|
|
2045
2418
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
2046
2419
|
res.end(serializeJSON({
|
|
2047
2420
|
cellType: cell.type,
|
|
@@ -2171,6 +2544,34 @@ function normalizeQueryResult(result, semanticRefs) {
|
|
|
2171
2544
|
...(hasRefs ? { semanticRefs } : {}),
|
|
2172
2545
|
};
|
|
2173
2546
|
}
|
|
2547
|
+
function isLLMProviderId(value) {
|
|
2548
|
+
return value === 'claude-agent-sdk'
|
|
2549
|
+
|| value === 'claude-code'
|
|
2550
|
+
|| value === 'openai'
|
|
2551
|
+
|| value === 'gemini'
|
|
2552
|
+
|| value === 'ollama';
|
|
2553
|
+
}
|
|
2554
|
+
function loadAppDashboard(projectRoot, appId, dashboardId) {
|
|
2555
|
+
for (const p of findAppDocuments(projectRoot)) {
|
|
2556
|
+
const { document: app } = loadAppDocument(p);
|
|
2557
|
+
if (!app || app.id !== appId)
|
|
2558
|
+
continue;
|
|
2559
|
+
const appDir = p.slice(0, -'/dql.app.json'.length);
|
|
2560
|
+
for (const d of findDashboardsForApp(appDir)) {
|
|
2561
|
+
const { document: dashboard } = loadDashboardDocument(d);
|
|
2562
|
+
if (dashboard?.id === dashboardId)
|
|
2563
|
+
return { app, dashboard };
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
return null;
|
|
2567
|
+
}
|
|
2568
|
+
function resolveDashboardItemBlock(item, manifest) {
|
|
2569
|
+
if (isBlockIdRef(item.block)) {
|
|
2570
|
+
return manifest.blocks[item.block.blockId] ?? null;
|
|
2571
|
+
}
|
|
2572
|
+
const normalizedRef = normalize(item.block.ref).replaceAll('\\', '/');
|
|
2573
|
+
return Object.values(manifest.blocks).find((b) => normalize(b.filePath).replaceAll('\\', '/') === normalizedRef) ?? null;
|
|
2574
|
+
}
|
|
2174
2575
|
export function serializeJSON(value) {
|
|
2175
2576
|
return JSON.stringify(value, (_key, current) => {
|
|
2176
2577
|
if (typeof current === 'bigint') {
|
|
@@ -2679,6 +3080,46 @@ function buildSemanticTableMapping(semanticLayer, rows) {
|
|
|
2679
3080
|
}
|
|
2680
3081
|
return Object.keys(tableMapping).length > 0 ? tableMapping : undefined;
|
|
2681
3082
|
}
|
|
3083
|
+
function isDbtSemanticRuntime(projectConfig, detectedProvider, semanticLayer) {
|
|
3084
|
+
if (projectConfig.semanticLayer?.provider === 'dbt' || detectedProvider === 'dbt')
|
|
3085
|
+
return true;
|
|
3086
|
+
return Boolean(semanticLayer?.listMetrics().some((metric) => metric.source?.provider === 'dbt'));
|
|
3087
|
+
}
|
|
3088
|
+
function composeRuntimeSemanticQuery(request, semanticLayer, context) {
|
|
3089
|
+
const useMetricFlow = request.engine === 'metricflow' || (request.engine !== 'native' &&
|
|
3090
|
+
isDbtSemanticRuntime(context.projectConfig, context.detectedProvider, semanticLayer));
|
|
3091
|
+
if (useMetricFlow) {
|
|
3092
|
+
const dbtProjectPath = context.projectConfig.semanticLayer?.projectPath;
|
|
3093
|
+
const compiled = compileMetricFlowQuery({
|
|
3094
|
+
projectRoot: context.projectRoot,
|
|
3095
|
+
dbtProjectPath,
|
|
3096
|
+
metrics: request.metrics,
|
|
3097
|
+
dimensions: request.dimensions,
|
|
3098
|
+
filters: request.filters,
|
|
3099
|
+
timeDimension: request.timeDimension,
|
|
3100
|
+
orderBy: request.orderBy,
|
|
3101
|
+
limit: request.limit,
|
|
3102
|
+
savedQuery: request.savedQuery,
|
|
3103
|
+
});
|
|
3104
|
+
return {
|
|
3105
|
+
sql: compiled.sql,
|
|
3106
|
+
joins: [],
|
|
3107
|
+
tables: [],
|
|
3108
|
+
engine: 'metricflow',
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
const composed = semanticLayer.composeQuery({
|
|
3112
|
+
metrics: request.metrics,
|
|
3113
|
+
dimensions: request.dimensions,
|
|
3114
|
+
filters: request.filters,
|
|
3115
|
+
limit: request.limit,
|
|
3116
|
+
timeDimension: request.timeDimension,
|
|
3117
|
+
orderBy: request.orderBy,
|
|
3118
|
+
driver: context.driver,
|
|
3119
|
+
tableMapping: context.tableMapping,
|
|
3120
|
+
});
|
|
3121
|
+
return composed ? { ...composed, engine: 'native' } : null;
|
|
3122
|
+
}
|
|
2682
3123
|
function composeSemanticBlockSql(source, semanticLayer, options) {
|
|
2683
3124
|
const config = parseSemanticBlockConfig(source);
|
|
2684
3125
|
const metrics = config.metrics.length > 0
|
|
@@ -2721,16 +3162,42 @@ function composeSemanticBlockSql(source, semanticLayer, options) {
|
|
|
2721
3162
|
if (diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
|
|
2722
3163
|
return { sql: null, diagnostics, semanticRefs };
|
|
2723
3164
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
3165
|
+
let composed;
|
|
3166
|
+
try {
|
|
3167
|
+
composed = options?.projectRoot && options.projectConfig
|
|
3168
|
+
? composeRuntimeSemanticQuery({
|
|
3169
|
+
metrics,
|
|
3170
|
+
dimensions: config.dimensions,
|
|
3171
|
+
timeDimension: config.timeDimension && config.granularity
|
|
3172
|
+
? { name: config.timeDimension, granularity: config.granularity }
|
|
3173
|
+
: undefined,
|
|
3174
|
+
limit: config.limit,
|
|
3175
|
+
}, semanticLayer, {
|
|
3176
|
+
projectRoot: options.projectRoot,
|
|
3177
|
+
projectConfig: options.projectConfig,
|
|
3178
|
+
detectedProvider: options.detectedProvider ?? null,
|
|
3179
|
+
driver: options.driver,
|
|
3180
|
+
tableMapping: options.tableMapping,
|
|
3181
|
+
})
|
|
3182
|
+
: semanticLayer.composeQuery({
|
|
3183
|
+
metrics,
|
|
3184
|
+
dimensions: config.dimensions,
|
|
3185
|
+
timeDimension: config.timeDimension && config.granularity
|
|
3186
|
+
? { name: config.timeDimension, granularity: config.granularity }
|
|
3187
|
+
: undefined,
|
|
3188
|
+
limit: config.limit,
|
|
3189
|
+
driver: options?.driver,
|
|
3190
|
+
tableMapping: options?.tableMapping,
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
catch (error) {
|
|
3194
|
+
diagnostics.push({
|
|
3195
|
+
severity: 'error',
|
|
3196
|
+
code: error instanceof MetricFlowUnavailableError ? 'metricflow_unavailable' : 'semantic_compose_failed',
|
|
3197
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3198
|
+
});
|
|
3199
|
+
return { sql: null, diagnostics, semanticRefs };
|
|
3200
|
+
}
|
|
2734
3201
|
if (!composed) {
|
|
2735
3202
|
diagnostics.push({
|
|
2736
3203
|
severity: 'error',
|
|
@@ -3833,4 +4300,58 @@ function computeSemanticDiff(filePath, before, after) {
|
|
|
3833
4300
|
return null;
|
|
3834
4301
|
}
|
|
3835
4302
|
}
|
|
4303
|
+
function collectSettingsEnvStatus() {
|
|
4304
|
+
const v = (key, label, description, optional = true) => ({
|
|
4305
|
+
key,
|
|
4306
|
+
label,
|
|
4307
|
+
description,
|
|
4308
|
+
optional,
|
|
4309
|
+
present: typeof process.env[key] === 'string' && process.env[key].trim().length > 0,
|
|
4310
|
+
});
|
|
4311
|
+
return [
|
|
4312
|
+
{
|
|
4313
|
+
id: 'ai',
|
|
4314
|
+
title: 'AI Chat Providers',
|
|
4315
|
+
description: 'Configure one or more providers. Missing keys are only a problem when that provider is selected.',
|
|
4316
|
+
vars: [
|
|
4317
|
+
v('ANTHROPIC_API_KEY', 'Claude Agent SDK', 'Hosted Claude provider for notebook Chat and agent commands.'),
|
|
4318
|
+
v('OPENAI_API_KEY', 'OpenAI', 'Hosted OpenAI provider for notebook Chat and agent commands.'),
|
|
4319
|
+
v('OPENAI_MODEL', 'OpenAI model', 'Optional override such as gpt-4.1-mini or the model your account uses.'),
|
|
4320
|
+
v('GEMINI_API_KEY', 'Gemini', 'Hosted Gemini provider for notebook Chat and agent commands.'),
|
|
4321
|
+
v('GEMINI_MODEL', 'Gemini model', 'Optional Gemini model override.'),
|
|
4322
|
+
v('OLLAMA_BASE_URL', 'Ollama base URL', 'Local Ollama HTTP endpoint. Docker defaults to http://ollama:11434.'),
|
|
4323
|
+
v('OLLAMA_MODEL', 'Ollama model', 'Optional local model name such as llama3.1.'),
|
|
4324
|
+
],
|
|
4325
|
+
},
|
|
4326
|
+
{
|
|
4327
|
+
id: 'slack',
|
|
4328
|
+
title: 'Slack',
|
|
4329
|
+
description: 'Use webhooks for scheduled App deliveries, or bot credentials for the Slack chat front-end.',
|
|
4330
|
+
vars: [
|
|
4331
|
+
v('DQL_SLACK_WEBHOOK', 'Schedule webhook', 'Incoming webhook used by App schedules that deliver to Slack.'),
|
|
4332
|
+
v('SLACK_SIGNING_SECRET', 'Slack signing secret', 'Required only when running `dql slack serve`.'),
|
|
4333
|
+
v('SLACK_BOT_TOKEN', 'Slack bot token', 'Bot token used by Slack chat commands when `dql slack serve` is enabled.'),
|
|
4334
|
+
],
|
|
4335
|
+
},
|
|
4336
|
+
{
|
|
4337
|
+
id: 'email',
|
|
4338
|
+
title: 'Email',
|
|
4339
|
+
description: 'SMTP is optional. Without it, email schedules stay in stub mode with a clear delivery message.',
|
|
4340
|
+
vars: [
|
|
4341
|
+
v('DQL_SMTP_URL', 'SMTP URL', 'SMTP connection URL for email schedule delivery.'),
|
|
4342
|
+
v('DQL_SMTP_FROM', 'SMTP sender', 'Optional sender address for scheduled emails.'),
|
|
4343
|
+
],
|
|
4344
|
+
},
|
|
4345
|
+
{
|
|
4346
|
+
id: 'runtime',
|
|
4347
|
+
title: 'Runtime',
|
|
4348
|
+
description: 'Local server and runtime toggles used by Docker and native notebook sessions.',
|
|
4349
|
+
vars: [
|
|
4350
|
+
v('DQL_HOST', 'Notebook bind host', 'Host interface for the local notebook server. Docker uses 0.0.0.0 inside the container.'),
|
|
4351
|
+
v('DQL_RUNTIME_URL', 'Runtime URL', 'Optional URL used by headless agent commands to call an existing runtime.'),
|
|
4352
|
+
v('DQL_LLM_KEY', 'Legacy LLM key', 'Fallback key accepted by older Claude provider configurations.'),
|
|
4353
|
+
],
|
|
4354
|
+
},
|
|
4355
|
+
];
|
|
4356
|
+
}
|
|
3836
4357
|
//# sourceMappingURL=local-runtime.js.map
|