@yejiming/dsh-data-agent 0.0.13 → 0.1.1
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/README.en.md +46 -8
- package/README.md +46 -8
- package/conformance/dsh-ecosystem/inventory.json +26 -3
- package/conformance/dsh-ecosystem/restrictions.json +2 -2
- package/cordis.patch.yml +6 -3
- package/dsh-plugin.json +9 -4
- package/lib/catalog-DEJqOXRo.js +1944 -0
- package/lib/catalog-identity-CVftmvQL.js +96 -0
- package/lib/client.js +2214 -106
- package/lib/client.js.map +1 -1
- package/lib/command-CzzSPmag.js +1719 -0
- package/lib/command.js +2 -2
- package/lib/{connections-CHY4uB6z.js → connections-CFXOZTHZ.js} +223 -9
- package/lib/index.js +366 -16
- package/lib/routes.js +257 -4
- package/lib/{tool-ZTOS4B33.js → tool-DNkywSph.js} +364 -3
- package/lib/tool.js +1 -1
- package/lib/types/catalog-adapters.d.ts +52 -0
- package/lib/types/catalog-ai.d.ts +49 -0
- package/lib/types/catalog-command.d.ts +28 -0
- package/lib/types/catalog-identity.d.ts +23 -0
- package/lib/types/catalog-storage.d.ts +265 -0
- package/lib/types/catalog-tools.d.ts +5 -0
- package/lib/types/catalog-tui.d.ts +18 -0
- package/lib/types/catalog-types.d.ts +1376 -0
- package/lib/types/catalog.d.ts +59 -0
- package/lib/types/client/CatalogPanel.d.ts +15 -0
- package/lib/types/client/catalog-client.d.ts +57 -0
- package/lib/types/client/locales.d.ts +242 -0
- package/lib/types/command.d.ts +14 -3
- package/lib/types/connections.d.ts +9 -0
- package/lib/types/defaults.d.ts +22 -0
- package/lib/types/index.d.ts +42 -5
- package/lib/types/tui-connection-form.d.ts +11 -5
- package/package.json +4 -2
- package/preset/data-agent/agent.cordis.yml +9 -1
- package/lib/command-utC5MHd9.js +0 -916
- package/lib/defaults-Cngd8Tf8.js +0 -131
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as
|
|
3
|
-
import {
|
|
4
|
-
import { n as apply$2 } from "./tool-
|
|
1
|
+
import { _ as clientsSchema, b as DATABASE_TYPES, c as DEFAULT_CATALOG_MAX_RESULT_CHARS, d as DEFAULT_CONNECT_TIMEOUT_MS, f as DEFAULT_MAX_QUERY_CHARS, h as DEFAULT_QUERY_TIMEOUT_MS, l as DEFAULT_CATALOG_MAX_TEXT_CHARS, m as DEFAULT_PRESET_ID, p as DEFAULT_MAX_RESULT_CHARS, s as DEFAULT_CATALOG_MAX_ASSETS, t as createConnectionService, u as DEFAULT_CATALOG_QUERY_TIMEOUT_MS } from "./connections-CFXOZTHZ.js";
|
|
2
|
+
import { a as catalogDateTimeSchema, c as catalogRunSchema, f as catalogSemanticEntrySchema, i as catalogAssetRevisionSchema, m as catalogSourceSchema, n as createCatalogService, o as catalogObservationSchema, p as catalogSemanticRevisionSchema, r as catalogAssetHeadSchema, s as catalogRelationSchema, u as catalogSearchItemSchema } from "./catalog-DEJqOXRo.js";
|
|
3
|
+
import { i as apply$1 } from "./command-CzzSPmag.js";
|
|
4
|
+
import { n as apply$2 } from "./tool-DNkywSph.js";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
7
7
|
import { homedir } from "node:os";
|
|
@@ -13,6 +13,301 @@ import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
|
13
13
|
import * as storageJsonPlugin from "@deepseek-ai/dsh-storage-json";
|
|
14
14
|
import z from "schemastery";
|
|
15
15
|
import { z as z$1 } from "zod";
|
|
16
|
+
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
17
|
+
//#region src/catalog-ai.ts
|
|
18
|
+
const MAX_MODEL_OUTPUT_CHARS = 65536;
|
|
19
|
+
const MAX_MODEL_OUTPUT_TOKENS = 16384;
|
|
20
|
+
var CatalogModelOutputTruncatedError = class extends Error {
|
|
21
|
+
constructor() {
|
|
22
|
+
super("Catalog AI meaning output was truncated by the model token limit");
|
|
23
|
+
this.name = "CatalogModelOutputTruncatedError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const modelResultSchema = z$1.strictObject({
|
|
27
|
+
table: z$1.strictObject({
|
|
28
|
+
assetId: z$1.string().min(1).max(256),
|
|
29
|
+
meaning: z$1.string().trim().min(1).max(4096)
|
|
30
|
+
}),
|
|
31
|
+
fields: z$1.array(z$1.strictObject({
|
|
32
|
+
assetId: z$1.string().min(1).max(256),
|
|
33
|
+
meaning: z$1.string().trim().min(1).max(4096)
|
|
34
|
+
})).max(512)
|
|
35
|
+
});
|
|
36
|
+
/** Resolve the exact current session model once, then use the host's configured LLM adapters and credentials. */
|
|
37
|
+
function createDshCatalogMeaningGenerator(agents, llm) {
|
|
38
|
+
return {
|
|
39
|
+
capture(sessionId) {
|
|
40
|
+
const agent = agents.get(sessionId);
|
|
41
|
+
if (agent === void 0) throw new Error("Catalog scan requires a live DSH session to use its configured AI model");
|
|
42
|
+
const configured = agent.session.requestHeader()?.config;
|
|
43
|
+
const provider = configured?.provider ?? agent.options.provider;
|
|
44
|
+
const model = configured?.model ?? agent.options.model;
|
|
45
|
+
if (provider === void 0 || provider.trim().length === 0 || model === void 0 || model.trim().length === 0) throw new Error("Catalog scan requires the current DSH session to have a configured AI model");
|
|
46
|
+
return {
|
|
47
|
+
provider,
|
|
48
|
+
model,
|
|
49
|
+
...configured?.reasoningEffort !== void 0 ? { reasoningEffort: configured.reasoningEffort } : {}
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
async generate(selection, input, signal) {
|
|
53
|
+
return generateCompleteModelResult(llm, selection, input, signal);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function generateCompleteModelResult(llm, selection, input, signal) {
|
|
58
|
+
try {
|
|
59
|
+
return await generateModelBatch(llm, selection, input, signal);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (!(error instanceof CatalogModelOutputTruncatedError)) throw error;
|
|
62
|
+
if (input.fields.length <= 1) throw new Error("Catalog AI meaning output remained truncated after retrying a single-field batch");
|
|
63
|
+
const middle = Math.ceil(input.fields.length / 2);
|
|
64
|
+
const batches = [input.fields.slice(0, middle), input.fields.slice(middle)];
|
|
65
|
+
const results = [];
|
|
66
|
+
for (const fields of batches) {
|
|
67
|
+
signal.throwIfAborted();
|
|
68
|
+
results.push(await generateCompleteModelResult(llm, selection, sliceTableInput(input, fields), signal));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
table: results[0].table,
|
|
72
|
+
fields: results.flatMap((result) => result.fields)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function generateModelBatch(llm, selection, input, signal) {
|
|
77
|
+
const config = {
|
|
78
|
+
provider: selection.provider,
|
|
79
|
+
model: selection.model,
|
|
80
|
+
...selection.reasoningEffort !== void 0 ? { reasoningEffort: selection.reasoningEffort } : {},
|
|
81
|
+
maxTokens: MAX_MODEL_OUTPUT_TOKENS
|
|
82
|
+
};
|
|
83
|
+
const prepared = await llm.prepareCall(config, signal);
|
|
84
|
+
const message = createUserMessage({
|
|
85
|
+
content: [{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: JSON.stringify(input)
|
|
88
|
+
}],
|
|
89
|
+
source: {
|
|
90
|
+
kind: "plugin",
|
|
91
|
+
plugin: "@yejiming/dsh-data-agent"
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
let output = "";
|
|
95
|
+
let finished = false;
|
|
96
|
+
for await (const chunk of prepared.stream({
|
|
97
|
+
...prepared.config,
|
|
98
|
+
messages: [message],
|
|
99
|
+
system: CATALOG_MEANING_SYSTEM_PROMPT,
|
|
100
|
+
signal
|
|
101
|
+
})) {
|
|
102
|
+
signal.throwIfAborted();
|
|
103
|
+
if (chunk.type === "text-delta") {
|
|
104
|
+
output += chunk.text;
|
|
105
|
+
if (output.length > MAX_MODEL_OUTPUT_CHARS) throw new Error("Catalog AI meaning output exceeded the configured bound");
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (chunk.type !== "finish") continue;
|
|
109
|
+
finished = true;
|
|
110
|
+
if (chunk.reason.kind === "error" || chunk.reason.kind === "aborted") throw new Error(`Catalog AI meaning generation failed: ${chunk.reason.failure.message}`);
|
|
111
|
+
if (chunk.reason.kind === "max-tokens") throw new CatalogModelOutputTruncatedError();
|
|
112
|
+
if (chunk.reason.kind !== "stop") throw new Error(`Catalog AI meaning generation stopped unexpectedly: ${chunk.reason.kind}`);
|
|
113
|
+
}
|
|
114
|
+
if (!finished) throw new Error("Catalog AI meaning generation ended without a finish event");
|
|
115
|
+
return validateModelResult(output, input);
|
|
116
|
+
}
|
|
117
|
+
function sliceTableInput(input, fields) {
|
|
118
|
+
const fieldIds = new Set(fields.map((field) => field.assetId));
|
|
119
|
+
return {
|
|
120
|
+
...input,
|
|
121
|
+
fields,
|
|
122
|
+
relations: input.relations.filter((relation) => relation.columnAssetIds.length === 0 || relation.columnAssetIds.some((assetId) => fieldIds.has(assetId)))
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function validateModelResult(raw, input) {
|
|
126
|
+
const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
|
|
127
|
+
const first = text.indexOf("{");
|
|
128
|
+
const last = text.lastIndexOf("}");
|
|
129
|
+
if (first < 0 || last <= first) throw new Error("Catalog AI meaning output was not a JSON object");
|
|
130
|
+
let decoded;
|
|
131
|
+
try {
|
|
132
|
+
decoded = JSON.parse(text.slice(first, last + 1));
|
|
133
|
+
} catch {
|
|
134
|
+
throw new Error("Catalog AI meaning output contained invalid JSON");
|
|
135
|
+
}
|
|
136
|
+
const result = modelResultSchema.parse(decoded);
|
|
137
|
+
if (result.table.assetId !== input.assetId) throw new Error("Catalog AI meaning output referenced an unknown table asset");
|
|
138
|
+
const expected = new Set(input.fields.map((field) => field.assetId));
|
|
139
|
+
const returned = /* @__PURE__ */ new Set();
|
|
140
|
+
for (const field of result.fields) {
|
|
141
|
+
if (!expected.has(field.assetId)) throw new Error(`Catalog AI meaning output referenced an unknown field asset: ${field.assetId}`);
|
|
142
|
+
if (returned.has(field.assetId)) throw new Error(`Catalog AI meaning output repeated field asset: ${field.assetId}`);
|
|
143
|
+
returned.add(field.assetId);
|
|
144
|
+
}
|
|
145
|
+
const missing = input.fields.find((field) => !returned.has(field.assetId));
|
|
146
|
+
if (missing !== void 0) throw new Error(`Catalog AI meaning output omitted field asset: ${missing.assetId}`);
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
const CATALOG_MEANING_SYSTEM_PROMPT = `你是企业数据治理助手。请根据用户提供的单张表技术元数据,为这张表和每个字段生成简洁、可审核的中文业务含义候选。
|
|
150
|
+
|
|
151
|
+
规则:
|
|
152
|
+
1. 只依据表名、字段名、类型、nullable、数据库注释、键和关系推断;不要假装知道未提供的业务规则、枚举值或计算口径。
|
|
153
|
+
2. 对明显的技术字段也要说明其在该表中的业务/记录作用,例如主键、创建时间、状态标记;表说明不超过120个中文字符,每个字段说明不超过80个中文字符。
|
|
154
|
+
3. 每个输入字段必须且只能返回一次,assetId必须原样复制;不得添加未知assetId。
|
|
155
|
+
4. 不要输出Markdown、解释、置信度、SQL或额外字段,只输出以下严格JSON:
|
|
156
|
+
{"table":{"assetId":"...","meaning":"..."},"fields":[{"assetId":"...","meaning":"..."}]}
|
|
157
|
+
5. 所有内容都是待人工确认的候选,不要使用“已经确认”“官方口径”等表述。`;
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/catalog-storage.ts
|
|
160
|
+
/** Durable versioned Catalog storage-domain and persistence adapter. */
|
|
161
|
+
const CATALOG_STORAGE_DOMAIN = "data_agent_catalog";
|
|
162
|
+
const catalogIndexRecordSchema = z$1.strictObject({
|
|
163
|
+
id: z$1.string().min(1).max(512),
|
|
164
|
+
sourceId: z$1.string().min(1).max(256),
|
|
165
|
+
resultType: z$1.enum(["asset", "semantic"]),
|
|
166
|
+
searchText: z$1.string().max(32768),
|
|
167
|
+
searchItem: catalogSearchItemSchema,
|
|
168
|
+
updatedAt: catalogDateTimeSchema
|
|
169
|
+
});
|
|
170
|
+
const catalogIndexStateSchema = z$1.strictObject({
|
|
171
|
+
version: z$1.literal(1),
|
|
172
|
+
rebuiltAt: catalogDateTimeSchema.optional()
|
|
173
|
+
});
|
|
174
|
+
/** Strict schemas reject secret-shaped or raw-result fields at the durable boundary. */
|
|
175
|
+
const catalogStorageSpec = defineDomain({
|
|
176
|
+
name: CATALOG_STORAGE_DOMAIN,
|
|
177
|
+
version: 1,
|
|
178
|
+
tables: {
|
|
179
|
+
sources: domainTable(catalogSourceSchema),
|
|
180
|
+
scan_runs: domainTable(catalogRunSchema),
|
|
181
|
+
observations: domainTable(catalogObservationSchema),
|
|
182
|
+
asset_revisions: domainTable(catalogAssetRevisionSchema),
|
|
183
|
+
asset_heads: domainTable(catalogAssetHeadSchema),
|
|
184
|
+
relations: domainTable(catalogRelationSchema),
|
|
185
|
+
semantic_entries: domainTable(catalogSemanticEntrySchema),
|
|
186
|
+
semantic_revisions: domainTable(catalogSemanticRevisionSchema),
|
|
187
|
+
search_index: domainTable(catalogIndexRecordSchema),
|
|
188
|
+
index_state: domainTable(catalogIndexStateSchema)
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
function createDomainCatalogPersistence(domain) {
|
|
192
|
+
const sources = domain.table("sources");
|
|
193
|
+
const runs = domain.table("scan_runs");
|
|
194
|
+
const observations = domain.table("observations");
|
|
195
|
+
const revisions = domain.table("asset_revisions");
|
|
196
|
+
const heads = domain.table("asset_heads");
|
|
197
|
+
const relations = domain.table("relations");
|
|
198
|
+
const semanticEntries = domain.table("semantic_entries");
|
|
199
|
+
const semanticRevisions = domain.table("semantic_revisions");
|
|
200
|
+
const searchIndex = domain.table("search_index");
|
|
201
|
+
const indexState = domain.table("index_state");
|
|
202
|
+
return {
|
|
203
|
+
getSource: (id) => sources.get(id),
|
|
204
|
+
listSources: () => sortedValues(sources.entries(), (value) => value.id),
|
|
205
|
+
putSource: (source) => sources.put(source.id, catalogSourceSchema.parse(source)),
|
|
206
|
+
getRun: (id) => runs.get(id),
|
|
207
|
+
listRuns: (sourceId) => sortedValues(runs.entries(), (value) => value.createdAt).filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
208
|
+
putRun: (run) => runs.put(run.id, catalogRunSchema.parse(run)),
|
|
209
|
+
putObservation: (observation) => observations.put(`${observation.runId}:${observation.assetId}`, catalogObservationSchema.parse(observation)),
|
|
210
|
+
listObservations: (runId) => sortedValues(observations.entries(), (value) => value.assetId).filter((value) => value.runId === runId),
|
|
211
|
+
async deleteObservations(runId) {
|
|
212
|
+
const keys = [...observations.entries()].filter(([, value]) => value.runId === runId).map(([key]) => key);
|
|
213
|
+
for (const key of keys) await observations.delete(key);
|
|
214
|
+
},
|
|
215
|
+
getAssetHead: (assetId) => heads.get(assetId),
|
|
216
|
+
listAssetHeads: (sourceId) => sortedValues(heads.entries(), (value) => value.assetId).filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
217
|
+
putAssetHead: (head) => heads.put(head.assetId, catalogAssetHeadSchema.parse(head)),
|
|
218
|
+
getAssetRevision: (id) => revisions.get(id),
|
|
219
|
+
listAssetRevisions: (assetId) => sortedValues(revisions.entries(), (value) => value.id).filter((value) => assetId === void 0 || value.assetId === assetId),
|
|
220
|
+
putAssetRevision: (revision) => revisions.put(revision.id, catalogAssetRevisionSchema.parse(revision)),
|
|
221
|
+
listRelations: (sourceId) => sortedValues(relations.entries(), (value) => value.id).filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
222
|
+
putRelation: (relation) => relations.put(`${relation.runId}:${relation.id}`, catalogRelationSchema.parse(relation)),
|
|
223
|
+
getSemanticEntry: (id) => semanticEntries.get(id),
|
|
224
|
+
listSemanticEntries: (sourceId) => sortedValues(semanticEntries.entries(), (value) => value.id).filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
225
|
+
putSemanticEntry: (entry) => semanticEntries.put(entry.id, catalogSemanticEntrySchema.parse(entry)),
|
|
226
|
+
getSemanticRevision: (id) => semanticRevisions.get(id),
|
|
227
|
+
listSemanticRevisions: (semanticId) => sortedValues(semanticRevisions.entries(), (value) => value.id).filter((value) => semanticId === void 0 || value.semanticId === semanticId),
|
|
228
|
+
putSemanticRevision: (revision) => semanticRevisions.put(revision.id, catalogSemanticRevisionSchema.parse(revision)),
|
|
229
|
+
listIndex: (sourceId) => sortedValues(searchIndex.entries(), (value) => value.id).filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
230
|
+
putIndex: (record) => searchIndex.put(record.id, catalogIndexRecordSchema.parse(record)),
|
|
231
|
+
async clearIndex(sourceId) {
|
|
232
|
+
const keys = [...searchIndex.entries()].filter(([, value]) => sourceId === void 0 || value.sourceId === sourceId).map(([key]) => key);
|
|
233
|
+
for (const key of keys) await searchIndex.delete(key);
|
|
234
|
+
},
|
|
235
|
+
getIndexState: () => indexState.get("current"),
|
|
236
|
+
putIndexState: (state) => indexState.put("current", catalogIndexStateSchema.parse(state))
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/** In-memory adapter used when Catalog persistence is explicitly disabled and by focused tests. */
|
|
240
|
+
function createMemoryCatalogPersistence() {
|
|
241
|
+
const map = () => /* @__PURE__ */ new Map();
|
|
242
|
+
const sources = map();
|
|
243
|
+
const runs = map();
|
|
244
|
+
const observations = map();
|
|
245
|
+
const heads = map();
|
|
246
|
+
const revisions = map();
|
|
247
|
+
const relations = map();
|
|
248
|
+
const entries = map();
|
|
249
|
+
const semanticRevisions = map();
|
|
250
|
+
const index = map();
|
|
251
|
+
let state;
|
|
252
|
+
return {
|
|
253
|
+
getSource: (id) => sources.get(id),
|
|
254
|
+
listSources: () => [...sources.values()].sort((a, b) => a.id.localeCompare(b.id)),
|
|
255
|
+
async putSource(source) {
|
|
256
|
+
sources.set(source.id, catalogSourceSchema.parse(source));
|
|
257
|
+
},
|
|
258
|
+
getRun: (id) => runs.get(id),
|
|
259
|
+
listRuns: (sourceId) => [...runs.values()].filter((value) => sourceId === void 0 || value.sourceId === sourceId).sort((a, b) => a.createdAt.localeCompare(b.createdAt)),
|
|
260
|
+
async putRun(run) {
|
|
261
|
+
runs.set(run.id, catalogRunSchema.parse(run));
|
|
262
|
+
},
|
|
263
|
+
async putObservation(value) {
|
|
264
|
+
observations.set(`${value.runId}:${value.assetId}`, catalogObservationSchema.parse(value));
|
|
265
|
+
},
|
|
266
|
+
listObservations: (runId) => [...observations.values()].filter((value) => value.runId === runId),
|
|
267
|
+
async deleteObservations(runId) {
|
|
268
|
+
for (const [key, value] of observations) if (value.runId === runId) observations.delete(key);
|
|
269
|
+
},
|
|
270
|
+
getAssetHead: (id) => heads.get(id),
|
|
271
|
+
listAssetHeads: (sourceId) => [...heads.values()].filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
272
|
+
async putAssetHead(value) {
|
|
273
|
+
heads.set(value.assetId, catalogAssetHeadSchema.parse(value));
|
|
274
|
+
},
|
|
275
|
+
getAssetRevision: (id) => revisions.get(id),
|
|
276
|
+
listAssetRevisions: (assetId) => [...revisions.values()].filter((value) => assetId === void 0 || value.assetId === assetId),
|
|
277
|
+
async putAssetRevision(value) {
|
|
278
|
+
revisions.set(value.id, catalogAssetRevisionSchema.parse(value));
|
|
279
|
+
},
|
|
280
|
+
listRelations: (sourceId) => [...relations.values()].filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
281
|
+
async putRelation(value) {
|
|
282
|
+
relations.set(`${value.runId}:${value.id}`, catalogRelationSchema.parse(value));
|
|
283
|
+
},
|
|
284
|
+
getSemanticEntry: (id) => entries.get(id),
|
|
285
|
+
listSemanticEntries: (sourceId) => [...entries.values()].filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
286
|
+
async putSemanticEntry(value) {
|
|
287
|
+
entries.set(value.id, catalogSemanticEntrySchema.parse(value));
|
|
288
|
+
},
|
|
289
|
+
getSemanticRevision: (id) => semanticRevisions.get(id),
|
|
290
|
+
listSemanticRevisions: (semanticId) => [...semanticRevisions.values()].filter((value) => semanticId === void 0 || value.semanticId === semanticId),
|
|
291
|
+
async putSemanticRevision(value) {
|
|
292
|
+
semanticRevisions.set(value.id, catalogSemanticRevisionSchema.parse(value));
|
|
293
|
+
},
|
|
294
|
+
listIndex: (sourceId) => [...index.values()].filter((value) => sourceId === void 0 || value.sourceId === sourceId),
|
|
295
|
+
async putIndex(value) {
|
|
296
|
+
index.set(value.id, catalogIndexRecordSchema.parse(value));
|
|
297
|
+
},
|
|
298
|
+
async clearIndex(sourceId) {
|
|
299
|
+
for (const [key, value] of index) if (sourceId === void 0 || value.sourceId === sourceId) index.delete(key);
|
|
300
|
+
},
|
|
301
|
+
getIndexState: () => state,
|
|
302
|
+
async putIndexState(value) {
|
|
303
|
+
state = catalogIndexStateSchema.parse(value);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function sortedValues(entries, by) {
|
|
308
|
+
return [...entries].map(([, value]) => value).sort((a, b) => by(a).localeCompare(by(b)));
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
16
311
|
//#region src/storage.ts
|
|
17
312
|
/**
|
|
18
313
|
* Durable, non-secret connection profiles, session bindings, and form drafts.
|
|
@@ -91,6 +386,12 @@ function createDomainConnectionPersistence(domain) {
|
|
|
91
386
|
getLatestProfile() {
|
|
92
387
|
return latestConnectionProfile(profiles.entries());
|
|
93
388
|
},
|
|
389
|
+
listProfiles() {
|
|
390
|
+
return [...profiles.entries()].map(([profileId, profile]) => ({
|
|
391
|
+
profileId,
|
|
392
|
+
profile
|
|
393
|
+
})).sort((left, right) => left.profileId.localeCompare(right.profileId));
|
|
394
|
+
},
|
|
94
395
|
putProfile(profileId, profile) {
|
|
95
396
|
return profiles.put(profileId, profile);
|
|
96
397
|
},
|
|
@@ -123,9 +424,10 @@ function createDomainConnectionPersistence(domain) {
|
|
|
123
424
|
* Data Agent profile entry. The host row provides the
|
|
124
425
|
* `dataAgentConnections` service (shared non-secret profile/binding storage;
|
|
125
426
|
* temporary passwords stay process-local), seeds config connections (`connections`, `'*'` =
|
|
126
|
-
* wildcard default), installs the `data-agent` agent preset into
|
|
427
|
+
* wildcard default), provides a separate versioned governance Catalog, installs the `data-agent` agent preset into
|
|
127
428
|
* `$DSH_HOME/.agent-presets/`, and preloads the preset-scoped database tools
|
|
128
|
-
*
|
|
429
|
+
* on every surface, while registering `/database` and `/catalog` only while
|
|
430
|
+
* the current Cordis composition actually loads the dsh-tui plugin.
|
|
129
431
|
*
|
|
130
432
|
* The HTTP routes live in the separate `./routes` entry
|
|
131
433
|
* (`@yejiming/dsh-data-agent/routes`, cordis row `data-agent-routes`) so
|
|
@@ -141,8 +443,10 @@ const name = "data-agent";
|
|
|
141
443
|
/** Services required before the profile entry can mount its preset layer. */
|
|
142
444
|
const inject = [
|
|
143
445
|
"agentPresets",
|
|
446
|
+
"agents",
|
|
144
447
|
"commands",
|
|
145
448
|
"credentials",
|
|
449
|
+
"llm",
|
|
146
450
|
"subprocess",
|
|
147
451
|
"tools"
|
|
148
452
|
];
|
|
@@ -153,6 +457,14 @@ const Config = z.object({
|
|
|
153
457
|
connectTimeoutMs: z.number().step(1).min(1e3).default(DEFAULT_CONNECT_TIMEOUT_MS),
|
|
154
458
|
introspectMaxTables: z.number().step(1).min(1).default(500),
|
|
155
459
|
queryTimeoutMs: z.number().step(1).min(1e3).default(DEFAULT_QUERY_TIMEOUT_MS),
|
|
460
|
+
catalogQueryTimeoutMs: z.number().step(1).min(1e3).default(DEFAULT_CATALOG_QUERY_TIMEOUT_MS),
|
|
461
|
+
catalogMaxResultChars: z.number().step(1).min(1024).default(DEFAULT_CATALOG_MAX_RESULT_CHARS),
|
|
462
|
+
catalogSchemaConcurrency: z.number().step(1).min(1).max(16).default(2),
|
|
463
|
+
catalogAssetConcurrency: z.number().step(1).min(1).max(32).default(4),
|
|
464
|
+
catalogMaxAssetsPerRun: z.number().step(1).min(1).max(1e6).default(DEFAULT_CATALOG_MAX_ASSETS),
|
|
465
|
+
catalogMaxTextChars: z.number().step(1).min(256).max(4096).default(DEFAULT_CATALOG_MAX_TEXT_CHARS),
|
|
466
|
+
catalogPageSize: z.number().step(1).min(1).max(200).default(50),
|
|
467
|
+
catalogMaxPageSize: z.number().step(1).min(1).max(200).default(200),
|
|
156
468
|
maxResultChars: z.number().step(1).min(1024).default(DEFAULT_MAX_RESULT_CHARS),
|
|
157
469
|
maxRows: z.number().step(1).min(1).default(100),
|
|
158
470
|
maxQueryChars: z.number().step(1).min(1024).default(DEFAULT_MAX_QUERY_CHARS),
|
|
@@ -217,7 +529,11 @@ async function installPreset(ctx, presetId) {
|
|
|
217
529
|
}
|
|
218
530
|
}
|
|
219
531
|
/** SHA-256 values of unmodified package-owned compositions safe to migrate. */
|
|
220
|
-
const LEGACY_MANAGED_PRESET_SHA256 = /* @__PURE__ */ new Set([
|
|
532
|
+
const LEGACY_MANAGED_PRESET_SHA256 = /* @__PURE__ */ new Set([
|
|
533
|
+
"bae875a90d638ea78715030246b0f8a9f1a2c3359ca61febb6ceb59d0fcd930a",
|
|
534
|
+
"d3c6f4049580069eec1c6b7de101f12c7fb30482ad317434afb69afb08a91fc6",
|
|
535
|
+
"11c4b5ef62c5934d1dc7133950bd78622dd68dc4e1075b5f24d0789011d6da9d"
|
|
536
|
+
]);
|
|
221
537
|
/** Public for regression tests of the non-destructive preset migration gate. */
|
|
222
538
|
function isLegacyManagedPreset(source) {
|
|
223
539
|
return LEGACY_MANAGED_PRESET_SHA256.has(createHash("sha256").update(source).digest("hex"));
|
|
@@ -253,14 +569,14 @@ function missingProfileDependencyMessage(profile) {
|
|
|
253
569
|
return `data-agent preset is visible, but its profile-preloaded capabilities are absent from profile "${profile}". Run: ${profileInstallCommand(profile)}`;
|
|
254
570
|
}
|
|
255
571
|
/**
|
|
256
|
-
* Register the statically imported database tools and
|
|
572
|
+
* Register the statically imported database tools and surface adapters under the exact
|
|
257
573
|
* standing key owned by the data-agent preset. Selecting the preset performs
|
|
258
574
|
* no package import and only links the agent scope to this key.
|
|
259
575
|
*/
|
|
260
|
-
async function mountPresetCapabilities(ctx, key, scopeTag, config) {
|
|
576
|
+
async function mountPresetCapabilities(ctx, key, scopeTag, config, commandOptions = {}) {
|
|
261
577
|
const scoped = ctx.extend({ [scopeTag]: key });
|
|
262
578
|
apply$2(scoped, config);
|
|
263
|
-
apply$1(scoped);
|
|
579
|
+
apply$1(scoped, commandOptions);
|
|
264
580
|
}
|
|
265
581
|
/** Read the host-owned scope tag from AgentPresets' already-created standing mount. */
|
|
266
582
|
async function standingScopeTag(ctx, presetId, key) {
|
|
@@ -280,12 +596,21 @@ async function standingScopeTag(ctx, presetId, key) {
|
|
|
280
596
|
* @param config - validated loader configuration.
|
|
281
597
|
*/
|
|
282
598
|
async function apply(ctx, config) {
|
|
599
|
+
if (config.catalogPageSize > config.catalogMaxPageSize) throw new Error("data-agent: catalogPageSize cannot exceed catalogMaxPageSize");
|
|
283
600
|
const resolved = {
|
|
284
601
|
presetId: config.presetId,
|
|
285
602
|
installPreset: config.installPreset,
|
|
286
603
|
connectTimeoutMs: config.connectTimeoutMs,
|
|
287
604
|
introspectMaxTables: config.introspectMaxTables,
|
|
288
605
|
queryTimeoutMs: config.queryTimeoutMs,
|
|
606
|
+
catalogQueryTimeoutMs: config.catalogQueryTimeoutMs,
|
|
607
|
+
catalogMaxResultChars: config.catalogMaxResultChars,
|
|
608
|
+
catalogSchemaConcurrency: config.catalogSchemaConcurrency,
|
|
609
|
+
catalogAssetConcurrency: config.catalogAssetConcurrency,
|
|
610
|
+
catalogMaxAssetsPerRun: config.catalogMaxAssetsPerRun,
|
|
611
|
+
catalogMaxTextChars: config.catalogMaxTextChars,
|
|
612
|
+
catalogPageSize: config.catalogPageSize,
|
|
613
|
+
catalogMaxPageSize: config.catalogMaxPageSize,
|
|
289
614
|
maxResultChars: config.maxResultChars,
|
|
290
615
|
maxRows: config.maxRows,
|
|
291
616
|
maxQueryChars: config.maxQueryChars,
|
|
@@ -294,15 +619,18 @@ async function apply(ctx, config) {
|
|
|
294
619
|
clients: config.clients,
|
|
295
620
|
connections: config.connections
|
|
296
621
|
};
|
|
297
|
-
const mountService = (scope, persistence) => {
|
|
622
|
+
const mountService = (scope, persistence, preferredProfileIds) => {
|
|
298
623
|
const store = createConnectionService(scope, {
|
|
299
624
|
connectTimeoutMs: resolved.connectTimeoutMs,
|
|
300
625
|
queryTimeoutMs: resolved.queryTimeoutMs,
|
|
626
|
+
catalogQueryTimeoutMs: resolved.catalogQueryTimeoutMs,
|
|
627
|
+
catalogMaxResultChars: resolved.catalogMaxResultChars,
|
|
301
628
|
maxResultChars: resolved.maxResultChars,
|
|
302
629
|
maxQueryChars: resolved.maxQueryChars,
|
|
303
630
|
introspectMaxTables: resolved.introspectMaxTables,
|
|
304
631
|
readonly: resolved.readonly,
|
|
305
|
-
clients: resolved.clients
|
|
632
|
+
clients: resolved.clients,
|
|
633
|
+
...preferredProfileIds !== void 0 ? { preferredProfileIds } : {}
|
|
306
634
|
}, persistence);
|
|
307
635
|
scope.provide("dataAgentConnections", store);
|
|
308
636
|
for (const [sessionId, spec] of Object.entries(resolved.connections)) {
|
|
@@ -318,16 +646,38 @@ async function apply(ctx, config) {
|
|
|
318
646
|
};
|
|
319
647
|
store.set(sessionId, connection);
|
|
320
648
|
}
|
|
649
|
+
return store;
|
|
321
650
|
};
|
|
322
651
|
const presetReady = resolved.installPreset ? await installPreset(ctx, resolved.presetId) : false;
|
|
652
|
+
let connectionPersistence;
|
|
653
|
+
let catalogPersistence;
|
|
323
654
|
if (resolved.persistConnections) {
|
|
324
|
-
const
|
|
655
|
+
const storageDomain = await ensureStorageDomain(ctx);
|
|
656
|
+
const domain = await storageDomain.open(connectionStorageSpec);
|
|
325
657
|
ctx.effect(() => () => domain.close(), "data-agent: close connection storage domain");
|
|
326
|
-
|
|
658
|
+
connectionPersistence = createDomainConnectionPersistence(domain);
|
|
659
|
+
const catalogDomain = await storageDomain.open(catalogStorageSpec);
|
|
660
|
+
ctx.effect(() => () => catalogDomain.close(), "data-agent: close Catalog storage domain");
|
|
661
|
+
catalogPersistence = createDomainCatalogPersistence(catalogDomain);
|
|
327
662
|
} else {
|
|
328
|
-
ctx.logger.warn("data-agent: persistConnections=false; connection state
|
|
329
|
-
|
|
663
|
+
ctx.logger.warn("data-agent: persistConnections=false; connection and Catalog state are process-local and cannot restore across Web/TUI");
|
|
664
|
+
catalogPersistence = createMemoryCatalogPersistence();
|
|
330
665
|
}
|
|
666
|
+
const connectionService = mountService(ctx, connectionPersistence, () => catalogPersistence.listSources().map((source) => source.profileId));
|
|
667
|
+
const catalog = await createCatalogService(connectionService, catalogPersistence, {
|
|
668
|
+
maxAssetsPerRun: resolved.catalogMaxAssetsPerRun,
|
|
669
|
+
maxTextChars: resolved.catalogMaxTextChars,
|
|
670
|
+
pageSize: resolved.catalogPageSize,
|
|
671
|
+
maxPageSize: resolved.catalogMaxPageSize,
|
|
672
|
+
schemaConcurrency: resolved.catalogSchemaConcurrency,
|
|
673
|
+
assetConcurrency: resolved.catalogAssetConcurrency,
|
|
674
|
+
meaningGenerator: createDshCatalogMeaningGenerator(ctx.agents, ctx.llm),
|
|
675
|
+
logger: ctx.logger
|
|
676
|
+
});
|
|
677
|
+
ctx.provide("dataAgentCatalog", catalog.read);
|
|
678
|
+
ctx.provide("dataAgentCatalogScanner", catalog.scanner);
|
|
679
|
+
ctx.provide("dataAgentCatalogReview", catalog.review);
|
|
680
|
+
ctx.effect(() => () => catalog.scanner.interruptActiveRuns(), "data-agent: interrupt active Catalog scans");
|
|
331
681
|
if (presetReady) {
|
|
332
682
|
const standingKey = await ctx.agentPresets.standingKeyFor(resolved.presetId);
|
|
333
683
|
await mountPresetCapabilities(ctx, standingKey, await standingScopeTag(ctx, resolved.presetId, standingKey), {
|