| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- #!/usr/bin/env python3
- """批量生成 fmode-skills 服务层子技能 ESM 结构(统一规范)"""
- import os, textwrap
- BASE = '/opt/data/projects/fmode-skills/skills'
- skills = [
- {
- "dir": "fmode-account",
- "name": "fmode-account",
- "slug": "fmode-skill-fmode-account",
- "version": "0.1.0",
- "title": "Fmode 账号(手机验证码注册/登录)",
- "summary": "Fmode 账号注册登录:手机验证码 → sessionToken → 企业开通。数字分身初始化第一步。",
- "triggers": "注册、登录、验证码、sessionToken、账号、手机号、数字分身初始化、fmod账号",
- "endpoints": [
- ("POST https://server.fmode.cn/api/apig/message", "发送验证码 {mobile,company}"),
- ("GET https://server.fmode.cn/api/fmode/mobile", "验证码登录 {mobile,code} → data.token/data.newapiToken"),
- ("GET https://server.fmode.cn/parse/users/me", "校验会话 Bearer token"),
- ],
- "keypoints": ["token 走受保护 profile(0700),不进聊天/project/Git/日志", "验证码只用于立即登录请求", "Windows 用当前用户目录,POSIX 0700/0600"],
- },
- {
- "dir": "fmode-enterprise",
- "name": "fmode-enterprise",
- "slug": "fmode-skill-fmode-enterprise",
- "version": "0.1.0",
- "title": "Fmode 企业(开通状态/独立部署)",
- "summary": "Fmode 企业状态:查询订阅、申请独立部署、轮询开通。数字分身企业开通。",
- "triggers": "企业、开通、订阅、enterprise、Company、独立部署、sub、trial",
- "endpoints": [
- ("POST https://server.fmode.cn/api/fmode/enterprise/subscription/status", "查企业状态 {sessionToken}"),
- ("POST https://server.fmode.cn/api/fmode/enterprise/subscription/trial-standalone", "申请独立部署 {name,sessionToken}"),
- ("POST https://server.fmode.cn/api/fmode/enterprise/subscription/provision/retry", "重试开通(仅失败后)"),
- ],
- "keypoints": ["不在企业时问人名(非企业名)再trial-standalone", "仅 provision pending/running 时轮询状态", "绝不索要数据库URI"],
- },
- {
- "dir": "fmode-cloud-functions",
- "name": "fmode-cloud-functions",
- "slug": "fmode-skill-fmode-cloud-functions",
- "version": "0.1.0",
- "title": "Fmode 云函数(Function 合同 + /api/functions)",
- "summary": "Fmode 云函数部署与调用:Function 合同表存动态函数源,/api/functions 发布执行。",
- "triggers": "云函数、Function、/api/functions、后端函数、业务接口、动态函数",
- "endpoints": [
- ("POST https://server.fmode.cn/api/functions", "调用/注册云函数 {token, path, params}"),
- ],
- "keypoints": ["CRUD 不建用户维护服务器,动态函数进 Function 合同表", "handler(request,response) 签名 + 会话鉴权 + 租户校验 + 幂等", "masterKey/dbURI/平台session 绝不进浏览器 bundle"],
- },
- {
- "dir": "fmode-cloud-resources",
- "name": "fmode-cloud-resources",
- "slug": "fmode-skill-fmode-cloud-resources",
- "version": "0.1.0",
- "title": "Fmode 云资源(华为云 STS 部署)",
- "summary": "Fmode 云资源领取:平台签发项目级 STS,华为云 OBS 部署,无需用户华为账号。",
- "triggers": "云资源、领取、部署、华为云、STS、obs、huaweicloud、deploy",
- "endpoints": [
- ("POST https://server.fmode.cn/api/apig/deploy/huaweicloud", "取 STS {token, projectId} → accessKey/secretKey/securityToken/expiresAt/obsPath"),
- ],
- "keypoints": ["STS 平台签发,非用户华为账号,用户无需华为账号/服务器", "过期(<5min)丢弃整套后重取,最多重试2次", "绝不替换为永久密钥"],
- },
- {
- "dir": "fmode-static-publish",
- "name": "fmode-static-publish",
- "slug": "fmode-skill-fmode-static-publish",
- "version": "0.1.0",
- "title": "Fmode 静态发布(OBS+CDN 公开 URL)",
- "summary": "Fmode 前后端发布:构建 → 注册 Project → STS → obsutil 同步 OBS → CDN 刷新 → 验证公开 URL。",
- "triggers": "发布、部署、上线、静态站、前端、课件、CDN、obsutil、public URL、huaweicloud-cdn-refresh",
- "endpoints": [
- ("POST https://server.fmode.cn/api/functions", "注册 Project {token,path:/fmode/project-register,params:{name,version,gitUrl}}"),
- ("POST https://server.fmode.cn/api/apig/deploy/huaweicloud", "取 STS {token,projectId}"),
- ("POST https://server.fmode.cn/api/apig/deploy/huaweicloud-cdn-refresh", "CDN 刷新 {token,projectId,path}"),
- ],
- "keypoints": ["注册Project→构建→扫token→取STS→上传obsPath→CDN刷新→验证URL 逐门禁报告", "obsPath 必须是 dev/<projectId>/", "会话过期先重播登录再取新STS,不求华为凭据"],
- },
- {
- "dir": "fmode-git-archive",
- "name": "fmode-git-archive",
- "slug": "fmode-skill-fmode-git-archive",
- "version": "0.1.0",
- "title": "Fmode Git 归档(Gitea + 源码入库)",
- "summary": "Fmode 项目归档:源码进 git.fmode.cn,建 Gogs 账号/个人 token/建仓/推送/验证 SHA。",
- "triggers": "归档、git、Gogs、Gitea、git.fmode.cn、仓库、源码入库、git archive",
- "endpoints": [
- ("POST https://server.fmode.cn/api/functions", "Gogs admin proxy 建用户 {token,path:/gogs/admin/proxy,params:{method:POST,path:/admin/users,...}}"),
- ("POST https://git.fmode.cn/api/v1/users/<username>/tokens", "创建个人 token"),
- ("POST https://git.fmode.cn/api/v1/user/repos", "创建私有仓库"),
- ],
- "keypoints": ["用户名强制 fmode-<phone>,请求值忽略占位", "token 绝不进 remote URL,用 HTTPS + token 密码", "排除 .env/密钥/构建产物/日志", "git ls-remote 验证远端 SHA,发布与归档分开报告"],
- },
- ]
- # 通用 ESM 脚本模板(fetch 封装)
- FETCH_TMPL = '''#!/usr/bin/env node
- /**
- * {title} — 辅助脚本(零 npm 依赖,Node>=18 自带 fetch)
- * Copyright (c) 未来飞马 MPL-2.0
- *
- * 用法:node index.mjs <action> [jsonPayload]
- */
- const BASE = "https://server.fmode.cn";
- async function call(method, path, body, token) {{
- const headers = {{ "content-type": "application/json" }};
- if (token) headers["authorization"] = "Bearer " + token;
- const res = await fetch(BASE + path, {{
- method, headers, body: body ? JSON.stringify(body) : undefined,
- }});
- const text = await res.text();
- // 脱敏输出:token/密钥不打印
- const safe = text.replace(/(token|authorization|accessKey|secretKey|securityToken)["'\\s:]+([^",}}]+)/gi, "$1:***");
- console.log(safe);
- return res;
- }}
- const [action, raw] = process.argv.slice(2);
- let payload = {{}};
- try {{ if (raw) payload = JSON.parse(raw); }} catch {{ payload = {{ _raw: raw }}; }}
- {actions}
- '''
- def actions_for(epoints):
- lines = []
- for ep in epoints:
- method, path = ep[0].split(' ')
- parts = path.replace('https://server.fmode.cn','').strip()
- # 生成一个如 call("<METHOD>","<PATH>", payload) 的示例
- lines.append(f'// 示例端点: {method} {ep[1]}')
- return "\n".join(lines)
- for s in skills:
- d = os.path.join(BASE, s['dir'])
- os.makedirs(os.path.join(d, 'scripts'), exist_ok=True)
- os.makedirs(os.path.join(d, 'references'), exist_ok=True)
- # SKILL.md
- ep_lines = "\n".join(f"| `{m} {p}` | {desc} |" for m, p, desc in [
- (m, p, dsc) for m, p, dsc in [(ep[0].split(' ')[0], ep[0].split(' ')[1], ep[1]) for ep in s['endpoints']]
- ])
- kp_lines = "\n".join(f"- ⚠️ {k}" for k in s['keypoints'])
- skill_md = f"""---
- slug: {s['slug']}
- displayName: {s['dir']}
- version: {s['version']}
- name: {s['dir']}
- description: {s['summary']} 触发词:{s['triggers']}
- tags: [FmodeAgent, FmodeCode, 服务级, {s['dir'].replace('fmode-','')}]
- platform: Both
- level: 服务级
- category: 平台基础设施
- icon: ⚙️
- license: MPL-2.0
- author: Fmode (未来飞马)
- ---
- # {s['title']}
- ## 触发词
- {s['triggers']}
- ## 快速开始(ESM)
- ```bash
- node scripts/index.mjs <action> '{{"...": ...}}'
- ```
- 或 Agent 直接按流程调用下述端点。
- ## 端点清单
- | 端点 | 作用 |
- |------|------|
- {ep_lines}
- ## 关键纪律
- {kp_lines}
- ## References
- - 详见 `references/` 目录
- - 完整平台规范见 skill-core-guide(`fmode-skills` 自带)
- ## License
- MPL-2.0 © 2026 Fmode (未来飞马)
- """
- with open(os.path.join(d, 'SKILL.md'), 'w', encoding='utf-8') as f:
- f.write(skill_md)
- # scripts/index.mjs(零依赖 fetch 封装)
- fetch_js = FETCH_TMPL.format(title=s['title'], actions=actions_for(s['endpoints']))
- with open(os.path.join(d, 'scripts', 'index.mjs'), 'w', encoding='utf-8') as f:
- f.write(fetch_js)
- # references/endpoints.md
- with open(os.path.join(d, 'references', 'endpoints.md'), 'w', encoding='utf-8') as f:
- f.write(f"# {s['title']} 端点参考\n\n" + ep_lines + "\n")
- print(f"✅ 生成 {s['dir']}")
- print("🎉 服务层子技能生成完毕")
|