@duckcodeailabs/dql-cli 1.4.3 → 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.
Files changed (260) hide show
  1. package/apps-api.d.ts +77 -0
  2. package/apps-api.d.ts.map +1 -0
  3. package/apps-api.js +612 -0
  4. package/apps-api.js.map +1 -0
  5. package/apps-api.test.d.ts +2 -0
  6. package/apps-api.test.d.ts.map +1 -0
  7. package/apps-api.test.js +111 -0
  8. package/apps-api.test.js.map +1 -0
  9. package/args.d.ts +30 -0
  10. package/args.d.ts.map +1 -0
  11. package/args.js +105 -0
  12. package/args.js.map +1 -0
  13. package/args.test.d.ts +2 -0
  14. package/args.test.d.ts.map +1 -0
  15. package/args.test.js +33 -0
  16. package/args.test.js.map +1 -0
  17. package/assets/dql-notebook/assets/codemirror-DJYUkPr1.js +11 -0
  18. package/assets/dql-notebook/assets/index-DUTeFz5j.js +858 -0
  19. package/assets/dql-notebook/assets/index-DrhoZmtv.css +1 -0
  20. package/assets/dql-notebook/assets/react-CRB3T2We.js +32 -0
  21. package/assets/dql-notebook/index.html +18 -0
  22. package/assets/notebook-browser/app.js +548 -0
  23. package/assets/notebook-browser/index.html +83 -0
  24. package/assets/notebook-browser/styles.css +336 -0
  25. package/block-templates.d.ts +8 -0
  26. package/block-templates.d.ts.map +1 -0
  27. package/block-templates.js +60 -0
  28. package/block-templates.js.map +1 -0
  29. package/commands/agent.d.ts +19 -0
  30. package/commands/agent.d.ts.map +1 -0
  31. package/commands/agent.js +165 -0
  32. package/commands/agent.js.map +1 -0
  33. package/commands/app.d.ts +32 -0
  34. package/commands/app.d.ts.map +1 -0
  35. package/commands/app.js +307 -0
  36. package/commands/app.js.map +1 -0
  37. package/commands/build.d.ts +3 -0
  38. package/commands/build.d.ts.map +1 -0
  39. package/commands/build.js +69 -0
  40. package/commands/build.js.map +1 -0
  41. package/commands/build.test.d.ts +2 -0
  42. package/commands/build.test.d.ts.map +1 -0
  43. package/commands/build.test.js +44 -0
  44. package/commands/build.test.js.map +1 -0
  45. package/commands/certify.d.ts +3 -0
  46. package/commands/certify.d.ts.map +1 -0
  47. package/commands/certify.js +228 -0
  48. package/commands/certify.js.map +1 -0
  49. package/commands/compile.d.ts +21 -0
  50. package/commands/compile.d.ts.map +1 -0
  51. package/commands/compile.js +198 -0
  52. package/commands/compile.js.map +1 -0
  53. package/commands/compile.test.d.ts +2 -0
  54. package/commands/compile.test.d.ts.map +1 -0
  55. package/commands/compile.test.js +115 -0
  56. package/commands/compile.test.js.map +1 -0
  57. package/commands/diff.d.ts +3 -0
  58. package/commands/diff.d.ts.map +1 -0
  59. package/commands/diff.js +52 -0
  60. package/commands/diff.js.map +1 -0
  61. package/commands/doctor.d.ts +3 -0
  62. package/commands/doctor.d.ts.map +1 -0
  63. package/commands/doctor.js +191 -0
  64. package/commands/doctor.js.map +1 -0
  65. package/commands/doctor.test.d.ts +2 -0
  66. package/commands/doctor.test.d.ts.map +1 -0
  67. package/commands/doctor.test.js +43 -0
  68. package/commands/doctor.test.js.map +1 -0
  69. package/commands/fmt.d.ts +3 -0
  70. package/commands/fmt.d.ts.map +1 -0
  71. package/commands/fmt.js +53 -0
  72. package/commands/fmt.js.map +1 -0
  73. package/commands/info.d.ts +3 -0
  74. package/commands/info.d.ts.map +1 -0
  75. package/commands/info.js +56 -0
  76. package/commands/info.js.map +1 -0
  77. package/commands/init.d.ts +3 -0
  78. package/commands/init.d.ts.map +1 -0
  79. package/commands/init.js +250 -0
  80. package/commands/init.js.map +1 -0
  81. package/commands/init.test.d.ts +2 -0
  82. package/commands/init.test.d.ts.map +1 -0
  83. package/commands/init.test.js +118 -0
  84. package/commands/init.test.js.map +1 -0
  85. package/commands/lineage.d.ts +24 -0
  86. package/commands/lineage.d.ts.map +1 -0
  87. package/commands/lineage.js +634 -0
  88. package/commands/lineage.js.map +1 -0
  89. package/commands/mcp.d.ts +7 -0
  90. package/commands/mcp.d.ts.map +1 -0
  91. package/commands/mcp.js +16 -0
  92. package/commands/mcp.js.map +1 -0
  93. package/commands/migrate.d.ts +12 -0
  94. package/commands/migrate.d.ts.map +1 -0
  95. package/commands/migrate.js +192 -0
  96. package/commands/migrate.js.map +1 -0
  97. package/commands/new.d.ts +3 -0
  98. package/commands/new.d.ts.map +1 -0
  99. package/commands/new.js +490 -0
  100. package/commands/new.js.map +1 -0
  101. package/commands/new.test.d.ts +2 -0
  102. package/commands/new.test.d.ts.map +1 -0
  103. package/commands/new.test.js +191 -0
  104. package/commands/new.test.js.map +1 -0
  105. package/commands/notebook.d.ts +3 -0
  106. package/commands/notebook.d.ts.map +1 -0
  107. package/commands/notebook.js +46 -0
  108. package/commands/notebook.js.map +1 -0
  109. package/commands/parse.d.ts +3 -0
  110. package/commands/parse.d.ts.map +1 -0
  111. package/commands/parse.js +63 -0
  112. package/commands/parse.js.map +1 -0
  113. package/commands/preview.d.ts +3 -0
  114. package/commands/preview.d.ts.map +1 -0
  115. package/commands/preview.js +42 -0
  116. package/commands/preview.js.map +1 -0
  117. package/commands/schedule.d.ts +3 -0
  118. package/commands/schedule.d.ts.map +1 -0
  119. package/commands/schedule.js +215 -0
  120. package/commands/schedule.js.map +1 -0
  121. package/commands/semantic.d.ts +12 -0
  122. package/commands/semantic.d.ts.map +1 -0
  123. package/commands/semantic.js +356 -0
  124. package/commands/semantic.js.map +1 -0
  125. package/commands/serve.d.ts +3 -0
  126. package/commands/serve.d.ts.map +1 -0
  127. package/commands/serve.js +30 -0
  128. package/commands/serve.js.map +1 -0
  129. package/commands/slack.d.ts +13 -0
  130. package/commands/slack.d.ts.map +1 -0
  131. package/commands/slack.js +53 -0
  132. package/commands/slack.js.map +1 -0
  133. package/commands/sync.d.ts +3 -0
  134. package/commands/sync.d.ts.map +1 -0
  135. package/commands/sync.js +192 -0
  136. package/commands/sync.js.map +1 -0
  137. package/commands/sync.test.d.ts +2 -0
  138. package/commands/sync.test.d.ts.map +1 -0
  139. package/commands/sync.test.js +147 -0
  140. package/commands/sync.test.js.map +1 -0
  141. package/commands/test.d.ts +3 -0
  142. package/commands/test.d.ts.map +1 -0
  143. package/commands/test.js +167 -0
  144. package/commands/test.js.map +1 -0
  145. package/commands/validate.d.ts +3 -0
  146. package/commands/validate.d.ts.map +1 -0
  147. package/commands/validate.js +116 -0
  148. package/commands/validate.js.map +1 -0
  149. package/commands/verify.d.ts +11 -0
  150. package/commands/verify.d.ts.map +1 -0
  151. package/commands/verify.js +74 -0
  152. package/commands/verify.js.map +1 -0
  153. package/digest.d.ts +10 -0
  154. package/digest.d.ts.map +1 -0
  155. package/digest.js +83 -0
  156. package/digest.js.map +1 -0
  157. package/git-service.d.ts +17 -0
  158. package/git-service.d.ts.map +1 -0
  159. package/git-service.js +54 -0
  160. package/git-service.js.map +1 -0
  161. package/governance-runtime.d.ts +15 -0
  162. package/governance-runtime.d.ts.map +1 -0
  163. package/governance-runtime.js +50 -0
  164. package/governance-runtime.js.map +1 -0
  165. package/index.d.ts +3 -0
  166. package/index.d.ts.map +1 -0
  167. package/index.js.map +1 -0
  168. package/llm/index.d.ts +4 -0
  169. package/llm/index.d.ts.map +1 -0
  170. package/llm/index.js +19 -0
  171. package/llm/index.js.map +1 -0
  172. package/llm/providers/claude-agent-sdk.d.ts +3 -0
  173. package/llm/providers/claude-agent-sdk.d.ts.map +1 -0
  174. package/llm/providers/claude-agent-sdk.js +174 -0
  175. package/llm/providers/claude-agent-sdk.js.map +1 -0
  176. package/llm/providers/claude-code.d.ts +8 -0
  177. package/llm/providers/claude-code.d.ts.map +1 -0
  178. package/llm/providers/claude-code.js +171 -0
  179. package/llm/providers/claude-code.js.map +1 -0
  180. package/llm/providers/dql-agent-provider.d.ts +5 -0
  181. package/llm/providers/dql-agent-provider.d.ts.map +1 -0
  182. package/llm/providers/dql-agent-provider.js +99 -0
  183. package/llm/providers/dql-agent-provider.js.map +1 -0
  184. package/llm/tools.d.ts +9 -0
  185. package/llm/tools.d.ts.map +1 -0
  186. package/llm/tools.js +112 -0
  187. package/llm/tools.js.map +1 -0
  188. package/llm/types.d.ts +70 -0
  189. package/llm/types.d.ts.map +1 -0
  190. package/llm/types.js +2 -0
  191. package/llm/types.js.map +1 -0
  192. package/local-runtime.d.ts +142 -0
  193. package/local-runtime.d.ts.map +1 -0
  194. package/local-runtime.js +4357 -0
  195. package/local-runtime.js.map +1 -0
  196. package/local-runtime.test.d.ts +2 -0
  197. package/local-runtime.test.d.ts.map +1 -0
  198. package/local-runtime.test.js +241 -0
  199. package/local-runtime.test.js.map +1 -0
  200. package/metricflow.d.ts +35 -0
  201. package/metricflow.d.ts.map +1 -0
  202. package/metricflow.js +122 -0
  203. package/metricflow.js.map +1 -0
  204. package/metricflow.test.d.ts +2 -0
  205. package/metricflow.test.d.ts.map +1 -0
  206. package/metricflow.test.js +54 -0
  207. package/metricflow.test.js.map +1 -0
  208. package/open-browser.d.ts +2 -0
  209. package/open-browser.d.ts.map +1 -0
  210. package/open-browser.js +29 -0
  211. package/open-browser.js.map +1 -0
  212. package/package.json +10 -13
  213. package/schedule/alerts.d.ts +5 -0
  214. package/schedule/alerts.d.ts.map +1 -0
  215. package/schedule/alerts.js +54 -0
  216. package/schedule/alerts.js.map +1 -0
  217. package/schedule/discovery.d.ts +4 -0
  218. package/schedule/discovery.d.ts.map +1 -0
  219. package/schedule/discovery.js +36 -0
  220. package/schedule/discovery.js.map +1 -0
  221. package/schedule/notifiers/email.d.ts +3 -0
  222. package/schedule/notifiers/email.d.ts.map +1 -0
  223. package/schedule/notifiers/email.js +76 -0
  224. package/schedule/notifiers/email.js.map +1 -0
  225. package/schedule/notifiers/file.d.ts +3 -0
  226. package/schedule/notifiers/file.d.ts.map +1 -0
  227. package/schedule/notifiers/file.js +50 -0
  228. package/schedule/notifiers/file.js.map +1 -0
  229. package/schedule/notifiers/index.d.ts +10 -0
  230. package/schedule/notifiers/index.d.ts.map +1 -0
  231. package/schedule/notifiers/index.js +33 -0
  232. package/schedule/notifiers/index.js.map +1 -0
  233. package/schedule/notifiers/slack.d.ts +3 -0
  234. package/schedule/notifiers/slack.d.ts.map +1 -0
  235. package/schedule/notifiers/slack.js +58 -0
  236. package/schedule/notifiers/slack.js.map +1 -0
  237. package/schedule/runner.d.ts +14 -0
  238. package/schedule/runner.d.ts.map +1 -0
  239. package/schedule/runner.js +221 -0
  240. package/schedule/runner.js.map +1 -0
  241. package/schedule/runs.d.ts +5 -0
  242. package/schedule/runs.d.ts.map +1 -0
  243. package/schedule/runs.js +41 -0
  244. package/schedule/runs.js.map +1 -0
  245. package/schedule/service.d.ts +13 -0
  246. package/schedule/service.d.ts.map +1 -0
  247. package/schedule/service.js +87 -0
  248. package/schedule/service.js.map +1 -0
  249. package/schedule/types.d.ts +70 -0
  250. package/schedule/types.d.ts.map +1 -0
  251. package/schedule/types.js +2 -0
  252. package/schedule/types.js.map +1 -0
  253. package/semantic-import.d.ts +135 -0
  254. package/semantic-import.d.ts.map +1 -0
  255. package/semantic-import.js +979 -0
  256. package/semantic-import.js.map +1 -0
  257. package/semantic-import.test.d.ts +2 -0
  258. package/semantic-import.test.d.ts.map +1 -0
  259. package/semantic-import.test.js +95 -0
  260. package/semantic-import.test.js.map +1 -0
package/apps-api.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * HTTP handlers for `/api/apps`, `/api/apps/:id`, `/api/apps/:id/dashboards/:did`,
3
+ * `/api/persona`. Designed to be invoked from `local-runtime.ts`'s request
4
+ * dispatcher — returns `true` if the request was handled, `false` otherwise.
5
+ */
6
+ import type { IncomingMessage, ServerResponse } from 'node:http';
7
+ import { type AppDocument } from '@duckcodeailabs/dql-core';
8
+ interface Ctx {
9
+ req: IncomingMessage;
10
+ res: ServerResponse;
11
+ url: URL;
12
+ path: string;
13
+ projectRoot: string;
14
+ }
15
+ export declare function handleAppsApi(ctx: Ctx): Promise<boolean>;
16
+ declare function collectAppsList(projectRoot: string): Array<{
17
+ id: string;
18
+ name: string;
19
+ domain: string;
20
+ description?: string;
21
+ audience?: string;
22
+ status?: 'ready' | 'empty';
23
+ owners: string[];
24
+ tags: string[];
25
+ members: number;
26
+ roles: number;
27
+ policies: number;
28
+ schedules: number;
29
+ dashboards: Array<{
30
+ id: string;
31
+ title: string;
32
+ }>;
33
+ homepage?: AppDocument['homepage'];
34
+ }>;
35
+ interface AppRecommendationRequest {
36
+ domain?: string;
37
+ tags?: string[];
38
+ purpose?: string;
39
+ audience?: string;
40
+ certifiedOnly?: boolean;
41
+ }
42
+ interface AppCreateRequest {
43
+ name?: string;
44
+ domain?: string;
45
+ purpose?: string;
46
+ audience?: string;
47
+ tags?: string[];
48
+ owners?: string[];
49
+ selectedBlockIds?: string[];
50
+ }
51
+ interface BlockCandidate {
52
+ id: string;
53
+ name: string;
54
+ domain: string;
55
+ status: string;
56
+ owner: string | null;
57
+ tags: string[];
58
+ path: string;
59
+ lastModified: string;
60
+ description: string;
61
+ llmContext: string | null;
62
+ chartType?: string;
63
+ score: number;
64
+ reasons: string[];
65
+ }
66
+ export declare function recommendBlocks(projectRoot: string, input: AppRecommendationRequest): BlockCandidate[];
67
+ export declare function createAppPackage(projectRoot: string, input: AppCreateRequest): {
68
+ ok: true;
69
+ app: ReturnType<typeof collectAppsList>[number];
70
+ paths: string[];
71
+ dashboardId: string;
72
+ } | {
73
+ ok: false;
74
+ error: string;
75
+ };
76
+ export {};
77
+ //# sourceMappingURL=apps-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apps-api.d.ts","sourceRoot":"","sources":["../src/apps-api.ts"],"names":[],"mappings":"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"}
package/apps-api.js ADDED
@@ -0,0 +1,612 @@
1
+ /**
2
+ * HTTP handlers for `/api/apps`, `/api/apps/:id`, `/api/apps/:id/dashboards/:did`,
3
+ * `/api/persona`. Designed to be invoked from `local-runtime.ts`'s request
4
+ * dispatcher — returns `true` if the request was handled, `false` otherwise.
5
+ */
6
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
7
+ import { join, dirname } from 'node:path';
8
+ import { loadAppDocument, findAppDocuments, loadDashboardDocument, findDashboardsForApp, parseAppDocument, parseDashboardDocument, suggestAppId, } from '@duckcodeailabs/dql-core';
9
+ import { defaultPersonaRegistry, personaFromMember, } from '@duckcodeailabs/dql-project';
10
+ export async function handleAppsApi(ctx) {
11
+ const { req, res, path, projectRoot } = ctx;
12
+ // ── Apps ────────────────────────────────────────────────────────────────
13
+ if (req.method === 'GET' && path === '/api/apps') {
14
+ const apps = collectAppsList(projectRoot);
15
+ sendJson(res, 200, { apps });
16
+ return true;
17
+ }
18
+ if (req.method === 'POST' && path === '/api/apps/recommend-blocks') {
19
+ try {
20
+ const body = await readJson(req);
21
+ sendJson(res, 200, { blocks: recommendBlocks(projectRoot, body) });
22
+ }
23
+ catch (err) {
24
+ sendJson(res, 500, { error: err.message });
25
+ }
26
+ return true;
27
+ }
28
+ if (req.method === 'POST' && path === '/api/apps') {
29
+ try {
30
+ const body = await readJson(req);
31
+ const result = createAppPackage(projectRoot, body);
32
+ if (!result.ok) {
33
+ sendJson(res, 400, { error: result.error });
34
+ return true;
35
+ }
36
+ sendJson(res, 201, result);
37
+ }
38
+ catch (err) {
39
+ sendJson(res, 500, { error: err.message });
40
+ }
41
+ return true;
42
+ }
43
+ // /api/apps/:id — single App with dashboards summary
44
+ let m = path.match(/^\/api\/apps\/([^/]+)$/);
45
+ if (m && req.method === 'GET') {
46
+ const id = m[1];
47
+ const result = loadAppById(projectRoot, id);
48
+ if (!result) {
49
+ sendJson(res, 404, { error: `App "${id}" not found` });
50
+ return true;
51
+ }
52
+ sendJson(res, 200, result);
53
+ return true;
54
+ }
55
+ // /api/apps/:id/dashboards
56
+ m = path.match(/^\/api\/apps\/([^/]+)\/dashboards$/);
57
+ if (m && req.method === 'GET') {
58
+ const id = m[1];
59
+ const dashboards = listDashboardsFor(projectRoot, id);
60
+ if (dashboards === null) {
61
+ sendJson(res, 404, { error: `App "${id}" not found` });
62
+ return true;
63
+ }
64
+ sendJson(res, 200, { dashboards });
65
+ return true;
66
+ }
67
+ // /api/apps/:id/dashboards/:did
68
+ m = path.match(/^\/api\/apps\/([^/]+)\/dashboards\/([^/]+)$/);
69
+ if (m) {
70
+ const id = m[1];
71
+ const did = m[2];
72
+ if (req.method === 'GET') {
73
+ const result = loadDashboardForApp(projectRoot, id, did);
74
+ if (!result) {
75
+ sendJson(res, 404, { error: `Dashboard "${did}" not found in app "${id}"` });
76
+ return true;
77
+ }
78
+ sendJson(res, 200, result);
79
+ return true;
80
+ }
81
+ if (req.method === 'PUT' || req.method === 'POST') {
82
+ try {
83
+ const body = await readJson(req);
84
+ const written = await writeDashboard(projectRoot, id, did, body);
85
+ if (!written.ok) {
86
+ sendJson(res, 400, { error: written.error });
87
+ return true;
88
+ }
89
+ sendJson(res, 200, { ok: true, path: written.path });
90
+ }
91
+ catch (err) {
92
+ sendJson(res, 500, { error: err.message });
93
+ }
94
+ return true;
95
+ }
96
+ }
97
+ // ── Persona ────────────────────────────────────────────────────────────
98
+ if (path === '/api/persona') {
99
+ if (req.method === 'GET') {
100
+ sendJson(res, 200, { persona: defaultPersonaRegistry.active });
101
+ return true;
102
+ }
103
+ if (req.method === 'DELETE') {
104
+ defaultPersonaRegistry.clear();
105
+ sendJson(res, 200, { persona: null });
106
+ return true;
107
+ }
108
+ if (req.method === 'POST') {
109
+ try {
110
+ const body = await readJson(req);
111
+ const userId = typeof body.userId === 'string' ? body.userId : null;
112
+ const appId = typeof body.appId === 'string' ? body.appId : null;
113
+ if (!userId) {
114
+ sendJson(res, 400, { error: 'userId is required' });
115
+ return true;
116
+ }
117
+ const persona = activatePersona(projectRoot, userId, appId);
118
+ if (!persona) {
119
+ sendJson(res, 404, { error: 'No App member matches this userId' });
120
+ return true;
121
+ }
122
+ sendJson(res, 200, { persona });
123
+ }
124
+ catch (err) {
125
+ sendJson(res, 500, { error: err.message });
126
+ }
127
+ return true;
128
+ }
129
+ }
130
+ return false;
131
+ }
132
+ // ---- Helpers ----
133
+ function collectAppsList(projectRoot) {
134
+ const out = [];
135
+ for (const p of findAppDocuments(projectRoot)) {
136
+ const { document } = loadAppDocument(p);
137
+ if (!document)
138
+ continue;
139
+ const appDir = p.slice(0, -'/dql.app.json'.length);
140
+ const dashboards = [];
141
+ for (const d of findDashboardsForApp(appDir)) {
142
+ const { document: dd } = loadDashboardDocument(d);
143
+ if (dd)
144
+ dashboards.push({ id: dd.id, title: dd.metadata.title });
145
+ }
146
+ out.push({
147
+ id: document.id,
148
+ name: document.name,
149
+ domain: document.domain,
150
+ description: document.description,
151
+ audience: audienceFromTags(document.tags ?? []),
152
+ status: dashboards.length > 0 ? 'ready' : 'empty',
153
+ owners: document.owners,
154
+ tags: document.tags ?? [],
155
+ members: document.members.length,
156
+ roles: document.roles.length,
157
+ policies: document.policies.length,
158
+ schedules: (document.schedules ?? []).length,
159
+ dashboards,
160
+ homepage: document.homepage,
161
+ });
162
+ }
163
+ return out.sort((a, b) => a.name.localeCompare(b.name));
164
+ }
165
+ export function recommendBlocks(projectRoot, input) {
166
+ const domain = cleanString(input.domain).toLowerCase();
167
+ const tags = normalizeTags(input.tags ?? []);
168
+ const text = [input.purpose, input.audience, ...(input.tags ?? [])].map((v) => cleanString(v).toLowerCase()).filter(Boolean);
169
+ const certifiedOnly = input.certifiedOnly !== false;
170
+ const hasCriteria = Boolean(domain || tags.length > 0 || text.length > 0);
171
+ return collectBlockCandidates(projectRoot)
172
+ .map((block) => {
173
+ let score = 0;
174
+ let criteriaScore = 0;
175
+ const reasons = [];
176
+ if (domain && block.domain.toLowerCase() === domain) {
177
+ score += 100;
178
+ criteriaScore += 100;
179
+ reasons.push('domain match');
180
+ }
181
+ if (certifiedOnly && block.status !== 'certified')
182
+ return null;
183
+ if (block.status === 'certified') {
184
+ score += 30;
185
+ reasons.push('certified');
186
+ }
187
+ const overlap = block.tags.filter((tag) => tags.includes(tag.toLowerCase()));
188
+ if (overlap.length > 0) {
189
+ score += overlap.length * 12;
190
+ criteriaScore += overlap.length * 12;
191
+ reasons.push(`tag match: ${overlap.join(', ')}`);
192
+ }
193
+ const haystack = [block.name, block.description, block.owner ?? '', block.llmContext ?? '', ...block.tags]
194
+ .join(' ')
195
+ .toLowerCase();
196
+ const textHits = text.filter((term) => term && haystack.includes(term));
197
+ if (textHits.length > 0) {
198
+ score += textHits.length * 6;
199
+ criteriaScore += textHits.length * 6;
200
+ reasons.push('context match');
201
+ }
202
+ if (score === 0 && !domain && tags.length === 0 && !certifiedOnly)
203
+ score = 1;
204
+ if (hasCriteria && criteriaScore === 0)
205
+ return null;
206
+ if (score === 0)
207
+ return null;
208
+ return { ...block, score, reasons };
209
+ })
210
+ .filter((block) => Boolean(block))
211
+ .sort((a, b) => b.score - a.score || new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime())
212
+ .slice(0, 50);
213
+ }
214
+ export function createAppPackage(projectRoot, input) {
215
+ const name = cleanString(input.name);
216
+ const domain = cleanString(input.domain);
217
+ if (!name)
218
+ return { ok: false, error: 'name is required' };
219
+ if (!domain)
220
+ return { ok: false, error: 'domain is required' };
221
+ const id = suggestAppId(name);
222
+ const appDir = join(projectRoot, 'apps', id);
223
+ if (existsSync(appDir))
224
+ return { ok: false, error: `App already exists: ${id}` };
225
+ const owner = cleanString(input.owners?.[0]) || `${process.env.USER ?? 'owner'}@local`;
226
+ const audience = cleanString(input.audience);
227
+ const tags = normalizeTags([...(input.tags ?? []), audience ? `audience:${slugify(audience)}` : '']);
228
+ const selectedIds = Array.from(new Set((input.selectedBlockIds ?? []).map(cleanString).filter(Boolean)));
229
+ const blocks = collectBlockCandidates(projectRoot);
230
+ const selectedBlocks = selectedIds
231
+ .map((blockId) => blocks.find((block) => block.id === blockId || block.name === blockId))
232
+ .filter((block) => Boolean(block));
233
+ const app = {
234
+ version: 1,
235
+ id,
236
+ name,
237
+ description: cleanString(input.purpose) || `${name} consumption surface for ${domain}`,
238
+ domain,
239
+ owners: [owner],
240
+ tags,
241
+ members: [
242
+ { userId: owner, displayName: owner, roles: ['owner', 'analyst'] },
243
+ ],
244
+ roles: [
245
+ { id: 'owner', displayName: 'Owner', description: 'Full access to dashboards and App configuration.' },
246
+ { id: 'analyst', displayName: 'Analyst', description: 'Can execute dashboards and review generated drafts.' },
247
+ { id: 'viewer', displayName: 'Viewer', description: 'Read-only access to certified dashboard consumption.' },
248
+ ],
249
+ policies: [
250
+ {
251
+ id: 'viewers-read',
252
+ domain,
253
+ minClassification: 'internal',
254
+ allowedRoles: ['viewer', 'analyst', 'owner'],
255
+ accessLevel: 'read',
256
+ enabled: true,
257
+ },
258
+ {
259
+ id: 'analyst-execute',
260
+ domain,
261
+ minClassification: 'internal',
262
+ allowedRoles: ['analyst', 'owner'],
263
+ accessLevel: 'execute',
264
+ enabled: true,
265
+ },
266
+ {
267
+ id: 'owner-admin',
268
+ domain,
269
+ minClassification: 'restricted',
270
+ allowedRoles: ['owner'],
271
+ accessLevel: 'admin',
272
+ enabled: true,
273
+ },
274
+ ],
275
+ rlsBindings: [],
276
+ schedules: [],
277
+ homepage: { type: 'dashboard', id: 'overview' },
278
+ };
279
+ const dashboard = {
280
+ version: 1,
281
+ id: 'overview',
282
+ metadata: {
283
+ title: `${name} Overview`,
284
+ description: cleanString(input.purpose) || `Starter dashboard for ${name}`,
285
+ domain,
286
+ tags,
287
+ },
288
+ layout: {
289
+ kind: 'grid',
290
+ cols: 12,
291
+ rowHeight: 80,
292
+ items: buildDashboardItems(selectedBlocks),
293
+ },
294
+ };
295
+ const paths = [
296
+ join(appDir, 'dql.app.json'),
297
+ join(appDir, 'README.md'),
298
+ join(appDir, 'dashboards', 'overview.dqld'),
299
+ join(appDir, 'notebooks'),
300
+ join(appDir, 'drafts'),
301
+ ];
302
+ mkdirSync(join(appDir, 'dashboards'), { recursive: true });
303
+ mkdirSync(join(appDir, 'notebooks'), { recursive: true });
304
+ mkdirSync(join(appDir, 'drafts'), { recursive: true });
305
+ writeFileSync(join(appDir, 'dql.app.json'), JSON.stringify(app, null, 2) + '\n', 'utf-8');
306
+ writeFileSync(join(appDir, 'dashboards', 'overview.dqld'), JSON.stringify(dashboard, null, 2) + '\n', 'utf-8');
307
+ writeFileSync(join(appDir, 'README.md'), appReadme(app, audience, selectedBlocks), 'utf-8');
308
+ const created = collectAppsList(projectRoot).find((entry) => entry.id === id);
309
+ if (!created)
310
+ return { ok: false, error: `App was written but could not be reloaded: ${id}` };
311
+ return {
312
+ ok: true,
313
+ app: created,
314
+ paths: paths.map((path) => path.startsWith(projectRoot) ? path.slice(projectRoot.length + 1) : path),
315
+ dashboardId: 'overview',
316
+ };
317
+ }
318
+ function buildDashboardItems(blocks) {
319
+ let x = 0;
320
+ let y = 0;
321
+ let rowH = 0;
322
+ return [...blocks]
323
+ .sort((a, b) => vizRank(a.chartType) - vizRank(b.chartType))
324
+ .map((block, index) => {
325
+ const chartType = normalizeVizType(block.chartType);
326
+ const size = tileSize(chartType);
327
+ if (x + size.w > 12) {
328
+ x = 0;
329
+ y += rowH || size.h;
330
+ rowH = 0;
331
+ }
332
+ const item = dashboardItemForBlock(block, chartType, x, y, size, index);
333
+ x += size.w;
334
+ rowH = Math.max(rowH, size.h);
335
+ return item;
336
+ });
337
+ }
338
+ function dashboardItemForBlock(block, chartType, x, y, size, index) {
339
+ return {
340
+ i: slugify(block.name) || `tile-${index + 1}`,
341
+ x,
342
+ y,
343
+ w: size.w,
344
+ h: size.h,
345
+ block: { blockId: block.name },
346
+ viz: { type: chartType },
347
+ title: block.name,
348
+ };
349
+ }
350
+ function vizRank(chartType) {
351
+ const normalized = normalizeVizType(chartType);
352
+ if (normalized === 'single_value' || normalized === 'kpi')
353
+ return 0;
354
+ if (normalized === 'line' || normalized === 'area')
355
+ return 1;
356
+ if (normalized === 'bar' || normalized === 'pie' || normalized === 'funnel' || normalized === 'map')
357
+ return 2;
358
+ return 3;
359
+ }
360
+ function tileSize(chartType) {
361
+ if (chartType === 'single_value' || chartType === 'kpi')
362
+ return { w: 3, h: 2 };
363
+ if (chartType === 'table' || chartType === 'pivot')
364
+ return { w: 6, h: 4 };
365
+ return { w: 6, h: 3 };
366
+ }
367
+ function normalizeVizType(chartType) {
368
+ const normalized = (chartType ?? 'table').toLowerCase().replace(/-/g, '_');
369
+ if (normalized === 'single' || normalized === 'single_value')
370
+ return 'single_value';
371
+ if (normalized === 'kpi')
372
+ return 'kpi';
373
+ if (normalized === 'line')
374
+ return 'line';
375
+ if (normalized === 'bar')
376
+ return 'bar';
377
+ if (normalized === 'area')
378
+ return 'area';
379
+ if (normalized === 'pie')
380
+ return 'pie';
381
+ if (normalized === 'pivot')
382
+ return 'pivot';
383
+ if (normalized === 'map')
384
+ return 'map';
385
+ if (normalized === 'funnel')
386
+ return 'funnel';
387
+ return 'table';
388
+ }
389
+ function appReadme(app, audience, blocks) {
390
+ return [
391
+ `# ${app.name}`,
392
+ '',
393
+ app.description ?? '',
394
+ '',
395
+ `- Domain: ${app.domain}`,
396
+ `- Audience: ${audience || 'not specified'}`,
397
+ `- Owners: ${app.owners.join(', ')}`,
398
+ `- Starter dashboard: dashboards/overview.dqld`,
399
+ '',
400
+ '## Selected Certified Blocks',
401
+ '',
402
+ ...(blocks.length > 0
403
+ ? blocks.map((block) => `- ${block.name} (${block.domain}, ${block.status}) - ${block.path}`)
404
+ : ['No blocks selected yet. Add certified blocks from the Apps Command Center.']),
405
+ '',
406
+ '## Governance',
407
+ '',
408
+ 'This OSS App uses local persona switching with owner, analyst, and viewer roles. Real authentication and SSO are intentionally outside OSS scope.',
409
+ '',
410
+ ].join('\n');
411
+ }
412
+ function collectBlockCandidates(projectRoot) {
413
+ const blocksDir = join(projectRoot, 'blocks');
414
+ const blocks = [];
415
+ if (!existsSync(blocksDir))
416
+ return blocks;
417
+ const scanDir = (dir) => {
418
+ for (const entry of readdirSyncSafe(dir)) {
419
+ const filePath = join(dir, entry.name);
420
+ if (entry.isDirectory()) {
421
+ scanDir(filePath);
422
+ }
423
+ else if (entry.isFile() && entry.name.endsWith('.dql')) {
424
+ try {
425
+ const source = readFileSync(filePath, 'utf-8');
426
+ const stat = statSyncSafe(filePath);
427
+ const name = matchString(source, /block\s+"([^"]+)"/) ?? entry.name.replace(/\.dql$/, '');
428
+ const tags = matchArray(source, /tags\s*=\s*\[([^\]]*)\]/);
429
+ blocks.push({
430
+ id: name,
431
+ name,
432
+ domain: matchString(source, /domain\s*=\s*"([^"]+)"/) ?? 'uncategorized',
433
+ status: matchString(source, /status\s*=\s*"([^"]+)"/) ?? 'draft',
434
+ owner: matchString(source, /owner\s*=\s*"([^"]+)"/),
435
+ tags,
436
+ path: filePath.slice(projectRoot.length + 1),
437
+ lastModified: stat?.mtime.toISOString() ?? new Date(0).toISOString(),
438
+ description: matchString(source, /description\s*=\s*"((?:[^"\\]|\\.)*)"/) ?? '',
439
+ llmContext: matchString(source, /llmContext\s*=\s*"((?:[^"\\]|\\.)*)"/),
440
+ chartType: matchString(source, /chart\s*=\s*"([^"]+)"/) ?? matchString(source, /chart\.(\w+)\s*\(/) ?? undefined,
441
+ score: 0,
442
+ reasons: [],
443
+ });
444
+ }
445
+ catch {
446
+ // skip unreadable block
447
+ }
448
+ }
449
+ }
450
+ };
451
+ scanDir(blocksDir);
452
+ return blocks;
453
+ }
454
+ function readdirSyncSafe(dir) {
455
+ try {
456
+ return readdirSync(dir, { withFileTypes: true });
457
+ }
458
+ catch {
459
+ return [];
460
+ }
461
+ }
462
+ function statSyncSafe(path) {
463
+ try {
464
+ return statSync(path);
465
+ }
466
+ catch {
467
+ return null;
468
+ }
469
+ }
470
+ function matchString(source, regex) {
471
+ const match = regex.exec(source);
472
+ return match?.[1]?.replace(/\\"/g, '"').trim() || null;
473
+ }
474
+ function matchArray(source, regex) {
475
+ const match = regex.exec(source);
476
+ if (!match)
477
+ return [];
478
+ return match[1]
479
+ .split(',')
480
+ .map((item) => item.trim().replace(/^"|"$/g, ''))
481
+ .filter(Boolean);
482
+ }
483
+ function cleanString(value) {
484
+ return typeof value === 'string' ? value.trim() : '';
485
+ }
486
+ function normalizeTags(values) {
487
+ return Array.from(new Set(values.map((value) => cleanString(value)).filter(Boolean)));
488
+ }
489
+ function slugify(value) {
490
+ return value
491
+ .toLowerCase()
492
+ .replace(/[^a-z0-9]+/g, '-')
493
+ .replace(/^-+|-+$/g, '');
494
+ }
495
+ function audienceFromTags(tags) {
496
+ const tag = tags.find((value) => value.startsWith('audience:'));
497
+ if (!tag)
498
+ return undefined;
499
+ return tag.slice('audience:'.length).replace(/-/g, ' ');
500
+ }
501
+ function loadAppById(projectRoot, id) {
502
+ for (const p of findAppDocuments(projectRoot)) {
503
+ const { document } = loadAppDocument(p);
504
+ if (!document || document.id !== id)
505
+ continue;
506
+ const appDir = p.slice(0, -'/dql.app.json'.length);
507
+ const dashboards = [];
508
+ for (const d of findDashboardsForApp(appDir)) {
509
+ const { document: dd } = loadDashboardDocument(d);
510
+ if (dd) {
511
+ dashboards.push({
512
+ id: dd.id,
513
+ title: dd.metadata.title,
514
+ description: dd.metadata.description,
515
+ itemCount: dd.layout.items.length,
516
+ });
517
+ }
518
+ }
519
+ return { app: document, dashboards };
520
+ }
521
+ return null;
522
+ }
523
+ function listDashboardsFor(projectRoot, id) {
524
+ const result = loadAppById(projectRoot, id);
525
+ return result?.dashboards ?? null;
526
+ }
527
+ function loadDashboardForApp(projectRoot, appId, dashboardId) {
528
+ for (const p of findAppDocuments(projectRoot)) {
529
+ const { document } = loadAppDocument(p);
530
+ if (!document || document.id !== appId)
531
+ continue;
532
+ const appDir = p.slice(0, -'/dql.app.json'.length);
533
+ for (const d of findDashboardsForApp(appDir)) {
534
+ const { document: dd } = loadDashboardDocument(d);
535
+ if (dd && dd.id === dashboardId) {
536
+ return { app: document, dashboard: dd };
537
+ }
538
+ }
539
+ }
540
+ return null;
541
+ }
542
+ async function writeDashboard(projectRoot, appId, dashboardId, payload) {
543
+ // Validate against the dashboard schema before touching disk.
544
+ const { document, errors } = parseDashboardDocument(JSON.stringify(payload), '<incoming>');
545
+ if (!document) {
546
+ return { ok: false, error: errors.map((e) => e.message).join('; ') };
547
+ }
548
+ if (document.id !== dashboardId) {
549
+ return { ok: false, error: `dashboard.id (${document.id}) does not match URL :did (${dashboardId})` };
550
+ }
551
+ // Confirm the App exists.
552
+ const appDir = join(projectRoot, 'apps', appId);
553
+ if (!existsSync(join(appDir, 'dql.app.json'))) {
554
+ return { ok: false, error: `App "${appId}" not found at ${appDir}` };
555
+ }
556
+ const dashboardPath = join(appDir, 'dashboards', `${dashboardId}.dqld`);
557
+ mkdirSync(dirname(dashboardPath), { recursive: true });
558
+ writeFileSync(dashboardPath, JSON.stringify(document, null, 2) + '\n', 'utf-8');
559
+ return { ok: true, path: dashboardPath };
560
+ }
561
+ function activatePersona(projectRoot, userId, appId) {
562
+ // If an App id is provided, scope the persona to it.
563
+ if (appId) {
564
+ for (const p of findAppDocuments(projectRoot)) {
565
+ const { document } = loadAppDocument(p);
566
+ if (!document || document.id !== appId)
567
+ continue;
568
+ return defaultPersonaRegistry.setFromApp(document, userId);
569
+ }
570
+ return null;
571
+ }
572
+ // Otherwise pick the first App that contains the user.
573
+ for (const p of findAppDocuments(projectRoot)) {
574
+ const { document } = loadAppDocument(p);
575
+ if (!document)
576
+ continue;
577
+ const member = document.members.find((m) => m.userId === userId);
578
+ if (member) {
579
+ const persona = personaFromMember(document, member);
580
+ defaultPersonaRegistry.set(persona);
581
+ return persona;
582
+ }
583
+ }
584
+ return null;
585
+ }
586
+ // ---- IO utilities ----
587
+ function sendJson(res, status, body) {
588
+ res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
589
+ res.end(JSON.stringify(body));
590
+ }
591
+ async function readJson(req) {
592
+ return new Promise((resolve, reject) => {
593
+ const chunks = [];
594
+ req.on('data', (c) => chunks.push(c));
595
+ req.on('end', () => {
596
+ const raw = Buffer.concat(chunks).toString('utf-8');
597
+ if (!raw)
598
+ return resolve({});
599
+ try {
600
+ resolve(JSON.parse(raw));
601
+ }
602
+ catch (err) {
603
+ reject(err);
604
+ }
605
+ });
606
+ req.on('error', reject);
607
+ });
608
+ }
609
+ // reference unused parseAppDocument/readFileSync to keep import stable for forward use
610
+ void parseAppDocument;
611
+ void readFileSync;
612
+ //# sourceMappingURL=apps-api.js.map