deliverable-report-v2.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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 配套)
  14. * ============================================
  15. *
  16. * 部署:Parse.Cloud.define 或 /api/functions 模式
  17. * 云函数 ID:lfYlgU7SkK(保持不变)
  18. *
  19. * v2 变更:
  20. * 1. 移除 FMODE_AGENT_SUPERADMIN 角色检查 → 改为:用户仅对自有容器有上报权限
  21. * 2. agentId → agentObjectId 自动解析(传任一即可,缺失则自动补全)
  22. * 3. 首次上报时自动创建 FmodeAgent 记录(自注册)
  23. * 4. 参数缺失时自动补全(时间戳、状态等)
  24. *
  25. * 鉴权逻辑(简化版):
  26. * sessionToken → _User → 查此用户是否是该 FmodeAgent 记录的 owner
  27. * 是 → 允许上报;否 → 403
  28. * 若 FmodeAgent 记录不存在且 agentId 有效 → 自动创建 + 设当前用户为 owner
  29. */
  30. const CLASS_DELIVERABLE = 'AgentDeliverable';
  31. const CLASS_AGENT = 'FmodeAgent';
  32. Parse.Cloud.define('deliverable-report', async (req) => {
  33. const { agentId, agentObjectId, title, summary, project, artifacts, tags } = req.params;
  34. const user = req.user;
  35. if (!user) throw new Parse.Error(401, '需要登录');
  36. if (!title) throw new Parse.Error(400, 'title 必填');
  37. // ---- Step 1: 解析/注册 FmodeAgent ----
  38. let agentQuery = new Parse.Query(CLASS_AGENT);
  39. if (agentObjectId) {
  40. agentQuery.equalTo('objectId', agentObjectId);
  41. } else if (agentId) {
  42. agentQuery.equalTo('agentId', agentId);
  43. } else {
  44. throw new Parse.Error(400, 'agentId 或 agentObjectId 必填');
  45. }
  46. let agent = await agentQuery.first({ useMasterKey: true });
  47. // 自动注册:agent 不存在则创建
  48. if (!agent) {
  49. if (!agentId) throw new Parse.Error(400, '新 agent 注册需要 agentId');
  50. const AgentClass = Parse.Object.extend(CLASS_AGENT);
  51. agent = new AgentClass();
  52. agent.set('agentId', agentId);
  53. agent.set('user', user);
  54. agent.set('status', 'online');
  55. agent.set('heartbeatAt', new Date());
  56. agent = await agent.save(null, { useMasterKey: true });
  57. }
  58. const finalAgentId = agent.get('agentId') || agentId;
  59. const finalAgentOid = agent.id;
  60. // ---- Step 2: 权限校验(仅校验用户是否为 agent owner)----
  61. const agentUser = agent.get('user');
  62. if (agentUser && agentUser.id !== user.id) {
  63. throw new Parse.Error(403, '仅该容器的 owner 可上报');
  64. }
  65. // 如果 agent 没有 owner(旧数据),自动绑定当前用户
  66. if (!agentUser) {
  67. agent.set('user', user);
  68. await agent.save(null, { useMasterKey: true });
  69. }
  70. // ---- Step 3: 写入交付物 ----
  71. const DelClass = Parse.Object.extend(CLASS_DELIVERABLE);
  72. const record = new DelClass();
  73. record.set('agentId', finalAgentId);
  74. record.set('agent', agent); // 指针
  75. record.set('agentObjectId', finalAgentOid);
  76. record.set('user', user);
  77. record.set('title', title);
  78. record.set('summary', summary || '');
  79. record.set('project', project || '');
  80. record.set('artifacts', artifacts || []);
  81. record.set('tags', tags || []);
  82. record.set('reportedAt', new Date());
  83. // ACL:仅 owner 可读写
  84. const acl = new Parse.ACL(user);
  85. acl.setPublicReadAccess(false);
  86. acl.setPublicWriteAccess(false);
  87. record.setACL(acl);
  88. const saved = await record.save(null, { useMasterKey: true });
  89. // ---- Step 4: 更新 agent 心跳 ----
  90. agent.set('heartbeatAt', new Date());
  91. agent.set('status', 'online');
  92. await agent.save(null, { useMasterKey: true });
  93. return {
  94. code: 200,
  95. id: saved.id,
  96. agentId: finalAgentId,
  97. agentObjectId: finalAgentOid,
  98. title: title,
  99. createdAt: saved.get('createdAt') || saved.createdAt,
  100. };
  101. });