@yejiming/dsh-data-agent 0.0.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.
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Pure CLI-client template construction for the supported database types.
3
+ * Everything here is a function of (type, connection, optional overrides) —
4
+ * no process, no I/O — so the injection-safety surface is unit-testable:
5
+ * argv stays an array (never shell-interpreted), the SQL itself always
6
+ * travels on stdin, and passwords only ever appear in the environment
7
+ * entries (`MYSQL_PWD` / `PGPASSWORD`) or in a stdin connect prefix
8
+ * (Oracle `connect`, Hive `!connect`) — never in argv, logs, or returns.
9
+ *
10
+ * Metadata (schemas / tables / describe) queries and their per-type output
11
+ * parsers live here too, so the /schemas /tables /describe routes stay thin.
12
+ * @module @yejiming/dsh-data-agent/clients
13
+ */
14
+ import type { DatabaseConnection, DatabaseType } from './connections.ts';
15
+ /** One deployment override for a database type's CLI client. */
16
+ export interface ClientConfig {
17
+ /** Executable name (resolved through PATH) or absolute path. */
18
+ command: string;
19
+ /** Extra flag arguments prepended before the built-in flags. */
20
+ args?: readonly string[];
21
+ }
22
+ /** Loader schema for one client override (all fields optional at input). */
23
+ export declare const clientConfigSchema: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
24
+ command: import("@deepseek-ai/schemastery").default<string, string>;
25
+ args: import("@deepseek-ai/schemastery").default<string[], string[]>;
26
+ }>, Schemastery.ObjectT<{
27
+ command: import("@deepseek-ai/schemastery").default<string, string>;
28
+ args: import("@deepseek-ai/schemastery").default<string[], string[]>;
29
+ }>>;
30
+ /** Loader schema for the whole `clients` config object (any type key). */
31
+ export declare const clientsSchema: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
32
+ command?: string | null | undefined;
33
+ args?: string[] | null | undefined;
34
+ } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
35
+ command: import("@deepseek-ai/schemastery").default<string, string>;
36
+ args: import("@deepseek-ai/schemastery").default<string[], string[]>;
37
+ }>, string>>;
38
+ /**
39
+ * A fully constructed client invocation: argv (command + flags, no SQL),
40
+ * the credential env entries, and the stdin prefix (Oracle/Hive connect
41
+ * lines) the runner writes before the SQL text.
42
+ */
43
+ export interface ClientTemplate {
44
+ /** Executable to resolve through {@link SubprocessService.resolveExecutable}. */
45
+ command: string;
46
+ /** Flag arguments only; the SQL text is written to stdin by the runner. */
47
+ args: readonly string[];
48
+ /** Credential env entries (e.g. `{ MYSQL_PWD }`), never argv. */
49
+ env: Readonly<Record<string, string>>;
50
+ /** stdin text written BEFORE the SQL (Oracle SET/connect, Hive !connect); '' otherwise. */
51
+ stdinPrefix: string;
52
+ }
53
+ /**
54
+ * Build one client invocation for a query execution (plain output). Flags
55
+ * come BEFORE the connection arguments everywhere: sqlite3 takes
56
+ * `[options] <database>`, and putting flags first is harmless for the others.
57
+ */
58
+ export declare function buildClientTemplate(type: DatabaseType, connection: DatabaseConnection, override?: ClientConfig): ClientTemplate;
59
+ /** Build one client invocation for metadata runs (machine-readable flags). */
60
+ export declare function buildIntrospectTemplate(type: DatabaseType, connection: DatabaseConnection, override?: ClientConfig): ClientTemplate;
61
+ /**
62
+ * The table-listing SQL per type, run at /connect time to verify
63
+ * connectivity: the connected database's own tables (mysql uses the
64
+ * connection's database as the schema; postgres lists `public`; oracle lists
65
+ * the connected user's tables; hive/impala list the default database).
66
+ */
67
+ export declare function tableListingSql(type: DatabaseType, connection?: DatabaseConnection): string;
68
+ /**
69
+ * Metadata query per kind × type. `schema`/`table` are identifier whitelist
70
+ * validated by the caller (`[A-Za-z0-9_$#.-]`) before they reach here.
71
+ */
72
+ export declare function metadataQuery(kind: 'schemas' | 'tables' | 'describe', type: DatabaseType, schema?: string, table?: string): string;
73
+ /**
74
+ * Split one type's machine-readable listing output into trimmed lines.
75
+ * Header lines are stripped per type: mysql `--batch` prints a header row
76
+ * (skip 1); postgres `-t`, sqlite `-noheader`, oracle `SET HEADING OFF`,
77
+ * hive/impala batch modes print none (skip 0).
78
+ */
79
+ export declare function parseListing(type: DatabaseType, stdout: string): string[];
80
+ /** Parse one type's table-listing output (the /connect connectivity check). */
81
+ export declare function parseTableListing(type: DatabaseType, stdout: string): string[];
82
+ /** One described column (nullable absent when the client reports none). */
83
+ export interface ColumnInfo {
84
+ name: string;
85
+ type: string;
86
+ nullable?: boolean;
87
+ }
88
+ /**
89
+ * Parse one type's describe output into columns. Formats:
90
+ * - mysql `--batch`: `Field\tType\tNull\tKey\t...` (skip header);
91
+ * - postgres `-t -A`: `name|type|is_nullable`;
92
+ * - sqlite `-noheader -list`: `cid|name|type|notnull|dflt|pk` (name is part 1);
93
+ * - oracle (`SET COLSEP '|'`, heading off): `NAME|TYPE|NULLABLE`;
94
+ * - hive/impala batch: `name\ttype\tcomment`.
95
+ */
96
+ export declare function parseColumns(type: DatabaseType, stdout: string): ColumnInfo[];
@@ -0,0 +1,70 @@
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.
6
+ *
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.
20
+ * @module @yejiming/dsh-data-agent/connections
21
+ */
22
+ /** Key of the wildcard (default) connection applied to any session without its own. */
23
+ export declare const WILDCARD_SESSION = "*";
24
+ /** Supported database client kinds. */
25
+ 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 {
33
+ type: DatabaseType;
34
+ host?: string;
35
+ port?: number;
36
+ user?: string;
37
+ database: string;
38
+ /** In-memory only; never exposed through {@link DataAgentConnections.get}. */
39
+ password?: string;
40
+ tables?: string[];
41
+ }
42
+ /** Password-free view of one connection (the wire/UI face). */
43
+ export interface ConnectionSummary {
44
+ type: DatabaseType;
45
+ host?: string;
46
+ port?: number;
47
+ user?: string;
48
+ database: string;
49
+ tables?: string[];
50
+ }
51
+ /** The host-plane connection store service (`ctx.dataAgentConnections`). */
52
+ export interface DataAgentConnections {
53
+ /** Save (replace) one session's connection, password included. */
54
+ set(sessionId: string, connection: DatabaseConnection): void;
55
+ /** Read one session's connection WITHOUT the password (a fresh copy). */
56
+ get(sessionId: string): ConnectionSummary | undefined;
57
+ /**
58
+ * Read one session's connection INCLUDING the password. Process-internal
59
+ * only (the sqlcmd tool half); never hand this to a wire/UI consumer.
60
+ */
61
+ getWithSecret(sessionId: string): DatabaseConnection | undefined;
62
+ /** Whether a session currently has a connection. */
63
+ has(sessionId: string): boolean;
64
+ /** Drop one session's connection. */
65
+ clear(sessionId: string): void;
66
+ }
67
+ /** Build the password-stripped copy of one connection. */
68
+ export declare function summarize(connection: DatabaseConnection): ConnectionSummary;
69
+ /** Create a fresh connection store (per-process singleton, one per plugin instance). */
70
+ export declare function createConnectionStore(): DataAgentConnections;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Package-wide defaults shared by the server half (`src/index.ts`) and the
3
+ * sqlcmd tool half (`src/tool.ts`). Loader schemas carry these as their
4
+ * defaults so a deployment may override every one of them in cordis.yml.
5
+ * @module @yejiming/dsh-data-agent/defaults
6
+ */
7
+ /** Preset directory name installed into `$DSH_HOME/.agent-presets/`. */
8
+ export declare const DEFAULT_PRESET_ID = "data-agent";
9
+ /** End-to-end deadline for one `/connect` connectivity check, milliseconds. */
10
+ export declare const DEFAULT_CONNECT_TIMEOUT_MS = 10000;
11
+ /** Cap on the table list returned by `/connect` and `/status`. */
12
+ export declare const DEFAULT_INTROSPECT_MAX_TABLES = 500;
13
+ /** End-to-end deadline for one sqlcmd query, milliseconds. */
14
+ export declare const DEFAULT_QUERY_TIMEOUT_MS = 30000;
15
+ /** In-memory cap on sqlcmd captured output (stdout and stderr each). */
16
+ export declare const DEFAULT_MAX_RESULT_CHARS = 20000;
17
+ /** Cap on one /query SQL text length (abuse guard; the wire body stays small). */
18
+ export declare const DEFAULT_MAX_QUERY_CHARS = 65536;
19
+ /** Grace period for the subprocess terminate escalation. */
20
+ export declare const DEFAULT_GRACE_MS = 5000;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Data Agent server half for the dsh web GUI. The host row provides the
3
+ * `dataAgentConnections` service (session-scoped in-memory store; passwords
4
+ * never leave memory), seeds config connections (`connections`, `'*'` =
5
+ * wildcard default), and installs the `data-agent` agent preset into
6
+ * `$DSH_HOME/.agent-presets/` (idempotent, never overwrites a user-edited
7
+ * directory).
8
+ *
9
+ * The HTTP routes live in the separate `./routes` entry
10
+ * (`@yejiming/dsh-data-agent/routes`, cordis row `data-agent-routes`) so
11
+ * this row keeps working in headless profiles without a webserver; the sqlcmd
12
+ * tool itself lives in the `./tool` entry and is mounted only by the
13
+ * data-agent preset.
14
+ * @module @yejiming/dsh-data-agent
15
+ */
16
+ import type { Context } from '@deepseek-ai/cordis';
17
+ /** The `dataAgentConnections` service face on the cordis context. */
18
+ declare module '@deepseek-ai/cordis' {
19
+ interface Context {
20
+ dataAgentConnections: DataAgentConnections;
21
+ }
22
+ }
23
+ import { type DataAgentConnections, type DatabaseType } from './connections.ts';
24
+ import { type ClientConfig } from './clients.ts';
25
+ /** Cordis plugin name (diagnostics only). */
26
+ export declare const name = "data-agent";
27
+ /** Services required before the store can serve. */
28
+ export declare const inject: string[];
29
+ /** Deployment overrides for one database type's CLI client. */
30
+ export interface ClientsConfig {
31
+ mysql?: ClientConfig;
32
+ postgres?: ClientConfig;
33
+ sqlite?: ClientConfig;
34
+ }
35
+ /**
36
+ * One config-seeded connection. Deliberately password-free: passwords are a
37
+ * memory-only / connect-time value, so only the /connect route may carry one.
38
+ * The key `'*'` seeds the wildcard default used by any session without its
39
+ * own connection (headless/keyless runs, deployments pinning one database).
40
+ */
41
+ export interface SeededConnectionConfig {
42
+ type: DatabaseType;
43
+ host?: string;
44
+ port?: number;
45
+ user?: string;
46
+ database: string;
47
+ }
48
+ /** Required plugin configuration (loader schema with deployment defaults). */
49
+ export interface Config {
50
+ /** Preset directory name installed under `$DSH_HOME/.agent-presets/`. */
51
+ presetId: string;
52
+ /** Whether to self-install the preset on startup (idempotent). */
53
+ installPreset: boolean;
54
+ /** Deadline for one /connect connectivity check, milliseconds. */
55
+ connectTimeoutMs: number;
56
+ /** Cap on the table list returned by /connect and /status. */
57
+ introspectMaxTables: number;
58
+ /** Deadline for one sqlcmd query, milliseconds. */
59
+ queryTimeoutMs: number;
60
+ /** In-memory cap on sqlcmd captured output. */
61
+ maxResultChars: number;
62
+ /** CLI client overrides keyed by database type. */
63
+ clients: ClientsConfig;
64
+ /** Config-seeded connections keyed by session id (`'*'` = wildcard default). */
65
+ connections: Record<string, SeededConnectionConfig>;
66
+ }
67
+ /** Loader schema with deployment defaults (no library defaults). */
68
+ export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
69
+ presetId: import("@deepseek-ai/schemastery").default<string, string>;
70
+ installPreset: import("@deepseek-ai/schemastery").default<boolean, boolean>;
71
+ connectTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
72
+ introspectMaxTables: import("@deepseek-ai/schemastery").default<number, number>;
73
+ queryTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
74
+ maxResultChars: import("@deepseek-ai/schemastery").default<number, number>;
75
+ clients: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
76
+ command?: string | null | undefined;
77
+ args?: string[] | null | undefined;
78
+ } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
79
+ command: import("@deepseek-ai/schemastery").default<string, string>;
80
+ args: import("@deepseek-ai/schemastery").default<string[], string[]>;
81
+ }>, string>>;
82
+ connections: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
83
+ type?: "mysql" | "postgres" | "sqlite" | "oracle" | "hive" | "impala" | null | undefined;
84
+ host?: string | null | undefined;
85
+ port?: number | null | undefined;
86
+ user?: string | null | undefined;
87
+ database?: string | null | undefined;
88
+ } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
89
+ type: import("@deepseek-ai/schemastery").default<"mysql" | "postgres" | "sqlite" | "oracle" | "hive" | "impala", "mysql" | "postgres" | "sqlite" | "oracle" | "hive" | "impala">;
90
+ host: import("@deepseek-ai/schemastery").default<string, string>;
91
+ port: import("@deepseek-ai/schemastery").default<number, number>;
92
+ user: import("@deepseek-ai/schemastery").default<string, string>;
93
+ database: import("@deepseek-ai/schemastery").default<string, string>;
94
+ }>, string>>;
95
+ }>, Schemastery.ObjectT<{
96
+ presetId: import("@deepseek-ai/schemastery").default<string, string>;
97
+ installPreset: import("@deepseek-ai/schemastery").default<boolean, boolean>;
98
+ connectTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
99
+ introspectMaxTables: import("@deepseek-ai/schemastery").default<number, number>;
100
+ queryTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
101
+ maxResultChars: import("@deepseek-ai/schemastery").default<number, number>;
102
+ clients: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
103
+ command?: string | null | undefined;
104
+ args?: string[] | null | undefined;
105
+ } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
106
+ command: import("@deepseek-ai/schemastery").default<string, string>;
107
+ args: import("@deepseek-ai/schemastery").default<string[], string[]>;
108
+ }>, string>>;
109
+ connections: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
110
+ type?: "mysql" | "postgres" | "sqlite" | "oracle" | "hive" | "impala" | null | undefined;
111
+ host?: string | null | undefined;
112
+ port?: number | null | undefined;
113
+ user?: string | null | undefined;
114
+ database?: string | null | undefined;
115
+ } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
116
+ type: import("@deepseek-ai/schemastery").default<"mysql" | "postgres" | "sqlite" | "oracle" | "hive" | "impala", "mysql" | "postgres" | "sqlite" | "oracle" | "hive" | "impala">;
117
+ host: import("@deepseek-ai/schemastery").default<string, string>;
118
+ port: import("@deepseek-ai/schemastery").default<number, number>;
119
+ user: import("@deepseek-ai/schemastery").default<string, string>;
120
+ database: import("@deepseek-ai/schemastery").default<string, string>;
121
+ }>, string>>;
122
+ }>>;
123
+ /**
124
+ * Resolve the harness home the same way `@deepseek-ai/dsh-paths` does:
125
+ * `$DSH_HOME` (non-blank) else `~/.dsh`, normalized absolute.
126
+ */
127
+ export declare function resolveDshHome(env?: Record<string, string | undefined>): string;
128
+ /**
129
+ * Install the packaged `preset/data-agent/` directory into
130
+ * `$DSH_HOME/.agent-presets/<presetId>/`. Idempotent: an existing target
131
+ * directory is left untouched (user edits survive); `installPreset: false`
132
+ * never calls this. Best-effort — a failure logs a warning with manual
133
+ * install instructions instead of failing the boot.
134
+ */
135
+ export declare function installPreset(ctx: Context, presetId: string): Promise<void>;
136
+ /**
137
+ * Mount the data-agent host row: connection store, config-seeded
138
+ * connections, and preset self-install. HTTP routes are the sibling
139
+ * `data-agent-routes` row (`./routes`).
140
+ * @param ctx - host cordis context.
141
+ * @param config - validated loader configuration.
142
+ */
143
+ export declare function apply(ctx: Context, config: Config): void;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Package-owned invariant companion for `@yejiming/dsh-data-agent`.
3
+ * @module @yejiming/dsh-data-agent/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "data-agent-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The shared client-process runner used by both halves: the /connect
3
+ * connectivity check (server half) and the sqlcmd tool (tool half). All
4
+ * execution goes through `ctx.subprocess` — no shell layer, argv arrays only,
5
+ * SQL on stdin, credentials in env entries — with a caller-owned timeout
6
+ * (AbortController → process-tree terminate escalation) and bounded captured
7
+ * output.
8
+ * @module @yejiming/dsh-data-agent/query
9
+ */
10
+ import type { Context } from '@deepseek-ai/cordis';
11
+ import type { DatabaseConnection, DatabaseType } from './connections.ts';
12
+ import { type ClientConfig } from './clients.ts';
13
+ /** One bounded captured-output read (the tail when truncated). */
14
+ export interface CapturedOutput {
15
+ text: string;
16
+ truncated: boolean;
17
+ }
18
+ /** The canonical sqlcmd / connectivity-check result. */
19
+ export interface QueryResult {
20
+ /** Process exit code; null when the process died from a signal. */
21
+ exitCode: number | null;
22
+ /** Captured stdout (tail when truncated). */
23
+ stdout: string;
24
+ /** Captured stderr (tail when truncated). */
25
+ stderr: string;
26
+ /** True when either stream hit the maxResultChars cap. */
27
+ truncated: boolean;
28
+ }
29
+ /** Runner options: client overrides, deadlines, output caps. */
30
+ export interface QueryOptions {
31
+ /** Deployment client overrides keyed by database type. */
32
+ clients: Readonly<Partial<Record<DatabaseType, ClientConfig>>>;
33
+ /** End-to-end deadline in milliseconds (timeout → terminate the tree). */
34
+ timeoutMs: number;
35
+ /** In-memory cap per captured stream. */
36
+ maxResultChars: number;
37
+ /** Grace period for the terminate escalation; defaults to 5s. */
38
+ graceMs?: number;
39
+ }
40
+ /**
41
+ * Run one SQL text through the type's CLI client. The SQL is written to the
42
+ * child's stdin (`{ data }` batch disposition) so it never appears in argv;
43
+ * passwords travel in the env entries built by the template.
44
+ *
45
+ * Failure classification:
46
+ * - the caller's external signal (e.g. the tool exec signal) aborts → the
47
+ * abort reason propagates;
48
+ * - the internal timeout fires → an Error naming the deadline is thrown;
49
+ * - the executable cannot be resolved → an Error naming the command is thrown;
50
+ * - the process runs to completion → `{ exitCode, stdout, stderr, truncated }`
51
+ * is returned even for a non-zero exit (the caller decides what that means).
52
+ * @param ctx - context exposing the subprocess service.
53
+ * @param connection - the stored connection (password included).
54
+ * @param sql - the SQL text (or client command) to run.
55
+ * @param options - timeouts, caps, client overrides.
56
+ * @param externalSignal - caller-owned cancellation (the tool exec signal).
57
+ * @param introspect - use the machine-readable introspection flag set.
58
+ * @returns the captured outcome.
59
+ */
60
+ export declare function runClientQuery(ctx: Context, connection: DatabaseConnection, sql: string, options: QueryOptions, externalSignal: AbortSignal, introspect?: boolean): Promise<QueryResult>;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Data Agent routes half (`@yejiming/dsh-data-agent/routes`): the
3
+ * `/plugins/data-agent` HTTP surface. A separate row from the main `data-agent`
4
+ * row so the plugin keeps working in headless profiles (no webserver): the
5
+ * connection store, preset self-install, and config-seeded connections all
6
+ * live on the main row, and this row simply never activates where
7
+ * `webServer` is absent.
8
+ *
9
+ * Routes:
10
+ * - `POST /plugins/data-agent/connect` — validate and store one session's
11
+ * database connection, verify connectivity by listing all tables, and
12
+ * return `{ ok, tables }` (or `{ ok: false, error }` without saving).
13
+ * - `POST /plugins/data-agent/disconnect` — drop one session's connection.
14
+ * - `GET /plugins/data-agent/status` — the current connection's
15
+ * password-stripped summary plus the table list.
16
+ * - `GET /plugins/data-agent/schemas` — schema/database list.
17
+ * - `GET /plugins/data-agent/tables` — table list of one schema.
18
+ * - `GET /plugins/data-agent/describe` — column structure of one table.
19
+ * - `POST /plugins/data-agent/query` — run one SQL text (the workbench
20
+ * command box; non-agent channel, same trust as sqlcmd).
21
+ * @module @yejiming/dsh-data-agent/routes
22
+ */
23
+ import type { IncomingMessage, ServerResponse } from 'node:http';
24
+ import type { Context } from '@deepseek-ai/cordis';
25
+ /**
26
+ * Minimal face of the host webserver service used by this row.
27
+ * The service was renamed from `httpServer` to `webServer` in
28
+ * dsh 0.1.0-rc.6; the nested inject below waits on `webServer`.
29
+ */
30
+ interface WebServerLike {
31
+ register(route: {
32
+ kind: 'exact' | 'prefix';
33
+ path: string;
34
+ handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>;
35
+ }): () => void;
36
+ }
37
+ declare module '@deepseek-ai/cordis' {
38
+ interface Context {
39
+ webServer: WebServerLike;
40
+ }
41
+ }
42
+ import type { DatabaseType } from './connections.ts';
43
+ /** Cordis plugin name (diagnostics only). */
44
+ export declare const name = "data-agent-routes";
45
+ /**
46
+ * No top-level `inject` export: the row must ACTIVATE even in headless
47
+ * profiles where `webServer` never exists (a permanently pending entry
48
+ * breaks one-shot runs). The routes register through a nested inject fiber
49
+ * the moment the webserver and the connection store are both available.
50
+ */
51
+ export declare const inject: string[];
52
+ /** Route prefix owned by this plugin (the browser half calls under it). */
53
+ export declare const DATA_AGENT_PATH = "/plugins/data-agent";
54
+ /** Routes-half configuration (defaults mirror the main row). */
55
+ export interface Config {
56
+ /** Deadline for one /connect connectivity check, milliseconds. */
57
+ connectTimeoutMs: number;
58
+ /** Cap on metadata lists returned by /connect /status /schemas /tables. */
59
+ introspectMaxTables: number;
60
+ /** In-memory cap on captured output. */
61
+ maxResultChars: number;
62
+ /** Deadline for one /query or metadata query, milliseconds. */
63
+ queryTimeoutMs: number;
64
+ /** Cap on one /query SQL text length. */
65
+ maxQueryChars: number;
66
+ }
67
+ /** Loader schema with deployment defaults (no library defaults). */
68
+ export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
69
+ connectTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
70
+ introspectMaxTables: import("@deepseek-ai/schemastery").default<number, number>;
71
+ maxResultChars: import("@deepseek-ai/schemastery").default<number, number>;
72
+ queryTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
73
+ maxQueryChars: import("@deepseek-ai/schemastery").default<number, number>;
74
+ }>, Schemastery.ObjectT<{
75
+ connectTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
76
+ introspectMaxTables: import("@deepseek-ai/schemastery").default<number, number>;
77
+ maxResultChars: import("@deepseek-ai/schemastery").default<number, number>;
78
+ queryTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
79
+ maxQueryChars: import("@deepseek-ai/schemastery").default<number, number>;
80
+ }>>;
81
+ /** The connection request wire body (validated in the /connect handler). */
82
+ export interface ConnectRequestBody {
83
+ sessionId: string;
84
+ type: DatabaseType;
85
+ host?: string;
86
+ port?: number;
87
+ user?: string;
88
+ database: string;
89
+ password?: string;
90
+ }
91
+ /**
92
+ * Validate an untrusted /connect body; sqlite paths resolve to absolute
93
+ * (the client resolves the path relative to its own cwd, so the server pins
94
+ * it at connect time). Oracle/Hive/Impala follow the mysql/postgres shape:
95
+ * host/port/user/database (Oracle database = service name/SID, Hive/Impala
96
+ * database = default schema).
97
+ */
98
+ export declare function validateConnectBody(value: unknown, cwd?: string): ConnectRequestBody;
99
+ /**
100
+ * Mount the data-agent routes against the host webserver, when one exists.
101
+ * The registration rides a nested inject fiber so this row activates in every
102
+ * profile; headless profiles simply never get routes.
103
+ * @param ctx - host cordis context.
104
+ * @param config - validated loader configuration.
105
+ */
106
+ export declare function apply(ctx: Context, config: Config): void;
107
+ export {};
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The sqlcmd tool half (`@yejiming/dsh-data-agent/tool`): mounted ONLY by
3
+ * the data-agent agent preset (`preset/data-agent/agent.cordis.yml`), never
4
+ * by the host composition. It consumes the host's `subprocess` service and
5
+ * the host-provided `dataAgentConnections` connection store, so it needs no
6
+ * realm and satisfies the preset guard (a preset row that only consumes).
7
+ *
8
+ * Execution model (see `src/query.ts`): the SQL text travels on the client's
9
+ * stdin, argv carries flags only, credentials go through environment entries
10
+ * (`MYSQL_PWD` / `PGPASSWORD`), and the caller's signal plus an internal
11
+ * deadline share one AbortController that drives the process-tree terminate
12
+ * escalation. Output is bounded per stream and marked `truncated`.
13
+ * @module @yejiming/dsh-data-agent/tool
14
+ */
15
+ import type { Context } from '@deepseek-ai/cordis';
16
+ import { type ClientConfig } from './clients.ts';
17
+ /** Cordis plugin name (diagnostics only). */
18
+ export declare const name = "data-agent-tool";
19
+ /** Services required before the tool can register. */
20
+ export declare const inject: string[];
21
+ /** Tool-half configuration (loader schema with the same defaults as the host). */
22
+ export interface Config {
23
+ /** Deadline for one sqlcmd query, milliseconds. */
24
+ queryTimeoutMs: number;
25
+ /** In-memory cap on captured output. */
26
+ maxResultChars: number;
27
+ /** Row-count guidance injected into the tool description. */
28
+ maxRows: number;
29
+ /** CLI client overrides keyed by database type. */
30
+ clients: Partial<Record<string, ClientConfig>>;
31
+ }
32
+ /** Loader schema with deployment defaults (no library defaults). */
33
+ export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<{
34
+ queryTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
35
+ maxResultChars: import("@deepseek-ai/schemastery").default<number, number>;
36
+ maxRows: import("@deepseek-ai/schemastery").default<number, number>;
37
+ clients: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
38
+ command?: string | null | undefined;
39
+ args?: string[] | null | undefined;
40
+ } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
41
+ command: import("@deepseek-ai/schemastery").default<string, string>;
42
+ args: import("@deepseek-ai/schemastery").default<string[], string[]>;
43
+ }>, string>>;
44
+ }>, Schemastery.ObjectT<{
45
+ queryTimeoutMs: import("@deepseek-ai/schemastery").default<number, number>;
46
+ maxResultChars: import("@deepseek-ai/schemastery").default<number, number>;
47
+ maxRows: import("@deepseek-ai/schemastery").default<number, number>;
48
+ clients: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/cosmokit").Dict<{
49
+ command?: string | null | undefined;
50
+ args?: string[] | null | undefined;
51
+ } & import("cosmokit").Dict, string>, import("@deepseek-ai/cosmokit").Dict<Schemastery.ObjectT<{
52
+ command: import("@deepseek-ai/schemastery").default<string, string>;
53
+ args: import("@deepseek-ai/schemastery").default<string[], string[]>;
54
+ }>, string>>;
55
+ }>>;
56
+ /**
57
+ * Mount the sqlcmd tool: register it into the current agent's tool registry.
58
+ * @param ctx - the preset-scoped agent context.
59
+ * @param config - validated loader configuration.
60
+ */
61
+ export declare function apply(ctx: Context, config: Config): void;