tmuxclihook 0.5.15 → 0.5.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmuxclihook",
3
- "version": "0.5.15",
3
+ "version": "0.5.16",
4
4
  "description": "交互式 TUI 安装器:环境侦测、AI CLI 侦测、tmux-ai-status hooks 装/卸/修、TPM 软链校验",
5
5
  "type": "module",
6
6
  "bin": {
@@ -74,13 +74,65 @@ adapter_check_integrity() {
74
74
  _CODEX_EVENTS="SessionStart UserPromptSubmit PreToolUse PermissionRequest PostToolUse Stop"
75
75
  _CODEX_LEGACY_TOML="${CODEX_HOME:-$HOME/.codex}/hooks.toml"
76
76
 
77
+ # Hook 授信记录(trust 维度,check --json 用):codex 把 user 来源 hook 的授信写在
78
+ # 每个实例 config.toml 的 [hooks.state."<hooks.json 路径>:<下划线事件>:<组>:<hook>"]。
79
+ # 只判键是否存在——bash 无法复现 trusted_hash;因此:
80
+ # - 键缺失 = 该 hook 一定不会执行(强信号)
81
+ # - 键存在 ≠ 一定执行(hooks 被改写后旧键仍在、hash 失效,codex 会重新弹 Trust)
82
+ # config.toml 不存在 → trusted:null(无法判定)。
83
+ _CODEX_TRUST_EVENTS="session_start user_prompt_submit pre_tool_use permission_request post_tool_use stop"
84
+
85
+ _codex_trust_scan() { # $1=config.toml $2=hooks.json → 每行 "event<TAB>0|1"
86
+ local cfg="$1" hooks="$2" keys e
87
+ [ -f "$cfg" ] || return 1
88
+ # 只提取键名,绝不读取 trusted_hash 值
89
+ keys=$(sed -n 's/^\[hooks\.state\."\([^"]*\)"\].*/\1/p' "$cfg" 2>/dev/null)
90
+ for e in $_CODEX_TRUST_EVENTS; do
91
+ case "$keys" in
92
+ *"$hooks:$e:"*) printf '%s\t1\n' "$e" ;;
93
+ *) printf '%s\t0\n' "$e" ;;
94
+ esac
95
+ done
96
+ }
97
+
98
+ # 汇总 $HOME/.codex 与所有 cc-switch 实例的授信状态(去重;config.toml 不存在则不收录,
99
+ # 当前实例不存在时 trusted:null)。只读。
100
+ _codex_trust_json() {
101
+ local cfg hooks rows e mark missing trusted seen=""
102
+ for cfg in "${HOME}/.codex/config.toml" "${HOME}"/.cc-switch/instances/codex/*/config.toml; do
103
+ [ -f "$cfg" ] || continue
104
+ [ "$cfg" = "$seen" ] && continue
105
+ seen="$cfg"
106
+ hooks="$(dirname "$cfg")/hooks.json"
107
+ rows=$(_codex_trust_scan "$cfg" "$hooks" 2>/dev/null) || continue
108
+ missing=""
109
+ while IFS=$'\t' read -r e mark; do
110
+ [ "$mark" = "0" ] && missing="${missing}${missing:+,}$e"
111
+ done <<EOF
112
+ $rows
113
+ EOF
114
+ if [ -n "$missing" ]; then trusted=0; else trusted=1; fi
115
+ printf '%s\t%s\t%s\t%s\n' "$cfg" "$hooks" "$trusted" "$missing"
116
+ done | jq -R -s --arg cur "${CODEX_HOME:-$HOME/.codex}/config.toml" '
117
+ split("\n") | map(select(length > 0) | split("\t")) as $rows
118
+ | [$rows[] | {configFile: .[0], hooksFile: .[1],
119
+ trusted: (.[2] == "1"),
120
+ missingEvents: (if .[3] == "" then [] else (.[3] | split(",")) end)}] as $inst
121
+ | ($inst | map(select(.configFile == $cur)) | first) as $cur_obj
122
+ | {configFile: $cur,
123
+ trusted: (if $cur_obj then $cur_obj.trusted else null end),
124
+ missingEvents: (if $cur_obj then $cur_obj.missingEvents else [] end),
125
+ instances: $inst}'
126
+ }
127
+
77
128
  # 明细检查(仅 install wrapper 的 check --json 调用;与 claude adapter 同构)。
78
129
  # 差异:无 cc-switch 分支(hooks.json 即文件真源,mode 恒 file)、6 事件、
79
130
  # 匹配串为 "tmux-ai-status codex"。失败输出空 events 降级片段,不 exit 非 0。
80
131
  adapter_check_detail() {
81
- local src="$ADAPTER_HOOKS_FILE" pairs rows line ev p a
132
+ local src="$ADAPTER_HOOKS_FILE" pairs rows line ev p a trust_json
133
+ trust_json=$(_codex_trust_json)
82
134
  if [ ! -f "$src" ]; then
83
- printf '{"mode":"file","events":{},"plugin":null,"registeredPaths":[]}\n'
135
+ printf '{"mode":"file","events":{},"plugin":null,"registeredPaths":[],"trust":%s}\n' "$trust_json"
84
136
  return 0
85
137
  fi
86
138
  pairs=$(jq -r '.hooks as $h |
@@ -98,7 +150,7 @@ adapter_check_detail() {
98
150
  done <<EOF
99
151
  $pairs
100
152
  EOF
101
- jq -R -s --arg mode "file" --arg events "$_CODEX_EVENTS" '
153
+ jq -R -s --arg mode "file" --arg events "$_CODEX_EVENTS" --argjson trust "$trust_json" '
102
154
  (reduce ($events | split(" ")[]) as $e
103
155
  ({}; .[$e] = {registered: 0, alive: 0, scriptPaths: []})) as $base
104
156
  | reduce (split("\n") | .[] | select(length > 0) | split("\t")) as $r (
@@ -109,7 +161,7 @@ EOF
109
161
  scriptPaths: ((.[$r[0]].scriptPaths + [$r[1]]) | unique)
110
162
  })
111
163
  | {mode: $mode, events: ., plugin: null,
112
- registeredPaths: ([.[] | .scriptPaths[]] | unique)}
164
+ registeredPaths: ([.[] | .scriptPaths[]] | unique), trust: $trust}
113
165
  ' <<<"$rows"
114
166
  }
115
167
 
@@ -32,6 +32,9 @@ function baseDetail(tool) {
32
32
  events: {},
33
33
  plugin: null,
34
34
  registeredPaths: [],
35
+ // codex 授信维度:{configFile, trusted: true|false|null, missingEvents, instances}
36
+ // 仅 codex adapter 提供;null = 该工具无此概念/旧版 adapter。
37
+ trust: null,
35
38
  drift: null,
36
39
  error: null,
37
40
  raw: null,
@@ -70,6 +73,7 @@ export async function checkToolDetail(tool, { run } = {}) {
70
73
  events: parsed.events || {},
71
74
  plugin: parsed.plugin ?? null,
72
75
  registeredPaths: parsed.registeredPaths || [],
76
+ trust: parsed.trust ?? null,
73
77
  drift: parsed.drift || null,
74
78
  error: parsed.error,
75
79
  raw: stdout,
@@ -85,6 +89,7 @@ export async function checkToolDetail(tool, { run } = {}) {
85
89
  events: parsed.events || {},
86
90
  plugin: parsed.plugin ?? null,
87
91
  registeredPaths: parsed.registeredPaths || [],
92
+ trust: parsed.trust ?? null,
88
93
  drift: parsed.drift || null,
89
94
  raw: stdout,
90
95
  };
@@ -107,6 +112,32 @@ export function renderToolDetail(detail) {
107
112
  lines.push(` ${WARN} 存在卸载意图标记(suppressed-${detail.tool}):自动修复已被抑制`);
108
113
  }
109
114
 
115
+ // codex 授信维度:hooks 注册完整 ≠ 会执行。键缺失 = 一定不执行;键存在只说明
116
+ // 有授信记录(bash 无法复现 trusted_hash,改写后仍可能需重新 Trust)。
117
+ const trust = detail.trust;
118
+ if (trust) {
119
+ if (trust.trusted === false) {
120
+ const miss = (trust.missingEvents || []).join(', ') || '全部事件';
121
+ lines.push(
122
+ ` ${WARN} hooks 未授信:当前实例缺少 [hooks.state] 记录(${miss})——codex 会跳过这些 hook,请在 codex TUI 选择 "Trust all and continue"`
123
+ );
124
+ } else if (trust.trusted === true) {
125
+ lines.push(` ${OK} 授信记录存在(键存在不等于 hash 匹配:hooks 改写后仍可能需重新 Trust)`);
126
+ } else if (detail.status === 'ok') {
127
+ lines.push(` ${WARN} 无法判定授信状态:${trust.configFile} 不存在`);
128
+ }
129
+ const otherUntrusted = (trust.instances || []).filter(
130
+ (i) => i.configFile !== trust.configFile && i.trusted === false
131
+ );
132
+ if (otherUntrusted.length > 0) {
133
+ lines.push(` ${WARN} 其他实例未授信(首次启动会弹 Hooks need review):`);
134
+ for (const i of otherUntrusted) {
135
+ const miss = (i.missingEvents || []).join(', ') || '全部事件';
136
+ lines.push(` ${i.configFile}(缺 ${miss})`);
137
+ }
138
+ }
139
+ }
140
+
110
141
  // 逐事件缺失明细(cc-switch/文件模式的 tools)
111
142
  if (detail.status === 'missing') {
112
143
  const broken = [];