evolcore 0.0.1 → 0.0.2
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 +813 -0
- package/LICENSE +21 -0
- package/MIGRATION-0.5.0.md +378 -0
- package/README.md +318 -14
- package/ROLE_ACCESS_CONTROL.md +174 -0
- package/assets/.env.template +4 -0
- package/assets/brand/evolcore/README.md +19 -0
- package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
- package/assets/brand/evolcore/evolcore-app-icon.svg +13 -0
- package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
- package/assets/brand/evolcore/evolcore-brand-board.svg +126 -0
- package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.svg +14 -0
- package/assets/brand/evolcore/evolcore-logo.png +0 -0
- package/assets/brand/evolcore/evolcore-logo.svg +14 -0
- package/assets/brand/evolcore/evolcore-mark.png +0 -0
- package/assets/brand/evolcore/evolcore-mark.svg +10 -0
- package/bin/ec-safe-output.js +161 -0
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent.js +163 -0
- package/dist/agents/claude-runner.js +2385 -0
- package/dist/agents/codex-app-server-client.js +448 -0
- package/dist/agents/codex-runner.js +2639 -0
- package/dist/agents/gemini-runner.js +666 -0
- package/dist/agents/runner-types.js +75 -0
- package/dist/aun/aid/agentmd.js +216 -0
- package/dist/aun/aid/client.js +132 -0
- package/dist/aun/aid/control-aid.js +91 -0
- package/dist/aun/aid/identity.js +518 -0
- package/dist/aun/aid/index.js +4 -0
- package/dist/aun/aid/store.js +74 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/group-identity.js +10 -0
- package/dist/aun/msg/group-index.js +6 -0
- package/dist/aun/msg/group.js +1231 -0
- package/dist/aun/msg/history.js +123 -0
- package/dist/aun/msg/index.js +5 -0
- package/dist/aun/msg/p2p.js +393 -0
- package/dist/aun/msg/payload-type.js +27 -0
- package/dist/aun/msg/upload.js +137 -0
- package/dist/aun/outbox.js +160 -0
- package/dist/aun/rpc/caller.js +42 -0
- package/dist/aun/rpc/connection.js +25 -0
- package/dist/aun/rpc/index.js +2 -0
- package/dist/aun/service-proxy.js +225 -0
- package/dist/aun/storage/download.js +29 -0
- package/dist/aun/storage/index.js +3 -0
- package/dist/aun/storage/manage.js +10 -0
- package/dist/aun/storage/upload.js +68 -0
- package/dist/channels/aun.js +4147 -0
- package/dist/channels/daemon.js +422 -0
- package/dist/channels/dingtalk.js +649 -0
- package/dist/channels/feishu.js +1789 -0
- package/dist/channels/qqbot.js +409 -0
- package/dist/channels/wechat.js +817 -0
- package/dist/channels/wecom.js +565 -0
- package/dist/cli/agent-command.js +641 -0
- package/dist/cli/agent.js +1059 -0
- package/dist/cli/aun-commands.js +1948 -0
- package/dist/cli/bench.js +1228 -0
- package/dist/cli/cli-argv.js +66 -0
- package/dist/cli/code-stats.js +329 -0
- package/dist/cli/command-log.js +82 -0
- package/dist/cli/config-selector.js +69 -0
- package/dist/cli/config.js +261 -0
- package/dist/cli/ctl-command.js +62 -0
- package/dist/cli/daemon-commands.js +2887 -0
- package/dist/cli/fs-command.js +1447 -0
- package/dist/cli/handoff-command.js +302 -0
- package/dist/cli/help.js +31 -0
- package/dist/cli/index.js +358 -0
- package/dist/cli/init-channel.js +1377 -0
- package/dist/cli/init.js +553 -0
- package/dist/cli/link-rules.js +240 -0
- package/dist/cli/model.js +590 -0
- package/dist/cli/net-check.js +723 -0
- package/dist/cli/queue-command.js +126 -0
- package/dist/cli/response.js +345 -0
- package/dist/cli/restart-monitor.js +456 -0
- package/dist/cli/stats.js +607 -0
- package/dist/cli/task-context.js +80 -0
- package/dist/cli/trigger-command.js +505 -0
- package/dist/cli/version.js +88 -0
- package/dist/cli/watch-logs.js +33 -0
- package/dist/cli/watch-msg.js +673 -0
- package/dist/config/boot-log.js +266 -0
- package/dist/config/builtin-role-templates.js +30 -0
- package/dist/config/builtin-roles.js +85 -0
- package/dist/config/config-batch-get.js +11 -0
- package/dist/config/config-field-policy.js +261 -0
- package/dist/config/config-manager.js +916 -0
- package/dist/config/config-operation-service.js +384 -0
- package/dist/config/contact-book.js +371 -0
- package/dist/config/gateway-config.js +858 -0
- package/dist/config/lifecycle.js +17 -0
- package/dist/config/mention-mode.js +27 -0
- package/dist/config/merge.js +161 -0
- package/dist/config/owner-policy.js +4 -0
- package/dist/config/peer-role-resolver.js +139 -0
- package/dist/config/resolved-config-op.js +483 -0
- package/dist/config/role-config-v4-startup.js +32 -0
- package/dist/config/role-config-v5-startup.js +27 -0
- package/dist/config/role-schema.js +105 -0
- package/dist/config/role-service.js +159 -0
- package/dist/config/role-store.js +204 -0
- package/dist/config/roles.js +55 -0
- package/dist/config/schema-registry.js +154 -0
- package/dist/config/snapshot.js +598 -0
- package/dist/config-store.js +503 -0
- package/dist/core/auth/agent-delegation.js +111 -0
- package/dist/core/auth/auth-gateway.js +166 -0
- package/dist/core/auth/authenticated-actor.js +6 -0
- package/dist/core/auth/authorization-audit.js +110 -0
- package/dist/core/auth/operation-authorizer.js +718 -0
- package/dist/core/auth/operation-catalog.js +675 -0
- package/dist/core/baseagent-loader.js +54 -0
- package/dist/core/bootstrap-service.js +175 -0
- package/dist/core/capability/capability-manager.js +316 -0
- package/dist/core/capability/providers/claude-capability-provider.js +176 -0
- package/dist/core/capability/providers/codex-capability-provider.js +148 -0
- package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
- package/dist/core/capability/types.js +27 -0
- package/dist/core/causation/audit.js +103 -0
- package/dist/core/causation/aun-association.js +111 -0
- package/dist/core/causation/context.js +93 -0
- package/dist/core/causation/index.js +4 -0
- package/dist/core/causation/types.js +2 -0
- package/dist/core/channel-loader.js +277 -0
- package/dist/core/command/agent-control.js +616 -0
- package/dist/core/command/cli-intent-parser.js +225 -0
- package/dist/core/command/command-handler.js +1638 -0
- package/dist/core/command/menu-handler.js +3354 -0
- package/dist/core/command/menu-protocol.js +247 -0
- package/dist/core/command/role-menu.js +1524 -0
- package/dist/core/command/slash-gate.js +148 -0
- package/dist/core/command/slash-handler.js +3056 -0
- package/dist/core/daemon-file-cache.js +216 -0
- package/dist/core/event-bus.js +32 -0
- package/dist/core/event-catalog.js +740 -0
- package/dist/core/evolagent-registry.js +546 -0
- package/dist/core/evolagent.js +331 -0
- package/dist/core/handoff/dispatcher.js +229 -0
- package/dist/core/handoff/mutex.js +46 -0
- package/dist/core/handoff/runtime.js +324 -0
- package/dist/core/handoff/store.js +537 -0
- package/dist/core/handoff/types.js +12 -0
- package/dist/core/inference/text-inference.js +173 -0
- package/dist/core/interaction-registration.js +10 -0
- package/dist/core/interaction-router.js +278 -0
- package/dist/core/message/create-status.js +67 -0
- package/dist/core/message/im-renderer.js +657 -0
- package/dist/core/message/items-formatter.js +76 -0
- package/dist/core/message/logical-queue-bridge.js +123 -0
- package/dist/core/message/message-bridge.js +803 -0
- package/dist/core/message/message-cache.js +56 -0
- package/dist/core/message/message-log.js +339 -0
- package/dist/core/message/message-processor.js +4 -0
- package/dist/core/message/message-queue.js +1436 -0
- package/dist/core/message/message-utils.js +70 -0
- package/dist/core/message/peer-mode.js +105 -0
- package/dist/core/message/pending-hints.js +232 -0
- package/dist/core/message/response-depth.js +33 -0
- package/dist/core/message/response-engine.js +3776 -0
- package/dist/core/message/response-snapshot.js +83 -0
- package/dist/core/message/stream-debouncer.js +130 -0
- package/dist/core/message/stream-idle-monitor.js +124 -0
- package/dist/core/model/config-scope.js +162 -0
- package/dist/core/model/field-scope.js +78 -0
- package/dist/core/model/model-catalog.js +227 -0
- package/dist/core/model/model-diagnostics.js +182 -0
- package/dist/core/model/model-permission.js +90 -0
- package/dist/core/permission/approval-gateway.js +1017 -0
- package/dist/core/permission/ec-command-parser.js +347 -0
- package/dist/core/permission/execution-sandbox.js +16 -0
- package/dist/core/permission/index.js +6 -0
- package/dist/core/permission/mode.js +24 -0
- package/dist/core/permission/sandbox-runtime.js +265 -0
- package/dist/core/permission/tool-policy.js +987 -0
- package/dist/core/permission/unix-socket-policy.js +99 -0
- package/dist/core/protected-paths.js +330 -0
- package/dist/core/relation/peer-identity.js +222 -0
- package/dist/core/relation/peer-key.js +1 -0
- package/dist/core/role/runtime-policy.js +141 -0
- package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
- package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
- package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
- package/dist/core/session/session-file-adapter.js +7 -0
- package/dist/core/session/session-file-health.js +45 -0
- package/dist/core/session/session-fs-store.js +232 -0
- package/dist/core/session/session-key.js +24 -0
- package/dist/core/session/session-manager.js +1587 -0
- package/dist/core/session/session-mapper.js +100 -0
- package/dist/core/session/session-renew.js +314 -0
- package/dist/core/session/session-title.js +128 -0
- package/dist/core/session/session-turn-coordinator.js +205 -0
- package/dist/core/session/session-turns.js +67 -0
- package/dist/core/system-channels.js +29 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/group-rules-sync.js +345 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/kit-renderer.js +359 -0
- package/dist/eck/manifest-engine.js +446 -0
- package/dist/eck/message-renderer.js +199 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +2870 -4
- package/dist/ipc.js +748 -0
- package/dist/paths.js +262 -0
- package/dist/product.js +18 -0
- package/dist/response-system/context-builder.js +71 -0
- package/dist/response-system/coordinator.js +117 -0
- package/dist/response-system/decision-executor.js +86 -0
- package/dist/response-system/engines/v1/index.js +21 -0
- package/dist/response-system/engines/v1/interactive-flow.js +27 -0
- package/dist/response-system/engines/v1/proactive-flow.js +137 -0
- package/dist/response-system/engines/v1/types.js +1 -0
- package/dist/response-system/extensions.js +41 -0
- package/dist/response-system/index.js +6 -0
- package/dist/response-system/modes/index.js +7 -0
- package/dist/response-system/modes/single-session/index.js +72 -0
- package/dist/response-system/queues/fifo-queue.js +44 -0
- package/dist/response-system/queues/index.js +6 -0
- package/dist/response-system/queues/lifo-queue.js +42 -0
- package/dist/response-system/queues/priority-queue.js +63 -0
- package/dist/response-system/registry.js +97 -0
- package/dist/response-system/resolver.js +37 -0
- package/dist/response-system/selector.js +23 -0
- package/dist/response-system/types.js +7 -0
- package/dist/stats/billing.js +151 -0
- package/dist/stats/budget.js +93 -0
- package/dist/stats/db.js +403 -0
- package/dist/stats/eck-vars.js +89 -0
- package/dist/stats/index.js +11 -0
- package/dist/stats/normalizer.js +80 -0
- package/dist/stats/price-resolver.js +138 -0
- package/dist/stats/query.js +763 -0
- package/dist/stats/role-budget.js +168 -0
- package/dist/stats/writer.js +151 -0
- package/dist/trigger/anomaly-store.js +258 -0
- package/dist/trigger/audit.js +152 -0
- package/dist/trigger/event-source.js +119 -0
- package/dist/trigger/feedback.js +685 -0
- package/dist/trigger/history.js +290 -0
- package/dist/trigger/manager.js +291 -0
- package/dist/trigger/parser.js +520 -0
- package/dist/trigger/patch.js +148 -0
- package/dist/trigger/scheduler.js +1600 -0
- package/dist/trigger/script-executor.js +155 -0
- package/dist/trigger/state.js +145 -0
- package/dist/trigger/types.js +1 -0
- package/dist/trigger/validation.js +621 -0
- package/dist/types.js +12 -0
- package/dist/utils/aid-bind.js +299 -0
- package/dist/utils/atomic-write.js +95 -0
- package/dist/utils/avatar-upload.js +123 -0
- package/dist/utils/cross-platform.js +297 -0
- package/dist/utils/ecweb-utils.js +73 -0
- package/dist/utils/error-dict.json +153 -0
- package/dist/utils/error-utils.js +349 -0
- package/dist/utils/instance-registry.js +437 -0
- package/dist/utils/locale.js +21 -0
- package/dist/utils/log-writer.js +224 -0
- package/dist/utils/logger.js +89 -0
- package/dist/utils/markdown-to-plain-text.js +20 -0
- package/dist/utils/media-cache.js +271 -0
- package/dist/utils/model-prices.jsonl +17 -0
- package/dist/utils/npm-ops.js +210 -0
- package/dist/utils/process-introspect.js +133 -0
- package/dist/utils/process-tree-stats.js +271 -0
- package/dist/utils/project-path.js +74 -0
- package/dist/utils/stats.js +410 -0
- package/dist/utils/tool-summary.js +284 -0
- package/dist/utils/welcome.js +268 -0
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +65 -0
- package/kits/docs/aun/CHEATSHEET.md +19 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/aun.md +65 -0
- package/kits/docs/channels/feishu.md +56 -0
- package/kits/docs/context-assembly.md +366 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolcore/INDEX.md +66 -0
- package/kits/docs/evolcore/agent.md +69 -0
- package/kits/docs/evolcore/aid.md +49 -0
- package/kits/docs/evolcore/config.md +149 -0
- package/kits/docs/evolcore/ctl.md +46 -0
- package/kits/docs/evolcore/event.md +216 -0
- package/kits/docs/evolcore/fs-architecture.md +1215 -0
- package/kits/docs/evolcore/fs.md +101 -0
- package/kits/docs/evolcore/group-fs.md +17 -0
- package/kits/docs/evolcore/group-rules.md +226 -0
- package/kits/docs/evolcore/group.md +141 -0
- package/kits/docs/evolcore/model.md +47 -0
- package/kits/docs/evolcore/msg.md +130 -0
- package/kits/docs/evolcore/response.md +80 -0
- package/kits/docs/evolcore/rpc.md +35 -0
- package/kits/docs/evolcore/self-summary.md +29 -0
- package/kits/docs/evolcore/stats.md +70 -0
- package/kits/docs/evolcore/storage.md +49 -0
- package/kits/docs/evolcore/trigger.md +524 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +23 -0
- package/kits/docs/identity/identity-tools.md +26 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/docs/prompt-loading-architecture.md +266 -0
- package/kits/docs/venues/aun-group.md +45 -0
- package/kits/docs/venues/aun-private.md +10 -0
- package/kits/docs/venues/client-desktop.md +10 -0
- package/kits/docs/venues/client-mobile.md +10 -0
- package/kits/docs/venues/feishu-group.md +13 -0
- package/kits/docs/venues/feishu-private.md +9 -0
- package/kits/docs/venues/group.md +25 -0
- package/kits/docs/venues/private.md +10 -0
- package/kits/eck_manifest.auxiliary.json +43 -0
- package/kits/eck_manifest.json +191 -0
- package/kits/eck_message_manifest.json +63 -0
- package/kits/migrations/README-role-config-v4.md +32 -0
- package/kits/migrations/migrate-role-config-v4.mjs +623 -0
- package/kits/migrations/migrate-role-config-v5.mjs +346 -0
- package/kits/migrations/rename-config-file.mjs +99 -0
- package/kits/rules/01-overview.md +142 -0
- package/kits/rules/02-navigation.md +76 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +59 -0
- package/kits/rules/05-venue.md +44 -0
- package/kits/rules/06-channel.md +59 -0
- package/kits/schemas/_meta.json +29 -0
- package/kits/schemas/agent-config.schema.1.json +177 -0
- package/kits/schemas/agent-config.schema.2.json +239 -0
- package/kits/schemas/agent-config.schema.3.json +119 -0
- package/kits/schemas/agent-config.schema.4.json +208 -0
- package/kits/schemas/agent-config.schema.5.json +326 -0
- package/kits/schemas/contact-book.schema.1.json +36 -0
- package/kits/schemas/daemon.schema.1.json +90 -0
- package/kits/schemas/defaults.schema.1.json +81 -0
- package/kits/schemas/menu-exec-schema-commands.md +208 -0
- package/kits/schemas/migrations/README.md +28 -0
- package/kits/schemas/relation-config.schema.1.json +158 -0
- package/kits/schemas/relation-config.schema.2.json +73 -0
- package/kits/schemas/relation-config.schema.3.json +50 -0
- package/kits/schemas/relation-config.schema.4.json +47 -0
- package/kits/schemas/role-config.schema.1.json +200 -0
- package/kits/schemas/role-registry.schema.1.json +35 -0
- package/kits/schemas/single-session.schema.1.json +31 -0
- package/kits/templates/bootstrap-welcome.md +15 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
- package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
- package/kits/templates/message-fragments/inject-default.md +2 -0
- package/kits/templates/message-fragments/item.md +2 -0
- package/kits/templates/roles/admin.json +8 -0
- package/kits/templates/roles/member.json +40 -0
- package/kits/templates/roles/owner.json +8 -0
- package/kits/templates/roles/visitor.json +39 -0
- package/kits/templates/system-fragments/baseagent.md +14 -0
- package/kits/templates/system-fragments/bootstrap.md +16 -0
- package/kits/templates/system-fragments/channel.md +48 -0
- package/kits/templates/system-fragments/commands.md +26 -0
- package/kits/templates/system-fragments/identity.md +11 -0
- package/kits/templates/system-fragments/relation.md +19 -0
- package/kits/templates/system-fragments/session.md +53 -0
- package/kits/templates/system-fragments/venue.md +31 -0
- package/package.json +50 -15
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import { getAidStore, loadAid, loadClient, AidLoadError, SLOT } from './store.js';
|
|
5
|
+
import { downloadCaRoot } from './client.js';
|
|
6
|
+
import { resolvePaths, agentMdPath, aunPath as defaultAunPath } from '../../paths.js';
|
|
7
|
+
// ==================== Validation ====================
|
|
8
|
+
export function isValidAid(name) {
|
|
9
|
+
const labels = name.split('.');
|
|
10
|
+
return labels.length >= 3 && labels.every(l => /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(l));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 根据扫描得到的状态推断 AID 分类。
|
|
14
|
+
* - hasPrivateKey + signVerified=true → mine
|
|
15
|
+
* - hasPrivateKey + (signVerified=false 或未实测) → broken
|
|
16
|
+
* - !hasPrivateKey + hasCert → peer-cert
|
|
17
|
+
* - !hasPrivateKey + !hasCert → no-cert
|
|
18
|
+
*/
|
|
19
|
+
function categorizeAid(info) {
|
|
20
|
+
if (info.hasPrivateKey) {
|
|
21
|
+
if (info.signVerified === true)
|
|
22
|
+
return 'mine';
|
|
23
|
+
return 'broken';
|
|
24
|
+
}
|
|
25
|
+
return info.hasCert ? 'peer-cert' : 'no-cert';
|
|
26
|
+
}
|
|
27
|
+
// ==================== AID Operations ====================
|
|
28
|
+
export function aidList(aunPath) {
|
|
29
|
+
const root = aunPath ?? defaultAunPath();
|
|
30
|
+
const aunAidsDir = path.join(root, 'AIDs');
|
|
31
|
+
const seen = new Map();
|
|
32
|
+
if (fs.existsSync(aunAidsDir)) {
|
|
33
|
+
for (const e of fs.readdirSync(aunAidsDir, { withFileTypes: true })) {
|
|
34
|
+
if (!e.isDirectory())
|
|
35
|
+
continue;
|
|
36
|
+
const aidDir = path.join(aunAidsDir, e.name);
|
|
37
|
+
const keyJsonPath = path.join(aidDir, 'private', 'key.json');
|
|
38
|
+
const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
|
|
39
|
+
const certPath = path.join(aidDir, 'public', 'cert.pem');
|
|
40
|
+
let hasCert = false;
|
|
41
|
+
let certExpired = false;
|
|
42
|
+
let keyMatchesCert = null;
|
|
43
|
+
if (fs.existsSync(certPath)) {
|
|
44
|
+
hasCert = true;
|
|
45
|
+
try {
|
|
46
|
+
const certPem = fs.readFileSync(certPath, 'utf-8');
|
|
47
|
+
const x509 = new crypto.X509Certificate(certPem);
|
|
48
|
+
certExpired = new Date(x509.validTo) < new Date();
|
|
49
|
+
if (hasPrivateKey && fs.existsSync(keyJsonPath)) {
|
|
50
|
+
try {
|
|
51
|
+
const kp = JSON.parse(fs.readFileSync(keyJsonPath, 'utf-8'));
|
|
52
|
+
const localPubB64 = typeof kp?.public_key_der_b64 === 'string' ? kp.public_key_der_b64 : '';
|
|
53
|
+
if (localPubB64) {
|
|
54
|
+
const certPubDer = x509.publicKey.export({ type: 'spki', format: 'der' });
|
|
55
|
+
const localPubDer = Buffer.from(localPubB64, 'base64');
|
|
56
|
+
keyMatchesCert = certPubDer.equals(localPubDer);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch { /* key.json 不可解析视作不匹配 */
|
|
60
|
+
keyMatchesCert = false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// 证书无法解析视为过期 / 不可用
|
|
66
|
+
certExpired = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
seen.set(e.name, {
|
|
70
|
+
aid: e.name,
|
|
71
|
+
category: categorizeAid({
|
|
72
|
+
hasPrivateKey,
|
|
73
|
+
hasCert,
|
|
74
|
+
// 静态扫描没跑实测,用 canSign 作为 mine/broken 的近似判定
|
|
75
|
+
signVerified: hasPrivateKey ? (hasCert && !certExpired && keyMatchesCert === true) : null,
|
|
76
|
+
canSign: hasPrivateKey && hasCert && !certExpired && keyMatchesCert === true,
|
|
77
|
+
}),
|
|
78
|
+
hasPrivateKey,
|
|
79
|
+
hasAgentMd: fs.existsSync(agentMdPath(e.name)),
|
|
80
|
+
hasCert,
|
|
81
|
+
certExpired,
|
|
82
|
+
keyMatchesCert,
|
|
83
|
+
canSign: hasPrivateKey && hasCert && !certExpired && keyMatchesCert === true,
|
|
84
|
+
signVerified: null,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return [...seen.values()];
|
|
89
|
+
}
|
|
90
|
+
// ==================== Sign Self-Test ====================
|
|
91
|
+
/**
|
|
92
|
+
* 实跑一次本地 sign + verify,验证 AID 是否真能签名/验签。
|
|
93
|
+
* 全本地(不联网):用 SDK 解密私钥 → ECDSA 签 payload → 用本地 cert 公钥验。
|
|
94
|
+
* 任一环节失败都视为不可签——包括私钥 passphrase 解不开、cert 被 SDK 主动 discard 等。
|
|
95
|
+
*/
|
|
96
|
+
export async function verifySignAbility(aid, opts) {
|
|
97
|
+
const aunPath = opts?.aunPath ?? defaultAunPath();
|
|
98
|
+
let ownStore = null;
|
|
99
|
+
try {
|
|
100
|
+
let store = opts?.store;
|
|
101
|
+
if (!store) {
|
|
102
|
+
store = await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
103
|
+
ownStore = store;
|
|
104
|
+
}
|
|
105
|
+
// 加载本地 AID 值对象(含私钥),sign + verify 全本地完成
|
|
106
|
+
let aidObj;
|
|
107
|
+
try {
|
|
108
|
+
aidObj = loadAid(store, aid);
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
const code = e instanceof AidLoadError ? e.code : 'LOAD_FAILED';
|
|
112
|
+
return { ok: false, reason: `load failed: ${code} ${String(e?.message || e).slice(0, 100)}` };
|
|
113
|
+
}
|
|
114
|
+
const probe = `# probe\naid: "${aid}"\n`;
|
|
115
|
+
const signRes = aidObj.signAgentMd(probe);
|
|
116
|
+
if (!signRes.ok) {
|
|
117
|
+
return { ok: false, reason: `sign failed: ${String(signRes.error?.message || signRes.error?.code).slice(0, 120)}` };
|
|
118
|
+
}
|
|
119
|
+
const verifyRes = aidObj.verifyAgentMd(signRes.data.signed);
|
|
120
|
+
if (!verifyRes.ok) {
|
|
121
|
+
return { ok: false, reason: `verify threw: ${String(verifyRes.error?.message || verifyRes.error?.code).slice(0, 120)}` };
|
|
122
|
+
}
|
|
123
|
+
if (verifyRes.data.status === 'verified')
|
|
124
|
+
return { ok: true };
|
|
125
|
+
return { ok: false, reason: `verify failed: ${verifyRes.data.status ?? 'unknown'}${verifyRes.data.reason ? ' — ' + verifyRes.data.reason : ''}` };
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
if (ownStore) {
|
|
129
|
+
try {
|
|
130
|
+
ownStore.close();
|
|
131
|
+
}
|
|
132
|
+
catch { /* ignore */ }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* aidList 的"实测版":先做静态扫描,再对每个 AID 跑一次本地 sign+verify。
|
|
138
|
+
* 共用同一个 AUNClient 实例,避免重复初始化 secret-store / sqlite。
|
|
139
|
+
*/
|
|
140
|
+
export async function aidListVerified(aunPath) {
|
|
141
|
+
const list = aidList(aunPath);
|
|
142
|
+
const root = aunPath ?? defaultAunPath();
|
|
143
|
+
const store = await getAidStore({ slotId: SLOT.cli, aunPath: root });
|
|
144
|
+
try {
|
|
145
|
+
for (const a of list) {
|
|
146
|
+
// canSign=false 的 AID 不必跑实测,结论已经明确
|
|
147
|
+
if (!a.canSign) {
|
|
148
|
+
a.signVerified = false;
|
|
149
|
+
if (!a.hasPrivateKey)
|
|
150
|
+
a.signError = 'no private key';
|
|
151
|
+
else if (!a.hasCert)
|
|
152
|
+
a.signError = 'no cert';
|
|
153
|
+
else if (a.certExpired)
|
|
154
|
+
a.signError = 'cert expired';
|
|
155
|
+
else if (a.keyMatchesCert === false)
|
|
156
|
+
a.signError = 'key/cert public-key mismatch';
|
|
157
|
+
a.category = categorizeAid({
|
|
158
|
+
hasPrivateKey: a.hasPrivateKey, hasCert: a.hasCert,
|
|
159
|
+
signVerified: a.signVerified, canSign: a.canSign,
|
|
160
|
+
});
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const r = await verifySignAbility(a.aid, { aunPath: root, store });
|
|
164
|
+
a.signVerified = r.ok;
|
|
165
|
+
if (!r.ok)
|
|
166
|
+
a.signError = r.reason;
|
|
167
|
+
a.category = categorizeAid({
|
|
168
|
+
hasPrivateKey: a.hasPrivateKey, hasCert: a.hasCert,
|
|
169
|
+
signVerified: a.signVerified, canSign: a.canSign,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
try {
|
|
175
|
+
store.close();
|
|
176
|
+
}
|
|
177
|
+
catch { /* ignore */ }
|
|
178
|
+
}
|
|
179
|
+
return list;
|
|
180
|
+
}
|
|
181
|
+
export async function aidCreate(aid, opts) {
|
|
182
|
+
const aunPath = opts?.aunPath ?? defaultAunPath();
|
|
183
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
184
|
+
const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
|
|
185
|
+
// 如果私钥已存在,先验证签名能力
|
|
186
|
+
if (hasPrivateKey) {
|
|
187
|
+
const verifyResult = await verifySignAbility(aid, { aunPath });
|
|
188
|
+
if (verifyResult.ok) {
|
|
189
|
+
// 身份有效:加载并认证,返回已认证的 client
|
|
190
|
+
const store = await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
191
|
+
try {
|
|
192
|
+
const client = await loadClient(store, aid);
|
|
193
|
+
const auth = await client.authenticate();
|
|
194
|
+
return { aid, alreadyExisted: true, gateway: String(auth?.gateway ?? ''), client, store };
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
store.close();
|
|
198
|
+
throw e;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// 签名验证失败
|
|
202
|
+
if (!opts?.force) {
|
|
203
|
+
const error = new Error(`AID ${aid} 已存在但身份无效(${verifyResult.reason || '签名验证失败'})。\n` +
|
|
204
|
+
`使用 --force 参数尝试恢复或重新注册。`);
|
|
205
|
+
error.code = 'AID_INVALID';
|
|
206
|
+
error.reason = verifyResult.reason;
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
// --force:先尝试 authenticate 恢复证书
|
|
210
|
+
const recoverStore = await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
211
|
+
try {
|
|
212
|
+
const recoverClient = await loadClient(recoverStore, aid);
|
|
213
|
+
const auth = await recoverClient.authenticate();
|
|
214
|
+
return { aid, alreadyExisted: true, gateway: String(auth?.gateway ?? ''), client: recoverClient, store: recoverStore };
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
recoverStore.close();
|
|
218
|
+
fs.rmSync(aidDir, { recursive: true, force: true });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// 新注册流程:AIDStore.register → downloadCaRoot → load → authenticate
|
|
222
|
+
const store = await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
223
|
+
try {
|
|
224
|
+
const regResult = await store.register(aid);
|
|
225
|
+
if (!regResult.ok) {
|
|
226
|
+
const e = new Error(regResult.error.message);
|
|
227
|
+
e.code = regResult.error.code;
|
|
228
|
+
throw e;
|
|
229
|
+
}
|
|
230
|
+
// 注册成功后下载 CA 根证书(如果还没有)
|
|
231
|
+
// 从 AID well-known 发现 gateway 用于 CA 下载
|
|
232
|
+
let gatewayUrl = '';
|
|
233
|
+
try {
|
|
234
|
+
const { GatewayDiscovery } = await import('@agentunion/fastaun');
|
|
235
|
+
const discovery = new GatewayDiscovery({});
|
|
236
|
+
gatewayUrl = await discovery.discover(`https://${aid}/.well-known/aun-gateway`);
|
|
237
|
+
}
|
|
238
|
+
catch { /* fall through */ }
|
|
239
|
+
if (gatewayUrl) {
|
|
240
|
+
await downloadCaRoot(aunPath, gatewayUrl);
|
|
241
|
+
}
|
|
242
|
+
// 重建 store(CA 可能刚下载,需要 rootCaPath 生效)
|
|
243
|
+
store.close();
|
|
244
|
+
const store2 = await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
245
|
+
try {
|
|
246
|
+
const client = await loadClient(store2, aid);
|
|
247
|
+
await client.authenticate();
|
|
248
|
+
return { aid, alreadyExisted: false, gateway: gatewayUrl, client, store: store2 };
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
store2.close();
|
|
252
|
+
throw e;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (e) {
|
|
256
|
+
store.close();
|
|
257
|
+
throw e;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// ==================== Show ====================
|
|
261
|
+
export async function aidShow(aid, opts) {
|
|
262
|
+
const aunPath = opts?.aunPath ?? defaultAunPath();
|
|
263
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
264
|
+
const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
|
|
265
|
+
const hasAgentMd = fs.existsSync(agentMdPath(aid));
|
|
266
|
+
let certExpiresAt = null;
|
|
267
|
+
let certSubject = null;
|
|
268
|
+
let certExpired = false;
|
|
269
|
+
let certPem = null;
|
|
270
|
+
let keyMatchesCert = null;
|
|
271
|
+
const certPath = path.join(aidDir, 'public', 'cert.pem');
|
|
272
|
+
if (fs.existsSync(certPath)) {
|
|
273
|
+
try {
|
|
274
|
+
certPem = fs.readFileSync(certPath, 'utf-8');
|
|
275
|
+
const x509 = new crypto.X509Certificate(certPem);
|
|
276
|
+
certExpiresAt = x509.validTo;
|
|
277
|
+
certSubject = x509.subject;
|
|
278
|
+
certExpired = new Date(x509.validTo) < new Date();
|
|
279
|
+
const keyJsonPath = path.join(aidDir, 'private', 'key.json');
|
|
280
|
+
if (hasPrivateKey && fs.existsSync(keyJsonPath)) {
|
|
281
|
+
try {
|
|
282
|
+
const kp = JSON.parse(fs.readFileSync(keyJsonPath, 'utf-8'));
|
|
283
|
+
const localPubB64 = typeof kp?.public_key_der_b64 === 'string' ? kp.public_key_der_b64 : '';
|
|
284
|
+
if (localPubB64) {
|
|
285
|
+
const certPubDer = x509.publicKey.export({ type: 'spki', format: 'der' });
|
|
286
|
+
const localPubDer = Buffer.from(localPubB64, 'base64');
|
|
287
|
+
keyMatchesCert = certPubDer.equals(localPubDer);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
keyMatchesCert = false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch { /* ignore parse errors */ }
|
|
296
|
+
}
|
|
297
|
+
let agentMdSignature = 'unknown';
|
|
298
|
+
let agentMdSignatureReason;
|
|
299
|
+
let signVerified = null;
|
|
300
|
+
let signError;
|
|
301
|
+
// 先做一次签名自检(共享 store,避免重复起 SDK)
|
|
302
|
+
const store = await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
303
|
+
try {
|
|
304
|
+
if (hasPrivateKey && certPem && !certExpired && keyMatchesCert !== false) {
|
|
305
|
+
const r = await verifySignAbility(aid, { aunPath, store });
|
|
306
|
+
signVerified = r.ok;
|
|
307
|
+
if (!r.ok)
|
|
308
|
+
signError = r.reason;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
signVerified = false;
|
|
312
|
+
if (!hasPrivateKey)
|
|
313
|
+
signError = 'no private key';
|
|
314
|
+
else if (!certPem)
|
|
315
|
+
signError = 'no cert';
|
|
316
|
+
else if (certExpired)
|
|
317
|
+
signError = 'cert expired';
|
|
318
|
+
else if (keyMatchesCert === false)
|
|
319
|
+
signError = 'key/cert public-key mismatch';
|
|
320
|
+
}
|
|
321
|
+
if (hasAgentMd) {
|
|
322
|
+
try {
|
|
323
|
+
const content = fs.readFileSync(agentMdPath(aid), 'utf-8');
|
|
324
|
+
if (!content.includes('AUN-SIGNATURE')) {
|
|
325
|
+
agentMdSignature = 'unsigned';
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
// 用本地 AID 值对象验签(含本地 cert 公钥)
|
|
329
|
+
const aidObj = loadAid(store, aid);
|
|
330
|
+
const result = aidObj.verifyAgentMd(content);
|
|
331
|
+
if (!result.ok) {
|
|
332
|
+
agentMdSignature = 'unknown';
|
|
333
|
+
agentMdSignatureReason = String(result.error?.message || result.error?.code).slice(0, 100);
|
|
334
|
+
}
|
|
335
|
+
else if (result.data.status === 'verified') {
|
|
336
|
+
agentMdSignature = 'verified';
|
|
337
|
+
}
|
|
338
|
+
else if (result.data.status === 'unsigned') {
|
|
339
|
+
agentMdSignature = 'unsigned';
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
agentMdSignature = 'invalid';
|
|
343
|
+
agentMdSignatureReason = result.data.reason;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (e) {
|
|
348
|
+
agentMdSignature = 'unknown';
|
|
349
|
+
agentMdSignatureReason = String(e?.message || e).slice(0, 100);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
finally {
|
|
354
|
+
try {
|
|
355
|
+
store.close();
|
|
356
|
+
}
|
|
357
|
+
catch { }
|
|
358
|
+
}
|
|
359
|
+
return { aid, hasPrivateKey, hasAgentMd, certExpiresAt, certSubject, certExpired, keyMatchesCert, signVerified, signError, agentMdSignature, agentMdSignatureReason };
|
|
360
|
+
}
|
|
361
|
+
// ==================== Delete ====================
|
|
362
|
+
export function aidDelete(aid, opts) {
|
|
363
|
+
const aunPath = opts?.aunPath ?? defaultAunPath();
|
|
364
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
365
|
+
if (!fs.existsSync(aidDir))
|
|
366
|
+
return false;
|
|
367
|
+
fs.rmSync(aidDir, { recursive: true, force: true });
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
export async function probePkiRecoverability(aid, opts) {
|
|
371
|
+
const aunPath = opts?.aunPath ?? defaultAunPath();
|
|
372
|
+
const timeoutMs = opts?.timeoutMs ?? 8000;
|
|
373
|
+
const keyJsonPath = path.join(aunPath, 'AIDs', aid, 'private', 'key.json');
|
|
374
|
+
if (!fs.existsSync(keyJsonPath))
|
|
375
|
+
return { kind: 'no-key' };
|
|
376
|
+
let localPubB64 = '';
|
|
377
|
+
try {
|
|
378
|
+
const kp = JSON.parse(fs.readFileSync(keyJsonPath, 'utf-8'));
|
|
379
|
+
if (typeof kp?.public_key_der_b64 !== 'string' || !kp.public_key_der_b64) {
|
|
380
|
+
return { kind: 'unknown', reason: 'key.json missing public_key_der_b64' };
|
|
381
|
+
}
|
|
382
|
+
localPubB64 = kp.public_key_der_b64;
|
|
383
|
+
}
|
|
384
|
+
catch (e) {
|
|
385
|
+
return { kind: 'unknown', reason: `key.json parse failed: ${String(e?.message || e).slice(0, 80)}` };
|
|
386
|
+
}
|
|
387
|
+
// 1. 发现网关
|
|
388
|
+
let gateway = '';
|
|
389
|
+
try {
|
|
390
|
+
const ctl = AbortSignal.timeout(timeoutMs);
|
|
391
|
+
const gwResp = await fetch(`https://${aid}/.well-known/aun-gateway`, { redirect: 'follow', signal: ctl });
|
|
392
|
+
if (gwResp.ok) {
|
|
393
|
+
const text = (await gwResp.text()).trim();
|
|
394
|
+
try {
|
|
395
|
+
const parsed = JSON.parse(text);
|
|
396
|
+
gateway = parsed?.gateways?.[0]?.url || text;
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
gateway = text;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
catch (e) {
|
|
404
|
+
return { kind: 'no-server-record', reason: `gateway discovery failed: ${String(e?.message || e).slice(0, 80)}` };
|
|
405
|
+
}
|
|
406
|
+
if (!gateway)
|
|
407
|
+
return { kind: 'no-server-record', reason: 'no gateway for AID' };
|
|
408
|
+
// 2. 拉云端 cert
|
|
409
|
+
let certPem = '';
|
|
410
|
+
try {
|
|
411
|
+
const parsed = new URL(gateway);
|
|
412
|
+
const scheme = parsed.protocol === 'wss:' ? 'https:' : 'http:';
|
|
413
|
+
const certUrl = `${scheme}//${parsed.host}/pki/cert/${encodeURIComponent(aid)}`;
|
|
414
|
+
const ctl = AbortSignal.timeout(timeoutMs);
|
|
415
|
+
const resp = await fetch(certUrl, { redirect: 'follow', signal: ctl });
|
|
416
|
+
if (!resp.ok) {
|
|
417
|
+
return { kind: 'no-server-record', reason: `pki/cert HTTP ${resp.status}` };
|
|
418
|
+
}
|
|
419
|
+
certPem = (await resp.text()).trim();
|
|
420
|
+
if (!certPem.includes('BEGIN CERTIFICATE')) {
|
|
421
|
+
return { kind: 'no-server-record', reason: 'pki/cert returned non-cert content' };
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
catch (e) {
|
|
425
|
+
return { kind: 'no-server-record', reason: `pki/cert fetch failed: ${String(e?.message || e).slice(0, 80)}` };
|
|
426
|
+
}
|
|
427
|
+
// 3. 比对公钥
|
|
428
|
+
try {
|
|
429
|
+
const x509 = new crypto.X509Certificate(certPem);
|
|
430
|
+
const certPubDer = x509.publicKey.export({ type: 'spki', format: 'der' });
|
|
431
|
+
const localPubDer = Buffer.from(localPubB64, 'base64');
|
|
432
|
+
if (certPubDer.equals(localPubDer)) {
|
|
433
|
+
return { kind: 'recoverable', serverCertPubB64: certPubDer.toString('base64') };
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
kind: 'unrecoverable',
|
|
437
|
+
reason: 'server has different public key registered, current local private key cannot be used',
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
return { kind: 'unknown', reason: `cert parse failed: ${String(e?.message || e).slice(0, 80)}` };
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// ==================== Lookup ====================
|
|
445
|
+
export async function aidLookup(aid) {
|
|
446
|
+
// gateway:well-known 探测(保留,供 aid lookup 命令展示)
|
|
447
|
+
let gateway = '';
|
|
448
|
+
try {
|
|
449
|
+
const gwResp = await fetch(`https://${aid}/.well-known/aun-gateway`, { redirect: 'follow' });
|
|
450
|
+
if (gwResp.ok) {
|
|
451
|
+
const text = await gwResp.text();
|
|
452
|
+
// Response may be JSON with gateways array or plain URL
|
|
453
|
+
try {
|
|
454
|
+
const parsed = JSON.parse(text.trim());
|
|
455
|
+
if (parsed.gateways?.[0]?.url) {
|
|
456
|
+
gateway = parsed.gateways[0].url;
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
gateway = text.trim();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
gateway = text.trim();
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
catch { /* ignore */ }
|
|
468
|
+
const { agentmdGet } = await import('./agentmd.js');
|
|
469
|
+
const store = await getAidStore({ slotId: SLOT.cli });
|
|
470
|
+
try {
|
|
471
|
+
// 权威注册判据:PKI 证书 HEAD(与 agent.md 无关)
|
|
472
|
+
const existsResult = await store.exists(aid);
|
|
473
|
+
if (!existsResult.ok) {
|
|
474
|
+
return { exists: false, aid, gateway, error: existsResult.error?.message ?? 'exists check failed' };
|
|
475
|
+
}
|
|
476
|
+
const exists = existsResult.data.exists;
|
|
477
|
+
if (!exists) {
|
|
478
|
+
return { exists: false, aid, gateway };
|
|
479
|
+
}
|
|
480
|
+
// 已注册:尽力拉 agent.md content(无 agent.md 不影响 exists)
|
|
481
|
+
let content;
|
|
482
|
+
try {
|
|
483
|
+
content = await agentmdGet(aid, { store });
|
|
484
|
+
}
|
|
485
|
+
catch { /* registered but no agent.md — content stays undefined */ }
|
|
486
|
+
return { exists, aid, gateway, content };
|
|
487
|
+
}
|
|
488
|
+
finally {
|
|
489
|
+
store.close();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function lifecycleLogPath(aid) {
|
|
493
|
+
const aidName = aid.startsWith('@') ? aid.slice(1) : aid;
|
|
494
|
+
return path.join(resolvePaths().aidLogsDir, `${aidName}.jsonl`);
|
|
495
|
+
}
|
|
496
|
+
export function appendAidLifecycle(event) {
|
|
497
|
+
const filePath = lifecycleLogPath(event.aid);
|
|
498
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
499
|
+
fs.appendFileSync(filePath, JSON.stringify(event) + '\n');
|
|
500
|
+
}
|
|
501
|
+
export function readAidLifecycle(aid, lastN = 50) {
|
|
502
|
+
const filePath = lifecycleLogPath(aid);
|
|
503
|
+
try {
|
|
504
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
505
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
506
|
+
const events = [];
|
|
507
|
+
for (const line of lines.slice(-lastN)) {
|
|
508
|
+
try {
|
|
509
|
+
events.push(JSON.parse(line));
|
|
510
|
+
}
|
|
511
|
+
catch { }
|
|
512
|
+
}
|
|
513
|
+
return events;
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
return [];
|
|
517
|
+
}
|
|
518
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { isValidAid, aidList, aidListVerified, aidCreate, aidShow, aidDelete, aidLookup, verifySignAbility, probePkiRecoverability, appendAidLifecycle, readAidLifecycle } from './identity.js';
|
|
2
|
+
export { buildInitialAgentMd, agentmdGet, agentmdPut, agentmdSync, stripAgentMdSignature, updateAgentMdFrontmatterAvatar, updateAgentMdFrontmatterName } from './agentmd.js';
|
|
3
|
+
export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, suppressSdkLogs, } from './client.js';
|
|
4
|
+
export { getAidStore, loadClient, loadAid, AidLoadError, SLOT } from './store.js';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Slot 命名约定(基于 fastaun 0.4.3 隔离键语义)。
|
|
5
|
+
*
|
|
6
|
+
* slotIsolationKey 取第一个分隔符(空格/'/'/':')之前的部分作为隔离键,
|
|
7
|
+
* 隔离键相同 = 共享消费通道(token / seq 游标 / 消息过滤)。
|
|
8
|
+
*
|
|
9
|
+
* 'evolcore daemon' → 隔离键 'evolcore' ┐
|
|
10
|
+
* 'evolcore cli' → 隔离键 'evolcore' ├ 共享 evolcore 消费通道
|
|
11
|
+
* 'evolcore netcheck' → 隔离键 'evolcore' ┘
|
|
12
|
+
* 'evolcore-bench' → 隔离键 'evolcore-bench'(连字符非分隔符)→ 独立通道
|
|
13
|
+
*
|
|
14
|
+
* 设计意图:
|
|
15
|
+
* - daemon 长连接 + cli 短连接共存于 evolcore 通道(1 长 + N 短,短连接不踢长连接)
|
|
16
|
+
* - netcheck 长连接抢 evolcore 长连接位 → 踢掉 daemon(用于踢人/extra_info 测试)
|
|
17
|
+
* - bench 各并发单元用 evolcore-bench-<N>,各自独立隔离键,互不干扰
|
|
18
|
+
*/
|
|
19
|
+
export const SLOT = {
|
|
20
|
+
daemon: 'evolcore daemon',
|
|
21
|
+
cli: 'evolcore cli',
|
|
22
|
+
bench: 'evolcore-bench',
|
|
23
|
+
netcheck: 'evolcore netcheck',
|
|
24
|
+
};
|
|
25
|
+
/** 加载身份失败时抛出,携带 SDK 的错误码与消息。 */
|
|
26
|
+
export class AidLoadError extends Error {
|
|
27
|
+
code;
|
|
28
|
+
constructor(code, message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'AidLoadError';
|
|
31
|
+
this.code = code;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 统一构造 AIDStore。
|
|
36
|
+
*
|
|
37
|
+
* 接管旧 createAunClient 的职责:注入 encryptionSeed、rootCaPath、debug、slotId。
|
|
38
|
+
* deviceId 不传 —— 由 SDK 从 {aunPath}/.device_id 读取或生成(同机共享)。
|
|
39
|
+
*/
|
|
40
|
+
export async function getAidStore(opts) {
|
|
41
|
+
const { aunPath: defaultAunPath } = await import('../../paths.js');
|
|
42
|
+
const { loadDaemonConfig } = await import('../../config-store.js');
|
|
43
|
+
const { AIDStore } = await import('@agentunion/fastaun');
|
|
44
|
+
const aunPath = opts.aunPath ?? defaultAunPath();
|
|
45
|
+
const encryptionSeed = loadDaemonConfig().aun?.encryptionSeed
|
|
46
|
+
?? process.env.AUN_ENCRYPTION_SEED
|
|
47
|
+
?? 'evol';
|
|
48
|
+
const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
|
|
49
|
+
const storeOpts = {
|
|
50
|
+
aunPath,
|
|
51
|
+
encryptionSeed,
|
|
52
|
+
slotId: opts.slotId,
|
|
53
|
+
debug: opts.debug ?? false,
|
|
54
|
+
};
|
|
55
|
+
if (fs.existsSync(caCertPath))
|
|
56
|
+
storeOpts.rootCaPath = caCertPath;
|
|
57
|
+
return new AIDStore(storeOpts);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 从 store 加载本地身份并构造已就绪的 AUNClient(尚未连接)。
|
|
61
|
+
*
|
|
62
|
+
* load 失败(证书缺失/过期/链断裂/私钥不匹配等)抛 AidLoadError,携带 SDK 错误码。
|
|
63
|
+
*/
|
|
64
|
+
export async function loadClient(store, aid) {
|
|
65
|
+
const { AUNClient } = await import('@agentunion/fastaun');
|
|
66
|
+
return new AUNClient(loadAid(store, aid));
|
|
67
|
+
}
|
|
68
|
+
/** 加载本地 AID 值对象(用于离线签名/验签,无需连接)。load 失败抛 AidLoadError。 */
|
|
69
|
+
export function loadAid(store, aid) {
|
|
70
|
+
const r = store.load(aid);
|
|
71
|
+
if (!r.ok)
|
|
72
|
+
throw new AidLoadError(r.error.code, r.error.message);
|
|
73
|
+
return r.data.aid;
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { isValidAid } from './identity.js';
|
|
4
|
+
/**
|
|
5
|
+
* `agents/<dirName>` 是否能被当作合法的 self-agent 目录:
|
|
6
|
+
* - 目录名通过 isValidAid(标准多级域名)
|
|
7
|
+
* - 目录下存在 config.json
|
|
8
|
+
*
|
|
9
|
+
* 不满足返回原因字符串,调用方决定是 warn-and-skip 还是 throw。
|
|
10
|
+
*/
|
|
11
|
+
export function checkAgentDir(agentsDir, dirName) {
|
|
12
|
+
if (!isValidAid(dirName)) {
|
|
13
|
+
return `dir name "${dirName}" is not a valid AID`;
|
|
14
|
+
}
|
|
15
|
+
const configPath = path.join(agentsDir, dirName, 'config.json');
|
|
16
|
+
if (!fs.existsSync(configPath)) {
|
|
17
|
+
return `missing ${path.join(dirName, 'config.json')}`;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
export { isValidAid };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function isExplicitGroupId(value) {
|
|
2
|
+
const id = value.trim();
|
|
3
|
+
if (!id)
|
|
4
|
+
return false;
|
|
5
|
+
return (id.startsWith('group.') && id.includes('/'))
|
|
6
|
+
|| /^\d+\.[a-z0-9.-]+$/i.test(id)
|
|
7
|
+
|| /^grp_[a-z0-9_-]+$/i.test(id)
|
|
8
|
+
|| /^g-[a-z0-9_-]+(?:[.@][a-z0-9.-]+)?$/i.test(id)
|
|
9
|
+
|| /(?:^|\.)groupid\.pub$/i.test(id);
|
|
10
|
+
}
|