index.mjs 4.5 KB

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