index.mjs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. /**
  2. * skill-deliverable SDK — 交付物上报核心
  3. *
  4. * 零依赖(使用 Node ≥ 18 内置 fetch + fs)
  5. * 所有凭据从 env → config 文件 → 兜底,按高兼容链解析
  6. */
  7. import { readFileSync, existsSync, constants } from 'fs';
  8. import { homedir, hostname } from 'os';
  9. import { join } from 'path';
  10. import { env } from 'process';
  11. const HOME = homedir();
  12. const GATEWAY = env.FMODE_API || 'https://server.fmode.cn';
  13. const FN_ID = env.FN_DELIVERABLES || 'lfYlgU7SkK';
  14. // ============================================================
  15. // Agent 身份高兼容解析
  16. // ============================================================
  17. export function resolveAgentId() {
  18. // 1. env
  19. if (env.FMODE_AGENT_ID) return env.FMODE_AGENT_ID;
  20. if (env.AGENT_ID) return env.AGENT_ID;
  21. // 2. ~/.fmode/config.json
  22. const homeCfg = tryReadJSON(join(HOME, '.fmode', 'config.json'));
  23. if (homeCfg?.agentId) return homeCfg.agentId;
  24. // 3. ./.fmode/config.json (cwd)
  25. const cwdCfg = tryReadJSON(join(process.cwd(), '.fmode', 'config.json'));
  26. if (cwdCfg?.agentId) return cwdCfg.agentId;
  27. // 4. 兜底 hostname(仅 warn)
  28. const hn = hostname();
  29. console.warn(`[warn] 未找到语义 agentId,回退到 hostname "${hn}"(可能不是注册名)`);
  30. return hn;
  31. }
  32. // ============================================================
  33. // Session Token 高兼容解析
  34. // ============================================================
  35. export function resolveSessionToken() {
  36. // 1. env
  37. if (env.FMODE_SESSION_TOKEN) return env.FMODE_SESSION_TOKEN;
  38. // 2. ~/.fmode/config/user.json
  39. const userCfg = tryReadJSON(join(HOME, '.fmode', 'config', 'user.json'));
  40. if (userCfg?.sessionToken) return userCfg.sessionToken;
  41. // 3. ~/.fmode/config.json
  42. const homeCfg = tryReadJSON(join(HOME, '.fmode', 'config.json'));
  43. if (homeCfg?.sessionToken) return homeCfg.sessionToken;
  44. // 4. ./.fmode/config.json (cwd)
  45. const cwdCfg = tryReadJSON(join(process.cwd(), '.fmode', 'config.json'));
  46. if (cwdCfg?.sessionToken) return cwdCfg.sessionToken;
  47. return null;
  48. }
  49. // ============================================================
  50. // 云函数调用
  51. // ============================================================
  52. export async function callFn(params) {
  53. const token = resolveSessionToken();
  54. if (!token) throw new Error('无 sessionToken:无法调用云函数。设置 FMODE_SESSION_TOKEN 环境变量或 ~/.fmode/config/user.json');
  55. const resp = await fetch(`${GATEWAY}/api/functions`, {
  56. method: 'POST',
  57. headers: { 'Content-Type': 'application/json' },
  58. body: JSON.stringify({ token, id: FN_ID, params }),
  59. });
  60. return resp.json();
  61. }
  62. // ============================================================
  63. // 上报交付物
  64. // ============================================================
  65. export async function report({ agentId, agentObjectId, title, summary, project, artifacts, tags }) {
  66. if (!title) throw new Error('title 必填');
  67. const resolvedAgentId = agentId || resolveAgentId();
  68. if (!resolvedAgentId) throw new Error('agentId 必填');
  69. return callFn({
  70. action: 'report',
  71. agentId: resolvedAgentId,
  72. agentObjectId: agentObjectId || '',
  73. title,
  74. summary: summary || '',
  75. project: project || '',
  76. artifacts: artifacts || [],
  77. tags: tags || [],
  78. });
  79. }
  80. // ============================================================
  81. // 查询交付物
  82. // ============================================================
  83. export async function list({ limit = 5, agentId } = {}) {
  84. const r = await callFn({ action: 'list', agentId: agentId || resolveAgentId(), limit });
  85. return r.deliverables || [];
  86. }
  87. // ============================================================
  88. // 工具
  89. // ============================================================
  90. function tryReadJSON(p) {
  91. try {
  92. if (!existsSync(p)) return null;
  93. return JSON.parse(readFileSync(p, 'utf-8'));
  94. } catch {
  95. return null;
  96. }
  97. }
  98. export default { resolveAgentId, resolveSessionToken, report, list, callFn };