evolcore 0.0.19 → 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/CHANGELOG.md +43 -1
- package/README.md +60 -9
- package/bin/install-codex-managed-hooks.mjs +61 -0
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +383 -111
- package/dist/agents/codex-app-server-client.js +10 -2
- package/dist/agents/codex-runner.js +405 -137
- package/dist/agents/ecagent-runner.js +174 -63
- 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 +66 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +86 -9
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +26 -11
- package/dist/aun/outbox.js +295 -68
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +1004 -273
- 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 +81 -121
- package/dist/cli/index.js +1 -0
- package/dist/cli/init.js +76 -24
- package/dist/cli/restart-monitor.js +3 -3
- package/dist/cli/task-context.js +46 -0
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +16 -5
- package/dist/config/config-manager.js +226 -24
- package/dist/config/config-operation-service.js +1 -2
- 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 +29 -16
- package/dist/config/lifecycle.js +16 -5
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +161 -12
- 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 +31 -1
- package/dist/core/auth/auth-gateway.js +33 -4
- package/dist/core/auth/authorization-audit.js +155 -4
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-messages.js +2 -2
- package/dist/core/bootstrap-service.js +27 -38
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/channel-loader.js +0 -2
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +290 -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 +279 -80
- 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 +350 -32
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +9 -4
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +10 -0
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +83 -84
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +124 -10
- package/dist/core/message/message-log.js +14 -7
- package/dist/core/message/message-queue.js +206 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +495 -72
- 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 +465 -56
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +205 -13
- package/dist/core/permission/tool-policy.js +673 -62
- package/dist/core/relation/peer-identity.js +18 -0
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +299 -30
- package/dist/core/session/session-renew.js +19 -12
- package/dist/core/session/session-turn-coordinator.js +11 -4
- package/dist/eck/kit-renderer.js +18 -9
- package/dist/index.js +283 -65
- package/dist/ipc.js +374 -24
- package/dist/paths.js +64 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +56 -5
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +3 -2
- 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 +21 -2
- 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/stats.js +52 -18
- package/dist/utils/welcome.js +2 -2
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/rules/01-overview.md +1 -1
- package/kits/rules/03-identity.md +1 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/schemas/_meta.json +10 -5
- package/kits/schemas/agent-config.schema.10.json +2 -1
- package/kits/schemas/agent-config.schema.11.json +421 -0
- package/kits/schemas/daemon.schema.5.json +135 -0
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +119 -0
- 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/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/package.json +7 -3
- package/skills/eclink/SKILL.md +2 -0
|
@@ -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,10 +17,11 @@ 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';
|
|
23
|
-
import { ConfigTarget, read as readConfig } from './config-manager.js';
|
|
24
|
+
import { ConfigTarget, read as readConfig, stripLegacyAunGatewayConfig } from './config-manager.js';
|
|
24
25
|
/** 已知 baseagent 类型(网关可管理范围)。 */
|
|
25
26
|
const GATEWAY_TYPES = ['claude', 'codex', 'gemini', 'ecagent'];
|
|
26
27
|
const DISPLAY_NAMES = {
|
|
@@ -84,8 +85,9 @@ function readDefaultsRaw() {
|
|
|
84
85
|
// loadDefaults 会展开 $ENV,掩码逻辑需看原始引用,故直接读盘
|
|
85
86
|
try {
|
|
86
87
|
const p = path.join(resolvePaths().agentsDir, 'defaults.json');
|
|
87
|
-
if (fs.existsSync(p))
|
|
88
|
-
return JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
88
|
+
if (fs.existsSync(p)) {
|
|
89
|
+
return stripLegacyAunGatewayConfig(JSON.parse(fs.readFileSync(p, 'utf-8')), p, message => logger.warn(message));
|
|
90
|
+
}
|
|
89
91
|
}
|
|
90
92
|
catch (e) {
|
|
91
93
|
logger.warn(`[gateway] read defaults.json failed: ${e}`);
|
|
@@ -95,8 +97,9 @@ function readDefaultsRaw() {
|
|
|
95
97
|
function readAgentRaw(aid) {
|
|
96
98
|
try {
|
|
97
99
|
const p = path.join(resolvePaths().agentsDir, aid, 'config.json');
|
|
98
|
-
if (fs.existsSync(p))
|
|
99
|
-
return JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
100
|
+
if (fs.existsSync(p)) {
|
|
101
|
+
return stripLegacyAunGatewayConfig(JSON.parse(fs.readFileSync(p, 'utf-8')), p, message => logger.warn(message));
|
|
102
|
+
}
|
|
100
103
|
}
|
|
101
104
|
catch (e) {
|
|
102
105
|
logger.warn(`[gateway] read agents/${aid}/config.json failed: ${e}`);
|
|
@@ -539,6 +542,7 @@ function resolveGatewayCreds(scope, type) {
|
|
|
539
542
|
baseUrl: r.baseUrl,
|
|
540
543
|
apiKey: r.apiKey,
|
|
541
544
|
headers: buildModelRequestHeaders({ baseagent: 'codex', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
545
|
+
queryParams: r.queryParams,
|
|
542
546
|
};
|
|
543
547
|
}
|
|
544
548
|
else if (type === 'claude') {
|
|
@@ -547,6 +551,7 @@ function resolveGatewayCreds(scope, type) {
|
|
|
547
551
|
baseUrl: r.baseUrl,
|
|
548
552
|
apiKey: r.apiKey,
|
|
549
553
|
headers: buildModelRequestHeaders({ baseagent: 'claude', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
554
|
+
queryParams: r.queryParams,
|
|
550
555
|
};
|
|
551
556
|
}
|
|
552
557
|
else if (type === 'ecagent') {
|
|
@@ -555,6 +560,7 @@ function resolveGatewayCreds(scope, type) {
|
|
|
555
560
|
baseUrl: r.baseUrl,
|
|
556
561
|
apiKey: r.apiKey,
|
|
557
562
|
headers: buildModelRequestHeaders({ baseagent: 'ecagent', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
563
|
+
queryParams: r.queryParams,
|
|
558
564
|
};
|
|
559
565
|
}
|
|
560
566
|
return { error: 'gemini 暂不支持 HTTP 探测(CLI 后端)', code: 'NOT_SUPPORTED' };
|
|
@@ -572,20 +578,19 @@ export async function gatewayTest(args) {
|
|
|
572
578
|
const creds = resolveGatewayCreds(scope, type);
|
|
573
579
|
if ('error' in creds)
|
|
574
580
|
return creds;
|
|
575
|
-
const { baseUrl, apiKey, headers } = creds;
|
|
581
|
+
const { baseUrl, apiKey, headers, queryParams } = creds;
|
|
576
582
|
if (!baseUrl)
|
|
577
583
|
return { error: '未配置 baseUrl(或为官方占位地址)', code: 'INVALID_ARGS' };
|
|
578
584
|
const start = Date.now();
|
|
585
|
+
let timer;
|
|
579
586
|
try {
|
|
580
587
|
const controller = new AbortController();
|
|
581
|
-
|
|
582
|
-
const
|
|
583
|
-
const modelsUrl = base.endsWith('/v1') ? `${base}/models` : `${base}/v1/models`;
|
|
588
|
+
timer = setTimeout(() => controller.abort(), 5000);
|
|
589
|
+
const modelsUrl = appendV1RequestPath(baseUrl, 'models', queryParams);
|
|
584
590
|
const resp = await fetch(modelsUrl, {
|
|
585
591
|
headers: { ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}), ...headers },
|
|
586
592
|
signal: controller.signal,
|
|
587
593
|
});
|
|
588
|
-
clearTimeout(timer);
|
|
589
594
|
const latency = Date.now() - start;
|
|
590
595
|
if (!resp.ok)
|
|
591
596
|
return { data: { ok: false, latency, error: `HTTP ${resp.status}` } };
|
|
@@ -598,6 +603,10 @@ export async function gatewayTest(args) {
|
|
|
598
603
|
catch (e) {
|
|
599
604
|
return { data: { ok: false, latency: Date.now() - start, error: e?.message || String(e) } };
|
|
600
605
|
}
|
|
606
|
+
finally {
|
|
607
|
+
if (timer)
|
|
608
|
+
clearTimeout(timer);
|
|
609
|
+
}
|
|
601
610
|
}
|
|
602
611
|
/** 从 model-prices.jsonl 的 PriceRecord 提取展示用价格四元组。 */
|
|
603
612
|
function priceRowToQuad(row) {
|
|
@@ -630,19 +639,19 @@ export async function gatewayModels(args) {
|
|
|
630
639
|
const creds = resolveGatewayCreds(scope, type);
|
|
631
640
|
if ('error' in creds)
|
|
632
641
|
return creds;
|
|
633
|
-
const { baseUrl, apiKey, headers } = creds;
|
|
642
|
+
const { baseUrl, apiKey, headers, queryParams } = creds;
|
|
634
643
|
if (!baseUrl)
|
|
635
644
|
return { error: '未配置 baseUrl(或为官方占位地址)', code: 'INVALID_ARGS' };
|
|
636
645
|
let arr = [];
|
|
637
646
|
let usdToCny = 7; // 默认汇率
|
|
647
|
+
let timer;
|
|
638
648
|
try {
|
|
639
649
|
const controller = new AbortController();
|
|
640
|
-
|
|
641
|
-
const resp = await fetch(
|
|
650
|
+
timer = setTimeout(() => controller.abort(), 8000);
|
|
651
|
+
const resp = await fetch(appendV1RequestPath(baseUrl, 'models', queryParams), {
|
|
642
652
|
headers: { ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}), ...headers },
|
|
643
653
|
signal: controller.signal,
|
|
644
654
|
});
|
|
645
|
-
clearTimeout(timer);
|
|
646
655
|
if (!resp.ok)
|
|
647
656
|
return { error: `HTTP ${resp.status}`, code: 'EXEC_FAILED' };
|
|
648
657
|
const json = await resp.json().catch(() => null);
|
|
@@ -655,6 +664,10 @@ export async function gatewayModels(args) {
|
|
|
655
664
|
catch (e) {
|
|
656
665
|
return { error: `请求失败:${e?.message || e}`, code: 'EXEC_FAILED' };
|
|
657
666
|
}
|
|
667
|
+
finally {
|
|
668
|
+
if (timer)
|
|
669
|
+
clearTimeout(timer);
|
|
670
|
+
}
|
|
658
671
|
const evolcoreHome = resolvePaths().root;
|
|
659
672
|
const now = Date.now();
|
|
660
673
|
const models = arr.map((m) => {
|
|
@@ -780,7 +793,7 @@ export async function gatewaySyncEnv(args) {
|
|
|
780
793
|
}
|
|
781
794
|
if (hasChanges) {
|
|
782
795
|
const defaultsPath = path.join(resolvePaths().agentsDir, 'defaults.json');
|
|
783
|
-
|
|
796
|
+
saveDefaultsSafe(stripLegacyAunGatewayConfig(defaults, defaultsPath, message => logger.warn(message)));
|
|
784
797
|
synced.push('全局配置 (defaults.json)');
|
|
785
798
|
}
|
|
786
799
|
}
|
|
@@ -826,7 +839,7 @@ export async function gatewaySyncEnv(args) {
|
|
|
826
839
|
}
|
|
827
840
|
if (hasChanges) {
|
|
828
841
|
const agentConfigPath = path.join(resolvePaths().agentsDir, aid, 'config.json');
|
|
829
|
-
|
|
842
|
+
saveAgent(stripLegacyAunGatewayConfig(agentRaw, agentConfigPath, message => logger.warn(message)));
|
|
830
843
|
synced.push(`${aid} (config.json)`);
|
|
831
844
|
}
|
|
832
845
|
}
|
package/dist/config/lifecycle.js
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { CONFIG_SCHEMA_VERSION } from '../types.js';
|
|
2
2
|
const VALID_LIFECYCLES = new Set(['created', 'bootstrapping', 'active']);
|
|
3
|
-
export function
|
|
3
|
+
export function isAgentLifecycle(value) {
|
|
4
|
+
return typeof value === 'string' && VALID_LIFECYCLES.has(value);
|
|
5
|
+
}
|
|
6
|
+
/** Resolve the effective lifecycle. Null means an explicit invalid value. */
|
|
7
|
+
export function resolveAgentLifecycle(config) {
|
|
4
8
|
const current = config.lifecycle;
|
|
5
|
-
if (
|
|
9
|
+
if (isAgentLifecycle(current))
|
|
10
|
+
return current;
|
|
11
|
+
if (current !== undefined)
|
|
12
|
+
return null;
|
|
13
|
+
return config.initialized === false ? 'created' : 'active';
|
|
14
|
+
}
|
|
15
|
+
export function normalizeAgentLifecycle(config) {
|
|
16
|
+
const lifecycle = resolveAgentLifecycle(config);
|
|
17
|
+
// Preserve explicit invalid values so validation and runtime gates can fail
|
|
18
|
+
// closed. Only an omitted lifecycle receives the compatibility default.
|
|
19
|
+
if (lifecycle === null || config.lifecycle === lifecycle) {
|
|
6
20
|
return config;
|
|
7
21
|
}
|
|
8
|
-
const lifecycle = config.initialized === true ? 'active'
|
|
9
|
-
: config.initialized === false ? 'created'
|
|
10
|
-
: 'active';
|
|
11
22
|
return { ...config, lifecycle };
|
|
12
23
|
}
|
|
13
24
|
export function withLifecycleForWrite(config, lifecycle) {
|
|
@@ -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
|
}
|