@carllee1983/dbcli 12.0.0 → 12.1.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/dbcli.mdc +46 -0
- package/.cursor/skills/dbcli/reference.md +54 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/skills/dbcli/SKILL.md +46 -0
- package/.github/skills/dbcli/reference.md +54 -1
- package/CHANGELOG.md +18 -0
- package/README.md +16 -13
- package/assets/SKILL.md +46 -0
- package/assets/reference.md +54 -1
- package/dist/cli-runtime.mjs +2097 -1807
- package/dist/cli.mjs +2 -1
- package/dist/core.d.ts +629 -562
- package/dist/core.mjs +6185 -6094
- package/gemini-extension.json +1 -1
- package/package.json +2 -1
- package/plugins/dbcli-agent/.codex-plugin/plugin.json +1 -1
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +46 -0
- package/plugins/dbcli-agent/skills/dbcli/reference.md +54 -1
- package/skills/dbcli/SKILL.md +46 -0
- package/skills/dbcli/reference.md +54 -1
package/dist/cli-runtime.mjs
CHANGED
|
@@ -1618,7 +1618,7 @@ var package_default;
|
|
|
1618
1618
|
var init_package = __esm(() => {
|
|
1619
1619
|
package_default = {
|
|
1620
1620
|
name: "@carllee1983/dbcli",
|
|
1621
|
-
version: "12.
|
|
1621
|
+
version: "12.1.0",
|
|
1622
1622
|
description: "Database CLI for AI agents",
|
|
1623
1623
|
type: "module",
|
|
1624
1624
|
publishConfig: {
|
|
@@ -1687,6 +1687,7 @@ var init_package = __esm(() => {
|
|
|
1687
1687
|
prepublishOnly: "bun run build",
|
|
1688
1688
|
audit: "bun run scripts/audit.ts",
|
|
1689
1689
|
"release:check": "bash scripts/release-check.sh",
|
|
1690
|
+
"release:extras": "bash scripts/release-extras.sh",
|
|
1690
1691
|
"plugin:sync": "bun run scripts/sync-plugin-assets.ts --write",
|
|
1691
1692
|
"plugin:check": "bun run scripts/sync-plugin-assets.ts",
|
|
1692
1693
|
"manifest:sync": "bun run scripts/check-plugin-manifests.ts --write",
|
|
@@ -1935,6 +1936,1028 @@ async function checkForUpdate(currentVersion, cachePath, existingCache) {
|
|
|
1935
1936
|
};
|
|
1936
1937
|
}
|
|
1937
1938
|
|
|
1939
|
+
// resources/lang/en/messages.json
|
|
1940
|
+
var messages_default;
|
|
1941
|
+
var init_messages = __esm(() => {
|
|
1942
|
+
messages_default = {
|
|
1943
|
+
cli: {
|
|
1944
|
+
agent_output_option: "Emit one Operation Envelope v1 (capabilities and capabilities check only)"
|
|
1945
|
+
},
|
|
1946
|
+
init: {
|
|
1947
|
+
description: "Initialize dbcli configuration with .env parsing and interactive prompts",
|
|
1948
|
+
welcome: "Welcome to dbcli",
|
|
1949
|
+
prompt_connection_name: "Connection name",
|
|
1950
|
+
connection_name_required: "Connection name is required. Provide --conn-name <name> (this is separate from the database --name).",
|
|
1951
|
+
select_system: "Select database system:",
|
|
1952
|
+
prompt_host: "Database host: ",
|
|
1953
|
+
prompt_port: "Database port: ",
|
|
1954
|
+
prompt_user: "Database user: ",
|
|
1955
|
+
prompt_password: "Database password: ",
|
|
1956
|
+
secret_alternatives_sql: "pass --password, supply the credential through .env or the process environment, or store a reference with --use-env-refs",
|
|
1957
|
+
secret_alternatives_mongo_password: "pass --password, supply the credential through .env or the process environment, or paste the connection string with --uri",
|
|
1958
|
+
secret_alternatives_mongo_uri: "pass the connection string with --uri",
|
|
1959
|
+
mongo_uri_conflicts_fields: "--uri cannot be combined with {flags}: the connection string already carries those settings. Use one or the other (--name may be combined to override the database name).",
|
|
1960
|
+
mongo_uri_with_env_refs: "--uri cannot be combined with --use-env-refs: the connection string is decomposed into config fields, and its credentials are not moved into environment variables. Either remove --use-env-refs so dbcli decomposes the string into the field config, or use the --env-* flags (--env-host, --env-user, --env-password, ...) to name one variable per field.",
|
|
1961
|
+
mongo_uri_fallback_warning: "\u26A0\uFE0F This connection string {reasons}, so it cannot become fields. It is stored as-is in uri, and its credentials will sit in plaintext in the config file.",
|
|
1962
|
+
mongo_uri_reason_unix_socket: "uses a Unix domain socket",
|
|
1963
|
+
mongo_uri_reason_multi_host: "contains more than one host (a field config holds only one)",
|
|
1964
|
+
mongo_uri_reason_secret_option: "contains secret option(s) ({keys}) that would land in plaintext options",
|
|
1965
|
+
mongo_uri_reason_duplicate_key: "repeats the query key(s) {keys} (a field config can hold only one value per key)",
|
|
1966
|
+
mongo_uri_reason_non_scram: "uses the non-SCRAM auth mechanism {mechanism} (field config cannot express it)",
|
|
1967
|
+
mongo_decomposed_header: "Connection string decomposed into these fields (password hidden):",
|
|
1968
|
+
mongo_paste_env_refs_warning: "\u26A0\uFE0F --use-env-refs does not apply to a pasted connection string: its credentials are written to the config file as they are, not as environment-variable references.",
|
|
1969
|
+
mongo_setup_mode_paste: "Paste a connection string (copied from Atlas / Compass)",
|
|
1970
|
+
mongo_string_invalid: "Not a valid MongoDB connection string (it is not echoed because it may carry credentials).",
|
|
1971
|
+
mongo_string_bad_host: "The connection string has an invalid host or port (the port must be an integer from 1 to 65535).",
|
|
1972
|
+
mongo_string_bad_credentials: "The connection string's credentials contain an unescaped reserved character such as @, : or /. Percent-encode it (for example @ as %40).",
|
|
1973
|
+
mongo_string_tls_conflict: "The connection string sets tls and ssl to different values.",
|
|
1974
|
+
mongo_string_bad_tls: "The connection string's {key} must be true or false.",
|
|
1975
|
+
mongo_string_empty_option: "The connection string sets {key} but leaves it empty. Give it a value or remove it.",
|
|
1976
|
+
prompt_name: "Database name: ",
|
|
1977
|
+
prompt_permission: "Permission level (query-only, read-write, data-admin, admin): ",
|
|
1978
|
+
env_parse_note: "Note: Unable to parse .env configuration, using interactive prompts",
|
|
1979
|
+
connection_testing: "Testing database connection...",
|
|
1980
|
+
connection_success: "\u2713 Database connection successful",
|
|
1981
|
+
connection_failed: "\u2717 Database connection failed",
|
|
1982
|
+
config_saved: "Configuration saved to .dbcli",
|
|
1983
|
+
config_saved_global: "Global configuration saved to {path}",
|
|
1984
|
+
config_exists_overwrite: "Configuration file .dbcli already exists. Overwrite? (y/n): ",
|
|
1985
|
+
cancelled: "Cancelled. Configuration not changed.",
|
|
1986
|
+
skip_test_env_ref: "Skipping connection test in env-ref mode",
|
|
1987
|
+
skip_test: "Skipping connection test (--skip-test)",
|
|
1988
|
+
connection_hints: "Hints:",
|
|
1989
|
+
config_exists_use_force: ".dbcli exists. Use --force option to overwrite.",
|
|
1990
|
+
connection_added: "Connection '{name}' added",
|
|
1991
|
+
connection_removed: "Connection '{name}' removed",
|
|
1992
|
+
connection_removed_default_cleared: "Connection '{name}' removed. Default connection cleared. Specify --use <name> or DBCLI_CONNECTION for database commands.",
|
|
1993
|
+
connection_renamed: "Connection '{oldName}' renamed to '{newName}'",
|
|
1994
|
+
v1_migration_prompt: "Detected old config format. Create new format and import existing connection as 'default'?",
|
|
1995
|
+
config_saved_v2: "V2 configuration saved to .dbcli",
|
|
1996
|
+
config_not_found: "Configuration file not found",
|
|
1997
|
+
requires_v2_remove: "Removing a connection requires V2 config format",
|
|
1998
|
+
requires_v2_rename: "Renaming a connection requires V2 config format",
|
|
1999
|
+
connection_not_found: "Connection '{name}' does not exist",
|
|
2000
|
+
cannot_remove_last: "Cannot remove the last connection",
|
|
2001
|
+
connection_already_exists: "Connection '{name}' already exists",
|
|
2002
|
+
rename_invalid_format: "Usage: --rename <old-name>:<new-name>",
|
|
2003
|
+
prompt_sqlite_file: "SQLite database file path: ",
|
|
2004
|
+
sql_tls_help: "Verified SQL TLS mode (verify-full; PostgreSQL/MySQL/MariaDB)",
|
|
2005
|
+
sql_tls_ca_help: "PEM CA bundle for --sql-tls verify-full"
|
|
2006
|
+
},
|
|
2007
|
+
inspect: {
|
|
2008
|
+
description: "Read-only snapshot of connection, permission, blacklist, objects, snippets, and suggested next commands.",
|
|
2009
|
+
no_config: "No dbcli configuration found. Run `dbcli init` to start."
|
|
2010
|
+
},
|
|
2011
|
+
report: {
|
|
2012
|
+
description: "Diagnostic report (health/capacity/perf) built from built-in snippets. Read-only, bounded.",
|
|
2013
|
+
no_config: "No dbcli configuration found. Run `dbcli init` to start.",
|
|
2014
|
+
section_invalid: "Unknown report section '{name}'. Allowed: health, capacity, perf"
|
|
2015
|
+
},
|
|
2016
|
+
guide: {
|
|
2017
|
+
description: "Deterministic next-command planner for a fixed set of database goals.",
|
|
2018
|
+
no_config: "No dbcli configuration found. Run `dbcli init` to start.",
|
|
2019
|
+
missing_goal: "Missing goal. Try `dbcli guide --list` to see available goals.",
|
|
2020
|
+
goal_invalid: "Unknown guide goal '{name}'. Run `dbcli guide --list` for valid IDs."
|
|
2021
|
+
},
|
|
2022
|
+
recovery: {
|
|
2023
|
+
description: "Look up the structured recovery envelope for a known dbcli error code, or list all codes.",
|
|
2024
|
+
missing_code: "Missing --code. Try `dbcli recovery --list` to see available codes.",
|
|
2025
|
+
code_invalid: "Unknown recovery code '{name}'. Run `dbcli recovery --list` for valid codes.",
|
|
2026
|
+
no_config: "No dbcli configuration found. Run `dbcli init` to start."
|
|
2027
|
+
},
|
|
2028
|
+
use: {
|
|
2029
|
+
description: "Switch or display the default database connection",
|
|
2030
|
+
switched: "Switched default connection to {name}",
|
|
2031
|
+
current: "Current default connection: {name}",
|
|
2032
|
+
current_none: "No default connection. Specify --use <name> or DBCLI_CONNECTION.",
|
|
2033
|
+
unset: "Default connection cleared. Specify --use <name> or DBCLI_CONNECTION for database commands.",
|
|
2034
|
+
unset_conflict: "--unset cannot be combined with a connection name, --list, --confirm-production, or --format json",
|
|
2035
|
+
requires_v2: "This feature requires v2 config format. Use 'dbcli init --conn-name <name>' to create one",
|
|
2036
|
+
available: "Available connections"
|
|
2037
|
+
},
|
|
2038
|
+
schema: {
|
|
2039
|
+
description: "Retrieve table structure or list all tables",
|
|
2040
|
+
fetching: "Fetching schema...",
|
|
2041
|
+
success: "Schema updated",
|
|
2042
|
+
not_found: "Table not found in database",
|
|
2043
|
+
refresh_prompt: "Schema has changes. Apply? (y/n): ",
|
|
2044
|
+
columns_header: "Columns",
|
|
2045
|
+
scanning_database: "\uD83D\uDD0D Scanning database schema...",
|
|
2046
|
+
tables_found: "\uD83D\uDCCD Found {count} table(s). Fetching schema details...",
|
|
2047
|
+
processing_tables: " Processed {processed}/{total} table(s)",
|
|
2048
|
+
schema_exists_warning: "\u26A0\uFE0F Database schema already exists in .dbcli",
|
|
2049
|
+
use_force_to_override: " Use --force to override without confirmation"
|
|
2050
|
+
},
|
|
2051
|
+
list: {
|
|
2052
|
+
description: "List all tables in the database",
|
|
2053
|
+
no_tables: "No tables found in database"
|
|
2054
|
+
},
|
|
2055
|
+
query: {
|
|
2056
|
+
description: "Execute SQL query",
|
|
2057
|
+
executing: "Executing query...",
|
|
2058
|
+
no_results: "No results returned",
|
|
2059
|
+
result_count: "Results: {count} row(s)"
|
|
2060
|
+
},
|
|
2061
|
+
q: {
|
|
2062
|
+
description: "Run a saved query snippet by @name",
|
|
2063
|
+
missing_param: "Missing required parameters for snippet '{name}'",
|
|
2064
|
+
engine_mismatch: "Engine mismatch for snippet '{name}'",
|
|
2065
|
+
snippet_not_found: "Snippet not found: {name}",
|
|
2066
|
+
dry_run_header: "Dry-run preview (no execution):"
|
|
2067
|
+
},
|
|
2068
|
+
audit: {
|
|
2069
|
+
description: "Inspect, look up, clear, or check the health of the audit log",
|
|
2070
|
+
tail: {
|
|
2071
|
+
description: "Show recent audit entries (default: last 10 from current connection)"
|
|
2072
|
+
},
|
|
2073
|
+
show: {
|
|
2074
|
+
description: "Look up a single audit entry by id (UUID prefix >=4) or --recovery-ref"
|
|
2075
|
+
},
|
|
2076
|
+
clear: {
|
|
2077
|
+
description: "Delete audit log files (.jsonl + .jsonl.1) for the current connection",
|
|
2078
|
+
prompt_header: "About to clear audit log for connection '{conn}':",
|
|
2079
|
+
prompt_file_line: " {file} \u2014 {entries} entries, {size}",
|
|
2080
|
+
prompt_continue: "Continue? [y/N] ",
|
|
2081
|
+
requires_tty_or_yes: "Cannot prompt for confirmation in non-interactive session. Use --yes to clear without prompt.",
|
|
2082
|
+
summary_cleared: "Cleared {count} entries from '{conn}'.",
|
|
2083
|
+
summary_nothing: "Nothing to clear.",
|
|
2084
|
+
summary_failed: "Failed to clear: {message}."
|
|
2085
|
+
},
|
|
2086
|
+
health: {
|
|
2087
|
+
description: "Show AuditLogger health snapshot (size, entries, lock state, last write)"
|
|
2088
|
+
},
|
|
2089
|
+
write_gate: {
|
|
2090
|
+
description: "Summarize tier-two write-gate decisions (ADR 0010's measurement)",
|
|
2091
|
+
header: "Write-gate summary \u2014 connection: {conn}",
|
|
2092
|
+
window: "Audit entries scanned: {scanned}, {from} \u2192 {to}",
|
|
2093
|
+
decisions: "Tier-two evaluations: {count}, {from} \u2192 {to}",
|
|
2094
|
+
tier_one: "Tier one, counted apart: {count} ({outcomes})",
|
|
2095
|
+
no_decisions: "Tier two was never reached in this window. Per ADR 0010 that falsifies the criterion, not the gate: the shapes it looks for are not the shapes people write.",
|
|
2096
|
+
no_entries: "The audit log is empty, so this says nothing about the gate either way. Run some writes, or check `dbcli audit health`."
|
|
2097
|
+
},
|
|
2098
|
+
disabled_hint: "Audit is disabled (audit.enabled = false in .dbcli). Use 'dbcli audit health' for details.",
|
|
2099
|
+
no_entries: "No audit entries.",
|
|
2100
|
+
show_no_match: "No audit entry matches '{prefix}'.",
|
|
2101
|
+
show_ambiguous: "Ambiguous prefix '{prefix}': matches {count} entries. Please use a longer prefix.",
|
|
2102
|
+
show_prefix_too_short: "Prefix must be at least 4 characters.",
|
|
2103
|
+
show_recovery_no_match: "No audit entry has recovery_ref '{ref}'.",
|
|
2104
|
+
show_recovery_ambiguous: "Multiple entries reference recovery_ref '{ref}': matches {count}. This should be rare; inspect file directly.",
|
|
2105
|
+
show_mutex_violation: "Provide either <id> argument or --recovery-ref, not both.",
|
|
2106
|
+
n_capped_warning: "--n value {requested} exceeds max {max}; capped to {max}.",
|
|
2107
|
+
n_must_be_positive: "--n must be a positive integer."
|
|
2108
|
+
},
|
|
2109
|
+
queries: {
|
|
2110
|
+
description: "Manage saved query snippets",
|
|
2111
|
+
list_description: "List saved snippets",
|
|
2112
|
+
show_description: "Show a snippet (frontmatter + SQL)",
|
|
2113
|
+
new_description: "Create a new snippet file",
|
|
2114
|
+
edit_description: "Open a snippet in $EDITOR",
|
|
2115
|
+
check_description: "Validate all snippets",
|
|
2116
|
+
delete_description: "Delete a local snippet file",
|
|
2117
|
+
rename_description: "Rename a local snippet",
|
|
2118
|
+
copy_description: "Copy a snippet from any tier into local",
|
|
2119
|
+
import_description: "Import an external .sql file into local",
|
|
2120
|
+
export_description: "Export a snippet's raw SQL to stdout or file",
|
|
2121
|
+
created: "Created {file}",
|
|
2122
|
+
first_run_hint: "Tip: commit .dbcli-shared/ to share snippets with your team.",
|
|
2123
|
+
no_snippets: "No snippets found. Create one with: dbcli queries new @<name>",
|
|
2124
|
+
check_ok: "\u2713 {count} snippets parsed successfully",
|
|
2125
|
+
check_failed: "\u2717 {count} snippet(s) have errors",
|
|
2126
|
+
search_description: "Search snippets by keywords (fuzzy ranking)",
|
|
2127
|
+
suggest_description: "Suggest snippets by intent prefix",
|
|
2128
|
+
search_no_keywords: "Provide at least one keyword, or try 'queries list' / 'queries suggest'.",
|
|
2129
|
+
search_no_results: "No snippets matched. Try 'queries list' or 'queries suggest <intent>'.",
|
|
2130
|
+
suggest_missing_intent: "Provide an intent (or prefix). Available: {available}",
|
|
2131
|
+
suggest_invalid_intent: "Invalid intent '{value}'. Must match ^[a-z][a-z0-9.-]*$",
|
|
2132
|
+
suggest_no_results: "No snippets with intent prefix '{intent}'. Available: {available}",
|
|
2133
|
+
no_active_connection_hint: "No active connection; showing snippets across all engines."
|
|
2134
|
+
},
|
|
2135
|
+
errors: {
|
|
2136
|
+
message: "Error: {message}",
|
|
2137
|
+
query_only_boundary_failed: "Could not establish the query-only database boundary for {system}: {detail}",
|
|
2138
|
+
query_only_boundary_verify: "Verify that {system} supports read-only transactions and that this account may start one",
|
|
2139
|
+
query_only_boundary_not_executed: "The target statement was not executed; fix the database setting or connection before retrying",
|
|
2140
|
+
query_only_cleanup_completed: "The query-only target completed, but transaction cleanup failed for {system}: {detail}",
|
|
2141
|
+
query_only_cleanup_uncertain: "The query-only target failed and its outcome is uncertain because transaction cleanup also failed for {system}: {detail}",
|
|
2142
|
+
query_only_cleanup_completed_retry: "The target statement ran; do not retry it unless repeating its external effects is safe",
|
|
2143
|
+
query_only_cleanup_uncertain_retry: "The target may have produced effects before failing; do not retry it unless repeating them is safe",
|
|
2144
|
+
query_only_cleanup_reconnect: "Reconnect before running another statement because this connection was discarded",
|
|
2145
|
+
invalid_config: "Invalid configuration: {field}",
|
|
2146
|
+
connection_failed: "Failed to connect to database: {message}",
|
|
2147
|
+
permission_denied: "Permission denied (required: {required})",
|
|
2148
|
+
permission_requires_level: "{type} operation requires {minimum} permission or higher (current level: {permission})",
|
|
2149
|
+
elasticsearch_requires_level: "Elasticsearch {type} operation requires {minimum} permission or higher (current level: {permission})",
|
|
2150
|
+
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}).",
|
|
2151
|
+
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.",
|
|
2152
|
+
unknown_statement_query_only: "Unrecognised SQL statement (current level: query-only). Security policy requires admin for unknown statements. If this is a legitimate read-only statement, please open an issue at https://github.com/CarlLee1983/dbcli/issues.",
|
|
2153
|
+
permission_denied_reason: "Permission denied: {reason}",
|
|
2154
|
+
table_not_found: "Table not found: {table}",
|
|
2155
|
+
unsupported_lang: "Language '{lang}' not supported, using English",
|
|
2156
|
+
invalid_json: "Invalid JSON: {message}",
|
|
2157
|
+
table_blacklisted: "Error: Table '{table}' is blacklisted for {operation} operations",
|
|
2158
|
+
redis_target_blacklisted: "Redis command refused by blacklist",
|
|
2159
|
+
column_blacklisted_write: "Error: {operation} on table '{table}' touches blacklisted columns: {columns}",
|
|
2160
|
+
table_already_blacklisted: "Error: Table '{table}' is already blacklisted",
|
|
2161
|
+
table_not_in_blacklist: "Error: Table '{table}' is not in the blacklist",
|
|
2162
|
+
column_already_blacklisted: "Error: Column '{table}.{column}' is already blacklisted",
|
|
2163
|
+
column_not_in_blacklist: "Error: Column '{table}.{column}' is not in the blacklist",
|
|
2164
|
+
invalid_table_name: "Error: Invalid table name: {table}",
|
|
2165
|
+
invalid_column_format: "Error: Invalid column format. Use 'table.column'",
|
|
2166
|
+
invalid_system: "Invalid database system: {system}",
|
|
2167
|
+
invalid_permission: "Invalid permission level: {permission}",
|
|
2168
|
+
invalid_port: "Invalid port: {port}",
|
|
2169
|
+
require_user: "Non-interactive mode requires --user option",
|
|
2170
|
+
require_name: "Non-interactive mode requires --name option",
|
|
2171
|
+
env_refs_missing_options: `When using --use-env-refs, environment variable names must be specified.
|
|
2172
|
+
Provide options: --env-host, --env-port, --env-user, --env-password, --env-database
|
|
2173
|
+
Or use interactive mode: run "dbcli init --use-env-refs" without --no-interactive`,
|
|
2174
|
+
env_var_not_defined: `Cannot test connection: environment variable {envKey} is not defined.
|
|
2175
|
+
Set {envKey} in .env or environment variables.
|
|
2176
|
+
Hint: run 'export {envKey}=<value>' and retry`,
|
|
2177
|
+
invalid_output_format: "--format {format} is not supported here \u2014 use text or json",
|
|
2178
|
+
sqlite_file_required: "A SQLite connection needs a file path. Pass --file <path> or run init interactively.",
|
|
2179
|
+
sql_tls_unsupported: "Explicit SQL TLS is supported only for PostgreSQL, MySQL and MariaDB",
|
|
2180
|
+
sql_tls_options_required: "Provide both --sql-tls verify-full and --sql-tls-ca <path>",
|
|
2181
|
+
sql_tls_change_engine: "Explicit SQL TLS is supported only for PostgreSQL, MySQL and MariaDB; cannot discard it when editing a profile",
|
|
2182
|
+
sql_tls_legacy_import: "SQL TLS legacy single-file import is unsupported. Keep the original profile; create a verified named profile in a separate config directory before switching. Do not remove SQL TLS.",
|
|
2183
|
+
sql_tls_ca_absolute: "SQL TLS CA path must be absolute",
|
|
2184
|
+
sql_tls_network_host: "Explicit SQL TLS requires a DNS or IP host, not a Unix socket",
|
|
2185
|
+
sql_tls_ca_empty: "CA bundle contains no PEM certificates",
|
|
2186
|
+
sql_tls_ca_load: "Cannot load SQL TLS CA bundle: {detail}",
|
|
2187
|
+
sql_tls_ca_restore: "Restore a readable, valid PEM CA bundle at sqlTls.caPath; keep verify-full enabled.",
|
|
2188
|
+
sql_tls_handshake: "TLS handshake failed: {detail}",
|
|
2189
|
+
sql_tls_ca_hint: "Set sqlTls.caPath to the correct readable PEM CA bundle.",
|
|
2190
|
+
sql_tls_identity_hint: "Verify the certificate validity and DNS/IP identity for {host}.",
|
|
2191
|
+
sql_tls_no_downgrade: "Keep verify-full enabled; do not remove SQL TLS or retry without verification.",
|
|
2192
|
+
sql_tls_driver_identity: "SQL TLS identity verification is unavailable in this driver",
|
|
2193
|
+
sql_tls_peer_unverified: "SQL TLS peer is not authenticated",
|
|
2194
|
+
sql_tls_peer_missing: "SQL TLS peer certificate is unavailable",
|
|
2195
|
+
sql_tls_recovery_config: "TLS recovery requires an explicit --config <path> or --global and the intended TLS profile; defaults cannot identify the failed target.",
|
|
2196
|
+
sql_tls_recovery_profile: "TLS recovery requires --use <name> (or DBCLI_CONNECTION); it cannot fall back to the default profile.",
|
|
2197
|
+
sql_tls_recovery_required: "TLS recovery requires a profile with explicit sqlTls verify-full; plaintext recovery is refused.",
|
|
2198
|
+
sql_tls_recovery_target: "TLS recovery requires an explicitly selected, verified TLS profile."
|
|
2199
|
+
},
|
|
2200
|
+
success: {
|
|
2201
|
+
inserted: "Successfully inserted {count} row(s)",
|
|
2202
|
+
updated: "Successfully updated {count} row(s)",
|
|
2203
|
+
deleted: "Successfully deleted {count} row(s)",
|
|
2204
|
+
no_changes: "No changes to apply"
|
|
2205
|
+
},
|
|
2206
|
+
insert: {
|
|
2207
|
+
description: "Insert data into a table",
|
|
2208
|
+
prompt_data: "Enter JSON data to insert: ",
|
|
2209
|
+
confirm: "Insert {count} row(s) into {table}? (y/n): ",
|
|
2210
|
+
elasticsearch_unsupported: "Elasticsearch does not support the insert command; for now, use an external tool (e.g. curl or Kibana DevTools) to write documents"
|
|
2211
|
+
},
|
|
2212
|
+
update: {
|
|
2213
|
+
description: "Update data in a table",
|
|
2214
|
+
prompt_where: "Enter WHERE clause (e.g., id=1): ",
|
|
2215
|
+
prompt_set: 'Enter JSON updates (e.g., {"status": "active"}): ',
|
|
2216
|
+
confirm: "Update rows in {table}? (y/n): ",
|
|
2217
|
+
elasticsearch_unsupported: "Elasticsearch does not support the update command; for now, use an external tool (e.g. curl or Kibana DevTools) to update documents"
|
|
2218
|
+
},
|
|
2219
|
+
delete: {
|
|
2220
|
+
description: "Delete data from a table",
|
|
2221
|
+
prompt_where: "Enter WHERE clause (e.g., id=1): ",
|
|
2222
|
+
confirm: "Delete rows from {table}? (y/n): ",
|
|
2223
|
+
admin_only: "Delete command requires data-admin or admin permission",
|
|
2224
|
+
elasticsearch_unsupported: "Elasticsearch does not support the delete command; for now, use an external tool (e.g. curl or Kibana DevTools) to delete documents"
|
|
2225
|
+
},
|
|
2226
|
+
export: {
|
|
2227
|
+
description: "Export query results",
|
|
2228
|
+
formats: "Supported formats: json, csv",
|
|
2229
|
+
exported: "Exported {count} rows to {file}"
|
|
2230
|
+
},
|
|
2231
|
+
upgrade: {
|
|
2232
|
+
description: "Check for updates and upgrade dbcli to the latest version",
|
|
2233
|
+
checking: "Checking for updates...",
|
|
2234
|
+
already_up_to_date: "\u2713 Already up to date (v{version})",
|
|
2235
|
+
new_version_available: "New version available: v{version}",
|
|
2236
|
+
current_version: "Current version : v{version}",
|
|
2237
|
+
latest_version: "Latest version : v{version}",
|
|
2238
|
+
update_hint: '[INFO] dbcli v{version} available. Run "dbcli upgrade" to upgrade.',
|
|
2239
|
+
install_hint: 'Run "dbcli upgrade" to install the update.',
|
|
2240
|
+
upgrading: "Upgrading...",
|
|
2241
|
+
success: "\u2713 Successfully upgraded to v{version}",
|
|
2242
|
+
skill_recheck_hint: 'Hint: If you have installed AI skills, run "dbcli skill --install <platform>" to ensure they are up to date.',
|
|
2243
|
+
failed: "\u2717 Upgrade failed. Try running manually:",
|
|
2244
|
+
manual_hint: "bun add -g @carllee1983/dbcli@latest",
|
|
2245
|
+
network_error: "\u26A0 Could not check for updates (network error or registry unavailable)"
|
|
2246
|
+
},
|
|
2247
|
+
skill: {
|
|
2248
|
+
description: "Generate AI skill documentation",
|
|
2249
|
+
context_description: "Generate AI-friendly database schema and query snippet context",
|
|
2250
|
+
installed: `Skill: {path}
|
|
2251
|
+
Reference: {referencePath}`,
|
|
2252
|
+
update_available: "Skill updates available for the following platforms:",
|
|
2253
|
+
update_hint: 'Run "dbcli skill --install <platform>" to update.'
|
|
2254
|
+
},
|
|
2255
|
+
blacklist: {
|
|
2256
|
+
description: "Manage sensitive data blacklist to prevent AI access",
|
|
2257
|
+
list_title: "Current Blacklist Configuration",
|
|
2258
|
+
tables_label: "Blacklisted tables",
|
|
2259
|
+
columns_label: "Blacklisted columns",
|
|
2260
|
+
none: "No tables or columns are currently blacklisted",
|
|
2261
|
+
table_added: "\u2713 Table '{table}' added to blacklist",
|
|
2262
|
+
table_removed: "\u2713 Table '{table}' removed from blacklist",
|
|
2263
|
+
column_added: "\u2713 Column '{table}.{column}' added to blacklist",
|
|
2264
|
+
column_removed: "\u2713 Column '{table}.{column}' removed from blacklist",
|
|
2265
|
+
refuse_wildcard: "Refused: '{table}' contains wildcard characters, which are matched literally on {system} \u2014 the entry would never match anything. Write the exact name."
|
|
2266
|
+
},
|
|
2267
|
+
security: {
|
|
2268
|
+
columns_omitted: "Security: {count} column(s) were omitted based on your blacklist"
|
|
2269
|
+
},
|
|
2270
|
+
warnings: {
|
|
2271
|
+
blacklist_override_used: "Warning: Blacklist override enabled (DBCLI_OVERRIDE_BLACKLIST=true). Executing {operation} on blacklisted table '{table}'",
|
|
2272
|
+
redis_blacklist_override_active: "Warning: Redis blacklist enforcement disabled by DBCLI_OVERRIDE_BLACKLIST=true"
|
|
2273
|
+
},
|
|
2274
|
+
version: {
|
|
2275
|
+
unsupported_warning: "{system} {version} is below minimum supported version {minVersion}. Some features may not work correctly.",
|
|
2276
|
+
doctor_pass: "{system} {version} (meets >= {minVersion})",
|
|
2277
|
+
doctor_warn: "{system} {version} is below supported >= {minVersion}"
|
|
2278
|
+
},
|
|
2279
|
+
migrate: {
|
|
2280
|
+
description: "Schema DDL operations (CREATE/DROP/ALTER TABLE, INDEX, CONSTRAINT, ENUM)",
|
|
2281
|
+
dry_run_preview: "[DRY RUN] Generated SQL (not executed):",
|
|
2282
|
+
executing: "Executing DDL...",
|
|
2283
|
+
success: "DDL executed successfully",
|
|
2284
|
+
confirm_drop: "This is a destructive operation ({operation}). Proceed?",
|
|
2285
|
+
permission_required: "DDL operations require admin permission (current: {permission})",
|
|
2286
|
+
blacklist_blocked: 'Table "{table}" is blacklisted \u2014 DDL operations are blocked',
|
|
2287
|
+
schema_refreshed: 'Schema cache updated for table "{table}"',
|
|
2288
|
+
no_sql_generated: "No SQL generated (operation not applicable for this database)",
|
|
2289
|
+
create_description: "Create a new table",
|
|
2290
|
+
drop_description: "Drop a table",
|
|
2291
|
+
add_column_description: "Add a column to a table",
|
|
2292
|
+
drop_column_description: "Drop a column from a table",
|
|
2293
|
+
alter_column_description: "Alter a column in a table",
|
|
2294
|
+
add_index_description: "Add an index to a table",
|
|
2295
|
+
drop_index_description: "Drop an index",
|
|
2296
|
+
add_constraint_description: "Add a constraint to a table",
|
|
2297
|
+
drop_constraint_description: "Drop a constraint from a table",
|
|
2298
|
+
add_enum_description: "Create an enum type (PostgreSQL only)",
|
|
2299
|
+
alter_enum_description: "Add a value to an enum type",
|
|
2300
|
+
drop_enum_description: "Drop an enum type"
|
|
2301
|
+
},
|
|
2302
|
+
password: {
|
|
2303
|
+
description: "Change the password of a single connection, leaving every other setting untouched",
|
|
2304
|
+
arg_connection: "Connection name (defaults to the current default connection)",
|
|
2305
|
+
opt_stdin: "Read the new password from stdin (for rotation scripts)",
|
|
2306
|
+
opt_password: "New password given inline (visible in shell history and process list)",
|
|
2307
|
+
opt_skip_test: "Skip verifying the new password against the database before saving",
|
|
2308
|
+
prompt: "New password for connection '{name}'",
|
|
2309
|
+
source_conflict: "--password and --stdin cannot be used together; pick one source.",
|
|
2310
|
+
needs_source: "No terminal available for a masked prompt. Pass the value with --stdin or --password.",
|
|
2311
|
+
empty: "The new password is empty; nothing was changed.",
|
|
2312
|
+
updated: "Password for connection '{name}' updated \u2192 {envFile} ({varName})",
|
|
2313
|
+
converted: 'The literal password in config.json was replaced by a {{ "$env": "{varName}" }} reference; later rotations only touch the env file.',
|
|
2314
|
+
skipped_test: "Verification skipped (--skip-test): the new password was not checked against the database."
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
});
|
|
2318
|
+
|
|
2319
|
+
// resources/lang/zh-TW/messages.json
|
|
2320
|
+
var messages_default2;
|
|
2321
|
+
var init_messages2 = __esm(() => {
|
|
2322
|
+
messages_default2 = {
|
|
2323
|
+
cli: {
|
|
2324
|
+
agent_output_option: "\u8F38\u51FA\u4E00\u4EFD Operation Envelope v1\uFF08\u50C5\u652F\u63F4 capabilities \u8207 capabilities check\uFF09"
|
|
2325
|
+
},
|
|
2326
|
+
init: {
|
|
2327
|
+
description: "\u521D\u59CB\u5316 dbcli \u914D\u7F6E\uFF0C\u89E3\u6790 .env \u4E26\u9032\u884C\u4E92\u52D5\u5F0F\u63D0\u793A",
|
|
2328
|
+
welcome: "\u6B61\u8FCE\u4F7F\u7528 dbcli",
|
|
2329
|
+
prompt_connection_name: "\u9023\u7DDA\u540D\u7A31",
|
|
2330
|
+
connection_name_required: "\u9023\u7DDA\u540D\u7A31\u4E0D\u53EF\u7A7A\u767D\uFF0C\u8ACB\u63D0\u4F9B --conn-name <name>\uFF08\u8207\u8CC7\u6599\u5EAB\u540D\u7A31 --name \u4E0D\u540C\uFF09\u3002",
|
|
2331
|
+
select_system: "\u9078\u64C7\u8CC7\u6599\u5EAB\u7CFB\u7D71\uFF1A",
|
|
2332
|
+
prompt_host: "\u8CC7\u6599\u5EAB\u4E3B\u6A5F\uFF1A",
|
|
2333
|
+
prompt_port: "\u8CC7\u6599\u5EAB\u57E0\u865F\uFF1A",
|
|
2334
|
+
prompt_user: "\u8CC7\u6599\u5EAB\u4F7F\u7528\u8005\uFF1A",
|
|
2335
|
+
prompt_password: "\u8CC7\u6599\u5EAB\u5BC6\u78BC\uFF1A",
|
|
2336
|
+
secret_alternatives_sql: "\u6539\u7528 --password\u3001\u628A\u6191\u8B49\u653E\u9032 .env \u6216\u884C\u7A0B\u74B0\u5883\u8B8A\u6578\uFF0C\u6216\u4EE5 --use-env-refs \u5B58\u6210\u53C3\u7167",
|
|
2337
|
+
secret_alternatives_mongo_password: "\u6539\u7528 --password\u3001\u628A\u6191\u8B49\u653E\u9032 .env \u6216\u884C\u7A0B\u74B0\u5883\u8B8A\u6578\uFF0C\u6216\u4EE5 --uri \u8CBC\u4E0A\u9023\u7DDA\u5B57\u4E32",
|
|
2338
|
+
secret_alternatives_mongo_uri: "\u4EE5 --uri \u50B3\u5165\u9023\u7DDA\u5B57\u4E32",
|
|
2339
|
+
mongo_uri_conflicts_fields: "--uri \u4E0D\u80FD\u8207 {flags} \u4E26\u7528\uFF1A\u9023\u7DDA\u5B57\u4E32\u5DF2\u5305\u542B\u9019\u4E9B\u8A2D\u5B9A\u3002\u8ACB\u64C7\u4E00\u4F7F\u7528\uFF08--name \u53EF\u4E26\u7528\uFF0C\u7528\u4F86\u8986\u84CB\u8CC7\u6599\u5EAB\u540D\u7A31\uFF09\u3002",
|
|
2340
|
+
mongo_uri_with_env_refs: "--uri \u4E0D\u80FD\u8207 --use-env-refs \u4E26\u7528\uFF1A\u9023\u7DDA\u5B57\u4E32\u6703\u88AB\u62C6\u89E3\u6210\u6B04\u4F4D\u8A2D\u5B9A\u5BEB\u5165\uFF0C\u5E33\u5BC6\u4E0D\u6703\u88AB\u62BD\u6210\u74B0\u5883\u8B8A\u6578\u53C3\u7167\u3002\u8ACB\u6539\u8D70\u4E0B\u5217\u4EFB\u4E00\u505A\u6CD5\uFF1A\u62FF\u6389 --use-env-refs\uFF0C\u8B93 dbcli \u62C6\u89E3\u5B57\u4E32\u5BEB\u5165\u6B04\u4F4D\u8A2D\u5B9A\uFF1B\u6216\u6539\u7528 --env-* flag\uFF08--env-host\u3001--env-user\u3001--env-password \u7B49\uFF09\u9010\u6B04\u7D66\u8B8A\u6578\u540D\u7A31\u3002",
|
|
2341
|
+
mongo_uri_fallback_warning: "\u26A0\uFE0F \u6B64\u9023\u7DDA\u5B57\u4E32{reasons}\uFF0C\u7121\u6CD5\u62C6\u6210\u6B04\u4F4D\u8A2D\u5B9A\uFF0C\u5DF2\u539F\u6A23\u5B58\u5165 uri\uFF1B\u5E33\u5BC6\u5C07\u660E\u6587\u5B58\u653E\u65BC\u8A2D\u5B9A\u6A94\u3002",
|
|
2342
|
+
mongo_uri_reason_unix_socket: "\u4F7F\u7528 Unix domain socket\uFF08\u672C\u6A5F\u901A\u8A0A\u7AEF\uFF09",
|
|
2343
|
+
mongo_uri_reason_multi_host: "\u542B\u591A\u7D44 host\uFF08\u6B04\u4F4D\u8A2D\u5B9A\u53EA\u80FD\u8868\u9054\u4E00\u7D44\uFF09",
|
|
2344
|
+
mongo_uri_reason_secret_option: "\u542B\u6A5F\u5BC6 option\uFF08{keys}\uFF09\uFF0C\u62C6\u9032 options \u6703\u660E\u6587\u843D\u76E4",
|
|
2345
|
+
mongo_uri_reason_duplicate_key: "\u542B\u91CD\u8907\u7684 query \u9375\uFF08{keys}\uFF09\uFF08\u6B04\u4F4D\u8A2D\u5B9A\u6BCF\u500B\u9375\u53EA\u80FD\u5B58\u4E00\u500B\u503C\uFF09",
|
|
2346
|
+
mongo_uri_reason_non_scram: "\u4F7F\u7528\u975E SCRAM \u7684\u8A8D\u8B49\u6A5F\u5236 {mechanism}\uFF08\u6B04\u4F4D\u8A2D\u5B9A\u7121\u6CD5\u8868\u9054\uFF09",
|
|
2347
|
+
mongo_decomposed_header: "\u9023\u7DDA\u5B57\u4E32\u5DF2\u62C6\u89E3\u6210\u4E0B\u5217\u6B04\u4F4D\uFF08\u5BC6\u78BC\u5DF2\u906E\u853D\uFF09\uFF1A",
|
|
2348
|
+
mongo_paste_env_refs_warning: "\u26A0\uFE0F --use-env-refs \u4E0D\u9069\u7528\u65BC\u8CBC\u4E0A\u7684\u9023\u7DDA\u5B57\u4E32\uFF1A\u5E33\u5BC6\u6703\u4EE5\u5B57\u9762\u503C\u5BEB\u5165\u8A2D\u5B9A\u6A94\uFF0C\u4E0D\u6703\u8B8A\u6210\u74B0\u5883\u8B8A\u6578\u53C3\u7167\u3002",
|
|
2349
|
+
mongo_setup_mode_paste: "\u8CBC\u4E0A\u9023\u7DDA\u5B57\u4E32\uFF08Atlas / Compass \u8907\u88FD\u7684\uFF09",
|
|
2350
|
+
mongo_string_invalid: "\u4E0D\u662F\u5408\u6CD5\u7684 MongoDB \u9023\u7DDA\u5B57\u4E32\uFF08\u5B57\u4E32\u53EF\u80FD\u5E36\u6709\u5E33\u5BC6\uFF0C\u6240\u4EE5\u4E0D\u5370\u51FA\uFF09\u3002",
|
|
2351
|
+
mongo_string_bad_host: "\u9023\u7DDA\u5B57\u4E32\u7684 host \u6216\u57E0\u865F\u4E0D\u5408\u6CD5\uFF08\u57E0\u865F\u9808\u70BA 1 \u81F3 65535 \u7684\u6574\u6578\uFF09\u3002",
|
|
2352
|
+
mongo_string_bad_credentials: "\u9023\u7DDA\u5B57\u4E32\u7684\u5E33\u865F\u6216\u5BC6\u78BC\u542B\u6709\u672A\u8DF3\u812B\u7684\u4FDD\u7559\u5B57\u5143\uFF08\u5982 @\u3001:\u3001/\uFF09\u3002\u8ACB\u505A\u767E\u5206\u6BD4\u7DE8\u78BC\uFF08\u4F8B\u5982 @ \u5BEB\u6210 %40\uFF09\u3002",
|
|
2353
|
+
mongo_string_tls_conflict: "\u9023\u7DDA\u5B57\u4E32\u540C\u6642\u6307\u5B9A\u4E86 tls \u8207 ssl \u4E14\u503C\u4E0D\u540C\u3002",
|
|
2354
|
+
mongo_string_bad_tls: "\u9023\u7DDA\u5B57\u4E32\u7684 {key} \u53EA\u63A5\u53D7 true \u6216 false\u3002",
|
|
2355
|
+
mongo_string_empty_option: "\u9023\u7DDA\u5B57\u4E32\u6307\u5B9A\u4E86 {key} \u537B\u6C92\u6709\u503C\u3002\u8ACB\u7D66\u503C\u6216\u79FB\u9664\u3002",
|
|
2356
|
+
prompt_name: "\u8CC7\u6599\u5EAB\u540D\u7A31\uFF1A",
|
|
2357
|
+
prompt_permission: "\u6B0A\u9650\u7B49\u7D1A\uFF08query-only, read-write, data-admin, admin\uFF09\uFF1A",
|
|
2358
|
+
env_parse_note: "\u63D0\u793A\uFF1A\u7121\u6CD5\u89E3\u6790 .env \u914D\u7F6E\uFF0C\u4F7F\u7528\u4E92\u52D5\u5F0F\u63D0\u793A",
|
|
2359
|
+
connection_testing: "\u6B63\u5728\u6E2C\u8A66\u8CC7\u6599\u5EAB\u9023\u63A5...",
|
|
2360
|
+
connection_success: "\u2713 \u8CC7\u6599\u5EAB\u9023\u63A5\u6210\u529F",
|
|
2361
|
+
connection_failed: "\u2717 \u8CC7\u6599\u5EAB\u9023\u63A5\u5931\u6557",
|
|
2362
|
+
config_saved: "\u914D\u7F6E\u5DF2\u4FDD\u5B58\u81F3 .dbcli",
|
|
2363
|
+
config_saved_global: "\u5168\u57DF\u8A2D\u5B9A\u5DF2\u5132\u5B58\u81F3 {path}",
|
|
2364
|
+
config_exists_overwrite: "\u914D\u7F6E\u6A94\u6848 .dbcli \u5DF2\u5B58\u5728\u3002\u662F\u5426\u8986\u84CB\uFF1F (y/n)\uFF1A",
|
|
2365
|
+
cancelled: "\u5DF2\u53D6\u6D88\u3002\u914D\u7F6E\u672A\u66F4\u6539\u3002",
|
|
2366
|
+
skip_test_env_ref: "\u8DF3\u904E\u9023\u7DDA\u6E2C\u8A66\uFF08\u74B0\u5883\u8B8A\u6578\u53C3\u7167\u6A21\u5F0F\uFF09",
|
|
2367
|
+
skip_test: "\u8DF3\u904E\u9023\u7DDA\u6E2C\u8A66\uFF08--skip-test\uFF09",
|
|
2368
|
+
connection_hints: "\u63D0\u793A\uFF1A",
|
|
2369
|
+
config_exists_use_force: ".dbcli \u5DF2\u5B58\u5728\u3002\u4F7F\u7528 --force \u9078\u9805\u8986\u84CB\u3002",
|
|
2370
|
+
connection_added: "\u5DF2\u65B0\u589E\u9023\u7DDA '{name}'",
|
|
2371
|
+
connection_removed: "\u5DF2\u79FB\u9664\u9023\u7DDA '{name}'",
|
|
2372
|
+
connection_removed_default_cleared: "\u5DF2\u79FB\u9664\u9023\u7DDA '{name}'\uFF0C\u9810\u8A2D\u9023\u7DDA\u5DF2\u6E05\u7A7A\u3002\u57F7\u884C\u8CC7\u6599\u5EAB\u6307\u4EE4\u6642\u8ACB\u4F7F\u7528 --use <name> \u6216\u8A2D\u5B9A DBCLI_CONNECTION\u3002",
|
|
2373
|
+
connection_renamed: "\u5DF2\u5C07\u9023\u7DDA '{oldName}' \u91CD\u65B0\u547D\u540D\u70BA '{newName}'",
|
|
2374
|
+
v1_migration_prompt: "\u5075\u6E2C\u5230\u820A\u683C\u5F0F\u8A2D\u5B9A\uFF0C\u5C07\u5EFA\u7ACB\u65B0\u683C\u5F0F\u4E26\u5C07\u73FE\u6709\u9023\u7DDA\u532F\u5165\u70BA 'default'\uFF0C\u662F\u5426\u7E7C\u7E8C\uFF1F",
|
|
2375
|
+
config_saved_v2: "V2 \u8A2D\u5B9A\u5DF2\u5132\u5B58\u81F3 .dbcli",
|
|
2376
|
+
config_not_found: "\u627E\u4E0D\u5230\u8A2D\u5B9A\u6A94",
|
|
2377
|
+
requires_v2_remove: "\u79FB\u9664\u9023\u7DDA\u9700\u8981 V2 \u683C\u5F0F\u8A2D\u5B9A",
|
|
2378
|
+
requires_v2_rename: "\u91CD\u65B0\u547D\u540D\u9023\u7DDA\u9700\u8981 V2 \u683C\u5F0F\u8A2D\u5B9A",
|
|
2379
|
+
connection_not_found: "\u9023\u7DDA '{name}' \u4E0D\u5B58\u5728",
|
|
2380
|
+
cannot_remove_last: "\u7121\u6CD5\u79FB\u9664\u6700\u5F8C\u4E00\u500B\u9023\u7DDA",
|
|
2381
|
+
connection_already_exists: "\u9023\u7DDA '{name}' \u5DF2\u5B58\u5728",
|
|
2382
|
+
rename_invalid_format: "\u7528\u6CD5\uFF1A--rename <\u820A\u540D\u7A31>:<\u65B0\u540D\u7A31>",
|
|
2383
|
+
prompt_sqlite_file: "SQLite \u8CC7\u6599\u5EAB\u6A94\u6848\u8DEF\u5F91\uFF1A",
|
|
2384
|
+
sql_tls_help: "\u9A57\u8B49\u5F0F SQL TLS \u6A21\u5F0F\uFF08verify-full\uFF1BPostgreSQL\uFF0FMySQL\uFF0FMariaDB\uFF09",
|
|
2385
|
+
sql_tls_ca_help: "\u4F9B --sql-tls verify-full \u4F7F\u7528\u7684 PEM CA bundle"
|
|
2386
|
+
},
|
|
2387
|
+
inspect: {
|
|
2388
|
+
description: "\u552F\u8B80\u6AA2\u8996\uFF1A\u9023\u7DDA\u3001\u6B0A\u9650\u3001\u9ED1\u540D\u55AE\u3001\u7269\u4EF6\u3001\u7247\u6BB5\u8207\u5EFA\u8B70\u4E0B\u4E00\u6B65\u6307\u4EE4\u3002",
|
|
2389
|
+
no_config: "\u627E\u4E0D\u5230 dbcli \u8A2D\u5B9A\u6A94\u3002\u8ACB\u5148\u57F7\u884C `dbcli init`\u3002"
|
|
2390
|
+
},
|
|
2391
|
+
report: {
|
|
2392
|
+
description: "\u8A3A\u65B7\u5831\u544A\uFF08health/capacity/perf\uFF09\uFF1A\u4EE5\u5167\u5EFA\u7247\u6BB5\u7522\u751F\u552F\u8B80\u3001\u6709\u754C\u7684\u8A3A\u65B7\u5FEB\u7167\u3002",
|
|
2393
|
+
no_config: "\u627E\u4E0D\u5230 dbcli \u8A2D\u5B9A\u6A94\u3002\u8ACB\u5148\u57F7\u884C `dbcli init`\u3002",
|
|
2394
|
+
section_invalid: "\u672A\u77E5\u7684 report section '{name}'\u3002\u5141\u8A31\u503C\uFF1Ahealth\u3001capacity\u3001perf"
|
|
2395
|
+
},
|
|
2396
|
+
guide: {
|
|
2397
|
+
description: "\u91DD\u5C0D\u56FA\u5B9A\u8CC7\u6599\u5EAB\u76EE\u6A19\u6E05\u55AE\uFF0C\u8F38\u51FA\u53EF\u9810\u6E2C\u7684\u4E0B\u4E00\u6B65\u6307\u4EE4\u8A08\u756B\u3002",
|
|
2398
|
+
no_config: "\u627E\u4E0D\u5230 dbcli \u8A2D\u5B9A\u6A94\u3002\u8ACB\u5148\u57F7\u884C `dbcli init`\u3002",
|
|
2399
|
+
missing_goal: "\u7F3A\u5C11 goal \u53C3\u6578\u3002\u8ACB\u57F7\u884C `dbcli guide --list` \u67E5\u770B\u53EF\u7528\u76EE\u6A19\u3002",
|
|
2400
|
+
goal_invalid: "\u672A\u77E5\u7684 guide goal '{name}'\u3002\u8ACB\u57F7\u884C `dbcli guide --list` \u53D6\u5F97\u6709\u6548\u76EE\u6A19 ID\u3002"
|
|
2401
|
+
},
|
|
2402
|
+
recovery: {
|
|
2403
|
+
description: "\u67E5\u8A62\u5DF2\u77E5 dbcli \u932F\u8AA4\u78BC\u5C0D\u61C9\u7684\u7D50\u69CB\u5316\u6062\u5FA9\u65B9\u6848\uFF0C\u6216\u5217\u51FA\u6240\u6709\u652F\u63F4\u7684\u932F\u8AA4\u78BC\u3002",
|
|
2404
|
+
missing_code: "\u7F3A\u5C11 --code \u53C3\u6578\u3002\u8ACB\u57F7\u884C `dbcli recovery --list` \u67E5\u770B\u53EF\u7528\u4EE3\u78BC\u3002",
|
|
2405
|
+
code_invalid: "\u672A\u77E5\u7684 recovery code '{name}'\u3002\u8ACB\u57F7\u884C `dbcli recovery --list` \u53D6\u5F97\u6709\u6548\u4EE3\u78BC\u3002",
|
|
2406
|
+
no_config: "\u627E\u4E0D\u5230 dbcli \u8A2D\u5B9A\u6A94\u3002\u8ACB\u5148\u57F7\u884C `dbcli init`\u3002"
|
|
2407
|
+
},
|
|
2408
|
+
use: {
|
|
2409
|
+
description: "\u5207\u63DB\u6216\u986F\u793A\u9810\u8A2D\u8CC7\u6599\u5EAB\u9023\u7DDA",
|
|
2410
|
+
switched: "\u5DF2\u5207\u63DB\u9810\u8A2D\u9023\u7DDA\u70BA {name}",
|
|
2411
|
+
current: "\u76EE\u524D\u7684\u9810\u8A2D\u9023\u7DDA\uFF1A{name}",
|
|
2412
|
+
current_none: "\u76EE\u524D\u6C92\u6709\u9810\u8A2D\u9023\u7DDA\u3002\u8ACB\u4F7F\u7528 --use <name> \u6216\u8A2D\u5B9A DBCLI_CONNECTION\u3002",
|
|
2413
|
+
unset: "\u9810\u8A2D\u9023\u7DDA\u5DF2\u6E05\u7A7A\u3002\u57F7\u884C\u8CC7\u6599\u5EAB\u6307\u4EE4\u6642\u8ACB\u4F7F\u7528 --use <name> \u6216\u8A2D\u5B9A DBCLI_CONNECTION\u3002",
|
|
2414
|
+
unset_conflict: "--unset \u4E0D\u53EF\u8207\u9023\u7DDA\u540D\u7A31\u3001--list\u3001--confirm-production \u6216 --format json \u540C\u6642\u4F7F\u7528",
|
|
2415
|
+
requires_v2: "\u6B64\u529F\u80FD\u9700\u8981\u65B0\u683C\u5F0F\u8A2D\u5B9A\u3002\u8ACB\u4F7F\u7528 dbcli init --conn-name <\u540D\u7A31> \u5EFA\u7ACB\u591A\u9023\u7DDA\u8A2D\u5B9A",
|
|
2416
|
+
available: "\u53EF\u7528\u9023\u7DDA"
|
|
2417
|
+
},
|
|
2418
|
+
schema: {
|
|
2419
|
+
description: "\u6AA2\u7D22\u8868\u683C\u7D50\u69CB\u6216\u5217\u51FA\u6240\u6709\u8868\u683C",
|
|
2420
|
+
fetching: "\u6B63\u5728\u6293\u53D6\u6A21\u5F0F...",
|
|
2421
|
+
success: "\u6A21\u5F0F\u5DF2\u66F4\u65B0",
|
|
2422
|
+
not_found: "\u8CC7\u6599\u5EAB\u4E2D\u627E\u4E0D\u5230\u8868\u683C",
|
|
2423
|
+
refresh_prompt: "\u6A21\u5F0F\u6709\u8B8A\u66F4\u3002\u662F\u5426\u5957\u7528\uFF1F (y/n)\uFF1A",
|
|
2424
|
+
columns_header: "\u6B04\u4F4D",
|
|
2425
|
+
scanning_database: "\uD83D\uDD0D \u6B63\u5728\u6383\u63CF\u8CC7\u6599\u5EAB\u67B6\u69CB...",
|
|
2426
|
+
tables_found: "\uD83D\uDCCD \u627E\u5230 {count} \u500B\u8868\u683C\u3002\u6B63\u5728\u7372\u53D6\u67B6\u69CB\u8A73\u60C5...",
|
|
2427
|
+
processing_tables: " \u8655\u7406\u4E86 {processed}/{total} \u500B\u8868\u683C",
|
|
2428
|
+
schema_exists_warning: "\u26A0\uFE0F \u8CC7\u6599\u5EAB\u67B6\u69CB\u5DF2\u5B58\u5728\u65BC .dbcli",
|
|
2429
|
+
use_force_to_override: " \u4F7F\u7528 --force \u9032\u884C\u8986\u84CB\u800C\u7121\u9700\u78BA\u8A8D"
|
|
2430
|
+
},
|
|
2431
|
+
list: {
|
|
2432
|
+
description: "\u5217\u51FA\u8CC7\u6599\u5EAB\u4E2D\u7684\u6240\u6709\u8868\u683C",
|
|
2433
|
+
no_tables: "\u8CC7\u6599\u5EAB\u4E2D\u672A\u627E\u5230\u8868\u683C"
|
|
2434
|
+
},
|
|
2435
|
+
query: {
|
|
2436
|
+
description: "\u57F7\u884C SQL \u67E5\u8A62",
|
|
2437
|
+
executing: "\u6B63\u5728\u57F7\u884C\u67E5\u8A62...",
|
|
2438
|
+
no_results: "\u672A\u8FD4\u56DE\u4EFB\u4F55\u7D50\u679C",
|
|
2439
|
+
result_count: "\u7D50\u679C\uFF1A{count} \u5217"
|
|
2440
|
+
},
|
|
2441
|
+
q: {
|
|
2442
|
+
description: "\u4F9D @name \u57F7\u884C\u5DF2\u4FDD\u5B58\u7684\u67E5\u8A62\u7247\u6BB5",
|
|
2443
|
+
missing_param: "\u7247\u6BB5 '{name}' \u7F3A\u5C11\u5FC5\u8981\u53C3\u6578",
|
|
2444
|
+
engine_mismatch: "\u7247\u6BB5 '{name}' \u5F15\u64CE\u4E0D\u7B26",
|
|
2445
|
+
snippet_not_found: "\u627E\u4E0D\u5230\u7247\u6BB5\uFF1A{name}",
|
|
2446
|
+
dry_run_header: "Dry-run \u9810\u89BD\uFF08\u4E0D\u6703\u57F7\u884C\uFF09\uFF1A"
|
|
2447
|
+
},
|
|
2448
|
+
audit: {
|
|
2449
|
+
description: "\u6AA2\u8996\u3001\u67E5\u8A62\u3001\u6E05\u7A7A\u6216\u6AA2\u67E5 audit log \u5065\u5EB7\u72C0\u614B",
|
|
2450
|
+
tail: {
|
|
2451
|
+
description: "\u986F\u793A\u6700\u8FD1\u7684 audit \u7D00\u9304\uFF08\u9810\u8A2D\uFF1A\u7576\u524D\u9023\u7DDA\u6700\u5F8C 10 \u7B46\uFF09"
|
|
2452
|
+
},
|
|
2453
|
+
show: {
|
|
2454
|
+
description: "\u4F9D id\uFF08UUID \u524D\u7DB4 \u22654 \u78BC\uFF09\u6216 --recovery-ref \u67E5\u8A62\u55AE\u7B46 audit \u7D00\u9304"
|
|
2455
|
+
},
|
|
2456
|
+
clear: {
|
|
2457
|
+
description: "\u522A\u9664\u7576\u524D\u9023\u7DDA\u7684 audit log \u6A94\u6848\uFF08.jsonl \u8207 .jsonl.1\uFF09",
|
|
2458
|
+
prompt_header: "\u5373\u5C07\u6E05\u7A7A\u9023\u7DDA '{conn}' \u7684 audit log\uFF1A",
|
|
2459
|
+
prompt_file_line: " {file} \u2014 {entries} \u7B46\uFF0C{size}",
|
|
2460
|
+
prompt_continue: "\u78BA\u8A8D\u57F7\u884C\uFF1F[y/N] ",
|
|
2461
|
+
requires_tty_or_yes: "\u975E\u4E92\u52D5 session \u7121\u6CD5\u986F\u793A\u78BA\u8A8D\u63D0\u793A\u3002\u8ACB\u4F7F\u7528 --yes \u76F4\u63A5\u6E05\u7A7A\u3002",
|
|
2462
|
+
summary_cleared: "\u5DF2\u5F9E '{conn}' \u6E05\u9664 {count} \u7B46 audit \u7D00\u9304\u3002",
|
|
2463
|
+
summary_nothing: "\u6C92\u6709\u53EF\u6E05\u9664\u7684\u5167\u5BB9\u3002",
|
|
2464
|
+
summary_failed: "\u6E05\u9664\u5931\u6557\uFF1A{message}\u3002"
|
|
2465
|
+
},
|
|
2466
|
+
health: {
|
|
2467
|
+
description: "\u986F\u793A AuditLogger \u5065\u5EB7\u72C0\u614B\uFF08\u6A94\u6848\u5927\u5C0F\u3001\u7B46\u6578\u3001lock \u72C0\u614B\u3001\u6700\u5F8C\u5BEB\u5165\u7D50\u679C\uFF09"
|
|
2468
|
+
},
|
|
2469
|
+
write_gate: {
|
|
2470
|
+
description: "\u7D71\u8A08\u7B2C\u4E8C\u7D1A\u5BEB\u5165\u9598\u9580\u7684\u5224\u5B9A\uFF08ADR 0010 \u62BC\u6CE8\u7684\u90A3\u500B\u91CF\u6E2C\uFF09",
|
|
2471
|
+
header: "\u5BEB\u5165\u9598\u9580\u6458\u8981 \u2014 \u9023\u7DDA\uFF1A{conn}",
|
|
2472
|
+
window: "\u6383\u904E\u7684\u7A3D\u6838\u7D00\u9304\uFF1A{scanned} \u7B46\uFF0C{from} \u2192 {to}",
|
|
2473
|
+
decisions: "\u7B2C\u4E8C\u7D1A\u5224\u5B9A\uFF1A{count} \u6B21\uFF0C{from} \u2192 {to}",
|
|
2474
|
+
tier_one: "\u7B2C\u4E00\u7D1A\uFF0C\u5206\u958B\u8A08\u7B97\uFF1A{count} \u6B21\uFF08{outcomes}\uFF09",
|
|
2475
|
+
no_decisions: "\u9019\u6BB5\u5340\u9593\u5167\u7B2C\u4E8C\u7D1A\u4E00\u6B21\u90FD\u6C92\u88AB\u89F8\u53CA\u3002\u4F9D ADR 0010\uFF0C\u9019\u63A8\u7FFB\u7684\u662F\u5224\u6E96\u800C\u4E0D\u662F\u9598\u9580\uFF1A\u5B83\u5728\u627E\u7684\u5F62\u72C0\u4E0D\u662F\u5927\u5BB6\u5BE6\u969B\u6703\u5BEB\u7684\u5F62\u72C0\u3002",
|
|
2476
|
+
no_entries: "\u7A3D\u6838\u7D00\u9304\u662F\u7A7A\u7684\uFF0C\u6240\u4EE5\u9019\u5C0D\u9598\u9580\u6C92\u6709\u4EFB\u4F55\u7D50\u8AD6\u3002\u5148\u8DD1\u4E00\u4E9B\u5BEB\u5165\uFF0C\u6216\u7528 `dbcli audit health` \u6AA2\u67E5\u3002"
|
|
2477
|
+
},
|
|
2478
|
+
disabled_hint: "Audit \u5DF2\u505C\u7528\uFF08.dbcli \u4E2D audit.enabled = false\uFF09\u3002\u57F7\u884C `dbcli audit health` \u67E5\u770B\u8A73\u60C5\u3002",
|
|
2479
|
+
no_entries: "\u6C92\u6709 audit \u7D00\u9304\u3002",
|
|
2480
|
+
show_no_match: "\u627E\u4E0D\u5230\u7B26\u5408 '{prefix}' \u7684 audit \u7D00\u9304\u3002",
|
|
2481
|
+
show_ambiguous: "\u524D\u7DB4 '{prefix}' \u4E0D\u5920\u7CBE\u78BA\uFF1A\u5339\u914D\u5230 {count} \u7B46\u7D00\u9304\uFF0C\u8ACB\u6539\u7528\u66F4\u9577\u7684\u524D\u7DB4\u3002",
|
|
2482
|
+
show_prefix_too_short: "\u524D\u7DB4\u81F3\u5C11\u9700 4 \u500B\u5B57\u5143\u3002",
|
|
2483
|
+
show_recovery_no_match: "\u6C92\u6709\u4EFB\u4F55 audit \u7D00\u9304\u7684 recovery_ref \u70BA '{ref}'\u3002",
|
|
2484
|
+
show_recovery_ambiguous: "\u6709 {count} \u7B46\u7D00\u9304\u7684 recovery_ref \u70BA '{ref}'\uFF0C\u9019\u61C9\u5C6C\u7F55\u898B\u60C5\u6CC1\uFF1B\u8ACB\u76F4\u63A5\u6AA2\u8996\u6A94\u6848\u3002",
|
|
2485
|
+
show_mutex_violation: "\u8ACB\u64C7\u4E00\u63D0\u4F9B <id> \u53C3\u6578\u6216 --recovery-ref\uFF0C\u5169\u8005\u4E0D\u53EF\u540C\u6642\u4F7F\u7528\u3002",
|
|
2486
|
+
n_capped_warning: "--n \u503C {requested} \u8D85\u904E\u4E0A\u9650 {max}\uFF0C\u5DF2\u81EA\u52D5\u7E2E\u6E1B\u70BA {max}\u3002",
|
|
2487
|
+
n_must_be_positive: "--n \u5FC5\u9808\u70BA\u6B63\u6574\u6578\u3002"
|
|
2488
|
+
},
|
|
2489
|
+
queries: {
|
|
2490
|
+
description: "\u7BA1\u7406\u5DF2\u4FDD\u5B58\u7684\u67E5\u8A62\u7247\u6BB5",
|
|
2491
|
+
list_description: "\u5217\u51FA\u6240\u6709\u7247\u6BB5",
|
|
2492
|
+
show_description: "\u986F\u793A\u7247\u6BB5\u5167\u5BB9\uFF08frontmatter + SQL\uFF09",
|
|
2493
|
+
new_description: "\u5EFA\u7ACB\u65B0\u7684\u7247\u6BB5\u6A94",
|
|
2494
|
+
edit_description: "\u4EE5 $EDITOR \u958B\u555F\u7247\u6BB5",
|
|
2495
|
+
check_description: "\u9A57\u8B49\u6240\u6709\u7247\u6BB5",
|
|
2496
|
+
delete_description: "\u522A\u9664 local snippet \u6A94\u6848",
|
|
2497
|
+
rename_description: "\u91CD\u65B0\u547D\u540D local snippet",
|
|
2498
|
+
copy_description: "\u5C07\u4EFB\u4E00\u5C64\u7D1A\u7684 snippet \u8907\u88FD\u5230 local",
|
|
2499
|
+
import_description: "\u532F\u5165\u5916\u90E8 .sql \u6A94\u6848\u5230 local",
|
|
2500
|
+
export_description: "\u5C07 snippet \u539F\u59CB SQL \u8F38\u51FA\u5230 stdout \u6216\u6A94\u6848",
|
|
2501
|
+
created: "\u5DF2\u5EFA\u7ACB {file}",
|
|
2502
|
+
first_run_hint: "\u63D0\u793A\uFF1A\u5C07 .dbcli-shared/ commit \u9032\u7248\u63A7\u53EF\u8207\u5718\u968A\u5171\u4EAB\u7247\u6BB5\u3002",
|
|
2503
|
+
no_snippets: "\u5C1A\u7121\u4EFB\u4F55\u7247\u6BB5\u3002\u57F7\u884C dbcli queries new @<name> \u5EFA\u7ACB\u3002",
|
|
2504
|
+
check_ok: "\u2713 \u6210\u529F\u89E3\u6790 {count} \u500B\u7247\u6BB5",
|
|
2505
|
+
check_failed: "\u2717 \u6709 {count} \u500B\u7247\u6BB5\u89E3\u6790\u5931\u6557",
|
|
2506
|
+
search_description: "\u4F9D\u95DC\u9375\u5B57\u641C\u5C0B snippet\uFF08fuzzy \u6392\u5E8F\uFF09",
|
|
2507
|
+
suggest_description: "\u4F9D intent prefix \u63A8\u85A6 snippet",
|
|
2508
|
+
search_no_keywords: "\u81F3\u5C11\u63D0\u4F9B\u4E00\u500B\u95DC\u9375\u5B57\uFF0C\u6216\u8A66\u8A66 'queries list' / 'queries suggest'\u3002",
|
|
2509
|
+
search_no_results: "\u6C92\u6709\u547D\u4E2D\u7684 snippet\u3002\u8A66\u8A66 'queries list' \u6216 'queries suggest <intent>'\u3002",
|
|
2510
|
+
suggest_missing_intent: "\u8ACB\u63D0\u4F9B intent\uFF08\u6216 prefix\uFF09\u3002\u53EF\u7528\uFF1A{available}",
|
|
2511
|
+
suggest_invalid_intent: "Intent '{value}' \u683C\u5F0F\u932F\u8AA4\uFF0C\u5FC5\u9808\u7B26\u5408 ^[a-z][a-z0-9.-]*$",
|
|
2512
|
+
suggest_no_results: "\u6C92\u6709 intent prefix \u70BA '{intent}' \u7684 snippet\u3002\u53EF\u7528\uFF1A{available}",
|
|
2513
|
+
no_active_connection_hint: "\u5C1A\u672A\u8A2D\u5B9A connection\uFF1B\u5217\u51FA\u6240\u6709\u5F15\u64CE\u7684 snippet\u3002"
|
|
2514
|
+
},
|
|
2515
|
+
errors: {
|
|
2516
|
+
message: "\u932F\u8AA4\uFF1A{message}",
|
|
2517
|
+
query_only_boundary_failed: "\u7121\u6CD5\u70BA {system} \u5EFA\u7ACB query-only \u8CC7\u6599\u5EAB\u908A\u754C\uFF1A{detail}",
|
|
2518
|
+
query_only_boundary_verify: "\u8ACB\u78BA\u8A8D {system} \u652F\u63F4\u552F\u8B80\u4EA4\u6613\uFF0C\u4E14\u6B64\u5E33\u865F\u53EF\u4EE5\u555F\u52D5\u4EA4\u6613",
|
|
2519
|
+
query_only_boundary_not_executed: "\u76EE\u6A19\u8A9E\u53E5\u5C1A\u672A\u57F7\u884C\uFF1B\u8ACB\u4FEE\u6B63\u8CC7\u6599\u5EAB\u8A2D\u5B9A\u6216\u9023\u7DDA\u5F8C\u518D\u8A66",
|
|
2520
|
+
query_only_cleanup_completed: "query-only \u76EE\u6A19\u5DF2\u5B8C\u6210\uFF0C\u4F46 {system} \u7684\u4EA4\u6613\u6E05\u7406\u5931\u6557\uFF1A{detail}",
|
|
2521
|
+
query_only_cleanup_uncertain: "query-only \u76EE\u6A19\u5931\u6557\uFF0C\u4E14 {system} \u7684\u4EA4\u6613\u6E05\u7406\u4E5F\u5931\u6557\uFF0C\u56E0\u6B64\u7D50\u679C\u4E0D\u78BA\u5B9A\uFF1A{detail}",
|
|
2522
|
+
query_only_cleanup_completed_retry: "\u76EE\u6A19\u8A9E\u53E5\u5DF2\u57F7\u884C\uFF1B\u9664\u975E\u53EF\u5B89\u5168\u91CD\u8907\u5176\u5916\u90E8\u526F\u4F5C\u7528\uFF0C\u5426\u5247\u4E0D\u8981\u91CD\u8A66",
|
|
2523
|
+
query_only_cleanup_uncertain_retry: "\u76EE\u6A19\u8A9E\u53E5\u53EF\u80FD\u5728\u5931\u6557\u524D\u7522\u751F\u526F\u4F5C\u7528\uFF1B\u9664\u975E\u53EF\u5B89\u5168\u91CD\u8907\u9019\u4E9B\u526F\u4F5C\u7528\uFF0C\u5426\u5247\u4E0D\u8981\u91CD\u8A66",
|
|
2524
|
+
query_only_cleanup_reconnect: "\u6B64\u9023\u7DDA\u5DF2\u4E1F\u68C4\uFF1B\u57F7\u884C\u4E0B\u4E00\u53E5\u524D\u8ACB\u5148\u91CD\u65B0\u9023\u7DDA",
|
|
2525
|
+
invalid_config: "\u914D\u7F6E\u7121\u6548\uFF1A{field}",
|
|
2526
|
+
connection_failed: "\u7121\u6CD5\u9023\u63A5\u5230\u8CC7\u6599\u5EAB\uFF1A{message}",
|
|
2527
|
+
permission_denied: "\u6B0A\u9650\u88AB\u62D2\uFF08\u9700\u8981\uFF1A{required}\uFF09",
|
|
2528
|
+
permission_requires_level: "{type} \u64CD\u4F5C\u9700\u8981 {minimum} \u4EE5\u4E0A\u7684\u6B0A\u9650\uFF08\u76EE\u524D\u5C64\u7D1A\uFF1A{permission}\uFF09",
|
|
2529
|
+
elasticsearch_requires_level: "Elasticsearch {type} \u64CD\u4F5C\u9700\u8981 {minimum} \u4EE5\u4E0A\u7684\u6B0A\u9650\uFF08\u76EE\u524D\u5C64\u7D1A\uFF1A{permission}\uFF09",
|
|
2530
|
+
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",
|
|
2531
|
+
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",
|
|
2532
|
+
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\u9700\u8981 admin \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",
|
|
2533
|
+
permission_denied_reason: "\u6B0A\u9650\u88AB\u62D2\uFF1A{reason}",
|
|
2534
|
+
table_not_found: "\u627E\u4E0D\u5230\u8868\u683C\uFF1A{table}",
|
|
2535
|
+
unsupported_lang: "\u4E0D\u652F\u63F4\u7684\u8A9E\u8A00 '{lang}'\uFF0C\u4F7F\u7528\u82F1\u6587",
|
|
2536
|
+
invalid_json: "\u7121\u6548\u7684 JSON\uFF1A{message}",
|
|
2537
|
+
table_blacklisted: "\u932F\u8AA4: \u8868\u683C '{table}' \u7684 {operation} \u64CD\u4F5C\u5DF2\u88AB\u9ED1\u540D\u55AE",
|
|
2538
|
+
redis_target_blacklisted: "Redis \u6307\u4EE4\u5DF2\u88AB\u9ED1\u540D\u55AE\u62D2\u7D55",
|
|
2539
|
+
column_blacklisted_write: "\u932F\u8AA4: \u8868\u683C '{table}' \u7684 {operation} \u64CD\u4F5C\u5305\u542B\u9ED1\u540D\u55AE\u6B04\u4F4D: {columns}",
|
|
2540
|
+
table_already_blacklisted: "\u932F\u8AA4: \u8868\u683C '{table}' \u5DF2\u5728\u9ED1\u540D\u55AE\u4E2D",
|
|
2541
|
+
table_not_in_blacklist: "\u932F\u8AA4: \u8868\u683C '{table}' \u4E0D\u5728\u9ED1\u540D\u55AE\u4E2D",
|
|
2542
|
+
column_already_blacklisted: "\u932F\u8AA4: \u6B04\u4F4D '{table}.{column}' \u5DF2\u5728\u9ED1\u540D\u55AE\u4E2D",
|
|
2543
|
+
column_not_in_blacklist: "\u932F\u8AA4: \u6B04\u4F4D '{table}.{column}' \u4E0D\u5728\u9ED1\u540D\u55AE\u4E2D",
|
|
2544
|
+
invalid_table_name: "\u932F\u8AA4: \u7121\u6548\u7684\u8868\u683C\u540D\u7A31: {table}",
|
|
2545
|
+
invalid_column_format: "\u932F\u8AA4: \u7121\u6548\u7684\u6B04\u4F4D\u683C\u5F0F\u3002\u4F7F\u7528 'table.column'",
|
|
2546
|
+
invalid_system: "\u7121\u6548\u7684\u8CC7\u6599\u5EAB\u7CFB\u7D71\uFF1A{system}",
|
|
2547
|
+
invalid_permission: "\u7121\u6548\u7684\u6B0A\u9650\u7B49\u7D1A\uFF1A{permission}",
|
|
2548
|
+
invalid_port: "\u7121\u6548\u7684\u57E0\u865F\uFF1A{port}",
|
|
2549
|
+
require_user: "\u975E\u4E92\u52D5\u6A21\u5F0F\u9700\u8981 --user \u9078\u9805",
|
|
2550
|
+
require_name: "\u975E\u4E92\u52D5\u6A21\u5F0F\u9700\u8981 --name \u9078\u9805",
|
|
2551
|
+
env_refs_missing_options: `\u4F7F\u7528 --use-env-refs \u6642\uFF0C\u5FC5\u9808\u6307\u5B9A\u74B0\u5883\u8B8A\u6578\u540D\u7A31\u3002
|
|
2552
|
+
\u8ACB\u63D0\u4F9B\u9078\u9805\uFF1A--env-host\u3001--env-port\u3001--env-user\u3001--env-password\u3001--env-database
|
|
2553
|
+
\u6216\u4F7F\u7528\u4E92\u52D5\u6A21\u5F0F\uFF1A\u57F7\u884C "dbcli init --use-env-refs"\uFF08\u4E0D\u52A0 --no-interactive\uFF09`,
|
|
2554
|
+
env_var_not_defined: `\u7121\u6CD5\u6E2C\u8A66\u9023\u7DDA\uFF1A\u74B0\u5883\u8B8A\u6578 {envKey} \u672A\u5B9A\u7FA9\u3002
|
|
2555
|
+
\u8ACB\u5728 .env \u6216\u74B0\u5883\u8B8A\u6578\u4E2D\u8A2D\u5B9A {envKey}\u3002
|
|
2556
|
+
\u63D0\u793A\uFF1A\u57F7\u884C 'export {envKey}=<value>' \u5F8C\u91CD\u8A66`,
|
|
2557
|
+
invalid_output_format: "\u7121\u6CD5\u4F7F\u7528 --format {format}\uFF0C\u8ACB\u4F7F\u7528 text \u6216 json",
|
|
2558
|
+
sqlite_file_required: "SQLite \u9023\u7DDA\u9700\u8981\u4E00\u500B\u6A94\u6848\u8DEF\u5F91\u3002\u8ACB\u7528 --file <path>\uFF0C\u6216\u4EE5\u4E92\u52D5\u6A21\u5F0F\u57F7\u884C init\u3002",
|
|
2559
|
+
sql_tls_unsupported: "\u660E\u78BA SQL TLS \u76EE\u524D\u50C5\u652F\u63F4 PostgreSQL\u3001MySQL \u8207 MariaDB",
|
|
2560
|
+
sql_tls_options_required: "\u8ACB\u540C\u6642\u63D0\u4F9B --sql-tls verify-full \u8207 --sql-tls-ca <path>",
|
|
2561
|
+
sql_tls_change_engine: "\u660E\u78BA SQL TLS \u76EE\u524D\u50C5\u652F\u63F4 PostgreSQL\u3001MySQL \u8207 MariaDB\uFF1B\u7DE8\u8F2F\u9023\u7DDA\u6642\u4E0D\u80FD\u6368\u68C4\u6B64\u8981\u6C42",
|
|
2562
|
+
sql_tls_legacy_import: "\u4E0D\u652F\u63F4\u5E36\u6709 SQL TLS \u7684\u820A\u7248\u55AE\u6A94\u81EA\u52D5\u532F\u5165\u3002\u8ACB\u4FDD\u7559\u539F\u9023\u7DDA\uFF0C\u5728\u53E6\u4E00\u500B\u8A2D\u5B9A\u76EE\u9304\u5EFA\u7ACB\u4E26\u9A57\u8B49\u5177\u540D\u9023\u7DDA\u5F8C\u518D\u5207\u63DB\uFF1B\u4E0D\u8981\u79FB\u9664 SQL TLS\u3002",
|
|
2563
|
+
sql_tls_ca_absolute: "SQL TLS CA \u8DEF\u5F91\u5FC5\u9808\u70BA\u7D55\u5C0D\u8DEF\u5F91",
|
|
2564
|
+
sql_tls_network_host: "\u660E\u78BA SQL TLS \u5FC5\u9808\u6307\u5B9A DNS \u6216 IP \u4E3B\u6A5F\uFF0C\u4E0D\u63A5\u53D7 Unix socket",
|
|
2565
|
+
sql_tls_ca_empty: "CA bundle \u4E0D\u542B PEM \u6191\u8B49",
|
|
2566
|
+
sql_tls_ca_load: "\u7121\u6CD5\u8F09\u5165 SQL TLS CA bundle\uFF1A{detail}",
|
|
2567
|
+
sql_tls_ca_restore: "\u8ACB\u6062\u5FA9 sqlTls.caPath \u6307\u5411\u7684\u53EF\u8B80\u6709\u6548 PEM CA bundle\uFF0C\u4E26\u4FDD\u6301 verify-full\u3002",
|
|
2568
|
+
sql_tls_handshake: "TLS \u63E1\u624B\u5931\u6557\uFF1A{detail}",
|
|
2569
|
+
sql_tls_ca_hint: "\u8ACB\u5C07 sqlTls.caPath \u8A2D\u70BA\u6B63\u78BA\u4E14\u53EF\u8B80\u7684 PEM CA bundle\u3002",
|
|
2570
|
+
sql_tls_identity_hint: "\u8ACB\u78BA\u8A8D\u6191\u8B49\u6709\u6548\u671F\u53CA {host} \u7684 DNS\uFF0FIP \u8EAB\u5206\u76F8\u7B26\u3002",
|
|
2571
|
+
sql_tls_no_downgrade: "\u8ACB\u4FDD\u6301 verify-full\uFF1B\u4E0D\u8981\u79FB\u9664 SQL TLS\uFF0C\u4E5F\u4E0D\u8981\u95DC\u9589\u9A57\u8B49\u5F8C\u91CD\u8A66\u3002",
|
|
2572
|
+
sql_tls_driver_identity: "\u6B64\u9A45\u52D5\u7A0B\u5F0F\u7121\u6CD5\u57F7\u884C SQL TLS \u8EAB\u5206\u9A57\u8B49",
|
|
2573
|
+
sql_tls_peer_unverified: "SQL TLS \u5C0D\u7AEF\u5C1A\u672A\u901A\u904E\u9A57\u8B49",
|
|
2574
|
+
sql_tls_peer_missing: "\u7121\u6CD5\u53D6\u5F97 SQL TLS \u5C0D\u7AEF\u6191\u8B49",
|
|
2575
|
+
sql_tls_recovery_config: "TLS \u5FA9\u539F\u9808\u660E\u78BA\u6307\u5B9A --config <path> \u6216 --global \u8207\u9810\u671F\u7684 TLS profile\uFF1B\u9810\u8A2D\u503C\u7121\u6CD5\u8B58\u5225\u5931\u6557\u7684\u76EE\u6A19\u3002",
|
|
2576
|
+
sql_tls_recovery_profile: "TLS \u5FA9\u539F\u9808\u6307\u5B9A --use <name>\uFF08\u6216 DBCLI_CONNECTION\uFF09\uFF1B\u4E0D\u80FD\u9000\u56DE\u9810\u8A2D profile\u3002",
|
|
2577
|
+
sql_tls_recovery_required: "TLS \u5FA9\u539F\u9808\u4F7F\u7528\u660E\u78BA\u8A2D\u5B9A sqlTls verify-full \u7684 profile\uFF1B\u62D2\u7D55\u660E\u6587\u5FA9\u539F\u3002",
|
|
2578
|
+
sql_tls_recovery_target: "TLS \u5FA9\u539F\u9808\u660E\u78BA\u9078\u53D6\u5DF2\u8A2D\u5B9A\u9A57\u8B49\u5F0F TLS \u7684 profile\u3002"
|
|
2579
|
+
},
|
|
2580
|
+
success: {
|
|
2581
|
+
inserted: "\u6210\u529F\u63D2\u5165 {count} \u5217",
|
|
2582
|
+
updated: "\u6210\u529F\u66F4\u65B0 {count} \u5217",
|
|
2583
|
+
deleted: "\u6210\u529F\u522A\u9664 {count} \u5217",
|
|
2584
|
+
no_changes: "\u6C92\u6709\u8B8A\u66F4\u8981\u5957\u7528"
|
|
2585
|
+
},
|
|
2586
|
+
insert: {
|
|
2587
|
+
description: "\u63D2\u5165\u8CC7\u6599\u5230\u8868\u683C",
|
|
2588
|
+
prompt_data: "\u8F38\u5165\u8981\u63D2\u5165\u7684 JSON \u8CC7\u6599\uFF1A",
|
|
2589
|
+
confirm: "\u5C07 {count} \u5217\u63D2\u5165\u5230 {table}\uFF1F (y/n)\uFF1A",
|
|
2590
|
+
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"
|
|
2591
|
+
},
|
|
2592
|
+
update: {
|
|
2593
|
+
description: "\u66F4\u65B0\u8868\u683C\u4E2D\u7684\u8CC7\u6599",
|
|
2594
|
+
prompt_where: "\u8F38\u5165 WHERE \u5B50\u53E5 (\u4F8B\u5982\uFF1Aid=1)\uFF1A",
|
|
2595
|
+
prompt_set: '\u8F38\u5165 JSON \u66F4\u65B0 (\u4F8B\u5982\uFF1A{"status": "active"})\uFF1A',
|
|
2596
|
+
confirm: "\u66F4\u65B0 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A",
|
|
2597
|
+
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"
|
|
2598
|
+
},
|
|
2599
|
+
delete: {
|
|
2600
|
+
description: "\u522A\u9664\u8868\u683C\u4E2D\u7684\u8CC7\u6599",
|
|
2601
|
+
prompt_where: "\u8F38\u5165 WHERE \u5B50\u53E5 (\u4F8B\u5982\uFF1Aid=1)\uFF1A",
|
|
2602
|
+
confirm: "\u522A\u9664 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A",
|
|
2603
|
+
admin_only: "\u522A\u9664\u547D\u4EE4\u9700\u8981 data-admin \u6216 admin \u6B0A\u9650",
|
|
2604
|
+
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"
|
|
2605
|
+
},
|
|
2606
|
+
export: {
|
|
2607
|
+
description: "\u532F\u51FA\u67E5\u8A62\u7D50\u679C",
|
|
2608
|
+
formats: "\u652F\u63F4\u7684\u683C\u5F0F\uFF1Ajson\u3001csv",
|
|
2609
|
+
exported: "\u5DF2\u5C07 {count} \u5217\u532F\u51FA\u81F3 {file}"
|
|
2610
|
+
},
|
|
2611
|
+
upgrade: {
|
|
2612
|
+
description: "\u6AA2\u67E5\u66F4\u65B0\u4E26\u5C07 dbcli \u5347\u7D1A\u81F3\u6700\u65B0\u7248\u672C",
|
|
2613
|
+
checking: "\u6B63\u5728\u6AA2\u67E5\u66F4\u65B0...",
|
|
2614
|
+
already_up_to_date: "\u2713 \u5DF2\u7D93\u662F\u6700\u65B0\u7248\u672C (v{version})",
|
|
2615
|
+
new_version_available: "\u767C\u73FE\u65B0\u7248\u672C\uFF1Av{version}",
|
|
2616
|
+
current_version: "\u76EE\u524D\u7248\u672C\uFF1Av{version}",
|
|
2617
|
+
latest_version: "\u6700\u65B0\u7248\u672C\uFF1Av{version}",
|
|
2618
|
+
update_hint: '[\u63D0\u793A] dbcli v{version} \u5DF2\u767C\u5E03\u3002\u57F7\u884C "dbcli upgrade" \u9032\u884C\u5347\u7D1A\u3002',
|
|
2619
|
+
install_hint: '\u57F7\u884C "dbcli upgrade" \u5B89\u88DD\u66F4\u65B0\u3002',
|
|
2620
|
+
upgrading: "\u6B63\u5728\u5347\u7D1A...",
|
|
2621
|
+
success: "\u2713 \u6210\u529F\u5347\u7D1A\u81F3 v{version}",
|
|
2622
|
+
skill_recheck_hint: '\u63D0\u793A\uFF1A\u82E5\u5DF2\u5B89\u88DD AI \u6280\u80FD\uFF0C\u8ACB\u57F7\u884C "dbcli skill --install <platform>" \u78BA\u8A8D\u6280\u80FD\u70BA\u6700\u65B0\u7248\u672C\u3002',
|
|
2623
|
+
failed: "\u2717 \u5347\u7D1A\u5931\u6557\u3002\u8ACB\u5617\u8A66\u624B\u52D5\u57F7\u884C\uFF1A",
|
|
2624
|
+
manual_hint: "bun add -g @carllee1983/dbcli@latest",
|
|
2625
|
+
network_error: "\u26A0 \u7121\u6CD5\u6AA2\u67E5\u66F4\u65B0\uFF08\u7DB2\u8DEF\u932F\u8AA4\u6216\u8A3B\u518A\u8868\u4E0D\u53EF\u7528\uFF09"
|
|
2626
|
+
},
|
|
2627
|
+
skill: {
|
|
2628
|
+
description: "\u751F\u6210 AI \u6280\u80FD\u6587\u6A94",
|
|
2629
|
+
context_description: "\u751F\u6210 AI \u5354\u4F5C\u6240\u9700\u7684\u8CC7\u6599\u5EAB\u67B6\u69CB\u8207\u67E5\u8A62\u7247\u6BB5\u4E0A\u4E0B\u6587",
|
|
2630
|
+
installed: `\u6280\u80FD: {path}
|
|
2631
|
+
\u53C3\u8003: {referencePath}`,
|
|
2632
|
+
update_available: "\u4EE5\u4E0B\u5E73\u53F0\u7684\u6280\u80FD\u9700\u8981\u66F4\u65B0\uFF1A",
|
|
2633
|
+
update_hint: '\u57F7\u884C "dbcli skill --install <platform>" \u9032\u884C\u66F4\u65B0\u3002'
|
|
2634
|
+
},
|
|
2635
|
+
blacklist: {
|
|
2636
|
+
description: "\u7BA1\u7406\u654F\u611F\u8CC7\u6599\u9ED1\u540D\u55AE\u4EE5\u9632\u6B62 AI \u5B58\u53D6",
|
|
2637
|
+
list_title: "\u76EE\u524D\u9ED1\u540D\u55AE\u914D\u7F6E",
|
|
2638
|
+
tables_label: "\u5DF2\u9ED1\u540D\u55AE\u7684\u8868\u683C",
|
|
2639
|
+
columns_label: "\u5DF2\u9ED1\u540D\u55AE\u7684\u6B04\u4F4D",
|
|
2640
|
+
none: "\u76EE\u524D\u6C92\u6709\u9ED1\u540D\u55AE\u8868\u683C\u6216\u6B04\u4F4D",
|
|
2641
|
+
table_added: "\u2713 \u8868\u683C '{table}' \u5DF2\u65B0\u589E\u81F3\u9ED1\u540D\u55AE",
|
|
2642
|
+
table_removed: "\u2713 \u8868\u683C '{table}' \u5DF2\u5F9E\u9ED1\u540D\u55AE\u79FB\u9664",
|
|
2643
|
+
column_added: "\u2713 \u6B04\u4F4D '{table}.{column}' \u5DF2\u65B0\u589E\u81F3\u9ED1\u540D\u55AE",
|
|
2644
|
+
column_removed: "\u2713 \u6B04\u4F4D '{table}.{column}' \u5DF2\u5F9E\u9ED1\u540D\u55AE\u79FB\u9664",
|
|
2645
|
+
refuse_wildcard: "\u5DF2\u62D2\u7D55\uFF1A'{table}' \u542B\u6709\u842C\u7528\u5B57\u5143\uFF0C\u800C {system} \u4E0A\u7684\u6BD4\u5C0D\u662F\u9010\u5B57\u9032\u884C\u7684\u2014\u2014\u9019\u7B46\u8A2D\u5B9A\u6C38\u9060\u4E0D\u6703\u547D\u4E2D\u4EFB\u4F55\u6771\u897F\u3002\u8ACB\u5BEB\u51FA\u78BA\u5207\u7684\u540D\u7A31\u3002"
|
|
2646
|
+
},
|
|
2647
|
+
security: {
|
|
2648
|
+
columns_omitted: "\u5B89\u5168: {count} \u500B\u6B04\u4F4D\u6839\u64DA\u4F60\u7684\u9ED1\u540D\u55AE\u5DF2\u88AB\u96B1\u85CF"
|
|
2649
|
+
},
|
|
2650
|
+
warnings: {
|
|
2651
|
+
blacklist_override_used: "\u8B66\u544A: \u5DF2\u555F\u7528\u9ED1\u540D\u55AE\u8986\u84CB (DBCLI_OVERRIDE_BLACKLIST=true)\u3002\u57F7\u884C {operation} \u5728\u5DF2\u9ED1\u540D\u55AE\u7684\u8868\u683C '{table}' \u4E0A",
|
|
2652
|
+
redis_blacklist_override_active: "\u8B66\u544A: DBCLI_OVERRIDE_BLACKLIST=true \u5DF2\u505C\u7528 Redis \u9ED1\u540D\u55AE\u5F37\u5236\u57F7\u884C"
|
|
2653
|
+
},
|
|
2654
|
+
version: {
|
|
2655
|
+
unsupported_warning: "{system} {version} \u4F4E\u65BC\u6700\u4F4E\u652F\u63F4\u7248\u672C {minVersion}\u3002\u90E8\u5206\u529F\u80FD\u53EF\u80FD\u7121\u6CD5\u6B63\u5E38\u904B\u4F5C\u3002",
|
|
2656
|
+
doctor_pass: "{system} {version}\uFF08\u7B26\u5408 >= {minVersion}\uFF09",
|
|
2657
|
+
doctor_warn: "{system} {version} \u4F4E\u65BC\u652F\u63F4\u7248\u672C >= {minVersion}"
|
|
2658
|
+
},
|
|
2659
|
+
migrate: {
|
|
2660
|
+
description: "Schema DDL \u64CD\u4F5C\uFF08CREATE/DROP/ALTER TABLE\u3001INDEX\u3001CONSTRAINT\u3001ENUM\uFF09",
|
|
2661
|
+
dry_run_preview: "[\u9810\u89BD] \u7522\u751F\u7684 SQL\uFF08\u672A\u57F7\u884C\uFF09\uFF1A",
|
|
2662
|
+
executing: "\u57F7\u884C DDL \u4E2D...",
|
|
2663
|
+
success: "DDL \u57F7\u884C\u6210\u529F",
|
|
2664
|
+
confirm_drop: "\u9019\u662F\u7834\u58DE\u6027\u64CD\u4F5C\uFF08{operation}\uFF09\uFF0C\u78BA\u5B9A\u7E7C\u7E8C\uFF1F",
|
|
2665
|
+
permission_required: "DDL \u64CD\u4F5C\u9700\u8981 admin \u6B0A\u9650\uFF08\u76EE\u524D\uFF1A{permission}\uFF09",
|
|
2666
|
+
blacklist_blocked: '\u8868\u683C "{table}" \u5DF2\u88AB\u5217\u5165\u9ED1\u540D\u55AE \u2014 DDL \u64CD\u4F5C\u88AB\u963B\u64CB',
|
|
2667
|
+
schema_refreshed: '\u5DF2\u66F4\u65B0\u8868\u683C "{table}" \u7684 Schema \u5FEB\u53D6',
|
|
2668
|
+
no_sql_generated: "\u672A\u7522\u751F SQL\uFF08\u6B64\u64CD\u4F5C\u4E0D\u9069\u7528\u65BC\u6B64\u8CC7\u6599\u5EAB\uFF09",
|
|
2669
|
+
create_description: "\u5EFA\u7ACB\u65B0\u8868\u683C",
|
|
2670
|
+
drop_description: "\u522A\u9664\u8868\u683C",
|
|
2671
|
+
add_column_description: "\u65B0\u589E\u6B04\u4F4D\u81F3\u8868\u683C",
|
|
2672
|
+
drop_column_description: "\u522A\u9664\u8868\u683C\u6B04\u4F4D",
|
|
2673
|
+
alter_column_description: "\u4FEE\u6539\u8868\u683C\u6B04\u4F4D",
|
|
2674
|
+
add_index_description: "\u65B0\u589E\u7D22\u5F15\u81F3\u8868\u683C",
|
|
2675
|
+
drop_index_description: "\u522A\u9664\u7D22\u5F15",
|
|
2676
|
+
add_constraint_description: "\u65B0\u589E\u7D04\u675F\u81F3\u8868\u683C",
|
|
2677
|
+
drop_constraint_description: "\u522A\u9664\u8868\u683C\u7D04\u675F",
|
|
2678
|
+
add_enum_description: "\u5EFA\u7ACB\u5217\u8209\u578B\u5225\uFF08\u50C5 PostgreSQL\uFF09",
|
|
2679
|
+
alter_enum_description: "\u65B0\u589E\u5217\u8209\u503C",
|
|
2680
|
+
drop_enum_description: "\u522A\u9664\u5217\u8209\u578B\u5225"
|
|
2681
|
+
},
|
|
2682
|
+
password: {
|
|
2683
|
+
description: "\u53EA\u8B8A\u66F4\u55AE\u4E00\u9023\u7DDA\u7684\u5BC6\u78BC\uFF0C\u5176\u9918\u8A2D\u5B9A\u539F\u5C01\u4E0D\u52D5",
|
|
2684
|
+
arg_connection: "\u9023\u7DDA\u540D\u7A31\uFF08\u7701\u7565\u6642\u6539\u9810\u8A2D\u9023\u7DDA\uFF09",
|
|
2685
|
+
opt_stdin: "\u5F9E stdin \u8B80\u53D6\u65B0\u5BC6\u78BC\uFF08\u4F9B\u8F2A\u66FF\u8173\u672C\u4F7F\u7528\uFF09",
|
|
2686
|
+
opt_password: "\u76F4\u63A5\u5E36\u5165\u65B0\u5BC6\u78BC\uFF08\u6703\u7559\u5728 shell \u6B77\u53F2\u8207\u884C\u7A0B\u5217\u8868\uFF09",
|
|
2687
|
+
opt_skip_test: "\u5B58\u6A94\u524D\u4E0D\u5148\u7528\u65B0\u5BC6\u78BC\u9023\u7DDA\u9A57\u8B49",
|
|
2688
|
+
prompt: "\u9023\u7DDA '{name}' \u7684\u65B0\u5BC6\u78BC",
|
|
2689
|
+
source_conflict: "--password \u8207 --stdin \u53EA\u80FD\u64C7\u4E00\u3002",
|
|
2690
|
+
needs_source: "\u6C92\u6709\u7D42\u7AEF\u6A5F\u53EF\u505A\u906E\u853D\u8F38\u5165\uFF0C\u8ACB\u6539\u7528 --stdin \u6216 --password \u50B3\u5165\u3002",
|
|
2691
|
+
empty: "\u65B0\u5BC6\u78BC\u662F\u7A7A\u7684\uFF0C\u672A\u505A\u4EFB\u4F55\u8B8A\u66F4\u3002",
|
|
2692
|
+
updated: "\u5DF2\u66F4\u65B0\u9023\u7DDA '{name}' \u7684\u5BC6\u78BC \u2192 {envFile}\uFF08{varName}\uFF09",
|
|
2693
|
+
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',
|
|
2694
|
+
skipped_test: "\u5DF2\u8DF3\u904E\u9A57\u8B49\uFF08--skip-test\uFF09\uFF1A\u65B0\u5BC6\u78BC\u672A\u7D93\u8CC7\u6599\u5EAB\u78BA\u8A8D\u3002"
|
|
2695
|
+
}
|
|
2696
|
+
};
|
|
2697
|
+
});
|
|
2698
|
+
|
|
2699
|
+
// resources/lang/en/shell.json
|
|
2700
|
+
var shell_default;
|
|
2701
|
+
var init_shell = __esm(() => {
|
|
2702
|
+
shell_default = {
|
|
2703
|
+
welcome: "Connected to {system} ({database}@{host}:{port})",
|
|
2704
|
+
welcome_permission: "Permission: {permission}",
|
|
2705
|
+
prompt: "dbcli",
|
|
2706
|
+
continuation_prompt: " ...",
|
|
2707
|
+
goodbye: "Goodbye.",
|
|
2708
|
+
error_no_config: "No .dbcli config found. Run 'dbcli init' first.",
|
|
2709
|
+
error_connection_failed: "Connection failed: {message}",
|
|
2710
|
+
error_reconnecting: "Connection lost. Attempting to reconnect...",
|
|
2711
|
+
error_reconnect_failed: "Reconnection failed: {message}",
|
|
2712
|
+
error_reconnect_success: "Reconnected successfully.",
|
|
2713
|
+
error_command_failed: "Command error: {message}",
|
|
2714
|
+
error_sql_failed: "SQL error: {message}",
|
|
2715
|
+
error_permission: "Permission denied. Required: {required} (current: {current})",
|
|
2716
|
+
error_blacklisted: "Table '{table}' is blacklisted.",
|
|
2717
|
+
sql_mode_hint: "SQL-only mode. Only SQL statements are accepted.",
|
|
2718
|
+
multiline_cancelled: "Multiline input cancelled.",
|
|
2719
|
+
timing_display: "Time: {ms}ms",
|
|
2720
|
+
rows_display: "{count} row(s)",
|
|
2721
|
+
unknown_command: "Unknown command: '{command}'. Type '.help' for available commands.",
|
|
2722
|
+
subcommand_prefix_hint: "This line is being read as SQL and is waiting for its `;`. If you meant the dbcli subcommand, type {command} instead. Ctrl-C cancels.",
|
|
2723
|
+
es: {
|
|
2724
|
+
parse_empty: "Empty request",
|
|
2725
|
+
parse_needs_method_path: 'Request requires a method and a path, e.g. "GET /index/_search"',
|
|
2726
|
+
parse_needs_path: "Request requires a path",
|
|
2727
|
+
refuse_dot_segment: "Refused: '{path}' contains a '..' segment once decoded, and dbcli cannot tell which path Elasticsearch would route it to. Write the path out without '..'.",
|
|
2728
|
+
refuse_unparseable: "Refused: '{path}' is not the request the server would receive. It cannot be parsed as a path.",
|
|
2729
|
+
refuse_not_canonical: "Refused: '{path}' is not the request the server would receive. Write it as '{canonical}'.",
|
|
2730
|
+
refuse_source_param: "Refused: `source` in the query string is a request body by another route. Write the body as JSON.",
|
|
2731
|
+
refuse_search_template: "Refused: '{path}' is a search template, whose body cannot be inspected \u2014 it is a string rendered on the cluster, or stored there. Write the query out in full.",
|
|
2732
|
+
refuse_string_body: "Refused: a quoted string request body is not supported. Write the body as JSON.",
|
|
2733
|
+
refuse_audit_strict: "Refused: the audit entry for this request could not be written, and audit.strict is on. Fix the audit sink (disk space, directory permissions, a stale lockfile in .dbcli/audit) or turn audit.strict off to accept unrecorded requests.",
|
|
2734
|
+
blacklist_index: "index '{index}' is blacklist-protected",
|
|
2735
|
+
blacklist_unscoped: "'{path}' names no index, so it cannot be checked against the blacklist. Scope the request to an index, e.g. GET /<index>/_search.",
|
|
2736
|
+
blacklist_field: "field '{field}' is blacklist-protected and cannot be named in a request \u2014 sorting, aggregating or scripting on it would return its values.",
|
|
2737
|
+
banner_title: "Elasticsearch shell \u2014 Kibana Dev Tools syntax",
|
|
2738
|
+
banner_hint: 'Enter "<METHOD> /<path>" then an optional JSON body; submit with a blank line. Try: GET /_cat/indices',
|
|
2739
|
+
banner_keys: 'Ctrl+C cancels the current block; Ctrl+D or "exit" quits.',
|
|
2740
|
+
block_cancelled: "(block cancelled)",
|
|
2741
|
+
goodbye: "Goodbye"
|
|
2742
|
+
}
|
|
2743
|
+
};
|
|
2744
|
+
});
|
|
2745
|
+
|
|
2746
|
+
// resources/lang/zh-TW/shell.json
|
|
2747
|
+
var shell_default2;
|
|
2748
|
+
var init_shell2 = __esm(() => {
|
|
2749
|
+
shell_default2 = {
|
|
2750
|
+
welcome: "\u5DF2\u9023\u7DDA\u81F3 {system}\uFF08{database}@{host}:{port}\uFF09",
|
|
2751
|
+
welcome_permission: "\u6B0A\u9650\u7B49\u7D1A\uFF1A{permission}",
|
|
2752
|
+
prompt: "dbcli",
|
|
2753
|
+
continuation_prompt: " ...",
|
|
2754
|
+
goodbye: "\u518D\u898B\u3002",
|
|
2755
|
+
error_no_config: "\u627E\u4E0D\u5230 .dbcli \u8A2D\u5B9A\u6A94\u3002\u8ACB\u5148\u57F7\u884C 'dbcli init'\u3002",
|
|
2756
|
+
error_connection_failed: "\u9023\u7DDA\u5931\u6557\uFF1A{message}",
|
|
2757
|
+
error_reconnecting: "\u9023\u7DDA\u5DF2\u4E2D\u65B7\uFF0C\u5617\u8A66\u91CD\u65B0\u9023\u7DDA...",
|
|
2758
|
+
error_reconnect_failed: "\u91CD\u65B0\u9023\u7DDA\u5931\u6557\uFF1A{message}",
|
|
2759
|
+
error_reconnect_success: "\u91CD\u65B0\u9023\u7DDA\u6210\u529F\u3002",
|
|
2760
|
+
error_command_failed: "\u6307\u4EE4\u932F\u8AA4\uFF1A{message}",
|
|
2761
|
+
error_sql_failed: "SQL \u932F\u8AA4\uFF1A{message}",
|
|
2762
|
+
error_permission: "\u6B0A\u9650\u4E0D\u8DB3\u3002\u9700\u8981\uFF1A{required}\uFF08\u76EE\u524D\uFF1A{current}\uFF09",
|
|
2763
|
+
error_blacklisted: "\u8868\u683C '{table}' \u5DF2\u88AB\u5217\u5165\u9ED1\u540D\u55AE\u3002",
|
|
2764
|
+
sql_mode_hint: "\u7D14 SQL \u6A21\u5F0F\u3002\u50C5\u63A5\u53D7 SQL \u8A9E\u53E5\u3002",
|
|
2765
|
+
multiline_cancelled: "\u5DF2\u53D6\u6D88\u591A\u884C\u8F38\u5165\u3002",
|
|
2766
|
+
timing_display: "\u8017\u6642\uFF1A{ms}ms",
|
|
2767
|
+
rows_display: "{count} \u7B46\u8CC7\u6599",
|
|
2768
|
+
unknown_command: "\u672A\u77E5\u6307\u4EE4\uFF1A'{command}'\u3002\u8F38\u5165 '.help' \u67E5\u770B\u53EF\u7528\u6307\u4EE4\u3002",
|
|
2769
|
+
subcommand_prefix_hint: "\u9019\u4E00\u884C\u88AB\u7576\u6210 SQL\uFF0C\u6B63\u5728\u7B49\u5206\u865F\u3002\u5982\u679C\u4F60\u8981\u7684\u662F dbcli \u5B50\u6307\u4EE4\uFF0C\u8ACB\u6539\u6253 {command}\u3002Ctrl-C \u53EF\u53D6\u6D88\u3002",
|
|
2770
|
+
es: {
|
|
2771
|
+
parse_empty: "\u7A7A\u7684\u8ACB\u6C42",
|
|
2772
|
+
parse_needs_method_path: '\u8ACB\u6C42\u9700\u8981\u4E00\u500B method \u8207\u4E00\u500B path\uFF0C\u4F8B\u5982 "GET /index/_search"',
|
|
2773
|
+
parse_needs_path: "\u8ACB\u6C42\u9700\u8981\u4E00\u500B path",
|
|
2774
|
+
refuse_dot_segment: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u89E3\u78BC\u5F8C\u542B\u6709 '..' \u5340\u6BB5\uFF0Cdbcli \u7121\u6CD5\u5224\u65B7 Elasticsearch \u6703\u628A\u5B83\u8DEF\u7531\u5230\u54EA\u500B\u8DEF\u5F91\u3002\u8ACB\u628A\u8DEF\u5F91\u5BEB\u6210\u4E0D\u542B '..' \u7684\u5F62\u5F0F\u3002",
|
|
2775
|
+
refuse_unparseable: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u4E0D\u662F\u4F3A\u670D\u5668\u6703\u6536\u5230\u7684\u90A3\u500B\u8ACB\u6C42\u3002\u5B83\u7121\u6CD5\u88AB\u89E3\u6790\u6210\u4E00\u500B\u8DEF\u5F91\u3002",
|
|
2776
|
+
refuse_not_canonical: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u4E0D\u662F\u4F3A\u670D\u5668\u6703\u6536\u5230\u7684\u90A3\u500B\u8ACB\u6C42\u3002\u8ACB\u5BEB\u6210 '{canonical}'\u3002",
|
|
2777
|
+
refuse_source_param: "\u5DF2\u62D2\u7D55\uFF1Aquery string \u88E1\u7684 `source` \u662F\u53E6\u4E00\u689D\u8DEF\u5F91\u4E0A\u7684 request body\u3002\u8ACB\u628A body \u5BEB\u6210 JSON\u3002",
|
|
2778
|
+
refuse_search_template: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u662F search template\uFF0C\u5B83\u7684 body \u7121\u6CD5\u6AA2\u67E5\u2014\u2014\u90A3\u662F\u4E00\u500B\u5728\u53E2\u96C6\u4E0A\u7B97\u51FA\u4F86\u7684\u5B57\u4E32\uFF0C\u6216\u8005\u6839\u672C\u5B58\u5728\u53E2\u96C6\u88E1\u3002\u8ACB\u628A\u67E5\u8A62\u5B8C\u6574\u5BEB\u51FA\u4F86\u3002",
|
|
2779
|
+
refuse_string_body: "\u5DF2\u62D2\u7D55\uFF1A\u4E0D\u652F\u63F4\u4EE5\u5F15\u865F\u5B57\u4E32\u5F62\u5F0F\u5BEB\u7684 request body\u3002\u8ACB\u628A body \u5BEB\u6210 JSON\u3002",
|
|
2780
|
+
refuse_audit_strict: "\u5DF2\u62D2\u7D55\uFF1A\u9019\u500B\u8ACB\u6C42\u7684\u7A3D\u6838\u7D00\u9304\u5BEB\u4E0D\u51FA\u53BB\uFF0C\u800C audit.strict \u662F\u958B\u7684\u3002\u8ACB\u4FEE\u597D\u7A3D\u6838\u8F38\u51FA\u7AEF\uFF08\u78C1\u789F\u7A7A\u9593\u3001\u76EE\u9304\u6B0A\u9650\u3001.dbcli/audit \u88E1\u6B98\u7559\u7684 lockfile\uFF09\uFF0C\u6216\u95DC\u6389 audit.strict \u4EE5\u63A5\u53D7\u6C92\u6709\u7D00\u9304\u7684\u8ACB\u6C42\u3002",
|
|
2781
|
+
blacklist_index: "\u7D22\u5F15 '{index}' \u53D7\u9ED1\u540D\u55AE\u4FDD\u8B77",
|
|
2782
|
+
blacklist_unscoped: "'{path}' \u6307\u4E0D\u51FA\u4EFB\u4F55\u7D22\u5F15\uFF0C\u56E0\u6B64\u7121\u6CD5\u5C0D\u7167\u9ED1\u540D\u55AE\u6AA2\u67E5\u3002\u8ACB\u628A\u8ACB\u6C42\u9650\u5B9A\u5230\u67D0\u500B\u7D22\u5F15\uFF0C\u4F8B\u5982 GET /<index>/_search\u3002",
|
|
2783
|
+
blacklist_field: "\u6B04\u4F4D '{field}' \u53D7\u9ED1\u540D\u55AE\u4FDD\u8B77\uFF0C\u4E0D\u80FD\u51FA\u73FE\u5728\u8ACB\u6C42\u88E1\u2014\u2014\u5C0D\u5B83\u6392\u5E8F\u3001\u805A\u5408\u6216\u8DD1 script \u90FD\u6703\u628A\u5B83\u7684\u503C\u53D6\u56DE\u4F86\u3002",
|
|
2784
|
+
banner_title: "Elasticsearch shell \u2014\u2014 Kibana Dev Tools \u8A9E\u6CD5",
|
|
2785
|
+
banner_hint: '\u8F38\u5165 "<METHOD> /<path>"\uFF0C\u63A5\u8457\u5BEB\u53EF\u9078\u7684 JSON body\uFF1B\u7A7A\u4E00\u884C\u9001\u51FA\u3002\u53EF\u4EE5\u5148\u8A66\uFF1AGET /_cat/indices',
|
|
2786
|
+
banner_keys: 'Ctrl+C \u53D6\u6D88\u76EE\u524D\u9019\u500B block\uFF1BCtrl+D \u6216 "exit" \u96E2\u958B\u3002',
|
|
2787
|
+
block_cancelled: "\uFF08block \u5DF2\u53D6\u6D88\uFF09",
|
|
2788
|
+
goodbye: "\u518D\u898B"
|
|
2789
|
+
}
|
|
2790
|
+
};
|
|
2791
|
+
});
|
|
2792
|
+
|
|
2793
|
+
// resources/lang/en/ceremony.json
|
|
2794
|
+
var ceremony_default;
|
|
2795
|
+
var init_ceremony = __esm(() => {
|
|
2796
|
+
ceremony_default = {
|
|
2797
|
+
updated: "Updated {count} row(s) in {table}",
|
|
2798
|
+
deleted: "Deleted {count} row(s) from {table}",
|
|
2799
|
+
inserted: "Inserted {count} row(s) into {table}",
|
|
2800
|
+
matched_nothing: "No rows matched in {table} \u2014 nothing changed",
|
|
2801
|
+
cancelled: "Cancelled. {table} was not changed",
|
|
2802
|
+
dry_run: "Preview only. {table} was not changed",
|
|
2803
|
+
elapsed: "Took {seconds}s",
|
|
2804
|
+
failed: "Failed: {reason}",
|
|
2805
|
+
unknown_error: "unknown error",
|
|
2806
|
+
confirm_sql: "Generated SQL:",
|
|
2807
|
+
confirm_command: "Command to run:",
|
|
2808
|
+
confirm_params: "Parameters:",
|
|
2809
|
+
confirm_destructive_warning: "\u26A0\uFE0F Warning: DELETE operation is destructive and cannot be undone!",
|
|
2810
|
+
confirm_destructive_prompt: "Are you sure you want to execute this DELETE operation? This cannot be undone.",
|
|
2811
|
+
confirm_destructive_warning_ddl: "\u26A0\uFE0F Warning: this schema change is destructive and cannot be undone!",
|
|
2812
|
+
confirm_ddl_prompt: "Are you sure you want to run this {operation}? This cannot be undone.",
|
|
2813
|
+
confirm_prompt: "Proceed with this operation?",
|
|
2814
|
+
gate_summary: "This will {operation} rows in {table}.",
|
|
2815
|
+
gate_summary_untargeted: "This will run a {operation} statement.",
|
|
2816
|
+
gate_statement: "Statement:",
|
|
2817
|
+
gate_cancelled: "Cancelled. Nothing was run.",
|
|
2818
|
+
gate_full_table_warning: "\u26A0\uFE0F This affects EVERY row in {table} and cannot be undone.",
|
|
2819
|
+
gate_ddl_warning: "\u26A0\uFE0F This destroys {table} and cannot be undone.",
|
|
2820
|
+
gate_ddl_warning_untargeted: "\u26A0\uFE0F This is a destructive schema change and cannot be undone.",
|
|
2821
|
+
gate_unparseable_warning: "\u26A0\uFE0F dbcli could not parse this statement, so it is treated as affecting everything.",
|
|
2822
|
+
gate_multiple_statements_warning: "\u26A0\uFE0F This is more than one statement, and dbcli will not judge them one at a time.",
|
|
2823
|
+
gate_multi_table_warning: "This write joins another table. How many rows of the target it touches depends on the data, not on the statement, so it cannot be checked before it runs.",
|
|
2824
|
+
gate_nested_write_warning: "\u26A0\uFE0F This statement carries a second write inside it, and dbcli will not read the tier of that write off this one.",
|
|
2825
|
+
gate_typed_prompt: "Type {phrase} to run it",
|
|
2826
|
+
gate_typed_mismatch: "That did not match {phrase}. Nothing was run.",
|
|
2827
|
+
gate_refused: "Refused: this statement is not limited to specific rows and nobody is here to confirm it (reason={reason}).",
|
|
2828
|
+
gate_refused_in_shell: "Refused: a dbcli subcommand started from the shell has no way to ask you, even though you are right here (reason={reason}).",
|
|
2829
|
+
gate_remedy_no_where: "Add a WHERE clause \u2014 WHERE 1=1 if you really do mean every row \u2014 or a LIMIT, then run it again.",
|
|
2830
|
+
gate_remedy_ddl_destruction: "No flag skips this. Run it from an interactive terminal, or apply the schema change through a reviewed migration.",
|
|
2831
|
+
gate_remedy_unparseable: "dbcli could not parse this statement and will not guess. Simplify it, or run it from an interactive terminal.",
|
|
2832
|
+
gate_remedy_multiple_statements: "Run one statement per command.",
|
|
2833
|
+
gate_remedy_multi_table: "Rewrite it as a single-table statement \u2014 move the other table into a subquery in the WHERE \u2014 or run it where someone can confirm it.",
|
|
2834
|
+
gate_remedy_nested_write: "Run the nested write as its own statement, or run this one where someone can confirm it.",
|
|
2835
|
+
gate_remedy_non_unique_where: "Match on a primary key or a uniquely indexed column, or run it from an interactive terminal.",
|
|
2836
|
+
gate_remedy_in_shell: "Type the statement at the dbcli> prompt instead \u2014 there the shell asks you to type the target table name.",
|
|
2837
|
+
recovery_none: "This cannot be undone automatically \u2014 restore from a backup to get the previous values back.",
|
|
2838
|
+
recovery_retry: "Re-run with --recovery to write a recovery plan, then read it with: dbcli recover",
|
|
2839
|
+
blacklist_hint: "Run `dbcli blacklist list` to see what is protected"
|
|
2840
|
+
};
|
|
2841
|
+
});
|
|
2842
|
+
|
|
2843
|
+
// resources/lang/zh-TW/ceremony.json
|
|
2844
|
+
var ceremony_default2;
|
|
2845
|
+
var init_ceremony2 = __esm(() => {
|
|
2846
|
+
ceremony_default2 = {
|
|
2847
|
+
updated: "\u5DF2\u66F4\u65B0 {table} \u7684 {count} \u5217",
|
|
2848
|
+
deleted: "\u5DF2\u5F9E {table} \u522A\u9664 {count} \u5217",
|
|
2849
|
+
inserted: "\u5DF2\u5BEB\u5165 {count} \u5217\u5230 {table}",
|
|
2850
|
+
matched_nothing: "{table} \u6C92\u6709\u4EFB\u4F55\u5217\u7B26\u5408\u689D\u4EF6\uFF0C\u8CC7\u6599\u672A\u8B8A\u52D5",
|
|
2851
|
+
cancelled: "\u5DF2\u53D6\u6D88\uFF0C{table} \u672A\u8B8A\u52D5",
|
|
2852
|
+
dry_run: "\u50C5\u9810\u89BD\uFF0C{table} \u672A\u8B8A\u52D5",
|
|
2853
|
+
elapsed: "\u8017\u6642 {seconds} \u79D2",
|
|
2854
|
+
failed: "\u5931\u6557\uFF1A{reason}",
|
|
2855
|
+
unknown_error: "\u672A\u77E5\u7684\u932F\u8AA4",
|
|
2856
|
+
confirm_sql: "\u7522\u751F\u7684 SQL\uFF1A",
|
|
2857
|
+
confirm_command: "\u5373\u5C07\u57F7\u884C\u7684\u6307\u4EE4\uFF1A",
|
|
2858
|
+
confirm_params: "\u53C3\u6578\uFF1A",
|
|
2859
|
+
confirm_destructive_warning: "\u26A0\uFE0F \u8B66\u544A\uFF1ADELETE \u5177\u7834\u58DE\u6027\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\uFF01",
|
|
2860
|
+
confirm_destructive_prompt: "\u78BA\u5B9A\u8981\u57F7\u884C\u9019\u500B DELETE \u55CE\uFF1F\u6B64\u64CD\u4F5C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
2861
|
+
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",
|
|
2862
|
+
confirm_ddl_prompt: "\u78BA\u5B9A\u8981\u57F7\u884C\u9019\u500B {operation} \u55CE\uFF1F\u6B64\u64CD\u4F5C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
2863
|
+
confirm_prompt: "\u8981\u7E7C\u7E8C\u57F7\u884C\u9019\u500B\u64CD\u4F5C\u55CE\uFF1F",
|
|
2864
|
+
gate_summary: "\u9019\u53E5\u6703\u5C0D {table} \u57F7\u884C {operation}\u3002",
|
|
2865
|
+
gate_summary_untargeted: "\u9019\u53E5\u6703\u57F7\u884C {operation}\u3002",
|
|
2866
|
+
gate_statement: "\u8A9E\u53E5\uFF1A",
|
|
2867
|
+
gate_cancelled: "\u5DF2\u53D6\u6D88\uFF0C\u6C92\u6709\u57F7\u884C\u4EFB\u4F55\u8A9E\u53E5\u3002",
|
|
2868
|
+
gate_full_table_warning: "\u26A0\uFE0F \u9019\u53E5\u6703\u5F71\u97FF {table} \u7684\u6BCF\u4E00\u5217\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
2869
|
+
gate_ddl_warning: "\u26A0\uFE0F \u9019\u53E5\u6703\u6467\u6BC0 {table}\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
2870
|
+
gate_ddl_warning_untargeted: "\u26A0\uFE0F \u9019\u662F\u7834\u58DE\u6027\u7684 schema \u8B8A\u66F4\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
2871
|
+
gate_unparseable_warning: "\u26A0\uFE0F dbcli \u7121\u6CD5\u89E3\u6790\u9019\u53E5\uFF0C\u56E0\u6B64\u4E00\u5F8B\u8996\u70BA\u6703\u5F71\u97FF\u5168\u90E8\u8CC7\u6599\u3002",
|
|
2872
|
+
gate_multiple_statements_warning: "\u26A0\uFE0F \u9019\u88E1\u4E0D\u53EA\u4E00\u53E5\u8A9E\u53E5\uFF0Cdbcli \u4E0D\u6703\u9010\u53E5\u5224\u65B7\u3002",
|
|
2873
|
+
gate_multi_table_warning: "\u9019\u500B\u5BEB\u5165\u63A5\u4E86\u53E6\u4E00\u5F35\u8868\u3002\u5B83\u6703\u52D5\u5230\u76EE\u6A19\u8868\u5E7E\u5217\u53D6\u6C7A\u65BC\u8CC7\u6599\u800C\u4E0D\u662F\u8A9E\u53E5\u672C\u8EAB\uFF0C\u57F7\u884C\u524D\u7121\u6CD5\u6AA2\u67E5\u3002",
|
|
2874
|
+
gate_nested_write_warning: "\u26A0\uFE0F \u9019\u53E5\u88E1\u9762\u9084\u593E\u8457\u53E6\u4E00\u500B\u5BEB\u5165\uFF0Cdbcli \u4E0D\u6703\u62FF\u9019\u53E5\u7684\u5C64\u7D1A\u4EE3\u66FF\u90A3\u500B\u5BEB\u5165\u7684\u5C64\u7D1A\u3002",
|
|
2875
|
+
gate_typed_prompt: "\u8F38\u5165 {phrase} \u624D\u6703\u57F7\u884C",
|
|
2876
|
+
gate_typed_mismatch: "\u8F38\u5165\u7684\u5167\u5BB9\u8207 {phrase} \u4E0D\u7B26\uFF0C\u6C92\u6709\u57F7\u884C\u4EFB\u4F55\u8A9E\u53E5\u3002",
|
|
2877
|
+
gate_refused: "\u5DF2\u62D2\u7D55\uFF1A\u9019\u53E5\u6C92\u6709\u9650\u5B9A\u8981\u52D5\u54EA\u4E9B\u5217\uFF0C\u800C\u73FE\u5728\u6C92\u6709\u4EBA\u80FD\u78BA\u8A8D\uFF08reason={reason}\uFF09\u3002",
|
|
2878
|
+
gate_refused_in_shell: "\u5DF2\u62D2\u7D55\uFF1A\u5F9E shell \u555F\u52D5\u7684 dbcli \u5B50\u6307\u4EE4\u6C92\u6709\u8FA6\u6CD5\u5411\u4F60\u63D0\u554F\uFF0C\u5373\u4F7F\u4F60\u4EBA\u5C31\u5728\u9019\u88E1\uFF08reason={reason}\uFF09\u3002",
|
|
2879
|
+
gate_remedy_no_where: "\u88DC\u4E0A WHERE\uFF08\u771F\u7684\u8981\u5168\u8868\u5C31\u5BEB WHERE 1=1\uFF09\u6216 LIMIT\uFF0C\u7136\u5F8C\u91CD\u8DD1\u3002",
|
|
2880
|
+
gate_remedy_ddl_destruction: "\u6C92\u6709\u4EFB\u4F55\u65D7\u6A19\u53EF\u4EE5\u8DF3\u904E\u9019\u4E00\u95DC\u3002\u8ACB\u5728\u4E92\u52D5\u7D42\u7AEF\u4E0B\u57F7\u884C\uFF0C\u6216\u6539\u8D70\u7D93\u904E\u5BE9\u67E5\u7684 migration\u3002",
|
|
2881
|
+
gate_remedy_unparseable: "dbcli \u7121\u6CD5\u89E3\u6790\u9019\u53E5\uFF0C\u4E5F\u4E0D\u6703\u7528\u731C\u7684\u3002\u8ACB\u7C21\u5316\u8A9E\u53E5\uFF0C\u6216\u5728\u4E92\u52D5\u7D42\u7AEF\u4E0B\u57F7\u884C\u3002",
|
|
2882
|
+
gate_remedy_multiple_statements: "\u4E00\u6B21\u53EA\u57F7\u884C\u4E00\u53E5\u8A9E\u53E5\u3002",
|
|
2883
|
+
gate_remedy_multi_table: "\u6539\u5BEB\u6210\u55AE\u8868\u8A9E\u53E5\u2014\u2014\u628A\u53E6\u4E00\u5F35\u8868\u79FB\u9032 WHERE \u7684\u5B50\u67E5\u8A62\u88E1\u2014\u2014\u6216\u6539\u5728\u6709\u4EBA\u53EF\u4EE5\u78BA\u8A8D\u7684\u5730\u65B9\u57F7\u884C\u3002",
|
|
2884
|
+
gate_remedy_nested_write: "\u628A\u88E1\u9762\u90A3\u500B\u5BEB\u5165\u62C6\u6210\u7368\u7ACB\u7684\u4E00\u53E5\u4F86\u57F7\u884C\uFF0C\u6216\u6539\u5728\u6709\u4EBA\u53EF\u4EE5\u78BA\u8A8D\u7684\u5730\u65B9\u57F7\u884C\u3002",
|
|
2885
|
+
gate_remedy_non_unique_where: "\u6539\u7528\u4E3B\u9375\u6216\u552F\u4E00\u7D22\u5F15\u6B04\u4F4D\u7576\u689D\u4EF6\uFF0C\u6216\u5728\u4E92\u52D5\u7D42\u7AEF\u4E0B\u57F7\u884C\u3002",
|
|
2886
|
+
gate_remedy_in_shell: "\u8ACB\u76F4\u63A5\u5728 dbcli> \u63D0\u793A\u7B26\u6253\u9019\u53E5\u8A9E\u53E5\u2014\u2014\u5728\u90A3\u88E1 dbcli \u6703\u8ACB\u4F60\u6253\u51FA\u76EE\u6A19\u8CC7\u6599\u8868\u7684\u540D\u7A31\u4F86\u78BA\u8A8D\u3002",
|
|
2887
|
+
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",
|
|
2888
|
+
recovery_retry: "\u52A0\u4E0A --recovery \u91CD\u8DD1\u53EF\u7522\u751F\u56DE\u5FA9\u8A08\u756B\uFF0C\u518D\u7528 dbcli recover \u8B80\u53D6",
|
|
2889
|
+
blacklist_hint: "\u57F7\u884C `dbcli blacklist list` \u67E5\u770B\u53D7\u4FDD\u8B77\u7684\u9805\u76EE"
|
|
2890
|
+
};
|
|
2891
|
+
});
|
|
2892
|
+
|
|
2893
|
+
// src/i18n/message-loader.ts
|
|
2894
|
+
class MessageLoader {
|
|
2895
|
+
static instance = null;
|
|
2896
|
+
messages = {};
|
|
2897
|
+
fallbackMessages = {};
|
|
2898
|
+
currentLang;
|
|
2899
|
+
constructor() {
|
|
2900
|
+
this.currentLang = Bun.env.DBCLI_LANG || "en", this.loadMessages();
|
|
2901
|
+
}
|
|
2902
|
+
static getInstance() {
|
|
2903
|
+
if (!MessageLoader.instance)
|
|
2904
|
+
MessageLoader.instance = new MessageLoader;
|
|
2905
|
+
return MessageLoader.instance;
|
|
2906
|
+
}
|
|
2907
|
+
loadMessages() {
|
|
2908
|
+
if (this.currentLang !== "en")
|
|
2909
|
+
this.messages = BUNDLED_MESSAGES[this.currentLang] || {};
|
|
2910
|
+
this.fallbackMessages = BUNDLED_MESSAGES.en || {};
|
|
2911
|
+
}
|
|
2912
|
+
t(key) {
|
|
2913
|
+
let parts = key.split("."), value = this.messages;
|
|
2914
|
+
for (let part of parts)
|
|
2915
|
+
if (value && typeof value === "object")
|
|
2916
|
+
value = value[part];
|
|
2917
|
+
else {
|
|
2918
|
+
value = void 0;
|
|
2919
|
+
break;
|
|
2920
|
+
}
|
|
2921
|
+
if (typeof value === "string")
|
|
2922
|
+
return value;
|
|
2923
|
+
value = this.fallbackMessages;
|
|
2924
|
+
for (let part of parts)
|
|
2925
|
+
if (value && typeof value === "object")
|
|
2926
|
+
value = value[part];
|
|
2927
|
+
else {
|
|
2928
|
+
value = void 0;
|
|
2929
|
+
break;
|
|
2930
|
+
}
|
|
2931
|
+
if (typeof value === "string")
|
|
2932
|
+
return value;
|
|
2933
|
+
return key;
|
|
2934
|
+
}
|
|
2935
|
+
setLanguage(lang) {
|
|
2936
|
+
this.currentLang = lang, this.messages = {}, this.loadMessages();
|
|
2937
|
+
}
|
|
2938
|
+
interpolate(key, vars) {
|
|
2939
|
+
return this.t(key).replace(/\{([^{}]+)\}/g, (match, name) => Object.prototype.hasOwnProperty.call(vars, name) ? String(vars[name]) : match);
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
var BUNDLED_MESSAGES, messageLoader, t = (key) => messageLoader.t(key), t_vars = (key, vars) => messageLoader.interpolate(key, vars);
|
|
2943
|
+
var init_message_loader = __esm(() => {
|
|
2944
|
+
init_messages();
|
|
2945
|
+
init_messages2();
|
|
2946
|
+
init_shell();
|
|
2947
|
+
init_shell2();
|
|
2948
|
+
init_ceremony();
|
|
2949
|
+
init_ceremony2();
|
|
2950
|
+
BUNDLED_MESSAGES = {
|
|
2951
|
+
en: { ...messages_default, shell: shell_default, ceremony: ceremony_default },
|
|
2952
|
+
"zh-TW": {
|
|
2953
|
+
...messages_default2,
|
|
2954
|
+
shell: shell_default2,
|
|
2955
|
+
ceremony: ceremony_default2
|
|
2956
|
+
}
|
|
2957
|
+
};
|
|
2958
|
+
messageLoader = MessageLoader.getInstance();
|
|
2959
|
+
});
|
|
2960
|
+
|
|
1938
2961
|
// node_modules/zod/v3/helpers/util.js
|
|
1939
2962
|
var util, objectUtil, ZodParsedType, getParsedType = (data) => {
|
|
1940
2963
|
switch (typeof data) {
|
|
@@ -5119,7 +6142,261 @@ var init_zod = __esm(() => {
|
|
|
5119
6142
|
init_external();
|
|
5120
6143
|
});
|
|
5121
6144
|
|
|
6145
|
+
// src/adapters/capabilities.ts
|
|
6146
|
+
function cap(status, tier, note) {
|
|
6147
|
+
return Object.freeze({ status, tier, note });
|
|
6148
|
+
}
|
|
6149
|
+
function getEngineCapabilities(system) {
|
|
6150
|
+
return Object.freeze({ ...ENGINE_CAPABILITIES[system] });
|
|
6151
|
+
}
|
|
6152
|
+
function getEngineCapability(system, key) {
|
|
6153
|
+
return ENGINE_CAPABILITIES[system][key];
|
|
6154
|
+
}
|
|
6155
|
+
function supportsCapability(system, key) {
|
|
6156
|
+
return getEngineCapability(system, key).status === "supported";
|
|
6157
|
+
}
|
|
6158
|
+
function supportsSqlTls(system) {
|
|
6159
|
+
return typeof system === "string" && Object.hasOwn(ENGINE_CAPABILITIES, system) && supportsCapability(system, "connectionTls");
|
|
6160
|
+
}
|
|
6161
|
+
var COMMAND_CAPABILITY_KEYS, ENGINE_INDEPENDENT, SQL_BASE, SQL_ONLY_UNSUPPORTED, SQL_MODE_LIMITED, VERIFIED_SQL_TLS, ENGINE_CAPABILITIES;
|
|
6162
|
+
var init_capabilities = __esm(() => {
|
|
6163
|
+
COMMAND_CAPABILITY_KEYS = Object.freeze([
|
|
6164
|
+
"connectionTls",
|
|
6165
|
+
"init",
|
|
6166
|
+
"use",
|
|
6167
|
+
"list",
|
|
6168
|
+
"schema",
|
|
6169
|
+
"schemaSingle",
|
|
6170
|
+
"schemaFullScan",
|
|
6171
|
+
"query",
|
|
6172
|
+
"lint",
|
|
6173
|
+
"queryOutput",
|
|
6174
|
+
"queryLimitGuard",
|
|
6175
|
+
"q",
|
|
6176
|
+
"queries",
|
|
6177
|
+
"insert",
|
|
6178
|
+
"update",
|
|
6179
|
+
"delete",
|
|
6180
|
+
"export",
|
|
6181
|
+
"blacklistRead",
|
|
6182
|
+
"blacklist",
|
|
6183
|
+
"check",
|
|
6184
|
+
"diff",
|
|
6185
|
+
"migrate",
|
|
6186
|
+
"shell",
|
|
6187
|
+
"status",
|
|
6188
|
+
"doctor",
|
|
6189
|
+
"inspect",
|
|
6190
|
+
"report",
|
|
6191
|
+
"guide",
|
|
6192
|
+
"completion",
|
|
6193
|
+
"upgrade",
|
|
6194
|
+
"recover",
|
|
6195
|
+
"skill",
|
|
6196
|
+
"auditTail",
|
|
6197
|
+
"auditShow",
|
|
6198
|
+
"auditClear",
|
|
6199
|
+
"auditHealth",
|
|
6200
|
+
"explain",
|
|
6201
|
+
"plan",
|
|
6202
|
+
"impactAssess",
|
|
6203
|
+
"assert",
|
|
6204
|
+
"snapshot",
|
|
6205
|
+
"verify",
|
|
6206
|
+
"semantic",
|
|
6207
|
+
"design",
|
|
6208
|
+
"proxy",
|
|
6209
|
+
"proxyAnalyze",
|
|
6210
|
+
"verification",
|
|
6211
|
+
"verificationPrune",
|
|
6212
|
+
"evidence",
|
|
6213
|
+
"contract",
|
|
6214
|
+
"recovery",
|
|
6215
|
+
"backfillArtifact",
|
|
6216
|
+
"password",
|
|
6217
|
+
"capabilityDiscover",
|
|
6218
|
+
"capabilityCheck"
|
|
6219
|
+
]);
|
|
6220
|
+
ENGINE_INDEPENDENT = {
|
|
6221
|
+
completion: cap("not-applicable", "none", "Shell completion is engine-independent."),
|
|
6222
|
+
upgrade: cap("not-applicable", "local-write", "Package update checks are engine-independent."),
|
|
6223
|
+
recover: cap("not-applicable", "dry-run", "Recovery operates on saved envelopes and gated command steps."),
|
|
6224
|
+
skill: cap("not-applicable", "local-write", "Skill and task-pack generation are engine-independent."),
|
|
6225
|
+
auditTail: cap("supported", "readonly", "Reads JSONL audit entries; never writes to engines."),
|
|
6226
|
+
auditShow: cap("supported", "readonly", "Looks up a single audit entry by id prefix or recovery_ref."),
|
|
6227
|
+
auditHealth: cap("supported", "readonly", "Renders AuditLogger.getHealth() snapshot."),
|
|
6228
|
+
auditClear: cap("supported", "local-write", "Removes <conn>.jsonl + .jsonl.1 from local disk; never touches DB.")
|
|
6229
|
+
}, SQL_BASE = {
|
|
6230
|
+
connectionTls: cap("unsupported", "none", "Explicit verified SQL TLS is not supported by this engine."),
|
|
6231
|
+
init: cap("supported", "interactive", "SQL connection initialization is supported."),
|
|
6232
|
+
use: cap("supported", "local-write", "V2 config supports named SQL connections."),
|
|
6233
|
+
list: cap("supported", "readonly", "Lists relational tables."),
|
|
6234
|
+
schema: cap("supported", "readonly", "Unified schema command (single-table or full scan)."),
|
|
6235
|
+
schemaSingle: cap("supported", "readonly", "Reads a single table schema."),
|
|
6236
|
+
schemaFullScan: cap("supported", "readonly", "Full scan, refresh, and reset are supported."),
|
|
6237
|
+
query: cap("supported", "readonly", "Runs SQL through permission and blacklist guards."),
|
|
6238
|
+
lint: cap("supported", "readonly", "Statically analyzes SQL without connecting to the database."),
|
|
6239
|
+
queryOutput: cap("supported", "readonly", "table/json/csv output is supported."),
|
|
6240
|
+
queryLimitGuard: cap("supported", "readonly", "Query-only auto-limit and size guard are supported."),
|
|
6241
|
+
q: cap("supported", "readonly", "Saved SQL snippets allow SELECT/WITH only."),
|
|
6242
|
+
queries: cap("supported", "local-write", "Snippet management is available regardless of active connection."),
|
|
6243
|
+
insert: cap("supported", "db-write", "Dedicated INSERT command is supported."),
|
|
6244
|
+
update: cap("supported", "db-write", "Dedicated UPDATE command is supported."),
|
|
6245
|
+
delete: cap("supported", "db-write", "Dedicated DELETE command is supported."),
|
|
6246
|
+
export: cap("supported", "readonly", "SQL export is supported."),
|
|
6247
|
+
blacklistRead: cap("supported", "readonly", "Reads the configured blacklist rules."),
|
|
6248
|
+
blacklist: cap("supported", "local-write", "Blacklist management and enforcement are supported."),
|
|
6249
|
+
check: cap("supported", "readonly", "Data health checks are SQL-only."),
|
|
6250
|
+
diff: cap("supported", "readonly", "Relational schema snapshots are supported."),
|
|
6251
|
+
migrate: cap("supported", "db-write", "DDL migrations are supported for SQL engines."),
|
|
6252
|
+
shell: cap("supported", "interactive", "Interactive SQL shell is supported."),
|
|
6253
|
+
status: cap("supported", "readonly", "Safe config summary is supported."),
|
|
6254
|
+
doctor: cap("supported", "readonly", "Engine-specific diagnostics are supported."),
|
|
6255
|
+
inspect: cap("supported", "readonly", "Agent context snapshot is supported."),
|
|
6256
|
+
report: cap("supported", "readonly", "Diagnostic report is supported."),
|
|
6257
|
+
guide: cap("supported", "readonly", "Deterministic next-command planner is supported."),
|
|
6258
|
+
explain: cap("supported", "readonly", "EXPLAIN plans are read from SQL engines."),
|
|
6259
|
+
plan: cap("supported", "readonly", "Offline write-risk analysis of SQL against the schema cache."),
|
|
6260
|
+
impactAssess: cap("supported", "local-write", "Offline impact assessment of a SQL schema change; writes its report locally."),
|
|
6261
|
+
assert: cap("supported", "local-write", "Data assertions over SQL results; optionally writes a verification artifact."),
|
|
6262
|
+
snapshot: cap("supported", "local-write", "Captures a SQL result fingerprint under .dbcli/snapshots/."),
|
|
6263
|
+
verify: cap("supported", "local-write", "Scenario verification over SQL; writes a verification artifact."),
|
|
6264
|
+
semantic: cap("supported", "readonly", "Governed semantic context and drift over a SQL schema cache."),
|
|
6265
|
+
design: cap("supported", "local-write", "Schema design authoring and comparison for SQL dialects; init writes a template."),
|
|
6266
|
+
proxy: cap("supported", "interactive", "Local observing proxy for the MySQL, MariaDB and PostgreSQL wire protocols."),
|
|
6267
|
+
password: cap("supported", "interactive", "Rotates the stored connection credential; refused under agent mode."),
|
|
6268
|
+
proxyAnalyze: cap("not-applicable", "readonly", "Reads locally recorded proxy events; never contacts an engine."),
|
|
6269
|
+
verification: cap("not-applicable", "readonly", "Inspects verification artifacts under .dbcli/verification/."),
|
|
6270
|
+
verificationPrune: cap("not-applicable", "local-write", "Deletes local verification artifacts; dry-run unless --execute --force."),
|
|
6271
|
+
evidence: cap("not-applicable", "local-write", "Composes, validates and renders evidence packs from local artifacts."),
|
|
6272
|
+
contract: cap("not-applicable", "readonly", "Validates and inspects governed semantic contracts offline."),
|
|
6273
|
+
recovery: cap("not-applicable", "readonly", "Renders recovery codes and their remediation steps from a static table."),
|
|
6274
|
+
backfillArtifact: cap("not-applicable", "local-write", "Builds a backfill artifact from a source manifest and v2 connection identity."),
|
|
6275
|
+
capabilityDiscover: cap("not-applicable", "readonly", "Emits the static capability catalog; opens no connection."),
|
|
6276
|
+
capabilityCheck: cap("not-applicable", "readonly", "Evaluates required capability ids against the local configuration only."),
|
|
6277
|
+
...ENGINE_INDEPENDENT
|
|
6278
|
+
}, SQL_ONLY_UNSUPPORTED = {
|
|
6279
|
+
explain: cap("unsupported", "none", "src/commands/explain.ts refuses a non-SQL connection."),
|
|
6280
|
+
plan: cap("unsupported", "none", "Risk analysis is SQL-dialect based; toSqlDialect resolves no dialect here."),
|
|
6281
|
+
assert: cap("unsupported", "none", "src/commands/assert.ts refuses a non-SQL connection."),
|
|
6282
|
+
snapshot: cap("unsupported", "none", "src/commands/snapshot.ts refuses a non-SQL connection."),
|
|
6283
|
+
verify: cap("unsupported", "none", "src/commands/verify.ts refuses a non-SQL connection."),
|
|
6284
|
+
proxy: cap("unsupported", "none", "The proxy speaks the MySQL and PostgreSQL wire protocols only.")
|
|
6285
|
+
}, SQL_MODE_LIMITED = {
|
|
6286
|
+
impactAssess: cap("limited", "local-write", "Runs against an ORM artifact; --against-cache needs a SQL connection."),
|
|
6287
|
+
semantic: cap("limited", "readonly", "Governed context, search and drift work; draft validate needs a SQL connection."),
|
|
6288
|
+
design: cap("limited", "local-write", "Authoring and ORM comparison work; --against-cache needs a SQL connection.")
|
|
6289
|
+
}, VERIFIED_SQL_TLS = cap("supported", "readonly", "Uses profile-scoped verified SQL TLS; availability does not validate CA or handshake."), ENGINE_CAPABILITIES = Object.freeze({
|
|
6290
|
+
postgresql: Object.freeze({
|
|
6291
|
+
...SQL_BASE,
|
|
6292
|
+
connectionTls: VERIFIED_SQL_TLS,
|
|
6293
|
+
check: cap("limited", "readonly", "SQL-only and strongest on MySQL/MariaDB.")
|
|
6294
|
+
}),
|
|
6295
|
+
sqlite: Object.freeze({
|
|
6296
|
+
...SQL_BASE,
|
|
6297
|
+
init: cap("supported", "local-write", "Asks for a file path and a permission; the file must already exist."),
|
|
6298
|
+
use: cap("supported", "local-write", "Switches connections and displays the file path."),
|
|
6299
|
+
schemaFullScan: cap("unsupported", "none", "Full schema cache scan is not implemented for SQLite."),
|
|
6300
|
+
lint: cap("unsupported", "none", "Static lint is not enabled for the SQLite dialect yet."),
|
|
6301
|
+
queries: cap("supported", "local-write", "Snippets may declare `engine: sqlite` and bind with `?` placeholders."),
|
|
6302
|
+
insert: cap("supported", "db-write", "Row insert through the shared data-executor path."),
|
|
6303
|
+
update: cap("supported", "db-write", "Row update with a mandatory --where clause."),
|
|
6304
|
+
delete: cap("supported", "db-write", "Row delete with a mandatory --where clause."),
|
|
6305
|
+
export: cap("supported", "local-write", "Exports SELECT results to json, csv or html through the shared query path."),
|
|
6306
|
+
check: cap("unsupported", "none", "Data health check is not enabled for SQLite."),
|
|
6307
|
+
diff: cap("unsupported", "none", "Schema snapshots are not enabled for SQLite."),
|
|
6308
|
+
migrate: cap("unsupported", "none", "SQLite ALTER TABLE covers few operations and a column change is a table rebuild; no DDL generator exists."),
|
|
6309
|
+
shell: cap("unsupported", "none", "The REPL is not enabled for SQLite."),
|
|
6310
|
+
doctor: cap("supported", "readonly", "Checks the file exists, is readable, and is writable when the permission allows writes."),
|
|
6311
|
+
inspect: cap("unsupported", "none", "Inspection collectors are not enabled for SQLite."),
|
|
6312
|
+
report: cap("unsupported", "none", "Report diagnostics query engine-specific statistics views."),
|
|
6313
|
+
guide: cap("unsupported", "none", "Guides depend on engine-specific EXPLAIN output."),
|
|
6314
|
+
explain: cap("unsupported", "none", "EXPLAIN handling is not enabled for SQLite."),
|
|
6315
|
+
plan: cap("unsupported", "none", "Plan capture is not enabled for SQLite."),
|
|
6316
|
+
impactAssess: cap("unsupported", "none", "Impact assessment is not enabled for SQLite."),
|
|
6317
|
+
assert: cap("unsupported", "none", "Assertions are not enabled for SQLite."),
|
|
6318
|
+
snapshot: cap("unsupported", "none", "Result snapshots are not enabled for SQLite."),
|
|
6319
|
+
verify: cap("unsupported", "none", "Write verification is not enabled for SQLite."),
|
|
6320
|
+
semantic: cap("unsupported", "none", "Semantic context work is not enabled for SQLite."),
|
|
6321
|
+
design: cap("unsupported", "none", "Schema design work is not enabled for SQLite."),
|
|
6322
|
+
proxy: cap("not-applicable", "none", "SQLite has no network protocol to proxy."),
|
|
6323
|
+
proxyAnalyze: cap("not-applicable", "none", "There are no SQLite proxy events to analyse."),
|
|
6324
|
+
password: cap("not-applicable", "none", "A SQLite connection has no credential to store or rotate."),
|
|
6325
|
+
queryLimitGuard: cap("limited", "readonly", "Applies the same LIMIT rewrite as the other SQL engines; under query-only the handle is opened read-only, which a database with an unreplayed WAL cannot satisfy.")
|
|
6326
|
+
}),
|
|
6327
|
+
mysql: Object.freeze({ ...SQL_BASE, connectionTls: VERIFIED_SQL_TLS }),
|
|
6328
|
+
mariadb: Object.freeze({ ...SQL_BASE, connectionTls: VERIFIED_SQL_TLS }),
|
|
6329
|
+
mongodb: Object.freeze({
|
|
6330
|
+
...SQL_BASE,
|
|
6331
|
+
...SQL_ONLY_UNSUPPORTED,
|
|
6332
|
+
...SQL_MODE_LIMITED,
|
|
6333
|
+
...SQL_MODE_LIMITED,
|
|
6334
|
+
...SQL_MODE_LIMITED,
|
|
6335
|
+
lint: cap("unsupported", "none", "Static lint accepts SQL connections only."),
|
|
6336
|
+
schemaSingle: cap("limited", "readonly", "MongoDB schema is sampled from documents."),
|
|
6337
|
+
schemaFullScan: cap("limited", "readonly", "Full scan is sampled and document-oriented."),
|
|
6338
|
+
query: cap("limited", "readonly", "Uses JSON filter or aggregation syntax, not SQL."),
|
|
6339
|
+
queryLimitGuard: cap("limited", "readonly", "Applies result limits and collection size guard."),
|
|
6340
|
+
queries: cap("limited", "local-write", "Snippet management works with MongoDB-specific saved-query limitations."),
|
|
6341
|
+
q: cap("limited", "readonly", "Saved MongoDB snippets require collection frontmatter, accept JSON find/aggregate bodies, and JSON-encode parameter substitutions."),
|
|
6342
|
+
insert: cap("limited", "db-write", "Document insert is supported with Mongo-specific behavior."),
|
|
6343
|
+
update: cap("limited", "db-write", "Document update is supported with Mongo-specific behavior."),
|
|
6344
|
+
delete: cap("limited", "db-write", "Document delete is supported with Mongo-specific behavior."),
|
|
6345
|
+
export: cap("limited", "readonly", "MongoDB export is supported with document-query syntax."),
|
|
6346
|
+
blacklistRead: cap("limited", "readonly", "Reads the engine-specific blacklist rules."),
|
|
6347
|
+
blacklist: cap("limited", "local-write", "Rule management works; enforcement differs from SQL column enforcement."),
|
|
6348
|
+
check: cap("unsupported", "none", "Data health check is SQL-only."),
|
|
6349
|
+
diff: cap("unsupported", "none", "Schema snapshots are relational only."),
|
|
6350
|
+
migrate: cap("unsupported", "none", "DDL migrations are SQL-only."),
|
|
6351
|
+
shell: cap("limited", "interactive", "MongoDB shell support is narrower than SQL shell support.")
|
|
6352
|
+
}),
|
|
6353
|
+
redis: Object.freeze({
|
|
6354
|
+
...SQL_BASE,
|
|
6355
|
+
...SQL_ONLY_UNSUPPORTED,
|
|
6356
|
+
lint: cap("unsupported", "none", "Static lint accepts SQL connections only."),
|
|
6357
|
+
schemaSingle: cap("limited", "readonly", "Per-key synthetic schema only."),
|
|
6358
|
+
schemaFullScan: cap("unsupported", "none", "Redis has no full schema cache scan."),
|
|
6359
|
+
query: cap("limited", "readonly", "Runs allow-listed Redis commands."),
|
|
6360
|
+
queryLimitGuard: cap("limited", "readonly", "SCAN/LRANGE/ZRANGE COUNT/stop rewrite; HGETALL/SMEMBERS/KEYS truncate at 1000; --no-limit bypasses."),
|
|
6361
|
+
q: cap("limited", "readonly", "Saved Redis snippets use a read-only allowlist and range guards."),
|
|
6362
|
+
queries: cap("limited", "local-write", "Snippet management works with Redis-specific saved-query limitations."),
|
|
6363
|
+
insert: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
6364
|
+
update: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
6365
|
+
delete: cap("limited", "db-write", "Basic delete via DEL/HDEL/LREM/SREM/ZREM (needs data-admin); supports --dry-run."),
|
|
6366
|
+
export: cap("unsupported", "none", "Redis export is not supported."),
|
|
6367
|
+
blacklistRead: cap("limited", "readonly", "Reads the engine-specific blacklist rules."),
|
|
6368
|
+
blacklist: cap("limited", "local-write", "Key-glob rejection (Redis-native pattern) plus value/hash-field masking ([REDACTED])."),
|
|
6369
|
+
check: cap("unsupported", "none", "Data health check is SQL-only."),
|
|
6370
|
+
diff: cap("unsupported", "none", "Schema snapshots are relational only."),
|
|
6371
|
+
migrate: cap("unsupported", "none", "DDL migrations are SQL-only."),
|
|
6372
|
+
shell: cap("limited", "interactive", "Single-line Redis REPL (no trailing semicolon needed) with auto-limit and key-glob blacklist; narrower than SQL shell.")
|
|
6373
|
+
}),
|
|
6374
|
+
elasticsearch: Object.freeze({
|
|
6375
|
+
...SQL_BASE,
|
|
6376
|
+
...SQL_ONLY_UNSUPPORTED,
|
|
6377
|
+
lint: cap("unsupported", "none", "Static lint accepts SQL connections only."),
|
|
6378
|
+
schemaSingle: cap("limited", "readonly", "Schema flattens index mappings."),
|
|
6379
|
+
schemaFullScan: cap("supported", "readonly", "Full scan iterates non-system indices."),
|
|
6380
|
+
query: cap("limited", "readonly", "Uses JSON DSL or Lucene query strings with an index."),
|
|
6381
|
+
queryLimitGuard: cap("limited", "readonly", "Applies Elasticsearch size guard."),
|
|
6382
|
+
q: cap("limited", "readonly", "Saved ES snippets require index frontmatter and reject scripts."),
|
|
6383
|
+
queries: cap("limited", "local-write", "Snippet management works with Elasticsearch-specific saved-query limitations."),
|
|
6384
|
+
insert: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
6385
|
+
update: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
6386
|
+
delete: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
6387
|
+
export: cap("limited", "readonly", "Exports DSL hits or a full index via scroll, capped at 1000 rows (use --no-limit)."),
|
|
6388
|
+
blacklistRead: cap("limited", "readonly", "Reads the engine-specific blacklist rules."),
|
|
6389
|
+
blacklist: cap("limited", "local-write", "Index-level and flattened-column enforcement differ from SQL."),
|
|
6390
|
+
check: cap("unsupported", "none", "Data health check is SQL-only."),
|
|
6391
|
+
diff: cap("unsupported", "none", "Schema snapshots are relational only."),
|
|
6392
|
+
migrate: cap("unsupported", "none", "DDL migrations are SQL-only."),
|
|
6393
|
+
shell: cap("limited", "interactive", "Dedicated Kibana Dev Tools-style REPL (method/path + JSON body, blank-line submit); read-focused.")
|
|
6394
|
+
})
|
|
6395
|
+
});
|
|
6396
|
+
});
|
|
6397
|
+
|
|
5122
6398
|
// src/utils/validation.ts
|
|
6399
|
+
import { isAbsolute } from "path";
|
|
5123
6400
|
function parseTimeoutOption(value) {
|
|
5124
6401
|
let parsed = Number(value);
|
|
5125
6402
|
if (!Number.isInteger(parsed) || parsed < MIN_CONNECTION_TIMEOUT_MS || parsed > MAX_CONNECTION_TIMEOUT_MS)
|
|
@@ -5144,16 +6421,30 @@ function isSqliteMemoryTarget(value) {
|
|
|
5144
6421
|
let query = queryStart === -1 ? "" : withoutScheme.slice(queryStart + 1);
|
|
5145
6422
|
return new URLSearchParams(query).get("mode")?.toLowerCase() === "memory";
|
|
5146
6423
|
}
|
|
6424
|
+
function rejectUnsupportedSqlTls(raw, ctx) {
|
|
6425
|
+
if (raw !== null && typeof raw === "object" && "sqlTls" in raw && raw.sqlTls !== void 0 && (!("system" in raw) || !supportsSqlTls(raw.system)))
|
|
6426
|
+
ctx.addIssue({
|
|
6427
|
+
code: ZodIssueCode.custom,
|
|
6428
|
+
path: ["sqlTls"],
|
|
6429
|
+
message: t("errors.sql_tls_unsupported")
|
|
6430
|
+
});
|
|
6431
|
+
return raw;
|
|
6432
|
+
}
|
|
5147
6433
|
function validateFormat(value, allowedFormats, commandName) {
|
|
5148
6434
|
if (!allowedFormats.includes(value)) {
|
|
5149
6435
|
let allowed = allowedFormats.join(", ");
|
|
5150
6436
|
throw Error(`Invalid format "${value}" for ${commandName}. Allowed: ${allowed}`);
|
|
5151
6437
|
}
|
|
5152
6438
|
}
|
|
5153
|
-
var EnvRefSchema, StringOrEnvRef, NumberOrEnvRef, OptStringOrEnvRef, OptNumberOrEnvRef, MIN_CONNECTION_TIMEOUT_MS = 100, MAX_CONNECTION_TIMEOUT_MS = 600000, MIN_STATEMENT_TIMEOUT_MS = 0, MAX_STATEMENT_TIMEOUT_MS = 3600000, TimeoutField, RESERVED_MONGODB_OPTION_KEYS, MongoDBOptionsSchema, MongoDBConnectionConfigSchema, SqlConnectionConfigSchema, SQLITE_MEMORY_TARGET_MESSAGE = "SQLite in-memory databases are refused: a memory target is a different database on every connection, so it cannot be the connection identity the blacklist, audit log and schema cache are scoped to. Point `file` at a database file on disk. See ADR-0038.", SqliteConnectionConfigSchema, RedisConnectionConfigSchema, ElasticsearchConnectionConfigSchema, ConnectionConfigSchema, PermissionSchema, EnvironmentLabelSchema, MetadataSchema, BLACKLIST_KEY_ALIASES, BlacklistConfigSchema, RedisMaskRuleSchema, RedisConfigSchema, DEFAULT_AUDIT_ROTATION, AuditRotationConfigSchema, AuditConfigSchema, DbcliConfigSchema, SqlNamedConnectionSchema, SqliteNamedConnectionSchema, MongoDBNamedConnectionSchema, RedisNamedConnectionSchema, ElasticsearchNamedConnectionSchema, NamedConnectionUnion, NamedConnectionSchema, DbcliConfigV2Schema;
|
|
6439
|
+
var SqlTlsConfigSchema, EnvRefSchema, StringOrEnvRef, NumberOrEnvRef, OptStringOrEnvRef, OptNumberOrEnvRef, MIN_CONNECTION_TIMEOUT_MS = 100, MAX_CONNECTION_TIMEOUT_MS = 600000, MIN_STATEMENT_TIMEOUT_MS = 0, MAX_STATEMENT_TIMEOUT_MS = 3600000, TimeoutField, RESERVED_MONGODB_OPTION_KEYS, MongoDBOptionsSchema, MongoDBConnectionConfigSchema, SqlConnectionConfigSchema, SQLITE_MEMORY_TARGET_MESSAGE = "SQLite in-memory databases are refused: a memory target is a different database on every connection, so it cannot be the connection identity the blacklist, audit log and schema cache are scoped to. Point `file` at a database file on disk. See ADR-0038.", SqliteConnectionConfigSchema, RedisConnectionConfigSchema, ElasticsearchConnectionConfigSchema, ConnectionConfigSchema, PermissionSchema, EnvironmentLabelSchema, MetadataSchema, BLACKLIST_KEY_ALIASES, BlacklistConfigSchema, RedisMaskRuleSchema, RedisConfigSchema, DEFAULT_AUDIT_ROTATION, AuditRotationConfigSchema, AuditConfigSchema, DbcliConfigSchema, SqlNamedConnectionSchema, SqliteNamedConnectionSchema, MongoDBNamedConnectionSchema, RedisNamedConnectionSchema, ElasticsearchNamedConnectionSchema, NamedConnectionUnion, NamedConnectionSchema, DbcliConfigV2Schema;
|
|
5154
6440
|
var init_validation = __esm(() => {
|
|
6441
|
+
init_message_loader();
|
|
5155
6442
|
init_zod();
|
|
5156
|
-
|
|
6443
|
+
init_capabilities();
|
|
6444
|
+
SqlTlsConfigSchema = objectType({
|
|
6445
|
+
mode: literalType("verify-full"),
|
|
6446
|
+
caPath: stringType().min(1).refine(isAbsolute, () => ({ message: t("errors.sql_tls_ca_absolute") }))
|
|
6447
|
+
}).strict(), EnvRefSchema = objectType({
|
|
5157
6448
|
$env: stringType()
|
|
5158
6449
|
}).strict(), StringOrEnvRef = unionType([stringType().min(1), EnvRefSchema]), NumberOrEnvRef = unionType([numberType().int().min(1).max(65535), EnvRefSchema]), OptStringOrEnvRef = unionType([stringType(), EnvRefSchema]).optional().default(""), OptNumberOrEnvRef = unionType([numberType().int(), EnvRefSchema]).optional().default(27017), TimeoutField = {
|
|
5159
6450
|
timeout: numberType().int().min(MIN_CONNECTION_TIMEOUT_MS).max(MAX_CONNECTION_TIMEOUT_MS).optional(),
|
|
@@ -5189,6 +6480,7 @@ var init_validation = __esm(() => {
|
|
|
5189
6480
|
srv: booleanType().optional().default(!1),
|
|
5190
6481
|
...TimeoutField
|
|
5191
6482
|
}), SqlConnectionConfigSchema = objectType({
|
|
6483
|
+
sqlTls: SqlTlsConfigSchema.optional(),
|
|
5192
6484
|
system: enumType(["postgresql", "mysql", "mariadb"]),
|
|
5193
6485
|
host: StringOrEnvRef,
|
|
5194
6486
|
port: NumberOrEnvRef,
|
|
@@ -5231,13 +6523,14 @@ var init_validation = __esm(() => {
|
|
|
5231
6523
|
caPath: stringType().optional(),
|
|
5232
6524
|
rejectUnauthorized: booleanType().optional().default(!0),
|
|
5233
6525
|
...TimeoutField
|
|
5234
|
-
})
|
|
6526
|
+
});
|
|
6527
|
+
ConnectionConfigSchema = preprocessType(rejectUnsupportedSqlTls, unionType([
|
|
5235
6528
|
SqlConnectionConfigSchema,
|
|
5236
6529
|
SqliteConnectionConfigSchema,
|
|
5237
6530
|
MongoDBConnectionConfigSchema,
|
|
5238
6531
|
RedisConnectionConfigSchema,
|
|
5239
6532
|
ElasticsearchConnectionConfigSchema
|
|
5240
|
-
]), PermissionSchema = enumType(["query-only", "read-write", "data-admin", "admin"]).default("query-only"), EnvironmentLabelSchema = stringType().trim().transform((value) => value || void 0).optional(), MetadataSchema = objectType({
|
|
6533
|
+
])), PermissionSchema = enumType(["query-only", "read-write", "data-admin", "admin"]).default("query-only"), EnvironmentLabelSchema = stringType().trim().transform((value) => value || void 0).optional(), MetadataSchema = objectType({
|
|
5241
6534
|
createdAt: stringType().datetime().optional(),
|
|
5242
6535
|
version: stringType().default("1.0"),
|
|
5243
6536
|
schemaLastUpdated: stringType().datetime().optional(),
|
|
@@ -5312,7 +6605,7 @@ var init_validation = __esm(() => {
|
|
|
5312
6605
|
RedisNamedConnectionSchema,
|
|
5313
6606
|
ElasticsearchNamedConnectionSchema
|
|
5314
6607
|
]), NamedConnectionSchema = preprocessType((raw, ctx) => {
|
|
5315
|
-
if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
|
|
6608
|
+
if (rejectUnsupportedSqlTls(raw, ctx), raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
|
|
5316
6609
|
if ("blacklist" in raw)
|
|
5317
6610
|
ctx.addIssue({
|
|
5318
6611
|
code: ZodIssueCode.custom,
|
|
@@ -6755,1532 +8048,556 @@ class SchemaIndexBuilder {
|
|
|
6755
8048
|
}
|
|
6756
8049
|
static getFileForTable(tableName) {
|
|
6757
8050
|
if (/^(old_|legacy_|archive_)/.test(tableName))
|
|
6758
|
-
return "legacy.json";
|
|
6759
|
-
return "infrequent.json";
|
|
6760
|
-
}
|
|
6761
|
-
}
|
|
6762
|
-
var init_schema_index = __esm(() => {
|
|
6763
|
-
init_schema_path();
|
|
6764
|
-
});
|
|
6765
|
-
|
|
6766
|
-
// src/core/schema-loader.ts
|
|
6767
|
-
import { join as join7 } from "path";
|
|
6768
|
-
|
|
6769
|
-
class SchemaLayeredLoader {
|
|
6770
|
-
dbcliPath;
|
|
6771
|
-
connectionName;
|
|
6772
|
-
options;
|
|
6773
|
-
cache = null;
|
|
6774
|
-
index = null;
|
|
6775
|
-
loadTime = 0;
|
|
6776
|
-
constructor(dbcliPath, options) {
|
|
6777
|
-
this.dbcliPath = dbcliPath, this.connectionName = options?.connectionName, this.options = {
|
|
6778
|
-
maxCacheItems: options?.maxCacheItems || 100,
|
|
6779
|
-
maxCacheSize: options?.maxCacheSize || 52428800,
|
|
6780
|
-
hotTableThreshold: options?.hotTableThreshold || 20,
|
|
6781
|
-
enableStreaming: options?.enableStreaming || !1,
|
|
6782
|
-
streamingTimeout: options?.streamingTimeout || 30000
|
|
6783
|
-
};
|
|
6784
|
-
}
|
|
6785
|
-
async initialize() {
|
|
6786
|
-
let startTime = performance.now();
|
|
6787
|
-
try {
|
|
6788
|
-
if (await this.ensureDirectories(), this.index = await SchemaIndexBuilder.loadIndex(this.dbcliPath, this.connectionName), this.cache = new SchemaCacheManager(this.dbcliPath, {
|
|
6789
|
-
maxCacheItems: this.options.maxCacheItems,
|
|
6790
|
-
maxCacheSize: this.options.maxCacheSize,
|
|
6791
|
-
connectionName: this.connectionName
|
|
6792
|
-
}), await this.cache.initialize(), this.loadTime = performance.now() - startTime, this.loadTime > 50)
|
|
6793
|
-
console.warn(`Schema initialization took ${this.loadTime.toFixed(2)}ms (target: < 100ms)`);
|
|
6794
|
-
return {
|
|
6795
|
-
cache: this.cache,
|
|
6796
|
-
index: this.index,
|
|
6797
|
-
loadTime: this.loadTime
|
|
6798
|
-
};
|
|
6799
|
-
} catch (error) {
|
|
6800
|
-
if (console.error("Failed to initialize schema loader:", error), this.loadTime = performance.now() - startTime, !this.cache)
|
|
6801
|
-
this.cache = new SchemaCacheManager(this.dbcliPath, {
|
|
6802
|
-
maxCacheItems: this.options.maxCacheItems,
|
|
6803
|
-
maxCacheSize: this.options.maxCacheSize,
|
|
6804
|
-
connectionName: this.connectionName
|
|
6805
|
-
});
|
|
6806
|
-
return {
|
|
6807
|
-
cache: this.cache,
|
|
6808
|
-
index: this.index,
|
|
6809
|
-
loadTime: this.loadTime
|
|
6810
|
-
};
|
|
6811
|
-
}
|
|
6812
|
-
}
|
|
6813
|
-
async loadColdTable(tableName, cache) {
|
|
6814
|
-
try {
|
|
6815
|
-
return await cache.getTableSchema(tableName);
|
|
6816
|
-
} catch (error) {
|
|
6817
|
-
return console.error(`Failed to load cold table ${tableName}:`, error), null;
|
|
6818
|
-
}
|
|
6819
|
-
}
|
|
6820
|
-
async ensureDirectories() {
|
|
6821
|
-
let base = resolveSchemaPath(this.dbcliPath, this.connectionName), dirs = [base, join7(base, "cold")];
|
|
6822
|
-
for (let dir of dirs)
|
|
6823
|
-
try {
|
|
6824
|
-
if (!await Bun.file(dir).exists()) {
|
|
6825
|
-
if (await Bun.spawn(["mkdir", "-p", dir]).exited !== 0)
|
|
6826
|
-
console.error(`Failed to create directory: ${dir}`);
|
|
6827
|
-
}
|
|
6828
|
-
} catch (error) {
|
|
6829
|
-
console.error(`Error ensuring directory ${dir}:`, error);
|
|
6830
|
-
}
|
|
6831
|
-
}
|
|
6832
|
-
getBenchmark() {
|
|
6833
|
-
if (!this.cache || !this.index)
|
|
6834
|
-
return {
|
|
6835
|
-
initTime: this.loadTime,
|
|
6836
|
-
hotTables: 0,
|
|
6837
|
-
totalTables: 0,
|
|
6838
|
-
estimatedSize: 0
|
|
6839
|
-
};
|
|
6840
|
-
let estimatedSize = Object.values(this.index.tables).reduce((sum, table) => sum + (table.estimatedSize || 0), 0);
|
|
6841
|
-
return {
|
|
6842
|
-
initTime: this.loadTime,
|
|
6843
|
-
hotTables: this.index.hotTables.length,
|
|
6844
|
-
totalTables: this.index.metadata.totalTables,
|
|
6845
|
-
estimatedSize
|
|
6846
|
-
};
|
|
6847
|
-
}
|
|
6848
|
-
}
|
|
6849
|
-
var init_schema_loader = __esm(() => {
|
|
6850
|
-
init_schema_cache();
|
|
6851
|
-
init_schema_index();
|
|
6852
|
-
init_schema_path();
|
|
6853
|
-
});
|
|
6854
|
-
|
|
6855
|
-
// src/core/config.ts
|
|
6856
|
-
import { join as join8 } from "path";
|
|
6857
|
-
import { mkdir as mkdir3 } from "fs/promises";
|
|
6858
|
-
function assertNoConnectionSelectorOnV1(connectionName) {
|
|
6859
|
-
if (connectionName === void 0)
|
|
6860
|
-
return;
|
|
6861
|
-
throw new ConfigError(`\u9023\u7DDA '${connectionName}' \u4E0D\u5B58\u5728\uFF1A\u6B64\u5C08\u6848\u4F7F\u7528\u55AE\u4E00\u9023\u7DDA (v1) \u8A2D\u5B9A\uFF0C\u6C92\u6709\u5177\u540D\u9023\u7DDA\u3002` + " \u79FB\u9664 --use / DBCLI_CONNECTION\uFF0C\u6216\u6539\u7528 'dbcli init --conn-name <name>' \u5347\u7D1A\u70BA\u591A\u9023\u7DDA (v2) \u8A2D\u5B9A\u3002");
|
|
6862
|
-
}
|
|
6863
|
-
function setGlobalConnectionName(name) {
|
|
6864
|
-
_globalConnectionName = name;
|
|
6865
|
-
}
|
|
6866
|
-
function getGlobalConnectionName() {
|
|
6867
|
-
return _globalConnectionName;
|
|
6868
|
-
}
|
|
6869
|
-
async function getSchemaIsolationConnectionName(dbcliPath) {
|
|
6870
|
-
let effectiveName = getGlobalConnectionName();
|
|
6871
|
-
try {
|
|
6872
|
-
let storagePath = await resolveConfigStoragePath(dbcliPath);
|
|
6873
|
-
if (!((await Bun.file(storagePath).stat())?.isDirectory() ?? !1))
|
|
6874
|
-
return;
|
|
6875
|
-
let configJsonPath = join8(storagePath, "config.json"), configFile = Bun.file(configJsonPath);
|
|
6876
|
-
if (!await configFile.exists())
|
|
6877
|
-
return;
|
|
6878
|
-
let raw = JSON.parse(await configFile.text());
|
|
6879
|
-
if (detectConfigVersion(raw) !== 2)
|
|
6880
|
-
return;
|
|
6881
|
-
let v2Config = DbcliConfigV2Schema.parse(raw);
|
|
6882
|
-
return resolveConnection(v2Config, effectiveName).name;
|
|
6883
|
-
} catch {
|
|
6884
|
-
return;
|
|
6885
|
-
}
|
|
6886
|
-
}
|
|
6887
|
-
function isEnvReference(value) {
|
|
6888
|
-
return typeof value === "object" && value !== null && "$env" in value && typeof value.$env === "string";
|
|
6889
|
-
}
|
|
6890
|
-
function resolveEnvReferences(config, env, parentKey) {
|
|
6891
|
-
if (isEnvReference(config)) {
|
|
6892
|
-
let envKey = config.$env, value = resolveEnvRef(config, parentKey ?? "<root>", env);
|
|
6893
|
-
if (parentKey === "port") {
|
|
6894
|
-
let portNum = parseInt(value, 10);
|
|
6895
|
-
if (isNaN(portNum) || portNum < 1 || portNum > 65535)
|
|
6896
|
-
throw new ConfigError(`${envKey} must be a valid port number (1-65535), got: ${value}`);
|
|
6897
|
-
return portNum;
|
|
6898
|
-
}
|
|
6899
|
-
return value;
|
|
6900
|
-
}
|
|
6901
|
-
if (Array.isArray(config))
|
|
6902
|
-
return config.map((item) => resolveEnvReferences(item, env, parentKey));
|
|
6903
|
-
if (typeof config === "object" && config !== null) {
|
|
6904
|
-
let resolved = {};
|
|
6905
|
-
for (let [key, value] of Object.entries(config))
|
|
6906
|
-
resolved[key] = resolveEnvReferences(value, env, key);
|
|
6907
|
-
return resolved;
|
|
6908
|
-
}
|
|
6909
|
-
return config;
|
|
6910
|
-
}
|
|
6911
|
-
function parseEnvPassword(content) {
|
|
6912
|
-
let match = content.match(/^DBCLI_PASSWORD=(.+)$/m);
|
|
6913
|
-
if (match?.[1] == null)
|
|
6914
|
-
return null;
|
|
6915
|
-
let raw = match[1].trim();
|
|
6916
|
-
return (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("'") && raw.endsWith("'")) && raw.length >= 2 ? raw.slice(1, -1) : raw;
|
|
6917
|
-
}
|
|
6918
|
-
var _globalConnectionName, DEFAULT_CONFIG, configModule;
|
|
6919
|
-
var init_config = __esm(() => {
|
|
6920
|
-
init_validation();
|
|
6921
|
-
init_errors3();
|
|
6922
|
-
init_config_error_format();
|
|
6923
|
-
init_config_v2();
|
|
6924
|
-
init_config_binding();
|
|
6925
|
-
init_public();
|
|
6926
|
-
init_config_mutation_guard();
|
|
6927
|
-
init_config_integrity();
|
|
6928
|
-
DEFAULT_CONFIG = {
|
|
6929
|
-
connection: {
|
|
6930
|
-
system: "postgresql",
|
|
6931
|
-
host: "localhost",
|
|
6932
|
-
port: 5432,
|
|
6933
|
-
user: "",
|
|
6934
|
-
password: "",
|
|
6935
|
-
database: ""
|
|
6936
|
-
},
|
|
6937
|
-
permission: "query-only",
|
|
6938
|
-
schema: {},
|
|
6939
|
-
metadata: {
|
|
6940
|
-
version: "1.0"
|
|
6941
|
-
},
|
|
6942
|
-
blacklist: { tables: [], columns: {} },
|
|
6943
|
-
audit: {
|
|
6944
|
-
enabled: !0,
|
|
6945
|
-
strict: !1,
|
|
6946
|
-
rotation: { ...DEFAULT_AUDIT_ROTATION }
|
|
6947
|
-
}
|
|
6948
|
-
};
|
|
6949
|
-
configModule = {
|
|
6950
|
-
async read(path, connectionName, options = {}) {
|
|
6951
|
-
let effectiveConnectionName = connectionName ?? _globalConnectionName, rawForDiagnostics;
|
|
6952
|
-
try {
|
|
6953
|
-
let binding = await readProjectBinding(path), storagePath = await resolveConfigStoragePath(path);
|
|
6954
|
-
if (binding) {
|
|
6955
|
-
if (!await Bun.file(join8(storagePath, "config.json")).exists())
|
|
6956
|
-
throw new ConfigError(`Bound dbcli config not found: ${join8(storagePath, "config.json")}`);
|
|
6957
|
-
}
|
|
6958
|
-
let isDirectory = !1;
|
|
6959
|
-
try {
|
|
6960
|
-
isDirectory = (await Bun.file(storagePath).stat())?.isDirectory() ?? !1;
|
|
6961
|
-
} catch {
|
|
6962
|
-
isDirectory = !1;
|
|
6963
|
-
}
|
|
6964
|
-
if (isDirectory) {
|
|
6965
|
-
let configPath = join8(storagePath, "config.json"), configFile = Bun.file(configPath);
|
|
6966
|
-
if (await configFile.exists()) {
|
|
6967
|
-
await assertAgentReadableFile(configPath);
|
|
6968
|
-
let content = await configFile.text();
|
|
6969
|
-
await assertConfigIntegrity(storagePath, content, { requireRecord: !0 });
|
|
6970
|
-
let config = JSON.parse(content);
|
|
6971
|
-
if (rawForDiagnostics = config, detectConfigVersion(config) === 2) {
|
|
6972
|
-
let v2Config = DbcliConfigV2Schema.parse(config), resolved = resolveConnection(v2Config, effectiveConnectionName);
|
|
6973
|
-
assertExplicitProductionSelection(resolved, effectiveConnectionName), await loadConnectionEnv(resolved, storagePath);
|
|
6974
|
-
let envLocalPath = join8(storagePath, ".env.local"), envLocalFile = Bun.file(envLocalPath), legacyPassword = null;
|
|
6975
|
-
if (await envLocalFile.exists()) {
|
|
6976
|
-
let envContent = await envLocalFile.text();
|
|
6977
|
-
if (legacyPassword = parseEnvPassword(envContent), legacyPassword && !process.env.DBCLI_PASSWORD)
|
|
6978
|
-
process.env.DBCLI_PASSWORD = legacyPassword;
|
|
6979
|
-
}
|
|
6980
|
-
let resolvedConnection = resolveEnvReferences(resolved.connection, process.env);
|
|
6981
|
-
if (!resolvedConnection.password && legacyPassword)
|
|
6982
|
-
resolvedConnection.password = legacyPassword;
|
|
6983
|
-
let schema = (v2Config.schemas ?? {})[resolved.name] ?? v2Config.schema;
|
|
6984
|
-
if (options.loadLayeredSchema !== !1)
|
|
6985
|
-
try {
|
|
6986
|
-
await Promise.resolve().then(() => init_schema_loader());
|
|
6987
|
-
let loader = new SchemaLayeredLoader(storagePath, {
|
|
6988
|
-
connectionName: resolved.name
|
|
6989
|
-
}), { cache, index } = await loader.initialize();
|
|
6990
|
-
if (index && Object.keys(index.tables).length > 0) {
|
|
6991
|
-
let layeredSchema = {};
|
|
6992
|
-
for (let tableName of Object.keys(index.tables)) {
|
|
6993
|
-
let s = await cache.getTableSchema(tableName);
|
|
6994
|
-
if (s)
|
|
6995
|
-
layeredSchema[tableName] = s;
|
|
6996
|
-
}
|
|
6997
|
-
if (Object.keys(layeredSchema).length > 0)
|
|
6998
|
-
schema = layeredSchema;
|
|
6999
|
-
}
|
|
7000
|
-
} catch {
|
|
7001
|
-
console.warn("Warning: Failed to load layered schema cache, falling back to config.json");
|
|
7002
|
-
}
|
|
7003
|
-
return {
|
|
7004
|
-
...DbcliConfigSchema.parse({
|
|
7005
|
-
connection: resolvedConnection,
|
|
7006
|
-
permission: resolved.permission,
|
|
7007
|
-
schema,
|
|
7008
|
-
metadata: v2Config.metadata,
|
|
7009
|
-
blacklist: v2Config.blacklist,
|
|
7010
|
-
audit: v2Config.audit,
|
|
7011
|
-
redis: v2Config.redis
|
|
7012
|
-
}),
|
|
7013
|
-
effectiveConnectionName: resolved.name,
|
|
7014
|
-
...resolved.environment && { effectiveEnvironment: resolved.environment }
|
|
7015
|
-
};
|
|
7016
|
-
}
|
|
7017
|
-
assertNoConnectionSelectorOnV1(effectiveConnectionName);
|
|
7018
|
-
let resolvedConfig = resolveEnvReferences(config, process.env), envPath = join8(storagePath, ".env.local"), envFile = Bun.file(envPath);
|
|
7019
|
-
if (await envFile.exists()) {
|
|
7020
|
-
let envContent = await envFile.text(), password = parseEnvPassword(envContent);
|
|
7021
|
-
if (password && !resolvedConfig.connection.password)
|
|
7022
|
-
resolvedConfig.connection.password = password;
|
|
7023
|
-
}
|
|
7024
|
-
return DbcliConfigSchema.parse(resolvedConfig);
|
|
7025
|
-
}
|
|
7026
|
-
}
|
|
7027
|
-
let file = Bun.file(path);
|
|
7028
|
-
if (await file.exists()) {
|
|
7029
|
-
if (process.env.DBCLI_AGENT_MODE === "1")
|
|
7030
|
-
throw new ConfigError(`Agent mode refuses legacy single-file config: ${path}. Run the human/admin migration workflow to move it to V2 home storage first.`);
|
|
7031
|
-
assertNoConnectionSelectorOnV1(effectiveConnectionName);
|
|
7032
|
-
let content = await file.text(), raw = JSON.parse(content);
|
|
7033
|
-
rawForDiagnostics = raw;
|
|
7034
|
-
let resolved = resolveEnvReferences(raw, process.env);
|
|
7035
|
-
return DbcliConfigSchema.parse(resolved);
|
|
7036
|
-
}
|
|
7037
|
-
if (process.env.DBCLI_AGENT_MODE === "1")
|
|
7038
|
-
throw new ConfigError(`Agent mode refuses a missing config: ${path}. Run the human/admin setup workflow to provision it.`);
|
|
7039
|
-
return { ...DEFAULT_CONFIG };
|
|
7040
|
-
} catch (error) {
|
|
7041
|
-
if (error instanceof ConfigError)
|
|
7042
|
-
throw error;
|
|
7043
|
-
if (isZodError(error))
|
|
7044
|
-
throw new ConfigError(`Failed to read .dbcli config: \u8A2D\u5B9A\u5167\u5BB9\u4E0D\u7B26\u5408\u7D50\u69CB
|
|
7045
|
-
${formatConfigValidationError(error, rawForDiagnostics)}`);
|
|
7046
|
-
if (error instanceof Error && error.message.includes("JSON"))
|
|
7047
|
-
throw new ConfigError(`Failed to parse .dbcli file: ${error.message}`);
|
|
7048
|
-
throw new ConfigError(`Failed to read .dbcli config: ${error instanceof Error ? error.message : String(error)}`);
|
|
7049
|
-
}
|
|
7050
|
-
},
|
|
7051
|
-
validate(raw) {
|
|
7052
|
-
try {
|
|
7053
|
-
return DbcliConfigSchema.parse(raw);
|
|
7054
|
-
} catch (error) {
|
|
7055
|
-
if (isZodError(error))
|
|
7056
|
-
throw new ConfigError(`Invalid .dbcli config structure:
|
|
7057
|
-
${formatConfigValidationError(error, raw)}`);
|
|
7058
|
-
let errorMessage = error instanceof Error ? error.message : String(error);
|
|
7059
|
-
throw new ConfigError(`Invalid .dbcli config structure: ${errorMessage}`);
|
|
7060
|
-
}
|
|
7061
|
-
},
|
|
7062
|
-
merge(existing, updates) {
|
|
7063
|
-
return {
|
|
7064
|
-
...existing,
|
|
7065
|
-
...updates,
|
|
7066
|
-
connection: {
|
|
7067
|
-
...existing.connection,
|
|
7068
|
-
...updates.connection || {}
|
|
7069
|
-
},
|
|
7070
|
-
schema: {
|
|
7071
|
-
...existing.schema,
|
|
7072
|
-
...updates.schema || {}
|
|
7073
|
-
},
|
|
7074
|
-
metadata: {
|
|
7075
|
-
...existing.metadata,
|
|
7076
|
-
...updates.metadata || {},
|
|
7077
|
-
createdAt: existing.metadata?.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
7078
|
-
version: existing.metadata?.version || "1.0"
|
|
7079
|
-
}
|
|
7080
|
-
};
|
|
7081
|
-
},
|
|
7082
|
-
async write(path, config) {
|
|
7083
|
-
try {
|
|
7084
|
-
assertConfigMutationApproved(), this.validate(config);
|
|
7085
|
-
let storagePath = await resolveConfigStoragePath(path), isDirectory = !1;
|
|
7086
|
-
try {
|
|
7087
|
-
isDirectory = (await Bun.file(storagePath).stat())?.isDirectory() ?? !1;
|
|
7088
|
-
} catch {
|
|
7089
|
-
isDirectory = !1;
|
|
7090
|
-
}
|
|
7091
|
-
if (isDirectory || path.endsWith(".dbcli") || path === storagePath && isDirectory)
|
|
7092
|
-
if (await mkdir3(storagePath, { recursive: !0 }), isEnvReference(config.connection.password)) {
|
|
7093
|
-
let configJson = JSON.stringify(config, null, 2);
|
|
7094
|
-
await writeConfigWithIntegrity(storagePath, configJson);
|
|
7095
|
-
} else {
|
|
7096
|
-
let password = config.connection.password, configWithoutPassword = {
|
|
7097
|
-
...config,
|
|
7098
|
-
connection: {
|
|
7099
|
-
...config.connection,
|
|
7100
|
-
password: void 0
|
|
7101
|
-
}
|
|
7102
|
-
};
|
|
7103
|
-
delete configWithoutPassword.connection.password;
|
|
7104
|
-
let configJson = JSON.stringify(configWithoutPassword, null, 2);
|
|
7105
|
-
if (await writeConfigWithIntegrity(storagePath, configJson), password) {
|
|
7106
|
-
let envPath = join8(storagePath, ".env.local"), envContent = `# Database Credentials - DO NOT commit to git
|
|
7107
|
-
|
|
7108
|
-
DBCLI_PASSWORD=${password}
|
|
7109
|
-
`;
|
|
7110
|
-
await Bun.file(envPath).write(envContent);
|
|
7111
|
-
}
|
|
7112
|
-
}
|
|
7113
|
-
else {
|
|
7114
|
-
let json = JSON.stringify(config, null, 2);
|
|
7115
|
-
await Bun.file(path).write(json);
|
|
7116
|
-
}
|
|
7117
|
-
} catch (error) {
|
|
7118
|
-
if (error instanceof ConfigError)
|
|
7119
|
-
throw error;
|
|
7120
|
-
throw new ConfigError(`Failed to write .dbcli config: ${error instanceof Error ? error.message : String(error)}`);
|
|
7121
|
-
}
|
|
7122
|
-
}
|
|
7123
|
-
};
|
|
7124
|
-
});
|
|
7125
|
-
|
|
7126
|
-
// src/core/correlation-id.ts
|
|
7127
|
-
function isCorrelationId(value) {
|
|
7128
|
-
return typeof value === "string" && CORRELATION_ID_PATTERN.test(value);
|
|
7129
|
-
}
|
|
7130
|
-
function setGlobalCorrelationId(value) {
|
|
7131
|
-
if (value !== void 0 && !isCorrelationId(value))
|
|
7132
|
-
throw Error("Invalid correlation ID");
|
|
7133
|
-
globalCorrelationId = value;
|
|
7134
|
-
}
|
|
7135
|
-
function getGlobalCorrelationId() {
|
|
7136
|
-
return globalCorrelationId;
|
|
7137
|
-
}
|
|
7138
|
-
var CORRELATION_ID_PATTERN, globalCorrelationId;
|
|
7139
|
-
var init_correlation_id = __esm(() => {
|
|
7140
|
-
CORRELATION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/;
|
|
7141
|
-
});
|
|
7142
|
-
|
|
7143
|
-
// src/utils/connection-timeout.ts
|
|
7144
|
-
function setGlobalConnectionTimeout(timeout) {
|
|
7145
|
-
_globalConnectionTimeout = timeout;
|
|
7146
|
-
}
|
|
7147
|
-
function setGlobalStatementTimeout(timeout) {
|
|
7148
|
-
_globalStatementTimeout = timeout;
|
|
7149
|
-
}
|
|
7150
|
-
function resolveConnectionTimeout(configured) {
|
|
7151
|
-
return _globalConnectionTimeout ?? configured;
|
|
7152
|
-
}
|
|
7153
|
-
function resolveStatementTimeout(configured) {
|
|
7154
|
-
return _globalStatementTimeout ?? configured;
|
|
7155
|
-
}
|
|
7156
|
-
function withResolvedTimeout(options) {
|
|
7157
|
-
let timeout = resolveConnectionTimeout(options.timeout), statementTimeout = resolveStatementTimeout(options.statementTimeout);
|
|
7158
|
-
if (timeout === options.timeout && statementTimeout === options.statementTimeout)
|
|
7159
|
-
return options;
|
|
7160
|
-
return { ...options, timeout, statementTimeout };
|
|
7161
|
-
}
|
|
7162
|
-
var _globalConnectionTimeout, _globalStatementTimeout;
|
|
7163
|
-
|
|
7164
|
-
// src/core/connection-selector.ts
|
|
7165
|
-
function createConnectionSelectorOption() {
|
|
7166
|
-
return new Option2("--use <connection>", CONNECTION_SELECTOR_DESCRIPTION);
|
|
7167
|
-
}
|
|
7168
|
-
var CONNECTION_SELECTOR_DESCRIPTION = "Use a specific named connection for this invocation (v2 config)";
|
|
7169
|
-
var init_connection_selector = __esm(() => {
|
|
7170
|
-
init_esm();
|
|
7171
|
-
});
|
|
7172
|
-
|
|
7173
|
-
// src/utils/config-path.ts
|
|
7174
|
-
function resolveConfigPath(command, options, fallback = ".dbcli") {
|
|
7175
|
-
for (let current = command;current; current = current.parent ?? void 0) {
|
|
7176
|
-
let source = current.getOptionValueSource("config");
|
|
7177
|
-
if (source && source !== "default") {
|
|
7178
|
-
let currentOptions = current.opts();
|
|
7179
|
-
if (typeof currentOptions.config === "string" && currentOptions.config.length > 0)
|
|
7180
|
-
return currentOptions.config;
|
|
7181
|
-
}
|
|
7182
|
-
}
|
|
7183
|
-
if (typeof options?.config === "string" && options.config.length > 0 && options.config !== fallback)
|
|
7184
|
-
return options.config;
|
|
7185
|
-
for (let current = command;current; current = current.parent ?? void 0) {
|
|
7186
|
-
let source = current.getOptionValueSource("global");
|
|
7187
|
-
if (source && source !== "default" && current.opts().global === !0)
|
|
7188
|
-
return getGlobalConfigPath();
|
|
7189
|
-
}
|
|
7190
|
-
if (options?.global === !0)
|
|
7191
|
-
return getGlobalConfigPath();
|
|
7192
|
-
if (typeof options?.config === "string" && options.config.length > 0)
|
|
7193
|
-
return options.config;
|
|
7194
|
-
return fallback;
|
|
7195
|
-
}
|
|
7196
|
-
var init_config_path = __esm(() => {
|
|
7197
|
-
init_config_binding();
|
|
7198
|
-
});
|
|
7199
|
-
|
|
7200
|
-
// src/utils/config-sidecar-path.ts
|
|
7201
|
-
import { dirname as dirname2, extname, join as join9 } from "path";
|
|
7202
|
-
function configSidecarPath(configPath, fileName) {
|
|
7203
|
-
let directory = extname(configPath) !== "" && !configPath.endsWith(".dbcli") ? dirname2(configPath) : configPath;
|
|
7204
|
-
return join9(directory, fileName);
|
|
7205
|
-
}
|
|
7206
|
-
var init_config_sidecar_path = () => {};
|
|
7207
|
-
|
|
7208
|
-
// src/utils/skill-check-cache.ts
|
|
7209
|
-
import { mkdir as mkdir5, rename as rename3, unlink as unlink4, writeFile as writeFile2 } from "fs/promises";
|
|
7210
|
-
import { dirname as dirname4 } from "path";
|
|
7211
|
-
function cachePath(configPath) {
|
|
7212
|
-
return configSidecarPath(configPath, CACHE_FILE_NAME);
|
|
7213
|
-
}
|
|
7214
|
-
async function readSkillCheckCache(configPath, dbcliVersion, options = {}) {
|
|
7215
|
-
try {
|
|
7216
|
-
let file = Bun.file(cachePath(configPath));
|
|
7217
|
-
if (!await file.exists())
|
|
7218
|
-
return null;
|
|
7219
|
-
let raw = await file.json();
|
|
7220
|
-
if (raw.version !== 1 || !Array.isArray(raw.outdated))
|
|
7221
|
-
return null;
|
|
7222
|
-
if (raw.dbcliVersion !== dbcliVersion)
|
|
7223
|
-
return null;
|
|
7224
|
-
if (typeof raw.checkedAt !== "number")
|
|
7225
|
-
return null;
|
|
7226
|
-
let age = (options.now ?? Date.now()) - raw.checkedAt;
|
|
7227
|
-
if (age < 0 || age > SKILL_CHECK_TTL_MS)
|
|
7228
|
-
return null;
|
|
7229
|
-
return raw.outdated;
|
|
7230
|
-
} catch {
|
|
7231
|
-
return null;
|
|
8051
|
+
return "legacy.json";
|
|
8052
|
+
return "infrequent.json";
|
|
7232
8053
|
}
|
|
7233
8054
|
}
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
version: 1,
|
|
7237
|
-
dbcliVersion,
|
|
7238
|
-
checkedAt: options.now ?? Date.now(),
|
|
7239
|
-
outdated
|
|
7240
|
-
};
|
|
7241
|
-
try {
|
|
7242
|
-
await mkdir5(dirname4(path), { recursive: !0 });
|
|
7243
|
-
let temporary = `${path}.tmp-${process.pid}`;
|
|
7244
|
-
await writeFile2(temporary, JSON.stringify(payload), "utf8"), await rename3(temporary, path);
|
|
7245
|
-
} catch {}
|
|
7246
|
-
}
|
|
7247
|
-
async function invalidateSkillCheckCache(configPath) {
|
|
7248
|
-
await unlink4(cachePath(configPath)).catch(() => {
|
|
7249
|
-
return;
|
|
7250
|
-
});
|
|
7251
|
-
}
|
|
7252
|
-
var SKILL_CHECK_TTL_MS = 86400000, CACHE_FILE_NAME = "skill-check.json";
|
|
7253
|
-
var init_skill_check_cache = __esm(() => {
|
|
7254
|
-
init_config_sidecar_path();
|
|
8055
|
+
var init_schema_index = __esm(() => {
|
|
8056
|
+
init_schema_path();
|
|
7255
8057
|
});
|
|
7256
8058
|
|
|
7257
|
-
// src/core/
|
|
7258
|
-
|
|
7259
|
-
if (options.recovery === !0)
|
|
7260
|
-
return 0;
|
|
7261
|
-
let threshold = options.slowMs ?? 1000;
|
|
7262
|
-
return assertValidSlowQueryThreshold(threshold), threshold;
|
|
7263
|
-
}
|
|
7264
|
-
function assertValidSlowQueryThreshold(value) {
|
|
7265
|
-
if (value === void 0)
|
|
7266
|
-
return;
|
|
7267
|
-
if (!Number.isSafeInteger(value) || value < 0)
|
|
7268
|
-
throw Error("slow-ms must be a non-negative integer");
|
|
7269
|
-
}
|
|
7270
|
-
function parseSlowQueryThreshold(value) {
|
|
7271
|
-
if (!/^\d+$/.test(value))
|
|
7272
|
-
throw Error("slow-ms must be a non-negative integer");
|
|
7273
|
-
let parsed = Number(value);
|
|
7274
|
-
return assertValidSlowQueryThreshold(parsed), parsed;
|
|
7275
|
-
}
|
|
7276
|
-
function recommendationFor(system) {
|
|
7277
|
-
return system === void 0 || GUIDE_SLOW_QUERY_SYSTEMS.includes(system) ? "Review safely with: dbcli guide slow-query --format markdown. This hint runs no additional database diagnostics." : `Re-check the query shape and the filters it scans; dbcli ships no slow-query diagnostics for ${system}. This hint runs no additional database diagnostics.`;
|
|
7278
|
-
}
|
|
7279
|
-
function buildSlowQueryAdvisory(executionTimeMs, options) {
|
|
7280
|
-
let thresholdMs = slowQueryThresholdFor(options);
|
|
7281
|
-
if (thresholdMs === 0 || executionTimeMs === void 0 || executionTimeMs < thresholdMs)
|
|
7282
|
-
return;
|
|
7283
|
-
return {
|
|
7284
|
-
code: "SLOW_QUERY",
|
|
7285
|
-
executionTimeMs,
|
|
7286
|
-
thresholdMs,
|
|
7287
|
-
recommendation: recommendationFor(options.system)
|
|
7288
|
-
};
|
|
7289
|
-
}
|
|
7290
|
-
function attachSlowQueryAdvisory(result, options) {
|
|
7291
|
-
let advisory = buildSlowQueryAdvisory(result.executionTimeMs, options);
|
|
7292
|
-
if (!advisory)
|
|
7293
|
-
return result;
|
|
7294
|
-
return {
|
|
7295
|
-
...result,
|
|
7296
|
-
metadata: {
|
|
7297
|
-
statement: result.metadata?.statement ?? "UNKNOWN",
|
|
7298
|
-
...result.metadata,
|
|
7299
|
-
performanceAdvisory: advisory
|
|
7300
|
-
}
|
|
7301
|
-
};
|
|
7302
|
-
}
|
|
7303
|
-
var GUIDE_SLOW_QUERY_SYSTEMS;
|
|
7304
|
-
var init_slow_query_advisory = __esm(() => {
|
|
7305
|
-
GUIDE_SLOW_QUERY_SYSTEMS = ["mariadb", "mysql", "postgresql", "redis"];
|
|
7306
|
-
});
|
|
8059
|
+
// src/core/schema-loader.ts
|
|
8060
|
+
import { join as join7 } from "path";
|
|
7307
8061
|
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
connection_failed: "\u2717 Database connection failed",
|
|
7351
|
-
config_saved: "Configuration saved to .dbcli",
|
|
7352
|
-
config_saved_global: "Global configuration saved to {path}",
|
|
7353
|
-
config_exists_overwrite: "Configuration file .dbcli already exists. Overwrite? (y/n): ",
|
|
7354
|
-
cancelled: "Cancelled. Configuration not changed.",
|
|
7355
|
-
skip_test_env_ref: "Skipping connection test in env-ref mode",
|
|
7356
|
-
skip_test: "Skipping connection test (--skip-test)",
|
|
7357
|
-
connection_hints: "Hints:",
|
|
7358
|
-
config_exists_use_force: ".dbcli exists. Use --force option to overwrite.",
|
|
7359
|
-
connection_added: "Connection '{name}' added",
|
|
7360
|
-
connection_removed: "Connection '{name}' removed",
|
|
7361
|
-
connection_removed_default_cleared: "Connection '{name}' removed. Default connection cleared. Specify --use <name> or DBCLI_CONNECTION for database commands.",
|
|
7362
|
-
connection_renamed: "Connection '{oldName}' renamed to '{newName}'",
|
|
7363
|
-
v1_migration_prompt: "Detected old config format. Create new format and import existing connection as 'default'?",
|
|
7364
|
-
config_saved_v2: "V2 configuration saved to .dbcli",
|
|
7365
|
-
config_not_found: "Configuration file not found",
|
|
7366
|
-
requires_v2_remove: "Removing a connection requires V2 config format",
|
|
7367
|
-
requires_v2_rename: "Renaming a connection requires V2 config format",
|
|
7368
|
-
connection_not_found: "Connection '{name}' does not exist",
|
|
7369
|
-
cannot_remove_last: "Cannot remove the last connection",
|
|
7370
|
-
connection_already_exists: "Connection '{name}' already exists",
|
|
7371
|
-
rename_invalid_format: "Usage: --rename <old-name>:<new-name>",
|
|
7372
|
-
prompt_sqlite_file: "SQLite database file path: "
|
|
7373
|
-
},
|
|
7374
|
-
inspect: {
|
|
7375
|
-
description: "Read-only snapshot of connection, permission, blacklist, objects, snippets, and suggested next commands.",
|
|
7376
|
-
no_config: "No dbcli configuration found. Run `dbcli init` to start."
|
|
7377
|
-
},
|
|
7378
|
-
report: {
|
|
7379
|
-
description: "Diagnostic report (health/capacity/perf) built from built-in snippets. Read-only, bounded.",
|
|
7380
|
-
no_config: "No dbcli configuration found. Run `dbcli init` to start.",
|
|
7381
|
-
section_invalid: "Unknown report section '{name}'. Allowed: health, capacity, perf"
|
|
7382
|
-
},
|
|
7383
|
-
guide: {
|
|
7384
|
-
description: "Deterministic next-command planner for a fixed set of database goals.",
|
|
7385
|
-
no_config: "No dbcli configuration found. Run `dbcli init` to start.",
|
|
7386
|
-
missing_goal: "Missing goal. Try `dbcli guide --list` to see available goals.",
|
|
7387
|
-
goal_invalid: "Unknown guide goal '{name}'. Run `dbcli guide --list` for valid IDs."
|
|
7388
|
-
},
|
|
7389
|
-
recovery: {
|
|
7390
|
-
description: "Look up the structured recovery envelope for a known dbcli error code, or list all codes.",
|
|
7391
|
-
missing_code: "Missing --code. Try `dbcli recovery --list` to see available codes.",
|
|
7392
|
-
code_invalid: "Unknown recovery code '{name}'. Run `dbcli recovery --list` for valid codes.",
|
|
7393
|
-
no_config: "No dbcli configuration found. Run `dbcli init` to start."
|
|
7394
|
-
},
|
|
7395
|
-
use: {
|
|
7396
|
-
description: "Switch or display the default database connection",
|
|
7397
|
-
switched: "Switched default connection to {name}",
|
|
7398
|
-
current: "Current default connection: {name}",
|
|
7399
|
-
current_none: "No default connection. Specify --use <name> or DBCLI_CONNECTION.",
|
|
7400
|
-
unset: "Default connection cleared. Specify --use <name> or DBCLI_CONNECTION for database commands.",
|
|
7401
|
-
unset_conflict: "--unset cannot be combined with a connection name, --list, --confirm-production, or --format json",
|
|
7402
|
-
requires_v2: "This feature requires v2 config format. Use 'dbcli init --conn-name <name>' to create one",
|
|
7403
|
-
available: "Available connections"
|
|
7404
|
-
},
|
|
7405
|
-
schema: {
|
|
7406
|
-
description: "Retrieve table structure or list all tables",
|
|
7407
|
-
fetching: "Fetching schema...",
|
|
7408
|
-
success: "Schema updated",
|
|
7409
|
-
not_found: "Table not found in database",
|
|
7410
|
-
refresh_prompt: "Schema has changes. Apply? (y/n): ",
|
|
7411
|
-
columns_header: "Columns",
|
|
7412
|
-
scanning_database: "\uD83D\uDD0D Scanning database schema...",
|
|
7413
|
-
tables_found: "\uD83D\uDCCD Found {count} table(s). Fetching schema details...",
|
|
7414
|
-
processing_tables: " Processed {processed}/{total} table(s)",
|
|
7415
|
-
schema_exists_warning: "\u26A0\uFE0F Database schema already exists in .dbcli",
|
|
7416
|
-
use_force_to_override: " Use --force to override without confirmation"
|
|
7417
|
-
},
|
|
7418
|
-
list: {
|
|
7419
|
-
description: "List all tables in the database",
|
|
7420
|
-
no_tables: "No tables found in database"
|
|
7421
|
-
},
|
|
7422
|
-
query: {
|
|
7423
|
-
description: "Execute SQL query",
|
|
7424
|
-
executing: "Executing query...",
|
|
7425
|
-
no_results: "No results returned",
|
|
7426
|
-
result_count: "Results: {count} row(s)"
|
|
7427
|
-
},
|
|
7428
|
-
q: {
|
|
7429
|
-
description: "Run a saved query snippet by @name",
|
|
7430
|
-
missing_param: "Missing required parameters for snippet '{name}'",
|
|
7431
|
-
engine_mismatch: "Engine mismatch for snippet '{name}'",
|
|
7432
|
-
snippet_not_found: "Snippet not found: {name}",
|
|
7433
|
-
dry_run_header: "Dry-run preview (no execution):"
|
|
7434
|
-
},
|
|
7435
|
-
audit: {
|
|
7436
|
-
description: "Inspect, look up, clear, or check the health of the audit log",
|
|
7437
|
-
tail: {
|
|
7438
|
-
description: "Show recent audit entries (default: last 10 from current connection)"
|
|
7439
|
-
},
|
|
7440
|
-
show: {
|
|
7441
|
-
description: "Look up a single audit entry by id (UUID prefix >=4) or --recovery-ref"
|
|
7442
|
-
},
|
|
7443
|
-
clear: {
|
|
7444
|
-
description: "Delete audit log files (.jsonl + .jsonl.1) for the current connection",
|
|
7445
|
-
prompt_header: "About to clear audit log for connection '{conn}':",
|
|
7446
|
-
prompt_file_line: " {file} \u2014 {entries} entries, {size}",
|
|
7447
|
-
prompt_continue: "Continue? [y/N] ",
|
|
7448
|
-
requires_tty_or_yes: "Cannot prompt for confirmation in non-interactive session. Use --yes to clear without prompt.",
|
|
7449
|
-
summary_cleared: "Cleared {count} entries from '{conn}'.",
|
|
7450
|
-
summary_nothing: "Nothing to clear.",
|
|
7451
|
-
summary_failed: "Failed to clear: {message}."
|
|
7452
|
-
},
|
|
7453
|
-
health: {
|
|
7454
|
-
description: "Show AuditLogger health snapshot (size, entries, lock state, last write)"
|
|
7455
|
-
},
|
|
7456
|
-
write_gate: {
|
|
7457
|
-
description: "Summarize tier-two write-gate decisions (ADR 0010's measurement)",
|
|
7458
|
-
header: "Write-gate summary \u2014 connection: {conn}",
|
|
7459
|
-
window: "Audit entries scanned: {scanned}, {from} \u2192 {to}",
|
|
7460
|
-
decisions: "Tier-two evaluations: {count}, {from} \u2192 {to}",
|
|
7461
|
-
tier_one: "Tier one, counted apart: {count} ({outcomes})",
|
|
7462
|
-
no_decisions: "Tier two was never reached in this window. Per ADR 0010 that falsifies the criterion, not the gate: the shapes it looks for are not the shapes people write.",
|
|
7463
|
-
no_entries: "The audit log is empty, so this says nothing about the gate either way. Run some writes, or check `dbcli audit health`."
|
|
7464
|
-
},
|
|
7465
|
-
disabled_hint: "Audit is disabled (audit.enabled = false in .dbcli). Use 'dbcli audit health' for details.",
|
|
7466
|
-
no_entries: "No audit entries.",
|
|
7467
|
-
show_no_match: "No audit entry matches '{prefix}'.",
|
|
7468
|
-
show_ambiguous: "Ambiguous prefix '{prefix}': matches {count} entries. Please use a longer prefix.",
|
|
7469
|
-
show_prefix_too_short: "Prefix must be at least 4 characters.",
|
|
7470
|
-
show_recovery_no_match: "No audit entry has recovery_ref '{ref}'.",
|
|
7471
|
-
show_recovery_ambiguous: "Multiple entries reference recovery_ref '{ref}': matches {count}. This should be rare; inspect file directly.",
|
|
7472
|
-
show_mutex_violation: "Provide either <id> argument or --recovery-ref, not both.",
|
|
7473
|
-
n_capped_warning: "--n value {requested} exceeds max {max}; capped to {max}.",
|
|
7474
|
-
n_must_be_positive: "--n must be a positive integer."
|
|
7475
|
-
},
|
|
7476
|
-
queries: {
|
|
7477
|
-
description: "Manage saved query snippets",
|
|
7478
|
-
list_description: "List saved snippets",
|
|
7479
|
-
show_description: "Show a snippet (frontmatter + SQL)",
|
|
7480
|
-
new_description: "Create a new snippet file",
|
|
7481
|
-
edit_description: "Open a snippet in $EDITOR",
|
|
7482
|
-
check_description: "Validate all snippets",
|
|
7483
|
-
delete_description: "Delete a local snippet file",
|
|
7484
|
-
rename_description: "Rename a local snippet",
|
|
7485
|
-
copy_description: "Copy a snippet from any tier into local",
|
|
7486
|
-
import_description: "Import an external .sql file into local",
|
|
7487
|
-
export_description: "Export a snippet's raw SQL to stdout or file",
|
|
7488
|
-
created: "Created {file}",
|
|
7489
|
-
first_run_hint: "Tip: commit .dbcli-shared/ to share snippets with your team.",
|
|
7490
|
-
no_snippets: "No snippets found. Create one with: dbcli queries new @<name>",
|
|
7491
|
-
check_ok: "\u2713 {count} snippets parsed successfully",
|
|
7492
|
-
check_failed: "\u2717 {count} snippet(s) have errors",
|
|
7493
|
-
search_description: "Search snippets by keywords (fuzzy ranking)",
|
|
7494
|
-
suggest_description: "Suggest snippets by intent prefix",
|
|
7495
|
-
search_no_keywords: "Provide at least one keyword, or try 'queries list' / 'queries suggest'.",
|
|
7496
|
-
search_no_results: "No snippets matched. Try 'queries list' or 'queries suggest <intent>'.",
|
|
7497
|
-
suggest_missing_intent: "Provide an intent (or prefix). Available: {available}",
|
|
7498
|
-
suggest_invalid_intent: "Invalid intent '{value}'. Must match ^[a-z][a-z0-9.-]*$",
|
|
7499
|
-
suggest_no_results: "No snippets with intent prefix '{intent}'. Available: {available}",
|
|
7500
|
-
no_active_connection_hint: "No active connection; showing snippets across all engines."
|
|
7501
|
-
},
|
|
7502
|
-
errors: {
|
|
7503
|
-
message: "Error: {message}",
|
|
7504
|
-
query_only_boundary_failed: "Could not establish the query-only database boundary for {system}: {detail}",
|
|
7505
|
-
query_only_boundary_verify: "Verify that {system} supports read-only transactions and that this account may start one",
|
|
7506
|
-
query_only_boundary_not_executed: "The target statement was not executed; fix the database setting or connection before retrying",
|
|
7507
|
-
query_only_cleanup_completed: "The query-only target completed, but transaction cleanup failed for {system}: {detail}",
|
|
7508
|
-
query_only_cleanup_uncertain: "The query-only target failed and its outcome is uncertain because transaction cleanup also failed for {system}: {detail}",
|
|
7509
|
-
query_only_cleanup_completed_retry: "The target statement ran; do not retry it unless repeating its external effects is safe",
|
|
7510
|
-
query_only_cleanup_uncertain_retry: "The target may have produced effects before failing; do not retry it unless repeating them is safe",
|
|
7511
|
-
query_only_cleanup_reconnect: "Reconnect before running another statement because this connection was discarded",
|
|
7512
|
-
invalid_config: "Invalid configuration: {field}",
|
|
7513
|
-
connection_failed: "Failed to connect to database: {message}",
|
|
7514
|
-
permission_denied: "Permission denied (required: {required})",
|
|
7515
|
-
permission_requires_level: "{type} operation requires {minimum} permission or higher (current level: {permission})",
|
|
7516
|
-
elasticsearch_requires_level: "Elasticsearch {type} operation requires {minimum} permission or higher (current level: {permission})",
|
|
7517
|
-
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}).",
|
|
7518
|
-
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.",
|
|
7519
|
-
unknown_statement_query_only: "Unrecognised SQL statement (current level: query-only). Security policy requires admin for unknown statements. If this is a legitimate read-only statement, please open an issue at https://github.com/CarlLee1983/dbcli/issues.",
|
|
7520
|
-
permission_denied_reason: "Permission denied: {reason}",
|
|
7521
|
-
table_not_found: "Table not found: {table}",
|
|
7522
|
-
unsupported_lang: "Language '{lang}' not supported, using English",
|
|
7523
|
-
invalid_json: "Invalid JSON: {message}",
|
|
7524
|
-
table_blacklisted: "Error: Table '{table}' is blacklisted for {operation} operations",
|
|
7525
|
-
redis_target_blacklisted: "Redis command refused by blacklist",
|
|
7526
|
-
column_blacklisted_write: "Error: {operation} on table '{table}' touches blacklisted columns: {columns}",
|
|
7527
|
-
table_already_blacklisted: "Error: Table '{table}' is already blacklisted",
|
|
7528
|
-
table_not_in_blacklist: "Error: Table '{table}' is not in the blacklist",
|
|
7529
|
-
column_already_blacklisted: "Error: Column '{table}.{column}' is already blacklisted",
|
|
7530
|
-
column_not_in_blacklist: "Error: Column '{table}.{column}' is not in the blacklist",
|
|
7531
|
-
invalid_table_name: "Error: Invalid table name: {table}",
|
|
7532
|
-
invalid_column_format: "Error: Invalid column format. Use 'table.column'",
|
|
7533
|
-
invalid_system: "Invalid database system: {system}",
|
|
7534
|
-
invalid_permission: "Invalid permission level: {permission}",
|
|
7535
|
-
invalid_port: "Invalid port: {port}",
|
|
7536
|
-
require_user: "Non-interactive mode requires --user option",
|
|
7537
|
-
require_name: "Non-interactive mode requires --name option",
|
|
7538
|
-
env_refs_missing_options: `When using --use-env-refs, environment variable names must be specified.
|
|
7539
|
-
Provide options: --env-host, --env-port, --env-user, --env-password, --env-database
|
|
7540
|
-
Or use interactive mode: run "dbcli init --use-env-refs" without --no-interactive`,
|
|
7541
|
-
env_var_not_defined: `Cannot test connection: environment variable {envKey} is not defined.
|
|
7542
|
-
Set {envKey} in .env or environment variables.
|
|
7543
|
-
Hint: run 'export {envKey}=<value>' and retry`,
|
|
7544
|
-
invalid_output_format: "--format {format} is not supported here \u2014 use text or json",
|
|
7545
|
-
sqlite_file_required: "A SQLite connection needs a file path. Pass --file <path> or run init interactively."
|
|
7546
|
-
},
|
|
7547
|
-
success: {
|
|
7548
|
-
inserted: "Successfully inserted {count} row(s)",
|
|
7549
|
-
updated: "Successfully updated {count} row(s)",
|
|
7550
|
-
deleted: "Successfully deleted {count} row(s)",
|
|
7551
|
-
no_changes: "No changes to apply"
|
|
7552
|
-
},
|
|
7553
|
-
insert: {
|
|
7554
|
-
description: "Insert data into a table",
|
|
7555
|
-
prompt_data: "Enter JSON data to insert: ",
|
|
7556
|
-
confirm: "Insert {count} row(s) into {table}? (y/n): ",
|
|
7557
|
-
elasticsearch_unsupported: "Elasticsearch does not support the insert command; for now, use an external tool (e.g. curl or Kibana DevTools) to write documents"
|
|
7558
|
-
},
|
|
7559
|
-
update: {
|
|
7560
|
-
description: "Update data in a table",
|
|
7561
|
-
prompt_where: "Enter WHERE clause (e.g., id=1): ",
|
|
7562
|
-
prompt_set: 'Enter JSON updates (e.g., {"status": "active"}): ',
|
|
7563
|
-
confirm: "Update rows in {table}? (y/n): ",
|
|
7564
|
-
elasticsearch_unsupported: "Elasticsearch does not support the update command; for now, use an external tool (e.g. curl or Kibana DevTools) to update documents"
|
|
7565
|
-
},
|
|
7566
|
-
delete: {
|
|
7567
|
-
description: "Delete data from a table",
|
|
7568
|
-
prompt_where: "Enter WHERE clause (e.g., id=1): ",
|
|
7569
|
-
confirm: "Delete rows from {table}? (y/n): ",
|
|
7570
|
-
admin_only: "Delete command requires data-admin or admin permission",
|
|
7571
|
-
elasticsearch_unsupported: "Elasticsearch does not support the delete command; for now, use an external tool (e.g. curl or Kibana DevTools) to delete documents"
|
|
7572
|
-
},
|
|
7573
|
-
export: {
|
|
7574
|
-
description: "Export query results",
|
|
7575
|
-
formats: "Supported formats: json, csv",
|
|
7576
|
-
exported: "Exported {count} rows to {file}"
|
|
7577
|
-
},
|
|
7578
|
-
upgrade: {
|
|
7579
|
-
description: "Check for updates and upgrade dbcli to the latest version",
|
|
7580
|
-
checking: "Checking for updates...",
|
|
7581
|
-
already_up_to_date: "\u2713 Already up to date (v{version})",
|
|
7582
|
-
new_version_available: "New version available: v{version}",
|
|
7583
|
-
current_version: "Current version : v{version}",
|
|
7584
|
-
latest_version: "Latest version : v{version}",
|
|
7585
|
-
update_hint: '[INFO] dbcli v{version} available. Run "dbcli upgrade" to upgrade.',
|
|
7586
|
-
install_hint: 'Run "dbcli upgrade" to install the update.',
|
|
7587
|
-
upgrading: "Upgrading...",
|
|
7588
|
-
success: "\u2713 Successfully upgraded to v{version}",
|
|
7589
|
-
skill_recheck_hint: 'Hint: If you have installed AI skills, run "dbcli skill --install <platform>" to ensure they are up to date.',
|
|
7590
|
-
failed: "\u2717 Upgrade failed. Try running manually:",
|
|
7591
|
-
manual_hint: "bun add -g @carllee1983/dbcli@latest",
|
|
7592
|
-
network_error: "\u26A0 Could not check for updates (network error or registry unavailable)"
|
|
7593
|
-
},
|
|
7594
|
-
skill: {
|
|
7595
|
-
description: "Generate AI skill documentation",
|
|
7596
|
-
context_description: "Generate AI-friendly database schema and query snippet context",
|
|
7597
|
-
installed: `Skill: {path}
|
|
7598
|
-
Reference: {referencePath}`,
|
|
7599
|
-
update_available: "Skill updates available for the following platforms:",
|
|
7600
|
-
update_hint: 'Run "dbcli skill --install <platform>" to update.'
|
|
7601
|
-
},
|
|
7602
|
-
blacklist: {
|
|
7603
|
-
description: "Manage sensitive data blacklist to prevent AI access",
|
|
7604
|
-
list_title: "Current Blacklist Configuration",
|
|
7605
|
-
tables_label: "Blacklisted tables",
|
|
7606
|
-
columns_label: "Blacklisted columns",
|
|
7607
|
-
none: "No tables or columns are currently blacklisted",
|
|
7608
|
-
table_added: "\u2713 Table '{table}' added to blacklist",
|
|
7609
|
-
table_removed: "\u2713 Table '{table}' removed from blacklist",
|
|
7610
|
-
column_added: "\u2713 Column '{table}.{column}' added to blacklist",
|
|
7611
|
-
column_removed: "\u2713 Column '{table}.{column}' removed from blacklist",
|
|
7612
|
-
refuse_wildcard: "Refused: '{table}' contains wildcard characters, which are matched literally on {system} \u2014 the entry would never match anything. Write the exact name."
|
|
7613
|
-
},
|
|
7614
|
-
security: {
|
|
7615
|
-
columns_omitted: "Security: {count} column(s) were omitted based on your blacklist"
|
|
7616
|
-
},
|
|
7617
|
-
warnings: {
|
|
7618
|
-
blacklist_override_used: "Warning: Blacklist override enabled (DBCLI_OVERRIDE_BLACKLIST=true). Executing {operation} on blacklisted table '{table}'",
|
|
7619
|
-
redis_blacklist_override_active: "Warning: Redis blacklist enforcement disabled by DBCLI_OVERRIDE_BLACKLIST=true"
|
|
7620
|
-
},
|
|
7621
|
-
version: {
|
|
7622
|
-
unsupported_warning: "{system} {version} is below minimum supported version {minVersion}. Some features may not work correctly.",
|
|
7623
|
-
doctor_pass: "{system} {version} (meets >= {minVersion})",
|
|
7624
|
-
doctor_warn: "{system} {version} is below supported >= {minVersion}"
|
|
7625
|
-
},
|
|
7626
|
-
migrate: {
|
|
7627
|
-
description: "Schema DDL operations (CREATE/DROP/ALTER TABLE, INDEX, CONSTRAINT, ENUM)",
|
|
7628
|
-
dry_run_preview: "[DRY RUN] Generated SQL (not executed):",
|
|
7629
|
-
executing: "Executing DDL...",
|
|
7630
|
-
success: "DDL executed successfully",
|
|
7631
|
-
confirm_drop: "This is a destructive operation ({operation}). Proceed?",
|
|
7632
|
-
permission_required: "DDL operations require admin permission (current: {permission})",
|
|
7633
|
-
blacklist_blocked: 'Table "{table}" is blacklisted \u2014 DDL operations are blocked',
|
|
7634
|
-
schema_refreshed: 'Schema cache updated for table "{table}"',
|
|
7635
|
-
no_sql_generated: "No SQL generated (operation not applicable for this database)",
|
|
7636
|
-
create_description: "Create a new table",
|
|
7637
|
-
drop_description: "Drop a table",
|
|
7638
|
-
add_column_description: "Add a column to a table",
|
|
7639
|
-
drop_column_description: "Drop a column from a table",
|
|
7640
|
-
alter_column_description: "Alter a column in a table",
|
|
7641
|
-
add_index_description: "Add an index to a table",
|
|
7642
|
-
drop_index_description: "Drop an index",
|
|
7643
|
-
add_constraint_description: "Add a constraint to a table",
|
|
7644
|
-
drop_constraint_description: "Drop a constraint from a table",
|
|
7645
|
-
add_enum_description: "Create an enum type (PostgreSQL only)",
|
|
7646
|
-
alter_enum_description: "Add a value to an enum type",
|
|
7647
|
-
drop_enum_description: "Drop an enum type"
|
|
7648
|
-
},
|
|
7649
|
-
password: {
|
|
7650
|
-
description: "Change the password of a single connection, leaving every other setting untouched",
|
|
7651
|
-
arg_connection: "Connection name (defaults to the current default connection)",
|
|
7652
|
-
opt_stdin: "Read the new password from stdin (for rotation scripts)",
|
|
7653
|
-
opt_password: "New password given inline (visible in shell history and process list)",
|
|
7654
|
-
opt_skip_test: "Skip verifying the new password against the database before saving",
|
|
7655
|
-
prompt: "New password for connection '{name}'",
|
|
7656
|
-
source_conflict: "--password and --stdin cannot be used together; pick one source.",
|
|
7657
|
-
needs_source: "No terminal available for a masked prompt. Pass the value with --stdin or --password.",
|
|
7658
|
-
empty: "The new password is empty; nothing was changed.",
|
|
7659
|
-
updated: "Password for connection '{name}' updated \u2192 {envFile} ({varName})",
|
|
7660
|
-
converted: 'The literal password in config.json was replaced by a {{ "$env": "{varName}" }} reference; later rotations only touch the env file.',
|
|
7661
|
-
skipped_test: "Verification skipped (--skip-test): the new password was not checked against the database."
|
|
8062
|
+
class SchemaLayeredLoader {
|
|
8063
|
+
dbcliPath;
|
|
8064
|
+
connectionName;
|
|
8065
|
+
options;
|
|
8066
|
+
cache = null;
|
|
8067
|
+
index = null;
|
|
8068
|
+
loadTime = 0;
|
|
8069
|
+
constructor(dbcliPath, options) {
|
|
8070
|
+
this.dbcliPath = dbcliPath, this.connectionName = options?.connectionName, this.options = {
|
|
8071
|
+
maxCacheItems: options?.maxCacheItems || 100,
|
|
8072
|
+
maxCacheSize: options?.maxCacheSize || 52428800,
|
|
8073
|
+
hotTableThreshold: options?.hotTableThreshold || 20,
|
|
8074
|
+
enableStreaming: options?.enableStreaming || !1,
|
|
8075
|
+
streamingTimeout: options?.streamingTimeout || 30000
|
|
8076
|
+
};
|
|
8077
|
+
}
|
|
8078
|
+
async initialize() {
|
|
8079
|
+
let startTime = performance.now();
|
|
8080
|
+
try {
|
|
8081
|
+
if (await this.ensureDirectories(), this.index = await SchemaIndexBuilder.loadIndex(this.dbcliPath, this.connectionName), this.cache = new SchemaCacheManager(this.dbcliPath, {
|
|
8082
|
+
maxCacheItems: this.options.maxCacheItems,
|
|
8083
|
+
maxCacheSize: this.options.maxCacheSize,
|
|
8084
|
+
connectionName: this.connectionName
|
|
8085
|
+
}), await this.cache.initialize(), this.loadTime = performance.now() - startTime, this.loadTime > 50)
|
|
8086
|
+
console.warn(`Schema initialization took ${this.loadTime.toFixed(2)}ms (target: < 100ms)`);
|
|
8087
|
+
return {
|
|
8088
|
+
cache: this.cache,
|
|
8089
|
+
index: this.index,
|
|
8090
|
+
loadTime: this.loadTime
|
|
8091
|
+
};
|
|
8092
|
+
} catch (error) {
|
|
8093
|
+
if (console.error("Failed to initialize schema loader:", error), this.loadTime = performance.now() - startTime, !this.cache)
|
|
8094
|
+
this.cache = new SchemaCacheManager(this.dbcliPath, {
|
|
8095
|
+
maxCacheItems: this.options.maxCacheItems,
|
|
8096
|
+
maxCacheSize: this.options.maxCacheSize,
|
|
8097
|
+
connectionName: this.connectionName
|
|
8098
|
+
});
|
|
8099
|
+
return {
|
|
8100
|
+
cache: this.cache,
|
|
8101
|
+
index: this.index,
|
|
8102
|
+
loadTime: this.loadTime
|
|
8103
|
+
};
|
|
7662
8104
|
}
|
|
7663
|
-
}
|
|
8105
|
+
}
|
|
8106
|
+
async loadColdTable(tableName, cache) {
|
|
8107
|
+
try {
|
|
8108
|
+
return await cache.getTableSchema(tableName);
|
|
8109
|
+
} catch (error) {
|
|
8110
|
+
return console.error(`Failed to load cold table ${tableName}:`, error), null;
|
|
8111
|
+
}
|
|
8112
|
+
}
|
|
8113
|
+
async ensureDirectories() {
|
|
8114
|
+
let base = resolveSchemaPath(this.dbcliPath, this.connectionName), dirs = [base, join7(base, "cold")];
|
|
8115
|
+
for (let dir of dirs)
|
|
8116
|
+
try {
|
|
8117
|
+
if (!await Bun.file(dir).exists()) {
|
|
8118
|
+
if (await Bun.spawn(["mkdir", "-p", dir]).exited !== 0)
|
|
8119
|
+
console.error(`Failed to create directory: ${dir}`);
|
|
8120
|
+
}
|
|
8121
|
+
} catch (error) {
|
|
8122
|
+
console.error(`Error ensuring directory ${dir}:`, error);
|
|
8123
|
+
}
|
|
8124
|
+
}
|
|
8125
|
+
getBenchmark() {
|
|
8126
|
+
if (!this.cache || !this.index)
|
|
8127
|
+
return {
|
|
8128
|
+
initTime: this.loadTime,
|
|
8129
|
+
hotTables: 0,
|
|
8130
|
+
totalTables: 0,
|
|
8131
|
+
estimatedSize: 0
|
|
8132
|
+
};
|
|
8133
|
+
let estimatedSize = Object.values(this.index.tables).reduce((sum, table) => sum + (table.estimatedSize || 0), 0);
|
|
8134
|
+
return {
|
|
8135
|
+
initTime: this.loadTime,
|
|
8136
|
+
hotTables: this.index.hotTables.length,
|
|
8137
|
+
totalTables: this.index.metadata.totalTables,
|
|
8138
|
+
estimatedSize
|
|
8139
|
+
};
|
|
8140
|
+
}
|
|
8141
|
+
}
|
|
8142
|
+
var init_schema_loader = __esm(() => {
|
|
8143
|
+
init_schema_cache();
|
|
8144
|
+
init_schema_index();
|
|
8145
|
+
init_schema_path();
|
|
7664
8146
|
});
|
|
7665
8147
|
|
|
7666
|
-
//
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
recovery: {
|
|
7748
|
-
description: "\u67E5\u8A62\u5DF2\u77E5 dbcli \u932F\u8AA4\u78BC\u5C0D\u61C9\u7684\u7D50\u69CB\u5316\u6062\u5FA9\u65B9\u6848\uFF0C\u6216\u5217\u51FA\u6240\u6709\u652F\u63F4\u7684\u932F\u8AA4\u78BC\u3002",
|
|
7749
|
-
missing_code: "\u7F3A\u5C11 --code \u53C3\u6578\u3002\u8ACB\u57F7\u884C `dbcli recovery --list` \u67E5\u770B\u53EF\u7528\u4EE3\u78BC\u3002",
|
|
7750
|
-
code_invalid: "\u672A\u77E5\u7684 recovery code '{name}'\u3002\u8ACB\u57F7\u884C `dbcli recovery --list` \u53D6\u5F97\u6709\u6548\u4EE3\u78BC\u3002",
|
|
7751
|
-
no_config: "\u627E\u4E0D\u5230 dbcli \u8A2D\u5B9A\u6A94\u3002\u8ACB\u5148\u57F7\u884C `dbcli init`\u3002"
|
|
7752
|
-
},
|
|
7753
|
-
use: {
|
|
7754
|
-
description: "\u5207\u63DB\u6216\u986F\u793A\u9810\u8A2D\u8CC7\u6599\u5EAB\u9023\u7DDA",
|
|
7755
|
-
switched: "\u5DF2\u5207\u63DB\u9810\u8A2D\u9023\u7DDA\u70BA {name}",
|
|
7756
|
-
current: "\u76EE\u524D\u7684\u9810\u8A2D\u9023\u7DDA\uFF1A{name}",
|
|
7757
|
-
current_none: "\u76EE\u524D\u6C92\u6709\u9810\u8A2D\u9023\u7DDA\u3002\u8ACB\u4F7F\u7528 --use <name> \u6216\u8A2D\u5B9A DBCLI_CONNECTION\u3002",
|
|
7758
|
-
unset: "\u9810\u8A2D\u9023\u7DDA\u5DF2\u6E05\u7A7A\u3002\u57F7\u884C\u8CC7\u6599\u5EAB\u6307\u4EE4\u6642\u8ACB\u4F7F\u7528 --use <name> \u6216\u8A2D\u5B9A DBCLI_CONNECTION\u3002",
|
|
7759
|
-
unset_conflict: "--unset \u4E0D\u53EF\u8207\u9023\u7DDA\u540D\u7A31\u3001--list\u3001--confirm-production \u6216 --format json \u540C\u6642\u4F7F\u7528",
|
|
7760
|
-
requires_v2: "\u6B64\u529F\u80FD\u9700\u8981\u65B0\u683C\u5F0F\u8A2D\u5B9A\u3002\u8ACB\u4F7F\u7528 dbcli init --conn-name <\u540D\u7A31> \u5EFA\u7ACB\u591A\u9023\u7DDA\u8A2D\u5B9A",
|
|
7761
|
-
available: "\u53EF\u7528\u9023\u7DDA"
|
|
7762
|
-
},
|
|
7763
|
-
schema: {
|
|
7764
|
-
description: "\u6AA2\u7D22\u8868\u683C\u7D50\u69CB\u6216\u5217\u51FA\u6240\u6709\u8868\u683C",
|
|
7765
|
-
fetching: "\u6B63\u5728\u6293\u53D6\u6A21\u5F0F...",
|
|
7766
|
-
success: "\u6A21\u5F0F\u5DF2\u66F4\u65B0",
|
|
7767
|
-
not_found: "\u8CC7\u6599\u5EAB\u4E2D\u627E\u4E0D\u5230\u8868\u683C",
|
|
7768
|
-
refresh_prompt: "\u6A21\u5F0F\u6709\u8B8A\u66F4\u3002\u662F\u5426\u5957\u7528\uFF1F (y/n)\uFF1A",
|
|
7769
|
-
columns_header: "\u6B04\u4F4D",
|
|
7770
|
-
scanning_database: "\uD83D\uDD0D \u6B63\u5728\u6383\u63CF\u8CC7\u6599\u5EAB\u67B6\u69CB...",
|
|
7771
|
-
tables_found: "\uD83D\uDCCD \u627E\u5230 {count} \u500B\u8868\u683C\u3002\u6B63\u5728\u7372\u53D6\u67B6\u69CB\u8A73\u60C5...",
|
|
7772
|
-
processing_tables: " \u8655\u7406\u4E86 {processed}/{total} \u500B\u8868\u683C",
|
|
7773
|
-
schema_exists_warning: "\u26A0\uFE0F \u8CC7\u6599\u5EAB\u67B6\u69CB\u5DF2\u5B58\u5728\u65BC .dbcli",
|
|
7774
|
-
use_force_to_override: " \u4F7F\u7528 --force \u9032\u884C\u8986\u84CB\u800C\u7121\u9700\u78BA\u8A8D"
|
|
7775
|
-
},
|
|
7776
|
-
list: {
|
|
7777
|
-
description: "\u5217\u51FA\u8CC7\u6599\u5EAB\u4E2D\u7684\u6240\u6709\u8868\u683C",
|
|
7778
|
-
no_tables: "\u8CC7\u6599\u5EAB\u4E2D\u672A\u627E\u5230\u8868\u683C"
|
|
7779
|
-
},
|
|
7780
|
-
query: {
|
|
7781
|
-
description: "\u57F7\u884C SQL \u67E5\u8A62",
|
|
7782
|
-
executing: "\u6B63\u5728\u57F7\u884C\u67E5\u8A62...",
|
|
7783
|
-
no_results: "\u672A\u8FD4\u56DE\u4EFB\u4F55\u7D50\u679C",
|
|
7784
|
-
result_count: "\u7D50\u679C\uFF1A{count} \u5217"
|
|
8148
|
+
// src/core/config.ts
|
|
8149
|
+
import { join as join8 } from "path";
|
|
8150
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
8151
|
+
function assertNoConnectionSelectorOnV1(connectionName) {
|
|
8152
|
+
if (connectionName === void 0)
|
|
8153
|
+
return;
|
|
8154
|
+
throw new ConfigError(`\u9023\u7DDA '${connectionName}' \u4E0D\u5B58\u5728\uFF1A\u6B64\u5C08\u6848\u4F7F\u7528\u55AE\u4E00\u9023\u7DDA (v1) \u8A2D\u5B9A\uFF0C\u6C92\u6709\u5177\u540D\u9023\u7DDA\u3002` + " \u79FB\u9664 --use / DBCLI_CONNECTION\uFF0C\u6216\u6539\u7528 'dbcli init --conn-name <name>' \u5347\u7D1A\u70BA\u591A\u9023\u7DDA (v2) \u8A2D\u5B9A\u3002");
|
|
8155
|
+
}
|
|
8156
|
+
function setGlobalConnectionName(name) {
|
|
8157
|
+
_globalConnectionName = name;
|
|
8158
|
+
}
|
|
8159
|
+
function getGlobalConnectionName() {
|
|
8160
|
+
return _globalConnectionName;
|
|
8161
|
+
}
|
|
8162
|
+
async function getSchemaIsolationConnectionName(dbcliPath) {
|
|
8163
|
+
let effectiveName = getGlobalConnectionName();
|
|
8164
|
+
try {
|
|
8165
|
+
let storagePath = await resolveConfigStoragePath(dbcliPath);
|
|
8166
|
+
if (!((await Bun.file(storagePath).stat())?.isDirectory() ?? !1))
|
|
8167
|
+
return;
|
|
8168
|
+
let configJsonPath = join8(storagePath, "config.json"), configFile = Bun.file(configJsonPath);
|
|
8169
|
+
if (!await configFile.exists())
|
|
8170
|
+
return;
|
|
8171
|
+
let raw = JSON.parse(await configFile.text());
|
|
8172
|
+
if (detectConfigVersion(raw) !== 2)
|
|
8173
|
+
return;
|
|
8174
|
+
let v2Config = DbcliConfigV2Schema.parse(raw);
|
|
8175
|
+
return resolveConnection(v2Config, effectiveName).name;
|
|
8176
|
+
} catch {
|
|
8177
|
+
return;
|
|
8178
|
+
}
|
|
8179
|
+
}
|
|
8180
|
+
function isEnvReference(value) {
|
|
8181
|
+
return typeof value === "object" && value !== null && "$env" in value && typeof value.$env === "string";
|
|
8182
|
+
}
|
|
8183
|
+
function resolveEnvReferences(config, env, parentKey) {
|
|
8184
|
+
if (isEnvReference(config)) {
|
|
8185
|
+
let envKey = config.$env, value = resolveEnvRef(config, parentKey ?? "<root>", env);
|
|
8186
|
+
if (parentKey === "port") {
|
|
8187
|
+
let portNum = parseInt(value, 10);
|
|
8188
|
+
if (isNaN(portNum) || portNum < 1 || portNum > 65535)
|
|
8189
|
+
throw new ConfigError(`${envKey} must be a valid port number (1-65535), got: ${value}`);
|
|
8190
|
+
return portNum;
|
|
8191
|
+
}
|
|
8192
|
+
return value;
|
|
8193
|
+
}
|
|
8194
|
+
if (Array.isArray(config))
|
|
8195
|
+
return config.map((item) => resolveEnvReferences(item, env, parentKey));
|
|
8196
|
+
if (typeof config === "object" && config !== null) {
|
|
8197
|
+
let resolved = {};
|
|
8198
|
+
for (let [key, value] of Object.entries(config))
|
|
8199
|
+
resolved[key] = resolveEnvReferences(value, env, key);
|
|
8200
|
+
return resolved;
|
|
8201
|
+
}
|
|
8202
|
+
return config;
|
|
8203
|
+
}
|
|
8204
|
+
function parseEnvPassword(content) {
|
|
8205
|
+
let match = content.match(/^DBCLI_PASSWORD=(.+)$/m);
|
|
8206
|
+
if (match?.[1] == null)
|
|
8207
|
+
return null;
|
|
8208
|
+
let raw = match[1].trim();
|
|
8209
|
+
return (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("'") && raw.endsWith("'")) && raw.length >= 2 ? raw.slice(1, -1) : raw;
|
|
8210
|
+
}
|
|
8211
|
+
var _globalConnectionName, DEFAULT_CONFIG, configModule;
|
|
8212
|
+
var init_config = __esm(() => {
|
|
8213
|
+
init_validation();
|
|
8214
|
+
init_errors3();
|
|
8215
|
+
init_config_error_format();
|
|
8216
|
+
init_config_v2();
|
|
8217
|
+
init_config_binding();
|
|
8218
|
+
init_public();
|
|
8219
|
+
init_config_mutation_guard();
|
|
8220
|
+
init_config_integrity();
|
|
8221
|
+
DEFAULT_CONFIG = {
|
|
8222
|
+
connection: {
|
|
8223
|
+
system: "postgresql",
|
|
8224
|
+
host: "localhost",
|
|
8225
|
+
port: 5432,
|
|
8226
|
+
user: "",
|
|
8227
|
+
password: "",
|
|
8228
|
+
database: ""
|
|
7785
8229
|
},
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
snippet_not_found: "\u627E\u4E0D\u5230\u7247\u6BB5\uFF1A{name}",
|
|
7791
|
-
dry_run_header: "Dry-run \u9810\u89BD\uFF08\u4E0D\u6703\u57F7\u884C\uFF09\uFF1A"
|
|
8230
|
+
permission: "query-only",
|
|
8231
|
+
schema: {},
|
|
8232
|
+
metadata: {
|
|
8233
|
+
version: "1.0"
|
|
7792
8234
|
},
|
|
8235
|
+
blacklist: { tables: [], columns: {} },
|
|
7793
8236
|
audit: {
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
invalid_output_format: "\u7121\u6CD5\u4F7F\u7528 --format {format}\uFF0C\u8ACB\u4F7F\u7528 text \u6216 json",
|
|
7903
|
-
sqlite_file_required: "SQLite \u9023\u7DDA\u9700\u8981\u4E00\u500B\u6A94\u6848\u8DEF\u5F91\u3002\u8ACB\u7528 --file <path>\uFF0C\u6216\u4EE5\u4E92\u52D5\u6A21\u5F0F\u57F7\u884C init\u3002"
|
|
7904
|
-
},
|
|
7905
|
-
success: {
|
|
7906
|
-
inserted: "\u6210\u529F\u63D2\u5165 {count} \u5217",
|
|
7907
|
-
updated: "\u6210\u529F\u66F4\u65B0 {count} \u5217",
|
|
7908
|
-
deleted: "\u6210\u529F\u522A\u9664 {count} \u5217",
|
|
7909
|
-
no_changes: "\u6C92\u6709\u8B8A\u66F4\u8981\u5957\u7528"
|
|
7910
|
-
},
|
|
7911
|
-
insert: {
|
|
7912
|
-
description: "\u63D2\u5165\u8CC7\u6599\u5230\u8868\u683C",
|
|
7913
|
-
prompt_data: "\u8F38\u5165\u8981\u63D2\u5165\u7684 JSON \u8CC7\u6599\uFF1A",
|
|
7914
|
-
confirm: "\u5C07 {count} \u5217\u63D2\u5165\u5230 {table}\uFF1F (y/n)\uFF1A",
|
|
7915
|
-
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"
|
|
7916
|
-
},
|
|
7917
|
-
update: {
|
|
7918
|
-
description: "\u66F4\u65B0\u8868\u683C\u4E2D\u7684\u8CC7\u6599",
|
|
7919
|
-
prompt_where: "\u8F38\u5165 WHERE \u5B50\u53E5 (\u4F8B\u5982\uFF1Aid=1)\uFF1A",
|
|
7920
|
-
prompt_set: '\u8F38\u5165 JSON \u66F4\u65B0 (\u4F8B\u5982\uFF1A{"status": "active"})\uFF1A',
|
|
7921
|
-
confirm: "\u66F4\u65B0 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A",
|
|
7922
|
-
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"
|
|
7923
|
-
},
|
|
7924
|
-
delete: {
|
|
7925
|
-
description: "\u522A\u9664\u8868\u683C\u4E2D\u7684\u8CC7\u6599",
|
|
7926
|
-
prompt_where: "\u8F38\u5165 WHERE \u5B50\u53E5 (\u4F8B\u5982\uFF1Aid=1)\uFF1A",
|
|
7927
|
-
confirm: "\u522A\u9664 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A",
|
|
7928
|
-
admin_only: "\u522A\u9664\u547D\u4EE4\u9700\u8981 data-admin \u6216 admin \u6B0A\u9650",
|
|
7929
|
-
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"
|
|
7930
|
-
},
|
|
7931
|
-
export: {
|
|
7932
|
-
description: "\u532F\u51FA\u67E5\u8A62\u7D50\u679C",
|
|
7933
|
-
formats: "\u652F\u63F4\u7684\u683C\u5F0F\uFF1Ajson\u3001csv",
|
|
7934
|
-
exported: "\u5DF2\u5C07 {count} \u5217\u532F\u51FA\u81F3 {file}"
|
|
7935
|
-
},
|
|
7936
|
-
upgrade: {
|
|
7937
|
-
description: "\u6AA2\u67E5\u66F4\u65B0\u4E26\u5C07 dbcli \u5347\u7D1A\u81F3\u6700\u65B0\u7248\u672C",
|
|
7938
|
-
checking: "\u6B63\u5728\u6AA2\u67E5\u66F4\u65B0...",
|
|
7939
|
-
already_up_to_date: "\u2713 \u5DF2\u7D93\u662F\u6700\u65B0\u7248\u672C (v{version})",
|
|
7940
|
-
new_version_available: "\u767C\u73FE\u65B0\u7248\u672C\uFF1Av{version}",
|
|
7941
|
-
current_version: "\u76EE\u524D\u7248\u672C\uFF1Av{version}",
|
|
7942
|
-
latest_version: "\u6700\u65B0\u7248\u672C\uFF1Av{version}",
|
|
7943
|
-
update_hint: '[\u63D0\u793A] dbcli v{version} \u5DF2\u767C\u5E03\u3002\u57F7\u884C "dbcli upgrade" \u9032\u884C\u5347\u7D1A\u3002',
|
|
7944
|
-
install_hint: '\u57F7\u884C "dbcli upgrade" \u5B89\u88DD\u66F4\u65B0\u3002',
|
|
7945
|
-
upgrading: "\u6B63\u5728\u5347\u7D1A...",
|
|
7946
|
-
success: "\u2713 \u6210\u529F\u5347\u7D1A\u81F3 v{version}",
|
|
7947
|
-
skill_recheck_hint: '\u63D0\u793A\uFF1A\u82E5\u5DF2\u5B89\u88DD AI \u6280\u80FD\uFF0C\u8ACB\u57F7\u884C "dbcli skill --install <platform>" \u78BA\u8A8D\u6280\u80FD\u70BA\u6700\u65B0\u7248\u672C\u3002',
|
|
7948
|
-
failed: "\u2717 \u5347\u7D1A\u5931\u6557\u3002\u8ACB\u5617\u8A66\u624B\u52D5\u57F7\u884C\uFF1A",
|
|
7949
|
-
manual_hint: "bun add -g @carllee1983/dbcli@latest",
|
|
7950
|
-
network_error: "\u26A0 \u7121\u6CD5\u6AA2\u67E5\u66F4\u65B0\uFF08\u7DB2\u8DEF\u932F\u8AA4\u6216\u8A3B\u518A\u8868\u4E0D\u53EF\u7528\uFF09"
|
|
7951
|
-
},
|
|
7952
|
-
skill: {
|
|
7953
|
-
description: "\u751F\u6210 AI \u6280\u80FD\u6587\u6A94",
|
|
7954
|
-
context_description: "\u751F\u6210 AI \u5354\u4F5C\u6240\u9700\u7684\u8CC7\u6599\u5EAB\u67B6\u69CB\u8207\u67E5\u8A62\u7247\u6BB5\u4E0A\u4E0B\u6587",
|
|
7955
|
-
installed: `\u6280\u80FD: {path}
|
|
7956
|
-
\u53C3\u8003: {referencePath}`,
|
|
7957
|
-
update_available: "\u4EE5\u4E0B\u5E73\u53F0\u7684\u6280\u80FD\u9700\u8981\u66F4\u65B0\uFF1A",
|
|
7958
|
-
update_hint: '\u57F7\u884C "dbcli skill --install <platform>" \u9032\u884C\u66F4\u65B0\u3002'
|
|
7959
|
-
},
|
|
7960
|
-
blacklist: {
|
|
7961
|
-
description: "\u7BA1\u7406\u654F\u611F\u8CC7\u6599\u9ED1\u540D\u55AE\u4EE5\u9632\u6B62 AI \u5B58\u53D6",
|
|
7962
|
-
list_title: "\u76EE\u524D\u9ED1\u540D\u55AE\u914D\u7F6E",
|
|
7963
|
-
tables_label: "\u5DF2\u9ED1\u540D\u55AE\u7684\u8868\u683C",
|
|
7964
|
-
columns_label: "\u5DF2\u9ED1\u540D\u55AE\u7684\u6B04\u4F4D",
|
|
7965
|
-
none: "\u76EE\u524D\u6C92\u6709\u9ED1\u540D\u55AE\u8868\u683C\u6216\u6B04\u4F4D",
|
|
7966
|
-
table_added: "\u2713 \u8868\u683C '{table}' \u5DF2\u65B0\u589E\u81F3\u9ED1\u540D\u55AE",
|
|
7967
|
-
table_removed: "\u2713 \u8868\u683C '{table}' \u5DF2\u5F9E\u9ED1\u540D\u55AE\u79FB\u9664",
|
|
7968
|
-
column_added: "\u2713 \u6B04\u4F4D '{table}.{column}' \u5DF2\u65B0\u589E\u81F3\u9ED1\u540D\u55AE",
|
|
7969
|
-
column_removed: "\u2713 \u6B04\u4F4D '{table}.{column}' \u5DF2\u5F9E\u9ED1\u540D\u55AE\u79FB\u9664",
|
|
7970
|
-
refuse_wildcard: "\u5DF2\u62D2\u7D55\uFF1A'{table}' \u542B\u6709\u842C\u7528\u5B57\u5143\uFF0C\u800C {system} \u4E0A\u7684\u6BD4\u5C0D\u662F\u9010\u5B57\u9032\u884C\u7684\u2014\u2014\u9019\u7B46\u8A2D\u5B9A\u6C38\u9060\u4E0D\u6703\u547D\u4E2D\u4EFB\u4F55\u6771\u897F\u3002\u8ACB\u5BEB\u51FA\u78BA\u5207\u7684\u540D\u7A31\u3002"
|
|
7971
|
-
},
|
|
7972
|
-
security: {
|
|
7973
|
-
columns_omitted: "\u5B89\u5168: {count} \u500B\u6B04\u4F4D\u6839\u64DA\u4F60\u7684\u9ED1\u540D\u55AE\u5DF2\u88AB\u96B1\u85CF"
|
|
7974
|
-
},
|
|
7975
|
-
warnings: {
|
|
7976
|
-
blacklist_override_used: "\u8B66\u544A: \u5DF2\u555F\u7528\u9ED1\u540D\u55AE\u8986\u84CB (DBCLI_OVERRIDE_BLACKLIST=true)\u3002\u57F7\u884C {operation} \u5728\u5DF2\u9ED1\u540D\u55AE\u7684\u8868\u683C '{table}' \u4E0A",
|
|
7977
|
-
redis_blacklist_override_active: "\u8B66\u544A: DBCLI_OVERRIDE_BLACKLIST=true \u5DF2\u505C\u7528 Redis \u9ED1\u540D\u55AE\u5F37\u5236\u57F7\u884C"
|
|
8237
|
+
enabled: !0,
|
|
8238
|
+
strict: !1,
|
|
8239
|
+
rotation: { ...DEFAULT_AUDIT_ROTATION }
|
|
8240
|
+
}
|
|
8241
|
+
};
|
|
8242
|
+
configModule = {
|
|
8243
|
+
async read(path, connectionName, options = {}) {
|
|
8244
|
+
let effectiveConnectionName = connectionName ?? _globalConnectionName, rawForDiagnostics;
|
|
8245
|
+
try {
|
|
8246
|
+
let binding = await readProjectBinding(path), storagePath = await resolveConfigStoragePath(path);
|
|
8247
|
+
if (binding) {
|
|
8248
|
+
if (!await Bun.file(join8(storagePath, "config.json")).exists())
|
|
8249
|
+
throw new ConfigError(`Bound dbcli config not found: ${join8(storagePath, "config.json")}`);
|
|
8250
|
+
}
|
|
8251
|
+
let isDirectory = !1;
|
|
8252
|
+
try {
|
|
8253
|
+
isDirectory = (await Bun.file(storagePath).stat())?.isDirectory() ?? !1;
|
|
8254
|
+
} catch {
|
|
8255
|
+
isDirectory = !1;
|
|
8256
|
+
}
|
|
8257
|
+
if (isDirectory) {
|
|
8258
|
+
let configPath = join8(storagePath, "config.json"), configFile = Bun.file(configPath);
|
|
8259
|
+
if (await configFile.exists()) {
|
|
8260
|
+
await assertAgentReadableFile(configPath);
|
|
8261
|
+
let content = await configFile.text();
|
|
8262
|
+
await assertConfigIntegrity(storagePath, content, { requireRecord: !0 });
|
|
8263
|
+
let config = JSON.parse(content);
|
|
8264
|
+
if (rawForDiagnostics = config, detectConfigVersion(config) === 2) {
|
|
8265
|
+
let v2Config = DbcliConfigV2Schema.parse(config), resolved = resolveConnection(v2Config, effectiveConnectionName);
|
|
8266
|
+
assertExplicitProductionSelection(resolved, effectiveConnectionName), await loadConnectionEnv(resolved, storagePath);
|
|
8267
|
+
let envLocalPath = join8(storagePath, ".env.local"), envLocalFile = Bun.file(envLocalPath), legacyPassword = null;
|
|
8268
|
+
if (await envLocalFile.exists()) {
|
|
8269
|
+
let envContent = await envLocalFile.text();
|
|
8270
|
+
if (legacyPassword = parseEnvPassword(envContent), legacyPassword && !process.env.DBCLI_PASSWORD)
|
|
8271
|
+
process.env.DBCLI_PASSWORD = legacyPassword;
|
|
8272
|
+
}
|
|
8273
|
+
let resolvedConnection = resolveEnvReferences(resolved.connection, process.env);
|
|
8274
|
+
if (!resolvedConnection.password && legacyPassword)
|
|
8275
|
+
resolvedConnection.password = legacyPassword;
|
|
8276
|
+
let schema = (v2Config.schemas ?? {})[resolved.name] ?? v2Config.schema;
|
|
8277
|
+
if (options.loadLayeredSchema !== !1)
|
|
8278
|
+
try {
|
|
8279
|
+
await Promise.resolve().then(() => init_schema_loader());
|
|
8280
|
+
let loader = new SchemaLayeredLoader(storagePath, {
|
|
8281
|
+
connectionName: resolved.name
|
|
8282
|
+
}), { cache, index } = await loader.initialize();
|
|
8283
|
+
if (index && Object.keys(index.tables).length > 0) {
|
|
8284
|
+
let layeredSchema = {};
|
|
8285
|
+
for (let tableName of Object.keys(index.tables)) {
|
|
8286
|
+
let s = await cache.getTableSchema(tableName);
|
|
8287
|
+
if (s)
|
|
8288
|
+
layeredSchema[tableName] = s;
|
|
8289
|
+
}
|
|
8290
|
+
if (Object.keys(layeredSchema).length > 0)
|
|
8291
|
+
schema = layeredSchema;
|
|
8292
|
+
}
|
|
8293
|
+
} catch {
|
|
8294
|
+
console.warn("Warning: Failed to load layered schema cache, falling back to config.json");
|
|
8295
|
+
}
|
|
8296
|
+
return {
|
|
8297
|
+
...DbcliConfigSchema.parse({
|
|
8298
|
+
connection: resolvedConnection,
|
|
8299
|
+
permission: resolved.permission,
|
|
8300
|
+
schema,
|
|
8301
|
+
metadata: v2Config.metadata,
|
|
8302
|
+
blacklist: v2Config.blacklist,
|
|
8303
|
+
audit: v2Config.audit,
|
|
8304
|
+
redis: v2Config.redis
|
|
8305
|
+
}),
|
|
8306
|
+
effectiveConnectionName: resolved.name,
|
|
8307
|
+
...resolved.environment && { effectiveEnvironment: resolved.environment }
|
|
8308
|
+
};
|
|
8309
|
+
}
|
|
8310
|
+
if (assertNoConnectionSelectorOnV1(effectiveConnectionName), config.connection?.sqlTls !== void 0)
|
|
8311
|
+
ConnectionConfigSchema.parse(config.connection);
|
|
8312
|
+
let resolvedConfig = resolveEnvReferences(config, process.env), envPath = join8(storagePath, ".env.local"), envFile = Bun.file(envPath);
|
|
8313
|
+
if (await envFile.exists()) {
|
|
8314
|
+
let envContent = await envFile.text(), password = parseEnvPassword(envContent);
|
|
8315
|
+
if (password && !resolvedConfig.connection.password)
|
|
8316
|
+
resolvedConfig.connection.password = password;
|
|
8317
|
+
}
|
|
8318
|
+
return DbcliConfigSchema.parse(resolvedConfig);
|
|
8319
|
+
}
|
|
8320
|
+
}
|
|
8321
|
+
let file = Bun.file(path);
|
|
8322
|
+
if (await file.exists()) {
|
|
8323
|
+
if (process.env.DBCLI_AGENT_MODE === "1")
|
|
8324
|
+
throw new ConfigError(`Agent mode refuses legacy single-file config: ${path}. Run the human/admin migration workflow to move it to V2 home storage first.`);
|
|
8325
|
+
assertNoConnectionSelectorOnV1(effectiveConnectionName);
|
|
8326
|
+
let content = await file.text(), raw = JSON.parse(content);
|
|
8327
|
+
if (rawForDiagnostics = raw, raw.connection?.sqlTls !== void 0)
|
|
8328
|
+
ConnectionConfigSchema.parse(raw.connection);
|
|
8329
|
+
let resolved = resolveEnvReferences(raw, process.env);
|
|
8330
|
+
return DbcliConfigSchema.parse(resolved);
|
|
8331
|
+
}
|
|
8332
|
+
if (process.env.DBCLI_AGENT_MODE === "1")
|
|
8333
|
+
throw new ConfigError(`Agent mode refuses a missing config: ${path}. Run the human/admin setup workflow to provision it.`);
|
|
8334
|
+
return { ...DEFAULT_CONFIG };
|
|
8335
|
+
} catch (error) {
|
|
8336
|
+
if (error instanceof ConfigError)
|
|
8337
|
+
throw error;
|
|
8338
|
+
if (isZodError(error))
|
|
8339
|
+
throw new ConfigError(`Failed to read .dbcli config: \u8A2D\u5B9A\u5167\u5BB9\u4E0D\u7B26\u5408\u7D50\u69CB
|
|
8340
|
+
${formatConfigValidationError(error, rawForDiagnostics)}`);
|
|
8341
|
+
if (error instanceof Error && error.message.includes("JSON"))
|
|
8342
|
+
throw new ConfigError(`Failed to parse .dbcli file: ${error.message}`);
|
|
8343
|
+
throw new ConfigError(`Failed to read .dbcli config: ${error instanceof Error ? error.message : String(error)}`);
|
|
8344
|
+
}
|
|
7978
8345
|
},
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
8346
|
+
validate(raw) {
|
|
8347
|
+
try {
|
|
8348
|
+
return DbcliConfigSchema.parse(raw);
|
|
8349
|
+
} catch (error) {
|
|
8350
|
+
if (isZodError(error))
|
|
8351
|
+
throw new ConfigError(`Invalid .dbcli config structure:
|
|
8352
|
+
${formatConfigValidationError(error, raw)}`);
|
|
8353
|
+
let errorMessage = error instanceof Error ? error.message : String(error);
|
|
8354
|
+
throw new ConfigError(`Invalid .dbcli config structure: ${errorMessage}`);
|
|
8355
|
+
}
|
|
7983
8356
|
},
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
add_enum_description: "\u5EFA\u7ACB\u5217\u8209\u578B\u5225\uFF08\u50C5 PostgreSQL\uFF09",
|
|
8004
|
-
alter_enum_description: "\u65B0\u589E\u5217\u8209\u503C",
|
|
8005
|
-
drop_enum_description: "\u522A\u9664\u5217\u8209\u578B\u5225"
|
|
8357
|
+
merge(existing, updates) {
|
|
8358
|
+
return {
|
|
8359
|
+
...existing,
|
|
8360
|
+
...updates,
|
|
8361
|
+
connection: {
|
|
8362
|
+
...existing.connection,
|
|
8363
|
+
...updates.connection || {}
|
|
8364
|
+
},
|
|
8365
|
+
schema: {
|
|
8366
|
+
...existing.schema,
|
|
8367
|
+
...updates.schema || {}
|
|
8368
|
+
},
|
|
8369
|
+
metadata: {
|
|
8370
|
+
...existing.metadata,
|
|
8371
|
+
...updates.metadata || {},
|
|
8372
|
+
createdAt: existing.metadata?.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
8373
|
+
version: existing.metadata?.version || "1.0"
|
|
8374
|
+
}
|
|
8375
|
+
};
|
|
8006
8376
|
},
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8377
|
+
async write(path, config) {
|
|
8378
|
+
try {
|
|
8379
|
+
assertConfigMutationApproved(), this.validate(config);
|
|
8380
|
+
let storagePath = await resolveConfigStoragePath(path), isDirectory = !1;
|
|
8381
|
+
try {
|
|
8382
|
+
isDirectory = (await Bun.file(storagePath).stat())?.isDirectory() ?? !1;
|
|
8383
|
+
} catch {
|
|
8384
|
+
isDirectory = !1;
|
|
8385
|
+
}
|
|
8386
|
+
if (isDirectory || path.endsWith(".dbcli") || path === storagePath && isDirectory)
|
|
8387
|
+
if (await mkdir3(storagePath, { recursive: !0 }), isEnvReference(config.connection.password)) {
|
|
8388
|
+
let configJson = JSON.stringify(config, null, 2);
|
|
8389
|
+
await writeConfigWithIntegrity(storagePath, configJson);
|
|
8390
|
+
} else {
|
|
8391
|
+
let password = config.connection.password, configWithoutPassword = {
|
|
8392
|
+
...config,
|
|
8393
|
+
connection: {
|
|
8394
|
+
...config.connection,
|
|
8395
|
+
password: void 0
|
|
8396
|
+
}
|
|
8397
|
+
};
|
|
8398
|
+
delete configWithoutPassword.connection.password;
|
|
8399
|
+
let configJson = JSON.stringify(configWithoutPassword, null, 2);
|
|
8400
|
+
if (await writeConfigWithIntegrity(storagePath, configJson), password) {
|
|
8401
|
+
let envPath = join8(storagePath, ".env.local"), envContent = `# Database Credentials - DO NOT commit to git
|
|
8023
8402
|
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
error_command_failed: "Command error: {message}",
|
|
8039
|
-
error_sql_failed: "SQL error: {message}",
|
|
8040
|
-
error_permission: "Permission denied. Required: {required} (current: {current})",
|
|
8041
|
-
error_blacklisted: "Table '{table}' is blacklisted.",
|
|
8042
|
-
sql_mode_hint: "SQL-only mode. Only SQL statements are accepted.",
|
|
8043
|
-
multiline_cancelled: "Multiline input cancelled.",
|
|
8044
|
-
timing_display: "Time: {ms}ms",
|
|
8045
|
-
rows_display: "{count} row(s)",
|
|
8046
|
-
unknown_command: "Unknown command: '{command}'. Type '.help' for available commands.",
|
|
8047
|
-
subcommand_prefix_hint: "This line is being read as SQL and is waiting for its `;`. If you meant the dbcli subcommand, type {command} instead. Ctrl-C cancels.",
|
|
8048
|
-
es: {
|
|
8049
|
-
parse_empty: "Empty request",
|
|
8050
|
-
parse_needs_method_path: 'Request requires a method and a path, e.g. "GET /index/_search"',
|
|
8051
|
-
parse_needs_path: "Request requires a path",
|
|
8052
|
-
refuse_dot_segment: "Refused: '{path}' contains a '..' segment once decoded, and dbcli cannot tell which path Elasticsearch would route it to. Write the path out without '..'.",
|
|
8053
|
-
refuse_unparseable: "Refused: '{path}' is not the request the server would receive. It cannot be parsed as a path.",
|
|
8054
|
-
refuse_not_canonical: "Refused: '{path}' is not the request the server would receive. Write it as '{canonical}'.",
|
|
8055
|
-
refuse_source_param: "Refused: `source` in the query string is a request body by another route. Write the body as JSON.",
|
|
8056
|
-
refuse_search_template: "Refused: '{path}' is a search template, whose body cannot be inspected \u2014 it is a string rendered on the cluster, or stored there. Write the query out in full.",
|
|
8057
|
-
refuse_string_body: "Refused: a quoted string request body is not supported. Write the body as JSON.",
|
|
8058
|
-
refuse_audit_strict: "Refused: the audit entry for this request could not be written, and audit.strict is on. Fix the audit sink (disk space, directory permissions, a stale lockfile in .dbcli/audit) or turn audit.strict off to accept unrecorded requests.",
|
|
8059
|
-
blacklist_index: "index '{index}' is blacklist-protected",
|
|
8060
|
-
blacklist_unscoped: "'{path}' names no index, so it cannot be checked against the blacklist. Scope the request to an index, e.g. GET /<index>/_search.",
|
|
8061
|
-
blacklist_field: "field '{field}' is blacklist-protected and cannot be named in a request \u2014 sorting, aggregating or scripting on it would return its values.",
|
|
8062
|
-
banner_title: "Elasticsearch shell \u2014 Kibana Dev Tools syntax",
|
|
8063
|
-
banner_hint: 'Enter "<METHOD> /<path>" then an optional JSON body; submit with a blank line. Try: GET /_cat/indices',
|
|
8064
|
-
banner_keys: 'Ctrl+C cancels the current block; Ctrl+D or "exit" quits.',
|
|
8065
|
-
block_cancelled: "(block cancelled)",
|
|
8066
|
-
goodbye: "Goodbye"
|
|
8403
|
+
DBCLI_PASSWORD=${password}
|
|
8404
|
+
`;
|
|
8405
|
+
await Bun.file(envPath).write(envContent);
|
|
8406
|
+
}
|
|
8407
|
+
}
|
|
8408
|
+
else {
|
|
8409
|
+
let json = JSON.stringify(config, null, 2);
|
|
8410
|
+
await Bun.file(path).write(json);
|
|
8411
|
+
}
|
|
8412
|
+
} catch (error) {
|
|
8413
|
+
if (error instanceof ConfigError)
|
|
8414
|
+
throw error;
|
|
8415
|
+
throw new ConfigError(`Failed to write .dbcli config: ${error instanceof Error ? error.message : String(error)}`);
|
|
8416
|
+
}
|
|
8067
8417
|
}
|
|
8068
8418
|
};
|
|
8069
8419
|
});
|
|
8070
8420
|
|
|
8071
|
-
//
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
error_sql_failed: "SQL \u932F\u8AA4\uFF1A{message}",
|
|
8087
|
-
error_permission: "\u6B0A\u9650\u4E0D\u8DB3\u3002\u9700\u8981\uFF1A{required}\uFF08\u76EE\u524D\uFF1A{current}\uFF09",
|
|
8088
|
-
error_blacklisted: "\u8868\u683C '{table}' \u5DF2\u88AB\u5217\u5165\u9ED1\u540D\u55AE\u3002",
|
|
8089
|
-
sql_mode_hint: "\u7D14 SQL \u6A21\u5F0F\u3002\u50C5\u63A5\u53D7 SQL \u8A9E\u53E5\u3002",
|
|
8090
|
-
multiline_cancelled: "\u5DF2\u53D6\u6D88\u591A\u884C\u8F38\u5165\u3002",
|
|
8091
|
-
timing_display: "\u8017\u6642\uFF1A{ms}ms",
|
|
8092
|
-
rows_display: "{count} \u7B46\u8CC7\u6599",
|
|
8093
|
-
unknown_command: "\u672A\u77E5\u6307\u4EE4\uFF1A'{command}'\u3002\u8F38\u5165 '.help' \u67E5\u770B\u53EF\u7528\u6307\u4EE4\u3002",
|
|
8094
|
-
subcommand_prefix_hint: "\u9019\u4E00\u884C\u88AB\u7576\u6210 SQL\uFF0C\u6B63\u5728\u7B49\u5206\u865F\u3002\u5982\u679C\u4F60\u8981\u7684\u662F dbcli \u5B50\u6307\u4EE4\uFF0C\u8ACB\u6539\u6253 {command}\u3002Ctrl-C \u53EF\u53D6\u6D88\u3002",
|
|
8095
|
-
es: {
|
|
8096
|
-
parse_empty: "\u7A7A\u7684\u8ACB\u6C42",
|
|
8097
|
-
parse_needs_method_path: '\u8ACB\u6C42\u9700\u8981\u4E00\u500B method \u8207\u4E00\u500B path\uFF0C\u4F8B\u5982 "GET /index/_search"',
|
|
8098
|
-
parse_needs_path: "\u8ACB\u6C42\u9700\u8981\u4E00\u500B path",
|
|
8099
|
-
refuse_dot_segment: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u89E3\u78BC\u5F8C\u542B\u6709 '..' \u5340\u6BB5\uFF0Cdbcli \u7121\u6CD5\u5224\u65B7 Elasticsearch \u6703\u628A\u5B83\u8DEF\u7531\u5230\u54EA\u500B\u8DEF\u5F91\u3002\u8ACB\u628A\u8DEF\u5F91\u5BEB\u6210\u4E0D\u542B '..' \u7684\u5F62\u5F0F\u3002",
|
|
8100
|
-
refuse_unparseable: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u4E0D\u662F\u4F3A\u670D\u5668\u6703\u6536\u5230\u7684\u90A3\u500B\u8ACB\u6C42\u3002\u5B83\u7121\u6CD5\u88AB\u89E3\u6790\u6210\u4E00\u500B\u8DEF\u5F91\u3002",
|
|
8101
|
-
refuse_not_canonical: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u4E0D\u662F\u4F3A\u670D\u5668\u6703\u6536\u5230\u7684\u90A3\u500B\u8ACB\u6C42\u3002\u8ACB\u5BEB\u6210 '{canonical}'\u3002",
|
|
8102
|
-
refuse_source_param: "\u5DF2\u62D2\u7D55\uFF1Aquery string \u88E1\u7684 `source` \u662F\u53E6\u4E00\u689D\u8DEF\u5F91\u4E0A\u7684 request body\u3002\u8ACB\u628A body \u5BEB\u6210 JSON\u3002",
|
|
8103
|
-
refuse_search_template: "\u5DF2\u62D2\u7D55\uFF1A'{path}' \u662F search template\uFF0C\u5B83\u7684 body \u7121\u6CD5\u6AA2\u67E5\u2014\u2014\u90A3\u662F\u4E00\u500B\u5728\u53E2\u96C6\u4E0A\u7B97\u51FA\u4F86\u7684\u5B57\u4E32\uFF0C\u6216\u8005\u6839\u672C\u5B58\u5728\u53E2\u96C6\u88E1\u3002\u8ACB\u628A\u67E5\u8A62\u5B8C\u6574\u5BEB\u51FA\u4F86\u3002",
|
|
8104
|
-
refuse_string_body: "\u5DF2\u62D2\u7D55\uFF1A\u4E0D\u652F\u63F4\u4EE5\u5F15\u865F\u5B57\u4E32\u5F62\u5F0F\u5BEB\u7684 request body\u3002\u8ACB\u628A body \u5BEB\u6210 JSON\u3002",
|
|
8105
|
-
refuse_audit_strict: "\u5DF2\u62D2\u7D55\uFF1A\u9019\u500B\u8ACB\u6C42\u7684\u7A3D\u6838\u7D00\u9304\u5BEB\u4E0D\u51FA\u53BB\uFF0C\u800C audit.strict \u662F\u958B\u7684\u3002\u8ACB\u4FEE\u597D\u7A3D\u6838\u8F38\u51FA\u7AEF\uFF08\u78C1\u789F\u7A7A\u9593\u3001\u76EE\u9304\u6B0A\u9650\u3001.dbcli/audit \u88E1\u6B98\u7559\u7684 lockfile\uFF09\uFF0C\u6216\u95DC\u6389 audit.strict \u4EE5\u63A5\u53D7\u6C92\u6709\u7D00\u9304\u7684\u8ACB\u6C42\u3002",
|
|
8106
|
-
blacklist_index: "\u7D22\u5F15 '{index}' \u53D7\u9ED1\u540D\u55AE\u4FDD\u8B77",
|
|
8107
|
-
blacklist_unscoped: "'{path}' \u6307\u4E0D\u51FA\u4EFB\u4F55\u7D22\u5F15\uFF0C\u56E0\u6B64\u7121\u6CD5\u5C0D\u7167\u9ED1\u540D\u55AE\u6AA2\u67E5\u3002\u8ACB\u628A\u8ACB\u6C42\u9650\u5B9A\u5230\u67D0\u500B\u7D22\u5F15\uFF0C\u4F8B\u5982 GET /<index>/_search\u3002",
|
|
8108
|
-
blacklist_field: "\u6B04\u4F4D '{field}' \u53D7\u9ED1\u540D\u55AE\u4FDD\u8B77\uFF0C\u4E0D\u80FD\u51FA\u73FE\u5728\u8ACB\u6C42\u88E1\u2014\u2014\u5C0D\u5B83\u6392\u5E8F\u3001\u805A\u5408\u6216\u8DD1 script \u90FD\u6703\u628A\u5B83\u7684\u503C\u53D6\u56DE\u4F86\u3002",
|
|
8109
|
-
banner_title: "Elasticsearch shell \u2014\u2014 Kibana Dev Tools \u8A9E\u6CD5",
|
|
8110
|
-
banner_hint: '\u8F38\u5165 "<METHOD> /<path>"\uFF0C\u63A5\u8457\u5BEB\u53EF\u9078\u7684 JSON body\uFF1B\u7A7A\u4E00\u884C\u9001\u51FA\u3002\u53EF\u4EE5\u5148\u8A66\uFF1AGET /_cat/indices',
|
|
8111
|
-
banner_keys: 'Ctrl+C \u53D6\u6D88\u76EE\u524D\u9019\u500B block\uFF1BCtrl+D \u6216 "exit" \u96E2\u958B\u3002',
|
|
8112
|
-
block_cancelled: "\uFF08block \u5DF2\u53D6\u6D88\uFF09",
|
|
8113
|
-
goodbye: "\u518D\u898B"
|
|
8114
|
-
}
|
|
8115
|
-
};
|
|
8421
|
+
// src/core/correlation-id.ts
|
|
8422
|
+
function isCorrelationId(value) {
|
|
8423
|
+
return typeof value === "string" && CORRELATION_ID_PATTERN.test(value);
|
|
8424
|
+
}
|
|
8425
|
+
function setGlobalCorrelationId(value) {
|
|
8426
|
+
if (value !== void 0 && !isCorrelationId(value))
|
|
8427
|
+
throw Error("Invalid correlation ID");
|
|
8428
|
+
globalCorrelationId = value;
|
|
8429
|
+
}
|
|
8430
|
+
function getGlobalCorrelationId() {
|
|
8431
|
+
return globalCorrelationId;
|
|
8432
|
+
}
|
|
8433
|
+
var CORRELATION_ID_PATTERN, globalCorrelationId;
|
|
8434
|
+
var init_correlation_id = __esm(() => {
|
|
8435
|
+
CORRELATION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/;
|
|
8116
8436
|
});
|
|
8117
8437
|
|
|
8118
|
-
//
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
confirm_prompt: "Proceed with this operation?",
|
|
8139
|
-
gate_summary: "This will {operation} rows in {table}.",
|
|
8140
|
-
gate_summary_untargeted: "This will run a {operation} statement.",
|
|
8141
|
-
gate_statement: "Statement:",
|
|
8142
|
-
gate_cancelled: "Cancelled. Nothing was run.",
|
|
8143
|
-
gate_full_table_warning: "\u26A0\uFE0F This affects EVERY row in {table} and cannot be undone.",
|
|
8144
|
-
gate_ddl_warning: "\u26A0\uFE0F This destroys {table} and cannot be undone.",
|
|
8145
|
-
gate_ddl_warning_untargeted: "\u26A0\uFE0F This is a destructive schema change and cannot be undone.",
|
|
8146
|
-
gate_unparseable_warning: "\u26A0\uFE0F dbcli could not parse this statement, so it is treated as affecting everything.",
|
|
8147
|
-
gate_multiple_statements_warning: "\u26A0\uFE0F This is more than one statement, and dbcli will not judge them one at a time.",
|
|
8148
|
-
gate_multi_table_warning: "This write joins another table. How many rows of the target it touches depends on the data, not on the statement, so it cannot be checked before it runs.",
|
|
8149
|
-
gate_nested_write_warning: "\u26A0\uFE0F This statement carries a second write inside it, and dbcli will not read the tier of that write off this one.",
|
|
8150
|
-
gate_typed_prompt: "Type {phrase} to run it",
|
|
8151
|
-
gate_typed_mismatch: "That did not match {phrase}. Nothing was run.",
|
|
8152
|
-
gate_refused: "Refused: this statement is not limited to specific rows and nobody is here to confirm it (reason={reason}).",
|
|
8153
|
-
gate_refused_in_shell: "Refused: a dbcli subcommand started from the shell has no way to ask you, even though you are right here (reason={reason}).",
|
|
8154
|
-
gate_remedy_no_where: "Add a WHERE clause \u2014 WHERE 1=1 if you really do mean every row \u2014 or a LIMIT, then run it again.",
|
|
8155
|
-
gate_remedy_ddl_destruction: "No flag skips this. Run it from an interactive terminal, or apply the schema change through a reviewed migration.",
|
|
8156
|
-
gate_remedy_unparseable: "dbcli could not parse this statement and will not guess. Simplify it, or run it from an interactive terminal.",
|
|
8157
|
-
gate_remedy_multiple_statements: "Run one statement per command.",
|
|
8158
|
-
gate_remedy_multi_table: "Rewrite it as a single-table statement \u2014 move the other table into a subquery in the WHERE \u2014 or run it where someone can confirm it.",
|
|
8159
|
-
gate_remedy_nested_write: "Run the nested write as its own statement, or run this one where someone can confirm it.",
|
|
8160
|
-
gate_remedy_non_unique_where: "Match on a primary key or a uniquely indexed column, or run it from an interactive terminal.",
|
|
8161
|
-
gate_remedy_in_shell: "Type the statement at the dbcli> prompt instead \u2014 there the shell asks you to type the target table name.",
|
|
8162
|
-
recovery_none: "This cannot be undone automatically \u2014 restore from a backup to get the previous values back.",
|
|
8163
|
-
recovery_retry: "Re-run with --recovery to write a recovery plan, then read it with: dbcli recover",
|
|
8164
|
-
blacklist_hint: "Run `dbcli blacklist list` to see what is protected"
|
|
8165
|
-
};
|
|
8166
|
-
});
|
|
8438
|
+
// src/utils/connection-timeout.ts
|
|
8439
|
+
function setGlobalConnectionTimeout(timeout) {
|
|
8440
|
+
_globalConnectionTimeout = timeout;
|
|
8441
|
+
}
|
|
8442
|
+
function setGlobalStatementTimeout(timeout) {
|
|
8443
|
+
_globalStatementTimeout = timeout;
|
|
8444
|
+
}
|
|
8445
|
+
function resolveConnectionTimeout(configured) {
|
|
8446
|
+
return _globalConnectionTimeout ?? configured;
|
|
8447
|
+
}
|
|
8448
|
+
function resolveStatementTimeout(configured) {
|
|
8449
|
+
return _globalStatementTimeout ?? configured;
|
|
8450
|
+
}
|
|
8451
|
+
function withResolvedTimeout(options) {
|
|
8452
|
+
let timeout = resolveConnectionTimeout(options.timeout), statementTimeout = resolveStatementTimeout(options.statementTimeout);
|
|
8453
|
+
if (timeout === options.timeout && statementTimeout === options.statementTimeout)
|
|
8454
|
+
return options;
|
|
8455
|
+
return { ...options, timeout, statementTimeout };
|
|
8456
|
+
}
|
|
8457
|
+
var _globalConnectionTimeout, _globalStatementTimeout;
|
|
8167
8458
|
|
|
8168
|
-
//
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
matched_nothing: "{table} \u6C92\u6709\u4EFB\u4F55\u5217\u7B26\u5408\u689D\u4EF6\uFF0C\u8CC7\u6599\u672A\u8B8A\u52D5",
|
|
8176
|
-
cancelled: "\u5DF2\u53D6\u6D88\uFF0C{table} \u672A\u8B8A\u52D5",
|
|
8177
|
-
dry_run: "\u50C5\u9810\u89BD\uFF0C{table} \u672A\u8B8A\u52D5",
|
|
8178
|
-
elapsed: "\u8017\u6642 {seconds} \u79D2",
|
|
8179
|
-
failed: "\u5931\u6557\uFF1A{reason}",
|
|
8180
|
-
unknown_error: "\u672A\u77E5\u7684\u932F\u8AA4",
|
|
8181
|
-
confirm_sql: "\u7522\u751F\u7684 SQL\uFF1A",
|
|
8182
|
-
confirm_command: "\u5373\u5C07\u57F7\u884C\u7684\u6307\u4EE4\uFF1A",
|
|
8183
|
-
confirm_params: "\u53C3\u6578\uFF1A",
|
|
8184
|
-
confirm_destructive_warning: "\u26A0\uFE0F \u8B66\u544A\uFF1ADELETE \u5177\u7834\u58DE\u6027\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\uFF01",
|
|
8185
|
-
confirm_destructive_prompt: "\u78BA\u5B9A\u8981\u57F7\u884C\u9019\u500B DELETE \u55CE\uFF1F\u6B64\u64CD\u4F5C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
8186
|
-
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",
|
|
8187
|
-
confirm_ddl_prompt: "\u78BA\u5B9A\u8981\u57F7\u884C\u9019\u500B {operation} \u55CE\uFF1F\u6B64\u64CD\u4F5C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
8188
|
-
confirm_prompt: "\u8981\u7E7C\u7E8C\u57F7\u884C\u9019\u500B\u64CD\u4F5C\u55CE\uFF1F",
|
|
8189
|
-
gate_summary: "\u9019\u53E5\u6703\u5C0D {table} \u57F7\u884C {operation}\u3002",
|
|
8190
|
-
gate_summary_untargeted: "\u9019\u53E5\u6703\u57F7\u884C {operation}\u3002",
|
|
8191
|
-
gate_statement: "\u8A9E\u53E5\uFF1A",
|
|
8192
|
-
gate_cancelled: "\u5DF2\u53D6\u6D88\uFF0C\u6C92\u6709\u57F7\u884C\u4EFB\u4F55\u8A9E\u53E5\u3002",
|
|
8193
|
-
gate_full_table_warning: "\u26A0\uFE0F \u9019\u53E5\u6703\u5F71\u97FF {table} \u7684\u6BCF\u4E00\u5217\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
8194
|
-
gate_ddl_warning: "\u26A0\uFE0F \u9019\u53E5\u6703\u6467\u6BC0 {table}\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
8195
|
-
gate_ddl_warning_untargeted: "\u26A0\uFE0F \u9019\u662F\u7834\u58DE\u6027\u7684 schema \u8B8A\u66F4\uFF0C\u57F7\u884C\u5F8C\u7121\u6CD5\u5FA9\u539F\u3002",
|
|
8196
|
-
gate_unparseable_warning: "\u26A0\uFE0F dbcli \u7121\u6CD5\u89E3\u6790\u9019\u53E5\uFF0C\u56E0\u6B64\u4E00\u5F8B\u8996\u70BA\u6703\u5F71\u97FF\u5168\u90E8\u8CC7\u6599\u3002",
|
|
8197
|
-
gate_multiple_statements_warning: "\u26A0\uFE0F \u9019\u88E1\u4E0D\u53EA\u4E00\u53E5\u8A9E\u53E5\uFF0Cdbcli \u4E0D\u6703\u9010\u53E5\u5224\u65B7\u3002",
|
|
8198
|
-
gate_multi_table_warning: "\u9019\u500B\u5BEB\u5165\u63A5\u4E86\u53E6\u4E00\u5F35\u8868\u3002\u5B83\u6703\u52D5\u5230\u76EE\u6A19\u8868\u5E7E\u5217\u53D6\u6C7A\u65BC\u8CC7\u6599\u800C\u4E0D\u662F\u8A9E\u53E5\u672C\u8EAB\uFF0C\u57F7\u884C\u524D\u7121\u6CD5\u6AA2\u67E5\u3002",
|
|
8199
|
-
gate_nested_write_warning: "\u26A0\uFE0F \u9019\u53E5\u88E1\u9762\u9084\u593E\u8457\u53E6\u4E00\u500B\u5BEB\u5165\uFF0Cdbcli \u4E0D\u6703\u62FF\u9019\u53E5\u7684\u5C64\u7D1A\u4EE3\u66FF\u90A3\u500B\u5BEB\u5165\u7684\u5C64\u7D1A\u3002",
|
|
8200
|
-
gate_typed_prompt: "\u8F38\u5165 {phrase} \u624D\u6703\u57F7\u884C",
|
|
8201
|
-
gate_typed_mismatch: "\u8F38\u5165\u7684\u5167\u5BB9\u8207 {phrase} \u4E0D\u7B26\uFF0C\u6C92\u6709\u57F7\u884C\u4EFB\u4F55\u8A9E\u53E5\u3002",
|
|
8202
|
-
gate_refused: "\u5DF2\u62D2\u7D55\uFF1A\u9019\u53E5\u6C92\u6709\u9650\u5B9A\u8981\u52D5\u54EA\u4E9B\u5217\uFF0C\u800C\u73FE\u5728\u6C92\u6709\u4EBA\u80FD\u78BA\u8A8D\uFF08reason={reason}\uFF09\u3002",
|
|
8203
|
-
gate_refused_in_shell: "\u5DF2\u62D2\u7D55\uFF1A\u5F9E shell \u555F\u52D5\u7684 dbcli \u5B50\u6307\u4EE4\u6C92\u6709\u8FA6\u6CD5\u5411\u4F60\u63D0\u554F\uFF0C\u5373\u4F7F\u4F60\u4EBA\u5C31\u5728\u9019\u88E1\uFF08reason={reason}\uFF09\u3002",
|
|
8204
|
-
gate_remedy_no_where: "\u88DC\u4E0A WHERE\uFF08\u771F\u7684\u8981\u5168\u8868\u5C31\u5BEB WHERE 1=1\uFF09\u6216 LIMIT\uFF0C\u7136\u5F8C\u91CD\u8DD1\u3002",
|
|
8205
|
-
gate_remedy_ddl_destruction: "\u6C92\u6709\u4EFB\u4F55\u65D7\u6A19\u53EF\u4EE5\u8DF3\u904E\u9019\u4E00\u95DC\u3002\u8ACB\u5728\u4E92\u52D5\u7D42\u7AEF\u4E0B\u57F7\u884C\uFF0C\u6216\u6539\u8D70\u7D93\u904E\u5BE9\u67E5\u7684 migration\u3002",
|
|
8206
|
-
gate_remedy_unparseable: "dbcli \u7121\u6CD5\u89E3\u6790\u9019\u53E5\uFF0C\u4E5F\u4E0D\u6703\u7528\u731C\u7684\u3002\u8ACB\u7C21\u5316\u8A9E\u53E5\uFF0C\u6216\u5728\u4E92\u52D5\u7D42\u7AEF\u4E0B\u57F7\u884C\u3002",
|
|
8207
|
-
gate_remedy_multiple_statements: "\u4E00\u6B21\u53EA\u57F7\u884C\u4E00\u53E5\u8A9E\u53E5\u3002",
|
|
8208
|
-
gate_remedy_multi_table: "\u6539\u5BEB\u6210\u55AE\u8868\u8A9E\u53E5\u2014\u2014\u628A\u53E6\u4E00\u5F35\u8868\u79FB\u9032 WHERE \u7684\u5B50\u67E5\u8A62\u88E1\u2014\u2014\u6216\u6539\u5728\u6709\u4EBA\u53EF\u4EE5\u78BA\u8A8D\u7684\u5730\u65B9\u57F7\u884C\u3002",
|
|
8209
|
-
gate_remedy_nested_write: "\u628A\u88E1\u9762\u90A3\u500B\u5BEB\u5165\u62C6\u6210\u7368\u7ACB\u7684\u4E00\u53E5\u4F86\u57F7\u884C\uFF0C\u6216\u6539\u5728\u6709\u4EBA\u53EF\u4EE5\u78BA\u8A8D\u7684\u5730\u65B9\u57F7\u884C\u3002",
|
|
8210
|
-
gate_remedy_non_unique_where: "\u6539\u7528\u4E3B\u9375\u6216\u552F\u4E00\u7D22\u5F15\u6B04\u4F4D\u7576\u689D\u4EF6\uFF0C\u6216\u5728\u4E92\u52D5\u7D42\u7AEF\u4E0B\u57F7\u884C\u3002",
|
|
8211
|
-
gate_remedy_in_shell: "\u8ACB\u76F4\u63A5\u5728 dbcli> \u63D0\u793A\u7B26\u6253\u9019\u53E5\u8A9E\u53E5\u2014\u2014\u5728\u90A3\u88E1 dbcli \u6703\u8ACB\u4F60\u6253\u51FA\u76EE\u6A19\u8CC7\u6599\u8868\u7684\u540D\u7A31\u4F86\u78BA\u8A8D\u3002",
|
|
8212
|
-
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",
|
|
8213
|
-
recovery_retry: "\u52A0\u4E0A --recovery \u91CD\u8DD1\u53EF\u7522\u751F\u56DE\u5FA9\u8A08\u756B\uFF0C\u518D\u7528 dbcli recover \u8B80\u53D6",
|
|
8214
|
-
blacklist_hint: "\u57F7\u884C `dbcli blacklist list` \u67E5\u770B\u53D7\u4FDD\u8B77\u7684\u9805\u76EE"
|
|
8215
|
-
};
|
|
8459
|
+
// src/core/connection-selector.ts
|
|
8460
|
+
function createConnectionSelectorOption() {
|
|
8461
|
+
return new Option2("--use <connection>", CONNECTION_SELECTOR_DESCRIPTION);
|
|
8462
|
+
}
|
|
8463
|
+
var CONNECTION_SELECTOR_DESCRIPTION = "Use a specific named connection for this invocation (v2 config)";
|
|
8464
|
+
var init_connection_selector = __esm(() => {
|
|
8465
|
+
init_esm();
|
|
8216
8466
|
});
|
|
8217
8467
|
|
|
8218
|
-
// src/
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
static getInstance() {
|
|
8228
|
-
if (!MessageLoader.instance)
|
|
8229
|
-
MessageLoader.instance = new MessageLoader;
|
|
8230
|
-
return MessageLoader.instance;
|
|
8231
|
-
}
|
|
8232
|
-
loadMessages() {
|
|
8233
|
-
if (this.currentLang !== "en")
|
|
8234
|
-
this.messages = BUNDLED_MESSAGES[this.currentLang] || {};
|
|
8235
|
-
this.fallbackMessages = BUNDLED_MESSAGES.en || {};
|
|
8236
|
-
}
|
|
8237
|
-
t(key) {
|
|
8238
|
-
let parts = key.split("."), value = this.messages;
|
|
8239
|
-
for (let part of parts)
|
|
8240
|
-
if (value && typeof value === "object")
|
|
8241
|
-
value = value[part];
|
|
8242
|
-
else {
|
|
8243
|
-
value = void 0;
|
|
8244
|
-
break;
|
|
8245
|
-
}
|
|
8246
|
-
if (typeof value === "string")
|
|
8247
|
-
return value;
|
|
8248
|
-
value = this.fallbackMessages;
|
|
8249
|
-
for (let part of parts)
|
|
8250
|
-
if (value && typeof value === "object")
|
|
8251
|
-
value = value[part];
|
|
8252
|
-
else {
|
|
8253
|
-
value = void 0;
|
|
8254
|
-
break;
|
|
8255
|
-
}
|
|
8256
|
-
if (typeof value === "string")
|
|
8257
|
-
return value;
|
|
8258
|
-
return key;
|
|
8468
|
+
// src/utils/config-path.ts
|
|
8469
|
+
function resolveConfigPath(command, options, fallback = ".dbcli") {
|
|
8470
|
+
for (let current = command;current; current = current.parent ?? void 0) {
|
|
8471
|
+
let source = current.getOptionValueSource("config");
|
|
8472
|
+
if (source && source !== "default") {
|
|
8473
|
+
let currentOptions = current.opts();
|
|
8474
|
+
if (typeof currentOptions.config === "string" && currentOptions.config.length > 0)
|
|
8475
|
+
return currentOptions.config;
|
|
8476
|
+
}
|
|
8259
8477
|
}
|
|
8260
|
-
|
|
8261
|
-
|
|
8478
|
+
if (typeof options?.config === "string" && options.config.length > 0 && options.config !== fallback)
|
|
8479
|
+
return options.config;
|
|
8480
|
+
for (let current = command;current; current = current.parent ?? void 0) {
|
|
8481
|
+
let source = current.getOptionValueSource("global");
|
|
8482
|
+
if (source && source !== "default" && current.opts().global === !0)
|
|
8483
|
+
return getGlobalConfigPath();
|
|
8262
8484
|
}
|
|
8263
|
-
|
|
8264
|
-
return
|
|
8485
|
+
if (options?.global === !0)
|
|
8486
|
+
return getGlobalConfigPath();
|
|
8487
|
+
if (typeof options?.config === "string" && options.config.length > 0)
|
|
8488
|
+
return options.config;
|
|
8489
|
+
return fallback;
|
|
8490
|
+
}
|
|
8491
|
+
var init_config_path = __esm(() => {
|
|
8492
|
+
init_config_binding();
|
|
8493
|
+
});
|
|
8494
|
+
|
|
8495
|
+
// src/utils/config-sidecar-path.ts
|
|
8496
|
+
import { dirname as dirname2, extname, join as join9 } from "path";
|
|
8497
|
+
function configSidecarPath(configPath, fileName) {
|
|
8498
|
+
let directory = extname(configPath) !== "" && !configPath.endsWith(".dbcli") ? dirname2(configPath) : configPath;
|
|
8499
|
+
return join9(directory, fileName);
|
|
8500
|
+
}
|
|
8501
|
+
var init_config_sidecar_path = () => {};
|
|
8502
|
+
|
|
8503
|
+
// src/utils/skill-check-cache.ts
|
|
8504
|
+
import { mkdir as mkdir5, rename as rename3, unlink as unlink4, writeFile as writeFile2 } from "fs/promises";
|
|
8505
|
+
import { dirname as dirname4 } from "path";
|
|
8506
|
+
function cachePath(configPath) {
|
|
8507
|
+
return configSidecarPath(configPath, CACHE_FILE_NAME);
|
|
8508
|
+
}
|
|
8509
|
+
async function readSkillCheckCache(configPath, dbcliVersion, options = {}) {
|
|
8510
|
+
try {
|
|
8511
|
+
let file = Bun.file(cachePath(configPath));
|
|
8512
|
+
if (!await file.exists())
|
|
8513
|
+
return null;
|
|
8514
|
+
let raw = await file.json();
|
|
8515
|
+
if (raw.version !== 1 || !Array.isArray(raw.outdated))
|
|
8516
|
+
return null;
|
|
8517
|
+
if (raw.dbcliVersion !== dbcliVersion)
|
|
8518
|
+
return null;
|
|
8519
|
+
if (typeof raw.checkedAt !== "number")
|
|
8520
|
+
return null;
|
|
8521
|
+
let age = (options.now ?? Date.now()) - raw.checkedAt;
|
|
8522
|
+
if (age < 0 || age > SKILL_CHECK_TTL_MS)
|
|
8523
|
+
return null;
|
|
8524
|
+
return raw.outdated;
|
|
8525
|
+
} catch {
|
|
8526
|
+
return null;
|
|
8265
8527
|
}
|
|
8266
8528
|
}
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
"
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8529
|
+
async function writeSkillCheckCache(configPath, dbcliVersion, outdated, options = {}) {
|
|
8530
|
+
let path = cachePath(configPath), payload = {
|
|
8531
|
+
version: 1,
|
|
8532
|
+
dbcliVersion,
|
|
8533
|
+
checkedAt: options.now ?? Date.now(),
|
|
8534
|
+
outdated
|
|
8535
|
+
};
|
|
8536
|
+
try {
|
|
8537
|
+
await mkdir5(dirname4(path), { recursive: !0 });
|
|
8538
|
+
let temporary = `${path}.tmp-${process.pid}`;
|
|
8539
|
+
await writeFile2(temporary, JSON.stringify(payload), "utf8"), await rename3(temporary, path);
|
|
8540
|
+
} catch {}
|
|
8541
|
+
}
|
|
8542
|
+
async function invalidateSkillCheckCache(configPath) {
|
|
8543
|
+
await unlink4(cachePath(configPath)).catch(() => {
|
|
8544
|
+
return;
|
|
8545
|
+
});
|
|
8546
|
+
}
|
|
8547
|
+
var SKILL_CHECK_TTL_MS = 86400000, CACHE_FILE_NAME = "skill-check.json";
|
|
8548
|
+
var init_skill_check_cache = __esm(() => {
|
|
8549
|
+
init_config_sidecar_path();
|
|
8550
|
+
});
|
|
8551
|
+
|
|
8552
|
+
// src/core/slow-query-advisory.ts
|
|
8553
|
+
function slowQueryThresholdFor(options) {
|
|
8554
|
+
if (options.recovery === !0)
|
|
8555
|
+
return 0;
|
|
8556
|
+
let threshold = options.slowMs ?? 1000;
|
|
8557
|
+
return assertValidSlowQueryThreshold(threshold), threshold;
|
|
8558
|
+
}
|
|
8559
|
+
function assertValidSlowQueryThreshold(value) {
|
|
8560
|
+
if (value === void 0)
|
|
8561
|
+
return;
|
|
8562
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
8563
|
+
throw Error("slow-ms must be a non-negative integer");
|
|
8564
|
+
}
|
|
8565
|
+
function parseSlowQueryThreshold(value) {
|
|
8566
|
+
if (!/^\d+$/.test(value))
|
|
8567
|
+
throw Error("slow-ms must be a non-negative integer");
|
|
8568
|
+
let parsed = Number(value);
|
|
8569
|
+
return assertValidSlowQueryThreshold(parsed), parsed;
|
|
8570
|
+
}
|
|
8571
|
+
function recommendationFor(system) {
|
|
8572
|
+
return system === void 0 || GUIDE_SLOW_QUERY_SYSTEMS.includes(system) ? "Review safely with: dbcli guide slow-query --format markdown. This hint runs no additional database diagnostics." : `Re-check the query shape and the filters it scans; dbcli ships no slow-query diagnostics for ${system}. This hint runs no additional database diagnostics.`;
|
|
8573
|
+
}
|
|
8574
|
+
function buildSlowQueryAdvisory(executionTimeMs, options) {
|
|
8575
|
+
let thresholdMs = slowQueryThresholdFor(options);
|
|
8576
|
+
if (thresholdMs === 0 || executionTimeMs === void 0 || executionTimeMs < thresholdMs)
|
|
8577
|
+
return;
|
|
8578
|
+
return {
|
|
8579
|
+
code: "SLOW_QUERY",
|
|
8580
|
+
executionTimeMs,
|
|
8581
|
+
thresholdMs,
|
|
8582
|
+
recommendation: recommendationFor(options.system)
|
|
8583
|
+
};
|
|
8584
|
+
}
|
|
8585
|
+
function attachSlowQueryAdvisory(result, options) {
|
|
8586
|
+
let advisory = buildSlowQueryAdvisory(result.executionTimeMs, options);
|
|
8587
|
+
if (!advisory)
|
|
8588
|
+
return result;
|
|
8589
|
+
return {
|
|
8590
|
+
...result,
|
|
8591
|
+
metadata: {
|
|
8592
|
+
statement: result.metadata?.statement ?? "UNKNOWN",
|
|
8593
|
+
...result.metadata,
|
|
8594
|
+
performanceAdvisory: advisory
|
|
8281
8595
|
}
|
|
8282
8596
|
};
|
|
8283
|
-
|
|
8597
|
+
}
|
|
8598
|
+
var GUIDE_SLOW_QUERY_SYSTEMS;
|
|
8599
|
+
var init_slow_query_advisory = __esm(() => {
|
|
8600
|
+
GUIDE_SLOW_QUERY_SYSTEMS = ["mariadb", "mysql", "postgresql", "redis"];
|
|
8284
8601
|
});
|
|
8285
8602
|
|
|
8286
8603
|
// src/program-root.ts
|
|
@@ -8460,6 +8777,8 @@ function removeConnection(config, name) {
|
|
|
8460
8777
|
return updated;
|
|
8461
8778
|
}
|
|
8462
8779
|
var init_config_v2_mutations = __esm(() => {
|
|
8780
|
+
init_message_loader();
|
|
8781
|
+
init_capabilities();
|
|
8463
8782
|
init_validation();
|
|
8464
8783
|
});
|
|
8465
8784
|
|
|
@@ -8792,250 +9111,6 @@ var init_types2 = __esm(() => {
|
|
|
8792
9111
|
};
|
|
8793
9112
|
});
|
|
8794
9113
|
|
|
8795
|
-
// src/adapters/capabilities.ts
|
|
8796
|
-
function cap(status, tier, note) {
|
|
8797
|
-
return Object.freeze({ status, tier, note });
|
|
8798
|
-
}
|
|
8799
|
-
function getEngineCapabilities(system) {
|
|
8800
|
-
return Object.freeze({ ...ENGINE_CAPABILITIES[system] });
|
|
8801
|
-
}
|
|
8802
|
-
function getEngineCapability(system, key) {
|
|
8803
|
-
return ENGINE_CAPABILITIES[system][key];
|
|
8804
|
-
}
|
|
8805
|
-
var COMMAND_CAPABILITY_KEYS, ENGINE_INDEPENDENT, SQL_BASE, SQL_ONLY_UNSUPPORTED, SQL_MODE_LIMITED, ENGINE_CAPABILITIES;
|
|
8806
|
-
var init_capabilities = __esm(() => {
|
|
8807
|
-
COMMAND_CAPABILITY_KEYS = Object.freeze([
|
|
8808
|
-
"init",
|
|
8809
|
-
"use",
|
|
8810
|
-
"list",
|
|
8811
|
-
"schema",
|
|
8812
|
-
"schemaSingle",
|
|
8813
|
-
"schemaFullScan",
|
|
8814
|
-
"query",
|
|
8815
|
-
"lint",
|
|
8816
|
-
"queryOutput",
|
|
8817
|
-
"queryLimitGuard",
|
|
8818
|
-
"q",
|
|
8819
|
-
"queries",
|
|
8820
|
-
"insert",
|
|
8821
|
-
"update",
|
|
8822
|
-
"delete",
|
|
8823
|
-
"export",
|
|
8824
|
-
"blacklistRead",
|
|
8825
|
-
"blacklist",
|
|
8826
|
-
"check",
|
|
8827
|
-
"diff",
|
|
8828
|
-
"migrate",
|
|
8829
|
-
"shell",
|
|
8830
|
-
"status",
|
|
8831
|
-
"doctor",
|
|
8832
|
-
"inspect",
|
|
8833
|
-
"report",
|
|
8834
|
-
"guide",
|
|
8835
|
-
"completion",
|
|
8836
|
-
"upgrade",
|
|
8837
|
-
"recover",
|
|
8838
|
-
"skill",
|
|
8839
|
-
"auditTail",
|
|
8840
|
-
"auditShow",
|
|
8841
|
-
"auditClear",
|
|
8842
|
-
"auditHealth",
|
|
8843
|
-
"explain",
|
|
8844
|
-
"plan",
|
|
8845
|
-
"impactAssess",
|
|
8846
|
-
"assert",
|
|
8847
|
-
"snapshot",
|
|
8848
|
-
"verify",
|
|
8849
|
-
"semantic",
|
|
8850
|
-
"design",
|
|
8851
|
-
"proxy",
|
|
8852
|
-
"proxyAnalyze",
|
|
8853
|
-
"verification",
|
|
8854
|
-
"verificationPrune",
|
|
8855
|
-
"evidence",
|
|
8856
|
-
"contract",
|
|
8857
|
-
"recovery",
|
|
8858
|
-
"backfillArtifact",
|
|
8859
|
-
"password",
|
|
8860
|
-
"capabilityDiscover",
|
|
8861
|
-
"capabilityCheck"
|
|
8862
|
-
]);
|
|
8863
|
-
ENGINE_INDEPENDENT = {
|
|
8864
|
-
completion: cap("not-applicable", "none", "Shell completion is engine-independent."),
|
|
8865
|
-
upgrade: cap("not-applicable", "local-write", "Package update checks are engine-independent."),
|
|
8866
|
-
recover: cap("not-applicable", "dry-run", "Recovery operates on saved envelopes and gated command steps."),
|
|
8867
|
-
skill: cap("not-applicable", "local-write", "Skill and task-pack generation are engine-independent."),
|
|
8868
|
-
auditTail: cap("supported", "readonly", "Reads JSONL audit entries; never writes to engines."),
|
|
8869
|
-
auditShow: cap("supported", "readonly", "Looks up a single audit entry by id prefix or recovery_ref."),
|
|
8870
|
-
auditHealth: cap("supported", "readonly", "Renders AuditLogger.getHealth() snapshot."),
|
|
8871
|
-
auditClear: cap("supported", "local-write", "Removes <conn>.jsonl + .jsonl.1 from local disk; never touches DB.")
|
|
8872
|
-
}, SQL_BASE = {
|
|
8873
|
-
init: cap("supported", "interactive", "SQL connection initialization is supported."),
|
|
8874
|
-
use: cap("supported", "local-write", "V2 config supports named SQL connections."),
|
|
8875
|
-
list: cap("supported", "readonly", "Lists relational tables."),
|
|
8876
|
-
schema: cap("supported", "readonly", "Unified schema command (single-table or full scan)."),
|
|
8877
|
-
schemaSingle: cap("supported", "readonly", "Reads a single table schema."),
|
|
8878
|
-
schemaFullScan: cap("supported", "readonly", "Full scan, refresh, and reset are supported."),
|
|
8879
|
-
query: cap("supported", "readonly", "Runs SQL through permission and blacklist guards."),
|
|
8880
|
-
lint: cap("supported", "readonly", "Statically analyzes SQL without connecting to the database."),
|
|
8881
|
-
queryOutput: cap("supported", "readonly", "table/json/csv output is supported."),
|
|
8882
|
-
queryLimitGuard: cap("supported", "readonly", "Query-only auto-limit and size guard are supported."),
|
|
8883
|
-
q: cap("supported", "readonly", "Saved SQL snippets allow SELECT/WITH only."),
|
|
8884
|
-
queries: cap("supported", "local-write", "Snippet management is available regardless of active connection."),
|
|
8885
|
-
insert: cap("supported", "db-write", "Dedicated INSERT command is supported."),
|
|
8886
|
-
update: cap("supported", "db-write", "Dedicated UPDATE command is supported."),
|
|
8887
|
-
delete: cap("supported", "db-write", "Dedicated DELETE command is supported."),
|
|
8888
|
-
export: cap("supported", "readonly", "SQL export is supported."),
|
|
8889
|
-
blacklistRead: cap("supported", "readonly", "Reads the configured blacklist rules."),
|
|
8890
|
-
blacklist: cap("supported", "local-write", "Blacklist management and enforcement are supported."),
|
|
8891
|
-
check: cap("supported", "readonly", "Data health checks are SQL-only."),
|
|
8892
|
-
diff: cap("supported", "readonly", "Relational schema snapshots are supported."),
|
|
8893
|
-
migrate: cap("supported", "db-write", "DDL migrations are supported for SQL engines."),
|
|
8894
|
-
shell: cap("supported", "interactive", "Interactive SQL shell is supported."),
|
|
8895
|
-
status: cap("supported", "readonly", "Safe config summary is supported."),
|
|
8896
|
-
doctor: cap("supported", "readonly", "Engine-specific diagnostics are supported."),
|
|
8897
|
-
inspect: cap("supported", "readonly", "Agent context snapshot is supported."),
|
|
8898
|
-
report: cap("supported", "readonly", "Diagnostic report is supported."),
|
|
8899
|
-
guide: cap("supported", "readonly", "Deterministic next-command planner is supported."),
|
|
8900
|
-
explain: cap("supported", "readonly", "EXPLAIN plans are read from SQL engines."),
|
|
8901
|
-
plan: cap("supported", "readonly", "Offline write-risk analysis of SQL against the schema cache."),
|
|
8902
|
-
impactAssess: cap("supported", "local-write", "Offline impact assessment of a SQL schema change; writes its report locally."),
|
|
8903
|
-
assert: cap("supported", "local-write", "Data assertions over SQL results; optionally writes a verification artifact."),
|
|
8904
|
-
snapshot: cap("supported", "local-write", "Captures a SQL result fingerprint under .dbcli/snapshots/."),
|
|
8905
|
-
verify: cap("supported", "local-write", "Scenario verification over SQL; writes a verification artifact."),
|
|
8906
|
-
semantic: cap("supported", "readonly", "Governed semantic context and drift over a SQL schema cache."),
|
|
8907
|
-
design: cap("supported", "local-write", "Schema design authoring and comparison for SQL dialects; init writes a template."),
|
|
8908
|
-
proxy: cap("supported", "interactive", "Local observing proxy for the MySQL, MariaDB and PostgreSQL wire protocols."),
|
|
8909
|
-
password: cap("supported", "interactive", "Rotates the stored connection credential; refused under agent mode."),
|
|
8910
|
-
proxyAnalyze: cap("not-applicable", "readonly", "Reads locally recorded proxy events; never contacts an engine."),
|
|
8911
|
-
verification: cap("not-applicable", "readonly", "Inspects verification artifacts under .dbcli/verification/."),
|
|
8912
|
-
verificationPrune: cap("not-applicable", "local-write", "Deletes local verification artifacts; dry-run unless --execute --force."),
|
|
8913
|
-
evidence: cap("not-applicable", "local-write", "Composes, validates and renders evidence packs from local artifacts."),
|
|
8914
|
-
contract: cap("not-applicable", "readonly", "Validates and inspects governed semantic contracts offline."),
|
|
8915
|
-
recovery: cap("not-applicable", "readonly", "Renders recovery codes and their remediation steps from a static table."),
|
|
8916
|
-
backfillArtifact: cap("not-applicable", "local-write", "Builds a backfill artifact from a source manifest and v2 connection identity."),
|
|
8917
|
-
capabilityDiscover: cap("not-applicable", "readonly", "Emits the static capability catalog; opens no connection."),
|
|
8918
|
-
capabilityCheck: cap("not-applicable", "readonly", "Evaluates required capability ids against the local configuration only."),
|
|
8919
|
-
...ENGINE_INDEPENDENT
|
|
8920
|
-
}, SQL_ONLY_UNSUPPORTED = {
|
|
8921
|
-
explain: cap("unsupported", "none", "src/commands/explain.ts refuses a non-SQL connection."),
|
|
8922
|
-
plan: cap("unsupported", "none", "Risk analysis is SQL-dialect based; toSqlDialect resolves no dialect here."),
|
|
8923
|
-
assert: cap("unsupported", "none", "src/commands/assert.ts refuses a non-SQL connection."),
|
|
8924
|
-
snapshot: cap("unsupported", "none", "src/commands/snapshot.ts refuses a non-SQL connection."),
|
|
8925
|
-
verify: cap("unsupported", "none", "src/commands/verify.ts refuses a non-SQL connection."),
|
|
8926
|
-
proxy: cap("unsupported", "none", "The proxy speaks the MySQL and PostgreSQL wire protocols only.")
|
|
8927
|
-
}, SQL_MODE_LIMITED = {
|
|
8928
|
-
impactAssess: cap("limited", "local-write", "Runs against an ORM artifact; --against-cache needs a SQL connection."),
|
|
8929
|
-
semantic: cap("limited", "readonly", "Governed context, search and drift work; draft validate needs a SQL connection."),
|
|
8930
|
-
design: cap("limited", "local-write", "Authoring and ORM comparison work; --against-cache needs a SQL connection.")
|
|
8931
|
-
}, ENGINE_CAPABILITIES = Object.freeze({
|
|
8932
|
-
postgresql: Object.freeze({
|
|
8933
|
-
...SQL_BASE,
|
|
8934
|
-
check: cap("limited", "readonly", "SQL-only and strongest on MySQL/MariaDB.")
|
|
8935
|
-
}),
|
|
8936
|
-
sqlite: Object.freeze({
|
|
8937
|
-
...SQL_BASE,
|
|
8938
|
-
init: cap("supported", "local-write", "Asks for a file path and a permission; the file must already exist."),
|
|
8939
|
-
use: cap("supported", "local-write", "Switches connections and displays the file path."),
|
|
8940
|
-
schemaFullScan: cap("unsupported", "none", "Full schema cache scan is not implemented for SQLite."),
|
|
8941
|
-
lint: cap("unsupported", "none", "Static lint is not enabled for the SQLite dialect yet."),
|
|
8942
|
-
queries: cap("supported", "local-write", "Snippets may declare `engine: sqlite` and bind with `?` placeholders."),
|
|
8943
|
-
insert: cap("supported", "db-write", "Row insert through the shared data-executor path."),
|
|
8944
|
-
update: cap("supported", "db-write", "Row update with a mandatory --where clause."),
|
|
8945
|
-
delete: cap("supported", "db-write", "Row delete with a mandatory --where clause."),
|
|
8946
|
-
export: cap("supported", "local-write", "Exports SELECT results to json, csv or html through the shared query path."),
|
|
8947
|
-
check: cap("unsupported", "none", "Data health check is not enabled for SQLite."),
|
|
8948
|
-
diff: cap("unsupported", "none", "Schema snapshots are not enabled for SQLite."),
|
|
8949
|
-
migrate: cap("unsupported", "none", "SQLite ALTER TABLE covers few operations and a column change is a table rebuild; no DDL generator exists."),
|
|
8950
|
-
shell: cap("unsupported", "none", "The REPL is not enabled for SQLite."),
|
|
8951
|
-
doctor: cap("supported", "readonly", "Checks the file exists, is readable, and is writable when the permission allows writes."),
|
|
8952
|
-
inspect: cap("unsupported", "none", "Inspection collectors are not enabled for SQLite."),
|
|
8953
|
-
report: cap("unsupported", "none", "Report diagnostics query engine-specific statistics views."),
|
|
8954
|
-
guide: cap("unsupported", "none", "Guides depend on engine-specific EXPLAIN output."),
|
|
8955
|
-
explain: cap("unsupported", "none", "EXPLAIN handling is not enabled for SQLite."),
|
|
8956
|
-
plan: cap("unsupported", "none", "Plan capture is not enabled for SQLite."),
|
|
8957
|
-
impactAssess: cap("unsupported", "none", "Impact assessment is not enabled for SQLite."),
|
|
8958
|
-
assert: cap("unsupported", "none", "Assertions are not enabled for SQLite."),
|
|
8959
|
-
snapshot: cap("unsupported", "none", "Result snapshots are not enabled for SQLite."),
|
|
8960
|
-
verify: cap("unsupported", "none", "Write verification is not enabled for SQLite."),
|
|
8961
|
-
semantic: cap("unsupported", "none", "Semantic context work is not enabled for SQLite."),
|
|
8962
|
-
design: cap("unsupported", "none", "Schema design work is not enabled for SQLite."),
|
|
8963
|
-
proxy: cap("not-applicable", "none", "SQLite has no network protocol to proxy."),
|
|
8964
|
-
proxyAnalyze: cap("not-applicable", "none", "There are no SQLite proxy events to analyse."),
|
|
8965
|
-
password: cap("not-applicable", "none", "A SQLite connection has no credential to store or rotate."),
|
|
8966
|
-
queryLimitGuard: cap("limited", "readonly", "Applies the same LIMIT rewrite as the other SQL engines; under query-only the handle is opened read-only, which a database with an unreplayed WAL cannot satisfy.")
|
|
8967
|
-
}),
|
|
8968
|
-
mysql: Object.freeze(SQL_BASE),
|
|
8969
|
-
mariadb: Object.freeze(SQL_BASE),
|
|
8970
|
-
mongodb: Object.freeze({
|
|
8971
|
-
...SQL_BASE,
|
|
8972
|
-
...SQL_ONLY_UNSUPPORTED,
|
|
8973
|
-
...SQL_MODE_LIMITED,
|
|
8974
|
-
...SQL_MODE_LIMITED,
|
|
8975
|
-
...SQL_MODE_LIMITED,
|
|
8976
|
-
lint: cap("unsupported", "none", "Static lint accepts SQL connections only."),
|
|
8977
|
-
schemaSingle: cap("limited", "readonly", "MongoDB schema is sampled from documents."),
|
|
8978
|
-
schemaFullScan: cap("limited", "readonly", "Full scan is sampled and document-oriented."),
|
|
8979
|
-
query: cap("limited", "readonly", "Uses JSON filter or aggregation syntax, not SQL."),
|
|
8980
|
-
queryLimitGuard: cap("limited", "readonly", "Applies result limits and collection size guard."),
|
|
8981
|
-
queries: cap("limited", "local-write", "Snippet management works with MongoDB-specific saved-query limitations."),
|
|
8982
|
-
q: cap("limited", "readonly", "Saved MongoDB snippets require collection frontmatter, accept JSON find/aggregate bodies, and JSON-encode parameter substitutions."),
|
|
8983
|
-
insert: cap("limited", "db-write", "Document insert is supported with Mongo-specific behavior."),
|
|
8984
|
-
update: cap("limited", "db-write", "Document update is supported with Mongo-specific behavior."),
|
|
8985
|
-
delete: cap("limited", "db-write", "Document delete is supported with Mongo-specific behavior."),
|
|
8986
|
-
export: cap("limited", "readonly", "MongoDB export is supported with document-query syntax."),
|
|
8987
|
-
blacklistRead: cap("limited", "readonly", "Reads the engine-specific blacklist rules."),
|
|
8988
|
-
blacklist: cap("limited", "local-write", "Rule management works; enforcement differs from SQL column enforcement."),
|
|
8989
|
-
check: cap("unsupported", "none", "Data health check is SQL-only."),
|
|
8990
|
-
diff: cap("unsupported", "none", "Schema snapshots are relational only."),
|
|
8991
|
-
migrate: cap("unsupported", "none", "DDL migrations are SQL-only."),
|
|
8992
|
-
shell: cap("limited", "interactive", "MongoDB shell support is narrower than SQL shell support.")
|
|
8993
|
-
}),
|
|
8994
|
-
redis: Object.freeze({
|
|
8995
|
-
...SQL_BASE,
|
|
8996
|
-
...SQL_ONLY_UNSUPPORTED,
|
|
8997
|
-
lint: cap("unsupported", "none", "Static lint accepts SQL connections only."),
|
|
8998
|
-
schemaSingle: cap("limited", "readonly", "Per-key synthetic schema only."),
|
|
8999
|
-
schemaFullScan: cap("unsupported", "none", "Redis has no full schema cache scan."),
|
|
9000
|
-
query: cap("limited", "readonly", "Runs allow-listed Redis commands."),
|
|
9001
|
-
queryLimitGuard: cap("limited", "readonly", "SCAN/LRANGE/ZRANGE COUNT/stop rewrite; HGETALL/SMEMBERS/KEYS truncate at 1000; --no-limit bypasses."),
|
|
9002
|
-
q: cap("limited", "readonly", "Saved Redis snippets use a read-only allowlist and range guards."),
|
|
9003
|
-
queries: cap("limited", "local-write", "Snippet management works with Redis-specific saved-query limitations."),
|
|
9004
|
-
insert: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
9005
|
-
update: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
9006
|
-
delete: cap("limited", "db-write", "Basic delete via DEL/HDEL/LREM/SREM/ZREM (needs data-admin); supports --dry-run."),
|
|
9007
|
-
export: cap("unsupported", "none", "Redis export is not supported."),
|
|
9008
|
-
blacklistRead: cap("limited", "readonly", "Reads the engine-specific blacklist rules."),
|
|
9009
|
-
blacklist: cap("limited", "local-write", "Key-glob rejection (Redis-native pattern) plus value/hash-field masking ([REDACTED])."),
|
|
9010
|
-
check: cap("unsupported", "none", "Data health check is SQL-only."),
|
|
9011
|
-
diff: cap("unsupported", "none", "Schema snapshots are relational only."),
|
|
9012
|
-
migrate: cap("unsupported", "none", "DDL migrations are SQL-only."),
|
|
9013
|
-
shell: cap("limited", "interactive", "Single-line Redis REPL (no trailing semicolon needed) with auto-limit and key-glob blacklist; narrower than SQL shell.")
|
|
9014
|
-
}),
|
|
9015
|
-
elasticsearch: Object.freeze({
|
|
9016
|
-
...SQL_BASE,
|
|
9017
|
-
...SQL_ONLY_UNSUPPORTED,
|
|
9018
|
-
lint: cap("unsupported", "none", "Static lint accepts SQL connections only."),
|
|
9019
|
-
schemaSingle: cap("limited", "readonly", "Schema flattens index mappings."),
|
|
9020
|
-
schemaFullScan: cap("supported", "readonly", "Full scan iterates non-system indices."),
|
|
9021
|
-
query: cap("limited", "readonly", "Uses JSON DSL or Lucene query strings with an index."),
|
|
9022
|
-
queryLimitGuard: cap("limited", "readonly", "Applies Elasticsearch size guard."),
|
|
9023
|
-
q: cap("limited", "readonly", "Saved ES snippets require index frontmatter and reject scripts."),
|
|
9024
|
-
queries: cap("limited", "local-write", "Snippet management works with Elasticsearch-specific saved-query limitations."),
|
|
9025
|
-
insert: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
9026
|
-
update: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
9027
|
-
delete: cap("unsupported", "none", "Dedicated write subcommand is not exposed."),
|
|
9028
|
-
export: cap("limited", "readonly", "Exports DSL hits or a full index via scroll, capped at 1000 rows (use --no-limit)."),
|
|
9029
|
-
blacklistRead: cap("limited", "readonly", "Reads the engine-specific blacklist rules."),
|
|
9030
|
-
blacklist: cap("limited", "local-write", "Index-level and flattened-column enforcement differ from SQL."),
|
|
9031
|
-
check: cap("unsupported", "none", "Data health check is SQL-only."),
|
|
9032
|
-
diff: cap("unsupported", "none", "Schema snapshots are relational only."),
|
|
9033
|
-
migrate: cap("unsupported", "none", "DDL migrations are SQL-only."),
|
|
9034
|
-
shell: cap("limited", "interactive", "Dedicated Kibana Dev Tools-style REPL (method/path + JSON body, blank-line submit); read-focused.")
|
|
9035
|
-
})
|
|
9036
|
-
});
|
|
9037
|
-
});
|
|
9038
|
-
|
|
9039
9114
|
// src/adapters/error-mapper.ts
|
|
9040
9115
|
function isTransportDriverCode(code) {
|
|
9041
9116
|
return TLS_CODE_PREFIX.test(code) || TRANSPORT_CODES[code] !== void 0;
|
|
@@ -9115,6 +9190,12 @@ function mapError(error, system, options) {
|
|
|
9115
9190
|
`Confirm the address is the one you meant: ${options.host}:${options.port}`
|
|
9116
9191
|
]);
|
|
9117
9192
|
case "TLS_ERROR":
|
|
9193
|
+
if (options.sqlTls !== void 0)
|
|
9194
|
+
return new ConnectionError("TLS_ERROR", t_vars("errors.sql_tls_handshake", { detail: errMsg }), [
|
|
9195
|
+
t("errors.sql_tls_ca_hint"),
|
|
9196
|
+
t_vars("errors.sql_tls_identity_hint", { host: options.host }),
|
|
9197
|
+
t("errors.sql_tls_no_downgrade")
|
|
9198
|
+
]);
|
|
9118
9199
|
return new ConnectionError("TLS_ERROR", `TLS handshake failed: ${errMsg}`, [
|
|
9119
9200
|
'Point the connection at the right CA bundle: set "caPath" in .dbcli',
|
|
9120
9201
|
'For a self-signed certificate in a trusted network, set "rejectUnauthorized": false',
|
|
@@ -9180,8 +9261,11 @@ function mapError(error, system, options) {
|
|
|
9180
9261
|
}
|
|
9181
9262
|
var TRANSPORT_CODES, TLS_CODE_PREFIX, MYSQL_CODES, MYSQL_ERRNOS, SQLSTATES, REDIS_PREFIXES, FALLBACK_PATTERNS, PG_CANCEL_BY_TIMEOUT, SYSTEM_FACTS;
|
|
9182
9263
|
var init_error_mapper = __esm(() => {
|
|
9264
|
+
init_message_loader();
|
|
9183
9265
|
init_types2();
|
|
9184
9266
|
TRANSPORT_CODES = {
|
|
9267
|
+
HANDSHAKE_SSL_ERROR: "TLS_ERROR",
|
|
9268
|
+
HANDSHAKE_NO_SSL_SUPPORT: "TLS_ERROR",
|
|
9185
9269
|
ECONNREFUSED: "ECONNREFUSED",
|
|
9186
9270
|
ETIMEDOUT: "ETIMEDOUT",
|
|
9187
9271
|
ESOCKETTIMEDOUT: "ETIMEDOUT",
|
|
@@ -9249,6 +9333,7 @@ var init_error_mapper = __esm(() => {
|
|
|
9249
9333
|
NOAUTH: "AUTH_FAILED",
|
|
9250
9334
|
WRONGPASS: "AUTH_FAILED"
|
|
9251
9335
|
}, FALLBACK_PATTERNS = [
|
|
9336
|
+
[/server does not support SSL connections/i, "TLS_ERROR"],
|
|
9252
9337
|
[/connection refused/i, "ECONNREFUSED"],
|
|
9253
9338
|
[/\bECONNREFUSED\b/, "ECONNREFUSED"],
|
|
9254
9339
|
[/connect(?:ion)? time(?:d )?out/i, "ETIMEDOUT"],
|
|
@@ -9495,6 +9580,51 @@ function statementTimeoutSql(system, ms) {
|
|
|
9495
9580
|
return `SET SESSION max_execution_time = ${Math.round(ms)}`;
|
|
9496
9581
|
}
|
|
9497
9582
|
|
|
9583
|
+
// src/adapters/sql-tls.ts
|
|
9584
|
+
import { X509Certificate } from "crypto";
|
|
9585
|
+
async function sqlTlsCa(options) {
|
|
9586
|
+
if (options.sqlTls === void 0)
|
|
9587
|
+
return;
|
|
9588
|
+
let config = SqlTlsConfigSchema.parse(options.sqlTls);
|
|
9589
|
+
if (!options.host || options.host.startsWith("/"))
|
|
9590
|
+
throw new ConnectionError("TLS_ERROR", t("errors.sql_tls_network_host"), []);
|
|
9591
|
+
try {
|
|
9592
|
+
let ca = await Bun.file(config.caPath).text(), certificates = ca.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
9593
|
+
if (!certificates?.length)
|
|
9594
|
+
throw Error(t("errors.sql_tls_ca_empty"));
|
|
9595
|
+
for (let pem of certificates)
|
|
9596
|
+
new X509Certificate(pem);
|
|
9597
|
+
return ca;
|
|
9598
|
+
} catch (cause) {
|
|
9599
|
+
throw new ConnectionError("TLS_ERROR", t_vars("errors.sql_tls_ca_load", {
|
|
9600
|
+
detail: cause instanceof Error ? cause.message : String(cause)
|
|
9601
|
+
}), [t("errors.sql_tls_ca_restore")]);
|
|
9602
|
+
}
|
|
9603
|
+
}
|
|
9604
|
+
var init_sql_tls = __esm(() => {
|
|
9605
|
+
init_message_loader();
|
|
9606
|
+
init_validation();
|
|
9607
|
+
init_types2();
|
|
9608
|
+
});
|
|
9609
|
+
|
|
9610
|
+
// src/adapters/postgresql-tls.ts
|
|
9611
|
+
import { isIP } from "net";
|
|
9612
|
+
import { checkServerIdentity } from "tls";
|
|
9613
|
+
async function postgresqlTlsOptions(options) {
|
|
9614
|
+
let ca = await sqlTlsCa(options);
|
|
9615
|
+
if (ca === void 0)
|
|
9616
|
+
return;
|
|
9617
|
+
return {
|
|
9618
|
+
ca,
|
|
9619
|
+
rejectUnauthorized: !0,
|
|
9620
|
+
...isIP(options.host) === 0 && { servername: options.host },
|
|
9621
|
+
checkServerIdentity: (_hostname, certificate) => checkServerIdentity(options.host, certificate)
|
|
9622
|
+
};
|
|
9623
|
+
}
|
|
9624
|
+
var init_postgresql_tls = __esm(() => {
|
|
9625
|
+
init_sql_tls();
|
|
9626
|
+
});
|
|
9627
|
+
|
|
9498
9628
|
// src/utils/connection-error-message.ts
|
|
9499
9629
|
function isTransportFailure(error) {
|
|
9500
9630
|
if (error instanceof ConnectionError)
|
|
@@ -9547,7 +9677,7 @@ class PostgreSQLAdapter {
|
|
|
9547
9677
|
}
|
|
9548
9678
|
async connect() {
|
|
9549
9679
|
await withMappedConnectionError("postgresql", this.options, async () => {
|
|
9550
|
-
let timeouts = resolveTimeoutPolicy(this.options), { Pool } = await import("pg");
|
|
9680
|
+
let timeouts = resolveTimeoutPolicy(this.options), ssl = await postgresqlTlsOptions(this.options), { Pool } = await import("pg");
|
|
9551
9681
|
this.pool = new Pool({
|
|
9552
9682
|
host: this.options.host,
|
|
9553
9683
|
port: this.options.port,
|
|
@@ -9555,6 +9685,7 @@ class PostgreSQLAdapter {
|
|
|
9555
9685
|
password: this.options.password,
|
|
9556
9686
|
database: this.options.database,
|
|
9557
9687
|
connectionTimeoutMillis: timeouts.connectMs,
|
|
9688
|
+
...ssl && { ssl },
|
|
9558
9689
|
...timeouts.statementMs !== void 0 && { statement_timeout: timeouts.statementMs }
|
|
9559
9690
|
}), await this.testConnection();
|
|
9560
9691
|
try {
|
|
@@ -9861,6 +9992,7 @@ var init_postgresql_adapter = __esm(() => {
|
|
|
9861
9992
|
init_error_mapper();
|
|
9862
9993
|
init_db_version_check();
|
|
9863
9994
|
init_encoding();
|
|
9995
|
+
init_postgresql_tls();
|
|
9864
9996
|
init_connection_error_message();
|
|
9865
9997
|
});
|
|
9866
9998
|
|
|
@@ -10189,6 +10321,59 @@ var init_grant_redaction = __esm(() => {
|
|
|
10189
10321
|
]);
|
|
10190
10322
|
});
|
|
10191
10323
|
|
|
10324
|
+
// src/adapters/mysql-tls.ts
|
|
10325
|
+
import { checkServerIdentity as checkServerIdentity2, TLSSocket } from "tls";
|
|
10326
|
+
async function connectVerifiedMysql(options, ca, host) {
|
|
10327
|
+
let { default: driver } = await import("mysql2"), connection = driver.createConnection({
|
|
10328
|
+
...options,
|
|
10329
|
+
ssl: {
|
|
10330
|
+
ca,
|
|
10331
|
+
rejectUnauthorized: !0,
|
|
10332
|
+
verifyIdentity: !1
|
|
10333
|
+
}
|
|
10334
|
+
});
|
|
10335
|
+
return guardMysqlIdentity(connection, host), new Promise((resolve, reject) => {
|
|
10336
|
+
connection.connect((error) => {
|
|
10337
|
+
if (error)
|
|
10338
|
+
connection.destroy(), reject(error);
|
|
10339
|
+
else
|
|
10340
|
+
resolve(connection.promise());
|
|
10341
|
+
});
|
|
10342
|
+
});
|
|
10343
|
+
}
|
|
10344
|
+
function guardMysqlIdentity(connection, host) {
|
|
10345
|
+
let startTLS = connection.startTLS;
|
|
10346
|
+
if (typeof startTLS !== "function")
|
|
10347
|
+
throw connection.destroy(), new ConnectionError("TLS_ERROR", t("errors.sql_tls_driver_identity"), []);
|
|
10348
|
+
connection.startTLS = (onSecure) => {
|
|
10349
|
+
let completed = !1;
|
|
10350
|
+
startTLS.call(connection, (error) => {
|
|
10351
|
+
if (completed)
|
|
10352
|
+
return;
|
|
10353
|
+
completed = !0;
|
|
10354
|
+
let failure = error;
|
|
10355
|
+
if (!failure)
|
|
10356
|
+
try {
|
|
10357
|
+
let socket = connection.stream;
|
|
10358
|
+
if (!(socket instanceof TLSSocket) || !socket.encrypted || !socket.authorized)
|
|
10359
|
+
throw Error(t("errors.sql_tls_peer_unverified"));
|
|
10360
|
+
let certificate = socket.getPeerCertificate(!0);
|
|
10361
|
+
if (!certificate.raw?.length)
|
|
10362
|
+
throw Error(t("errors.sql_tls_peer_missing"));
|
|
10363
|
+
failure = checkServerIdentity2(host, certificate);
|
|
10364
|
+
} catch (cause) {
|
|
10365
|
+
failure = cause instanceof Error ? cause : Error(String(cause));
|
|
10366
|
+
}
|
|
10367
|
+
if (onSecure(failure), failure)
|
|
10368
|
+
connection.destroy();
|
|
10369
|
+
});
|
|
10370
|
+
};
|
|
10371
|
+
}
|
|
10372
|
+
var init_mysql_tls = __esm(() => {
|
|
10373
|
+
init_types2();
|
|
10374
|
+
init_message_loader();
|
|
10375
|
+
});
|
|
10376
|
+
|
|
10192
10377
|
// src/adapters/mysql-adapter.ts
|
|
10193
10378
|
function parseEnumValues(columnType) {
|
|
10194
10379
|
let match = columnType.match(/^enum\((.+)\)$/i);
|
|
@@ -10209,8 +10394,7 @@ class MySQLAdapter {
|
|
|
10209
10394
|
}
|
|
10210
10395
|
async connect() {
|
|
10211
10396
|
this.serverVersion = void 0, await withMappedConnectionError(this.system, this.options, async () => {
|
|
10212
|
-
let timeouts = resolveTimeoutPolicy(this.options),
|
|
10213
|
-
if (this.db = await driver.createConnection({
|
|
10397
|
+
let timeouts = resolveTimeoutPolicy(this.options), ca = await sqlTlsCa(this.options), connectionOptions = {
|
|
10214
10398
|
host: this.options.host,
|
|
10215
10399
|
port: this.options.port,
|
|
10216
10400
|
user: this.options.user,
|
|
@@ -10218,7 +10402,14 @@ class MySQLAdapter {
|
|
|
10218
10402
|
database: this.options.database,
|
|
10219
10403
|
charset: "utf8mb4",
|
|
10220
10404
|
connectTimeout: timeouts.connectMs
|
|
10221
|
-
}
|
|
10405
|
+
};
|
|
10406
|
+
if (ca !== void 0)
|
|
10407
|
+
this.db = await connectVerifiedMysql(connectionOptions, ca, this.options.host);
|
|
10408
|
+
else {
|
|
10409
|
+
let { default: driver } = await import("mysql2/promise");
|
|
10410
|
+
this.db = await driver.createConnection(connectionOptions);
|
|
10411
|
+
}
|
|
10412
|
+
if (await this.db.execute("SET NAMES utf8mb4"), timeouts.statementMs !== void 0)
|
|
10222
10413
|
try {
|
|
10223
10414
|
await this.db.query(statementTimeoutSql(this.system, timeouts.statementMs));
|
|
10224
10415
|
} catch {}
|
|
@@ -10453,6 +10644,8 @@ var init_mysql_adapter = __esm(() => {
|
|
|
10453
10644
|
init_connection_error_message();
|
|
10454
10645
|
init_grant_query();
|
|
10455
10646
|
init_grant_redaction();
|
|
10647
|
+
init_sql_tls();
|
|
10648
|
+
init_mysql_tls();
|
|
10456
10649
|
});
|
|
10457
10650
|
|
|
10458
10651
|
// src/adapters/sqlite-adapter.ts
|
|
@@ -12946,8 +13139,14 @@ var init_blacklist_manager = __esm(() => {
|
|
|
12946
13139
|
});
|
|
12947
13140
|
|
|
12948
13141
|
// src/adapters/factory.ts
|
|
13142
|
+
function assertSqlTlsEngine(options) {
|
|
13143
|
+
if (options.sqlTls !== void 0 && !supportsSqlTls(options.system))
|
|
13144
|
+
throw Error(t("errors.sql_tls_unsupported"));
|
|
13145
|
+
}
|
|
13146
|
+
|
|
12949
13147
|
class AdapterFactory {
|
|
12950
13148
|
static createSqlAdapter(rawOptions) {
|
|
13149
|
+
assertSqlTlsEngine(rawOptions);
|
|
12951
13150
|
let options = withResolvedTimeout(rawOptions);
|
|
12952
13151
|
switch (options.system) {
|
|
12953
13152
|
case "postgresql":
|
|
@@ -12962,6 +13161,7 @@ class AdapterFactory {
|
|
|
12962
13161
|
}
|
|
12963
13162
|
}
|
|
12964
13163
|
static createQueryableAdapterWithoutRules(rawOptions) {
|
|
13164
|
+
assertSqlTlsEngine(rawOptions);
|
|
12965
13165
|
let options = withResolvedTimeout(rawOptions);
|
|
12966
13166
|
switch (options.system) {
|
|
12967
13167
|
case "mongodb":
|
|
@@ -13016,7 +13216,7 @@ class AdapterFactory {
|
|
|
13016
13216
|
}
|
|
13017
13217
|
static createRedisAdapter(config) {
|
|
13018
13218
|
let rawOptions = config.connection;
|
|
13019
|
-
if (rawOptions.system !== "redis")
|
|
13219
|
+
if (assertSqlTlsEngine(rawOptions), rawOptions.system !== "redis")
|
|
13020
13220
|
throw Error("createRedisAdapter requires system: redis");
|
|
13021
13221
|
let options = withResolvedTimeout(rawOptions), adapter = new RedisAdapter(options), blacklistRules = config.blacklist?.tables ?? [], overrideEnabled = isBlacklistOverrideEnabled();
|
|
13022
13222
|
if (overrideEnabled && blacklistRules.length > 0)
|
|
@@ -13038,6 +13238,7 @@ var init_factory = __esm(() => {
|
|
|
13038
13238
|
init_elasticsearch_adapter();
|
|
13039
13239
|
init_blacklist_manager();
|
|
13040
13240
|
init_message_loader();
|
|
13241
|
+
init_capabilities();
|
|
13041
13242
|
});
|
|
13042
13243
|
|
|
13043
13244
|
// src/adapters/index.ts
|
|
@@ -13067,6 +13268,11 @@ async function checkOverwrite(configPath, shouldPrompt, force) {
|
|
|
13067
13268
|
async function assertV2InitImportSupported(configPath) {
|
|
13068
13269
|
if (isGlobalConfigPath(configPath))
|
|
13069
13270
|
return;
|
|
13271
|
+
let legacyFile = Bun.file(configPath);
|
|
13272
|
+
if ((await legacyFile.stat().catch(() => null))?.isFile()) {
|
|
13273
|
+
if ((await legacyFile.json()).connection?.sqlTls !== void 0)
|
|
13274
|
+
throw Error(t("errors.sql_tls_legacy_import"));
|
|
13275
|
+
}
|
|
13070
13276
|
let projectConfig = Bun.file(join10(configPath, "config.json"));
|
|
13071
13277
|
if (!await projectConfig.exists())
|
|
13072
13278
|
return;
|
|
@@ -16624,7 +16830,7 @@ var init_init_sqlite = __esm(() => {
|
|
|
16624
16830
|
});
|
|
16625
16831
|
|
|
16626
16832
|
// src/commands/init.ts
|
|
16627
|
-
import { join as join11 } from "path";
|
|
16833
|
+
import { join as join11, resolve as resolve3 } from "path";
|
|
16628
16834
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
16629
16835
|
async function handleRemove(configPath, name) {
|
|
16630
16836
|
let storagePath = await resolveConfigStoragePath(configPath), configFile = Bun.file(join11(storagePath, "config.json"));
|
|
@@ -16710,6 +16916,25 @@ async function initCommandHandler(options, command) {
|
|
|
16710
16916
|
system = await promptUser.select(t("init.select_system"), [...INIT_SYSTEM_CHOICES]);
|
|
16711
16917
|
if (!DATABASE_SYSTEMS.includes(system))
|
|
16712
16918
|
throw Error(t_vars("errors.invalid_system", { system }));
|
|
16919
|
+
let hasSqlTls = options.sqlTls !== void 0 || options.sqlTlsCa !== void 0, sqlTls;
|
|
16920
|
+
if (hasSqlTls) {
|
|
16921
|
+
if (!supportsSqlTls(system))
|
|
16922
|
+
throw Error(t("errors.sql_tls_unsupported"));
|
|
16923
|
+
if (options.sqlTls !== "verify-full" || typeof options.sqlTlsCa !== "string" || !options.sqlTlsCa.trim())
|
|
16924
|
+
throw Error(t("errors.sql_tls_options_required"));
|
|
16925
|
+
sqlTls = SqlTlsConfigSchema.parse({ mode: options.sqlTls, caPath: resolve3(options.sqlTlsCa) });
|
|
16926
|
+
}
|
|
16927
|
+
let existingStorage = await resolveConfigStoragePath(configPath), existingDocument = Bun.file(join11(existingStorage, "config.json"));
|
|
16928
|
+
if (await existingDocument.exists()) {
|
|
16929
|
+
if (detectConfigVersion(await existingDocument.json()) === 2) {
|
|
16930
|
+
let prior = (await readV2Config(existingStorage)).connections[connectionName];
|
|
16931
|
+
if (prior && "sqlTls" in prior)
|
|
16932
|
+
sqlTls ??= prior.sqlTls;
|
|
16933
|
+
} else if (connectionName === "default" && existingConfig && "sqlTls" in existingConfig.connection)
|
|
16934
|
+
sqlTls ??= existingConfig.connection.sqlTls;
|
|
16935
|
+
}
|
|
16936
|
+
if (sqlTls && !supportsSqlTls(system))
|
|
16937
|
+
throw Error(t("errors.sql_tls_change_engine"));
|
|
16713
16938
|
if (system === "sqlite") {
|
|
16714
16939
|
await handleSQLiteInit({
|
|
16715
16940
|
options,
|
|
@@ -16734,7 +16959,8 @@ async function initCommandHandler(options, command) {
|
|
|
16734
16959
|
return;
|
|
16735
16960
|
}
|
|
16736
16961
|
let connection = {
|
|
16737
|
-
system
|
|
16962
|
+
system,
|
|
16963
|
+
...sqlTls && { sqlTls }
|
|
16738
16964
|
}, configForWrite;
|
|
16739
16965
|
if (options.useEnvRefs && shouldPrompt) {
|
|
16740
16966
|
let envHost = options.envHost || await promptUser.text(t("init.prompt_host"), "DB_HOST"), envPort = options.envPort || await promptUser.text(t("init.prompt_port"), "DB_PORT"), envUser = options.envUser || await promptUser.text(t("init.prompt_user"), "DB_USER"), envPassword = options.envPassword || await promptUser.text(t("init.prompt_password"), "DB_PASSWORD"), envDatabase = options.envDatabase || await promptUser.text(t("init.prompt_name"), "DB_DATABASE");
|
|
@@ -16744,7 +16970,8 @@ async function initCommandHandler(options, command) {
|
|
|
16744
16970
|
port: { $env: envPort },
|
|
16745
16971
|
user: { $env: envUser },
|
|
16746
16972
|
password: { $env: envPassword },
|
|
16747
|
-
database: { $env: envDatabase }
|
|
16973
|
+
database: { $env: envDatabase },
|
|
16974
|
+
...sqlTls && { sqlTls }
|
|
16748
16975
|
};
|
|
16749
16976
|
let permission = (typeof options.permission === "string" ? options.permission : void 0) ?? "query-only";
|
|
16750
16977
|
if (!options.permission)
|
|
@@ -16803,7 +17030,8 @@ async function initCommandHandler(options, command) {
|
|
|
16803
17030
|
port: { $env: envPort },
|
|
16804
17031
|
user: { $env: envUser },
|
|
16805
17032
|
password: { $env: envPassword },
|
|
16806
|
-
database: { $env: envDatabase }
|
|
17033
|
+
database: { $env: envDatabase },
|
|
17034
|
+
...sqlTls && { sqlTls }
|
|
16807
17035
|
};
|
|
16808
17036
|
}
|
|
16809
17037
|
let updates = {
|
|
@@ -16828,7 +17056,8 @@ async function initCommandHandler(options, command) {
|
|
|
16828
17056
|
port: parseInt(String(resolveValue(newConfig.connection.port, "port")), 10) || 5432,
|
|
16829
17057
|
user: String(resolveValue(newConfig.connection.user, "user")),
|
|
16830
17058
|
password: String(resolveValue(newConfig.connection.password, "password")) || "",
|
|
16831
|
-
database: String(resolveValue(newConfig.connection.database, "database"))
|
|
17059
|
+
database: String(resolveValue(newConfig.connection.database, "database")),
|
|
17060
|
+
...sqlTls && { sqlTls }
|
|
16832
17061
|
}, adapter = AdapterFactory.createAdapterWithoutRules(testConnection);
|
|
16833
17062
|
try {
|
|
16834
17063
|
if (await adapter.connect(), await adapter.testConnection())
|
|
@@ -16862,17 +17091,19 @@ var init_init = __esm(() => {
|
|
|
16862
17091
|
init_config();
|
|
16863
17092
|
init_config_v2();
|
|
16864
17093
|
init_config_v2_mutations();
|
|
17094
|
+
init_capabilities();
|
|
16865
17095
|
init_prompts();
|
|
16866
17096
|
init_redaction();
|
|
16867
17097
|
init_adapters();
|
|
16868
17098
|
init_types2();
|
|
17099
|
+
init_validation();
|
|
16869
17100
|
init_config_path();
|
|
16870
17101
|
init_config_binding();
|
|
16871
17102
|
init_init_shared();
|
|
16872
17103
|
init_init_mongodb();
|
|
16873
17104
|
init_init_sqlite();
|
|
16874
17105
|
INIT_SYSTEM_CHOICES = DATABASE_SYSTEMS, VALID_PERMISSIONS3 = ["query-only", "read-write", "data-admin", "admin"];
|
|
16875
|
-
initCommand = new Command2("init").description("Initialize dbcli configuration with .env parsing and interactive prompts").option("--host <host>", "Database host").option("--port <port>", "Database port").option("--user <user>", "Database user").option("--password <password>", "Database password").option("--name <name>", "Database name").option("--system <system>", "Database system (postgresql, mysql, mariadb, mongodb, redis, elasticsearch)").option("--file <path>", "SQLite database file path (the database must already exist)").option("--cloud-id <id>", "Elasticsearch Cloud ID").option("--api-key <key>", "Elasticsearch API Key").option("--uri <uri>", "MongoDB connection string, decomposed into fields on write (mongodb://user:pass@host:27017/db?authSource=admin)").option("--auth-source <authSource>", "MongoDB auth database (default: admin when user/password are set)").option("--replica-set <name>", "MongoDB replica set name").option("--srv", "MongoDB: connect via a mongodb+srv:// DNS SRV domain (port is not written)").option("--tls", "MongoDB: enable TLS (omit both --tls and --no-tls to use the driver default)").option("--no-tls", "MongoDB: disable TLS").option("--permission <permission>", "Permission level (query-only, read-write, data-admin, admin)", "query-only").option("--use-env-refs", "Store env var references in config instead of actual values (for CI/CD or multi-env)", !1).option("--env-host <var>", "Env var name for host (with --use-env-refs)").option("--env-port <var>", "Env var name for port (with --use-env-refs)").option("--env-user <var>", "Env var name for user (with --use-env-refs)").option("--env-password <var>", "Env var name for password (with --use-env-refs)").option("--env-database <var>", "Env var name for database (with --use-env-refs)").option("--skip-test", "Skip database connection test").option("--no-interactive", "Non-interactive mode (requires all values via flags)").option("--force", "Skip overwrite confirmation if .dbcli exists").option("--conn-name <name>", "Connection name (required in non-interactive mode)").option("--env-file <path>", "Path to env file for this connection").option("--remove <name>", "Remove a named connection").option("--rename <names>", "Rename a connection (format: old:new)").action(async (options) => {
|
|
17106
|
+
initCommand = new Command2("init").description("Initialize dbcli configuration with .env parsing and interactive prompts").option("--host <host>", "Database host").option("--port <port>", "Database port").option("--user <user>", "Database user").option("--password <password>", "Database password").option("--name <name>", "Database name").option("--system <system>", "Database system (postgresql, mysql, mariadb, mongodb, redis, elasticsearch)").option("--sql-tls <mode>", t("init.sql_tls_help")).option("--sql-tls-ca <path>", t("init.sql_tls_ca_help")).option("--file <path>", "SQLite database file path (the database must already exist)").option("--cloud-id <id>", "Elasticsearch Cloud ID").option("--api-key <key>", "Elasticsearch API Key").option("--uri <uri>", "MongoDB connection string, decomposed into fields on write (mongodb://user:pass@host:27017/db?authSource=admin)").option("--auth-source <authSource>", "MongoDB auth database (default: admin when user/password are set)").option("--replica-set <name>", "MongoDB replica set name").option("--srv", "MongoDB: connect via a mongodb+srv:// DNS SRV domain (port is not written)").option("--tls", "MongoDB: enable TLS (omit both --tls and --no-tls to use the driver default)").option("--no-tls", "MongoDB: disable TLS").option("--permission <permission>", "Permission level (query-only, read-write, data-admin, admin)", "query-only").option("--use-env-refs", "Store env var references in config instead of actual values (for CI/CD or multi-env)", !1).option("--env-host <var>", "Env var name for host (with --use-env-refs)").option("--env-port <var>", "Env var name for port (with --use-env-refs)").option("--env-user <var>", "Env var name for user (with --use-env-refs)").option("--env-password <var>", "Env var name for password (with --use-env-refs)").option("--env-database <var>", "Env var name for database (with --use-env-refs)").option("--skip-test", "Skip database connection test").option("--no-interactive", "Non-interactive mode (requires all values via flags)").option("--force", "Skip overwrite confirmation if .dbcli exists").option("--conn-name <name>", "Connection name (required in non-interactive mode)").option("--env-file <path>", "Path to env file for this connection").option("--remove <name>", "Remove a named connection").option("--rename <names>", "Rename a connection (format: old:new)").action(async (options) => {
|
|
16876
17107
|
try {
|
|
16877
17108
|
await initCommandHandler(options, initCommand);
|
|
16878
17109
|
} catch (error) {
|
|
@@ -18917,7 +19148,7 @@ var init_assert_artifact = __esm(() => {
|
|
|
18917
19148
|
|
|
18918
19149
|
// src/core/verification/reader.ts
|
|
18919
19150
|
import { lstat as lstat2, readdir, readFile as readFile2 } from "fs/promises";
|
|
18920
|
-
import { join as join13, isAbsolute, resolve as
|
|
19151
|
+
import { join as join13, isAbsolute as isAbsolute2, resolve as resolve4, sep } from "path";
|
|
18921
19152
|
function isArtifactFilename(name) {
|
|
18922
19153
|
return /^verification-.*\.json$/.test(name);
|
|
18923
19154
|
}
|
|
@@ -19058,17 +19289,17 @@ function summarizeVerificationArtifacts(input, filters) {
|
|
|
19058
19289
|
return { storageDir: input.storageDir, latest, counts, subjects };
|
|
19059
19290
|
}
|
|
19060
19291
|
function looksLikePath(selector) {
|
|
19061
|
-
return selector.includes("/") || selector.includes("\\") ||
|
|
19292
|
+
return selector.includes("/") || selector.includes("\\") || isAbsolute2(selector);
|
|
19062
19293
|
}
|
|
19063
19294
|
function findVerificationArtifact(input, selector) {
|
|
19064
19295
|
if (looksLikePath(selector)) {
|
|
19065
|
-
let resolved =
|
|
19296
|
+
let resolved = resolve4(selector), root = resolve4(input.storageDir);
|
|
19066
19297
|
if (resolved !== root && !resolved.startsWith(root + sep))
|
|
19067
19298
|
throw new VerificationArtifactSelectionError(`Path is outside the verification directory: ${resolved}`);
|
|
19068
|
-
let hit = input.artifacts.find((r) =>
|
|
19299
|
+
let hit = input.artifacts.find((r) => resolve4(r.path) === resolved);
|
|
19069
19300
|
if (hit)
|
|
19070
19301
|
return hit;
|
|
19071
|
-
let bad = input.invalid.find((r) =>
|
|
19302
|
+
let bad = input.invalid.find((r) => resolve4(r.path) === resolved);
|
|
19072
19303
|
if (bad)
|
|
19073
19304
|
throw new VerificationArtifactSelectionError(`Artifact at ${bad.filename} is invalid: ${bad.error}`);
|
|
19074
19305
|
throw new VerificationArtifactSelectionError(`No artifact found at path: ${resolved}`);
|
|
@@ -19104,7 +19335,7 @@ var init_reader = __esm(() => {
|
|
|
19104
19335
|
|
|
19105
19336
|
// src/core/verification/retention.ts
|
|
19106
19337
|
import { lstat as lstat3, unlink as unlink6 } from "fs/promises";
|
|
19107
|
-
import { basename as basename2, resolve as
|
|
19338
|
+
import { basename as basename2, resolve as resolve5, sep as sep2 } from "path";
|
|
19108
19339
|
function parseOlderThanDays(raw) {
|
|
19109
19340
|
let match = /^(\d+)d$/.exec(raw);
|
|
19110
19341
|
if (!match)
|
|
@@ -19153,14 +19384,14 @@ function boundError2(message) {
|
|
|
19153
19384
|
return single.length <= 200 ? single : single.slice(0, 199) + "\u2026";
|
|
19154
19385
|
}
|
|
19155
19386
|
function isInsideStorageDir(storageDir, candidatePath) {
|
|
19156
|
-
let base =
|
|
19387
|
+
let base = resolve5(storageDir), resolved = resolve5(candidatePath);
|
|
19157
19388
|
return resolved === base || resolved.startsWith(base + sep2);
|
|
19158
19389
|
}
|
|
19159
19390
|
function hasArtifactFilename(candidatePath) {
|
|
19160
19391
|
return /^verification-.*\.json$/.test(basename2(candidatePath));
|
|
19161
19392
|
}
|
|
19162
19393
|
async function safeDeleteCandidate(storageDir, candidate) {
|
|
19163
|
-
let resolved =
|
|
19394
|
+
let resolved = resolve5(candidate.path);
|
|
19164
19395
|
if (!isInsideStorageDir(storageDir, resolved))
|
|
19165
19396
|
return { ok: !1, reason: "outside-storage-dir" };
|
|
19166
19397
|
if (!hasArtifactFilename(resolved))
|
|
@@ -19306,7 +19537,7 @@ var init_legacy = __esm(() => {
|
|
|
19306
19537
|
// src/core/evidence-receipt/index.ts
|
|
19307
19538
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
|
|
19308
19539
|
import { link as link2, lstat as lstat4, mkdir as mkdir10, readFile as readFile3, realpath, unlink as unlink7, writeFile as writeFile4 } from "fs/promises";
|
|
19309
|
-
import { basename as basename3, dirname as dirname5, relative, resolve as
|
|
19540
|
+
import { basename as basename3, dirname as dirname5, relative, resolve as resolve6, sep as sep3 } from "path";
|
|
19310
19541
|
function exact(value, keys, label) {
|
|
19311
19542
|
if (Object.keys(value).length !== keys.length || Object.keys(value).some((key) => !keys.includes(key)))
|
|
19312
19543
|
throw new EvidenceReceiptValidationError(`${label} contains an unknown field`);
|
|
@@ -19546,7 +19777,7 @@ async function existingAncestor(path) {
|
|
|
19546
19777
|
}
|
|
19547
19778
|
async function writeEvidenceReceipt(workspaceRoot, outputPath, receipt) {
|
|
19548
19779
|
parseEvidenceReceipt(receipt);
|
|
19549
|
-
let workspace = await realpath(workspaceRoot), requested =
|
|
19780
|
+
let workspace = await realpath(workspaceRoot), requested = resolve6(workspace, outputPath);
|
|
19550
19781
|
if (!inside(workspace, requested))
|
|
19551
19782
|
throw new EvidenceReceiptValidationError("output path must stay inside the workspace");
|
|
19552
19783
|
let parent = dirname5(requested);
|
|
@@ -19556,7 +19787,7 @@ async function writeEvidenceReceipt(workspaceRoot, outputPath, receipt) {
|
|
|
19556
19787
|
let realParent = await realpath(parent);
|
|
19557
19788
|
if (!inside(workspace, realParent))
|
|
19558
19789
|
throw new EvidenceReceiptValidationError("output path resolves outside the workspace");
|
|
19559
|
-
let target =
|
|
19790
|
+
let target = resolve6(realParent, basename3(requested));
|
|
19560
19791
|
try {
|
|
19561
19792
|
throw await lstat4(target), new EvidenceReceiptValidationError("evidence receipt output already exists");
|
|
19562
19793
|
} catch (error) {
|
|
@@ -19565,7 +19796,7 @@ async function writeEvidenceReceipt(workspaceRoot, outputPath, receipt) {
|
|
|
19565
19796
|
if (error.code !== "ENOENT")
|
|
19566
19797
|
throw error;
|
|
19567
19798
|
}
|
|
19568
|
-
let temp =
|
|
19799
|
+
let temp = resolve6(realParent, `.${basename3(target)}.${process.pid}.${randomUUID2()}.tmp`);
|
|
19569
19800
|
try {
|
|
19570
19801
|
await writeFile4(temp, `${JSON.stringify(receipt, null, 2)}
|
|
19571
19802
|
`, "utf8");
|
|
@@ -25777,8 +26008,16 @@ var init_apply_gate = __esm(() => {
|
|
|
25777
26008
|
});
|
|
25778
26009
|
|
|
25779
26010
|
// src/core/recovery/apply-exec.ts
|
|
26011
|
+
import { fileURLToPath } from "url";
|
|
25780
26012
|
async function executeStep(argv, opts) {
|
|
25781
|
-
let stdoutCap = opts.stdoutCap ??
|
|
26013
|
+
let stdoutCap = opts.stdoutCap ?? DEFAULT_CAP, stderrCap = opts.stderrCap ?? DEFAULT_CAP, startedAt = performance.now(), command = argv[0] === "dbcli" ? [
|
|
26014
|
+
process.execPath,
|
|
26015
|
+
"--no-env-file",
|
|
26016
|
+
CLI_ENTRY,
|
|
26017
|
+
...opts.target ? ["--config", opts.target.configPath] : [],
|
|
26018
|
+
...opts.target?.connectionName ? ["--use", opts.target.connectionName] : [],
|
|
26019
|
+
...argv.slice(1)
|
|
26020
|
+
] : argv, proc = Bun.spawn(command, {
|
|
25782
26021
|
cwd: opts.cwd,
|
|
25783
26022
|
env: opts.env,
|
|
25784
26023
|
stdin: "ignore",
|
|
@@ -25826,6 +26065,10 @@ async function capStream(stream, cap) {
|
|
|
25826
26065
|
} catch {}
|
|
25827
26066
|
return { text, truncated };
|
|
25828
26067
|
}
|
|
26068
|
+
var CLI_ENTRY, DEFAULT_CAP = 65536;
|
|
26069
|
+
var init_apply_exec = __esm(() => {
|
|
26070
|
+
CLI_ENTRY = fileURLToPath(new URL(import.meta.url.endsWith(".ts") ? "../../cli.ts" : "./cli.mjs", import.meta.url));
|
|
26071
|
+
});
|
|
25829
26072
|
|
|
25830
26073
|
// src/core/recovery/verify-heuristic.ts
|
|
25831
26074
|
function evaluateVerify(code, outcome) {
|
|
@@ -25894,7 +26137,8 @@ async function runVerifyStep(step, ctx) {
|
|
|
25894
26137
|
env: ctx.env,
|
|
25895
26138
|
timeoutMs: ctx.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
25896
26139
|
stdoutCap: ctx.stdoutCap,
|
|
25897
|
-
stderrCap: ctx.stderrCap
|
|
26140
|
+
stderrCap: ctx.stderrCap,
|
|
26141
|
+
target: ctx.target
|
|
25898
26142
|
}), ok = outcome.exitCode === 0 && !outcome.timedOut, result = {
|
|
25899
26143
|
order: VERIFY_ORDER_SENTINEL,
|
|
25900
26144
|
command: step.command,
|
|
@@ -25915,6 +26159,7 @@ async function runVerifyStep(step, ctx) {
|
|
|
25915
26159
|
var executor, DEFAULT_TIMEOUT_MS = 60000, VERIFY_ORDER_SENTINEL = 0;
|
|
25916
26160
|
var init_apply_verify = __esm(() => {
|
|
25917
26161
|
init_apply_gate();
|
|
26162
|
+
init_apply_exec();
|
|
25918
26163
|
init_verification();
|
|
25919
26164
|
executor = executeStep;
|
|
25920
26165
|
});
|
|
@@ -25923,6 +26168,15 @@ var init_apply_verify = __esm(() => {
|
|
|
25923
26168
|
async function runApply(input, opts) {
|
|
25924
26169
|
let startedAt = (/* @__PURE__ */ new Date()).toISOString(), results = [], finalStatus = "skipped-only", stoppedAt = null;
|
|
25925
26170
|
for (let step of input.envelope.recovery) {
|
|
26171
|
+
if (input.envelope.error.details?.connectionCode === "TLS_ERROR" && !opts.target) {
|
|
26172
|
+
results.push({
|
|
26173
|
+
order: step.order,
|
|
26174
|
+
command: step.command,
|
|
26175
|
+
status: "skipped:unsafe-command",
|
|
26176
|
+
reason: t("errors.sql_tls_recovery_target")
|
|
26177
|
+
});
|
|
26178
|
+
continue;
|
|
26179
|
+
}
|
|
25926
26180
|
let decision = classifyStep(step, opts.allowWrite, input.envelope.error.code);
|
|
25927
26181
|
if (decision.kind !== "run") {
|
|
25928
26182
|
results.push({
|
|
@@ -25938,7 +26192,8 @@ async function runApply(input, opts) {
|
|
|
25938
26192
|
env: process.env,
|
|
25939
26193
|
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
25940
26194
|
stdoutCap: opts.stdoutCap,
|
|
25941
|
-
stderrCap: opts.stderrCap
|
|
26195
|
+
stderrCap: opts.stderrCap,
|
|
26196
|
+
target: opts.target
|
|
25942
26197
|
}), ok = outcome.exitCode === 0 && !outcome.timedOut;
|
|
25943
26198
|
if (results.push({
|
|
25944
26199
|
order: step.order,
|
|
@@ -25963,7 +26218,8 @@ async function runApply(input, opts) {
|
|
|
25963
26218
|
env: process.env,
|
|
25964
26219
|
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
25965
26220
|
stdoutCap: opts.stdoutCap,
|
|
25966
|
-
stderrCap: opts.stderrCap
|
|
26221
|
+
stderrCap: opts.stderrCap,
|
|
26222
|
+
target: opts.target
|
|
25967
26223
|
});
|
|
25968
26224
|
verifyResult = v.result, verifyStatus = v.status, verificationStatus = v.contractStatus, verificationBlockedReason = v.blockedReason;
|
|
25969
26225
|
}
|
|
@@ -25984,7 +26240,9 @@ async function runApply(input, opts) {
|
|
|
25984
26240
|
}
|
|
25985
26241
|
var executor2, DEFAULT_TIMEOUT_MS2 = 60000;
|
|
25986
26242
|
var init_apply = __esm(() => {
|
|
26243
|
+
init_message_loader();
|
|
25987
26244
|
init_apply_gate();
|
|
26245
|
+
init_apply_exec();
|
|
25988
26246
|
init_apply_verify();
|
|
25989
26247
|
init_apply_types();
|
|
25990
26248
|
executor2 = executeStep;
|
|
@@ -26030,7 +26288,7 @@ var NEXT_SCHEMA_VERSION = 1, STEP_RESULT_SUMMARY_FIELD_CAP = 4096;
|
|
|
26030
26288
|
|
|
26031
26289
|
// src/core/recovery/next-step-schema.ts
|
|
26032
26290
|
import { stat as stat5 } from "fs/promises";
|
|
26033
|
-
import { resolve as
|
|
26291
|
+
import { resolve as resolve8 } from "path";
|
|
26034
26292
|
function summarizeZodError(err) {
|
|
26035
26293
|
return err.issues.map((iss) => `${iss.path.join(".") || "<root>"}: ${iss.message}`).join("; ");
|
|
26036
26294
|
}
|
|
@@ -26045,7 +26303,7 @@ async function loadStepResultSummary(arg, cwd) {
|
|
|
26045
26303
|
return { ok: !1, reason: "empty --result value" };
|
|
26046
26304
|
let raw;
|
|
26047
26305
|
if (arg.startsWith("@")) {
|
|
26048
|
-
let path =
|
|
26306
|
+
let path = resolve8(cwd, arg.slice(1));
|
|
26049
26307
|
try {
|
|
26050
26308
|
await stat5(path);
|
|
26051
26309
|
} catch {
|
|
@@ -31556,12 +31814,12 @@ function escapeXml(str) {
|
|
|
31556
31814
|
|
|
31557
31815
|
// src/core/data-access/index.ts
|
|
31558
31816
|
import { lstat as lstat5, realpath as realpath2 } from "fs/promises";
|
|
31559
|
-
import { isAbsolute as
|
|
31817
|
+
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve9, sep as sep5 } from "path";
|
|
31560
31818
|
function hasDataAccessReferenceIssue(issues) {
|
|
31561
31819
|
return issues.some(({ message }) => REFERENCE_MESSAGES3.has(message));
|
|
31562
31820
|
}
|
|
31563
31821
|
async function loadDataAccessManifest(input) {
|
|
31564
|
-
let workspace = await safeWorkspace(input.workspaceRoot, []), requested =
|
|
31822
|
+
let workspace = await safeWorkspace(input.workspaceRoot, []), requested = resolve9(workspace, input.filePath ?? defaultDataAccessManifestFile(workspace));
|
|
31565
31823
|
if (!inside2(workspace, requested))
|
|
31566
31824
|
throw new DataAccessManifestValidationError([
|
|
31567
31825
|
{ path: "$", message: "file must stay inside the workspace" }
|
|
@@ -31655,16 +31913,16 @@ async function safeWorkspace(workspaceRoot, issues) {
|
|
|
31655
31913
|
try {
|
|
31656
31914
|
return await realpath2(workspaceRoot);
|
|
31657
31915
|
} catch {
|
|
31658
|
-
return issue3(issues, "$", "workspace is unavailable"),
|
|
31916
|
+
return issue3(issues, "$", "workspace is unavailable"), resolve9(workspaceRoot);
|
|
31659
31917
|
}
|
|
31660
31918
|
}
|
|
31661
31919
|
async function normalizeSource(operation, index, workspace, issues) {
|
|
31662
31920
|
let path = `$.operations[${index}].source`;
|
|
31663
31921
|
if (operation.source.length === 0 || operation.source.length > MAX_NAME_LENGTH2 || /[\0\r\n]/.test(operation.source))
|
|
31664
31922
|
return issue3(issues, path, "must be a bounded single-line source path"), operation;
|
|
31665
|
-
if (
|
|
31923
|
+
if (isAbsolute3(operation.source))
|
|
31666
31924
|
return issue3(issues, path, "must be workspace-relative"), operation;
|
|
31667
|
-
let target =
|
|
31925
|
+
let target = resolve9(workspace, operation.source);
|
|
31668
31926
|
if (!inside2(workspace, target))
|
|
31669
31927
|
return issue3(issues, path, "must stay inside the workspace"), operation;
|
|
31670
31928
|
try {
|
|
@@ -31682,7 +31940,7 @@ async function normalizeSource(operation, index, workspace, issues) {
|
|
|
31682
31940
|
}
|
|
31683
31941
|
function inside2(root, target) {
|
|
31684
31942
|
let value = relative3(root, target);
|
|
31685
|
-
return value !== "" && !
|
|
31943
|
+
return value !== "" && !isAbsolute3(value) && !value.startsWith(`..${sep5}`) && value !== ".." && !value.includes(`..${sep5}`);
|
|
31686
31944
|
}
|
|
31687
31945
|
function record4(value, path, issues) {
|
|
31688
31946
|
if (typeof value === "object" && value !== null && !Array.isArray(value))
|
|
@@ -31709,7 +31967,7 @@ function issue3(issues, path, message) {
|
|
|
31709
31967
|
function codePointOrder(left, right) {
|
|
31710
31968
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
31711
31969
|
}
|
|
31712
|
-
var DATA_ACCESS_MANIFEST_VERSION = 1, MAX_FILE_BYTES3 = 524288, MAX_OPERATIONS = 500, MAX_REFERENCES = 100, MAX_NAME_LENGTH2 = 200, defaultDataAccessManifestFile = (workspaceRoot) =>
|
|
31970
|
+
var DATA_ACCESS_MANIFEST_VERSION = 1, MAX_FILE_BYTES3 = 524288, MAX_OPERATIONS = 500, MAX_REFERENCES = 100, MAX_NAME_LENGTH2 = 200, defaultDataAccessManifestFile = (workspaceRoot) => resolve9(workspaceRoot, "dbcli.data-access.json"), PROTECTED_REFERENCE = "must not contain a protected semantic reference", UNAVAILABLE_REFERENCE = "must reference an available semantic entity", REFERENCE_MESSAGES3, DataAccessManifestValidationError;
|
|
31713
31971
|
var init_data_access = __esm(() => {
|
|
31714
31972
|
init_semantic();
|
|
31715
31973
|
REFERENCE_MESSAGES3 = /* @__PURE__ */ new Set([
|
|
@@ -31727,7 +31985,7 @@ var init_data_access = __esm(() => {
|
|
|
31727
31985
|
|
|
31728
31986
|
// src/core/context/context-v2.ts
|
|
31729
31987
|
import { lstat as lstat6, realpath as realpath3 } from "fs/promises";
|
|
31730
|
-
import { join as join28, resolve as
|
|
31988
|
+
import { join as join28, resolve as resolve10 } from "path";
|
|
31731
31989
|
async function gatherContextV2(workspaceRoot, configPath) {
|
|
31732
31990
|
await rejectUnknownConfiguredEngine(configPath);
|
|
31733
31991
|
let config = await configModule.read(configPath), system = config.connection?.system ?? "unknown";
|
|
@@ -31887,7 +32145,7 @@ function parseElasticsearchIndex(raw, manager, blockedTerms) {
|
|
|
31887
32145
|
return unique(fields.map(({ path }) => path), "INVALID_SCHEMA_CACHE"), { id, name, fields };
|
|
31888
32146
|
}
|
|
31889
32147
|
async function collectRedisResources(workspaceRoot, config, blockedTerms, gaps) {
|
|
31890
|
-
let filePath =
|
|
32148
|
+
let filePath = resolve10(workspaceRoot, "dbcli.redis-context.json"), stats;
|
|
31891
32149
|
try {
|
|
31892
32150
|
stats = await lstat6(filePath);
|
|
31893
32151
|
} catch {
|
|
@@ -32682,6 +32940,14 @@ var init_registry = __esm(() => {
|
|
|
32682
32940
|
init_types2();
|
|
32683
32941
|
init_permission_guard();
|
|
32684
32942
|
DECLARATIONS = [
|
|
32943
|
+
{
|
|
32944
|
+
id: "connection.tls",
|
|
32945
|
+
key: "connectionTls",
|
|
32946
|
+
command: "query",
|
|
32947
|
+
description: "Use explicit verified SQL TLS from a configured profile. Availability does not validate TLS policy, CA loading or handshake, or authorize configuration changes.",
|
|
32948
|
+
statementType: "SELECT",
|
|
32949
|
+
requiresConnection: !0
|
|
32950
|
+
},
|
|
32685
32951
|
{
|
|
32686
32952
|
id: "connection.init",
|
|
32687
32953
|
key: "init",
|
|
@@ -36247,7 +36513,7 @@ var init_artifact_json = __esm(() => {
|
|
|
36247
36513
|
});
|
|
36248
36514
|
|
|
36249
36515
|
// src/core/orm-drift/input.ts
|
|
36250
|
-
import { resolve as
|
|
36516
|
+
import { resolve as resolve11 } from "path";
|
|
36251
36517
|
function isDdlFormat(format) {
|
|
36252
36518
|
return format === "ddl" || format in ORM_ALIASES;
|
|
36253
36519
|
}
|
|
@@ -36264,7 +36530,7 @@ async function expandOrmPaths(inputs) {
|
|
|
36264
36530
|
let paths = parseAgainstOrmValues(inputs), expanded = /* @__PURE__ */ new Set;
|
|
36265
36531
|
for (let path of paths) {
|
|
36266
36532
|
if (!hasGlobMagic(path)) {
|
|
36267
|
-
expanded.add(
|
|
36533
|
+
expanded.add(resolve11(path));
|
|
36268
36534
|
continue;
|
|
36269
36535
|
}
|
|
36270
36536
|
let matches = await Array.fromAsync(new Bun.Glob(path).scan({
|
|
@@ -36275,7 +36541,7 @@ async function expandOrmPaths(inputs) {
|
|
|
36275
36541
|
if (matches.length === 0)
|
|
36276
36542
|
throw Error(`ORM schema glob matched no files: ${path}`);
|
|
36277
36543
|
for (let match of matches)
|
|
36278
|
-
expanded.add(
|
|
36544
|
+
expanded.add(resolve11(match));
|
|
36279
36545
|
}
|
|
36280
36546
|
return [...expanded].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
36281
36547
|
}
|
|
@@ -38844,10 +39110,32 @@ var init_envelope_schema = __esm(() => {
|
|
|
38844
39110
|
|
|
38845
39111
|
// src/commands/recover.ts
|
|
38846
39112
|
import { stat as stat6 } from "fs/promises";
|
|
38847
|
-
import { resolve as
|
|
39113
|
+
import { resolve as resolve12 } from "path";
|
|
39114
|
+
async function tlsRecoveryTarget() {
|
|
39115
|
+
let explicitConfig = !1;
|
|
39116
|
+
for (let command = recoverCommand;command; command = command.parent)
|
|
39117
|
+
for (let key of ["config", "global"]) {
|
|
39118
|
+
let source = command.getOptionValueSource(key);
|
|
39119
|
+
if (source && source !== "default")
|
|
39120
|
+
explicitConfig = !0;
|
|
39121
|
+
}
|
|
39122
|
+
if (!explicitConfig)
|
|
39123
|
+
throw new RecoverCliError(t("errors.sql_tls_recovery_config"), EXIT_CODE.malformed);
|
|
39124
|
+
let configPath = resolve12(resolveConfigPath(recoverCommand)), connectionName = resolveConnectionSelector({
|
|
39125
|
+
root: recoverCommand.optsWithGlobals().use,
|
|
39126
|
+
environment: process.env.DBCLI_CONNECTION
|
|
39127
|
+
});
|
|
39128
|
+
await Promise.resolve().then(() => init_config());
|
|
39129
|
+
let config = await configModule.read(configPath, connectionName, { loadLayeredSchema: !1 });
|
|
39130
|
+
if (config?.effectiveConnectionName && !connectionName)
|
|
39131
|
+
throw new RecoverCliError(t("errors.sql_tls_recovery_profile"), EXIT_CODE.malformed);
|
|
39132
|
+
if (!config || !("sqlTls" in config.connection) || config.connection.sqlTls?.mode !== "verify-full")
|
|
39133
|
+
throw new RecoverCliError(t("errors.sql_tls_recovery_required"), EXIT_CODE.malformed);
|
|
39134
|
+
return { configPath, ...connectionName && { connectionName } };
|
|
39135
|
+
}
|
|
38848
39136
|
async function resolveApplySource(opts) {
|
|
38849
39137
|
if (opts.from !== void 0) {
|
|
38850
|
-
let path =
|
|
39138
|
+
let path = resolve12(opts.cwd, opts.from), raw;
|
|
38851
39139
|
try {
|
|
38852
39140
|
raw = await Bun.file(path).text();
|
|
38853
39141
|
} catch {
|
|
@@ -38968,6 +39256,7 @@ async function runNext(options, source) {
|
|
|
38968
39256
|
}
|
|
38969
39257
|
var ALLOWED_FORMATS13, ALLOWED_TIERS, RecoverCliError, recoverCommand;
|
|
38970
39258
|
var init_recover = __esm(() => {
|
|
39259
|
+
init_message_loader();
|
|
38971
39260
|
init_esm();
|
|
38972
39261
|
init_recovery();
|
|
38973
39262
|
init_last_envelope();
|
|
@@ -38975,6 +39264,7 @@ var init_recover = __esm(() => {
|
|
|
38975
39264
|
init_next_step();
|
|
38976
39265
|
init_next_step_schema();
|
|
38977
39266
|
init_validation();
|
|
39267
|
+
init_config_path();
|
|
38978
39268
|
ALLOWED_FORMATS13 = ["json", "markdown"], ALLOWED_TIERS = ["readonly-cmd", "write-cmd"];
|
|
38979
39269
|
RecoverCliError = class RecoverCliError extends Error {
|
|
38980
39270
|
exitCode;
|
|
@@ -39026,11 +39316,11 @@ var init_recover = __esm(() => {
|
|
|
39026
39316
|
console.log(JSON.stringify({ ...source.envelope, audit_recent }, null, 2));
|
|
39027
39317
|
return;
|
|
39028
39318
|
}
|
|
39029
|
-
let noVerify = options.verify === !1, result = await runApply({
|
|
39319
|
+
let noVerify = options.verify === !1, target = source.envelope.error.details?.connectionCode === "TLS_ERROR" ? await tlsRecoveryTarget() : void 0, result = await runApply({
|
|
39030
39320
|
envelope: source.envelope,
|
|
39031
39321
|
cwd: source.cwd,
|
|
39032
39322
|
source: { kind: source.kind, path: source.path }
|
|
39033
|
-
}, { allowWrite, noVerify }), verificationArtifactPath;
|
|
39323
|
+
}, { allowWrite, noVerify, target }), verificationArtifactPath;
|
|
39034
39324
|
if (options.writeVerificationArtifact === !0 && result.verificationStatus !== void 0)
|
|
39035
39325
|
try {
|
|
39036
39326
|
await Promise.resolve().then(() => init_verification());
|
|
@@ -50245,7 +50535,7 @@ var init_backfill_artifact = __esm(() => {
|
|
|
50245
50535
|
});
|
|
50246
50536
|
|
|
50247
50537
|
// src/commands/backfill.ts
|
|
50248
|
-
import { dirname as dirname18, resolve as
|
|
50538
|
+
import { dirname as dirname18, resolve as resolve13 } from "path";
|
|
50249
50539
|
import { mkdir as mkdir21 } from "fs/promises";
|
|
50250
50540
|
function identityFor(config, name) {
|
|
50251
50541
|
let connection = config.connections[name];
|
|
@@ -50272,7 +50562,7 @@ var init_backfill = __esm(() => {
|
|
|
50272
50562
|
init_backfill_artifact();
|
|
50273
50563
|
backfillCommand = new Command2("backfill").description("Generate reviewable source-to-SQL backfill artifacts; never executes writes");
|
|
50274
50564
|
backfillCommand.command("artifact").description("Build a dry-run backfill artifact from a bounded JSON source catalog and two named connections").requiredOption("--source <path>", "JSON source catalog with table, keyColumns, rows, verifyQuery, and expect").requiredOption("--source-use <name>", "Named source connection used to identify the catalog environment").requiredOption("--target-use <name>", "Named target connection for generated SQL").option("--out <path>", "Write artifact JSON to this path").option("--stdout", "Print artifact JSON instead of writing it", !1).action(async (options, command) => {
|
|
50275
|
-
let configPath = resolveConfigPath(command), config = await readV2Config(configPath), sourcePath =
|
|
50565
|
+
let configPath = resolveConfigPath(command), config = await readV2Config(configPath), sourcePath = resolve13(options.source), sourceContent = await Bun.file(sourcePath).text(), raw;
|
|
50276
50566
|
try {
|
|
50277
50567
|
raw = JSON.parse(sourceContent);
|
|
50278
50568
|
} catch {
|
|
@@ -50289,7 +50579,7 @@ var init_backfill = __esm(() => {
|
|
|
50289
50579
|
console.log(JSON.stringify(artifact, null, 2));
|
|
50290
50580
|
return;
|
|
50291
50581
|
}
|
|
50292
|
-
let out =
|
|
50582
|
+
let out = resolve13(options.out ?? `.dbcli/backfills/${artifact.source.sha256.slice(0, 12)}.json`);
|
|
50293
50583
|
await mkdir21(dirname18(out), { recursive: !0 }), await Bun.write(out, JSON.stringify(artifact, null, 2) + `
|
|
50294
50584
|
`), console.log(JSON.stringify({ path: out, artifact }, null, 2));
|
|
50295
50585
|
});
|
|
@@ -50423,7 +50713,7 @@ var init_legacy2 = __esm(() => {
|
|
|
50423
50713
|
// src/core/evidence-pack/index.ts
|
|
50424
50714
|
import { createHash as createHash10, randomUUID as randomUUID5 } from "crypto";
|
|
50425
50715
|
import { link as link3, lstat as lstat7, mkdir as mkdir22, realpath as realpath4, unlink as unlink8, writeFile as writeFile10 } from "fs/promises";
|
|
50426
|
-
import { basename as basename9, dirname as dirname19, relative as relative6, resolve as
|
|
50716
|
+
import { basename as basename9, dirname as dirname19, relative as relative6, resolve as resolve14, sep as sep8 } from "path";
|
|
50427
50717
|
function isRecord6(value) {
|
|
50428
50718
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50429
50719
|
}
|
|
@@ -50662,7 +50952,7 @@ async function realExistingAncestor(path) {
|
|
|
50662
50952
|
}
|
|
50663
50953
|
}
|
|
50664
50954
|
async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
50665
|
-
let workspace = await realpath4(workspaceRoot), requestedTarget =
|
|
50955
|
+
let workspace = await realpath4(workspaceRoot), requestedTarget = resolve14(workspace, outputPath);
|
|
50666
50956
|
if (!isInside(workspace, requestedTarget))
|
|
50667
50957
|
throw new EvidencePackValidationError("output path must stay inside the workspace");
|
|
50668
50958
|
let parent = dirname19(requestedTarget), existingAncestor = await realExistingAncestor(parent);
|
|
@@ -50672,7 +50962,7 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
50672
50962
|
let realParent = await realpath4(parent);
|
|
50673
50963
|
if (!isInside(workspace, realParent) && realParent !== workspace)
|
|
50674
50964
|
throw new EvidencePackValidationError("output path resolves outside the workspace");
|
|
50675
|
-
let target =
|
|
50965
|
+
let target = resolve14(realParent, basename9(requestedTarget));
|
|
50676
50966
|
try {
|
|
50677
50967
|
throw await lstat7(target), new EvidencePackValidationError("evidence pack output already exists");
|
|
50678
50968
|
} catch (error) {
|
|
@@ -50681,7 +50971,7 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
50681
50971
|
if (error.code !== "ENOENT")
|
|
50682
50972
|
throw error;
|
|
50683
50973
|
}
|
|
50684
|
-
let temp =
|
|
50974
|
+
let temp = resolve14(realParent, `.${basename9(target)}.${process.pid}.${randomUUID5()}.tmp`);
|
|
50685
50975
|
try {
|
|
50686
50976
|
await writeFile10(temp, `${JSON.stringify(pack, null, 2)}
|
|
50687
50977
|
`, "utf8");
|
|
@@ -50761,7 +51051,7 @@ var init_evidence_pack = __esm(() => {
|
|
|
50761
51051
|
});
|
|
50762
51052
|
|
|
50763
51053
|
// src/commands/evidence.ts
|
|
50764
|
-
import { join as join45, relative as relative7, resolve as
|
|
51054
|
+
import { join as join45, relative as relative7, resolve as resolve15, sep as sep9 } from "path";
|
|
50765
51055
|
import { realpath as realpath5 } from "fs/promises";
|
|
50766
51056
|
import { createHash as createHash11 } from "crypto";
|
|
50767
51057
|
function safeMessage(error) {
|
|
@@ -50881,7 +51171,7 @@ async function resolveReferences(verificationSelectors, auditSelectors, receiptP
|
|
|
50881
51171
|
references.push(auditReference(selectAudit(entries, selector), connectionName));
|
|
50882
51172
|
}
|
|
50883
51173
|
for (let receiptPath of receiptPaths) {
|
|
50884
|
-
let workspace = await realpath5(process.cwd()), requested =
|
|
51174
|
+
let workspace = await realpath5(process.cwd()), requested = resolve15(workspace, receiptPath), path = relative7(workspace, requested);
|
|
50885
51175
|
if (path === "" || path.startsWith(`..${sep9}`) || path === ".." || path.includes(`..${sep9}`))
|
|
50886
51176
|
throw new EvidencePackValidationError("receipt evidence path must be workspace-relative");
|
|
50887
51177
|
let resolved = await realpath5(requested).catch(() => {
|
|
@@ -50920,7 +51210,7 @@ async function expiredReferences(pack, configPath, config) {
|
|
|
50920
51210
|
if (!found || audits.connectionName !== reference.connectionName || !sameReference(reference, auditReference(found, audits.connectionName)))
|
|
50921
51211
|
throw Error("missing");
|
|
50922
51212
|
} else {
|
|
50923
|
-
let workspace = await realpath5(process.cwd()), target = await realpath5(
|
|
51213
|
+
let workspace = await realpath5(process.cwd()), target = await realpath5(resolve15(workspace, reference.path)), safePath = relative7(workspace, target);
|
|
50924
51214
|
if (safePath.startsWith(`..${sep9}`) || safePath === ".." || safePath.includes(`..${sep9}`))
|
|
50925
51215
|
throw Error("outside");
|
|
50926
51216
|
let receipt = await readEvidenceReceipt(target), digest = `sha256:${createHash11("sha256").update(JSON.stringify(receipt)).digest("hex")}`;
|
|
@@ -51928,7 +52218,7 @@ function escapeText2(value) {
|
|
|
51928
52218
|
// src/commands/impact.ts
|
|
51929
52219
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
51930
52220
|
import { link as link4, lstat as lstat9, mkdir as mkdir23, realpath as realpath6, unlink as unlink9, writeFile as writeFile11 } from "fs/promises";
|
|
51931
|
-
import { basename as basename10, dirname as dirname20, relative as relative8, resolve as
|
|
52221
|
+
import { basename as basename10, dirname as dirname20, relative as relative8, resolve as resolve16, sep as sep10 } from "path";
|
|
51932
52222
|
function collectOption3(value, previous) {
|
|
51933
52223
|
return [...previous, value];
|
|
51934
52224
|
}
|
|
@@ -52094,7 +52384,7 @@ async function loadObservedWorkloadSource(path, workspaceRoot, blocked) {
|
|
|
52094
52384
|
malformedLines: 0,
|
|
52095
52385
|
issues: []
|
|
52096
52386
|
};
|
|
52097
|
-
return loadWorkloadSource({ path:
|
|
52387
|
+
return loadWorkloadSource({ path: resolve16(workspaceRoot, path), blockedIdentifiers: blocked });
|
|
52098
52388
|
}
|
|
52099
52389
|
function shouldFail(report, failOn) {
|
|
52100
52390
|
if (failOn === "never")
|
|
@@ -52127,7 +52417,7 @@ async function realExistingAncestor2(path) {
|
|
|
52127
52417
|
}
|
|
52128
52418
|
}
|
|
52129
52419
|
async function resolveOutput(workspaceRoot, outputPath) {
|
|
52130
|
-
let workspace = await realpath6(workspaceRoot), target =
|
|
52420
|
+
let workspace = await realpath6(workspaceRoot), target = resolve16(workspace, outputPath);
|
|
52131
52421
|
if (!isInside2(workspace, target))
|
|
52132
52422
|
throw Error("output path must stay inside the workspace");
|
|
52133
52423
|
let parent = dirname20(target), existingAncestor = await realExistingAncestor2(parent);
|
|
@@ -52137,7 +52427,7 @@ async function resolveOutput(workspaceRoot, outputPath) {
|
|
|
52137
52427
|
let realParent = await realpath6(parent);
|
|
52138
52428
|
if (!isInside2(workspace, realParent) && realParent !== workspace)
|
|
52139
52429
|
throw Error("output path must stay inside the workspace");
|
|
52140
|
-
return
|
|
52430
|
+
return resolve16(realParent, basename10(target));
|
|
52141
52431
|
}
|
|
52142
52432
|
async function assertOutputParent(workspaceRoot, path) {
|
|
52143
52433
|
let workspace = await realpath6(workspaceRoot), parent = await realpath6(dirname20(path));
|
|
@@ -52152,7 +52442,7 @@ async function writeOutput(workspaceRoot, path, content) {
|
|
|
52152
52442
|
if (error.code !== "ENOENT")
|
|
52153
52443
|
throw error;
|
|
52154
52444
|
}
|
|
52155
|
-
let temp =
|
|
52445
|
+
let temp = resolve16(dirname20(path), `.${basename10(path)}.${process.pid}.${randomUUID6()}.tmp`);
|
|
52156
52446
|
try {
|
|
52157
52447
|
await writeFile11(temp, content, "utf8"), await assertOutputParent(workspaceRoot, path), await link4(temp, path);
|
|
52158
52448
|
} catch (error) {
|
|
@@ -52216,7 +52506,7 @@ var init_impact2 = __esm(() => {
|
|
|
52216
52506
|
observedWorkload,
|
|
52217
52507
|
blockedIdentifiers
|
|
52218
52508
|
}), rendered = formatImpact(report, format), output = await resolveOutput(workspaceRoot, String(options.output));
|
|
52219
|
-
if (
|
|
52509
|
+
if (resolve16(workspaceRoot, String(options.design)) === output)
|
|
52220
52510
|
throw Error("design and output paths must differ");
|
|
52221
52511
|
await writeOutput(workspaceRoot, output, rendered), console.log(JSON.stringify({
|
|
52222
52512
|
path: relative8(workspaceRoot, output),
|