evolclaw 2.8.3 → 3.0.0
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/README.md +21 -12
- package/dist/agents/claude-runner.js +102 -38
- package/dist/agents/codex-runner.js +11 -14
- package/dist/agents/gemini-runner.js +10 -12
- package/dist/agents/resolve.js +134 -0
- package/dist/agents/templates.js +3 -3
- package/dist/aun/aid/agentmd.js +186 -0
- package/dist/aun/aid/client.js +134 -0
- package/dist/aun/aid/identity.js +131 -0
- package/dist/aun/aid/index.js +3 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/msg/group.js +291 -0
- package/dist/aun/msg/index.js +4 -0
- package/dist/aun/msg/p2p.js +144 -0
- package/dist/aun/msg/payload-type.js +27 -0
- package/dist/aun/msg/upload.js +98 -0
- package/dist/aun/outbox.js +138 -0
- package/dist/aun/rpc/caller.js +42 -0
- package/dist/aun/rpc/connection.js +34 -0
- package/dist/aun/rpc/index.js +2 -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 +35 -0
- package/dist/channels/aun.js +1051 -288
- package/dist/channels/dingtalk.js +58 -5
- package/dist/channels/feishu.js +266 -30
- package/dist/channels/qqbot.js +67 -12
- package/dist/channels/wechat.js +61 -4
- package/dist/channels/wecom.js +58 -5
- package/dist/cli/agent.js +800 -0
- package/dist/cli/index.js +4253 -0
- package/dist/{utils → cli}/init-channel.js +211 -621
- package/dist/cli/init.js +178 -0
- package/dist/config-store.js +613 -0
- package/dist/core/{agent-loader.js → baseagent-loader.js} +6 -12
- package/dist/core/channel-loader.js +162 -11
- package/dist/core/command-handler.js +858 -847
- package/dist/core/evolagent-registry.js +191 -371
- package/dist/core/evolagent.js +203 -234
- package/dist/core/interaction-router.js +52 -5
- package/dist/core/message/im-renderer.js +480 -0
- package/dist/core/message/items-formatter.js +61 -0
- package/dist/core/message/message-bridge.js +104 -56
- package/dist/core/message/message-log.js +91 -0
- package/dist/core/message/message-processor.js +309 -142
- package/dist/core/message/message-queue.js +3 -3
- package/dist/core/permission.js +21 -8
- package/dist/core/session/adapters/codex-session-file-adapter.js +24 -2
- package/dist/core/session/session-fs-store.js +230 -0
- package/dist/core/session/session-manager.js +704 -775
- package/dist/core/session/session-mapper.js +87 -0
- package/dist/core/trigger/manager.js +122 -0
- package/dist/core/trigger/parser.js +128 -0
- package/dist/core/trigger/scheduler.js +224 -0
- package/dist/{templates → data}/prompts.md +34 -1
- package/dist/index.js +431 -275
- package/dist/ipc.js +49 -0
- package/dist/paths.js +82 -9
- package/dist/types.js +8 -2
- package/dist/utils/atomic-write.js +79 -0
- package/dist/utils/channel-helpers.js +46 -0
- package/dist/utils/cross-platform.js +0 -18
- package/dist/utils/instance-registry.js +433 -0
- package/dist/utils/log-writer.js +216 -0
- package/dist/utils/logger.js +24 -77
- package/dist/utils/media-cache.js +23 -0
- package/dist/utils/{upgrade.js → npm-ops.js} +52 -21
- package/dist/utils/process-introspect.js +144 -0
- package/dist/utils/stats.js +192 -0
- package/dist/watch-msg.js +529 -0
- package/evolclaw-install-aun.md +114 -46
- package/kits/aun/meta.md +25 -0
- package/kits/aun/role.md +25 -0
- package/kits/channels/aun.md +25 -0
- package/kits/evolclaw/commands.md +31 -0
- package/kits/evolclaw/identity-tools.md +26 -0
- package/kits/evolclaw/self-summary.md +29 -0
- package/kits/evolclaw/tools.md +25 -0
- package/kits/templates/group.md +20 -0
- package/kits/templates/private.md +9 -0
- package/kits/templates/system-fragments/personal-context.md +3 -0
- package/kits/templates/system-fragments/self-intro.md +5 -0
- package/kits/templates/system-fragments/speaker-intro.md +5 -0
- package/kits/templates/system-fragments/venue-intro.md +5 -0
- package/package.json +7 -5
- package/data/evolclaw.sample.json +0 -60
- package/dist/channels/aun-ops.js +0 -275
- package/dist/cli.js +0 -2178
- package/dist/config.js +0 -591
- package/dist/core/agent-registry.js +0 -450
- package/dist/core/evolagent-schema.js +0 -72
- package/dist/core/message/stream-flusher.js +0 -238
- package/dist/core/message/thought-emitter.js +0 -162
- package/dist/core/reload-hooks.js +0 -87
- package/dist/prompts/templates.js +0 -122
- package/dist/templates/skills.md +0 -66
- package/dist/utils/channel-fingerprint.js +0 -59
- package/dist/utils/error-dict.js +0 -63
- package/dist/utils/format.js +0 -32
- package/dist/utils/init.js +0 -645
- package/dist/utils/migrate-project.js +0 -122
- package/dist/utils/reload-hooks.js +0 -87
- package/dist/utils/stats-collector.js +0 -99
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { getAunClient } from './client.js';
|
|
5
|
+
export function buildInitialAgentMd(opts) {
|
|
6
|
+
const agentName = opts.aid.split('.')[0];
|
|
7
|
+
const agentType = opts.type || 'ai';
|
|
8
|
+
return `---\naid: "${opts.aid}"\nname: "${agentName}"\ntype: "${agentType}"\nversion: "1.0.0"\ndescription: ""\ntags:\n - evolclaw\n---\n`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the gateway URL for an AID via .well-known discovery.
|
|
12
|
+
*/
|
|
13
|
+
async function discoverGateway(aid) {
|
|
14
|
+
try {
|
|
15
|
+
const resp = await fetch(`https://${aid}/.well-known/aun-gateway`, { redirect: 'follow' });
|
|
16
|
+
if (!resp.ok)
|
|
17
|
+
return undefined;
|
|
18
|
+
const text = await resp.text();
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(text.trim()).gateways?.[0]?.url ?? text.trim();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return text.trim();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Obtain cert PEM for an AID: local first, then network.
|
|
32
|
+
* Persists fetched cert to local for future use.
|
|
33
|
+
*/
|
|
34
|
+
async function obtainCertPem(aid, aunPath, client) {
|
|
35
|
+
const localCert = path.join(aunPath, 'AIDs', aid, 'public', 'cert.pem');
|
|
36
|
+
if (fs.existsSync(localCert)) {
|
|
37
|
+
return fs.readFileSync(localCert, 'utf-8');
|
|
38
|
+
}
|
|
39
|
+
// Fetch from network via SDK's _fetchPeerCert (needs gateway)
|
|
40
|
+
if (client) {
|
|
41
|
+
try {
|
|
42
|
+
if (!client._gatewayUrl) {
|
|
43
|
+
client._gatewayUrl = await discoverGateway(aid);
|
|
44
|
+
}
|
|
45
|
+
if (client._gatewayUrl) {
|
|
46
|
+
const certPem = await client._fetchPeerCert.call(client, aid);
|
|
47
|
+
// Persist for future use
|
|
48
|
+
if (certPem) {
|
|
49
|
+
const certDir = path.join(aunPath, 'AIDs', aid, 'public');
|
|
50
|
+
fs.mkdirSync(certDir, { recursive: true });
|
|
51
|
+
fs.writeFileSync(localCert, certPem, 'utf-8');
|
|
52
|
+
}
|
|
53
|
+
return certPem;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch { /* fall through */ }
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Verify agent.md content using SDK.
|
|
62
|
+
*/
|
|
63
|
+
async function verifyContent(content, aid, certPem, client) {
|
|
64
|
+
if (!content.includes('AUN-SIGNATURE')) {
|
|
65
|
+
return { status: 'unsigned' };
|
|
66
|
+
}
|
|
67
|
+
if (!certPem) {
|
|
68
|
+
return { status: 'invalid', reason: 'certificate not available' };
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const result = await client.auth.verifyAgentMd(content, { aid, certPem });
|
|
72
|
+
if (result.status === 'verified' || result.verified) {
|
|
73
|
+
return { status: 'verified' };
|
|
74
|
+
}
|
|
75
|
+
return { status: 'invalid', reason: result.reason };
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
return { status: 'invalid', reason: `verify error: ${String(e.message || e).slice(0, 100)}` };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create a bare AUNClient (no createAid) for read-only operations.
|
|
83
|
+
*/
|
|
84
|
+
async function createBareClient(aunPath) {
|
|
85
|
+
const { AUNClient } = await import('@agentunion/fastaun');
|
|
86
|
+
const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
|
|
87
|
+
const clientOpts = { aun_path: aunPath, debug: false };
|
|
88
|
+
if (fs.existsSync(caCertPath))
|
|
89
|
+
clientOpts.root_ca_path = caCertPath;
|
|
90
|
+
return new AUNClient(clientOpts);
|
|
91
|
+
}
|
|
92
|
+
export async function agentmdGet(aid, opts) {
|
|
93
|
+
const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
|
|
94
|
+
const localPath = path.join(aunPath, 'AIDs', aid, 'agent.md');
|
|
95
|
+
// === Path A: local agent.md exists ===
|
|
96
|
+
if (fs.existsSync(localPath)) {
|
|
97
|
+
const content = fs.readFileSync(localPath, 'utf-8');
|
|
98
|
+
if (!opts?.withVerification)
|
|
99
|
+
return content;
|
|
100
|
+
// Verify local content
|
|
101
|
+
const client = opts?.client ?? await createBareClient(aunPath);
|
|
102
|
+
const ownClient = !opts?.client;
|
|
103
|
+
try {
|
|
104
|
+
const certPem = await obtainCertPem(aid, aunPath, client);
|
|
105
|
+
const verification = await verifyContent(content, aid, certPem, client);
|
|
106
|
+
if (verification.status !== 'invalid') {
|
|
107
|
+
return { content, verification };
|
|
108
|
+
}
|
|
109
|
+
// Fallback: local invalid → try remote
|
|
110
|
+
try {
|
|
111
|
+
const remote = await client.auth.downloadAgentMd(aid);
|
|
112
|
+
if (remote) {
|
|
113
|
+
const remoteVerification = await verifyContent(remote, aid, certPem, client);
|
|
114
|
+
if (remoteVerification.status === 'verified') {
|
|
115
|
+
fs.writeFileSync(localPath, remote, 'utf-8');
|
|
116
|
+
return { content: remote, verification: remoteVerification };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch { /* remote fetch failed, return local invalid result */ }
|
|
121
|
+
return { content, verification };
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
if (ownClient)
|
|
125
|
+
try {
|
|
126
|
+
await client.close();
|
|
127
|
+
}
|
|
128
|
+
catch { /* ignore */ }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// === Path B: no local agent.md → download from remote ===
|
|
132
|
+
const client = opts?.client ?? await createBareClient(aunPath);
|
|
133
|
+
const ownClient = !opts?.client;
|
|
134
|
+
try {
|
|
135
|
+
const raw = await client.auth.downloadAgentMd(aid);
|
|
136
|
+
if (!opts?.withVerification) {
|
|
137
|
+
// Persist without verification
|
|
138
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
139
|
+
fs.mkdirSync(aidDir, { recursive: true });
|
|
140
|
+
fs.writeFileSync(path.join(aidDir, 'agent.md'), raw, 'utf-8');
|
|
141
|
+
return raw;
|
|
142
|
+
}
|
|
143
|
+
const certPem = await obtainCertPem(aid, aunPath, client);
|
|
144
|
+
const verification = await verifyContent(raw, aid, certPem, client);
|
|
145
|
+
// Persist to local
|
|
146
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
147
|
+
fs.mkdirSync(aidDir, { recursive: true });
|
|
148
|
+
fs.writeFileSync(path.join(aidDir, 'agent.md'), raw, 'utf-8');
|
|
149
|
+
return { content: raw, verification };
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
if (ownClient)
|
|
153
|
+
try {
|
|
154
|
+
await client.close();
|
|
155
|
+
}
|
|
156
|
+
catch { /* ignore */ }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Upload agent.md: auto-sign + upload + sync to local file.
|
|
161
|
+
*/
|
|
162
|
+
export async function agentmdPut(content, opts) {
|
|
163
|
+
const aunPath = opts.aunPath ?? path.join(os.homedir(), '.aun');
|
|
164
|
+
const client = opts.client ?? await getAunClient(opts.aid, { aunPath });
|
|
165
|
+
const ownClient = !opts.client;
|
|
166
|
+
try {
|
|
167
|
+
let signed;
|
|
168
|
+
try {
|
|
169
|
+
signed = await client.auth.signAgentMd(content);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
signed = content;
|
|
173
|
+
}
|
|
174
|
+
await client.auth.uploadAgentMd(signed);
|
|
175
|
+
const aidDir = path.join(aunPath, 'AIDs', opts.aid);
|
|
176
|
+
fs.mkdirSync(aidDir, { recursive: true });
|
|
177
|
+
fs.writeFileSync(path.join(aidDir, 'agent.md'), signed, 'utf-8');
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
if (ownClient)
|
|
181
|
+
try {
|
|
182
|
+
await client.close();
|
|
183
|
+
}
|
|
184
|
+
catch { /* ignore */ }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { execFileSync } from 'child_process';
|
|
6
|
+
import { isWindows } from '../../utils/cross-platform.js';
|
|
7
|
+
/**
|
|
8
|
+
* Suppress SDK console logs (DEBUG/INFO/WARN) in CLI context.
|
|
9
|
+
* Call once at CLI entry point — NOT at module load time, to avoid
|
|
10
|
+
* affecting the daemon process which imports this module for slash commands.
|
|
11
|
+
*/
|
|
12
|
+
export function suppressSdkLogs() {
|
|
13
|
+
process.env.AUN_LOG_INI_DISABLE = '1';
|
|
14
|
+
const _origLog = console.log;
|
|
15
|
+
const _origInfo = console.info;
|
|
16
|
+
const _origWarn = console.warn;
|
|
17
|
+
const _origError = console.error;
|
|
18
|
+
const SDK_LOG_RE = /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]\[(?:DEBUG|INFO|WARN)\]/;
|
|
19
|
+
const SDK_ERROR_RE = /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]\[ERROR\]/;
|
|
20
|
+
console.log = (...args) => { if (typeof args[0] === 'string') {
|
|
21
|
+
if (SDK_LOG_RE.test(args[0]))
|
|
22
|
+
return;
|
|
23
|
+
if (SDK_ERROR_RE.test(args[0])) {
|
|
24
|
+
_origError(...args);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
} _origLog(...args); };
|
|
28
|
+
console.info = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
|
|
29
|
+
return; _origInfo(...args); };
|
|
30
|
+
console.warn = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
|
|
31
|
+
return; _origWarn(...args); };
|
|
32
|
+
}
|
|
33
|
+
// ==================== Constants ====================
|
|
34
|
+
export const MIN_AUN_CORE_SDK = [0, 2, 17];
|
|
35
|
+
export const AUN_CORE_SDK_PKG = '@agentunion/fastaun';
|
|
36
|
+
// ==================== SDK & Environment ====================
|
|
37
|
+
function compareVersion(a, min) {
|
|
38
|
+
const parts = a.split('.').map(n => parseInt(n, 10));
|
|
39
|
+
if (parts.length < 3 || parts.some(isNaN))
|
|
40
|
+
return false;
|
|
41
|
+
if (parts[0] !== min[0])
|
|
42
|
+
return parts[0] > min[0];
|
|
43
|
+
if (parts[1] !== min[1])
|
|
44
|
+
return parts[1] > min[1];
|
|
45
|
+
return parts[2] >= min[2];
|
|
46
|
+
}
|
|
47
|
+
export function isAunSdkVersionOk(version) {
|
|
48
|
+
return compareVersion(version, MIN_AUN_CORE_SDK);
|
|
49
|
+
}
|
|
50
|
+
export function resolveAunCoreSdkPkg() {
|
|
51
|
+
try {
|
|
52
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
53
|
+
while (true) {
|
|
54
|
+
const candidate = path.join(dir, 'node_modules', AUN_CORE_SDK_PKG, 'package.json');
|
|
55
|
+
if (fs.existsSync(candidate)) {
|
|
56
|
+
const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
|
|
57
|
+
if (data.name === AUN_CORE_SDK_PKG)
|
|
58
|
+
return { version: data.version, path: candidate };
|
|
59
|
+
}
|
|
60
|
+
const parent = path.dirname(dir);
|
|
61
|
+
if (parent === dir)
|
|
62
|
+
break;
|
|
63
|
+
dir = parent;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch { /* fall through */ }
|
|
67
|
+
try {
|
|
68
|
+
const npmCmd = isWindows ? 'npm.cmd' : 'npm';
|
|
69
|
+
const globalRoot = execFileSync(npmCmd, ['root', '-g'], {
|
|
70
|
+
encoding: 'utf-8', timeout: 10000, shell: isWindows,
|
|
71
|
+
}).trim();
|
|
72
|
+
const pkgPath = path.join(globalRoot, AUN_CORE_SDK_PKG, 'package.json');
|
|
73
|
+
if (fs.existsSync(pkgPath)) {
|
|
74
|
+
const data = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
75
|
+
return { version: data.version, path: pkgPath };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch { /* not found */ }
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
export async function ensureAunSdk() {
|
|
82
|
+
const installed = resolveAunCoreSdkPkg();
|
|
83
|
+
if (installed && isAunSdkVersionOk(installed.version))
|
|
84
|
+
return;
|
|
85
|
+
const { npmInstallGlobal } = await import('../../utils/npm-ops.js');
|
|
86
|
+
console.log(`正在安装 ${AUN_CORE_SDK_PKG}@latest...`);
|
|
87
|
+
await npmInstallGlobal(`${AUN_CORE_SDK_PKG}@latest`);
|
|
88
|
+
}
|
|
89
|
+
export function isAunSdkReady() {
|
|
90
|
+
const installed = resolveAunCoreSdkPkg();
|
|
91
|
+
return !!(installed && isAunSdkVersionOk(installed.version));
|
|
92
|
+
}
|
|
93
|
+
// ==================== CA Root ====================
|
|
94
|
+
export async function downloadCaRoot(aunPath, gatewayUrl, indent = '') {
|
|
95
|
+
const caDir = path.join(aunPath, 'CA', 'root');
|
|
96
|
+
const caCertPath = path.join(caDir, 'root.crt');
|
|
97
|
+
if (fs.existsSync(caCertPath))
|
|
98
|
+
return true;
|
|
99
|
+
if (!gatewayUrl)
|
|
100
|
+
return false;
|
|
101
|
+
try {
|
|
102
|
+
fs.mkdirSync(caDir, { recursive: true });
|
|
103
|
+
const gwHttp = gatewayUrl.replace(/^wss?:/, 'https:').replace(/\/aun$/, '');
|
|
104
|
+
const resp = await fetch(`${gwHttp}/pki/chain`, { redirect: 'follow' });
|
|
105
|
+
if (!resp.ok) {
|
|
106
|
+
console.warn(`${indent}⚠ CA 根证书下载失败: HTTP ${resp.status}`);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const body = await resp.text();
|
|
110
|
+
if (!body.includes('BEGIN CERTIFICATE')) {
|
|
111
|
+
console.warn(`${indent}⚠ CA 根证书响应内容无效,跳过写入`);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
fs.writeFileSync(caCertPath, body);
|
|
115
|
+
console.log(`${indent}✓ CA 根证书已下载`);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
console.warn(`${indent}⚠ CA 根证书下载失败: ${e},可稍后手动下载`);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ==================== AUNClient Factory ====================
|
|
124
|
+
export async function getAunClient(aid, opts) {
|
|
125
|
+
const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
|
|
126
|
+
const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
|
|
127
|
+
const { AUNClient } = await import('@agentunion/fastaun');
|
|
128
|
+
const clientOpts = { aun_path: aunPath, debug: false };
|
|
129
|
+
if (fs.existsSync(caCertPath))
|
|
130
|
+
clientOpts.root_ca_path = caCertPath;
|
|
131
|
+
const client = new AUNClient(clientOpts);
|
|
132
|
+
await client.auth.createAid({ aid });
|
|
133
|
+
return client;
|
|
134
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import crypto from 'crypto';
|
|
5
|
+
import { getAunClient, downloadCaRoot } from './client.js';
|
|
6
|
+
// ==================== Validation ====================
|
|
7
|
+
export function isValidAid(name) {
|
|
8
|
+
const labels = name.split('.');
|
|
9
|
+
return labels.length >= 3 && labels.every(l => /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(l));
|
|
10
|
+
}
|
|
11
|
+
// ==================== AID Operations ====================
|
|
12
|
+
export function aidList(aunPath) {
|
|
13
|
+
const aidsDir = path.join(aunPath ?? path.join(os.homedir(), '.aun'), 'AIDs');
|
|
14
|
+
if (!fs.existsSync(aidsDir))
|
|
15
|
+
return [];
|
|
16
|
+
const entries = fs.readdirSync(aidsDir, { withFileTypes: true });
|
|
17
|
+
return entries
|
|
18
|
+
.filter(e => e.isDirectory())
|
|
19
|
+
.map(e => ({
|
|
20
|
+
aid: e.name,
|
|
21
|
+
hasPrivateKey: fs.existsSync(path.join(aidsDir, e.name, 'private')),
|
|
22
|
+
hasAgentMd: fs.existsSync(path.join(aidsDir, e.name, 'agent.md')),
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
export async function aidCreate(aid, opts) {
|
|
26
|
+
const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
|
|
27
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
28
|
+
if (fs.existsSync(aidDir) && fs.existsSync(path.join(aidDir, 'private'))) {
|
|
29
|
+
const client = await getAunClient(aid, { aunPath });
|
|
30
|
+
return { aid, alreadyExisted: true, gateway: '', client };
|
|
31
|
+
}
|
|
32
|
+
const { AUNClient, GatewayDiscovery } = await import('@agentunion/fastaun');
|
|
33
|
+
let client = new AUNClient({ aun_path: aunPath });
|
|
34
|
+
try {
|
|
35
|
+
const result = await client.auth.createAid({ aid });
|
|
36
|
+
const gateway = result.gateway || '';
|
|
37
|
+
const caDownloaded = await downloadCaRoot(aunPath, gateway);
|
|
38
|
+
const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
|
|
39
|
+
if (caDownloaded && fs.existsSync(caCertPath)) {
|
|
40
|
+
try {
|
|
41
|
+
await client.close();
|
|
42
|
+
}
|
|
43
|
+
catch { /* ignore */ }
|
|
44
|
+
client = new AUNClient({ aun_path: aunPath, root_ca_path: caCertPath });
|
|
45
|
+
await client.auth.createAid({ aid });
|
|
46
|
+
}
|
|
47
|
+
let gatewayUrl = gateway;
|
|
48
|
+
if (!gatewayUrl) {
|
|
49
|
+
try {
|
|
50
|
+
const discovery = new GatewayDiscovery({});
|
|
51
|
+
gatewayUrl = await discovery.discover(`https://${aid}/.well-known/aun-gateway`);
|
|
52
|
+
}
|
|
53
|
+
catch { /* fall through */ }
|
|
54
|
+
}
|
|
55
|
+
if (gatewayUrl) {
|
|
56
|
+
client._gatewayUrl = gatewayUrl;
|
|
57
|
+
}
|
|
58
|
+
return { aid, alreadyExisted: false, gateway: gatewayUrl, client };
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
try {
|
|
62
|
+
await client.close();
|
|
63
|
+
}
|
|
64
|
+
catch { /* ignore */ }
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ==================== Show ====================
|
|
69
|
+
export function aidShow(aid, opts) {
|
|
70
|
+
const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
|
|
71
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
72
|
+
const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
|
|
73
|
+
const hasAgentMd = fs.existsSync(path.join(aidDir, 'agent.md'));
|
|
74
|
+
let certExpiresAt = null;
|
|
75
|
+
let certSubject = null;
|
|
76
|
+
const certPath = path.join(aidDir, 'public', 'cert.pem');
|
|
77
|
+
if (fs.existsSync(certPath)) {
|
|
78
|
+
try {
|
|
79
|
+
const pem = fs.readFileSync(certPath, 'utf-8');
|
|
80
|
+
const x509 = new crypto.X509Certificate(pem);
|
|
81
|
+
certExpiresAt = x509.validTo;
|
|
82
|
+
certSubject = x509.subject;
|
|
83
|
+
}
|
|
84
|
+
catch { /* ignore parse errors */ }
|
|
85
|
+
}
|
|
86
|
+
return { aid, hasPrivateKey, hasAgentMd, certExpiresAt, certSubject };
|
|
87
|
+
}
|
|
88
|
+
// ==================== Delete ====================
|
|
89
|
+
export function aidDelete(aid, opts) {
|
|
90
|
+
const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
|
|
91
|
+
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
92
|
+
if (!fs.existsSync(aidDir))
|
|
93
|
+
return false;
|
|
94
|
+
fs.rmSync(aidDir, { recursive: true, force: true });
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
// ==================== Lookup ====================
|
|
98
|
+
export async function aidLookup(aid) {
|
|
99
|
+
let gateway = '';
|
|
100
|
+
try {
|
|
101
|
+
const gwResp = await fetch(`https://${aid}/.well-known/aun-gateway`, { redirect: 'follow' });
|
|
102
|
+
if (gwResp.ok) {
|
|
103
|
+
const text = await gwResp.text();
|
|
104
|
+
// Response may be JSON with gateways array or plain URL
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(text.trim());
|
|
107
|
+
if (parsed.gateways?.[0]?.url) {
|
|
108
|
+
gateway = parsed.gateways[0].url;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
gateway = text.trim();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
gateway = text.trim();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch { /* ignore */ }
|
|
120
|
+
try {
|
|
121
|
+
const resp = await fetch(`https://${aid}/agent.md`, { redirect: 'follow' });
|
|
122
|
+
if (resp.ok) {
|
|
123
|
+
const content = await resp.text();
|
|
124
|
+
return { exists: true, aid, gateway, content };
|
|
125
|
+
}
|
|
126
|
+
return { exists: false, aid, gateway, error: `agent_md_not_found` };
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
return { exists: false, aid, gateway, error: String(e.message || e) };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { isValidAid, aidList, aidCreate, aidShow, aidDelete, aidLookup } from './identity.js';
|
|
2
|
+
export { buildInitialAgentMd, agentmdGet, agentmdPut } from './agentmd.js';
|
|
3
|
+
export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, getAunClient, suppressSdkLogs, } from './client.js';
|
|
@@ -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 };
|