ec-web 0.0.27 → 0.0.29-test.0
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 +21 -3
- package/dist/sources/basic-settings.js +4 -3
- package/dist/sources/msg.js +9 -5
- package/dist/sources/relation-roles.js +5 -0
- package/dist/sources/role-definitions.js +7 -0
- package/dist/sources/session-origin.js +2 -2
- package/dist/sources/triggers.js +2 -12
- package/dist/static/app.js +732 -146
- package/dist/static/basic-settings.js +10 -9
- package/dist/static/index.html +33 -12
- package/dist/static/style.css +613 -38
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -616,13 +616,31 @@ function handleConnection(ws, req, log, tokenRecord) {
|
|
|
616
616
|
const timeoutMs = Number.isFinite(Number(msg.timeoutMs)) && Number(msg.timeoutMs) > 0
|
|
617
617
|
? Math.min(Number(msg.timeoutMs), 120_000)
|
|
618
618
|
: 5000;
|
|
619
|
+
const ecwebRequestId = (typeof msg.requestId === 'string' || typeof msg.requestId === 'number')
|
|
620
|
+
? String(msg.requestId)
|
|
621
|
+
: crypto.randomUUID();
|
|
622
|
+
const logicalToolName = typeof msg.payload?.name === 'string' && msg.payload.name.trim()
|
|
623
|
+
? msg.payload.name.trim()
|
|
624
|
+
: typeof msg.payload?.cmd === 'string' && msg.payload.cmd.trim()
|
|
625
|
+
? msg.payload.cmd.trim().split(/\s+/, 1)[0]
|
|
626
|
+
: typeof msg.payload?.type === 'string' ? msg.payload.type : undefined;
|
|
619
627
|
const menuAuth = daemonOwnerMenuAuth(localDirect, actorAid);
|
|
620
|
-
const resp = await ipcQuery(p.socket, {
|
|
628
|
+
const resp = await ipcQuery(p.socket, {
|
|
629
|
+
type: 'menu.exec',
|
|
630
|
+
payload: msg.payload,
|
|
631
|
+
auth: {
|
|
632
|
+
source: 'ecweb',
|
|
633
|
+
...menuAuth,
|
|
634
|
+
ecwebRequestId,
|
|
635
|
+
endpoint: 'WS menu.exec',
|
|
636
|
+
logicalToolName,
|
|
637
|
+
},
|
|
638
|
+
}, timeoutMs);
|
|
621
639
|
if (resp?.ok) {
|
|
622
|
-
send({ type: 'menu.response', requestId:
|
|
640
|
+
send({ type: 'menu.response', requestId: ecwebRequestId, data: resp.response });
|
|
623
641
|
}
|
|
624
642
|
else {
|
|
625
|
-
send({ type: 'menu.response', requestId:
|
|
643
|
+
send({ type: 'menu.response', requestId: ecwebRequestId, data: {
|
|
626
644
|
type: 'menu.response', id: msg.payload?.id ?? '',
|
|
627
645
|
error: { code: 'INTERNAL', message: resp?.error ?? 'daemon unreachable' },
|
|
628
646
|
} });
|
|
@@ -18,13 +18,13 @@ const SERVICE_VISIBILITIES = new Set(['public', 'private', 'aun-auth']);
|
|
|
18
18
|
const SENSITIVE_METADATA_KEY = /(?:^|[_-])(endpoint|url|uri|token|access[_-]?token|authorization|cookie|secret|password|private[_-]?key|key|cert|certificate)(?:$|[_-])/i;
|
|
19
19
|
const DAEMON_EXACT_PATHS = new Set([
|
|
20
20
|
'owners',
|
|
21
|
-
'fullaccess.enabled',
|
|
22
21
|
'aun.defaultAidDomain',
|
|
23
22
|
'aun.minEvolVersion',
|
|
24
23
|
'aun.menuTokenRequired',
|
|
25
24
|
'aun.defaultEncrypt',
|
|
26
25
|
'services',
|
|
27
26
|
'configSnapshots',
|
|
27
|
+
'restartRecovery.enabled',
|
|
28
28
|
'idleMonitor.enabled',
|
|
29
29
|
'idleMonitor.timeout',
|
|
30
30
|
'idleMonitor.apiRetryTimeout',
|
|
@@ -97,10 +97,10 @@ const BASEAGENT_FIELDS = {
|
|
|
97
97
|
]),
|
|
98
98
|
};
|
|
99
99
|
const BOOLEAN_PATHS = new Set([
|
|
100
|
-
'fullaccess.enabled',
|
|
101
100
|
'aun.menuTokenRequired',
|
|
102
101
|
'aun.defaultEncrypt',
|
|
103
102
|
'configSnapshots',
|
|
103
|
+
'restartRecovery.enabled',
|
|
104
104
|
'idleMonitor.enabled',
|
|
105
105
|
'debug.aunTrace',
|
|
106
106
|
'debug.aunSdkLog',
|
|
@@ -122,6 +122,7 @@ const RESTART_DAEMON_PREFIXES = [
|
|
|
122
122
|
'aun.',
|
|
123
123
|
'services',
|
|
124
124
|
'configSnapshots',
|
|
125
|
+
'restartRecovery.',
|
|
125
126
|
'idleMonitor.',
|
|
126
127
|
'debug.',
|
|
127
128
|
];
|
|
@@ -484,7 +485,7 @@ function validateDefaultsValue(fieldPath, value) {
|
|
|
484
485
|
return;
|
|
485
486
|
}
|
|
486
487
|
if (fieldPath === 'config.mentionMode') {
|
|
487
|
-
if (!['disabled', 'mention-only'].includes(String(value)))
|
|
488
|
+
if (!['disabled', 'mention-only', 'auto'].includes(String(value)))
|
|
488
489
|
throw new Error(`${fieldPath} is invalid`);
|
|
489
490
|
return;
|
|
490
491
|
}
|
package/dist/sources/msg.js
CHANGED
|
@@ -14,7 +14,7 @@ import { decodeDirSegment, isTransientProtocolMessage, readAllJsonlLines, readJs
|
|
|
14
14
|
function topicMatchesArchiveFilter(chat, archived) {
|
|
15
15
|
if (archived === 'all')
|
|
16
16
|
return true;
|
|
17
|
-
const isArchived = chat.partition === 'thread' && chat.
|
|
17
|
+
const isArchived = chat.partition === 'thread' && chat.topicStatus === 'archived';
|
|
18
18
|
return archived === true ? isArchived : !isArchived;
|
|
19
19
|
}
|
|
20
20
|
let contextModulesPromise = null;
|
|
@@ -589,8 +589,10 @@ function loadChats(peerDisplayResolver, metrics) {
|
|
|
589
589
|
threadName: item.partition === 'thread'
|
|
590
590
|
? String(partitionActive.name || partitionActive.metadata?.topicName || item.threadId)
|
|
591
591
|
: null,
|
|
592
|
-
|
|
593
|
-
? partitionActive.lable || null : null,
|
|
592
|
+
label: item.partition === 'thread'
|
|
593
|
+
? partitionActive.label || (partitionActive.lable === '归档' ? null : partitionActive.lable || null) : null,
|
|
594
|
+
topicStatus: item.partition === 'thread'
|
|
595
|
+
? partitionActive.topicStatus || (partitionActive.lable === '归档' ? 'archived' : 'pending') : null,
|
|
594
596
|
updatedAt,
|
|
595
597
|
messages: item.messages,
|
|
596
598
|
active: partitionActive,
|
|
@@ -700,7 +702,8 @@ function mergePeerInfo(chats) {
|
|
|
700
702
|
partition: chat.partition,
|
|
701
703
|
threadId: chat.threadId,
|
|
702
704
|
threadName: chat.threadName,
|
|
703
|
-
|
|
705
|
+
label: chat.label,
|
|
706
|
+
topicStatus: chat.topicStatus,
|
|
704
707
|
inbound: 0,
|
|
705
708
|
outbound: 0,
|
|
706
709
|
lastAt: 0,
|
|
@@ -831,7 +834,8 @@ async function buildSessionContext(chat) {
|
|
|
831
834
|
sessionId: active.id || null,
|
|
832
835
|
evolcoreSessionId: active.id || null,
|
|
833
836
|
name: active.name || null,
|
|
834
|
-
|
|
837
|
+
label: chat.label,
|
|
838
|
+
topicStatus: chat.topicStatus,
|
|
835
839
|
baseagent: active.baseagent || active.agentType || null,
|
|
836
840
|
chatMode,
|
|
837
841
|
permissionMode,
|
|
@@ -129,6 +129,7 @@ async function authorizeRoleAssignmentRead(modules, aid, auth) {
|
|
|
129
129
|
processRole: actor.daemonOwner ? 'daemon-owner' : 'none',
|
|
130
130
|
isDaemonOwner: actor.daemonOwner,
|
|
131
131
|
decision: decision.allow ? 'allow' : 'deny',
|
|
132
|
+
...(decision.allow ? {} : { decisionSource: 'role', errorCode: 'ROLE_DENIED' }),
|
|
132
133
|
code: decision.allow ? undefined : decision.code,
|
|
133
134
|
reason: decision.allow ? undefined : decision.reason,
|
|
134
135
|
matchedRule: decision.matchedRule,
|
|
@@ -209,6 +210,8 @@ async function authorizeRoleAssignmentWrite(modules, aid, auth, operation, targe
|
|
|
209
210
|
processRole: daemonOwner ? 'daemon-owner' : 'none',
|
|
210
211
|
isDaemonOwner: daemonOwner,
|
|
211
212
|
decision: 'deny',
|
|
213
|
+
decisionSource: 'role',
|
|
214
|
+
errorCode: 'ROLE_DENIED',
|
|
212
215
|
code: decision.code,
|
|
213
216
|
reason: decision.reason,
|
|
214
217
|
matchedRule: decision.matchedRule,
|
|
@@ -243,6 +246,8 @@ async function authorizeRoleAssignmentWrite(modules, aid, auth, operation, targe
|
|
|
243
246
|
processRole: daemonOwner ? 'daemon-owner' : 'none',
|
|
244
247
|
isDaemonOwner: daemonOwner,
|
|
245
248
|
decision: 'deny',
|
|
249
|
+
decisionSource: 'role',
|
|
250
|
+
errorCode: 'ROLE_DENIED',
|
|
246
251
|
code: 'NOT_ALLOWED',
|
|
247
252
|
reason,
|
|
248
253
|
matchedRule: decision.matchedRule,
|
|
@@ -189,7 +189,9 @@ const COMMAND_CONSTRAINT_KEYS = new Set([
|
|
|
189
189
|
'requireDaemonOwner',
|
|
190
190
|
'requireControlChannel',
|
|
191
191
|
'requireExplicitDangerousGrant',
|
|
192
|
+
'currentRelationOnly',
|
|
192
193
|
'requireFieldOverride',
|
|
194
|
+
'configFieldPolicy',
|
|
193
195
|
'allowedArgs',
|
|
194
196
|
'deniedArgs',
|
|
195
197
|
'forbiddenFlags',
|
|
@@ -211,7 +213,9 @@ const COMMAND_BOOLEAN_CONSTRAINTS = new Set([
|
|
|
211
213
|
'requireDaemonOwner',
|
|
212
214
|
'requireControlChannel',
|
|
213
215
|
'requireExplicitDangerousGrant',
|
|
216
|
+
'currentRelationOnly',
|
|
214
217
|
]);
|
|
218
|
+
const COMMAND_CONFIG_FIELD_POLICIES = new Set(['behavior-read', 'role-overridable-write']);
|
|
215
219
|
const COMMAND_STRING_ARRAY_CONSTRAINTS = new Set([
|
|
216
220
|
'forbiddenFlags',
|
|
217
221
|
'allowedConfigKeys',
|
|
@@ -394,6 +398,9 @@ function validateCommandPermissionConstraints(rule, constraints, errors) {
|
|
|
394
398
|
else if (key === 'requireFieldOverride' && typeof value !== 'string') {
|
|
395
399
|
errors.push(`${label} must be a string`);
|
|
396
400
|
}
|
|
401
|
+
else if (key === 'configFieldPolicy' && !COMMAND_CONFIG_FIELD_POLICIES.has(String(value))) {
|
|
402
|
+
errors.push(`${label} must be behavior-read or role-overridable-write`);
|
|
403
|
+
}
|
|
397
404
|
else if (COMMAND_STRING_ARRAY_CONSTRAINTS.has(key)) {
|
|
398
405
|
if (!Array.isArray(value) || !value.every(item => typeof item === 'string')) {
|
|
399
406
|
errors.push(`${label} must be a string array`);
|
|
@@ -197,13 +197,13 @@ export function classifyTranscriptSource(binding, cliCandidate) {
|
|
|
197
197
|
let historyModulePromise = null;
|
|
198
198
|
async function historyModule() {
|
|
199
199
|
if (!historyModulePromise) {
|
|
200
|
-
historyModulePromise = import(toFileUrl(resolveParentDistModule('core', 'session', 'runner-
|
|
200
|
+
historyModulePromise = import(toFileUrl(resolveParentDistModule('core', 'session', 'runner-event-summary.js')));
|
|
201
201
|
}
|
|
202
202
|
return historyModulePromise;
|
|
203
203
|
}
|
|
204
204
|
export async function readRunnerTaskTurns(logsDir, evolSessionId) {
|
|
205
205
|
const module = await historyModule();
|
|
206
|
-
return module.
|
|
206
|
+
return module.readRunnerEventSummary(logsDir, evolSessionId).map((task) => ({
|
|
207
207
|
role: 'system', ts: task.startedAt,
|
|
208
208
|
uuid: `runner-task:${evolSessionId}:${task.taskId}`,
|
|
209
209
|
category: 'subtask', blocks: [{ kind: 'subtask', ...task }],
|
package/dist/sources/triggers.js
CHANGED
|
@@ -39,15 +39,6 @@ function readDaemonOwnerAid(p) {
|
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
function readFullAccessEnabled(p) {
|
|
43
|
-
try {
|
|
44
|
-
const raw = JSON.parse(fs.readFileSync(p.daemonConfig, 'utf8'));
|
|
45
|
-
return raw?.fullaccess?.enabled === undefined || raw.fullaccess.enabled === true;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
42
|
function normalizeAgent(item) {
|
|
52
43
|
const aid = String(item?.value || item?.aid || '').trim();
|
|
53
44
|
if (!aid)
|
|
@@ -169,7 +160,6 @@ async function listTriggersForAgent(agentAid, agentLabel) {
|
|
|
169
160
|
}
|
|
170
161
|
async function buildSnapshot(params) {
|
|
171
162
|
const p = resolvePaths();
|
|
172
|
-
const fullaccessEnabled = readFullAccessEnabled(p);
|
|
173
163
|
// This capability is derived from the server-injected ECWeb connection
|
|
174
164
|
// context; browser-supplied subscription parameters are never trusted for it.
|
|
175
165
|
const canApprovePersistentFullAccess = watchMenuAuth(params).canApprovePersistentFullAccess;
|
|
@@ -201,11 +191,11 @@ async function buildSnapshot(params) {
|
|
|
201
191
|
if (agent) {
|
|
202
192
|
const found = agentInfos.find((x) => x.aid === agent);
|
|
203
193
|
const triggers = await listTriggersForAgent(agent, found?.label || agent);
|
|
204
|
-
return { agents: agentInfos, triggers, selectedAgent: agent,
|
|
194
|
+
return { agents: agentInfos, triggers, selectedAgent: agent, canApprovePersistentFullAccess };
|
|
205
195
|
}
|
|
206
196
|
const batches = await Promise.all(agentInfos.map((x) => listTriggersForAgent(x.aid, x.label)));
|
|
207
197
|
const triggers = batches.flat();
|
|
208
|
-
return { agents: agentInfos, triggers, selectedAgent: agent,
|
|
198
|
+
return { agents: agentInfos, triggers, selectedAgent: agent, canApprovePersistentFullAccess };
|
|
209
199
|
}
|
|
210
200
|
export const triggersSource = {
|
|
211
201
|
kind: 'triggers',
|