Show effective settings in bds-cli config.
This commit is contained in:
@@ -16,7 +16,7 @@ The project is under active development. Core blogging workflows are broadly ava
|
|||||||
- Read-only in-app browsers for the bundled global `DOCUMENTATION.md` and the generated Lua API reference, public types, and runnable examples, with safe GFM rendering and confirmed external links.
|
- Read-only in-app browsers for the bundled global `DOCUMENTATION.md` and the generated Lua API reference, public types, and runnable examples, with safe GFM rendering and confirmed external links.
|
||||||
- A localized OPML menu editor manages pages, submenus, and category archives with protected Home ordering, keyboard-accessible tree controls, drag-and-drop, and bDS2-compatible persistence.
|
- A localized OPML menu editor manages pages, submenus, and category archives with protected Home ordering, keyboard-accessible tree controls, drag-and-drop, and bDS2-compatible persistence.
|
||||||
- Project-scoped typed domain events synchronize desktop views with shared-engine and future CLI mutations; persisted CLI notifications are consumed once, and the selected UI language is shared through settings.
|
- Project-scoped typed domain events synchronize desktop views with shared-engine and future CLI mutations; persisted CLI notifications are consumed once, and the selected UI language is shared through settings.
|
||||||
- Headless `bds-cli` automation for rebuild/repair/render, publishing and Git sync, post/media/gallery creation, shared settings/projects, utility Lua tasks, JSON I/O, airplane-mode AI routing, and guarded launcher installation from Settings → Data or `bds-cli install`.
|
- Headless `bds-cli` automation for rebuild/repair/render, publishing and Git sync, post/media/gallery creation, effective shared settings with secret-presence redaction, projects, utility Lua tasks, JSON I/O, airplane-mode AI routing, and guarded launcher installation from Settings → Data or `bds-cli install`.
|
||||||
- Local MCP automation over stdio or a localhost-only stateless HTTP endpoint, with project resources, read/search/count tools, inert write proposals, explicit desktop approval, and opt-in Claude Code/Copilot configuration.
|
- Local MCP automation over stdio or a localhost-only stateless HTTP endpoint, with project resources, read/search/count tools, inert write proposals, explicit desktop approval, and opt-in Claude Code/Copilot configuration.
|
||||||
- A full Ratatui terminal workspace, available locally through `bds-cli tui`/`BDS_MODE=tui` and remotely through authenticated SSH shell sessions, with shared post/template/script editing and publishing, project/search/command overlays, settings, tags, Git, reports, task progress, live multi-client updates, locale changes, and airplane-mode AI gating.
|
- A full Ratatui terminal workspace, available locally through `bds-cli tui`/`BDS_MODE=tui` and remotely through authenticated SSH shell sessions, with shared post/template/script editing and publishing, project/search/command overlays, settings, tags, Git, reports, task progress, live multi-client updates, locale changes, and airplane-mode AI gating.
|
||||||
- `bds-cli server` hosting the shared application engines over a loopback-by-default, public-key-only SSH service, with restrictive private key material, live authorization updates, terminal-session transport, CLI-change synchronization, ordered domain/task events, and native desktop remote-project selection.
|
- `bds-cli server` hosting the shared application engines over a loopback-by-default, public-key-only SSH service, with restrictive private key material, live authorization updates, terminal-session transport, CLI-change synchronization, ordered domain/task events, and native desktop remote-project selection.
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ Done:
|
|||||||
Done:
|
Done:
|
||||||
|
|
||||||
- Domain event bus from `events.allium` for desktop, CLI, TUI, server, and future remote clients, including deterministic subscriptions, project scope, and persisted CLI notification consumption/pruning.
|
- Domain event bus from `events.allium` for desktop, CLI, TUI, server, and future remote clients, including deterministic subscriptions, project scope, and persisted CLI notification consumption/pruning.
|
||||||
- Native `bds-cli` with Clap help/error handling, optional JSON output, shared application paths/database/projects/settings, full and incremental rebuild, derived-data repair, full/targeted/forced generation, publishing, fast-forward Git sync, post/media/gallery creation, offline/local AI routing and translation, project/config operations, sandboxed utility Lua execution, `server`/`tui` boot modes, and guarded launcher installation.
|
- Native `bds-cli` with Clap help/error handling, optional JSON output, shared application paths/database/projects and effective global settings with redacted secret presence, full and incremental rebuild, derived-data repair, full/targeted/forced generation, publishing, fast-forward Git sync, post/media/gallery creation, offline/local AI routing and translation, project/config operations, sandboxed utility Lua execution, `server`/`tui` boot modes, and guarded launcher installation.
|
||||||
- CLI process and dispatch tests use temporary databases/projects; CLI mutations persist deduplicated desktop notifications and imported filesystem metadata survives rebuild.
|
- CLI process and dispatch tests use temporary databases/projects; CLI mutations persist deduplicated desktop notifications and imported filesystem metadata survives rebuild.
|
||||||
- Settings → Data exposes the same localized packaged-launcher installer as `bds-cli install`; its forwarding launcher executes the packaged CLI in place beside the shared runtime.
|
- Settings → Data exposes the same localized packaged-launcher installer as `bds-cli install`; its forwarding launcher executes the packaged CLI in place beside the shared runtime.
|
||||||
- MCP exposes the complete `bds://` resource set and typed read/search/count tools through packaged stdio and stateless localhost-only HTTP transports with Origin/Host validation and CORS.
|
- MCP exposes the complete `bds://` resource set and typed read/search/count tools through packaged stdio and stateless localhost-only HTTP transports with Origin/Host validation and CORS.
|
||||||
|
|||||||
@@ -874,32 +874,63 @@ fn create_gallery(
|
|||||||
fn config(db: &Database, command: ConfigCommand) -> Result<CommandOutput> {
|
fn config(db: &Database, command: ConfigCommand) -> Result<CommandOutput> {
|
||||||
match command {
|
match command {
|
||||||
ConfigCommand::Get { key } => {
|
ConfigCommand::Get { key } => {
|
||||||
let value = engine::settings::get(db.conn(), &key)?
|
let value = engine::settings::get_effective(db.conn(), &key)?
|
||||||
.ok_or_else(|| anyhow!("{key} is not set"))?;
|
.ok_or_else(|| anyhow!("{key} is not set"))?;
|
||||||
|
let value = printable_config_value(&key, &value);
|
||||||
Ok(output(&value, json!({"key": key, "value": value})))
|
Ok(output(&value, json!({"key": key, "value": value})))
|
||||||
}
|
}
|
||||||
ConfigCommand::Set { key, value } => {
|
ConfigCommand::Set { key, value } => {
|
||||||
cli_sync::run_cli_mutation(db.conn(), || {
|
cli_sync::run_cli_mutation(db.conn(), || {
|
||||||
engine::settings::set(db.conn(), &key, &value)
|
if key == engine::settings::ONLINE_API_KEY {
|
||||||
|
engine::ai::save_online_api_key(db.conn(), &value)
|
||||||
|
} else {
|
||||||
|
engine::settings::set(db.conn(), &key, &value)
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
|
let printable = printable_config_value(&key, &value);
|
||||||
Ok(output(
|
Ok(output(
|
||||||
&format!("{key} = {value}"),
|
&format!("{key} = {printable}"),
|
||||||
json!({"key": key, "value": value}),
|
json!({"key": key, "value": printable}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
ConfigCommand::List => {
|
ConfigCommand::List => {
|
||||||
let settings = bds_core::db::queries::setting::list_all_settings(db.conn())?;
|
let settings = engine::settings::list_effective(db.conn())?;
|
||||||
let mut message = String::new();
|
let mut message = String::new();
|
||||||
let mut values = serde_json::Map::new();
|
let mut values = serde_json::Map::new();
|
||||||
for setting in settings {
|
for (key, value) in settings {
|
||||||
let _ = writeln!(message, "{}={}", setting.key, setting.value);
|
let value = printable_config_value(&key, &value);
|
||||||
values.insert(setting.key, Value::String(setting.value));
|
let _ = writeln!(message, "{key}={value}");
|
||||||
|
values.insert(key, Value::String(value));
|
||||||
}
|
}
|
||||||
Ok(output(message.trim_end(), Value::Object(values)))
|
Ok(output(message.trim_end(), Value::Object(values)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn printable_config_value(key: &str, value: &str) -> String {
|
||||||
|
let key = key.to_ascii_lowercase();
|
||||||
|
if [
|
||||||
|
"api_key",
|
||||||
|
"access_key",
|
||||||
|
"private_key",
|
||||||
|
"password",
|
||||||
|
"secret",
|
||||||
|
"token",
|
||||||
|
"credential",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.any(|name| key.ends_with(name) || key.split(['.', '-']).any(|part| part == *name))
|
||||||
|
{
|
||||||
|
if value.trim().is_empty() {
|
||||||
|
"<not set>".to_string()
|
||||||
|
} else {
|
||||||
|
"<set>".to_string()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn project(db: &Database, command: ProjectCommand) -> Result<CommandOutput> {
|
fn project(db: &Database, command: ProjectCommand) -> Result<CommandOutput> {
|
||||||
match command {
|
match command {
|
||||||
ProjectCommand::List => {
|
ProjectCommand::List => {
|
||||||
@@ -1268,6 +1299,24 @@ mod tests {
|
|||||||
fn config_and_project_families_dispatch_success_and_failure() {
|
fn config_and_project_families_dispatch_success_and_failure() {
|
||||||
let fixture = Fixture::new(false);
|
let fixture = Fixture::new(false);
|
||||||
assert!(fixture.run(&["config", "get", "missing"], "").is_err());
|
assert!(fixture.run(&["config", "get", "missing"], "").is_err());
|
||||||
|
|
||||||
|
let defaults = fixture.run(&["config", "list"], "").unwrap();
|
||||||
|
assert_eq!(defaults.data["editor.default_mode"], "markdown");
|
||||||
|
assert_eq!(defaults.data["editor.diff_view_style"], "inline");
|
||||||
|
assert_eq!(defaults.data["ai.endpoint.online.api_key"], "<not set>");
|
||||||
|
assert!(
|
||||||
|
!defaults
|
||||||
|
.message
|
||||||
|
.contains("app.search-index-rebuild-required")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fixture
|
||||||
|
.run(&["config", "get", "editor.default_mode"], "")
|
||||||
|
.unwrap()
|
||||||
|
.message,
|
||||||
|
"markdown"
|
||||||
|
);
|
||||||
|
|
||||||
fixture
|
fixture
|
||||||
.run(&["config", "set", "editor.mode", "markdown"], "")
|
.run(&["config", "set", "editor.mode", "markdown"], "")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1276,6 +1325,31 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(read.data["value"], "markdown");
|
assert_eq!(read.data["value"], "markdown");
|
||||||
assert!(read.to_string().contains("\"ok\":true"));
|
assert!(read.to_string().contains("\"ok\":true"));
|
||||||
|
|
||||||
|
let secret = fixture
|
||||||
|
.run(&["config", "set", "service.api_key", "secret-token"], "")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(secret.message, "service.api_key = <set>");
|
||||||
|
assert!(!secret.to_string().contains("secret-token"));
|
||||||
|
let secret = fixture
|
||||||
|
.run(&["--json", "config", "get", "service.api_key"], "")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(secret.data["value"], "<set>");
|
||||||
|
assert!(!secret.to_string().contains("secret-token"));
|
||||||
|
let secrets = fixture.run(&["--json", "config", "list"], "").unwrap();
|
||||||
|
assert_eq!(secrets.data["service.api_key"], "<set>");
|
||||||
|
assert!(!secrets.to_string().contains("secret-token"));
|
||||||
|
|
||||||
|
let db = open_database(&fixture.database_path).unwrap();
|
||||||
|
engine::settings::set(db.conn(), "ai.endpoint.online.api_key_configured", "true").unwrap();
|
||||||
|
let configured = fixture.run(&["config", "list"], "").unwrap();
|
||||||
|
assert_eq!(configured.data["ai.endpoint.online.api_key"], "<set>");
|
||||||
|
assert!(
|
||||||
|
!configured
|
||||||
|
.message
|
||||||
|
.contains("ai.endpoint.online.api_key_configured")
|
||||||
|
);
|
||||||
|
|
||||||
assert!(fixture.run(&["project", "switch", "missing"], "").is_err());
|
assert!(fixture.run(&["project", "switch", "missing"], "").is_err());
|
||||||
|
|
||||||
let project = fixture._root.path().join("second");
|
let project = fixture._root.path().join("second");
|
||||||
|
|||||||
@@ -197,30 +197,36 @@ pub fn save_endpoint(conn: &Connection, endpoint: &AiEndpointConfig) -> EngineRe
|
|||||||
checked_at,
|
checked_at,
|
||||||
)?;
|
)?;
|
||||||
if endpoint.kind == AiEndpointKind::Online {
|
if endpoint.kind == AiEndpointKind::Online {
|
||||||
let entry = endpoint_keyring_entry(endpoint.kind)?;
|
|
||||||
if let Some(api_key) = &endpoint.api_key {
|
if let Some(api_key) = &endpoint.api_key {
|
||||||
if api_key.trim().is_empty() {
|
save_online_api_key_at(conn, api_key, checked_at)?;
|
||||||
entry.delete_credential().ok();
|
|
||||||
set_setting(
|
|
||||||
conn,
|
|
||||||
&endpoint_setting_key(endpoint.kind, "api_key_configured"),
|
|
||||||
"false",
|
|
||||||
checked_at,
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
entry.set_password(api_key.trim()).map_err(keyring_error)?;
|
|
||||||
set_setting(
|
|
||||||
conn,
|
|
||||||
&endpoint_setting_key(endpoint.kind, "api_key_configured"),
|
|
||||||
"true",
|
|
||||||
checked_at,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn save_online_api_key(conn: &Connection, api_key: &str) -> EngineResult<()> {
|
||||||
|
save_online_api_key_at(conn, api_key, now_unix_ms())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_online_api_key_at(conn: &Connection, api_key: &str, updated_at: i64) -> EngineResult<()> {
|
||||||
|
let entry = endpoint_keyring_entry(AiEndpointKind::Online)?;
|
||||||
|
let configured = !api_key.trim().is_empty();
|
||||||
|
if configured {
|
||||||
|
entry.set_password(api_key.trim()).map_err(keyring_error)?;
|
||||||
|
} else {
|
||||||
|
match entry.delete_credential() {
|
||||||
|
Ok(()) | Err(keyring::Error::NoEntry) => {}
|
||||||
|
Err(error) => return Err(keyring_error(error)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_setting(
|
||||||
|
conn,
|
||||||
|
&endpoint_setting_key(AiEndpointKind::Online, "api_key_configured"),
|
||||||
|
if configured { "true" } else { "false" },
|
||||||
|
updated_at,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save_model_preferences(
|
pub fn save_model_preferences(
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
default_model: Option<&str>,
|
default_model: Option<&str>,
|
||||||
|
|||||||
@@ -1,9 +1,32 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::db::DbConnection as Connection;
|
use crate::db::DbConnection as Connection;
|
||||||
use crate::db::queries::setting;
|
use crate::db::queries::setting;
|
||||||
use crate::engine::{EngineError, EngineResult, domain_events};
|
use crate::engine::{EngineError, EngineResult, domain_events};
|
||||||
use crate::util::now_unix_ms;
|
use crate::util::now_unix_ms;
|
||||||
|
|
||||||
pub const UI_LANGUAGE_KEY: &str = "ui.language";
|
pub const UI_LANGUAGE_KEY: &str = "ui.language";
|
||||||
|
pub const ONLINE_API_KEY: &str = "ai.endpoint.online.api_key";
|
||||||
|
const ONLINE_API_KEY_CONFIGURED: &str = "ai.endpoint.online.api_key_configured";
|
||||||
|
const DEFAULTS: &[(&str, &str)] = &[
|
||||||
|
("editor.default_mode", "markdown"),
|
||||||
|
("editor.diff_view_style", "inline"),
|
||||||
|
("editor.wrap_long_lines", "true"),
|
||||||
|
("editor.hide_unchanged_regions", "false"),
|
||||||
|
("ai.endpoint.online.url", ""),
|
||||||
|
("ai.endpoint.online.model", ""),
|
||||||
|
(ONLINE_API_KEY, ""),
|
||||||
|
("ai.endpoint.airplane.url", ""),
|
||||||
|
("ai.endpoint.airplane.model", ""),
|
||||||
|
("ai.default_model", ""),
|
||||||
|
("ai.title_model", ""),
|
||||||
|
("ai.image_model", ""),
|
||||||
|
("ai.system_prompt", ""),
|
||||||
|
("mcp.http.enabled", "false"),
|
||||||
|
("data.automatic_rebuild", "true"),
|
||||||
|
("style.theme", "system"),
|
||||||
|
("style.content_width", "72"),
|
||||||
|
];
|
||||||
|
|
||||||
pub fn get(conn: &Connection, key: &str) -> EngineResult<Option<String>> {
|
pub fn get(conn: &Connection, key: &str) -> EngineResult<Option<String>> {
|
||||||
match setting::get_setting_by_key(conn, key) {
|
match setting::get_setting_by_key(conn, key) {
|
||||||
@@ -13,6 +36,49 @@ pub fn get(conn: &Connection, key: &str) -> EngineResult<Option<String>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_effective(conn: &Connection, key: &str) -> EngineResult<Option<String>> {
|
||||||
|
if key == ONLINE_API_KEY {
|
||||||
|
return Ok(Some(
|
||||||
|
get(conn, ONLINE_API_KEY_CONFIGURED)?
|
||||||
|
.filter(|value| value == "true")
|
||||||
|
.map_or_else(String::new, |_| "configured".to_string()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(value) = get(conn, key)? {
|
||||||
|
return Ok(Some(value));
|
||||||
|
}
|
||||||
|
if key == UI_LANGUAGE_KEY {
|
||||||
|
return Ok(Some(crate::i18n::detect_os_locale().code().to_string()));
|
||||||
|
}
|
||||||
|
Ok(DEFAULTS
|
||||||
|
.iter()
|
||||||
|
.find(|(candidate, _)| *candidate == key)
|
||||||
|
.map(|(_, value)| (*value).to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_effective(conn: &Connection) -> EngineResult<BTreeMap<String, String>> {
|
||||||
|
let mut values = DEFAULTS
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| ((*key).to_string(), (*value).to_string()))
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
values.insert(
|
||||||
|
UI_LANGUAGE_KEY.to_string(),
|
||||||
|
crate::i18n::detect_os_locale().code().to_string(),
|
||||||
|
);
|
||||||
|
for value in setting::list_all_settings(conn)? {
|
||||||
|
if !value.key.starts_with("app.")
|
||||||
|
&& value.key != ONLINE_API_KEY
|
||||||
|
&& value.key != ONLINE_API_KEY_CONFIGURED
|
||||||
|
{
|
||||||
|
values.insert(value.key, value.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if get(conn, ONLINE_API_KEY_CONFIGURED)?.as_deref() == Some("true") {
|
||||||
|
values.insert(ONLINE_API_KEY.to_string(), "configured".to_string());
|
||||||
|
}
|
||||||
|
Ok(values)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set(conn: &Connection, key: &str, value: &str) -> EngineResult<()> {
|
pub fn set(conn: &Connection, key: &str, value: &str) -> EngineResult<()> {
|
||||||
set_at(conn, key, value, now_unix_ms())
|
set_at(conn, key, value, now_unix_ms())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1836,8 +1836,8 @@ impl TuiApp {
|
|||||||
(
|
(
|
||||||
"Diff style",
|
"Diff style",
|
||||||
"editor.diff_view_style",
|
"editor.diff_view_style",
|
||||||
FieldKind::Enum(&["unified", "split"]),
|
FieldKind::Enum(&["inline", "side-by-side"]),
|
||||||
"unified",
|
"inline",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Wrap long lines",
|
"Wrap long lines",
|
||||||
@@ -2066,7 +2066,7 @@ impl TuiApp {
|
|||||||
) {
|
) {
|
||||||
fallback
|
fallback
|
||||||
} else {
|
} else {
|
||||||
engine::settings::get(db.conn(), key)?.unwrap_or(fallback)
|
engine::settings::get_effective(db.conn(), key)?.unwrap_or(fallback)
|
||||||
};
|
};
|
||||||
Ok(SettingField {
|
Ok(SettingField {
|
||||||
label: setting_field_label(self.locale, key, label),
|
label: setting_field_label(self.locale, key, label),
|
||||||
|
|||||||
@@ -6796,28 +6796,25 @@ impl BdsApp {
|
|||||||
.map(|template| template.slug)
|
.map(|template| template.slug)
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
if let Ok(setting) =
|
if let Ok(Some(value)) =
|
||||||
bds_core::db::queries::setting::get_setting_by_key(db.conn(), "editor.default_mode")
|
engine::settings::get_effective(db.conn(), "editor.default_mode")
|
||||||
{
|
{
|
||||||
state.default_mode = setting.value;
|
state.default_mode = value;
|
||||||
}
|
}
|
||||||
if let Ok(setting) = bds_core::db::queries::setting::get_setting_by_key(
|
if let Ok(Some(value)) =
|
||||||
db.conn(),
|
engine::settings::get_effective(db.conn(), "editor.diff_view_style")
|
||||||
"editor.diff_view_style",
|
{
|
||||||
) {
|
state.diff_view_style = value;
|
||||||
state.diff_view_style = setting.value;
|
|
||||||
}
|
}
|
||||||
if let Ok(setting) = bds_core::db::queries::setting::get_setting_by_key(
|
if let Ok(Some(value)) =
|
||||||
db.conn(),
|
engine::settings::get_effective(db.conn(), "editor.wrap_long_lines")
|
||||||
"editor.wrap_long_lines",
|
{
|
||||||
) {
|
state.wrap_long_lines = value == "true";
|
||||||
state.wrap_long_lines = setting.value == "true";
|
|
||||||
}
|
}
|
||||||
if let Ok(setting) = bds_core::db::queries::setting::get_setting_by_key(
|
if let Ok(Some(value)) =
|
||||||
db.conn(),
|
engine::settings::get_effective(db.conn(), "editor.hide_unchanged_regions")
|
||||||
"editor.hide_unchanged_regions",
|
{
|
||||||
) {
|
state.hide_unchanged_regions = value == "true";
|
||||||
state.hide_unchanged_regions = setting.value == "true";
|
|
||||||
}
|
}
|
||||||
if let Ok(setting) =
|
if let Ok(setting) =
|
||||||
bds_core::db::queries::setting::get_setting_by_key(db.conn(), "ai.system_prompt")
|
bds_core::db::queries::setting::get_setting_by_key(db.conn(), "ai.system_prompt")
|
||||||
@@ -6848,7 +6845,7 @@ impl BdsApp {
|
|||||||
state.title_model = ai_settings.title_model.unwrap_or_default();
|
state.title_model = ai_settings.title_model.unwrap_or_default();
|
||||||
state.image_model = ai_settings.image_model.unwrap_or_default();
|
state.image_model = ai_settings.image_model.unwrap_or_default();
|
||||||
}
|
}
|
||||||
state.mcp_enabled = engine::settings::get(db.conn(), "mcp.http.enabled")
|
state.mcp_enabled = engine::settings::get_effective(db.conn(), "mcp.http.enabled")
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.is_some_and(|value| value == "true");
|
.is_some_and(|value| value == "true");
|
||||||
|
|||||||
@@ -150,7 +150,11 @@ rule CreateGallery {
|
|||||||
|
|
||||||
rule Preferences {
|
rule Preferences {
|
||||||
when: CliCommandExecuted(command: config)
|
when: CliCommandExecuted(command: config)
|
||||||
-- get/set/list of the global settings store shared with the app.
|
-- get/list expose effective global preferences shared with the app:
|
||||||
|
-- canonical defaults overlaid by persisted values. Project-file settings and
|
||||||
|
-- internal maintenance state are outside this list. Secret-backed keys remain
|
||||||
|
-- visible, but text and JSON output reveal only whether each secret is set;
|
||||||
|
-- get, list, and set confirmation never print secret values.
|
||||||
ensures: PreferencesAccessed()
|
ensures: PreferencesAccessed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user