@clawhub-grey0758-39f496a965
Create a repository under your own GitHub account, wire a local project repo to it, and push committed history safely. 为你自己的 GitHub 账户创建仓库,把本地项目仓库接过去,并安全推送已提...
---
name: github-personal-repo-publisher
description: Create a repository under your own GitHub account, wire a local project repo to it, and push committed history safely. 为你自己的 GitHub 账户创建仓库,把本地项目仓库接过去,并安全推送已提交历史。
license: CC-BY-4.0
compatibility: OpenClaw, Codex, Claude Code, and ClawHub-style markdown skill runners with bash, git, curl, ssh, network access, and 1Password CLI available.
---
# GitHub Personal Repo Publisher
Use this skill when a local Git project should be published to a new repository under your own GitHub account and the account access is managed through SSH plus a 1Password-stored PAT.
当本地 Git 项目需要发布到你自己 GitHub 账户下的新仓库,且账户访问通过 SSH 和 1Password 中保存的 PAT 管理时,使用这个 skill。
## Read First | 先读这些
- `{baseDir}/README.md`
- `{baseDir}/WORKFLOW.md`
- `{baseDir}/FAQ.md`
- `{baseDir}/CHANGELOG.md`
## Primary Rule | 核心原则
Treat Git history as the publish boundary: only committed local history should be described as pushed.
把 Git 提交历史当作发布边界:只有已提交的本地历史,才能算已经推送。
## Workflow | 执行流程
1. inspect the local repository state
检查本地仓库状态
2. verify SSH auth to the personal GitHub account
验证到个人 GitHub 账户的 SSH 认证
3. check whether the target repository already exists
检查目标仓库是否已存在
4. create the GitHub repository through the API if missing
如果缺失,则通过 API 创建 GitHub 仓库
5. add or update the local `origin`
添加或更新本地 `origin`
6. push the current branch and set upstream
推送当前分支并建立 upstream
7. verify remote wiring and pushed commit
校验远端绑定和已推送提交
8. state clearly what remains local-only
明确说明哪些内容仍只存在于本地
## Strong Heuristics | 强判断规则
- if `origin` already points to the correct repo, do not recreate the repository
- if SSH auth fails, do not continue to remote rewiring or push
- if the GitHub repo does not exist and `gh` is unavailable, use the REST API with a 1Password-managed PAT
- default new personal project repositories to `private` unless the user clearly wants public exposure
- if the worktree is dirty, say explicitly that uncommitted files were not pushed
- if `origin` exists but points elsewhere, update it intentionally rather than assuming it is disposable
中文解释:
- 如果 `origin` 已经指向正确仓库,就不要重复建仓库。
- SSH 认证失败时,不要继续改 remote 或 push。
- GitHub 仓库不存在且 `gh` 不可用时,用 1Password 管理的 PAT 调 REST API。
- 新的个人项目仓库默认建成 `private`,除非用户明确要求公开。
- 工作区有脏改动时,要明确说明未提交文件并没有被推送。
- `origin` 已存在但指向别处时,要有意识地更新,不要假设它可以随便覆盖。
## Safe Commands | 安全命令
```bash
git -C /path/to/project status --short
git -C /path/to/project remote -v
git -C /path/to/project log --oneline -3
ssh -T git@github-grey0758
git ls-remote git@github-grey0758:grey0758/your-repo.git HEAD
TOKEN="$(op read 'op://OpenClaw/GitHub Fine-Grained PAT - Repo Admin - grey0758/credential')"
curl -fsSL -X POST -H 'Accept: application/vnd.github+json' -H "Authorization: Bearer $TOKEN" https://api.github.com/user/repos -d '{"name":"your-repo","private":true}'
git -C /path/to/project remote add origin git@github-grey0758:grey0758/your-repo.git
git -C /path/to/project push -u origin main
```
## Response Format | 输出格式
Always return:
始终返回:
1. current local repo status
2. GitHub repo existence or creation status
3. remote wiring status
4. push status
5. local-only work that still remains
## Constraints | 约束
- do not reveal PAT values or copy secret material into files
- do not say a project is fully published when uncommitted changes still exist
- do not assume `gh` is installed
- keep the owner, SSH alias, and target repo name explicit
FILE:CHANGELOG.md
# Changelog
## 1.0.0
- Initial release
- Added canonical GitHub personal repo creation and project push workflow
- Added bilingual `SKILL.md`
- Added `README.md`
- Added `WORKFLOW.md`
- Added `FAQ.md`
- Added `agents/openai.yaml` with UI metadata
FILE:FAQ.md
# FAQ
## Does this skill require `gh`? | 这个 skill 需要 `gh` 吗?
No.
- It uses `curl` plus the GitHub API when needed
- It reads the PAT from 1Password
## Why check SSH first? | 为什么先查 SSH?
Because repo creation and repo push use different mechanisms.
- API access may work while SSH push is broken
- Early SSH validation shortens recovery time
## Why say uncommitted work is still local-only? | 为什么要强调未提交改动仍只在本地?
Because that is the actual Git boundary.
- committed history can be pushed
- uncommitted files cannot
## Should new repos default to private? | 新仓库默认私有吗?
Yes, unless the user clearly asks for public.
FILE:README.md
# GitHub Personal Repo Publisher
This skill turns a local Git project plus your own-account GitHub publishing request into a verified repo-creation, remote-wiring, and push workflow.
这个 skill 会把“本地 Git 项目 + 发布到自己 GitHub 账户”的请求,转成一条已验证的建仓库、接 remote 和推送流程。
## What This Skill Is For | 适用场景
Use it when:
适用于:
- a local project repo should be published under your own GitHub account
- the target repository may not exist yet
- `gh` may be unavailable
- account access is managed through SSH and a 1Password-stored GitHub PAT
## Validated Defaults | 已验证默认值
- GitHub owner: `grey0758`
- SSH host alias: `github-grey0758`
- PAT item: `GitHub Fine-Grained PAT - Repo Admin - grey0758`
- new repo visibility: `private`
## Important Decision Rules | 关键判断规则
- verify SSH auth before changing remotes
- create the GitHub repo first when it does not already exist
- push only committed history
- explicitly call out any remaining local-only changes
## Included Files | 包含文件
- `SKILL.md`
- `README.md`
- `WORKFLOW.md`
- `FAQ.md`
- `CHANGELOG.md`
- `agents/openai.yaml`
## ClawHub Publish Shape | ClawHub 发布方式
This folder is self-contained so it can be published to ClawHub as a single bundle.
这个目录是自包含的,可以直接作为一个 skill 包发布到 ClawHub。
```bash
clawhub publish ./skills/shared/github-personal-repo-publisher \
--slug github-personal-repo-publisher \
--name "GitHub Personal Repo Publisher" \
--version 1.0.0 \
--tags latest,github,git,repository,publish
```
FILE:WORKFLOW.md
# Workflow
## Goal | 目标
Create a new repository under your own GitHub account and safely wire a local project repo to it.
在你自己的 GitHub 账户下创建新仓库,并把本地项目仓库安全接过去。
## Build Order | 构建顺序
1. inspect local repo state
2. verify SSH auth
3. check remote repo existence
4. create the repo if missing
5. add or update `origin`
6. push and set upstream
7. verify remote state
8. report what remains local-only
## Core Rule | 核心规则
Repository creation success does not mean the local project is fully published. Only committed history that was actually pushed counts as published.
仓库创建成功不代表本地项目已经完整发布。只有真正 push 出去的已提交历史,才算已发布。
FILE:agents/openai.yaml
interface:
display_name: "GitHub Repo Publisher"
short_description: "Create your own GitHub repo and push a local project safely"
default_prompt: "Use $github-personal-repo-publisher to create a repo under my GitHub account, connect this local project to it, and push only the committed history safely."
policy:
allow_implicit_invocation: false
Update Mihomo site routing rules from natural-language requests, rebuild the published subscription, and verify the live output. 根据自然语言路由请求更新 Mihomo 规则、重建已发布...
---
name: mihomo-subscription-route-publisher
description: Update Mihomo site routing rules from natural-language requests, rebuild the published subscription, and verify the live output. 根据自然语言路由请求更新 Mihomo 规则、重建已发布订阅并验证线上结果。
license: CC-BY-4.0
compatibility: OpenClaw, Codex, Claude Code, and ClawHub-style markdown skill runners with bash, git, node, wrangler, network access, and 1Password CLI available.
---
# Mihomo Subscription Route Publisher
Use this skill when the user says a site, domain, or Mihomo rule should use a specific route target and expects the published subscription to update.
当用户说某个网站、域名或 Mihomo 规则应该走某个目标出口,并希望已发布订阅同步更新时,使用这个 skill。
## Read First | 先读这些
- `{baseDir}/README.md`
- `{baseDir}/WORKFLOW.md`
- `{baseDir}/FAQ.md`
- `{baseDir}/CHANGELOG.md`
## Primary Rule | 核心原则
Treat `/home/grey/mihomo-fullstack-deploy` as the canonical source, `rules.xiannai.me` as the distribution layer, and live validation as mandatory before declaring success.
把 `/home/grey/mihomo-fullstack-deploy` 当作规范源,把 `rules.xiannai.me` 当作分发层,并把线上验证当作必选步骤。
## Workflow | 执行流程
1. normalize the user request into explicit Mihomo rule lines
把自然语言请求归一化成明确的 Mihomo 规则行
2. map the requested node to a stable group when possible
尽量把节点名映射到稳定代理组
3. edit the canonical source file for that target
修改该目标对应的规范源文件
4. regenerate worker artifacts
重生成 worker 构建产物
5. validate syntax and config
校验语法和配置
6. deploy worker and trigger `/sync`
发布 worker 并触发 `/sync`
7. verify the live published artifact with `?ts=`
用 `?ts=` 校验线上已发布产物
8. update the local Linux runtime config only if needed
仅在需要时同步本机 Linux 运行时配置
9. commit and push if the repo should remain canonical upstream
如果仓库要保持上游规范源,就提交并推送
## Stable Route Map | 稳定路由映射
- `直连`
Canonical source:
`/home/grey/mihomo-fullstack-deploy/worker/src/inline-rules.js`
- `故障转移`
Canonical source:
`/home/grey/mihomo-fullstack-deploy/rules/user_ruleset/user_proxy_rules.txt`
- `定向出口`
Canonical source:
- `/home/grey/mihomo-fullstack-deploy/etc/mihomo/config.yaml`
- `/home/grey/mihomo-fullstack-deploy/etc/mihomo/config.windows.yaml`
Node-name aliases:
- `onlygays1` -> `故障转移`
- `racknerd-reality` -> `定向出口`
- `DIRECT` -> `直连`
## Strong Heuristics | 强判断规则
- if the user names a raw node, prefer the stable group backed by that node
- if the route should affect all clients, do not stop after only changing Linux
- if only `故障转移` rules changed, do not restart local Mihomo unnecessarily
- if `定向出口` rules changed, validate the Linux config and published Linux/Windows configs
- if the repo is dirty in unrelated files, touch only the routing files required for the task
- always verify remote output with cache busting
中文解释:
- 用户说的是裸节点名时,优先落到对应的稳定代理组。
- 如果目标是所有客户端生效,不要只改 Linux 就结束。
- 只改 `故障转移` 规则时,不要无意义重启本机 Mihomo。
- 改了 `定向出口` 时,要同时校验 Linux 配置和已发布的 Linux/Windows 配置。
- 仓库里有不相关脏文件时,只碰本次路由任务需要的文件。
- 远端验证必须带缓存穿透参数。
## Safe Commands | 安全命令
```bash
sed -n '1,120p' /home/grey/mihomo-fullstack-deploy/rules/user_ruleset/user_proxy_rules.txt
sed -n '1,80p' /home/grey/mihomo-fullstack-deploy/worker/src/inline-rules.js
cd /home/grey/mihomo-fullstack-deploy/worker && node --check src/index.js
HOME=/etc/mihomo XDG_CONFIG_HOME=/etc/mihomo/.config /usr/local/bin/mihomo -t -f /home/grey/mihomo-fullstack-deploy/etc/mihomo/config.yaml
curl -fsSL "https://rules.xiannai.me/sync?ts=$(date +%s)"
curl -fsSL "https://rules.xiannai.me/configs/linux.yaml?ts=$(date +%s)"
```
## Response Format | 输出格式
Always return:
始终返回:
1. normalized routing request
2. files changed
3. publish status
4. live verification result
5. next action if anything is still pending
## Constraints | 约束
- do not reveal Cloudflare or GitHub token values
- do not treat `rules.xiannai.me` as canonical; the repo stays canonical
- do not silently leave live and repo state diverged
- do not promise cross-client parity for `定向出口` unless the relevant configs were updated in the same change
FILE:CHANGELOG.md
# Changelog
## 1.0.0
- Initial release
- Added bilingual `SKILL.md`
- Added `README.md`
- Added `WORKFLOW.md`
- Added bilingual `FAQ.md`
- Added canonical knowledge runbook references
FILE:FAQ.md
# FAQ
## Can I say a node name instead of a group name? | 我可以直接说节点名吗?
Yes, but the skill should usually map it to a stable group.
- `onlygays1` -> `故障转移`
- `racknerd-reality` -> `定向出口`
可以,但通常应先映射到稳定代理组。
## Does this skill edit the live server too? | 这个 skill 会改本机正在运行的配置吗?
Only when needed.
- Rule-file-only changes do not require changing `/etc/mihomo/config.yaml`
- Config-level `定向出口` changes may require syncing `/etc/mihomo/config.yaml` and restarting `mihomo`
只有在需要时才会。
## Which file handles `故障转移` routes? | `故障转移` 规则改哪个文件?
- `/home/grey/mihomo-fullstack-deploy/rules/user_ruleset/user_proxy_rules.txt`
## Which file handles `直连` routes? | `直连` 规则改哪个文件?
- `/home/grey/mihomo-fullstack-deploy/worker/src/inline-rules.js`
## Which file handles `定向出口` routes? | `定向出口` 规则改哪个文件?
- `/home/grey/mihomo-fullstack-deploy/etc/mihomo/config.yaml`
- `/home/grey/mihomo-fullstack-deploy/etc/mihomo/config.windows.yaml`
## Why not stop after editing the repo? | 为什么不能只改仓库不发布?
Because the user asked for client-visible subscription updates. Repo-only changes are not enough.
因为目标是让客户端订阅更新,只改仓库还不够。
FILE:README.md
# Mihomo Subscription Route Publisher
This skill converts a routing request such as “let `example.com` use `定向出口`” into a repo change, a worker redeploy, and a verified subscription update.
这个 skill 会把类似“让 `example.com` 走 `定向出口`”这样的请求,转成仓库变更、worker 重发和已验证的订阅更新。
## What This Skill Is For | 适用场景
Use it when:
适用于:
- a user names one or more sites, domains, or Mihomo rule lines
- the user expects a specific route target such as `直连`, `故障转移`, or `定向出口`
- the published subscription at `rules.xiannai.me` must update
- the repo should remain the source of truth
## Canonical Repository | 规范仓库
- `/home/grey/mihomo-fullstack-deploy`
## Distribution Layer | 分发层
- Worker custom domain: `https://rules.xiannai.me`
## What It Produces | 产出物
- updated canonical route rules
- regenerated worker assets
- redeployed worker
- refreshed published subscription objects
- verification proof against the live subscription endpoint
## Included Files | 包含文件
- `SKILL.md`
- `README.md`
- `WORKFLOW.md`
- `FAQ.md`
- `CHANGELOG.md`
## Important Decision Rules | 关键判断规则
- prefer stable group names over raw node names
- keep the repo canonical and the worker distributive
- validate live output before closing the task
- only restart local Mihomo when the runtime config itself changed
## Local Use | 本地使用
Place this folder under one of these locations:
- `<workspace>/skills/`
- `~/.openclaw/skills/`
Then refresh skills or start a new session.
## ClawHub Publish Shape | ClawHub 发布方式
```bash
clawhub publish /home/grey/work/agent-knowledge-stack/skills/shared/mihomo-subscription-route-publisher \
--slug mihomo-subscription-route-publisher \
--name "Mihomo Subscription Route Publisher" \
--version 1.0.0 \
--tags latest,mihomo,subscription,route,worker,cloudflare
```
FILE:WORKFLOW.md
# Workflow
## Goal | 目标
Update Mihomo route behavior from a user request and republish the subscription safely.
根据用户请求更新 Mihomo 路由行为,并安全地重新发布订阅。
## Build Order | 构建顺序
1. normalize the user request
2. resolve the target group
3. edit the canonical source file
4. regenerate worker artifacts
5. validate locally
6. deploy worker
7. trigger subscription sync
8. verify live output
9. optionally restart local Mihomo
10. optionally commit and push
## Core Rule | 核心规则
The workflow is not complete until the live subscription endpoint shows the new content.
只有当线上订阅端点已经返回新内容时,这个流程才算完成。
Create and maintain a Cloudflare Tunnel for Open WebUI using a 1Password-managed API token, Docker runtime, and optional systemd persistence. 使用 1Password 管理...
---
name: cloudflare-openwebui-tunnel-operator
description: Create and maintain a Cloudflare Tunnel for Open WebUI using a 1Password-managed API token, Docker runtime, and optional systemd persistence. 使用 1Password 管理的 API token、Docker 运行时和可选 systemd 持久化,为 Open WebUI 创建并维护 Cloudflare Tunnel。
homepage: https://docs.openclaw.ai/tools/clawhub
---
# Cloudflare Open WebUI Tunnel Operator
Use this skill when Open WebUI should be exposed through a Cloudflare Tunnel and the Cloudflare API token is stored in 1Password.
当需要通过 Cloudflare Tunnel 暴露 Open WebUI,且 Cloudflare API token 保存在 1Password 中时,使用这个 skill。
## Read First | 先读这些
- `{baseDir}/README.md`
- `{baseDir}/WORKFLOW.md`
- `{baseDir}/FAQ.md`
- `{baseDir}/CHANGELOG.md`
## Primary Rule | 核心原则
Treat 1Password as the secret source, `knowledge/` as the canonical documentation source, and ClawHub only as the distribution layer.
把 1Password 当作密钥来源,把 `knowledge/` 当作规范文档来源,把 ClawHub 仅当作分发层。
## Workflow | 执行流程
1. confirm local Open WebUI health
确认本地 Open WebUI 健康
2. confirm `op` can read the Cloudflare token
确认 `op` 能读取 Cloudflare token
3. create or update the remote-managed tunnel and DNS
创建或更新 remote-managed tunnel 与 DNS
4. write the runtime tunnel token to a local env file
把运行态 tunnel token 写入本地 env 文件
5. start `cloudflared` with Docker
用 Docker 启动 `cloudflared`
6. persist the tunnel with `systemd` if reboots must survive
如果需要跨重启持久化,用 `systemd`
7. verify both local and public URLs
验证本地与公网 URL
8. backfill `account_id` in 1Password if it was inferred
如果 `account_id` 是推断得到的,回填到 1Password
## Strong Heuristics | 强判断规则
- if the local Open WebUI is down, do not debug Cloudflare first
- if the 1Password item lacks `account_id`, derive it once and write it back
- if `systemd` cannot authenticate to 1Password, check whether it is calling the wrong `op` binary
- if the public URL returns `502`, check origin readiness before changing tunnel config
- use a hostname derived from project meaning, not the machine hostname
中文解释:
- 本地 Open WebUI 没起来,就不要先查 Cloudflare。
- 1Password 缺 `account_id` 时,可先推断一次,再回填。
- `systemd` 认证不到 1Password 时,优先检查它是否调用了错误的 `op`。
- 公网返回 `502` 时,先检查 origin 是否就绪,不要先改 tunnel 配置。
- 域名应按项目语义命名,不要按机器名命名。
## Safe Commands | 安全命令
```bash
op whoami
docker compose ps
curl -I http://localhost:3301
curl -I https://your-hostname.example.com
systemctl status --no-pager your-tunnel.service
```
## Response Format | 输出格式
Always return:
始终返回:
1. current workflow status
2. missing artifacts
3. next single best action
4. verification after that
## Constraints | 约束
- do not reveal secret values from 1Password, `.env`, or runtime env files
- do not publish machine-specific tokens or raw transcripts
- prefer self-contained docs and package content
- keep the hostname and service mapping explicit
中文约束:
- 不要泄露 1Password、`.env` 或运行态 env 文件中的密钥值。
- 不要发布机器专属 token 或原始聊天记录。
- 优先保持文档和 skill 包自包含。
- 明确写清楚 hostname 和 origin service 的映射关系。
FILE:CHANGELOG.md
# Changelog
## 1.0.0
- Initial public release
- Added bilingual `SKILL.md`
- Added bilingual `README.md`
- Added bilingual `WORKFLOW.md`
- Added bilingual `FAQ.md`
- Captured the Cloudflare Tunnel, 1Password, Docker, and `systemd` workflow for Open WebUI
FILE:FAQ.md
# FAQ
## Should the public hostname come from the machine name? | 公网 hostname 应该取自机器名吗?
No. It should come from the project meaning.
不应该。应当取自项目语义。
## Is the Cloudflare API token supposed to live in Git? | Cloudflare API token 应该放进 Git 吗?
No. Keep it in 1Password or another secret manager.
不应该。应当放在 1Password 或其他密钥管理器中。
## Can `account_id` be inferred from the zone? | `account_id` 可以从 zone 推断吗?
Yes, but it is better to write it back into the 1Password item after the first successful run.
可以,但最好在第一次成功执行后回填到 1Password 条目中。
## Why did the public URL return `502` right after startup? | 为什么刚启动时公网 URL 会返回 `502`?
Because `cloudflared` may connect to Cloudflare before the Open WebUI origin is ready inside the Docker network.
因为 `cloudflared` 可能先连上 Cloudflare,而 Docker 网络里的 Open WebUI origin 还没就绪。
## Why must `systemd` use the same `op` binary as the shell? | 为什么 `systemd` 必须使用和 shell 相同的 `op`?
Because the working shell command may actually be a wrapper that injects a service-account token before calling the real CLI.
因为 shell 里能工作的命令,实际可能是一个包装器:它先注入 service-account token,再调用真正的 CLI。
## Should ClawHub be treated as the source of truth? | ClawHub 应该作为事实源吗?
No. Keep the source of truth in `knowledge/` and the local skill folder.
不应该。事实源应保留在 `knowledge/` 和本地 skill 目录中。
FILE:README.md
# Cloudflare Open WebUI Tunnel Operator
This skill helps expose Open WebUI through a Cloudflare Tunnel using a Cloudflare API token managed in 1Password.
这个 skill 用来通过 Cloudflare Tunnel 暴露 Open WebUI,并使用保存在 1Password 中的 Cloudflare API token。
## What This Skill Is For | 适用场景
Use it when:
适用于:
- Open WebUI already runs locally and you need a public HTTPS hostname
- you want the Cloudflare API token to stay in 1Password
- you want the tunnel and DNS to be created or updated by API
- you want `cloudflared` to run under Docker
- you want a `systemd` path for reboot persistence
## What It Produces | 产出物
The workflow aims to produce:
这个流程目标产出:
- a stable public hostname for Open WebUI
- a repeatable tunnel creation workflow
- a local runtime env file for `cloudflared`
- an optional `systemd` unit
- a reusable, self-contained skill package
## Included Files | 包含文件
- `SKILL.md`
- `README.md`
- `WORKFLOW.md`
- `FAQ.md`
- `CHANGELOG.md`
## Important Decision Rules | 关键判断规则
- keep 1Password as the secret source of truth
- keep canonical docs in `knowledge/`
- treat ClawHub as distribution, not canonical storage
- use project-meaningful hostnames such as `knowledge-stack.xiannai.me`
## Local Use | 本地使用
Place this folder under one of these locations:
把这个目录放到以下任一位置:
- `<workspace>/skills/`
- `~/.openclaw/skills/`
Then start a new OpenClaw session or refresh skills.
然后重新开始一个 OpenClaw 会话,或刷新 skills。
## ClawHub Publish Shape | ClawHub 发布方式
This folder is self-contained so it can be published as a single bundle.
这个目录是自包含的,可以直接作为单个 skill 包发布。
Example publish command:
发布示例命令:
```bash
clawhub publish ./skills/shared/cloudflare-openwebui-tunnel-operator \
--slug cloudflare-openwebui-tunnel-operator \
--name "Cloudflare Open WebUI Tunnel Operator" \
--version 1.0.0 \
--tags latest,cloudflare,tunnel,open-webui,1password,systemd
```
FILE:WORKFLOW.md
# Workflow
## Goal | 目标
Expose Open WebUI through a Cloudflare Tunnel using a Cloudflare API token stored in 1Password, then keep the tunnel stable with Docker and optional `systemd`.
把保存在 1Password 中的 Cloudflare API token 用于创建 Cloudflare Tunnel 暴露 Open WebUI,并结合 Docker 与可选 `systemd` 保持隧道稳定运行。
## Canonical Sequence | 标准顺序
1. verify local Open WebUI health
验证本地 Open WebUI 健康状态
2. verify `op` access to the Cloudflare token item
验证 `op` 能访问 Cloudflare token 条目
3. resolve the Cloudflare zone and account
解析 Cloudflare zone 和 account
4. create or reuse a remote-managed tunnel
创建或复用 remote-managed tunnel
5. apply ingress from the public hostname to the local Open WebUI service
把公网 hostname 映射到本地 Open WebUI 服务
6. create or update the proxied DNS CNAME
创建或更新代理 CNAME 记录
7. fetch the tunnel runtime token and write a local env file
获取 tunnel 运行 token 并写入本地 env 文件
8. start `cloudflared` in Docker
用 Docker 启动 `cloudflared`
9. enable `systemd` if reboot persistence is required
如果要求跨重启持久化,启用 `systemd`
10. verify local and public URLs
验证本地与公网 URL
11. backfill `account_id` into 1Password if it was inferred
如果 `account_id` 是推断出来的,则回填到 1Password
## Recommended Checks | 推荐检查
```bash
op whoami
docker compose ps
curl -I http://localhost:3301
curl -I https://your-hostname.example.com
docker compose logs --tail=50 cloudflared
systemctl status --no-pager your-tunnel.service
```
## Interpretation Rules | 解释规则
- local `200 OK`, public `502`: the tunnel is up before the origin is ready
- local failure, public failure: fix Open WebUI first
- `systemd` can not read 1Password: it is often using the wrong `op` binary or wrong `PATH`
- missing `account_id`: derive it once, then persist it back into 1Password
## Packaging Rules | 打包规则
- do not ship secrets
- do not ship raw command transcripts
- keep the package self-contained
- keep the hostname, tunnel name, and origin service explicit
- if the audience is mixed, keep the docs bilingual
Generate a reusable runbook plus self-contained skill kit from a validated workflow. 从一个已验证流程批量生成可复用的 runbook 与自包含 skill 套件。
---
name: runbook-skill-kit-template-factory
description: Generate a reusable runbook plus self-contained skill kit from a validated workflow. 从一个已验证流程批量生成可复用的 runbook 与自包含 skill 套件。
homepage: https://docs.openclaw.ai/tools/clawhub
---
# Runbook Skill Kit Template Factory
Use this skill when you want a repeatable template that turns a validated workflow into both knowledge docs and a reusable skill bundle.
当你需要一个可重复使用的模板,把已验证流程同时变成知识文档和可复用 skill 套件时,使用这个 skill。
## Read First | 先读这些
- `{baseDir}/README.md`
- `{baseDir}/WORKFLOW.md`
- `{baseDir}/TEMPLATES.md`
- `{baseDir}/FAQ.md`
- `{baseDir}/CHANGELOG.md`
## Primary Rule | 核心原则
Treat the template as a production kit generator, not as a place to dump raw notes.
把这个模板当成生产套件的生成器,不要把它当成随手堆原始笔记的地方。
## Workflow | 执行流程
1. identify the validated workflow
确认已经验证过的流程
2. choose a topic slug and display name
选定 topic slug 和显示名称
3. instantiate the knowledge doc trio
生成三件套知识文档
4. instantiate the self-contained skill bundle
生成自包含 skill 包
5. replace placeholders consistently
一致地替换所有占位符
6. add or update changelog
增补 changelog
7. sync memory, publish, and verify
同步 memory、发布并验证
## Strong Heuristics | 强判断规则
- if the process is not stable yet, keep it as draft notes
- if the package references outside docs, inline or bundle them before publish
- if operators are mixed-language, keep the kit bilingual
- if the workflow will be reused often, favor templates over ad hoc copying
中文解释:
- 流程还不稳定,就先留在草稿,不要直接模板化。
- 如果包依赖外部文档,发布前先内联或打包进去。
- 如果使用者语言混合,优先双语。
- 如果流程会反复复用,优先做模板,不要每次手工复制。
## Safe Commands | 安全命令
```bash
make sync
clawhub publish /absolute/path/to/skill-folder --slug your-skill --name "Your Skill" --version 1.0.0
clawhub inspect your-skill --version 1.0.0 --files
```
## Constraints | 约束
- keep `knowledge/` as canonical source
- keep the skill bundle self-contained
- do not ship secrets or raw transcripts
- prefer stable naming and consistent file layout
FILE:CHANGELOG.md
# Changelog
## 1.0.1
- Added `agents/openai.yaml` with UI metadata
- Added display name, short description, and default prompt for skill UIs
## 1.0.0
- Initial public release
- Added bilingual `SKILL.md`
- Added bilingual `README.md`
- Added `WORKFLOW.md`
- Added `TEMPLATES.md`
- Added bilingual `FAQ.md`
- Added `CHANGELOG.md`
FILE:FAQ.md
# FAQ
## Is this a topic-specific skill? | 这是针对某一个话题的 skill 吗?
No. It is a general factory template for many topics.
不是。它是面向多个话题的通用工厂模板。
## What is the main advantage? | 最大价值是什么?
It standardizes the package layout and reduces repeated manual authoring.
它把包结构标准化,减少重复手工编写。
## Should I publish every generated kit? | 每个生成出来的套件都要发布吗?
No. Publish only the ones worth reuse outside the local repository.
不一定。只发布那些值得在本地仓库之外复用的套件。
FILE:README.md
# Runbook Skill Kit Template Factory
This skill provides a general template for producing a reusable documentation-and-skill kit from any validated workflow.
这个 skill 提供一个通用模板,用来把任意已验证流程做成可复用的“文档 + skill”套件。
## What This Skill Is For | 适用场景
Use it when:
适用于:
- you repeatedly convert solved workflows into docs and skills
- you want a consistent file layout
- you want the result to be publishable to ClawHub
- you want a kit that works for both humans and agents
## Output Kit | 产出套件
- knowledge index
- full knowledge runbook
- knowledge FAQ
- self-contained skill bundle
- release changelog
## Included Files | 包含文件
- `SKILL.md`
- `README.md`
- `WORKFLOW.md`
- `TEMPLATES.md`
- `FAQ.md`
- `CHANGELOG.md`
## ClawHub Publish Shape | ClawHub 发布方式
```bash
clawhub publish ./skills/shared/runbook-skill-kit-template-factory \
--slug runbook-skill-kit-template-factory \
--name "Runbook Skill Kit Template Factory" \
--version 1.0.0 \
--tags latest,template,workflow,runbook,skill,bilingual
```
FILE:TEMPLATES.md
# Templates
## Knowledge Index Template
```md
# <Topic> Index
## Start here
- Full runbook: [<topic>.md](./<topic>.md)
- Short FAQ: [<topic>-faq.md](./<topic>-faq.md)
```
## Knowledge Runbook Template
```md
# <Topic>
## Goal
<what this workflow solves>
## Entry conditions
- <condition 1>
- <condition 2>
## Workflow
1. <step 1>
2. <step 2>
3. <step 3>
## Verification
- <proof 1>
- <proof 2>
```
## Skill Bundle Template
```text
<skill-name>/
SKILL.md
README.md
WORKFLOW.md
FAQ.md
CHANGELOG.md
```
## SKILL.md Template Hints
- include a precise trigger description
- reference bundled docs with `{baseDir}`
- keep the rules and constraints explicit
- keep the bundle self-contained
FILE:WORKFLOW.md
# Workflow
## Goal | 目标
Mass-produce a consistent runbook-and-skill kit from a validated workflow.
从一个已验证流程批量产出一致的 runbook 与 skill 套件。
## Build Order | 构建顺序
1. create the knowledge doc trio
2. create the skill folder
3. fill in the topic-specific workflow
4. update changelog
5. sync memory
6. publish and verify
## Core Rule | 核心规则
The topic-specific content should change, but the kit structure should remain stable.
主题内容可以变化,但套件结构应尽量稳定。
FILE:agents/openai.yaml
interface:
display_name: "Runbook Skill Kit Factory"
short_description: "Template validated workflows into runbook and skill kits"
default_prompt: "Use $runbook-skill-kit-template-factory to turn this validated workflow into a reusable runbook and self-contained skill kit."
policy:
allow_implicit_invocation: false
Turn a solved workflow into canonical knowledge docs and a self-contained ClawHub-publishable skill. 把一个已验证流程沉淀成规范知识文档,并打包成可发布到 ClawHub 的自包含 skill。
---
name: knowledge-to-clawhub-skill-publisher
description: Turn a solved workflow into canonical knowledge docs and a self-contained ClawHub-publishable skill. 把一个已验证流程沉淀成规范知识文档,并打包成可发布到 ClawHub 的自包含 skill。
homepage: https://docs.openclaw.ai/tools/clawhub
---
# Knowledge to ClawHub Skill Publisher
Use this skill when a workflow has already been proven in practice and should now be captured as durable docs plus a reusable skill.
当一个流程已经在实战中验证过,并且现在需要沉淀成长期文档与可复用 skill 时,使用这个 skill。
## Read First | 先读这些
Review these files before doing any packaging work:
在做打包工作之前,先看这些文件:
- `{baseDir}/README.md`
- `{baseDir}/WORKFLOW.md`
- `{baseDir}/FAQ.md`
- `{baseDir}/CHANGELOG.md`
## Primary Rule | 核心原则
Do not publish a skill from a raw chat transcript. First extract a stable workflow and write canonical docs.
不要直接把原始聊天记录发布成 skill。先提炼稳定流程,再写成规范知识文档。
## Workflow | 执行流程
1. Identify the stable workflow and remove one-off chat noise.
识别稳定流程,去掉一次性聊天噪音。
2. Write canonical docs under `knowledge/runbooks/` if working inside a shared knowledge repository.
如果在共享知识仓库中工作,先把规范文档写到 `knowledge/runbooks/`。
3. Create a self-contained skill folder under `skills/shared/<skill-name>/`.
在 `skills/shared/<skill-name>/` 下创建自包含 skill 目录。
4. Add:
补齐以下文件:
- `SKILL.md`
- `README.md`
- `WORKFLOW.md`
- `FAQ.md`
- `CHANGELOG.md`
5. Keep package references self-contained via `{baseDir}`.
使用 `{baseDir}` 保持包内引用自包含。
6. Publish only after the docs and package are aligned.
只在文档和包内容一致后再发布。
7. Verify the remote package after publish.
发布后验证远端包内容。
## Strong Heuristics | 强判断规则
- if the process is not proven yet, document it as a draft instead of publishing a skill
- if the workflow depends on hidden workspace files, make the package self-contained first
- if `CHANGELOG.md` is missing, add it before the first stable release
- if the audience is mixed, prefer bilingual docs
中文解释:
- 流程还没验证过,就先记成草稿,不要急着发布 skill。
- 如果流程依赖工作区外部文件,先把包改成自包含。
- 如果缺 `CHANGELOG.md`,最好在第一个稳定版本前补上。
- 面向中英文混合用户时,优先双语。
## Safe Commands | 安全命令
```bash
make sync
clawhub publish /absolute/path/to/skill-folder --slug your-skill --name "Your Skill" --version 1.0.0
clawhub inspect your-skill --version 1.0.0 --files
```
## Response Format | 输出格式
Always return:
始终返回:
1. current workflow status
2. missing artifacts
3. next single best action
4. verification after that
## Constraints | 约束
- do not treat ClawHub as the source of truth
- do not publish secrets, machine-specific tokens, or raw transcripts
- keep skill bundles self-contained when possible
- prefer `knowledge/` as the canonical Markdown source
中文约束:
- 不要把 ClawHub 当成事实源。
- 不要发布密钥、机器专属 token,或原始聊天记录。
- 尽量保持 skill 包自包含。
- 优先把 `knowledge/` 作为规范 Markdown 来源。
FILE:CHANGELOG.md
# Changelog
## 1.0.1
- Added `agents/openai.yaml` with UI metadata
- Added display name, short description, and default prompt for skill UIs
## 1.0.0
- Initial public release
- Added bilingual `SKILL.md`
- Added bilingual `README.md`
- Added bilingual `WORKFLOW.md`
- Added bilingual `FAQ.md`
- Added release tracking with `CHANGELOG.md`
FILE:FAQ.md
# FAQ
## Should I start from raw chat logs? | 应该直接从聊天记录开始吗?
No. Extract the stable workflow first.
不要。先提炼稳定流程。
## Should I write the knowledge docs or the skill first? | 先写知识文档还是先写 skill?
Write the canonical knowledge docs first, then package the skill.
先写规范知识文档,再打包 skill。
## Why add `CHANGELOG.md` early? | 为什么要尽早加 `CHANGELOG.md`?
It keeps source and published package aligned from the first release.
这样从第一个发布版本起,源码和发布包就能保持一致。
## Should I always publish to ClawHub? | 是否每次都要发到 ClawHub?
No. Publish only when the workflow is reusable beyond the current local repository.
不一定。只有当这个流程值得在当前仓库之外复用时再发布。
FILE:README.md
# Knowledge to ClawHub Skill Publisher
This skill helps turn a solved workflow into canonical knowledge docs and a self-contained skill bundle that can be published to ClawHub.
这个 skill 用来把一个已验证流程整理成规范知识文档,并打包成可发布到 ClawHub 的自包含 skill。
## What This Skill Is For | 适用场景
Use it when:
适用于:
- a troubleshooting or operations flow has already been proven
- you want to preserve the flow in `knowledge/`
- you want agents to reuse the same flow via a skill
- you want to publish that skill to ClawHub
## What It Produces | 产出物
The skill aims to produce:
这个 skill 目标产出:
- canonical Markdown docs
- a self-contained skill folder
- a changelog-backed release
- an optionally installed local copy under `~/.openclaw/skills/`
## Included Files | 包含文件
- `SKILL.md`
- `README.md`
- `WORKFLOW.md`
- `FAQ.md`
- `CHANGELOG.md`
## Local Use | 本地使用
Place this folder under one of these locations:
把这个目录放到以下任一位置:
- `<workspace>/skills/`
- `~/.openclaw/skills/`
Then start a new OpenClaw session or refresh skills.
然后重新开始一个 OpenClaw 会话,或刷新 skills。
## ClawHub Publish Shape | ClawHub 发布方式
This folder is self-contained so it can be published to ClawHub as a single bundle.
这个目录是自包含的,可以直接作为一个 skill 包发布到 ClawHub。
Example publish command:
发布示例命令:
```bash
clawhub publish ./skills/shared/knowledge-to-clawhub-skill-publisher \
--slug knowledge-to-clawhub-skill-publisher \
--name "Knowledge to ClawHub Skill Publisher" \
--version 1.0.0 \
--tags latest,workflow,knowledge,skill,clawhub
```
FILE:WORKFLOW.md
# Workflow
## Goal | 目标
Turn a solved workflow into durable docs plus a reusable ClawHub-ready skill.
把一个已验证流程转成长期文档和可复用、可发布到 ClawHub 的 skill。
## Steps | 步骤
1. extract the stable workflow
提炼稳定流程
2. write canonical docs in the repository knowledge tree
把规范文档写入仓库知识树
3. build a self-contained skill folder
构建自包含 skill 目录
4. add changelog before publishing
在发布前补好 changelog
5. sync the OpenClaw memory mirror
同步 OpenClaw memory mirror
6. publish to ClawHub
发布到 ClawHub
7. verify files and versions
验证文件和版本
8. optionally install or update locally
按需本地安装或更新
## Rules | 规则
- keep `knowledge/` as source of truth
- do not ship secrets
- do not publish raw transcripts
- prefer bilingual packaging when the audience is mixed
FILE:agents/openai.yaml
interface:
display_name: "Knowledge to ClawHub Publisher"
short_description: "Turn proven workflows into docs and publishable skills"
default_prompt: "Use $knowledge-to-clawhub-skill-publisher to turn this validated workflow into canonical knowledge docs and a publishable ClawHub skill."
policy:
allow_implicit_invocation: false
Diagnose why Telegram forum topics do not reliably route into OpenClaw ACP sessions backed by codex. 诊断 Telegram 话题为何无法稳定进入 OpenClaw 的 codex ACP 会话。
---
name: openclaw-telegram-acp-troubleshooter
description: Diagnose why Telegram forum topics do not reliably route into OpenClaw ACP sessions backed by codex. 诊断 Telegram 话题为何无法稳定进入 OpenClaw 的 codex ACP 会话。
homepage: https://docs.openclaw.ai/tools/clawhub
---
# OpenClaw Telegram ACP Troubleshooter
Use this skill when Telegram group topics are expected to talk directly to an ACP session, but the bot does not reply reliably.
当 Telegram 群组话题本应直接进入 ACP 会话,但机器人回复不稳定时,使用这个 skill。
This skill is designed to be self-contained for workspace use and ClawHub publishing.
这个 skill 设计为可独立打包,既可在本地工作区使用,也可直接发布到 ClawHub。
## Read First | 先读这些
Review these files before concluding anything:
在下结论之前,先看这些文件:
- `{baseDir}/README.md`
- `{baseDir}/TROUBLESHOOTING.md`
- `{baseDir}/FAQ.md`
## Primary Rule | 核心判断
If `/new` works but normal topic text does not, do not blame ACP first. Treat Telegram delivery as the leading suspect until logs prove otherwise.
如果 `/new` 能工作,但普通话题文本不工作,先不要怀疑 ACP。除非日志证明不是,否则优先把 Telegram 投递当成头号嫌疑。
## Workflow | 诊断顺序
1. Confirm the routing target:
确认路由目标:
- group default agent
- topic-level `agentId`
- `requireMention` setting
2. Confirm gateway health:
确认 gateway 健康状态:
- `systemctl --user is-active openclaw-gateway`
- inspect the current gateway log
3. Confirm OpenClaw ingestion:
确认 OpenClaw 是否真的收到了输入:
- watch `~/.openclaw/logs/commands.log`
- inspect the relevant session file under `~/.openclaw/agents/codex/sessions/`
4. Separate the failure class:
区分故障类别:
- token or auth
- duplicate poller
- Telegram not delivering normal text
- routing bound to the wrong agent
5. Recommend the next action in priority order, with evidence.
按概率和证据给出下一步动作。
6. If the user wants durable documentation, point them to:
如果用户想要长期保存文档,指向:
- `{baseDir}/README.md` for usage
- `{baseDir}/TROUBLESHOOTING.md` for the full runbook
- `{baseDir}/FAQ.md` for short operator answers
## Strong Heuristics | 强判断规则
- `/new` works: ACP path is alive.
- `/new` works: ACP path is alive.
- outbound send works: token is valid enough for sends.
- plain text missing from `commands.log`: Telegram delivery problem or upstream filter.
- `409 Conflict`: another poller is active.
- privacy mode was recently changed: re-add the bot before patching code.
中文解释:
- `/new` 能回复:ACP 通路正常。
- 能主动发消息:token 至少对发送是有效的。
- 普通文本没进 `commands.log`:大概率是 Telegram 投递或更上游的过滤问题。
- `409 Conflict`:还有别的轮询器在抢同一个 bot token。
- 刚改过 privacy mode:先移除并重新拉机器人入群,再谈改代码。
## Safe Commands | 安全命令
```bash
systemctl --user show -p MainPID -p ActiveEnterTimestamp openclaw-gateway
tail -f ~/.openclaw/logs/commands.log
tail -f /tmp/openclaw/openclaw-$(date +%F).log
```
## Response Format | 输出格式
Always return:
始终按下面格式返回:
1. current conclusion
2. evidence
3. next single best action
4. what to test after that
## Constraints | 约束
- Do not reveal secret values from env vars, 1Password, or config.
- Do not recommend source patches before Telegram delivery checks are exhausted.
- Prefer topic-level binding for important threads.
- Keep recommendations actionable and ordered by probability.
中文约束:
- 不要泄露 env、1Password 或配置中的密钥值。
- 在 Telegram 投递问题没排干净前,不要先建议改源码。
- 重要话题优先使用 topic 级绑定。
- 建议必须可执行,并按概率高低排序。
FILE:CHANGELOG.md
# Changelog
## 1.0.1
- Added `CHANGELOG.md` to the published skill bundle
- No workflow or diagnostic logic changes
## 1.0.0
- Initial public release
- Added bilingual Chinese and English `SKILL.md`
- Added bilingual `README.md`
- Added bilingual `TROUBLESHOOTING.md`
- Added bilingual `FAQ.md`
- Published to ClawHub under `openclaw-telegram-acp-troubleshooter`
## Versioning notes
- Use semantic versioning
- Patch versions for doc and packaging fixes
- Minor versions for expanded diagnostic flow or new supported platforms
- Major versions for breaking changes in workflow or file layout
FILE:FAQ.md
# FAQ
## Why does `/new` reply but normal text does not? | 为什么 `/new` 能回,但普通文本不行?
Usually because Telegram delivered the command-style update, but did not reliably deliver normal group or topic messages to the bot.
通常是因为 Telegram 投递了命令型更新,但没有稳定投递普通群消息或话题消息给机器人。
## Does `requireMention = false` guarantee replies? | `requireMention = false` 能保证回复吗?
No. It only removes the mention requirement inside OpenClaw routing. Telegram still has to deliver the message update first.
不能。它只是在 OpenClaw 路由里取消必须 `@bot` 的限制,前提仍然是 Telegram 先把消息投递进来。
## If a topic is bound to `codex`, does every message become an ACP `codex` chat? | 话题绑定到 `codex` 后,是否每条消息都会进入 ACP `codex`?
Only if the message reaches OpenClaw.
只有消息真的到达 OpenClaw 才会。
## Is ACP the same as `codex`? | ACP 和 `codex` 是一回事吗?
No. ACP is the conversation and execution path. `codex` is the agent behind that conversation.
不是。ACP 是会话和执行通路,`codex` 是这个会话背后的 agent。
## After disabling privacy mode, do I need to re-add the bot? | 关掉 privacy mode 后,需要重新拉机器人进群吗?
Often yes. In practice, re-adding the bot is one of the most common fixes for this symptom.
很多时候需要。实战里,重新把机器人移出再拉回群,是这个问题最常见的修复动作之一。
## How do I know a message really reached ACP? | 怎么确认消息真的进了 ACP?
Check `~/.openclaw/logs/commands.log` and the matching file in `~/.openclaw/agents/codex/sessions/`.
检查 `~/.openclaw/logs/commands.log`,以及 `~/.openclaw/agents/codex/sessions/` 下对应的 session 文件。
FILE:README.md
# OpenClaw Telegram ACP Troubleshooter
This skill helps diagnose why Telegram forum topics do not reliably reach an OpenClaw ACP session backed by `codex`.
这个 skill 用来诊断 Telegram forum 话题为什么不能稳定进入由 `codex` 驱动的 OpenClaw ACP 会话。
## What This Skill Is For | 适用场景
Use it when:
适用于:
- `/new` replies in a Telegram topic, but normal text does not
- the bot can send outbound messages, but user messages do not trigger replies
- topic routing is configured, but the wrong agent responds
- you need to distinguish Telegram delivery problems from ACP or `codex` problems
中文概括:
- `/new` 有回复,但普通文本没回复
- 机器人能主动发消息,但用户消息不触发回复
- 话题路由已经配置,但回应的 agent 不对
- 需要区分到底是 Telegram 投递问题,还是 ACP / `codex` 执行问题
## What It Checks | 它会检查什么
The skill is built around a practical decision order:
这个 skill 按实战顺序检查:
1. confirm routing and topic binding
2. confirm gateway health
3. confirm whether input reached OpenClaw at all
4. separate Telegram delivery issues from ACP execution issues
5. recommend the next single best fix
## Included Files | 包含文件
- `SKILL.md`: the executable skill instructions
- `TROUBLESHOOTING.md`: the full runbook
- `FAQ.md`: the short operator FAQ
## Typical Conclusion Pattern | 常见结论模式
If `/new` works but plain text does not appear in `commands.log`, the most likely problem is Telegram delivery rather than ACP or `codex`.
如果 `/new` 正常,但普通文本没有进入 `commands.log`,最可能的问题是 Telegram 投递,而不是 ACP 或 `codex`。
## Local Use | 本地使用
Place this folder under one of these locations:
把这个目录放到以下任一位置:
- `<workspace>/skills/`
- `~/.openclaw/skills/`
Then start a new OpenClaw session or refresh skills.
然后重新开始一个 OpenClaw 会话,或刷新 skills。
## ClawHub Publish Shape | ClawHub 发布方式
This folder is self-contained so it can be published to ClawHub as a single skill bundle.
这个目录是自包含的,可以直接作为一个 skill 包发布到 ClawHub。
Example publish command:
发布示例命令:
```bash
clawhub publish ./skills/shared/openclaw-telegram-acp-troubleshooter \
--slug openclaw-telegram-acp-troubleshooter \
--name "OpenClaw Telegram ACP Troubleshooter" \
--version 1.0.0 \
--tags latest,telegram,acp,troubleshooting
```
## Recommended Companion Docs | 配套文档建议
If you are maintaining a shared workspace, also keep the canonical source docs in Markdown under your knowledge tree so they can be versioned and mirrored into OpenClaw memory.
如果你在维护共享工作区,建议同时把规范文档保存在知识库 Markdown 里,这样可以做版本管理,并同步到 OpenClaw memory。
FILE:TROUBLESHOOTING.md
# Troubleshooting
## Goal | 目标
Stabilize OpenClaw replies in Telegram forum topics when the topic is expected to route into an ACP session backed by `codex`.
当 Telegram forum 话题本应路由到由 `codex` 驱动的 ACP 会话时,稳定 OpenClaw 的回复行为。
## Typical Symptoms | 典型症状
- `/new` replies in the topic, but plain text like `111` does not
- the bot can send outbound messages into the topic, but user messages do not produce a reply
- `requireMention = false` is already set, but non-command text still does not reach the agent
- topic routing is configured, but the topic does not behave like a normal ACP chat
## Fast Diagnosis | 快速判断
### Case 1
- `/new` replies
- plain text does not reply
Most likely cause:
- Telegram is not delivering normal topic messages to the bot reliably
- this is usually not an ACP or `codex` problem
最可能原因:
- Telegram 没有稳定把普通话题消息投递给机器人
- 这通常不是 ACP 或 `codex` 本身的问题
### Case 2
- nothing replies
- outbound send also fails
Most likely cause:
- bot token, webhook, gateway, or Telegram connectivity issue
最可能原因:
- bot token、webhook、gateway 或 Telegram 连通性问题
### Case 3
- messages arrive, but the wrong agent answers
Most likely cause:
- routing or default agent binding issue
最可能原因:
- 路由配置或默认 agent 绑定有误
## Required Checks | 必查项
- confirm the group default agent
- confirm topic-level `agentId` binding for important topics
- confirm `requireMention = false`
- confirm ACP default agent is `codex`
## Runtime Checks | 运行态检查
### Gateway Health | 网关状态
```bash
systemctl --user is-active openclaw-gateway
systemctl --user show -p MainPID -p ActiveEnterTimestamp openclaw-gateway
```
### Main Log | 主日志
```bash
tail -f /tmp/openclaw/openclaw-$(date +%F).log
```
Watch for:
重点看:
- `401 Unauthorized`
- `409 Conflict`
- routing load issues
- agent execution failures
### Command Log | 命令日志
```bash
tail -f ~/.openclaw/logs/commands.log
```
Expected topic session key shape:
```text
agent:codex:telegram:group:<group_id>:topic:<topic_id>
```
### Session Proof | 会话证据
Inspect the matching file under:
检查以下目录中的对应 session 文件:
```text
~/.openclaw/agents/codex/sessions/
```
If `/new` appears there but later plain text does not, the issue is upstream of ACP.
如果这里能看到 `/new`,却看不到后续普通文本,问题就在 ACP 上游。
## Telegram-side Checks | Telegram 侧检查
### Privacy Mode | 隐私模式
If privacy mode was previously enabled, disabling it in BotFather may not be enough for an already-joined group.
如果之前开过 privacy mode,只在 BotFather 里关掉,往往不足以让已加入的群立即恢复正常。
Recommended repair:
推荐修复动作:
1. remove the bot from the group
2. re-add the bot after privacy mode is disabled
3. promote the bot to admin if possible
### Polling Conflict | 轮询冲突
If logs show `409 Conflict: terminated by other getUpdates request`, another process is polling the same bot token.
如果日志里出现 `409 Conflict: terminated by other getUpdates request`,说明还有别的进程在轮询同一个 bot token。
Fix:
修复动作:
- stop the duplicate poller
- restart `openclaw-gateway`
### Bot Identity | 机器人身份校验
After token rotation, confirm the running gateway really restarted and is using the intended token and bot identity.
如果你轮换过 token,要确认运行中的 gateway 确实重启过,并且加载的是新的目标 token 和 bot 身份。
## Interpretation Rules | 解释规则
- if `/new` works, ACP is working
- if outbound `sendMessage` works, the token is valid enough for sends
- if plain text never appears in `commands.log`, the message did not stably enter OpenClaw
- `requireMention = false` only matters after Telegram delivers the update
## Repair Order | 修复顺序
1. verify gateway is active
2. verify `/new` in the topic
3. watch `commands.log`
4. send plain text such as `111`
5. if plain text does not appear in logs, remove and re-add the bot
6. promote the bot to admin if possible
7. re-test plain text in the same topic
8. only after that, consider source-level Telegram plugin investigation
## Verification Checklist | 验证清单
- `/new` replies in the topic
- plain text replies without `@bot`
- `commands.log` records the topic session
- the matching `codex` session file contains the user text
- no `401 Unauthorized`
- no `409 Conflict`
Inspect, trigger, and clean up GitHub mirror repositories that use a safe-sync GitHub Actions workflow. Use when Codex needs to work on repository mirroring...
---
name: github-safe-sync
description: "Inspect, trigger, and clean up GitHub mirror repositories that use a safe-sync GitHub Actions workflow. Use when Codex needs to work on repository mirroring or sync automation tasks such as: (1) checking whether a mirror repo is up to date with its upstream, (2) triggering `safe-sync.yml` manually, (3) auditing recent workflow runs, backup branches, or force-push alert issues, (4) closing false-positive sync issues, or (5) deleting stale `backup/` branches after a sync fix."
---
# GitHub Safe Sync
Use this skill for repositories that mirror an upstream GitHub repo and preserve local `.github` workflow files with a `safe-sync.yml` workflow.
## Requirements
- Set `GITHUB_TOKEN` before running the script.
- Pass repositories as `owner/repo`.
- Treat tokens as secrets. Do not write them into the skill or commit them into repo files.
## Quick Start
Inspect a mirror repo:
```bash
export GITHUB_TOKEN=...
./scripts/github_safe_sync.py status \
--owner grey00758 \
--repo ai-code-board \
--upstream grey0758/ai-code-board
```
Trigger a manual sync:
```bash
export GITHUB_TOKEN=...
./scripts/github_safe_sync.py dispatch \
--owner grey00758 \
--repo ai-code-board
```
Clean false-positive artifacts after a workflow fix:
```bash
export GITHUB_TOKEN=...
./scripts/github_safe_sync.py close-force-push-issues \
--owner grey00758 \
--repo ai-code-board
./scripts/github_safe_sync.py delete-backups \
--owner grey00758 \
--repo ai-code-board
```
## Workflow
1. Inspect the mirror repository with `status`.
2. If the workflow is inactive or the latest run failed, review the repo before dispatching anything.
3. If the mirror should sync now, run `dispatch`.
4. If a workflow fix stopped false-positive force-push alerts, run `close-force-push-issues` and `delete-backups`.
5. Re-run `status` to verify the repo is clean.
## Interpreting `status`
- `effective_state=exact`: Mirror and upstream branch heads are identical.
- `effective_state=metadata-ahead`: Mirror is only ahead by local `.github`-only commits. This is normally healthy for safe-sync mirrors.
- `effective_state=behind`: Upstream has newer commits and the mirror has not caught up yet.
- `effective_state=local-ahead`: Mirror has non-metadata commits that do not exist upstream. Inspect before forcing anything.
- `effective_state=metadata-diverged`: Histories differ, but the mirror-only side is metadata-only. This often means the sync workflow logic still needs review.
- `effective_state=diverged`: Mirror and upstream both changed in incompatible ways. Treat this as a real sync problem until proven otherwise.
## Commands
### `status`
Use `status` first. It returns JSON with:
- workflow metadata
- latest workflow runs
- count of open force-push alert issues
- count of `backup/` branches
- optional upstream branch comparison
If the requested upstream branch does not exist, the script falls back to the upstream repo default branch.
### `dispatch`
Use `dispatch` to trigger `workflow_dispatch` on the sync workflow. Add `--force-sync` only when you intentionally want the workflow to ignore the normal no-op path.
### `close-force-push-issues`
Use this only after you have confirmed the force-push alerts were false positives. It closes open issues whose title contains `检测到上游强制推送`.
### `delete-backups`
Use this only after you have confirmed the backup branches are noise. Start with `--dry-run` if you want to preview what would be removed.
## Safety Rules
- Do not close force-push alert issues until you have verified the alert was false.
- Do not delete `backup/` branches until the mirror workflow is healthy and the backups are no longer needed.
- Do not assume `main`; pass `--branch` or `--upstream` explicitly when the repo uses a different upstream default branch.
- Re-run `status` after every write operation.
FILE:agents/openai.yaml
interface:
display_name: "GitHub Safe Sync"
short_description: "Inspect and operate GitHub mirror sync workflows."
default_prompt: "Use $github-safe-sync to inspect or operate a GitHub mirror sync workflow for a repository."
FILE:scripts/github_safe_sync.py
#!/usr/bin/env python3
"""Operate GitHub mirror repositories that use a safe-sync workflow.
The script expects a GitHub token in the environment:
GITHUB_TOKEN=... ./scripts/github_safe_sync.py status --owner ... --repo ...
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import urllib.error
import urllib.parse
import urllib.request
from typing import Any
API_ROOT = "https://api.github.com"
DEFAULT_WORKFLOW = "safe-sync.yml"
FORCE_PUSH_TITLE = "检测到上游强制推送"
def api_request(
path: str,
token: str,
method: str = "GET",
body: dict[str, Any] | None = None,
) -> Any:
url = path if path.startswith("http") else f"{API_ROOT}{path}"
data = None
if body is not None:
data = json.dumps(body).encode("utf-8")
req = urllib.request.Request(url, data=data, method=method)
req.add_header("Authorization", f"Bearer {token}")
req.add_header("Accept", "application/vnd.github+json")
if body is not None:
req.add_header("Content-Type", "application/json")
try:
with urllib.request.urlopen(req) as resp:
raw = resp.read()
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", "replace")
raise SystemExit(f"GitHub API error {exc.code} for {url}: {detail}") from exc
except urllib.error.URLError as exc:
raise SystemExit(f"Network error for {url}: {exc}") from exc
if not raw:
return None
return json.loads(raw.decode("utf-8"))
def paginate(path: str, token: str) -> list[dict[str, Any]]:
page = 1
items: list[dict[str, Any]] = []
while True:
separator = "&" if "?" in path else "?"
data = api_request(f"{path}{separator}per_page=100&page={page}", token)
if not isinstance(data, list) or not data:
return items
items.extend(data)
page += 1
def get_token() -> str:
token = os.environ.get("GITHUB_TOKEN", "").strip()
if not token:
raise SystemExit("Set GITHUB_TOKEN before running this script.")
return token
def metadata_only_files(files: list[str]) -> bool:
return bool(files) and all(f.startswith(".github/") for f in files)
def fetch_commit_files(owner: str, repo: str, sha: str, token: str) -> list[str]:
data = api_request(f"/repos/{owner}/{repo}/commits/{sha}", token)
return [item["filename"] for item in data.get("files", [])]
def range_is_metadata_only(owner: str, repo: str, base: str | None, head: str, token: str) -> bool:
if base:
data = api_request(f"/repos/{owner}/{repo}/compare/{base}...{head}", token)
commits = data.get("commits", [])
else:
data = api_request(f"/repos/{owner}/{repo}/commits?sha={head}&per_page=100", token)
commits = data
if not commits:
return False
for commit in commits:
sha = commit["sha"]
files = fetch_commit_files(owner, repo, sha, token)
if not metadata_only_files(files):
return False
return True
def compare_branches(
mirror_owner: str,
mirror_repo: str,
upstream_owner: str,
upstream_repo: str,
branch: str,
token: str,
) -> dict[str, Any]:
mirror_branch = api_request(f"/repos/{mirror_owner}/{mirror_repo}/branches/{branch}", token)
mirror_sha = mirror_branch["commit"]["sha"]
try:
upstream_branch = api_request(f"/repos/{upstream_owner}/{upstream_repo}/branches/{branch}", token)
upstream_branch_name = branch
except SystemExit:
upstream_repo_meta = api_request(f"/repos/{upstream_owner}/{upstream_repo}", token)
upstream_branch_name = upstream_repo_meta["default_branch"]
upstream_branch = api_request(
f"/repos/{upstream_owner}/{upstream_repo}/branches/{upstream_branch_name}",
token,
)
upstream_sha = upstream_branch["commit"]["sha"]
compare = api_request(
f"/repos/{mirror_owner}/{mirror_repo}/compare/{upstream_sha}...{mirror_sha}",
token,
)
ahead_by = compare.get("ahead_by", 0)
behind_by = compare.get("behind_by", 0)
status = compare.get("status")
merge_base = compare.get("merge_base_commit", {}).get("sha")
effective = "diverged"
if behind_by == 0 and ahead_by == 0:
effective = "exact"
elif behind_by > 0 and ahead_by == 0:
effective = "behind"
elif ahead_by > 0 and behind_by == 0:
if merge_base and range_is_metadata_only(mirror_owner, mirror_repo, merge_base, mirror_sha, token):
effective = "metadata-ahead"
else:
effective = "local-ahead"
else:
if merge_base and range_is_metadata_only(mirror_owner, mirror_repo, merge_base, mirror_sha, token):
effective = "metadata-diverged"
return {
"mirror_branch": branch,
"upstream_branch": upstream_branch_name,
"mirror_sha": mirror_sha,
"upstream_sha": upstream_sha,
"compare_status": status,
"ahead_by": ahead_by,
"behind_by": behind_by,
"effective_state": effective,
}
def cmd_status(args: argparse.Namespace) -> int:
token = get_token()
repo_path = f"/repos/{args.owner}/{args.repo}"
workflows = api_request(f"{repo_path}/actions/workflows", token).get("workflows", [])
workflow = next((w for w in workflows if w["path"].endswith(args.workflow)), None)
runs = api_request(f"{repo_path}/actions/runs?per_page=5", token).get("workflow_runs", [])
backup_branches = [
item["name"]
for item in paginate(f"{repo_path}/branches", token)
if item["name"].startswith(args.backup_prefix)
]
open_issues = [
item
for item in paginate(f"{repo_path}/issues?state=open", token)
if FORCE_PUSH_TITLE in item.get("title", "") and "pull_request" not in item
]
result: dict[str, Any] = {
"repository": f"{args.owner}/{args.repo}",
"workflow": workflow and {
"id": workflow["id"],
"name": workflow["name"],
"state": workflow["state"],
"path": workflow["path"],
},
"latest_runs": [
{
"id": run["id"],
"event": run["event"],
"status": run["status"],
"conclusion": run.get("conclusion"),
"created_at": run["created_at"],
"html_url": run["html_url"],
}
for run in runs
],
"open_force_push_issue_count": len(open_issues),
"backup_branch_count": len(backup_branches),
"backup_branch_examples": backup_branches[:10],
}
if args.upstream:
upstream_owner, upstream_repo = args.upstream.split("/", 1)
result["branch_comparison"] = compare_branches(
args.owner,
args.repo,
upstream_owner,
upstream_repo,
args.branch,
token,
)
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0
def cmd_dispatch(args: argparse.Namespace) -> int:
token = get_token()
body = {"ref": args.ref, "inputs": {"force_sync": bool(args.force_sync)}}
api_request(
f"/repos/{args.owner}/{args.repo}/actions/workflows/{args.workflow}/dispatches",
token,
method="POST",
body=body,
)
print(json.dumps({"ok": True, "repository": f"{args.owner}/{args.repo}", "workflow": args.workflow}))
return 0
def cmd_close_force_push_issues(args: argparse.Namespace) -> int:
token = get_token()
repo_path = f"/repos/{args.owner}/{args.repo}"
issues = [
item
for item in paginate(f"{repo_path}/issues?state=open", token)
if FORCE_PUSH_TITLE in item.get("title", "") and "pull_request" not in item
]
closed: list[int] = []
for issue in issues[: args.limit]:
api_request(
f"{repo_path}/issues/{issue['number']}",
token,
method="PATCH",
body={"state": "closed"},
)
closed.append(issue["number"])
print(json.dumps({"closed_count": len(closed), "issue_numbers": closed}, ensure_ascii=False, indent=2))
return 0
def cmd_delete_backups(args: argparse.Namespace) -> int:
token = get_token()
repo_path = f"/repos/{args.owner}/{args.repo}"
branches = [
item["name"]
for item in paginate(f"{repo_path}/branches", token)
if item["name"].startswith(args.backup_prefix)
]
deleted: list[str] = []
for name in branches[: args.limit]:
if args.dry_run:
deleted.append(name)
continue
encoded = urllib.parse.quote(f"heads/{name}", safe="")
api_request(f"{repo_path}/git/refs/{encoded}", token, method="DELETE")
deleted.append(name)
print(json.dumps({"matched_count": len(branches), "processed_count": len(deleted), "branches": deleted}, ensure_ascii=False, indent=2))
return 0
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest="command", required=True)
status = sub.add_parser("status", help="Inspect workflow, runs, issues, backups, and optional upstream comparison.")
status.add_argument("--owner", required=True)
status.add_argument("--repo", required=True)
status.add_argument("--workflow", default=DEFAULT_WORKFLOW)
status.add_argument("--branch", default="main")
status.add_argument("--upstream", help="Optional upstream repository in owner/repo form.")
status.add_argument("--backup-prefix", default="backup/")
status.set_defaults(func=cmd_status)
dispatch = sub.add_parser("dispatch", help="Trigger a workflow_dispatch run.")
dispatch.add_argument("--owner", required=True)
dispatch.add_argument("--repo", required=True)
dispatch.add_argument("--workflow", default=DEFAULT_WORKFLOW)
dispatch.add_argument("--ref", default="main")
dispatch.add_argument("--force-sync", action="store_true")
dispatch.set_defaults(func=cmd_dispatch)
close_issues = sub.add_parser("close-force-push-issues", help="Close open force-push alert issues.")
close_issues.add_argument("--owner", required=True)
close_issues.add_argument("--repo", required=True)
close_issues.add_argument("--limit", type=int, default=500)
close_issues.set_defaults(func=cmd_close_force_push_issues)
delete_backups = sub.add_parser("delete-backups", help="Delete backup/* branches.")
delete_backups.add_argument("--owner", required=True)
delete_backups.add_argument("--repo", required=True)
delete_backups.add_argument("--backup-prefix", default="backup/")
delete_backups.add_argument("--limit", type=int, default=1000)
delete_backups.add_argument("--dry-run", action="store_true")
delete_backups.set_defaults(func=cmd_delete_backups)
return parser
def main() -> int:
parser = build_parser()
args = parser.parse_args()
return args.func(args)
if __name__ == "__main__":
sys.exit(main())