@yejiming/dsh-data-agent 0.0.9 → 0.0.11
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 +32 -14
- package/README.md +32 -14
- package/cordis.patch.yml +8 -9
- package/lib/client.js +576 -593
- package/lib/client.js.map +1 -1
- package/lib/command-DuCpwVbl.js +875 -0
- package/lib/command.js +1 -874
- package/lib/{connections-DeauhaZi.js → connections-5sfdEDsG.js} +344 -34
- package/lib/index.js +104 -36
- package/lib/routes.js +6 -2
- package/lib/tool-DVh61An-.js +1128 -0
- package/lib/tool.js +1 -1127
- package/lib/types/client/DataAgentWorkbench.d.ts +2 -2
- package/lib/types/client/index.d.ts +3 -4
- package/lib/types/client/locales.d.ts +24 -0
- package/lib/types/client/persistence.d.ts +1 -1
- package/lib/types/client-discovery.d.ts +45 -0
- package/lib/types/clients.d.ts +7 -1
- package/lib/types/connections.d.ts +10 -0
- package/lib/types/defaults.d.ts +2 -0
- package/lib/types/index.d.ts +42 -16
- package/lib/types/routes.d.ts +0 -14
- package/lib/types/storage.d.ts +5 -0
- package/lib/types/tool.d.ts +4 -0
- package/package.json +5 -1
- package/preset/data-agent/agent.cordis.yml +6 -25
- package/preset/data-agent/preset.yml +1 -1
package/lib/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { o as clientsSchema, t as createConnectionService } from "./connections-
|
|
1
|
+
import { o as clientsSchema, t as createConnectionService } from "./connections-5sfdEDsG.js";
|
|
2
2
|
import { a as DEFAULT_PRESET_ID, i as DEFAULT_MAX_RESULT_CHARS, o as DEFAULT_QUERY_TIMEOUT_MS, r as DEFAULT_MAX_QUERY_CHARS, t as DEFAULT_CONNECT_TIMEOUT_MS } from "./defaults-DP4RyRh1.js";
|
|
3
|
-
import {
|
|
3
|
+
import { r as apply$1 } from "./command-DuCpwVbl.js";
|
|
4
|
+
import { n as apply$2 } from "./tool-DVh61An-.js";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
7
|
import { homedir } from "node:os";
|
|
5
8
|
import { join, resolve } from "node:path";
|
|
6
9
|
import { fileURLToPath } from "node:url";
|
|
@@ -40,6 +43,11 @@ const persistedConnectionProfileSchema = z$1.object({
|
|
|
40
43
|
database: z$1.string().min(1),
|
|
41
44
|
readonly: z$1.boolean().optional(),
|
|
42
45
|
passwordRef: z$1.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional(),
|
|
46
|
+
credentialMode: z$1.enum([
|
|
47
|
+
"none",
|
|
48
|
+
"password",
|
|
49
|
+
"reference"
|
|
50
|
+
]).optional(),
|
|
43
51
|
updatedAt: z$1.string().min(1)
|
|
44
52
|
}).strict();
|
|
45
53
|
/** Durable session-to-profile binding schema. */
|
|
@@ -109,24 +117,32 @@ function createDomainConnectionPersistence(domain) {
|
|
|
109
117
|
//#endregion
|
|
110
118
|
//#region src/index.ts
|
|
111
119
|
/**
|
|
112
|
-
* Data Agent
|
|
120
|
+
* Data Agent profile entry. The host row provides the
|
|
113
121
|
* `dataAgentConnections` service (shared non-secret profile/binding storage;
|
|
114
122
|
* temporary passwords stay process-local), seeds config connections (`connections`, `'*'` =
|
|
115
|
-
* wildcard default),
|
|
116
|
-
* `$DSH_HOME/.agent-presets
|
|
117
|
-
*
|
|
123
|
+
* wildcard default), installs the `data-agent` agent preset into
|
|
124
|
+
* `$DSH_HOME/.agent-presets/`, and preloads the preset-scoped database tools
|
|
125
|
+
* and command through this profile bundle entry.
|
|
118
126
|
*
|
|
119
127
|
* The HTTP routes live in the separate `./routes` entry
|
|
120
128
|
* (`@yejiming/dsh-data-agent/routes`, cordis row `data-agent-routes`) so
|
|
121
|
-
* this row keeps working in headless profiles without a webserver
|
|
122
|
-
* database
|
|
123
|
-
*
|
|
129
|
+
* this row keeps working in headless profiles without a webserver. The
|
|
130
|
+
* database implementations still have public `./tool` and `./command`
|
|
131
|
+
* exports, but the shipped preset does not dynamically import those package
|
|
132
|
+
* subpaths. Loading them here keeps Desktop on the same profile-startup path
|
|
133
|
+
* as other UI bundles and avoids Electron ASAR package-resolution drift.
|
|
124
134
|
* @module @yejiming/dsh-data-agent
|
|
125
135
|
*/
|
|
126
136
|
/** Cordis plugin name (diagnostics only). */
|
|
127
137
|
const name = "data-agent";
|
|
128
|
-
/** Services required before the
|
|
129
|
-
const inject = [
|
|
138
|
+
/** Services required before the profile entry can mount its preset layer. */
|
|
139
|
+
const inject = [
|
|
140
|
+
"agentPresets",
|
|
141
|
+
"commands",
|
|
142
|
+
"credentials",
|
|
143
|
+
"subprocess",
|
|
144
|
+
"tools"
|
|
145
|
+
];
|
|
130
146
|
/** Loader schema with deployment defaults (no library defaults). */
|
|
131
147
|
const Config = z.object({
|
|
132
148
|
presetId: z.string().default(DEFAULT_PRESET_ID),
|
|
@@ -135,6 +151,7 @@ const Config = z.object({
|
|
|
135
151
|
introspectMaxTables: z.number().step(1).min(1).default(500),
|
|
136
152
|
queryTimeoutMs: z.number().step(1).min(1e3).default(DEFAULT_QUERY_TIMEOUT_MS),
|
|
137
153
|
maxResultChars: z.number().step(1).min(1024).default(DEFAULT_MAX_RESULT_CHARS),
|
|
154
|
+
maxRows: z.number().step(1).min(1).default(100),
|
|
138
155
|
maxQueryChars: z.number().step(1).min(1024).default(DEFAULT_MAX_QUERY_CHARS),
|
|
139
156
|
readonly: z.boolean().default(false),
|
|
140
157
|
persistConnections: z.boolean().default(true),
|
|
@@ -168,26 +185,57 @@ function resolveDshHome(env = process.env) {
|
|
|
168
185
|
}
|
|
169
186
|
/**
|
|
170
187
|
* Install the packaged `preset/data-agent/` directory into
|
|
171
|
-
* `$DSH_HOME/.agent-presets/<presetId>/`. Idempotent: an existing target
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
188
|
+
* `$DSH_HOME/.agent-presets/<presetId>/`. Idempotent: an existing target is
|
|
189
|
+
* normally left untouched. The exact package-owned 0.0.9 composition is
|
|
190
|
+
* migrated once because its two dynamic package rows are incompatible with
|
|
191
|
+
* DSH Desktop's unpacked-ASAR loader; user-edited compositions are never
|
|
192
|
+
* overwritten. `installPreset: false` never calls this. Best-effort — a
|
|
193
|
+
* failure logs a warning with manual install instructions instead of failing
|
|
194
|
+
* the boot.
|
|
175
195
|
*/
|
|
176
196
|
async function installPreset(ctx, presetId) {
|
|
177
197
|
const targetDir = join(resolveDshHome(), ".agent-presets", presetId);
|
|
198
|
+
const sourceDir = fileURLToPath(new URL("../preset/data-agent/", import.meta.url));
|
|
178
199
|
try {
|
|
179
200
|
await access(targetDir);
|
|
180
|
-
|
|
181
|
-
await diagnoseExistingPreset(ctx, targetDir);
|
|
182
|
-
return;
|
|
201
|
+
return await synchronizeExistingPreset(ctx, targetDir, sourceDir, presetId);
|
|
183
202
|
} catch {}
|
|
184
|
-
const sourceDir = fileURLToPath(new URL("../preset/data-agent/", import.meta.url));
|
|
185
203
|
try {
|
|
186
204
|
await mkdir(targetDir, { recursive: true });
|
|
187
205
|
await cp(sourceDir, targetDir, { recursive: true });
|
|
188
206
|
ctx.logger.info("data-agent: installed preset \"%s\" to %s", presetId, targetDir);
|
|
207
|
+
return true;
|
|
189
208
|
} catch (error) {
|
|
190
209
|
ctx.logger.warn("data-agent: failed to install preset \"%s\" to %s (%s); copy preset/data-agent/ manually to enable the 数据模式 preset", presetId, targetDir, error instanceof Error ? error.message : String(error));
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** SHA-256 of the unmodified 0.0.9 composition that imported /tool and /command dynamically. */
|
|
214
|
+
const LEGACY_PRESET_0_0_9_SHA256 = "bae875a90d638ea78715030246b0f8a9f1a2c3359ca61febb6ceb59d0fcd930a";
|
|
215
|
+
/** Public for regression tests of the non-destructive preset migration gate. */
|
|
216
|
+
function isLegacyManagedPreset(source) {
|
|
217
|
+
return createHash("sha256").update(source).digest("hex") === LEGACY_PRESET_0_0_9_SHA256;
|
|
218
|
+
}
|
|
219
|
+
/** Upgrade only the exact package-owned legacy composition; preserve every edited preset. */
|
|
220
|
+
async function synchronizeExistingPreset(ctx, targetDir, sourceDir, presetId) {
|
|
221
|
+
const composition = join(targetDir, "agent.cordis.yml");
|
|
222
|
+
try {
|
|
223
|
+
const current = await readFile(composition, "utf8");
|
|
224
|
+
if (isLegacyManagedPreset(current)) {
|
|
225
|
+
const replacement = await readFile(join(sourceDir, "agent.cordis.yml"), "utf8");
|
|
226
|
+
await writeFile(composition, replacement, "utf8");
|
|
227
|
+
ctx.logger.info("data-agent: migrated preset at %s to profile-preloaded tools (removed dynamic /tool and /command rows)", composition);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
if (current.includes("@yejiming/dsh-data-agent/tool") || current.includes("@yejiming/dsh-data-agent/command")) {
|
|
231
|
+
ctx.logger.warn("data-agent: user-edited preset at %s still imports /tool or /command dynamically; remove those rows so the profile-preloaded preset capabilities can activate in DSH Desktop", composition);
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
ctx.logger.info("data-agent: preset \"%s\" already present at %s, skipping install", presetId, targetDir);
|
|
235
|
+
return true;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
ctx.logger.warn("data-agent: could not inspect existing preset %s (%s); it was not overwritten", composition, error instanceof Error ? error.message : String(error));
|
|
238
|
+
return false;
|
|
191
239
|
}
|
|
192
240
|
}
|
|
193
241
|
/** Exact profile-local package installation command used by diagnostics/docs. */
|
|
@@ -196,24 +244,32 @@ function profileInstallCommand(profile) {
|
|
|
196
244
|
}
|
|
197
245
|
/** Actionable diagnostic for a roster-visible preset whose profile lacks this package. */
|
|
198
246
|
function missingProfileDependencyMessage(profile) {
|
|
199
|
-
return `data-agent preset is visible, but profile "${profile}"
|
|
247
|
+
return `data-agent preset is visible, but its profile-preloaded capabilities are absent from profile "${profile}". Run: ${profileInstallCommand(profile)}`;
|
|
200
248
|
}
|
|
201
|
-
/**
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
249
|
+
/**
|
|
250
|
+
* Register the statically imported database tools and command under the exact
|
|
251
|
+
* standing key owned by the data-agent preset. Selecting the preset performs
|
|
252
|
+
* no package import and only links the agent scope to this key.
|
|
253
|
+
*/
|
|
254
|
+
async function mountPresetCapabilities(ctx, key, scopeTag, config) {
|
|
255
|
+
const scoped = ctx.extend({ [scopeTag]: key });
|
|
256
|
+
apply$2(scoped, config);
|
|
257
|
+
apply$1(scoped);
|
|
258
|
+
}
|
|
259
|
+
/** Read the host-owned scope tag from AgentPresets' already-created standing mount. */
|
|
260
|
+
async function standingScopeTag(ctx, presetId, key) {
|
|
261
|
+
const pending = ctx.agentPresets.standing?.get(presetId);
|
|
262
|
+
if (pending === void 0) throw new Error(`data-agent: preset "${presetId}" has no standing scope after standingKeyFor()`);
|
|
263
|
+
const standing = await pending;
|
|
264
|
+
if (standing.key !== key) throw new Error(`data-agent: preset "${presetId}" standing scope changed during profile preload`);
|
|
265
|
+
const tag = Object.getOwnPropertySymbols(standing.scope.ctx).find((candidate) => Reflect.get(standing.scope.ctx, candidate) === key);
|
|
266
|
+
if (tag === void 0) throw new Error(`data-agent: preset "${presetId}" standing context exposes no scope tag`);
|
|
267
|
+
return tag;
|
|
212
268
|
}
|
|
213
269
|
/**
|
|
214
|
-
* Mount the data-agent
|
|
215
|
-
* connections,
|
|
216
|
-
* `data-agent-routes` row (`./routes`).
|
|
270
|
+
* Mount the data-agent profile row: connection store, config-seeded
|
|
271
|
+
* connections, preset installation, and profile-preloaded preset capabilities.
|
|
272
|
+
* HTTP routes are the sibling `data-agent-routes` row (`./routes`).
|
|
217
273
|
* @param ctx - host cordis context.
|
|
218
274
|
* @param config - validated loader configuration.
|
|
219
275
|
*/
|
|
@@ -225,6 +281,7 @@ async function apply(ctx, config) {
|
|
|
225
281
|
introspectMaxTables: config.introspectMaxTables,
|
|
226
282
|
queryTimeoutMs: config.queryTimeoutMs,
|
|
227
283
|
maxResultChars: config.maxResultChars,
|
|
284
|
+
maxRows: config.maxRows,
|
|
228
285
|
maxQueryChars: config.maxQueryChars,
|
|
229
286
|
readonly: config.readonly,
|
|
230
287
|
persistConnections: config.persistConnections,
|
|
@@ -255,6 +312,7 @@ async function apply(ctx, config) {
|
|
|
255
312
|
store.set(sessionId, connection);
|
|
256
313
|
}
|
|
257
314
|
};
|
|
315
|
+
const presetReady = resolved.installPreset ? await installPreset(ctx, resolved.presetId) : false;
|
|
258
316
|
if (resolved.persistConnections) {
|
|
259
317
|
const domain = await (await ensureStorageDomain(ctx)).open(connectionStorageSpec);
|
|
260
318
|
ctx.effect(() => () => domain.close(), "data-agent: close connection storage domain");
|
|
@@ -263,7 +321,17 @@ async function apply(ctx, config) {
|
|
|
263
321
|
ctx.logger.warn("data-agent: persistConnections=false; connection state is process-local and cannot restore across Web/TUI");
|
|
264
322
|
mountService(ctx);
|
|
265
323
|
}
|
|
266
|
-
if (
|
|
324
|
+
if (presetReady) {
|
|
325
|
+
const standingKey = await ctx.agentPresets.standingKeyFor(resolved.presetId);
|
|
326
|
+
await mountPresetCapabilities(ctx, standingKey, await standingScopeTag(ctx, resolved.presetId, standingKey), {
|
|
327
|
+
queryTimeoutMs: resolved.queryTimeoutMs,
|
|
328
|
+
maxResultChars: resolved.maxResultChars,
|
|
329
|
+
maxRows: resolved.maxRows,
|
|
330
|
+
maxQueryChars: resolved.maxQueryChars,
|
|
331
|
+
readonly: resolved.readonly,
|
|
332
|
+
clients: resolved.clients
|
|
333
|
+
});
|
|
334
|
+
}
|
|
267
335
|
}
|
|
268
336
|
/**
|
|
269
337
|
* Reuse a surface-provided storage stack (Web) or mount the same JSON stack
|
|
@@ -289,4 +357,4 @@ async function ensureStorageDomain(ctx) {
|
|
|
289
357
|
return facility;
|
|
290
358
|
}
|
|
291
359
|
//#endregion
|
|
292
|
-
export { Config, apply, inject, installPreset, missingProfileDependencyMessage, name, profileInstallCommand, resolveDshHome };
|
|
360
|
+
export { Config, apply, inject, installPreset, isLegacyManagedPreset, missingProfileDependencyMessage, mountPresetCapabilities, name, profileInstallCommand, resolveDshHome };
|
package/lib/routes.js
CHANGED
|
@@ -90,8 +90,12 @@ function apply(ctx, _config) {
|
|
|
90
90
|
if (req.method === "GET" && routeIs(segments, "status")) {
|
|
91
91
|
const sessionId = requireString(url.searchParams.get("sessionId"), "sessionId");
|
|
92
92
|
const summary = await scope.dataAgentConnections.status(sessionId);
|
|
93
|
-
writeJson(200, summary === void 0 ? {
|
|
94
|
-
connected:
|
|
93
|
+
writeJson(200, summary === void 0 ? {
|
|
94
|
+
connected: false,
|
|
95
|
+
reconnectRequired: false
|
|
96
|
+
} : {
|
|
97
|
+
connected: summary.ready === true,
|
|
98
|
+
reconnectRequired: summary.reconnectRequired === true,
|
|
95
99
|
summary
|
|
96
100
|
});
|
|
97
101
|
return;
|