Add OpenClaw skills, platform kit, and template docs

Made-with: Cursor
This commit is contained in:
2026-04-04 10:35:02 +08:00
parent e37b03c00f
commit 35f4758da2
83 changed files with 8971 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# 打 tag如 v1.0.2)后触发:加密打包、调用平台 API 同步元数据(入库)、上传制品、清理旧版本。
# 逻辑在 admin/jiangchang-platform-kit 的 reusable-release-skill.yaml 中。
name: 头条技能发布
on:
push:
tags: ["v*"]
jobs:
release:
uses: admin/jiangchang-platform-kit/.github/workflows/reusable-release-skill.yaml@main
with:
artifact_platform: windows
pyarmor_platform: windows.x86_64

8
toutiao-publisher/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
__pycache__/
*.py[cod]
*.pyo
.Python
.venv/
venv/
.env
.env.*

View File

@@ -0,0 +1,40 @@
# 头条号批量发布toutiao-publisher
头条号批量发布技能仓库;当前为**占位阶段**,仅含 CLI 骨架与文档,**发布功能待后续迭代**。
## 目录一览
| 路径 | 作用 |
|------|------|
| `SKILL.md` | 技能清单YAML 头 + Markdown 正文) |
| `release.ps1` | 本地一键发布(依赖与 `jiangchang-platform-kit` 并列,见 `account-manager` 同款) |
| `scripts/skill_main.py` | CLI 入口:`health` / `version` |
| `docs/` | 运行时与可移植性说明 |
| `optional/` | 可选片段路径、SQLite 等),默认不引用 |
## 本地试跑
```bash
python scripts/skill_main.py health
python scripts/skill_main.py version
```
## 版本
`SKILL.md``version` 字段对齐更新。
## Git 远程(避免推到模板仓)
`skill-template` 克隆的目录,默认 `origin` 仍指向模板仓库。本技能应使用独立仓库,例如:
```text
http://120.25.191.12:3000/admin/toutiao-publisher.git
```
在 Gitea 上**新建同名空仓库**后执行:
```bash
git remote set-url origin http://120.25.191.12:3000/admin/toutiao-publisher.git
git push -u origin main
git push origin v1.0.1 # 若本地已有 tag 且需同步
```

View File

@@ -0,0 +1,52 @@
---
# ---------------------------------------------------------------------------
# 技能清单Skill Manifest
# ---------------------------------------------------------------------------
name: 头条号批量发布
description: 头条号批量发布技能(骨架阶段:仅健康检查与版本;发布逻辑待实现)。
version: 0.1.0
author: 深圳匠厂科技有限公司
metadata:
openclaw:
slug: toutiao-publisher
emoji: "📰"
category: "内容发布"
skill:
slug: toutiao-publisher
emoji: "📰"
category: "内容发布"
allowed-tools:
- bash
---
# 头条号批量发布toutiao-publisher
## 使用时机
- 用户需要**在头条号侧批量或自动化发布内容**时(具体话术与流程待业务实现后补充)。
## 执行步骤
### 健康检查
```bash
python3 {baseDir}/scripts/skill_main.py health
```
### 查看版本
```bash
python3 {baseDir}/scripts/skill_main.py version
```
### 发布与其它子命令
实现中:后续将在 `scripts/` 下增加发布入口,并在此文档补充命令与参数说明。
## 环境依赖
详见本仓库 `docs/RUNTIME.md``CLAW_DATA_ROOT``CLAW_USER_ID` 等)。
## 数据与隐私
本技能若产生持久化数据,应仅写入 `{CLAW_DATA_ROOT}/{CLAW_USER_ID}/toutiao-publisher/` 下;不得将用户数据提交到版本库。

View File

@@ -0,0 +1,56 @@
# 多宿主(多 Claw可移植性说明
## 设计目标
同一套技能仓库应能在**不同 Claw 实现**下工作,只要宿主满足本文档的**最小契约**:能启动进程、传入环境变量、并把 `SKILL.md` 提供给编排层阅读。
## 行业上常见的「技能包」形态
- **声明式清单**Markdown + YAML 头):描述名称、描述、版本、工具权限、触发场景。
- **可执行入口**:一至多个脚本/二进制,由宿主通过 `bash` / `python` 等调用。
- **用户数据与代码分离**:持久化数据落在用户可写目录,不写在安装目录内。
本仓库按上述惯例组织;具体宿主如何解析 `SKILL.md` 的 YAML 键名,以各宿主文档为准。
## 标识技能:`metadata.skill`
`SKILL.md` 中使用 **`metadata.skill.slug`**(及 `metadata.openclaw.slug`)作为**可移植**的机器可读标识(短横线命名,如 `toutiao-publisher`)。
若你的宿主仍要求其它键名(例如历史实现里的嵌套字段),请在宿主侧做**映射**,或在 `SKILL.md` 中**并列声明**两组 metadata保持 `slug` 值一致)。不要在业务代码里写死某一宿主品牌名。
## 环境变量:推荐前缀 `CLAW_*`
为减少对单一产品名的耦合,文档与可选片段推荐使用:
| 变量 | 含义 |
|------|------|
| `CLAW_DATA_ROOT` | 用户数据根目录(多技能共享的上一级) |
| `CLAW_USER_ID` | 当前工作空间或用户标识,用于数据隔离 |
| `CLAW_SKILLS_ROOT` | 可选;多个技能并排安装时的根目录,便于 `subprocess` 调用兄弟技能 |
宿主若已使用其它名称,推荐在**启动子进程时**注入别名,例如:
- 将宿主内部的「数据根」映射为 `CLAW_DATA_ROOT`
- 将宿主内部「用户 ID」映射为 `CLAW_USER_ID`
这样技能脚本无需分支判断宿主品牌。
## 路径布局约定(逻辑路径)
`CLAW_DATA_ROOT``CLAW_USER_ID` 可用时,本技能推荐将私有数据放在:
```text
{CLAW_DATA_ROOT}/{CLAW_USER_ID}/{skill_slug}/
```
其中 `skill_slug``SKILL.md``metadata.skill.slug` 一致。若环境变量缺失,`optional/paths_snippet.py` 中提供了**仅用于开发机**的 fallback见该文件注释生产环境应由宿主注入变量。
## 发布与制品
不同组织对加密、签名、制品格式要求不同。`.github/workflows/release_skill.yaml` 仅作占位:**务必替换**为你们自己的复用工作流或删除。
## 自检
- [ ] `SKILL.md``slug` 与目录名/制品名策略是否与宿主一致
- [ ] 宿主文档要求的环境变量是否已全部注入
- [ ] 是否在文档中说明了「未注入变量时的行为」(拒绝运行 / 本地 fallback

View File

@@ -0,0 +1,55 @@
# 运行时契约(环境变量与目录)
本文档定义技能进程**建议依赖**的外部条件,便于不同 Claw 宿主统一接入。业务技能应**读取环境变量**,而不是在代码里写死路径或用户名。
## 必需程度说明
- **强烈建议**:生产环境由宿主注入;技能应对缺失给出明确错误提示,避免静默写到意外目录。
- **可选**:没有时技能仍可部分运行(例如只读 `health`)。
## 变量一览
### `CLAW_DATA_ROOT`(强烈建议)
用户数据根。多个技能、多个用户的数据都在此根之下分区。
- 典型场景:组织策略指定的盘符路径或 `~/.your-org-data`
- 未设置时:技能**不应**猜测网络盘;开发机 fallback 仅限 `optional/paths_snippet.py` 中说明的情形。
### `CLAW_USER_ID`(强烈建议)
当前会话所代表的用户或工作空间 ID字符串。与数据隔离强相关。
- 用于拼接:`{CLAW_DATA_ROOT}/{CLAW_USER_ID}/{skill_slug}/`
- 未设置时:可用匿名占位(如 `_anon`**仅用于开发**,生产应显式注入。
### `CLAW_SKILLS_ROOT`(可选)
多个技能以并列目录安装时的根路径,例如:
```text
{CLAW_SKILLS_ROOT}/skill-a/scripts/...
{CLAW_SKILLS_ROOT}/skill-b/scripts/...
```
编排型技能若需要通过子进程调用兄弟技能,应基于该变量定位脚本,避免写死绝对路径。
## 本技能推荐的数据目录
```text
{CLAW_DATA_ROOT}/{CLAW_USER_ID}/{skill_slug}/
```
- `skill_slug`:与 `SKILL.md``metadata.skill.slug` 一致。
- 在此目录下可放置 SQLite 文件、缓存、上传临时文件等;**不要**向版本库提交该目录内容。
## 标准输出约定(建议)
为便于宿主与自动化解析,建议:
- 致命错误:单行前缀 `ERROR:`,例如 `ERROR:MISSING_ENV_CLAW_DATA_ROOT`
- 成功:人类可读一行或多行;若有机读需求,可用 `JSON` 单行输出并在 `SKILL.md` 中说明格式。
## 与具体宿主的关系
若某宿主文档规定了另一套变量名,应在**宿主启动技能子进程时**注入为本文档中的 `CLAW_*` 名称,或在技能内使用一层极薄的 `getenv` 封装(见 `optional/paths_snippet.py` 注释示例)。**不要在业务模块中散落多套变量名判断。**

View File

@@ -0,0 +1,33 @@
# 技能形态与自检清单(无业务)
开发前先选定形态,避免把「编排、存储、浏览器」混在一个脚本里难以测试。
## 类型 A无状态工具型
- **特征**:不持久化用户数据,或只读配置文件;输入输出主要在 stdin/stdout。
- **数据目录**:通常不需要 `CLAW_DATA_ROOT` 下的专属库;若需要缓存,仍建议放在契约目录下。
- **自检**:离线可跑;`health` 不访问网络也可成功。
## 类型 B本地持久化型
- **特征**:使用 SQLite、本地文件等保存用户数据。
- **数据目录**:必须使用 `{CLAW_DATA_ROOT}/{CLAW_USER_ID}/{skill_slug}/`
- **自检**:首次运行自动建库/建表;文档中说明库文件路径与备份方式。
## 类型 C编排型调用其它技能或外部 CLI
- **特征**:自身逻辑薄,主要 `subprocess` 或 HTTP 调用其它组件。
- **依赖**:在 `SKILL.md` 中明确写出**先决条件**(兄弟技能已安装、某 CLI 在 PATH 等)。
- **自检**`health` 可检查兄弟可执行文件是否存在;缺失时打印清晰错误。
## 类型 D混合型
- **特征**:既有本地存储,又调用外部能力。
- **建议**:拆模块(存储 / 编排 / 领域逻辑),入口脚本只做参数解析与调度。
## 发布前通用自检
- [ ] `SKILL.md` 中触发条件与示例命令与实际入口一致
- [ ] 未注入 `CLAW_DATA_ROOT` / `CLAW_USER_ID` 时行为已文档化
- [ ] 不向仓库提交用户数据、密钥、大型二进制
- [ ] 错误信息包含「如何修复」(缺什么环境变量、缺哪个依赖)

View File

@@ -0,0 +1,17 @@
# optional/ 目录说明
本目录下的文件**不会**被 `scripts/skill_main.py` 自动引用。
## 为什么要单独放
- 避免可选片段拖慢最小 `health` 起步,按需再复制进业务代码。
- 需要时**整文件复制**到 `scripts/` 或你们自己的包路径下,再按文件头注释改名、改常量。
## 文件列表
| 文件 | 用途 |
|------|------|
| `paths_snippet.py` | `CLAW_*` 数据目录解析与 fallback 说明 |
| `sqlite_minimal.py` | 无业务含义的 SQLite 建表示例 |
本技能数据子目录与 `SKILL_SLUG``toutiao-publisher`;若复制片段到其它项目请改为对应 slug 与表名。

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
"""
可选片段:路径与环境变量(请复制到 scripts/ 或作为独立模块使用)
================================================================
【用途】
统一解析「用户数据根」「用户 ID」「本技能私有目录」避免在业务代码里重复 os.getenv。
【使用步骤】
1. 将本文件复制到 skills/your-slug/scripts/paths.py或任意模块名
2. 把 SKILL_SLUG 改为与 SKILL.md 中 metadata.skill.slug 一致。
3. 在业务代码中: from paths import get_skill_data_dir (按实际包路径调整)
【可移植性】
- 优先读取标准名 CLAW_*(见 docs/RUNTIME.md
- 若你的组织在宿主侧仍使用历史变量名,可在此文件 _aliases 列表中追加 (标准名, 备选名)
由宿主注入其一即可;不要在业务里再写第三套名字。
【注意】
- 未设置 CLAW_DATA_ROOT 时的 fallback 仅适合开发机;生产环境应由宿主注入。
"""
from __future__ import annotations
import os
import sys
# TODO: 复制本文件后改为你的 slug与 SKILL.md metadata.skill.slug 一致)
SKILL_SLUG = "toutiao-publisher"
def _getenv_first(names: tuple[str, ...]) -> str:
"""按顺序读取多个环境变量名,返回第一个非空值。"""
for n in names:
v = (os.getenv(n) or "").strip()
if v:
return v
return ""
def get_data_root() -> str:
"""
用户数据根目录。
顺序CLAW_DATA_ROOT → (可选)宿主别名,见下方元组。
若皆空Windows 默认 D:\\claw-data其它系统默认 ~/.claw-data —— 仅开发便利,生产请注入 CLAW_DATA_ROOT。
"""
root = _getenv_first(
(
"CLAW_DATA_ROOT",
# 在此追加组织内别名,例如 "MYORG_USER_DATA_ROOT",
)
)
if root:
return root
if sys.platform == "win32":
return r"D:\claw-data"
return os.path.join(os.path.expanduser("~"), ".claw-data")
def get_user_id() -> str:
"""当前用户或工作空间 ID未设置时用 _anon仅开发"""
uid = _getenv_first(
(
"CLAW_USER_ID",
# 在此追加别名,例如 "MYORG_WORKSPACE_ID",
)
)
return uid or "_anon"
def get_skill_data_dir() -> str:
"""
本技能可写目录:{数据根}/{用户ID}/{skill_slug}/
会自动 os.makedirs(..., exist_ok=True)。
"""
path = os.path.join(get_data_root(), get_user_id(), SKILL_SLUG)
os.makedirs(path, exist_ok=True)
return path
def get_skills_root() -> str:
"""
可选:并列安装的多技能根目录。
未设置 CLAW_SKILLS_ROOT 时,默认使用本文件所在仓库的上一级目录的上一级
(即:.../toutiao-publisher/optional/ → 仅作开发时并列技能推断参考)。
"""
root = _getenv_first(("CLAW_SKILLS_ROOT",))
if root:
return root
# optional/ 下:仓库根为 dirname(dirname(__file__))
here = os.path.dirname(os.path.abspath(__file__))
return os.path.dirname(here)

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
"""
可选片段:最小 SQLite 示例(请复制后按需修改)
==============================================
【用途】
演示「单表 + 自增主键 + INTEGER 时间戳」的一种严谨写法;与具体业务无关。
【使用步骤】
1. 复制到 scripts/db_example.py或并入你的模块
2. 修改 TABLE_SQL 中的表名与字段;保持时间字段为 INTEGER Unix 秒UTC若需跨时区一致。
3. 在入口脚本中仅在需要持久化时 import。
【注意】
- 本示例不做迁移兼容schema 变更请用你们组织的迁移策略。
- 数据库文件路径建议get_skill_data_dir() / "skill.db"paths_snippet 中函数)。
"""
from __future__ import annotations
import sqlite3
import time
# 示例表:与任何业务无关
TABLE_SQL = """
CREATE TABLE IF NOT EXISTS skill_audit (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 自增主键
action TEXT NOT NULL, -- 动作标识,如 health_ok
created_at INTEGER NOT NULL -- Unix 秒 UTC
);
"""
def connect(db_path: str) -> sqlite3.Connection:
return sqlite3.connect(db_path)
def init_db(conn: sqlite3.Connection) -> None:
conn.execute(TABLE_SQL)
conn.commit()
def record_action(conn: sqlite3.Connection, action: str) -> None:
conn.execute(
"INSERT INTO skill_audit (action, created_at) VALUES (?, ?)",
(action, int(time.time())),
)
conn.commit()

View File

@@ -0,0 +1,23 @@
[CmdletBinding()]
param(
[string]$Prefix = "v",
[string]$Message = "正式发布",
[switch]$AutoCommit,
[switch]$RequireClean,
[string]$CommitMessage,
[switch]$DryRun
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$sharedScript = Join-Path $scriptDir "..\jiangchang-platform-kit\tools\release.ps1"
$sharedScript = [System.IO.Path]::GetFullPath($sharedScript)
if (-not (Test-Path $sharedScript)) {
throw "Shared release script not found: $sharedScript"
}
& $sharedScript @PSBoundParameters
exit $LASTEXITCODE

View File

@@ -0,0 +1,83 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
头条号发布技能 — CLI 入口
========================
【职责】
- 作为宿主调用时的统一 CLI 入口子进程、终端、CI 均可)。
- 只做:参数解析、环境检查、分发到具体子命令;复杂逻辑放到同目录其它模块。
【如何扩展】
1. 在 main() 的 dispatch 字典中增加 "your_cmd": handler 项。
2. 实现 handler(argv) 或 handler();出错时打印 ERROR: 前缀信息并 sys.exit(非0)。
3. 在仓库根目录 SKILL.md「执行步骤」中补充示例命令。
【多宿主注意】
- 不要在本文件写死某一品牌宿主名。
- 路径与环境变量约定见 ../docs/RUNTIME.md可选辅助代码见 ../optional/paths_snippet.py需自行复制或 import 路径按项目调整)。
【编码】
Windows 下若宿主仍使用系统默认编码,可在宿主侧设置 UTF-8此处不强制改 sys.stdout避免与宿主捕获冲突
"""
from __future__ import annotations
import argparse
import json
import sys
from typing import Callable, Dict, List, Optional
# 与 SKILL.md 中 metadata.openclaw.slug / metadata.skill.slug 保持一致
SKILL_SLUG = "toutiao-publisher"
def cmd_version(_args: argparse.Namespace) -> int:
"""打印版本信息(与 SKILL.md frontmatter 中 version 应对齐,此处为占位)。"""
payload = {
"skill_slug": SKILL_SLUG,
"version": "0.1.0",
"entry": "skill_main.py",
}
print(json.dumps(payload, ensure_ascii=False))
return 0
def cmd_health(_args: argparse.Namespace) -> int:
"""
健康检查:应快速、可离线(除非技能本身强依赖网络)。
失败时打印 ERROR: 前缀,便于宿主与自动化解析。
"""
# 示例:检查 Python 版本(可按需改为检查关键依赖 import
if sys.version_info < (3, 9):
print("ERROR:PYTHON_VERSION need >= 3.9", file=sys.stderr)
return 1
print(f"OK skill={SKILL_SLUG} python={sys.version.split()[0]}")
return 0
def build_parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(
description="toutiao-publisher — Toutiao batch publish skill CLI (skeleton).",
)
sub = p.add_subparsers(dest="command", required=True)
sp = sub.add_parser("version", help="Print version JSON.")
sp.set_defaults(handler=cmd_version)
sp = sub.add_parser("health", help="Quick health check.")
sp.set_defaults(handler=cmd_health)
return p
def main(argv: Optional[List[str]] = None) -> int:
argv = argv if argv is not None else sys.argv[1:]
parser = build_parser()
args = parser.parse_args(argv)
handler: Callable[[argparse.Namespace], int] = args.handler
return handler(args)
if __name__ == "__main__":
raise SystemExit(main())