acp-ts 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentcp.d.ts +5 -0
- package/dist/agentcp.js +36 -0
- package/dist/group/client.js +11 -2
- package/dist/server.js +267 -36
- package/package.json +1 -1
package/dist/agentcp.d.ts
CHANGED
|
@@ -85,6 +85,11 @@ declare class AgentCP implements IAgentCP {
|
|
|
85
85
|
* 设置群组事件处理器
|
|
86
86
|
*/
|
|
87
87
|
setGroupEventHandler(handler: ACPGroupEventHandler): void;
|
|
88
|
+
/**
|
|
89
|
+
* 创建默认的群组事件处理器(防御性,仅打印日志)。
|
|
90
|
+
* 外部可通过 setGroupEventHandler 覆盖。
|
|
91
|
+
*/
|
|
92
|
+
private _createDefaultGroupEventHandler;
|
|
88
93
|
/**
|
|
89
94
|
* 设置群组游标存储
|
|
90
95
|
*/
|
package/dist/agentcp.js
CHANGED
|
@@ -333,6 +333,8 @@ class AgentCP {
|
|
|
333
333
|
};
|
|
334
334
|
this.groupClient = new group_1.ACPGroupClient(this.activeAid, sendFunc);
|
|
335
335
|
this.groupOps = new group_1.GroupOperations(this.groupClient);
|
|
336
|
+
// 设置默认 event handler,防止通知被静默丢弃
|
|
337
|
+
this.groupClient.setEventHandler(this._createDefaultGroupEventHandler());
|
|
336
338
|
}
|
|
337
339
|
/**
|
|
338
340
|
* 初始化跨AP群组客户端。
|
|
@@ -349,6 +351,8 @@ class AgentCP {
|
|
|
349
351
|
};
|
|
350
352
|
this.groupClient = new group_1.ACPGroupClient(this.activeAid, sendFunc);
|
|
351
353
|
this.groupOps = new group_1.GroupOperations(this.groupClient);
|
|
354
|
+
// 设置默认 event handler,防止通知被静默丢弃
|
|
355
|
+
this.groupClient.setEventHandler(this._createDefaultGroupEventHandler());
|
|
352
356
|
}
|
|
353
357
|
/**
|
|
354
358
|
* 处理群组协议消息路由。
|
|
@@ -393,6 +397,38 @@ class AgentCP {
|
|
|
393
397
|
this.groupClient.setEventHandler(handler);
|
|
394
398
|
}
|
|
395
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* 创建默认的群组事件处理器(防御性,仅打印日志)。
|
|
402
|
+
* 外部可通过 setGroupEventHandler 覆盖。
|
|
403
|
+
*/
|
|
404
|
+
_createDefaultGroupEventHandler() {
|
|
405
|
+
return {
|
|
406
|
+
onNewMessage(groupId, latestMsgId, sender, preview) {
|
|
407
|
+
console.log(`[Group][DefaultHandler] onNewMessage: group=${groupId} msgId=${latestMsgId} sender=${sender} preview=${preview}`);
|
|
408
|
+
},
|
|
409
|
+
onNewEvent(groupId, latestEventId, eventType, summary) {
|
|
410
|
+
console.log(`[Group][DefaultHandler] onNewEvent: group=${groupId} eventId=${latestEventId} type=${eventType}`);
|
|
411
|
+
},
|
|
412
|
+
onGroupInvite(groupId, groupAddress, invitedBy) {
|
|
413
|
+
console.log(`[Group][DefaultHandler] onGroupInvite: group=${groupId} address=${groupAddress} invitedBy=${invitedBy}`);
|
|
414
|
+
},
|
|
415
|
+
onJoinApproved(groupId, groupAddress) {
|
|
416
|
+
console.log(`[Group][DefaultHandler] onJoinApproved: group=${groupId} address=${groupAddress}`);
|
|
417
|
+
},
|
|
418
|
+
onJoinRejected(groupId, reason) {
|
|
419
|
+
console.log(`[Group][DefaultHandler] onJoinRejected: group=${groupId} reason=${reason}`);
|
|
420
|
+
},
|
|
421
|
+
onJoinRequestReceived(groupId, agentId, message) {
|
|
422
|
+
console.log(`[Group][DefaultHandler] onJoinRequestReceived: group=${groupId} agent=${agentId}`);
|
|
423
|
+
},
|
|
424
|
+
onGroupMessage(groupId, msg) {
|
|
425
|
+
console.log(`[Group][DefaultHandler] onGroupMessage: group=${groupId} msgId=${msg.msg_id} sender=${msg.sender}`);
|
|
426
|
+
},
|
|
427
|
+
onGroupEvent(groupId, evt) {
|
|
428
|
+
console.log(`[Group][DefaultHandler] onGroupEvent: group=${groupId} event=${evt.event_type}`);
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
}
|
|
396
432
|
/**
|
|
397
433
|
* 设置群组游标存储
|
|
398
434
|
*/
|
package/dist/group/client.js
CHANGED
|
@@ -74,7 +74,7 @@ class ACPGroupClient {
|
|
|
74
74
|
* Called by the message dispatch chain in AgentCP.
|
|
75
75
|
*/
|
|
76
76
|
handleIncoming(payload) {
|
|
77
|
-
var _a, _b;
|
|
77
|
+
var _a, _b, _c;
|
|
78
78
|
// console.log(`[GroupClient] <<< handleIncoming raw payload (first 500 chars): ${payload.substring(0, 500)}`);
|
|
79
79
|
let data;
|
|
80
80
|
try {
|
|
@@ -95,6 +95,12 @@ class ACPGroupClient {
|
|
|
95
95
|
clearTimeout(pending.timer);
|
|
96
96
|
this._pendingReqs.delete(requestId);
|
|
97
97
|
pending.resolve(resp);
|
|
98
|
+
// 如果响应同时携带 event 字段,也要 dispatch 通知
|
|
99
|
+
const event = (_b = data.event) !== null && _b !== void 0 ? _b : "";
|
|
100
|
+
if (event && this._handler != null) {
|
|
101
|
+
const notify = (0, types_1.parseGroupNotify)(data);
|
|
102
|
+
(0, events_1.dispatchAcpNotify)(this._handler, notify);
|
|
103
|
+
}
|
|
98
104
|
return;
|
|
99
105
|
}
|
|
100
106
|
else {
|
|
@@ -102,12 +108,15 @@ class ACPGroupClient {
|
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
// Try as notification (has event field)
|
|
105
|
-
const event = (
|
|
111
|
+
const event = (_c = data.event) !== null && _c !== void 0 ? _c : "";
|
|
106
112
|
if (event) {
|
|
107
113
|
const notify = (0, types_1.parseGroupNotify)(data);
|
|
108
114
|
if (this._handler != null) {
|
|
109
115
|
(0, events_1.dispatchAcpNotify)(this._handler, notify);
|
|
110
116
|
}
|
|
117
|
+
else {
|
|
118
|
+
console.warn(`[GroupClient] !!! notification event="${event}" dropped: no event handler registered. Call setEventHandler() first.`);
|
|
119
|
+
}
|
|
111
120
|
return;
|
|
112
121
|
}
|
|
113
122
|
console.warn(`[GroupClient] !!! unhandled incoming message: no request_id and no event field`, JSON.stringify(data).substring(0, 300));
|
package/dist/server.js
CHANGED
|
@@ -84,7 +84,8 @@ async function doEnsureOnline(aid) {
|
|
|
84
84
|
const cp = new agentcp_1.AgentCP(globalApiUrl, '', globalDataDir || undefined, { persistMessages: true, persistGroupMessages: true });
|
|
85
85
|
await cp.loadAid(aid);
|
|
86
86
|
cp.setAutoGenerateAgentMd(true);
|
|
87
|
-
|
|
87
|
+
const customOpts = getAidMdOptionsForAid(aid);
|
|
88
|
+
cp.setAgentMdOptions(Object.assign({ type: 'human', tags: ['human', 'acp'] }, customOpts));
|
|
88
89
|
const connConfig = await cp.online();
|
|
89
90
|
console.log(`[Server] 自动上线 AID: ${aid}`);
|
|
90
91
|
const hb = new heartbeat_1.HeartbeatClient(aid, connConfig.heartbeatServer, '');
|
|
@@ -223,6 +224,37 @@ async function ensureGroupClient(instance) {
|
|
|
223
224
|
instance.agentWS.onRawMessage((message) => {
|
|
224
225
|
return instance.agentCP.handleGroupMessage(message);
|
|
225
226
|
});
|
|
227
|
+
// 注册群组事件处理器,确保 SDK 通知回调可靠触发
|
|
228
|
+
instance.agentCP.setGroupEventHandler({
|
|
229
|
+
onNewMessage(groupId, latestMsgId, sender, preview) {
|
|
230
|
+
console.log(`[Group] onNewMessage: group=${groupId} msgId=${latestMsgId} sender=${sender} preview=${preview}`);
|
|
231
|
+
// 收到新消息通知时,主动拉取并存储到本地
|
|
232
|
+
instance.agentCP.pullAndStoreGroupMessages(groupId, 20).catch(e => {
|
|
233
|
+
console.error(`[Group] auto-pull messages failed for group=${groupId}:`, e);
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
onNewEvent(groupId, latestEventId, eventType, summary) {
|
|
237
|
+
console.log(`[Group] onNewEvent: group=${groupId} eventId=${latestEventId} type=${eventType} summary=${summary}`);
|
|
238
|
+
},
|
|
239
|
+
onGroupInvite(groupId, groupAddress, invitedBy) {
|
|
240
|
+
console.log(`[Group] onGroupInvite: group=${groupId} address=${groupAddress} invitedBy=${invitedBy}`);
|
|
241
|
+
},
|
|
242
|
+
onJoinApproved(groupId, groupAddress) {
|
|
243
|
+
console.log(`[Group] onJoinApproved: group=${groupId} address=${groupAddress}`);
|
|
244
|
+
},
|
|
245
|
+
onJoinRejected(groupId, reason) {
|
|
246
|
+
console.log(`[Group] onJoinRejected: group=${groupId} reason=${reason}`);
|
|
247
|
+
},
|
|
248
|
+
onJoinRequestReceived(groupId, agentId, message) {
|
|
249
|
+
console.log(`[Group] onJoinRequestReceived: group=${groupId} agent=${agentId} msg=${message}`);
|
|
250
|
+
},
|
|
251
|
+
onGroupMessage(groupId, msg) {
|
|
252
|
+
console.log(`[Group] onGroupMessage: group=${groupId} msgId=${msg.msg_id} sender=${msg.sender}`);
|
|
253
|
+
},
|
|
254
|
+
onGroupEvent(groupId, evt) {
|
|
255
|
+
console.log(`[Group] onGroupEvent: group=${groupId} event=${evt.event_type}`);
|
|
256
|
+
},
|
|
257
|
+
});
|
|
226
258
|
instance.groupInitialized = true;
|
|
227
259
|
instance.groupSessionId = groupSessionId;
|
|
228
260
|
instance.groupTargetAid = targetAid;
|
|
@@ -257,7 +289,7 @@ async function getAidStatusList() {
|
|
|
257
289
|
}
|
|
258
290
|
return result;
|
|
259
291
|
}
|
|
260
|
-
// agent.md 信息缓存 (aid -> { type, name, description, cachedAt })
|
|
292
|
+
// agent.md 信息缓存 (aid -> { type, name, description, tags, cachedAt })
|
|
261
293
|
// 内存缓存 + 本地文件持久化,TTL 24 小时
|
|
262
294
|
const agentInfoCache = new Map();
|
|
263
295
|
const AGENT_INFO_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
|
@@ -274,7 +306,7 @@ function loadAgentInfoCacheFromDisk() {
|
|
|
274
306
|
const now = Date.now();
|
|
275
307
|
for (const [aid, info] of entries) {
|
|
276
308
|
if (now - info.cachedAt < AGENT_INFO_CACHE_TTL) {
|
|
277
|
-
agentInfoCache.set(aid, info);
|
|
309
|
+
agentInfoCache.set(aid, Object.assign(Object.assign({}, info), { tags: info.tags || [] }));
|
|
278
310
|
}
|
|
279
311
|
}
|
|
280
312
|
console.log(`[Server] 已加载 agent info 缓存: ${agentInfoCache.size} 条`);
|
|
@@ -315,7 +347,7 @@ function fetchAgentMd(aid) {
|
|
|
315
347
|
});
|
|
316
348
|
}
|
|
317
349
|
function parseAgentMdFrontmatter(content) {
|
|
318
|
-
const result = { type: '', name: '', description: '' };
|
|
350
|
+
const result = { type: '', name: '', description: '', tags: [] };
|
|
319
351
|
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
320
352
|
if (!match)
|
|
321
353
|
return result;
|
|
@@ -329,12 +361,20 @@ function parseAgentMdFrontmatter(content) {
|
|
|
329
361
|
result.name = nameMatch[1].trim();
|
|
330
362
|
if (descMatch)
|
|
331
363
|
result.description = descMatch[1].trim();
|
|
364
|
+
// parse tags list
|
|
365
|
+
const tagsBlock = yaml.match(/^tags:\s*\n((?:\s+-\s+.*\n?)*)/m);
|
|
366
|
+
if (tagsBlock) {
|
|
367
|
+
const tagLines = tagsBlock[1].match(/^\s+-\s+(.+)$/gm);
|
|
368
|
+
if (tagLines) {
|
|
369
|
+
result.tags = tagLines.map(l => l.replace(/^\s+-\s+/, '').trim().replace(/^"(.*)"$/, '$1'));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
332
372
|
return result;
|
|
333
373
|
}
|
|
334
374
|
async function getAgentInfo(aid) {
|
|
335
375
|
const cached = agentInfoCache.get(aid);
|
|
336
376
|
if (cached && Date.now() - cached.cachedAt < AGENT_INFO_CACHE_TTL) {
|
|
337
|
-
return { type: cached.type, name: cached.name, description: cached.description };
|
|
377
|
+
return { type: cached.type, name: cached.name, description: cached.description, tags: cached.tags || [] };
|
|
338
378
|
}
|
|
339
379
|
try {
|
|
340
380
|
const md = await fetchAgentMd(aid);
|
|
@@ -346,10 +386,40 @@ async function getAgentInfo(aid) {
|
|
|
346
386
|
catch (_a) {
|
|
347
387
|
// 远程请求失败时,如果有过期缓存也先用着
|
|
348
388
|
if (cached) {
|
|
349
|
-
return { type: cached.type, name: cached.name, description: cached.description };
|
|
389
|
+
return { type: cached.type, name: cached.name, description: cached.description, tags: cached.tags || [] };
|
|
350
390
|
}
|
|
351
|
-
return { type: '', name: '', description: '' };
|
|
391
|
+
return { type: '', name: '', description: '', tags: [] };
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// 每个 AID 的自定义 agent.md 选项 (昵称、描述)
|
|
395
|
+
function getAidMdOptionsPath() {
|
|
396
|
+
const dir = globalDataDir || process.cwd();
|
|
397
|
+
return path.join(dir, 'AIDs', '.aid-md-options.json');
|
|
398
|
+
}
|
|
399
|
+
function loadAidMdOptions() {
|
|
400
|
+
try {
|
|
401
|
+
const filePath = getAidMdOptionsPath();
|
|
402
|
+
if (fs.existsSync(filePath)) {
|
|
403
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch (_a) { }
|
|
407
|
+
return {};
|
|
408
|
+
}
|
|
409
|
+
function saveAidMdOptions(aid, opts) {
|
|
410
|
+
try {
|
|
411
|
+
const filePath = getAidMdOptionsPath();
|
|
412
|
+
const dir = path.dirname(filePath);
|
|
413
|
+
if (!fs.existsSync(dir))
|
|
414
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
415
|
+
const all = loadAidMdOptions();
|
|
416
|
+
all[aid] = opts;
|
|
417
|
+
fs.writeFileSync(filePath, JSON.stringify(all, null, 2), 'utf-8');
|
|
352
418
|
}
|
|
419
|
+
catch (_a) { }
|
|
420
|
+
}
|
|
421
|
+
function getAidMdOptionsForAid(aid) {
|
|
422
|
+
return loadAidMdOptions()[aid] || {};
|
|
353
423
|
}
|
|
354
424
|
// 消息与会话管理 — 每个 AID 独立 MessageStore
|
|
355
425
|
let activeSessionId = null;
|
|
@@ -450,6 +520,10 @@ const indexHtml = `<!DOCTYPE html>
|
|
|
450
520
|
<span class="dot-separator">.</span>
|
|
451
521
|
<select id="apSelect"></select>
|
|
452
522
|
</div>
|
|
523
|
+
<div style="display:flex;gap:8px;margin-top:8px;">
|
|
524
|
+
<input type="text" id="aidNickname" placeholder="昵称(选填)" style="flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-size:14px;">
|
|
525
|
+
<input type="text" id="aidDescription" placeholder="描述(选填)" style="flex:2;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-size:14px;">
|
|
526
|
+
</div>
|
|
453
527
|
<button class="btn btn-primary" onclick="createAid()">注册 AID</button>
|
|
454
528
|
</div>
|
|
455
529
|
|
|
@@ -558,10 +632,12 @@ const indexHtml = `<!DOCTYPE html>
|
|
|
558
632
|
var ap = document.getElementById('apSelect').value;
|
|
559
633
|
if (!ap) { showStatus('请选择 AP', 'error'); return; }
|
|
560
634
|
var fullPrefix = prefix + '.' + ap;
|
|
635
|
+
var nickname = document.getElementById('aidNickname').value.trim();
|
|
636
|
+
var description = document.getElementById('aidDescription').value.trim();
|
|
561
637
|
try {
|
|
562
|
-
var res = await fetch('/api/aid/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prefix: fullPrefix }) });
|
|
638
|
+
var res = await fetch('/api/aid/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prefix: fullPrefix, nickname: nickname, description: description }) });
|
|
563
639
|
var data = await res.json();
|
|
564
|
-
if (data.success) { showStatus('AID 注册成功', 'success'); document.getElementById('newAid').value = ''; loadAidInfo(); }
|
|
640
|
+
if (data.success) { showStatus('AID 注册成功', 'success'); document.getElementById('newAid').value = ''; document.getElementById('aidNickname').value = ''; document.getElementById('aidDescription').value = ''; loadAidInfo(); }
|
|
565
641
|
else { showStatus(data.error || '注册失败', 'error'); }
|
|
566
642
|
} catch (e) { showStatus('注册失败: ' + e.message, 'error'); }
|
|
567
643
|
}
|
|
@@ -659,12 +735,12 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
659
735
|
|
|
660
736
|
/* AID Group */
|
|
661
737
|
.aid-group { border-bottom:1px solid var(--border); }
|
|
662
|
-
.aid-group-header { padding:12px 14px; display:flex; align-items:center; cursor:pointer; background:linear-gradient(135deg,#
|
|
663
|
-
.aid-group-header:hover { background:linear-gradient(135deg,#
|
|
738
|
+
.aid-group-header { padding:12px 14px; display:flex; align-items:center; cursor:pointer; background:linear-gradient(135deg,#eef4ff,#e8f0fe); user-select:none; border-left:3px solid var(--primary); transition:all 0.2s; }
|
|
739
|
+
.aid-group-header:hover { background:linear-gradient(135deg,#dbeafe,#d0e4fd); }
|
|
664
740
|
.aid-group-info { flex:1; min-width:0; margin-left:4px; }
|
|
665
|
-
.aid-group-title { font-size:13px; font-weight:700; color:
|
|
666
|
-
.aid-group-desc { font-size:10px; color
|
|
667
|
-
.aid-group-arrow { font-size:10px; color:var(--
|
|
741
|
+
.aid-group-title { font-size:13px; font-weight:700; color:#1e40af; background:linear-gradient(135deg,#dbeafe,#c7d7fe); padding:2px 8px; border-radius:6px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:inline-block; max-width:100%; border:1px solid #bfdbfe; }
|
|
742
|
+
.aid-group-desc { font-size:10px; color:#6b7280; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-top:3px; display:block; padding-left:2px; }
|
|
743
|
+
.aid-group-arrow { font-size:10px; color:var(--primary); transition:transform 0.2s; flex-shrink:0; }
|
|
668
744
|
.aid-group-arrow.open { transform:rotate(90deg); }
|
|
669
745
|
.aid-group-badge { font-size:10px; background:var(--primary); color:#fff; padding:1px 6px; border-radius:8px; margin-left:8px; flex-shrink:0; }
|
|
670
746
|
.aid-group-add { background:none; border:1px solid var(--border); color:var(--t2); width:22px; height:22px; border-radius:4px; cursor:pointer; font-size:14px; line-height:20px; text-align:center; margin-left:6px; flex-shrink:0; }
|
|
@@ -672,24 +748,25 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
672
748
|
.aid-group-del { background:none; border:none; color:var(--t2); width:20px; height:20px; border-radius:4px; cursor:pointer; font-size:12px; line-height:20px; text-align:center; margin-left:4px; flex-shrink:0; display:none; }
|
|
673
749
|
.aid-group-header:hover .aid-group-del { display:block; }
|
|
674
750
|
.aid-group-del:hover { color:#dc3545; background:#ffebeb; }
|
|
675
|
-
.session-del { position:absolute; right:8px; top:
|
|
751
|
+
.session-del { position:absolute; right:8px; top:50%; transform:translateY(-50%); background:none; border:none; color:var(--t2); font-size:12px; cursor:pointer; display:none; padding:2px; }
|
|
676
752
|
.session-item:hover .session-del { display:block; }
|
|
677
753
|
.session-del:hover { color:#dc3545; }
|
|
678
|
-
.aid-group-sessions { display:none; }
|
|
754
|
+
.aid-group-sessions { display:none; background:#fafbfc; }
|
|
679
755
|
.aid-group-sessions.open { display:block; }
|
|
680
756
|
|
|
681
|
-
.aid-group-avatar { width:
|
|
757
|
+
.aid-group-avatar { width:36px; height:36px; border-radius:50%; object-fit:cover; flex-shrink:0; margin-right:8px; box-shadow:0 1px 4px rgba(37,99,235,0.18); border:2px solid #bfdbfe; }
|
|
682
758
|
|
|
683
|
-
.session-item { padding:10px 14px 10px 32px; border-bottom:1px solid #
|
|
684
|
-
.session-item::before { content:''; position:absolute; left:18px; top:
|
|
685
|
-
.session-item:hover { background:#
|
|
759
|
+
.session-item { padding:10px 14px 10px 32px; border-bottom:1px solid #f0f1f3; cursor:pointer; transition:all 0.15s; position:relative; }
|
|
760
|
+
.session-item::before { content:''; position:absolute; left:18px; top:50%; transform:translateY(-50%); width:6px; height:6px; border-radius:50%; background:#d1d5db; }
|
|
761
|
+
.session-item:hover { background:#f0f5ff; }
|
|
686
762
|
.session-item.active { background:#eff6ff; border-left:3px solid var(--primary); padding-left:29px; }
|
|
687
|
-
.session-item.active::before { background:var(--primary); }
|
|
688
|
-
.session-peer { font-weight:
|
|
689
|
-
.session-
|
|
690
|
-
.
|
|
763
|
+
.session-item.active::before { background:var(--primary); box-shadow:0 0 0 2px rgba(37,99,235,0.2); }
|
|
764
|
+
.session-peer { font-weight:500; font-size:12px; color:var(--t1); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-left:10px; background:#f1f5f9; border-radius:4px; padding:3px 8px 3px 10px; border:1px solid #e8ecf1; }
|
|
765
|
+
.session-item.active .session-peer { background:#dbeafe; border-color:#bfdbfe; color:#1e40af; }
|
|
766
|
+
.session-meta { font-size:10px; color:var(--t2); margin-top:4px; display:flex; align-items:center; gap:6px; padding-left:10px; }
|
|
767
|
+
.tag { font-size:9px; padding:1px 5px; border-radius:3px; color:#fff; font-weight:600; letter-spacing:0.3px; }
|
|
691
768
|
.tag.outgoing { background:var(--ok); }
|
|
692
|
-
.tag.incoming { background
|
|
769
|
+
.tag.incoming { background:#8b5cf6; }
|
|
693
770
|
|
|
694
771
|
/* Chat Area */
|
|
695
772
|
.chat-area { flex:1; display:flex; flex-direction:column; background:var(--chat-bg); min-width:0; }
|
|
@@ -755,7 +832,8 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
755
832
|
.bubble ul, .bubble ol { padding-left:1.5em; margin-bottom:0.5em; }
|
|
756
833
|
.bubble li { margin-bottom:0.2em; }
|
|
757
834
|
.bubble blockquote { margin:0.5em 0; padding-left:1em; border-left:4px solid rgba(0,0,0,0.1); color:var(--t2); }
|
|
758
|
-
.bubble a { color:var(--primary); text-decoration:
|
|
835
|
+
.bubble a { color:var(--primary); text-decoration:underline; } .bubble a:hover { opacity:0.85; }
|
|
836
|
+
.message.sent .bubble a { color:#fff; } .message.sent .bubble a:hover { opacity:0.85; }
|
|
759
837
|
.bubble img { max-width:100%; border-radius:4px; }
|
|
760
838
|
.bubble code { background:rgba(0,0,0,0.1); padding:2px 4px; border-radius:3px; font-family:monospace; font-size:0.9em; }
|
|
761
839
|
.bubble pre { background:#2d2d2d; color:#fff; padding:12px; border-radius:6px; overflow-x:auto; margin:8px 0; }
|
|
@@ -908,9 +986,9 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
908
986
|
</div>
|
|
909
987
|
</div>
|
|
910
988
|
<div class="modal-overlay" id="membersModal">
|
|
911
|
-
<div class="modal">
|
|
989
|
+
<div class="modal" style="max-width:520px;">
|
|
912
990
|
<h3>群组成员</h3>
|
|
913
|
-
<div id="membersList" style="max-height:
|
|
991
|
+
<div id="membersList" style="max-height:400px;overflow-y:auto;margin-bottom:16px;font-size:13px;"></div>
|
|
914
992
|
<div class="modal-btns">
|
|
915
993
|
<button class="mbtn mbtn-cancel" onclick="hideMembersModal()">关闭</button>
|
|
916
994
|
</div>
|
|
@@ -942,7 +1020,7 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
942
1020
|
var d = await r.json();
|
|
943
1021
|
if (d.type || d.name) { agentInfoCache[aid] = d; }
|
|
944
1022
|
return d;
|
|
945
|
-
} catch(e) { return { type:'', name:'', description:'' }; }
|
|
1023
|
+
} catch(e) { return { type:'', name:'', description:'', tags:[] }; }
|
|
946
1024
|
}
|
|
947
1025
|
async function deleteSession(e, sessionId){
|
|
948
1026
|
e.stopPropagation();
|
|
@@ -1040,12 +1118,17 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
1040
1118
|
var _pollCount=0;
|
|
1041
1119
|
async function poll(){
|
|
1042
1120
|
try {
|
|
1043
|
-
var
|
|
1044
|
-
var
|
|
1045
|
-
if(sd.sessions) updateSessions(sd.sessions, sd.activeSessionId);
|
|
1046
|
-
S.closed=md.closed||false;
|
|
1047
|
-
if(md.messages) renderMsgs(md.messages, S.closed);
|
|
1121
|
+
var wr=await fetch('/api/ws/status');
|
|
1122
|
+
var wd=await wr.json();
|
|
1048
1123
|
updateDot(wd.status);
|
|
1124
|
+
// P2P会话和消息仅在P2P标签页时刷新
|
|
1125
|
+
if(S.tab==='p2p'){
|
|
1126
|
+
var [sr,mr] = await Promise.all([fetch('/api/sessions'),fetch('/api/messages')]);
|
|
1127
|
+
var sd=await sr.json(), md=await mr.json();
|
|
1128
|
+
if(sd.sessions) updateSessions(sd.sessions, sd.activeSessionId);
|
|
1129
|
+
S.closed=md.closed||false;
|
|
1130
|
+
if(md.messages) renderMsgs(md.messages, S.closed);
|
|
1131
|
+
}
|
|
1049
1132
|
// 每5次轮询刷新一次AID在线状态
|
|
1050
1133
|
if(++_pollCount%5===0){
|
|
1051
1134
|
var ar=await fetch('/api/aid'); var ad=await ar.json();
|
|
@@ -1273,6 +1356,8 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
1273
1356
|
D.groupInfoBar.style.display=S.activeGroupId?'flex':'none';
|
|
1274
1357
|
D.input.placeholder='输入群消息...';
|
|
1275
1358
|
D.input.disabled=!S.activeGroupId;
|
|
1359
|
+
D.msgs.dataset.s='';
|
|
1360
|
+
_lastGroupMsgSig='';
|
|
1276
1361
|
initGroupClient();
|
|
1277
1362
|
pollGroupList();
|
|
1278
1363
|
if(S.activeGroupId) pollGroupMessages();
|
|
@@ -1282,6 +1367,17 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
1282
1367
|
D.groupInfoBar.style.display='none';
|
|
1283
1368
|
D.input.placeholder='输入消息...';
|
|
1284
1369
|
D.input.disabled=false;
|
|
1370
|
+
_lastGroupMsgSig='';
|
|
1371
|
+
// 切回P2P时立即刷新消息
|
|
1372
|
+
D.msgs.dataset.s='';
|
|
1373
|
+
if(S.sid){
|
|
1374
|
+
fetch('/api/messages').then(function(r){ return r.json(); }).then(function(d){
|
|
1375
|
+
S.closed=d.closed||false;
|
|
1376
|
+
if(d.messages) renderMsgs(d.messages, S.closed);
|
|
1377
|
+
}).catch(function(){});
|
|
1378
|
+
} else {
|
|
1379
|
+
D.msgs.innerHTML='';
|
|
1380
|
+
}
|
|
1285
1381
|
}
|
|
1286
1382
|
}
|
|
1287
1383
|
|
|
@@ -1477,6 +1573,62 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
1477
1573
|
} catch(e){ alert('生成邀请码失败: '+e.message); }
|
|
1478
1574
|
}
|
|
1479
1575
|
|
|
1576
|
+
function copyMemberAid(btn,aid){
|
|
1577
|
+
navigator.clipboard.writeText(aid).then(function(){
|
|
1578
|
+
btn.textContent='已复制';
|
|
1579
|
+
setTimeout(function(){ btn.textContent='复制'; },1200);
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
async function openAgentMdPage(aid){
|
|
1584
|
+
try {
|
|
1585
|
+
var r=await fetch('/api/agent-md-raw?aid='+encodeURIComponent(aid));
|
|
1586
|
+
var d=await r.json();
|
|
1587
|
+
if(!d.success||!d.content){ alert(d.error||'获取 agent.md 失败'); return; }
|
|
1588
|
+
var md=d.content;
|
|
1589
|
+
// 简单 markdown 渲染
|
|
1590
|
+
function renderMd(src){
|
|
1591
|
+
var h=escH(src);
|
|
1592
|
+
// headings
|
|
1593
|
+
h=h.replace(/^######\\s+(.+)$/gm,'<h6>$1</h6>');
|
|
1594
|
+
h=h.replace(/^#####\\s+(.+)$/gm,'<h5>$1</h5>');
|
|
1595
|
+
h=h.replace(/^####\\s+(.+)$/gm,'<h4>$1</h4>');
|
|
1596
|
+
h=h.replace(/^###\\s+(.+)$/gm,'<h3>$1</h3>');
|
|
1597
|
+
h=h.replace(/^##\\s+(.+)$/gm,'<h2>$1</h2>');
|
|
1598
|
+
h=h.replace(/^#\\s+(.+)$/gm,'<h1>$1</h1>');
|
|
1599
|
+
// bold & italic
|
|
1600
|
+
h=h.replace(/\\*\\*(.+?)\\*\\*/g,'<strong>$1</strong>');
|
|
1601
|
+
h=h.replace(/\\*(.+?)\\*/g,'<em>$1</em>');
|
|
1602
|
+
// blockquote
|
|
1603
|
+
h=h.replace(/^>\\s?(.+)$/gm,'<blockquote style="border-left:3px solid #ddd;padding-left:12px;color:#666;margin:8px 0;">$1</blockquote>');
|
|
1604
|
+
// list items
|
|
1605
|
+
h=h.replace(/^-\\s+(.+)$/gm,'<li>$1</li>');
|
|
1606
|
+
// code inline
|
|
1607
|
+
var bt=String.fromCharCode(96);
|
|
1608
|
+
h=h.replace(new RegExp(bt+'([^'+bt+']+)'+bt,'g'),'<code style="background:#f5f5f5;padding:1px 4px;border-radius:3px;font-size:12px;">$1</code>');
|
|
1609
|
+
// frontmatter block: hide ---...---
|
|
1610
|
+
h=h.replace(/^---[\\s\\S]*?---\\s*/,'');
|
|
1611
|
+
// paragraphs
|
|
1612
|
+
h=h.replace(/\\n\\n/g,'</p><p>');
|
|
1613
|
+
h='<p>'+h+'</p>';
|
|
1614
|
+
return h;
|
|
1615
|
+
}
|
|
1616
|
+
var html='<!DOCTYPE html><html><head><meta charset="utf-8"><title>'+escH(aid)+' - Agent Profile</title>'
|
|
1617
|
+
+'<style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;max-width:720px;margin:40px auto;padding:0 20px;color:#333;line-height:1.6;}'
|
|
1618
|
+
+'h1{border-bottom:2px solid #eee;padding-bottom:8px;}h2{border-bottom:1px solid #eee;padding-bottom:6px;margin-top:24px;}'
|
|
1619
|
+
+'ul{padding-left:20px;}li{margin:4px 0;}blockquote{margin:12px 0;}'
|
|
1620
|
+
+'pre{background:#f5f5f5;padding:12px;border-radius:6px;overflow-x:auto;}'
|
|
1621
|
+
+'.aid-badge{display:inline-block;background:#e8f4fd;color:#0969da;padding:2px 8px;border-radius:10px;font-size:12px;font-family:monospace;margin-bottom:16px;}'
|
|
1622
|
+
+'</style></head><body>'
|
|
1623
|
+
+'<div class="aid-badge">'+escH(aid)+'</div>'
|
|
1624
|
+
+renderMd(md)
|
|
1625
|
+
+'</body></html>';
|
|
1626
|
+
var w=window.open('','_blank');
|
|
1627
|
+
if(w){ w.document.write(html); w.document.close(); }
|
|
1628
|
+
else { alert('弹窗被拦截,请允许弹窗后重试'); }
|
|
1629
|
+
} catch(e){ alert('获取 agent.md 失败: '+e.message); }
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1480
1632
|
async function showGroupMembers(){
|
|
1481
1633
|
if(!S.activeGroupId) return;
|
|
1482
1634
|
try {
|
|
@@ -1485,10 +1637,62 @@ const chatHtml = `<!DOCTYPE html>
|
|
|
1485
1637
|
if(d.members){
|
|
1486
1638
|
var html=d.members.map(function(m){
|
|
1487
1639
|
var aid=m.agent_id||m;
|
|
1488
|
-
|
|
1489
|
-
|
|
1640
|
+
if(typeof aid!=='string') aid=JSON.stringify(aid);
|
|
1641
|
+
var role=m.role||'';
|
|
1642
|
+
var cachedInfo=agentInfoCache[aid];
|
|
1643
|
+
var avatarSrc=getAvatarSrc(cachedInfo?cachedInfo.type:'');
|
|
1644
|
+
var displayName=(cachedInfo&&cachedInfo.name)?cachedInfo.name:aid.split('.')[0];
|
|
1645
|
+
var typeTags='';
|
|
1646
|
+
if(cachedInfo&&cachedInfo.tags&&cachedInfo.tags.length){
|
|
1647
|
+
typeTags=cachedInfo.tags.map(function(t){ return '<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;margin-right:4px;">'+escH(t)+'</span>'; }).join('');
|
|
1648
|
+
} else if(cachedInfo&&cachedInfo.type){
|
|
1649
|
+
typeTags='<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;">'+escH(cachedInfo.type)+'</span>';
|
|
1650
|
+
}
|
|
1651
|
+
if(role){ typeTags+='<span style="display:inline-block;background:#fff3cd;color:#856404;padding:1px 6px;border-radius:8px;font-size:10px;margin-left:4px;">'+escH(role)+'</span>'; }
|
|
1652
|
+
var safeId='member-'+escH(aid).replace(/\\./g,'_');
|
|
1653
|
+
return '<div id="'+safeId+'" style="padding:10px 0;border-bottom:1px solid #f3f4f6;display:flex;align-items:center;gap:10px;">'
|
|
1654
|
+
+'<img src="'+avatarSrc+'" style="width:36px;height:36px;border-radius:50%;flex-shrink:0;" class="member-avatar" data-aid="'+escH(aid)+'">'
|
|
1655
|
+
+'<div style="flex:1;min-width:0;">'
|
|
1656
|
+
+'<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">'
|
|
1657
|
+
+'<span style="font-size:13px;font-weight:500;" class="member-name" data-aid="'+escH(aid)+'">'+escH(displayName)+'</span>'
|
|
1658
|
+
+'<span class="member-tags" data-aid="'+escH(aid)+'">'+typeTags+'</span>'
|
|
1659
|
+
+'</div>'
|
|
1660
|
+
+'<div style="font-size:11px;color:var(--t2);font-family:monospace;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">'+escH(aid)+'</div>'
|
|
1661
|
+
+'</div>'
|
|
1662
|
+
+'<div style="display:flex;gap:4px;flex-shrink:0;">'
|
|
1663
|
+
+'<button class="mbtn mbtn-ok" style="padding:4px 10px;font-size:11px;" onclick="copyMemberAid(this,\\''+escH(aid)+'\\')">复制</button>'
|
|
1664
|
+
+'<button class="mbtn mbtn-cancel" style="padding:4px 10px;font-size:11px;" onclick="openAgentMdPage(\\''+escH(aid)+'\\')">查看</button>'
|
|
1665
|
+
+'</div></div>';
|
|
1490
1666
|
}).join('');
|
|
1491
1667
|
$('membersList').innerHTML=html||'<div style="color:#999;">暂无成员</div>';
|
|
1668
|
+
// 异步加载未缓存的 agent info
|
|
1669
|
+
d.members.forEach(function(m){
|
|
1670
|
+
var aid=m.agent_id||m;
|
|
1671
|
+
if(typeof aid!=='string') aid=JSON.stringify(aid);
|
|
1672
|
+
if(!aid||agentInfoCache[aid]) return;
|
|
1673
|
+
fetchAgentInfo(aid).then(function(info){
|
|
1674
|
+
if(!info||(!info.name&&!info.type)) return;
|
|
1675
|
+
var safeId='member-'+aid.replace(/\\./g,'_');
|
|
1676
|
+
var el=document.getElementById(safeId);
|
|
1677
|
+
if(!el) return;
|
|
1678
|
+
var avatarEl=el.querySelector('.member-avatar[data-aid="'+aid+'"]');
|
|
1679
|
+
var nameEl=el.querySelector('.member-name[data-aid="'+aid+'"]');
|
|
1680
|
+
var tagsEl=el.querySelector('.member-tags[data-aid="'+aid+'"]');
|
|
1681
|
+
if(avatarEl) avatarEl.src=getAvatarSrc(info.type);
|
|
1682
|
+
if(nameEl) nameEl.textContent=info.name||aid.split('.')[0];
|
|
1683
|
+
if(tagsEl){
|
|
1684
|
+
var tags='';
|
|
1685
|
+
if(info.tags&&info.tags.length){
|
|
1686
|
+
tags=info.tags.map(function(t){ return '<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;margin-right:4px;">'+escH(t)+'</span>'; }).join('');
|
|
1687
|
+
} else if(info.type){
|
|
1688
|
+
tags='<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;">'+escH(info.type)+'</span>';
|
|
1689
|
+
}
|
|
1690
|
+
// 保留已有的 role tag
|
|
1691
|
+
var existingRole=tagsEl.querySelector('span[style*="fff3cd"]');
|
|
1692
|
+
tagsEl.innerHTML=tags+(existingRole?existingRole.outerHTML:'');
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
});
|
|
1492
1696
|
} else { $('membersList').innerHTML='<div style="color:#999;">获取失败</div>'; }
|
|
1493
1697
|
$('membersModal').classList.add('show');
|
|
1494
1698
|
} catch(e){ alert('获取成员失败: '+e.message); }
|
|
@@ -1676,6 +1880,22 @@ async function handleRequest(req, res) {
|
|
|
1676
1880
|
sendJson(res, info);
|
|
1677
1881
|
return;
|
|
1678
1882
|
}
|
|
1883
|
+
// 获取远程 agent.md 原始内容
|
|
1884
|
+
if (pathname === '/api/agent-md-raw' && method === 'GET') {
|
|
1885
|
+
const aid = parsedUrl.query.aid;
|
|
1886
|
+
if (!aid) {
|
|
1887
|
+
sendJson(res, { success: false, error: '缺少 aid' });
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
try {
|
|
1891
|
+
const md = await fetchAgentMd(aid);
|
|
1892
|
+
sendJson(res, { success: true, content: md });
|
|
1893
|
+
}
|
|
1894
|
+
catch (e) {
|
|
1895
|
+
sendJson(res, { success: false, error: e.message || '获取失败' });
|
|
1896
|
+
}
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1679
1899
|
if (pathname === '/api/aid' && method === 'GET') {
|
|
1680
1900
|
try {
|
|
1681
1901
|
const aidList = await datamanager_1.CertAndKeyStore.getAids();
|
|
@@ -1752,6 +1972,17 @@ async function handleRequest(req, res) {
|
|
|
1752
1972
|
try {
|
|
1753
1973
|
const created = await agentCP.createAid(aid);
|
|
1754
1974
|
currentAid = created;
|
|
1975
|
+
// 保存自定义昵称和描述
|
|
1976
|
+
const nickname = (body.nickname || '').trim();
|
|
1977
|
+
const description = (body.description || '').trim();
|
|
1978
|
+
if (nickname || description) {
|
|
1979
|
+
const opts = {};
|
|
1980
|
+
if (nickname)
|
|
1981
|
+
opts.name = nickname;
|
|
1982
|
+
if (description)
|
|
1983
|
+
opts.description = description;
|
|
1984
|
+
saveAidMdOptions(created, opts);
|
|
1985
|
+
}
|
|
1755
1986
|
sendJson(res, { success: true, aid: created });
|
|
1756
1987
|
}
|
|
1757
1988
|
catch (createErr) {
|