#!/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//", "会话过期先重播登录再取新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//tokens", "创建个人 token"), ("POST https://git.fmode.cn/api/v1/user/repos", "创建私有仓库"), ], "keypoints": ["用户名强制 fmode-,请求值忽略占位", "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 [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("","", 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 '{{"...": ...}}' ``` 或 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("🎉 服务层子技能生成完毕")