ec-web 0.0.20 → 0.0.21
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/server.js +117 -23
- package/dist/sources/aid.js +69 -49
- package/dist/sources/baseagent-detector.js +60 -6
- package/dist/sources/basic-settings.js +818 -0
- package/dist/sources/gateway.js +5 -4
- package/dist/sources/msg.js +4 -96
- package/dist/sources/relation-roles.js +150 -191
- package/dist/sources/role-definitions.js +138 -331
- package/dist/sources/session-codex.js +189 -78
- package/dist/sources/system.js +10 -9
- package/dist/sources/triggers.js +24 -13
- package/dist/sources/types.js +20 -1
- package/dist/static/app.js +83 -9
- package/dist/static/basic-settings.js +1576 -0
- package/dist/static/index.html +43 -1
- package/dist/static/style.css +586 -2
- package/ecweb-wrapper.cjs +5 -1
- package/package.json +1 -1
|
@@ -77,6 +77,128 @@ function getDb() {
|
|
|
77
77
|
}
|
|
78
78
|
return _db;
|
|
79
79
|
}
|
|
80
|
+
function messageContentText(content) {
|
|
81
|
+
if (typeof content === 'string')
|
|
82
|
+
return content;
|
|
83
|
+
if (content && typeof content === 'object' && !Array.isArray(content)) {
|
|
84
|
+
return typeof content.text === 'string' ? content.text.trim() : '';
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(content))
|
|
87
|
+
return '';
|
|
88
|
+
return content
|
|
89
|
+
.map((part) => {
|
|
90
|
+
if (typeof part === 'string')
|
|
91
|
+
return part;
|
|
92
|
+
if (!part || typeof part !== 'object')
|
|
93
|
+
return '';
|
|
94
|
+
if (typeof part.text === 'string')
|
|
95
|
+
return part.text;
|
|
96
|
+
return '';
|
|
97
|
+
})
|
|
98
|
+
.filter(Boolean)
|
|
99
|
+
.join('\n')
|
|
100
|
+
.trim();
|
|
101
|
+
}
|
|
102
|
+
function parseRolloutEvents(raw) {
|
|
103
|
+
const events = [];
|
|
104
|
+
for (const line of raw.split('\n')) {
|
|
105
|
+
if (!line.trim())
|
|
106
|
+
continue;
|
|
107
|
+
try {
|
|
108
|
+
events.push(JSON.parse(line));
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
}
|
|
112
|
+
return events;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Normalize both Codex CLI events and Codex app-server lifecycle events.
|
|
116
|
+
* The app-server writes the same message twice: response_item.message and
|
|
117
|
+
* event_msg.item_completed. We intentionally consume the latter because it
|
|
118
|
+
* carries the canonical UserMessage/AgentMessage item and stable id.
|
|
119
|
+
*/
|
|
120
|
+
export function parseCodexMessageEvent(event) {
|
|
121
|
+
if (!event || !event.payload)
|
|
122
|
+
return null;
|
|
123
|
+
const payload = event.payload;
|
|
124
|
+
if (event.type === 'response_item') {
|
|
125
|
+
if (payload.type !== 'message' || (payload.role !== 'user' && payload.role !== 'assistant'))
|
|
126
|
+
return null;
|
|
127
|
+
const text = messageContentText(payload.content ?? payload.message ?? payload.text);
|
|
128
|
+
if (!text)
|
|
129
|
+
return null;
|
|
130
|
+
return {
|
|
131
|
+
role: payload.role,
|
|
132
|
+
text,
|
|
133
|
+
id: String(payload.id || ''),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (event.type !== 'event_msg')
|
|
137
|
+
return null;
|
|
138
|
+
if (payload.type === 'user_message' && payload.message) {
|
|
139
|
+
const text = messageContentText(payload.message);
|
|
140
|
+
if (!text)
|
|
141
|
+
return null;
|
|
142
|
+
return {
|
|
143
|
+
role: 'user',
|
|
144
|
+
text,
|
|
145
|
+
id: String(payload.id || ''),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (payload.type === 'agent_message' && payload.message) {
|
|
149
|
+
const text = messageContentText(payload.message);
|
|
150
|
+
if (!text)
|
|
151
|
+
return null;
|
|
152
|
+
return {
|
|
153
|
+
role: 'assistant',
|
|
154
|
+
text,
|
|
155
|
+
id: String(payload.id || ''),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (payload.type !== 'item_completed' || !payload.item)
|
|
159
|
+
return null;
|
|
160
|
+
const item = payload.item;
|
|
161
|
+
const role = item.type === 'UserMessage'
|
|
162
|
+
? 'user'
|
|
163
|
+
: item.type === 'AgentMessage'
|
|
164
|
+
? 'assistant'
|
|
165
|
+
: null;
|
|
166
|
+
if (!role)
|
|
167
|
+
return null;
|
|
168
|
+
const text = messageContentText(item.content ?? item.message ?? item.text);
|
|
169
|
+
if (!text)
|
|
170
|
+
return null;
|
|
171
|
+
return {
|
|
172
|
+
role,
|
|
173
|
+
text,
|
|
174
|
+
id: String(item.id || payload.id || ''),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function normalizedCodexMessages(events) {
|
|
178
|
+
// app-server emits response_item.message followed by item_completed for
|
|
179
|
+
// the same logical message; prefer lifecycle items when available. This
|
|
180
|
+
// also prevents injected context response items from being counted as user
|
|
181
|
+
// turns in an app-server rollout.
|
|
182
|
+
const hasCanonicalMessages = events.some(event => event?.type === 'event_msg' && parseCodexMessageEvent(event) !== null);
|
|
183
|
+
const out = [];
|
|
184
|
+
for (const event of events) {
|
|
185
|
+
const message = parseCodexMessageEvent(event);
|
|
186
|
+
if (!message)
|
|
187
|
+
continue;
|
|
188
|
+
if (event.type === 'response_item' && hasCanonicalMessages)
|
|
189
|
+
continue;
|
|
190
|
+
if (event.type === 'response_item' && message.role === 'user' && isSyntheticCodexContext(message.text))
|
|
191
|
+
continue;
|
|
192
|
+
out.push({ event, message });
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
function isSyntheticCodexContext(text) {
|
|
197
|
+
const trimmed = text.trimStart();
|
|
198
|
+
return trimmed.startsWith('# AGENTS.md instructions')
|
|
199
|
+
|| trimmed.startsWith('<system-reminder>')
|
|
200
|
+
|| trimmed.startsWith('<environment_context>');
|
|
201
|
+
}
|
|
80
202
|
// ── 项目列表 ──
|
|
81
203
|
function listProjects() {
|
|
82
204
|
const db = getDb();
|
|
@@ -110,7 +232,7 @@ function listProjects() {
|
|
|
110
232
|
}
|
|
111
233
|
}
|
|
112
234
|
// ── 会话列表 ──
|
|
113
|
-
const CACHE_VERSION =
|
|
235
|
+
const CACHE_VERSION = 4;
|
|
114
236
|
const _metaCache = new Map();
|
|
115
237
|
const _detailCache = new Map();
|
|
116
238
|
const MAX_DETAIL_CACHE_ENTRIES = 6;
|
|
@@ -186,36 +308,29 @@ function extractMetaFromRollout(file, id) {
|
|
|
186
308
|
catch { }
|
|
187
309
|
let title = '', firstUser = '', gitBranch = '', version = '', userMsgs = 0, totalMsgs = 0;
|
|
188
310
|
let hasRealUserInput = false;
|
|
189
|
-
const
|
|
190
|
-
for (const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
hasRealUserInput = true;
|
|
208
|
-
if (!firstUser) {
|
|
209
|
-
const text = event.payload.message.trim().replace(/\s+/g, ' ');
|
|
210
|
-
firstUser = text.substring(0, 120);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// 统计 assistant 消息
|
|
214
|
-
if (event.type === 'event_msg' && event.payload?.type === 'assistant_message') {
|
|
215
|
-
totalMsgs++;
|
|
311
|
+
const events = parseRolloutEvents(raw);
|
|
312
|
+
for (const event of events) {
|
|
313
|
+
// 提取 cwd 和 version
|
|
314
|
+
if (event.type === 'session_meta' && event.payload) {
|
|
315
|
+
if (!gitBranch && event.payload.cwd)
|
|
316
|
+
gitBranch = event.payload.cwd;
|
|
317
|
+
if (!version && event.payload.cli_version)
|
|
318
|
+
version = event.payload.cli_version;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
for (const { message } of normalizedCodexMessages(events)) {
|
|
322
|
+
if (message.role === 'user') {
|
|
323
|
+
totalMsgs++;
|
|
324
|
+
userMsgs++;
|
|
325
|
+
if (!isSyntheticCliPrompt(message.text, 'codex'))
|
|
326
|
+
hasRealUserInput = true;
|
|
327
|
+
if (!firstUser) {
|
|
328
|
+
firstUser = message.text.replace(/\s+/g, ' ').substring(0, 120);
|
|
216
329
|
}
|
|
217
330
|
}
|
|
218
|
-
|
|
331
|
+
else {
|
|
332
|
+
totalMsgs++;
|
|
333
|
+
}
|
|
219
334
|
}
|
|
220
335
|
let stat;
|
|
221
336
|
try {
|
|
@@ -241,8 +356,15 @@ function extractMetaFromRollout(file, id) {
|
|
|
241
356
|
};
|
|
242
357
|
}
|
|
243
358
|
function withThreadSource(meta, row) {
|
|
359
|
+
const dbFirstUser = typeof row.first_user_message === 'string'
|
|
360
|
+
? row.first_user_message.trim().replace(/\s+/g, ' ').substring(0, 120)
|
|
361
|
+
: '';
|
|
362
|
+
const dbTitle = typeof row.title === 'string' ? row.title.trim() : '';
|
|
244
363
|
return {
|
|
245
364
|
...meta,
|
|
365
|
+
title: dbTitle || meta.title,
|
|
366
|
+
firstUser: dbFirstUser || meta.firstUser,
|
|
367
|
+
hasRealUserInput: meta.hasRealUserInput || (!!dbFirstUser && !isSyntheticCliPrompt(dbFirstUser, 'codex')),
|
|
246
368
|
backendSource: typeof row.source === 'string' ? row.source : '',
|
|
247
369
|
threadSource: typeof row.thread_source === 'string' ? row.thread_source : '',
|
|
248
370
|
evolcoreRouted: isEvolcoreRoutedMessage(row.first_user_message),
|
|
@@ -584,16 +706,9 @@ function readTranscriptFile(threadId, cwd, readStats) {
|
|
|
584
706
|
let lastUsageKey = '';
|
|
585
707
|
const seenToolCalls = new Set();
|
|
586
708
|
const seenToolResults = new Set();
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
let event;
|
|
591
|
-
try {
|
|
592
|
-
event = JSON.parse(line);
|
|
593
|
-
}
|
|
594
|
-
catch {
|
|
595
|
-
continue;
|
|
596
|
-
}
|
|
709
|
+
const events = parseRolloutEvents(raw);
|
|
710
|
+
const messagesByEvent = new Map(normalizedCodexMessages(events).map(({ event, message }) => [event, message]));
|
|
711
|
+
for (const event of events) {
|
|
597
712
|
// 提取 session_meta
|
|
598
713
|
if (event.type === 'session_meta' && event.payload) {
|
|
599
714
|
if (!version && event.payload.cli_version)
|
|
@@ -624,44 +739,37 @@ function readTranscriptFile(threadId, cwd, readStats) {
|
|
|
624
739
|
}
|
|
625
740
|
}
|
|
626
741
|
}
|
|
627
|
-
//
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
else if (payload.type === 'reasoning' && payload.message) {
|
|
660
|
-
// Codex reasoning 对应 Claude 的 thinking
|
|
661
|
-
const lastTurn = turns[turns.length - 1];
|
|
662
|
-
if (lastTurn && lastTurn.role === 'assistant') {
|
|
663
|
-
lastTurn.blocks.unshift({ kind: 'thinking', text: payload.message });
|
|
664
|
-
}
|
|
742
|
+
// 处理用户/助手消息。托管 app-server 使用 item_completed,旧版 CLI
|
|
743
|
+
// 使用 payload.type=user_message/agent_message;统一入口避免重复解析。
|
|
744
|
+
const message = messagesByEvent.get(event);
|
|
745
|
+
if (message?.role === 'user') {
|
|
746
|
+
totalMsgs++;
|
|
747
|
+
userMsgs++;
|
|
748
|
+
counts.userInput++;
|
|
749
|
+
turns.push({
|
|
750
|
+
role: 'user',
|
|
751
|
+
ts: event.timestamp ? Date.parse(event.timestamp) : 0,
|
|
752
|
+
uuid: message.id,
|
|
753
|
+
category: 'user_input',
|
|
754
|
+
blocks: [{ kind: 'text', text: message.text }],
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
else if (message?.role === 'assistant') {
|
|
758
|
+
totalMsgs++;
|
|
759
|
+
counts.modelOutput++;
|
|
760
|
+
turns.push({
|
|
761
|
+
role: 'assistant',
|
|
762
|
+
ts: event.timestamp ? Date.parse(event.timestamp) : 0,
|
|
763
|
+
uuid: message.id,
|
|
764
|
+
category: 'model_output',
|
|
765
|
+
blocks: [{ kind: 'text', text: message.text }],
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
else if (event.type === 'event_msg' && event.payload?.type === 'reasoning' && event.payload.message) {
|
|
769
|
+
// Codex reasoning 对应 Claude 的 thinking
|
|
770
|
+
const lastTurn = turns[turns.length - 1];
|
|
771
|
+
if (lastTurn && lastTurn.role === 'assistant') {
|
|
772
|
+
lastTurn.blocks.unshift({ kind: 'thinking', text: event.payload.message });
|
|
665
773
|
}
|
|
666
774
|
}
|
|
667
775
|
const toolEvent = parseCodexToolEvent(event);
|
|
@@ -748,6 +856,9 @@ async function buildSnapshot(params) {
|
|
|
748
856
|
metrics.totalMs = Date.now() - startedAt;
|
|
749
857
|
return {
|
|
750
858
|
...m,
|
|
859
|
+
// EvolCore topics carry the user-facing name; Codex's DB title is often
|
|
860
|
+
// the routed envelope text for AUN-hosted sessions.
|
|
861
|
+
title: bind?.name?.trim() || m.title,
|
|
751
862
|
sourceType: classifyTranscriptSource(bind, isCodexCliCandidate(m)),
|
|
752
863
|
bound: !!bind,
|
|
753
864
|
boundChannel: bind?.channelType ?? null,
|
|
@@ -781,7 +892,7 @@ async function buildSnapshot(params) {
|
|
|
781
892
|
const transcript = metas.find(meta => meta.id === sessionId);
|
|
782
893
|
const header = {
|
|
783
894
|
sessionId,
|
|
784
|
-
title: detail.title,
|
|
895
|
+
title: bind?.name?.trim() || detail.title,
|
|
785
896
|
model: detail.model,
|
|
786
897
|
gitBranch: detail.gitBranch,
|
|
787
898
|
version: detail.version,
|
package/dist/sources/system.js
CHANGED
|
@@ -11,6 +11,7 @@ import fs from 'fs';
|
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { resolvePaths } from '../paths.js';
|
|
13
13
|
import { ipcQuery } from '../ipc-client.js';
|
|
14
|
+
import { watchMenuRequestAuth } from './types.js';
|
|
14
15
|
function readDefaultBaseagents() {
|
|
15
16
|
try {
|
|
16
17
|
const p = resolvePaths();
|
|
@@ -34,15 +35,15 @@ function readDefaultBaseagents() {
|
|
|
34
35
|
return [];
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
async function menuExec(payload) {
|
|
38
|
+
async function menuExec(payload, params) {
|
|
38
39
|
const p = resolvePaths();
|
|
39
|
-
const r = await ipcQuery(p.socket, { type: 'menu.exec', payload, auth:
|
|
40
|
+
const r = await ipcQuery(p.socket, { type: 'menu.exec', payload, auth: watchMenuRequestAuth(params) }, 5000);
|
|
40
41
|
return r?.ok ? r.response : null;
|
|
41
42
|
}
|
|
42
|
-
async function buildSnapshot() {
|
|
43
|
+
async function buildSnapshot(params = {}) {
|
|
43
44
|
const [listResp, sysResp] = await Promise.all([
|
|
44
|
-
menuExec({ type: 'menu.list', id: 'sys-list' }),
|
|
45
|
-
menuExec({ type: 'menu.query', id: 'sys-q', name: 'system' }),
|
|
45
|
+
menuExec({ type: 'menu.list', id: 'sys-list' }, params),
|
|
46
|
+
menuExec({ type: 'menu.query', id: 'sys-q', name: 'system' }, params),
|
|
46
47
|
]);
|
|
47
48
|
const daemonRunning = listResp !== null;
|
|
48
49
|
// sysResp 形如 { type:'menu.response', id, name, data | error }
|
|
@@ -58,17 +59,17 @@ async function buildSnapshot() {
|
|
|
58
59
|
}
|
|
59
60
|
export const systemSource = {
|
|
60
61
|
kind: 'system',
|
|
61
|
-
async snapshot() {
|
|
62
|
-
return buildSnapshot();
|
|
62
|
+
async snapshot(params = {}) {
|
|
63
|
+
return buildSnapshot(params);
|
|
63
64
|
},
|
|
64
|
-
subscribe(
|
|
65
|
+
subscribe(params, push) {
|
|
65
66
|
let lastJson = '';
|
|
66
67
|
let stopped = false;
|
|
67
68
|
const tick = async () => {
|
|
68
69
|
if (stopped)
|
|
69
70
|
return;
|
|
70
71
|
try {
|
|
71
|
-
const snap = await buildSnapshot();
|
|
72
|
+
const snap = await buildSnapshot(params);
|
|
72
73
|
const json = JSON.stringify(snap);
|
|
73
74
|
if (json !== lastJson) {
|
|
74
75
|
lastJson = json;
|
package/dist/sources/triggers.js
CHANGED
|
@@ -11,9 +11,10 @@ import fs from 'fs';
|
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { resolvePaths } from '../paths.js';
|
|
13
13
|
import { ipcQuery } from '../ipc-client.js';
|
|
14
|
-
|
|
14
|
+
import { watchMenuAuth, watchMenuRequestAuth } from './types.js';
|
|
15
|
+
async function menuExec(payload, params) {
|
|
15
16
|
const p = resolvePaths();
|
|
16
|
-
const r = await ipcQuery(p.socket, { type: 'menu.exec', payload, auth:
|
|
17
|
+
const r = await ipcQuery(p.socket, { type: 'menu.exec', payload, auth: watchMenuRequestAuth(params) }, 5000);
|
|
17
18
|
return r?.ok ? r.response : null;
|
|
18
19
|
}
|
|
19
20
|
function readTriggerControlToken(instanceDir) {
|
|
@@ -38,6 +39,15 @@ function readDaemonOwnerAid(p) {
|
|
|
38
39
|
return null;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
function readFullAccessEnabled(p) {
|
|
43
|
+
try {
|
|
44
|
+
const raw = JSON.parse(fs.readFileSync(p.daemonConfig, 'utf8'));
|
|
45
|
+
return raw?.fullaccess?.enabled === true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
41
51
|
function normalizeAgent(item) {
|
|
42
52
|
const aid = String(item?.value || item?.aid || '').trim();
|
|
43
53
|
if (!aid)
|
|
@@ -53,14 +63,11 @@ function normalizeAgent(item) {
|
|
|
53
63
|
async function listTriggersForAgent(agentAid, agentLabel) {
|
|
54
64
|
const p = resolvePaths();
|
|
55
65
|
const controlToken = readTriggerControlToken(p.instanceDir);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
agent: agentAid,
|
|
62
|
-
});
|
|
63
|
-
const items = Array.isArray(resp?.data) ? resp.data : [];
|
|
66
|
+
// ECWeb is a management surface, so use the canonical Trigger IPC rather
|
|
67
|
+
// than routing through a regular Agent channel. This also covers the
|
|
68
|
+
// daemon-owned scheduler, which has no self-agent channel in the registry.
|
|
69
|
+
const resp = await ipcQuery(p.socket, { type: 'trigger.list', agentAid, all: true, controlToken }, 5000);
|
|
70
|
+
const items = Array.isArray(resp?.triggers) ? resp.triggers : [];
|
|
64
71
|
return Promise.all(items.map(async (trigger) => {
|
|
65
72
|
const effectiveAgentAid = trigger.agentAid || trigger.schedulerAid || agentAid;
|
|
66
73
|
let definition = null;
|
|
@@ -104,9 +111,13 @@ async function listTriggersForAgent(agentAid, agentLabel) {
|
|
|
104
111
|
}
|
|
105
112
|
async function buildSnapshot(params) {
|
|
106
113
|
const p = resolvePaths();
|
|
114
|
+
const fullaccessEnabled = readFullAccessEnabled(p);
|
|
115
|
+
// This capability is derived from the server-injected ECWeb connection
|
|
116
|
+
// context; browser-supplied subscription parameters are never trusted for it.
|
|
117
|
+
const canApprovePersistentFullAccess = watchMenuAuth(params).canApprovePersistentFullAccess;
|
|
107
118
|
const agent = typeof params?.agent === 'string' && params.agent ? params.agent : null;
|
|
108
119
|
const [agentsResp, runtimeAgentsResp] = await Promise.all([
|
|
109
|
-
menuExec({ type: 'menu.options', id: 'tr-agents', name: 'agent' }),
|
|
120
|
+
menuExec({ type: 'menu.options', id: 'tr-agents', name: 'agent' }, params),
|
|
110
121
|
ipcQuery(p.socket, { type: 'evolagent.list' }, 5000),
|
|
111
122
|
]);
|
|
112
123
|
// menu.options 响应形如 { type:'menu.response', id, name, data: MenuItem[] }
|
|
@@ -132,11 +143,11 @@ async function buildSnapshot(params) {
|
|
|
132
143
|
if (agent) {
|
|
133
144
|
const found = agentInfos.find((x) => x.aid === agent);
|
|
134
145
|
const triggers = await listTriggersForAgent(agent, found?.label || agent);
|
|
135
|
-
return { agents: agentInfos, triggers, selectedAgent: agent };
|
|
146
|
+
return { agents: agentInfos, triggers, selectedAgent: agent, fullaccessEnabled, canApprovePersistentFullAccess };
|
|
136
147
|
}
|
|
137
148
|
const batches = await Promise.all(agentInfos.map((x) => listTriggersForAgent(x.aid, x.label)));
|
|
138
149
|
const triggers = batches.flat();
|
|
139
|
-
return { agents: agentInfos, triggers, selectedAgent: agent };
|
|
150
|
+
return { agents: agentInfos, triggers, selectedAgent: agent, fullaccessEnabled, canApprovePersistentFullAccess };
|
|
140
151
|
}
|
|
141
152
|
export const triggersSource = {
|
|
142
153
|
kind: 'triggers',
|
package/dist/sources/types.js
CHANGED
|
@@ -7,4 +7,23 @@
|
|
|
7
7
|
*
|
|
8
8
|
* aid/cache 走 IPC 轮询(无推送),msg/session 走 fs.watch 文件监听。
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
/** Read only the server-injected auth marker; browser parameters never decide it. */
|
|
11
|
+
export function watchMenuAuth(params) {
|
|
12
|
+
const auth = params?.__menuAuth;
|
|
13
|
+
return {
|
|
14
|
+
localDirect: auth?.localDirect === true,
|
|
15
|
+
actorAid: typeof auth?.actorAid === 'string' ? auth.actorAid : null,
|
|
16
|
+
// This marker is injected by the ECWeb server, never read from browser
|
|
17
|
+
// request parameters. Authenticated ECWeb has daemon-owner authority.
|
|
18
|
+
canApprovePersistentFullAccess: auth?.canApprovePersistentFullAccess === true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function watchMenuRequestAuth(params) {
|
|
22
|
+
const auth = watchMenuAuth(params);
|
|
23
|
+
return {
|
|
24
|
+
source: 'ecweb',
|
|
25
|
+
localDirect: auth.localDirect,
|
|
26
|
+
actorAid: auth.actorAid ?? null,
|
|
27
|
+
canApprovePersistentFullAccess: auth.canApprovePersistentFullAccess,
|
|
28
|
+
};
|
|
29
|
+
}
|