index.mjs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright (c) 未来飞马
  2. //
  3. // Licensed under the MIT License. See LICENSE in the project root
  4. // for the full license text.
  5. //
  6. // Trademark Notice:
  7. // The MIT license grants copyright permissions for source code only.
  8. // It does NOT grant any rights to use trademarks including "未来飞马",
  9. // "Harness Loop", "RSI", and associated slogan "让AI进化提前发生,让AI落地快人一步".
  10. // Any use of these trademarks requires separate written permission.
  11. /**
  12. * skill-deliverable SDK — 交付物上报核心
  13. *
  14. * 零依赖(使用 Node ≥ 18 内置 fetch + fs)
  15. * 所有凭据从 env → config 文件 → 兜底,按高兼容链解析
  16. */
  17. import { readFileSync, existsSync, constants } from 'fs';
  18. import { homedir, hostname } from 'os';
  19. import { join } from 'path';
  20. import { env } from 'process';
  21. const HOME = homedir();
  22. const GATEWAY = env.FMODE_API || 'https://server.fmode.cn';
  23. const FN_ID = env.FN_DELIVERABLES || 'lfYlgU7SkK';
  24. // ============================================================
  25. // Agent 身份高兼容解析
  26. // ============================================================
  27. export function resolveAgentId() {
  28. // 1. env
  29. if (env.FMODE_AGENT_ID) return env.FMODE_AGENT_ID;
  30. if (env.AGENT_ID) return env.AGENT_ID;
  31. // 2. ~/.fmode/config.json
  32. const homeCfg = tryReadJSON(join(HOME, '.fmode', 'config.json'));
  33. if (homeCfg?.agentId) return homeCfg.agentId;
  34. // 3. ./.fmode/config.json (cwd)
  35. const cwdCfg = tryReadJSON(join(process.cwd(), '.fmode', 'config.json'));
  36. if (cwdCfg?.agentId) return cwdCfg.agentId;
  37. // 4. 兜底 hostname(仅 warn)
  38. const hn = hostname();
  39. console.warn(`[warn] 未找到语义 agentId,回退到 hostname "${hn}"(可能不是注册名)`);
  40. return hn;
  41. }
  42. // ============================================================
  43. // Session Token 高兼容解析
  44. // ============================================================
  45. export function resolveSessionToken() {
  46. // 1. env
  47. if (env.FMODE_SESSION_TOKEN) return env.FMODE_SESSION_TOKEN;
  48. // 2. ~/.fmode/config/user.json
  49. const userCfg = tryReadJSON(join(HOME, '.fmode', 'config', 'user.json'));
  50. if (userCfg?.sessionToken) return userCfg.sessionToken;
  51. // 3. ~/.fmode/config.json
  52. const homeCfg = tryReadJSON(join(HOME, '.fmode', 'config.json'));
  53. if (homeCfg?.sessionToken) return homeCfg.sessionToken;
  54. // 4. ./.fmode/config.json (cwd)
  55. const cwdCfg = tryReadJSON(join(process.cwd(), '.fmode', 'config.json'));
  56. if (cwdCfg?.sessionToken) return cwdCfg.sessionToken;
  57. return null;
  58. }
  59. // ============================================================
  60. // 云函数调用
  61. // ============================================================
  62. export async function callFn(params) {
  63. const token = resolveSessionToken();
  64. if (!token) throw new Error('无 sessionToken:无法调用云函数。设置 FMODE_SESSION_TOKEN 环境变量或 ~/.fmode/config/user.json');
  65. const resp = await fetch(`${GATEWAY}/api/functions`, {
  66. method: 'POST',
  67. headers: { 'Content-Type': 'application/json' },
  68. body: JSON.stringify({ token, id: FN_ID, params }),
  69. });
  70. return resp.json();
  71. }
  72. // ============================================================
  73. // 上报交付物
  74. // ============================================================
  75. export async function report({ agentId, agentObjectId, title, summary, project, artifacts, tags }) {
  76. if (!title) throw new Error('title 必填');
  77. const resolvedAgentId = agentId || resolveAgentId();
  78. if (!resolvedAgentId) throw new Error('agentId 必填');
  79. return callFn({
  80. action: 'report',
  81. agentId: resolvedAgentId,
  82. agentObjectId: agentObjectId || '',
  83. title,
  84. summary: summary || '',
  85. project: project || '',
  86. artifacts: artifacts || [],
  87. tags: tags || [],
  88. });
  89. }
  90. // ============================================================
  91. // 查询交付物
  92. // ============================================================
  93. export async function list({ limit = 5, agentId } = {}) {
  94. const r = await callFn({ action: 'list', agentId: agentId || resolveAgentId(), limit });
  95. return r.deliverables || [];
  96. }
  97. // ============================================================
  98. // 工具
  99. // ============================================================
  100. function tryReadJSON(p) {
  101. try {
  102. if (!existsSync(p)) return null;
  103. return JSON.parse(readFileSync(p, 'utf-8'));
  104. } catch {
  105. return null;
  106. }
  107. }
  108. export default { resolveAgentId, resolveSessionToken, report, list, callFn };