Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f973208fe3 | |||
| 5bc3ce6810 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ __pycache__/
|
|||||||
venv/
|
venv/
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
.claude/
|
||||||
41
README.md
41
README.md
@@ -1,23 +1,40 @@
|
|||||||
# 头条号批量发布(toutiao-publisher)
|
# Claw 技能项目模板(通用)
|
||||||
|
|
||||||
基于团队 **skill-template** 骨架的头条号发布技能仓库;当前为**占位阶段**,仅含 CLI 骨架与文档,**发布功能待后续迭代**。
|
本目录是一个**与具体业务无关**的技能(Skill)工程骨架,供团队在各类 **Claw / Agent 宿主**(桌面端、网关、IDE 插件等)上交付可安装技能包时参考。
|
||||||
|
|
||||||
|
## 你从这里能得到什么
|
||||||
|
|
||||||
|
- **行业标准对齐**:技能以「清单文件 + 可执行入口 + 文档」组织——与常见的 Agent Skill、CLI 工具包、内部自动化脚本仓库的惯例一致;不绑定某一厂商私有协议。
|
||||||
|
- **可移植约定**:数据目录、用户隔离、兄弟技能路径等通过**环境变量契约**描述(见 `docs/RUNTIME.md`);不同宿主只需注入同名变量或做一层别名映射。
|
||||||
|
- **低学习成本**:每个文件顶部与关键步骤都有注释;按下面顺序做即可跑通第一个命令。
|
||||||
|
|
||||||
|
## 建议的上手顺序(约 15~30 分钟)
|
||||||
|
|
||||||
|
1. **复制本模板**为新仓库或新目录,全局把占位符 `your-skill-slug` / `Your Skill Display Name` 换成你的技能标识(与 `SKILL.md` 里 `metadata.skill.slug` 一致)。
|
||||||
|
2. **阅读** `docs/RUNTIME.md`,确认你的宿主会注入哪些环境变量;若宿主使用另一套名字,在宿主侧做映射,或改 `optional/paths_snippet.py` 中的读取顺序(文件内有说明)。
|
||||||
|
3. **本地试跑**:`python scripts/skill_main.py health` 应输出成功信息。
|
||||||
|
4. **扩展子命令**:在 `scripts/skill_main.py` 的 `dispatch` 中增加分支;业务逻辑放在同目录其它模块或子包中,保持入口轻薄。
|
||||||
|
5. **编写/调整 `SKILL.md`**:只改「何时触发、如何调用、参数含义」,不要写实现细节;实现细节放在 `docs/` 或代码注释里。
|
||||||
|
6. **发布**:若使用 GitHub Actions,编辑 `.github/workflows/release_skill.yaml`,把 `uses:` 指向**你们组织**的复用工作流;若不用 CI,可删除该目录。
|
||||||
|
7. **一键打标签推送(与匠厂 monorepo 对齐)**:在技能仓库根目录执行 `.\release.ps1`(需与 `jiangchang-platform-kit` 位于同一父目录,以便调用 `..\jiangchang-platform-kit\tools\release.ps1`)。支持 `-DryRun`、`-AutoCommit`、`-CommitMessage` 等参数,与 `account-manager` / `sohu-publisher` 一致。
|
||||||
|
|
||||||
## 目录一览
|
## 目录一览
|
||||||
|
|
||||||
| 路径 | 作用 |
|
| 路径 | 作用 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `SKILL.md` | 技能清单(YAML 头 + Markdown 正文) |
|
| `SKILL.md` | 技能清单(YAML 头 + Markdown 正文),供宿主与协作者阅读 |
|
||||||
| `scripts/skill_main.py` | CLI 入口:`health` / `version` |
|
| `release.ps1` | 转调平台套件的发布脚本(提交/推送/语义化 tag);依赖并列的 `jiangchang-platform-kit` |
|
||||||
| `docs/` | 运行时与可移植性说明 |
|
| `scripts/skill_main.py` | 推荐唯一 CLI 入口;含 `health` / `version` 示例 |
|
||||||
| `optional/` | 可选片段(路径、SQLite 等),默认不引用 |
|
| `docs/RUNTIME.md` | 环境与目录契约(多宿主通用) |
|
||||||
|
| `docs/SKILL_TYPES.md` | 常见技能形态与自检清单 |
|
||||||
|
| `docs/PORTABILITY.md` | 多 Claw 宿主差异与兼容建议 |
|
||||||
|
| `optional/` | 可选复制进项目的片段(路径、SQLite 示例),**不默认 import** |
|
||||||
|
|
||||||
## 本地试跑
|
## 不要做的事
|
||||||
|
|
||||||
```bash
|
- 不要在模板中提交真实密钥、真实业务表结构或平台专用逻辑。
|
||||||
python scripts/skill_main.py health
|
- 不要把模板改成只支持某一种宿主;特殊项写在 `docs/PORTABILITY.md` 的「宿主附录」中。
|
||||||
python scripts/skill_main.py version
|
|
||||||
```
|
|
||||||
|
|
||||||
## 版本
|
## 版本
|
||||||
|
|
||||||
与 `SKILL.md` 中 `version` 字段对齐更新。
|
模板自身版本见 `SKILL.md` 的 `version` 字段;与你技能的业务版本一致更新即可。
|
||||||
|
|||||||
36
SKILL.md
36
SKILL.md
@@ -1,33 +1,37 @@
|
|||||||
---
|
---
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 技能清单(Skill Manifest)
|
# 技能清单(Skill Manifest)
|
||||||
|
# 行业常见做法:YAML 头描述元数据,正文 Markdown 描述「何时用、怎么调」。
|
||||||
|
# 不同 Claw 宿主解析字段可能略有差异;可移植标识请使用 metadata.skill.slug。
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
name: 头条号批量发布
|
name: 您的技能显示名称
|
||||||
description: 头条号批量发布技能(骨架阶段:仅健康检查与版本;发布逻辑待实现)。
|
description: 一句话说明技能做什么(给编排层与人类阅读,不写实现细节)。
|
||||||
version: 0.1.0
|
version: 1.0.0
|
||||||
author: 深圳匠厂科技有限公司
|
author: 深圳匠厂科技有限公司
|
||||||
metadata:
|
metadata:
|
||||||
openclaw:
|
|
||||||
slug: toutiao-publisher
|
|
||||||
emoji: "📰"
|
|
||||||
category: "内容发布"
|
|
||||||
skill:
|
skill:
|
||||||
slug: toutiao-publisher
|
# 机器可读、稳定标识:小写字母、数字、短横线;与数据子目录名一致。
|
||||||
emoji: "📰"
|
slug: your-skill-slug
|
||||||
category: "内容发布"
|
emoji: "📦"
|
||||||
|
category: "通用"
|
||||||
|
# 宿主若限制可调用的工具类型,在此列出(按宿主文档填写)。
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- bash
|
- bash
|
||||||
---
|
---
|
||||||
|
|
||||||
# 头条号批量发布(toutiao-publisher)
|
# Your Skill Display Name
|
||||||
|
|
||||||
## 使用时机
|
## 使用时机
|
||||||
|
|
||||||
- 用户需要**在头条号侧批量或自动化发布内容**时(具体话术与流程待业务实现后补充)。
|
<!-- 由技能作者填写:用户在自然语言里怎样表达时应触发本技能。 -->
|
||||||
|
|
||||||
|
- 示例:用户说「检查技能是否可用」「运行某某任务」
|
||||||
|
|
||||||
## 执行步骤
|
## 执行步骤
|
||||||
|
|
||||||
### 健康检查
|
<!-- 将 {baseDir} 替换为宿主提供的技能根目录;若宿主使用其它占位符,以宿主文档为准。 -->
|
||||||
|
|
||||||
|
### 健康检查(推荐自动化先跑)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 {baseDir}/scripts/skill_main.py health
|
python3 {baseDir}/scripts/skill_main.py health
|
||||||
@@ -39,9 +43,9 @@ python3 {baseDir}/scripts/skill_main.py health
|
|||||||
python3 {baseDir}/scripts/skill_main.py version
|
python3 {baseDir}/scripts/skill_main.py version
|
||||||
```
|
```
|
||||||
|
|
||||||
### 发布与其它子命令
|
### 扩展子命令
|
||||||
|
|
||||||
实现中:后续将在 `scripts/` 下增加发布入口,并在此文档补充命令与参数说明。
|
在 `scripts/skill_main.py` 中增加新的子命令分支,并在此处追加对应示例命令与参数说明。
|
||||||
|
|
||||||
## 环境依赖
|
## 环境依赖
|
||||||
|
|
||||||
@@ -49,4 +53,4 @@ python3 {baseDir}/scripts/skill_main.py version
|
|||||||
|
|
||||||
## 数据与隐私
|
## 数据与隐私
|
||||||
|
|
||||||
本技能若产生持久化数据,应仅写入 `{CLAW_DATA_ROOT}/{CLAW_USER_ID}/toutiao-publisher/` 下;不得将用户数据提交到版本库。
|
本技能若产生持久化数据,应仅写入 `{CLAW_DATA_ROOT}/{CLAW_USER_ID}/your-skill-slug/` 下;不得将用户数据提交到版本库。
|
||||||
|
|||||||
76
docs/LOGGING.md
Normal file
76
docs/LOGGING.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# OpenClaw 技能日志约定(各技能统一)
|
||||||
|
|
||||||
|
规范目标:同一用户工作区下**一份按日轮转的主日志**,行内带 **技能标识** 与 **trace_id**,便于跨技能排查。
|
||||||
|
|
||||||
|
## 目录与文件
|
||||||
|
|
||||||
|
- 统一日志目录(与技能数据目录**无关**,不按 skill 分子目录):
|
||||||
|
- `{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/logs/`
|
||||||
|
- 主日志文件(默认):
|
||||||
|
- `{...}/logs/jiangchang.log`(午夜轮转,历史带日期后缀,由 `TimedRotatingFileHandler` 管理)
|
||||||
|
- 覆盖主日志**绝对路径**(可选):
|
||||||
|
- `JIANGCHANG_LOG_FILE`
|
||||||
|
|
||||||
|
实现源码以 **`jiangchang-platform-kit/sdk/jiangchang_skill_core/unified_logging.py`** 为准;各技能 `scripts/jiangchang_skill_core/unified_logging.py` 为发布用副本,**修改后请同步各技能**。
|
||||||
|
|
||||||
|
数据根与用户 id 的解析与本地 CLI 注入见同目录 **`runtime_env.py`**(`get_data_root` / `get_user_id` / `apply_cli_local_defaults`);新技能应在 `main.py` 最早阶段调用 `apply_cli_local_defaults()`(在 import 业务包之前)。
|
||||||
|
|
||||||
|
## 轮转与编码
|
||||||
|
|
||||||
|
- `TimedRotatingFileHandler`,`when=midnight`,保留份数默认 30,可用 `JIANGCHANG_LOG_BACKUP_COUNT` 覆盖(1~365)。
|
||||||
|
- 编码 **UTF-8**。
|
||||||
|
- 目录在首次写日志前创建(`exist_ok=True`)。
|
||||||
|
|
||||||
|
## 日志格式(行文本)
|
||||||
|
|
||||||
|
```text
|
||||||
|
%(asctime)s | %(levelname)-8s | %(trace_id)s | %(skill_slug)s | %(name)s | %(message)s
|
||||||
|
```
|
||||||
|
|
||||||
|
- `datefmt`:`%Y-%m-%dT%H:%M:%S`(本地时间)
|
||||||
|
- `skill_slug`:与 `SKILL.md` 中 `metadata.openclaw.slug` 一致(如 `account-manager`)
|
||||||
|
- `name`:分层 logger,如 `openclaw.skill.account_manager`、`openclaw.skill.account_manager.login_child`
|
||||||
|
- `trace_id`:调用链 ID,见下节
|
||||||
|
|
||||||
|
## 调用链(trace)
|
||||||
|
|
||||||
|
- 环境变量 **`JIANGCHANG_TRACE_ID`**:同一 shell/进程树内子进程默认继承;跨技能 `subprocess` 时父进程应使用 `subprocess_env_with_trace()` 构造 `env`,保证子进程沿用同一 trace。
|
||||||
|
- 若未设置,各技能在 `setup_skill_logging` 时会生成 12 位 hex 并写入环境,供后续子进程继承。
|
||||||
|
|
||||||
|
## Logger 命名
|
||||||
|
|
||||||
|
- 主 logger:`openclaw.skill.<slug_下划线>`(如 `openclaw.skill.content_manager`)
|
||||||
|
- 子进程专用 logger:如 `openclaw.skill.account_manager.login_child`,仍写入**同一**主日志文件(`FileHandler` 追加,格式与主进程一致)
|
||||||
|
|
||||||
|
## 环境变量
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `JIANGCHANG_DATA_ROOT` | 数据根 |
|
||||||
|
| `JIANGCHANG_USER_ID` | 用户/工作区 id,默认 `_anon` |
|
||||||
|
| `JIANGCHANG_LOG_LEVEL` | 默认 `INFO` |
|
||||||
|
| `JIANGCHANG_LOG_TO_STDERR` | `1`/`true` 时 WARNING+ 同时打 stderr |
|
||||||
|
| `JIANGCHANG_LOG_FILE` | 覆盖主日志文件绝对路径 |
|
||||||
|
| `JIANGCHANG_LOG_BACKUP_COUNT` | 轮转保留份数,默认 `30` |
|
||||||
|
| `JIANGCHANG_TRACE_ID` | 可选;由运行时注入以串联调用链 |
|
||||||
|
|
||||||
|
## 技能侧接入(最少改动)
|
||||||
|
|
||||||
|
1. 保证 `scripts/jiangchang_skill_core/` 与平台 kit 中实现保持同步。
|
||||||
|
2. `main.py` 将 `scripts` 目录加入 `sys.path`(若尚未加入)。
|
||||||
|
3. CLI `main` 早期调用:`setup_skill_logging(SKILL_SLUG, LOG_LOGGER_NAME)`。
|
||||||
|
4. 业务代码:`get_skill_logger()` 打日志。
|
||||||
|
5. 拉起子技能 CLI 时:`subprocess.run(..., env=subprocess_env_with_trace())`。
|
||||||
|
|
||||||
|
## 级别与敏感信息
|
||||||
|
|
||||||
|
- **INFO**:子命令入口、成功结束(含关键业务 id)。
|
||||||
|
- **WARNING**:可恢复问题、校验失败。
|
||||||
|
- **ERROR**:不可继续的异常。
|
||||||
|
- **DEBUG**:高频细节;默认关闭。
|
||||||
|
- 日志可能含路径、账号信息,注意权限与留存。
|
||||||
|
|
||||||
|
## 参考实现
|
||||||
|
|
||||||
|
- `jiangchang-platform-kit/sdk/jiangchang_skill_core/unified_logging.py`
|
||||||
|
- `account-manager` / `content-manager` / `llm-manager` 的 `util/logging_config.py`(re-export)与 CLI 入口
|
||||||
@@ -34,6 +34,18 @@
|
|||||||
|
|
||||||
编排型技能若需要通过子进程调用兄弟技能,应基于该变量定位脚本,避免写死绝对路径。
|
编排型技能若需要通过子进程调用兄弟技能,应基于该变量定位脚本,避免写死绝对路径。
|
||||||
|
|
||||||
|
### `JIANGCHANG_SKILLS_ROOT`(可选)
|
||||||
|
|
||||||
|
与 `CLAW_SKILLS_ROOT` 同义;匠厂桌面宿主可只注入此名。未设置时,技能可从「本技能 `scripts/` 的上一级目录的上一级」推断并列根(OpenClaw 开发仓与 `skills/<slug>/` 解压布局均适用)。
|
||||||
|
|
||||||
|
### `JIANGCHANG_APP_ROOT`(可选)
|
||||||
|
|
||||||
|
匠厂应用安装根。未设置且未设置上述技能根时,Windows 下会尝试用默认 `D:\AI\jiangchang`:若存在子目录 `skills` 且其下像并列技能安装,则技能根为 `{APP}/skills`;否则若 `{APP}` 下直接可见各技能 slug 子目录,则 `{APP}` 即技能根。其他平台默认回退为 `~/.openclaw/skills`。
|
||||||
|
|
||||||
|
### 加密发布包布局
|
||||||
|
|
||||||
|
CI 产物 ZIP 解压后与仓库技能目录结构一致:含 `SKILL.md` 与 **`scripts/`**(PyArmor 输出在 `scripts/` 内)。宿主启动入口应为 **`python scripts/main.py`**(工作目录为技能根)。
|
||||||
|
|
||||||
## 本技能推荐的数据目录
|
## 本技能推荐的数据目录
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -14,4 +14,4 @@
|
|||||||
| `paths_snippet.py` | `CLAW_*` 数据目录解析与 fallback 说明 |
|
| `paths_snippet.py` | `CLAW_*` 数据目录解析与 fallback 说明 |
|
||||||
| `sqlite_minimal.py` | 无业务含义的 SQLite 建表示例 |
|
| `sqlite_minimal.py` | 无业务含义的 SQLite 建表示例 |
|
||||||
|
|
||||||
本技能数据子目录与 `SKILL_SLUG` 为 `toutiao-publisher`;若复制片段到其它项目请改为对应 slug 与表名。
|
复制后务必全局替换 `your-skill-slug` 与表名,避免与别的技能冲突。
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
# TODO: 复制本文件后改为你的 slug(与 SKILL.md metadata.skill.slug 一致)
|
# TODO: 复制本文件后改为你的 slug(与 SKILL.md metadata.skill.slug 一致)
|
||||||
SKILL_SLUG = "toutiao-publisher"
|
SKILL_SLUG = "your-skill-slug"
|
||||||
|
|
||||||
|
|
||||||
def _getenv_first(names: tuple[str, ...]) -> str:
|
def _getenv_first(names: tuple[str, ...]) -> str:
|
||||||
@@ -82,7 +82,7 @@ def get_skills_root() -> str:
|
|||||||
"""
|
"""
|
||||||
可选:并列安装的多技能根目录。
|
可选:并列安装的多技能根目录。
|
||||||
未设置 CLAW_SKILLS_ROOT 时,默认使用本文件所在仓库的上一级目录的上一级
|
未设置 CLAW_SKILLS_ROOT 时,默认使用本文件所在仓库的上一级目录的上一级
|
||||||
(即:.../toutiao-publisher/optional/ → 仅作开发时并列技能推断参考)。
|
(即:.../skill-template/scripts/ → 误推断;复制后请改为 BASE_DIR 逻辑)。
|
||||||
"""
|
"""
|
||||||
root = _getenv_first(("CLAW_SKILLS_ROOT",))
|
root = _getenv_first(("CLAW_SKILLS_ROOT",))
|
||||||
if root:
|
if root:
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import json
|
|||||||
import sys
|
import sys
|
||||||
from typing import Callable, Dict, List, Optional
|
from typing import Callable, Dict, List, Optional
|
||||||
|
|
||||||
# 与 SKILL.md 中 metadata.openclaw.slug / metadata.skill.slug 保持一致
|
# 与 SKILL.md 中 metadata.skill.slug 保持一致(模板占位,复制后请修改)
|
||||||
SKILL_SLUG = "toutiao-publisher"
|
SKILL_SLUG = "your-skill-slug"
|
||||||
|
|
||||||
|
|
||||||
def cmd_version(_args: argparse.Namespace) -> int:
|
def cmd_version(_args: argparse.Namespace) -> int:
|
||||||
@@ -58,7 +58,7 @@ def cmd_health(_args: argparse.Namespace) -> int:
|
|||||||
|
|
||||||
def build_parser() -> argparse.ArgumentParser:
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
p = argparse.ArgumentParser(
|
p = argparse.ArgumentParser(
|
||||||
description="toutiao-publisher — Toutiao batch publish skill CLI (skeleton).",
|
description="Claw skill CLI template — replace SKILL_SLUG and add subcommands.",
|
||||||
)
|
)
|
||||||
sub = p.add_subparsers(dest="command", required=True)
|
sub = p.add_subparsers(dest="command", required=True)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user