stellarium-ts 0.17.0 → 0.18.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.
@@ -9,6 +9,7 @@ export interface FixedPaymentVoucher {
9
9
  expiresAt: string;
10
10
  invocationHash: string;
11
11
  publicKey: string;
12
+ runId?: string;
12
13
  }
13
14
  export interface InferenceVoucher {
14
15
  kind: 'inference';
@@ -23,6 +24,7 @@ export interface InferenceVoucher {
23
24
  expiresAt: string;
24
25
  invocationHash: string;
25
26
  publicKey: string;
27
+ runId?: string;
26
28
  }
27
29
  export interface FixedPaymentSubmission {
28
30
  voucher: FixedPaymentVoucher;
@@ -43,6 +45,23 @@ export type AuthorizeResult = {
43
45
  status: number;
44
46
  error: string;
45
47
  };
48
+ /** What a node sends to authorize a script run billed to an API client. Matches apps/operator/src/runs/runService.ts. */
49
+ export interface RunAuthorizationRequest {
50
+ /** The run token the caller presented. */
51
+ token: string;
52
+ runId: string;
53
+ source: string;
54
+ }
55
+ /** The operator's hold for a run: up to `heldMicroUsd` for the script's tokens and its paid calls together. */
56
+ export type RunAuthorizeResult = {
57
+ ok: true;
58
+ heldMicroUsd: number;
59
+ maxTokens: number;
60
+ } | {
61
+ ok: false;
62
+ status: number;
63
+ error: string;
64
+ };
46
65
  export declare const OPERATOR_MAX_RETRIES = 5;
47
66
  /** Authorization runs while the caller waits, so it gives up much sooner than capture and release. */
48
67
  export declare const AUTHORIZE_MAX_RETRIES = 2;
@@ -67,7 +86,7 @@ export declare class OperatorClient {
67
86
  * enrolls the node first; a node that is already enrolled ignores the token. Throws if the node is
68
87
  * not enrolled or the operator refuses.
69
88
  */
70
- session(enrollmentToken?: string): Promise<{
89
+ session(enrollmentToken?: string, baseUrl?: string): Promise<{
71
90
  organizationId: string;
72
91
  }>;
73
92
  /**
@@ -75,6 +94,22 @@ export declare class OperatorClient {
75
94
  * running the paid function. An unreachable operator is reported as a 503 refusal.
76
95
  */
77
96
  authorize(voucher: FixedPaymentVoucher | InferenceVoucher): Promise<AuthorizeResult>;
97
+ /**
98
+ * Asks the operator to hold a script run's cost on the paying API client's organization, before running
99
+ * it. An unreachable operator is reported as a 503 refusal.
100
+ */
101
+ authorizeRun(request: RunAuthorizationRequest): Promise<RunAuthorizeResult>;
102
+ /** Charges a held run for the tokens its script executed, and releases the rest of its hold. */
103
+ captureRun(request: {
104
+ runId: string;
105
+ tokens: number;
106
+ }): Promise<void>;
107
+ /** Releases a held run without charging it, for a script this node failed to run. */
108
+ releaseRun(request: {
109
+ runId: string;
110
+ }): Promise<void>;
111
+ /** Authorization runs while the caller waits, so it retries briefly and reports an unreachable operator as 503. */
112
+ private hold;
78
113
  /** Charges a held voucher: its amount, or for inference the reported usage capped at the hold. */
79
114
  capture(payment: PaymentSubmission): Promise<void>;
80
115
  /** Releases a held voucher without charging it, for a call that failed or cost nothing. */
@@ -1 +1 @@
1
- {"version":3,"file":"operatorClient.d.ts","sourceRoot":"","sources":["../../src/client/operatorClient.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG,0BAA0B,CAAC;AAEpF,MAAM,WAAW,mBAAmB;IAChC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB,EAAE,MAAM,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC,EAAE,MAAM,CAAC;IAC1C,kCAAkC,EAAE,MAAM,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB,EAAE,MAAM,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACnC,OAAO,EAAE,mBAAmB,CAAC;CAChC;AAED,yHAAyH;AACzH,MAAM,WAAW,0BAA0B;IACvC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,KAAK,EAAE;QACH,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAC;CACL;AAED,2GAA2G;AAC3G,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,sGAAsG;AACtG,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAwB1C,uFAAuF;AACvF,qBAAa,cAAc;IACX,OAAO,CAAC,QAAQ,CAAC,GAAG;IAAsB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAAU,OAAO,CAAC,QAAQ,CAAC,UAAU;IAArH,YAA6B,GAAG,EAAE,MAAM,GAAG,SAAS,EAAmB,SAAS,EAAE,MAAM,EAAmB,UAAU,EAAE,MAAM,EAAI;IAEjI;;;;OAIG;YACW,IAAI;IA+BlB;;;;OAIG;IACG,OAAO,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC,CAM3E;IAED;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAczF;IAED,kGAAkG;IAC5F,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvD;IAED,2FAA2F;IACrF,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;IAED,6HAA6H;YAC/G,MAAM;CAMvB"}
1
+ {"version":3,"file":"operatorClient.d.ts","sourceRoot":"","sources":["../../src/client/operatorClient.ts"],"names":[],"mappings":"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"}
@@ -76,8 +76,9 @@ export class OperatorClient {
76
76
  * enrolls the node first; a node that is already enrolled ignores the token. Throws if the node is
77
77
  * not enrolled or the operator refuses.
78
78
  */
79
- async session(enrollmentToken) {
80
- const response = await this.post('/nodes/session', enrollmentToken ? { enrollmentToken } : {}, OPERATOR_MAX_RETRIES, OPERATOR_BASE_DELAY_MS);
79
+ async session(enrollmentToken, baseUrl) {
80
+ const body = { ...(enrollmentToken ? { enrollmentToken } : {}), ...(baseUrl ? { baseUrl } : {}) };
81
+ const response = await this.post('/nodes/session', body, OPERATOR_MAX_RETRIES, OPERATOR_BASE_DELAY_MS);
81
82
  if (!response.ok) {
82
83
  throw new Error(`Operator refused this node's session: ${await errorMessage(response)}`);
83
84
  }
@@ -88,16 +89,40 @@ export class OperatorClient {
88
89
  * running the paid function. An unreachable operator is reported as a 503 refusal.
89
90
  */
90
91
  async authorize(voucher) {
92
+ const result = await this.hold('/payments/authorize', { voucher });
93
+ return result.ok ? { ok: true } : result;
94
+ }
95
+ /**
96
+ * Asks the operator to hold a script run's cost on the paying API client's organization, before running
97
+ * it. An unreachable operator is reported as a 503 refusal.
98
+ */
99
+ async authorizeRun(request) {
100
+ const result = await this.hold('/runs/authorize', request);
101
+ if (!result.ok)
102
+ return result;
103
+ const { heldMicroUsd, maxTokens } = result.body;
104
+ return { ok: true, heldMicroUsd, maxTokens };
105
+ }
106
+ /** Charges a held run for the tokens its script executed, and releases the rest of its hold. */
107
+ async captureRun(request) {
108
+ await this.settle('/runs/capture', request);
109
+ }
110
+ /** Releases a held run without charging it, for a script this node failed to run. */
111
+ async releaseRun(request) {
112
+ await this.settle('/runs/release', request);
113
+ }
114
+ /** Authorization runs while the caller waits, so it retries briefly and reports an unreachable operator as 503. */
115
+ async hold(path, body) {
91
116
  let response;
92
117
  try {
93
- response = await this.post('/payments/authorize', { voucher }, AUTHORIZE_MAX_RETRIES, AUTHORIZE_BASE_DELAY_MS);
118
+ response = await this.post(path, body, AUTHORIZE_MAX_RETRIES, AUTHORIZE_BASE_DELAY_MS);
94
119
  }
95
120
  catch (error) {
96
- console.error('Failed to reach the operator to authorize a payment', error);
121
+ console.error(`Failed to reach the operator for ${path}`, error);
97
122
  return { ok: false, status: 503, error: 'Payment operator is unavailable' };
98
123
  }
99
124
  if (response.ok) {
100
- return { ok: true };
125
+ return { ok: true, body: await response.json().catch(() => undefined) };
101
126
  }
102
127
  // Retries ran out on a transient failure: the caller may try again later.
103
128
  const status = RETRYABLE_STATUSES.has(response.status) ? 503 : response.status;
@@ -0,0 +1,23 @@
1
+ import { type JWTVerifyGetKey } from "jose";
2
+ /** The `iss` of every run token. Copied from RUN_TOKEN_ISSUER in apps/operator/src/runs/tokens.ts. */
3
+ export declare const RUN_TOKEN_ISSUER = "stellarium";
4
+ /** Who a run token lets run scripts on this node, and which organization pays. */
5
+ export interface RunTokenClaims {
6
+ clientId: string;
7
+ organizationId: string;
8
+ }
9
+ /** Whether `token` has the three base64url segments of a JWT, as opposed to an API key. */
10
+ export declare function looksLikeJwt(token: string): boolean;
11
+ /**
12
+ * Verifies the run tokens API clients get from the operator's `POST /v1/oauth/token`, against the
13
+ * operator's published keys. A token is only valid at the node whose public key is its audience.
14
+ */
15
+ export declare class RunTokenVerifier {
16
+ private readonly nodePublicKey;
17
+ private readonly keys;
18
+ /** `keys` replaces fetching `{operatorUrl}/.well-known/jwks.json`, in tests. */
19
+ constructor(operatorUrl: string, nodePublicKey: string, keys?: JWTVerifyGetKey);
20
+ /** The token's claims, or undefined unless it is signed by the operator, unexpired and addressed to this node. */
21
+ verify(token: string): Promise<RunTokenClaims | undefined>;
22
+ }
23
+ //# sourceMappingURL=runTokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTokens.d.ts","sourceRoot":"","sources":["../../src/client/runTokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,eAAe,EAAa,MAAM,MAAM,CAAC;AAE3E,sGAAsG;AACtG,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAE7C,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAIQ,OAAO,CAAC,QAAQ,CAAC,aAAa;IAH/D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IAEvC,gFAAgF;IAChF,YAAY,WAAW,EAAE,MAAM,EAAmB,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,eAAe,EAG9F;IAED,kHAAkH;IAC5G,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAU/D;CACJ"}
@@ -0,0 +1,34 @@
1
+ import { createRemoteJWKSet, jwtVerify } from "jose";
2
+ /** The `iss` of every run token. Copied from RUN_TOKEN_ISSUER in apps/operator/src/runs/tokens.ts. */
3
+ export const RUN_TOKEN_ISSUER = 'stellarium';
4
+ /** Whether `token` has the three base64url segments of a JWT, as opposed to an API key. */
5
+ export function looksLikeJwt(token) {
6
+ return /^[\w-]+\.[\w-]+\.[\w-]+$/.test(token);
7
+ }
8
+ /**
9
+ * Verifies the run tokens API clients get from the operator's `POST /v1/oauth/token`, against the
10
+ * operator's published keys. A token is only valid at the node whose public key is its audience.
11
+ */
12
+ export class RunTokenVerifier {
13
+ nodePublicKey;
14
+ keys;
15
+ /** `keys` replaces fetching `{operatorUrl}/.well-known/jwks.json`, in tests. */
16
+ constructor(operatorUrl, nodePublicKey, keys) {
17
+ this.nodePublicKey = nodePublicKey;
18
+ // Cached, and refetched when a token names a key it doesn't have.
19
+ this.keys = keys ?? createRemoteJWKSet(new URL(`${operatorUrl}/.well-known/jwks.json`));
20
+ }
21
+ /** The token's claims, or undefined unless it is signed by the operator, unexpired and addressed to this node. */
22
+ async verify(token) {
23
+ try {
24
+ const { payload } = await jwtVerify(token, this.keys, { issuer: RUN_TOKEN_ISSUER, audience: this.nodePublicKey, algorithms: ['EdDSA'] });
25
+ if (typeof payload.sub !== 'string' || typeof payload.org !== 'string') {
26
+ return undefined;
27
+ }
28
+ return { clientId: payload.sub, organizationId: payload.org };
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  export { StellariumNode, resolveConfig, type StellariumNodeOptions } from './server.js';
2
2
  export type { NodeConfig } from './service.js';
3
3
  export type { StellariumFixedPaymentManifest, StellariumInferencePaymentManifest, StellariumResource, StellariumResourceFunction, StellariumResourceFunctionKind, StellariumResourceFunctionParameter, StellariumResourceFunctionPaymentManifest, } from './resource/repository.js';
4
- export { CombinationResourceInvoker, inferenceVoucherPayload, invocationHash, paymentVoucherPayload, type InferenceEstimate, type InferencePaymentVoucher, type PaymentVoucher, } from './resource/invoker.js';
4
+ export { CombinationResourceInvoker, inferenceVoucherPayload, invocationHash, paymentVoucherPayload, type RunContext, type InferenceEstimate, type InferencePaymentVoucher, type PaymentVoucher, } from './resource/invoker.js';
5
5
  export { inferenceCharge, MAX_TOKENS_PARAMETER, type InferenceResult, type InferenceUsage } from './resource/inference.js';
6
- export type { FixedPaymentVoucher, InferencePaymentSubmission, InferenceVoucher, PaymentSubmission } from './client/operatorClient.js';
6
+ export type { FixedPaymentVoucher, InferencePaymentSubmission, InferenceVoucher, PaymentSubmission, RunAuthorizationRequest } from './client/operatorClient.js';
7
+ export { RUN_TOKEN_ISSUER, RunTokenVerifier, type RunTokenClaims } from './client/runTokens.js';
7
8
  export { OPERATOR_AUTH_SCHEME, operatorAuthorization, operatorRequestPayload } from './client/operatorAuth.js';
8
9
  export { grantHash, grantPayload, grantRenewalPayload, grantRequestPayload, grantsHash, CLOCK_SKEW_TOLERANCE_MS, invocationAuthorizationPayload, MAX_AUTHORIZATION_LIFETIME_MS, MAX_GRANT_CHAIN_LENGTH, MAX_GRANT_LIFETIME_MS, type Capability, type Grant, type GrantRenewalRequest, type GrantRequest, type InvocationAuthorization, } from './grant/grant.js';
9
10
  export type { ApproveGrant, GrantApproval, GrantOptions } from './grant/service.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EACR,8BAA8B,EAC9B,kCAAkC,EAClC,kBAAkB,EAClB,0BAA0B,EAC1B,8BAA8B,EAC9B,mCAAmC,EACnC,yCAAyC,GAC5C,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACH,0BAA0B,EAC1B,uBAAuB,EACvB,cAAc,EACd,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC3H,YAAY,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEvI,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAE/G,OAAO,EACH,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,uBAAuB,GAC/B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EACR,8BAA8B,EAC9B,kCAAkC,EAClC,kBAAkB,EAClB,0BAA0B,EAC1B,8BAA8B,EAC9B,mCAAmC,EACnC,yCAAyC,GAC5C,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACH,0BAA0B,EAC1B,uBAAuB,EACvB,cAAc,EACd,qBAAqB,EACrB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC3H,YAAY,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAEhK,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAE/G,OAAO,EACH,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,uBAAuB,GAC/B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@ export { StellariumNode, resolveConfig } from './server.js';
2
2
  // The voucher wire format, so the operator and other settlers can test against the real signer.
3
3
  export { CombinationResourceInvoker, inferenceVoucherPayload, invocationHash, paymentVoucherPayload, } from './resource/invoker.js';
4
4
  export { inferenceCharge, MAX_TOKENS_PARAMETER } from './resource/inference.js';
5
+ // Run token verification, so the operator can test its tokens against the real verifier.
6
+ export { RUN_TOKEN_ISSUER, RunTokenVerifier } from './client/runTokens.js';
5
7
  // The operator request signature, so the operator can test against the real signer.
6
8
  export { OPERATOR_AUTH_SCHEME, operatorAuthorization, operatorRequestPayload } from './client/operatorAuth.js';
7
9
  // Grant wire format, so other implementations can test against the real signer.
@@ -27,6 +27,8 @@ export declare function isInferenceFunction(fn: Pick<StellariumResourceFunction,
27
27
  export declare function inferenceCharge(rates: InferenceRates, inputTokens: number, outputTokens: number): number;
28
28
  /** The call's `maxTokens` value, or undefined unless it is a positive int32. */
29
29
  export declare function parseMaxTokens(parameters: ResourceFunctionCallParameter[]): number | undefined;
30
+ /** The function's own input-token estimate, or undefined if its estimator failed or returned something other than a count. */
31
+ export declare function estimateInputTokens(fn: StellariumResourceFunction, parameters: ResourceFunctionCallParameter[]): Promise<number | undefined>;
30
32
  /** The usage an inference `exec` reported, or undefined if it isn't two nonnegative safe integers. */
31
33
  export declare function parseInferenceResult(result: unknown): InferenceResult | undefined;
32
34
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/resource/inference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,kCAAkC,EAAE,0BAA0B,EAAE,yCAAyC,EAAE,MAAM,iBAAiB,CAAC;AAEjJ,8FAA8F;AAC9F,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAIhD,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;CACzB;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC3B,iCAAiC,EAAE,MAAM,CAAC;IAC1C,kCAAkC,EAAE,MAAM,CAAC;CAC9C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,yCAAyC,GAAG,SAAS,GAAG,QAAQ,IAAI,kCAAkC,CAEnJ;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,IAAI,CAAC,0BAA0B,EAAE,MAAM,CAAC,GAAG,OAAO,CAEzF;AAMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAQxG;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,UAAU,EAAE,6BAA6B,EAAE,GAAG,MAAM,GAAG,SAAS,CAG9F;AAED,sGAAsG;AACtG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAajF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,0BAA0B,GAAG,IAAI,CAsB9E"}
1
+ {"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/resource/inference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,kCAAkC,EAAE,0BAA0B,EAAE,yCAAyC,EAAE,MAAM,iBAAiB,CAAC;AAEjJ,8FAA8F;AAC9F,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAIhD,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;CACzB;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC3B,iCAAiC,EAAE,MAAM,CAAC;IAC1C,kCAAkC,EAAE,MAAM,CAAC;CAC9C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,yCAAyC,GAAG,SAAS,GAAG,QAAQ,IAAI,kCAAkC,CAEnJ;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,IAAI,CAAC,0BAA0B,EAAE,MAAM,CAAC,GAAG,OAAO,CAEzF;AAMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAQxG;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,UAAU,EAAE,6BAA6B,EAAE,GAAG,MAAM,GAAG,SAAS,CAG9F;AAED,8HAA8H;AAC9H,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,0BAA0B,EAAE,UAAU,EAAE,6BAA6B,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOlJ;AAED,sGAAsG;AACtG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAajF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,0BAA0B,GAAG,IAAI,CAsB9E"}
@@ -29,6 +29,16 @@ export function parseMaxTokens(parameters) {
29
29
  const value = parameters.find(parameter => parameter.name === MAX_TOKENS_PARAMETER)?.value;
30
30
  return Number.isInteger(value) && value > 0 && value <= INT32_MAX ? value : undefined;
31
31
  }
32
+ /** The function's own input-token estimate, or undefined if its estimator failed or returned something other than a count. */
33
+ export async function estimateInputTokens(fn, parameters) {
34
+ try {
35
+ const inputTokens = await fn.estimateInputTokens?.(parameters);
36
+ return Number.isSafeInteger(inputTokens) && inputTokens >= 0 ? inputTokens : undefined;
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
32
42
  /** The usage an inference `exec` reported, or undefined if it isn't two nonnegative safe integers. */
33
43
  export function parseInferenceResult(result) {
34
44
  if (typeof result !== 'object' || result === null || !('usage' in result)) {
@@ -1,4 +1,5 @@
1
1
  import { Resource, ResourceFunctionCallParameter, ResourceInvoker } from "requestscript";
2
+ import type { OperatorClient } from "../client/operatorClient.js";
2
3
  import type { GrantService } from "../grant/service.js";
3
4
  import { type InferenceUsage } from "./inference.js";
4
5
  import { ResourceRepository, type StellariumInferencePaymentManifest } from "./repository.js";
@@ -11,6 +12,8 @@ export interface PaymentVoucher {
11
12
  expiresAt: string;
12
13
  invocationHash: string;
13
14
  publicKey: string;
15
+ /** Set for a call made by a script run billed to an API client: the run's hold pays instead of this node's organization. */
16
+ runId?: string;
14
17
  }
15
18
  /**
16
19
  * A voucher for an inference function. It signs the provider's per-million-token rates and the call's
@@ -30,6 +33,8 @@ export interface InferencePaymentVoucher {
30
33
  expiresAt: string;
31
34
  invocationHash: string;
32
35
  publicKey: string;
36
+ /** Set for a call made by a script run billed to an API client: the run's hold pays instead of this node's organization. */
37
+ runId?: string;
33
38
  }
34
39
  /** What a provider's `POST /v1/run/resource/estimate` answers. */
35
40
  export interface InferenceEstimate {
@@ -54,12 +59,14 @@ export declare function paymentVoucherPayload(voucher: Omit<PaymentVoucher, "sig
54
59
  expiresAt: string;
55
60
  invocationHash: string;
56
61
  publicKey: string;
62
+ runId?: string;
57
63
  };
58
64
  /**
59
65
  * The object `createInferenceVoucher` signs: every inference voucher field except `signature`.
60
66
  * Key order is part of the signed bytes.
61
67
  */
62
68
  export declare function inferenceVoucherPayload(voucher: Omit<InferencePaymentVoucher, "signature">): {
69
+ runId?: string;
63
70
  kind: "inference";
64
71
  requestId: string;
65
72
  invocationId: string;
@@ -82,6 +89,16 @@ export declare const EXTERNAL_INVOKE_BASE_DELAY_MS = 500;
82
89
  export declare const EXTERNAL_INVOKE_TIMEOUT_MS = 30000;
83
90
  /** Per attempt, for calls to inference functions. Every attempt fits within `INFERENCE_VOUCHER_LIFETIME_MS`. */
84
91
  export declare const INFERENCE_INVOKE_TIMEOUT_MS = 120000;
92
+ /** A script run billed to another organization's API client. Its paid calls draw on the run's hold. */
93
+ export interface RunContext {
94
+ runId: string;
95
+ }
96
+ /** How a node settles its own paid functions when a billed run calls them. */
97
+ export interface LocalSettlement {
98
+ operatorClient: OperatorClient;
99
+ /** The organization this node's functions pay. */
100
+ organizationId?: string;
101
+ }
85
102
  export declare class CombinationResourceInvoker implements ResourceInvoker {
86
103
  private readonly resourceRepository;
87
104
  private readonly costLimitMicroUsd;
@@ -89,11 +106,19 @@ export declare class CombinationResourceInvoker implements ResourceInvoker {
89
106
  private readonly privateKey;
90
107
  private readonly grantService?;
91
108
  private readonly inferenceCostLimitMicroUsd;
109
+ private readonly localSettlement?;
92
110
  /**
93
111
  * Without a `grantService`, calls to restricted remote functions fail.
94
112
  * `inferenceCostLimitMicroUsd` caps an inference voucher's `maxAmount`, and defaults to `costLimitMicroUsd`.
113
+ * Without `localSettlement`, a billed run can't call this node's own paid functions.
114
+ */
115
+ constructor(resourceRepository: ResourceRepository, costLimitMicroUsd: number, publicKey: string, privateKey: string, grantService?: GrantService | undefined, inferenceCostLimitMicroUsd?: number, localSettlement?: LocalSettlement | undefined);
116
+ /**
117
+ * An invoker for one billed run. Its paid calls, remote or to this node's own paid functions, carry
118
+ * the run's id, so the run's hold pays for them. The run's hold limits what they cost, in place of the
119
+ * cost limits.
95
120
  */
96
- constructor(resourceRepository: ResourceRepository, costLimitMicroUsd: number, publicKey: string, privateKey: string, grantService?: GrantService | undefined, inferenceCostLimitMicroUsd?: number);
121
+ forRun(run: RunContext): ResourceInvoker;
97
122
  /**
98
123
  * Invoke a function on a resource.
99
124
  * If the resource has a baseUrl, it will be invoked as a remote function.
@@ -114,9 +139,18 @@ export declare class CombinationResourceInvoker implements ResourceInvoker {
114
139
  } | null;
115
140
  usage?: InferenceUsage;
116
141
  }>;
142
+ private invokeIn;
143
+ private execLocal;
144
+ /**
145
+ * Calls one of this node's paid functions for a billed run: signs a voucher with the run's id that pays
146
+ * this node's organization, then authorizes, runs and settles it as `POST /v1/run/resource` would.
147
+ */
148
+ private invokeLocalPaid;
117
149
  /** Asks the provider what a call to a paid inference function will cost at most. */
118
150
  private estimate;
119
- createPaymentVoucher(recipientOrganizationId: string, amount: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[]): PaymentVoucher;
120
- createInferenceVoucher(manifest: StellariumInferencePaymentManifest, maxAmount: number, maxTokens: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[]): InferencePaymentVoucher;
151
+ /** With `runId`, the voucher is paid from that run's hold. */
152
+ createPaymentVoucher(recipientOrganizationId: string, amount: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[], runId?: string): PaymentVoucher;
153
+ /** With `runId`, the voucher is paid from that run's hold. */
154
+ createInferenceVoucher(manifest: StellariumInferencePaymentManifest, maxAmount: number, maxTokens: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[], runId?: string): InferencePaymentVoucher;
121
155
  }
122
156
  //# sourceMappingURL=invoker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"invoker.d.ts","sourceRoot":"","sources":["../../src/resource/invoker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"invoker.d.ts","sourceRoot":"","sources":["../../src/resource/invoker.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { hashData, signData } from "../crypto/utils.js";
2
- import { inferenceCharge, isInferenceFunction, isInferenceManifest, parseInferenceResult, parseMaxTokens } from "./inference.js";
2
+ import { estimateInputTokens, inferenceCharge, isInferenceFunction, isInferenceManifest, parseInferenceResult, parseMaxTokens } from "./inference.js";
3
+ import { settlePaidCall } from "./settlement.js";
3
4
  /**
4
5
  * Hash binding a voucher to one function call. Providers recompute it from the
5
6
  * request they receive and compare it to `voucher.invocationHash`.
@@ -14,7 +15,11 @@ export function invocationHash(resourcePath, resourceName, functionName, paramet
14
15
  */
15
16
  export function paymentVoucherPayload(voucher) {
16
17
  const { requestId, invocationId, amount, recipientOrganizationId, expiresAt, invocationHash, publicKey } = voucher;
17
- return { requestId, invocationId, amount, recipientOrganizationId, expiresAt, invocationHash, publicKey };
18
+ return { requestId, invocationId, amount, recipientOrganizationId, expiresAt, invocationHash, publicKey, ...runPayload(voucher.runId) };
19
+ }
20
+ /** `runId` is signed last, and only when set, so a voucher without one signs the same bytes as before it existed. */
21
+ function runPayload(runId) {
22
+ return runId === undefined ? {} : { runId };
18
23
  }
19
24
  /**
20
25
  * The object `createInferenceVoucher` signs: every inference voucher field except `signature`.
@@ -22,7 +27,7 @@ export function paymentVoucherPayload(voucher) {
22
27
  */
23
28
  export function inferenceVoucherPayload(voucher) {
24
29
  const { kind, requestId, invocationId, maxAmount, inputCostMicroUsdPerMillionTokens, outputCostMicroUsdPerMillionTokens, maxTokens, recipientOrganizationId, expiresAt, invocationHash, publicKey } = voucher;
25
- return { kind, requestId, invocationId, maxAmount, inputCostMicroUsdPerMillionTokens, outputCostMicroUsdPerMillionTokens, maxTokens, recipientOrganizationId, expiresAt, invocationHash, publicKey };
30
+ return { kind, requestId, invocationId, maxAmount, inputCostMicroUsdPerMillionTokens, outputCostMicroUsdPerMillionTokens, maxTokens, recipientOrganizationId, expiresAt, invocationHash, publicKey, ...runPayload(voucher.runId) };
26
31
  }
27
32
  export function isInferenceVoucher(voucher) {
28
33
  return voucher.kind === 'inference';
@@ -68,17 +73,28 @@ export class CombinationResourceInvoker {
68
73
  privateKey;
69
74
  grantService;
70
75
  inferenceCostLimitMicroUsd;
76
+ localSettlement;
71
77
  /**
72
78
  * Without a `grantService`, calls to restricted remote functions fail.
73
79
  * `inferenceCostLimitMicroUsd` caps an inference voucher's `maxAmount`, and defaults to `costLimitMicroUsd`.
80
+ * Without `localSettlement`, a billed run can't call this node's own paid functions.
74
81
  */
75
- constructor(resourceRepository, costLimitMicroUsd, publicKey, privateKey, grantService, inferenceCostLimitMicroUsd = costLimitMicroUsd) {
82
+ constructor(resourceRepository, costLimitMicroUsd, publicKey, privateKey, grantService, inferenceCostLimitMicroUsd = costLimitMicroUsd, localSettlement) {
76
83
  this.resourceRepository = resourceRepository;
77
84
  this.costLimitMicroUsd = costLimitMicroUsd;
78
85
  this.publicKey = publicKey;
79
86
  this.privateKey = privateKey;
80
87
  this.grantService = grantService;
81
88
  this.inferenceCostLimitMicroUsd = inferenceCostLimitMicroUsd;
89
+ this.localSettlement = localSettlement;
90
+ }
91
+ /**
92
+ * An invoker for one billed run. Its paid calls, remote or to this node's own paid functions, carry
93
+ * the run's id, so the run's hold pays for them. The run's hold limits what they cost, in place of the
94
+ * cost limits.
95
+ */
96
+ forRun(run) {
97
+ return { invoke: (resource, functionName, parameters) => this.invokeIn(resource, functionName, parameters, run) };
82
98
  }
83
99
  /**
84
100
  * Invoke a function on a resource.
@@ -94,6 +110,9 @@ export class CombinationResourceInvoker {
94
110
  * @returns The return value of the function
95
111
  */
96
112
  async invoke(resource, functionName, parameters) {
113
+ return this.invokeIn(resource, functionName, parameters, undefined);
114
+ }
115
+ async invokeIn(resource, functionName, parameters, run) {
97
116
  if (resource.metadata.baseUrl) {
98
117
  let requestBody = {
99
118
  resourcePath: resource.path,
@@ -139,20 +158,20 @@ export class CombinationResourceInvoker {
139
158
  if (inferenceCharge(manifest, estimate.inputTokens, maxTokens) !== estimate.maxAmount) {
140
159
  return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: the provider's estimate doesn't match its advertised rates` } };
141
160
  }
142
- if (estimate.maxAmount > this.inferenceCostLimitMicroUsd) {
161
+ if (!run && estimate.maxAmount > this.inferenceCostLimitMicroUsd) {
143
162
  throw new Error(`Function ${functionName} may cost up to ${estimate.maxAmount} micro-USD, which exceeds the inference cost limit of ${this.inferenceCostLimitMicroUsd} micro-USD`);
144
163
  }
145
- requestBody.paymentVoucher = this.createInferenceVoucher(manifest, estimate.maxAmount, maxTokens, resource.path, resource.name, functionName, parameters);
164
+ requestBody.paymentVoucher = this.createInferenceVoucher(manifest, estimate.maxAmount, maxTokens, resource.path, resource.name, functionName, parameters, run?.runId);
146
165
  }
147
166
  else if (manifest) {
148
167
  console.log(`Resource function ${resource.name}.${functionName} has a payment manifest, creating payment voucher...`);
149
168
  if (isInferenceManifest(manifest)) {
150
169
  return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: it has per-token pricing but isn't an inference function` } };
151
170
  }
152
- if (manifest.costMicroUsd > this.costLimitMicroUsd) {
171
+ if (!run && manifest.costMicroUsd > this.costLimitMicroUsd) {
153
172
  throw new Error(`Function ${functionName} cost ${manifest.costMicroUsd} micro-USD, which exceeds the cost limit of ${this.costLimitMicroUsd} micro-USD`);
154
173
  }
155
- requestBody.paymentVoucher = this.createPaymentVoucher(manifest.recipientOrganizationId, manifest.costMicroUsd, resource.path, resource.name, functionName, parameters);
174
+ requestBody.paymentVoucher = this.createPaymentVoucher(manifest.recipientOrganizationId, manifest.costMicroUsd, resource.path, resource.name, functionName, parameters, run?.runId);
156
175
  }
157
176
  // The voucher is created once and reused on every attempt, so the operator's
158
177
  // requestId/invocationId check prevents a retried call from being settled twice.
@@ -178,17 +197,57 @@ export class CombinationResourceInvoker {
178
197
  if (!fn) {
179
198
  return { value: null, error: { message: `Function ${functionName} not found in resource ${resource.name}` } };
180
199
  }
181
- const result = await fn.exec(parameters);
182
- if (isInferenceFunction(fn)) {
183
- const inferenceResult = parseInferenceResult(result);
184
- if (!inferenceResult) {
185
- return { value: null, error: { message: `Inference function ${functionName} must return { value, usage: { inputTokens, outputTokens } }` } };
186
- }
187
- return { value: inferenceResult.value, error: null, usage: inferenceResult.usage };
200
+ // A billed run pays for this node's own paid functions as a remote caller would.
201
+ if (run && fn.paymentManifest) {
202
+ return this.invokeLocalPaid(resource, fn, parameters, run);
188
203
  }
189
- return { value: result, error: null };
204
+ return this.execLocal(fn, parameters);
190
205
  }
191
206
  }
207
+ async execLocal(fn, parameters) {
208
+ const result = await fn.exec(parameters);
209
+ if (isInferenceFunction(fn)) {
210
+ const inferenceResult = parseInferenceResult(result);
211
+ if (!inferenceResult) {
212
+ return { value: null, error: { message: `Inference function ${fn.name} must return { value, usage: { inputTokens, outputTokens } }` } };
213
+ }
214
+ return { value: inferenceResult.value, error: null, usage: inferenceResult.usage };
215
+ }
216
+ return { value: result, error: null };
217
+ }
218
+ /**
219
+ * Calls one of this node's paid functions for a billed run: signs a voucher with the run's id that pays
220
+ * this node's organization, then authorizes, runs and settles it as `POST /v1/run/resource` would.
221
+ */
222
+ async invokeLocalPaid(resource, fn, parameters, run) {
223
+ const failed = (reason) => ({ value: null, error: { message: `Failed to invoke ${resource.name}.${fn.name}: ${reason}` } });
224
+ const organizationId = this.localSettlement?.organizationId;
225
+ if (!this.localSettlement || !organizationId) {
226
+ return failed('this node is not enrolled in an organization that can be paid');
227
+ }
228
+ const manifest = fn.paymentManifest;
229
+ let voucher;
230
+ if (isInferenceFunction(fn) && isInferenceManifest(manifest)) {
231
+ const maxTokens = parseMaxTokens(parameters);
232
+ if (maxTokens === undefined) {
233
+ return failed('maxTokens must be a positive int32');
234
+ }
235
+ const inputTokens = await estimateInputTokens(fn, parameters);
236
+ if (inputTokens === undefined) {
237
+ return failed('failed to estimate input tokens');
238
+ }
239
+ const maxAmount = inferenceCharge(manifest, inputTokens, maxTokens);
240
+ voucher = this.createInferenceVoucher({ ...manifest, recipientOrganizationId: organizationId }, maxAmount, maxTokens, resource.path, resource.name, fn.name, parameters, run.runId);
241
+ }
242
+ else if (!isInferenceManifest(manifest)) {
243
+ voucher = this.createPaymentVoucher(organizationId, manifest.costMicroUsd, resource.path, resource.name, fn.name, parameters, run.runId);
244
+ }
245
+ else {
246
+ return failed('it has per-token pricing but isn\'t an inference function');
247
+ }
248
+ const result = await settlePaidCall(this.localSettlement.operatorClient, voucher, () => this.execLocal(fn, parameters), (error, message) => console.error(message, error));
249
+ return 'refused' in result ? failed(result.refused.error) : result;
250
+ }
192
251
  /** Asks the provider what a call to a paid inference function will cost at most. */
193
252
  async estimate(url, target, chain) {
194
253
  let response;
@@ -213,15 +272,22 @@ export class CombinationResourceInvoker {
213
272
  }
214
273
  return { inputTokens: inputTokens, maxAmount: maxAmount };
215
274
  }
216
- createPaymentVoucher(recipientOrganizationId, amount, resourcePath, resourceName, functionName, parameters) {
217
- const requestId = crypto.randomUUID();
218
- const invocationId = crypto.randomUUID();
219
- const expiresAt = new Date(Date.now() + PAYMENT_VOUCHER_LIFETIME_MS).toISOString();
220
- const hash = invocationHash(resourcePath, resourceName, functionName, parameters);
221
- const signature = signData(paymentVoucherPayload({ requestId, invocationId, amount, recipientOrganizationId, expiresAt, invocationHash: hash, publicKey: this.publicKey }), this.privateKey);
222
- return { requestId, invocationId, amount, recipientOrganizationId, signature, expiresAt, invocationHash: hash, publicKey: this.publicKey };
275
+ /** With `runId`, the voucher is paid from that run's hold. */
276
+ createPaymentVoucher(recipientOrganizationId, amount, resourcePath, resourceName, functionName, parameters, runId) {
277
+ const unsigned = {
278
+ requestId: crypto.randomUUID(),
279
+ invocationId: crypto.randomUUID(),
280
+ amount,
281
+ recipientOrganizationId,
282
+ expiresAt: new Date(Date.now() + PAYMENT_VOUCHER_LIFETIME_MS).toISOString(),
283
+ invocationHash: invocationHash(resourcePath, resourceName, functionName, parameters),
284
+ publicKey: this.publicKey,
285
+ ...runPayload(runId),
286
+ };
287
+ return { ...unsigned, signature: signData(paymentVoucherPayload(unsigned), this.privateKey) };
223
288
  }
224
- createInferenceVoucher(manifest, maxAmount, maxTokens, resourcePath, resourceName, functionName, parameters) {
289
+ /** With `runId`, the voucher is paid from that run's hold. */
290
+ createInferenceVoucher(manifest, maxAmount, maxTokens, resourcePath, resourceName, functionName, parameters, runId) {
225
291
  const unsigned = {
226
292
  kind: 'inference',
227
293
  requestId: crypto.randomUUID(),
@@ -234,6 +300,7 @@ export class CombinationResourceInvoker {
234
300
  expiresAt: new Date(Date.now() + INFERENCE_VOUCHER_LIFETIME_MS).toISOString(),
235
301
  invocationHash: invocationHash(resourcePath, resourceName, functionName, parameters),
236
302
  publicKey: this.publicKey,
303
+ ...runPayload(runId),
237
304
  };
238
305
  return { ...unsigned, signature: signData(inferenceVoucherPayload(unsigned), this.privateKey) };
239
306
  }
@@ -0,0 +1,25 @@
1
+ import type { OperatorClient } from "../client/operatorClient.js";
2
+ import { type InferenceUsage } from "./inference.js";
3
+ import { type InferencePaymentVoucher, type PaymentVoucher } from "./invoker.js";
4
+ /** What a resource function call evaluates to, with an inference function's usage. */
5
+ export interface InvokeResult {
6
+ value: unknown;
7
+ error: {
8
+ message: string;
9
+ } | null;
10
+ usage?: InferenceUsage;
11
+ }
12
+ /** The operator refused to hold the voucher, so the call didn't run. */
13
+ export interface PaymentRefusal {
14
+ refused: {
15
+ status: number;
16
+ error: string;
17
+ };
18
+ }
19
+ /**
20
+ * Runs a paid call: holds the voucher's amount with the operator first, so a paid function never runs
21
+ * unpaid, then runs `call` and captures the voucher, or releases it for a failed or free call. Settlement is
22
+ * scheduled after returning, so it doesn't delay the caller, and failures go to `onSettleError`.
23
+ */
24
+ export declare function settlePaidCall(operatorClient: OperatorClient, voucher: PaymentVoucher | InferencePaymentVoucher, call: () => Promise<InvokeResult>, onSettleError: (error: unknown, message: string) => void): Promise<InvokeResult | PaymentRefusal>;
25
+ //# sourceMappingURL=settlement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settlement.d.ts","sourceRoot":"","sources":["../../src/resource/settlement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,6BAA6B,CAAC;AACrF,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,KAAK,uBAAuB,EAAsB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAErG,sFAAsF;AACtF,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAYD;;;;GAIG;AACH,wBAAsB,cAAc,CAChC,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,cAAc,GAAG,uBAAuB,EACjD,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,EACjC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GACzD,OAAO,CAAC,YAAY,GAAG,cAAc,CAAC,CAuCxC"}