@carllee1983/dbcli 1.56.0 → 1.58.0
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/.cursor/rules/dbcli.mdc +20 -2
- package/.cursor/skills/dbcli/reference.md +53 -3
- package/.github/skills/dbcli/SKILL.md +20 -2
- package/.github/skills/dbcli/reference.md +53 -3
- package/CHANGELOG.md +56 -0
- package/assets/SKILL.md +20 -2
- package/assets/SKILL.zh-TW.md +15 -1
- package/assets/reference.md +53 -3
- package/dist/cli-runtime.mjs +1160 -618
- package/dist/cli.mjs +4 -2
- package/dist/core.d.ts +223 -146
- package/dist/core.mjs +481 -371
- package/package.json +4 -2
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +20 -2
- package/plugins/dbcli-agent/skills/dbcli/reference.md +53 -3
- package/skills/dbcli/SKILL.md +20 -2
- package/skills/dbcli/reference.md +53 -3
package/dist/cli-runtime.mjs
CHANGED
|
@@ -51,7 +51,7 @@ var package_default;
|
|
|
51
51
|
var init_package = __esm(() => {
|
|
52
52
|
package_default = {
|
|
53
53
|
name: "@carllee1983/dbcli",
|
|
54
|
-
version: "1.
|
|
54
|
+
version: "1.58.0",
|
|
55
55
|
description: "Database CLI for AI agents",
|
|
56
56
|
type: "module",
|
|
57
57
|
publishConfig: {
|
|
@@ -125,12 +125,14 @@ var init_package = __esm(() => {
|
|
|
125
125
|
"test:unit": "bun test tests/unit tests/core",
|
|
126
126
|
"test:integration": "bun test tests/integration",
|
|
127
127
|
"test:gherkin": "bun test tests/gherkin",
|
|
128
|
-
"test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration
|
|
128
|
+
"test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration; docker compose -f docker-compose.test.yml down",
|
|
129
|
+
"services:check": "bun run scripts/check-test-services.ts",
|
|
129
130
|
"docs:check": "bun run scripts/check-user-docs.ts",
|
|
130
131
|
"contract:check": "bun run scripts/check-cli-contract.ts",
|
|
131
132
|
"skill:check": "bun run scripts/check-skill-parity.ts",
|
|
132
133
|
"platform:check": "bun run scripts/check-platform-parity.ts",
|
|
133
134
|
"agent-core:check": "bun run scripts/check-agent-core-purity.ts",
|
|
135
|
+
"core-stdout:check": "bun run scripts/check-core-no-stdout.ts",
|
|
134
136
|
typecheck: "tsc --noEmit --pretty false",
|
|
135
137
|
"test:perf": "bun test ./tests/perf/*.bench.ts",
|
|
136
138
|
lint: "eslint src tests scripts --ext .ts --max-warnings=0",
|
|
@@ -6232,7 +6234,11 @@ function resolveEnvReferences(config, env, parentKey) {
|
|
|
6232
6234
|
}
|
|
6233
6235
|
function parseEnvPassword(content) {
|
|
6234
6236
|
const match = content.match(/^DBCLI_PASSWORD=(.+)$/m);
|
|
6235
|
-
|
|
6237
|
+
if (match?.[1] == null)
|
|
6238
|
+
return null;
|
|
6239
|
+
const raw = match[1].trim();
|
|
6240
|
+
const quoted = raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("'") && raw.endsWith("'");
|
|
6241
|
+
return quoted && raw.length >= 2 ? raw.slice(1, -1) : raw;
|
|
6236
6242
|
}
|
|
6237
6243
|
var _globalConnectionName, DEFAULT_CONFIG, configModule;
|
|
6238
6244
|
var init_config = __esm(() => {
|
|
@@ -8939,6 +8945,12 @@ var init_messages = __esm(() => {
|
|
|
8939
8945
|
invalid_config: "Invalid configuration: {field}",
|
|
8940
8946
|
connection_failed: "Failed to connect to database: {message}",
|
|
8941
8947
|
permission_denied: "Permission denied (required: {required})",
|
|
8948
|
+
permission_requires_level: "{type} operation requires {minimum} permission or higher (current level: {permission})",
|
|
8949
|
+
elasticsearch_requires_level: "Elasticsearch {type} operation requires {minimum} permission or higher (current level: {permission})",
|
|
8950
|
+
escalated_write_requires_admin: "This statement opens as a read but contains an executable {keyword}. A write hidden inside a read statement requires admin permission (current level: {permission}).",
|
|
8951
|
+
multiple_statements_refused: "SQL containing multiple statements is refused below admin permission, because only the first statement determines the permission check. Run each statement separately.",
|
|
8952
|
+
unknown_statement_query_only: "Unrecognised SQL statement (current level: query-only). Security policy requires read-write+ for unknown statements. If this is a legitimate read-only statement, please open an issue at https://github.com/CarlLee1983/dbcli/issues.",
|
|
8953
|
+
permission_denied_reason: "Permission denied: {reason}",
|
|
8942
8954
|
table_not_found: "Table not found: {table}",
|
|
8943
8955
|
unsupported_lang: "Language '{lang}' not supported, using English",
|
|
8944
8956
|
invalid_json: "Invalid JSON: {message}",
|
|
@@ -8960,7 +8972,8 @@ Provide options: --env-host, --env-port, --env-user, --env-password, --env-datab
|
|
|
8960
8972
|
Or use interactive mode: run "dbcli init --use-env-refs" without --no-interactive`,
|
|
8961
8973
|
env_var_not_defined: `Cannot test connection: environment variable {envKey} is not defined.
|
|
8962
8974
|
Set {envKey} in .env or environment variables.
|
|
8963
|
-
Hint: run 'export {envKey}=<value>' and retry
|
|
8975
|
+
Hint: run 'export {envKey}=<value>' and retry`,
|
|
8976
|
+
invalid_output_format: "--format {format} is not supported here \u2014 use text or json"
|
|
8964
8977
|
},
|
|
8965
8978
|
success: {
|
|
8966
8979
|
inserted: "Successfully inserted {count} row(s)",
|
|
@@ -8971,19 +8984,22 @@ Hint: run 'export {envKey}=<value>' and retry`
|
|
|
8971
8984
|
insert: {
|
|
8972
8985
|
description: "Insert data into a table",
|
|
8973
8986
|
prompt_data: "Enter JSON data to insert: ",
|
|
8974
|
-
confirm: "Insert {count} row(s) into {table}? (y/n): "
|
|
8987
|
+
confirm: "Insert {count} row(s) into {table}? (y/n): ",
|
|
8988
|
+
elasticsearch_unsupported: "Elasticsearch does not support the insert command; for now, use an external tool (e.g. curl or Kibana DevTools) to write documents"
|
|
8975
8989
|
},
|
|
8976
8990
|
update: {
|
|
8977
8991
|
description: "Update data in a table",
|
|
8978
8992
|
prompt_where: "Enter WHERE clause (e.g., id=1): ",
|
|
8979
8993
|
prompt_set: 'Enter JSON updates (e.g., {"status": "active"}): ',
|
|
8980
|
-
confirm: "Update rows in {table}? (y/n): "
|
|
8994
|
+
confirm: "Update rows in {table}? (y/n): ",
|
|
8995
|
+
elasticsearch_unsupported: "Elasticsearch does not support the update command; for now, use an external tool (e.g. curl or Kibana DevTools) to update documents"
|
|
8981
8996
|
},
|
|
8982
8997
|
delete: {
|
|
8983
8998
|
description: "Delete data from a table",
|
|
8984
8999
|
prompt_where: "Enter WHERE clause (e.g., id=1): ",
|
|
8985
9000
|
confirm: "Delete rows from {table}? (y/n): ",
|
|
8986
|
-
admin_only: "Delete command requires data-admin or admin permission"
|
|
9001
|
+
admin_only: "Delete command requires data-admin or admin permission",
|
|
9002
|
+
elasticsearch_unsupported: "Elasticsearch does not support the delete command; for now, use an external tool (e.g. curl or Kibana DevTools) to delete documents"
|
|
8987
9003
|
},
|
|
8988
9004
|
export: {
|
|
8989
9005
|
description: "Export query results",
|
|
@@ -9058,6 +9074,20 @@ Reference: {referencePath}`,
|
|
|
9058
9074
|
add_enum_description: "Create an enum type (PostgreSQL only)",
|
|
9059
9075
|
alter_enum_description: "Add a value to an enum type",
|
|
9060
9076
|
drop_enum_description: "Drop an enum type"
|
|
9077
|
+
},
|
|
9078
|
+
password: {
|
|
9079
|
+
description: "Change the password of a single connection, leaving every other setting untouched",
|
|
9080
|
+
arg_connection: "Connection name (defaults to the current default connection)",
|
|
9081
|
+
opt_stdin: "Read the new password from stdin (for rotation scripts)",
|
|
9082
|
+
opt_password: "New password given inline (visible in shell history and process list)",
|
|
9083
|
+
opt_skip_test: "Skip verifying the new password against the database before saving",
|
|
9084
|
+
prompt: "New password for connection '{name}'",
|
|
9085
|
+
source_conflict: "--password and --stdin cannot be used together; pick one source.",
|
|
9086
|
+
needs_source: "No terminal available for a masked prompt. Pass the value with --stdin or --password.",
|
|
9087
|
+
empty: "The new password is empty; nothing was changed.",
|
|
9088
|
+
updated: "Password for connection '{name}' updated \u2192 {envFile} ({varName})",
|
|
9089
|
+
converted: 'The literal password in config.json was replaced by a {{ "$env": "{varName}" }} reference; later rotations only touch the env file.',
|
|
9090
|
+
skipped_test: "Verification skipped (--skip-test): the new password was not checked against the database."
|
|
9061
9091
|
}
|
|
9062
9092
|
};
|
|
9063
9093
|
});
|
|
@@ -9223,6 +9253,12 @@ var init_messages2 = __esm(() => {
|
|
|
9223
9253
|
invalid_config: "\u914D\u7F6E\u7121\u6548\uFF1A{field}",
|
|
9224
9254
|
connection_failed: "\u7121\u6CD5\u9023\u63A5\u5230\u8CC7\u6599\u5EAB\uFF1A{message}",
|
|
9225
9255
|
permission_denied: "\u6B0A\u9650\u88AB\u62D2\uFF08\u9700\u8981\uFF1A{required}\uFF09",
|
|
9256
|
+
permission_requires_level: "{type} \u64CD\u4F5C\u9700\u8981 {minimum} \u4EE5\u4E0A\u7684\u6B0A\u9650\uFF08\u76EE\u524D\u5C64\u7D1A\uFF1A{permission}\uFF09",
|
|
9257
|
+
elasticsearch_requires_level: "Elasticsearch {type} \u64CD\u4F5C\u9700\u8981 {minimum} \u4EE5\u4E0A\u7684\u6B0A\u9650\uFF08\u76EE\u524D\u5C64\u7D1A\uFF1A{permission}\uFF09",
|
|
9258
|
+
escalated_write_requires_admin: "\u6B64\u8A9E\u53E5\u8868\u9762\u4E0A\u662F\u8B80\u53D6\uFF0C\u4F46\u5BE6\u969B\u5305\u542B\u53EF\u57F7\u884C\u7684 {keyword}\u3002\u8B80\u53D6\u8A9E\u53E5\u4E2D\u96B1\u85CF\u5BEB\u5165\u64CD\u4F5C\u9700\u8981 admin \u6B0A\u9650\uFF08\u76EE\u524D\u5C64\u7D1A\uFF1A{permission}\uFF09\u3002",
|
|
9259
|
+
multiple_statements_refused: "\u4F4E\u65BC admin \u6B0A\u9650\u6642\u62D2\u7D55\u57F7\u884C\u5305\u542B\u591A\u500B\u8A9E\u53E5\u7684 SQL\uFF0C\u56E0\u70BA\u6B0A\u9650\u6AA2\u67E5\u53EA\u6703\u4F9D\u64DA\u7B2C\u4E00\u500B\u8A9E\u53E5\u5224\u65B7\u3002\u8ACB\u5C07\u5404\u8A9E\u53E5\u5206\u958B\u57F7\u884C\u3002",
|
|
9260
|
+
unknown_statement_query_only: "\u7121\u6CD5\u8FA8\u8B58\u7684 SQL \u8A9E\u53E5\uFF08\u76EE\u524D\u5C64\u7D1A\uFF1Aquery-only\uFF09\u3002\u5B89\u5168\u653F\u7B56\u8981\u6C42\u672A\u77E5\u8A9E\u53E5\u81F3\u5C11\u9700\u8981 read-write \u4EE5\u4E0A\u6B0A\u9650\u3002\u82E5\u9019\u5176\u5BE6\u662F\u5408\u6CD5\u7684\u552F\u8B80\u8A9E\u53E5\uFF0C\u8ACB\u81F3 https://github.com/CarlLee1983/dbcli/issues \u63D0\u51FA\u8B70\u984C\u3002",
|
|
9261
|
+
permission_denied_reason: "\u6B0A\u9650\u88AB\u62D2\uFF1A{reason}",
|
|
9226
9262
|
table_not_found: "\u627E\u4E0D\u5230\u8868\u683C\uFF1A{table}",
|
|
9227
9263
|
unsupported_lang: "\u4E0D\u652F\u63F4\u7684\u8A9E\u8A00 '{lang}'\uFF0C\u4F7F\u7528\u82F1\u6587",
|
|
9228
9264
|
invalid_json: "\u7121\u6548\u7684 JSON\uFF1A{message}",
|
|
@@ -9244,7 +9280,8 @@ var init_messages2 = __esm(() => {
|
|
|
9244
9280
|
\u6216\u4F7F\u7528\u4E92\u52D5\u6A21\u5F0F\uFF1A\u57F7\u884C "dbcli init --use-env-refs"\uFF08\u4E0D\u52A0 --no-interactive\uFF09`,
|
|
9245
9281
|
env_var_not_defined: `\u7121\u6CD5\u6E2C\u8A66\u9023\u7DDA\uFF1A\u74B0\u5883\u8B8A\u6578 {envKey} \u672A\u5B9A\u7FA9\u3002
|
|
9246
9282
|
\u8ACB\u5728 .env \u6216\u74B0\u5883\u8B8A\u6578\u4E2D\u8A2D\u5B9A {envKey}\u3002
|
|
9247
|
-
\u63D0\u793A\uFF1A\u57F7\u884C 'export {envKey}=<value>' \u5F8C\u91CD\u8A66
|
|
9283
|
+
\u63D0\u793A\uFF1A\u57F7\u884C 'export {envKey}=<value>' \u5F8C\u91CD\u8A66`,
|
|
9284
|
+
invalid_output_format: "\u7121\u6CD5\u4F7F\u7528 --format {format}\uFF0C\u8ACB\u4F7F\u7528 text \u6216 json"
|
|
9248
9285
|
},
|
|
9249
9286
|
success: {
|
|
9250
9287
|
inserted: "\u6210\u529F\u63D2\u5165 {count} \u5217",
|
|
@@ -9255,19 +9292,22 @@ var init_messages2 = __esm(() => {
|
|
|
9255
9292
|
insert: {
|
|
9256
9293
|
description: "\u63D2\u5165\u8CC7\u6599\u5230\u8868\u683C",
|
|
9257
9294
|
prompt_data: "\u8F38\u5165\u8981\u63D2\u5165\u7684 JSON \u8CC7\u6599\uFF1A",
|
|
9258
|
-
confirm: "\u5C07 {count} \u5217\u63D2\u5165\u5230 {table}\uFF1F (y/n)\uFF1A"
|
|
9295
|
+
confirm: "\u5C07 {count} \u5217\u63D2\u5165\u5230 {table}\uFF1F (y/n)\uFF1A",
|
|
9296
|
+
elasticsearch_unsupported: "Elasticsearch \u4E0D\u652F\u63F4 insert \u6307\u4EE4\uFF1B\u76EE\u524D\u8ACB\u4F7F\u7528\u5916\u90E8\u5DE5\u5177\uFF08\u5982 curl \u6216 Kibana DevTools\uFF09\u9032\u884C\u6587\u4EF6\u5BEB\u5165"
|
|
9259
9297
|
},
|
|
9260
9298
|
update: {
|
|
9261
9299
|
description: "\u66F4\u65B0\u8868\u683C\u4E2D\u7684\u8CC7\u6599",
|
|
9262
9300
|
prompt_where: "\u8F38\u5165 WHERE \u5B50\u53E5 (\u4F8B\u5982\uFF1Aid=1)\uFF1A",
|
|
9263
9301
|
prompt_set: '\u8F38\u5165 JSON \u66F4\u65B0 (\u4F8B\u5982\uFF1A{"status": "active"})\uFF1A',
|
|
9264
|
-
confirm: "\u66F4\u65B0 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A"
|
|
9302
|
+
confirm: "\u66F4\u65B0 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A",
|
|
9303
|
+
elasticsearch_unsupported: "Elasticsearch \u4E0D\u652F\u63F4 update \u6307\u4EE4\uFF1B\u76EE\u524D\u8ACB\u4F7F\u7528\u5916\u90E8\u5DE5\u5177\uFF08\u5982 curl \u6216 Kibana DevTools\uFF09\u9032\u884C\u6587\u4EF6\u66F4\u65B0"
|
|
9265
9304
|
},
|
|
9266
9305
|
delete: {
|
|
9267
9306
|
description: "\u522A\u9664\u8868\u683C\u4E2D\u7684\u8CC7\u6599",
|
|
9268
9307
|
prompt_where: "\u8F38\u5165 WHERE \u5B50\u53E5 (\u4F8B\u5982\uFF1Aid=1)\uFF1A",
|
|
9269
9308
|
confirm: "\u522A\u9664 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A",
|
|
9270
|
-
admin_only: "\u522A\u9664\u547D\u4EE4\u9700\u8981 data-admin \u6216 admin \u6B0A\u9650"
|
|
9309
|
+
admin_only: "\u522A\u9664\u547D\u4EE4\u9700\u8981 data-admin \u6216 admin \u6B0A\u9650",
|
|
9310
|
+
elasticsearch_unsupported: "Elasticsearch \u4E0D\u652F\u63F4 delete \u6307\u4EE4\uFF1B\u76EE\u524D\u8ACB\u4F7F\u7528\u5916\u90E8\u5DE5\u5177\uFF08\u5982 curl \u6216 Kibana DevTools\uFF09\u9032\u884C\u6587\u4EF6\u522A\u9664"
|
|
9271
9311
|
},
|
|
9272
9312
|
export: {
|
|
9273
9313
|
description: "\u532F\u51FA\u67E5\u8A62\u7D50\u679C",
|
|
@@ -9342,6 +9382,20 @@ var init_messages2 = __esm(() => {
|
|
|
9342
9382
|
add_enum_description: "\u5EFA\u7ACB\u5217\u8209\u578B\u5225\uFF08\u50C5 PostgreSQL\uFF09",
|
|
9343
9383
|
alter_enum_description: "\u65B0\u589E\u5217\u8209\u503C",
|
|
9344
9384
|
drop_enum_description: "\u522A\u9664\u5217\u8209\u578B\u5225"
|
|
9385
|
+
},
|
|
9386
|
+
password: {
|
|
9387
|
+
description: "\u53EA\u8B8A\u66F4\u55AE\u4E00\u9023\u7DDA\u7684\u5BC6\u78BC\uFF0C\u5176\u9918\u8A2D\u5B9A\u539F\u5C01\u4E0D\u52D5",
|
|
9388
|
+
arg_connection: "\u9023\u7DDA\u540D\u7A31\uFF08\u7701\u7565\u6642\u6539\u9810\u8A2D\u9023\u7DDA\uFF09",
|
|
9389
|
+
opt_stdin: "\u5F9E stdin \u8B80\u53D6\u65B0\u5BC6\u78BC\uFF08\u4F9B\u8F2A\u66FF\u8173\u672C\u4F7F\u7528\uFF09",
|
|
9390
|
+
opt_password: "\u76F4\u63A5\u5E36\u5165\u65B0\u5BC6\u78BC\uFF08\u6703\u7559\u5728 shell \u6B77\u53F2\u8207\u884C\u7A0B\u5217\u8868\uFF09",
|
|
9391
|
+
opt_skip_test: "\u5B58\u6A94\u524D\u4E0D\u5148\u7528\u65B0\u5BC6\u78BC\u9023\u7DDA\u9A57\u8B49",
|
|
9392
|
+
prompt: "\u9023\u7DDA '{name}' \u7684\u65B0\u5BC6\u78BC",
|
|
9393
|
+
source_conflict: "--password \u8207 --stdin \u53EA\u80FD\u64C7\u4E00\u3002",
|
|
9394
|
+
needs_source: "\u6C92\u6709\u7D42\u7AEF\u6A5F\u53EF\u505A\u906E\u853D\u8F38\u5165\uFF0C\u8ACB\u6539\u7528 --stdin \u6216 --password \u50B3\u5165\u3002",
|
|
9395
|
+
empty: "\u65B0\u5BC6\u78BC\u662F\u7A7A\u7684\uFF0C\u672A\u505A\u4EFB\u4F55\u8B8A\u66F4\u3002",
|
|
9396
|
+
updated: "\u5DF2\u66F4\u65B0\u9023\u7DDA '{name}' \u7684\u5BC6\u78BC \u2192 {envFile}\uFF08{varName}\uFF09",
|
|
9397
|
+
converted: 'config.json \u88E1\u7684\u660E\u6587\u5BC6\u78BC\u5DF2\u6539\u6210 {{ "$env": "{varName}" }} \u53C3\u7167\uFF0C\u4E4B\u5F8C\u8F2A\u66FF\u53EA\u6703\u52D5 env \u6A94\u3002',
|
|
9398
|
+
skipped_test: "\u5DF2\u8DF3\u904E\u9A57\u8B49\uFF08--skip-test\uFF09\uFF1A\u65B0\u5BC6\u78BC\u672A\u7D93\u8CC7\u6599\u5EAB\u78BA\u8A8D\u3002"
|
|
9345
9399
|
}
|
|
9346
9400
|
};
|
|
9347
9401
|
});
|
|
@@ -9398,6 +9452,60 @@ var init_shell2 = __esm(() => {
|
|
|
9398
9452
|
};
|
|
9399
9453
|
});
|
|
9400
9454
|
|
|
9455
|
+
// resources/lang/en/ceremony.json
|
|
9456
|
+
var ceremony_default;
|
|
9457
|
+
var init_ceremony = __esm(() => {
|
|
9458
|
+
ceremony_default = {
|
|
9459
|
+
updated: "Updated {count} row(s) in {table}",
|
|
9460
|
+
deleted: "Deleted {count} row(s) from {table}",
|
|
9461
|
+
inserted: "Inserted {count} row(s) into {table}",
|
|
9462
|
+
matched_nothing: "No rows matched in {table} \u2014 nothing changed",
|
|
9463
|
+
cancelled: "Cancelled. {table} was not changed",
|
|
9464
|
+
dry_run: "Preview only. {table} was not changed",
|
|
9465
|
+
elapsed: "Took {seconds}s",
|
|
9466
|
+
failed: "Failed: {reason}",
|
|
9467
|
+
unknown_error: "unknown error",
|
|
9468
|
+
confirm_sql: "Generated SQL:",
|
|
9469
|
+
confirm_command: "Command to run:",
|
|
9470
|
+
confirm_params: "Parameters:",
|
|
9471
|
+
confirm_destructive_warning: "\u26A0\uFE0F Warning: DELETE operation is destructive and cannot be undone!",
|
|
9472
|
+
confirm_destructive_prompt: "Are you sure you want to execute this DELETE operation? This cannot be undone.",
|
|
9473
|
+
confirm_destructive_warning_ddl: "\u26A0\uFE0F Warning: this schema change is destructive and cannot be undone!",
|
|
9474
|
+
confirm_ddl_prompt: "Are you sure you want to run this {operation}? This cannot be undone.",
|
|
9475
|
+
confirm_prompt: "Proceed with this operation?",
|
|
9476
|
+
recovery_none: "This cannot be undone automatically \u2014 restore from a backup to get the previous values back.",
|
|
9477
|
+
recovery_retry: "Re-run with --recovery to write a recovery plan, then read it with: dbcli recover",
|
|
9478
|
+
blacklist_hint: "Run `dbcli blacklist list` to see what is protected"
|
|
9479
|
+
};
|
|
9480
|
+
});
|
|
9481
|
+
|
|
9482
|
+
// resources/lang/zh-TW/ceremony.json
|
|
9483
|
+
var ceremony_default2;
|
|
9484
|
+
var init_ceremony2 = __esm(() => {
|
|
9485
|
+
ceremony_default2 = {
|
|
9486
|
+
updated: "\u5DF2\u66F4\u65B0 {table} \u7684 {count} \u5217",
|
|
9487
|
+
deleted: "\u5DF2\u5F9E {table} \u522A\u9664 {count} \u5217",
|
|
9488
|
+
inserted: "\u5DF2\u5BEB\u5165 {count} \u5217\u5230 {table}",
|
|
9489
|
+
matched_nothing: "{table} \u6C92\u6709\u4EFB\u4F55\u5217\u7B26\u5408\u689D\u4EF6\uFF0C\u8CC7\u6599\u672A\u8B8A\u52D5",
|
|
9490
|
+
cancelled: "\u5DF2\u53D6\u6D88\uFF0C{table} \u672A\u8B8A\u52D5",
|
|
9491
|
+
dry_run: "\u50C5\u9810\u89BD\uFF0C{table} \u672A\u8B8A\u52D5",
|
|
9492
|
+
elapsed: "\u8017\u6642 {seconds} \u79D2",
|
|
9493
|
+
failed: "\u5931\u6557\uFF1A{reason}",
|
|
9494
|
+
unknown_error: "\u672A\u77E5\u7684\u932F\u8AA4",
|
|
9495
|
+
confirm_sql: "\u7522\u751F\u7684 SQL\uFF1A",
|
|
9496
|
+
confirm_command: "\u5373\u5C07\u57F7\u884C\u7684\u6307\u4EE4\uFF1A",
|
|
9497
|
+
confirm_params: "\u53C3\u6578\uFF1A",
|
|
9498
|
+
confirm_destructive_warning: "\u26A0\uFE0F \u8B66\u544A\uFF1ADELETE \u5177\u7834\u58DE\u6027\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\uFF01",
|
|
9499
|
+
confirm_destructive_prompt: "\u78BA\u5B9A\u8981\u57F7\u884C\u9019\u500B DELETE \u55CE\uFF1F\u6B64\u64CD\u4F5C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
9500
|
+
confirm_destructive_warning_ddl: "\u26A0\uFE0F \u8B66\u544A\uFF1A\u9019\u500B schema \u8B8A\u66F4\u5177\u7834\u58DE\u6027\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\uFF01",
|
|
9501
|
+
confirm_ddl_prompt: "\u78BA\u5B9A\u8981\u57F7\u884C\u9019\u500B {operation} \u55CE\uFF1F\u6B64\u64CD\u4F5C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
9502
|
+
confirm_prompt: "\u8981\u7E7C\u7E8C\u57F7\u884C\u9019\u500B\u64CD\u4F5C\u55CE\uFF1F",
|
|
9503
|
+
recovery_none: "\u6B64\u64CD\u4F5C\u7121\u6CD5\u81EA\u52D5\u9084\u539F\uFF0C\u8981\u53D6\u56DE\u5148\u524D\u7684\u8CC7\u6599\u8ACB\u5F9E\u5099\u4EFD\u56DE\u5FA9\u3002",
|
|
9504
|
+
recovery_retry: "\u52A0\u4E0A --recovery \u91CD\u8DD1\u53EF\u7522\u751F\u56DE\u5FA9\u8A08\u756B\uFF0C\u518D\u7528 dbcli recover \u8B80\u53D6",
|
|
9505
|
+
blacklist_hint: "\u57F7\u884C `dbcli blacklist list` \u67E5\u770B\u53D7\u4FDD\u8B77\u7684\u9805\u76EE"
|
|
9506
|
+
};
|
|
9507
|
+
});
|
|
9508
|
+
|
|
9401
9509
|
// src/i18n/message-loader.ts
|
|
9402
9510
|
class MessageLoader {
|
|
9403
9511
|
static instance = null;
|
|
@@ -9464,9 +9572,15 @@ var init_message_loader = __esm(() => {
|
|
|
9464
9572
|
init_messages2();
|
|
9465
9573
|
init_shell();
|
|
9466
9574
|
init_shell2();
|
|
9575
|
+
init_ceremony();
|
|
9576
|
+
init_ceremony2();
|
|
9467
9577
|
BUNDLED_MESSAGES = {
|
|
9468
|
-
en: { ...messages_default, shell: shell_default },
|
|
9469
|
-
"zh-TW": {
|
|
9578
|
+
en: { ...messages_default, shell: shell_default, ceremony: ceremony_default },
|
|
9579
|
+
"zh-TW": {
|
|
9580
|
+
...messages_default2,
|
|
9581
|
+
shell: shell_default2,
|
|
9582
|
+
ceremony: ceremony_default2
|
|
9583
|
+
}
|
|
9470
9584
|
};
|
|
9471
9585
|
messageLoader = MessageLoader.getInstance();
|
|
9472
9586
|
});
|
|
@@ -9632,7 +9746,8 @@ async function selectFallback(message, choices) {
|
|
|
9632
9746
|
return choices[0] ?? "";
|
|
9633
9747
|
}
|
|
9634
9748
|
async function confirmFallback(message) {
|
|
9635
|
-
|
|
9749
|
+
process.stderr.write(`${message} (y/n): `);
|
|
9750
|
+
const answer = await readLineFromStdin();
|
|
9636
9751
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
9637
9752
|
}
|
|
9638
9753
|
async function text(message, defaultValue) {
|
|
@@ -9651,20 +9766,28 @@ async function select(message, choices) {
|
|
|
9651
9766
|
return await selectFallback(message, choices);
|
|
9652
9767
|
return await inquirer.select({ message, choices });
|
|
9653
9768
|
}
|
|
9769
|
+
async function secret(message) {
|
|
9770
|
+
const inquirer = await loadInquirer();
|
|
9771
|
+
if (!inquirer) {
|
|
9772
|
+
throw new Error("Masked input is unavailable; pass the value with --stdin or --password instead.");
|
|
9773
|
+
}
|
|
9774
|
+
return await inquirer.password({ message, mask: "*" });
|
|
9775
|
+
}
|
|
9654
9776
|
async function confirm(message) {
|
|
9655
9777
|
if (!process.stdin.isTTY)
|
|
9656
9778
|
return await confirmFallback(message);
|
|
9657
9779
|
const inquirer = await loadInquirer();
|
|
9658
9780
|
if (!inquirer)
|
|
9659
9781
|
return await confirmFallback(message);
|
|
9660
|
-
return await inquirer.confirm({ message });
|
|
9782
|
+
return await inquirer.confirm({ message }, { output: process.stderr });
|
|
9661
9783
|
}
|
|
9662
9784
|
var promptsUnavailableReported = false, promptUser;
|
|
9663
9785
|
var init_prompts = __esm(() => {
|
|
9664
9786
|
promptUser = {
|
|
9665
9787
|
text,
|
|
9666
9788
|
select,
|
|
9667
|
-
confirm
|
|
9789
|
+
confirm,
|
|
9790
|
+
secret
|
|
9668
9791
|
};
|
|
9669
9792
|
});
|
|
9670
9793
|
|
|
@@ -18515,33 +18638,7 @@ var init_sql_lexical = __esm(() => {
|
|
|
18515
18638
|
DOLLAR_QUOTE_DELIMITER = /^\$(?:(?:[A-Za-z_]|[\u0080-\uFFFF])(?:[A-Za-z0-9_]|[\u0080-\uFFFF])*)?\$/;
|
|
18516
18639
|
});
|
|
18517
18640
|
|
|
18518
|
-
// src/core/permission-
|
|
18519
|
-
var exports_permission_guard = {};
|
|
18520
|
-
__export(exports_permission_guard, {
|
|
18521
|
-
toSqlDialect: () => toSqlDialect,
|
|
18522
|
-
stripCommentsAndStrings: () => stripCommentsAndStrings,
|
|
18523
|
-
removeParameterMarkers: () => removeParameterMarkers,
|
|
18524
|
-
permissionAtLeast: () => permissionAtLeast,
|
|
18525
|
-
normalizeSQL: () => normalizeSQL,
|
|
18526
|
-
mapKeywordToType: () => mapKeywordToType,
|
|
18527
|
-
isDestructiveOperation: () => isDestructiveOperation,
|
|
18528
|
-
findWriteKeyword: () => findWriteKeyword,
|
|
18529
|
-
extractFirstKeyword: () => extractFirstKeyword,
|
|
18530
|
-
extractAllKeywords: () => extractAllKeywords,
|
|
18531
|
-
enforceRedisPermission: () => enforceRedisPermission,
|
|
18532
|
-
enforcePermission: () => enforcePermission,
|
|
18533
|
-
enforceElasticsearchPermission: () => enforceElasticsearchPermission,
|
|
18534
|
-
determineConfidence: () => determineConfidence,
|
|
18535
|
-
detectCompositePatterns: () => detectCompositePatterns,
|
|
18536
|
-
containsMultipleStatements: () => containsMultipleStatements,
|
|
18537
|
-
classifyStatement: () => classifyStatement,
|
|
18538
|
-
classifyRedisCommand: () => classifyRedisCommand,
|
|
18539
|
-
classifyElasticsearchRequest: () => classifyElasticsearchRequest,
|
|
18540
|
-
checkPermission: () => checkPermission,
|
|
18541
|
-
SQL_WRITE_OR_DDL_KEYWORDS: () => SQL_WRITE_OR_DDL_KEYWORDS,
|
|
18542
|
-
SQL_DIALECTS: () => SQL_DIALECTS,
|
|
18543
|
-
PermissionError: () => PermissionError
|
|
18544
|
-
});
|
|
18641
|
+
// src/core/permission/sql-analysis.ts
|
|
18545
18642
|
function normalizeSQL(sql) {
|
|
18546
18643
|
return sql.replace(/--[^\n]*\n/g, `
|
|
18547
18644
|
`).replace(/\/\*[\s\S]*?\*\//g, " ").trim().replace(/\s+/g, " ");
|
|
@@ -18788,6 +18885,29 @@ function determineConfidence(type, _keyword, _sql) {
|
|
|
18788
18885
|
function removeParameterMarkers(sql) {
|
|
18789
18886
|
return sql.replace(/\$\d+/g, "").replace(/\?/g, "");
|
|
18790
18887
|
}
|
|
18888
|
+
var init_sql_analysis = __esm(() => {
|
|
18889
|
+
init_sql_lexical();
|
|
18890
|
+
});
|
|
18891
|
+
|
|
18892
|
+
// src/core/permission-guard.ts
|
|
18893
|
+
var exports_permission_guard = {};
|
|
18894
|
+
__export(exports_permission_guard, {
|
|
18895
|
+
toSqlDialect: () => toSqlDialect,
|
|
18896
|
+
permitsOperation: () => permitsOperation,
|
|
18897
|
+
permissionAtLeast: () => permissionAtLeast,
|
|
18898
|
+
minimumPermissionFor: () => minimumPermissionFor,
|
|
18899
|
+
findWriteKeyword: () => findWriteKeyword,
|
|
18900
|
+
enforcePermissionForType: () => enforcePermissionForType,
|
|
18901
|
+
enforcePermission: () => enforcePermission,
|
|
18902
|
+
containsMultipleStatements: () => containsMultipleStatements,
|
|
18903
|
+
classifyStatement: () => classifyStatement,
|
|
18904
|
+
classificationForType: () => classificationForType,
|
|
18905
|
+
checkPermissionForClassification: () => checkPermissionForClassification,
|
|
18906
|
+
checkPermission: () => checkPermission,
|
|
18907
|
+
SQL_WRITE_OR_DDL_KEYWORDS: () => SQL_WRITE_OR_DDL_KEYWORDS,
|
|
18908
|
+
SQL_DIALECTS: () => SQL_DIALECTS,
|
|
18909
|
+
PermissionError: () => PermissionError
|
|
18910
|
+
});
|
|
18791
18911
|
function classifyStatement(sql) {
|
|
18792
18912
|
const normalized = normalizeSQL(sql);
|
|
18793
18913
|
const stripped = stripCommentsAndStrings(normalized);
|
|
@@ -18853,15 +18973,33 @@ function checkPermission(sql, permission, dialect) {
|
|
|
18853
18973
|
if (permission !== "admin" && containsMultipleStatements(sql, dialect)) {
|
|
18854
18974
|
return {
|
|
18855
18975
|
allowed: false,
|
|
18856
|
-
reason: "
|
|
18857
|
-
classification
|
|
18976
|
+
reason: t("errors.multiple_statements_refused"),
|
|
18977
|
+
classification,
|
|
18978
|
+
requiredPermission: "admin"
|
|
18858
18979
|
};
|
|
18859
18980
|
}
|
|
18981
|
+
return checkPermissionForClassification(classification, permission);
|
|
18982
|
+
}
|
|
18983
|
+
function minimumPermissionFor(type) {
|
|
18984
|
+
return TIER_GRANTS.find((tier) => tier.types.includes(type))?.permission ?? "admin";
|
|
18985
|
+
}
|
|
18986
|
+
function refusalReason(type, permission) {
|
|
18987
|
+
return t_vars("errors.permission_requires_level", {
|
|
18988
|
+
type,
|
|
18989
|
+
minimum: minimumPermissionFor(type),
|
|
18990
|
+
permission
|
|
18991
|
+
});
|
|
18992
|
+
}
|
|
18993
|
+
function checkPermissionForClassification(classification, permission) {
|
|
18860
18994
|
if (permission !== "admin" && classification.escalatedFrom) {
|
|
18861
18995
|
return {
|
|
18862
18996
|
allowed: false,
|
|
18863
|
-
reason:
|
|
18864
|
-
|
|
18997
|
+
reason: t_vars("errors.escalated_write_requires_admin", {
|
|
18998
|
+
keyword: classification.escalatedFrom,
|
|
18999
|
+
permission
|
|
19000
|
+
}),
|
|
19001
|
+
classification,
|
|
19002
|
+
requiredPermission: "admin"
|
|
18865
19003
|
};
|
|
18866
19004
|
}
|
|
18867
19005
|
if (permission === "admin") {
|
|
@@ -18871,220 +19009,64 @@ function checkPermission(sql, permission, dialect) {
|
|
|
18871
19009
|
classification
|
|
18872
19010
|
};
|
|
18873
19011
|
}
|
|
18874
|
-
|
|
18875
|
-
|
|
18876
|
-
|
|
18877
|
-
return {
|
|
18878
|
-
allowed: true,
|
|
18879
|
-
reason: `${classification.type} operation allowed in data-admin mode`,
|
|
18880
|
-
classification
|
|
18881
|
-
};
|
|
18882
|
-
}
|
|
18883
|
-
return {
|
|
18884
|
-
allowed: false,
|
|
18885
|
-
reason: `${classification.type} operation requires admin permission`,
|
|
18886
|
-
classification
|
|
18887
|
-
};
|
|
18888
|
-
}
|
|
18889
|
-
if (permission === "read-write") {
|
|
18890
|
-
const allowedTypes = ["SELECT", "INSERT", "UPDATE", "SHOW", "DESCRIBE", "EXPLAIN"];
|
|
18891
|
-
if (allowedTypes.includes(classification.type)) {
|
|
18892
|
-
return {
|
|
18893
|
-
allowed: true,
|
|
18894
|
-
reason: `${classification.type} operation allowed in read-write mode`,
|
|
18895
|
-
classification
|
|
18896
|
-
};
|
|
18897
|
-
}
|
|
18898
|
-
return {
|
|
18899
|
-
allowed: false,
|
|
18900
|
-
reason: `${classification.type} operation requires data-admin or admin permission`,
|
|
18901
|
-
classification
|
|
18902
|
-
};
|
|
18903
|
-
}
|
|
18904
|
-
if (permission === "query-only") {
|
|
18905
|
-
const allowedTypes = ["SELECT", "SHOW", "DESCRIBE", "EXPLAIN"];
|
|
19012
|
+
const tierIndex = TIER_GRANTS.findIndex((tier) => tier.permission === permission);
|
|
19013
|
+
if (tierIndex !== -1) {
|
|
19014
|
+
const allowedTypes = TIER_GRANTS.slice(0, tierIndex + 1).flatMap((tier) => tier.types);
|
|
18906
19015
|
if (allowedTypes.includes(classification.type)) {
|
|
18907
19016
|
return {
|
|
18908
19017
|
allowed: true,
|
|
18909
|
-
reason: `${classification.type} operation allowed in
|
|
19018
|
+
reason: `${classification.type} operation allowed in ${permission} mode`,
|
|
18910
19019
|
classification
|
|
18911
19020
|
};
|
|
18912
19021
|
}
|
|
18913
|
-
const isUnknown = classification.type === "UNKNOWN";
|
|
19022
|
+
const isUnknown = permission === "query-only" && classification.type === "UNKNOWN";
|
|
18914
19023
|
return {
|
|
18915
19024
|
allowed: false,
|
|
18916
|
-
reason: isUnknown ?
|
|
18917
|
-
classification
|
|
19025
|
+
reason: isUnknown ? t("errors.unknown_statement_query_only") : refusalReason(classification.type, permission),
|
|
19026
|
+
classification,
|
|
19027
|
+
requiredPermission: isUnknown ? "read-write" : minimumPermissionFor(classification.type)
|
|
18918
19028
|
};
|
|
18919
19029
|
}
|
|
18920
19030
|
return {
|
|
18921
19031
|
allowed: false,
|
|
18922
19032
|
reason: `Unknown permission level: ${permission}`,
|
|
18923
|
-
classification
|
|
19033
|
+
classification,
|
|
19034
|
+
requiredPermission: "admin"
|
|
18924
19035
|
};
|
|
18925
19036
|
}
|
|
18926
|
-
function
|
|
18927
|
-
const result = checkPermission(sql, permission, dialect);
|
|
18928
|
-
if (!result.allowed) {
|
|
18929
|
-
throw new PermissionError(result.reason, result.classification, permission);
|
|
18930
|
-
}
|
|
18931
|
-
return result.classification;
|
|
18932
|
-
}
|
|
18933
|
-
function classifyRedisCommand(command) {
|
|
18934
|
-
const head = command.trim().split(/\s+/)[0]?.toUpperCase() ?? "";
|
|
18935
|
-
const required = REDIS_COMMAND_PERMISSION[head];
|
|
18936
|
-
let type;
|
|
18937
|
-
if (!required)
|
|
18938
|
-
type = "UNKNOWN";
|
|
18939
|
-
else if (required === "query-only")
|
|
18940
|
-
type = "SELECT";
|
|
18941
|
-
else if (required === "read-write")
|
|
18942
|
-
type = "UPDATE";
|
|
18943
|
-
else if (required === "data-admin")
|
|
18944
|
-
type = "DELETE";
|
|
18945
|
-
else
|
|
18946
|
-
type = "DROP";
|
|
19037
|
+
function classificationForType(type) {
|
|
18947
19038
|
return {
|
|
18948
|
-
command: head,
|
|
18949
|
-
requiredPermission: required ?? "unknown",
|
|
18950
19039
|
type,
|
|
18951
|
-
isDangerous:
|
|
18952
|
-
|
|
18953
|
-
}
|
|
18954
|
-
function permissionAtLeast(actual, required) {
|
|
18955
|
-
return PERMISSION_RANK[actual] >= PERMISSION_RANK[required];
|
|
18956
|
-
}
|
|
18957
|
-
function enforceRedisPermission(command, permission) {
|
|
18958
|
-
const classification = classifyRedisCommand(command);
|
|
18959
|
-
const required = classification.requiredPermission;
|
|
18960
|
-
const stmt = {
|
|
18961
|
-
type: classification.type,
|
|
18962
|
-
isDangerous: classification.isDangerous,
|
|
18963
|
-
keywords: [classification.command],
|
|
19040
|
+
isDangerous: isDestructiveOperation(type),
|
|
19041
|
+
keywords: [type],
|
|
18964
19042
|
isComposite: false,
|
|
18965
|
-
confidence:
|
|
19043
|
+
confidence: "HIGH"
|
|
18966
19044
|
};
|
|
18967
|
-
if (required === "unknown") {
|
|
18968
|
-
throw new PermissionError(`Redis command "${classification.command}" is not whitelisted; refusing to execute`, stmt, "admin");
|
|
18969
|
-
}
|
|
18970
|
-
if (!permissionAtLeast(permission, required)) {
|
|
18971
|
-
throw new PermissionError(`Redis command "${classification.command}" requires ${required} permission`, stmt, required);
|
|
18972
|
-
}
|
|
18973
|
-
return classification;
|
|
18974
19045
|
}
|
|
18975
|
-
function
|
|
18976
|
-
|
|
18977
|
-
const path = request.apiPath.toLowerCase();
|
|
18978
|
-
if (path.includes("_bulk")) {
|
|
18979
|
-
return classifyElasticsearchBulk(request.body ?? "");
|
|
18980
|
-
}
|
|
18981
|
-
if (path.includes("_search") || path.includes("_count") || path.includes("_mapping") || path.includes("_settings") || path.includes("_alias") || method === "GET" && (path.includes("_doc") || path.includes("_source"))) {
|
|
18982
|
-
return {
|
|
18983
|
-
type: "SELECT",
|
|
18984
|
-
isDangerous: false,
|
|
18985
|
-
keywords: [method, path],
|
|
18986
|
-
isComposite: false,
|
|
18987
|
-
confidence: "HIGH"
|
|
18988
|
-
};
|
|
18989
|
-
}
|
|
18990
|
-
if (path.includes("_update") || method === "POST" && path.includes("_doc")) {
|
|
18991
|
-
return {
|
|
18992
|
-
type: "UPDATE",
|
|
18993
|
-
isDangerous: false,
|
|
18994
|
-
keywords: [method, path],
|
|
18995
|
-
isComposite: false,
|
|
18996
|
-
confidence: "HIGH"
|
|
18997
|
-
};
|
|
18998
|
-
}
|
|
18999
|
-
if (method === "PUT" && (path.includes("_doc") || path.includes("_create"))) {
|
|
19000
|
-
return {
|
|
19001
|
-
type: "INSERT",
|
|
19002
|
-
isDangerous: false,
|
|
19003
|
-
keywords: [method, path],
|
|
19004
|
-
isComposite: false,
|
|
19005
|
-
confidence: "HIGH"
|
|
19006
|
-
};
|
|
19007
|
-
}
|
|
19008
|
-
if (method === "DELETE") {
|
|
19009
|
-
return {
|
|
19010
|
-
type: "DELETE",
|
|
19011
|
-
isDangerous: true,
|
|
19012
|
-
keywords: [method, path],
|
|
19013
|
-
isComposite: false,
|
|
19014
|
-
confidence: "HIGH"
|
|
19015
|
-
};
|
|
19016
|
-
}
|
|
19017
|
-
return {
|
|
19018
|
-
type: "DROP",
|
|
19019
|
-
isDangerous: true,
|
|
19020
|
-
keywords: [method, path],
|
|
19021
|
-
isComposite: false,
|
|
19022
|
-
confidence: "LOW"
|
|
19023
|
-
};
|
|
19046
|
+
function permitsOperation(type, permission) {
|
|
19047
|
+
return checkPermissionForClassification(classificationForType(type), permission).allowed;
|
|
19024
19048
|
}
|
|
19025
|
-
function
|
|
19026
|
-
const
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
for (const line of lines) {
|
|
19031
|
-
try {
|
|
19032
|
-
const action = JSON.parse(line);
|
|
19033
|
-
const op = Object.keys(action)[0];
|
|
19034
|
-
if (op === "delete") {
|
|
19035
|
-
highestType = "DELETE";
|
|
19036
|
-
isDangerous = true;
|
|
19037
|
-
break;
|
|
19038
|
-
}
|
|
19039
|
-
if (op === "update" && highestType !== "DELETE") {
|
|
19040
|
-
highestType = "UPDATE";
|
|
19041
|
-
}
|
|
19042
|
-
if ((op === "index" || op === "create") && !["DELETE", "UPDATE"].includes(highestType)) {
|
|
19043
|
-
highestType = "INSERT";
|
|
19044
|
-
}
|
|
19045
|
-
} catch {}
|
|
19049
|
+
function enforcePermissionForType(type, permission) {
|
|
19050
|
+
const classification = classificationForType(type);
|
|
19051
|
+
const result = checkPermissionForClassification(classification, permission);
|
|
19052
|
+
if (!result.allowed) {
|
|
19053
|
+
throw new PermissionError(result.reason, classification, minimumPermissionFor(type));
|
|
19046
19054
|
}
|
|
19047
|
-
return {
|
|
19048
|
-
type: highestType,
|
|
19049
|
-
isDangerous,
|
|
19050
|
-
keywords: ["BULK"],
|
|
19051
|
-
isComposite: true,
|
|
19052
|
-
confidence: "HIGH"
|
|
19053
|
-
};
|
|
19054
19055
|
}
|
|
19055
|
-
function
|
|
19056
|
-
const
|
|
19057
|
-
const result = checkElasticsearchPermission(classification, permission);
|
|
19056
|
+
function enforcePermission(sql, permission, dialect) {
|
|
19057
|
+
const result = checkPermission(sql, permission, dialect);
|
|
19058
19058
|
if (!result.allowed) {
|
|
19059
|
-
throw new PermissionError(result.reason, classification,
|
|
19059
|
+
throw new PermissionError(result.reason, result.classification, result.requiredPermission ?? "admin");
|
|
19060
19060
|
}
|
|
19061
|
-
return classification;
|
|
19061
|
+
return result.classification;
|
|
19062
19062
|
}
|
|
19063
|
-
function
|
|
19064
|
-
|
|
19065
|
-
return { allowed: true, reason: "Admin" };
|
|
19066
|
-
if (permission === "data-admin") {
|
|
19067
|
-
const allowed = ["SELECT", "INSERT", "UPDATE", "DELETE"];
|
|
19068
|
-
if (allowed.includes(classification.type))
|
|
19069
|
-
return { allowed: true, reason: "Data-Admin" };
|
|
19070
|
-
}
|
|
19071
|
-
if (permission === "read-write") {
|
|
19072
|
-
const allowed = ["SELECT", "INSERT", "UPDATE"];
|
|
19073
|
-
if (allowed.includes(classification.type))
|
|
19074
|
-
return { allowed: true, reason: "Read-Write" };
|
|
19075
|
-
}
|
|
19076
|
-
if (permission === "query-only") {
|
|
19077
|
-
if (classification.type === "SELECT")
|
|
19078
|
-
return { allowed: true, reason: "Query-Only" };
|
|
19079
|
-
}
|
|
19080
|
-
return {
|
|
19081
|
-
allowed: false,
|
|
19082
|
-
reason: `Elasticsearch ${classification.type} operation requires higher permission tier`
|
|
19083
|
-
};
|
|
19063
|
+
function permissionAtLeast(actual, required) {
|
|
19064
|
+
return PERMISSION_RANK[actual] >= PERMISSION_RANK[required];
|
|
19084
19065
|
}
|
|
19085
|
-
var PermissionError, SQL_DIALECTS, SQL_WRITE_OR_DDL_KEYWORDS, SQL_LOCK_CLAUSE, ESCALATABLE_READ_TYPES,
|
|
19066
|
+
var PermissionError, SQL_DIALECTS, SQL_WRITE_OR_DDL_KEYWORDS, SQL_LOCK_CLAUSE, ESCALATABLE_READ_TYPES, TIER_GRANTS, PERMISSION_RANK;
|
|
19086
19067
|
var init_permission_guard = __esm(() => {
|
|
19087
|
-
|
|
19068
|
+
init_message_loader();
|
|
19069
|
+
init_sql_analysis();
|
|
19088
19070
|
PermissionError = class PermissionError extends Error {
|
|
19089
19071
|
classification;
|
|
19090
19072
|
requiredPermission;
|
|
@@ -19100,90 +19082,11 @@ var init_permission_guard = __esm(() => {
|
|
|
19100
19082
|
SQL_WRITE_OR_DDL_KEYWORDS = /(?<![.\w])(INSERT|UPDATE|DELETE|MERGE|UPSERT|REPLACE|TRUNCATE|DROP|ALTER|CREATE|GRANT|REVOKE|RENAME|INTO)\b(?!\s*\()/i;
|
|
19101
19083
|
SQL_LOCK_CLAUSE = /\bFOR\s+(?:NO\s+KEY\s+)?UPDATE\b|\bFOR\s+(?:KEY\s+)?SHARE\b/gi;
|
|
19102
19084
|
ESCALATABLE_READ_TYPES = new Set(["SELECT", "EXPLAIN", "DESCRIBE"]);
|
|
19103
|
-
|
|
19104
|
-
|
|
19105
|
-
|
|
19106
|
-
|
|
19107
|
-
|
|
19108
|
-
TTL: "query-only",
|
|
19109
|
-
PTTL: "query-only",
|
|
19110
|
-
TYPE: "query-only",
|
|
19111
|
-
SCAN: "query-only",
|
|
19112
|
-
HGET: "query-only",
|
|
19113
|
-
HGETALL: "query-only",
|
|
19114
|
-
HKEYS: "query-only",
|
|
19115
|
-
HVALS: "query-only",
|
|
19116
|
-
HLEN: "query-only",
|
|
19117
|
-
HEXISTS: "query-only",
|
|
19118
|
-
HMGET: "query-only",
|
|
19119
|
-
LRANGE: "query-only",
|
|
19120
|
-
LLEN: "query-only",
|
|
19121
|
-
LINDEX: "query-only",
|
|
19122
|
-
SMEMBERS: "query-only",
|
|
19123
|
-
SCARD: "query-only",
|
|
19124
|
-
SISMEMBER: "query-only",
|
|
19125
|
-
ZRANGE: "query-only",
|
|
19126
|
-
ZREVRANGE: "query-only",
|
|
19127
|
-
ZRANGEBYSCORE: "query-only",
|
|
19128
|
-
ZCARD: "query-only",
|
|
19129
|
-
ZSCORE: "query-only",
|
|
19130
|
-
PING: "query-only",
|
|
19131
|
-
ECHO: "query-only",
|
|
19132
|
-
SET: "read-write",
|
|
19133
|
-
SETEX: "read-write",
|
|
19134
|
-
SETNX: "read-write",
|
|
19135
|
-
PSETEX: "read-write",
|
|
19136
|
-
MSET: "read-write",
|
|
19137
|
-
MSETNX: "read-write",
|
|
19138
|
-
APPEND: "read-write",
|
|
19139
|
-
INCR: "read-write",
|
|
19140
|
-
INCRBY: "read-write",
|
|
19141
|
-
DECR: "read-write",
|
|
19142
|
-
DECRBY: "read-write",
|
|
19143
|
-
HSET: "read-write",
|
|
19144
|
-
HSETNX: "read-write",
|
|
19145
|
-
HMSET: "read-write",
|
|
19146
|
-
HINCRBY: "read-write",
|
|
19147
|
-
LPUSH: "read-write",
|
|
19148
|
-
RPUSH: "read-write",
|
|
19149
|
-
LPOP: "read-write",
|
|
19150
|
-
RPOP: "read-write",
|
|
19151
|
-
LSET: "read-write",
|
|
19152
|
-
LREM: "read-write",
|
|
19153
|
-
SADD: "read-write",
|
|
19154
|
-
SREM: "read-write",
|
|
19155
|
-
ZADD: "read-write",
|
|
19156
|
-
ZREM: "read-write",
|
|
19157
|
-
XADD: "read-write",
|
|
19158
|
-
XDEL: "data-admin",
|
|
19159
|
-
XLEN: "query-only",
|
|
19160
|
-
XREAD: "query-only",
|
|
19161
|
-
XRANGE: "query-only",
|
|
19162
|
-
XREVRANGE: "query-only",
|
|
19163
|
-
EXPIRE: "read-write",
|
|
19164
|
-
EXPIREAT: "read-write",
|
|
19165
|
-
PEXPIRE: "read-write",
|
|
19166
|
-
PERSIST: "read-write",
|
|
19167
|
-
RENAME: "read-write",
|
|
19168
|
-
DEL: "data-admin",
|
|
19169
|
-
UNLINK: "data-admin",
|
|
19170
|
-
HDEL: "data-admin",
|
|
19171
|
-
FLUSHDB: "admin",
|
|
19172
|
-
FLUSHALL: "admin",
|
|
19173
|
-
CONFIG: "admin",
|
|
19174
|
-
INFO: "admin",
|
|
19175
|
-
CLIENT: "admin",
|
|
19176
|
-
DEBUG: "admin",
|
|
19177
|
-
SHUTDOWN: "admin",
|
|
19178
|
-
KEYS: "admin",
|
|
19179
|
-
MONITOR: "admin",
|
|
19180
|
-
SAVE: "admin",
|
|
19181
|
-
BGSAVE: "admin",
|
|
19182
|
-
BGREWRITEAOF: "admin",
|
|
19183
|
-
REPLICAOF: "admin",
|
|
19184
|
-
SLAVEOF: "admin",
|
|
19185
|
-
ACL: "admin"
|
|
19186
|
-
};
|
|
19085
|
+
TIER_GRANTS = [
|
|
19086
|
+
{ permission: "query-only", types: ["SELECT", "SHOW", "DESCRIBE", "EXPLAIN"] },
|
|
19087
|
+
{ permission: "read-write", types: ["INSERT", "UPDATE"] },
|
|
19088
|
+
{ permission: "data-admin", types: ["DELETE"] }
|
|
19089
|
+
];
|
|
19187
19090
|
PERMISSION_RANK = {
|
|
19188
19091
|
"query-only": 1,
|
|
19189
19092
|
"read-write": 2,
|
|
@@ -21874,6 +21777,7 @@ function inferColumnType(value) {
|
|
|
21874
21777
|
}
|
|
21875
21778
|
var init_query_executor = __esm(() => {
|
|
21876
21779
|
init_permission_guard();
|
|
21780
|
+
init_sql_analysis();
|
|
21877
21781
|
init_error_suggester();
|
|
21878
21782
|
init_sql_tables();
|
|
21879
21783
|
init_applied_limit();
|
|
@@ -22173,6 +22077,263 @@ function assertFanOutReadOnlySql(sql, dialect) {
|
|
|
22173
22077
|
var init_query_fanout = __esm(() => {
|
|
22174
22078
|
init_cli_error();
|
|
22175
22079
|
init_permission_guard();
|
|
22080
|
+
init_sql_analysis();
|
|
22081
|
+
});
|
|
22082
|
+
|
|
22083
|
+
// src/core/permission/elasticsearch.ts
|
|
22084
|
+
function classifyElasticsearchRequest(request) {
|
|
22085
|
+
const method = request.method.toUpperCase();
|
|
22086
|
+
const path2 = request.apiPath.toLowerCase();
|
|
22087
|
+
if (path2.includes("_bulk")) {
|
|
22088
|
+
return classifyElasticsearchBulk(request.body ?? "");
|
|
22089
|
+
}
|
|
22090
|
+
const readMethod = method === "GET" || method === "HEAD";
|
|
22091
|
+
const searchPath = path2.includes("_search") || path2.includes("_count");
|
|
22092
|
+
if (searchPath && (readMethod || method === "POST") || readMethod && (path2.includes("_mapping") || path2.includes("_settings") || path2.includes("_alias") || path2.includes("_doc") || path2.includes("_source"))) {
|
|
22093
|
+
return {
|
|
22094
|
+
type: "SELECT",
|
|
22095
|
+
isDangerous: false,
|
|
22096
|
+
keywords: [method, path2],
|
|
22097
|
+
isComposite: false,
|
|
22098
|
+
confidence: "HIGH"
|
|
22099
|
+
};
|
|
22100
|
+
}
|
|
22101
|
+
if (path2.includes("_update") || method === "POST" && path2.includes("_doc")) {
|
|
22102
|
+
return {
|
|
22103
|
+
type: "UPDATE",
|
|
22104
|
+
isDangerous: false,
|
|
22105
|
+
keywords: [method, path2],
|
|
22106
|
+
isComposite: false,
|
|
22107
|
+
confidence: "HIGH"
|
|
22108
|
+
};
|
|
22109
|
+
}
|
|
22110
|
+
if (method === "PUT" && (path2.includes("_doc") || path2.includes("_create"))) {
|
|
22111
|
+
return {
|
|
22112
|
+
type: "INSERT",
|
|
22113
|
+
isDangerous: false,
|
|
22114
|
+
keywords: [method, path2],
|
|
22115
|
+
isComposite: false,
|
|
22116
|
+
confidence: "HIGH"
|
|
22117
|
+
};
|
|
22118
|
+
}
|
|
22119
|
+
if (method === "DELETE") {
|
|
22120
|
+
if (ES_DOCUMENT_PATH.test(path2)) {
|
|
22121
|
+
return {
|
|
22122
|
+
type: "DELETE",
|
|
22123
|
+
isDangerous: true,
|
|
22124
|
+
keywords: [method, path2],
|
|
22125
|
+
isComposite: false,
|
|
22126
|
+
confidence: "HIGH"
|
|
22127
|
+
};
|
|
22128
|
+
}
|
|
22129
|
+
return {
|
|
22130
|
+
type: "DROP",
|
|
22131
|
+
isDangerous: true,
|
|
22132
|
+
keywords: [method, path2],
|
|
22133
|
+
isComposite: false,
|
|
22134
|
+
confidence: "HIGH"
|
|
22135
|
+
};
|
|
22136
|
+
}
|
|
22137
|
+
return {
|
|
22138
|
+
type: "DROP",
|
|
22139
|
+
isDangerous: true,
|
|
22140
|
+
keywords: [method, path2],
|
|
22141
|
+
isComposite: false,
|
|
22142
|
+
confidence: "LOW"
|
|
22143
|
+
};
|
|
22144
|
+
}
|
|
22145
|
+
function classifyElasticsearchBulk(body) {
|
|
22146
|
+
const lines = body.split(`
|
|
22147
|
+
`).filter((l) => l.trim().length > 0);
|
|
22148
|
+
let highestType = "SELECT";
|
|
22149
|
+
let isDangerous = false;
|
|
22150
|
+
for (const line of lines) {
|
|
22151
|
+
try {
|
|
22152
|
+
const action = JSON.parse(line);
|
|
22153
|
+
const op = Object.keys(action)[0];
|
|
22154
|
+
if (op === "delete") {
|
|
22155
|
+
highestType = "DELETE";
|
|
22156
|
+
isDangerous = true;
|
|
22157
|
+
break;
|
|
22158
|
+
}
|
|
22159
|
+
if (op === "update" && highestType !== "DELETE") {
|
|
22160
|
+
highestType = "UPDATE";
|
|
22161
|
+
}
|
|
22162
|
+
if ((op === "index" || op === "create") && !["DELETE", "UPDATE"].includes(highestType)) {
|
|
22163
|
+
highestType = "INSERT";
|
|
22164
|
+
}
|
|
22165
|
+
} catch {}
|
|
22166
|
+
}
|
|
22167
|
+
return {
|
|
22168
|
+
type: highestType,
|
|
22169
|
+
isDangerous,
|
|
22170
|
+
keywords: ["BULK"],
|
|
22171
|
+
isComposite: true,
|
|
22172
|
+
confidence: "HIGH"
|
|
22173
|
+
};
|
|
22174
|
+
}
|
|
22175
|
+
function enforceElasticsearchPermission(request, permission) {
|
|
22176
|
+
const classification = classifyElasticsearchRequest(request);
|
|
22177
|
+
const result = checkElasticsearchPermission(classification, permission);
|
|
22178
|
+
if (!result.allowed) {
|
|
22179
|
+
throw new PermissionError(result.reason, classification, result.requiredPermission ?? "admin");
|
|
22180
|
+
}
|
|
22181
|
+
return classification;
|
|
22182
|
+
}
|
|
22183
|
+
function checkElasticsearchPermission(classification, permission) {
|
|
22184
|
+
if (permitsOperation(classification.type, permission)) {
|
|
22185
|
+
return { allowed: true, reason: `${classification.type} allowed in ${permission} mode` };
|
|
22186
|
+
}
|
|
22187
|
+
const minimum = minimumPermissionFor(classification.type);
|
|
22188
|
+
return {
|
|
22189
|
+
allowed: false,
|
|
22190
|
+
reason: t_vars("errors.elasticsearch_requires_level", {
|
|
22191
|
+
type: classification.type,
|
|
22192
|
+
minimum,
|
|
22193
|
+
permission
|
|
22194
|
+
}),
|
|
22195
|
+
requiredPermission: minimum
|
|
22196
|
+
};
|
|
22197
|
+
}
|
|
22198
|
+
var ES_DOCUMENT_PATH;
|
|
22199
|
+
var init_elasticsearch = __esm(() => {
|
|
22200
|
+
init_permission_guard();
|
|
22201
|
+
init_message_loader();
|
|
22202
|
+
ES_DOCUMENT_PATH = /\/(?:_doc|_source)\/[^/?]+/;
|
|
22203
|
+
});
|
|
22204
|
+
|
|
22205
|
+
// src/core/permission/redis.ts
|
|
22206
|
+
var exports_redis = {};
|
|
22207
|
+
__export(exports_redis, {
|
|
22208
|
+
enforceRedisPermission: () => enforceRedisPermission,
|
|
22209
|
+
classifyRedisCommand: () => classifyRedisCommand
|
|
22210
|
+
});
|
|
22211
|
+
function classifyRedisCommand(command) {
|
|
22212
|
+
const head = command.trim().split(/\s+/)[0]?.toUpperCase() ?? "";
|
|
22213
|
+
const required = REDIS_COMMAND_PERMISSION[head];
|
|
22214
|
+
let type;
|
|
22215
|
+
if (!required)
|
|
22216
|
+
type = "UNKNOWN";
|
|
22217
|
+
else if (required === "query-only")
|
|
22218
|
+
type = "SELECT";
|
|
22219
|
+
else if (required === "read-write")
|
|
22220
|
+
type = "UPDATE";
|
|
22221
|
+
else if (required === "data-admin")
|
|
22222
|
+
type = "DELETE";
|
|
22223
|
+
else
|
|
22224
|
+
type = "DROP";
|
|
22225
|
+
return {
|
|
22226
|
+
command: head,
|
|
22227
|
+
requiredPermission: required ?? "unknown",
|
|
22228
|
+
type,
|
|
22229
|
+
isDangerous: required === "admin" || required === "data-admin"
|
|
22230
|
+
};
|
|
22231
|
+
}
|
|
22232
|
+
function enforceRedisPermission(command, permission) {
|
|
22233
|
+
const classification = classifyRedisCommand(command);
|
|
22234
|
+
const required = classification.requiredPermission;
|
|
22235
|
+
const stmt = {
|
|
22236
|
+
type: classification.type,
|
|
22237
|
+
isDangerous: classification.isDangerous,
|
|
22238
|
+
keywords: [classification.command],
|
|
22239
|
+
isComposite: false,
|
|
22240
|
+
confidence: required === "unknown" ? "LOW" : "HIGH"
|
|
22241
|
+
};
|
|
22242
|
+
if (required === "unknown") {
|
|
22243
|
+
throw new PermissionError(`Redis command "${classification.command}" is not whitelisted; refusing to execute`, stmt, "admin");
|
|
22244
|
+
}
|
|
22245
|
+
if (!permissionAtLeast(permission, required)) {
|
|
22246
|
+
throw new PermissionError(`Redis command "${classification.command}" requires ${required} permission`, stmt, required);
|
|
22247
|
+
}
|
|
22248
|
+
return classification;
|
|
22249
|
+
}
|
|
22250
|
+
var REDIS_COMMAND_PERMISSION;
|
|
22251
|
+
var init_redis = __esm(() => {
|
|
22252
|
+
init_permission_guard();
|
|
22253
|
+
REDIS_COMMAND_PERMISSION = {
|
|
22254
|
+
GET: "query-only",
|
|
22255
|
+
MGET: "query-only",
|
|
22256
|
+
STRLEN: "query-only",
|
|
22257
|
+
EXISTS: "query-only",
|
|
22258
|
+
TTL: "query-only",
|
|
22259
|
+
PTTL: "query-only",
|
|
22260
|
+
TYPE: "query-only",
|
|
22261
|
+
SCAN: "query-only",
|
|
22262
|
+
HGET: "query-only",
|
|
22263
|
+
HGETALL: "query-only",
|
|
22264
|
+
HKEYS: "query-only",
|
|
22265
|
+
HVALS: "query-only",
|
|
22266
|
+
HLEN: "query-only",
|
|
22267
|
+
HEXISTS: "query-only",
|
|
22268
|
+
HMGET: "query-only",
|
|
22269
|
+
LRANGE: "query-only",
|
|
22270
|
+
LLEN: "query-only",
|
|
22271
|
+
LINDEX: "query-only",
|
|
22272
|
+
SMEMBERS: "query-only",
|
|
22273
|
+
SCARD: "query-only",
|
|
22274
|
+
SISMEMBER: "query-only",
|
|
22275
|
+
ZRANGE: "query-only",
|
|
22276
|
+
ZREVRANGE: "query-only",
|
|
22277
|
+
ZRANGEBYSCORE: "query-only",
|
|
22278
|
+
ZCARD: "query-only",
|
|
22279
|
+
ZSCORE: "query-only",
|
|
22280
|
+
PING: "query-only",
|
|
22281
|
+
ECHO: "query-only",
|
|
22282
|
+
SET: "read-write",
|
|
22283
|
+
SETEX: "read-write",
|
|
22284
|
+
SETNX: "read-write",
|
|
22285
|
+
PSETEX: "read-write",
|
|
22286
|
+
MSET: "read-write",
|
|
22287
|
+
MSETNX: "read-write",
|
|
22288
|
+
APPEND: "read-write",
|
|
22289
|
+
INCR: "read-write",
|
|
22290
|
+
INCRBY: "read-write",
|
|
22291
|
+
DECR: "read-write",
|
|
22292
|
+
DECRBY: "read-write",
|
|
22293
|
+
HSET: "read-write",
|
|
22294
|
+
HSETNX: "read-write",
|
|
22295
|
+
HMSET: "read-write",
|
|
22296
|
+
HINCRBY: "read-write",
|
|
22297
|
+
LPUSH: "read-write",
|
|
22298
|
+
RPUSH: "read-write",
|
|
22299
|
+
LPOP: "read-write",
|
|
22300
|
+
RPOP: "read-write",
|
|
22301
|
+
LSET: "read-write",
|
|
22302
|
+
LREM: "read-write",
|
|
22303
|
+
SADD: "read-write",
|
|
22304
|
+
SREM: "read-write",
|
|
22305
|
+
ZADD: "read-write",
|
|
22306
|
+
ZREM: "read-write",
|
|
22307
|
+
XADD: "read-write",
|
|
22308
|
+
XDEL: "data-admin",
|
|
22309
|
+
XLEN: "query-only",
|
|
22310
|
+
XREAD: "query-only",
|
|
22311
|
+
XRANGE: "query-only",
|
|
22312
|
+
XREVRANGE: "query-only",
|
|
22313
|
+
EXPIRE: "read-write",
|
|
22314
|
+
EXPIREAT: "read-write",
|
|
22315
|
+
PEXPIRE: "read-write",
|
|
22316
|
+
PERSIST: "read-write",
|
|
22317
|
+
RENAME: "read-write",
|
|
22318
|
+
DEL: "data-admin",
|
|
22319
|
+
UNLINK: "data-admin",
|
|
22320
|
+
HDEL: "data-admin",
|
|
22321
|
+
FLUSHDB: "admin",
|
|
22322
|
+
FLUSHALL: "admin",
|
|
22323
|
+
CONFIG: "admin",
|
|
22324
|
+
INFO: "admin",
|
|
22325
|
+
CLIENT: "admin",
|
|
22326
|
+
DEBUG: "admin",
|
|
22327
|
+
SHUTDOWN: "admin",
|
|
22328
|
+
KEYS: "admin",
|
|
22329
|
+
MONITOR: "admin",
|
|
22330
|
+
SAVE: "admin",
|
|
22331
|
+
BGSAVE: "admin",
|
|
22332
|
+
BGREWRITEAOF: "admin",
|
|
22333
|
+
REPLICAOF: "admin",
|
|
22334
|
+
SLAVEOF: "admin",
|
|
22335
|
+
ACL: "admin"
|
|
22336
|
+
};
|
|
22176
22337
|
});
|
|
22177
22338
|
|
|
22178
22339
|
// src/commands/query-table-schema.ts
|
|
@@ -22469,7 +22630,7 @@ async function preflightQuery(query, options, context, fieldSelection, multiConn
|
|
|
22469
22630
|
if (multiConnection) {
|
|
22470
22631
|
throw new Error("Redis queries do not support multiple connections");
|
|
22471
22632
|
}
|
|
22472
|
-
const { enforceRedisPermission: enforceRedisPermission2 } = await Promise.resolve().then(() => (
|
|
22633
|
+
const { enforceRedisPermission: enforceRedisPermission2 } = await Promise.resolve().then(() => (init_redis(), exports_redis));
|
|
22473
22634
|
enforceRedisPermission2(query, config.permission);
|
|
22474
22635
|
return;
|
|
22475
22636
|
}
|
|
@@ -22890,7 +23051,7 @@ var init_query = __esm(() => {
|
|
|
22890
23051
|
init_field_projection();
|
|
22891
23052
|
init_connection_selector();
|
|
22892
23053
|
init_query_fanout();
|
|
22893
|
-
|
|
23054
|
+
init_elasticsearch();
|
|
22894
23055
|
init_slow_query_advisory();
|
|
22895
23056
|
ALLOWED_FORMATS3 = ["table", "json", "csv"];
|
|
22896
23057
|
defaultQueryCommandRuntime = {
|
|
@@ -22949,7 +23110,7 @@ function registerQCommand(program2, run) {
|
|
|
22949
23110
|
});
|
|
22950
23111
|
}
|
|
22951
23112
|
function registerInsertCommand(program2, run) {
|
|
22952
|
-
program2.command("insert <table>").description(t("insert.description")).option("--data <json>", "JSON object to insert").option("--dry-run", "Show generated SQL without executing").option("--force", "Skip confirmation prompt").option("--plan", "Analyze risk without connecting or executing").option("--format <type>", "Output format
|
|
23113
|
+
program2.command("insert <table>").description(t("insert.description")).option("--data <json>", "JSON object to insert").option("--dry-run", "Show generated SQL without executing").option("--force", "Skip confirmation prompt").option("--plan", "Analyze risk without connecting or executing").option("--format <type>", "Output format: text or json", "text").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (table, options, command) => {
|
|
22953
23114
|
try {
|
|
22954
23115
|
await run(table, options, command);
|
|
22955
23116
|
} catch (error) {
|
|
@@ -22963,7 +23124,7 @@ function registerInsertCommand(program2, run) {
|
|
|
22963
23124
|
});
|
|
22964
23125
|
}
|
|
22965
23126
|
function registerUpdateCommand(program2, run) {
|
|
22966
|
-
program2.command("update <table>").description(t("update.description")).option("--where <condition>", 'WHERE clause (required, e.g. "id=1")').option("--set <json>", `JSON with fields to update (required, e.g. '{"name":"Bob"}')`).option("--dry-run", "Show generated SQL without executing").option("--force", "Skip confirmation prompt").option("--plan", "Analyze risk without connecting or executing").option("--format <type>", "Output format
|
|
23127
|
+
program2.command("update <table>").description(t("update.description")).option("--where <condition>", 'WHERE clause (required, e.g. "id=1")').option("--set <json>", `JSON with fields to update (required, e.g. '{"name":"Bob"}')`).option("--dry-run", "Show generated SQL without executing").option("--force", "Skip confirmation prompt").option("--plan", "Analyze risk without connecting or executing").option("--format <type>", "Output format: text or json", "text").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (table, options, command) => {
|
|
22967
23128
|
try {
|
|
22968
23129
|
await run(table, options, command);
|
|
22969
23130
|
} catch (error) {
|
|
@@ -22977,7 +23138,7 @@ function registerUpdateCommand(program2, run) {
|
|
|
22977
23138
|
});
|
|
22978
23139
|
}
|
|
22979
23140
|
function registerDeleteCommand(program2, run) {
|
|
22980
|
-
program2.command("delete <table>").description(t("delete.description")).option("--where <condition>", 'WHERE clause (required, e.g. "id=1")').option("--dry-run", "Show generated SQL without executing").option("--force", "Skip confirmation prompt").option("--plan", "Analyze risk without connecting or executing").option("--format <type>", "Output format
|
|
23141
|
+
program2.command("delete <table>").description(t("delete.description")).option("--where <condition>", 'WHERE clause (required, e.g. "id=1")').option("--dry-run", "Show generated SQL without executing").option("--force", "Skip confirmation prompt").option("--plan", "Analyze risk without connecting or executing").option("--format <type>", "Output format: text or json", "text").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (table, options, command) => {
|
|
22981
23142
|
try {
|
|
22982
23143
|
await run(table, options, command);
|
|
22983
23144
|
} catch (error) {
|
|
@@ -23698,7 +23859,7 @@ function deepHasScript(value) {
|
|
|
23698
23859
|
return Object.values(obj).some(deepHasScript);
|
|
23699
23860
|
}
|
|
23700
23861
|
var NAME_RE2, ES_SIZE_CAP = 1000, ES_MAX_RESULT_WINDOW = 1e4, esStrategy;
|
|
23701
|
-
var
|
|
23862
|
+
var init_elasticsearch2 = __esm(() => {
|
|
23702
23863
|
init_types5();
|
|
23703
23864
|
NAME_RE2 = /:([a-zA-Z_][a-zA-Z0-9_]*)/g;
|
|
23704
23865
|
esStrategy = {
|
|
@@ -23796,7 +23957,7 @@ function applyRedisSizeGuard(command, noLimit) {
|
|
|
23796
23957
|
return { command, warnings };
|
|
23797
23958
|
}
|
|
23798
23959
|
var REDIS_NAME_RE, REDIS_RANGE_CAP = 1000, RANGE_VERBS, SCAN_VERBS, REDIS_READONLY_VERBS, REDIS_HARD_REJECT, redisStrategy;
|
|
23799
|
-
var
|
|
23960
|
+
var init_redis2 = __esm(() => {
|
|
23800
23961
|
init_types5();
|
|
23801
23962
|
REDIS_NAME_RE = /:([a-zA-Z_][a-zA-Z0-9_]*)/g;
|
|
23802
23963
|
RANGE_VERBS = new Set(["LRANGE", "ZRANGE", "ZRANGEBYSCORE", "ZRANGEBYLEX"]);
|
|
@@ -23995,8 +24156,8 @@ function getStrategy(family) {
|
|
|
23995
24156
|
}
|
|
23996
24157
|
var init_strategies = __esm(() => {
|
|
23997
24158
|
init_sql();
|
|
23998
|
-
|
|
23999
|
-
|
|
24159
|
+
init_elasticsearch2();
|
|
24160
|
+
init_redis2();
|
|
24000
24161
|
init_mongodb();
|
|
24001
24162
|
});
|
|
24002
24163
|
|
|
@@ -25228,8 +25389,8 @@ class DataExecutor {
|
|
|
25228
25389
|
const timestamp = new Date().toISOString();
|
|
25229
25390
|
try {
|
|
25230
25391
|
this.checkBlacklist("INSERT", tableName);
|
|
25231
|
-
|
|
25232
|
-
return await this.executeMutation("insert", this.buildInsertSql(tableName, data, schema), timestamp, options, {
|
|
25392
|
+
enforcePermissionForType("INSERT", this.permission);
|
|
25393
|
+
return await this.executeMutation("insert", this.buildInsertSql(tableName, data, schema), timestamp, options, { destructive: false });
|
|
25233
25394
|
} catch (error) {
|
|
25234
25395
|
return this.handleMutationError("insert", timestamp, error);
|
|
25235
25396
|
}
|
|
@@ -25238,8 +25399,8 @@ class DataExecutor {
|
|
|
25238
25399
|
const timestamp = new Date().toISOString();
|
|
25239
25400
|
try {
|
|
25240
25401
|
this.checkBlacklist("UPDATE", tableName);
|
|
25241
|
-
|
|
25242
|
-
return await this.executeMutation("update", this.buildUpdateSql(tableName, data, where, schema), timestamp, options, {
|
|
25402
|
+
enforcePermissionForType("UPDATE", this.permission);
|
|
25403
|
+
return await this.executeMutation("update", this.buildUpdateSql(tableName, data, where, schema), timestamp, options, { destructive: false });
|
|
25243
25404
|
} catch (error) {
|
|
25244
25405
|
return this.handleMutationError("update", timestamp, error);
|
|
25245
25406
|
}
|
|
@@ -25248,19 +25409,8 @@ class DataExecutor {
|
|
|
25248
25409
|
const timestamp = new Date().toISOString();
|
|
25249
25410
|
try {
|
|
25250
25411
|
this.checkBlacklist("DELETE", tableName);
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
status: "error",
|
|
25254
|
-
operation: "delete",
|
|
25255
|
-
rows_affected: 0,
|
|
25256
|
-
timestamp,
|
|
25257
|
-
error: "Permission denied: DELETE operation requires Data-Admin or Admin permission."
|
|
25258
|
-
};
|
|
25259
|
-
}
|
|
25260
|
-
return await this.executeMutation("delete", this.buildDeleteSql(tableName, where, schema), timestamp, options, {
|
|
25261
|
-
warning: "\u26A0\uFE0F Warning: DELETE operation is destructive and cannot be undone!",
|
|
25262
|
-
prompt: "Are you sure you want to execute this DELETE operation? This cannot be undone."
|
|
25263
|
-
});
|
|
25412
|
+
enforcePermissionForType("DELETE", this.permission);
|
|
25413
|
+
return await this.executeMutation("delete", this.buildDeleteSql(tableName, where, schema), timestamp, options, { destructive: true });
|
|
25264
25414
|
} catch (error) {
|
|
25265
25415
|
return this.handleMutationError("delete", timestamp, error);
|
|
25266
25416
|
}
|
|
@@ -25274,31 +25424,31 @@ class DataExecutor {
|
|
|
25274
25424
|
checkBlacklist(operation, tableName) {
|
|
25275
25425
|
this.blacklistValidator?.checkTableBlacklist(operation, tableName, []);
|
|
25276
25426
|
}
|
|
25277
|
-
async executeMutation(operation, statement, timestamp, options,
|
|
25427
|
+
async executeMutation(operation, statement, timestamp, options, { destructive }) {
|
|
25278
25428
|
if (options?.dryRun) {
|
|
25279
|
-
return this.
|
|
25429
|
+
return this.outcomeResult("dry_run", operation, 0, timestamp, statement.sql);
|
|
25280
25430
|
}
|
|
25281
25431
|
if (!options?.force) {
|
|
25282
|
-
if (
|
|
25283
|
-
|
|
25284
|
-
${confirmation.warning}`);
|
|
25432
|
+
if (!options?.confirm) {
|
|
25433
|
+
throw new Error(`Refusing to ${operation} without confirmation: no confirmation handler was supplied. ` + "Pass options.confirm to ask the user, or options.force to proceed unattended.");
|
|
25285
25434
|
}
|
|
25286
|
-
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25435
|
+
const proceed = await options.confirm({
|
|
25436
|
+
operation,
|
|
25437
|
+
engine: "sql",
|
|
25438
|
+
sql: statement.sql,
|
|
25439
|
+
params: statement.params,
|
|
25440
|
+
destructive
|
|
25441
|
+
});
|
|
25442
|
+
if (!proceed) {
|
|
25443
|
+
return this.outcomeResult("cancelled", operation, 0, timestamp, statement.sql);
|
|
25294
25444
|
}
|
|
25295
25445
|
}
|
|
25296
25446
|
const result = await this.adapter.execute(statement.sql, statement.params);
|
|
25297
|
-
return this.
|
|
25447
|
+
return this.outcomeResult("success", operation, result.affectedRows, timestamp, statement.sql);
|
|
25298
25448
|
}
|
|
25299
|
-
|
|
25449
|
+
outcomeResult(status2, operation, rowsAffected, timestamp, sql) {
|
|
25300
25450
|
return {
|
|
25301
|
-
status:
|
|
25451
|
+
status: status2,
|
|
25302
25452
|
operation,
|
|
25303
25453
|
rows_affected: rowsAffected,
|
|
25304
25454
|
timestamp,
|
|
@@ -25309,13 +25459,13 @@ Parameters:`);
|
|
|
25309
25459
|
if (error instanceof BlacklistError) {
|
|
25310
25460
|
throw error;
|
|
25311
25461
|
}
|
|
25312
|
-
if (error instanceof PermissionError
|
|
25462
|
+
if (error instanceof PermissionError) {
|
|
25313
25463
|
return {
|
|
25314
25464
|
status: "error",
|
|
25315
25465
|
operation,
|
|
25316
25466
|
rows_affected: 0,
|
|
25317
25467
|
timestamp,
|
|
25318
|
-
error:
|
|
25468
|
+
error: t_vars("errors.permission_denied_reason", { reason: error.message })
|
|
25319
25469
|
};
|
|
25320
25470
|
}
|
|
25321
25471
|
const errorMessage2 = error instanceof Error ? error.message : String(error);
|
|
@@ -25373,8 +25523,152 @@ Parameters:`);
|
|
|
25373
25523
|
}
|
|
25374
25524
|
var init_data_executor = __esm(() => {
|
|
25375
25525
|
init_permission_guard();
|
|
25526
|
+
init_message_loader();
|
|
25527
|
+
init_blacklist();
|
|
25528
|
+
});
|
|
25529
|
+
|
|
25530
|
+
// src/commands/mutation-confirm.ts
|
|
25531
|
+
async function confirmDirectMutation(request) {
|
|
25532
|
+
if (request.force === true)
|
|
25533
|
+
return true;
|
|
25534
|
+
return confirmMutationInteractively({
|
|
25535
|
+
operation: request.operation,
|
|
25536
|
+
engine: request.engine,
|
|
25537
|
+
sql: request.preview,
|
|
25538
|
+
params: [],
|
|
25539
|
+
destructive: request.destructive
|
|
25540
|
+
});
|
|
25541
|
+
}
|
|
25542
|
+
var confirmMutationInteractively = async (request) => {
|
|
25543
|
+
if (request.destructive) {
|
|
25544
|
+
process.stderr.write(`
|
|
25545
|
+
${t("ceremony.confirm_destructive_warning")}
|
|
25546
|
+
`);
|
|
25547
|
+
}
|
|
25548
|
+
const label = request.engine === "sql" ? "ceremony.confirm_sql" : "ceremony.confirm_command";
|
|
25549
|
+
process.stderr.write(`
|
|
25550
|
+
${t(label)}
|
|
25551
|
+
${request.sql}
|
|
25552
|
+
`);
|
|
25553
|
+
if (request.params.length > 0) {
|
|
25554
|
+
process.stderr.write(`
|
|
25555
|
+
${t("ceremony.confirm_params")}
|
|
25556
|
+
${JSON.stringify(request.params, null, 2)}
|
|
25557
|
+
`);
|
|
25558
|
+
}
|
|
25559
|
+
return promptUser.confirm(t(request.destructive ? "ceremony.confirm_destructive_prompt" : "ceremony.confirm_prompt"));
|
|
25560
|
+
}, confirmDdlInteractively = async (request) => {
|
|
25561
|
+
process.stderr.write(`
|
|
25562
|
+
${t("ceremony.confirm_destructive_warning_ddl")}
|
|
25563
|
+
`);
|
|
25564
|
+
process.stderr.write(`
|
|
25565
|
+
${t("ceremony.confirm_sql")}
|
|
25566
|
+
${request.sql}
|
|
25567
|
+
`);
|
|
25568
|
+
return promptUser.confirm(t_vars("ceremony.confirm_ddl_prompt", { operation: request.operation }));
|
|
25569
|
+
};
|
|
25570
|
+
var init_mutation_confirm = __esm(() => {
|
|
25571
|
+
init_message_loader();
|
|
25376
25572
|
init_prompts();
|
|
25573
|
+
});
|
|
25574
|
+
|
|
25575
|
+
// src/commands/mutation-audit.ts
|
|
25576
|
+
function auditOutcomeForMutation(result, table, recoveryRef) {
|
|
25577
|
+
return {
|
|
25578
|
+
success: result.status === "success" || result.status === "dry_run",
|
|
25579
|
+
target: table,
|
|
25580
|
+
...recoveryRef && { recovery_ref: recoveryRef },
|
|
25581
|
+
...result.sql && { sql: result.sql },
|
|
25582
|
+
metadata: {
|
|
25583
|
+
rows_affected: result.rows_affected,
|
|
25584
|
+
outcome: result.status,
|
|
25585
|
+
...result.status === "dry_run" && { dry_run: true }
|
|
25586
|
+
},
|
|
25587
|
+
...result.status === "error" && {
|
|
25588
|
+
error: new Error(result.error ?? `${result.operation} failed`)
|
|
25589
|
+
}
|
|
25590
|
+
};
|
|
25591
|
+
}
|
|
25592
|
+
|
|
25593
|
+
// src/commands/mutation-outcome.ts
|
|
25594
|
+
function shouldRenderForHuman(context) {
|
|
25595
|
+
if (context.format === "json")
|
|
25596
|
+
return false;
|
|
25597
|
+
return context.isTTY;
|
|
25598
|
+
}
|
|
25599
|
+
function humanOutputContext(options) {
|
|
25600
|
+
return { isTTY: process.stdout.isTTY === true, ...options.format && { format: options.format } };
|
|
25601
|
+
}
|
|
25602
|
+
function renderMutationOutcome(result, table, elapsedMs) {
|
|
25603
|
+
if (result.status === "error") {
|
|
25604
|
+
return [
|
|
25605
|
+
t_vars("ceremony.failed", { reason: result.error ?? t("ceremony.unknown_error") }),
|
|
25606
|
+
t("ceremony.recovery_retry")
|
|
25607
|
+
];
|
|
25608
|
+
}
|
|
25609
|
+
if (result.status === "cancelled") {
|
|
25610
|
+
return [t_vars("ceremony.cancelled", { table })];
|
|
25611
|
+
}
|
|
25612
|
+
if (result.status === "dry_run") {
|
|
25613
|
+
return [t_vars("ceremony.dry_run", { table })];
|
|
25614
|
+
}
|
|
25615
|
+
const elapsed = t_vars("ceremony.elapsed", { seconds: (elapsedMs / 1000).toFixed(2) });
|
|
25616
|
+
if (result.rows_affected === 0) {
|
|
25617
|
+
return [t_vars("ceremony.matched_nothing", { table }), elapsed];
|
|
25618
|
+
}
|
|
25619
|
+
return [
|
|
25620
|
+
t_vars(VERB_KEY[result.operation], { count: result.rows_affected, table }),
|
|
25621
|
+
elapsed,
|
|
25622
|
+
t("ceremony.recovery_none")
|
|
25623
|
+
];
|
|
25624
|
+
}
|
|
25625
|
+
function printMutationOutcome(result, table, elapsedMs, context, envelopeExtras = {}) {
|
|
25626
|
+
if (shouldRenderForHuman(context)) {
|
|
25627
|
+
const writeLine = result.status === "error" ? console.error : console.log;
|
|
25628
|
+
for (const line of renderMutationOutcome(result, table, elapsedMs))
|
|
25629
|
+
writeLine(line);
|
|
25630
|
+
return;
|
|
25631
|
+
}
|
|
25632
|
+
console.log(JSON.stringify({ ...mutationEnvelope(result), ...envelopeExtras }, null, 2));
|
|
25633
|
+
}
|
|
25634
|
+
function printMutationFailure(error, operation, context) {
|
|
25635
|
+
if (shouldRenderForHuman(context)) {
|
|
25636
|
+
console.error(t_vars("ceremony.failed", { reason: error.message }));
|
|
25637
|
+
if (error instanceof BlacklistError) {
|
|
25638
|
+
console.error(t("ceremony.blacklist_hint"));
|
|
25639
|
+
}
|
|
25640
|
+
return;
|
|
25641
|
+
}
|
|
25642
|
+
console.log(JSON.stringify({ status: "error", operation, rows_affected: 0, error: error.message }, null, 2));
|
|
25643
|
+
}
|
|
25644
|
+
function cancelledOutcome(operation, preview) {
|
|
25645
|
+
return {
|
|
25646
|
+
status: "cancelled",
|
|
25647
|
+
operation,
|
|
25648
|
+
rows_affected: 0,
|
|
25649
|
+
sql: preview,
|
|
25650
|
+
timestamp: new Date().toISOString()
|
|
25651
|
+
};
|
|
25652
|
+
}
|
|
25653
|
+
function mutationEnvelope(result) {
|
|
25654
|
+
return {
|
|
25655
|
+
status: result.status,
|
|
25656
|
+
operation: result.operation,
|
|
25657
|
+
rows_affected: result.rows_affected,
|
|
25658
|
+
...result.timestamp && { timestamp: result.timestamp },
|
|
25659
|
+
...result.sql && { sql: result.sql },
|
|
25660
|
+
...result.error && { error: result.error }
|
|
25661
|
+
};
|
|
25662
|
+
}
|
|
25663
|
+
var VERB_KEY;
|
|
25664
|
+
var init_mutation_outcome = __esm(() => {
|
|
25665
|
+
init_message_loader();
|
|
25377
25666
|
init_blacklist();
|
|
25667
|
+
VERB_KEY = {
|
|
25668
|
+
insert: "ceremony.inserted",
|
|
25669
|
+
update: "ceremony.updated",
|
|
25670
|
+
delete: "ceremony.deleted"
|
|
25671
|
+
};
|
|
25378
25672
|
});
|
|
25379
25673
|
|
|
25380
25674
|
// src/core/mongo/dry-run-formatter.ts
|
|
@@ -26218,6 +26512,9 @@ async function insertCommand(table, options, command) {
|
|
|
26218
26512
|
throw new Error("Table name required");
|
|
26219
26513
|
}
|
|
26220
26514
|
table = table.trim();
|
|
26515
|
+
if (options.format !== undefined && options.format !== "text" && options.format !== "json") {
|
|
26516
|
+
throw new Error(t_vars("errors.invalid_output_format", { format: options.format }));
|
|
26517
|
+
}
|
|
26221
26518
|
let jsonInput = "";
|
|
26222
26519
|
if (options.data) {
|
|
26223
26520
|
jsonInput = options.data;
|
|
@@ -26258,30 +26555,40 @@ async function insertCommand(table, options, command) {
|
|
|
26258
26555
|
return;
|
|
26259
26556
|
}
|
|
26260
26557
|
if (config.connection.system === "redis") {
|
|
26261
|
-
|
|
26558
|
+
enforcePermissionForType("INSERT", config.permission);
|
|
26262
26559
|
const blacklistManager = new BlacklistManager(config);
|
|
26263
26560
|
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
26264
26561
|
blacklistValidator.checkTableBlacklist("INSERT", table, []);
|
|
26265
26562
|
blacklistValidator.checkColumnBlacklistOnWrite(table, Object.keys(data), "INSERT");
|
|
26563
|
+
const preview = `SET ${table} ... (Redis Insert)`;
|
|
26266
26564
|
if (options.dryRun) {
|
|
26267
26565
|
const output = {
|
|
26268
|
-
status: "
|
|
26566
|
+
status: "dry_run",
|
|
26269
26567
|
operation: "insert",
|
|
26270
26568
|
rows_affected: 0,
|
|
26271
|
-
sql:
|
|
26569
|
+
sql: preview,
|
|
26272
26570
|
timestamp: new Date().toISOString()
|
|
26273
26571
|
};
|
|
26274
|
-
|
|
26275
|
-
await writeAuditEntry(config, "insert", options,
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26279
|
-
|
|
26572
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26573
|
+
await writeAuditEntry(config, "insert", options, auditOutcomeForMutation(output, table));
|
|
26574
|
+
return;
|
|
26575
|
+
}
|
|
26576
|
+
if (!await confirmDirectMutation({
|
|
26577
|
+
operation: "insert",
|
|
26578
|
+
engine: "redis",
|
|
26579
|
+
preview,
|
|
26580
|
+
destructive: false,
|
|
26581
|
+
force: options.force
|
|
26582
|
+
})) {
|
|
26583
|
+
const output = cancelledOutcome("insert", preview);
|
|
26584
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26585
|
+
await writeAuditEntry(config, "insert", options, auditOutcomeForMutation(output, table));
|
|
26280
26586
|
return;
|
|
26281
26587
|
}
|
|
26282
26588
|
const adapter2 = AdapterFactory.createRedisAdapter(config.connection, config.blacklist?.tables ?? []);
|
|
26283
26589
|
await adapter2.connect();
|
|
26284
26590
|
try {
|
|
26591
|
+
const startedAt = performance.now();
|
|
26285
26592
|
const result = await adapter2.insert(table, data);
|
|
26286
26593
|
const output = {
|
|
26287
26594
|
status: "success",
|
|
@@ -26289,12 +26596,8 @@ async function insertCommand(table, options, command) {
|
|
|
26289
26596
|
rows_affected: result.affectedRows,
|
|
26290
26597
|
timestamp: new Date().toISOString()
|
|
26291
26598
|
};
|
|
26292
|
-
|
|
26293
|
-
await writeAuditEntry(config, "insert", options,
|
|
26294
|
-
success: true,
|
|
26295
|
-
target: table,
|
|
26296
|
-
metadata: { rows_affected: result.affectedRows }
|
|
26297
|
-
});
|
|
26599
|
+
printMutationOutcome(output, table, performance.now() - startedAt, humanOutputContext(options));
|
|
26600
|
+
await writeAuditEntry(config, "insert", options, auditOutcomeForMutation(output, table));
|
|
26298
26601
|
return;
|
|
26299
26602
|
} finally {
|
|
26300
26603
|
await adapter2.disconnect();
|
|
@@ -26306,7 +26609,7 @@ async function insertCommand(table, options, command) {
|
|
|
26306
26609
|
operation: "insert",
|
|
26307
26610
|
rows_affected: 0,
|
|
26308
26611
|
timestamp: new Date().toISOString(),
|
|
26309
|
-
error: "
|
|
26612
|
+
error: t("insert.elasticsearch_unsupported")
|
|
26310
26613
|
};
|
|
26311
26614
|
await writeAuditEntry(config, "insert", options, {
|
|
26312
26615
|
success: false,
|
|
@@ -26317,44 +26620,49 @@ async function insertCommand(table, options, command) {
|
|
|
26317
26620
|
process.exit(1);
|
|
26318
26621
|
}
|
|
26319
26622
|
if (config.connection.system === "mongodb") {
|
|
26320
|
-
|
|
26623
|
+
enforcePermissionForType("INSERT", config.permission);
|
|
26321
26624
|
const blacklistManager = new BlacklistManager(config);
|
|
26322
26625
|
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
26323
26626
|
blacklistValidator.checkTableBlacklist("INSERT", table, []);
|
|
26324
26627
|
blacklistValidator.checkColumnBlacklistOnWrite(table, Object.keys(data), "INSERT");
|
|
26628
|
+
const preview = previewInsert(table, data);
|
|
26325
26629
|
if (options.dryRun) {
|
|
26326
26630
|
const output = {
|
|
26327
|
-
status: "
|
|
26631
|
+
status: "dry_run",
|
|
26328
26632
|
operation: "insert",
|
|
26329
26633
|
rows_affected: 0,
|
|
26330
|
-
sql:
|
|
26634
|
+
sql: preview,
|
|
26331
26635
|
timestamp: new Date().toISOString()
|
|
26332
26636
|
};
|
|
26333
|
-
|
|
26334
|
-
await writeAuditEntry(config, "insert", options,
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26637
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26638
|
+
await writeAuditEntry(config, "insert", options, auditOutcomeForMutation(output, table));
|
|
26639
|
+
return;
|
|
26640
|
+
}
|
|
26641
|
+
if (!await confirmDirectMutation({
|
|
26642
|
+
operation: "insert",
|
|
26643
|
+
engine: "mongodb",
|
|
26644
|
+
preview,
|
|
26645
|
+
destructive: false,
|
|
26646
|
+
force: options.force
|
|
26647
|
+
})) {
|
|
26648
|
+
const output = cancelledOutcome("insert", preview);
|
|
26649
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26650
|
+
await writeAuditEntry(config, "insert", options, auditOutcomeForMutation(output, table));
|
|
26339
26651
|
return;
|
|
26340
26652
|
}
|
|
26341
26653
|
const adapter2 = AdapterFactory.createMongoDBAdapter(config.connection);
|
|
26342
26654
|
await adapter2.connect();
|
|
26343
26655
|
try {
|
|
26656
|
+
const startedAt = performance.now();
|
|
26344
26657
|
const result = await adapter2.insert(table, data);
|
|
26345
26658
|
const output = {
|
|
26346
26659
|
status: "success",
|
|
26347
26660
|
operation: "insert",
|
|
26348
26661
|
rows_affected: result.affectedRows,
|
|
26349
|
-
timestamp: new Date().toISOString()
|
|
26350
|
-
lastInsertId: result.lastInsertId
|
|
26662
|
+
timestamp: new Date().toISOString()
|
|
26351
26663
|
};
|
|
26352
|
-
|
|
26353
|
-
await writeAuditEntry(config, "insert", options,
|
|
26354
|
-
success: true,
|
|
26355
|
-
target: table,
|
|
26356
|
-
metadata: { rows_affected: result.affectedRows }
|
|
26357
|
-
});
|
|
26664
|
+
printMutationOutcome(output, table, performance.now() - startedAt, humanOutputContext(options), { lastInsertId: result.lastInsertId });
|
|
26665
|
+
await writeAuditEntry(config, "insert", options, auditOutcomeForMutation(output, table));
|
|
26358
26666
|
return;
|
|
26359
26667
|
} finally {
|
|
26360
26668
|
await adapter2.disconnect();
|
|
@@ -26368,31 +26676,20 @@ async function insertCommand(table, options, command) {
|
|
|
26368
26676
|
const blacklistManager = new BlacklistManager(config);
|
|
26369
26677
|
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
26370
26678
|
const executor3 = new DataExecutor(adapter, config.permission, dbSystem, blacklistValidator);
|
|
26679
|
+
const startedAt = performance.now();
|
|
26371
26680
|
const result = await executor3.executeInsert(table, data, schema, {
|
|
26372
26681
|
dryRun: options.dryRun,
|
|
26373
|
-
force: options.force
|
|
26374
|
-
|
|
26375
|
-
const output = {
|
|
26376
|
-
status: result.status,
|
|
26377
|
-
operation: result.operation,
|
|
26378
|
-
rows_affected: result.rows_affected,
|
|
26379
|
-
timestamp: result.timestamp,
|
|
26380
|
-
...result.sql && { sql: result.sql },
|
|
26381
|
-
...result.error && { error: result.error }
|
|
26382
|
-
};
|
|
26383
|
-
console.log(JSON.stringify(output, null, 2));
|
|
26384
|
-
await writeAuditEntry(config, "insert", options, {
|
|
26385
|
-
success: result.status === "success",
|
|
26386
|
-
target: table,
|
|
26387
|
-
...result.sql && { sql: result.sql },
|
|
26388
|
-
metadata: {
|
|
26389
|
-
rows_affected: result.rows_affected,
|
|
26390
|
-
...options.dryRun && { dry_run: true }
|
|
26391
|
-
},
|
|
26392
|
-
...result.status === "error" && {
|
|
26393
|
-
error: new Error(result.error ?? "insert failed")
|
|
26394
|
-
}
|
|
26682
|
+
force: options.force,
|
|
26683
|
+
confirm: confirmMutationInteractively
|
|
26395
26684
|
});
|
|
26685
|
+
const elapsedMs = performance.now() - startedAt;
|
|
26686
|
+
const recoveryEnvelopeId = result.status === "error" && options.recovery === true ? crypto5.randomUUID() : undefined;
|
|
26687
|
+
const auditId = await writeAuditEntry(config, "insert", options, auditOutcomeForMutation(result, table, recoveryEnvelopeId));
|
|
26688
|
+
if (recoveryEnvelopeId !== undefined) {
|
|
26689
|
+
const { emitRecoveryEnvelope: emitRecoveryEnvelope2 } = await Promise.resolve().then(() => (init_recovery(), exports_recovery));
|
|
26690
|
+
emitRecoveryEnvelope2(new Error(result.error ?? "INSERT failed"), { operation: "insert", table, writeOperation: "INSERT" }, { envelopeId: recoveryEnvelopeId, auditRef: auditId ?? undefined });
|
|
26691
|
+
}
|
|
26692
|
+
printMutationOutcome(result, table, elapsedMs, humanOutputContext(options));
|
|
26396
26693
|
if (result.status === "error") {
|
|
26397
26694
|
process.exit(1);
|
|
26398
26695
|
}
|
|
@@ -26418,13 +26715,7 @@ async function insertCommand(table, options, command) {
|
|
|
26418
26715
|
emitRecoveryEnvelope2(error, { operation: "insert", table, writeOperation: "INSERT" }, { envelopeId, auditRef: auditId ?? undefined });
|
|
26419
26716
|
}
|
|
26420
26717
|
if (error instanceof BlacklistError) {
|
|
26421
|
-
|
|
26422
|
-
status: "error",
|
|
26423
|
-
operation: "insert",
|
|
26424
|
-
rows_affected: 0,
|
|
26425
|
-
error: error.message
|
|
26426
|
-
};
|
|
26427
|
-
console.log(JSON.stringify(output2, null, 2));
|
|
26718
|
+
printMutationFailure(error, "insert", humanOutputContext(options));
|
|
26428
26719
|
process.exit(1);
|
|
26429
26720
|
}
|
|
26430
26721
|
if (error instanceof PermissionError) {
|
|
@@ -26437,13 +26728,7 @@ async function insertCommand(table, options, command) {
|
|
|
26437
26728
|
printLocalizedCliError(formatCliError(presentConnectionError(error)), error);
|
|
26438
26729
|
process.exit(1);
|
|
26439
26730
|
}
|
|
26440
|
-
|
|
26441
|
-
status: "error",
|
|
26442
|
-
operation: "insert",
|
|
26443
|
-
rows_affected: 0,
|
|
26444
|
-
error: error.message
|
|
26445
|
-
};
|
|
26446
|
-
console.log(JSON.stringify(output, null, 2));
|
|
26731
|
+
printMutationFailure(error, "insert", humanOutputContext(options));
|
|
26447
26732
|
process.exit(1);
|
|
26448
26733
|
}
|
|
26449
26734
|
}
|
|
@@ -26453,6 +26738,8 @@ var init_insert = __esm(() => {
|
|
|
26453
26738
|
init_connection_error_message();
|
|
26454
26739
|
init_adapters();
|
|
26455
26740
|
init_data_executor();
|
|
26741
|
+
init_mutation_confirm();
|
|
26742
|
+
init_mutation_outcome();
|
|
26456
26743
|
init_config();
|
|
26457
26744
|
init_permission_guard();
|
|
26458
26745
|
init_blacklist_validator();
|
|
@@ -26481,6 +26768,9 @@ async function updateCommand(table, options, command) {
|
|
|
26481
26768
|
throw new Error("Table name required");
|
|
26482
26769
|
}
|
|
26483
26770
|
table = table.trim();
|
|
26771
|
+
if (options.format !== undefined && options.format !== "text" && options.format !== "json") {
|
|
26772
|
+
throw new Error(t_vars("errors.invalid_output_format", { format: options.format }));
|
|
26773
|
+
}
|
|
26484
26774
|
if (!options.plan && (!options.where || options.where.trim() === "")) {
|
|
26485
26775
|
throw new Error('UPDATE requires --where clause (e.g. --where "id=1")');
|
|
26486
26776
|
}
|
|
@@ -26541,29 +26831,39 @@ async function updateCommand(table, options, command) {
|
|
|
26541
26831
|
return;
|
|
26542
26832
|
}
|
|
26543
26833
|
if (config.connection?.system === "redis") {
|
|
26544
|
-
|
|
26834
|
+
enforcePermissionForType("UPDATE", config.permission);
|
|
26545
26835
|
const blacklistManager = new BlacklistManager(config);
|
|
26546
26836
|
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
26547
26837
|
blacklistValidator.checkTableBlacklist("UPDATE", table, []);
|
|
26838
|
+
const preview = `HSET ${table} ... (Redis Update)`;
|
|
26548
26839
|
if (options.dryRun) {
|
|
26549
26840
|
const output = {
|
|
26550
|
-
status: "
|
|
26841
|
+
status: "dry_run",
|
|
26551
26842
|
operation: "update",
|
|
26552
26843
|
rows_affected: 0,
|
|
26553
|
-
sql:
|
|
26844
|
+
sql: preview,
|
|
26554
26845
|
timestamp: new Date().toISOString()
|
|
26555
26846
|
};
|
|
26556
|
-
|
|
26557
|
-
await writeAuditEntry(config, "update", options,
|
|
26558
|
-
|
|
26559
|
-
|
|
26560
|
-
|
|
26561
|
-
|
|
26847
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26848
|
+
await writeAuditEntry(config, "update", options, auditOutcomeForMutation(output, table));
|
|
26849
|
+
return;
|
|
26850
|
+
}
|
|
26851
|
+
if (!await confirmDirectMutation({
|
|
26852
|
+
operation: "update",
|
|
26853
|
+
engine: "redis",
|
|
26854
|
+
preview,
|
|
26855
|
+
destructive: false,
|
|
26856
|
+
force: options.force
|
|
26857
|
+
})) {
|
|
26858
|
+
const output = cancelledOutcome("update", preview);
|
|
26859
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26860
|
+
await writeAuditEntry(config, "update", options, auditOutcomeForMutation(output, table));
|
|
26562
26861
|
return;
|
|
26563
26862
|
}
|
|
26564
26863
|
const adapter2 = AdapterFactory.createRedisAdapter(config.connection, config.blacklist?.tables ?? []);
|
|
26565
26864
|
await adapter2.connect();
|
|
26566
26865
|
try {
|
|
26866
|
+
const startedAt = performance.now();
|
|
26567
26867
|
const result = await adapter2.update(table, {}, setData);
|
|
26568
26868
|
const output = {
|
|
26569
26869
|
status: "success",
|
|
@@ -26571,12 +26871,8 @@ async function updateCommand(table, options, command) {
|
|
|
26571
26871
|
rows_affected: result.affectedRows,
|
|
26572
26872
|
timestamp: new Date().toISOString()
|
|
26573
26873
|
};
|
|
26574
|
-
|
|
26575
|
-
await writeAuditEntry(config, "update", options,
|
|
26576
|
-
success: true,
|
|
26577
|
-
target: table,
|
|
26578
|
-
metadata: { rows_affected: result.affectedRows }
|
|
26579
|
-
});
|
|
26874
|
+
printMutationOutcome(output, table, performance.now() - startedAt, humanOutputContext(options));
|
|
26875
|
+
await writeAuditEntry(config, "update", options, auditOutcomeForMutation(output, table));
|
|
26580
26876
|
return;
|
|
26581
26877
|
} finally {
|
|
26582
26878
|
await adapter2.disconnect();
|
|
@@ -26588,7 +26884,7 @@ async function updateCommand(table, options, command) {
|
|
|
26588
26884
|
operation: "update",
|
|
26589
26885
|
rows_affected: 0,
|
|
26590
26886
|
timestamp: new Date().toISOString(),
|
|
26591
|
-
error: "
|
|
26887
|
+
error: t("update.elasticsearch_unsupported")
|
|
26592
26888
|
};
|
|
26593
26889
|
await writeAuditEntry(config, "update", options, {
|
|
26594
26890
|
success: false,
|
|
@@ -26599,7 +26895,7 @@ async function updateCommand(table, options, command) {
|
|
|
26599
26895
|
process.exit(1);
|
|
26600
26896
|
}
|
|
26601
26897
|
if (config.connection?.system === "mongodb") {
|
|
26602
|
-
|
|
26898
|
+
enforcePermissionForType("UPDATE", config.permission);
|
|
26603
26899
|
const hasOperator = Object.keys(setData).some((key) => key.startsWith("$"));
|
|
26604
26900
|
const updateDoc = hasOperator ? setData : { $set: setData };
|
|
26605
26901
|
const writtenFields = new Set;
|
|
@@ -26625,25 +26921,35 @@ async function updateCommand(table, options, command) {
|
|
|
26625
26921
|
} catch {
|
|
26626
26922
|
filter = parseWhereClause(options.where);
|
|
26627
26923
|
}
|
|
26924
|
+
const preview = previewUpdate(table, filter, updateDoc);
|
|
26628
26925
|
if (options.dryRun) {
|
|
26629
26926
|
const output = {
|
|
26630
|
-
status: "
|
|
26927
|
+
status: "dry_run",
|
|
26631
26928
|
operation: "update",
|
|
26632
26929
|
rows_affected: 0,
|
|
26633
|
-
sql:
|
|
26930
|
+
sql: preview,
|
|
26634
26931
|
timestamp: new Date().toISOString()
|
|
26635
26932
|
};
|
|
26636
|
-
|
|
26637
|
-
await writeAuditEntry(config, "update", options,
|
|
26638
|
-
|
|
26639
|
-
|
|
26640
|
-
|
|
26641
|
-
|
|
26933
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26934
|
+
await writeAuditEntry(config, "update", options, auditOutcomeForMutation(output, table));
|
|
26935
|
+
return;
|
|
26936
|
+
}
|
|
26937
|
+
if (!await confirmDirectMutation({
|
|
26938
|
+
operation: "update",
|
|
26939
|
+
engine: "mongodb",
|
|
26940
|
+
preview,
|
|
26941
|
+
destructive: false,
|
|
26942
|
+
force: options.force
|
|
26943
|
+
})) {
|
|
26944
|
+
const output = cancelledOutcome("update", preview);
|
|
26945
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
26946
|
+
await writeAuditEntry(config, "update", options, auditOutcomeForMutation(output, table));
|
|
26642
26947
|
return;
|
|
26643
26948
|
}
|
|
26644
26949
|
const adapter2 = AdapterFactory.createMongoDBAdapter(config.connection);
|
|
26645
26950
|
await adapter2.connect();
|
|
26646
26951
|
try {
|
|
26952
|
+
const startedAt = performance.now();
|
|
26647
26953
|
const result = await adapter2.update(table, filter, updateDoc);
|
|
26648
26954
|
const output = {
|
|
26649
26955
|
status: "success",
|
|
@@ -26651,12 +26957,8 @@ async function updateCommand(table, options, command) {
|
|
|
26651
26957
|
rows_affected: result.affectedRows,
|
|
26652
26958
|
timestamp: new Date().toISOString()
|
|
26653
26959
|
};
|
|
26654
|
-
|
|
26655
|
-
await writeAuditEntry(config, "update", options,
|
|
26656
|
-
success: true,
|
|
26657
|
-
target: table,
|
|
26658
|
-
metadata: { rows_affected: result.affectedRows }
|
|
26659
|
-
});
|
|
26960
|
+
printMutationOutcome(output, table, performance.now() - startedAt, humanOutputContext(options));
|
|
26961
|
+
await writeAuditEntry(config, "update", options, auditOutcomeForMutation(output, table));
|
|
26660
26962
|
return;
|
|
26661
26963
|
} finally {
|
|
26662
26964
|
await adapter2.disconnect();
|
|
@@ -26676,31 +26978,20 @@ async function updateCommand(table, options, command) {
|
|
|
26676
26978
|
const blacklistManager = new BlacklistManager(config);
|
|
26677
26979
|
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
26678
26980
|
const executor3 = new DataExecutor(adapter, config.permission, dbSystem, blacklistValidator);
|
|
26981
|
+
const startedAt = performance.now();
|
|
26679
26982
|
const result = await executor3.executeUpdate(table, setData, whereConditions, schema, {
|
|
26680
26983
|
dryRun: options.dryRun,
|
|
26681
|
-
force: options.force
|
|
26682
|
-
|
|
26683
|
-
const output = {
|
|
26684
|
-
status: result.status,
|
|
26685
|
-
operation: result.operation,
|
|
26686
|
-
rows_affected: result.rows_affected,
|
|
26687
|
-
timestamp: result.timestamp,
|
|
26688
|
-
...result.sql && { sql: result.sql },
|
|
26689
|
-
...result.error && { error: result.error }
|
|
26690
|
-
};
|
|
26691
|
-
console.log(JSON.stringify(output, null, 2));
|
|
26692
|
-
await writeAuditEntry(config, "update", options, {
|
|
26693
|
-
success: result.status === "success",
|
|
26694
|
-
target: table,
|
|
26695
|
-
...result.sql && { sql: result.sql },
|
|
26696
|
-
metadata: {
|
|
26697
|
-
rows_affected: result.rows_affected,
|
|
26698
|
-
...options.dryRun && { dry_run: true }
|
|
26699
|
-
},
|
|
26700
|
-
...result.status === "error" && {
|
|
26701
|
-
error: new Error(result.error ?? "update failed")
|
|
26702
|
-
}
|
|
26984
|
+
force: options.force,
|
|
26985
|
+
confirm: confirmMutationInteractively
|
|
26703
26986
|
});
|
|
26987
|
+
const elapsedMs = performance.now() - startedAt;
|
|
26988
|
+
const recoveryEnvelopeId = result.status === "error" && options.recovery === true ? crypto6.randomUUID() : undefined;
|
|
26989
|
+
const auditId = await writeAuditEntry(config, "update", options, auditOutcomeForMutation(result, table, recoveryEnvelopeId));
|
|
26990
|
+
if (recoveryEnvelopeId !== undefined) {
|
|
26991
|
+
const { emitRecoveryEnvelope: emitRecoveryEnvelope2 } = await Promise.resolve().then(() => (init_recovery(), exports_recovery));
|
|
26992
|
+
emitRecoveryEnvelope2(new Error(result.error ?? "UPDATE failed"), { operation: "update", table, writeOperation: "UPDATE" }, { envelopeId: recoveryEnvelopeId, auditRef: auditId ?? undefined });
|
|
26993
|
+
}
|
|
26994
|
+
printMutationOutcome(result, table, elapsedMs, humanOutputContext(options));
|
|
26704
26995
|
if (result.status === "error") {
|
|
26705
26996
|
process.exit(1);
|
|
26706
26997
|
}
|
|
@@ -26726,13 +27017,7 @@ async function updateCommand(table, options, command) {
|
|
|
26726
27017
|
emitRecoveryEnvelope2(error, { operation: "update", table, writeOperation: "UPDATE" }, { envelopeId, auditRef: auditId ?? undefined });
|
|
26727
27018
|
}
|
|
26728
27019
|
if (error instanceof BlacklistError) {
|
|
26729
|
-
|
|
26730
|
-
status: "error",
|
|
26731
|
-
operation: "update",
|
|
26732
|
-
rows_affected: 0,
|
|
26733
|
-
error: error.message
|
|
26734
|
-
};
|
|
26735
|
-
console.log(JSON.stringify(output2, null, 2));
|
|
27020
|
+
printMutationFailure(error, "update", humanOutputContext(options));
|
|
26736
27021
|
process.exit(1);
|
|
26737
27022
|
}
|
|
26738
27023
|
if (error instanceof PermissionError) {
|
|
@@ -26745,13 +27030,7 @@ async function updateCommand(table, options, command) {
|
|
|
26745
27030
|
printLocalizedCliError(formatCliError(presentConnectionError(error)), error);
|
|
26746
27031
|
process.exit(1);
|
|
26747
27032
|
}
|
|
26748
|
-
|
|
26749
|
-
status: "error",
|
|
26750
|
-
operation: "update",
|
|
26751
|
-
rows_affected: 0,
|
|
26752
|
-
error: error.message
|
|
26753
|
-
};
|
|
26754
|
-
console.log(JSON.stringify(output, null, 2));
|
|
27033
|
+
printMutationFailure(error, "update", humanOutputContext(options));
|
|
26755
27034
|
process.exit(1);
|
|
26756
27035
|
}
|
|
26757
27036
|
}
|
|
@@ -26761,6 +27040,8 @@ var init_update = __esm(() => {
|
|
|
26761
27040
|
init_connection_error_message();
|
|
26762
27041
|
init_adapters();
|
|
26763
27042
|
init_data_executor();
|
|
27043
|
+
init_mutation_confirm();
|
|
27044
|
+
init_mutation_outcome();
|
|
26764
27045
|
init_config();
|
|
26765
27046
|
init_permission_guard();
|
|
26766
27047
|
init_blacklist_validator();
|
|
@@ -26789,6 +27070,9 @@ async function deleteCommand(table, options, command) {
|
|
|
26789
27070
|
throw new Error("Table name required");
|
|
26790
27071
|
}
|
|
26791
27072
|
table = table.trim();
|
|
27073
|
+
if (options.format !== undefined && options.format !== "text" && options.format !== "json") {
|
|
27074
|
+
throw new Error(t_vars("errors.invalid_output_format", { format: options.format }));
|
|
27075
|
+
}
|
|
26792
27076
|
if (!options.plan && (!options.where || options.where.trim() === "")) {
|
|
26793
27077
|
throw new Error('DELETE requires --where clause (e.g. --where "id=1")');
|
|
26794
27078
|
}
|
|
@@ -26835,14 +27119,8 @@ async function deleteCommand(table, options, command) {
|
|
|
26835
27119
|
});
|
|
26836
27120
|
return;
|
|
26837
27121
|
}
|
|
26838
|
-
if (
|
|
26839
|
-
throw new PermissionError(t("delete.admin_only"),
|
|
26840
|
-
type: "DELETE",
|
|
26841
|
-
isDangerous: true,
|
|
26842
|
-
keywords: ["DELETE"],
|
|
26843
|
-
isComposite: false,
|
|
26844
|
-
confidence: "HIGH"
|
|
26845
|
-
}, config.permission);
|
|
27122
|
+
if (!permitsOperation("DELETE", config.permission)) {
|
|
27123
|
+
throw new PermissionError(t("delete.admin_only"), classificationForType("DELETE"), minimumPermissionFor("DELETE"));
|
|
26846
27124
|
}
|
|
26847
27125
|
if (config.connection?.system === "redis") {
|
|
26848
27126
|
const blacklistManager = new BlacklistManager(config);
|
|
@@ -26854,25 +27132,35 @@ async function deleteCommand(table, options, command) {
|
|
|
26854
27132
|
} catch {
|
|
26855
27133
|
filter = parseWhereClause(options.where);
|
|
26856
27134
|
}
|
|
27135
|
+
const preview = `DEL ${table} (Redis Delete)`;
|
|
26857
27136
|
if (options.dryRun) {
|
|
26858
27137
|
const output = {
|
|
26859
|
-
status: "
|
|
27138
|
+
status: "dry_run",
|
|
26860
27139
|
operation: "delete",
|
|
26861
27140
|
rows_affected: 0,
|
|
26862
|
-
sql:
|
|
27141
|
+
sql: preview,
|
|
26863
27142
|
timestamp: new Date().toISOString()
|
|
26864
27143
|
};
|
|
26865
|
-
|
|
26866
|
-
await writeAuditEntry(config, "delete", options,
|
|
26867
|
-
|
|
26868
|
-
|
|
26869
|
-
|
|
26870
|
-
|
|
27144
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
27145
|
+
await writeAuditEntry(config, "delete", options, auditOutcomeForMutation(output, table));
|
|
27146
|
+
return;
|
|
27147
|
+
}
|
|
27148
|
+
if (!await confirmDirectMutation({
|
|
27149
|
+
operation: "delete",
|
|
27150
|
+
engine: "redis",
|
|
27151
|
+
preview,
|
|
27152
|
+
destructive: true,
|
|
27153
|
+
force: options.force
|
|
27154
|
+
})) {
|
|
27155
|
+
const output = cancelledOutcome("delete", preview);
|
|
27156
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
27157
|
+
await writeAuditEntry(config, "delete", options, auditOutcomeForMutation(output, table));
|
|
26871
27158
|
return;
|
|
26872
27159
|
}
|
|
26873
27160
|
const adapter2 = AdapterFactory.createRedisAdapter(config.connection, config.blacklist?.tables ?? []);
|
|
26874
27161
|
await adapter2.connect();
|
|
26875
27162
|
try {
|
|
27163
|
+
const startedAt = performance.now();
|
|
26876
27164
|
const result = await adapter2.delete(table, filter);
|
|
26877
27165
|
const output = {
|
|
26878
27166
|
status: "success",
|
|
@@ -26880,12 +27168,8 @@ async function deleteCommand(table, options, command) {
|
|
|
26880
27168
|
rows_affected: result.affectedRows,
|
|
26881
27169
|
timestamp: new Date().toISOString()
|
|
26882
27170
|
};
|
|
26883
|
-
|
|
26884
|
-
await writeAuditEntry(config, "delete", options,
|
|
26885
|
-
success: true,
|
|
26886
|
-
target: table,
|
|
26887
|
-
metadata: { rows_affected: result.affectedRows }
|
|
26888
|
-
});
|
|
27171
|
+
printMutationOutcome(output, table, performance.now() - startedAt, humanOutputContext(options));
|
|
27172
|
+
await writeAuditEntry(config, "delete", options, auditOutcomeForMutation(output, table));
|
|
26889
27173
|
return;
|
|
26890
27174
|
} finally {
|
|
26891
27175
|
await adapter2.disconnect();
|
|
@@ -26897,7 +27181,7 @@ async function deleteCommand(table, options, command) {
|
|
|
26897
27181
|
operation: "delete",
|
|
26898
27182
|
rows_affected: 0,
|
|
26899
27183
|
timestamp: new Date().toISOString(),
|
|
26900
|
-
error: "
|
|
27184
|
+
error: t("delete.elasticsearch_unsupported")
|
|
26901
27185
|
};
|
|
26902
27186
|
await writeAuditEntry(config, "delete", options, {
|
|
26903
27187
|
success: false,
|
|
@@ -26917,25 +27201,35 @@ async function deleteCommand(table, options, command) {
|
|
|
26917
27201
|
} catch {
|
|
26918
27202
|
filter = parseWhereClause(options.where);
|
|
26919
27203
|
}
|
|
27204
|
+
const preview = previewDelete(table, filter);
|
|
26920
27205
|
if (options.dryRun) {
|
|
26921
27206
|
const output = {
|
|
26922
|
-
status: "
|
|
27207
|
+
status: "dry_run",
|
|
26923
27208
|
operation: "delete",
|
|
26924
27209
|
rows_affected: 0,
|
|
26925
|
-
sql:
|
|
27210
|
+
sql: preview,
|
|
26926
27211
|
timestamp: new Date().toISOString()
|
|
26927
27212
|
};
|
|
26928
|
-
|
|
26929
|
-
await writeAuditEntry(config, "delete", options,
|
|
26930
|
-
|
|
26931
|
-
|
|
26932
|
-
|
|
26933
|
-
|
|
27213
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
27214
|
+
await writeAuditEntry(config, "delete", options, auditOutcomeForMutation(output, table));
|
|
27215
|
+
return;
|
|
27216
|
+
}
|
|
27217
|
+
if (!await confirmDirectMutation({
|
|
27218
|
+
operation: "delete",
|
|
27219
|
+
engine: "mongodb",
|
|
27220
|
+
preview,
|
|
27221
|
+
destructive: true,
|
|
27222
|
+
force: options.force
|
|
27223
|
+
})) {
|
|
27224
|
+
const output = cancelledOutcome("delete", preview);
|
|
27225
|
+
printMutationOutcome(output, table, 0, humanOutputContext(options));
|
|
27226
|
+
await writeAuditEntry(config, "delete", options, auditOutcomeForMutation(output, table));
|
|
26934
27227
|
return;
|
|
26935
27228
|
}
|
|
26936
27229
|
const adapter2 = AdapterFactory.createMongoDBAdapter(config.connection);
|
|
26937
27230
|
await adapter2.connect();
|
|
26938
27231
|
try {
|
|
27232
|
+
const startedAt = performance.now();
|
|
26939
27233
|
const result = await adapter2.delete(table, filter);
|
|
26940
27234
|
const output = {
|
|
26941
27235
|
status: "success",
|
|
@@ -26943,12 +27237,8 @@ async function deleteCommand(table, options, command) {
|
|
|
26943
27237
|
rows_affected: result.affectedRows,
|
|
26944
27238
|
timestamp: new Date().toISOString()
|
|
26945
27239
|
};
|
|
26946
|
-
|
|
26947
|
-
await writeAuditEntry(config, "delete", options,
|
|
26948
|
-
success: true,
|
|
26949
|
-
target: table,
|
|
26950
|
-
metadata: { rows_affected: result.affectedRows }
|
|
26951
|
-
});
|
|
27240
|
+
printMutationOutcome(output, table, performance.now() - startedAt, humanOutputContext(options));
|
|
27241
|
+
await writeAuditEntry(config, "delete", options, auditOutcomeForMutation(output, table));
|
|
26952
27242
|
return;
|
|
26953
27243
|
} finally {
|
|
26954
27244
|
await adapter2.disconnect();
|
|
@@ -26968,31 +27258,20 @@ async function deleteCommand(table, options, command) {
|
|
|
26968
27258
|
const blacklistManager = new BlacklistManager(config);
|
|
26969
27259
|
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
26970
27260
|
const executor3 = new DataExecutor(adapter, config.permission, dbSystem, blacklistValidator);
|
|
27261
|
+
const startedAt = performance.now();
|
|
26971
27262
|
const result = await executor3.executeDelete(table, whereConditions, schema, {
|
|
26972
27263
|
dryRun: options.dryRun,
|
|
26973
|
-
force: options.force
|
|
26974
|
-
|
|
26975
|
-
const output = {
|
|
26976
|
-
status: result.status,
|
|
26977
|
-
operation: result.operation,
|
|
26978
|
-
rows_affected: result.rows_affected,
|
|
26979
|
-
timestamp: result.timestamp,
|
|
26980
|
-
...result.sql && { sql: result.sql },
|
|
26981
|
-
...result.error && { error: result.error }
|
|
26982
|
-
};
|
|
26983
|
-
console.log(JSON.stringify(output, null, 2));
|
|
26984
|
-
await writeAuditEntry(config, "delete", options, {
|
|
26985
|
-
success: result.status === "success",
|
|
26986
|
-
target: table,
|
|
26987
|
-
...result.sql && { sql: result.sql },
|
|
26988
|
-
metadata: {
|
|
26989
|
-
rows_affected: result.rows_affected,
|
|
26990
|
-
...options.dryRun && { dry_run: true }
|
|
26991
|
-
},
|
|
26992
|
-
...result.status === "error" && {
|
|
26993
|
-
error: new Error(result.error ?? "delete failed")
|
|
26994
|
-
}
|
|
27264
|
+
force: options.force,
|
|
27265
|
+
confirm: confirmMutationInteractively
|
|
26995
27266
|
});
|
|
27267
|
+
const elapsedMs = performance.now() - startedAt;
|
|
27268
|
+
const recoveryEnvelopeId = result.status === "error" && options.recovery === true ? crypto7.randomUUID() : undefined;
|
|
27269
|
+
const auditId = await writeAuditEntry(config, "delete", options, auditOutcomeForMutation(result, table, recoveryEnvelopeId));
|
|
27270
|
+
if (recoveryEnvelopeId !== undefined) {
|
|
27271
|
+
const { emitRecoveryEnvelope: emitRecoveryEnvelope2 } = await Promise.resolve().then(() => (init_recovery(), exports_recovery));
|
|
27272
|
+
emitRecoveryEnvelope2(new Error(result.error ?? "DELETE failed"), { operation: "delete", table, writeOperation: "DELETE" }, { envelopeId: recoveryEnvelopeId, auditRef: auditId ?? undefined });
|
|
27273
|
+
}
|
|
27274
|
+
printMutationOutcome(result, table, elapsedMs, humanOutputContext(options));
|
|
26996
27275
|
if (result.status === "error") {
|
|
26997
27276
|
process.exit(1);
|
|
26998
27277
|
}
|
|
@@ -27018,17 +27297,11 @@ async function deleteCommand(table, options, command) {
|
|
|
27018
27297
|
emitRecoveryEnvelope2(error, { operation: "delete", table, writeOperation: "DELETE" }, { envelopeId, auditRef: auditId ?? undefined });
|
|
27019
27298
|
}
|
|
27020
27299
|
if (error instanceof BlacklistError) {
|
|
27021
|
-
|
|
27022
|
-
status: "error",
|
|
27023
|
-
operation: "delete",
|
|
27024
|
-
rows_affected: 0,
|
|
27025
|
-
error: error.message
|
|
27026
|
-
};
|
|
27027
|
-
console.log(JSON.stringify(output2, null, 2));
|
|
27300
|
+
printMutationFailure(error, "delete", humanOutputContext(options));
|
|
27028
27301
|
process.exit(1);
|
|
27029
27302
|
}
|
|
27030
27303
|
if (error instanceof PermissionError) {
|
|
27031
|
-
console.error(t_vars("errors.permission_denied", { required:
|
|
27304
|
+
console.error(t_vars("errors.permission_denied", { required: error.requiredPermission }));
|
|
27032
27305
|
console.error(` Operation: ${error.classification.type}`);
|
|
27033
27306
|
console.error(` Message: ${error.message}`);
|
|
27034
27307
|
process.exit(1);
|
|
@@ -27037,13 +27310,7 @@ async function deleteCommand(table, options, command) {
|
|
|
27037
27310
|
printLocalizedCliError(formatCliError(presentConnectionError(error)), error);
|
|
27038
27311
|
process.exit(1);
|
|
27039
27312
|
}
|
|
27040
|
-
|
|
27041
|
-
status: "error",
|
|
27042
|
-
operation: "delete",
|
|
27043
|
-
rows_affected: 0,
|
|
27044
|
-
error: error.message
|
|
27045
|
-
};
|
|
27046
|
-
console.log(JSON.stringify(output, null, 2));
|
|
27313
|
+
printMutationFailure(error, "delete", humanOutputContext(options));
|
|
27047
27314
|
process.exit(1);
|
|
27048
27315
|
}
|
|
27049
27316
|
}
|
|
@@ -27053,6 +27320,8 @@ var init_delete = __esm(() => {
|
|
|
27053
27320
|
init_connection_error_message();
|
|
27054
27321
|
init_adapters();
|
|
27055
27322
|
init_data_executor();
|
|
27323
|
+
init_mutation_confirm();
|
|
27324
|
+
init_mutation_outcome();
|
|
27056
27325
|
init_config();
|
|
27057
27326
|
init_permission_guard();
|
|
27058
27327
|
init_blacklist_validator();
|
|
@@ -27216,7 +27485,7 @@ async function exportCommand(sql, options, command) {
|
|
|
27216
27485
|
}
|
|
27217
27486
|
}
|
|
27218
27487
|
async function redisExportBranch(command, options, config) {
|
|
27219
|
-
const { enforceRedisPermission: enforceRedisPermission2 } = await Promise.resolve().then(() => (
|
|
27488
|
+
const { enforceRedisPermission: enforceRedisPermission2 } = await Promise.resolve().then(() => (init_redis(), exports_redis));
|
|
27220
27489
|
enforceRedisPermission2(command, config.permission);
|
|
27221
27490
|
const redisAdapter = AdapterFactory.createRedisAdapter(config.connection, config.blacklist?.tables ?? [], config.redis?.mask ?? []);
|
|
27222
27491
|
await redisAdapter.connect();
|
|
@@ -44092,6 +44361,7 @@ var init_repl_engine = __esm(() => {
|
|
|
44092
44361
|
init_meta_commands();
|
|
44093
44362
|
init_history_manager();
|
|
44094
44363
|
init_permission_guard();
|
|
44364
|
+
init_redis();
|
|
44095
44365
|
init_query_result_formatter();
|
|
44096
44366
|
init_message_loader();
|
|
44097
44367
|
init_sql_tables();
|
|
@@ -45220,13 +45490,16 @@ class DDLExecutor {
|
|
|
45220
45490
|
};
|
|
45221
45491
|
}
|
|
45222
45492
|
if (isDestructiveOperation2(operation) && !options.force) {
|
|
45223
|
-
|
|
45493
|
+
if (!options.confirm) {
|
|
45494
|
+
throw new DDLExecutionError(`Refusing to run ${operation.kind} without confirmation: no confirmation handler was ` + "supplied. Pass options.confirm to ask the user, or options.force to proceed unattended.");
|
|
45495
|
+
}
|
|
45496
|
+
const confirmed = await options.confirm({ operation: operation.kind, sql });
|
|
45224
45497
|
if (!confirmed) {
|
|
45225
45498
|
return {
|
|
45226
|
-
status: "
|
|
45499
|
+
status: "cancelled",
|
|
45227
45500
|
operation: operation.kind,
|
|
45228
45501
|
sql,
|
|
45229
|
-
warnings
|
|
45502
|
+
warnings,
|
|
45230
45503
|
timestamp: timestamp2,
|
|
45231
45504
|
dryRun: false
|
|
45232
45505
|
};
|
|
@@ -45301,8 +45574,15 @@ class DDLExecutor {
|
|
|
45301
45574
|
} catch {}
|
|
45302
45575
|
}
|
|
45303
45576
|
}
|
|
45577
|
+
var DDLExecutionError;
|
|
45304
45578
|
var init_ddl_executor = __esm(() => {
|
|
45305
|
-
|
|
45579
|
+
DDLExecutionError = class DDLExecutionError extends Error {
|
|
45580
|
+
constructor(message) {
|
|
45581
|
+
super(message);
|
|
45582
|
+
this.name = "DDLExecutionError";
|
|
45583
|
+
Object.setPrototypeOf(this, DDLExecutionError.prototype);
|
|
45584
|
+
}
|
|
45585
|
+
};
|
|
45306
45586
|
});
|
|
45307
45587
|
|
|
45308
45588
|
// src/commands/migrate.ts
|
|
@@ -45346,7 +45626,8 @@ async function runDDL(operation, opts, command) {
|
|
|
45346
45626
|
const executor3 = new DDLExecutor(adapter, generator, config.permission, blacklistManager);
|
|
45347
45627
|
const result = await executor3.execute(operation, {
|
|
45348
45628
|
execute: opts.execute,
|
|
45349
|
-
force: opts.force
|
|
45629
|
+
force: opts.force,
|
|
45630
|
+
confirm: confirmDdlInteractively
|
|
45350
45631
|
});
|
|
45351
45632
|
console.log(JSON.stringify({
|
|
45352
45633
|
status: result.status,
|
|
@@ -45388,6 +45669,7 @@ var init_migrate = __esm(() => {
|
|
|
45388
45669
|
init_adapters();
|
|
45389
45670
|
init_ddl2();
|
|
45390
45671
|
init_ddl_executor();
|
|
45672
|
+
init_mutation_confirm();
|
|
45391
45673
|
init_config_path();
|
|
45392
45674
|
migrateCommand = new Command("migrate").description(t("migrate.description"));
|
|
45393
45675
|
addExecOpts(migrateCommand.command("create <table>").description(t("migrate.create_description")).option("--column <spec...>", 'Column definitions (e.g., "id:serial:pk" "name:varchar(50):not-null")')).action(async (table, opts, command) => {
|
|
@@ -45685,6 +45967,258 @@ var init_use = __esm(() => {
|
|
|
45685
45967
|
});
|
|
45686
45968
|
});
|
|
45687
45969
|
|
|
45970
|
+
// src/core/config-v2-mutations.ts
|
|
45971
|
+
function envVarNameFor(connName, field) {
|
|
45972
|
+
const slug = connName.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
45973
|
+
return `DBCLI_${slug}_${field.toUpperCase()}`;
|
|
45974
|
+
}
|
|
45975
|
+
var init_config_v2_mutations = () => {};
|
|
45976
|
+
|
|
45977
|
+
// src/core/env-file-writer.ts
|
|
45978
|
+
import { chmod as chmod2, mkdir as mkdir18, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
45979
|
+
import { dirname as dirname16 } from "path";
|
|
45980
|
+
function escapeForRegExp(value) {
|
|
45981
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45982
|
+
}
|
|
45983
|
+
async function upsertEnvVar(envPath, varName, value) {
|
|
45984
|
+
let content = "";
|
|
45985
|
+
try {
|
|
45986
|
+
content = await readFile9(envPath, "utf8");
|
|
45987
|
+
} catch (cause) {
|
|
45988
|
+
const code = cause.code;
|
|
45989
|
+
if (code !== "ENOENT")
|
|
45990
|
+
throw cause;
|
|
45991
|
+
await mkdir18(dirname16(envPath), { recursive: true });
|
|
45992
|
+
}
|
|
45993
|
+
const line = `${varName}="${value}"`;
|
|
45994
|
+
const existing = new RegExp(`^${escapeForRegExp(varName)}=.*$`, "m");
|
|
45995
|
+
if (existing.test(content)) {
|
|
45996
|
+
content = content.replace(existing, () => line);
|
|
45997
|
+
} else {
|
|
45998
|
+
content = content.length && !content.endsWith(`
|
|
45999
|
+
`) ? `${content}
|
|
46000
|
+
${line}
|
|
46001
|
+
` : `${content}${line}
|
|
46002
|
+
`;
|
|
46003
|
+
}
|
|
46004
|
+
await writeFile9(envPath, content, { mode: SECRET_FILE_MODE });
|
|
46005
|
+
await chmod2(envPath, SECRET_FILE_MODE);
|
|
46006
|
+
}
|
|
46007
|
+
var SECRET_FILE_MODE = 384;
|
|
46008
|
+
var init_env_file_writer = () => {};
|
|
46009
|
+
|
|
46010
|
+
// src/core/connection-credential.ts
|
|
46011
|
+
import { join as join39 } from "path";
|
|
46012
|
+
async function readRawConfig(storagePath) {
|
|
46013
|
+
const configPath = join39(storagePath, "config.json");
|
|
46014
|
+
const file = Bun.file(configPath);
|
|
46015
|
+
if (!await file.exists()) {
|
|
46016
|
+
throw new ConfigError(`\u627E\u4E0D\u5230\u8A2D\u5B9A\u6A94\uFF1A${configPath}`);
|
|
46017
|
+
}
|
|
46018
|
+
return JSON.parse(await file.text());
|
|
46019
|
+
}
|
|
46020
|
+
function envRefName(value) {
|
|
46021
|
+
if (typeof value === "object" && value !== null && "$env" in value) {
|
|
46022
|
+
return String(value.$env);
|
|
46023
|
+
}
|
|
46024
|
+
return;
|
|
46025
|
+
}
|
|
46026
|
+
async function resolvePasswordTarget(projectPath, connectionName) {
|
|
46027
|
+
const storagePath = await resolveConfigStoragePath(projectPath);
|
|
46028
|
+
const raw = await readRawConfig(storagePath);
|
|
46029
|
+
if (detectConfigVersion(raw) !== 2) {
|
|
46030
|
+
if (connectionName && connectionName !== V1_CONNECTION_NAME) {
|
|
46031
|
+
throw new ConfigError(`\u9019\u662F v1 \u8A2D\u5B9A\uFF0C\u53EA\u6709\u4E00\u689D\u9023\u7DDA\uFF0C\u7121\u6CD5\u6307\u5B9A\u9023\u7DDA\u540D\u7A31 '${connectionName}'\u3002` + `\u9700\u8981\u591A\u9023\u7DDA\u8ACB\u5148\u7528 dbcli init --conn-name <name> \u5347\u7D1A\u5230 v2\u3002`);
|
|
46032
|
+
}
|
|
46033
|
+
const connection2 = raw.connection ?? {};
|
|
46034
|
+
const customRef = envRefName(connection2.password);
|
|
46035
|
+
if (customRef !== undefined && customRef !== V1_VAR_NAME) {
|
|
46036
|
+
throw new ConfigError(`\u9019\u500B v1 \u8A2D\u5B9A\u7684\u5BC6\u78BC\u4F86\u81EA\u74B0\u5883\u8B8A\u6578 '${customRef}'\uFF0Cdbcli \u7121\u6CD5\u4EE3\u70BA\u8F2A\u66FF\u3002` + `\u8ACB\u76F4\u63A5\u66F4\u65B0\u8A72\u74B0\u5883\u8B8A\u6578\uFF0C\u6216\u7528 dbcli init --conn-name <name> \u5347\u7D1A\u5230 v2 \u5F8C\u518D\u8F2A\u66FF\u3002`);
|
|
46037
|
+
}
|
|
46038
|
+
return {
|
|
46039
|
+
connection: V1_CONNECTION_NAME,
|
|
46040
|
+
envFile: DEFAULT_ENV_FILE,
|
|
46041
|
+
varName: V1_VAR_NAME,
|
|
46042
|
+
convertedToEnvRef: false
|
|
46043
|
+
};
|
|
46044
|
+
}
|
|
46045
|
+
const config = await readV2Config(projectPath);
|
|
46046
|
+
const targetName = connectionName ?? config.default;
|
|
46047
|
+
const connection = config.connections[targetName];
|
|
46048
|
+
if (!connection) {
|
|
46049
|
+
throw new ConfigError(connectionNotFoundMessage(targetName, Object.keys(config.connections)));
|
|
46050
|
+
}
|
|
46051
|
+
const referencedVar = envRefName(connection.password);
|
|
46052
|
+
return {
|
|
46053
|
+
connection: targetName,
|
|
46054
|
+
envFile: connection.envFile ?? DEFAULT_ENV_FILE,
|
|
46055
|
+
varName: referencedVar ?? envVarNameFor(targetName, "password"),
|
|
46056
|
+
convertedToEnvRef: referencedVar === undefined
|
|
46057
|
+
};
|
|
46058
|
+
}
|
|
46059
|
+
function assertSingleLine(value) {
|
|
46060
|
+
if (value.includes(`
|
|
46061
|
+
`) || value.includes("\r")) {
|
|
46062
|
+
throw new ConfigError("\u5BC6\u78BC\u4E0D\u53EF\u5305\u542B\u63DB\u884C\u5B57\u5143");
|
|
46063
|
+
}
|
|
46064
|
+
}
|
|
46065
|
+
async function stripV1LiteralPassword(storagePath, raw) {
|
|
46066
|
+
const connection = raw.connection ?? {};
|
|
46067
|
+
if (typeof connection.password !== "string")
|
|
46068
|
+
return;
|
|
46069
|
+
const stripped = { ...raw, connection: { ...connection } };
|
|
46070
|
+
delete stripped.connection.password;
|
|
46071
|
+
await writeConfigWithIntegrity(storagePath, JSON.stringify(stripped, null, 2));
|
|
46072
|
+
}
|
|
46073
|
+
async function setConnectionPassword(projectPath, connectionName, password) {
|
|
46074
|
+
assertConfigMutationApproved();
|
|
46075
|
+
assertSingleLine(password);
|
|
46076
|
+
const target = await resolvePasswordTarget(projectPath, connectionName);
|
|
46077
|
+
const storagePath = await resolveConfigStoragePath(projectPath);
|
|
46078
|
+
const raw = await readRawConfig(storagePath);
|
|
46079
|
+
await upsertEnvVar(join39(storagePath, target.envFile), target.varName, password);
|
|
46080
|
+
if (detectConfigVersion(raw) !== 2) {
|
|
46081
|
+
await stripV1LiteralPassword(storagePath, raw);
|
|
46082
|
+
return target;
|
|
46083
|
+
}
|
|
46084
|
+
const config = await readV2Config(projectPath);
|
|
46085
|
+
const connection = config.connections[target.connection];
|
|
46086
|
+
const needsEnvFile = connection.envFile === undefined;
|
|
46087
|
+
if (target.convertedToEnvRef || needsEnvFile) {
|
|
46088
|
+
await writeV2Config(projectPath, {
|
|
46089
|
+
...config,
|
|
46090
|
+
connections: {
|
|
46091
|
+
...config.connections,
|
|
46092
|
+
[target.connection]: {
|
|
46093
|
+
...connection,
|
|
46094
|
+
envFile: target.envFile,
|
|
46095
|
+
...target.convertedToEnvRef && { password: { $env: target.varName } }
|
|
46096
|
+
}
|
|
46097
|
+
}
|
|
46098
|
+
});
|
|
46099
|
+
}
|
|
46100
|
+
return target;
|
|
46101
|
+
}
|
|
46102
|
+
var DEFAULT_ENV_FILE = ".env.local", V1_VAR_NAME = "DBCLI_PASSWORD", V1_CONNECTION_NAME = "default";
|
|
46103
|
+
var init_connection_credential = __esm(() => {
|
|
46104
|
+
init_config_binding();
|
|
46105
|
+
init_config_v2();
|
|
46106
|
+
init_config_integrity();
|
|
46107
|
+
init_config_mutation_guard();
|
|
46108
|
+
init_config_v2_mutations();
|
|
46109
|
+
init_errors3();
|
|
46110
|
+
init_env_file_writer();
|
|
46111
|
+
});
|
|
46112
|
+
|
|
46113
|
+
// src/commands/credential.ts
|
|
46114
|
+
var exports_credential = {};
|
|
46115
|
+
__export(exports_credential, {
|
|
46116
|
+
resolveNewPassword: () => resolveNewPassword,
|
|
46117
|
+
passwordCommand: () => passwordCommand
|
|
46118
|
+
});
|
|
46119
|
+
async function readAllStdin() {
|
|
46120
|
+
return await Bun.stdin.text();
|
|
46121
|
+
}
|
|
46122
|
+
async function resolveNewPassword(options, io) {
|
|
46123
|
+
if (options.password !== undefined && options.stdin) {
|
|
46124
|
+
throw new ConfigError(t("password.source_conflict"));
|
|
46125
|
+
}
|
|
46126
|
+
let value;
|
|
46127
|
+
if (options.password !== undefined) {
|
|
46128
|
+
value = options.password;
|
|
46129
|
+
} else if (options.stdin) {
|
|
46130
|
+
value = (await io.readStdin()).replace(/\r?\n$/, "");
|
|
46131
|
+
} else if (io.isTTY) {
|
|
46132
|
+
value = await io.prompt();
|
|
46133
|
+
} else {
|
|
46134
|
+
throw new ConfigError(t("password.needs_source"));
|
|
46135
|
+
}
|
|
46136
|
+
if (value.length === 0) {
|
|
46137
|
+
throw new ConfigError(t("password.empty"));
|
|
46138
|
+
}
|
|
46139
|
+
return value;
|
|
46140
|
+
}
|
|
46141
|
+
async function verifyNewPassword(configPath, target, connectionName, password) {
|
|
46142
|
+
const storagePath = await resolveConfigStoragePath(configPath);
|
|
46143
|
+
const previous = process.env[target.varName];
|
|
46144
|
+
process.env[target.varName] = password;
|
|
46145
|
+
try {
|
|
46146
|
+
const config = await configModule.read(storagePath, connectionName);
|
|
46147
|
+
const adapter = AdapterFactory.createAdapter({
|
|
46148
|
+
...config.connection,
|
|
46149
|
+
password
|
|
46150
|
+
});
|
|
46151
|
+
try {
|
|
46152
|
+
await adapter.connect();
|
|
46153
|
+
await adapter.testConnection();
|
|
46154
|
+
} finally {
|
|
46155
|
+
await adapter.disconnect().catch(() => {
|
|
46156
|
+
return;
|
|
46157
|
+
});
|
|
46158
|
+
}
|
|
46159
|
+
} finally {
|
|
46160
|
+
if (previous === undefined)
|
|
46161
|
+
delete process.env[target.varName];
|
|
46162
|
+
else
|
|
46163
|
+
process.env[target.varName] = previous;
|
|
46164
|
+
}
|
|
46165
|
+
}
|
|
46166
|
+
var passwordCommand;
|
|
46167
|
+
var init_credential = __esm(() => {
|
|
46168
|
+
init_esm();
|
|
46169
|
+
init_adapters();
|
|
46170
|
+
init_config();
|
|
46171
|
+
init_config_mutation_guard();
|
|
46172
|
+
init_config_binding();
|
|
46173
|
+
init_connection_credential();
|
|
46174
|
+
init_config_path();
|
|
46175
|
+
init_prompts();
|
|
46176
|
+
init_errors3();
|
|
46177
|
+
init_message_loader();
|
|
46178
|
+
passwordCommand = new Command("password").description(t("password.description")).argument("[connection]", t("password.arg_connection")).option("--stdin", t("password.opt_stdin")).option("--password <value>", t("password.opt_password")).option("--skip-test", t("password.opt_skip_test")).option("--format <format>", "Output format (text, json)", "text").action(async (connection, options) => {
|
|
46179
|
+
try {
|
|
46180
|
+
assertConfigMutationApproved();
|
|
46181
|
+
const configPath = resolveConfigPath(passwordCommand);
|
|
46182
|
+
const target = await resolvePasswordTarget(configPath, connection);
|
|
46183
|
+
const value = await resolveNewPassword({
|
|
46184
|
+
password: typeof options.password === "string" ? options.password : undefined,
|
|
46185
|
+
stdin: options.stdin === true
|
|
46186
|
+
}, {
|
|
46187
|
+
isTTY: Boolean(process.stdin.isTTY),
|
|
46188
|
+
readStdin: readAllStdin,
|
|
46189
|
+
prompt: () => promptUser.secret(t_vars("password.prompt", { name: target.connection }))
|
|
46190
|
+
});
|
|
46191
|
+
if (options.skipTest !== true) {
|
|
46192
|
+
await verifyNewPassword(configPath, target, connection, value);
|
|
46193
|
+
}
|
|
46194
|
+
const written = await setConnectionPassword(configPath, connection, value);
|
|
46195
|
+
if (options.format === "json") {
|
|
46196
|
+
console.log(JSON.stringify({ success: true, ...written }, null, 2));
|
|
46197
|
+
} else {
|
|
46198
|
+
console.log(t_vars("password.updated", {
|
|
46199
|
+
name: written.connection,
|
|
46200
|
+
envFile: written.envFile,
|
|
46201
|
+
varName: written.varName
|
|
46202
|
+
}));
|
|
46203
|
+
if (written.convertedToEnvRef) {
|
|
46204
|
+
console.log(t_vars("password.converted", { varName: written.varName }));
|
|
46205
|
+
}
|
|
46206
|
+
if (options.skipTest === true) {
|
|
46207
|
+
console.log(t("password.skipped_test"));
|
|
46208
|
+
}
|
|
46209
|
+
}
|
|
46210
|
+
} catch (error) {
|
|
46211
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46212
|
+
if (options.format === "json") {
|
|
46213
|
+
console.error(JSON.stringify({ success: false, error: message }, null, 2));
|
|
46214
|
+
} else {
|
|
46215
|
+
console.error(t_vars("errors.message", { message }));
|
|
46216
|
+
}
|
|
46217
|
+
process.exit(1);
|
|
46218
|
+
}
|
|
46219
|
+
});
|
|
46220
|
+
});
|
|
46221
|
+
|
|
45688
46222
|
// src/proxy/relay.ts
|
|
45689
46223
|
class TcpRelay {
|
|
45690
46224
|
clientBytes = 0;
|
|
@@ -45759,8 +46293,8 @@ var init_sql_metadata = __esm(() => {
|
|
|
45759
46293
|
});
|
|
45760
46294
|
|
|
45761
46295
|
// src/proxy/events.ts
|
|
45762
|
-
import { appendFile as appendFile2, mkdir as
|
|
45763
|
-
import { dirname as
|
|
46296
|
+
import { appendFile as appendFile2, mkdir as mkdir19, readFile as readFile10, stat as stat9 } from "fs/promises";
|
|
46297
|
+
import { dirname as dirname17 } from "path";
|
|
45764
46298
|
function hasSql(e) {
|
|
45765
46299
|
return e.type === "query_observed" || e.type === "query_completed" || e.type === "query_errored";
|
|
45766
46300
|
}
|
|
@@ -45799,7 +46333,7 @@ class EventWriter {
|
|
|
45799
46333
|
}
|
|
45800
46334
|
async writeInternal(event) {
|
|
45801
46335
|
if (!this.dirEnsured) {
|
|
45802
|
-
await
|
|
46336
|
+
await mkdir19(dirname17(this.path), { recursive: true });
|
|
45803
46337
|
this.dirEnsured = true;
|
|
45804
46338
|
}
|
|
45805
46339
|
if (!this.initialized) {
|
|
@@ -45823,7 +46357,7 @@ class EventWriter {
|
|
|
45823
46357
|
try {
|
|
45824
46358
|
const s = await stat9(this.path);
|
|
45825
46359
|
this.currentSizeBytes = s.size;
|
|
45826
|
-
const raw = await
|
|
46360
|
+
const raw = await readFile10(this.path, "utf8");
|
|
45827
46361
|
this.currentEntryCount = raw.split(`
|
|
45828
46362
|
`).filter(Boolean).length;
|
|
45829
46363
|
} catch {
|
|
@@ -46420,7 +46954,7 @@ var init_server = __esm(() => {
|
|
|
46420
46954
|
});
|
|
46421
46955
|
|
|
46422
46956
|
// src/proxy/event-reader.ts
|
|
46423
|
-
import { readFile as
|
|
46957
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
46424
46958
|
async function readEvents(path4, opts) {
|
|
46425
46959
|
const candidates = opts.includeRotated ? [path4, `${path4}.1`] : [path4];
|
|
46426
46960
|
const files = [];
|
|
@@ -46429,7 +46963,7 @@ async function readEvents(path4, opts) {
|
|
|
46429
46963
|
for (const file of candidates) {
|
|
46430
46964
|
let raw;
|
|
46431
46965
|
try {
|
|
46432
|
-
raw = await
|
|
46966
|
+
raw = await readFile11(file, "utf8");
|
|
46433
46967
|
} catch (err) {
|
|
46434
46968
|
if (err.code !== "ENOENT")
|
|
46435
46969
|
throw err;
|
|
@@ -46860,7 +47394,7 @@ __export(exports_proxy, {
|
|
|
46860
47394
|
proxyCommand: () => proxyCommand,
|
|
46861
47395
|
parseHostPort: () => parseHostPort
|
|
46862
47396
|
});
|
|
46863
|
-
import { join as
|
|
47397
|
+
import { join as join40 } from "path";
|
|
46864
47398
|
function parseHostPort(value) {
|
|
46865
47399
|
const idx = value.lastIndexOf(":");
|
|
46866
47400
|
if (idx <= 0 || idx === value.length - 1) {
|
|
@@ -46923,7 +47457,7 @@ async function runProxy(subcommandEngine, options, command) {
|
|
|
46923
47457
|
target: options.target,
|
|
46924
47458
|
connection
|
|
46925
47459
|
});
|
|
46926
|
-
const eventsPath = options.events ??
|
|
47460
|
+
const eventsPath = options.events ?? join40(".dbcli", "proxy", "events.jsonl");
|
|
46927
47461
|
const slowMs = Number(options.slowMs ?? 1000);
|
|
46928
47462
|
if (!Number.isFinite(slowMs) || slowMs < 0) {
|
|
46929
47463
|
throw new Error(`Invalid --slow-ms "${options.slowMs}". Expected a non-negative number`);
|
|
@@ -46972,7 +47506,7 @@ async function runProxy(subcommandEngine, options, command) {
|
|
|
46972
47506
|
}
|
|
46973
47507
|
}
|
|
46974
47508
|
function addCommonOptions(cmd) {
|
|
46975
|
-
return cmd.option("--listen <host:port>", "Local proxy listen address (required)").option("--target <host:port>", "Upstream DB target (optional when config provides host/port)").option("--events <path>", "Event JSONL path",
|
|
47509
|
+
return cmd.option("--listen <host:port>", "Local proxy listen address (required)").option("--target <host:port>", "Upstream DB target (optional when config provides host/port)").option("--events <path>", "Event JSONL path", join40(".dbcli", "proxy", "events.jsonl")).option("--slow-ms <number>", "Threshold (ms); queries at/above it get slow:true in the event + a terminal warning", "1000").option("--redact <mode>", "SQL redaction: none | literals", "none").option("--format <format>", "Runtime status output: text | json", "text");
|
|
46976
47510
|
}
|
|
46977
47511
|
function parseNonNegInt(value, flag, fallback) {
|
|
46978
47512
|
if (value === undefined)
|
|
@@ -46990,7 +47524,7 @@ async function runAnalyze(options) {
|
|
|
46990
47524
|
const top = parseNonNegInt(options.top, "top", 20);
|
|
46991
47525
|
const slowMs = parseNonNegInt(options.slowMs, "slow-ms", 1000);
|
|
46992
47526
|
const nPlusOne = parseNonNegInt(options.nPlusOne, "n-plus-one", 10);
|
|
46993
|
-
const eventsPath = options.events ??
|
|
47527
|
+
const eventsPath = options.events ?? join40(".dbcli", "proxy", "events.jsonl");
|
|
46994
47528
|
const { events, malformedLines, files } = await readEvents(eventsPath, {
|
|
46995
47529
|
includeRotated: options.includeRotated !== false
|
|
46996
47530
|
});
|
|
@@ -47043,7 +47577,7 @@ var init_proxy = __esm(() => {
|
|
|
47043
47577
|
});
|
|
47044
47578
|
}
|
|
47045
47579
|
ANALYZE_FORMATS = ["json", "text", "markdown"];
|
|
47046
|
-
proxyCommand.command("analyze").description("Analyze a proxy event log offline (no DB connection)").option("--events <path>", "Event JSONL path",
|
|
47580
|
+
proxyCommand.command("analyze").description("Analyze a proxy event log offline (no DB connection)").option("--events <path>", "Event JSONL path", join40(".dbcli", "proxy", "events.jsonl")).option("--format <format>", "Output format: json | text | markdown (QueryLens)", "json").option("--top <number>", "Rows shown in text + suggestedCommands depth", "20").option("--slow-ms <number>", "Slow-query threshold (ms) for slowCount", "1000").option("--n-plus-one <number>", "Min repeats per (session,fingerprint) to flag N+1", "10").option("--no-include-rotated", "Do not merge the rotated <events>.1 segment").action(async (options) => {
|
|
47047
47581
|
await runAnalyze(options);
|
|
47048
47582
|
});
|
|
47049
47583
|
addCommonOptions(proxyCommand).action(async (options, command) => {
|
|
@@ -47377,9 +47911,9 @@ var init_semantic2 = __esm(() => {
|
|
|
47377
47911
|
});
|
|
47378
47912
|
|
|
47379
47913
|
// src/core/design/index.ts
|
|
47380
|
-
import { join as
|
|
47914
|
+
import { join as join41 } from "path";
|
|
47381
47915
|
function defaultDesignFile(workspaceRoot) {
|
|
47382
|
-
return
|
|
47916
|
+
return join41(workspaceRoot, DEFAULT_FILE3);
|
|
47383
47917
|
}
|
|
47384
47918
|
function parseDesignSpec(raw, filePath = DEFAULT_FILE3) {
|
|
47385
47919
|
const parsed = specSchema.safeParse(raw);
|
|
@@ -48210,8 +48744,8 @@ var exports_backfill = {};
|
|
|
48210
48744
|
__export(exports_backfill, {
|
|
48211
48745
|
backfillCommand: () => backfillCommand
|
|
48212
48746
|
});
|
|
48213
|
-
import { dirname as
|
|
48214
|
-
import { mkdir as
|
|
48747
|
+
import { dirname as dirname18, resolve as resolve10 } from "path";
|
|
48748
|
+
import { mkdir as mkdir20 } from "fs/promises";
|
|
48215
48749
|
function identityFor(config, name) {
|
|
48216
48750
|
const connection = config.connections[name];
|
|
48217
48751
|
if (!connection) {
|
|
@@ -48261,7 +48795,7 @@ var init_backfill = __esm(() => {
|
|
|
48261
48795
|
return;
|
|
48262
48796
|
}
|
|
48263
48797
|
const out = resolve10(options.out ?? `.dbcli/backfills/${artifact2.source.sha256.slice(0, 12)}.json`);
|
|
48264
|
-
await
|
|
48798
|
+
await mkdir20(dirname18(out), { recursive: true });
|
|
48265
48799
|
await Bun.write(out, JSON.stringify(artifact2, null, 2) + `
|
|
48266
48800
|
`);
|
|
48267
48801
|
console.log(JSON.stringify({ path: out, artifact: artifact2 }, null, 2));
|
|
@@ -48270,8 +48804,8 @@ var init_backfill = __esm(() => {
|
|
|
48270
48804
|
|
|
48271
48805
|
// src/core/evidence-pack/index.ts
|
|
48272
48806
|
import { createHash as createHash8, randomUUID as randomUUID6 } from "crypto";
|
|
48273
|
-
import { link as link3, lstat as lstat5, mkdir as
|
|
48274
|
-
import { basename as basename9, dirname as
|
|
48807
|
+
import { link as link3, lstat as lstat5, mkdir as mkdir21, realpath as realpath2, unlink as unlink8, writeFile as writeFile10 } from "fs/promises";
|
|
48808
|
+
import { basename as basename9, dirname as dirname19, relative as relative5, resolve as resolve11, sep as sep7 } from "path";
|
|
48275
48809
|
function isRecord4(value) {
|
|
48276
48810
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48277
48811
|
}
|
|
@@ -48522,7 +49056,7 @@ async function realExistingAncestor(path4) {
|
|
|
48522
49056
|
} catch (error) {
|
|
48523
49057
|
if (error.code !== "ENOENT")
|
|
48524
49058
|
throw error;
|
|
48525
|
-
const parent =
|
|
49059
|
+
const parent = dirname19(candidate);
|
|
48526
49060
|
if (parent === candidate)
|
|
48527
49061
|
throw error;
|
|
48528
49062
|
candidate = parent;
|
|
@@ -48534,12 +49068,12 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
48534
49068
|
const requestedTarget = resolve11(workspace, outputPath);
|
|
48535
49069
|
if (!isInside(workspace, requestedTarget))
|
|
48536
49070
|
throw new EvidencePackValidationError("output path must stay inside the workspace");
|
|
48537
|
-
const parent =
|
|
49071
|
+
const parent = dirname19(requestedTarget);
|
|
48538
49072
|
const existingAncestor2 = await realExistingAncestor(parent);
|
|
48539
49073
|
if (!isInside(workspace, existingAncestor2) && existingAncestor2 !== workspace) {
|
|
48540
49074
|
throw new EvidencePackValidationError("output path resolves outside the workspace");
|
|
48541
49075
|
}
|
|
48542
|
-
await
|
|
49076
|
+
await mkdir21(parent, { recursive: true });
|
|
48543
49077
|
const realParent = await realpath2(parent);
|
|
48544
49078
|
if (!isInside(workspace, realParent) && realParent !== workspace) {
|
|
48545
49079
|
throw new EvidencePackValidationError("output path resolves outside the workspace");
|
|
@@ -48556,7 +49090,7 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
48556
49090
|
}
|
|
48557
49091
|
const temp = resolve11(realParent, `.${basename9(target)}.${process.pid}.${randomUUID6()}.tmp`);
|
|
48558
49092
|
try {
|
|
48559
|
-
await
|
|
49093
|
+
await writeFile10(temp, `${JSON.stringify(pack, null, 2)}
|
|
48560
49094
|
`, "utf8");
|
|
48561
49095
|
try {
|
|
48562
49096
|
await link3(temp, target);
|
|
@@ -48633,7 +49167,7 @@ var exports_evidence = {};
|
|
|
48633
49167
|
__export(exports_evidence, {
|
|
48634
49168
|
evidenceCommand: () => evidenceCommand
|
|
48635
49169
|
});
|
|
48636
|
-
import { join as
|
|
49170
|
+
import { join as join42, relative as relative6, resolve as resolve12, sep as sep8 } from "path";
|
|
48637
49171
|
import { realpath as realpath3 } from "fs/promises";
|
|
48638
49172
|
import { createHash as createHash9 } from "crypto";
|
|
48639
49173
|
function safeMessage(error) {
|
|
@@ -48709,7 +49243,7 @@ async function auditEntries(configPath, config) {
|
|
|
48709
49243
|
throw new EvidencePackValidationError("audit evidence is disabled");
|
|
48710
49244
|
const storagePath = await resolveConfigStoragePath(configPath);
|
|
48711
49245
|
const connectionName = config.effectiveConnectionName || getGlobalConnectionName() || "default";
|
|
48712
|
-
const file =
|
|
49246
|
+
const file = join42(storagePath, ".dbcli", "audit", `${connectionName}.jsonl`);
|
|
48713
49247
|
return { connectionName, entries: await readEntries(file, { include_rotated: true }) };
|
|
48714
49248
|
}
|
|
48715
49249
|
function auditReference(entry, connectionName) {
|
|
@@ -50073,8 +50607,8 @@ __export(exports_impact, {
|
|
|
50073
50607
|
impactCommand: () => impactCommand
|
|
50074
50608
|
});
|
|
50075
50609
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
50076
|
-
import { link as link4, lstat as lstat8, mkdir as
|
|
50077
|
-
import { basename as basename10, dirname as
|
|
50610
|
+
import { link as link4, lstat as lstat8, mkdir as mkdir22, realpath as realpath5, unlink as unlink9, writeFile as writeFile11 } from "fs/promises";
|
|
50611
|
+
import { basename as basename10, dirname as dirname20, relative as relative8, resolve as resolve14, sep as sep10 } from "path";
|
|
50078
50612
|
function collectOption3(value, previous) {
|
|
50079
50613
|
return [...previous, value];
|
|
50080
50614
|
}
|
|
@@ -50278,7 +50812,7 @@ async function realExistingAncestor2(path4) {
|
|
|
50278
50812
|
} catch (error) {
|
|
50279
50813
|
if (error.code !== "ENOENT")
|
|
50280
50814
|
throw error;
|
|
50281
|
-
const parent =
|
|
50815
|
+
const parent = dirname20(candidate);
|
|
50282
50816
|
if (parent === candidate)
|
|
50283
50817
|
throw error;
|
|
50284
50818
|
candidate = parent;
|
|
@@ -50290,11 +50824,11 @@ async function resolveOutput(workspaceRoot, outputPath) {
|
|
|
50290
50824
|
const target = resolve14(workspace, outputPath);
|
|
50291
50825
|
if (!isInside2(workspace, target))
|
|
50292
50826
|
throw new Error("output path must stay inside the workspace");
|
|
50293
|
-
const parent =
|
|
50827
|
+
const parent = dirname20(target);
|
|
50294
50828
|
const existingAncestor2 = await realExistingAncestor2(parent);
|
|
50295
50829
|
if (!isInside2(workspace, existingAncestor2) && existingAncestor2 !== workspace)
|
|
50296
50830
|
throw new Error("output path must stay inside the workspace");
|
|
50297
|
-
await
|
|
50831
|
+
await mkdir22(parent, { recursive: true });
|
|
50298
50832
|
const realParent = await realpath5(parent);
|
|
50299
50833
|
if (!isInside2(workspace, realParent) && realParent !== workspace)
|
|
50300
50834
|
throw new Error("output path must stay inside the workspace");
|
|
@@ -50302,7 +50836,7 @@ async function resolveOutput(workspaceRoot, outputPath) {
|
|
|
50302
50836
|
}
|
|
50303
50837
|
async function assertOutputParent(workspaceRoot, path4) {
|
|
50304
50838
|
const workspace = await realpath5(workspaceRoot);
|
|
50305
|
-
const parent = await realpath5(
|
|
50839
|
+
const parent = await realpath5(dirname20(path4));
|
|
50306
50840
|
if (!isInside2(workspace, parent) && parent !== workspace) {
|
|
50307
50841
|
throw new Error("output path must stay inside the workspace");
|
|
50308
50842
|
}
|
|
@@ -50316,9 +50850,9 @@ async function writeOutput(workspaceRoot, path4, content) {
|
|
|
50316
50850
|
if (error.code !== "ENOENT")
|
|
50317
50851
|
throw error;
|
|
50318
50852
|
}
|
|
50319
|
-
const temp = resolve14(
|
|
50853
|
+
const temp = resolve14(dirname20(path4), `.${basename10(path4)}.${process.pid}.${randomUUID7()}.tmp`);
|
|
50320
50854
|
try {
|
|
50321
|
-
await
|
|
50855
|
+
await writeFile11(temp, content, "utf8");
|
|
50322
50856
|
await assertOutputParent(workspaceRoot, path4);
|
|
50323
50857
|
await link4(temp, path4);
|
|
50324
50858
|
} catch (error) {
|
|
@@ -50457,6 +50991,7 @@ function buildProgram() {
|
|
|
50457
50991
|
program2.addCommand(shellCommand);
|
|
50458
50992
|
program2.addCommand(migrateCommand);
|
|
50459
50993
|
program2.addCommand(useCommand);
|
|
50994
|
+
program2.addCommand(passwordCommand);
|
|
50460
50995
|
program2.addCommand(queriesCommand);
|
|
50461
50996
|
program2.addCommand(explainCommand);
|
|
50462
50997
|
program2.addCommand(lintCommand);
|
|
@@ -50510,6 +51045,7 @@ var init_program = __esm(() => {
|
|
|
50510
51045
|
init_shell3();
|
|
50511
51046
|
init_migrate();
|
|
50512
51047
|
init_use();
|
|
51048
|
+
init_credential();
|
|
50513
51049
|
init_proxy();
|
|
50514
51050
|
init_semantic2();
|
|
50515
51051
|
init_design2();
|
|
@@ -50533,9 +51069,9 @@ __export(exports_completion, {
|
|
|
50533
51069
|
detectShell: () => detectShell,
|
|
50534
51070
|
completionCommand: () => completionCommand
|
|
50535
51071
|
});
|
|
50536
|
-
import { join as
|
|
51072
|
+
import { join as join43 } from "path";
|
|
50537
51073
|
import { homedir as homedir4 } from "os";
|
|
50538
|
-
import { mkdir as
|
|
51074
|
+
import { mkdir as mkdir23 } from "fs/promises";
|
|
50539
51075
|
function resolveHome() {
|
|
50540
51076
|
return process.env.HOME ?? homedir4();
|
|
50541
51077
|
}
|
|
@@ -50785,11 +51321,11 @@ function getInstallPath2(shell) {
|
|
|
50785
51321
|
const home = resolveHome();
|
|
50786
51322
|
switch (shell) {
|
|
50787
51323
|
case "bash":
|
|
50788
|
-
return
|
|
51324
|
+
return join43(home, ".bashrc");
|
|
50789
51325
|
case "zsh":
|
|
50790
|
-
return
|
|
51326
|
+
return join43(home, ".zshrc");
|
|
50791
51327
|
case "fish":
|
|
50792
|
-
return
|
|
51328
|
+
return join43(home, ".config", "fish", "completions", "dbcli.fish");
|
|
50793
51329
|
default:
|
|
50794
51330
|
throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
|
|
50795
51331
|
}
|
|
@@ -50807,8 +51343,8 @@ function detectShell() {
|
|
|
50807
51343
|
async function installCompletion(shell, script) {
|
|
50808
51344
|
const targetPath = getInstallPath2(shell);
|
|
50809
51345
|
if (shell === "fish") {
|
|
50810
|
-
const dir =
|
|
50811
|
-
await
|
|
51346
|
+
const dir = join43(resolveHome(), ".config", "fish", "completions");
|
|
51347
|
+
await mkdir23(dir, { recursive: true });
|
|
50812
51348
|
await Bun.file(targetPath).write(script);
|
|
50813
51349
|
console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
|
|
50814
51350
|
return;
|
|
@@ -51147,6 +51683,12 @@ var COMMAND_LOADERS = {
|
|
|
51147
51683
|
program2.addCommand(migrateCommand2);
|
|
51148
51684
|
};
|
|
51149
51685
|
},
|
|
51686
|
+
password: async () => {
|
|
51687
|
+
const { passwordCommand: passwordCommand2 } = await Promise.resolve().then(() => (init_credential(), exports_credential));
|
|
51688
|
+
return (program2) => {
|
|
51689
|
+
program2.addCommand(passwordCommand2);
|
|
51690
|
+
};
|
|
51691
|
+
},
|
|
51150
51692
|
use: async () => {
|
|
51151
51693
|
const { useCommand: useCommand2 } = await Promise.resolve().then(() => (init_use(), exports_use));
|
|
51152
51694
|
return (program2) => {
|
|
@@ -51287,7 +51829,7 @@ async function buildProgramFor(argv) {
|
|
|
51287
51829
|
|
|
51288
51830
|
// src/cli-runtime.ts
|
|
51289
51831
|
init_cli_error();
|
|
51290
|
-
import { join as
|
|
51832
|
+
import { join as join44 } from "path";
|
|
51291
51833
|
import { writeSync as writeSync2 } from "fs";
|
|
51292
51834
|
import { format } from "util";
|
|
51293
51835
|
function installSynchronousRedirectedStdout() {
|
|
@@ -51425,7 +51967,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
|
|
|
51425
51967
|
try {
|
|
51426
51968
|
let cache = null;
|
|
51427
51969
|
try {
|
|
51428
|
-
const cacheFile = Bun.file(
|
|
51970
|
+
const cacheFile = Bun.file(join44(configPath, "version-check.json"));
|
|
51429
51971
|
if (await cacheFile.exists()) {
|
|
51430
51972
|
cache = await cacheFile.json();
|
|
51431
51973
|
}
|