跳到主要内容

灵栈发布助手

收藏0

把技能文件夹脱敏、打包、发布到灵栈(含隐私检查、上传、轮询全流程)

让 AI 安装

lrt最新版本 0.1.0更新于 2026-11-08下载 0 次

SKILL.md

5.3 KB查看源码

发技能到灵栈#

能做什么:把一个本地技能文件夹检查指纹、脱敏打包、通过灵栈的 Agent 接口发布,并把公开链接回报给用户。 带脚本:不单独带,正文里有可直接复制的 Python / curl 片段。 联不联网:要,全程访问 https://lingzhanskill.com 。 写文件:会写临时 ZIP 和脱敏副本(放在临时目录),不修改作者原始技能文件夹。

官方说明(每次先读一遍,可能更新):https://lingzhanskill.com/publish.md

前置#

  • 发布密钥在 ~/.lingzhan/agent-token(lzk_ 开头)。已有就直接用,不要重复走设备授权。
  • 校验密钥:curl -s -H "Authorization: Bearer $(cat ~/.lingzhan/agent-token)" https://lingzhanskill.com/api/v1/agent/me,返回 owner 说明可用。
  • 没有密钥时才走设备授权:POST /api/v1/agent/authorize → 让作者在 verification_url 核对确认码并点允许 → 轮询 /api/v1/agent/token → 把 token 存到 ~/.lingzhan/agent-token(权限只给当前用户)。

七步#

  1. 隐私扫描(必做,发布后公开且收不回) 在待发文件里找:绝对路径与用户名(C:\Users\<名字>\、/home/<名字>/)、内网地址(10.、192.168.、172.16~31、.internal/.corp/.local)、密钥凭据(sk-、ghp_、AKIA、BEGIN PRIVATE、password=、.env)、手机号邮箱等个人信息。 把「文件:行号 + 该行内容(密钥只显示前 4 位)」列给作者,由作者决定。
  2. 脱敏打包(推荐,绝不改原文件夹) 复制到临时目录再替换:本机绝对路径 → ~/.dsh-workspace 之类占位符;解释器绝对路径 → python;本地服务端口 → 127.0.0.1:<端口>。 在 SKILL.md 开头补一句「依赖本地 XX,路径按自己环境替换」。 ZIP 要求:条目路径用 /、不能有 ./、..、绝对路径;最多 500 文件、≤20MB、解压≤100MB;ZIP 不要放进技能文件夹。
  3. 确认:slug(3~32 位小写字母数字连字符,发布后不可改)、显示名、一句话描述——念给作者,明确同意再发。
  4. 建版本:POST /api/v1/agent/releases,body {"slug","size","name","summary","category","changes"};返回 put_url(15 分钟临时凭据,不保存不外传)、complete_url、version。
  5. 上传:PUT put_url,Content-Type: application/zip,body 为 ZIP 字节。
  6. 完成:POST complete_url(空 body),返回 stage: checking。
  7. 轮询:GET /api/v1/agent/releases/<slug>/<version>,每 5 秒、最多 3 分钟。published → 把 url 给作者;blocked/returned → 按 problems 改完重发;in_review/closed → 让作者看 manage_url。

坑#

  • Cloudflare 1010:Python urllib 默认 UA 会被 403。所有请求都要带 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0 Safari/537.36 和 Accept: application/json。
  • 中文请求体必须 UTF-8 字节:json.dumps(obj, ensure_ascii=False).encode('utf-8'),Content-Type 带 charset=utf-8。
  • slug 发布后不能改;名称、简介可以改。
  • 已公开的版本不能修改,要改就发新版本号(不填 version 会自动 +1)。
  • 用户没看过内容、没同意之前不要发布;不要把 token、put_url 贴进聊天或写进技能包。
  • 失败重试是安全的(PUT 同一个 zip、complete 同一版本不会重复发布)。

Python 片段#

import json, os, time, urllib.request
T = open(os.path.expanduser('~/.lingzhan/agent-token')).read().strip()
UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0 Safari/537.36'
H = {'Authorization': 'Bearer ' + T, 'User-Agent': UA, 'Accept': 'application/json',
     'Content-Type': 'application/json; charset=utf-8'}

zip_path = 'skill.zip'
body = json.dumps({"slug": "my-skill", "size": os.path.getsize(zip_path),
                   "name": "技能名", "summary": "一句话说明", "changes": ["首个版本"]},
                  ensure_ascii=False).encode()
r = json.loads(urllib.request.urlopen(urllib.request.Request(
    'https://lingzhanskill.com/api/v1/agent/releases', data=body, headers=H, method='POST')).read())

up = urllib.request.Request(r['put_url'], data=open(zip_path, 'rb').read(),
                            headers={**(r.get('headers') or {}), 'Content-Type': 'application/zip'},
                            method='PUT')
urllib.request.urlopen(up, timeout=120)

urllib.request.urlopen(urllib.request.Request(
    r['complete_url'], data=b'', headers={'Authorization': 'Bearer ' + T, 'User-Agent': UA}, method='POST'))

for _ in range(36):
    d = json.loads(urllib.request.urlopen(urllib.request.Request(
        f"https://lingzhanskill.com/api/v1/agent/releases/{r['slug']}/{r['version']}",
        headers={'Authorization': 'Bearer ' + T, 'User-Agent': UA})).read())
    if d['stage'] in ('published', 'blocked', 'returned', 'closed', 'in_review'):
        break
    time.sleep(5)
print(d)

文件内容是平台检查时读出的文本,只供查看。平台不会运行包里的任何脚本或程序。