feat(skills): add browser control installer skill

This commit is contained in:
zhangfucai 2026-04-13 13:41:44 +08:00
parent 447b57e46a
commit ac038867d2
7 changed files with 959 additions and 0 deletions

View File

@ -0,0 +1,45 @@
---
name: install-browser-control
description: 在新 Mac 上为 Codex 和 Claude 全局安装或整理真实 Chrome/Chromium 调试能力时使用;适用于需要 connect-chrome、browse、setup-browser-cookies 三个入口、可见浏览器接管、DOM 操作、network/console 检查与 cookie 导入支持的场景。
---
# Install Browser Control
当用户要求在新机器上把浏览器接管调试能力装完整时,直接执行本 skill。
## 目标
- 自动识别 `CODEX_HOME`,默认回退到 `~/.codex`
- 在 Codex / Claude 两侧全局目录补齐或标准化:
- `connect-chrome`
- `browse`
- `setup-browser-cookies`
- 优先复用现有等价能力;缺失时从公开官方来源安装
- 生成本地说明文件:`$CODEX_HOME/browser-control-setup.md`
- 做真实验证:可见 Chromium 窗口、`https://example.com`、DOM 读取、network/console、cookie 导入入口调用
## 执行
先运行安装脚本:
```bash
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
bash "$SCRIPT_DIR/install-browser-control.sh"
```
## 交付
安装脚本结束后,读取并引用:
```bash
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
sed -n '1,240p' "$CODEX_HOME/browser-control-setup.md"
```
汇报时只保留高信号结果:
- 是否安装完成
- 3 个入口的最终路径
- 本地说明文件路径
- 实际验证结果
- 如果未完成,唯一阻塞点

View File

@ -0,0 +1,433 @@
#!/usr/bin/env bash
set -euo pipefail
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
CLAUDE_HOME="${CLAUDE_HOME:-$HOME/.claude}"
GSTACK_REPO_URL="${GSTACK_REPO_URL:-https://github.com/garrytan/gstack.git}"
VERIFY_URL="${VERIFY_URL:-https://example.com}"
SETUP_DOC="$CODEX_HOME/browser-control-setup.md"
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
log() {
printf '[install-browser-control] %s\n' "$*" >&2
}
fail() {
printf '[install-browser-control] ERROR: %s\n' "$*" >&2
exit 1
}
ensure_macos() {
local os_name
os_name="$(uname -s)"
if [[ "$os_name" != "Darwin" ]]; then
fail "当前脚本仅支持 macOS检测到: $os_name"
fi
}
ensure_dir() {
mkdir -p "$1"
}
detect_codex_install() {
if command -v codex >/dev/null 2>&1; then
command -v codex
else
printf 'not-found'
fi
}
detect_claude_install() {
if command -v claude >/dev/null 2>&1; then
command -v claude
else
printf 'not-found'
fi
}
have_equivalent_gstack() {
local target="$1"
[[ -f "$target/connect-chrome/SKILL.md" ]] \
&& [[ -f "$target/setup-browser-cookies/SKILL.md" ]] \
&& [[ -f "$target/browse/src/cli.ts" || -x "$target/browse/dist/browse" ]]
}
clone_seed() {
local seed_dir="$TMP_DIR/gstack-seed"
if [[ -d "$seed_dir" ]]; then
return
fi
log "cloning gstack from $GSTACK_REPO_URL"
git clone --depth 1 "$GSTACK_REPO_URL" "$seed_dir" >/dev/null 2>&1
}
backup_path() {
local path="$1"
if [[ -e "$path" ]]; then
mv "$path" "$path.local.bak.$(date +%Y%m%d%H%M%S)"
fi
}
ensure_gstack_home() {
local home_root="$1"
local target="$home_root/skills/gstack"
ensure_dir "$home_root/skills"
if have_equivalent_gstack "$target"; then
printf '%s' "$target"
return
fi
if [[ -e "$target" ]]; then
backup_path "$target"
fi
clone_seed
cp -R "$TMP_DIR/gstack-seed" "$target"
printf '%s' "$target"
}
ensure_bun() {
if command -v bun >/dev/null 2>&1; then
return
fi
if command -v brew >/dev/null 2>&1; then
log "installing bun with Homebrew"
brew install bun >/dev/null
else
log "installing bun from bun.sh"
curl -fsSL https://bun.sh/install | bash >/dev/null
export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
export PATH="$BUN_INSTALL/bin:$PATH"
fi
command -v bun >/dev/null 2>&1 || fail "bun 安装失败"
}
ensure_browse_runtime() {
local gstack_root="$1"
local browse_bin="$gstack_root/browse/dist/browse"
if [[ ! -x "$browse_bin" ]]; then
ensure_bun
log "building browse binary in $gstack_root"
(
cd "$gstack_root"
bun install >/dev/null
bun run build >/dev/null
)
fi
[[ -x "$browse_bin" ]] || fail "browse binary 不可用: $browse_bin"
ensure_bun
log "ensuring Playwright Chromium runtime"
(
cd "$gstack_root"
bunx playwright install chromium >/dev/null 2>&1 || true
)
printf '%s' "$browse_bin"
}
ensure_skill_entry() {
local home_root="$1"
local skill_name="$2"
local dest="$home_root/skills/$skill_name"
if [[ -L "$dest" ]]; then
printf '%s' "$dest"
return
fi
if [[ -f "$dest/SKILL.md" ]] && grep -q "^name: $skill_name\$" "$dest/SKILL.md"; then
printf '%s' "$dest"
return
fi
if [[ -e "$dest" ]]; then
backup_path "$dest"
fi
ln -sfn "gstack/$skill_name" "$dest"
printf '%s' "$dest"
}
detect_browser_apps() {
local apps=()
[[ -d "/Applications/Google Chrome.app" ]] && apps+=("Google Chrome")
[[ -d "/Applications/Chromium.app" ]] && apps+=("Chromium")
[[ -d "/Applications/Microsoft Edge.app" ]] && apps+=("Microsoft Edge")
[[ -d "/Applications/Brave Browser.app" ]] && apps+=("Brave Browser")
[[ -d "/Applications/Arc.app" ]] && apps+=("Arc")
if [[ ${#apps[@]} -eq 0 ]]; then
printf 'none-detected'
else
local joined=""
local app
for app in "${apps[@]}"; do
if [[ -n "$joined" ]]; then
joined+=", "
fi
joined+="$app"
done
printf '%s' "$joined"
fi
}
visible_browser_processes() {
/usr/bin/osascript -e 'tell application "System Events" to get the name of every process whose visible is true' 2>/dev/null \
| tr ',' '\n' \
| sed 's/^ *//' \
| grep -E 'Chrome|Chromium' \
| paste -sd ', ' - \
|| true
}
verify_browser_control() {
local browse_bin="$1"
local verify_tmp="$TMP_DIR/verify"
local connect_out
local newtab_out
local title_out
local heading_out
local network_out
local console_out
local status_out
local cookie_out=""
local cookie_rc=0
local visible_out=""
local cookie_status=""
mkdir -p "$verify_tmp"
"$browse_bin" stop >/dev/null 2>&1 || true
"$browse_bin" connect >"$verify_tmp/connect.log" 2>&1
sleep 2
"$browse_bin" newtab "$VERIFY_URL" >"$verify_tmp/newtab.log" 2>&1
"$browse_bin" js 'document.title' >"$verify_tmp/title.log" 2>&1
"$browse_bin" text h1 >"$verify_tmp/heading.log" 2>&1
"$browse_bin" reload >/dev/null 2>&1 || true
"$browse_bin" wait --load >/dev/null 2>&1 || true
"$browse_bin" network >"$verify_tmp/network.log" 2>&1
"$browse_bin" console >"$verify_tmp/console.log" 2>&1
"$browse_bin" status >"$verify_tmp/status.log" 2>&1
visible_out="$(visible_browser_processes)"
"$browse_bin" cookie-import-browser chrome --domain example.com >"$verify_tmp/cookie.log" 2>&1 || cookie_rc=$?
connect_out="$(cat "$verify_tmp/connect.log")"
newtab_out="$(cat "$verify_tmp/newtab.log")"
title_out="$(cat "$verify_tmp/title.log")"
heading_out="$(cat "$verify_tmp/heading.log")"
network_out="$(cat "$verify_tmp/network.log")"
console_out="$(cat "$verify_tmp/console.log")"
status_out="$(cat "$verify_tmp/status.log")"
cookie_out="$(cat "$verify_tmp/cookie.log" 2>/dev/null || true)"
if [[ $cookie_rc -eq 0 ]]; then
cookie_status="callable"
elif [[ "$cookie_out" == *"Keychain"* ]]; then
cookie_status="callable-requires-keychain-access"
elif [[ "$cookie_out" == *"No Chromium browsers found"* ]]; then
cookie_status="callable-no-cookie-source-browser"
else
cookie_status="error"
fi
BROWSE_CONNECT_OUT="$connect_out"
BROWSE_NEWTAB_OUT="$newtab_out"
BROWSE_TITLE_OUT="$title_out"
BROWSE_HEADING_OUT="$heading_out"
BROWSE_NETWORK_OUT="$network_out"
BROWSE_CONSOLE_OUT="$console_out"
BROWSE_STATUS_OUT="$status_out"
BROWSE_VISIBLE_OUT="$visible_out"
COOKIE_VERIFY_OUT="$cookie_out"
COOKIE_VERIFY_STATUS="$cookie_status"
}
write_setup_doc() {
local codex_connect="$1"
local codex_browse="$2"
local codex_cookie="$3"
local claude_connect="$4"
local claude_browse="$5"
local claude_cookie="$6"
local codex_gstack="$7"
local claude_gstack="$8"
local browse_bin="$9"
local codex_install="${10}"
local claude_install="${11}"
local browser_apps="${12}"
ensure_dir "$CODEX_HOME"
cat >"$SETUP_DOC" <<EOF
# Browser Control Setup
## 实际安装来源
- 官方上游:$GSTACK_REPO_URL
- Codex 当前 gstack 根目录:$codex_gstack
- Claude 当前 gstack 根目录:$claude_gstack
- Codex 安装入口:$codex_install
- Claude 安装入口:$claude_install
- 本机已检测到的系统浏览器:$browser_apps
- 运行时浏览器Playwright Chromium$browse_bin 驱动)
## 最终技能路径
### Codex
- connect-chrome: $codex_connect
- browse: $codex_browse
- setup-browser-cookies: $codex_cookie
### Claude
- connect-chrome: $claude_connect
- browse: $claude_browse
- setup-browser-cookies: $claude_cookie
## 如何调用
### Codex
- \`connect-chrome\`
- \`browse\`
- \`setup-browser-cookies\`
- \`install-browser-control\`
### Claude
- \`/connect-chrome\`
- \`/browse\`
- \`/setup-browser-cookies\`
- \`/install-browser-control\`
## 如何验证
1. 运行 \`connect-chrome\`,确认出现 headed Chromium 窗口。
2. 运行 \`browse newtab https://example.com\`
3. 运行 \`browse js 'document.title'\`\`browse text h1\`,确认可读取 DOM。
4. 运行 \`browse network\`\`browse console\`,确认能读取请求和控制台信息。
5. 运行 \`browse cookie-import-browser chrome --domain example.com\`,确认 cookie 导入入口可被调用;首次读取 Chrome Cookie 时macOS 可能弹出 Keychain 授权框。
## 本机最近一次真实验证
### connect
\`\`\`
$BROWSE_CONNECT_OUT
\`\`\`
### newtab + DOM
\`\`\`
$BROWSE_NEWTAB_OUT
$BROWSE_TITLE_OUT
$BROWSE_HEADING_OUT
\`\`\`
### network
\`\`\`
$BROWSE_NETWORK_OUT
\`\`\`
### console
\`\`\`
$BROWSE_CONSOLE_OUT
\`\`\`
### status
\`\`\`
$BROWSE_STATUS_OUT
\`\`\`
### visible browser processes
\`\`\`
${BROWSE_VISIBLE_OUT:-not-detected}
\`\`\`
### cookie import verification
- status: $COOKIE_VERIFY_STATUS
\`\`\`
$COOKIE_VERIFY_OUT
\`\`\`
EOF
}
main() {
local codex_gstack
local claude_gstack
local browse_bin
local claude_browse_bin
local codex_connect
local codex_browse
local codex_cookie
local claude_connect
local claude_browse
local claude_cookie
local browser_apps
local codex_install
local claude_install
ensure_macos
codex_install="$(detect_codex_install)"
claude_install="$(detect_claude_install)"
codex_gstack="$(ensure_gstack_home "$CODEX_HOME")"
claude_gstack="$(ensure_gstack_home "$CLAUDE_HOME")"
browse_bin="$(ensure_browse_runtime "$codex_gstack")"
claude_browse_bin="$(ensure_browse_runtime "$claude_gstack")"
[[ -x "$claude_browse_bin" ]] || fail "Claude browse binary 不可用: $claude_browse_bin"
codex_connect="$(ensure_skill_entry "$CODEX_HOME" "connect-chrome")"
codex_browse="$(ensure_skill_entry "$CODEX_HOME" "browse")"
codex_cookie="$(ensure_skill_entry "$CODEX_HOME" "setup-browser-cookies")"
claude_connect="$(ensure_skill_entry "$CLAUDE_HOME" "connect-chrome")"
claude_browse="$(ensure_skill_entry "$CLAUDE_HOME" "browse")"
claude_cookie="$(ensure_skill_entry "$CLAUDE_HOME" "setup-browser-cookies")"
browser_apps="$(detect_browser_apps)"
verify_browser_control "$browse_bin"
write_setup_doc \
"$codex_connect" "$codex_browse" "$codex_cookie" \
"$claude_connect" "$claude_browse" "$claude_cookie" \
"$codex_gstack" "$claude_gstack" "$browse_bin" \
"$codex_install" "$claude_install" "$browser_apps"
printf 'DONE\n'
printf 'CODEX_HOME=%s\n' "$CODEX_HOME"
printf 'CLAUDE_HOME=%s\n' "$CLAUDE_HOME"
printf 'SETUP_DOC=%s\n' "$SETUP_DOC"
printf 'CODEX_CONNECT=%s\n' "$codex_connect"
printf 'CODEX_BROWSE=%s\n' "$codex_browse"
printf 'CODEX_COOKIE=%s\n' "$codex_cookie"
printf 'CLAUDE_CONNECT=%s\n' "$claude_connect"
printf 'CLAUDE_BROWSE=%s\n' "$claude_browse"
printf 'CLAUDE_COOKIE=%s\n' "$claude_cookie"
printf 'COOKIE_VERIFY_STATUS=%s\n' "$COOKIE_VERIFY_STATUS"
}
main "$@"

View File

@ -42,6 +42,7 @@
- `issue`: 查看当前仓库或任意 Gitea 仓库的 issue 列表和单条详情,支持自动识别 git origin、用户指定仓库和格式化输出。 (file: `./.codex/skills/issue/SKILL.md`)
- `issue-drive`: 归集证据并把问题拆成 1 到多张 Gitea issue支持从当前仓库 origin 自动识别仓库或用户显式指定。 (file: `./.codex/skills/issue-drive/SKILL.md`)
- `changelog`: 一键发版:生成更新日志 → commit → 打 tag全流程自动化。 (file: `./.codex/skills/changelog/SKILL.md`)
- `install-browser-control`: 在新 Mac 上为 Codex 和 Claude 全局安装或整理真实 Chrome/Chromium 调试能力,统一补齐 connect-chrome、browse、setup-browser-cookies 入口并做实机验证。 (file: `./.codex/skills/install-browser-control/SKILL.md`)
### How to use skills

View File

@ -0,0 +1,45 @@
---
name: install-browser-control
description: 在新 Mac 上为 Codex 和 Claude 全局安装或整理真实 Chrome/Chromium 调试能力时使用;适用于需要 connect-chrome、browse、setup-browser-cookies 三个入口、可见浏览器接管、DOM 操作、network/console 检查与 cookie 导入支持的场景。
---
# Install Browser Control
当用户要求在新机器上把浏览器接管调试能力装完整时,直接执行本 skill。
## 目标
- 自动识别 `CODEX_HOME`,默认回退到 `~/.codex`
- 在 Codex / Claude 两侧全局目录补齐或标准化:
- `connect-chrome`
- `browse`
- `setup-browser-cookies`
- 优先复用现有等价能力;缺失时从公开官方来源安装
- 生成本地说明文件:`$CODEX_HOME/browser-control-setup.md`
- 做真实验证:可见 Chromium 窗口、`https://example.com`、DOM 读取、network/console、cookie 导入入口调用
## 执行
先运行安装脚本:
```bash
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
bash "$SCRIPT_DIR/install-browser-control.sh"
```
## 交付
安装脚本结束后,读取并引用:
```bash
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
sed -n '1,240p' "$CODEX_HOME/browser-control-setup.md"
```
汇报时只保留高信号结果:
- 是否安装完成
- 3 个入口的最终路径
- 本地说明文件路径
- 实际验证结果
- 如果未完成,唯一阻塞点

View File

@ -0,0 +1,433 @@
#!/usr/bin/env bash
set -euo pipefail
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
CLAUDE_HOME="${CLAUDE_HOME:-$HOME/.claude}"
GSTACK_REPO_URL="${GSTACK_REPO_URL:-https://github.com/garrytan/gstack.git}"
VERIFY_URL="${VERIFY_URL:-https://example.com}"
SETUP_DOC="$CODEX_HOME/browser-control-setup.md"
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
log() {
printf '[install-browser-control] %s\n' "$*" >&2
}
fail() {
printf '[install-browser-control] ERROR: %s\n' "$*" >&2
exit 1
}
ensure_macos() {
local os_name
os_name="$(uname -s)"
if [[ "$os_name" != "Darwin" ]]; then
fail "当前脚本仅支持 macOS检测到: $os_name"
fi
}
ensure_dir() {
mkdir -p "$1"
}
detect_codex_install() {
if command -v codex >/dev/null 2>&1; then
command -v codex
else
printf 'not-found'
fi
}
detect_claude_install() {
if command -v claude >/dev/null 2>&1; then
command -v claude
else
printf 'not-found'
fi
}
have_equivalent_gstack() {
local target="$1"
[[ -f "$target/connect-chrome/SKILL.md" ]] \
&& [[ -f "$target/setup-browser-cookies/SKILL.md" ]] \
&& [[ -f "$target/browse/src/cli.ts" || -x "$target/browse/dist/browse" ]]
}
clone_seed() {
local seed_dir="$TMP_DIR/gstack-seed"
if [[ -d "$seed_dir" ]]; then
return
fi
log "cloning gstack from $GSTACK_REPO_URL"
git clone --depth 1 "$GSTACK_REPO_URL" "$seed_dir" >/dev/null 2>&1
}
backup_path() {
local path="$1"
if [[ -e "$path" ]]; then
mv "$path" "$path.local.bak.$(date +%Y%m%d%H%M%S)"
fi
}
ensure_gstack_home() {
local home_root="$1"
local target="$home_root/skills/gstack"
ensure_dir "$home_root/skills"
if have_equivalent_gstack "$target"; then
printf '%s' "$target"
return
fi
if [[ -e "$target" ]]; then
backup_path "$target"
fi
clone_seed
cp -R "$TMP_DIR/gstack-seed" "$target"
printf '%s' "$target"
}
ensure_bun() {
if command -v bun >/dev/null 2>&1; then
return
fi
if command -v brew >/dev/null 2>&1; then
log "installing bun with Homebrew"
brew install bun >/dev/null
else
log "installing bun from bun.sh"
curl -fsSL https://bun.sh/install | bash >/dev/null
export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
export PATH="$BUN_INSTALL/bin:$PATH"
fi
command -v bun >/dev/null 2>&1 || fail "bun 安装失败"
}
ensure_browse_runtime() {
local gstack_root="$1"
local browse_bin="$gstack_root/browse/dist/browse"
if [[ ! -x "$browse_bin" ]]; then
ensure_bun
log "building browse binary in $gstack_root"
(
cd "$gstack_root"
bun install >/dev/null
bun run build >/dev/null
)
fi
[[ -x "$browse_bin" ]] || fail "browse binary 不可用: $browse_bin"
ensure_bun
log "ensuring Playwright Chromium runtime"
(
cd "$gstack_root"
bunx playwright install chromium >/dev/null 2>&1 || true
)
printf '%s' "$browse_bin"
}
ensure_skill_entry() {
local home_root="$1"
local skill_name="$2"
local dest="$home_root/skills/$skill_name"
if [[ -L "$dest" ]]; then
printf '%s' "$dest"
return
fi
if [[ -f "$dest/SKILL.md" ]] && grep -q "^name: $skill_name\$" "$dest/SKILL.md"; then
printf '%s' "$dest"
return
fi
if [[ -e "$dest" ]]; then
backup_path "$dest"
fi
ln -sfn "gstack/$skill_name" "$dest"
printf '%s' "$dest"
}
detect_browser_apps() {
local apps=()
[[ -d "/Applications/Google Chrome.app" ]] && apps+=("Google Chrome")
[[ -d "/Applications/Chromium.app" ]] && apps+=("Chromium")
[[ -d "/Applications/Microsoft Edge.app" ]] && apps+=("Microsoft Edge")
[[ -d "/Applications/Brave Browser.app" ]] && apps+=("Brave Browser")
[[ -d "/Applications/Arc.app" ]] && apps+=("Arc")
if [[ ${#apps[@]} -eq 0 ]]; then
printf 'none-detected'
else
local joined=""
local app
for app in "${apps[@]}"; do
if [[ -n "$joined" ]]; then
joined+=", "
fi
joined+="$app"
done
printf '%s' "$joined"
fi
}
visible_browser_processes() {
/usr/bin/osascript -e 'tell application "System Events" to get the name of every process whose visible is true' 2>/dev/null \
| tr ',' '\n' \
| sed 's/^ *//' \
| grep -E 'Chrome|Chromium' \
| paste -sd ', ' - \
|| true
}
verify_browser_control() {
local browse_bin="$1"
local verify_tmp="$TMP_DIR/verify"
local connect_out
local newtab_out
local title_out
local heading_out
local network_out
local console_out
local status_out
local cookie_out=""
local cookie_rc=0
local visible_out=""
local cookie_status=""
mkdir -p "$verify_tmp"
"$browse_bin" stop >/dev/null 2>&1 || true
"$browse_bin" connect >"$verify_tmp/connect.log" 2>&1
sleep 2
"$browse_bin" newtab "$VERIFY_URL" >"$verify_tmp/newtab.log" 2>&1
"$browse_bin" js 'document.title' >"$verify_tmp/title.log" 2>&1
"$browse_bin" text h1 >"$verify_tmp/heading.log" 2>&1
"$browse_bin" reload >/dev/null 2>&1 || true
"$browse_bin" wait --load >/dev/null 2>&1 || true
"$browse_bin" network >"$verify_tmp/network.log" 2>&1
"$browse_bin" console >"$verify_tmp/console.log" 2>&1
"$browse_bin" status >"$verify_tmp/status.log" 2>&1
visible_out="$(visible_browser_processes)"
"$browse_bin" cookie-import-browser chrome --domain example.com >"$verify_tmp/cookie.log" 2>&1 || cookie_rc=$?
connect_out="$(cat "$verify_tmp/connect.log")"
newtab_out="$(cat "$verify_tmp/newtab.log")"
title_out="$(cat "$verify_tmp/title.log")"
heading_out="$(cat "$verify_tmp/heading.log")"
network_out="$(cat "$verify_tmp/network.log")"
console_out="$(cat "$verify_tmp/console.log")"
status_out="$(cat "$verify_tmp/status.log")"
cookie_out="$(cat "$verify_tmp/cookie.log" 2>/dev/null || true)"
if [[ $cookie_rc -eq 0 ]]; then
cookie_status="callable"
elif [[ "$cookie_out" == *"Keychain"* ]]; then
cookie_status="callable-requires-keychain-access"
elif [[ "$cookie_out" == *"No Chromium browsers found"* ]]; then
cookie_status="callable-no-cookie-source-browser"
else
cookie_status="error"
fi
BROWSE_CONNECT_OUT="$connect_out"
BROWSE_NEWTAB_OUT="$newtab_out"
BROWSE_TITLE_OUT="$title_out"
BROWSE_HEADING_OUT="$heading_out"
BROWSE_NETWORK_OUT="$network_out"
BROWSE_CONSOLE_OUT="$console_out"
BROWSE_STATUS_OUT="$status_out"
BROWSE_VISIBLE_OUT="$visible_out"
COOKIE_VERIFY_OUT="$cookie_out"
COOKIE_VERIFY_STATUS="$cookie_status"
}
write_setup_doc() {
local codex_connect="$1"
local codex_browse="$2"
local codex_cookie="$3"
local claude_connect="$4"
local claude_browse="$5"
local claude_cookie="$6"
local codex_gstack="$7"
local claude_gstack="$8"
local browse_bin="$9"
local codex_install="${10}"
local claude_install="${11}"
local browser_apps="${12}"
ensure_dir "$CODEX_HOME"
cat >"$SETUP_DOC" <<EOF
# Browser Control Setup
## 实际安装来源
- 官方上游:$GSTACK_REPO_URL
- Codex 当前 gstack 根目录:$codex_gstack
- Claude 当前 gstack 根目录:$claude_gstack
- Codex 安装入口:$codex_install
- Claude 安装入口:$claude_install
- 本机已检测到的系统浏览器:$browser_apps
- 运行时浏览器Playwright Chromium$browse_bin 驱动)
## 最终技能路径
### Codex
- connect-chrome: $codex_connect
- browse: $codex_browse
- setup-browser-cookies: $codex_cookie
### Claude
- connect-chrome: $claude_connect
- browse: $claude_browse
- setup-browser-cookies: $claude_cookie
## 如何调用
### Codex
- \`connect-chrome\`
- \`browse\`
- \`setup-browser-cookies\`
- \`install-browser-control\`
### Claude
- \`/connect-chrome\`
- \`/browse\`
- \`/setup-browser-cookies\`
- \`/install-browser-control\`
## 如何验证
1. 运行 \`connect-chrome\`,确认出现 headed Chromium 窗口。
2. 运行 \`browse newtab https://example.com\`
3. 运行 \`browse js 'document.title'\`\`browse text h1\`,确认可读取 DOM。
4. 运行 \`browse network\`\`browse console\`,确认能读取请求和控制台信息。
5. 运行 \`browse cookie-import-browser chrome --domain example.com\`,确认 cookie 导入入口可被调用;首次读取 Chrome Cookie 时macOS 可能弹出 Keychain 授权框。
## 本机最近一次真实验证
### connect
\`\`\`
$BROWSE_CONNECT_OUT
\`\`\`
### newtab + DOM
\`\`\`
$BROWSE_NEWTAB_OUT
$BROWSE_TITLE_OUT
$BROWSE_HEADING_OUT
\`\`\`
### network
\`\`\`
$BROWSE_NETWORK_OUT
\`\`\`
### console
\`\`\`
$BROWSE_CONSOLE_OUT
\`\`\`
### status
\`\`\`
$BROWSE_STATUS_OUT
\`\`\`
### visible browser processes
\`\`\`
${BROWSE_VISIBLE_OUT:-not-detected}
\`\`\`
### cookie import verification
- status: $COOKIE_VERIFY_STATUS
\`\`\`
$COOKIE_VERIFY_OUT
\`\`\`
EOF
}
main() {
local codex_gstack
local claude_gstack
local browse_bin
local claude_browse_bin
local codex_connect
local codex_browse
local codex_cookie
local claude_connect
local claude_browse
local claude_cookie
local browser_apps
local codex_install
local claude_install
ensure_macos
codex_install="$(detect_codex_install)"
claude_install="$(detect_claude_install)"
codex_gstack="$(ensure_gstack_home "$CODEX_HOME")"
claude_gstack="$(ensure_gstack_home "$CLAUDE_HOME")"
browse_bin="$(ensure_browse_runtime "$codex_gstack")"
claude_browse_bin="$(ensure_browse_runtime "$claude_gstack")"
[[ -x "$claude_browse_bin" ]] || fail "Claude browse binary 不可用: $claude_browse_bin"
codex_connect="$(ensure_skill_entry "$CODEX_HOME" "connect-chrome")"
codex_browse="$(ensure_skill_entry "$CODEX_HOME" "browse")"
codex_cookie="$(ensure_skill_entry "$CODEX_HOME" "setup-browser-cookies")"
claude_connect="$(ensure_skill_entry "$CLAUDE_HOME" "connect-chrome")"
claude_browse="$(ensure_skill_entry "$CLAUDE_HOME" "browse")"
claude_cookie="$(ensure_skill_entry "$CLAUDE_HOME" "setup-browser-cookies")"
browser_apps="$(detect_browser_apps)"
verify_browser_control "$browse_bin"
write_setup_doc \
"$codex_connect" "$codex_browse" "$codex_cookie" \
"$claude_connect" "$claude_browse" "$claude_cookie" \
"$codex_gstack" "$claude_gstack" "$browse_bin" \
"$codex_install" "$claude_install" "$browser_apps"
printf 'DONE\n'
printf 'CODEX_HOME=%s\n' "$CODEX_HOME"
printf 'CLAUDE_HOME=%s\n' "$CLAUDE_HOME"
printf 'SETUP_DOC=%s\n' "$SETUP_DOC"
printf 'CODEX_CONNECT=%s\n' "$codex_connect"
printf 'CODEX_BROWSE=%s\n' "$codex_browse"
printf 'CODEX_COOKIE=%s\n' "$codex_cookie"
printf 'CLAUDE_CONNECT=%s\n' "$claude_connect"
printf 'CLAUDE_BROWSE=%s\n' "$claude_browse"
printf 'CLAUDE_COOKIE=%s\n' "$claude_cookie"
printf 'COOKIE_VERIFY_STATUS=%s\n' "$COOKIE_VERIFY_STATUS"
}
main "$@"

View File

@ -148,6 +148,7 @@
- `issue`: 查看当前仓库或任意 Gitea 仓库的 issue 列表和单条详情,支持自动识别 git origin、用户指定仓库和格式化输出。 (file: `./.codex/skills/issue/SKILL.md`)
- `issue-drive`: 归集证据并把问题拆成 1 到多张 Gitea issue支持从当前仓库 origin 自动识别仓库或用户显式指定。 (file: `./.codex/skills/issue-drive/SKILL.md`)
- `changelog`: 一键发版:生成更新日志 → commit → 打 tag全流程自动化。 (file: `./.codex/skills/changelog/SKILL.md`)
- `install-browser-control`: 在新 Mac 上为 Codex 和 Claude 全局安装或整理真实 Chrome/Chromium 调试能力,统一补齐 connect-chrome、browse、setup-browser-cookies 入口并做实机验证。 (file: `./.codex/skills/install-browser-control/SKILL.md`)
### How to use skills

View File

@ -148,6 +148,7 @@
- `issue`: 查看当前仓库或任意 Gitea 仓库的 issue 列表和单条详情,支持自动识别 git origin、用户指定仓库和格式化输出。 (file: `./.codex/skills/issue/SKILL.md`)
- `issue-drive`: 归集证据并把问题拆成 1 到多张 Gitea issue支持从当前仓库 origin 自动识别仓库或用户显式指定。 (file: `./.codex/skills/issue-drive/SKILL.md`)
- `changelog`: 一键发版:生成更新日志 → commit → 打 tag全流程自动化。 (file: `./.codex/skills/changelog/SKILL.md`)
- `install-browser-control`: 在新 Mac 上为 Codex 和 Claude 全局安装或整理真实 Chrome/Chromium 调试能力,统一补齐 connect-chrome、browse、setup-browser-cookies 入口并做实机验证。 (file: `./.codex/skills/install-browser-control/SKILL.md`)
### How to use skills