| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- // fmodeagent-deliverables v0.0.2 — 智能体任务成果上报与聚合 (表: AgentDeliverable)
- // 调用: POST /api/functions {token, id:<Function objectId>, params:{action:'report'|'list'|'byProject', ...}}
- // v0.0.2 变更:
- // 1. report 移除 FMODE_AGENT_SUPERADMIN 检查 → 仅校验「该 agent 的 owner」;agent 无 owner 时首次上报自动绑定
- // 2. agent 不存在时自动注册(自注册,agentId 有效即可)
- // 3. agentId / agentObjectId 任一可传,自动补全,并写入 agent 指针
- // 4. list/byProject 可见性保持 v0.0.1 逻辑(超管全量 | 有公司看本公司 | 无公司看自己)
- var _sessionToken = function () {
- if (params && params.token) return params.token;
- if (request.body && request.body.token) return request.body.token;
- return null;
- };
- var _getUser = async function (t) {
- if (!t) return null;
- if (request.user) return request.user;
- try {
- var q = new Parse.Query(Parse.Session); q.equalTo('sessionToken', t); q.include('user');
- var s = await q.first({ useMasterKey: true });
- return s ? s.get('user') : null;
- } catch (e) { return null; }
- };
- var _isSuper = async function (user) {
- try {
- var q = new Parse.Query(Parse.Role); q.equalTo('name', 'FMODE_AGENT_SUPERADMIN'); q.equalTo('users', user);
- return (await q.first({ useMasterKey: true })) != null;
- } catch (e) { return false; }
- };
- var _idOf = function (v) {
- if (!v) return null;
- if (typeof v === 'string') return v;
- return v.id || null;
- };
- var UPDATABLE = ['agentId','title','summary','project','projectName','artifacts','tags','deliveredAt','status','extra'];
- function handler(request, response) {
- (async function () {
- try {
- var p = params || {};
- var user = await _getUser(_sessionToken());
- if (!user) return _respond(response, 401, { code: 401, msg: 'SessionToken 无效' });
- var act = p.action || 'list';
- var superAd = await _isSuper(user);
- var uCompany = user.get('company') || null;
- if (act === 'report') {
- if (!p.title) return _respond(response, 400, { code: 400, msg: 'title required' });
- if (!p.agentId && !p.agentObjectId) return _respond(response, 400, { code: 400, msg: 'agentId/agentObjectId required' });
- // ---- 解析 agent:先按 objectId,再按 agentId;都没有则自注册 ----
- var agent = null;
- if (p.agentObjectId) {
- var q0 = new Parse.Query('FmodeAgent'); q0.equalTo('objectId', p.agentObjectId);
- agent = await q0.first({ useMasterKey: true });
- }
- if (!agent && p.agentId) {
- var q1 = new Parse.Query('FmodeAgent'); q1.equalTo('agentId', p.agentId);
- agent = await q1.first({ useMasterKey: true });
- }
- if (!agent) {
- if (!p.agentId) return _respond(response, 400, { code: 400, msg: '新 agent 注册需要 agentId' });
- agent = new Parse.Object('FmodeAgent');
- agent.set('agentId', p.agentId);
- agent.set('user', user);
- agent.set('status', 'online');
- agent.set('heartbeatAt', new Date());
- agent = await agent.save(null, { useMasterKey: true });
- }
- // ---- 鉴权:仅该 agent 的 owner(v0.0.2 移除超管代报)----
- var agentUserId = _idOf(agent.get('user'));
- if (agentUserId && agentUserId !== user.id) {
- return _respond(response, 403, { code: 403, msg: '仅该容器的 owner 可上报' });
- }
- if (!agentUserId) {
- agent.set('user', user);
- await agent.save(null, { useMasterKey: true });
- agentUserId = user.id;
- }
- // ---- 写交付物 ----
- var obj = new Parse.Object('AgentDeliverable');
- for (var i = 0; i < UPDATABLE.length; i++) {
- var k = UPDATABLE[i];
- if (p[k] === undefined) continue;
- if (k === 'deliveredAt') obj.set('deliveredAt', new Date(p[k])); else obj.set(k, p[k]);
- }
- obj.set('agentId', agent.get('agentId') || p.agentId);
- obj.set('agent', agent);
- obj.set('agentObjectId', agent.id);
- obj.set('user', user);
- if (agent.get('company')) obj.set('company', agent.get('company'));
- obj.set('reportedAt', new Date());
- if (p.deliveredAt === undefined) obj.set('deliveredAt', new Date());
- if (!p.status) obj.set('status', 'delivered');
- var acl = new Parse.ACL();
- acl.setReadAccess(user.id, true);
- acl.setWriteAccess(user.id, true);
- acl.setPublicReadAccess(false);
- acl.setPublicWriteAccess(false);
- obj.setACL(acl);
- var saved = await obj.save(null, { useMasterKey: true });
- // ---- 更新 agent 心跳 ----
- agent.set('heartbeatAt', new Date());
- agent.set('status', 'online');
- await agent.save(null, { useMasterKey: true });
- return _respond(response, 200, {
- code: 200, id: saved.id, agentId: obj.get('agentId'),
- agentObjectId: agent.id, title: p.title
- });
- }
- // ---- list / byProject(沿用 v0.0.1)----
- var q = new Parse.Query('AgentDeliverable');
- if (p.agentId) q.equalTo('agentId', p.agentId);
- if (p.project) q.equalTo('project', p.project);
- if (!superAd) { if (uCompany) { q.equalTo('company', uCompany); } else { q.equalTo('user', user); } }
- if (p.before) q.lessThan('deliveredAt', new Date(p.before));
- q.descending('deliveredAt'); q.limit(Math.min(p.limit || 50, 200));
- var rows = await q.find({ useMasterKey: true });
- var items = rows.map(function (r) {
- return { id: r.id, agentId: r.get('agentId'), title: r.get('title'), summary: r.get('summary'),
- project: r.get('project'), projectName: r.get('projectName'), artifacts: r.get('artifacts') || [],
- tags: r.get('tags') || [], deliveredAt: r.get('deliveredAt'), status: r.get('status') || 'delivered', extra: r.get('extra') };
- });
- if (act === 'byProject') {
- var groups = {};
- for (var j = 0; j < items.length; j++) {
- var it = items[j]; var key = it.project || '未归类';
- if (!groups[key]) groups[key] = { project: key, projectName: it.projectName || key, count: 0, items: [] };
- groups[key].count++; groups[key].items.push(it);
- }
- return _respond(response, 200, { code: 200, groups: Object.keys(groups).map(function (k2) { return groups[k2]; }) });
- }
- return _respond(response, 200, { code: 200, count: items.length, deliverables: items });
- } catch (e) {
- return _respond(response, 500, { code: 500, msg: String(e && e.message || e) });
- }
- })();
- }
- function _respond(response, status, body) {
- if (response && typeof response.status === 'function') { response.status(status); response.json(body); }
- else if (response && typeof response.success === 'function') { status === 200 ? response.success(body) : response.error(body); }
- return body;
- }
|