@prismnetwork/agent-sdk 0.1.0 → 0.2.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 +3 -3
- package/package.json +29 -8
- package/prism.mjs +41 -17
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@ Headless GPU leasing on [Prism Network](https://prismnetwork.tech) for autonomou
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
Not yet published to npm. Until it is, install it from the repo alongside its `viem` peer dependency:
|
|
8
|
-
|
|
9
7
|
```
|
|
10
|
-
npm install /
|
|
8
|
+
npm install @prismnetwork/agent-sdk viem
|
|
11
9
|
```
|
|
12
10
|
|
|
11
|
+
`viem` is a peer dependency.
|
|
12
|
+
|
|
13
13
|
## Use
|
|
14
14
|
|
|
15
15
|
```js
|
package/package.json
CHANGED
|
@@ -1,21 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prismnetwork/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Headless GPU leasing on Prism Network for wallet-holding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "prism.mjs",
|
|
7
|
-
"exports": {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./prism.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"prism.mjs",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=20"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"viem": "^2"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"prism",
|
|
22
|
+
"gpu",
|
|
23
|
+
"agent",
|
|
24
|
+
"web3",
|
|
25
|
+
"usdg",
|
|
26
|
+
"compute",
|
|
27
|
+
"llm"
|
|
28
|
+
],
|
|
12
29
|
"homepage": "https://prismnetwork.tech",
|
|
13
30
|
"repository": {
|
|
14
31
|
"type": "git",
|
|
15
32
|
"url": "git+https://github.com/prismnetwork-tech/prism.git",
|
|
16
33
|
"directory": "sdk"
|
|
17
34
|
},
|
|
18
|
-
"bugs": {
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/prismnetwork-tech/prism/issues"
|
|
37
|
+
},
|
|
19
38
|
"license": "Apache-2.0",
|
|
20
|
-
"publishConfig": {
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
21
42
|
}
|
package/prism.mjs
CHANGED
|
@@ -29,6 +29,10 @@ export const USDG = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168";
|
|
|
29
29
|
export const DEFAULT_IMAGE =
|
|
30
30
|
"docker.io/ollama/ollama@sha256:a61a8fd395dbb931cc8cb1b5da7a2510746575c87113fdc45b647ee59ef7f808";
|
|
31
31
|
|
|
32
|
+
// Weakest to strongest. "open" means the supplier can read everything the
|
|
33
|
+
// workload touches, so an agent handling anything sensitive should raise this.
|
|
34
|
+
export const TRUST_CLASSES = ["open", "isolated", "attested", "confidential"];
|
|
35
|
+
|
|
32
36
|
const CONFIRMATIONS = 12;
|
|
33
37
|
const FETCH_TIMEOUT_MS = 30_000;
|
|
34
38
|
|
|
@@ -42,6 +46,13 @@ const escrowAbi = parseAbi([
|
|
|
42
46
|
"function createLease(bytes32 nodeId, uint32 duration, bytes32 clientReference) returns (uint256)",
|
|
43
47
|
]);
|
|
44
48
|
|
|
49
|
+
function assertTrustClass(value) {
|
|
50
|
+
if (!TRUST_CLASSES.includes(value)) {
|
|
51
|
+
throw new PrismError(400, "invalid_trust_class", { expected: TRUST_CLASSES });
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
function parseBaseUnits(value, field) {
|
|
46
57
|
if (typeof value === "number" && Number.isInteger(value) && value >= 0) return BigInt(value);
|
|
47
58
|
if (typeof value === "string" && /^[0-9]+$/.test(value)) return BigInt(value);
|
|
@@ -96,8 +107,8 @@ export class PrismAgent {
|
|
|
96
107
|
return session;
|
|
97
108
|
}
|
|
98
109
|
|
|
99
|
-
async offers() {
|
|
100
|
-
return this.#proxy("GET", ["offers"]);
|
|
110
|
+
async offers({ minTrust = "open" } = {}) {
|
|
111
|
+
return this.#proxy("GET", ["offers"], { query: { min_trust: assertTrustClass(minTrust) } });
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
async balances() {
|
|
@@ -125,18 +136,21 @@ export class PrismAgent {
|
|
|
125
136
|
}
|
|
126
137
|
}
|
|
127
138
|
|
|
128
|
-
async quote({ image, durationSeconds, minVramMib = 16000, preferredNodeId = null } = {}) {
|
|
139
|
+
async quote({ image, durationSeconds, minVramMib = 16000, preferredNodeId = null, minTrustClass = "open" } = {}) {
|
|
129
140
|
if (typeof image !== "string" || !/@sha256:[0-9a-f]{64}$/.test(image)) {
|
|
130
141
|
throw new PrismError(400, "image_must_be_digest_pinned", { hint: "use ollama@sha256:... or DEFAULT_IMAGE" });
|
|
131
142
|
}
|
|
132
143
|
if (!Number.isInteger(durationSeconds) || durationSeconds <= 0) throw new PrismError(400, "invalid_duration");
|
|
133
144
|
if (!Number.isInteger(minVramMib) || minVramMib <= 0) throw new PrismError(400, "invalid_min_vram_mib");
|
|
134
145
|
return this.#proxy("POST", ["leases", "match"], {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
146
|
+
body: {
|
|
147
|
+
request: {
|
|
148
|
+
image,
|
|
149
|
+
duration_seconds: durationSeconds,
|
|
150
|
+
min_vram_mib: minVramMib,
|
|
151
|
+
preferred_node_id: preferredNodeId,
|
|
152
|
+
min_trust_class: assertTrustClass(minTrustClass),
|
|
153
|
+
},
|
|
140
154
|
},
|
|
141
155
|
});
|
|
142
156
|
}
|
|
@@ -185,9 +199,11 @@ export class PrismAgent {
|
|
|
185
199
|
|
|
186
200
|
async confirm({ quoteId, transactionHash, sshAuthorizedKey }) {
|
|
187
201
|
return this.#proxy("POST", ["leases", "confirm"], {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
body: {
|
|
203
|
+
quote_id: quoteId,
|
|
204
|
+
transaction_hash: transactionHash,
|
|
205
|
+
ssh_authorized_key: sshAuthorizedKey,
|
|
206
|
+
},
|
|
191
207
|
});
|
|
192
208
|
}
|
|
193
209
|
|
|
@@ -202,7 +218,7 @@ export class PrismAgent {
|
|
|
202
218
|
async waitForAccess(leaseId, { timeoutMs = 600_000, intervalMs = 10_000 } = {}) {
|
|
203
219
|
const deadline = Date.now() + timeoutMs;
|
|
204
220
|
while (Date.now() < deadline) {
|
|
205
|
-
const res = await this.#proxy("GET", ["leases", String(leaseId), "access"],
|
|
221
|
+
const res = await this.#proxy("GET", ["leases", String(leaseId), "access"], { raw: true });
|
|
206
222
|
if (res.status === 200) {
|
|
207
223
|
if (!res.body?.ssh_host && res.body?.mode !== "gateway") throw new PrismError(502, "malformed_access");
|
|
208
224
|
return res.body;
|
|
@@ -214,9 +230,16 @@ export class PrismAgent {
|
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
// quote -> ssh keygen -> fund on-chain -> confirm -> wait for access.
|
|
217
|
-
async lease({
|
|
233
|
+
async lease({
|
|
234
|
+
image,
|
|
235
|
+
durationSeconds,
|
|
236
|
+
minVramMib,
|
|
237
|
+
preferredNodeId = null,
|
|
238
|
+
maxDeposit = null,
|
|
239
|
+
minTrustClass = "open",
|
|
240
|
+
} = {}) {
|
|
218
241
|
if (!this.session) await this.authenticate();
|
|
219
|
-
const quote = await this.quote({ image, durationSeconds, minVramMib, preferredNodeId });
|
|
242
|
+
const quote = await this.quote({ image, durationSeconds, minVramMib, preferredNodeId, minTrustClass });
|
|
220
243
|
if (maxDeposit != null && parseBaseUnits(quote.maximum_escrow, "maximum_escrow") > BigInt(maxDeposit)) {
|
|
221
244
|
throw new PrismError(402, "cost_exceeds_max", { required: quote.maximum_escrow, max: String(maxDeposit) });
|
|
222
245
|
}
|
|
@@ -325,9 +348,10 @@ export class PrismAgent {
|
|
|
325
348
|
});
|
|
326
349
|
}
|
|
327
350
|
|
|
328
|
-
async #proxy(method, segments, body = null, raw = false, reauthed = false) {
|
|
351
|
+
async #proxy(method, segments, { body = null, raw = false, query = null, reauthed = false } = {}) {
|
|
329
352
|
if (!this.session) await this.authenticate();
|
|
330
|
-
const
|
|
353
|
+
const search = query ? `?${new URLSearchParams(query)}` : "";
|
|
354
|
+
const res = await this.#fetch(`/api/agent/proxy/${segments.join("/")}${search}`, {
|
|
331
355
|
method,
|
|
332
356
|
body,
|
|
333
357
|
headers: { authorization: `Bearer ${this.session}` },
|
|
@@ -336,7 +360,7 @@ export class PrismAgent {
|
|
|
336
360
|
if (res.status === 401 && !reauthed) {
|
|
337
361
|
this.session = null;
|
|
338
362
|
await this.authenticate();
|
|
339
|
-
return this.#proxy(method, segments, body, raw, true);
|
|
363
|
+
return this.#proxy(method, segments, { body, raw, query, reauthed: true });
|
|
340
364
|
}
|
|
341
365
|
if (raw) return { status: res.status, body: await res.json().catch(() => null) };
|
|
342
366
|
return this.#unwrap(res);
|