evolcore 0.0.10 → 0.0.12
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/CHANGELOG.md +44 -0
- package/README.md +3 -3
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/baseagent.js +4 -0
- package/dist/agents/claude-runner.js +158 -44
- package/dist/agents/codex-app-server-client.js +153 -23
- package/dist/agents/codex-runner.js +331 -43
- package/dist/agents/ecagent-runner.js +53 -6
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/agents/request-identity.js +55 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/outbox.js +28 -31
- package/dist/channels/aun.js +418 -235
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +72 -17
- package/dist/cli/agent.js +249 -22
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +52 -33
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +106 -63
- package/dist/cli/init-cancel.js +208 -0
- package/dist/cli/init-channel.js +343 -195
- package/dist/cli/init.js +21 -9
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +5 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/gateway-config.js +26 -10
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/agent-reload-coordinator.js +53 -0
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +62 -154
- package/dist/core/auth/operation-catalog.js +264 -22
- package/dist/core/bootstrap-messages.js +68 -0
- package/dist/core/bootstrap-service.js +144 -10
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +24 -7
- package/dist/core/command/agent-control.js +14 -11
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +366 -0
- package/dist/core/command/menu-handler.js +213 -110
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +45 -25
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent-registry.js +125 -35
- package/dist/core/evolagent.js +12 -5
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +38 -4
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +237 -79
- package/dist/core/message/message-log.js +23 -0
- package/dist/core/message/message-queue.js +20 -4
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/model/model-catalog.js +143 -24
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/index.js +1 -0
- package/dist/core/permission/readonly-shell-query.js +532 -0
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/shell-environment.js +46 -0
- package/dist/core/permission/tool-policy.js +292 -111
- package/dist/core/protected-paths.js +17 -9
- package/dist/core/runner-reload-transaction.js +57 -0
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +644 -180
- package/dist/ipc.js +246 -19
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/utils/aid-bind.js +3 -8
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/log-writer.js +6 -10
- package/dist/utils/logger.js +5 -5
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/evolcore/msg.md +13 -0
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/rules/01-overview.md +9 -0
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/agent-config.schema.3.json +1 -1
- package/kits/schemas/agent-config.schema.4.json +1 -1
- package/kits/schemas/relation-config.schema.2.json +1 -1
- package/kits/schemas/role-config.schema.1.json +1 -1
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +27 -0
- package/kits/templates/roles/member.json +44 -0
- package/kits/templates/roles/visitor.json +40 -2
- package/kits/templates/system-fragments/bootstrap.md +12 -6
- package/kits/templates/system-fragments/channel.md +6 -0
- package/kits/templates/system-fragments/session.md +2 -0
- package/package.json +3 -2
- package/skills/eclink/SKILL.md +15 -3
- package/skills/eclink/agents/openai.yaml +3 -3
- package/dist/core/command/evol-menu-version-gate.js +0 -39
|
@@ -15,7 +15,7 @@ import { execFileSync } from 'child_process';
|
|
|
15
15
|
import { CronExpressionParser } from 'cron-parser';
|
|
16
16
|
import { parseDuration } from '../../trigger/parser.js';
|
|
17
17
|
import { checkLatestVersion, getLocalVersion, isLinkedInstall, compareVersions, resolveGlobalPkg } from '../../utils/npm-ops.js';
|
|
18
|
-
import { commandExists } from '../../utils/cross-platform.js';
|
|
18
|
+
import { commandExists, spawnDetachedNode } from '../../utils/cross-platform.js';
|
|
19
19
|
import { loadDefaults, loadDaemonConfig } from '../../config-store.js';
|
|
20
20
|
import { WEB_PACKAGE_NAME } from '../../product.js';
|
|
21
21
|
import { read as cfgRead, resolveEffective, resolveEffectiveFieldWithSource, routeFieldPath, write as cfgWrite, ConfigTarget, } from '../../config/config-manager.js';
|
|
@@ -36,11 +36,11 @@ import { isManagementRole } from '../../config/builtin-roles.js';
|
|
|
36
36
|
import { SYSTEM_CONTROL_CHANNEL } from '../system-channels.js';
|
|
37
37
|
import { logger } from '../../utils/logger.js';
|
|
38
38
|
import { shouldSuppressRealRestart } from '../../utils/restart-safety.js';
|
|
39
|
-
import {
|
|
40
|
-
import { menuFailure, menuSuccess, normalizeMenuError, validateConfigWriteScope, validateMenuRequest } from './menu-protocol.js';
|
|
39
|
+
import { evolMenuResponseTransportMetadata, menuFailure, menuCommandForName, menuSuccess, normalizeMenuError, validateConfigWriteScope, validateMenuRequest, } from './menu-protocol.js';
|
|
41
40
|
import { roleMenuAction, roleMenuOperation, roleMenuOptions, roleMenuQuery, roleMenuUpdate, } from './role-menu.js';
|
|
42
41
|
import { handleConnectMenu, connectMenuOperation } from './connect-menu.js';
|
|
43
42
|
import { groupMenuAuthorizationArgs, groupMenuIntent, handleGroupMenu, } from './group-menu.js';
|
|
43
|
+
import { getMenuCatalog, execMenuValueBatch, resolveExplicitMenuExecutionContext, validateMenuCatalogArgs, validateMenuValueBatchArgs } from './menu-catalog.js';
|
|
44
44
|
/**
|
|
45
45
|
* 获取 baseagent CLI 的版本号(claude/gemini/codex)。
|
|
46
46
|
* 失败返回 null(命令不存在或执行失败)。
|
|
@@ -60,6 +60,7 @@ function getBaseagentVersion(cmd) {
|
|
|
60
60
|
encoding: 'utf-8',
|
|
61
61
|
timeout: 3000,
|
|
62
62
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
63
|
+
windowsHide: process.platform === 'win32',
|
|
63
64
|
});
|
|
64
65
|
const versionOutput = String(output).trim();
|
|
65
66
|
// claude: "2.1.187 (Claude Code)" → 提取 "2.1.187"
|
|
@@ -873,20 +874,36 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
873
874
|
});
|
|
874
875
|
const modelScope = args?.scope === 'agent' ? 'agent' : 'relation';
|
|
875
876
|
if (verb === 'query') {
|
|
877
|
+
if (cmdBase === '/pwd')
|
|
878
|
+
return intent('project.current', 'relation');
|
|
879
|
+
if (cmdBase === '/session' || cmdBase === '/s')
|
|
880
|
+
return intent('session.current', 'relation');
|
|
881
|
+
if (cmdBase === '/topic')
|
|
882
|
+
return intent('session.topic.current', 'relation');
|
|
883
|
+
if (cmdBase === '/baseagent')
|
|
884
|
+
return intent('agent.baseagent.current', fromControlChannel || args?.scope === 'agent' ? 'agent' : 'relation');
|
|
876
885
|
if (cmdBase === '/file')
|
|
877
886
|
return intent('file.fetch', 'filesystem', { filePath: args?.path });
|
|
878
887
|
if (cmdBase === '/model')
|
|
879
888
|
return intent('model.current', modelScope);
|
|
880
889
|
if (cmdBase === '/effort')
|
|
881
|
-
return intent('model.current', modelScope);
|
|
890
|
+
return intent('model.effort.current', modelScope);
|
|
882
891
|
if (cmdBase === '/chatmode')
|
|
883
892
|
return intent('chatmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
884
893
|
if (cmdBase === '/mentionmode')
|
|
885
894
|
return intent('mentionmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
895
|
+
if (cmdBase === '/perm')
|
|
896
|
+
return intent('permission.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
897
|
+
if (cmdBase === '/activity')
|
|
898
|
+
return intent('activity.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
899
|
+
if (cmdBase === '/observable')
|
|
900
|
+
return intent('observable.current', 'agent');
|
|
901
|
+
if (cmdBase === '/capability')
|
|
902
|
+
return intent('capability.read', 'agent');
|
|
886
903
|
if (cmdBase === '/gateway')
|
|
887
904
|
return intent('gateway.read', 'process');
|
|
888
905
|
if (cmdBase === '/config')
|
|
889
|
-
return intent('config.read', 'process');
|
|
906
|
+
return intent('config.read', args?.scope === 'agent' ? 'agent' : 'process');
|
|
890
907
|
if (cmdBase === '/system')
|
|
891
908
|
return intent('system.status', 'process');
|
|
892
909
|
if (cmdBase === '/agent') {
|
|
@@ -895,7 +912,35 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
895
912
|
if (cmdBase === '/trigger')
|
|
896
913
|
return intent('trigger.list', 'relation');
|
|
897
914
|
}
|
|
915
|
+
if (verb === 'options') {
|
|
916
|
+
if (cmdBase === '/agent')
|
|
917
|
+
return intent(fromControlChannel ? 'agent.list' : 'agent.show', fromControlChannel ? 'control' : 'agent');
|
|
918
|
+
if (cmdBase === '/capability')
|
|
919
|
+
return intent('capability.read', 'agent');
|
|
920
|
+
if (cmdBase === '/trigger')
|
|
921
|
+
return intent('trigger.list', 'relation');
|
|
922
|
+
if (cmdBase === '/topic')
|
|
923
|
+
return intent('session.topic.list', 'relation');
|
|
924
|
+
if (cmdBase === '/session' || cmdBase === '/s' || cmdBase === '/del')
|
|
925
|
+
return intent('session.list', 'relation');
|
|
926
|
+
if (cmdBase === '/baseagent')
|
|
927
|
+
return intent('agent.baseagent.list', 'agent');
|
|
928
|
+
if (cmdBase === '/model')
|
|
929
|
+
return intent('model.list', modelScope);
|
|
930
|
+
if (cmdBase === '/effort')
|
|
931
|
+
return intent('model.effort.current', modelScope);
|
|
932
|
+
if (cmdBase === '/chatmode')
|
|
933
|
+
return intent('chatmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
934
|
+
if (cmdBase === '/mentionmode')
|
|
935
|
+
return intent('mentionmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
936
|
+
if (cmdBase === '/activity')
|
|
937
|
+
return intent('activity.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
938
|
+
if (cmdBase === '/perm')
|
|
939
|
+
return intent('permission.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
940
|
+
}
|
|
898
941
|
if (verb === 'update') {
|
|
942
|
+
if (cmdBase === '/baseagent')
|
|
943
|
+
return intent('agent.baseagent.update', 'agent', { baseagent: value });
|
|
899
944
|
if (cmdBase === '/model')
|
|
900
945
|
return intent('model.use', modelScope, { model: value });
|
|
901
946
|
if (cmdBase === '/effort')
|
|
@@ -904,6 +949,10 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
904
949
|
return intent('chatmode.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
|
|
905
950
|
if (cmdBase === '/mentionmode')
|
|
906
951
|
return intent('mentionmode.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
|
|
952
|
+
if (cmdBase === '/activity')
|
|
953
|
+
return intent('activity.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
|
|
954
|
+
if (cmdBase === '/observable')
|
|
955
|
+
return intent('observable.update', 'agent', { value });
|
|
907
956
|
if (cmdBase === '/gateway')
|
|
908
957
|
return intent('gateway.write', 'process');
|
|
909
958
|
if (cmdBase === '/config')
|
|
@@ -1010,6 +1059,9 @@ async function authorizeMenuIntent(params) {
|
|
|
1010
1059
|
peerKey: subject.peerKey,
|
|
1011
1060
|
});
|
|
1012
1061
|
}
|
|
1062
|
+
else if (intent.scope === 'agent' && subject.selfAid && intent.args.self === undefined) {
|
|
1063
|
+
intent.args = { ...intent.args, self: subject.selfAid };
|
|
1064
|
+
}
|
|
1013
1065
|
const source = params.source ?? 'menu';
|
|
1014
1066
|
intent.source = source;
|
|
1015
1067
|
const decision = await authorizeOperation({ source, intent, subject });
|
|
@@ -1044,6 +1096,9 @@ function buildRoleMenuContext(owner, channel, subject) {
|
|
|
1044
1096
|
}
|
|
1045
1097
|
const aunAvailable = (owningAgent?.channelInstanceNames?.() ?? [])
|
|
1046
1098
|
.some((channelName) => owner.hasRegisteredChannel?.(channelName, 'aun') === true);
|
|
1099
|
+
const aunChannelName = (owningAgent?.channelInstanceNames?.() ?? [])
|
|
1100
|
+
.find((name) => String(name).split('#', 1)[0].toLowerCase() === 'aun');
|
|
1101
|
+
const contactAdapter = aunChannelName ? owner.getAdapter?.(aunChannelName) : undefined;
|
|
1047
1102
|
return {
|
|
1048
1103
|
self,
|
|
1049
1104
|
actorRole: subject.role,
|
|
@@ -1052,6 +1107,26 @@ function buildRoleMenuContext(owner, channel, subject) {
|
|
|
1052
1107
|
actorAid: subject.actorId,
|
|
1053
1108
|
chatType: subject.chatType,
|
|
1054
1109
|
aunAvailable,
|
|
1110
|
+
...(contactAdapter?.send ? {
|
|
1111
|
+
notifyContactRequestReviewed: async ({ applicantAid, requestId, expiresAt, status }) => {
|
|
1112
|
+
await contactAdapter.send(buildEnvelope({
|
|
1113
|
+
taskId: `contact-request-${requestId}`,
|
|
1114
|
+
channel: contactAdapter.channelName,
|
|
1115
|
+
channelId: applicantAid,
|
|
1116
|
+
agentName: owningAgent?.name ?? '<unknown>',
|
|
1117
|
+
replyContext: { metadata: evolMenuResponseTransportMetadata() },
|
|
1118
|
+
}), {
|
|
1119
|
+
kind: 'custom',
|
|
1120
|
+
channelType: 'aun',
|
|
1121
|
+
payload: {
|
|
1122
|
+
type: 'contact.request.response',
|
|
1123
|
+
status,
|
|
1124
|
+
request_id: requestId,
|
|
1125
|
+
expires_at: expiresAt,
|
|
1126
|
+
},
|
|
1127
|
+
});
|
|
1128
|
+
},
|
|
1129
|
+
} : {}),
|
|
1055
1130
|
availableBaseagents: Array.from(new Set([
|
|
1056
1131
|
...Object.keys(owningAgent?.config?.baseagents ?? {}),
|
|
1057
1132
|
...(owningAgent?.baseagent ? [owningAgent.baseagent] : []),
|
|
@@ -1240,6 +1315,7 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent', authSu
|
|
|
1240
1315
|
fromControlChannel: isControlScope,
|
|
1241
1316
|
}).allow;
|
|
1242
1317
|
};
|
|
1318
|
+
const canReadModel = canOperation('model.list', 'relation') || canOperation('model.current', 'relation');
|
|
1243
1319
|
const canUseModel = canOperation('model.use', 'relation');
|
|
1244
1320
|
const canSetEffort = canOperation('model.effort', 'relation');
|
|
1245
1321
|
const canSetChatmode = canOperation('chatmode.update', 'relation');
|
|
@@ -1248,7 +1324,12 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent', authSu
|
|
|
1248
1324
|
const canUseFile = canOperation('file.fetch', 'filesystem') || canOperation('file.list', 'filesystem');
|
|
1249
1325
|
const canManageProcess = authSubject ? authSubject.isDaemonOwner : isOwner;
|
|
1250
1326
|
const configurableSettings = [
|
|
1251
|
-
...(
|
|
1327
|
+
...(canReadModel ? [{
|
|
1328
|
+
cmd: '/model',
|
|
1329
|
+
label: canUseModel ? '切换模型' : '查看模型',
|
|
1330
|
+
desc: canUseModel ? '切换当前 Agent 使用的模型版本' : '查看当前 Agent 使用的模型版本',
|
|
1331
|
+
next: canUseModel ? { type: 'select', dynamic: true } : undefined,
|
|
1332
|
+
}] : []),
|
|
1252
1333
|
...(canSetEffort ? [{ cmd: '/effort', label: '切换推理强度', desc: '调整模型推理深度,影响响应速度与质量', next: { type: 'select', items: [
|
|
1253
1334
|
{ value: 'low', label: 'Low' },
|
|
1254
1335
|
{ value: 'medium', label: 'Medium' },
|
|
@@ -1428,13 +1509,25 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1428
1509
|
throw { code: result.code, message: result.error, data: result.data };
|
|
1429
1510
|
return result.data;
|
|
1430
1511
|
}
|
|
1512
|
+
const optionsIdentity = overrideIdentity ?? this.sessionManager.resolveIdentity(channel, userId, ...menuIdentityArgs(session));
|
|
1513
|
+
const optionsAuthDenied = await authorizeMenuIntent.call(this, {
|
|
1514
|
+
intent: buildMenuIntent('options', cmdBase0, args, undefined, undefined, fromControlChannel),
|
|
1515
|
+
identity: optionsIdentity,
|
|
1516
|
+
subject,
|
|
1517
|
+
session,
|
|
1518
|
+
explicitChatType,
|
|
1519
|
+
channel,
|
|
1520
|
+
channelId,
|
|
1521
|
+
userId,
|
|
1522
|
+
fromControlChannel,
|
|
1523
|
+
source,
|
|
1524
|
+
});
|
|
1525
|
+
if (optionsAuthDenied)
|
|
1526
|
+
throw { code: optionsAuthDenied.code, message: optionsAuthDenied.error, data: optionsAuthDenied.data };
|
|
1431
1527
|
// ── /agent list(只读) ──
|
|
1432
|
-
// 控制 channel
|
|
1528
|
+
// 控制 channel:返回全量;agent channel:仅返回自身单条。
|
|
1433
1529
|
if (cmd === '/agent') {
|
|
1434
1530
|
if (fromControlChannel) {
|
|
1435
|
-
if (!subject.isDaemonOwner) {
|
|
1436
|
-
throw { code: 'FORBIDDEN', message: '操作需要 owner 权限' };
|
|
1437
|
-
}
|
|
1438
1531
|
const res = await execAgentOptions(args);
|
|
1439
1532
|
if ('error' in res)
|
|
1440
1533
|
throw { code: res.code, message: res.error };
|
|
@@ -1465,24 +1558,9 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1465
1558
|
}
|
|
1466
1559
|
// ── 关系级 /trigger list(每个 trigger 一个 MenuItem) ──
|
|
1467
1560
|
if (cmd === '/trigger') {
|
|
1468
|
-
const identity = overrideIdentity ?? this.sessionManager.resolveIdentity(channel, userId, ...menuIdentityArgs(session));
|
|
1469
|
-
const authDenied = await authorizeMenuIntent.call(this, {
|
|
1470
|
-
intent: buildMenuIntent('query', cmd, args, undefined, undefined, fromControlChannel),
|
|
1471
|
-
identity,
|
|
1472
|
-
subject,
|
|
1473
|
-
session,
|
|
1474
|
-
explicitChatType,
|
|
1475
|
-
channel,
|
|
1476
|
-
channelId,
|
|
1477
|
-
userId,
|
|
1478
|
-
fromControlChannel,
|
|
1479
|
-
source,
|
|
1480
|
-
});
|
|
1481
|
-
if (authDenied)
|
|
1482
|
-
throw { code: authDenied.code, message: authDenied.error, data: authDenied.data };
|
|
1483
1561
|
const triggerScheduler = this.getTriggerSchedulerForChannel?.(channel);
|
|
1484
1562
|
const scope = args?.options === 'all' ? 'all' : 'enabled';
|
|
1485
|
-
const role =
|
|
1563
|
+
const role = optionsIdentity.role;
|
|
1486
1564
|
const isAdmin = isManagementRole(role);
|
|
1487
1565
|
if (!triggerScheduler)
|
|
1488
1566
|
return [];
|
|
@@ -1501,10 +1579,6 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1501
1579
|
});
|
|
1502
1580
|
}
|
|
1503
1581
|
if (cmd === '/topic') {
|
|
1504
|
-
const identity = overrideIdentity ?? this.sessionManager.resolveIdentity(channel, userId, ...menuIdentityArgs(session));
|
|
1505
|
-
if (!this.canReadTopics(identity.role)) {
|
|
1506
|
-
throw { code: 'FORBIDDEN', message: '无权限查看话题' };
|
|
1507
|
-
}
|
|
1508
1582
|
if (args?.mode === 'fork-turns') {
|
|
1509
1583
|
const source = await resolveTopicForkSource(this.sessionManager, channel, channelId, args?.sourceThreadId);
|
|
1510
1584
|
if (!source)
|
|
@@ -1577,14 +1651,13 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1577
1651
|
return available.map((name) => ({ value: name, label: name, selected: name === currentAgent }));
|
|
1578
1652
|
}
|
|
1579
1653
|
if (cmd === '/model') {
|
|
1580
|
-
const identity = overrideIdentity ?? this.sessionManager.resolveIdentity(channel, userId, ...menuIdentityArgs(session));
|
|
1581
1654
|
const target = resolveMenuModelTarget.call(this, {
|
|
1582
1655
|
args,
|
|
1583
1656
|
session,
|
|
1584
1657
|
channel,
|
|
1585
1658
|
channelId,
|
|
1586
1659
|
userId,
|
|
1587
|
-
role:
|
|
1660
|
+
role: optionsIdentity.role,
|
|
1588
1661
|
explicitChatType,
|
|
1589
1662
|
fromControlChannel,
|
|
1590
1663
|
field: 'model',
|
|
@@ -1593,31 +1666,8 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1593
1666
|
throw { code: target.code, message: target.error };
|
|
1594
1667
|
const agent = this.getAgent(channel, target.baseagent);
|
|
1595
1668
|
if (hasModelSwitcher(agent) && agent.listModels) {
|
|
1596
|
-
const role = identity.role;
|
|
1597
|
-
const authIntent = {
|
|
1598
|
-
operation: 'model.list',
|
|
1599
|
-
scope: target.scope,
|
|
1600
|
-
source: 'menu',
|
|
1601
|
-
args: {},
|
|
1602
|
-
};
|
|
1603
|
-
const subject = buildMenuAuthSubject(this, {
|
|
1604
|
-
identity,
|
|
1605
|
-
session,
|
|
1606
|
-
explicitChatType,
|
|
1607
|
-
channel,
|
|
1608
|
-
channelId,
|
|
1609
|
-
userId,
|
|
1610
|
-
fromControlChannel,
|
|
1611
|
-
subject: authSubject,
|
|
1612
|
-
});
|
|
1613
|
-
authIntent.args = target.scope === 'relation'
|
|
1614
|
-
? buildRelationIntentArgs({ args, selfAid: target.sel.self, peerKey: target.sel.peerKey })
|
|
1615
|
-
: { ...(args ?? {}), self: target.sel.self };
|
|
1616
|
-
const decision = await authorizeOperation({ source: 'menu', intent: authIntent, subject, audit: false });
|
|
1617
|
-
if (!decision.allow)
|
|
1618
|
-
throw { code: decision.code, message: decision.reason };
|
|
1619
1669
|
const rawModels = await agent.listModels() ?? [];
|
|
1620
|
-
const models = filterModelsForRole(role, target.baseagent, rawModels, agent.resolveModelId?.bind(agent));
|
|
1670
|
+
const models = filterModelsForRole(optionsIdentity.role, target.baseagent, rawModels, agent.resolveModelId?.bind(agent));
|
|
1621
1671
|
const requestedModel = menuStringArg(args, 'model') ?? menuStringArg(args, 'current');
|
|
1622
1672
|
const currentModel = requestedModel || readMenuModel(target, agent).value || agent.getModel();
|
|
1623
1673
|
if (models.length > 0)
|
|
@@ -1715,7 +1765,7 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1715
1765
|
});
|
|
1716
1766
|
if ('error' in target)
|
|
1717
1767
|
throw { code: target.code, message: target.error };
|
|
1718
|
-
const fallback =
|
|
1768
|
+
const fallback = session?.metadata?.mentionMode ?? null;
|
|
1719
1769
|
const currentMode = readMenuMentionMode(target, fallback, fallback === null ? null : 'session').value;
|
|
1720
1770
|
return [
|
|
1721
1771
|
{ value: 'mention-only', label: '@提及时响应', selected: currentMode === 'mention-only' },
|
|
@@ -1741,6 +1791,10 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
1741
1791
|
const cmdBase = cmd.trim().split(' ')[0];
|
|
1742
1792
|
if (!cmdBase)
|
|
1743
1793
|
return { error: '缺少命令', code: 'MISSING_CMD' };
|
|
1794
|
+
const isRequesterRoleQuery = cmdBase === '/role' && args?.view === 'requester';
|
|
1795
|
+
if (isRequesterRoleQuery && Object.keys(args).some(key => key !== 'view')) {
|
|
1796
|
+
return { error: 'role requester query only accepts args.view', code: 'INVALID_ARGUMENT' };
|
|
1797
|
+
}
|
|
1744
1798
|
const gated = gateControlScope.call(this, { cmdBase, args, channel, fromControlChannel });
|
|
1745
1799
|
if (gated)
|
|
1746
1800
|
return gated;
|
|
@@ -1756,6 +1810,18 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
1756
1810
|
fromControlChannel,
|
|
1757
1811
|
subject: authSubject,
|
|
1758
1812
|
});
|
|
1813
|
+
if (isRequesterRoleQuery) {
|
|
1814
|
+
if (!subject.allowAccess) {
|
|
1815
|
+
return { error: `Role ${subject.role} is not allowed to access this agent`, code: 'ROLE_ACCESS_DENIED' };
|
|
1816
|
+
}
|
|
1817
|
+
return {
|
|
1818
|
+
data: {
|
|
1819
|
+
roleId: subject.role,
|
|
1820
|
+
source: subject.roleSource,
|
|
1821
|
+
allowAccess: subject.allowAccess,
|
|
1822
|
+
},
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1759
1825
|
if (cmdBase === '/role') {
|
|
1760
1826
|
const authorized = await authorizeRoleMenu(this, {
|
|
1761
1827
|
kind: 'query', args, identity, subject, session, channel, channelId,
|
|
@@ -1813,12 +1879,9 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
1813
1879
|
if (authDenied)
|
|
1814
1880
|
return authDenied;
|
|
1815
1881
|
// ── /agent 查询(只读) ──
|
|
1816
|
-
//
|
|
1882
|
+
// 授权由 agent.list/agent.show operation 决定;channel 闸门只负责目标范围。
|
|
1817
1883
|
if (cmdBase === '/agent') {
|
|
1818
1884
|
if (fromControlChannel) {
|
|
1819
|
-
if (!subject.isDaemonOwner) {
|
|
1820
|
-
return { error: '操作需要 owner 权限', code: 'FORBIDDEN' };
|
|
1821
|
-
}
|
|
1822
1885
|
return await execAgentQuery(args);
|
|
1823
1886
|
}
|
|
1824
1887
|
const selfAid = this.getOwningAgent?.(channel)?.aid;
|
|
@@ -1827,29 +1890,17 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
1827
1890
|
return await execAgentQuery({ ...(args ?? {}), aid: selfAid });
|
|
1828
1891
|
}
|
|
1829
1892
|
// ── /gateway 查询(只读,列出全部作用域的网关配置;apiKey 已掩码) ──
|
|
1830
|
-
// 进程级:闸门已要求 fromControlChannel;此处再验 owners 非空。
|
|
1831
1893
|
if (cmdBase === '/gateway') {
|
|
1832
|
-
if (!subject.isDaemonOwner) {
|
|
1833
|
-
return { error: '操作需要 owner 权限', code: 'FORBIDDEN' };
|
|
1834
|
-
}
|
|
1835
1894
|
return gatewayList();
|
|
1836
1895
|
}
|
|
1837
1896
|
// ── /config 查询(只读,查询各层配置) ──
|
|
1838
1897
|
if (cmdBase === '/config') {
|
|
1839
1898
|
const scope = args?.scope || 'process';
|
|
1840
1899
|
if (scope === 'process') {
|
|
1841
|
-
// 进程级配置需要 owner 权限
|
|
1842
|
-
if (!fromControlChannel || !subject.isDaemonOwner) {
|
|
1843
|
-
return { error: '操作需要 owner 权限', code: 'FORBIDDEN' };
|
|
1844
|
-
}
|
|
1845
1900
|
const cfg = loadDaemonConfig();
|
|
1846
1901
|
return { data: { scope: 'process', config: cfg } };
|
|
1847
1902
|
}
|
|
1848
1903
|
if (scope === 'defaults') {
|
|
1849
|
-
// 全局默认配置需要 owner 权限
|
|
1850
|
-
if (!fromControlChannel || !subject.isDaemonOwner) {
|
|
1851
|
-
return { error: '操作需要 owner 权限', code: 'FORBIDDEN' };
|
|
1852
|
-
}
|
|
1853
1904
|
const { read, ConfigTarget } = await import('../../config/config-manager.js');
|
|
1854
1905
|
const cfg = read(ConfigTarget.Defaults, undefined, { cache: true });
|
|
1855
1906
|
return { data: { scope: 'defaults', config: cfg } };
|
|
@@ -1938,9 +1989,6 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
1938
1989
|
return { data };
|
|
1939
1990
|
}
|
|
1940
1991
|
if (cmdBase === '/topic') {
|
|
1941
|
-
if (!this.canReadTopics(identity.role)) {
|
|
1942
|
-
return { error: '无权限查看话题', code: 'FORBIDDEN' };
|
|
1943
|
-
}
|
|
1944
1992
|
const target = (args?.target ?? '').toString().trim();
|
|
1945
1993
|
if (!target)
|
|
1946
1994
|
return { error: '缺少 args.target', code: 'MISSING_VALUE' };
|
|
@@ -2095,8 +2143,7 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2095
2143
|
});
|
|
2096
2144
|
if ('error' in target)
|
|
2097
2145
|
return target;
|
|
2098
|
-
|
|
2099
|
-
const fallback = dispatchToMentionMode(session?.metadata?.dispatchMode) ?? null;
|
|
2146
|
+
const fallback = session?.metadata?.mentionMode ?? null;
|
|
2100
2147
|
const current = readMenuMentionMode(target, fallback, fallback === null ? null : 'session');
|
|
2101
2148
|
return {
|
|
2102
2149
|
data: {
|
|
@@ -2110,8 +2157,6 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2110
2157
|
};
|
|
2111
2158
|
}
|
|
2112
2159
|
if (cmdBase === '/observable') {
|
|
2113
|
-
if (identity.role !== 'owner')
|
|
2114
|
-
return { error: '观察者模式仅限 owner 查看', code: 'NO_PERMISSION' };
|
|
2115
2160
|
if (!evolagent)
|
|
2116
2161
|
return { error: '找不到通道所属 agent', code: 'MISSING_AID' };
|
|
2117
2162
|
const observable = evolagent?.getObservable() ?? false;
|
|
@@ -2173,9 +2218,6 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2173
2218
|
};
|
|
2174
2219
|
}
|
|
2175
2220
|
if (cmdBase === '/system') {
|
|
2176
|
-
if (!subject.isDaemonOwner) {
|
|
2177
|
-
return { error: '操作需要 owner 权限', code: 'FORBIDDEN' };
|
|
2178
|
-
}
|
|
2179
2221
|
const data = {
|
|
2180
2222
|
aid: loadDaemonConfig().aid ?? null,
|
|
2181
2223
|
pid: process.pid,
|
|
@@ -2614,8 +2656,6 @@ export async function execMenuUpdate(cmd, value, channel, channelId, userId, ove
|
|
|
2614
2656
|
const newMode = modeMap[arg];
|
|
2615
2657
|
if (!newMode)
|
|
2616
2658
|
return { error: `无效模式: ${arg},可选: all / text / none`, code: 'INVALID_VALUE' };
|
|
2617
|
-
if (identity.role !== 'owner')
|
|
2618
|
-
return { error: '中间输出模式切换仅限 owner', code: 'NO_PERMISSION' };
|
|
2619
2659
|
const target = resolveMenuActivityTarget.call(this, {
|
|
2620
2660
|
args,
|
|
2621
2661
|
session,
|
|
@@ -3278,12 +3318,9 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3278
3318
|
const controlDir = daemonControlDir();
|
|
3279
3319
|
fs.mkdirSync(controlDir, { recursive: true });
|
|
3280
3320
|
fs.writeFileSync(path.join(controlDir, 'restart-pending.json'), JSON.stringify(restartInfo));
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
stdio: 'ignore',
|
|
3285
|
-
env: { ...process.env, EVOLCORE_HOME: resolvePaths().root }
|
|
3286
|
-
}).unref();
|
|
3321
|
+
spawnDetachedNode([path.join(getPackageRoot(), 'dist', 'cli', 'index.js'), 'restart-monitor'], {
|
|
3322
|
+
EVOLCORE_HOME: resolvePaths().root,
|
|
3323
|
+
});
|
|
3287
3324
|
}
|
|
3288
3325
|
else {
|
|
3289
3326
|
logger.info('[System] Suppressed real menu restart in test runtime');
|
|
@@ -3319,7 +3356,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3319
3356
|
data: {
|
|
3320
3357
|
devMode,
|
|
3321
3358
|
evolcore: { local: localEvolcore, remote: evolcoreRemote, hasUpdate: cmp(localEvolcore, evolcoreRemote) },
|
|
3322
|
-
fastaun: { local: localFastaun, remote: fastaunRemote, hasUpdate: cmp(localFastaun, fastaunRemote) },
|
|
3359
|
+
fastaun: { local: localFastaun, remote: fastaunRemote, hasUpdate: cmp(localFastaun, fastaunRemote), managedBy: 'evolcore' },
|
|
3323
3360
|
// ecweb 本地版本由 ECWeb 进程自身注入(data.ecwebVersion),此处仅给 remote
|
|
3324
3361
|
ecweb: { remote: ecwebRemote },
|
|
3325
3362
|
},
|
|
@@ -3500,16 +3537,6 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3500
3537
|
}
|
|
3501
3538
|
return { error: `不支持 action: ${cmdBase}`, code: 'NOT_SUPPORTED' };
|
|
3502
3539
|
}
|
|
3503
|
-
const SYSTEM_CONTROL_NAME_MAP = {
|
|
3504
|
-
pwd: '/pwd', session: '/session', baseagent: '/baseagent', model: '/model',
|
|
3505
|
-
topic: '/topic',
|
|
3506
|
-
effort: '/effort', chatmode: '/chatmode', mentionmode: '/mentionmode',
|
|
3507
|
-
permission: '/perm', activity: '/activity', system: '/system',
|
|
3508
|
-
observable: '/observable',
|
|
3509
|
-
agent: '/agent', trigger: '/trigger', file: '/file', gateway: '/gateway',
|
|
3510
|
-
config: '/config', capability: '/capability', role: '/role',
|
|
3511
|
-
group: '/group', connect: '/connect',
|
|
3512
|
-
};
|
|
3513
3540
|
function menuCommandToken(cmd) {
|
|
3514
3541
|
if (typeof cmd !== 'string')
|
|
3515
3542
|
return undefined;
|
|
@@ -3520,6 +3547,13 @@ function isProcessLevelMenu(name, cmd) {
|
|
|
3520
3547
|
return name === 'system' || name === 'agent' || name === 'gateway' || name === 'config'
|
|
3521
3548
|
|| cmdToken === '/system' || cmdToken === '/agent' || cmdToken === '/gateway' || cmdToken === '/config';
|
|
3522
3549
|
}
|
|
3550
|
+
function isProcessContextMenu(payload) {
|
|
3551
|
+
const isCatalog = payload?.type === 'menu.list' && payload?.args?.view === 'catalog';
|
|
3552
|
+
const isValueBatch = payload?.type === 'menu.query'
|
|
3553
|
+
&& payload?.name === 'menu'
|
|
3554
|
+
&& payload?.args?.view === 'values';
|
|
3555
|
+
return (isCatalog || isValueBatch) && payload?.args?.context?.kind === 'process';
|
|
3556
|
+
}
|
|
3523
3557
|
async function execMenuForSystemControl(payload, context) {
|
|
3524
3558
|
const id = payload?.id ?? '';
|
|
3525
3559
|
const name = payload?.name;
|
|
@@ -3540,13 +3574,15 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3540
3574
|
: 'cli 不在控制 channel 范围';
|
|
3541
3575
|
return { type: 'menu.response', id, name, error: { code: 'NOT_SUPPORTED', message } };
|
|
3542
3576
|
}
|
|
3543
|
-
const isProcessLevel = isProcessLevelMenu(name, payload?.cmd);
|
|
3577
|
+
const isProcessLevel = isProcessLevelMenu(name, payload?.cmd) || isProcessContextMenu(payload);
|
|
3578
|
+
const requestedContextKind = payload?.args?.context?.kind;
|
|
3544
3579
|
const isRoleRequest = name === 'role' || compatCmd === '/role';
|
|
3545
3580
|
const isConnectRequest = name === 'connect' || compatCmd === '/connect';
|
|
3546
3581
|
const requiresAgentTarget = isRoleRequest || isConnectRequest || [
|
|
3547
3582
|
'observable', 'baseagent', 'model', 'effort', 'trigger', 'capability', 'group',
|
|
3548
3583
|
].includes(name)
|
|
3549
|
-
|| ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(compatCmd ?? '')
|
|
3584
|
+
|| ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(compatCmd ?? '')
|
|
3585
|
+
|| (!isProcessLevel && requestedContextKind !== undefined && requestedContextKind !== 'process');
|
|
3550
3586
|
const target = isProcessLevel
|
|
3551
3587
|
? { channel: SYSTEM_CONTROL_CHANNEL }
|
|
3552
3588
|
: resolveExternalMenuAgentChannel(this, payload, SYSTEM_CONTROL_CHANNEL, requiresAgentTarget);
|
|
@@ -3567,21 +3603,71 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3567
3603
|
fromControlChannel: true,
|
|
3568
3604
|
});
|
|
3569
3605
|
const trustedIdentity = trustedSubject.identity;
|
|
3570
|
-
const cmd = name ? (
|
|
3606
|
+
const cmd = name ? (menuCommandForName(name) ?? payload.cmd) : payload.cmd;
|
|
3571
3607
|
try {
|
|
3572
3608
|
switch (payload?.type) {
|
|
3573
3609
|
case 'menu.list':
|
|
3610
|
+
if (payload?.args?.view === 'catalog') {
|
|
3611
|
+
validateMenuCatalogArgs(payload.args);
|
|
3612
|
+
const data = await getMenuCatalog({
|
|
3613
|
+
subject: trustedSubject,
|
|
3614
|
+
channelType: 'aun',
|
|
3615
|
+
channelScope: 'control',
|
|
3616
|
+
channelId: targetChannel,
|
|
3617
|
+
request: payload.args.context,
|
|
3618
|
+
});
|
|
3619
|
+
return menuSuccess({ id, ...(name ? { name } : {}) }, data);
|
|
3620
|
+
}
|
|
3621
|
+
if (payload?.args?.view !== undefined)
|
|
3622
|
+
return ecwebErr(id, name, 'NOT_SUPPORTED', `Unsupported menu.list view: ${String(payload.args.view)}`);
|
|
3574
3623
|
return menuSuccess({ id }, this.getMenuItems(trustedIdentity.role, 'private', 'control', trustedSubject));
|
|
3575
3624
|
case 'menu.query': {
|
|
3625
|
+
if (name === 'menu' && payload?.args?.view === 'values') {
|
|
3626
|
+
validateMenuValueBatchArgs(payload.args);
|
|
3627
|
+
const data = await execMenuValueBatch({
|
|
3628
|
+
subject: trustedSubject,
|
|
3629
|
+
channelType: 'aun',
|
|
3630
|
+
channelScope: 'control',
|
|
3631
|
+
channelId: targetChannel,
|
|
3632
|
+
request: payload.args.context,
|
|
3633
|
+
items: payload.args.items,
|
|
3634
|
+
schemaVersion: payload.args.schemaVersion,
|
|
3635
|
+
consistency: payload.args.consistency,
|
|
3636
|
+
execQuery: async (entryCmd, resolvedChannelId, queryArgs, catalogContext) => this.execMenuQuery(entryCmd, targetChannel, resolvedChannelId, catalogContext.subject.actorId, queryArgs, catalogContext.subject.chatType, true, catalogContext.subject.identity, catalogContext.subject, context.source),
|
|
3637
|
+
});
|
|
3638
|
+
return menuSuccess({ id, ...(name ? { name } : {}) }, data);
|
|
3639
|
+
}
|
|
3640
|
+
if (name === 'menu')
|
|
3641
|
+
return ecwebErr(id, name, 'NOT_SUPPORTED', `Unsupported menu.query view: ${String(payload?.args?.view)}`);
|
|
3576
3642
|
if (!cmd)
|
|
3577
3643
|
return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
|
|
3578
|
-
const
|
|
3644
|
+
const hasExplicitContext = !!payload?.args && Object.prototype.hasOwnProperty.call(payload.args, 'context');
|
|
3645
|
+
const execution = await resolveExplicitMenuExecutionContext({
|
|
3646
|
+
subject: trustedSubject,
|
|
3647
|
+
channelType: 'aun',
|
|
3648
|
+
channelScope: 'control',
|
|
3649
|
+
channelId: targetChannel,
|
|
3650
|
+
args: payload.args,
|
|
3651
|
+
});
|
|
3652
|
+
const r = hasExplicitContext
|
|
3653
|
+
? await this.execMenuQuery(cmd, targetChannel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.chatType, true, execution.subject.identity, execution.subject, context.source)
|
|
3654
|
+
: await this.execMenuQuery(cmd, targetChannel, targetChannel, context.actorAid, payload.args, undefined, true, trustedIdentity, trustedSubject, context.source);
|
|
3579
3655
|
return ecwebResp(id, name, r);
|
|
3580
3656
|
}
|
|
3581
3657
|
case 'menu.options': {
|
|
3582
3658
|
if (!cmd)
|
|
3583
3659
|
return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
|
|
3584
|
-
const
|
|
3660
|
+
const hasExplicitContext = !!payload?.args && Object.prototype.hasOwnProperty.call(payload.args, 'context');
|
|
3661
|
+
const execution = await resolveExplicitMenuExecutionContext({
|
|
3662
|
+
subject: trustedSubject,
|
|
3663
|
+
channelType: 'aun',
|
|
3664
|
+
channelScope: 'control',
|
|
3665
|
+
channelId: targetChannel,
|
|
3666
|
+
args: payload.args,
|
|
3667
|
+
});
|
|
3668
|
+
const data = hasExplicitContext
|
|
3669
|
+
? await this.getSubMenuItems(cmd, targetChannel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.identity, execution.subject.chatType, true, execution.subject, context.source) ?? []
|
|
3670
|
+
: await this.getSubMenuItems(cmd, targetChannel, targetChannel, context.actorAid, payload.args, trustedIdentity, undefined, true, trustedSubject, context.source) ?? [];
|
|
3585
3671
|
return menuSuccess({ id, ...(name ? { name } : {}) }, data);
|
|
3586
3672
|
}
|
|
3587
3673
|
case 'menu.update': {
|
|
@@ -3589,7 +3675,14 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3589
3675
|
return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
|
|
3590
3676
|
if (!payload.value)
|
|
3591
3677
|
return ecwebErr(id, name, 'MISSING_VALUE', '缺少 value');
|
|
3592
|
-
const
|
|
3678
|
+
const execution = await resolveExplicitMenuExecutionContext({
|
|
3679
|
+
subject: trustedSubject,
|
|
3680
|
+
channelType: 'aun',
|
|
3681
|
+
channelScope: 'control',
|
|
3682
|
+
channelId: targetChannel,
|
|
3683
|
+
args: payload.args,
|
|
3684
|
+
});
|
|
3685
|
+
const r = await this.execMenuUpdate(cmd, payload.value, targetChannel, execution.channelId, execution.subject.actorId, execution.subject.identity, true, execution.args, execution.subject, context.source);
|
|
3593
3686
|
return ecwebResp(id, name, r);
|
|
3594
3687
|
}
|
|
3595
3688
|
case 'menu.action': {
|
|
@@ -3597,7 +3690,17 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3597
3690
|
return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
|
|
3598
3691
|
if (!payload.action)
|
|
3599
3692
|
return ecwebErr(id, name, 'MISSING_VALUE', '缺少 action');
|
|
3600
|
-
const
|
|
3693
|
+
const hasExplicitContext = !!payload?.args && Object.prototype.hasOwnProperty.call(payload.args, 'context');
|
|
3694
|
+
const execution = await resolveExplicitMenuExecutionContext({
|
|
3695
|
+
subject: trustedSubject,
|
|
3696
|
+
channelType: 'aun',
|
|
3697
|
+
channelScope: 'control',
|
|
3698
|
+
channelId: targetChannel,
|
|
3699
|
+
args: payload.args,
|
|
3700
|
+
});
|
|
3701
|
+
const r = hasExplicitContext
|
|
3702
|
+
? await this.execMenuAction(cmd, payload.action, execution.args, targetChannel, execution.channelId, execution.subject.actorId, execution.subject.identity, execution.subject.chatType, id, true, execution.subject, context.source)
|
|
3703
|
+
: await this.execMenuAction(cmd, payload.action, payload.args, targetChannel, targetChannel, context.actorAid, trustedIdentity, undefined, id, true, trustedSubject, context.source);
|
|
3601
3704
|
return ecwebResp(id, name, r);
|
|
3602
3705
|
}
|
|
3603
3706
|
default:
|
|
@@ -3615,7 +3718,7 @@ export async function execMenuForEcweb(payload, trusted) {
|
|
|
3615
3718
|
const validationError = validateMenuRequest(payload ?? {});
|
|
3616
3719
|
if (validationError)
|
|
3617
3720
|
return menuFailure({ id, ...(name ? { name } : {}) }, validationError);
|
|
3618
|
-
const isProcessLevel = isProcessLevelMenu(name, payload?.cmd);
|
|
3721
|
+
const isProcessLevel = isProcessLevelMenu(name, payload?.cmd) || isProcessContextMenu(payload);
|
|
3619
3722
|
const owners = loadDaemonConfig().owners ?? [];
|
|
3620
3723
|
if (isProcessLevel && owners.length === 0) {
|
|
3621
3724
|
return { type: 'menu.response', id, name, error: { code: 'FORBIDDEN', message: '请在 daemon.json 配置 owners 后使用进程级操作' } };
|