@clawcrony/claw-crony 1.0.1
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 +82 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +720 -0
- package/dist/index.js.map +1 -0
- package/dist/src/agent-card.d.ts +4 -0
- package/dist/src/agent-card.d.ts.map +1 -0
- package/dist/src/agent-card.js +61 -0
- package/dist/src/agent-card.js.map +1 -0
- package/dist/src/audit.d.ts +36 -0
- package/dist/src/audit.d.ts.map +1 -0
- package/dist/src/audit.js +88 -0
- package/dist/src/audit.js.map +1 -0
- package/dist/src/client.d.ts +53 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +322 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/executor.d.ts +34 -0
- package/dist/src/executor.d.ts.map +1 -0
- package/dist/src/executor.js +994 -0
- package/dist/src/executor.js.map +1 -0
- package/dist/src/file-security.d.ts +63 -0
- package/dist/src/file-security.d.ts.map +1 -0
- package/dist/src/file-security.js +350 -0
- package/dist/src/file-security.js.map +1 -0
- package/dist/src/hub-match.d.ts +73 -0
- package/dist/src/hub-match.d.ts.map +1 -0
- package/dist/src/hub-match.js +120 -0
- package/dist/src/hub-match.js.map +1 -0
- package/dist/src/hub-registration.d.ts +24 -0
- package/dist/src/hub-registration.d.ts.map +1 -0
- package/dist/src/hub-registration.js +242 -0
- package/dist/src/hub-registration.js.map +1 -0
- package/dist/src/internal/envelope.d.ts +33 -0
- package/dist/src/internal/envelope.d.ts.map +1 -0
- package/dist/src/internal/envelope.js +152 -0
- package/dist/src/internal/envelope.js.map +1 -0
- package/dist/src/internal/idempotency.d.ts +48 -0
- package/dist/src/internal/idempotency.d.ts.map +1 -0
- package/dist/src/internal/idempotency.js +82 -0
- package/dist/src/internal/idempotency.js.map +1 -0
- package/dist/src/internal/metrics.d.ts +38 -0
- package/dist/src/internal/metrics.d.ts.map +1 -0
- package/dist/src/internal/metrics.js +83 -0
- package/dist/src/internal/metrics.js.map +1 -0
- package/dist/src/internal/outbox.d.ts +49 -0
- package/dist/src/internal/outbox.d.ts.map +1 -0
- package/dist/src/internal/outbox.js +149 -0
- package/dist/src/internal/outbox.js.map +1 -0
- package/dist/src/internal/routing.d.ts +28 -0
- package/dist/src/internal/routing.d.ts.map +1 -0
- package/dist/src/internal/routing.js +57 -0
- package/dist/src/internal/routing.js.map +1 -0
- package/dist/src/internal/security.d.ts +53 -0
- package/dist/src/internal/security.d.ts.map +1 -0
- package/dist/src/internal/security.js +122 -0
- package/dist/src/internal/security.js.map +1 -0
- package/dist/src/internal/transport.d.ts +49 -0
- package/dist/src/internal/transport.d.ts.map +1 -0
- package/dist/src/internal/transport.js +207 -0
- package/dist/src/internal/transport.js.map +1 -0
- package/dist/src/internal/types-internal.d.ts +95 -0
- package/dist/src/internal/types-internal.d.ts.map +1 -0
- package/dist/src/internal/types-internal.js +9 -0
- package/dist/src/internal/types-internal.js.map +1 -0
- package/dist/src/peer-health.d.ts +47 -0
- package/dist/src/peer-health.d.ts.map +1 -0
- package/dist/src/peer-health.js +169 -0
- package/dist/src/peer-health.js.map +1 -0
- package/dist/src/peer-retry.d.ts +16 -0
- package/dist/src/peer-retry.d.ts.map +1 -0
- package/dist/src/peer-retry.js +75 -0
- package/dist/src/peer-retry.js.map +1 -0
- package/dist/src/queueing-executor.d.ts +23 -0
- package/dist/src/queueing-executor.d.ts.map +1 -0
- package/dist/src/queueing-executor.js +179 -0
- package/dist/src/queueing-executor.js.map +1 -0
- package/dist/src/routing-rules.d.ts +53 -0
- package/dist/src/routing-rules.d.ts.map +1 -0
- package/dist/src/routing-rules.js +130 -0
- package/dist/src/routing-rules.js.map +1 -0
- package/dist/src/task-cleanup.d.ts +21 -0
- package/dist/src/task-cleanup.d.ts.map +1 -0
- package/dist/src/task-cleanup.js +77 -0
- package/dist/src/task-cleanup.js.map +1 -0
- package/dist/src/task-store.d.ts +16 -0
- package/dist/src/task-store.d.ts.map +1 -0
- package/dist/src/task-store.js +80 -0
- package/dist/src/task-store.js.map +1 -0
- package/dist/src/telemetry.d.ts +88 -0
- package/dist/src/telemetry.d.ts.map +1 -0
- package/dist/src/telemetry.js +235 -0
- package/dist/src/telemetry.js.map +1 -0
- package/dist/src/transport-fallback.d.ts +29 -0
- package/dist/src/transport-fallback.d.ts.map +1 -0
- package/dist/src/transport-fallback.js +81 -0
- package/dist/src/transport-fallback.js.map +1 -0
- package/dist/src/types.d.ts +160 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +7 -0
- package/dist/src/types.js.map +1 -0
- package/openclaw.plugin.json +272 -0
- package/package.json +56 -0
- package/skill/SKILL.md +230 -0
- package/skill/references/tools-md-template.md +57 -0
- package/skill/scripts/a2a-send.mjs +357 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hub Match API client for openclaw-a2a-gateway.
|
|
3
|
+
*
|
|
4
|
+
* Provides a typed client for the hub's /api/matches endpoints:
|
|
5
|
+
* - POST /api/matches createMatch
|
|
6
|
+
* - GET /api/matches/{id} getMatch
|
|
7
|
+
* - GET /api/matches/pending getPendingMatches
|
|
8
|
+
* - POST /api/matches/{id}/token submitToken
|
|
9
|
+
* - POST /api/matches/{id}/complete completeMatch
|
|
10
|
+
* - POST /api/matches/{id}/cancel cancelMatch
|
|
11
|
+
*/
|
|
12
|
+
import { loadRegistration } from "./hub-registration.js";
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// HubMatchClient
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export class HubMatchClient {
|
|
17
|
+
hubUrl;
|
|
18
|
+
registration;
|
|
19
|
+
constructor(hubUrl, registration) {
|
|
20
|
+
this.hubUrl = hubUrl.replace(/\/$/, "");
|
|
21
|
+
this.registration = registration;
|
|
22
|
+
}
|
|
23
|
+
static async create() {
|
|
24
|
+
const registration = loadRegistration();
|
|
25
|
+
if (!registration) {
|
|
26
|
+
throw new Error("No hub registration found. Run the gateway first to register with the hub.");
|
|
27
|
+
}
|
|
28
|
+
const configUrl = registration.hubUrl;
|
|
29
|
+
return new HubMatchClient(configUrl, registration);
|
|
30
|
+
}
|
|
31
|
+
async request(path, options = {}) {
|
|
32
|
+
const url = `${this.hubUrl}${path}`;
|
|
33
|
+
const res = await fetch(url, {
|
|
34
|
+
...options,
|
|
35
|
+
headers: {
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
"Authorization": `Bearer ${this.registration.token}`,
|
|
38
|
+
...(options.headers ?? {}),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
const body = await res.json().catch(() => ({}));
|
|
43
|
+
throw new Error(`Hub match API error ${res.status}: ${JSON.stringify(body)}`);
|
|
44
|
+
}
|
|
45
|
+
return res.json();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create a new match request.
|
|
49
|
+
* @param params.skills - Skills to search for in a provider
|
|
50
|
+
* @param params.description - Optional description of the match request
|
|
51
|
+
* @param params.token - Optional bearer token to include for this agent
|
|
52
|
+
*/
|
|
53
|
+
async createMatch(params) {
|
|
54
|
+
return this.request("/api/matches", {
|
|
55
|
+
method: "POST",
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
agentId: this.registration.agentId,
|
|
58
|
+
requiredSkills: params.skills,
|
|
59
|
+
description: params.description ?? "",
|
|
60
|
+
token: params.token,
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get a match result by ID.
|
|
66
|
+
* @param matchId - The match ID
|
|
67
|
+
* @param callerId - Optional agent ID to set callerId query param (affects yourToken)
|
|
68
|
+
*/
|
|
69
|
+
async getMatch(matchId, callerId) {
|
|
70
|
+
const path = callerId != null ? `/api/matches/${matchId}?callerId=${callerId}` : `/api/matches/${matchId}`;
|
|
71
|
+
return this.request(path);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get all pending matches for this agent.
|
|
75
|
+
*/
|
|
76
|
+
async getPendingMatches() {
|
|
77
|
+
return this.request(`/api/matches/pending?agentId=${this.registration.agentId}`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Submit this agent's token for a match.
|
|
81
|
+
* @param matchId - The match ID
|
|
82
|
+
* @param token - This agent's bearer token
|
|
83
|
+
*/
|
|
84
|
+
async submitToken(matchId, token) {
|
|
85
|
+
return this.request(`/api/matches/${matchId}/token`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
agentId: this.registration.agentId,
|
|
89
|
+
token,
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Mark a match as completed (both parties have submitted tokens).
|
|
95
|
+
* @param matchId - The match ID
|
|
96
|
+
* @param token - This agent's bearer token (for authorization)
|
|
97
|
+
*/
|
|
98
|
+
async completeMatch(matchId, token) {
|
|
99
|
+
return this.request(`/api/matches/${matchId}/complete`, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
agentId: this.registration.agentId,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Cancel a pending or token_exchange match.
|
|
108
|
+
* @param matchId - The match ID
|
|
109
|
+
* @param token - This agent's bearer token (for authorization)
|
|
110
|
+
*/
|
|
111
|
+
async cancelMatch(matchId, token) {
|
|
112
|
+
return this.request(`/api/matches/${matchId}/cancel`, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
agentId: this.registration.agentId,
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=hub-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-match.js","sourceRoot":"","sources":["../../src/hub-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hub registration module for openclaw-a2a-gateway.
|
|
3
|
+
*
|
|
4
|
+
* Handles automatic registration of the gateway with the hub server on first startup,
|
|
5
|
+
* token generation, and idempotent re-registration.
|
|
6
|
+
*/
|
|
7
|
+
import type { GatewayConfig, HubConfig, HubRegistrationData, OpenClawPluginApi, RegistrationConfig } from "./types.js";
|
|
8
|
+
export declare function loadRegistration(configDir?: string): HubRegistrationData | null;
|
|
9
|
+
export declare function saveRegistration(configDir: string, data: HubRegistrationData): void;
|
|
10
|
+
export interface HubRegistration {
|
|
11
|
+
agentId: number;
|
|
12
|
+
token: string;
|
|
13
|
+
address: string;
|
|
14
|
+
name: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Run the full hub registration flow:
|
|
18
|
+
* 1. Load existing registration (if any)
|
|
19
|
+
* 2. Validate existing token with hub
|
|
20
|
+
* 3. If no valid registration, create new one (handling 409 conflicts)
|
|
21
|
+
* 4. Save registration file atomically
|
|
22
|
+
*/
|
|
23
|
+
export declare function runHubRegistration(api: OpenClawPluginApi, config: GatewayConfig, hubConfig: HubConfig, registrationConfig: RegistrationConfig): Promise<HubRegistration | null>;
|
|
24
|
+
//# sourceMappingURL=hub-registration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-registration.d.ts","sourceRoot":"","sources":["../../src/hub-registration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAwBpB,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAQ/E;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,mBAAmB,GACxB,IAAI,CAKN;AAiGD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,iBAAiB,EACtB,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,SAAS,EACpB,kBAAkB,EAAE,kBAAkB,GACrC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAyJjC"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hub registration module for openclaw-a2a-gateway.
|
|
3
|
+
*
|
|
4
|
+
* Handles automatic registration of the gateway with the hub server on first startup,
|
|
5
|
+
* token generation, and idempotent re-registration.
|
|
6
|
+
*/
|
|
7
|
+
import crypto from "node:crypto";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Token generation
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
function generateToken() {
|
|
15
|
+
return crypto.randomBytes(32).toString("hex");
|
|
16
|
+
}
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Registration file path & I/O
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const REGISTRATION_FILENAME = "a2a-registration.json";
|
|
21
|
+
function getRegistrationPath(configDir) {
|
|
22
|
+
return path.join(configDir, REGISTRATION_FILENAME);
|
|
23
|
+
}
|
|
24
|
+
function getConfigDir() {
|
|
25
|
+
return path.join(os.homedir(), ".openclaw");
|
|
26
|
+
}
|
|
27
|
+
export function loadRegistration(configDir) {
|
|
28
|
+
const regPath = getRegistrationPath(configDir ?? getConfigDir());
|
|
29
|
+
try {
|
|
30
|
+
const raw = fs.readFileSync(regPath, "utf-8");
|
|
31
|
+
return JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function saveRegistration(configDir, data) {
|
|
38
|
+
const regPath = path.join(configDir, REGISTRATION_FILENAME);
|
|
39
|
+
const tmpPath = `${regPath}.tmp`;
|
|
40
|
+
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
41
|
+
fs.renameSync(tmpPath, regPath);
|
|
42
|
+
}
|
|
43
|
+
async function registerWithHub(hubUrl, payload) {
|
|
44
|
+
const url = `${hubUrl.replace(/\/$/, "")}/api/agents`;
|
|
45
|
+
const res = await fetch(url, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: { "Content-Type": "application/json" },
|
|
48
|
+
body: JSON.stringify(payload),
|
|
49
|
+
});
|
|
50
|
+
if (res.status === 409) {
|
|
51
|
+
const err = await res.json().catch(() => ({}));
|
|
52
|
+
throw Object.assign(new Error("Agent address already registered"), { status: 409, body: err });
|
|
53
|
+
}
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const err = await res.json().catch(() => ({}));
|
|
56
|
+
throw Object.assign(new Error(`Hub rejected registration: ${JSON.stringify(err)}`), { status: res.status });
|
|
57
|
+
}
|
|
58
|
+
return res.json();
|
|
59
|
+
}
|
|
60
|
+
async function lookupAgentByAddress(hubUrl, address) {
|
|
61
|
+
const url = `${hubUrl.replace(/\/$/, "")}/api/agents?address=${encodeURIComponent(address)}`;
|
|
62
|
+
const res = await fetch(url);
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
throw Object.assign(new Error(`Hub lookup failed: ${res.status}`), { status: res.status });
|
|
65
|
+
}
|
|
66
|
+
const agents = await res.json();
|
|
67
|
+
return agents.length > 0 ? agents[0] : null;
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Retry with exponential backoff
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
async function retryWithBackoff(fn, maxRetries, baseDelayMs, maxDelayMs) {
|
|
73
|
+
let lastError;
|
|
74
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
75
|
+
try {
|
|
76
|
+
return await fn();
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
lastError = err;
|
|
80
|
+
if (attempt < maxRetries) {
|
|
81
|
+
const delay = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
|
|
82
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw lastError;
|
|
87
|
+
}
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Flatten skills from agent card config
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
function flattenSkills(skills) {
|
|
92
|
+
return skills.map((s) => (typeof s === "string" ? s : s.name));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Run the full hub registration flow:
|
|
96
|
+
* 1. Load existing registration (if any)
|
|
97
|
+
* 2. Validate existing token with hub
|
|
98
|
+
* 3. If no valid registration, create new one (handling 409 conflicts)
|
|
99
|
+
* 4. Save registration file atomically
|
|
100
|
+
*/
|
|
101
|
+
export async function runHubRegistration(api, config, hubConfig, registrationConfig) {
|
|
102
|
+
const configDir = getConfigDir();
|
|
103
|
+
const hubUrl = hubConfig.url;
|
|
104
|
+
// Derive address from agentCard.url (the externally reachable address), not server.bind
|
|
105
|
+
// e.g. "http://100.10.10.1:18800/a2a/jsonrpc" -> "100.10.10.1:18800"
|
|
106
|
+
let address;
|
|
107
|
+
if (config.agentCard.url) {
|
|
108
|
+
try {
|
|
109
|
+
const urlObj = new URL(config.agentCard.url);
|
|
110
|
+
address = `${urlObj.hostname}:${urlObj.port || (urlObj.protocol === "https:" ? 443 : 80)}`;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
address = `${config.server.host}:${config.server.port}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
address = `${config.server.host}:${config.server.port}`;
|
|
118
|
+
}
|
|
119
|
+
// Ensure config directory exists
|
|
120
|
+
if (!fs.existsSync(configDir)) {
|
|
121
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
// Load existing registration
|
|
124
|
+
const existing = loadRegistration(configDir);
|
|
125
|
+
// If we have a registration with a matching address and token, validate it
|
|
126
|
+
if (existing && existing.address === address && existing.hubUrl === hubUrl && existing.token) {
|
|
127
|
+
try {
|
|
128
|
+
const agent = await lookupAgentByAddress(hubUrl, address);
|
|
129
|
+
if (agent && agent.id === existing.agentId) {
|
|
130
|
+
api.logger.info(`a2a-gateway: using existing hub registration (agentId=${existing.agentId})`);
|
|
131
|
+
return {
|
|
132
|
+
agentId: existing.agentId,
|
|
133
|
+
token: existing.token,
|
|
134
|
+
address: existing.address,
|
|
135
|
+
name: existing.name,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Hub unreachable or agent not found — try re-registering with existing token first
|
|
141
|
+
api.logger.warn("a2a-gateway: existing registration invalid, trying with existing token");
|
|
142
|
+
const existingToken = existing.token;
|
|
143
|
+
try {
|
|
144
|
+
const existingPayload = {
|
|
145
|
+
name: config.agentCard.name,
|
|
146
|
+
description: config.agentCard.description ?? "",
|
|
147
|
+
skills: flattenSkills(config.agentCard.skills),
|
|
148
|
+
address,
|
|
149
|
+
token: existingToken,
|
|
150
|
+
username: registrationConfig.username ?? config.agentCard.name,
|
|
151
|
+
email: registrationConfig.email ?? "",
|
|
152
|
+
};
|
|
153
|
+
const agent = await retryWithBackoff(() => registerWithHub(hubUrl, existingPayload), 3, 1000, 10000);
|
|
154
|
+
api.logger.info(`a2a-gateway: re-registered with hub using existing token (agentId=${agent.id})`);
|
|
155
|
+
// Re-save to update registration file
|
|
156
|
+
const updatedData = {
|
|
157
|
+
...existing,
|
|
158
|
+
registeredAt: new Date().toISOString(),
|
|
159
|
+
};
|
|
160
|
+
saveRegistration(configDir, updatedData);
|
|
161
|
+
return { agentId: agent.id, token: existingToken, address, name: config.agentCard.name };
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
// Existing token also failed (409 means address conflict from elsewhere)
|
|
165
|
+
const status = typeof err === "object" && err !== null ? err.status : undefined;
|
|
166
|
+
if (status === 409) {
|
|
167
|
+
api.logger.warn("a2a-gateway: existing token rejected, generating new token");
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
api.logger.warn(`a2a-gateway: re-registration with existing token failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
171
|
+
}
|
|
172
|
+
// fall through to generate new token
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Generate new token
|
|
177
|
+
const token = generateToken();
|
|
178
|
+
const name = config.agentCard.name;
|
|
179
|
+
const description = config.agentCard.description ?? "";
|
|
180
|
+
const skills = flattenSkills(config.agentCard.skills);
|
|
181
|
+
const username = registrationConfig.username ?? name;
|
|
182
|
+
const email = registrationConfig.email ?? "";
|
|
183
|
+
const payload = {
|
|
184
|
+
name,
|
|
185
|
+
description,
|
|
186
|
+
skills,
|
|
187
|
+
address,
|
|
188
|
+
token,
|
|
189
|
+
username,
|
|
190
|
+
email,
|
|
191
|
+
};
|
|
192
|
+
let agentId;
|
|
193
|
+
try {
|
|
194
|
+
const agent = await retryWithBackoff(() => registerWithHub(hubUrl, payload), 3, 1000, 10000);
|
|
195
|
+
agentId = agent.id;
|
|
196
|
+
api.logger.info(`a2a-gateway: registered with hub (agentId=${agentId})`);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
// 409 Conflict: address already registered by someone else — try to find our agentId
|
|
200
|
+
if (typeof err === "object" && err !== null && err.status === 409) {
|
|
201
|
+
try {
|
|
202
|
+
const existingAgent = await lookupAgentByAddress(hubUrl, address);
|
|
203
|
+
if (existingAgent) {
|
|
204
|
+
agentId = existingAgent.id;
|
|
205
|
+
api.logger.info(`a2a-gateway: found existing registration (agentId=${agentId})`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
api.logger.error("a2a-gateway: address conflict but could not find existing agent");
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
api.logger.error("a2a-gateway: address conflict and hub lookup failed");
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
api.logger.warn(`a2a-gateway: hub registration failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Save registration file
|
|
223
|
+
const registrationData = {
|
|
224
|
+
version: 1,
|
|
225
|
+
hubUrl,
|
|
226
|
+
agentId,
|
|
227
|
+
address,
|
|
228
|
+
token,
|
|
229
|
+
registeredAt: new Date().toISOString(),
|
|
230
|
+
name,
|
|
231
|
+
description,
|
|
232
|
+
skills,
|
|
233
|
+
};
|
|
234
|
+
try {
|
|
235
|
+
saveRegistration(configDir, registrationData);
|
|
236
|
+
}
|
|
237
|
+
catch (saveErr) {
|
|
238
|
+
api.logger.warn(`a2a-gateway: failed to save registration file — ${saveErr instanceof Error ? saveErr.message : String(saveErr)}`);
|
|
239
|
+
}
|
|
240
|
+
return { agentId, token, address, name };
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=hub-registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-registration.js","sourceRoot":"","sources":["../../src/hub-registration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Gateway — Envelope creation and validation
|
|
3
|
+
*
|
|
4
|
+
* OpenClaw gateway-internal module — NOT part of the A2A spec.
|
|
5
|
+
*/
|
|
6
|
+
import type { A2AEnvelope, A2AAck, A2AConfig, A2ASource, A2ADestination, MessageType } from "./types-internal.js";
|
|
7
|
+
export type EnvelopeErrorCode = "INVALID_VERSION" | "MISSING_FIELD" | "INVALID_TYPE" | "EXPIRED" | "LOOP_DETECTED" | "HOP_LIMIT" | "PAYLOAD_TOO_LARGE";
|
|
8
|
+
export interface EnvelopeError {
|
|
9
|
+
code: EnvelopeErrorCode;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
12
|
+
/** Generate a ULID-like time-ordered unique ID: `{timestamp_hex}-{uuid_last12}` */
|
|
13
|
+
export declare function generateId(): string;
|
|
14
|
+
export interface CreateEnvelopeOpts {
|
|
15
|
+
source: A2ASource;
|
|
16
|
+
destination: A2ADestination;
|
|
17
|
+
message_type: MessageType;
|
|
18
|
+
payload: unknown;
|
|
19
|
+
ttl_seconds?: number;
|
|
20
|
+
correlation_id?: string;
|
|
21
|
+
trace_id?: string;
|
|
22
|
+
span_id?: string;
|
|
23
|
+
idempotency_key?: string;
|
|
24
|
+
hop_count?: number;
|
|
25
|
+
route_path?: string[];
|
|
26
|
+
}
|
|
27
|
+
/** Build a valid A2AEnvelope with sensible defaults. */
|
|
28
|
+
export declare function createEnvelope(opts: CreateEnvelopeOpts): A2AEnvelope;
|
|
29
|
+
/** Validate an incoming envelope. Returns null on success or an EnvelopeError. */
|
|
30
|
+
export declare function validateEnvelope(envelope: A2AEnvelope, config: A2AConfig): EnvelopeError | null;
|
|
31
|
+
/** Create an A2AAck in response to an envelope. */
|
|
32
|
+
export declare function createAck(envelope: A2AEnvelope, gatewayId: string, status: "accepted" | "rejected", reason?: string): A2AAck;
|
|
33
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/internal/envelope.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,SAAS,EACT,SAAS,EACT,cAAc,EACd,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAM7B,MAAM,MAAM,iBAAiB,GACzB,iBAAiB,GACjB,eAAe,GACf,cAAc,GACd,SAAS,GACT,eAAe,GACf,WAAW,GACX,mBAAmB,CAAC;AAExB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAkBD,mFAAmF;AACnF,wBAAgB,UAAU,IAAI,MAAM,CAKnC;AAMD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,EAAE,cAAc,CAAC;IAC5B,YAAY,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wDAAwD;AACxD,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,WAAW,CAiBpE;AAMD,kFAAkF;AAClF,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,SAAS,GAChB,aAAa,GAAG,IAAI,CAsGtB;AAMD,mDAAmD;AACnD,wBAAgB,SAAS,CACvB,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GAAG,UAAU,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAWR"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Gateway — Envelope creation and validation
|
|
3
|
+
*
|
|
4
|
+
* OpenClaw gateway-internal module — NOT part of the A2A spec.
|
|
5
|
+
*/
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Valid message types
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
const VALID_MESSAGE_TYPES = new Set([
|
|
11
|
+
"command",
|
|
12
|
+
"event",
|
|
13
|
+
"response",
|
|
14
|
+
"error",
|
|
15
|
+
"ack",
|
|
16
|
+
]);
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// ID generation
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/** Generate a ULID-like time-ordered unique ID: `{timestamp_hex}-{uuid_last12}` */
|
|
21
|
+
export function generateId() {
|
|
22
|
+
const timestampHex = Date.now().toString(16);
|
|
23
|
+
const uuid = crypto.randomUUID().replace(/-/g, "");
|
|
24
|
+
const last12 = uuid.slice(-12);
|
|
25
|
+
return `${timestampHex}-${last12}`;
|
|
26
|
+
}
|
|
27
|
+
/** Build a valid A2AEnvelope with sensible defaults. */
|
|
28
|
+
export function createEnvelope(opts) {
|
|
29
|
+
return {
|
|
30
|
+
protocol_version: "a2a/v1",
|
|
31
|
+
message_id: generateId(),
|
|
32
|
+
idempotency_key: opts.idempotency_key ?? generateId(),
|
|
33
|
+
correlation_id: opts.correlation_id,
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
ttl_seconds: opts.ttl_seconds ?? 300,
|
|
36
|
+
trace_id: opts.trace_id,
|
|
37
|
+
span_id: opts.span_id,
|
|
38
|
+
source: opts.source,
|
|
39
|
+
destination: opts.destination,
|
|
40
|
+
message_type: opts.message_type,
|
|
41
|
+
hop_count: opts.hop_count ?? 0,
|
|
42
|
+
route_path: opts.route_path ?? [],
|
|
43
|
+
payload: opts.payload,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Validation
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** Validate an incoming envelope. Returns null on success or an EnvelopeError. */
|
|
50
|
+
export function validateEnvelope(envelope, config) {
|
|
51
|
+
// Protocol version
|
|
52
|
+
if (envelope.protocol_version !== "a2a/v1") {
|
|
53
|
+
return {
|
|
54
|
+
code: "INVALID_VERSION",
|
|
55
|
+
message: `Unsupported protocol version: ${envelope.protocol_version}`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Required fields
|
|
59
|
+
const requiredFields = [
|
|
60
|
+
{ key: "message_id", value: envelope.message_id },
|
|
61
|
+
{ key: "idempotency_key", value: envelope.idempotency_key },
|
|
62
|
+
{ key: "timestamp", value: envelope.timestamp },
|
|
63
|
+
{ key: "source.gateway_id", value: envelope.source?.gateway_id },
|
|
64
|
+
{ key: "destination", value: envelope.destination },
|
|
65
|
+
{ key: "message_type", value: envelope.message_type },
|
|
66
|
+
];
|
|
67
|
+
for (const { key, value } of requiredFields) {
|
|
68
|
+
if (value === undefined || value === null || value === "") {
|
|
69
|
+
return { code: "MISSING_FIELD", message: `Missing required field: ${key}` };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Message type
|
|
73
|
+
if (!VALID_MESSAGE_TYPES.has(envelope.message_type)) {
|
|
74
|
+
return {
|
|
75
|
+
code: "INVALID_TYPE",
|
|
76
|
+
message: `Invalid message_type: ${envelope.message_type}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Field type validation
|
|
80
|
+
if (typeof envelope.ttl_seconds !== "number" ||
|
|
81
|
+
!Number.isFinite(envelope.ttl_seconds) ||
|
|
82
|
+
envelope.ttl_seconds <= 0) {
|
|
83
|
+
return {
|
|
84
|
+
code: "MISSING_FIELD",
|
|
85
|
+
message: "ttl_seconds must be a finite positive number",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (typeof envelope.hop_count !== "number" ||
|
|
89
|
+
!Number.isFinite(envelope.hop_count) ||
|
|
90
|
+
!Number.isInteger(envelope.hop_count) ||
|
|
91
|
+
envelope.hop_count < 0) {
|
|
92
|
+
return {
|
|
93
|
+
code: "MISSING_FIELD",
|
|
94
|
+
message: "hop_count must be a finite non-negative integer",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (!Array.isArray(envelope.route_path) ||
|
|
98
|
+
!envelope.route_path.every((p) => typeof p === "string")) {
|
|
99
|
+
return {
|
|
100
|
+
code: "MISSING_FIELD",
|
|
101
|
+
message: "route_path must be an array of strings",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// TTL expiry
|
|
105
|
+
const envelopeTime = Date.parse(envelope.timestamp);
|
|
106
|
+
if (isNaN(envelopeTime)) {
|
|
107
|
+
return { code: "MISSING_FIELD", message: "Invalid timestamp format" };
|
|
108
|
+
}
|
|
109
|
+
if (envelopeTime + envelope.ttl_seconds * 1000 <= Date.now()) {
|
|
110
|
+
return { code: "EXPIRED", message: "Envelope TTL has expired" };
|
|
111
|
+
}
|
|
112
|
+
// Anti-loop: reject if our own gateway is already in route_path
|
|
113
|
+
if (envelope.route_path.includes(config.gatewayId)) {
|
|
114
|
+
return {
|
|
115
|
+
code: "LOOP_DETECTED",
|
|
116
|
+
message: `Loop detected: ${config.gatewayId} already in route_path`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// Hop limit
|
|
120
|
+
if (envelope.hop_count >= config.limits.maxHops) {
|
|
121
|
+
return {
|
|
122
|
+
code: "HOP_LIMIT",
|
|
123
|
+
message: `Hop count ${envelope.hop_count} exceeds limit ${config.limits.maxHops}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Payload size
|
|
127
|
+
const payloadSize = Buffer.byteLength(JSON.stringify(envelope.payload), "utf8");
|
|
128
|
+
if (payloadSize > config.limits.maxPayloadBytes) {
|
|
129
|
+
return {
|
|
130
|
+
code: "PAYLOAD_TOO_LARGE",
|
|
131
|
+
message: `Payload size ${payloadSize} exceeds limit ${config.limits.maxPayloadBytes}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// ACK creation
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
/** Create an A2AAck in response to an envelope. */
|
|
140
|
+
export function createAck(envelope, gatewayId, status, reason) {
|
|
141
|
+
return {
|
|
142
|
+
protocol_version: "a2a/v1",
|
|
143
|
+
message_id: generateId(),
|
|
144
|
+
correlation_id: envelope.message_id,
|
|
145
|
+
source: { gateway_id: gatewayId },
|
|
146
|
+
message_type: "ack",
|
|
147
|
+
timestamp: new Date().toISOString(),
|
|
148
|
+
status,
|
|
149
|
+
reason,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../../src/internal/envelope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Gateway Plugin — Idempotency Store
|
|
3
|
+
*
|
|
4
|
+
* OpenClaw gateway-internal module — NOT part of the A2A spec.
|
|
5
|
+
* SHA-256 payload fingerprinting and in-memory deduplication
|
|
6
|
+
* with configurable TTL and periodic cleanup.
|
|
7
|
+
*/
|
|
8
|
+
/** Create a SHA-256 hex fingerprint of the given payload string. */
|
|
9
|
+
export declare function createFingerprint(payload: string): string;
|
|
10
|
+
export type CheckResult = {
|
|
11
|
+
status: "new";
|
|
12
|
+
} | {
|
|
13
|
+
status: "duplicate";
|
|
14
|
+
response: string;
|
|
15
|
+
} | {
|
|
16
|
+
status: "conflict";
|
|
17
|
+
};
|
|
18
|
+
export interface IdempotencyStoreConfig {
|
|
19
|
+
defaultTtlSeconds: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class IdempotencyStore {
|
|
22
|
+
private entries;
|
|
23
|
+
private timer;
|
|
24
|
+
private config;
|
|
25
|
+
private expiredCleaned;
|
|
26
|
+
constructor(config: IdempotencyStoreConfig);
|
|
27
|
+
/**
|
|
28
|
+
* Check whether a key has been seen before.
|
|
29
|
+
* - Not found -> `{ status: 'new' }`
|
|
30
|
+
* - Found with same fingerprint -> `{ status: 'duplicate', response }`
|
|
31
|
+
* - Found with different fingerprint -> `{ status: 'conflict' }`
|
|
32
|
+
*/
|
|
33
|
+
check(key: string, payloadFingerprint: string): CheckResult;
|
|
34
|
+
/** Store a response keyed by idempotency key with optional TTL override. */
|
|
35
|
+
store(key: string, payloadFingerprint: string, response: string, ttlSeconds?: number): void;
|
|
36
|
+
/** Remove all expired entries. */
|
|
37
|
+
cleanup(): void;
|
|
38
|
+
/** Return summary stats. */
|
|
39
|
+
getStats(): {
|
|
40
|
+
total: number;
|
|
41
|
+
expired_cleaned: number;
|
|
42
|
+
};
|
|
43
|
+
/** Start periodic cleanup. Default interval is 60 000 ms. */
|
|
44
|
+
startCleanup(intervalMs?: number): void;
|
|
45
|
+
/** Stop periodic cleanup. */
|
|
46
|
+
stopCleanup(): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=idempotency.d.ts.map
|