@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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import "./defaults-DP4RyRh1.js";
|
|
2
|
-
import {
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { posix, resolve, win32 } from "node:path";
|
|
3
5
|
import z from "schemastery";
|
|
4
6
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
5
7
|
//#region src/sql.ts
|
|
@@ -595,7 +597,8 @@ function quoteStringLiteral(value) {
|
|
|
595
597
|
/** Loader schema for one client override (all fields optional at input). */
|
|
596
598
|
const clientConfigSchema = z.object({
|
|
597
599
|
command: z.string(),
|
|
598
|
-
args: z.array(z.string())
|
|
600
|
+
args: z.array(z.string()),
|
|
601
|
+
searchPaths: z.array(z.string())
|
|
599
602
|
});
|
|
600
603
|
/** Loader schema for the whole `clients` config object (any type key). */
|
|
601
604
|
const clientsSchema = z.dict(clientConfigSchema).default({});
|
|
@@ -891,6 +894,291 @@ function parseColumns(type, stdout) {
|
|
|
891
894
|
return columns;
|
|
892
895
|
}
|
|
893
896
|
//#endregion
|
|
897
|
+
//#region src/client-discovery.ts
|
|
898
|
+
/**
|
|
899
|
+
* Cross-platform database CLI discovery.
|
|
900
|
+
*
|
|
901
|
+
* The subprocess provider remains the authority for executable validation.
|
|
902
|
+
* This module only builds a bounded, platform-aware PATH fallback when the
|
|
903
|
+
* provider cannot resolve the configured/default bare command from its
|
|
904
|
+
* current execution environment. No shell, registry, or recursive scan is
|
|
905
|
+
* involved, and the exact discovery environment is returned for spawn.
|
|
906
|
+
* @module @yejiming/dsh-data-agent/client-discovery
|
|
907
|
+
*/
|
|
908
|
+
/** Maximum child names consumed from one known version/formula directory. */
|
|
909
|
+
const MAX_DYNAMIC_ENTRIES = 64;
|
|
910
|
+
/** Production host facts. */
|
|
911
|
+
const DEFAULT_SYSTEM = {
|
|
912
|
+
platform: process.platform,
|
|
913
|
+
env: process.env,
|
|
914
|
+
homeDir: homedir(),
|
|
915
|
+
cwd: process.cwd(),
|
|
916
|
+
async readDirectory(directory) {
|
|
917
|
+
return await readdir(directory);
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
const HOME_ENV_BY_TYPE = {
|
|
921
|
+
mysql: ["MYSQL_HOME"],
|
|
922
|
+
postgres: ["PGHOME", "PGROOT"],
|
|
923
|
+
sqlite: ["SQLITE_HOME"],
|
|
924
|
+
oracle: ["ORACLE_HOME"],
|
|
925
|
+
hive: ["HIVE_HOME"],
|
|
926
|
+
impala: ["IMPALA_HOME"]
|
|
927
|
+
};
|
|
928
|
+
function pathApi(platform) {
|
|
929
|
+
return platform === "win32" ? win32 : posix;
|
|
930
|
+
}
|
|
931
|
+
function environmentValue(env, name, platform) {
|
|
932
|
+
if (platform !== "win32") return env[name];
|
|
933
|
+
const key = Object.keys(env).find((candidate) => candidate.toLowerCase() === name.toLowerCase());
|
|
934
|
+
return key === void 0 ? void 0 : env[key];
|
|
935
|
+
}
|
|
936
|
+
function expandHome(directory, system, paths) {
|
|
937
|
+
const trimmed = directory.trim();
|
|
938
|
+
if (trimmed === "~") return system.homeDir;
|
|
939
|
+
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) return paths.join(system.homeDir, trimmed.slice(2));
|
|
940
|
+
return paths.isAbsolute(trimmed) ? paths.normalize(trimmed) : paths.resolve(system.cwd, trimmed);
|
|
941
|
+
}
|
|
942
|
+
function normalizeDirectories(directories, system, paths) {
|
|
943
|
+
const result = [];
|
|
944
|
+
const seen = /* @__PURE__ */ new Set();
|
|
945
|
+
for (const raw of directories) {
|
|
946
|
+
if (raw.trim() === "") continue;
|
|
947
|
+
const directory = expandHome(raw, system, paths);
|
|
948
|
+
const key = system.platform === "win32" ? directory.toLowerCase() : directory;
|
|
949
|
+
if (seen.has(key)) continue;
|
|
950
|
+
seen.add(key);
|
|
951
|
+
result.push(directory);
|
|
952
|
+
}
|
|
953
|
+
return result;
|
|
954
|
+
}
|
|
955
|
+
function clientHomeDirectories(type, system, paths) {
|
|
956
|
+
const result = [];
|
|
957
|
+
for (const name of HOME_ENV_BY_TYPE[type]) {
|
|
958
|
+
const value = environmentValue(system.env, name, system.platform)?.trim();
|
|
959
|
+
if (value === void 0 || value === "") continue;
|
|
960
|
+
result.push(paths.join(value, "bin"), value);
|
|
961
|
+
}
|
|
962
|
+
return result;
|
|
963
|
+
}
|
|
964
|
+
function macFixedDirectories(type) {
|
|
965
|
+
return [
|
|
966
|
+
"/opt/homebrew/bin",
|
|
967
|
+
...{
|
|
968
|
+
mysql: [
|
|
969
|
+
"/opt/homebrew/opt/mysql-client/bin",
|
|
970
|
+
"/opt/homebrew/opt/mysql/bin",
|
|
971
|
+
"/usr/local/opt/mysql-client/bin",
|
|
972
|
+
"/usr/local/opt/mysql/bin",
|
|
973
|
+
"/usr/local/mysql/bin"
|
|
974
|
+
],
|
|
975
|
+
postgres: [
|
|
976
|
+
"/opt/homebrew/opt/libpq/bin",
|
|
977
|
+
"/usr/local/opt/libpq/bin",
|
|
978
|
+
"/Applications/Postgres.app/Contents/Versions/latest/bin"
|
|
979
|
+
],
|
|
980
|
+
sqlite: ["/opt/homebrew/opt/sqlite/bin", "/usr/local/opt/sqlite/bin"],
|
|
981
|
+
oracle: [],
|
|
982
|
+
hive: ["/opt/homebrew/opt/hive/bin", "/usr/local/opt/hive/bin"],
|
|
983
|
+
impala: ["/opt/homebrew/opt/impala/bin", "/usr/local/opt/impala/bin"]
|
|
984
|
+
}[type],
|
|
985
|
+
"/usr/local/bin",
|
|
986
|
+
"/opt/local/bin",
|
|
987
|
+
"/usr/bin"
|
|
988
|
+
];
|
|
989
|
+
}
|
|
990
|
+
function linuxFixedDirectories(system, paths) {
|
|
991
|
+
return [
|
|
992
|
+
paths.join(system.homeDir, ".local", "bin"),
|
|
993
|
+
"/home/linuxbrew/.linuxbrew/bin",
|
|
994
|
+
paths.join(system.homeDir, ".linuxbrew", "bin"),
|
|
995
|
+
"/usr/local/bin",
|
|
996
|
+
"/usr/bin",
|
|
997
|
+
"/snap/bin",
|
|
998
|
+
paths.join(system.homeDir, ".nix-profile", "bin"),
|
|
999
|
+
"/nix/var/nix/profiles/default/bin"
|
|
1000
|
+
];
|
|
1001
|
+
}
|
|
1002
|
+
function windowsFixedDirectories(type, system, paths) {
|
|
1003
|
+
const localAppData = environmentValue(system.env, "LOCALAPPDATA", system.platform);
|
|
1004
|
+
const userProfile = environmentValue(system.env, "USERPROFILE", system.platform) ?? system.homeDir;
|
|
1005
|
+
const chocolatey = environmentValue(system.env, "ChocolateyInstall", system.platform);
|
|
1006
|
+
const programData = environmentValue(system.env, "ProgramData", system.platform) ?? "C:\\ProgramData";
|
|
1007
|
+
const programFiles = environmentValue(system.env, "ProgramFiles", system.platform) ?? "C:\\Program Files";
|
|
1008
|
+
const typeSpecific = {
|
|
1009
|
+
mysql: [],
|
|
1010
|
+
postgres: [],
|
|
1011
|
+
sqlite: [paths.join("C:\\", "sqlite"), paths.join(programFiles, "SQLite")],
|
|
1012
|
+
oracle: [],
|
|
1013
|
+
hive: [],
|
|
1014
|
+
impala: []
|
|
1015
|
+
};
|
|
1016
|
+
return [
|
|
1017
|
+
...localAppData === void 0 ? [] : [paths.join(localAppData, "Microsoft", "WinGet", "Links")],
|
|
1018
|
+
paths.join(userProfile, "scoop", "shims"),
|
|
1019
|
+
...chocolatey === void 0 ? [] : [paths.join(chocolatey, "bin")],
|
|
1020
|
+
paths.join(programData, "chocolatey", "bin"),
|
|
1021
|
+
...typeSpecific[type]
|
|
1022
|
+
];
|
|
1023
|
+
}
|
|
1024
|
+
function formulaPattern(type) {
|
|
1025
|
+
switch (type) {
|
|
1026
|
+
case "mysql": return /^(?:mysql|mysql-client)(?:@.+)?$/i;
|
|
1027
|
+
case "postgres": return /^(?:postgresql(?:@.+)?|libpq)$/i;
|
|
1028
|
+
case "sqlite": return /^sqlite(?:@.+)?$/i;
|
|
1029
|
+
case "oracle": return /^(?:oracle|instantclient)(?:@.+)?$/i;
|
|
1030
|
+
case "hive": return /^hive(?:@.+)?$/i;
|
|
1031
|
+
case "impala": return /^impala(?:@.+)?$/i;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
function dynamicDirectories(type, system, paths) {
|
|
1035
|
+
const result = [];
|
|
1036
|
+
if (system.platform === "darwin") {
|
|
1037
|
+
const pattern = formulaPattern(type);
|
|
1038
|
+
result.push({
|
|
1039
|
+
root: "/opt/homebrew/opt",
|
|
1040
|
+
accepts: (name) => pattern.test(name),
|
|
1041
|
+
suffix: ["bin"]
|
|
1042
|
+
}, {
|
|
1043
|
+
root: "/usr/local/opt",
|
|
1044
|
+
accepts: (name) => pattern.test(name),
|
|
1045
|
+
suffix: ["bin"]
|
|
1046
|
+
});
|
|
1047
|
+
if (type === "postgres") result.push({
|
|
1048
|
+
root: "/Library/PostgreSQL",
|
|
1049
|
+
accepts: () => true,
|
|
1050
|
+
suffix: ["bin"]
|
|
1051
|
+
}, {
|
|
1052
|
+
root: "/Applications/Postgres.app/Contents/Versions",
|
|
1053
|
+
accepts: (name) => name !== "latest",
|
|
1054
|
+
suffix: ["bin"]
|
|
1055
|
+
});
|
|
1056
|
+
if (type === "oracle") result.push({
|
|
1057
|
+
root: "/opt/oracle",
|
|
1058
|
+
accepts: (name) => /^instantclient/i.test(name),
|
|
1059
|
+
suffix: []
|
|
1060
|
+
});
|
|
1061
|
+
} else if (system.platform === "linux") {
|
|
1062
|
+
const pattern = formulaPattern(type);
|
|
1063
|
+
result.push({
|
|
1064
|
+
root: "/home/linuxbrew/.linuxbrew/opt",
|
|
1065
|
+
accepts: (name) => pattern.test(name),
|
|
1066
|
+
suffix: ["bin"]
|
|
1067
|
+
}, {
|
|
1068
|
+
root: paths.join(system.homeDir, ".linuxbrew", "opt"),
|
|
1069
|
+
accepts: (name) => pattern.test(name),
|
|
1070
|
+
suffix: ["bin"]
|
|
1071
|
+
});
|
|
1072
|
+
} else if (system.platform === "win32") {
|
|
1073
|
+
const roots = [environmentValue(system.env, "ProgramFiles", system.platform) ?? "C:\\Program Files", environmentValue(system.env, "ProgramFiles(x86)", system.platform) ?? "C:\\Program Files (x86)"];
|
|
1074
|
+
for (const root of roots) if (type === "mysql") result.push({
|
|
1075
|
+
root: paths.join(root, "MySQL"),
|
|
1076
|
+
accepts: () => true,
|
|
1077
|
+
suffix: ["bin"]
|
|
1078
|
+
}, {
|
|
1079
|
+
root,
|
|
1080
|
+
accepts: (name) => /^MariaDB/i.test(name),
|
|
1081
|
+
suffix: ["bin"]
|
|
1082
|
+
});
|
|
1083
|
+
else if (type === "postgres") result.push({
|
|
1084
|
+
root: paths.join(root, "PostgreSQL"),
|
|
1085
|
+
accepts: () => true,
|
|
1086
|
+
suffix: ["bin"]
|
|
1087
|
+
});
|
|
1088
|
+
else if (type === "oracle") result.push({
|
|
1089
|
+
root: paths.join(root, "Oracle"),
|
|
1090
|
+
accepts: () => true,
|
|
1091
|
+
suffix: ["bin"]
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
return result;
|
|
1095
|
+
}
|
|
1096
|
+
async function expandDynamicDirectories(descriptors, system, paths, signal) {
|
|
1097
|
+
return (await Promise.all(descriptors.map(async (descriptor) => {
|
|
1098
|
+
signal.throwIfAborted();
|
|
1099
|
+
let names;
|
|
1100
|
+
try {
|
|
1101
|
+
names = await system.readDirectory(descriptor.root);
|
|
1102
|
+
} catch {
|
|
1103
|
+
return [];
|
|
1104
|
+
}
|
|
1105
|
+
signal.throwIfAborted();
|
|
1106
|
+
return names.filter((name) => descriptor.accepts(name)).sort((left, right) => right.localeCompare(left, void 0, {
|
|
1107
|
+
numeric: true,
|
|
1108
|
+
sensitivity: "base"
|
|
1109
|
+
})).slice(0, MAX_DYNAMIC_ENTRIES).map((name) => paths.join(descriptor.root, name, ...descriptor.suffix));
|
|
1110
|
+
}))).flat();
|
|
1111
|
+
}
|
|
1112
|
+
/** Build ordered fallback directories without recursively scanning the host. */
|
|
1113
|
+
async function buildClientSearchDirectories(type, config, signal, system = DEFAULT_SYSTEM) {
|
|
1114
|
+
const paths = pathApi(system.platform);
|
|
1115
|
+
const configured = config?.searchPaths ?? [];
|
|
1116
|
+
const homes = clientHomeDirectories(type, system, paths);
|
|
1117
|
+
const fixed = system.platform === "win32" ? windowsFixedDirectories(type, system, paths) : system.platform === "darwin" ? macFixedDirectories(type) : linuxFixedDirectories(system, paths);
|
|
1118
|
+
const dynamic = await expandDynamicDirectories(dynamicDirectories(type, system, paths), system, paths, signal);
|
|
1119
|
+
signal.throwIfAborted();
|
|
1120
|
+
return normalizeDirectories([
|
|
1121
|
+
...configured,
|
|
1122
|
+
...homes,
|
|
1123
|
+
...fixed,
|
|
1124
|
+
...dynamic
|
|
1125
|
+
], system, paths);
|
|
1126
|
+
}
|
|
1127
|
+
function hasPathSeparator(command) {
|
|
1128
|
+
return command.includes("/") || command.includes("\\");
|
|
1129
|
+
}
|
|
1130
|
+
function withSearchPath(explicitEnv, directories, system) {
|
|
1131
|
+
const pathName = system.platform === "win32" ? Object.keys(system.env).find((name) => name.toLowerCase() === "path") ?? "Path" : "PATH";
|
|
1132
|
+
const explicitPathName = Object.keys(explicitEnv).find((name) => system.platform === "win32" ? name.toLowerCase() === "path" : name === "PATH");
|
|
1133
|
+
const parentPath = explicitPathName === void 0 ? environmentValue(system.env, "PATH", system.platform) : explicitEnv[explicitPathName];
|
|
1134
|
+
const separator = system.platform === "win32" ? ";" : ":";
|
|
1135
|
+
const prefix = directories.join(separator);
|
|
1136
|
+
const combined = parentPath === void 0 || parentPath === "" ? prefix : `${prefix}${separator}${parentPath}`;
|
|
1137
|
+
const result = { ...explicitEnv };
|
|
1138
|
+
if (explicitPathName !== void 0 && explicitPathName !== pathName) delete result[explicitPathName];
|
|
1139
|
+
result[pathName] = combined;
|
|
1140
|
+
return result;
|
|
1141
|
+
}
|
|
1142
|
+
function errorText(error) {
|
|
1143
|
+
return error instanceof Error ? error.message : String(error);
|
|
1144
|
+
}
|
|
1145
|
+
function checkedDirectoriesText(directories) {
|
|
1146
|
+
const visible = directories.slice(0, 16);
|
|
1147
|
+
const suffix = directories.length > visible.length ? `,另有${directories.length - visible.length}个目录` : "";
|
|
1148
|
+
return visible.length === 0 ? "无补充目录" : `${visible.join("、")}${suffix}`;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Resolve one configured/default client. Current PATH (or an explicit path)
|
|
1152
|
+
* always wins. Only a missing bare command activates bounded PATH discovery.
|
|
1153
|
+
*/
|
|
1154
|
+
async function resolveClientExecutable(options) {
|
|
1155
|
+
const system = options.system ?? DEFAULT_SYSTEM;
|
|
1156
|
+
let initialError;
|
|
1157
|
+
try {
|
|
1158
|
+
return {
|
|
1159
|
+
executable: await options.resolveExecutable(options.command, options.env, options.signal),
|
|
1160
|
+
env: options.env,
|
|
1161
|
+
searchedDirectories: []
|
|
1162
|
+
};
|
|
1163
|
+
} catch (error) {
|
|
1164
|
+
options.signal.throwIfAborted();
|
|
1165
|
+
initialError = error;
|
|
1166
|
+
}
|
|
1167
|
+
if (pathApi(system.platform).isAbsolute(options.command) || hasPathSeparator(options.command)) throw new Error(`无法解析数据库客户端 "${options.command}"(类型 ${options.type}:${errorText(initialError)});该显式路径不会回退到默认命令,请检查 clients.${options.type}.command`);
|
|
1168
|
+
const directories = await buildClientSearchDirectories(options.type, options.config, options.signal, system);
|
|
1169
|
+
const discoveryEnv = withSearchPath(options.env, directories, system);
|
|
1170
|
+
try {
|
|
1171
|
+
return {
|
|
1172
|
+
executable: await options.resolveExecutable(options.command, discoveryEnv, options.signal),
|
|
1173
|
+
env: discoveryEnv,
|
|
1174
|
+
searchedDirectories: directories
|
|
1175
|
+
};
|
|
1176
|
+
} catch (fallbackError) {
|
|
1177
|
+
options.signal.throwIfAborted();
|
|
1178
|
+
throw new Error(`无法解析数据库客户端 "${options.command}"(类型 ${options.type};当前PATH:${errorText(initialError)};补充PATH:${errorText(fallbackError)})。已检查:${checkedDirectoriesText(directories)};请确认客户端已安装,或配置 clients.${options.type}.command / clients.${options.type}.searchPaths`);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
//#endregion
|
|
894
1182
|
//#region src/query.ts
|
|
895
1183
|
/** Read one collected stream from offset 0. */
|
|
896
1184
|
function readCaptured(reader) {
|
|
@@ -934,15 +1222,16 @@ async function runClientQuery(ctx, connection, sql, options, externalSignal, int
|
|
|
934
1222
|
if (externalSignal.aborted) controller.abort(externalSignal.reason);
|
|
935
1223
|
else externalSignal.addEventListener("abort", onExternalAbort, { once: true });
|
|
936
1224
|
try {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1225
|
+
const resolution = await resolveClientExecutable({
|
|
1226
|
+
type: connection.type,
|
|
1227
|
+
command: template.command,
|
|
1228
|
+
config: options.clients[connection.type],
|
|
1229
|
+
env: template.env,
|
|
1230
|
+
signal: controller.signal,
|
|
1231
|
+
resolveExecutable: ctx.subprocess.resolveExecutable.bind(ctx.subprocess)
|
|
1232
|
+
});
|
|
944
1233
|
const handle = ctx.subprocess.spawn({
|
|
945
|
-
argv: [executable, ...template.args],
|
|
1234
|
+
argv: [resolution.executable, ...template.args],
|
|
946
1235
|
cwd: process.cwd(),
|
|
947
1236
|
stdio: {
|
|
948
1237
|
stdin: { data: `${template.stdinPrefix}${sql}\n` },
|
|
@@ -951,7 +1240,7 @@ async function runClientQuery(ctx, connection, sql, options, externalSignal, int
|
|
|
951
1240
|
},
|
|
952
1241
|
graceMs: options.graceMs ?? 5e3,
|
|
953
1242
|
signal: controller.signal,
|
|
954
|
-
env:
|
|
1243
|
+
env: resolution.env
|
|
955
1244
|
});
|
|
956
1245
|
let outcome;
|
|
957
1246
|
try {
|
|
@@ -1016,7 +1305,8 @@ function normalizeConnectionInput(input, cwd = process.cwd()) {
|
|
|
1016
1305
|
if (input.name !== void 0 && input.name.trim().length === 0) throw new Error("name 不能为空");
|
|
1017
1306
|
const connection = {
|
|
1018
1307
|
type: input.type,
|
|
1019
|
-
database: input.type === "sqlite" ? resolve(cwd, input.database) : input.database
|
|
1308
|
+
database: input.type === "sqlite" ? resolve(cwd, input.database) : input.database,
|
|
1309
|
+
credentialMode: input.type === "sqlite" ? "none" : input.passwordRef !== void 0 ? "reference" : input.password !== void 0 && input.password.length > 0 ? "password" : "none"
|
|
1020
1310
|
};
|
|
1021
1311
|
if (input.type !== "sqlite") {
|
|
1022
1312
|
if (input.host !== void 0 && input.host.length > 0) connection.host = input.host;
|
|
@@ -1057,16 +1347,21 @@ function createConnectionService(ctx, options, persistence) {
|
|
|
1057
1347
|
return ctx;
|
|
1058
1348
|
};
|
|
1059
1349
|
const resolveCredential = async (connection) => {
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1350
|
+
const mode = credentialModeOf(connection);
|
|
1351
|
+
if (mode === "reference") {
|
|
1352
|
+
if (connection.passwordRef === void 0) throw new Error("数据库凭据引用缺失,请重新配置连接");
|
|
1353
|
+
const ref = validatedCredentialRef(connection.passwordRef);
|
|
1354
|
+
const hit = await requireContext().credentials.resolve(ref);
|
|
1355
|
+
if (hit === void 0 || hit.value.length === 0) throw new Error(`凭据引用 "${connection.passwordRef}" 未配置`);
|
|
1356
|
+
return {
|
|
1357
|
+
...connection,
|
|
1358
|
+
password: hit.value,
|
|
1359
|
+
tables: copyTables(connection.tables)
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
if (mode === "password" && connection.password === void 0) throw new Error("数据库凭据需要重新输入;请打开数据库配置并重新连接");
|
|
1067
1363
|
return {
|
|
1068
1364
|
...connection,
|
|
1069
|
-
password: hit.value,
|
|
1070
1365
|
tables: copyTables(connection.tables)
|
|
1071
1366
|
};
|
|
1072
1367
|
};
|
|
@@ -1111,18 +1406,29 @@ function createConnectionService(ctx, options, persistence) {
|
|
|
1111
1406
|
}
|
|
1112
1407
|
};
|
|
1113
1408
|
const credentialSummary = async (connection) => {
|
|
1114
|
-
|
|
1115
|
-
if (connection.
|
|
1409
|
+
const mode = credentialModeOf(connection);
|
|
1410
|
+
if (connection.type === "sqlite" || mode === "none") return void 0;
|
|
1411
|
+
if (mode === "password") return connection.password === void 0 ? { configured: false } : {
|
|
1116
1412
|
configured: true,
|
|
1117
1413
|
source: "memory"
|
|
1118
1414
|
};
|
|
1119
|
-
if (connection.passwordRef === void 0) return { configured: false };
|
|
1415
|
+
if (mode !== "reference" || connection.passwordRef === void 0) return { configured: false };
|
|
1120
1416
|
const info = await requireContext().credentials.describe(validatedCredentialRef(connection.passwordRef));
|
|
1121
1417
|
return {
|
|
1122
1418
|
configured: info.configured,
|
|
1123
1419
|
...info.source !== void 0 ? { source: info.source } : {}
|
|
1124
1420
|
};
|
|
1125
1421
|
};
|
|
1422
|
+
const statusSummary = async (connection) => {
|
|
1423
|
+
const summary = summarize(connection);
|
|
1424
|
+
const mode = credentialModeOf(connection);
|
|
1425
|
+
summary.credentialMode = mode;
|
|
1426
|
+
summary.credential = await credentialSummary(connection);
|
|
1427
|
+
const ready = mode === "none" || summary.credential?.configured === true;
|
|
1428
|
+
summary.ready = ready;
|
|
1429
|
+
summary.reconnectRequired = !ready;
|
|
1430
|
+
return summary;
|
|
1431
|
+
};
|
|
1126
1432
|
const service = {
|
|
1127
1433
|
set(sessionId, connection) {
|
|
1128
1434
|
if (connection.password !== void 0 && connection.passwordRef !== void 0) throw new Error("password 与 passwordRef 不能同时提供");
|
|
@@ -1165,9 +1471,7 @@ function createConnectionService(ctx, options, persistence) {
|
|
|
1165
1471
|
async status(sessionId) {
|
|
1166
1472
|
const connection = rawConnection(sessionId);
|
|
1167
1473
|
if (connection === void 0) return void 0;
|
|
1168
|
-
|
|
1169
|
-
summary.credential = await credentialSummary(connection);
|
|
1170
|
-
return summary;
|
|
1474
|
+
return statusSummary(connection);
|
|
1171
1475
|
},
|
|
1172
1476
|
async connect(sessionId, input, signal) {
|
|
1173
1477
|
if (sessionId.length === 0) throw new Error("sessionId 必须是非空字符串");
|
|
@@ -1183,11 +1487,9 @@ function createConnectionService(ctx, options, persistence) {
|
|
|
1183
1487
|
tables
|
|
1184
1488
|
};
|
|
1185
1489
|
runtime.set(sessionId, published);
|
|
1186
|
-
const summary = summarize(published);
|
|
1187
|
-
summary.credential = await credentialSummary(published);
|
|
1188
1490
|
return {
|
|
1189
1491
|
tables,
|
|
1190
|
-
summary
|
|
1492
|
+
summary: await statusSummary(published)
|
|
1191
1493
|
};
|
|
1192
1494
|
},
|
|
1193
1495
|
async disconnect(sessionId) {
|
|
@@ -1202,11 +1504,9 @@ function createConnectionService(ctx, options, persistence) {
|
|
|
1202
1504
|
tables
|
|
1203
1505
|
};
|
|
1204
1506
|
runtime.set(sessionId, published);
|
|
1205
|
-
const summary = summarize(published);
|
|
1206
|
-
summary.credential = await credentialSummary(published);
|
|
1207
1507
|
return {
|
|
1208
1508
|
tables,
|
|
1209
|
-
summary
|
|
1509
|
+
summary: await statusSummary(published)
|
|
1210
1510
|
};
|
|
1211
1511
|
},
|
|
1212
1512
|
async resolveForExecution(sessionId) {
|
|
@@ -1294,7 +1594,8 @@ function connectionFromProfile(profileId, profile) {
|
|
|
1294
1594
|
...profile.port !== void 0 ? { port: profile.port } : {},
|
|
1295
1595
|
...profile.user !== void 0 ? { user: profile.user } : {},
|
|
1296
1596
|
...profile.readonly !== void 0 ? { readonly: profile.readonly } : {},
|
|
1297
|
-
...profile.passwordRef !== void 0 ? { passwordRef: profile.passwordRef } : {}
|
|
1597
|
+
...profile.passwordRef !== void 0 ? { passwordRef: profile.passwordRef } : {},
|
|
1598
|
+
credentialMode: profile.credentialMode ?? (profile.type === "sqlite" ? "none" : profile.passwordRef !== void 0 ? "reference" : "password")
|
|
1298
1599
|
};
|
|
1299
1600
|
}
|
|
1300
1601
|
function profileFromConnection(connection, updatedAt) {
|
|
@@ -1307,9 +1608,18 @@ function profileFromConnection(connection, updatedAt) {
|
|
|
1307
1608
|
...connection.port !== void 0 ? { port: connection.port } : {},
|
|
1308
1609
|
...connection.user !== void 0 ? { user: connection.user } : {},
|
|
1309
1610
|
...connection.readonly !== void 0 ? { readonly: connection.readonly } : {},
|
|
1310
|
-
...connection.passwordRef !== void 0 ? { passwordRef: connection.passwordRef } : {}
|
|
1611
|
+
...connection.passwordRef !== void 0 ? { passwordRef: connection.passwordRef } : {},
|
|
1612
|
+
...connection.credentialMode !== void 0 ? { credentialMode: connection.credentialMode } : {}
|
|
1311
1613
|
};
|
|
1312
1614
|
}
|
|
1615
|
+
/** Infer legacy records while leaving ambiguous secret-less SQL profiles conservative. */
|
|
1616
|
+
function credentialModeOf(connection) {
|
|
1617
|
+
if (connection.credentialMode !== void 0) return connection.credentialMode;
|
|
1618
|
+
if (connection.type === "sqlite") return "none";
|
|
1619
|
+
if (connection.passwordRef !== void 0) return "reference";
|
|
1620
|
+
if (connection.password !== void 0) return "password";
|
|
1621
|
+
return "none";
|
|
1622
|
+
}
|
|
1313
1623
|
function requireIdentifier(type, value, label) {
|
|
1314
1624
|
if (value === void 0 || value.length === 0) throw new Error(`${label} 不能为空`);
|
|
1315
1625
|
sanitizeIdentifier(type, value);
|