evolcore 0.0.20 → 0.0.22
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 +65 -0
- package/README.md +58 -9
- package/bin/codex-managed-hook.mjs +3 -0
- package/bin/install-codex-managed-hooks.mjs +3 -1
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +393 -108
- package/dist/agents/codex-app-server-client.js +41 -7
- package/dist/agents/codex-runner.js +1292 -220
- package/dist/agents/ecagent-runner.js +171 -61
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +59 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +72 -6
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +5 -0
- package/dist/aun/outbox.js +189 -80
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +618 -123
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +98 -123
- package/dist/cli/init.js +27 -15
- package/dist/cli/task-context.js +50 -0
- package/dist/cli/trigger-command.js +14 -5
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +19 -5
- package/dist/config/config-manager.js +167 -22
- package/dist/config/contact-book-store.js +25 -3
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +20 -9
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +151 -9
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +43 -1
- package/dist/core/auth/auth-gateway.js +41 -4
- package/dist/core/auth/authorization-audit.js +216 -8
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-messages.js +8 -0
- package/dist/core/bootstrap-service.js +99 -27
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +311 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +288 -80
- package/dist/core/command/menu-protocol.js +1 -1
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +377 -36
- package/dist/core/data-migration.js +11 -1
- package/dist/core/event-catalog.js +37 -0
- package/dist/core/evolagent.js +4 -0
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +33 -3
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +90 -87
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +184 -12
- package/dist/core/message/message-log.js +47 -7
- package/dist/core/message/message-queue.js +227 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +658 -109
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +627 -69
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +27 -14
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +159 -1
- package/dist/core/permission/tool-error-code.js +12 -0
- package/dist/core/permission/tool-policy.js +618 -23
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +329 -30
- package/dist/core/session/session-renew.js +37 -13
- package/dist/core/session/session-turn-coordinator.js +16 -5
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/index.js +316 -50
- package/dist/ipc.js +459 -29
- package/dist/paths.js +82 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/response-system/engines/v1/proactive-flow.js +7 -2
- package/dist/stats/price-resolver.js +4 -0
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +70 -7
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +13 -3
- package/dist/trigger/scheduler.js +20 -3
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +30 -6
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/dist/utils/tool-summary.js +59 -0
- package/dist/utils/windows-shell-trust.js +201 -0
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +3 -3
- package/kits/docs/evolcore/agent-create.md +146 -0
- package/kits/docs/evolcore/agent.md +6 -0
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/group-collaboration.md +251 -0
- package/kits/docs/evolcore/group-rules.md +1 -19
- package/kits/docs/evolcore/group.md +3 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/docs/evolcore/trigger.md +6 -3
- package/kits/docs/prompt-loading-architecture.md +6 -0
- package/kits/eck_message_manifest.json +6 -6
- package/kits/schemas/_meta.json +7 -4
- package/kits/schemas/agent-config.schema.11.json +13 -0
- package/kits/schemas/agent-config.schema.12.json +427 -0
- package/kits/schemas/daemon.schema.5.json +0 -1
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +15 -3
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/message-fragments/item.md +1 -1
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +2 -1
- package/kits/templates/system-fragments/commands.md +2 -2
- package/package.json +6 -3
- package/skills/eclink/SKILL.md +2 -0
- package/dist/config/aun-gateway-config.js +0 -2
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const SERVICE_TYPES = new Set(['http', 'websocket', 'ssh']);
|
|
2
|
+
const SERVICE_VISIBILITIES = new Set(['public', 'private', 'aun-auth']);
|
|
3
|
+
export const DEFAULT_ECWEB_PORT = 42705;
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Convert the former `ecweb` + `serviceProxy` process configuration into the
|
|
9
|
+
* service-instance model. The function is intentionally idempotent and does
|
|
10
|
+
* not mutate its input.
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeDaemonServices(raw, warn) {
|
|
13
|
+
if (!isRecord(raw))
|
|
14
|
+
return raw;
|
|
15
|
+
const next = { ...raw };
|
|
16
|
+
const hasLegacyEcweb = Object.prototype.hasOwnProperty.call(raw, 'ecweb');
|
|
17
|
+
const hasLegacyProxy = Object.prototype.hasOwnProperty.call(raw, 'serviceProxy');
|
|
18
|
+
const hasCanonicalServices = Object.prototype.hasOwnProperty.call(raw, 'services');
|
|
19
|
+
const services = [];
|
|
20
|
+
if (hasCanonicalServices) {
|
|
21
|
+
if (!Array.isArray(raw.services)) {
|
|
22
|
+
throw new Error('daemon.json.services must be an array');
|
|
23
|
+
}
|
|
24
|
+
for (const [index, value] of raw.services.entries()) {
|
|
25
|
+
if (!isRecord(value)) {
|
|
26
|
+
throw new Error(`daemon.json.services[${index}] must be an object`);
|
|
27
|
+
}
|
|
28
|
+
if (typeof value.name !== 'string' || value.name.trim() === '') {
|
|
29
|
+
throw new Error(`daemon.json.services[${index}].name must be a non-empty string`);
|
|
30
|
+
}
|
|
31
|
+
// Preserve the canonical object for schema validation. In particular,
|
|
32
|
+
// do not trim/drop fields here, otherwise malformed new-format config
|
|
33
|
+
// could be silently rewritten into a different valid-looking shape.
|
|
34
|
+
services.push({ ...value });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (hasLegacyEcweb && !isRecord(raw.ecweb)) {
|
|
38
|
+
throw new Error('daemon.json.ecweb must be an object before it can be migrated');
|
|
39
|
+
}
|
|
40
|
+
if (hasLegacyProxy && !isRecord(raw.serviceProxy)) {
|
|
41
|
+
throw new Error('daemon.json.serviceProxy must be an object before it can be migrated');
|
|
42
|
+
}
|
|
43
|
+
const legacyEcweb = hasLegacyEcweb ? raw.ecweb : undefined;
|
|
44
|
+
const legacyProxy = hasLegacyProxy ? raw.serviceProxy : undefined;
|
|
45
|
+
if (legacyEcweb?.enabled !== undefined && typeof legacyEcweb.enabled !== 'boolean') {
|
|
46
|
+
throw new Error('daemon.json.ecweb.enabled must be a boolean before it can be migrated');
|
|
47
|
+
}
|
|
48
|
+
if (legacyEcweb?.port !== undefined && typeof legacyEcweb.port !== 'number') {
|
|
49
|
+
throw new Error('daemon.json.ecweb.port must be a number before it can be migrated');
|
|
50
|
+
}
|
|
51
|
+
if (legacyProxy?.enabled !== undefined && typeof legacyProxy.enabled !== 'boolean') {
|
|
52
|
+
throw new Error('daemon.json.serviceProxy.enabled must be a boolean before it can be migrated');
|
|
53
|
+
}
|
|
54
|
+
const legacyProxyServices = [];
|
|
55
|
+
if (legacyProxy?.services !== undefined) {
|
|
56
|
+
if (!Array.isArray(legacyProxy.services)) {
|
|
57
|
+
throw new Error('daemon.json.serviceProxy.services must be an array before it can be migrated');
|
|
58
|
+
}
|
|
59
|
+
for (const [index, value] of legacyProxy.services.entries()) {
|
|
60
|
+
if (!isRecord(value)) {
|
|
61
|
+
throw new Error(`daemon.json.serviceProxy.services[${index}] cannot be represented by source="instance"`);
|
|
62
|
+
}
|
|
63
|
+
if (value.enabled !== undefined && typeof value.enabled !== 'boolean') {
|
|
64
|
+
throw new Error(`daemon.json.serviceProxy.services[${index}].enabled must be a boolean before it can be migrated`);
|
|
65
|
+
}
|
|
66
|
+
if (value.visibility !== undefined && typeof value.visibility !== 'string') {
|
|
67
|
+
throw new Error(`daemon.json.serviceProxy.services[${index}].visibility must be a string before it can be migrated`);
|
|
68
|
+
}
|
|
69
|
+
legacyProxyServices.push(value);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const legacyEcwebProxy = legacyProxyServices.find((value) => value.name === 'ecweb');
|
|
73
|
+
const ecwebIndex = services.findIndex((service) => service.name === 'ecweb');
|
|
74
|
+
// Convert the former flat serviceProxy entries into canonical services[].
|
|
75
|
+
// Keep every service rather than silently dropping non-ecweb entries.
|
|
76
|
+
for (const legacy of legacyProxyServices) {
|
|
77
|
+
const name = typeof legacy.name === 'string' ? legacy.name : '';
|
|
78
|
+
if (!name || name === 'ecweb')
|
|
79
|
+
continue;
|
|
80
|
+
if (services.some((service) => service.name === name))
|
|
81
|
+
continue;
|
|
82
|
+
const source = legacy.source === 'ecweb'
|
|
83
|
+
? 'instance'
|
|
84
|
+
: legacy.source === 'instance'
|
|
85
|
+
? 'instance'
|
|
86
|
+
: typeof legacy.endpoint === 'string' && legacy.endpoint.trim()
|
|
87
|
+
? 'static'
|
|
88
|
+
: 'static';
|
|
89
|
+
const proxy = {
|
|
90
|
+
enabled: legacyProxy?.enabled === true && legacy.enabled !== false,
|
|
91
|
+
source,
|
|
92
|
+
...(typeof legacy.endpoint === 'string' ? { endpoint: legacy.endpoint } : {}),
|
|
93
|
+
...(typeof legacy.serviceType === 'string' && SERVICE_TYPES.has(legacy.serviceType)
|
|
94
|
+
? { serviceType: legacy.serviceType } : {}),
|
|
95
|
+
...(typeof legacy.visibility === 'string' ? { visibility: legacy.visibility } : {}),
|
|
96
|
+
...(isRecord(legacy.metadata) ? { metadata: { ...legacy.metadata } } : {}),
|
|
97
|
+
};
|
|
98
|
+
services.push({ name, enabled: true, proxy });
|
|
99
|
+
}
|
|
100
|
+
if (legacyEcweb || legacyEcwebProxy) {
|
|
101
|
+
const service = ecwebIndex >= 0
|
|
102
|
+
? services[ecwebIndex]
|
|
103
|
+
: {
|
|
104
|
+
name: 'ecweb',
|
|
105
|
+
// The old ECWeb lifecycle switch and Service Proxy switch were
|
|
106
|
+
// independent. Preserve that distinction during migration: a
|
|
107
|
+
// proxy-only config must not start managing the local process.
|
|
108
|
+
enabled: legacyEcweb?.enabled === true,
|
|
109
|
+
};
|
|
110
|
+
if (legacyEcweb) {
|
|
111
|
+
if (service.enabled === undefined && typeof legacyEcweb.enabled === 'boolean')
|
|
112
|
+
service.enabled = legacyEcweb.enabled;
|
|
113
|
+
if (service.port === undefined && typeof legacyEcweb.port === 'number')
|
|
114
|
+
service.port = legacyEcweb.port;
|
|
115
|
+
}
|
|
116
|
+
if (service.proxy !== undefined && !isRecord(service.proxy)) {
|
|
117
|
+
throw new Error('daemon.json.services[ecweb].proxy must be an object before legacy proxy settings can be migrated');
|
|
118
|
+
}
|
|
119
|
+
const proxy = { ...(service.proxy ?? {}) };
|
|
120
|
+
if (legacyEcwebProxy) {
|
|
121
|
+
if (proxy.enabled === undefined) {
|
|
122
|
+
proxy.enabled = legacyProxy?.enabled === true && legacyEcwebProxy.enabled !== false;
|
|
123
|
+
}
|
|
124
|
+
if (proxy.source === undefined) {
|
|
125
|
+
proxy.source = legacyEcwebProxy.source === 'static'
|
|
126
|
+
|| (typeof legacyEcwebProxy.endpoint === 'string' && legacyEcwebProxy.endpoint.trim())
|
|
127
|
+
? 'static'
|
|
128
|
+
: 'instance';
|
|
129
|
+
}
|
|
130
|
+
if (proxy.endpoint === undefined && typeof legacyEcwebProxy.endpoint === 'string')
|
|
131
|
+
proxy.endpoint = legacyEcwebProxy.endpoint;
|
|
132
|
+
if (proxy.serviceType === undefined && typeof legacyEcwebProxy.serviceType === 'string'
|
|
133
|
+
&& SERVICE_TYPES.has(legacyEcwebProxy.serviceType)) {
|
|
134
|
+
proxy.serviceType = legacyEcwebProxy.serviceType;
|
|
135
|
+
}
|
|
136
|
+
if (proxy.visibility === undefined && typeof legacyEcwebProxy.visibility === 'string') {
|
|
137
|
+
proxy.visibility = legacyEcwebProxy.visibility;
|
|
138
|
+
}
|
|
139
|
+
if (proxy.metadata === undefined && isRecord(legacyEcwebProxy.metadata)) {
|
|
140
|
+
proxy.metadata = { ...legacyEcwebProxy.metadata };
|
|
141
|
+
}
|
|
142
|
+
if (proxy.enabled === true && proxy.visibility === undefined)
|
|
143
|
+
proxy.visibility = 'private';
|
|
144
|
+
}
|
|
145
|
+
if (Object.keys(proxy).length > 0)
|
|
146
|
+
service.proxy = proxy;
|
|
147
|
+
if (ecwebIndex < 0)
|
|
148
|
+
services.push(service);
|
|
149
|
+
}
|
|
150
|
+
if (hasCanonicalServices || services.length > 0)
|
|
151
|
+
next.services = services;
|
|
152
|
+
if (hasLegacyEcweb || hasLegacyProxy) {
|
|
153
|
+
warn?.('[config] daemon.json: ecweb/serviceProxy 已归一化为 services[]');
|
|
154
|
+
delete next.ecweb;
|
|
155
|
+
delete next.serviceProxy;
|
|
156
|
+
}
|
|
157
|
+
return next;
|
|
158
|
+
}
|
|
159
|
+
export function findService(services, name) {
|
|
160
|
+
return (services ?? []).find((service) => service.name === name);
|
|
161
|
+
}
|
|
162
|
+
export function ecwebService(services) {
|
|
163
|
+
return findService(services, 'ecweb');
|
|
164
|
+
}
|
|
165
|
+
export function upsertService(services, patch) {
|
|
166
|
+
const current = services ?? [];
|
|
167
|
+
const index = current.findIndex((service) => service.name === patch.name);
|
|
168
|
+
if (index < 0)
|
|
169
|
+
return [...current, patch];
|
|
170
|
+
const existing = current[index];
|
|
171
|
+
const replacement = {
|
|
172
|
+
...existing,
|
|
173
|
+
...patch,
|
|
174
|
+
...(existing.proxy || patch.proxy
|
|
175
|
+
? { proxy: { ...(existing.proxy ?? {}), ...(patch.proxy ?? {}) } }
|
|
176
|
+
: {}),
|
|
177
|
+
};
|
|
178
|
+
return current.map((service, serviceIndex) => serviceIndex === index ? replacement : service);
|
|
179
|
+
}
|
|
180
|
+
export function setEcwebEnabled(services, enabled, port = ecwebService(services)?.port ?? DEFAULT_ECWEB_PORT) {
|
|
181
|
+
return upsertService(services, {
|
|
182
|
+
name: 'ecweb',
|
|
183
|
+
enabled,
|
|
184
|
+
port,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
@@ -17,6 +17,7 @@ import { resolvePaths } from '../paths.js';
|
|
|
17
17
|
import { saveDefaultsSafe, saveAgent } from '../config-store.js';
|
|
18
18
|
import { resolveAnthropicConfig, resolveEcagentConfig, resolveOpenaiConfig } from '../agents/baseagent.js';
|
|
19
19
|
import { buildModelRequestHeaders } from '../agents/request-identity.js';
|
|
20
|
+
import { appendV1RequestPath } from '../agents/request-identity.js';
|
|
20
21
|
import { resolvePriceRow } from '../stats/billing.js';
|
|
21
22
|
import { ipcQuery } from '../ipc.js';
|
|
22
23
|
import { logger } from '../utils/logger.js';
|
|
@@ -541,6 +542,7 @@ function resolveGatewayCreds(scope, type) {
|
|
|
541
542
|
baseUrl: r.baseUrl,
|
|
542
543
|
apiKey: r.apiKey,
|
|
543
544
|
headers: buildModelRequestHeaders({ baseagent: 'codex', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
545
|
+
queryParams: r.queryParams,
|
|
544
546
|
};
|
|
545
547
|
}
|
|
546
548
|
else if (type === 'claude') {
|
|
@@ -549,6 +551,7 @@ function resolveGatewayCreds(scope, type) {
|
|
|
549
551
|
baseUrl: r.baseUrl,
|
|
550
552
|
apiKey: r.apiKey,
|
|
551
553
|
headers: buildModelRequestHeaders({ baseagent: 'claude', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
554
|
+
queryParams: r.queryParams,
|
|
552
555
|
};
|
|
553
556
|
}
|
|
554
557
|
else if (type === 'ecagent') {
|
|
@@ -557,6 +560,7 @@ function resolveGatewayCreds(scope, type) {
|
|
|
557
560
|
baseUrl: r.baseUrl,
|
|
558
561
|
apiKey: r.apiKey,
|
|
559
562
|
headers: buildModelRequestHeaders({ baseagent: 'ecagent', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
563
|
+
queryParams: r.queryParams,
|
|
560
564
|
};
|
|
561
565
|
}
|
|
562
566
|
return { error: 'gemini 暂不支持 HTTP 探测(CLI 后端)', code: 'NOT_SUPPORTED' };
|
|
@@ -574,20 +578,19 @@ export async function gatewayTest(args) {
|
|
|
574
578
|
const creds = resolveGatewayCreds(scope, type);
|
|
575
579
|
if ('error' in creds)
|
|
576
580
|
return creds;
|
|
577
|
-
const { baseUrl, apiKey, headers } = creds;
|
|
581
|
+
const { baseUrl, apiKey, headers, queryParams } = creds;
|
|
578
582
|
if (!baseUrl)
|
|
579
583
|
return { error: '未配置 baseUrl(或为官方占位地址)', code: 'INVALID_ARGS' };
|
|
580
584
|
const start = Date.now();
|
|
585
|
+
let timer;
|
|
581
586
|
try {
|
|
582
587
|
const controller = new AbortController();
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
const modelsUrl = base.endsWith('/v1') ? `${base}/models` : `${base}/v1/models`;
|
|
588
|
+
timer = setTimeout(() => controller.abort(), 5000);
|
|
589
|
+
const modelsUrl = appendV1RequestPath(baseUrl, 'models', queryParams);
|
|
586
590
|
const resp = await fetch(modelsUrl, {
|
|
587
591
|
headers: { ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}), ...headers },
|
|
588
592
|
signal: controller.signal,
|
|
589
593
|
});
|
|
590
|
-
clearTimeout(timer);
|
|
591
594
|
const latency = Date.now() - start;
|
|
592
595
|
if (!resp.ok)
|
|
593
596
|
return { data: { ok: false, latency, error: `HTTP ${resp.status}` } };
|
|
@@ -600,6 +603,10 @@ export async function gatewayTest(args) {
|
|
|
600
603
|
catch (e) {
|
|
601
604
|
return { data: { ok: false, latency: Date.now() - start, error: e?.message || String(e) } };
|
|
602
605
|
}
|
|
606
|
+
finally {
|
|
607
|
+
if (timer)
|
|
608
|
+
clearTimeout(timer);
|
|
609
|
+
}
|
|
603
610
|
}
|
|
604
611
|
/** 从 model-prices.jsonl 的 PriceRecord 提取展示用价格四元组。 */
|
|
605
612
|
function priceRowToQuad(row) {
|
|
@@ -632,19 +639,19 @@ export async function gatewayModels(args) {
|
|
|
632
639
|
const creds = resolveGatewayCreds(scope, type);
|
|
633
640
|
if ('error' in creds)
|
|
634
641
|
return creds;
|
|
635
|
-
const { baseUrl, apiKey, headers } = creds;
|
|
642
|
+
const { baseUrl, apiKey, headers, queryParams } = creds;
|
|
636
643
|
if (!baseUrl)
|
|
637
644
|
return { error: '未配置 baseUrl(或为官方占位地址)', code: 'INVALID_ARGS' };
|
|
638
645
|
let arr = [];
|
|
639
646
|
let usdToCny = 7; // 默认汇率
|
|
647
|
+
let timer;
|
|
640
648
|
try {
|
|
641
649
|
const controller = new AbortController();
|
|
642
|
-
|
|
643
|
-
const resp = await fetch(
|
|
650
|
+
timer = setTimeout(() => controller.abort(), 8000);
|
|
651
|
+
const resp = await fetch(appendV1RequestPath(baseUrl, 'models', queryParams), {
|
|
644
652
|
headers: { ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}), ...headers },
|
|
645
653
|
signal: controller.signal,
|
|
646
654
|
});
|
|
647
|
-
clearTimeout(timer);
|
|
648
655
|
if (!resp.ok)
|
|
649
656
|
return { error: `HTTP ${resp.status}`, code: 'EXEC_FAILED' };
|
|
650
657
|
const json = await resp.json().catch(() => null);
|
|
@@ -657,6 +664,10 @@ export async function gatewayModels(args) {
|
|
|
657
664
|
catch (e) {
|
|
658
665
|
return { error: `请求失败:${e?.message || e}`, code: 'EXEC_FAILED' };
|
|
659
666
|
}
|
|
667
|
+
finally {
|
|
668
|
+
if (timer)
|
|
669
|
+
clearTimeout(timer);
|
|
670
|
+
}
|
|
660
671
|
const evolcoreHome = resolvePaths().root;
|
|
661
672
|
const now = Date.now();
|
|
662
673
|
const models = arr.map((m) => {
|
|
@@ -54,6 +54,18 @@ export function writeRoleDefinition(selfAid, roleId, value) {
|
|
|
54
54
|
atomicWriteJson(file, normalized);
|
|
55
55
|
clearRoleStoreCache(selfAid);
|
|
56
56
|
}
|
|
57
|
+
/** Remove an agent-local override so the builtin role template is used again. */
|
|
58
|
+
export function resetRoleDefinition(selfAid, roleId) {
|
|
59
|
+
if (!isRoleName(roleId))
|
|
60
|
+
throw new RoleServiceError('INVALID_ROLE_ID', `Invalid role id: ${roleId}`);
|
|
61
|
+
const file = agentRoleConfig(selfAid, roleId);
|
|
62
|
+
const changed = fs.existsSync(file);
|
|
63
|
+
if (changed) {
|
|
64
|
+
fs.rmSync(file, { force: true });
|
|
65
|
+
clearRoleStoreCache(selfAid);
|
|
66
|
+
}
|
|
67
|
+
return changed;
|
|
68
|
+
}
|
|
57
69
|
export function registerRole(selfAid, roleId, definition) {
|
|
58
70
|
if (!isRoleName(roleId))
|
|
59
71
|
throw new RoleServiceError('INVALID_ROLE_ID', `Invalid role id: ${roleId}`);
|
|
@@ -72,17 +84,56 @@ export function registerRole(selfAid, roleId, definition) {
|
|
|
72
84
|
throw error;
|
|
73
85
|
}
|
|
74
86
|
}
|
|
75
|
-
export function unregisterRole(selfAid, roleId) {
|
|
87
|
+
export function unregisterRole(selfAid, roleId, replacementRoleId) {
|
|
76
88
|
if (roleId === 'visitor' || isManagementRole(roleId)) {
|
|
77
89
|
throw new RoleServiceError('ROLE_PROTECTED', `Role ${roleId} cannot be unregistered`);
|
|
78
90
|
}
|
|
79
91
|
const registry = readRoleRegistry(selfAid);
|
|
80
92
|
if (!registry.roles.includes(roleId))
|
|
81
93
|
return;
|
|
94
|
+
const hasReplacement = replacementRoleId !== undefined;
|
|
95
|
+
const replacement = replacementRoleId === undefined ? undefined : (replacementRoleId || null);
|
|
96
|
+
if (replacement === roleId) {
|
|
97
|
+
throw new RoleServiceError('INVALID_REPLACEMENT_ROLE', 'replacementRoleId must differ from the role being deleted');
|
|
98
|
+
}
|
|
99
|
+
if (replacement !== undefined && replacement !== null) {
|
|
100
|
+
if (!isRoleName(replacement) || replacement === 'owner' || !registry.roles.includes(replacement)
|
|
101
|
+
|| !hasValidRoleDefinition(selfAid, replacement)) {
|
|
102
|
+
throw new RoleServiceError('INVALID_REPLACEMENT_ROLE', `Invalid replacement role: ${replacement}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
82
105
|
const references = findRoleReferences(selfAid, roleId, registry);
|
|
83
|
-
if (references.length)
|
|
106
|
+
if (references.length && !hasReplacement) {
|
|
84
107
|
throw new RoleServiceError('ROLE_IN_USE', `Role ${roleId} is still referenced`, { references });
|
|
85
|
-
|
|
108
|
+
}
|
|
109
|
+
const nextRegistry = {
|
|
110
|
+
...registry,
|
|
111
|
+
roles: registry.roles.filter(item => item !== roleId),
|
|
112
|
+
defaultRoles: {
|
|
113
|
+
private: registry.defaultRoles.private === roleId ? (replacement ?? null) : registry.defaultRoles.private,
|
|
114
|
+
group: registry.defaultRoles.group === roleId ? (replacement ?? null) : registry.defaultRoles.group,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
if (hasReplacement) {
|
|
118
|
+
const relations = agentRelationsDir(selfAid);
|
|
119
|
+
if (fs.existsSync(relations)) {
|
|
120
|
+
for (const entry of fs.readdirSync(relations, { withFileTypes: true })) {
|
|
121
|
+
if (!entry.isDirectory() || entry.name.startsWith('_'))
|
|
122
|
+
continue;
|
|
123
|
+
const file = agentRelationConfig(selfAid, entry.name);
|
|
124
|
+
const config = atomicReadJson(file);
|
|
125
|
+
if (!config || config.role !== roleId)
|
|
126
|
+
continue;
|
|
127
|
+
const next = { ...config };
|
|
128
|
+
if (replacement === null)
|
|
129
|
+
delete next.role;
|
|
130
|
+
else
|
|
131
|
+
next.role = replacement;
|
|
132
|
+
atomicWriteJson(file, next);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
writeRoleRegistry(selfAid, nextRegistry);
|
|
86
137
|
try {
|
|
87
138
|
fs.rmSync(agentRoleConfig(selfAid, roleId), { force: true });
|
|
88
139
|
}
|