|
|
@@ -0,0 +1,209 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+"""批量生成 fmode-skillhub 服务层子技能 ESM 结构(统一规范)"""
|
|
|
+import os, textwrap
|
|
|
+
|
|
|
+BASE = '/opt/data/projects/fmode-skillhub/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-skillhub` 自带)
|
|
|
+
|
|
|
+## 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("🎉 服务层子技能生成完毕")
|