agentbnb 8.2.0 → 8.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-TBJ3FZKZ.js → chunk-4IPJJRTP.js} +1 -1
- package/dist/chunk-CKOOVZOI.js +158 -0
- package/dist/chunk-CQFBNTGT.js +145 -0
- package/dist/{chunk-P4LOYSLA.js → chunk-DYQOFGGI.js} +331 -416
- package/dist/{chunk-ALX4WS3A.js → chunk-EG6RS4JC.js} +70 -46
- package/dist/{chunk-CUONY5TO.js → chunk-EJKW57ZV.js} +19 -1
- package/dist/{chunk-5AAFG2V2.js → chunk-LKLKYXLV.js} +239 -24
- package/dist/{chunk-7EF3HYVZ.js → chunk-MCED4GDW.js} +499 -86
- package/dist/{chunk-YHY7OG6S.js → chunk-MWOXW7JQ.js} +7 -7
- package/dist/{chunk-E2OKP5CY.js → chunk-QCGIG7WW.js} +182 -86
- package/dist/{chunk-5GME4KJZ.js → chunk-QHZGOG3O.js} +148 -46
- package/dist/{chunk-D6RKW2XG.js → chunk-RYISHSHB.js} +302 -4
- package/dist/{chunk-O2OYBAVR.js → chunk-S3V6R3EN.js} +75 -39
- package/dist/{chunk-X32NE6V4.js → chunk-WNXXLCV5.js} +1 -1
- package/dist/{chunk-C537SFHV.js → chunk-XBGVQMQJ.js} +72 -48
- package/dist/{chunk-FTZTEHYG.js → chunk-Z2GEFFDO.js} +135 -8
- package/dist/cli/index.js +42 -67
- package/dist/{client-HKV3QWZ3.js → client-XOLP5IUZ.js} +4 -2
- package/dist/{conduct-W6XF6DJW.js → conduct-AZFLNUX3.js} +10 -11
- package/dist/{conduct-YB64OHI6.js → conduct-VPUYTNEA.js} +10 -11
- package/dist/{conductor-mode-AKREGDIU.js → conductor-mode-PLTB6MS3.js} +7 -8
- package/dist/{conductor-mode-TFCVCQHU.js → conductor-mode-WKB42PYM.js} +6 -3
- package/dist/{execute-EPE6MZLT.js → execute-NNDCXTN4.js} +3 -2
- package/dist/{execute-AYQWORVH.js → execute-RIRHTIBU.js} +6 -5
- package/dist/index.d.ts +8 -8
- package/dist/index.js +637 -693
- package/dist/{publish-capability-AH2HDW54.js → publish-capability-QDR2QIZ2.js} +2 -2
- package/dist/{request-HCCXSKAY.js → request-NX7GSPIG.js} +31 -36
- package/dist/{serve-skill-SZAQT5T5.js → serve-skill-E6EJQYAK.js} +10 -9
- package/dist/{server-LMY2A3GT.js → server-VBCT32FC.js} +12 -18
- package/dist/{service-coordinator-WGH6B2VT.js → service-coordinator-KMSA6BST.js} +137 -69
- package/dist/skills/agentbnb/bootstrap.js +561 -247
- package/package.json +13 -17
- package/skills/agentbnb/bootstrap.test.ts +8 -6
- package/skills/agentbnb/bootstrap.ts +21 -13
- package/skills/agentbnb/install.sh +0 -0
- package/dist/chunk-64AK4FJM.js +0 -84
- package/dist/chunk-KF3TZHA5.js +0 -91
- package/dist/chunk-LJM7FHPM.js +0 -138
- package/dist/chunk-OH7BP5NP.js +0 -96
|
@@ -7,10 +7,13 @@ import {
|
|
|
7
7
|
insertRequestLog,
|
|
8
8
|
recordEarning,
|
|
9
9
|
releaseEscrow,
|
|
10
|
+
resolveTargetCapability,
|
|
10
11
|
settleEscrow,
|
|
11
|
-
updateReputation
|
|
12
|
+
updateReputation
|
|
13
|
+
} from "./chunk-MCED4GDW.js";
|
|
14
|
+
import {
|
|
12
15
|
verifyEscrowReceipt
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-EJKW57ZV.js";
|
|
14
17
|
import {
|
|
15
18
|
loadConfig
|
|
16
19
|
} from "./chunk-IVOYM3WG.js";
|
|
@@ -290,8 +293,12 @@ async function executeCapabilityBatch(options) {
|
|
|
290
293
|
};
|
|
291
294
|
}
|
|
292
295
|
const executeItem = async (item, index) => {
|
|
293
|
-
const
|
|
294
|
-
|
|
296
|
+
const resolved = await resolveTargetCapability(item.skill_id, {
|
|
297
|
+
registryDb,
|
|
298
|
+
registryUrl: options.registryUrl,
|
|
299
|
+
onlineOnly: true
|
|
300
|
+
});
|
|
301
|
+
if (!resolved) {
|
|
295
302
|
return {
|
|
296
303
|
request_index: index,
|
|
297
304
|
status: "failed",
|
|
@@ -300,26 +307,11 @@ async function executeCapabilityBatch(options) {
|
|
|
300
307
|
error: `Card/skill not found: ${item.skill_id}`
|
|
301
308
|
};
|
|
302
309
|
}
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const skill = v2card.skills[0];
|
|
309
|
-
if (!skill) {
|
|
310
|
-
return {
|
|
311
|
-
request_index: index,
|
|
312
|
-
status: "failed",
|
|
313
|
-
credits_spent: 0,
|
|
314
|
-
credits_refunded: 0,
|
|
315
|
-
error: `No skills defined on card: ${item.skill_id}`
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
creditsNeeded = skill.pricing.credits_per_call;
|
|
319
|
-
resolvedSkillId = skill.id;
|
|
320
|
-
} else {
|
|
321
|
-
creditsNeeded = card.pricing.credits_per_call;
|
|
322
|
-
}
|
|
310
|
+
const localCard = getCard(registryDb, resolved.cardId);
|
|
311
|
+
const localCardRaw = localCard;
|
|
312
|
+
const cardName = typeof localCardRaw?.["name"] === "string" ? localCardRaw["name"] : typeof localCardRaw?.["agent_name"] === "string" ? localCardRaw["agent_name"] : resolved.cardId;
|
|
313
|
+
const creditsNeeded = resolved.credits_per_call;
|
|
314
|
+
const resolvedSkillId = resolved.skillId;
|
|
323
315
|
if (creditsNeeded > item.max_credits) {
|
|
324
316
|
return {
|
|
325
317
|
request_index: index,
|
|
@@ -341,7 +333,7 @@ async function executeCapabilityBatch(options) {
|
|
|
341
333
|
error: "Insufficient credits"
|
|
342
334
|
};
|
|
343
335
|
}
|
|
344
|
-
escrowId = holdEscrow(creditDb, owner, creditsNeeded,
|
|
336
|
+
escrowId = holdEscrow(creditDb, owner, creditsNeeded, resolved.cardId);
|
|
345
337
|
} catch (err) {
|
|
346
338
|
const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
|
|
347
339
|
return {
|
|
@@ -353,30 +345,62 @@ async function executeCapabilityBatch(options) {
|
|
|
353
345
|
};
|
|
354
346
|
}
|
|
355
347
|
const startMs = Date.now();
|
|
356
|
-
const latencyMs = Date.now() - startMs;
|
|
357
|
-
settleEscrow(creditDb, escrowId, card.owner);
|
|
358
|
-
updateReputation(registryDb, card.id, true, latencyMs);
|
|
359
348
|
try {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
349
|
+
const result = options.dispatchRequest ? await options.dispatchRequest({
|
|
350
|
+
target: resolved,
|
|
351
|
+
params: item.params,
|
|
352
|
+
requester: owner
|
|
353
|
+
}) : { card_id: resolved.cardId, skill_id: resolvedSkillId };
|
|
354
|
+
const latencyMs = Date.now() - startMs;
|
|
355
|
+
settleEscrow(creditDb, escrowId, resolved.owner);
|
|
356
|
+
updateReputation(registryDb, resolved.cardId, true, latencyMs);
|
|
357
|
+
try {
|
|
358
|
+
insertRequestLog(registryDb, {
|
|
359
|
+
id: randomUUID(),
|
|
360
|
+
card_id: resolved.cardId,
|
|
361
|
+
card_name: cardName,
|
|
362
|
+
skill_id: resolvedSkillId,
|
|
363
|
+
requester: owner,
|
|
364
|
+
status: "success",
|
|
365
|
+
latency_ms: latencyMs,
|
|
366
|
+
credits_charged: creditsNeeded,
|
|
367
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
368
|
+
});
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
request_index: index,
|
|
366
373
|
status: "success",
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
} catch {
|
|
374
|
+
result,
|
|
375
|
+
credits_spent: creditsNeeded,
|
|
376
|
+
credits_refunded: 0
|
|
377
|
+
};
|
|
378
|
+
} catch (err) {
|
|
379
|
+
releaseEscrow(creditDb, escrowId);
|
|
380
|
+
const latencyMs = Date.now() - startMs;
|
|
381
|
+
try {
|
|
382
|
+
insertRequestLog(registryDb, {
|
|
383
|
+
id: randomUUID(),
|
|
384
|
+
card_id: resolved.cardId,
|
|
385
|
+
card_name: cardName,
|
|
386
|
+
skill_id: resolvedSkillId,
|
|
387
|
+
requester: owner,
|
|
388
|
+
status: "failure",
|
|
389
|
+
latency_ms: latencyMs,
|
|
390
|
+
credits_charged: 0,
|
|
391
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
392
|
+
failure_reason: "not_found"
|
|
393
|
+
});
|
|
394
|
+
} catch {
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
request_index: index,
|
|
398
|
+
status: "failed",
|
|
399
|
+
credits_spent: 0,
|
|
400
|
+
credits_refunded: creditsNeeded,
|
|
401
|
+
error: err instanceof Error ? err.message : String(err)
|
|
402
|
+
};
|
|
372
403
|
}
|
|
373
|
-
return {
|
|
374
|
-
request_index: index,
|
|
375
|
-
status: "success",
|
|
376
|
-
result: { card_id: card.id, skill_id: resolvedSkillId },
|
|
377
|
-
credits_spent: creditsNeeded,
|
|
378
|
-
credits_refunded: 0
|
|
379
|
-
};
|
|
380
404
|
};
|
|
381
405
|
let results;
|
|
382
406
|
if (strategy === "sequential") {
|
|
@@ -35,7 +35,25 @@ function loadKeyPair(configDir) {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
function canonicalJson(data) {
|
|
38
|
-
return JSON.stringify(
|
|
38
|
+
return JSON.stringify(sortForCanonicalJson(data));
|
|
39
|
+
}
|
|
40
|
+
function sortForCanonicalJson(value) {
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
return value.map((item) => sortForCanonicalJson(item));
|
|
43
|
+
}
|
|
44
|
+
if (value !== null && typeof value === "object") {
|
|
45
|
+
const proto = Object.getPrototypeOf(value);
|
|
46
|
+
if (proto === Object.prototype || proto === null) {
|
|
47
|
+
const input = value;
|
|
48
|
+
const output = {};
|
|
49
|
+
const sortedKeys = Object.keys(input).sort();
|
|
50
|
+
for (const key of sortedKeys) {
|
|
51
|
+
output[key] = sortForCanonicalJson(input[key]);
|
|
52
|
+
}
|
|
53
|
+
return output;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
39
57
|
}
|
|
40
58
|
function signEscrowReceipt(data, privateKey) {
|
|
41
59
|
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
@@ -3,19 +3,154 @@ import {
|
|
|
3
3
|
getBalance,
|
|
4
4
|
getTransactions,
|
|
5
5
|
holdEscrow,
|
|
6
|
+
lookupAgent,
|
|
6
7
|
migrateOwner,
|
|
7
8
|
openCreditDb,
|
|
8
9
|
releaseEscrow,
|
|
9
10
|
settleEscrow
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RYISHSHB.js";
|
|
11
12
|
import {
|
|
13
|
+
generateKeyPair,
|
|
14
|
+
loadKeyPair,
|
|
15
|
+
saveKeyPair,
|
|
12
16
|
signEscrowReceipt,
|
|
13
17
|
verifyEscrowReceipt
|
|
14
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-EJKW57ZV.js";
|
|
15
19
|
import {
|
|
16
20
|
AgentBnBError
|
|
17
21
|
} from "./chunk-WVY2W7AA.js";
|
|
18
22
|
|
|
23
|
+
// src/identity/identity.ts
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
import { createHash, createPrivateKey, createPublicKey } from "crypto";
|
|
26
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
27
|
+
import { join } from "path";
|
|
28
|
+
var AgentIdentitySchema = z.object({
|
|
29
|
+
/** Deterministic ID derived from public key: sha256(hex).slice(0, 16). */
|
|
30
|
+
agent_id: z.string().min(1),
|
|
31
|
+
/** Human-readable owner name (from config or init). */
|
|
32
|
+
owner: z.string().min(1),
|
|
33
|
+
/** Hex-encoded Ed25519 public key. */
|
|
34
|
+
public_key: z.string().min(1),
|
|
35
|
+
/** ISO 8601 timestamp of identity creation. */
|
|
36
|
+
created_at: z.string().datetime(),
|
|
37
|
+
/** Optional guarantor info if linked to a human. */
|
|
38
|
+
guarantor: z.object({
|
|
39
|
+
github_login: z.string().min(1),
|
|
40
|
+
verified_at: z.string().datetime()
|
|
41
|
+
}).optional()
|
|
42
|
+
});
|
|
43
|
+
var AgentCertificateSchema = z.object({
|
|
44
|
+
identity: AgentIdentitySchema,
|
|
45
|
+
/** ISO 8601 timestamp of certificate issuance. */
|
|
46
|
+
issued_at: z.string().datetime(),
|
|
47
|
+
/** ISO 8601 timestamp of certificate expiry. */
|
|
48
|
+
expires_at: z.string().datetime(),
|
|
49
|
+
/** Hex-encoded public key of the issuer (same as identity for self-signed). */
|
|
50
|
+
issuer_public_key: z.string().min(1),
|
|
51
|
+
/** Base64url Ed25519 signature over { identity, issued_at, expires_at, issuer_public_key }. */
|
|
52
|
+
signature: z.string().min(1)
|
|
53
|
+
});
|
|
54
|
+
var IDENTITY_FILENAME = "identity.json";
|
|
55
|
+
var PRIVATE_KEY_FILENAME = "private.key";
|
|
56
|
+
var PUBLIC_KEY_FILENAME = "public.key";
|
|
57
|
+
function derivePublicKeyFromPrivate(privateKey) {
|
|
58
|
+
const privateKeyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
59
|
+
const publicKeyObject = createPublicKey(privateKeyObject);
|
|
60
|
+
const publicKey = publicKeyObject.export({ format: "der", type: "spki" });
|
|
61
|
+
return Buffer.from(publicKey);
|
|
62
|
+
}
|
|
63
|
+
function buildIdentityFromPublicKey(publicKey, owner, createdAt) {
|
|
64
|
+
const publicKeyHex = publicKey.toString("hex");
|
|
65
|
+
return {
|
|
66
|
+
agent_id: deriveAgentId(publicKeyHex),
|
|
67
|
+
owner,
|
|
68
|
+
public_key: publicKeyHex,
|
|
69
|
+
created_at: createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function generateFreshIdentity(configDir, owner) {
|
|
73
|
+
const keys = generateKeyPair();
|
|
74
|
+
saveKeyPair(configDir, keys);
|
|
75
|
+
const identity = buildIdentityFromPublicKey(keys.publicKey, owner);
|
|
76
|
+
saveIdentity(configDir, identity);
|
|
77
|
+
return { identity, keys, status: "generated" };
|
|
78
|
+
}
|
|
79
|
+
function deriveAgentId(publicKeyHex) {
|
|
80
|
+
return createHash("sha256").update(publicKeyHex, "hex").digest("hex").slice(0, 16);
|
|
81
|
+
}
|
|
82
|
+
function loadIdentity(configDir) {
|
|
83
|
+
const filePath = join(configDir, IDENTITY_FILENAME);
|
|
84
|
+
if (!existsSync(filePath)) return null;
|
|
85
|
+
try {
|
|
86
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
87
|
+
return AgentIdentitySchema.parse(JSON.parse(raw));
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function saveIdentity(configDir, identity) {
|
|
93
|
+
if (!existsSync(configDir)) {
|
|
94
|
+
mkdirSync(configDir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
const filePath = join(configDir, IDENTITY_FILENAME);
|
|
97
|
+
writeFileSync(filePath, JSON.stringify(identity, null, 2), "utf-8");
|
|
98
|
+
}
|
|
99
|
+
function loadOrRepairIdentity(configDir, ownerHint) {
|
|
100
|
+
if (!existsSync(configDir)) {
|
|
101
|
+
mkdirSync(configDir, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
const identityPath = join(configDir, IDENTITY_FILENAME);
|
|
104
|
+
const privateKeyPath = join(configDir, PRIVATE_KEY_FILENAME);
|
|
105
|
+
const publicKeyPath = join(configDir, PUBLIC_KEY_FILENAME);
|
|
106
|
+
const hasIdentity = existsSync(identityPath);
|
|
107
|
+
const hasPrivateKey = existsSync(privateKeyPath);
|
|
108
|
+
const hasPublicKey = existsSync(publicKeyPath);
|
|
109
|
+
if (!hasIdentity || !hasPrivateKey || !hasPublicKey) {
|
|
110
|
+
return generateFreshIdentity(configDir, ownerHint ?? "agent");
|
|
111
|
+
}
|
|
112
|
+
let keys;
|
|
113
|
+
try {
|
|
114
|
+
keys = loadKeyPair(configDir);
|
|
115
|
+
} catch {
|
|
116
|
+
return generateFreshIdentity(configDir, ownerHint ?? "agent");
|
|
117
|
+
}
|
|
118
|
+
let derivedPublicKey;
|
|
119
|
+
try {
|
|
120
|
+
derivedPublicKey = derivePublicKeyFromPrivate(keys.privateKey);
|
|
121
|
+
} catch {
|
|
122
|
+
return generateFreshIdentity(configDir, ownerHint ?? "agent");
|
|
123
|
+
}
|
|
124
|
+
let keypairRepaired = false;
|
|
125
|
+
if (!keys.publicKey.equals(derivedPublicKey)) {
|
|
126
|
+
keypairRepaired = true;
|
|
127
|
+
keys = { privateKey: keys.privateKey, publicKey: derivedPublicKey };
|
|
128
|
+
saveKeyPair(configDir, keys);
|
|
129
|
+
}
|
|
130
|
+
const loadedIdentity = loadIdentity(configDir);
|
|
131
|
+
const expectedAgentId = deriveAgentId(derivedPublicKey.toString("hex"));
|
|
132
|
+
const expectedPublicKeyHex = derivedPublicKey.toString("hex");
|
|
133
|
+
const identityMismatch = !loadedIdentity || loadedIdentity.public_key !== expectedPublicKeyHex || loadedIdentity.agent_id !== expectedAgentId;
|
|
134
|
+
if (identityMismatch) {
|
|
135
|
+
const repairedIdentity = buildIdentityFromPublicKey(
|
|
136
|
+
derivedPublicKey,
|
|
137
|
+
loadedIdentity?.owner ?? ownerHint ?? "agent",
|
|
138
|
+
loadedIdentity?.created_at
|
|
139
|
+
);
|
|
140
|
+
saveIdentity(configDir, repairedIdentity);
|
|
141
|
+
return { identity: repairedIdentity, keys, status: "repaired" };
|
|
142
|
+
}
|
|
143
|
+
if (ownerHint && loadedIdentity.owner !== ownerHint) {
|
|
144
|
+
const updatedIdentity = { ...loadedIdentity, owner: ownerHint };
|
|
145
|
+
saveIdentity(configDir, updatedIdentity);
|
|
146
|
+
return { identity: updatedIdentity, keys, status: "repaired" };
|
|
147
|
+
}
|
|
148
|
+
return { identity: loadedIdentity, keys, status: keypairRepaired ? "repaired" : "existing" };
|
|
149
|
+
}
|
|
150
|
+
function ensureIdentity(configDir, owner) {
|
|
151
|
+
return loadOrRepairIdentity(configDir, owner).identity;
|
|
152
|
+
}
|
|
153
|
+
|
|
19
154
|
// src/credit/local-credit-ledger.ts
|
|
20
155
|
var LocalCreditLedger = class {
|
|
21
156
|
constructor(db) {
|
|
@@ -91,11 +226,57 @@ var LocalCreditLedger = class {
|
|
|
91
226
|
|
|
92
227
|
// src/registry/identity-auth.ts
|
|
93
228
|
var MAX_REQUEST_AGE_MS = 5 * 60 * 1e3;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
229
|
+
function normalizeSignedParams(body) {
|
|
230
|
+
return body === void 0 ? null : body;
|
|
231
|
+
}
|
|
232
|
+
function buildIdentityPayload(method, path, timestamp, publicKeyHex, agentId, params) {
|
|
233
|
+
return {
|
|
234
|
+
method,
|
|
235
|
+
path,
|
|
236
|
+
timestamp,
|
|
237
|
+
publicKey: publicKeyHex,
|
|
238
|
+
agentId,
|
|
239
|
+
params: normalizeSignedParams(params)
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function extractClaimedRequester(request) {
|
|
243
|
+
const extractFromObject = (obj) => {
|
|
244
|
+
const directOwner = typeof obj.owner === "string" ? obj.owner.trim() : "";
|
|
245
|
+
if (directOwner) return directOwner;
|
|
246
|
+
const directRequester = typeof obj.requester === "string" ? obj.requester.trim() : "";
|
|
247
|
+
if (directRequester) return directRequester;
|
|
248
|
+
const oldOwner = typeof obj.oldOwner === "string" ? obj.oldOwner.trim() : "";
|
|
249
|
+
if (oldOwner) return oldOwner;
|
|
250
|
+
const nestedParams = obj.params;
|
|
251
|
+
if (nestedParams && typeof nestedParams === "object" && !Array.isArray(nestedParams)) {
|
|
252
|
+
const nested = nestedParams;
|
|
253
|
+
const nestedOwner = typeof nested.owner === "string" ? nested.owner.trim() : "";
|
|
254
|
+
if (nestedOwner) return nestedOwner;
|
|
255
|
+
const nestedRequester = typeof nested.requester === "string" ? nested.requester.trim() : "";
|
|
256
|
+
if (nestedRequester) return nestedRequester;
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
};
|
|
260
|
+
if (request.body && typeof request.body === "object" && !Array.isArray(request.body)) {
|
|
261
|
+
const claimed = extractFromObject(request.body);
|
|
262
|
+
if (claimed) return claimed;
|
|
263
|
+
}
|
|
264
|
+
if (request.params && typeof request.params === "object" && !Array.isArray(request.params)) {
|
|
265
|
+
const claimed = extractFromObject(request.params);
|
|
266
|
+
if (claimed) return claimed;
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
async function verifyIdentity(request, reply, options) {
|
|
271
|
+
const agentIdHeader = request.headers["x-agent-id"];
|
|
272
|
+
const publicKeyHeader = request.headers["x-agent-publickey"];
|
|
273
|
+
const signatureHeader = request.headers["x-agent-signature"];
|
|
274
|
+
const timestampHeader = request.headers["x-agent-timestamp"];
|
|
275
|
+
const agentId = agentIdHeader?.trim();
|
|
276
|
+
const publicKeyHex = publicKeyHeader?.trim();
|
|
277
|
+
const signature = signatureHeader?.trim();
|
|
278
|
+
const timestamp = timestampHeader?.trim();
|
|
279
|
+
if (!agentId || !publicKeyHex || !signature || !timestamp) {
|
|
99
280
|
await reply.code(401).send({ error: "Missing identity headers" });
|
|
100
281
|
return false;
|
|
101
282
|
}
|
|
@@ -104,12 +285,21 @@ async function verifyIdentity(request, reply) {
|
|
|
104
285
|
await reply.code(401).send({ error: "Request expired" });
|
|
105
286
|
return false;
|
|
106
287
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
288
|
+
if (!/^[0-9a-fA-F]+$/.test(publicKeyHex) || publicKeyHex.length % 2 !== 0) {
|
|
289
|
+
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
let expectedAgentId;
|
|
293
|
+
try {
|
|
294
|
+
expectedAgentId = deriveAgentId(publicKeyHex);
|
|
295
|
+
} catch {
|
|
296
|
+
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
if (agentId !== expectedAgentId) {
|
|
300
|
+
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
113
303
|
let publicKeyBuffer;
|
|
114
304
|
try {
|
|
115
305
|
publicKeyBuffer = Buffer.from(publicKeyHex, "hex");
|
|
@@ -117,30 +307,52 @@ async function verifyIdentity(request, reply) {
|
|
|
117
307
|
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
118
308
|
return false;
|
|
119
309
|
}
|
|
310
|
+
const knownAgent = options.agentDb ? lookupAgent(options.agentDb, agentId) : null;
|
|
311
|
+
if (knownAgent && knownAgent.public_key.toLowerCase() !== publicKeyHex.toLowerCase()) {
|
|
312
|
+
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
const payload = buildIdentityPayload(
|
|
316
|
+
request.method,
|
|
317
|
+
request.url,
|
|
318
|
+
timestamp,
|
|
319
|
+
publicKeyHex,
|
|
320
|
+
agentId,
|
|
321
|
+
request.body
|
|
322
|
+
);
|
|
120
323
|
const valid = verifyEscrowReceipt(payload, signature, publicKeyBuffer);
|
|
121
324
|
if (!valid) {
|
|
122
325
|
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
123
326
|
return false;
|
|
124
327
|
}
|
|
328
|
+
const claimedRequester = extractClaimedRequester(request);
|
|
329
|
+
if (claimedRequester) {
|
|
330
|
+
const matchesAgentId = claimedRequester === agentId;
|
|
331
|
+
const matchesLegacyOwner = knownAgent?.legacy_owner === claimedRequester;
|
|
332
|
+
if (!matchesAgentId && !matchesLegacyOwner) {
|
|
333
|
+
await reply.code(401).send({ error: "Identity does not match requester" });
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
125
337
|
request.agentPublicKey = publicKeyHex;
|
|
338
|
+
request.agentId = agentId;
|
|
126
339
|
return true;
|
|
127
340
|
}
|
|
128
|
-
function identityAuthPlugin(fastify) {
|
|
129
|
-
fastify.addHook("
|
|
130
|
-
await verifyIdentity(request, reply);
|
|
341
|
+
function identityAuthPlugin(fastify, options = {}) {
|
|
342
|
+
fastify.addHook("preHandler", async (request, reply) => {
|
|
343
|
+
const ok = await verifyIdentity(request, reply, options);
|
|
344
|
+
if (!ok) {
|
|
345
|
+
return reply;
|
|
346
|
+
}
|
|
131
347
|
});
|
|
132
348
|
}
|
|
133
|
-
function signRequest(method, path, body, privateKey, publicKeyHex) {
|
|
349
|
+
function signRequest(method, path, body, privateKey, publicKeyHex, agentIdOverride) {
|
|
134
350
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
path,
|
|
138
|
-
timestamp,
|
|
139
|
-
publicKey: publicKeyHex
|
|
140
|
-
};
|
|
141
|
-
void body;
|
|
351
|
+
const agentId = agentIdOverride ?? deriveAgentId(publicKeyHex);
|
|
352
|
+
const payload = buildIdentityPayload(method, path, timestamp, publicKeyHex, agentId, body);
|
|
142
353
|
const signature = signEscrowReceipt(payload, privateKey);
|
|
143
354
|
return {
|
|
355
|
+
"X-Agent-Id": agentId,
|
|
144
356
|
"X-Agent-PublicKey": publicKeyHex,
|
|
145
357
|
"X-Agent-Signature": signature,
|
|
146
358
|
"X-Agent-Timestamp": timestamp
|
|
@@ -368,6 +580,9 @@ function createLedger(opts) {
|
|
|
368
580
|
}
|
|
369
581
|
|
|
370
582
|
export {
|
|
583
|
+
deriveAgentId,
|
|
584
|
+
loadOrRepairIdentity,
|
|
585
|
+
ensureIdentity,
|
|
371
586
|
identityAuthPlugin,
|
|
372
587
|
createLedger
|
|
373
588
|
};
|