@yejiming/dsh-data-agent 0.0.6 → 0.0.10

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 (39) hide show
  1. package/README.en.md +142 -119
  2. package/README.md +138 -118
  3. package/cordis.patch.yml +8 -9
  4. package/lib/client.js +42423 -524
  5. package/lib/client.js.map +1 -1
  6. package/lib/command-LFgLb6el.js +875 -0
  7. package/lib/command.js +2 -0
  8. package/lib/connections-WmjuUrDj.js +1608 -0
  9. package/lib/defaults-DP4RyRh1.js +21 -0
  10. package/lib/index.js +265 -68
  11. package/lib/routes.js +94 -170
  12. package/lib/tool-Dka6RyEp.js +1128 -0
  13. package/lib/tool.js +1 -426
  14. package/lib/types/analysis.d.ts +1071 -0
  15. package/lib/types/client/AnalysisChart.d.ts +26 -0
  16. package/lib/types/client/AnalysisDashboard.d.ts +30 -0
  17. package/lib/types/client/DataAgentWorkbench.d.ts +2 -2
  18. package/lib/types/client/analysis-charts.d.ts +40 -0
  19. package/lib/types/client/analysis-view-model.d.ts +44 -0
  20. package/lib/types/client/index.d.ts +3 -4
  21. package/lib/types/client/locales.d.ts +66 -0
  22. package/lib/types/client/persistence.d.ts +6 -1
  23. package/lib/types/client-discovery.d.ts +45 -0
  24. package/lib/types/clients.d.ts +17 -10
  25. package/lib/types/command.d.ts +41 -0
  26. package/lib/types/connections.d.ts +115 -40
  27. package/lib/types/defaults.d.ts +2 -0
  28. package/lib/types/index.d.ts +109 -63
  29. package/lib/types/routes.d.ts +25 -91
  30. package/lib/types/sql.d.ts +1 -1
  31. package/lib/types/storage.d.ts +70 -0
  32. package/lib/types/structured-read.d.ts +50 -0
  33. package/lib/types/tool.d.ts +29 -20
  34. package/lib/types/tui-connection-form.d.ts +98 -0
  35. package/package.json +65 -4
  36. package/preset/data-agent/agent.cordis.yml +22 -25
  37. package/preset/data-agent/preset.yml +1 -1
  38. package/lib/defaults-Bac6QvNt.js +0 -911
  39. package/lib/query-CmhTFklw.js +0 -86
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Reusable chart container (task 3.3): owns one ECharts instance per mount,
3
+ * resizes it through a ResizeObserver, disposes it on unmount, and exposes an
4
+ * accessible image role + short text summary. Options arrive pre-built by
5
+ * chartOptionFor (token theme, non-HTML tooltips, reduced-motion included).
6
+ * @module @yejiming/dsh-data-agent/client/AnalysisChart
7
+ */
8
+ import type { EChartsCoreOption } from 'echarts/core';
9
+ /** Chart container props. */
10
+ export interface AnalysisChartProps {
11
+ /** Safe pre-built option (pure mapping of the constrained report). */
12
+ option: EChartsCoreOption;
13
+ /** Accessible name of the chart image. */
14
+ ariaLabel: string;
15
+ /** Short plain-text summary announced to assistive tech. */
16
+ summary: string;
17
+ /** Chart canvas height in px (the container always spans full width). */
18
+ height?: number;
19
+ }
20
+ /**
21
+ * One chart instance: init on mount, setOption on every option change (theme
22
+ * switches rebuild the option), resize on container changes, dispose on
23
+ * unmount. Null data points render as gaps because numericOrNull never
24
+ * converts null to zero.
25
+ */
26
+ export declare function AnalysisChart({ option, ariaLabel, summary, height }: AnalysisChartProps): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * render-analysis tool result row + Dashboard Modal (tasks 3.4, 4.x).
3
+ *
4
+ * The row is registered for the keyed tool.call.toolview slot under
5
+ * key: render-analysis. It is a pure function of the frozen ToolCallBlock
6
+ * owner payload (decoded by analysis-view-model): running/error/interrupted
7
+ * statuses, a compact summary for complex reports, a bounded inline preview
8
+ * for the simple single-chart report, and a native "查看分析" button that
9
+ * opens THIS call's own Dashboard Modal (local state — one session may hold
10
+ * many independent reports).
11
+ *
12
+ * The Modal reuses the host primitives Modal (Escape/mask close) headless,
13
+ * keeps its own header fixed, scrolls the body independently, and returns
14
+ * focus to the trigger button on close. All interactive elements are native
15
+ * buttons; every chart carries an aria-label plus a plain-text summary; all
16
+ * cell/label/axis values render as text (React escaping + ECharts richText),
17
+ * so no report field can become executable DOM.
18
+ * @module @yejiming/dsh-data-agent/client/AnalysisDashboard
19
+ */
20
+ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
21
+ import type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client';
22
+ import { type AnalysisViewV1, type AnalysisViewWidth } from '../analysis.ts';
23
+ /** Full row props: the toolview runtime share + the data-agent locale seat. */
24
+ export type RenderAnalysisRowProps = ToolCallViewProps & PropsLocale<'data-agent'>;
25
+ /** Effective grid width per view id: tables and the first chart default full. */
26
+ export declare function computeViewWidths(views: readonly AnalysisViewV1[]): Map<string, AnalysisViewWidth>;
27
+ /** Format one metric value without ever inventing data. */
28
+ export declare function formatMetricValue(value: string | null, format: 'number' | 'percent' | undefined, empty: string): string;
29
+ /** The registered render-analysis tool result row. */
30
+ export declare function RenderAnalysisRow({ toolName, block, t }: RenderAnalysisRowProps): import("react").JSX.Element;
@@ -16,7 +16,7 @@ export interface DataAgentWorkbenchInjected {
16
16
  };
17
17
  };
18
18
  }
19
- /** The workbench's full component props: the dock seat + the locale seat + the injected sessions hook. */
20
- export type DataAgentWorkbenchProps = PropsRuntime<'conversation.input.dock'> & PropsLocale<'data-agent'> & InjectFace<DataAgentWorkbenchInjected>;
19
+ /** The workbench's full component props: the composer-right seat + locale + sessions hook. */
20
+ export type DataAgentWorkbenchProps = PropsRuntime<'conversation.input.right'> & PropsLocale<'data-agent'> & InjectFace<DataAgentWorkbenchInjected>;
21
21
  /** The database workbench body. */
22
22
  export declare function DataAgentWorkbench({ sessionId, useSessions, t }: DataAgentWorkbenchProps): import("react").JSX.Element | null;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Safe ECharts mapping for the analysis dashboard (tasks 3.2/3.3). Only the
3
+ * four first-version chart types and their required components are registered
4
+ * from echarts/core — line/bar/pie/scatter + grid/tooltip/legend/aria with
5
+ * the SVG renderer — so the client bundle stays tree-shaken per chart type.
6
+ *
7
+ * Every option is a PURE mapping of the constrained AnalysisReportV1 (the
8
+ * client never aggregates, sorts, or null→0 converts), tooltips use the
9
+ * non-HTML richText render mode, all labels/values stay text, and the series
10
+ * palette is finite, colorblind-safe, and stable by series NAME (the same
11
+ * series name gets the same color across every view of one report).
12
+ * @module @yejiming/dsh-data-agent/client/analysis-charts
13
+ */
14
+ import type { EChartsCoreOption } from 'echarts/core';
15
+ import { type AnalysisDatasetResultV1, type AnalysisViewV1 } from '../analysis.ts';
16
+ /** Colorblind-safe, finite series palette (Tableau 10 order). */
17
+ export declare const ANALYSIS_PALETTE: readonly ["#4e79a7", "#f28e2b", "#59a14f", "#e15759", "#76b7b2", "#edc948", "#b07aa1", "#9c755f"];
18
+ /** Stable color per series NAME: same name → same color in every view. */
19
+ export declare function seriesColor(name: string): string;
20
+ /** DSH token values the charts need (fallback constants for non-DOM tests). */
21
+ export interface ChartThemeTokens {
22
+ fontFamily: string;
23
+ text: string;
24
+ textSecondary: string;
25
+ border: string;
26
+ background: string;
27
+ tooltipBorder: string;
28
+ grid: string;
29
+ }
30
+ /** Read the host's DSH tokens once per render (falls back off-DOM). */
31
+ export declare function readChartThemeTokens(): ChartThemeTokens;
32
+ /** Whether the environment asks for reduced motion. */
33
+ export declare function prefersReducedMotion(): boolean;
34
+ /**
35
+ * Map one constrained view + dataset pair to a safe ECharts option. Returns an
36
+ * empty option for metric/table views (they never reach the chart component).
37
+ */
38
+ export declare function chartOptionFor(view: AnalysisViewV1, dataset: AnalysisDatasetResultV1, tokens: ChartThemeTokens, ariaLabel: string): EChartsCoreOption;
39
+ /** Short plain-text summary of one chart (the accessible text description). */
40
+ export declare function chartTextSummary(view: AnalysisViewV1, dataset: AnalysisDatasetResultV1, kindLabel: string): string;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Frozen-session decoder for render-analysis tool results (task 3.1). The
3
+ * component owns NO database state: everything derives from the frozen
4
+ * ToolCallBlock the slot owner supplies, so history replay is a pure function
5
+ * of the persisted meta and never touches the database, HTTP routes, or any
6
+ * storage domain.
7
+ *
8
+ * States:
9
+ * - running: tool/call seen, tool/result not yet;
10
+ * - error / interrupted: settled with an error outcome (interrupted carries
11
+ * the host's interrupted error code);
12
+ * - report: settled with a valid AnalysisReportV1 meta;
13
+ * - fallback: missing, malformed, string-encoded or unknown-version meta —
14
+ * degrade to the safe model content text, never guess or re-query.
15
+ * @module @yejiming/dsh-data-agent/client/analysis-view-model
16
+ */
17
+ import type { ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client';
18
+ import { type AnalysisReportV1 } from '../analysis.ts';
19
+ /** The five display states of one render-analysis call. */
20
+ export type AnalysisBlockState = 'running' | 'error' | 'interrupted' | 'report' | 'fallback';
21
+ /** Decoded view model for one frozen block. */
22
+ export interface AnalysisViewModel {
23
+ state: AnalysisBlockState;
24
+ /** Valid version-1 report (state=report only). */
25
+ report?: AnalysisReportV1;
26
+ /** Safe error text (state=error/interrupted). */
27
+ errorText?: string;
28
+ /** Safe model content text (state=fallback). */
29
+ fallbackText?: string;
30
+ }
31
+ /** Join the text blocks of a settled result into one safe plain string. */
32
+ export declare function contentText(content: readonly unknown[]): string;
33
+ /**
34
+ * Decode one frozen ToolCallBlock into the display view model. The block is
35
+ * treated as immutable input; no lookups, no queries, no throws.
36
+ */
37
+ export declare function decodeAnalysisBlock(block: ToolCallBlock): AnalysisViewModel;
38
+ /** Whether a report is the simple single-chart case deserving an inline preview. */
39
+ export declare function isSimpleChartReport(report: AnalysisReportV1): boolean;
40
+ /** Human-facing dataset/view counts for the summary line. */
41
+ export declare function reportCounts(report: AnalysisReportV1): {
42
+ datasets: number;
43
+ views: number;
44
+ };
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Data Agent browser half, plugin entry: registers the database workbench
3
- * into the composer input dock (the strip ABOVE the input bar) for
4
- * data-agent sessions, and the `data-agent` dictionaries. The old
5
- * conversation-view tab is gone — the workbench lives inside the session.
3
+ * as a compact context-row control for data-agent sessions, and the
4
+ * `data-agent` dictionaries. The workbench itself opens in one Modal.
6
5
  * Connection state lives in the server-side connection store, so layout and
7
6
  * session switches never lose it — the view only mirrors what
8
7
  * `/plugins/data-agent/status` reports.
@@ -20,7 +19,7 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
20
19
  export declare const inject: string[];
21
20
  /**
22
21
  * Client plugin body: register the data-agent dictionaries and the database
23
- * workbench into the composer input dock. The registration rides the slot
22
+ * workbench trigger into the composer card's right control region. The registration rides the slot
24
23
  * service's effect wrapper, so plugin unload removes it.
25
24
  * @param ctx - client root context.
26
25
  */
@@ -17,6 +17,13 @@ export declare const zh: {
17
17
  'form.port': string;
18
18
  'form.user': string;
19
19
  'form.password': string;
20
+ 'form.credentialMode': string;
21
+ 'form.credentialMode.password': string;
22
+ 'form.credentialMode.reference': string;
23
+ 'form.passwordRef': string;
24
+ 'form.passwordRef.hint': string;
25
+ 'credential.configured': string;
26
+ 'credential.unconfigured': string;
20
27
  'form.rememberPassword': string;
21
28
  'form.rememberPassword.hint': string;
22
29
  'form.readonly': string;
@@ -33,6 +40,15 @@ export declare const zh: {
33
40
  'state.disconnected': string;
34
41
  'state.checking': string;
35
42
  'state.reconnecting': string;
43
+ 'workbench.open.connected': string;
44
+ 'workbench.open.disconnected': string;
45
+ 'workbench.open.checking': string;
46
+ 'workbench.open.error': string;
47
+ 'composer.placeholder.disconnected': string;
48
+ 'composer.placeholder.connected': string;
49
+ 'wb.workbench.title': string;
50
+ 'wb.workbench.description': string;
51
+ 'wb.workbench.tabs': string;
36
52
  'wb.schemas': string;
37
53
  'wb.tables': string;
38
54
  'wb.columns': string;
@@ -50,6 +66,23 @@ export declare const zh: {
50
66
  'action.browse': string;
51
67
  'action.close': string;
52
68
  'error.title': string;
69
+ 'analysis.running': string;
70
+ 'analysis.failed': string;
71
+ 'analysis.interrupted': string;
72
+ 'analysis.fallback': string;
73
+ 'analysis.view': string;
74
+ 'analysis.close': string;
75
+ 'analysis.summary': string;
76
+ 'analysis.empty': string;
77
+ 'analysis.kind.metric': string;
78
+ 'analysis.kind.line': string;
79
+ 'analysis.kind.bar': string;
80
+ 'analysis.kind.pie': string;
81
+ 'analysis.kind.scatter': string;
82
+ 'analysis.kind.table': string;
83
+ 'analysis.chart.summary': string;
84
+ 'analysis.metric.null': string;
85
+ 'analysis.row.title': string;
53
86
  };
54
87
  /** The data-agent namespace key union. */
55
88
  export type DataAgentKey = keyof typeof zh;
@@ -69,6 +102,13 @@ export declare const en: {
69
102
  'form.port': string;
70
103
  'form.user': string;
71
104
  'form.password': string;
105
+ 'form.credentialMode': string;
106
+ 'form.credentialMode.password': string;
107
+ 'form.credentialMode.reference': string;
108
+ 'form.passwordRef': string;
109
+ 'form.passwordRef.hint': string;
110
+ 'credential.configured': string;
111
+ 'credential.unconfigured': string;
72
112
  'form.rememberPassword': string;
73
113
  'form.rememberPassword.hint': string;
74
114
  'form.readonly': string;
@@ -85,6 +125,15 @@ export declare const en: {
85
125
  'state.disconnected': string;
86
126
  'state.checking': string;
87
127
  'state.reconnecting': string;
128
+ 'workbench.open.connected': string;
129
+ 'workbench.open.disconnected': string;
130
+ 'workbench.open.checking': string;
131
+ 'workbench.open.error': string;
132
+ 'composer.placeholder.disconnected': string;
133
+ 'composer.placeholder.connected': string;
134
+ 'wb.workbench.title': string;
135
+ 'wb.workbench.description': string;
136
+ 'wb.workbench.tabs': string;
88
137
  'wb.schemas': string;
89
138
  'wb.tables': string;
90
139
  'wb.columns': string;
@@ -102,4 +151,21 @@ export declare const en: {
102
151
  'action.browse': string;
103
152
  'action.close': string;
104
153
  'error.title': string;
154
+ 'analysis.running': string;
155
+ 'analysis.failed': string;
156
+ 'analysis.interrupted': string;
157
+ 'analysis.fallback': string;
158
+ 'analysis.view': string;
159
+ 'analysis.close': string;
160
+ 'analysis.summary': string;
161
+ 'analysis.empty': string;
162
+ 'analysis.kind.metric': string;
163
+ 'analysis.kind.line': string;
164
+ 'analysis.kind.bar': string;
165
+ 'analysis.kind.pie': string;
166
+ 'analysis.kind.scatter': string;
167
+ 'analysis.kind.table': string;
168
+ 'analysis.chart.summary': string;
169
+ 'analysis.metric.null': string;
170
+ 'analysis.row.title': string;
105
171
  };
@@ -2,7 +2,7 @@
2
2
  * Connection-config persistence for the database workbench. The most recent
3
3
  * successful connection (type/host/port/user/database/password) is kept in
4
4
  * localStorage under one key so remounts and restarts can restore the form
5
- * and auto-reconnect (the server-side connection store stays in-memory).
5
+ * and auto-reconnect (the server persists only non-secret profiles/bindings).
6
6
  *
7
7
  * Security note: the password is persisted in PLAIN TEXT by explicit user
8
8
  * decision (local single-user scenario) — see README 安全说明. The storage
@@ -21,8 +21,13 @@ export interface SavedConnection {
21
21
  database: string;
22
22
  /** Present only when the user explicitly opted in to persist the password. */
23
23
  password?: string;
24
+ /** Non-secret credential reference; mutually exclusive with `password`. */
25
+ passwordRef?: string;
26
+ /** Explicit form mode; absent legacy records infer it from passwordRef. */
27
+ credentialMode?: 'password' | 'reference';
24
28
  /** Opt-in flag; when true, {@link saveConnection} may write `password`. */
25
29
  persistPassword?: boolean;
30
+ readonly?: boolean;
26
31
  /** Diagnostic timestamp of the save. */
27
32
  savedAt: string;
28
33
  }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Cross-platform database CLI discovery.
3
+ *
4
+ * The subprocess provider remains the authority for executable validation.
5
+ * This module only builds a bounded, platform-aware PATH fallback when the
6
+ * provider cannot resolve the configured/default bare command from its
7
+ * current execution environment. No shell, registry, or recursive scan is
8
+ * involved, and the exact discovery environment is returned for spawn.
9
+ * @module @yejiming/dsh-data-agent/client-discovery
10
+ */
11
+ import type { ClientConfig } from './clients.ts';
12
+ import type { DatabaseType } from './connections.ts';
13
+ /** Host facts are injectable so all supported platforms can be tested on one CI host. */
14
+ export interface ClientDiscoverySystem {
15
+ platform: NodeJS.Platform;
16
+ env: Readonly<Record<string, string | undefined>>;
17
+ homeDir: string;
18
+ cwd: string;
19
+ readDirectory(directory: string): Promise<readonly string[]>;
20
+ }
21
+ /** DSH subprocess executable resolver face. */
22
+ export type ExecutableResolver = (command: string, env?: Readonly<Record<string, string>>, signal?: AbortSignal) => Promise<string>;
23
+ /** A resolved executable plus the environment that must also be used for spawn. */
24
+ export interface ClientExecutableResolution {
25
+ executable: string;
26
+ env: Readonly<Record<string, string>>;
27
+ searchedDirectories: readonly string[];
28
+ }
29
+ /** Input for one database client resolution attempt. */
30
+ export interface ResolveClientExecutableOptions {
31
+ type: DatabaseType;
32
+ command: string;
33
+ config?: ClientConfig;
34
+ env: Readonly<Record<string, string>>;
35
+ signal: AbortSignal;
36
+ resolveExecutable: ExecutableResolver;
37
+ system?: ClientDiscoverySystem;
38
+ }
39
+ /** Build ordered fallback directories without recursively scanning the host. */
40
+ export declare function buildClientSearchDirectories(type: DatabaseType, config: ClientConfig | undefined, signal: AbortSignal, system?: ClientDiscoverySystem): Promise<string[]>;
41
+ /**
42
+ * Resolve one configured/default client. Current PATH (or an explicit path)
43
+ * always wins. Only a missing bare command activates bounded PATH discovery.
44
+ */
45
+ export declare function resolveClientExecutable(options: ResolveClientExecutableOptions): Promise<ClientExecutableResolution>;
@@ -12,6 +12,7 @@
12
12
  * @module @yejiming/dsh-data-agent/clients
13
13
  */
14
14
  import type { DatabaseConnection, DatabaseType } from './connections.ts';
15
+ import z from 'schemastery';
15
16
  import { assertSingleStatement, hasTopLevelKeyword, stripTrailingTerminator } from './sql.ts';
16
17
  export { assertSingleStatement, hasTopLevelKeyword, stripTrailingTerminator };
17
18
  /**
@@ -44,25 +45,31 @@ export declare function sanitizeIdentifier(type: DatabaseType, identifier: strin
44
45
  /** One deployment override for a database type's CLI client. */
45
46
  export interface ClientConfig {
46
47
  /** Executable name (resolved through PATH) or absolute path. */
47
- command: string;
48
+ command?: string;
48
49
  /** Extra flag arguments prepended before the built-in flags. */
49
50
  args?: readonly string[];
51
+ /** Absolute directories searched after the current subprocess PATH. */
52
+ searchPaths?: readonly string[];
50
53
  }
51
54
  /** Loader schema for one client override (all fields optional at input). */
52
- export declare const clientConfigSchema: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
53
- command: import("@deepseek-ai/schemastery").default<string, string>;
54
- args: import("@deepseek-ai/schemastery").default<string[], string[]>;
55
+ export declare const clientConfigSchema: z<Schemastery.ObjectS<{
56
+ command: z<string, string>;
57
+ args: z<string[], string[]>;
58
+ searchPaths: z<string[], string[]>;
55
59
  }>, Schemastery.ObjectT<{
56
- command: import("@deepseek-ai/schemastery").default<string, string>;
57
- args: import("@deepseek-ai/schemastery").default<string[], string[]>;
60
+ command: z<string, string>;
61
+ args: z<string[], string[]>;
62
+ searchPaths: z<string[], string[]>;
58
63
  }>>;
59
64
  /** Loader schema for the whole `clients` config object (any type key). */
60
- export declare const clientsSchema: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
65
+ export declare const clientsSchema: z<import("cosmokit").Dict<{
61
66
  command?: string | null | undefined;
62
67
  args?: string[] | null | undefined;
63
- } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
64
- command: import("@deepseek-ai/schemastery").default<string, string>;
65
- args: import("@deepseek-ai/schemastery").default<string[], string[]>;
68
+ searchPaths?: string[] | null | undefined;
69
+ } & import("@deepseek-ai/cosmokit").Dict, string>, import("cosmokit").Dict<Schemastery.ObjectT<{
70
+ command: z<string, string>;
71
+ args: z<string[], string[]>;
72
+ searchPaths: z<string[], string[]>;
66
73
  }>, string>>;
67
74
  /**
68
75
  * A fully constructed client invocation: argv (command + flags, no SQL),
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Agent-scoped `/database` human command. The preset mounts this entry below
3
+ * the agent context, so the command registry scopes it to data-agent without
4
+ * importing dsh-tui, React, or Ink.
5
+ * @module @yejiming/dsh-data-agent/command
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands';
9
+ import { type ConnectionFormDraft, type ConnectionSummary, type DatabaseConnectionInput } from './connections.ts';
10
+ export declare const name = "data-agent-database-command";
11
+ export declare const inject: string[];
12
+ export declare const DATABASE_COMMAND_USAGE: string;
13
+ type DatabaseAction = {
14
+ kind: 'status';
15
+ } | {
16
+ kind: 'connect';
17
+ input?: DatabaseConnectionInput;
18
+ } | {
19
+ kind: 'test';
20
+ } | {
21
+ kind: 'disconnect';
22
+ };
23
+ export declare const DATA_AGENT_TOOL_NAMES: readonly ["str_replace_editor", "sql-query", "sql-write", "sql-cmd"];
24
+ export interface DatabaseCommandInteraction {
25
+ isTuiFormAvailable(): boolean;
26
+ collectTuiConnection(signal: AbortSignal, options: {
27
+ initialDraft?: ConnectionFormDraft;
28
+ persistDraft(draft: ConnectionFormDraft): Promise<void>;
29
+ }): Promise<DatabaseConnectionInput | undefined>;
30
+ }
31
+ /** Register the command in the calling preset/agent scope. */
32
+ export declare function apply(ctx: Context): void;
33
+ /** Public for focused command tests and alternate command adapters. */
34
+ export declare function executeDatabaseCommand(ctx: Context, invocation: CommandInvocation, interaction?: DatabaseCommandInteraction): Promise<CommandResult>;
35
+ /** Parse one command's raw input without ever accepting a plaintext password. */
36
+ export declare function parseDatabaseAction(rawInput: string): DatabaseAction;
37
+ /** Non-interactive `connect` argument grammar. */
38
+ export declare function parseConnectArguments(tokens: readonly string[]): DatabaseConnectionInput;
39
+ /** Render a public summary; no password-bearing field exists in the type. */
40
+ export declare function formatConnectionStatus(summary: ConnectionSummary | undefined): string;
41
+ export {};
@@ -1,74 +1,149 @@
1
1
  /**
2
- * The `dataAgentConnections` connection store: one in-memory connection per
3
- * session id, host-plane provided by the server half (`src/index.ts`) and
4
- * consumed by the sqlcmd tool half (`src/tool.ts`) inside the data-agent
5
- * preset.
2
+ * Surface-independent database connection service shared by Web routes,
3
+ * agent tools, and human commands.
6
4
  *
7
- * Security contract:
8
- * - passwords live in memory only never written to session logs, settings,
9
- * config, or disk;
10
- * - `get()` returns a password-stripped COPY, so UI/status consumers never
11
- * see the secret;
12
- * - `getWithSecret()` is the process-internal read used ONLY by the sqlcmd
13
- * tool half (same package), which forwards the password to the database
14
- * client through an environment variable.
15
- *
16
- * Wildcard: a connection stored under the key `'*'` acts as the fallback for
17
- * every session without its own entry (a deployment seeding a default
18
- * database, or a headless/keyless run). Config-seeded entries cannot carry
19
- * passwords, so the wildcard is always password-free.
5
+ * Runtime records may contain one temporary Web password. Durable records
6
+ * never do: they contain a non-secret profile plus an optional credential
7
+ * reference that is resolved again at the start of every database operation.
20
8
  * @module @yejiming/dsh-data-agent/connections
21
9
  */
22
- /** Key of the wildcard (default) connection applied to any session without its own. */
10
+ import type { Context } from '@deepseek-ai/cordis';
11
+ import { type ClientConfig, type ColumnInfo } from './clients.ts';
12
+ import { type QueryResult } from './query.ts';
13
+ /** Key of the wildcard connection applied to sessions without an exact entry. */
23
14
  export declare const WILDCARD_SESSION = "*";
24
15
  /** Supported database client kinds. */
25
16
  export type DatabaseType = 'mysql' | 'postgres' | 'sqlite' | 'oracle' | 'hive' | 'impala';
26
- /**
27
- * One session's database connection. `host`/`port`/`user` are empty for
28
- * SQLite, whose `database` is a file path (resolved to absolute at connect).
29
- * `tables` is the connectivity check's table listing, retained so the
30
- * browser half can restore it after a tab switch without re-querying.
31
- */
32
- export interface DatabaseConnection {
17
+ /** Safe credential facts returned to UI/command surfaces. */
18
+ export interface CredentialSummary {
19
+ configured: boolean;
20
+ source?: string;
21
+ }
22
+ /** One connect request accepted by every surface. */
23
+ export interface DatabaseConnectionInput {
33
24
  type: DatabaseType;
34
25
  host?: string;
35
26
  port?: number;
36
27
  user?: string;
37
28
  database: string;
38
- /** In-memory only; never exposed through {@link DataAgentConnections.get}. */
29
+ /** Temporary Web-only secret, retained in this process only. */
39
30
  password?: string;
40
- /** Optional per-session read-only guard (defaults to the plugin's `readonly`). */
31
+ /** Non-secret DSH credential reference, mutually exclusive with password. */
32
+ passwordRef?: string;
41
33
  readonly?: boolean;
34
+ /** Optional stable durable profile id. */
35
+ profileId?: string;
36
+ /** Optional human-readable profile label. */
37
+ name?: string;
38
+ }
39
+ /** Runtime connection. `tables` and temporary `password` are never durable. */
40
+ export interface DatabaseConnection extends DatabaseConnectionInput {
42
41
  tables?: string[];
43
42
  }
44
- /** Password-free view of one connection (the wire/UI face). */
43
+ /** Password-free public connection view. */
45
44
  export interface ConnectionSummary {
46
45
  type: DatabaseType;
47
46
  host?: string;
48
47
  port?: number;
49
48
  user?: string;
50
49
  database: string;
51
- /** Present only when the connection explicitly set it. */
50
+ passwordRef?: string;
52
51
  readonly?: boolean;
52
+ profileId?: string;
53
+ name?: string;
53
54
  tables?: string[];
55
+ credential?: CredentialSummary;
56
+ }
57
+ /** Value stored in the `profiles` domain table. Never add secrets here. */
58
+ export interface PersistedConnectionProfile {
59
+ name?: string;
60
+ type: DatabaseType;
61
+ host?: string;
62
+ port?: number;
63
+ user?: string;
64
+ database: string;
65
+ readonly?: boolean;
66
+ passwordRef?: string;
67
+ updatedAt: string;
68
+ }
69
+ /** Value stored in the `bindings` domain table. */
70
+ export interface SessionConnectionBinding {
71
+ profileId: string;
72
+ updatedAt: string;
73
+ }
74
+ /** Non-secret values restored when a session reopens an interactive form. */
75
+ export interface ConnectionFormDraft {
76
+ type: DatabaseType;
77
+ host: string;
78
+ port: string;
79
+ user: string;
80
+ database: string;
81
+ readonly: boolean;
82
+ }
83
+ /** Durable draft record. Passwords and credential references are forbidden. */
84
+ export interface PersistedConnectionFormDraft extends ConnectionFormDraft {
85
+ updatedAt: string;
86
+ }
87
+ /** Minimal durable seam; backed by a DSH storage domain in production. */
88
+ export interface ConnectionPersistence {
89
+ getProfile(profileId: string): PersistedConnectionProfile | undefined;
90
+ putProfile(profileId: string, profile: PersistedConnectionProfile): Promise<void>;
91
+ deleteProfile(profileId: string): Promise<boolean>;
92
+ getBinding(sessionId: string): SessionConnectionBinding | undefined;
93
+ putBinding(sessionId: string, binding: SessionConnectionBinding): Promise<void>;
94
+ deleteBinding(sessionId: string): Promise<boolean>;
95
+ getDraft?(sessionId: string): PersistedConnectionFormDraft | undefined;
96
+ putDraft?(sessionId: string, draft: PersistedConnectionFormDraft): Promise<void>;
97
+ }
98
+ /** Shared service configuration supplied by the host plugin. */
99
+ export interface ConnectionServiceOptions {
100
+ connectTimeoutMs: number;
101
+ queryTimeoutMs: number;
102
+ maxResultChars: number;
103
+ maxQueryChars?: number;
104
+ introspectMaxTables: number;
105
+ readonly: boolean;
106
+ clients: Partial<Record<string, ClientConfig>>;
107
+ cwd?: string;
108
+ }
109
+ export interface ConnectResult {
110
+ tables: string[];
111
+ summary: ConnectionSummary;
54
112
  }
55
- /** The host-plane connection store service (`ctx.dataAgentConnections`). */
113
+ /** Host-plane service (`ctx.dataAgentConnections`). */
56
114
  export interface DataAgentConnections {
57
- /** Save (replace) one session's connection, password included. */
115
+ /** Compatibility setter for config seeds/tests; does not persist. */
58
116
  set(sessionId: string, connection: DatabaseConnection): void;
59
- /** Read one session's connection WITHOUT the password (a fresh copy). */
117
+ /** Password-free synchronous status (runtime/binding/wildcard resolution). */
60
118
  get(sessionId: string): ConnectionSummary | undefined;
61
- /**
62
- * Read one session's connection INCLUDING the password. Process-internal
63
- * only (the sqlcmd tool half); never hand this to a wire/UI consumer.
64
- */
119
+ /** Compatibility internal read; credential references remain unresolved. */
65
120
  getWithSecret(sessionId: string): DatabaseConnection | undefined;
66
- /** Whether a session currently has a connection. */
67
121
  has(sessionId: string): boolean;
68
- /** Drop one session's connection. */
122
+ /** Compatibility runtime-only clear. Use disconnect() for durable bindings. */
69
123
  clear(sessionId: string): void;
124
+ /** Restore the latest non-secret interactive form values for this session. */
125
+ getFormDraft(sessionId: string): ConnectionFormDraft | undefined;
126
+ /** Save non-secret form values; the implementation never accepts a password. */
127
+ saveFormDraft(sessionId: string, draft: ConnectionFormDraft): Promise<void>;
128
+ status(sessionId: string): Promise<ConnectionSummary | undefined>;
129
+ connect(sessionId: string, input: DatabaseConnectionInput, signal: AbortSignal): Promise<ConnectResult>;
130
+ disconnect(sessionId: string): Promise<void>;
131
+ test(sessionId: string, signal: AbortSignal): Promise<ConnectResult>;
132
+ resolveForExecution(sessionId: string): Promise<DatabaseConnection>;
133
+ listSchemas(sessionId: string, signal: AbortSignal): Promise<string[]>;
134
+ listTables(sessionId: string, schema: string | undefined, signal: AbortSignal): Promise<string[]>;
135
+ describe(sessionId: string, schema: string | undefined, table: string, signal: AbortSignal): Promise<ColumnInfo[]>;
136
+ query(sessionId: string, sql: string, signal: AbortSignal): Promise<QueryResult>;
70
137
  }
71
- /** Build the password-stripped copy of one connection. */
138
+ /** Build a password-stripped copy of one connection. */
72
139
  export declare function summarize(connection: DatabaseConnection): ConnectionSummary;
73
- /** Create a fresh connection store (per-process singleton, one per plugin instance). */
140
+ /** Replace every occurrence of a resolved secret before crossing a public seam. */
141
+ export declare function redactSecretText(text: string, secrets: readonly (string | undefined)[]): string;
142
+ /** Redact a client result without mutating the runner-owned object. */
143
+ export declare function redactQueryResult(result: QueryResult, connection: DatabaseConnection): QueryResult;
144
+ /** Validate/normalize a shared connect input before any I/O. */
145
+ export declare function normalizeConnectionInput(input: DatabaseConnectionInput, cwd?: string): DatabaseConnection;
146
+ /** Create the surface-independent service. */
147
+ export declare function createConnectionService(ctx?: Context, options?: ConnectionServiceOptions, persistence?: ConnectionPersistence): DataAgentConnections;
148
+ /** Backward-compatible in-memory store factory used by embedders/tests. */
74
149
  export declare function createConnectionStore(): DataAgentConnections;
@@ -14,6 +14,8 @@ export declare const DEFAULT_INTROSPECT_MAX_TABLES = 500;
14
14
  export declare const DEFAULT_QUERY_TIMEOUT_MS = 30000;
15
15
  /** In-memory cap on database-tool captured output (stdout and stderr each). */
16
16
  export declare const DEFAULT_MAX_RESULT_CHARS = 20000;
17
+ /** Maximum structured rows returned by one database read tool call. */
18
+ export declare const DEFAULT_MAX_ROWS = 100;
17
19
  /** Cap on one /query SQL text length (abuse guard; the wire body stays small). */
18
20
  export declare const DEFAULT_MAX_QUERY_CHARS = 65536;
19
21
  /** Grace period for the subprocess terminate escalation. */