@opendatalabs/vana-sdk 4.1.0 → 4.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.
@@ -21,7 +21,52 @@ export type JobOperation = (typeof JOB_OPERATIONS)[number];
21
21
  export declare const JOB_STATES: readonly ["queued", "claimed", "running", "completed", "failed", "expired", "cancelled"];
22
22
  export type JobState = (typeof JOB_STATES)[number];
23
23
  /** Payment lifecycle recorded for a queued job. */
24
- export type PaymentState = "none" | "reserved" | "settled";
24
+ /**
25
+ * What actually happened to the money for one read.
26
+ *
27
+ * `none` is the pre-fee literal every job carried before job fees and stays in
28
+ * the union for those rows. `free` is a chain whose `data_access` fee is
29
+ * disabled or zero — priced honestly at nothing, not a placeholder. `unbilled`
30
+ * is a read the Gateway priced but did not charge, either during the fee
31
+ * rollout or on a path with no payer.
32
+ */
33
+ export type PaymentState = "none" | "free" | "unbilled" | "reserved" | "settling" | "settled" | "released";
34
+ /**
35
+ * The builder's signed authorization for the price of one read.
36
+ *
37
+ * The same EIP-712 `GenericPayment` a legacy Personal Server read signs into
38
+ * its `X-PAYMENT` header, over `opType: "job_access"` and
39
+ * `opId: keccak256(jobId)`. The Gateway stores it verbatim on the payment row.
40
+ */
41
+ export interface JobPaymentAuthorization {
42
+ signature: Hex;
43
+ /** uint256 decimal; must equal the Gateway's quote for this chain. */
44
+ amount: string;
45
+ asset: Address;
46
+ /** uint256 decimal, unique per (payer, kind). */
47
+ paymentNonce: string;
48
+ }
49
+ /** The price of one delivered read, as `GET /v1/jobs/quote` reports it. */
50
+ export interface JobQuote {
51
+ chainId: number;
52
+ /** uint256 decimal; "0" when this chain charges nothing. */
53
+ price: string;
54
+ asset: Address;
55
+ /** False when the fee is disabled or zero, so no payment is needed. */
56
+ payable: boolean;
57
+ /** Whether the Gateway refuses an unpaid submission today. */
58
+ enforced: boolean;
59
+ }
60
+ /** RecordDataAccess receipt fields plus the enclave signature over them. */
61
+ export interface JobAccessRecord {
62
+ dataPointId: Hex;
63
+ /** uint256 decimal — the data-point version actually served. */
64
+ version: string;
65
+ accessor: Address;
66
+ /** bytes32 per-event replay nonce; the contract pins it. */
67
+ recordId: Hex;
68
+ signature: Hex;
69
+ }
25
70
  export declare const DEFAULT_LEASE_SECONDS = 30;
26
71
  export declare const MAX_LEASE_SECONDS = 300;
27
72
  export declare const MAX_ATTEMPTS = 3;
@@ -64,6 +109,18 @@ export interface JobSubmission {
64
109
  deadline?: string;
65
110
  /** Base64 ECIES from `sealJobRequest`. */
66
111
  requestCiphertext: string;
112
+ /**
113
+ * The most the builder accepts for this read, uint256 decimal. The Gateway
114
+ * refuses a quote above it rather than charging a price nobody agreed to.
115
+ *
116
+ * @remarks
117
+ * This submission is authenticated by a Web3Signed body hash, not by an
118
+ * EIP-712 struct, so these three fields are covered by the builder's existing
119
+ * request signature. No typed-data version changes.
120
+ */
121
+ maxPrice?: string;
122
+ priceAsset?: Address;
123
+ payment?: JobPaymentAuthorization;
67
124
  }
68
125
  /** Where a completed job's sealed result lives. Bytes never transit the Gateway. */
69
126
  export interface ResultHandle {
@@ -89,6 +146,12 @@ export interface JobStatus {
89
146
  pinnedVersion: string | null;
90
147
  attempt: number;
91
148
  price: string;
149
+ /**
150
+ * Null exactly when the read is free; nothing owed is owed to nobody.
151
+ * Optional so a consumer's existing `JobStatus` fixtures keep compiling; the
152
+ * Gateway always sends it.
153
+ */
154
+ priceAsset?: Address | null;
92
155
  payer: "builder";
93
156
  paymentState: PaymentState;
94
157
  createdAt: string;
@@ -139,6 +202,13 @@ export interface CompleteRequest {
139
202
  resultSize: number;
140
203
  /** `jobresults/{chainId}/{jobId}`. */
141
204
  resultObjectKey: string;
205
+ /**
206
+ * Server-signed delivery receipt for the read this job served, minted by the
207
+ * node agent with the owner's enclave wallet. Binds the reserved payment to
208
+ * an on-chain `recordDataAccess`, exactly as the legacy paid read does.
209
+ * Required whenever the job reserved a fee.
210
+ */
211
+ accessRecord?: JobAccessRecord;
142
212
  }
143
213
  /** Request body for `POST /v1/jobs/:id/fail`. */
144
214
  export interface FailRequest {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\nexport type PaymentState = \"none\" | \"reserved\" | \"settled\";\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":"AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
1
+ {"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\n/**\n * What actually happened to the money for one read.\n *\n * `none` is the pre-fee literal every job carried before job fees and stays in\n * the union for those rows. `free` is a chain whose `data_access` fee is\n * disabled or zero — priced honestly at nothing, not a placeholder. `unbilled`\n * is a read the Gateway priced but did not charge, either during the fee\n * rollout or on a path with no payer.\n */\nexport type PaymentState =\n | \"none\"\n | \"free\"\n | \"unbilled\"\n | \"reserved\"\n | \"settling\"\n | \"settled\"\n | \"released\";\n\n/**\n * The builder's signed authorization for the price of one read.\n *\n * The same EIP-712 `GenericPayment` a legacy Personal Server read signs into\n * its `X-PAYMENT` header, over `opType: \"job_access\"` and\n * `opId: keccak256(jobId)`. The Gateway stores it verbatim on the payment row.\n */\nexport interface JobPaymentAuthorization {\n signature: Hex;\n /** uint256 decimal; must equal the Gateway's quote for this chain. */\n amount: string;\n asset: Address;\n /** uint256 decimal, unique per (payer, kind). */\n paymentNonce: string;\n}\n\n/** The price of one delivered read, as `GET /v1/jobs/quote` reports it. */\nexport interface JobQuote {\n chainId: number;\n /** uint256 decimal; \"0\" when this chain charges nothing. */\n price: string;\n asset: Address;\n /** False when the fee is disabled or zero, so no payment is needed. */\n payable: boolean;\n /** Whether the Gateway refuses an unpaid submission today. */\n enforced: boolean;\n}\n\n/** RecordDataAccess receipt fields plus the enclave signature over them. */\nexport interface JobAccessRecord {\n dataPointId: Hex;\n /** uint256 decimal — the data-point version actually served. */\n version: string;\n accessor: Address;\n /** bytes32 per-event replay nonce; the contract pins it. */\n recordId: Hex;\n signature: Hex;\n}\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n /**\n * The most the builder accepts for this read, uint256 decimal. The Gateway\n * refuses a quote above it rather than charging a price nobody agreed to.\n *\n * @remarks\n * This submission is authenticated by a Web3Signed body hash, not by an\n * EIP-712 struct, so these three fields are covered by the builder's existing\n * request signature. No typed-data version changes.\n */\n maxPrice?: string;\n priceAsset?: Address;\n payment?: JobPaymentAuthorization;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n /**\n * Null exactly when the read is free; nothing owed is owed to nobody.\n * Optional so a consumer's existing `JobStatus` fixtures keep compiling; the\n * Gateway always sends it.\n */\n priceAsset?: Address | null;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n /**\n * Server-signed delivery receipt for the read this job served, minted by the\n * node agent with the owner's enclave wallet. Binds the reserved payment to\n * an on-chain `recordDataAccess`, exactly as the legacy paid read does.\n * Required whenever the job reserved a fee.\n */\n accessRecord?: JobAccessRecord;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":"AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA2DO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/vana-sdk",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "A TypeScript library for interacting with Vana Network smart contracts.",
5
5
  "publishConfig": {
6
6
  "access": "public"