taurusdb-core 0.3.0 → 0.5.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -1
- package/dist/audit/jsonl-writer.d.ts +47 -0
- package/dist/audit/jsonl-writer.js +124 -0
- package/dist/auth/sql-profile-loader/env-source.js +10 -0
- package/dist/auth/sql-profile-loader/parsing.js +11 -2
- package/dist/auth/sql-profile-loader/runtime-override.js +10 -2
- package/dist/auth/sql-profile-loader/types.d.ts +5 -0
- package/dist/capability/probe.js +3 -1
- package/dist/cloud/auth.d.ts +8 -0
- package/dist/cloud/auth.js +61 -3
- package/dist/cloud/csms.d.ts +7 -0
- package/dist/cloud/csms.js +60 -0
- package/dist/cloud/keychain.d.ts +28 -0
- package/dist/cloud/keychain.js +200 -0
- package/dist/cloud/kms.d.ts +7 -0
- package/dist/cloud/kms.js +86 -0
- package/dist/config/env.js +20 -0
- package/dist/config/schema.d.ts +88 -0
- package/dist/config/schema.js +21 -0
- package/dist/context/datasource-resolver.js +7 -0
- package/dist/context/session-context.d.ts +7 -0
- package/dist/diagnostics/metrics-source.d.ts +3 -0
- package/dist/diagnostics/metrics-source.js +7 -2
- package/dist/diagnostics/replication-lag.d.ts +4 -0
- package/dist/diagnostics/replication-lag.js +138 -0
- package/dist/diagnostics/slow-sql-source/das-source.d.ts +3 -0
- package/dist/diagnostics/slow-sql-source/das-source.js +3 -0
- package/dist/diagnostics/slow-sql-source/factory.js +9 -2
- package/dist/diagnostics/slow-sql-source/taurus-api-source.d.ts +3 -0
- package/dist/diagnostics/slow-sql-source/taurus-api-source.js +3 -0
- package/dist/diagnostics/types.d.ts +5 -1
- package/dist/diagnostics/types.js +7 -7
- package/dist/engine/runtime.d.ts +2 -2
- package/dist/engine/runtime.js +8 -14
- package/dist/engine/types.d.ts +3 -2
- package/dist/engine.d.ts +4 -3
- package/dist/engine.js +44 -2
- package/dist/executor/bounded-sql.d.ts +1 -0
- package/dist/executor/bounded-sql.js +10 -0
- package/dist/executor/concurrency-limiter.d.ts +15 -0
- package/dist/executor/concurrency-limiter.js +67 -0
- package/dist/executor/connection-pool.d.ts +3 -2
- package/dist/executor/connection-pool.js +36 -24
- package/dist/executor/sql-executor.d.ts +3 -0
- package/dist/executor/sql-executor.js +52 -9
- package/dist/executor/types.d.ts +5 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +7 -1
- package/dist/safety/confirmation-store.d.ts +30 -7
- package/dist/safety/confirmation-store.js +154 -30
- package/dist/safety/guardrail.d.ts +3 -0
- package/dist/safety/guardrail.js +16 -6
- package/dist/safety/redaction.d.ts +6 -0
- package/dist/safety/redaction.js +47 -6
- package/dist/safety/sql-classifier.d.ts +1 -0
- package/dist/safety/sql-classifier.js +1 -0
- package/dist/safety/sql-validator.d.ts +1 -0
- package/dist/safety/sql-validator.js +24 -0
- package/dist/schema/adapters/mysql.js +3 -1
- package/dist/taurus/flashback.js +4 -10
- package/dist/taurus/recycle-bin.js +2 -8
- package/dist/utils/formatter.d.ts +6 -2
- package/dist/utils/formatter.js +7 -3
- package/dist/utils/logger.js +8 -0
- package/dist/utils/mysql-identifier.d.ts +1 -0
- package/dist/utils/mysql-identifier.js +9 -0
- package/package.json +14 -3
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
async function readMacOsKeychainPassword(service, account) {
|
|
5
|
+
try {
|
|
6
|
+
const { stdout } = await execFileAsync("security", [
|
|
7
|
+
"find-generic-password",
|
|
8
|
+
"-s",
|
|
9
|
+
service,
|
|
10
|
+
"-a",
|
|
11
|
+
account,
|
|
12
|
+
"-w",
|
|
13
|
+
]);
|
|
14
|
+
const value = stdout.replace(/\r?\n$/, "");
|
|
15
|
+
return value.length > 0 ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const code = error && typeof error === "object" && "code" in error
|
|
19
|
+
? String(error.code)
|
|
20
|
+
: undefined;
|
|
21
|
+
if (code === "44") {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Failed to read macOS Keychain item "${service}".`, {
|
|
25
|
+
cause: error,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function readLinuxSecretServicePassword(service, account, key) {
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await execFileAsync("secret-tool", [
|
|
32
|
+
"lookup",
|
|
33
|
+
"service",
|
|
34
|
+
service,
|
|
35
|
+
"account",
|
|
36
|
+
account,
|
|
37
|
+
"key",
|
|
38
|
+
key,
|
|
39
|
+
]);
|
|
40
|
+
const value = stdout.replace(/\r?\n$/, "");
|
|
41
|
+
return value.length > 0 ? value : undefined;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const code = error && typeof error === "object" && "code" in error
|
|
45
|
+
? String(error.code)
|
|
46
|
+
: undefined;
|
|
47
|
+
if (code === "1") {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (code === "ENOENT") {
|
|
51
|
+
throw new Error("Linux Secret Service requires the secret-tool command. Install libsecret-tools or the equivalent package.", { cause: error });
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`Failed to read Linux Secret Service item "${service}".`, {
|
|
54
|
+
cause: error,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const WINDOWS_CREDENTIAL_READ_SCRIPT = `
|
|
59
|
+
Add-Type -TypeDefinition @'
|
|
60
|
+
using System;
|
|
61
|
+
using System.Runtime.InteropServices;
|
|
62
|
+
public static class TaurusCredentialManager {
|
|
63
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
64
|
+
public struct Credential {
|
|
65
|
+
public UInt32 Flags;
|
|
66
|
+
public UInt32 Type;
|
|
67
|
+
public string TargetName;
|
|
68
|
+
public string Comment;
|
|
69
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
70
|
+
public UInt32 CredentialBlobSize;
|
|
71
|
+
public IntPtr CredentialBlob;
|
|
72
|
+
public UInt32 Persist;
|
|
73
|
+
public UInt32 AttributeCount;
|
|
74
|
+
public IntPtr Attributes;
|
|
75
|
+
public string TargetAlias;
|
|
76
|
+
public string UserName;
|
|
77
|
+
}
|
|
78
|
+
[DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
79
|
+
public static extern bool CredRead(string target, UInt32 type, UInt32 flags, out IntPtr credential);
|
|
80
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
81
|
+
public static extern void CredFree(IntPtr buffer);
|
|
82
|
+
}
|
|
83
|
+
'@
|
|
84
|
+
$pointer = [IntPtr]::Zero
|
|
85
|
+
if (-not [TaurusCredentialManager]::CredRead($env:TAURUSDB_CREDENTIAL_TARGET, 1, 0, [ref]$pointer)) {
|
|
86
|
+
if ([Runtime.InteropServices.Marshal]::GetLastWin32Error() -eq 1168) { exit 0 }
|
|
87
|
+
throw "CredReadW failed."
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
$credential = [Runtime.InteropServices.Marshal]::PtrToStructure($pointer, [type][TaurusCredentialManager+Credential])
|
|
91
|
+
if ($credential.CredentialBlobSize -gt 0) {
|
|
92
|
+
[Runtime.InteropServices.Marshal]::PtrToStringUni($credential.CredentialBlob, [int]($credential.CredentialBlobSize / 2))
|
|
93
|
+
}
|
|
94
|
+
} finally {
|
|
95
|
+
[TaurusCredentialManager]::CredFree($pointer)
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
async function readWindowsCredentialManagerPassword(service, account, key) {
|
|
99
|
+
const target = `${service}/${account}/${key}`;
|
|
100
|
+
const encodedScript = Buffer.from(WINDOWS_CREDENTIAL_READ_SCRIPT, "utf16le").toString("base64");
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await execFileAsync("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encodedScript], {
|
|
103
|
+
env: { ...process.env, TAURUSDB_CREDENTIAL_TARGET: target },
|
|
104
|
+
});
|
|
105
|
+
const value = stdout.replace(/\r?\n$/, "");
|
|
106
|
+
return value.length > 0 ? value : undefined;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const code = error && typeof error === "object" && "code" in error
|
|
110
|
+
? String(error.code)
|
|
111
|
+
: undefined;
|
|
112
|
+
if (code === "ENOENT") {
|
|
113
|
+
throw new Error("Windows Credential Manager requires Windows PowerShell.", {
|
|
114
|
+
cause: error,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Failed to read Windows Credential Manager item "${target}".`, {
|
|
118
|
+
cause: error,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function createCachedCredentialProvider(readValues) {
|
|
123
|
+
let cached;
|
|
124
|
+
return async () => {
|
|
125
|
+
cached ??= readValues();
|
|
126
|
+
return cached;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export function createMacOsKeychainCredentialProvider(options) {
|
|
130
|
+
const platform = options.platform ?? process.platform;
|
|
131
|
+
const account = options.account ?? "default";
|
|
132
|
+
const readPassword = options.readPassword ?? readMacOsKeychainPassword;
|
|
133
|
+
return createCachedCredentialProvider(async () => {
|
|
134
|
+
if (platform !== "darwin") {
|
|
135
|
+
throw new Error("Huawei Cloud system Keychain credentials currently require macOS.");
|
|
136
|
+
}
|
|
137
|
+
const [accessKeyId, secretAccessKey, securityToken] = await Promise.all([
|
|
138
|
+
readPassword(`${options.service}/access-key-id`, account),
|
|
139
|
+
readPassword(`${options.service}/secret-access-key`, account),
|
|
140
|
+
readPassword(`${options.service}/security-token`, account),
|
|
141
|
+
]);
|
|
142
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
143
|
+
throw new Error(`Huawei Cloud credentials were not found in macOS Keychain service "${options.service}" for account "${account}".`);
|
|
144
|
+
}
|
|
145
|
+
return { accessKeyId, secretAccessKey, securityToken };
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
export function createLinuxSecretServiceCredentialProvider(options) {
|
|
149
|
+
const platform = options.platform ?? process.platform;
|
|
150
|
+
const account = options.account ?? "default";
|
|
151
|
+
const readPassword = options.readPassword ?? readLinuxSecretServicePassword;
|
|
152
|
+
return createCachedCredentialProvider(async () => {
|
|
153
|
+
if (platform !== "linux") {
|
|
154
|
+
throw new Error("Huawei Cloud Linux Secret Service credentials require Linux.");
|
|
155
|
+
}
|
|
156
|
+
const [accessKeyId, secretAccessKey, securityToken] = await Promise.all([
|
|
157
|
+
readPassword(options.service, account, "access-key-id"),
|
|
158
|
+
readPassword(options.service, account, "secret-access-key"),
|
|
159
|
+
readPassword(options.service, account, "security-token"),
|
|
160
|
+
]);
|
|
161
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
162
|
+
throw new Error(`Huawei Cloud credentials were not found in Linux Secret Service "${options.service}" for account "${account}".`);
|
|
163
|
+
}
|
|
164
|
+
return { accessKeyId, secretAccessKey, securityToken };
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
export function createWindowsCredentialManagerProvider(options) {
|
|
168
|
+
const platform = options.platform ?? process.platform;
|
|
169
|
+
const account = options.account ?? "default";
|
|
170
|
+
const readPassword = options.readPassword ?? readWindowsCredentialManagerPassword;
|
|
171
|
+
return createCachedCredentialProvider(async () => {
|
|
172
|
+
if (platform !== "win32") {
|
|
173
|
+
throw new Error("Huawei Cloud Windows Credential Manager credentials require Windows.");
|
|
174
|
+
}
|
|
175
|
+
const [accessKeyId, secretAccessKey, securityToken] = await Promise.all([
|
|
176
|
+
readPassword(options.service, account, "access-key-id"),
|
|
177
|
+
readPassword(options.service, account, "secret-access-key"),
|
|
178
|
+
readPassword(options.service, account, "security-token"),
|
|
179
|
+
]);
|
|
180
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
181
|
+
throw new Error(`Huawei Cloud credentials were not found in Windows Credential Manager service "${options.service}" for account "${account}".`);
|
|
182
|
+
}
|
|
183
|
+
return { accessKeyId, secretAccessKey, securityToken };
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
export function createSystemCredentialProvider(options) {
|
|
187
|
+
const platform = options.platform ?? process.platform;
|
|
188
|
+
if (platform === "darwin") {
|
|
189
|
+
return createMacOsKeychainCredentialProvider({ ...options, platform });
|
|
190
|
+
}
|
|
191
|
+
if (platform === "linux") {
|
|
192
|
+
return createLinuxSecretServiceCredentialProvider({ ...options, platform });
|
|
193
|
+
}
|
|
194
|
+
if (platform === "win32") {
|
|
195
|
+
return createWindowsCredentialManagerProvider({ ...options, platform });
|
|
196
|
+
}
|
|
197
|
+
return async () => {
|
|
198
|
+
throw new Error(`Huawei Cloud system credential storage is not supported on platform "${platform}".`);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UriSecretResolver } from "../auth/secret-resolver.js";
|
|
2
|
+
import type { Config } from "../config/index.js";
|
|
3
|
+
export type HuaweiKmsSecretResolverOptions = {
|
|
4
|
+
config: Config;
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
};
|
|
7
|
+
export declare function createHuaweiKmsSecretResolver(options: HuaweiKmsSecretResolverOptions): UriSecretResolver;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fetchHuaweiCloud, getHuaweiCloudAuthFromConfig, resolveHuaweiCloudProjectId, } from "./auth.js";
|
|
5
|
+
function readString(value) {
|
|
6
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
7
|
+
}
|
|
8
|
+
function resolveFilePath(rawPath) {
|
|
9
|
+
const decoded = decodeURIComponent(rawPath);
|
|
10
|
+
if (decoded === "~") {
|
|
11
|
+
return os.homedir();
|
|
12
|
+
}
|
|
13
|
+
if (decoded.startsWith("~/")) {
|
|
14
|
+
return path.join(os.homedir(), decoded.slice(2));
|
|
15
|
+
}
|
|
16
|
+
return path.isAbsolute(decoded) ? decoded : path.resolve(process.cwd(), decoded);
|
|
17
|
+
}
|
|
18
|
+
function requireValue(value, context) {
|
|
19
|
+
const normalized = value.trim();
|
|
20
|
+
if (!normalized) {
|
|
21
|
+
throw new Error(`${context} must not be empty.`);
|
|
22
|
+
}
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
async function readCipherText(uri) {
|
|
26
|
+
if (uri.startsWith("hw-kms-file:")) {
|
|
27
|
+
const rawPath = uri.slice("hw-kms-file:".length).trim();
|
|
28
|
+
if (!rawPath) {
|
|
29
|
+
throw new Error("Huawei KMS file credential reference is missing a path.");
|
|
30
|
+
}
|
|
31
|
+
return requireValue(await readFile(resolveFilePath(rawPath), "utf8"), "Huawei KMS ciphertext");
|
|
32
|
+
}
|
|
33
|
+
if (uri.startsWith("hw-kms:")) {
|
|
34
|
+
return requireValue(uri.slice("hw-kms:".length), "Huawei KMS ciphertext");
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Unsupported Huawei KMS credential reference: ${uri}`);
|
|
37
|
+
}
|
|
38
|
+
function parsePlainText(payload) {
|
|
39
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
40
|
+
? payload
|
|
41
|
+
: {};
|
|
42
|
+
const plainTextBase64 = typeof record.plain_text_base64 === "string" ? record.plain_text_base64 : undefined;
|
|
43
|
+
if (plainTextBase64 !== undefined && plainTextBase64.length > 0) {
|
|
44
|
+
return Buffer.from(plainTextBase64, "base64").toString("utf8");
|
|
45
|
+
}
|
|
46
|
+
const plainText = typeof record.plain_text === "string" ? record.plain_text : undefined;
|
|
47
|
+
if (plainText !== undefined && plainText.length > 0) {
|
|
48
|
+
return plainText;
|
|
49
|
+
}
|
|
50
|
+
throw new Error("Huawei KMS decrypt response did not include plain_text or plain_text_base64.");
|
|
51
|
+
}
|
|
52
|
+
export function createHuaweiKmsSecretResolver(options) {
|
|
53
|
+
const auth = getHuaweiCloudAuthFromConfig(options.config);
|
|
54
|
+
const endpoint = options.config.cloud.kmsEndpoint?.replace(/\/+$/g, "");
|
|
55
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
56
|
+
return async (uri) => {
|
|
57
|
+
if (!endpoint) {
|
|
58
|
+
throw new Error("Huawei KMS endpoint is not configured. Set TAURUSDB_CLOUD_REGION or TAURUSDB_CLOUD_KMS_ENDPOINT.");
|
|
59
|
+
}
|
|
60
|
+
const [cipherText, projectId] = await Promise.all([
|
|
61
|
+
readCipherText(uri),
|
|
62
|
+
resolveHuaweiCloudProjectId(auth, fetchImpl),
|
|
63
|
+
]);
|
|
64
|
+
if (!projectId) {
|
|
65
|
+
throw new Error("Huawei KMS decrypt could not resolve a project id. Set TAURUSDB_CLOUD_PROJECT_ID or configure region plus Huawei Cloud credentials.");
|
|
66
|
+
}
|
|
67
|
+
const response = await fetchHuaweiCloud({
|
|
68
|
+
url: `${endpoint}/v1.0/${projectId}/kms/decrypt-data`,
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "content-type": "application/json" },
|
|
71
|
+
body: JSON.stringify({ cipher_text: cipherText }),
|
|
72
|
+
auth,
|
|
73
|
+
fetchImpl,
|
|
74
|
+
});
|
|
75
|
+
const payload = (await response.json().catch(() => ({})));
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
78
|
+
? payload
|
|
79
|
+
: {};
|
|
80
|
+
const code = readString(record.error_code) ?? readString(record.code);
|
|
81
|
+
const message = readString(record.error_msg) ?? readString(record.message);
|
|
82
|
+
throw new Error(`Huawei KMS decrypt failed with status ${response.status}${code ? ` (${code})` : ""}${message ? `: ${message}` : ""}.`);
|
|
83
|
+
}
|
|
84
|
+
return parsePlainText(payload);
|
|
85
|
+
};
|
|
86
|
+
}
|
package/dist/config/env.js
CHANGED
|
@@ -130,8 +130,14 @@ export function buildRawConfigFromEnv(env) {
|
|
|
130
130
|
accessKeyId: cloudAccessKeyId,
|
|
131
131
|
secretAccessKey: cloudSecretAccessKey,
|
|
132
132
|
securityToken: cloudSecurityToken,
|
|
133
|
+
keychainService: readString(env.TAURUSDB_CLOUD_KEYCHAIN_SERVICE),
|
|
134
|
+
keychainAccount: readString(env.TAURUSDB_CLOUD_KEYCHAIN_ACCOUNT),
|
|
133
135
|
apiEndpoint: buildHuaweiCloudEndpoint("gaussdb", cloudRegion, cloudDomainSuffix),
|
|
134
136
|
iamEndpoint: buildHuaweiCloudEndpoint("iam", cloudRegion, cloudDomainSuffix),
|
|
137
|
+
kmsEndpoint: readString(env.TAURUSDB_CLOUD_KMS_ENDPOINT) ??
|
|
138
|
+
buildHuaweiCloudEndpoint("kms", cloudRegion, cloudDomainSuffix),
|
|
139
|
+
csmsEndpoint: readString(env.TAURUSDB_CLOUD_CSMS_ENDPOINT) ??
|
|
140
|
+
buildHuaweiCloudEndpoint("csms", cloudRegion, cloudDomainSuffix),
|
|
135
141
|
domainSuffix: cloudDomainSuffix,
|
|
136
142
|
language: readString(env.TAURUSDB_CLOUD_LANGUAGE) ??
|
|
137
143
|
readString(env.TAURUSDB_SLOW_SQL_SOURCE_TAURUS_API_LANGUAGE),
|
|
@@ -141,10 +147,24 @@ export function buildRawConfigFromEnv(env) {
|
|
|
141
147
|
maxColumns: parseInteger(env.TAURUSDB_MCP_MAX_COLUMNS, "TAURUSDB_MCP_MAX_COLUMNS"),
|
|
142
148
|
maxStatementMs: parseInteger(env.TAURUSDB_MCP_MAX_STATEMENT_MS, "TAURUSDB_MCP_MAX_STATEMENT_MS"),
|
|
143
149
|
maxFieldChars: parseInteger(env.TAURUSDB_MCP_MAX_FIELD_CHARS, "TAURUSDB_MCP_MAX_FIELD_CHARS"),
|
|
150
|
+
maxResultBytes: parseInteger(env.TAURUSDB_MCP_MAX_RESULT_BYTES, "TAURUSDB_MCP_MAX_RESULT_BYTES"),
|
|
151
|
+
maxBlobBytes: parseInteger(env.TAURUSDB_MCP_MAX_BLOB_BYTES, "TAURUSDB_MCP_MAX_BLOB_BYTES"),
|
|
152
|
+
maxConcurrentQueries: parseInteger(env.TAURUSDB_MCP_MAX_CONCURRENT_QUERIES, "TAURUSDB_MCP_MAX_CONCURRENT_QUERIES"),
|
|
153
|
+
maxQueuedQueries: parseInteger(env.TAURUSDB_MCP_MAX_QUEUED_QUERIES, "TAURUSDB_MCP_MAX_QUEUED_QUERIES"),
|
|
154
|
+
queueTimeoutMs: parseInteger(env.TAURUSDB_MCP_QUEUE_TIMEOUT_MS, "TAURUSDB_MCP_QUEUE_TIMEOUT_MS"),
|
|
144
155
|
},
|
|
145
156
|
audit: {
|
|
146
157
|
logPath: expandTildePath(readString(env.TAURUSDB_MCP_AUDIT_LOG_PATH)),
|
|
147
158
|
includeRawSql: parseBoolean(env.TAURUSDB_MCP_AUDIT_INCLUDE_RAW_SQL, "TAURUSDB_MCP_AUDIT_INCLUDE_RAW_SQL"),
|
|
159
|
+
maxBytes: parseInteger(env.TAURUSDB_MCP_AUDIT_MAX_BYTES, "TAURUSDB_MCP_AUDIT_MAX_BYTES"),
|
|
160
|
+
maxFiles: parseInteger(env.TAURUSDB_MCP_AUDIT_MAX_FILES, "TAURUSDB_MCP_AUDIT_MAX_FILES"),
|
|
161
|
+
},
|
|
162
|
+
security: {
|
|
163
|
+
mutationsEnabled: parseBoolean(env.TAURUSDB_ENABLE_MUTATIONS, "TAURUSDB_ENABLE_MUTATIONS"),
|
|
164
|
+
dynamicTargetsEnabled: parseBoolean(env.TAURUSDB_ENABLE_DYNAMIC_TARGETS, "TAURUSDB_ENABLE_DYNAMIC_TARGETS"),
|
|
165
|
+
requireTls: parseBoolean(env.TAURUSDB_REQUIRE_TLS, "TAURUSDB_REQUIRE_TLS"),
|
|
166
|
+
approvalSecretPath: expandTildePath(readString(env.TAURUSDB_MUTATION_APPROVAL_SECRET_FILE)),
|
|
167
|
+
approvalTtlSeconds: parseInteger(env.TAURUSDB_MUTATION_APPROVAL_TTL_SECONDS, "TAURUSDB_MUTATION_APPROVAL_TTL_SECONDS"),
|
|
148
168
|
},
|
|
149
169
|
slowSqlSource: {
|
|
150
170
|
taurusApi: {
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -12,12 +12,17 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
12
12
|
accessKeyId: z.ZodOptional<z.ZodString>;
|
|
13
13
|
secretAccessKey: z.ZodOptional<z.ZodString>;
|
|
14
14
|
securityToken: z.ZodOptional<z.ZodString>;
|
|
15
|
+
keychainService: z.ZodOptional<z.ZodString>;
|
|
16
|
+
keychainAccount: z.ZodDefault<z.ZodString>;
|
|
15
17
|
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
16
18
|
iamEndpoint: z.ZodOptional<z.ZodString>;
|
|
19
|
+
kmsEndpoint: z.ZodOptional<z.ZodString>;
|
|
20
|
+
csmsEndpoint: z.ZodOptional<z.ZodString>;
|
|
17
21
|
domainSuffix: z.ZodDefault<z.ZodString>;
|
|
18
22
|
language: z.ZodDefault<z.ZodEnum<["en-us", "zh-cn"]>>;
|
|
19
23
|
}, "strip", z.ZodTypeAny, {
|
|
20
24
|
provider: "huaweicloud";
|
|
25
|
+
keychainAccount: string;
|
|
21
26
|
domainSuffix: string;
|
|
22
27
|
language: "en-us" | "zh-cn";
|
|
23
28
|
region?: string | undefined;
|
|
@@ -28,8 +33,11 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
28
33
|
accessKeyId?: string | undefined;
|
|
29
34
|
secretAccessKey?: string | undefined;
|
|
30
35
|
securityToken?: string | undefined;
|
|
36
|
+
keychainService?: string | undefined;
|
|
31
37
|
apiEndpoint?: string | undefined;
|
|
32
38
|
iamEndpoint?: string | undefined;
|
|
39
|
+
kmsEndpoint?: string | undefined;
|
|
40
|
+
csmsEndpoint?: string | undefined;
|
|
33
41
|
}, {
|
|
34
42
|
provider?: "huaweicloud" | undefined;
|
|
35
43
|
region?: string | undefined;
|
|
@@ -40,8 +48,12 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
40
48
|
accessKeyId?: string | undefined;
|
|
41
49
|
secretAccessKey?: string | undefined;
|
|
42
50
|
securityToken?: string | undefined;
|
|
51
|
+
keychainService?: string | undefined;
|
|
52
|
+
keychainAccount?: string | undefined;
|
|
43
53
|
apiEndpoint?: string | undefined;
|
|
44
54
|
iamEndpoint?: string | undefined;
|
|
55
|
+
kmsEndpoint?: string | undefined;
|
|
56
|
+
csmsEndpoint?: string | undefined;
|
|
45
57
|
domainSuffix?: string | undefined;
|
|
46
58
|
language?: "en-us" | "zh-cn" | undefined;
|
|
47
59
|
}>>;
|
|
@@ -50,26 +62,66 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
50
62
|
maxColumns: z.ZodDefault<z.ZodNumber>;
|
|
51
63
|
maxStatementMs: z.ZodDefault<z.ZodNumber>;
|
|
52
64
|
maxFieldChars: z.ZodDefault<z.ZodNumber>;
|
|
65
|
+
maxResultBytes: z.ZodDefault<z.ZodNumber>;
|
|
66
|
+
maxBlobBytes: z.ZodDefault<z.ZodNumber>;
|
|
67
|
+
maxConcurrentQueries: z.ZodDefault<z.ZodNumber>;
|
|
68
|
+
maxQueuedQueries: z.ZodDefault<z.ZodNumber>;
|
|
69
|
+
queueTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
53
70
|
}, "strip", z.ZodTypeAny, {
|
|
54
71
|
maxRows: number;
|
|
55
72
|
maxColumns: number;
|
|
56
73
|
maxStatementMs: number;
|
|
57
74
|
maxFieldChars: number;
|
|
75
|
+
maxResultBytes: number;
|
|
76
|
+
maxBlobBytes: number;
|
|
77
|
+
maxConcurrentQueries: number;
|
|
78
|
+
maxQueuedQueries: number;
|
|
79
|
+
queueTimeoutMs: number;
|
|
58
80
|
}, {
|
|
59
81
|
maxRows?: number | undefined;
|
|
60
82
|
maxColumns?: number | undefined;
|
|
61
83
|
maxStatementMs?: number | undefined;
|
|
62
84
|
maxFieldChars?: number | undefined;
|
|
85
|
+
maxResultBytes?: number | undefined;
|
|
86
|
+
maxBlobBytes?: number | undefined;
|
|
87
|
+
maxConcurrentQueries?: number | undefined;
|
|
88
|
+
maxQueuedQueries?: number | undefined;
|
|
89
|
+
queueTimeoutMs?: number | undefined;
|
|
63
90
|
}>>;
|
|
64
91
|
audit: z.ZodDefault<z.ZodObject<{
|
|
65
92
|
logPath: z.ZodDefault<z.ZodString>;
|
|
66
93
|
includeRawSql: z.ZodDefault<z.ZodBoolean>;
|
|
94
|
+
maxBytes: z.ZodDefault<z.ZodNumber>;
|
|
95
|
+
maxFiles: z.ZodDefault<z.ZodNumber>;
|
|
67
96
|
}, "strip", z.ZodTypeAny, {
|
|
68
97
|
logPath: string;
|
|
69
98
|
includeRawSql: boolean;
|
|
99
|
+
maxBytes: number;
|
|
100
|
+
maxFiles: number;
|
|
70
101
|
}, {
|
|
71
102
|
logPath?: string | undefined;
|
|
72
103
|
includeRawSql?: boolean | undefined;
|
|
104
|
+
maxBytes?: number | undefined;
|
|
105
|
+
maxFiles?: number | undefined;
|
|
106
|
+
}>>;
|
|
107
|
+
security: z.ZodDefault<z.ZodObject<{
|
|
108
|
+
mutationsEnabled: z.ZodDefault<z.ZodBoolean>;
|
|
109
|
+
dynamicTargetsEnabled: z.ZodDefault<z.ZodBoolean>;
|
|
110
|
+
requireTls: z.ZodDefault<z.ZodBoolean>;
|
|
111
|
+
approvalSecretPath: z.ZodOptional<z.ZodString>;
|
|
112
|
+
approvalTtlSeconds: z.ZodDefault<z.ZodNumber>;
|
|
113
|
+
}, "strip", z.ZodTypeAny, {
|
|
114
|
+
mutationsEnabled: boolean;
|
|
115
|
+
dynamicTargetsEnabled: boolean;
|
|
116
|
+
requireTls: boolean;
|
|
117
|
+
approvalTtlSeconds: number;
|
|
118
|
+
approvalSecretPath?: string | undefined;
|
|
119
|
+
}, {
|
|
120
|
+
mutationsEnabled?: boolean | undefined;
|
|
121
|
+
dynamicTargetsEnabled?: boolean | undefined;
|
|
122
|
+
requireTls?: boolean | undefined;
|
|
123
|
+
approvalSecretPath?: string | undefined;
|
|
124
|
+
approvalTtlSeconds?: number | undefined;
|
|
73
125
|
}>>;
|
|
74
126
|
slowSqlSource: z.ZodDefault<z.ZodObject<{
|
|
75
127
|
taurusApi: z.ZodDefault<z.ZodObject<{
|
|
@@ -271,6 +323,7 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
271
323
|
}, "strip", z.ZodTypeAny, {
|
|
272
324
|
cloud: {
|
|
273
325
|
provider: "huaweicloud";
|
|
326
|
+
keychainAccount: string;
|
|
274
327
|
domainSuffix: string;
|
|
275
328
|
language: "en-us" | "zh-cn";
|
|
276
329
|
region?: string | undefined;
|
|
@@ -281,18 +334,35 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
281
334
|
accessKeyId?: string | undefined;
|
|
282
335
|
secretAccessKey?: string | undefined;
|
|
283
336
|
securityToken?: string | undefined;
|
|
337
|
+
keychainService?: string | undefined;
|
|
284
338
|
apiEndpoint?: string | undefined;
|
|
285
339
|
iamEndpoint?: string | undefined;
|
|
340
|
+
kmsEndpoint?: string | undefined;
|
|
341
|
+
csmsEndpoint?: string | undefined;
|
|
286
342
|
};
|
|
287
343
|
limits: {
|
|
288
344
|
maxRows: number;
|
|
289
345
|
maxColumns: number;
|
|
290
346
|
maxStatementMs: number;
|
|
291
347
|
maxFieldChars: number;
|
|
348
|
+
maxResultBytes: number;
|
|
349
|
+
maxBlobBytes: number;
|
|
350
|
+
maxConcurrentQueries: number;
|
|
351
|
+
maxQueuedQueries: number;
|
|
352
|
+
queueTimeoutMs: number;
|
|
292
353
|
};
|
|
293
354
|
audit: {
|
|
294
355
|
logPath: string;
|
|
295
356
|
includeRawSql: boolean;
|
|
357
|
+
maxBytes: number;
|
|
358
|
+
maxFiles: number;
|
|
359
|
+
};
|
|
360
|
+
security: {
|
|
361
|
+
mutationsEnabled: boolean;
|
|
362
|
+
dynamicTargetsEnabled: boolean;
|
|
363
|
+
requireTls: boolean;
|
|
364
|
+
approvalTtlSeconds: number;
|
|
365
|
+
approvalSecretPath?: string | undefined;
|
|
296
366
|
};
|
|
297
367
|
slowSqlSource: {
|
|
298
368
|
taurusApi: {
|
|
@@ -352,8 +422,12 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
352
422
|
accessKeyId?: string | undefined;
|
|
353
423
|
secretAccessKey?: string | undefined;
|
|
354
424
|
securityToken?: string | undefined;
|
|
425
|
+
keychainService?: string | undefined;
|
|
426
|
+
keychainAccount?: string | undefined;
|
|
355
427
|
apiEndpoint?: string | undefined;
|
|
356
428
|
iamEndpoint?: string | undefined;
|
|
429
|
+
kmsEndpoint?: string | undefined;
|
|
430
|
+
csmsEndpoint?: string | undefined;
|
|
357
431
|
domainSuffix?: string | undefined;
|
|
358
432
|
language?: "en-us" | "zh-cn" | undefined;
|
|
359
433
|
} | undefined;
|
|
@@ -362,10 +436,24 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
362
436
|
maxColumns?: number | undefined;
|
|
363
437
|
maxStatementMs?: number | undefined;
|
|
364
438
|
maxFieldChars?: number | undefined;
|
|
439
|
+
maxResultBytes?: number | undefined;
|
|
440
|
+
maxBlobBytes?: number | undefined;
|
|
441
|
+
maxConcurrentQueries?: number | undefined;
|
|
442
|
+
maxQueuedQueries?: number | undefined;
|
|
443
|
+
queueTimeoutMs?: number | undefined;
|
|
365
444
|
} | undefined;
|
|
366
445
|
audit?: {
|
|
367
446
|
logPath?: string | undefined;
|
|
368
447
|
includeRawSql?: boolean | undefined;
|
|
448
|
+
maxBytes?: number | undefined;
|
|
449
|
+
maxFiles?: number | undefined;
|
|
450
|
+
} | undefined;
|
|
451
|
+
security?: {
|
|
452
|
+
mutationsEnabled?: boolean | undefined;
|
|
453
|
+
dynamicTargetsEnabled?: boolean | undefined;
|
|
454
|
+
requireTls?: boolean | undefined;
|
|
455
|
+
approvalSecretPath?: string | undefined;
|
|
456
|
+
approvalTtlSeconds?: number | undefined;
|
|
369
457
|
} | undefined;
|
|
370
458
|
slowSqlSource?: {
|
|
371
459
|
taurusApi?: {
|
package/dist/config/schema.js
CHANGED
|
@@ -5,12 +5,28 @@ const LimitsSchema = z
|
|
|
5
5
|
maxColumns: z.number().int().positive().default(50),
|
|
6
6
|
maxStatementMs: z.number().int().positive().default(15000),
|
|
7
7
|
maxFieldChars: z.number().int().positive().default(2048),
|
|
8
|
+
maxResultBytes: z.number().int().positive().default(1048576),
|
|
9
|
+
maxBlobBytes: z.number().int().positive().default(65536),
|
|
10
|
+
maxConcurrentQueries: z.number().int().positive().default(8),
|
|
11
|
+
maxQueuedQueries: z.number().int().nonnegative().default(32),
|
|
12
|
+
queueTimeoutMs: z.number().int().positive().default(5000),
|
|
8
13
|
})
|
|
9
14
|
.default({});
|
|
10
15
|
const AuditSchema = z
|
|
11
16
|
.object({
|
|
12
17
|
logPath: z.string().min(1).default("~/.taurusdb-mcp/audit.jsonl"),
|
|
13
18
|
includeRawSql: z.boolean().default(false),
|
|
19
|
+
maxBytes: z.number().int().positive().default(104857600),
|
|
20
|
+
maxFiles: z.number().int().positive().max(100).default(10),
|
|
21
|
+
})
|
|
22
|
+
.default({});
|
|
23
|
+
const SecuritySchema = z
|
|
24
|
+
.object({
|
|
25
|
+
mutationsEnabled: z.boolean().default(false),
|
|
26
|
+
dynamicTargetsEnabled: z.boolean().default(false),
|
|
27
|
+
requireTls: z.boolean().default(true),
|
|
28
|
+
approvalSecretPath: z.string().min(1).optional(),
|
|
29
|
+
approvalTtlSeconds: z.number().int().positive().max(3600).default(300),
|
|
14
30
|
})
|
|
15
31
|
.default({});
|
|
16
32
|
const CloudSchema = z
|
|
@@ -24,8 +40,12 @@ const CloudSchema = z
|
|
|
24
40
|
accessKeyId: z.string().min(1).optional(),
|
|
25
41
|
secretAccessKey: z.string().min(1).optional(),
|
|
26
42
|
securityToken: z.string().min(1).optional(),
|
|
43
|
+
keychainService: z.string().min(1).optional(),
|
|
44
|
+
keychainAccount: z.string().min(1).default("default"),
|
|
27
45
|
apiEndpoint: z.string().min(1).optional(),
|
|
28
46
|
iamEndpoint: z.string().min(1).optional(),
|
|
47
|
+
kmsEndpoint: z.string().min(1).optional(),
|
|
48
|
+
csmsEndpoint: z.string().min(1).optional(),
|
|
29
49
|
domainSuffix: z.string().min(1).default("myhuaweicloud.com"),
|
|
30
50
|
language: z.enum(["en-us", "zh-cn"]).default("zh-cn"),
|
|
31
51
|
})
|
|
@@ -85,6 +105,7 @@ export const ConfigSchema = z.object({
|
|
|
85
105
|
cloud: CloudSchema,
|
|
86
106
|
limits: LimitsSchema,
|
|
87
107
|
audit: AuditSchema,
|
|
108
|
+
security: SecuritySchema,
|
|
88
109
|
slowSqlSource: z
|
|
89
110
|
.object({
|
|
90
111
|
taurusApi: TaurusApiSlowSqlSourceSchema,
|
|
@@ -41,12 +41,19 @@ export class DefaultDatasourceResolver {
|
|
|
41
41
|
engine: profile.engine,
|
|
42
42
|
database: normalizeString(input.database) ?? profile.database,
|
|
43
43
|
schema: normalizeString(input.schema),
|
|
44
|
+
host: profile.host,
|
|
45
|
+
port: profile.port,
|
|
46
|
+
projectId: this.config.cloud.projectId,
|
|
47
|
+
instanceId: profile.instanceId ?? this.config.cloud.instanceId,
|
|
48
|
+
nodeId: profile.nodeId ?? this.config.cloud.nodeId,
|
|
44
49
|
limits: {
|
|
45
50
|
readonly: input.readonly ?? true,
|
|
46
51
|
timeoutMs: resolveTimeoutMs(input.timeout_ms, this.config.limits.maxStatementMs),
|
|
47
52
|
maxRows: this.config.limits.maxRows,
|
|
48
53
|
maxColumns: this.config.limits.maxColumns,
|
|
49
54
|
maxFieldChars: this.config.limits.maxFieldChars,
|
|
55
|
+
maxResultBytes: this.config.limits.maxResultBytes,
|
|
56
|
+
maxBlobBytes: this.config.limits.maxBlobBytes,
|
|
50
57
|
},
|
|
51
58
|
};
|
|
52
59
|
}
|
|
@@ -5,6 +5,8 @@ export interface RuntimeLimits {
|
|
|
5
5
|
maxRows: number;
|
|
6
6
|
maxColumns: number;
|
|
7
7
|
maxFieldChars: number;
|
|
8
|
+
maxResultBytes?: number;
|
|
9
|
+
maxBlobBytes?: number;
|
|
8
10
|
}
|
|
9
11
|
export interface SessionContext {
|
|
10
12
|
task_id: string;
|
|
@@ -12,6 +14,11 @@ export interface SessionContext {
|
|
|
12
14
|
engine: DatabaseEngine;
|
|
13
15
|
database?: string;
|
|
14
16
|
schema?: string;
|
|
17
|
+
host?: string;
|
|
18
|
+
port?: number;
|
|
19
|
+
projectId?: string;
|
|
20
|
+
instanceId?: string;
|
|
21
|
+
nodeId?: string;
|
|
15
22
|
limits: RuntimeLimits;
|
|
16
23
|
}
|
|
17
24
|
export interface DatasourceResolveInput {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Config } from "../config/index.js";
|
|
2
2
|
import type { SessionContext } from "../context/session-context.js";
|
|
3
|
+
import { type HuaweiCloudCredentialProvider } from "../cloud/auth.js";
|
|
3
4
|
import type { DiagnosisWindow } from "./types.js";
|
|
4
5
|
export type MetricAlias = "cpu_util" | "mem_util" | "connection_count" | "active_connection_count" | "connection_usage" | "qps" | "tps" | "slow_queries" | "storage_used_size" | "storage_write_delay" | "storage_read_delay" | "row_lock_time" | "row_lock_waits" | "long_trx_count" | "write_iops" | "read_iops" | "write_throughput" | "read_throughput" | "temp_tables_per_min";
|
|
5
6
|
export interface MetricPoint {
|
|
@@ -31,6 +32,7 @@ type CesMetricsSourceOptions = {
|
|
|
31
32
|
accessKeyId?: string;
|
|
32
33
|
secretAccessKey?: string;
|
|
33
34
|
securityToken?: string;
|
|
35
|
+
credentialProvider?: HuaweiCloudCredentialProvider;
|
|
34
36
|
namespace: string;
|
|
35
37
|
instanceDimension: string;
|
|
36
38
|
nodeDimension: string;
|
|
@@ -49,6 +51,7 @@ export declare class CesMetricsSource implements MetricsSource {
|
|
|
49
51
|
private readonly accessKeyId?;
|
|
50
52
|
private readonly secretAccessKey?;
|
|
51
53
|
private readonly securityToken?;
|
|
54
|
+
private readonly credentialProvider?;
|
|
52
55
|
private readonly namespace;
|
|
53
56
|
private readonly instanceDimension;
|
|
54
57
|
private readonly nodeDimension;
|