deliverable-report-v2.platform.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // fmodeagent-deliverables v0.0.2 — 智能体任务成果上报与聚合 (表: AgentDeliverable)
  2. // 调用: POST /api/functions {token, id:<Function objectId>, params:{action:'report'|'list'|'byProject', ...}}
  3. // v0.0.2 变更:
  4. // 1. report 移除 FMODE_AGENT_SUPERADMIN 检查 → 仅校验「该 agent 的 owner」;agent 无 owner 时首次上报自动绑定
  5. // 2. agent 不存在时自动注册(自注册,agentId 有效即可)
  6. // 3. agentId / agentObjectId 任一可传,自动补全,并写入 agent 指针
  7. // 4. list/byProject 可见性保持 v0.0.1 逻辑(超管全量 | 有公司看本公司 | 无公司看自己)
  8. var _sessionToken = function () {
  9. if (params && params.token) return params.token;
  10. if (request.body && request.body.token) return request.body.token;
  11. return null;
  12. };
  13. var _getUser = async function (t) {
  14. if (!t) return null;
  15. if (request.user) return request.user;
  16. try {
  17. var q = new Parse.Query(Parse.Session); q.equalTo('sessionToken', t); q.include('user');
  18. var s = await q.first({ useMasterKey: true });
  19. return s ? s.get('user') : null;
  20. } catch (e) { return null; }
  21. };
  22. var _isSuper = async function (user) {
  23. try {
  24. var q = new Parse.Query(Parse.Role); q.equalTo('name', 'FMODE_AGENT_SUPERADMIN'); q.equalTo('users', user);
  25. return (await q.first({ useMasterKey: true })) != null;
  26. } catch (e) { return false; }
  27. };
  28. var _idOf = function (v) {
  29. if (!v) return null;
  30. if (typeof v === 'string') return v;
  31. return v.id || null;
  32. };
  33. var UPDATABLE = ['agentId','title','summary','project','projectName','artifacts','tags','deliveredAt','status','extra'];
  34. function handler(request, response) {
  35. (async function () {
  36. try {
  37. var p = params || {};
  38. var user = await _getUser(_sessionToken());
  39. if (!user) return _respond(response, 401, { code: 401, msg: 'SessionToken 无效' });
  40. var act = p.action || 'list';
  41. var superAd = await _isSuper(user);
  42. var uCompany = user.get('company') || null;
  43. if (act === 'report') {
  44. if (!p.title) return _respond(response, 400, { code: 400, msg: 'title required' });
  45. if (!p.agentId && !p.agentObjectId) return _respond(response, 400, { code: 400, msg: 'agentId/agentObjectId required' });
  46. // ---- 解析 agent:先按 objectId,再按 agentId;都没有则自注册 ----
  47. var agent = null;
  48. if (p.agentObjectId) {
  49. var q0 = new Parse.Query('FmodeAgent'); q0.equalTo('objectId', p.agentObjectId);
  50. agent = await q0.first({ useMasterKey: true });
  51. }
  52. if (!agent && p.agentId) {
  53. var q1 = new Parse.Query('FmodeAgent'); q1.equalTo('agentId', p.agentId);
  54. agent = await q1.first({ useMasterKey: true });
  55. }
  56. if (!agent) {
  57. if (!p.agentId) return _respond(response, 400, { code: 400, msg: '新 agent 注册需要 agentId' });
  58. agent = new Parse.Object('FmodeAgent');
  59. agent.set('agentId', p.agentId);
  60. agent.set('user', user);
  61. agent.set('status', 'online');
  62. agent.set('heartbeatAt', new Date());
  63. agent = await agent.save(null, { useMasterKey: true });
  64. }
  65. // ---- 鉴权:仅该 agent 的 owner(v0.0.2 移除超管代报)----
  66. var agentUserId = _idOf(agent.get('user'));
  67. if (agentUserId && agentUserId !== user.id) {
  68. return _respond(response, 403, { code: 403, msg: '仅该容器的 owner 可上报' });
  69. }
  70. if (!agentUserId) {
  71. agent.set('user', user);
  72. await agent.save(null, { useMasterKey: true });
  73. agentUserId = user.id;
  74. }
  75. // ---- 写交付物 ----
  76. var obj = new Parse.Object('AgentDeliverable');
  77. for (var i = 0; i < UPDATABLE.length; i++) {
  78. var k = UPDATABLE[i];
  79. if (p[k] === undefined) continue;
  80. if (k === 'deliveredAt') obj.set('deliveredAt', new Date(p[k])); else obj.set(k, p[k]);
  81. }
  82. obj.set('agentId', agent.get('agentId') || p.agentId);
  83. obj.set('agent', agent);
  84. obj.set('agentObjectId', agent.id);
  85. obj.set('user', user);
  86. if (agent.get('company')) obj.set('company', agent.get('company'));
  87. obj.set('reportedAt', new Date());
  88. if (p.deliveredAt === undefined) obj.set('deliveredAt', new Date());
  89. if (!p.status) obj.set('status', 'delivered');
  90. var acl = new Parse.ACL();
  91. acl.setReadAccess(user.id, true);
  92. acl.setWriteAccess(user.id, true);
  93. acl.setPublicReadAccess(false);
  94. acl.setPublicWriteAccess(false);
  95. obj.setACL(acl);
  96. var saved = await obj.save(null, { useMasterKey: true });
  97. // ---- 更新 agent 心跳 ----
  98. agent.set('heartbeatAt', new Date());
  99. agent.set('status', 'online');
  100. await agent.save(null, { useMasterKey: true });
  101. return _respond(response, 200, {
  102. code: 200, id: saved.id, agentId: obj.get('agentId'),
  103. agentObjectId: agent.id, title: p.title
  104. });
  105. }
  106. // ---- list / byProject(沿用 v0.0.1)----
  107. var q = new Parse.Query('AgentDeliverable');
  108. if (p.agentId) q.equalTo('agentId', p.agentId);
  109. if (p.project) q.equalTo('project', p.project);
  110. if (!superAd) { if (uCompany) { q.equalTo('company', uCompany); } else { q.equalTo('user', user); } }
  111. if (p.before) q.lessThan('deliveredAt', new Date(p.before));
  112. q.descending('deliveredAt'); q.limit(Math.min(p.limit || 50, 200));
  113. var rows = await q.find({ useMasterKey: true });
  114. var items = rows.map(function (r) {
  115. return { id: r.id, agentId: r.get('agentId'), title: r.get('title'), summary: r.get('summary'),
  116. project: r.get('project'), projectName: r.get('projectName'), artifacts: r.get('artifacts') || [],
  117. tags: r.get('tags') || [], deliveredAt: r.get('deliveredAt'), status: r.get('status') || 'delivered', extra: r.get('extra') };
  118. });
  119. if (act === 'byProject') {
  120. var groups = {};
  121. for (var j = 0; j < items.length; j++) {
  122. var it = items[j]; var key = it.project || '未归类';
  123. if (!groups[key]) groups[key] = { project: key, projectName: it.projectName || key, count: 0, items: [] };
  124. groups[key].count++; groups[key].items.push(it);
  125. }
  126. return _respond(response, 200, { code: 200, groups: Object.keys(groups).map(function (k2) { return groups[k2]; }) });
  127. }
  128. return _respond(response, 200, { code: 200, count: items.length, deliverables: items });
  129. } catch (e) {
  130. return _respond(response, 500, { code: 500, msg: String(e && e.message || e) });
  131. }
  132. })();
  133. }
  134. function _respond(response, status, body) {
  135. if (response && typeof response.status === 'function') { response.status(status); response.json(body); }
  136. else if (response && typeof response.success === 'function') { status === 200 ? response.success(body) : response.error(body); }
  137. return body;
  138. }