@agentcontextdistributionprotocol/acdp 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/index.d.ts +402 -0
- package/index.js +321 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# acdp — Node.js SDK
|
|
2
|
+
|
|
3
|
+
Thin NAPI-rs binding over the [`acdp`](https://crates.io/crates/acdp)
|
|
4
|
+
Rust library. Implements the producer- and consumer-side crypto for the
|
|
5
|
+
Agent Context Distribution Protocol v0.1.0 (RFC-ACDP-0001/0003/0008).
|
|
6
|
+
HTTP is intentionally left to the caller — pair this with `fetch` /
|
|
7
|
+
`undici` for transport.
|
|
8
|
+
|
|
9
|
+
## Install (development)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install # installs @napi-rs/cli
|
|
13
|
+
npm run build:debug # produces index.js + acdp.<platform>.node
|
|
14
|
+
node --test tests/ # in-process unit tests, no HTTP
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Build a release binary
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm run build # release mode (LTO + strip)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { AcdpProducer, AcdpVerifier } from '@agentcontextdistributionprotocol/acdp';
|
|
27
|
+
|
|
28
|
+
const producer = AcdpProducer.generate(
|
|
29
|
+
'did:web:agents.example.com:my-agent',
|
|
30
|
+
'did:web:agents.example.com:my-agent#key-1',
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const raw = producer.buildPublishRequest({
|
|
34
|
+
title: 'Q1 snapshot',
|
|
35
|
+
contextType: 'data_snapshot',
|
|
36
|
+
summary: 'Quarter-end inventory',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// POST `raw` (the JSON string) to your registry. On retrieve:
|
|
40
|
+
const body = (await response.json()).body;
|
|
41
|
+
AcdpVerifier.verifyContentHash(JSON.stringify(body), body.content_hash);
|
|
42
|
+
AcdpVerifier.verifySignature(
|
|
43
|
+
pubKeyB64, // resolved from the producer's did:web doc
|
|
44
|
+
body.signature.value,
|
|
45
|
+
body.content_hash,
|
|
46
|
+
);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Design rules
|
|
50
|
+
|
|
51
|
+
* **JSON across the FFI boundary.** Every method accepts and returns
|
|
52
|
+
JSON strings — never a Rust type, never a JS class instance you'd
|
|
53
|
+
have to serialize before sending. The binary stays at ~500 lines of
|
|
54
|
+
glue.
|
|
55
|
+
* **Crypto in Rust, HTTP in JS.** Key generation, JCS + SHA-256 hashing,
|
|
56
|
+
Ed25519 signing, and signature verification happen in the underlying
|
|
57
|
+
`acdp` crate. Transport, retries, and observability are yours.
|
|
58
|
+
* **`AcdpProducer` stores a 32-byte seed.** The Rust `SigningKey` is
|
|
59
|
+
`ZeroizeOnDrop` and not `Clone`, so the binding rebuilds the signing
|
|
60
|
+
key from the seed on each call.
|
|
61
|
+
* **Golden vector parity.** `golden content_hash + signature match
|
|
62
|
+
sig-001` pins the JS-side `content_hash` and `signature.value`
|
|
63
|
+
against the spec's `sig-001` fixture — the same constants the Rust
|
|
64
|
+
suite asserts. A drift on either side is a protocol break.
|
|
65
|
+
|
|
66
|
+
## Layout
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
bindings/acdp-node/
|
|
70
|
+
├── Cargo.toml # standalone [workspace]; depends on `acdp` via path
|
|
71
|
+
├── build.rs # napi-build setup
|
|
72
|
+
├── package.json
|
|
73
|
+
├── README.md # this file
|
|
74
|
+
├── index.js # generated by `napi build`
|
|
75
|
+
├── index.d.ts # generated by `napi build`
|
|
76
|
+
├── acdp.<platform>.node # native binary
|
|
77
|
+
├── src/
|
|
78
|
+
│ ├── lib.rs # module entry — re-exports the napi classes
|
|
79
|
+
│ ├── producer.rs # AcdpProducer: build/sign publish requests
|
|
80
|
+
│ ├── verifier.rs # AcdpVerifier: content_hash + signature verify
|
|
81
|
+
│ └── helpers.rs # visibility / contextType parsers
|
|
82
|
+
└── tests/
|
|
83
|
+
└── test.mjs
|
|
84
|
+
```
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/* auto-generated by NAPI-RS */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A verification method resolved to raw public-key bytes, in the
|
|
8
|
+
* base64 shape the host's pinned-key directory speaks.
|
|
9
|
+
*/
|
|
10
|
+
export interface ResolvedDidKey {
|
|
11
|
+
/** Verification-method id (full DID URL with `#fragment`). */
|
|
12
|
+
keyId: string
|
|
13
|
+
/** `ed25519` or `ecdsa-p256`. */
|
|
14
|
+
algorithm: string
|
|
15
|
+
/**
|
|
16
|
+
* Standard base64 of the raw key bytes:
|
|
17
|
+
* * ed25519 — 32 bytes
|
|
18
|
+
* * ecdsa-p256 — 65-byte SEC1 uncompressed (`0x04 || x || y`)
|
|
19
|
+
*/
|
|
20
|
+
publicKeyB64: string
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Options for `buildPublishRequest`. Field names map directly to the
|
|
24
|
+
* PublishRequest wire schema (camelCase on the JS side).
|
|
25
|
+
*/
|
|
26
|
+
export interface PublishOpts {
|
|
27
|
+
/** Human-readable title (1..=500 chars). */
|
|
28
|
+
title: string
|
|
29
|
+
/** Closed enum or namespaced custom (`^[a-z][a-z0-9_]*:[a-z][a-z0-9_-]*$`). */
|
|
30
|
+
contextType: string
|
|
31
|
+
/** `public` | `restricted` | `private`. Defaults to `public`. */
|
|
32
|
+
visibility?: string
|
|
33
|
+
/** Long human-readable description (≤ 5000 chars). */
|
|
34
|
+
description?: string
|
|
35
|
+
/**
|
|
36
|
+
* Producer-supplied summary for search results (≤ 1000 chars).
|
|
37
|
+
* Part of ProducerContent — included in the content_hash preimage.
|
|
38
|
+
*/
|
|
39
|
+
summary?: string
|
|
40
|
+
/** Free-form tags (each: `^[A-Za-z0-9][A-Za-z0-9_.-]*$`, ≤ 100 chars). */
|
|
41
|
+
tags?: Array<string>
|
|
42
|
+
/** Subject-domain identifier (≤ 200 chars). */
|
|
43
|
+
domain?: string
|
|
44
|
+
/**
|
|
45
|
+
* Producer-specific structured metadata. MUST be a JSON-encoded
|
|
46
|
+
* object string (it is re-parsed so it lands as a JSON object,
|
|
47
|
+
* not a quoted string).
|
|
48
|
+
*/
|
|
49
|
+
metadata?: string
|
|
50
|
+
/**
|
|
51
|
+
* Lineage of contexts this body was derived from (`acdp://…` ids,
|
|
52
|
+
* ≤ 1000 unique).
|
|
53
|
+
*/
|
|
54
|
+
derivedFrom?: Array<string>
|
|
55
|
+
/** Audience DIDs — required (≥ 1) when `visibility = "restricted"`. */
|
|
56
|
+
audience?: Array<string>
|
|
57
|
+
/** Optional JSON Schema URI describing the metadata shape. */
|
|
58
|
+
schemaUri?: string
|
|
59
|
+
/** Contributors (DIDs, ≤ 100 unique). */
|
|
60
|
+
contributors?: Array<string>
|
|
61
|
+
/**
|
|
62
|
+
* Data references — a JSON-encoded array of `acdp-data-ref` objects.
|
|
63
|
+
* Part of ProducerContent, so it is included in the content_hash
|
|
64
|
+
* preimage.
|
|
65
|
+
*/
|
|
66
|
+
dataRefs?: string
|
|
67
|
+
/**
|
|
68
|
+
* RFC 3339 timestamp after which the conclusions should no longer be
|
|
69
|
+
* relied upon. Truncated to millisecond precision.
|
|
70
|
+
*/
|
|
71
|
+
expiresAt?: string
|
|
72
|
+
/**
|
|
73
|
+
* Time window the data covers — a JSON object
|
|
74
|
+
* `{"start": <rfc3339>, "end": <rfc3339>}`. Both ends truncated to
|
|
75
|
+
* millisecond precision.
|
|
76
|
+
*/
|
|
77
|
+
dataPeriod?: string
|
|
78
|
+
/**
|
|
79
|
+
* Self-verifying `lin:sha256:<hex>` lineage id. v2+ supersession
|
|
80
|
+
* only — rejected on first-version publishes.
|
|
81
|
+
*/
|
|
82
|
+
expectedLineageId?: string
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Options for `buildSupersedeRequest`. Any field omitted is carried
|
|
86
|
+
* over from `previousBodyJson` unchanged (mirrors `new_version_from`).
|
|
87
|
+
*/
|
|
88
|
+
export interface SupersedeOpts {
|
|
89
|
+
title?: string
|
|
90
|
+
summary?: string
|
|
91
|
+
description?: string
|
|
92
|
+
tags?: Array<string>
|
|
93
|
+
domain?: string
|
|
94
|
+
metadata?: string
|
|
95
|
+
/**
|
|
96
|
+
* JSON-encoded array of `acdp-data-ref` objects (replaces the
|
|
97
|
+
* carried-over data refs when present).
|
|
98
|
+
*/
|
|
99
|
+
dataRefs?: string
|
|
100
|
+
/** RFC 3339 expiry timestamp. */
|
|
101
|
+
expiresAt?: string
|
|
102
|
+
/** JSON object `{"start": <rfc3339>, "end": <rfc3339>}`. */
|
|
103
|
+
dataPeriod?: string
|
|
104
|
+
/** Self-verifying `lin:sha256:<hex>` lineage id (v2+). */
|
|
105
|
+
expectedLineageId?: string
|
|
106
|
+
}
|
|
107
|
+
/** Stateless did:web string helpers. All methods are static. */
|
|
108
|
+
export declare class AcdpDid {
|
|
109
|
+
/**
|
|
110
|
+
* Translate a `did:web:…` DID to the HTTPS URL of its DID document
|
|
111
|
+
* per RFC-ACDP-0001 §5.11.
|
|
112
|
+
*
|
|
113
|
+
* * `did:web:example.com` → `https://example.com/.well-known/did.json`
|
|
114
|
+
* * `did:web:example.com:users:alice` → `https://example.com/users/alice/did.json`
|
|
115
|
+
*
|
|
116
|
+
* Throws with `.code === "not_did_web"` if the input is not a
|
|
117
|
+
* `did:web` DID.
|
|
118
|
+
*/
|
|
119
|
+
static webToUrl(did: string): string
|
|
120
|
+
/**
|
|
121
|
+
* Strip the `#fragment` from a DID URL, returning the bare DID.
|
|
122
|
+
* Returns the input unchanged when it carries no fragment.
|
|
123
|
+
*/
|
|
124
|
+
static stripFragment(didUrl: string): string
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A parsed did:web DID document. Construct with
|
|
128
|
+
* [`AcdpDidDocument::parse`], then resolve a signing key with
|
|
129
|
+
* [`AcdpDidDocument::key_for_algorithm`].
|
|
130
|
+
*/
|
|
131
|
+
export declare class AcdpDidDocument {
|
|
132
|
+
/**
|
|
133
|
+
* Parse a DID document and assert its `id` equals `expectedDid`.
|
|
134
|
+
*
|
|
135
|
+
* `expectedDid` is the bare DID being resolved (no fragment). The
|
|
136
|
+
* `id`-match check (RFC-ACDP-0001 §9.1) stops a misconfigured or
|
|
137
|
+
* hostile server from substituting another DID's keys.
|
|
138
|
+
*
|
|
139
|
+
* Throws `.code === "parse_failed"` on malformed JSON / missing
|
|
140
|
+
* `id`, or `.code === "id_mismatch"` when `id` ≠ `expectedDid`.
|
|
141
|
+
*/
|
|
142
|
+
static parse(jsonStr: string, expectedDid: string): AcdpDidDocument
|
|
143
|
+
/** The DID this document describes. */
|
|
144
|
+
get id(): string
|
|
145
|
+
/**
|
|
146
|
+
* Resolve a verification method to raw public-key bytes, enforcing
|
|
147
|
+
* the full consumer-side gate:
|
|
148
|
+
*
|
|
149
|
+
* 1. the method `requestedKeyId` (matched by `#fragment`, exact —
|
|
150
|
+
* no loose suffix) must exist, else `.code === "key_not_found"`;
|
|
151
|
+
* 2. it must be authorized in `assertionMethod`, else
|
|
152
|
+
* `.code === "key_not_authorized"`;
|
|
153
|
+
* 3. any algorithm the method declares (via `type`, JWK params, or
|
|
154
|
+
* multibase multicodec prefix) must equal `requestedAlg`, else
|
|
155
|
+
* `.code === "alg_mismatch"` (downgrade defense, RFC-ACDP-0008
|
|
156
|
+
* §3.9);
|
|
157
|
+
* 4. the key bytes must decode for `requestedAlg`, else
|
|
158
|
+
* `.code === "malformed_key"`.
|
|
159
|
+
*
|
|
160
|
+
* `requestedAlg` is `"ed25519"` or `"ecdsa-p256"`
|
|
161
|
+
* (`.code === "unsupported_algorithm"` otherwise). `requestedKeyId`
|
|
162
|
+
* is the full DID URL from the signature's `key_id`.
|
|
163
|
+
*/
|
|
164
|
+
keyForAlgorithm(requestedKeyId: string, requestedAlg: string): ResolvedDidKey
|
|
165
|
+
}
|
|
166
|
+
/** RFC 8785 canonicalization utilities. All methods are static. */
|
|
167
|
+
export declare class AcdpCanonicalizer {
|
|
168
|
+
/**
|
|
169
|
+
* Canonicalize a JSON document to its RFC 8785 (JCS) form.
|
|
170
|
+
*
|
|
171
|
+
* * `jsonStr` — any JSON document as a string.
|
|
172
|
+
*
|
|
173
|
+
* Returns the canonical UTF-8 JSON string (sorted object keys, no
|
|
174
|
+
* whitespace, `-0.0` normalized to `0`, ECMAScript number
|
|
175
|
+
* formatting). Throws on malformed JSON, or if the document nests
|
|
176
|
+
* past the canonicalizer's recursion ceiling.
|
|
177
|
+
*/
|
|
178
|
+
static canonicalize(jsonStr: string): string
|
|
179
|
+
/**
|
|
180
|
+
* SHA-256 over the canonical (JCS) form of a JSON document, returned
|
|
181
|
+
* as the ACDP envelope `"sha256:<64-lowercase-hex>"`.
|
|
182
|
+
*
|
|
183
|
+
* This is the hashing primitive behind `content_hash` /
|
|
184
|
+
* `data_ref.content_hash`. It hashes the document *as given* — it
|
|
185
|
+
* does NOT strip the RFC-ACDP-0001 §5.7 exclusion set, so to
|
|
186
|
+
* recompute a body's `content_hash` the caller passes the already
|
|
187
|
+
* producer-controlled object (or uses `AcdpVerifier.verifyContentHash`).
|
|
188
|
+
*/
|
|
189
|
+
static contentHash(jsonStr: string): string
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* An ACDP producer: an Ed25519 signing key and its did:web identity.
|
|
193
|
+
*
|
|
194
|
+
* All methods return wire-ready JSON strings the caller sends via its
|
|
195
|
+
* own HTTP client. No HTTP calls are made inside this class.
|
|
196
|
+
*/
|
|
197
|
+
export declare class AcdpProducer {
|
|
198
|
+
/** Generate a producer with a fresh random Ed25519 key (OsRng). */
|
|
199
|
+
static generate(agentDid: string, keyId: string): AcdpProducer
|
|
200
|
+
/** Construct from a 32-byte Ed25519 seed (deterministic). */
|
|
201
|
+
static fromSeed(seed: Buffer, agentDid: string, keyId: string): AcdpProducer
|
|
202
|
+
/** The producer's DID (`did:web:…`). */
|
|
203
|
+
get agentDid(): string
|
|
204
|
+
/** The producer's signing-key DID URL (`did:web:…#key-1`). */
|
|
205
|
+
get keyId(): string
|
|
206
|
+
/**
|
|
207
|
+
* Raw Ed25519 public key as standard base64 (44 chars with padding).
|
|
208
|
+
* Use this to populate a did:web verification method.
|
|
209
|
+
*/
|
|
210
|
+
get publicKeyB64(): string
|
|
211
|
+
/**
|
|
212
|
+
* The raw 32-byte seed, for storage in a key vault. Returns a
|
|
213
|
+
* fresh `Buffer` each call — JS owns the bytes.
|
|
214
|
+
*/
|
|
215
|
+
seedBytes(): Buffer
|
|
216
|
+
/**
|
|
217
|
+
* Build and sign a first-version PublishRequest. Returns the
|
|
218
|
+
* wire JSON string.
|
|
219
|
+
*/
|
|
220
|
+
buildPublishRequest(opts: PublishOpts): string
|
|
221
|
+
/**
|
|
222
|
+
* Build and sign a supersession PublishRequest from a previous
|
|
223
|
+
* version's `Body` JSON. Version is propagated automatically
|
|
224
|
+
* (`previous.version + 1`) and `lineage_id` is carried forward.
|
|
225
|
+
*/
|
|
226
|
+
buildSupersedeRequest(previousBodyJson: string, opts: SupersedeOpts): string
|
|
227
|
+
/**
|
|
228
|
+
* Sign a registry auth-challenge `signingInput` string. Returns
|
|
229
|
+
* the base64-encoded Ed25519 signature (88 chars with padding).
|
|
230
|
+
* Used by the ACDP registry's bearer-token flow.
|
|
231
|
+
*/
|
|
232
|
+
signChallenge(signingInput: string): string
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* An ACDP producer signing with ECDSA-P256 (`ecdsa-p256`) instead of
|
|
236
|
+
* the Ed25519 baseline.
|
|
237
|
+
*
|
|
238
|
+
* Mirrors [`AcdpProducer`] exactly — same JSON-in/JSON-out surface and
|
|
239
|
+
* the same `PublishOpts` / `SupersedeOpts` shapes — but emits
|
|
240
|
+
* `signature.algorithm = "ecdsa-p256"` and the IEEE 1363 `r‖s` wire
|
|
241
|
+
* form. Use this when the producer's `did:web` verification method
|
|
242
|
+
* declares a P-256 key. The DID document MUST declare the P-256
|
|
243
|
+
* algorithm so consumers don't reject the signature on
|
|
244
|
+
* algorithm-downgrade grounds (RFC-ACDP-0008 §3.9); use
|
|
245
|
+
* [`AcdpP256Producer::did_verification_method`] to mint that entry.
|
|
246
|
+
*/
|
|
247
|
+
export declare class AcdpP256Producer {
|
|
248
|
+
/** Generate a producer with a fresh random P-256 key (OsRng). */
|
|
249
|
+
static generate(agentDid: string, keyId: string): AcdpP256Producer
|
|
250
|
+
/**
|
|
251
|
+
* Construct from a 32-byte P-256 private scalar (deterministic).
|
|
252
|
+
* Throws if the bytes are not exactly 32 or are not a valid scalar.
|
|
253
|
+
*/
|
|
254
|
+
static fromSeed(seed: Buffer, agentDid: string, keyId: string): AcdpP256Producer
|
|
255
|
+
/** The producer's DID (`did:web:…`). */
|
|
256
|
+
get agentDid(): string
|
|
257
|
+
/** The producer's signing-key DID URL (`did:web:…#key-1`). */
|
|
258
|
+
get keyId(): string
|
|
259
|
+
/**
|
|
260
|
+
* SEC1-uncompressed public key (`0x04 || x || y`, 65 bytes) as
|
|
261
|
+
* standard base64. Use this to populate a did:web `JsonWebKey2020`
|
|
262
|
+
* verification method.
|
|
263
|
+
*/
|
|
264
|
+
get publicKeySec1B64(): string
|
|
265
|
+
/**
|
|
266
|
+
* The producer's P-256 public key as a JWK
|
|
267
|
+
* (`{"kty":"EC","crv":"P-256","x":…,"y":…}`), returned as a JSON
|
|
268
|
+
* object string. Drop this straight into a did:web `JsonWebKey2020`
|
|
269
|
+
* verification method's `publicKeyJwk`.
|
|
270
|
+
*/
|
|
271
|
+
get publicKeyJwk(): string
|
|
272
|
+
/**
|
|
273
|
+
* A complete `verificationMethod` entry (JSON object string) for a
|
|
274
|
+
* did:web DID document, of type `JsonWebKey2020`.
|
|
275
|
+
*
|
|
276
|
+
* * `methodId` — the full DID URL for this key (e.g.
|
|
277
|
+
* `"did:web:agents.example.com:alice#key-1"`).
|
|
278
|
+
* * `controller` — the bare DID that owns the key (no fragment).
|
|
279
|
+
*
|
|
280
|
+
* Consumers resolve the signature algorithm from this entry, so
|
|
281
|
+
* publishing it is what keeps a P-256 signature from being rejected
|
|
282
|
+
* on algorithm-downgrade grounds (RFC-ACDP-0008 §3.9).
|
|
283
|
+
*/
|
|
284
|
+
didVerificationMethod(methodId: string, controller: string): string
|
|
285
|
+
/**
|
|
286
|
+
* The raw 32-byte private scalar, for storage in a key vault.
|
|
287
|
+
* Returns a fresh `Buffer` each call — JS owns the bytes.
|
|
288
|
+
*/
|
|
289
|
+
seedBytes(): Buffer
|
|
290
|
+
/**
|
|
291
|
+
* Build and sign a first-version PublishRequest. Returns the wire
|
|
292
|
+
* JSON string. Same surface as [`AcdpProducer::build_publish_request`];
|
|
293
|
+
* only the signature algorithm differs.
|
|
294
|
+
*/
|
|
295
|
+
buildPublishRequest(opts: PublishOpts): string
|
|
296
|
+
/**
|
|
297
|
+
* Build and sign a supersession PublishRequest from a previous
|
|
298
|
+
* version's `Body` JSON. Same semantics as
|
|
299
|
+
* [`AcdpProducer::build_supersede_request`].
|
|
300
|
+
*/
|
|
301
|
+
buildSupersedeRequest(previousBodyJson: string, opts: SupersedeOpts): string
|
|
302
|
+
/**
|
|
303
|
+
* Sign a registry auth-challenge `signingInput` string with the
|
|
304
|
+
* producer's P-256 key. Returns the base64 IEEE 1363 signature.
|
|
305
|
+
*/
|
|
306
|
+
signChallenge(signingInput: string): string
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* An SSRF policy: the synchronous classification half of the Rust
|
|
310
|
+
* `SsrfPolicy`, exposed verdict-only (no DNS, no sockets).
|
|
311
|
+
*/
|
|
312
|
+
export declare class AcdpSsrfPolicy {
|
|
313
|
+
/**
|
|
314
|
+
* The production policy: HTTPS-only, IP literals rejected, every
|
|
315
|
+
* private / loopback / link-local / IMDS / multicast / reserved
|
|
316
|
+
* range forbidden.
|
|
317
|
+
*/
|
|
318
|
+
static production(): AcdpSsrfPolicy
|
|
319
|
+
/**
|
|
320
|
+
* A test-only policy that additionally permits loopback
|
|
321
|
+
* (`127.0.0.0/8` / `::1`) so a test harness can target a local
|
|
322
|
+
* listener. Never use in production.
|
|
323
|
+
*/
|
|
324
|
+
static allowTestLoopback(): AcdpSsrfPolicy
|
|
325
|
+
/**
|
|
326
|
+
* Validate a URL: scheme (HTTPS-only), IP-literal rejection, and —
|
|
327
|
+
* for literal hosts — per-IP range filtering.
|
|
328
|
+
*
|
|
329
|
+
* Resolves on success. Throws an `Error` whose `.code` is the stable
|
|
330
|
+
* reason on a policy violation.
|
|
331
|
+
*/
|
|
332
|
+
checkUrl(url: string): void
|
|
333
|
+
/**
|
|
334
|
+
* Validate a single already-resolved IP address (IPv4 or IPv6
|
|
335
|
+
* string). This is the per-address predicate the host loops over
|
|
336
|
+
* after resolving DNS itself — rejecting the whole answer set if any
|
|
337
|
+
* address fails (the mixed-answer rule stays in the host).
|
|
338
|
+
*
|
|
339
|
+
* Resolves on success. Throws with `.code === "invalid_ip"` if `ip`
|
|
340
|
+
* is not a valid address, or with the stable range reason if it
|
|
341
|
+
* falls in a forbidden range.
|
|
342
|
+
*/
|
|
343
|
+
checkIp(ip: string): void
|
|
344
|
+
/**
|
|
345
|
+
* Validate that a redirect target stays within the origin's fetch
|
|
346
|
+
* authority — identical scheme, host, and effective port (an
|
|
347
|
+
* explicit `:443` equals the implicit https default).
|
|
348
|
+
*
|
|
349
|
+
* Resolves on success. Throws with `.code === "cross_authority"`
|
|
350
|
+
* when the authority differs.
|
|
351
|
+
*/
|
|
352
|
+
checkRedirectAuthority(fromUrl: string, toUrl: string): void
|
|
353
|
+
}
|
|
354
|
+
/** Consumer-side verification utilities. All methods are static. */
|
|
355
|
+
export declare class AcdpVerifier {
|
|
356
|
+
/**
|
|
357
|
+
* Verify that a body's `content_hash` matches the SHA-256 over
|
|
358
|
+
* its JCS-canonicalized producer-controlled fields.
|
|
359
|
+
*
|
|
360
|
+
* * `bodyJson` — the `body` object from a `FullContext` retrieval
|
|
361
|
+
* (or the `PublishRequest` itself — both share the §5.7 layout).
|
|
362
|
+
* * `expectedHash` — the `body.content_hash` string
|
|
363
|
+
* (`"sha256:<64-hex>"`).
|
|
364
|
+
*
|
|
365
|
+
* Returns `true` on success; throws on mismatch or bad JSON.
|
|
366
|
+
*/
|
|
367
|
+
static verifyContentHash(bodyJson: string, expectedHash: string): boolean
|
|
368
|
+
/**
|
|
369
|
+
* Verify an Ed25519 signature over a `content_hash` string.
|
|
370
|
+
*
|
|
371
|
+
* The signing input per RFC-ACDP-0001 §5.8 is the ASCII bytes of
|
|
372
|
+
* the full `"sha256:<hex>"` string — NOT the raw 32-byte digest.
|
|
373
|
+
*
|
|
374
|
+
* * `pubKeyB64` — standard base64 (padded) of the 32-byte raw
|
|
375
|
+
* Ed25519 public key (same shape as
|
|
376
|
+
* `AcdpProducer.publicKeyB64`).
|
|
377
|
+
* * `sigB64` — the `body.signature.value` field from the wire
|
|
378
|
+
* format.
|
|
379
|
+
* * `contentHash` — the `body.content_hash` string.
|
|
380
|
+
*
|
|
381
|
+
* Returns `true` on success; throws on failure.
|
|
382
|
+
*/
|
|
383
|
+
static verifySignature(pubKeyB64: string, sigB64: string, contentHash: string): boolean
|
|
384
|
+
/**
|
|
385
|
+
* Verify an ECDSA-P256 signature over a `content_hash` string.
|
|
386
|
+
*
|
|
387
|
+
* The counterpart to `AcdpP256Producer` signing. The signing input
|
|
388
|
+
* per RFC-ACDP-0001 §5.8 is the ASCII bytes of the full
|
|
389
|
+
* `"sha256:<hex>"` string — NOT the raw 32-byte digest. The wire
|
|
390
|
+
* signature is IEEE 1363 `r‖s` (64 bytes, base64), NOT DER.
|
|
391
|
+
*
|
|
392
|
+
* * `pubKeySec1B64` — standard base64 of the 65-byte
|
|
393
|
+
* SEC1-uncompressed public key (`0x04 || x || y`), the same shape
|
|
394
|
+
* as `AcdpP256Producer.publicKeySec1B64`.
|
|
395
|
+
* * `sigB64` — the `body.signature.value` field from the wire
|
|
396
|
+
* format (88-char base64 of the 64-byte `r‖s`).
|
|
397
|
+
* * `contentHash` — the `body.content_hash` string.
|
|
398
|
+
*
|
|
399
|
+
* Returns `true` on success; throws on failure.
|
|
400
|
+
*/
|
|
401
|
+
static verifySignatureP256(pubKeySec1B64: string, sigB64: string, contentHash: string): boolean
|
|
402
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/* prettier-ignore */
|
|
4
|
+
|
|
5
|
+
/* auto-generated by NAPI-RS */
|
|
6
|
+
|
|
7
|
+
const { existsSync, readFileSync } = require('fs')
|
|
8
|
+
const { join } = require('path')
|
|
9
|
+
|
|
10
|
+
const { platform, arch } = process
|
|
11
|
+
|
|
12
|
+
let nativeBinding = null
|
|
13
|
+
let localFileExisted = false
|
|
14
|
+
let loadError = null
|
|
15
|
+
|
|
16
|
+
function isMusl() {
|
|
17
|
+
// For Node 10
|
|
18
|
+
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
19
|
+
try {
|
|
20
|
+
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
|
21
|
+
return readFileSync(lddPath, 'utf8').includes('musl')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
const { glibcVersionRuntime } = process.report.getReport().header
|
|
27
|
+
return !glibcVersionRuntime
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
switch (platform) {
|
|
32
|
+
case 'android':
|
|
33
|
+
switch (arch) {
|
|
34
|
+
case 'arm64':
|
|
35
|
+
localFileExisted = existsSync(join(__dirname, 'acdp.android-arm64.node'))
|
|
36
|
+
try {
|
|
37
|
+
if (localFileExisted) {
|
|
38
|
+
nativeBinding = require('./acdp.android-arm64.node')
|
|
39
|
+
} else {
|
|
40
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-android-arm64')
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
loadError = e
|
|
44
|
+
}
|
|
45
|
+
break
|
|
46
|
+
case 'arm':
|
|
47
|
+
localFileExisted = existsSync(join(__dirname, 'acdp.android-arm-eabi.node'))
|
|
48
|
+
try {
|
|
49
|
+
if (localFileExisted) {
|
|
50
|
+
nativeBinding = require('./acdp.android-arm-eabi.node')
|
|
51
|
+
} else {
|
|
52
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-android-arm-eabi')
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
loadError = e
|
|
56
|
+
}
|
|
57
|
+
break
|
|
58
|
+
default:
|
|
59
|
+
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
60
|
+
}
|
|
61
|
+
break
|
|
62
|
+
case 'win32':
|
|
63
|
+
switch (arch) {
|
|
64
|
+
case 'x64':
|
|
65
|
+
localFileExisted = existsSync(
|
|
66
|
+
join(__dirname, 'acdp.win32-x64-msvc.node')
|
|
67
|
+
)
|
|
68
|
+
try {
|
|
69
|
+
if (localFileExisted) {
|
|
70
|
+
nativeBinding = require('./acdp.win32-x64-msvc.node')
|
|
71
|
+
} else {
|
|
72
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-win32-x64-msvc')
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
loadError = e
|
|
76
|
+
}
|
|
77
|
+
break
|
|
78
|
+
case 'ia32':
|
|
79
|
+
localFileExisted = existsSync(
|
|
80
|
+
join(__dirname, 'acdp.win32-ia32-msvc.node')
|
|
81
|
+
)
|
|
82
|
+
try {
|
|
83
|
+
if (localFileExisted) {
|
|
84
|
+
nativeBinding = require('./acdp.win32-ia32-msvc.node')
|
|
85
|
+
} else {
|
|
86
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-win32-ia32-msvc')
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
loadError = e
|
|
90
|
+
}
|
|
91
|
+
break
|
|
92
|
+
case 'arm64':
|
|
93
|
+
localFileExisted = existsSync(
|
|
94
|
+
join(__dirname, 'acdp.win32-arm64-msvc.node')
|
|
95
|
+
)
|
|
96
|
+
try {
|
|
97
|
+
if (localFileExisted) {
|
|
98
|
+
nativeBinding = require('./acdp.win32-arm64-msvc.node')
|
|
99
|
+
} else {
|
|
100
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-win32-arm64-msvc')
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
loadError = e
|
|
104
|
+
}
|
|
105
|
+
break
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
case 'darwin':
|
|
111
|
+
localFileExisted = existsSync(join(__dirname, 'acdp.darwin-universal.node'))
|
|
112
|
+
try {
|
|
113
|
+
if (localFileExisted) {
|
|
114
|
+
nativeBinding = require('./acdp.darwin-universal.node')
|
|
115
|
+
} else {
|
|
116
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-darwin-universal')
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
} catch {}
|
|
120
|
+
switch (arch) {
|
|
121
|
+
case 'x64':
|
|
122
|
+
localFileExisted = existsSync(join(__dirname, 'acdp.darwin-x64.node'))
|
|
123
|
+
try {
|
|
124
|
+
if (localFileExisted) {
|
|
125
|
+
nativeBinding = require('./acdp.darwin-x64.node')
|
|
126
|
+
} else {
|
|
127
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-darwin-x64')
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
loadError = e
|
|
131
|
+
}
|
|
132
|
+
break
|
|
133
|
+
case 'arm64':
|
|
134
|
+
localFileExisted = existsSync(
|
|
135
|
+
join(__dirname, 'acdp.darwin-arm64.node')
|
|
136
|
+
)
|
|
137
|
+
try {
|
|
138
|
+
if (localFileExisted) {
|
|
139
|
+
nativeBinding = require('./acdp.darwin-arm64.node')
|
|
140
|
+
} else {
|
|
141
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-darwin-arm64')
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
loadError = e
|
|
145
|
+
}
|
|
146
|
+
break
|
|
147
|
+
default:
|
|
148
|
+
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
|
149
|
+
}
|
|
150
|
+
break
|
|
151
|
+
case 'freebsd':
|
|
152
|
+
if (arch !== 'x64') {
|
|
153
|
+
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
|
154
|
+
}
|
|
155
|
+
localFileExisted = existsSync(join(__dirname, 'acdp.freebsd-x64.node'))
|
|
156
|
+
try {
|
|
157
|
+
if (localFileExisted) {
|
|
158
|
+
nativeBinding = require('./acdp.freebsd-x64.node')
|
|
159
|
+
} else {
|
|
160
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-freebsd-x64')
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
loadError = e
|
|
164
|
+
}
|
|
165
|
+
break
|
|
166
|
+
case 'linux':
|
|
167
|
+
switch (arch) {
|
|
168
|
+
case 'x64':
|
|
169
|
+
if (isMusl()) {
|
|
170
|
+
localFileExisted = existsSync(
|
|
171
|
+
join(__dirname, 'acdp.linux-x64-musl.node')
|
|
172
|
+
)
|
|
173
|
+
try {
|
|
174
|
+
if (localFileExisted) {
|
|
175
|
+
nativeBinding = require('./acdp.linux-x64-musl.node')
|
|
176
|
+
} else {
|
|
177
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-x64-musl')
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
loadError = e
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
localFileExisted = existsSync(
|
|
184
|
+
join(__dirname, 'acdp.linux-x64-gnu.node')
|
|
185
|
+
)
|
|
186
|
+
try {
|
|
187
|
+
if (localFileExisted) {
|
|
188
|
+
nativeBinding = require('./acdp.linux-x64-gnu.node')
|
|
189
|
+
} else {
|
|
190
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-x64-gnu')
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {
|
|
193
|
+
loadError = e
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break
|
|
197
|
+
case 'arm64':
|
|
198
|
+
if (isMusl()) {
|
|
199
|
+
localFileExisted = existsSync(
|
|
200
|
+
join(__dirname, 'acdp.linux-arm64-musl.node')
|
|
201
|
+
)
|
|
202
|
+
try {
|
|
203
|
+
if (localFileExisted) {
|
|
204
|
+
nativeBinding = require('./acdp.linux-arm64-musl.node')
|
|
205
|
+
} else {
|
|
206
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-arm64-musl')
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
loadError = e
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
localFileExisted = existsSync(
|
|
213
|
+
join(__dirname, 'acdp.linux-arm64-gnu.node')
|
|
214
|
+
)
|
|
215
|
+
try {
|
|
216
|
+
if (localFileExisted) {
|
|
217
|
+
nativeBinding = require('./acdp.linux-arm64-gnu.node')
|
|
218
|
+
} else {
|
|
219
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-arm64-gnu')
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
loadError = e
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
break
|
|
226
|
+
case 'arm':
|
|
227
|
+
if (isMusl()) {
|
|
228
|
+
localFileExisted = existsSync(
|
|
229
|
+
join(__dirname, 'acdp.linux-arm-musleabihf.node')
|
|
230
|
+
)
|
|
231
|
+
try {
|
|
232
|
+
if (localFileExisted) {
|
|
233
|
+
nativeBinding = require('./acdp.linux-arm-musleabihf.node')
|
|
234
|
+
} else {
|
|
235
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-arm-musleabihf')
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
loadError = e
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
localFileExisted = existsSync(
|
|
242
|
+
join(__dirname, 'acdp.linux-arm-gnueabihf.node')
|
|
243
|
+
)
|
|
244
|
+
try {
|
|
245
|
+
if (localFileExisted) {
|
|
246
|
+
nativeBinding = require('./acdp.linux-arm-gnueabihf.node')
|
|
247
|
+
} else {
|
|
248
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-arm-gnueabihf')
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
loadError = e
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
break
|
|
255
|
+
case 'riscv64':
|
|
256
|
+
if (isMusl()) {
|
|
257
|
+
localFileExisted = existsSync(
|
|
258
|
+
join(__dirname, 'acdp.linux-riscv64-musl.node')
|
|
259
|
+
)
|
|
260
|
+
try {
|
|
261
|
+
if (localFileExisted) {
|
|
262
|
+
nativeBinding = require('./acdp.linux-riscv64-musl.node')
|
|
263
|
+
} else {
|
|
264
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-riscv64-musl')
|
|
265
|
+
}
|
|
266
|
+
} catch (e) {
|
|
267
|
+
loadError = e
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
localFileExisted = existsSync(
|
|
271
|
+
join(__dirname, 'acdp.linux-riscv64-gnu.node')
|
|
272
|
+
)
|
|
273
|
+
try {
|
|
274
|
+
if (localFileExisted) {
|
|
275
|
+
nativeBinding = require('./acdp.linux-riscv64-gnu.node')
|
|
276
|
+
} else {
|
|
277
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-riscv64-gnu')
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
loadError = e
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
break
|
|
284
|
+
case 's390x':
|
|
285
|
+
localFileExisted = existsSync(
|
|
286
|
+
join(__dirname, 'acdp.linux-s390x-gnu.node')
|
|
287
|
+
)
|
|
288
|
+
try {
|
|
289
|
+
if (localFileExisted) {
|
|
290
|
+
nativeBinding = require('./acdp.linux-s390x-gnu.node')
|
|
291
|
+
} else {
|
|
292
|
+
nativeBinding = require('@agentcontextdistributionprotocol/acdp-linux-s390x-gnu')
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
loadError = e
|
|
296
|
+
}
|
|
297
|
+
break
|
|
298
|
+
default:
|
|
299
|
+
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
|
300
|
+
}
|
|
301
|
+
break
|
|
302
|
+
default:
|
|
303
|
+
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!nativeBinding) {
|
|
307
|
+
if (loadError) {
|
|
308
|
+
throw loadError
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`Failed to load native binding`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const { AcdpDid, AcdpDidDocument, AcdpCanonicalizer, AcdpProducer, AcdpP256Producer, AcdpSsrfPolicy, AcdpVerifier } = nativeBinding
|
|
314
|
+
|
|
315
|
+
module.exports.AcdpDid = AcdpDid
|
|
316
|
+
module.exports.AcdpDidDocument = AcdpDidDocument
|
|
317
|
+
module.exports.AcdpCanonicalizer = AcdpCanonicalizer
|
|
318
|
+
module.exports.AcdpProducer = AcdpProducer
|
|
319
|
+
module.exports.AcdpP256Producer = AcdpP256Producer
|
|
320
|
+
module.exports.AcdpSsrfPolicy = AcdpSsrfPolicy
|
|
321
|
+
module.exports.AcdpVerifier = AcdpVerifier
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentcontextdistributionprotocol/acdp",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Agent Context Distribution Protocol — Node.js SDK",
|
|
5
|
+
"license": "MIT OR Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/agentcontextdistributionprotocol/acdp-rs.git",
|
|
9
|
+
"directory": "bindings/acdp-node"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/agentcontextdistributionprotocol/acdp-rs/tree/main/bindings/acdp-node",
|
|
12
|
+
"main": "index.js",
|
|
13
|
+
"types": "index.d.ts",
|
|
14
|
+
"type": "commonjs",
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"index.d.ts"
|
|
18
|
+
],
|
|
19
|
+
"napi": {
|
|
20
|
+
"name": "acdp",
|
|
21
|
+
"triples": {
|
|
22
|
+
"defaults": false,
|
|
23
|
+
"additional": [
|
|
24
|
+
"x86_64-apple-darwin",
|
|
25
|
+
"aarch64-apple-darwin",
|
|
26
|
+
"x86_64-unknown-linux-gnu",
|
|
27
|
+
"aarch64-unknown-linux-gnu"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "napi build --platform --release",
|
|
33
|
+
"build:debug": "napi build --platform",
|
|
34
|
+
"artifacts": "napi artifacts",
|
|
35
|
+
"prepublishOnly": "napi prepublish -t npm",
|
|
36
|
+
"version": "napi version",
|
|
37
|
+
"test": "node --test tests/*.mjs"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@napi-rs/cli": "^2.18.0"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=16"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"registry": "https://registry.npmjs.org/"
|
|
48
|
+
},
|
|
49
|
+
"optionalDependencies": {
|
|
50
|
+
"@agentcontextdistributionprotocol/acdp-darwin-x64": "0.3.0",
|
|
51
|
+
"@agentcontextdistributionprotocol/acdp-darwin-arm64": "0.3.0",
|
|
52
|
+
"@agentcontextdistributionprotocol/acdp-linux-x64-gnu": "0.3.0",
|
|
53
|
+
"@agentcontextdistributionprotocol/acdp-linux-arm64-gnu": "0.3.0"
|
|
54
|
+
}
|
|
55
|
+
}
|