mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
6d747db7d8
# Why Hooks that need trust review were easy to miss, and the existing TUI flow made users discover `/hooks` manually before they could decide whether to inspect or trust them. # What - add a startup review prompt for new or changed hooks before normal composer use - add a top-level `t` shortcut in `/hooks` to trust every review-needed hook at once - make pending-review rows and helper copy use warning styling ## TUI ### Startup review interstitial ```text Hooks need review 2 hooks are new or changed. Hooks can run outside the sandbox after you trust them. › 1. Review hooks 2. Trust all and continue 3. Continue without trusting (hooks won't run) ``` ### Top-level `/hooks` page when review is needed ```text Hooks Lifecycle hooks from config and enabled plugins. ⚠ 1 hook needs review before it can run. Event Installed Active Review Description PreToolUse 1 0 1 Before a tool executes ... Press t to trust all; enter to review hooks; esc to close ```
101 lines
3.2 KiB
Rust
101 lines
3.2 KiB
Rust
use codex_app_server_client::AppServerRequestHandle;
|
|
use codex_app_server_protocol::ClientRequest;
|
|
use codex_app_server_protocol::ConfigBatchWriteParams;
|
|
use codex_app_server_protocol::ConfigWriteResponse;
|
|
use codex_app_server_protocol::HookMetadata;
|
|
use codex_app_server_protocol::HookTrustStatus;
|
|
use codex_app_server_protocol::HooksListEntry;
|
|
use codex_app_server_protocol::HooksListParams;
|
|
use codex_app_server_protocol::HooksListResponse;
|
|
use codex_app_server_protocol::MergeStrategy;
|
|
use codex_app_server_protocol::RequestId;
|
|
use color_eyre::eyre::Result;
|
|
use color_eyre::eyre::WrapErr;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub(crate) struct HookTrustUpdate {
|
|
pub(crate) key: String,
|
|
pub(crate) current_hash: String,
|
|
}
|
|
|
|
pub(crate) async fn fetch_hooks_list(
|
|
request_handle: AppServerRequestHandle,
|
|
cwd: PathBuf,
|
|
) -> Result<HooksListResponse> {
|
|
let request_id = RequestId::String(format!("hooks-list-{}", Uuid::new_v4()));
|
|
request_handle
|
|
.request_typed(ClientRequest::HooksList {
|
|
request_id,
|
|
params: HooksListParams { cwds: vec![cwd] },
|
|
})
|
|
.await
|
|
.wrap_err("hooks/list failed in TUI")
|
|
}
|
|
|
|
pub(crate) fn hooks_list_entry_for_cwd(response: HooksListResponse, cwd: &Path) -> HooksListEntry {
|
|
response
|
|
.data
|
|
.into_iter()
|
|
.find(|entry| entry.cwd.as_path() == cwd)
|
|
.unwrap_or_else(|| HooksListEntry {
|
|
cwd: cwd.to_path_buf(),
|
|
hooks: Vec::new(),
|
|
warnings: Vec::new(),
|
|
errors: Vec::new(),
|
|
})
|
|
}
|
|
|
|
pub(crate) fn hook_needs_review(hook: &HookMetadata) -> bool {
|
|
matches!(
|
|
hook.trust_status,
|
|
HookTrustStatus::Untrusted | HookTrustStatus::Modified
|
|
)
|
|
}
|
|
|
|
pub(crate) async fn write_hook_trusts(
|
|
request_handle: AppServerRequestHandle,
|
|
trust_updates: Vec<HookTrustUpdate>,
|
|
) -> Result<ConfigWriteResponse> {
|
|
let request_id = RequestId::String(format!("hooks-config-write-{}", Uuid::new_v4()));
|
|
let value = serde_json::Value::Object(
|
|
trust_updates
|
|
.into_iter()
|
|
.map(|update| {
|
|
(
|
|
update.key,
|
|
serde_json::json!({
|
|
"trusted_hash": update.current_hash,
|
|
}),
|
|
)
|
|
})
|
|
.collect(),
|
|
);
|
|
request_handle
|
|
.request_typed(ClientRequest::ConfigBatchWrite {
|
|
request_id,
|
|
params: ConfigBatchWriteParams {
|
|
edits: vec![codex_app_server_protocol::ConfigEdit {
|
|
key_path: "hooks.state".to_string(),
|
|
value,
|
|
merge_strategy: MergeStrategy::Upsert,
|
|
}],
|
|
file_path: None,
|
|
expected_version: None,
|
|
reload_user_config: true,
|
|
},
|
|
})
|
|
.await
|
|
.wrap_err("config/batchWrite failed while updating hook trust in TUI")
|
|
}
|
|
|
|
pub(crate) async fn write_hook_trust(
|
|
request_handle: AppServerRequestHandle,
|
|
key: String,
|
|
current_hash: String,
|
|
) -> Result<ConfigWriteResponse> {
|
|
write_hook_trusts(request_handle, vec![HookTrustUpdate { key, current_hash }]).await
|
|
}
|