evolcore 0.0.26 → 0.0.28
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 +36 -0
- package/bin/codex-managed-acl.ps1 +43 -0
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/baseagent.js +10 -5
- package/dist/agents/claude-runner.js +123 -22
- package/dist/agents/codex/app-server-client.js +70 -26
- package/dist/agents/codex/catalog.js +7 -1
- package/dist/agents/codex-runner.js +363 -421
- package/dist/agents/ecagent-runner.js +170 -25
- package/dist/agents/gemini-runner.js +6 -0
- package/dist/agents/runner-types.js +50 -2
- package/dist/aun/aid/agentmd.js +73 -31
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +3 -3
- package/dist/aun/group/history.js +303 -0
- package/dist/aun/group/identity.js +43 -0
- package/dist/aun/group/index.js +5 -0
- package/dist/aun/{msg/group.js → group/operations.js} +161 -38
- package/dist/aun/{menu-client.js → menu/client.js} +13 -7
- package/dist/aun/menu/index.js +2 -0
- package/dist/aun/menu/transport.js +49 -0
- package/dist/aun/msg/index.js +2 -1
- package/dist/aun/msg/managed-operation.js +84 -18
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +13 -8
- package/dist/aun/msg/upload.js +43 -88
- package/dist/aun/outbox.js +29 -7
- package/dist/aun/rpc/client.js +1 -0
- package/dist/aun/storage/client.js +39 -50
- package/dist/channels/aun.js +544 -239
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +108 -6
- package/dist/cli/agent-create.js +515 -2
- package/dist/cli/agent.js +109 -8
- package/dist/cli/aun-commands.js +191 -42
- package/dist/cli/config.js +8 -5
- package/dist/cli/contact.js +2 -2
- package/dist/cli/ctl-command.js +2 -2
- package/dist/cli/daemon-commands.js +111 -162
- package/dist/cli/diag.js +3 -3
- package/dist/cli/fs-command.js +3 -3
- package/dist/cli/handoff-command.js +6 -6
- package/dist/cli/index.js +8 -2
- package/dist/cli/init.js +34 -1
- package/dist/cli/managed-command-guard.js +2 -1
- package/dist/cli/menu-client.js +13 -5
- package/dist/cli/model.js +3 -3
- package/dist/cli/queue-command.js +2 -2
- package/dist/cli/response.js +2 -2
- package/dist/cli/restart-monitor.js +81 -19
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +20 -10
- package/dist/config/builtin-roles.js +10 -0
- package/dist/config/config-field-policy.js +11 -0
- package/dist/config/config-manager.js +32 -4
- package/dist/config/config-operation-service.js +20 -5
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +1 -1
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +5 -0
- package/dist/core/audit/event-key.js +17 -2
- package/dist/core/audit/log-integrity.js +34 -8
- package/dist/core/auth/agent-delegation.js +48 -14
- package/dist/core/auth/auth-gateway.js +2 -0
- package/dist/core/auth/authorization-audit.js +34 -38
- package/dist/core/auth/operation-authorizer.js +67 -5
- package/dist/core/auth/operation-catalog.js +110 -0
- package/dist/core/auth/remote-access-store.js +115 -9
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +110 -19
- package/dist/core/command/agent-control.js +88 -7
- package/dist/core/command/agent-storage-menu.js +443 -0
- package/dist/core/command/cli-intent-parser.js +19 -0
- package/dist/core/command/command-handler.js +105 -40
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/diag-service.js +292 -27
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +34 -4
- package/dist/core/command/menu-handler.js +364 -96
- package/dist/core/command/menu-protocol.js +43 -6
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-handler.js +61 -37
- package/dist/core/event-catalog.js +4 -0
- package/dist/core/evolagent-registry.js +28 -2
- package/dist/core/group/invite-service.js +924 -0
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +94 -16
- package/dist/core/message/message-bridge.js +140 -13
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +73 -41
- package/dist/core/message/response-engine.js +729 -118
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +76 -36
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +72 -3
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +8 -8
- package/dist/core/relation/peer-identity.js +57 -6
- package/dist/core/relation/shared-data.js +106 -0
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/runtime-lock.js +10 -26
- package/dist/core/session/runner-task-history.js +170 -0
- package/dist/core/session/session-manager.js +38 -6
- package/dist/core/session/session-mapper.js +41 -0
- package/dist/core/session/session-turn-coordinator.js +37 -7
- package/dist/core/session/session-turns.js +48 -7
- package/dist/core/task/task-recovery-service.js +176 -0
- package/dist/core/task/task-resume-operation.js +32 -0
- package/dist/eck/group-rules-sync.js +5 -3
- package/dist/eck/kit-renderer.js +1 -0
- package/dist/index.js +322 -207
- package/dist/ipc.js +141 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +3 -0
- package/dist/trigger/event-source.js +19 -0
- package/dist/trigger/feedback.js +233 -40
- package/dist/trigger/parser.js +55 -5
- package/dist/trigger/patch.js +9 -0
- package/dist/trigger/scheduler.js +30 -4
- package/dist/trigger/validation.js +20 -3
- package/dist/utils/atomic-write.js +98 -0
- package/dist/utils/cross-platform.js +87 -16
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-utils.js +40 -2
- package/dist/utils/logger.js +23 -13
- package/dist/utils/npm-ops.js +119 -4
- package/dist/utils/package-version-info.js +244 -0
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +174 -1
- package/dist/utils/windows-shell-trust.js +37 -0
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/INDEX.md +0 -6
- package/kits/docs/channels/aun.md +23 -2
- package/kits/docs/channels/feishu.md +1 -1
- package/kits/docs/context-assembly.md +29 -17
- package/kits/docs/evolcore/INDEX.md +8 -3
- package/kits/docs/evolcore/agent-create.md +39 -7
- package/kits/docs/evolcore/agent.md +33 -1
- package/kits/docs/evolcore/aid.md +9 -1
- package/kits/docs/evolcore/config.md +40 -1
- package/kits/docs/evolcore/group-collaboration.md +1 -1
- package/kits/docs/evolcore/group.md +117 -2
- package/kits/docs/evolcore/menu-client.md +16 -0
- package/kits/docs/evolcore/msg.md +3 -0
- package/kits/docs/evolcore/storage.md +3 -0
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +51 -3
- package/kits/docs/venues/group.md +5 -0
- package/kits/eck_manifest.json +27 -0
- package/kits/rules/01-overview.md +11 -7
- package/kits/rules/04-relation.md +2 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +6 -4
- package/kits/schemas/agent-config.schema.12.json +25 -1
- package/kits/schemas/daemon.schema.6.json +2 -2
- package/kits/schemas/defaults.schema.5.json +1 -1
- package/kits/schemas/relation-config.schema.1.json +1 -1
- package/kits/schemas/relation-config.schema.5.json +1 -1
- package/kits/schemas/role-config.schema.1.json +14 -0
- package/kits/templates/roles/member.json +5 -0
- package/kits/templates/roles/visitor.json +5 -0
- package/kits/templates/system-fragments/baseagent.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +26 -9
- package/kits/templates/system-fragments/channel.md +8 -44
- package/kits/templates/system-fragments/commands.md +10 -2
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/reply.md +56 -0
- package/package.json +7 -3
- package/dist/aun/group-identity.js +0 -9
- /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
- /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
package/dist/aun/aid/agentmd.js
CHANGED
|
@@ -5,10 +5,10 @@ import { getAidStore, loadAid, SLOT } from './store.js';
|
|
|
5
5
|
import { agentMdPath, aidLocalDir, resolveRoot } from '../../paths.js';
|
|
6
6
|
export const DEFAULT_AGENT_DESCRIPTION = 'EvolCore AI agent.';
|
|
7
7
|
/**
|
|
8
|
-
* AUN 主体类型白名单。没有 `ai` / `codeagent`
|
|
9
|
-
*
|
|
8
|
+
* AUN 主体类型白名单。没有 `ai` / `codeagent` 这类取值:使用大模型且没有
|
|
9
|
+
* 专用主体类型的服务程序默认归为 `agent`。新建 AID 未指定类型时使用 DEFAULT_AGENT_TYPE。
|
|
10
10
|
*/
|
|
11
|
-
export const AGENT_TYPES = ['human', 'agent', 'group', 'service', 'device', 'org'];
|
|
11
|
+
export const AGENT_TYPES = ['human', 'agent', 'group', 'service', 'device', 'org', 'host'];
|
|
12
12
|
export const DEFAULT_AGENT_TYPE = 'agent';
|
|
13
13
|
/**
|
|
14
14
|
* 归一化主体类型:大小写不敏感,统一收敛为白名单里的小写形式。
|
|
@@ -86,6 +86,12 @@ export function validateAgentTags(tags, options = {}) {
|
|
|
86
86
|
}
|
|
87
87
|
return errors;
|
|
88
88
|
}
|
|
89
|
+
function resolveAgentMdAunPath(opts) {
|
|
90
|
+
return opts?.aunPath ?? opts?.store?.aunPath ?? resolveRoot();
|
|
91
|
+
}
|
|
92
|
+
// A process can host agents that deliberately use different AUN keystore
|
|
93
|
+
// roots. Keep their display-name state separate so one root cannot shadow the
|
|
94
|
+
// other with a same-named AID.
|
|
89
95
|
const displayNameCache = new Map();
|
|
90
96
|
const displayNamePending = new Map();
|
|
91
97
|
const displayNameRevision = new Map();
|
|
@@ -101,9 +107,13 @@ export function parseAgentDisplayName(content) {
|
|
|
101
107
|
const name = frontmatter.match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1]?.trim();
|
|
102
108
|
return name || undefined;
|
|
103
109
|
}
|
|
104
|
-
function
|
|
110
|
+
function displayNameKey(aid, aunPath) {
|
|
111
|
+
const root = path.resolve(aunPath ?? resolveRoot());
|
|
112
|
+
return `${root}\u0000${aid.toLowerCase()}`;
|
|
113
|
+
}
|
|
114
|
+
function readLocalAgentDisplayName(aid, aunPath) {
|
|
105
115
|
try {
|
|
106
|
-
return parseAgentDisplayName(fs.readFileSync(agentMdPath(aid), 'utf-8'));
|
|
116
|
+
return parseAgentDisplayName(fs.readFileSync(agentMdPath(aid, aunPath), 'utf-8'));
|
|
107
117
|
}
|
|
108
118
|
catch {
|
|
109
119
|
return undefined;
|
|
@@ -114,56 +124,77 @@ function readLocalAgentDisplayName(aid) {
|
|
|
114
124
|
* Local content is returned immediately; a missing value starts one shared
|
|
115
125
|
* background refresh and remains undefined until a later lookup.
|
|
116
126
|
*/
|
|
117
|
-
export function resolveAgentDisplayName(aid) {
|
|
127
|
+
export function resolveAgentDisplayName(aid, opts) {
|
|
118
128
|
const normalizedAid = normalizeAid(aid);
|
|
119
129
|
if (!normalizedAid)
|
|
120
130
|
return undefined;
|
|
121
|
-
const
|
|
131
|
+
const aunPath = resolveAgentMdAunPath(opts);
|
|
132
|
+
const key = displayNameKey(normalizedAid, aunPath);
|
|
133
|
+
const cached = displayNameCache.get(key);
|
|
122
134
|
if (cached)
|
|
123
135
|
return cached;
|
|
124
|
-
const localName = readLocalAgentDisplayName(normalizedAid);
|
|
136
|
+
const localName = readLocalAgentDisplayName(normalizedAid, aunPath);
|
|
125
137
|
if (localName) {
|
|
126
|
-
displayNameCache.set(
|
|
138
|
+
displayNameCache.set(key, localName);
|
|
127
139
|
return localName;
|
|
128
140
|
}
|
|
129
|
-
void refreshAgentDisplayName(normalizedAid);
|
|
141
|
+
void refreshAgentDisplayName(normalizedAid, opts);
|
|
130
142
|
return undefined;
|
|
131
143
|
}
|
|
132
144
|
/** Fetch agent.md once and update the shared display-name cache. */
|
|
133
|
-
export function refreshAgentDisplayName(aid) {
|
|
145
|
+
export function refreshAgentDisplayName(aid, opts) {
|
|
134
146
|
const normalizedAid = normalizeAid(aid);
|
|
135
147
|
if (!normalizedAid)
|
|
136
148
|
return Promise.resolve(undefined);
|
|
137
|
-
const
|
|
149
|
+
const aunPath = resolveAgentMdAunPath(opts);
|
|
150
|
+
const key = displayNameKey(normalizedAid, aunPath);
|
|
151
|
+
const existing = displayNamePending.get(key);
|
|
138
152
|
if (existing)
|
|
139
153
|
return existing;
|
|
140
|
-
const revision = displayNameRevision.get(
|
|
154
|
+
const revision = displayNameRevision.get(key) ?? 0;
|
|
141
155
|
let pending;
|
|
142
|
-
pending = agentmdGet(normalizedAid)
|
|
156
|
+
pending = agentmdGet(normalizedAid, { aunPath, store: opts?.store })
|
|
143
157
|
.then(content => {
|
|
144
158
|
const name = typeof content === 'string' ? parseAgentDisplayName(content) : undefined;
|
|
145
|
-
if ((displayNameRevision.get(
|
|
159
|
+
if ((displayNameRevision.get(key) ?? 0) !== revision)
|
|
146
160
|
return undefined;
|
|
147
161
|
if (name)
|
|
148
|
-
displayNameCache.set(
|
|
162
|
+
displayNameCache.set(key, name);
|
|
149
163
|
return name;
|
|
150
164
|
})
|
|
151
165
|
.catch(() => undefined)
|
|
152
166
|
.finally(() => {
|
|
153
|
-
if (displayNamePending.get(
|
|
154
|
-
displayNamePending.delete(
|
|
167
|
+
if (displayNamePending.get(key) === pending) {
|
|
168
|
+
displayNamePending.delete(key);
|
|
155
169
|
}
|
|
156
170
|
});
|
|
157
|
-
displayNamePending.set(
|
|
171
|
+
displayNamePending.set(key, pending);
|
|
158
172
|
return pending;
|
|
159
173
|
}
|
|
160
|
-
export function invalidateAgentDisplayName(aid) {
|
|
174
|
+
export function invalidateAgentDisplayName(aid, aunPath) {
|
|
161
175
|
const normalizedAid = normalizeAid(aid);
|
|
162
176
|
if (!normalizedAid)
|
|
163
177
|
return;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
178
|
+
if (aunPath !== undefined) {
|
|
179
|
+
const key = displayNameKey(normalizedAid, aunPath);
|
|
180
|
+
displayNameCache.delete(key);
|
|
181
|
+
displayNamePending.delete(key);
|
|
182
|
+
displayNameRevision.set(key, (displayNameRevision.get(key) ?? 0) + 1);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const suffix = `\u0000${normalizedAid.toLowerCase()}`;
|
|
186
|
+
const keys = new Set([
|
|
187
|
+
...displayNameCache.keys(),
|
|
188
|
+
...displayNamePending.keys(),
|
|
189
|
+
...displayNameRevision.keys(),
|
|
190
|
+
]);
|
|
191
|
+
for (const key of keys) {
|
|
192
|
+
if (!key.endsWith(suffix))
|
|
193
|
+
continue;
|
|
194
|
+
displayNameCache.delete(key);
|
|
195
|
+
displayNamePending.delete(key);
|
|
196
|
+
displayNameRevision.set(key, (displayNameRevision.get(key) ?? 0) + 1);
|
|
197
|
+
}
|
|
167
198
|
}
|
|
168
199
|
export function buildInitialAgentMd(opts) {
|
|
169
200
|
const agentName = opts.name?.trim() || opts.aid.split('.')[0];
|
|
@@ -354,10 +385,10 @@ function verifyLocal(store, aid, content) {
|
|
|
354
385
|
}
|
|
355
386
|
}
|
|
356
387
|
export async function agentmdGet(aid, opts) {
|
|
357
|
-
const aunPath = opts
|
|
388
|
+
const aunPath = resolveAgentMdAunPath(opts);
|
|
358
389
|
const store = opts?.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
359
390
|
const ownStore = !opts?.store;
|
|
360
|
-
const localPath = agentMdPath(aid);
|
|
391
|
+
const localPath = agentMdPath(aid, aunPath);
|
|
361
392
|
try {
|
|
362
393
|
let content;
|
|
363
394
|
let verification;
|
|
@@ -367,6 +398,14 @@ export async function agentmdGet(aid, opts) {
|
|
|
367
398
|
verification = normalizeVerification(r.data.verification);
|
|
368
399
|
}
|
|
369
400
|
else {
|
|
401
|
+
if (opts?.remoteOnly) {
|
|
402
|
+
const code = String(r.error?.code ?? '');
|
|
403
|
+
const message = String(r.error?.message ?? r.error);
|
|
404
|
+
if (code === 'AGENTMD_NOT_FOUND' || /not found|404|missing/i.test(message)) {
|
|
405
|
+
throw Object.assign(new Error(`agent.md not found for ${aid}`), { code: 'AGENTMD_NOT_FOUND' });
|
|
406
|
+
}
|
|
407
|
+
throw new Error(`Remote agent.md verification unavailable for ${aid}`);
|
|
408
|
+
}
|
|
370
409
|
// Network/fetch failed — fall back to local file.
|
|
371
410
|
if (!fs.existsSync(localPath)) {
|
|
372
411
|
throw new Error(`fetch agent.md failed for ${aid}: ${r.error.message}`);
|
|
@@ -399,10 +438,10 @@ export async function agentmdPut(content, opts) {
|
|
|
399
438
|
if (!validation.valid) {
|
|
400
439
|
throw new Error(`invalid agent.md frontmatter: ${validation.errors.join('; ')}`);
|
|
401
440
|
}
|
|
402
|
-
const aunPath = opts
|
|
441
|
+
const aunPath = resolveAgentMdAunPath(opts);
|
|
403
442
|
const store = opts.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
404
443
|
const ownStore = !opts.store;
|
|
405
|
-
const dir = aidLocalDir(opts.aid);
|
|
444
|
+
const dir = aidLocalDir(opts.aid, aunPath);
|
|
406
445
|
const filePath = path.join(dir, 'agent.md');
|
|
407
446
|
const existed = fs.existsSync(filePath);
|
|
408
447
|
// 先写本地文件(与旧行为一致:本地内容更新应在上传失败时仍保留)
|
|
@@ -443,10 +482,10 @@ export async function agentmdPut(content, opts) {
|
|
|
443
482
|
* so a freshly-built store reports local_found=false and will fetch.
|
|
444
483
|
*/
|
|
445
484
|
export async function agentmdSync(aid, opts) {
|
|
446
|
-
const aunPath = opts
|
|
485
|
+
const aunPath = resolveAgentMdAunPath(opts);
|
|
447
486
|
const store = opts?.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
448
487
|
const ownStore = !opts?.store;
|
|
449
|
-
const localPath = agentMdPath(aid);
|
|
488
|
+
const localPath = agentMdPath(aid, aunPath);
|
|
450
489
|
try {
|
|
451
490
|
// AIDStore owns the agent.md consistency API in fastaun 0.5.x. Keep this
|
|
452
491
|
// runtime guard because callers and test doubles can still accidentally
|
|
@@ -455,9 +494,11 @@ export async function agentmdSync(aid, opts) {
|
|
|
455
494
|
if (typeof candidate.checkAgentMd !== 'function' || typeof candidate.downloadAgentMd !== 'function') {
|
|
456
495
|
throw new TypeError('invalid AIDStore: checkAgentMd/downloadAgentMd unavailable');
|
|
457
496
|
}
|
|
458
|
-
|
|
497
|
+
// A change event can describe Last-Modified alone. Bypass the SDK's
|
|
498
|
+
// independent freshness cache when Core knows its snapshot is stale.
|
|
499
|
+
const check = opts?.forceRefresh ? undefined : await store.checkAgentMd(aid, opts?.ttlDays ?? 30);
|
|
459
500
|
// In sync (cache fresh) — return local file content with cached verification status.
|
|
460
|
-
if (check
|
|
501
|
+
if (check?.ok && !check.data.needs_update && check.data.local_found) {
|
|
461
502
|
const content = fs.existsSync(localPath) ? fs.readFileSync(localPath, 'utf-8') : undefined;
|
|
462
503
|
const verification = check.data.verify_status
|
|
463
504
|
? normalizeVerification({ status: check.data.verify_status, reason: check.data.verify_error || undefined })
|
|
@@ -468,6 +509,7 @@ export async function agentmdSync(aid, opts) {
|
|
|
468
509
|
// SDK's downloadAgentMd persists to disk internally (AgentMdManager.saveRecord → writeContent).
|
|
469
510
|
const fetched = await store.downloadAgentMd(aid);
|
|
470
511
|
if (fetched.ok) {
|
|
512
|
+
invalidateAgentDisplayName(aid);
|
|
471
513
|
return { changed: true, content: fetched.data.content, verification: normalizeVerification(fetched.data.verification) };
|
|
472
514
|
}
|
|
473
515
|
// Fetch failed (network) — fall back to local file if present.
|
package/dist/aun/aid/client.js
CHANGED
|
@@ -39,7 +39,7 @@ export function suppressSdkLogs() {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
// ==================== SDK package ====================
|
|
42
|
-
export const MIN_AUN_CORE_SDK = [0, 5,
|
|
42
|
+
export const MIN_AUN_CORE_SDK = [0, 5, 17];
|
|
43
43
|
export const AUN_CORE_SDK_PKG = '@agentunion/fastaun';
|
|
44
44
|
function compareVersion(a, min) {
|
|
45
45
|
const parts = a.split('.').map(n => parseInt(n, 10));
|
|
@@ -1,10 +1,183 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { parseDocument } from 'yaml';
|
|
2
5
|
import { aidCreate } from './index.js';
|
|
3
6
|
import { DEFAULT_AID_DOMAIN, normalizeAidDomain } from './domain.js';
|
|
4
7
|
import { getAidStore, SLOT } from './store.js';
|
|
8
|
+
import { validateAgentMdDocument, verifyAgentMdUpload } from '@agentunion/fastaun';
|
|
9
|
+
import { agentMdPath, resolvePaths } from '../../paths.js';
|
|
10
|
+
import { PeerIdentityCache } from '../../core/relation/peer-identity.js';
|
|
11
|
+
import { agentmdPut, buildInitialAgentMd, invalidateAgentDisplayName, stripAgentMdSignature } from './agentmd.js';
|
|
5
12
|
import { logger } from '../../utils/logger.js';
|
|
6
13
|
const MAX_ATTEMPTS = 5;
|
|
7
14
|
export { DEFAULT_AID_DOMAIN } from './domain.js';
|
|
15
|
+
/** Human-facing platform label used in the control AID's agent.md name. */
|
|
16
|
+
export function controlHostPlatformLabel(platform = process.platform) {
|
|
17
|
+
switch (String(platform).trim().toLowerCase()) {
|
|
18
|
+
case 'win32':
|
|
19
|
+
case 'windows':
|
|
20
|
+
return 'Win';
|
|
21
|
+
case 'linux':
|
|
22
|
+
return 'Linux';
|
|
23
|
+
case 'darwin':
|
|
24
|
+
case 'macos':
|
|
25
|
+
case 'mac':
|
|
26
|
+
return 'MacOS';
|
|
27
|
+
default: {
|
|
28
|
+
const value = String(platform).trim();
|
|
29
|
+
return value ? `${value[0].toUpperCase()}${value.slice(1)}` : 'Host';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Build the stable display name for a daemon/control AID. */
|
|
34
|
+
export function buildControlAgentName(aid, platform = process.platform) {
|
|
35
|
+
const normalized = String(aid ?? '').trim().replace(/^@/, '');
|
|
36
|
+
const prefix = normalized.split('.', 1)[0] || normalized || 'unknown';
|
|
37
|
+
return `${controlHostPlatformLabel(platform)}主机-${prefix}`;
|
|
38
|
+
}
|
|
39
|
+
const CONTROL_AGENT_DESCRIPTION = 'EvolCore daemon host.';
|
|
40
|
+
const CONTROL_AGENT_TAGS = ['evolcore', 'control', 'host'];
|
|
41
|
+
const controlAgentMdPending = new Map();
|
|
42
|
+
function readJsonFile(filePath) {
|
|
43
|
+
try {
|
|
44
|
+
const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
45
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
46
|
+
? value
|
|
47
|
+
: undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function hasCanonicalControlFrontmatter(content, aid, name, aunRoot) {
|
|
54
|
+
try {
|
|
55
|
+
// A frontmatter-only match is insufficient here: an unsigned or malformed
|
|
56
|
+
// local card must be republished so the SDK can produce a valid signature.
|
|
57
|
+
validateAgentMdDocument(content, { expectedAid: aid, requireSignature: true });
|
|
58
|
+
const payload = stripAgentMdSignature(content);
|
|
59
|
+
const frontmatter = payload.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1];
|
|
60
|
+
if (!frontmatter)
|
|
61
|
+
return false;
|
|
62
|
+
const document = parseDocument(frontmatter);
|
|
63
|
+
if (document.errors.length > 0)
|
|
64
|
+
return false;
|
|
65
|
+
const fields = document.toJS();
|
|
66
|
+
if (!fields || typeof fields !== 'object' || Array.isArray(fields))
|
|
67
|
+
return false;
|
|
68
|
+
const values = fields;
|
|
69
|
+
const canonical = typeof values.aid === 'string'
|
|
70
|
+
&& values.aid.trim().toLowerCase() === aid.toLowerCase()
|
|
71
|
+
&& typeof values.name === 'string'
|
|
72
|
+
&& values.name.trim() === name
|
|
73
|
+
&& typeof values.type === 'string'
|
|
74
|
+
&& values.type.trim().toLowerCase() === 'host';
|
|
75
|
+
if (!canonical)
|
|
76
|
+
return false;
|
|
77
|
+
// The SDK validator checks that a signature block is well formed, but it
|
|
78
|
+
// deliberately does not verify the signature against the local cert.
|
|
79
|
+
// A damaged or stale control card must be republished, otherwise peers
|
|
80
|
+
// continue to reject it even though the frontmatter looks canonical.
|
|
81
|
+
const certPath = path.join(aunRoot, 'AIDs', aid, 'public', 'cert.pem');
|
|
82
|
+
const certPem = fs.readFileSync(certPath, 'utf8');
|
|
83
|
+
verifyAgentMdUpload(content, certPem, aid);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function clearControlAgentPeerCaches(aid, root = resolvePaths().root) {
|
|
91
|
+
const agentsDir = path.join(root, 'agents');
|
|
92
|
+
// A first-run installation may not have created any EvolCore agent yet.
|
|
93
|
+
// There is no cache to clear in that state, and ENOENT is not a warning.
|
|
94
|
+
if (!fs.existsSync(agentsDir))
|
|
95
|
+
return;
|
|
96
|
+
try {
|
|
97
|
+
for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
|
|
98
|
+
if (!entry.isDirectory())
|
|
99
|
+
continue;
|
|
100
|
+
PeerIdentityCache.clear('aun', aid, path.join(agentsDir, entry.name));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
logger.warn(`[control-aid] 清理旧身份缓存失败(不影响名片发布): ${error instanceof Error ? error.message : String(error)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Ensure a daemon/control AID has a signed host agent.md.
|
|
109
|
+
*
|
|
110
|
+
* Registration in fastaun creates a default `type: agent` card. This helper
|
|
111
|
+
* upgrades that card, supplements cards missing on older installations, and
|
|
112
|
+
* is idempotent once the local card is canonical. Upload errors are allowed to
|
|
113
|
+
* propagate to the caller so startup can log them without becoming blocked.
|
|
114
|
+
*/
|
|
115
|
+
export async function ensureControlAgentMd(aid, opts = {}) {
|
|
116
|
+
const normalizedAid = String(aid ?? '').trim().replace(/^@/, '');
|
|
117
|
+
if (!normalizedAid)
|
|
118
|
+
throw new Error('control AID is required');
|
|
119
|
+
const aunRoot = opts.aunPath ?? opts.store?.aunPath ?? resolvePaths().root;
|
|
120
|
+
const cacheRoot = opts.cacheRoot ?? resolvePaths().root;
|
|
121
|
+
const pendingKey = path.resolve(aunRoot) + '\u0000' + normalizedAid.toLowerCase();
|
|
122
|
+
const pending = controlAgentMdPending.get(pendingKey);
|
|
123
|
+
if (pending)
|
|
124
|
+
return pending;
|
|
125
|
+
const run = (async () => {
|
|
126
|
+
const name = buildControlAgentName(normalizedAid, opts.platform);
|
|
127
|
+
const localPath = agentMdPath(normalizedAid, aunRoot);
|
|
128
|
+
const metadataPath = path.join(path.dirname(localPath), 'agentmd.json');
|
|
129
|
+
let existing;
|
|
130
|
+
try {
|
|
131
|
+
existing = fs.readFileSync(localPath, 'utf8');
|
|
132
|
+
}
|
|
133
|
+
catch { /* missing */ }
|
|
134
|
+
const metadata = readJsonFile(metadataPath);
|
|
135
|
+
const remoteMissing = String(metadata?.remote_status ?? '').trim().toLowerCase() === 'missing';
|
|
136
|
+
const needsPublish = !existing || !hasCanonicalControlFrontmatter(existing, normalizedAid, name, aunRoot) || remoteMissing;
|
|
137
|
+
if (!needsPublish)
|
|
138
|
+
return { aid: normalizedAid, name, published: false };
|
|
139
|
+
const content = buildInitialAgentMd({
|
|
140
|
+
aid: normalizedAid,
|
|
141
|
+
name,
|
|
142
|
+
type: 'host',
|
|
143
|
+
description: CONTROL_AGENT_DESCRIPTION,
|
|
144
|
+
tags: CONTROL_AGENT_TAGS,
|
|
145
|
+
});
|
|
146
|
+
try {
|
|
147
|
+
await agentmdPut(content, { aid: normalizedAid, store: opts.store, aunPath: aunRoot });
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
// agentmdPut intentionally keeps edits to an existing card on failure;
|
|
151
|
+
// a control-card migration must restore the old card so a transient
|
|
152
|
+
// gateway failure cannot make the local state look successfully migrated.
|
|
153
|
+
try {
|
|
154
|
+
if (existing !== undefined) {
|
|
155
|
+
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
156
|
+
fs.writeFileSync(localPath, existing, 'utf8');
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
fs.unlinkSync(localPath);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch { /* preserve the original upload error */ }
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
// The upload changes the control AID's display name. Drop the process-wide
|
|
166
|
+
// name cache so subsequent context/status rendering cannot keep the legacy
|
|
167
|
+
// `agent` name until the normal TTL refresh.
|
|
168
|
+
invalidateAgentDisplayName(normalizedAid);
|
|
169
|
+
clearControlAgentPeerCaches(normalizedAid, cacheRoot);
|
|
170
|
+
return { aid: normalizedAid, name, published: true };
|
|
171
|
+
})();
|
|
172
|
+
controlAgentMdPending.set(pendingKey, run);
|
|
173
|
+
try {
|
|
174
|
+
return await run;
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
if (controlAgentMdPending.get(pendingKey) === run)
|
|
178
|
+
controlAgentMdPending.delete(pendingKey);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
8
181
|
function environmentValue(name) {
|
|
9
182
|
if (!Object.prototype.hasOwnProperty.call(process.env, name))
|
|
10
183
|
return undefined;
|
|
@@ -66,7 +239,7 @@ async function candidateExists(store, candidate) {
|
|
|
66
239
|
* 查重通过但注册失败时也换候选重试,覆盖查重与注册之间的竞态。
|
|
67
240
|
* - 查重走 GET 证书(见 candidateExists;不拉 agent.md,控制 AID 本就不传 agent.md)
|
|
68
241
|
* - fail-fast:查重探测失败(网关不可达)立即抛错,不掩盖成"均冲突"
|
|
69
|
-
* - agent.md
|
|
242
|
+
* - 注册后补发控制主机 agent.md;失败只告警,不丢弃已经注册的 AID
|
|
70
243
|
*/
|
|
71
244
|
export async function generateControlAid(aidDomain) {
|
|
72
245
|
const finalAidDomain = aidDomain === undefined
|
|
@@ -83,6 +256,12 @@ export async function generateControlAid(aidDomain) {
|
|
|
83
256
|
}
|
|
84
257
|
try {
|
|
85
258
|
const created = await aidCreate(candidate);
|
|
259
|
+
try {
|
|
260
|
+
await ensureControlAgentMd(created.aid, { store: created.store });
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
logger.warn(`[control-aid] ${created.aid} agent.md 发布失败(控制面可继续,后续启动会重试): ${e instanceof Error ? e.message : String(e)}`);
|
|
264
|
+
}
|
|
86
265
|
// 清理 aidCreate 内部另开的 client/store——关闭失败不可丢弃已注册的 AID(否则下次 init
|
|
87
266
|
// 会把它当冲突,白白消耗一次重试)。close 异常降级为 warn。
|
|
88
267
|
try {
|
package/dist/aun/aid/identity.js
CHANGED
|
@@ -76,7 +76,7 @@ export function aidList(aunPath) {
|
|
|
76
76
|
canSign: hasPrivateKey && hasCert && !certExpired && keyMatchesCert === true,
|
|
77
77
|
}),
|
|
78
78
|
hasPrivateKey,
|
|
79
|
-
hasAgentMd: fs.existsSync(agentMdPath(e.name)),
|
|
79
|
+
hasAgentMd: fs.existsSync(agentMdPath(e.name, root)),
|
|
80
80
|
hasCert,
|
|
81
81
|
certExpired,
|
|
82
82
|
keyMatchesCert,
|
|
@@ -265,7 +265,7 @@ export async function aidShow(aid, opts) {
|
|
|
265
265
|
const aunPath = opts?.aunPath ?? defaultAunPath();
|
|
266
266
|
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
267
267
|
const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
|
|
268
|
-
const hasAgentMd = fs.existsSync(agentMdPath(aid));
|
|
268
|
+
const hasAgentMd = fs.existsSync(agentMdPath(aid, aunPath));
|
|
269
269
|
let certExpiresAt = null;
|
|
270
270
|
let certSubject = null;
|
|
271
271
|
let certExpired = false;
|
|
@@ -323,7 +323,7 @@ export async function aidShow(aid, opts) {
|
|
|
323
323
|
}
|
|
324
324
|
if (hasAgentMd) {
|
|
325
325
|
try {
|
|
326
|
-
const content = fs.readFileSync(agentMdPath(aid), 'utf-8');
|
|
326
|
+
const content = fs.readFileSync(agentMdPath(aid, aunPath), 'utf-8');
|
|
327
327
|
if (!content.includes('AUN-SIGNATURE')) {
|
|
328
328
|
agentMdSignature = 'unsigned';
|
|
329
329
|
}
|