@twinsearth/agent-universe 2.3.6 → 2.5.7
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/LICENSE +21 -0
- package/README.md +411 -0
- package/index.d.ts +5 -208
- package/index.js +5 -69
- package/js/index.d.ts +209 -0
- package/js/index.js +71 -0
- package/js/lib/aca.js +259 -0
- package/js/lib/dht.js +92 -0
- package/js/lib/keychain.js +102 -0
- package/js/lib/market.js +240 -0
- package/js/lib/mcp.js +101 -0
- package/js/lib/models.js +124 -0
- package/js/package-lock.json +16 -0
- package/js/package.json +43 -0
- package/js/test/test.js +224 -0
- package/lib/aca.js +3 -259
- package/lib/dht.js +3 -92
- package/lib/keychain.js +3 -74
- package/lib/market.js +4 -240
- package/lib/mcp.js +3 -101
- package/lib/models.js +3 -124
- package/package.json +14 -10
package/index.js
CHANGED
|
@@ -1,71 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Agent Universe(智能体宇宙)主入口
|
|
1
|
+
// Agent Universe - 智能体宇宙
|
|
3
2
|
// 群众化的 AGI 路线,去中心化的智能体共享 & 开源网络
|
|
3
|
+
//
|
|
4
|
+
// 根入口:re-export js/ 完整 SDK 实现。
|
|
5
|
+
// 另有 Rust 核心库 gsn-core(cd gsn-core && cargo build)与 Python SDK (aip-sdk-py)。
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
const { AgentCard, Task, TaskStatus } = require('./lib/models');
|
|
7
|
-
const { MemoryDHT, ShardedIndex, shardOf } = require('./lib/dht');
|
|
8
|
-
const { AgentMarket, Bid, Reputation, MIN_STAKE } = require('./lib/market');
|
|
9
|
-
const { McpHttpClient, McpError, MCP_PROTOCOL_VERSION } = require('./lib/mcp');
|
|
10
|
-
const aca = require('./lib/aca');
|
|
11
|
-
|
|
12
|
-
const version = '2.3.6';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* AgentUniverse 门面:聚合身份、网络、市场能力
|
|
16
|
-
*/
|
|
17
|
-
class AgentUniverse {
|
|
18
|
-
constructor() {
|
|
19
|
-
this.identity = Keypair.generate();
|
|
20
|
-
this.dht = new MemoryDHT();
|
|
21
|
-
this.market = new AgentMarket();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
get did() {
|
|
25
|
-
return this.identity.did;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 注册一张 AgentCard 到本地网络 */
|
|
29
|
-
register(card) {
|
|
30
|
-
this.dht.put(card.did, card);
|
|
31
|
-
return card;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** 发现某能力的 Agent */
|
|
35
|
-
discover(capability) {
|
|
36
|
-
return this.market.discoverBySkill(capability);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = {
|
|
41
|
-
version,
|
|
42
|
-
AgentUniverse,
|
|
43
|
-
Keypair,
|
|
44
|
-
AgentCard,
|
|
45
|
-
Task,
|
|
46
|
-
TaskStatus,
|
|
47
|
-
MemoryDHT,
|
|
48
|
-
ShardedIndex,
|
|
49
|
-
shardOf,
|
|
50
|
-
AgentMarket,
|
|
51
|
-
Bid,
|
|
52
|
-
Reputation,
|
|
53
|
-
MIN_STAKE,
|
|
54
|
-
// MCP
|
|
55
|
-
McpHttpClient,
|
|
56
|
-
McpError,
|
|
57
|
-
MCP_PROTOCOL_VERSION,
|
|
58
|
-
// ACA(跨语言对齐身份与协议对象)
|
|
59
|
-
AipIdentity: aca.AipIdentity,
|
|
60
|
-
buildManifest: aca.buildManifest,
|
|
61
|
-
buildEnvelope: aca.buildEnvelope,
|
|
62
|
-
buildMessage: aca.buildMessage,
|
|
63
|
-
buildReceipt: aca.buildReceipt,
|
|
64
|
-
handshakeMessage: aca.handshakeMessage,
|
|
65
|
-
proposalMessage: aca.proposalMessage,
|
|
66
|
-
receiptMessage: aca.receiptMessage,
|
|
67
|
-
verifyManifest: aca.verifyManifest,
|
|
68
|
-
verifyMessage: aca.verifyMessage,
|
|
69
|
-
verifyReceipt: aca.verifyReceipt,
|
|
70
|
-
verifyReceiptResult: aca.verifyReceiptResult,
|
|
71
|
-
};
|
|
7
|
+
module.exports = require('./js');
|
package/js/index.d.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Type definitions for @twinsearth/agent-universe
|
|
2
|
+
|
|
3
|
+
export const version: string;
|
|
4
|
+
export const MIN_STAKE: number;
|
|
5
|
+
|
|
6
|
+
export class Keypair {
|
|
7
|
+
publicKey: string;
|
|
8
|
+
privateKey: string;
|
|
9
|
+
did: string;
|
|
10
|
+
static generate(): Keypair;
|
|
11
|
+
sign(message: string | Buffer): string;
|
|
12
|
+
verify(message: string | Buffer, signatureHex: string): boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class AgentCard {
|
|
16
|
+
did: string;
|
|
17
|
+
name: string;
|
|
18
|
+
capabilities: string[];
|
|
19
|
+
skills: string[];
|
|
20
|
+
version: string;
|
|
21
|
+
stake: number;
|
|
22
|
+
reputation: number;
|
|
23
|
+
static new(opts: { did: string; name: string }): AgentCard;
|
|
24
|
+
withCapability(cap: string): this;
|
|
25
|
+
withSkill(skill: string): this;
|
|
26
|
+
hasCapability(cap: string): boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const TaskStatus: Record<string, string>;
|
|
30
|
+
|
|
31
|
+
export class Task {
|
|
32
|
+
taskId: string;
|
|
33
|
+
goal: string;
|
|
34
|
+
status: string;
|
|
35
|
+
assignee: string | null;
|
|
36
|
+
history: string[];
|
|
37
|
+
constructor(taskId: string, goal: string);
|
|
38
|
+
assign(did: string): this;
|
|
39
|
+
start(): this;
|
|
40
|
+
complete(): this;
|
|
41
|
+
verify(passed: boolean): this;
|
|
42
|
+
settle(): this;
|
|
43
|
+
readonly isTerminal: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class MemoryDHT {
|
|
47
|
+
put(key: string, value: unknown): void;
|
|
48
|
+
get(key: string): unknown;
|
|
49
|
+
has(key: string): boolean;
|
|
50
|
+
remove(key: string): boolean;
|
|
51
|
+
readonly size: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export class ShardedIndex {
|
|
55
|
+
constructor(numShards?: number);
|
|
56
|
+
put(key: string, value: unknown): number;
|
|
57
|
+
get(key: string): unknown;
|
|
58
|
+
has(key: string): boolean;
|
|
59
|
+
remove(key: string): boolean;
|
|
60
|
+
readonly size: number;
|
|
61
|
+
distribution(): number[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function shardOf(key: string, numShards: number): number;
|
|
65
|
+
|
|
66
|
+
export class Bid {
|
|
67
|
+
agentId: string;
|
|
68
|
+
price: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class Reputation {
|
|
72
|
+
quality: number;
|
|
73
|
+
speed: number;
|
|
74
|
+
honesty: number;
|
|
75
|
+
availability: number;
|
|
76
|
+
overall(): number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export class AgentMarket {
|
|
80
|
+
agents: Map<string, unknown>;
|
|
81
|
+
tasks: Map<string, Task>;
|
|
82
|
+
deposit(account: string, amount: number): void;
|
|
83
|
+
balance(account: string): number;
|
|
84
|
+
registerAgent(card: AgentCard, stake: number): boolean;
|
|
85
|
+
publishTask(taskId: string, goal: string, budget: number, requester: string): Task;
|
|
86
|
+
submitBid(taskId: string, agentId: string, price: number): boolean;
|
|
87
|
+
matchTask(taskId: string): Bid;
|
|
88
|
+
completeTask(taskId: string, passed?: boolean): Task;
|
|
89
|
+
settle(taskId: string): { paid: number; reason: string };
|
|
90
|
+
slash(agentId: string, amount: number, reason: string): unknown;
|
|
91
|
+
discoverBySkill(skill: string): AgentCard[];
|
|
92
|
+
leaderboard(limit?: number): unknown[];
|
|
93
|
+
conservationCheck(totalDeposits: number): {
|
|
94
|
+
conserved: boolean;
|
|
95
|
+
balanceSum: number;
|
|
96
|
+
totalSlashed: number;
|
|
97
|
+
expected: number;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class AgentUniverse {
|
|
102
|
+
identity: Keypair;
|
|
103
|
+
dht: MemoryDHT;
|
|
104
|
+
market: AgentMarket;
|
|
105
|
+
readonly did: string;
|
|
106
|
+
register(card: AgentCard): AgentCard;
|
|
107
|
+
discover(capability: string): AgentCard[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── MCP ──
|
|
111
|
+
export const MCP_PROTOCOL_VERSION: string;
|
|
112
|
+
|
|
113
|
+
export class McpError extends Error {
|
|
114
|
+
code: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export class McpHttpClient {
|
|
118
|
+
initialized: boolean;
|
|
119
|
+
constructor(baseUrl?: string, path?: string, timeout?: number);
|
|
120
|
+
initialize(): Promise<{
|
|
121
|
+
protocolVersion: string;
|
|
122
|
+
serverInfo: { name: string; version: string };
|
|
123
|
+
}>;
|
|
124
|
+
ping(): Promise<boolean>;
|
|
125
|
+
listTools(): Promise<
|
|
126
|
+
Array<{
|
|
127
|
+
name: string;
|
|
128
|
+
description?: string;
|
|
129
|
+
inputSchema: Record<string, unknown>;
|
|
130
|
+
}>
|
|
131
|
+
>;
|
|
132
|
+
callTool(
|
|
133
|
+
name: string,
|
|
134
|
+
args?: Record<string, unknown>
|
|
135
|
+
): Promise<{ content: Array<{ type: string; text: string }>; isError?: boolean }>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── ACA ──
|
|
139
|
+
export class AipIdentity {
|
|
140
|
+
did: string;
|
|
141
|
+
readonly publicKey: Buffer;
|
|
142
|
+
static generate(): AipIdentity;
|
|
143
|
+
static fromSeed(seed: Buffer | Uint8Array): AipIdentity;
|
|
144
|
+
signObject(obj: Record<string, unknown>): string;
|
|
145
|
+
signInto(obj: Record<string, unknown>): Record<string, unknown>;
|
|
146
|
+
static verifyObject(obj: Record<string, unknown>, rawPub: Buffer): boolean;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function buildManifest(
|
|
150
|
+
identity: AipIdentity,
|
|
151
|
+
name: string,
|
|
152
|
+
capabilities: string[],
|
|
153
|
+
opts?: Record<string, unknown>
|
|
154
|
+
): Record<string, unknown>;
|
|
155
|
+
|
|
156
|
+
export function buildEnvelope(
|
|
157
|
+
requesterDid: string,
|
|
158
|
+
capability: string,
|
|
159
|
+
outputSpec: string,
|
|
160
|
+
opts?: Record<string, unknown>
|
|
161
|
+
): Record<string, unknown>;
|
|
162
|
+
|
|
163
|
+
export function buildMessage(
|
|
164
|
+
identity: AipIdentity,
|
|
165
|
+
msgType: string,
|
|
166
|
+
toDid: string,
|
|
167
|
+
payload: unknown
|
|
168
|
+
): Record<string, unknown>;
|
|
169
|
+
|
|
170
|
+
export function buildReceipt(
|
|
171
|
+
identity: AipIdentity,
|
|
172
|
+
taskId: string,
|
|
173
|
+
result: Buffer,
|
|
174
|
+
opts?: Record<string, unknown>
|
|
175
|
+
): Record<string, unknown>;
|
|
176
|
+
|
|
177
|
+
export function handshakeMessage(
|
|
178
|
+
identity: AipIdentity,
|
|
179
|
+
manifest: Record<string, unknown>
|
|
180
|
+
): Record<string, unknown>;
|
|
181
|
+
|
|
182
|
+
export function proposalMessage(
|
|
183
|
+
identity: AipIdentity,
|
|
184
|
+
toDid: string,
|
|
185
|
+
envelope: Record<string, unknown>
|
|
186
|
+
): Record<string, unknown>;
|
|
187
|
+
|
|
188
|
+
export function receiptMessage(
|
|
189
|
+
identity: AipIdentity,
|
|
190
|
+
toDid: string,
|
|
191
|
+
receipt: Record<string, unknown>
|
|
192
|
+
): Record<string, unknown>;
|
|
193
|
+
|
|
194
|
+
export function verifyManifest(
|
|
195
|
+
manifest: Record<string, unknown>,
|
|
196
|
+
rawPub: Buffer
|
|
197
|
+
): boolean;
|
|
198
|
+
export function verifyMessage(
|
|
199
|
+
message: Record<string, unknown>,
|
|
200
|
+
rawPub: Buffer
|
|
201
|
+
): boolean;
|
|
202
|
+
export function verifyReceipt(
|
|
203
|
+
receipt: Record<string, unknown>,
|
|
204
|
+
rawPub: Buffer
|
|
205
|
+
): boolean;
|
|
206
|
+
export function verifyReceiptResult(
|
|
207
|
+
receipt: Record<string, unknown>,
|
|
208
|
+
result: Buffer
|
|
209
|
+
): boolean;
|
package/js/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// index.js
|
|
2
|
+
// Agent Universe(智能体宇宙)主入口
|
|
3
|
+
// 群众化的 AGI 路线,去中心化的智能体共享 & 开源网络
|
|
4
|
+
|
|
5
|
+
const { Keypair } = require('./lib/keychain');
|
|
6
|
+
const { AgentCard, Task, TaskStatus } = require('./lib/models');
|
|
7
|
+
const { MemoryDHT, ShardedIndex, shardOf } = require('./lib/dht');
|
|
8
|
+
const { AgentMarket, Bid, Reputation, MIN_STAKE } = require('./lib/market');
|
|
9
|
+
const { McpHttpClient, McpError, MCP_PROTOCOL_VERSION } = require('./lib/mcp');
|
|
10
|
+
const aca = require('./lib/aca');
|
|
11
|
+
|
|
12
|
+
const version = '2.5.7';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* AgentUniverse 门面:聚合身份、网络、市场能力
|
|
16
|
+
*/
|
|
17
|
+
class AgentUniverse {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.identity = Keypair.generate();
|
|
20
|
+
this.dht = new MemoryDHT();
|
|
21
|
+
this.market = new AgentMarket();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get did() {
|
|
25
|
+
return this.identity.did;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 注册一张 AgentCard 到本地网络 */
|
|
29
|
+
register(card) {
|
|
30
|
+
this.dht.put(card.did, card);
|
|
31
|
+
return card;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 发现某能力的 Agent */
|
|
35
|
+
discover(capability) {
|
|
36
|
+
return this.market.discoverBySkill(capability);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
version,
|
|
42
|
+
AgentUniverse,
|
|
43
|
+
Keypair,
|
|
44
|
+
AgentCard,
|
|
45
|
+
Task,
|
|
46
|
+
TaskStatus,
|
|
47
|
+
MemoryDHT,
|
|
48
|
+
ShardedIndex,
|
|
49
|
+
shardOf,
|
|
50
|
+
AgentMarket,
|
|
51
|
+
Bid,
|
|
52
|
+
Reputation,
|
|
53
|
+
MIN_STAKE,
|
|
54
|
+
// MCP
|
|
55
|
+
McpHttpClient,
|
|
56
|
+
McpError,
|
|
57
|
+
MCP_PROTOCOL_VERSION,
|
|
58
|
+
// ACA(跨语言对齐身份与协议对象)
|
|
59
|
+
AipIdentity: aca.AipIdentity,
|
|
60
|
+
buildManifest: aca.buildManifest,
|
|
61
|
+
buildEnvelope: aca.buildEnvelope,
|
|
62
|
+
buildMessage: aca.buildMessage,
|
|
63
|
+
buildReceipt: aca.buildReceipt,
|
|
64
|
+
handshakeMessage: aca.handshakeMessage,
|
|
65
|
+
proposalMessage: aca.proposalMessage,
|
|
66
|
+
receiptMessage: aca.receiptMessage,
|
|
67
|
+
verifyManifest: aca.verifyManifest,
|
|
68
|
+
verifyMessage: aca.verifyMessage,
|
|
69
|
+
verifyReceipt: aca.verifyReceipt,
|
|
70
|
+
verifyReceiptResult: aca.verifyReceiptResult,
|
|
71
|
+
};
|
package/js/lib/aca.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// lib/aca.js
|
|
2
|
+
// ACA(Agent Communication Architecture):与 Rust gsn-core / Python SDK
|
|
3
|
+
// 对齐的身份、规范签名与协议对象构造。零依赖,使用 node:crypto。
|
|
4
|
+
//
|
|
5
|
+
// 跨语言一致性:canonical 载荷为移除 signature 键后,紧凑、按 key 字典序、
|
|
6
|
+
// 不转义非 ASCII 的 JSON 字节,与 Rust serde_json(BTreeMap)、Python
|
|
7
|
+
// sort_keys 产出逐字节一致,因此三端可互验签名。
|
|
8
|
+
|
|
9
|
+
const crypto = require('node:crypto');
|
|
10
|
+
|
|
11
|
+
// Ed25519 PKCS#8 DER 固定前缀(16 字节),后接 32 字节种子
|
|
12
|
+
const PKCS8_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex');
|
|
13
|
+
|
|
14
|
+
// ── 枚举标签(与 Rust serde 变体名一致) ──
|
|
15
|
+
const MODE_SELF_REPORTED = 'SelfReported';
|
|
16
|
+
const LEVEL_L0 = 'L0Sample';
|
|
17
|
+
const PRIVACY_PUBLIC = 'Public';
|
|
18
|
+
const PRIORITY_NORMAL = 'Normal';
|
|
19
|
+
const STATUS_COMPLETED = 'Completed';
|
|
20
|
+
const MSG_HANDSHAKE = 'Handshake';
|
|
21
|
+
const MSG_TASK_PROPOSAL = 'TaskProposal';
|
|
22
|
+
const MSG_RECEIPT = 'Receipt';
|
|
23
|
+
|
|
24
|
+
function nowSec() {
|
|
25
|
+
return Math.floor(Date.now() / 1000);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 稳定 JSON 序列化:紧凑、对象键按字典序、非 ASCII 原样输出 */
|
|
29
|
+
function stableStringify(value) {
|
|
30
|
+
if (value === null || typeof value !== 'object') {
|
|
31
|
+
return JSON.stringify(value);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
return '[' + value.map(stableStringify).join(',') + ']';
|
|
35
|
+
}
|
|
36
|
+
const keys = Object.keys(value).sort();
|
|
37
|
+
return (
|
|
38
|
+
'{' +
|
|
39
|
+
keys
|
|
40
|
+
.map((k) => JSON.stringify(k) + ':' + stableStringify(value[k]))
|
|
41
|
+
.join(',') +
|
|
42
|
+
'}'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 规范待签名载荷:移除 signature 键,返回 Buffer */
|
|
47
|
+
function canonicalPayload(obj) {
|
|
48
|
+
const copy = { ...obj };
|
|
49
|
+
delete copy.signature;
|
|
50
|
+
return Buffer.from(stableStringify(copy), 'utf8');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function rawPublicFromKey(pubKey) {
|
|
54
|
+
return Buffer.from(pubKey.export({ format: 'jwk' }).x, 'base64url');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pubKeyFromRaw(raw) {
|
|
58
|
+
return crypto.createPublicKey({
|
|
59
|
+
key: { kty: 'OKP', crv: 'Ed25519', x: raw.toString('base64url') },
|
|
60
|
+
format: 'jwk',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 跨语言对齐的节点身份:Ed25519 + did:aip(原始 32 字节公钥指纹)
|
|
66
|
+
*/
|
|
67
|
+
class AipIdentity {
|
|
68
|
+
constructor(privKey, rawPub) {
|
|
69
|
+
this._priv = privKey;
|
|
70
|
+
this._rawPub = rawPub;
|
|
71
|
+
const hash = crypto.createHash('sha256').update(rawPub).digest('hex');
|
|
72
|
+
this.did = 'did:aip:' + hash.slice(0, 16);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static generate() {
|
|
76
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
|
|
77
|
+
return new AipIdentity(privateKey, rawPublicFromKey(publicKey));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static fromSeed(seed) {
|
|
81
|
+
const buf = Buffer.isBuffer(seed) ? seed : Buffer.from(seed);
|
|
82
|
+
if (buf.length !== 32) throw new Error('seed 必须为 32 字节');
|
|
83
|
+
const priv = crypto.createPrivateKey({
|
|
84
|
+
key: Buffer.concat([PKCS8_PREFIX, buf]),
|
|
85
|
+
format: 'der',
|
|
86
|
+
type: 'pkcs8',
|
|
87
|
+
});
|
|
88
|
+
const rawPub = rawPublicFromKey(crypto.createPublicKey(priv));
|
|
89
|
+
return new AipIdentity(priv, rawPub);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 32 字节原始公钥 */
|
|
93
|
+
get publicKey() {
|
|
94
|
+
return this._rawPub;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
signObject(obj) {
|
|
98
|
+
return crypto.sign(null, canonicalPayload(obj), this._priv).toString('hex');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
signInto(obj) {
|
|
102
|
+
obj.signature = this.signObject(obj);
|
|
103
|
+
return obj;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static verifyObject(obj, rawPub) {
|
|
107
|
+
const sigHex = obj && obj.signature;
|
|
108
|
+
if (!sigHex) return false;
|
|
109
|
+
let sig;
|
|
110
|
+
try {
|
|
111
|
+
sig = Buffer.from(sigHex, 'hex');
|
|
112
|
+
} catch (e) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
return crypto.verify(
|
|
117
|
+
null,
|
|
118
|
+
canonicalPayload(obj),
|
|
119
|
+
pubKeyFromRaw(rawPub),
|
|
120
|
+
sig
|
|
121
|
+
);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function buildManifest(identity, name, capabilities, opts = {}) {
|
|
129
|
+
const manifest = {
|
|
130
|
+
did: identity.did,
|
|
131
|
+
name,
|
|
132
|
+
version: opts.version || '2.5.7',
|
|
133
|
+
capabilities: [...capabilities],
|
|
134
|
+
endpoints: opts.endpoints || [],
|
|
135
|
+
hardware: {
|
|
136
|
+
cpu_cores: opts.cpuCores || 0,
|
|
137
|
+
memory_mb: opts.memoryMb || 0,
|
|
138
|
+
disk_free_gb: opts.diskFreeGb || 0,
|
|
139
|
+
gpu: null,
|
|
140
|
+
bandwidth_up_mbps: 0,
|
|
141
|
+
bandwidth_down_mbps: 0,
|
|
142
|
+
platform: opts.platform || 'desktop',
|
|
143
|
+
},
|
|
144
|
+
verification_modes: opts.verificationModes || [MODE_SELF_REPORTED],
|
|
145
|
+
stake: opts.stake || 0,
|
|
146
|
+
reputation_ref: null,
|
|
147
|
+
model_hash: null,
|
|
148
|
+
mcp_tool_count: opts.mcpToolCount || 0,
|
|
149
|
+
signature: '',
|
|
150
|
+
timestamp: nowSec(),
|
|
151
|
+
};
|
|
152
|
+
return identity.signInto(manifest);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildEnvelope(requesterDid, capability, outputSpec, opts = {}) {
|
|
156
|
+
return {
|
|
157
|
+
task_id: crypto.randomUUID(),
|
|
158
|
+
requester_did: requesterDid,
|
|
159
|
+
capability,
|
|
160
|
+
input_cid: opts.inputCid || '',
|
|
161
|
+
output_spec: outputSpec,
|
|
162
|
+
verification_level: opts.verificationLevel || LEVEL_L0,
|
|
163
|
+
privacy: opts.privacy || PRIVACY_PUBLIC,
|
|
164
|
+
budget: opts.budget || 0,
|
|
165
|
+
timeout_secs: opts.timeoutSecs || 300,
|
|
166
|
+
priority: opts.priority || PRIORITY_NORMAL,
|
|
167
|
+
mcp_tool: opts.mcpTool || null,
|
|
168
|
+
created_at: nowSec(),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function buildMessage(identity, msgType, toDid, payload) {
|
|
173
|
+
const message = {
|
|
174
|
+
message_id: crypto.randomUUID(),
|
|
175
|
+
msg_type: msgType,
|
|
176
|
+
from_did: identity.did,
|
|
177
|
+
to_did: toDid,
|
|
178
|
+
payload,
|
|
179
|
+
timestamp: nowSec(),
|
|
180
|
+
signature: '',
|
|
181
|
+
};
|
|
182
|
+
return identity.signInto(message);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function handshakeMessage(identity, manifest) {
|
|
186
|
+
return buildMessage(identity, MSG_HANDSHAKE, '*', manifest);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function proposalMessage(identity, toDid, envelope) {
|
|
190
|
+
return buildMessage(identity, MSG_TASK_PROPOSAL, toDid, envelope);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function buildReceipt(identity, taskId, result, opts = {}) {
|
|
194
|
+
const hash = crypto.createHash('sha256').update(result).digest();
|
|
195
|
+
const metering = Object.assign(
|
|
196
|
+
{
|
|
197
|
+
compute_ms: 0,
|
|
198
|
+
memory_peak_mb: 0,
|
|
199
|
+
bandwidth_mb: 0,
|
|
200
|
+
storage_bytes: 0,
|
|
201
|
+
energy_joules: 0,
|
|
202
|
+
},
|
|
203
|
+
opts.metering || {}
|
|
204
|
+
);
|
|
205
|
+
const receipt = {
|
|
206
|
+
task_id: taskId,
|
|
207
|
+
executor_did: identity.did,
|
|
208
|
+
result_cid: 'cid:' + hash.toString('hex').slice(0, 16),
|
|
209
|
+
result_hash: [...hash],
|
|
210
|
+
status: opts.status || STATUS_COMPLETED,
|
|
211
|
+
metering,
|
|
212
|
+
tee_quote: opts.teeQuote || null,
|
|
213
|
+
zk_proof: opts.zkProof || null,
|
|
214
|
+
completed_at: nowSec(),
|
|
215
|
+
signature: '',
|
|
216
|
+
};
|
|
217
|
+
return identity.signInto(receipt);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function receiptMessage(identity, toDid, receipt) {
|
|
221
|
+
return buildMessage(identity, MSG_RECEIPT, toDid, receipt);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function verifyManifest(manifest, rawPub) {
|
|
225
|
+
return AipIdentity.verifyObject(manifest, rawPub);
|
|
226
|
+
}
|
|
227
|
+
function verifyMessage(message, rawPub) {
|
|
228
|
+
return AipIdentity.verifyObject(message, rawPub);
|
|
229
|
+
}
|
|
230
|
+
function verifyReceipt(receipt, rawPub) {
|
|
231
|
+
return AipIdentity.verifyObject(receipt, rawPub);
|
|
232
|
+
}
|
|
233
|
+
function verifyReceiptResult(receipt, result) {
|
|
234
|
+
const hash = crypto.createHash('sha256').update(result).digest();
|
|
235
|
+
return deepEqualArray(receipt.result_hash, [...hash]);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function deepEqualArray(a, b) {
|
|
239
|
+
if (!Array.isArray(a) || a.length !== b.length) return false;
|
|
240
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = {
|
|
245
|
+
AipIdentity,
|
|
246
|
+
canonicalPayload,
|
|
247
|
+
stableStringify,
|
|
248
|
+
buildManifest,
|
|
249
|
+
buildEnvelope,
|
|
250
|
+
buildMessage,
|
|
251
|
+
buildReceipt,
|
|
252
|
+
handshakeMessage,
|
|
253
|
+
proposalMessage,
|
|
254
|
+
receiptMessage,
|
|
255
|
+
verifyManifest,
|
|
256
|
+
verifyMessage,
|
|
257
|
+
verifyReceipt,
|
|
258
|
+
verifyReceiptResult,
|
|
259
|
+
};
|
package/js/lib/dht.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// lib/dht.js
|
|
2
|
+
// MemoryDHT / ShardedIndex / shardOf(SHA-256 分片)
|
|
3
|
+
|
|
4
|
+
const crypto = require('node:crypto');
|
|
5
|
+
|
|
6
|
+
/** 计算 key 所属分片:SHA-256(key) 前 8 hex 转整数,对 numShards 取模 */
|
|
7
|
+
function shardOf(key, numShards) {
|
|
8
|
+
const hash = crypto.createHash('sha256').update(String(key)).digest('hex');
|
|
9
|
+
const prefix = parseInt(hash.slice(0, 8), 16);
|
|
10
|
+
return prefix % numShards;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** 内存 DHT(键值存储) */
|
|
14
|
+
class MemoryDHT {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.store = new Map();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
put(key, value) {
|
|
20
|
+
this.store.set(String(key), value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get(key) {
|
|
24
|
+
return this.store.get(String(key));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
has(key) {
|
|
28
|
+
return this.store.has(String(key));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
remove(key) {
|
|
32
|
+
return this.store.delete(String(key));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get size() {
|
|
36
|
+
return this.store.size;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** 按前缀查找 */
|
|
40
|
+
findByPrefix(prefix) {
|
|
41
|
+
const result = [];
|
|
42
|
+
for (const [k, v] of this.store) {
|
|
43
|
+
if (k.startsWith(prefix)) result.push({ key: k, value: v });
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 分片索引:把 key 均匀分布到 numShards 个分片 */
|
|
50
|
+
class ShardedIndex {
|
|
51
|
+
constructor(numShards = 16) {
|
|
52
|
+
this.numShards = numShards;
|
|
53
|
+
this.shards = [];
|
|
54
|
+
for (let i = 0; i < numShards; i++) this.shards.push(new Map());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
put(key, value) {
|
|
58
|
+
const s = shardOf(key, this.numShards);
|
|
59
|
+
this.shards[s].set(String(key), value);
|
|
60
|
+
return s;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get(key) {
|
|
64
|
+
const s = shardOf(key, this.numShards);
|
|
65
|
+
return this.shards[s].get(String(key));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
has(key) {
|
|
69
|
+
const s = shardOf(key, this.numShards);
|
|
70
|
+
return this.shards[s].has(String(key));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
remove(key) {
|
|
74
|
+
const s = shardOf(key, this.numShards);
|
|
75
|
+
return this.shards[s].delete(String(key));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
shardSize(i) {
|
|
79
|
+
return this.shards[i].size;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get size() {
|
|
83
|
+
return this.shards.reduce((sum, m) => sum + m.size, 0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** 各分片条目数(用于验证分布均匀性) */
|
|
87
|
+
distribution() {
|
|
88
|
+
return this.shards.map((m) => m.size);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { MemoryDHT, ShardedIndex, shardOf };
|