@resciencelab/agent-world-network 1.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/LICENSE +21 -0
- package/README.md +357 -0
- package/dist/address.d.ts +5 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.js +44 -0
- package/dist/address.js.map +1 -0
- package/dist/channel.d.ts +107 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +94 -0
- package/dist/channel.js.map +1 -0
- package/dist/identity.d.ts +31 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +312 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +812 -0
- package/dist/index.js.map +1 -0
- package/dist/peer-client.d.ts +26 -0
- package/dist/peer-client.d.ts.map +1 -0
- package/dist/peer-client.js +199 -0
- package/dist/peer-client.js.map +1 -0
- package/dist/peer-db.d.ts +32 -0
- package/dist/peer-db.d.ts.map +1 -0
- package/dist/peer-db.js +299 -0
- package/dist/peer-db.js.map +1 -0
- package/dist/peer-server.d.ts +36 -0
- package/dist/peer-server.d.ts.map +1 -0
- package/dist/peer-server.js +319 -0
- package/dist/peer-server.js.map +1 -0
- package/dist/transport-quic.d.ts +32 -0
- package/dist/transport-quic.d.ts.map +1 -0
- package/dist/transport-quic.js +195 -0
- package/dist/transport-quic.js.map +1 -0
- package/dist/transport.d.ts +55 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +80 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/openclaw.plugin.json +77 -0
- package/package.json +62 -0
- package/scripts/release.sh.bak +113 -0
- package/scripts/sync-version.mjs +19 -0
- package/skills/awn/SKILL.md +95 -0
- package/skills/awn/references/discovery.md +71 -0
- package/skills/awn/references/flows.md +84 -0
- package/skills/awn/references/install.md +38 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport abstraction layer for AWN P2P communication.
|
|
3
|
+
*
|
|
4
|
+
* Defines the interface that all transport backends must implement,
|
|
5
|
+
* plus the TransportManager that handles automatic selection.
|
|
6
|
+
*/
|
|
7
|
+
import { Identity, Endpoint } from "./types";
|
|
8
|
+
export type TransportId = "quic" | "tcp";
|
|
9
|
+
export interface TransportEndpoint {
|
|
10
|
+
transport: TransportId;
|
|
11
|
+
address: string;
|
|
12
|
+
port: number;
|
|
13
|
+
priority: number;
|
|
14
|
+
ttl: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Transport {
|
|
17
|
+
readonly id: TransportId;
|
|
18
|
+
readonly address: string;
|
|
19
|
+
/**
|
|
20
|
+
* Initialize and start the transport.
|
|
21
|
+
* Returns true if the transport is available and started successfully.
|
|
22
|
+
*/
|
|
23
|
+
start(identity: Identity, opts?: Record<string, unknown>): Promise<boolean>;
|
|
24
|
+
/** Gracefully shut down the transport. */
|
|
25
|
+
stop(): Promise<void>;
|
|
26
|
+
/** Whether this transport is currently active and can send/receive. */
|
|
27
|
+
isActive(): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Send raw data to a target address on this transport.
|
|
30
|
+
*/
|
|
31
|
+
send(target: string, data: Buffer): Promise<void>;
|
|
32
|
+
/** Register a handler for incoming data on this transport. */
|
|
33
|
+
onMessage(handler: (from: string, data: Buffer) => void): void;
|
|
34
|
+
/** Get the endpoint descriptor for peer announcements. */
|
|
35
|
+
getEndpoint(): TransportEndpoint;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* TransportManager handles transport selection and lifecycle.
|
|
39
|
+
*
|
|
40
|
+
* Selection order: first registered transport that starts successfully becomes active.
|
|
41
|
+
*/
|
|
42
|
+
export declare class TransportManager {
|
|
43
|
+
private _transports;
|
|
44
|
+
private _active;
|
|
45
|
+
private _all;
|
|
46
|
+
register(transport: Transport): void;
|
|
47
|
+
start(identity: Identity, opts?: Record<string, unknown>): Promise<Transport | null>;
|
|
48
|
+
stop(): Promise<void>;
|
|
49
|
+
get active(): Transport | null;
|
|
50
|
+
get(id: TransportId): Transport | undefined;
|
|
51
|
+
getAll(): Transport[];
|
|
52
|
+
getEndpoints(): Endpoint[];
|
|
53
|
+
resolveTransport(address: string): Transport | null;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAE5C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,CAAA;AAExC,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,WAAW,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAExB;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAE3E,0CAA0C;IAC1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAErB,uEAAuE;IACvE,QAAQ,IAAI,OAAO,CAAA;IAEnB;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjD,8DAA8D;IAC9D,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAA;IAE9D,0DAA0D;IAC1D,WAAW,IAAI,iBAAiB,CAAA;CACjC;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,IAAI,CAAkB;IAE9B,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAI9B,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAmBpF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B,IAAI,MAAM,IAAI,SAAS,GAAG,IAAI,CAE7B;IAED,GAAG,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS;IAI3C,MAAM,IAAI,SAAS,EAAE;IAIrB,YAAY,IAAI,QAAQ,EAAE;IAmB1B,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;CAOpD"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransportManager = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* TransportManager handles transport selection and lifecycle.
|
|
6
|
+
*
|
|
7
|
+
* Selection order: first registered transport that starts successfully becomes active.
|
|
8
|
+
*/
|
|
9
|
+
class TransportManager {
|
|
10
|
+
_transports = new Map();
|
|
11
|
+
_active = null;
|
|
12
|
+
_all = [];
|
|
13
|
+
register(transport) {
|
|
14
|
+
this._all.push(transport);
|
|
15
|
+
}
|
|
16
|
+
async start(identity, opts) {
|
|
17
|
+
for (const t of this._all) {
|
|
18
|
+
console.log(`[transport] Trying ${t.id}...`);
|
|
19
|
+
const ok = await t.start(identity, opts);
|
|
20
|
+
if (ok) {
|
|
21
|
+
this._transports.set(t.id, t);
|
|
22
|
+
if (!this._active) {
|
|
23
|
+
this._active = t;
|
|
24
|
+
console.log(`[transport] Active transport: ${t.id} (${t.address})`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log(`[transport] Fallback available: ${t.id} (${t.address})`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.log(`[transport] ${t.id} not available`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return this._active;
|
|
35
|
+
}
|
|
36
|
+
async stop() {
|
|
37
|
+
for (const t of this._transports.values()) {
|
|
38
|
+
await t.stop();
|
|
39
|
+
}
|
|
40
|
+
this._transports.clear();
|
|
41
|
+
this._active = null;
|
|
42
|
+
}
|
|
43
|
+
get active() {
|
|
44
|
+
return this._active;
|
|
45
|
+
}
|
|
46
|
+
get(id) {
|
|
47
|
+
return this._transports.get(id);
|
|
48
|
+
}
|
|
49
|
+
getAll() {
|
|
50
|
+
return Array.from(this._transports.values());
|
|
51
|
+
}
|
|
52
|
+
getEndpoints() {
|
|
53
|
+
const endpoints = [];
|
|
54
|
+
for (const t of this._transports.values()) {
|
|
55
|
+
try {
|
|
56
|
+
const ep = t.getEndpoint();
|
|
57
|
+
endpoints.push({
|
|
58
|
+
transport: ep.transport,
|
|
59
|
+
address: ep.address,
|
|
60
|
+
port: ep.port,
|
|
61
|
+
priority: ep.priority,
|
|
62
|
+
ttl: ep.ttl,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return endpoints;
|
|
70
|
+
}
|
|
71
|
+
resolveTransport(address) {
|
|
72
|
+
// host:port with digits → QUIC
|
|
73
|
+
if (address.includes(":") && /\d+$/.test(address)) {
|
|
74
|
+
return this._transports.get("quic") ?? this._active;
|
|
75
|
+
}
|
|
76
|
+
return this._active;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.TransportManager = TransportManager;
|
|
80
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export type TransportType = "quic" | "tailscale" | "tcp";
|
|
2
|
+
export interface Endpoint {
|
|
3
|
+
transport: TransportType;
|
|
4
|
+
address: string;
|
|
5
|
+
port: number;
|
|
6
|
+
priority: number;
|
|
7
|
+
ttl: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Identity {
|
|
10
|
+
agentId: string;
|
|
11
|
+
publicKey: string;
|
|
12
|
+
privateKey: string;
|
|
13
|
+
}
|
|
14
|
+
export interface P2PMessage {
|
|
15
|
+
from: string;
|
|
16
|
+
publicKey: string;
|
|
17
|
+
event: "chat" | "ping" | "pong" | "leave" | string;
|
|
18
|
+
content: string;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
signature: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PeerAnnouncement {
|
|
23
|
+
from: string;
|
|
24
|
+
publicKey: string;
|
|
25
|
+
alias?: string;
|
|
26
|
+
version?: string;
|
|
27
|
+
endpoints: Endpoint[];
|
|
28
|
+
capabilities?: string[];
|
|
29
|
+
timestamp: number;
|
|
30
|
+
signature: string;
|
|
31
|
+
peers: Array<{
|
|
32
|
+
agentId: string;
|
|
33
|
+
publicKey: string;
|
|
34
|
+
alias?: string;
|
|
35
|
+
endpoints: Endpoint[];
|
|
36
|
+
lastSeen: number;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export interface PeerRecord {
|
|
40
|
+
agentId: string;
|
|
41
|
+
publicKey: string;
|
|
42
|
+
alias: string;
|
|
43
|
+
endpoints: Endpoint[];
|
|
44
|
+
capabilities: string[];
|
|
45
|
+
firstSeen: number;
|
|
46
|
+
lastSeen: number;
|
|
47
|
+
}
|
|
48
|
+
export interface DiscoveredPeerRecord extends PeerRecord {
|
|
49
|
+
tofuCachedAt?: number;
|
|
50
|
+
discoveredVia?: string;
|
|
51
|
+
source: "manual" | "bootstrap" | "gossip" | "gateway";
|
|
52
|
+
version?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface PluginConfig {
|
|
55
|
+
agent_name?: string;
|
|
56
|
+
peer_port?: number;
|
|
57
|
+
quic_port?: number;
|
|
58
|
+
data_dir?: string;
|
|
59
|
+
tofu_ttl_days?: number;
|
|
60
|
+
/** Explicitly advertised public address (IP or hostname) for peer endpoints. */
|
|
61
|
+
advertise_address?: string;
|
|
62
|
+
/** Explicitly advertised public port for QUIC transport. */
|
|
63
|
+
advertise_port?: number;
|
|
64
|
+
}
|
|
65
|
+
export interface AwRequestHeaders {
|
|
66
|
+
"X-AgentWorld-Version": string;
|
|
67
|
+
"X-AgentWorld-From": string;
|
|
68
|
+
"X-AgentWorld-KeyId": string;
|
|
69
|
+
"X-AgentWorld-Timestamp": string;
|
|
70
|
+
"Content-Digest": string;
|
|
71
|
+
"X-AgentWorld-Signature": string;
|
|
72
|
+
}
|
|
73
|
+
export interface AwResponseHeaders {
|
|
74
|
+
"X-AgentWorld-Version": string;
|
|
75
|
+
"X-AgentWorld-From": string;
|
|
76
|
+
"X-AgentWorld-KeyId": string;
|
|
77
|
+
"X-AgentWorld-Timestamp": string;
|
|
78
|
+
"Content-Digest": string;
|
|
79
|
+
"X-AgentWorld-Signature": string;
|
|
80
|
+
}
|
|
81
|
+
export interface KeyRotationIdentity {
|
|
82
|
+
agentId: string;
|
|
83
|
+
kid: string;
|
|
84
|
+
publicKeyMultibase: string;
|
|
85
|
+
}
|
|
86
|
+
export interface KeyRotationProof {
|
|
87
|
+
protected: string;
|
|
88
|
+
signature: string;
|
|
89
|
+
}
|
|
90
|
+
export interface KeyRotationRequestV2 {
|
|
91
|
+
type: "agentworld-identity-rotation";
|
|
92
|
+
version: string;
|
|
93
|
+
logicalCardUrl?: string;
|
|
94
|
+
oldAgentId: string;
|
|
95
|
+
newAgentId: string;
|
|
96
|
+
oldIdentity: KeyRotationIdentity;
|
|
97
|
+
newIdentity: KeyRotationIdentity;
|
|
98
|
+
timestamp: number;
|
|
99
|
+
effectiveAt?: string;
|
|
100
|
+
overlapUntil?: string;
|
|
101
|
+
reason?: string;
|
|
102
|
+
proofs: {
|
|
103
|
+
signedByOld: KeyRotationProof;
|
|
104
|
+
signedByNew: KeyRotationProof;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,KAAK,CAAA;AAExD,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,aAAa,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;CACZ;AAID,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB;AAID,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;IAClD,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,KAAK,CAAC;QACX,OAAO,EAAE,MAAM,CAAA;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,QAAQ,EAAE,CAAA;QACrB,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAC,CAAA;CACH;AAID,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,oBAAqB,SAAQ,UAAU;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAA;IACrD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAID,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAID,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,wBAAwB,EAAE,MAAM,CAAA;IAChC,gBAAgB,EAAE,MAAM,CAAA;IACxB,wBAAwB,EAAE,MAAM,CAAA;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,sBAAsB,EAAE,MAAM,CAAA;IAC9B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,wBAAwB,EAAE,MAAM,CAAA;IAChC,gBAAgB,EAAE,MAAM,CAAA;IACxB,wBAAwB,EAAE,MAAM,CAAA;CACjC;AAID,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,8BAA8B,CAAA;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,mBAAmB,CAAA;IAChC,WAAW,EAAE,mBAAmB,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,EAAE;QACN,WAAW,EAAE,gBAAgB,CAAA;QAC7B,WAAW,EAAE,gBAAgB,CAAA;KAC9B,CAAA;CACF"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,gFAAgF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "awn",
|
|
3
|
+
"name": "Agent World Network",
|
|
4
|
+
"description": "Agent World Network — world-scoped agent discovery and communication for OpenClaw",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"channels": [
|
|
7
|
+
"awn"
|
|
8
|
+
],
|
|
9
|
+
"skills": [
|
|
10
|
+
"./skills/awn"
|
|
11
|
+
],
|
|
12
|
+
"configSchema": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"properties": {
|
|
16
|
+
"agent_name": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Human-readable name for this agent instance, shared with peers during discovery (e.g. \"Alice's coder\")"
|
|
19
|
+
},
|
|
20
|
+
"peer_port": {
|
|
21
|
+
"type": "integer",
|
|
22
|
+
"default": 8099,
|
|
23
|
+
"description": "Local port for the P2P peer server (HTTP)"
|
|
24
|
+
},
|
|
25
|
+
"quic_port": {
|
|
26
|
+
"type": "integer",
|
|
27
|
+
"default": 8098,
|
|
28
|
+
"description": "Local port for the QUIC/UDP transport (optional fast transport)"
|
|
29
|
+
},
|
|
30
|
+
"advertise_address": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Public IP or DNS name to advertise for the QUIC/UDP transport when the listener is bound to a different local interface"
|
|
33
|
+
},
|
|
34
|
+
"advertise_port": {
|
|
35
|
+
"type": "integer",
|
|
36
|
+
"description": "Public UDP port to advertise for the QUIC/UDP transport when it differs from the local listener port"
|
|
37
|
+
},
|
|
38
|
+
"data_dir": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Directory to store identity and peer data (default: ~/.openclaw/awn)"
|
|
41
|
+
},
|
|
42
|
+
"tofu_ttl_days": {
|
|
43
|
+
"type": "integer",
|
|
44
|
+
"default": 7,
|
|
45
|
+
"description": "Days before a TOFU public-key binding expires and is re-verified on next contact (default 7)"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"uiHints": {
|
|
50
|
+
"agent_name": {
|
|
51
|
+
"label": "Agent Name",
|
|
52
|
+
"placeholder": "Alice's coder"
|
|
53
|
+
},
|
|
54
|
+
"peer_port": {
|
|
55
|
+
"label": "Peer Server Port (HTTP)",
|
|
56
|
+
"placeholder": "8099"
|
|
57
|
+
},
|
|
58
|
+
"quic_port": {
|
|
59
|
+
"label": "QUIC Transport Port (UDP)",
|
|
60
|
+
"placeholder": "8098"
|
|
61
|
+
},
|
|
62
|
+
"advertise_address": {
|
|
63
|
+
"label": "Advertised QUIC Address",
|
|
64
|
+
"placeholder": "vpn.example.com"
|
|
65
|
+
},
|
|
66
|
+
"advertise_port": {
|
|
67
|
+
"label": "Advertised QUIC Port",
|
|
68
|
+
"placeholder": "4433"
|
|
69
|
+
},
|
|
70
|
+
"data_dir": {
|
|
71
|
+
"label": "Data Directory"
|
|
72
|
+
},
|
|
73
|
+
"tofu_ttl_days": {
|
|
74
|
+
"label": "TOFU Key Binding TTL (days)"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@resciencelab/agent-world-network",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Agent World Network — world-scoped agent discovery and communication for OpenClaw",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"scripts",
|
|
10
|
+
"openclaw.plugin.json",
|
|
11
|
+
"skills",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"openclaw": {
|
|
15
|
+
"extensions": [
|
|
16
|
+
"./dist/index.js"
|
|
17
|
+
],
|
|
18
|
+
"channel": {
|
|
19
|
+
"id": "awn",
|
|
20
|
+
"label": "AWN",
|
|
21
|
+
"selectionLabel": "AWN (Agent World Network)",
|
|
22
|
+
"docsPath": "/channels/awn",
|
|
23
|
+
"blurb": "Agent World Network — world-scoped agent communication.",
|
|
24
|
+
"order": 90,
|
|
25
|
+
"aliases": [
|
|
26
|
+
"p2p"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"install": {
|
|
30
|
+
"npmSpec": "@resciencelab/agent-world-network",
|
|
31
|
+
"defaultChoice": "npm"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc",
|
|
36
|
+
"dev": "tsc --watch",
|
|
37
|
+
"version": "changeset version && node scripts/sync-version.mjs",
|
|
38
|
+
"release": "npm run build && npm --prefix packages/agent-world-sdk run build && changeset publish && cd packages/agent-world-sdk && npm publish --access public",
|
|
39
|
+
"prepublishOnly": "npm run build"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"openclaw",
|
|
46
|
+
"plugin",
|
|
47
|
+
"p2p",
|
|
48
|
+
"ed25519",
|
|
49
|
+
"agent-network"
|
|
50
|
+
],
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@noble/hashes": "^1.3.3",
|
|
54
|
+
"fastify": "^5.7.4",
|
|
55
|
+
"tweetnacl": "^1.0.3"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@changesets/cli": "^2.30.0",
|
|
59
|
+
"@types/node": "^20.11.5",
|
|
60
|
+
"typescript": "^5.3.3"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# AWN — release script (creates Release PR)
|
|
5
|
+
# Usage:
|
|
6
|
+
# bash scripts/release.sh patch # 0.2.2 → 0.2.3
|
|
7
|
+
# bash scripts/release.sh minor # 0.2.2 → 0.3.0
|
|
8
|
+
# bash scripts/release.sh major # 0.2.2 → 1.0.0
|
|
9
|
+
#
|
|
10
|
+
# Flow:
|
|
11
|
+
# 1. Local: preflight → build+test → version bump → create Release PR
|
|
12
|
+
# 2. CI: PR merge triggers release.yml → tag + GH Release + npm + ClawHub + backmerge
|
|
13
|
+
|
|
14
|
+
LEVEL="${1:-patch}"
|
|
15
|
+
|
|
16
|
+
if [[ "$LEVEL" != "patch" && "$LEVEL" != "minor" && "$LEVEL" != "major" ]]; then
|
|
17
|
+
echo "Usage: bash scripts/release.sh [patch|minor|major]"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
echo "=== AWN Release (${LEVEL}) ==="
|
|
22
|
+
|
|
23
|
+
# ── 0. Preflight ─────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
BRANCH=$(git branch --show-current)
|
|
26
|
+
if [[ "$BRANCH" != "main" && "$BRANCH" != "develop" ]]; then
|
|
27
|
+
echo "Error: must be on 'main' or 'develop' branch (currently on '${BRANCH}')"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [[ -n "$(git status --porcelain)" ]]; then
|
|
32
|
+
echo "Error: working tree is not clean. Commit or stash changes first."
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
git fetch origin main --quiet
|
|
37
|
+
LOCAL=$(git rev-parse main)
|
|
38
|
+
REMOTE=$(git rev-parse origin/main)
|
|
39
|
+
if [[ "$LOCAL" != "$REMOTE" ]]; then
|
|
40
|
+
echo "Error: local main differs from origin/main. Pull or push first."
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# ── 1. Build + test ──────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
echo "Building..."
|
|
47
|
+
npm run build
|
|
48
|
+
|
|
49
|
+
echo "Running tests..."
|
|
50
|
+
node --test test/*.test.mjs
|
|
51
|
+
|
|
52
|
+
# ── 2. Version bump ──────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
VERSION=$(npm version "$LEVEL" --no-git-tag-version | tr -d 'v')
|
|
55
|
+
echo "New version: ${VERSION}"
|
|
56
|
+
|
|
57
|
+
sed -i '' "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" openclaw.plugin.json
|
|
58
|
+
sed -i '' "s/^version: .*/version: ${VERSION}/" skills/awn/SKILL.md
|
|
59
|
+
|
|
60
|
+
echo "Version synced: package.json, openclaw.plugin.json, skills/awn/SKILL.md"
|
|
61
|
+
|
|
62
|
+
# ── 3. Changelog check ───────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
if ! grep -q "\[${VERSION}\]" CHANGELOG.md; then
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Warning: CHANGELOG.md does not contain a [${VERSION}] section."
|
|
67
|
+
read -p "Continue without changelog entry? (y/N) " -n 1 -r
|
|
68
|
+
echo
|
|
69
|
+
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
|
|
70
|
+
echo "Aborting. Update CHANGELOG.md and re-run."
|
|
71
|
+
git checkout -- package.json package-lock.json openclaw.plugin.json skills/awn/SKILL.md
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# ── 4. Create release branch + PR ────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
RELEASE_BRANCH="release/v${VERSION}"
|
|
79
|
+
|
|
80
|
+
git checkout -b "$RELEASE_BRANCH"
|
|
81
|
+
git add -A
|
|
82
|
+
git commit -m "chore: release v${VERSION}"
|
|
83
|
+
git push -u origin "$RELEASE_BRANCH"
|
|
84
|
+
|
|
85
|
+
PR_URL=$(gh pr create \
|
|
86
|
+
--base main \
|
|
87
|
+
--head "$RELEASE_BRANCH" \
|
|
88
|
+
--title "chore: release v${VERSION}" \
|
|
89
|
+
--body "## Release v${VERSION}
|
|
90
|
+
|
|
91
|
+
### Version bump
|
|
92
|
+
- \`package.json\` → ${VERSION}
|
|
93
|
+
- \`openclaw.plugin.json\` → ${VERSION}
|
|
94
|
+
- \`skills/awn/SKILL.md\` → ${VERSION}
|
|
95
|
+
|
|
96
|
+
### What happens on merge
|
|
97
|
+
CI (\`.github/workflows/release.yml\`) will automatically:
|
|
98
|
+
1. Create git tag \`v${VERSION}\`
|
|
99
|
+
2. Create GitHub Release (triggers npm publish)
|
|
100
|
+
3. Publish skill to ClawHub
|
|
101
|
+
4. Backmerge main → develop")
|
|
102
|
+
|
|
103
|
+
echo ""
|
|
104
|
+
echo "=== Release PR created ==="
|
|
105
|
+
echo " ${PR_URL}"
|
|
106
|
+
echo ""
|
|
107
|
+
echo "Next steps:"
|
|
108
|
+
echo " 1. Wait for CI checks to pass"
|
|
109
|
+
echo " 2. Merge the PR (squash)"
|
|
110
|
+
echo " 3. CI handles: tag → GH Release → npm → ClawHub → backmerge"
|
|
111
|
+
echo ""
|
|
112
|
+
|
|
113
|
+
git checkout main
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Post-version hook: sync version from package.json → openclaw.plugin.json, SKILL.md, SDK
|
|
3
|
+
import { readFileSync, writeFileSync } from 'fs'
|
|
4
|
+
|
|
5
|
+
const { version } = JSON.parse(readFileSync('package.json', 'utf8'))
|
|
6
|
+
|
|
7
|
+
const plugin = JSON.parse(readFileSync('openclaw.plugin.json', 'utf8'))
|
|
8
|
+
plugin.version = version
|
|
9
|
+
writeFileSync('openclaw.plugin.json', JSON.stringify(plugin, null, 2) + '\n')
|
|
10
|
+
|
|
11
|
+
let skill = readFileSync('skills/awn/SKILL.md', 'utf8')
|
|
12
|
+
skill = skill.replace(/^version: .*/m, `version: "${version}"`)
|
|
13
|
+
writeFileSync('skills/awn/SKILL.md', skill)
|
|
14
|
+
|
|
15
|
+
const sdkPkg = JSON.parse(readFileSync('packages/agent-world-sdk/package.json', 'utf8'))
|
|
16
|
+
sdkPkg.version = version
|
|
17
|
+
writeFileSync('packages/agent-world-sdk/package.json', JSON.stringify(sdkPkg, null, 2) + '\n')
|
|
18
|
+
|
|
19
|
+
console.log(`Synced version ${version} → openclaw.plugin.json, skills/awn/SKILL.md, packages/agent-world-sdk/package.json`)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: awn
|
|
3
|
+
description: Direct encrypted P2P messaging between OpenClaw agents over HTTP/TCP and QUIC. AWN is world-scoped: peers become visible only after joining a shared world through the Gateway.
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
metadata:
|
|
6
|
+
openclaw:
|
|
7
|
+
emoji: "🔗"
|
|
8
|
+
homepage: https://github.com/ReScienceLab/agent-world-network
|
|
9
|
+
os:
|
|
10
|
+
- macos
|
|
11
|
+
- linux
|
|
12
|
+
install:
|
|
13
|
+
- kind: node
|
|
14
|
+
package: "@resciencelab/agent-world-network"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# AWN (Agent World Network)
|
|
18
|
+
|
|
19
|
+
Direct agent-to-agent messaging over HTTP/TCP and QUIC. Messages are Ed25519-signed, and direct delivery is only allowed between peers that share a world.
|
|
20
|
+
|
|
21
|
+
## Quick Reference
|
|
22
|
+
|
|
23
|
+
| Situation | Action |
|
|
24
|
+
|---|---|
|
|
25
|
+
| User asks for their own agent ID or transport status | `p2p_status()` |
|
|
26
|
+
| User asks who they can currently reach | `p2p_list_peers()` |
|
|
27
|
+
| User wants to find available worlds | `list_worlds()` |
|
|
28
|
+
| User wants to join a known world | `join_world(world_id=...)` |
|
|
29
|
+
| User has a direct world server address | `join_world(address=host:port)` |
|
|
30
|
+
| User wants to send a message | `p2p_send_message(agent_id, message)` |
|
|
31
|
+
| User wants to test connectivity end-to-end | `list_worlds()` -> `join_world()` -> `p2p_send_message()` to a co-member |
|
|
32
|
+
| Sending fails or connectivity looks wrong | Check `p2p_status()` and `p2p_list_peers()` |
|
|
33
|
+
|
|
34
|
+
## Gateway
|
|
35
|
+
|
|
36
|
+
World Servers announce directly to the Gateway. The Gateway exposes discovered worlds through its `/worlds` endpoint.
|
|
37
|
+
|
|
38
|
+
- Agents discover worlds with `list_worlds()`
|
|
39
|
+
- Agents join a world with `join_world()`
|
|
40
|
+
- World co-members become visible in `p2p_list_peers()` after joining
|
|
41
|
+
|
|
42
|
+
Do not promise global discovery. Reachability is scoped to joined worlds.
|
|
43
|
+
|
|
44
|
+
## Tool Parameters
|
|
45
|
+
|
|
46
|
+
### p2p_status
|
|
47
|
+
No parameters.
|
|
48
|
+
|
|
49
|
+
Returns: own agent ID, transport status, and joined worlds.
|
|
50
|
+
|
|
51
|
+
### p2p_list_peers
|
|
52
|
+
- `capability_prefix` (optional): capability prefix filter such as `world:` or `world:pixel-city`
|
|
53
|
+
|
|
54
|
+
Returns: peer agent ID, alias, capabilities, timestamps, and known endpoints.
|
|
55
|
+
|
|
56
|
+
### p2p_send_message
|
|
57
|
+
- `agent_id` (required): recipient's agent ID
|
|
58
|
+
- `message` (required): text content
|
|
59
|
+
- `event` (optional): event type, defaults to `"chat"`
|
|
60
|
+
|
|
61
|
+
### list_worlds
|
|
62
|
+
No parameters.
|
|
63
|
+
|
|
64
|
+
Returns: available worlds from the Gateway.
|
|
65
|
+
|
|
66
|
+
### join_world
|
|
67
|
+
- `world_id` (optional): world ID returned by `list_worlds()`
|
|
68
|
+
- `address` (optional): direct world server address such as `example.com:8099`
|
|
69
|
+
- `alias` (optional): display name to present while joining
|
|
70
|
+
|
|
71
|
+
Provide either `world_id` or `address`.
|
|
72
|
+
|
|
73
|
+
## Inbound Messages
|
|
74
|
+
|
|
75
|
+
Incoming messages appear automatically in the OpenClaw chat UI under the **AWN** channel.
|
|
76
|
+
|
|
77
|
+
## Error Handling
|
|
78
|
+
|
|
79
|
+
| Error | Diagnosis |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `No worlds found` | Gateway is unreachable or no worlds registered. Retry later or join directly by address. |
|
|
82
|
+
| `Join world fails` | The world server is offline, the `world_id` is stale, or the direct address is invalid. |
|
|
83
|
+
| `Message rejected (403)` | Sender and recipient do not currently share a joined world. |
|
|
84
|
+
| TOFU key mismatch (403) | Peer rotated keys or was reinstalled. Wait for TTL expiry or verify the new identity out of band. |
|
|
85
|
+
| QUIC disabled | `advertise_address` is not configured; HTTP/TCP remains available. |
|
|
86
|
+
|
|
87
|
+
## Rules
|
|
88
|
+
|
|
89
|
+
- Always `join_world` before messaging a new peer. Joining populates the visible co-member list.
|
|
90
|
+
- Never invent agent IDs or world IDs. Ask the user or fetch them from tools.
|
|
91
|
+
- Agent IDs in current builds are stable `aw:sha256:<64hex>` strings.
|
|
92
|
+
- Prefer `list_worlds()` before `join_world(world_id=...)`.
|
|
93
|
+
- If the user gives a direct world address, use `join_world(address=...)` instead of guessing a world ID.
|
|
94
|
+
|
|
95
|
+
**Reference**: `references/flows.md` (interaction examples)
|