runcycles 0.1.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/LICENSE +201 -0
- package/README.md +494 -0
- package/dist/index.cjs +1450 -0
- package/dist/index.d.cts +551 -0
- package/dist/index.d.ts +551 -0
- package/dist/index.js +1374 -0
- package/package.json +64 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1450 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BudgetExceededError: () => BudgetExceededError,
|
|
24
|
+
CommitOveragePolicy: () => CommitOveragePolicy,
|
|
25
|
+
CommitStatus: () => CommitStatus,
|
|
26
|
+
CyclesClient: () => CyclesClient,
|
|
27
|
+
CyclesConfig: () => CyclesConfig,
|
|
28
|
+
CyclesError: () => CyclesError,
|
|
29
|
+
CyclesProtocolError: () => CyclesProtocolError,
|
|
30
|
+
CyclesResponse: () => CyclesResponse,
|
|
31
|
+
CyclesTransportError: () => CyclesTransportError,
|
|
32
|
+
DebtOutstandingError: () => DebtOutstandingError,
|
|
33
|
+
Decision: () => Decision,
|
|
34
|
+
ErrorCode: () => ErrorCode,
|
|
35
|
+
EventStatus: () => EventStatus,
|
|
36
|
+
ExtendStatus: () => ExtendStatus,
|
|
37
|
+
OverdraftLimitExceededError: () => OverdraftLimitExceededError,
|
|
38
|
+
ReleaseStatus: () => ReleaseStatus,
|
|
39
|
+
ReservationExpiredError: () => ReservationExpiredError,
|
|
40
|
+
ReservationFinalizedError: () => ReservationFinalizedError,
|
|
41
|
+
ReservationStatus: () => ReservationStatus,
|
|
42
|
+
Unit: () => Unit,
|
|
43
|
+
balanceResponseFromWire: () => balanceResponseFromWire,
|
|
44
|
+
capsFromWire: () => capsFromWire,
|
|
45
|
+
commitRequestToWire: () => commitRequestToWire,
|
|
46
|
+
commitResponseFromWire: () => commitResponseFromWire,
|
|
47
|
+
decisionRequestToWire: () => decisionRequestToWire,
|
|
48
|
+
decisionResponseFromWire: () => decisionResponseFromWire,
|
|
49
|
+
errorCodeFromString: () => errorCodeFromString,
|
|
50
|
+
errorResponseFromWire: () => errorResponseFromWire,
|
|
51
|
+
eventCreateRequestToWire: () => eventCreateRequestToWire,
|
|
52
|
+
eventCreateResponseFromWire: () => eventCreateResponseFromWire,
|
|
53
|
+
getCyclesContext: () => getCyclesContext,
|
|
54
|
+
isAllowed: () => isAllowed,
|
|
55
|
+
isDenied: () => isDenied,
|
|
56
|
+
isMetricsEmpty: () => isMetricsEmpty2,
|
|
57
|
+
isRetryableErrorCode: () => isRetryableErrorCode,
|
|
58
|
+
isToolAllowed: () => isToolAllowed,
|
|
59
|
+
metricsToWire: () => metricsToWire,
|
|
60
|
+
releaseRequestToWire: () => releaseRequestToWire,
|
|
61
|
+
releaseResponseFromWire: () => releaseResponseFromWire,
|
|
62
|
+
reservationCreateRequestToWire: () => reservationCreateRequestToWire,
|
|
63
|
+
reservationCreateResponseFromWire: () => reservationCreateResponseFromWire,
|
|
64
|
+
reservationDetailFromWire: () => reservationDetailFromWire,
|
|
65
|
+
reservationExtendRequestToWire: () => reservationExtendRequestToWire,
|
|
66
|
+
reservationExtendResponseFromWire: () => reservationExtendResponseFromWire,
|
|
67
|
+
reservationListResponseFromWire: () => reservationListResponseFromWire,
|
|
68
|
+
reservationSummaryFromWire: () => reservationSummaryFromWire,
|
|
69
|
+
reserveForStream: () => reserveForStream,
|
|
70
|
+
setDefaultClient: () => setDefaultClient,
|
|
71
|
+
setDefaultConfig: () => setDefaultConfig,
|
|
72
|
+
withCycles: () => withCycles
|
|
73
|
+
});
|
|
74
|
+
module.exports = __toCommonJS(index_exports);
|
|
75
|
+
|
|
76
|
+
// src/constants.ts
|
|
77
|
+
var API_KEY_HEADER = "X-Cycles-API-Key";
|
|
78
|
+
var IDEMPOTENCY_KEY_HEADER = "X-Idempotency-Key";
|
|
79
|
+
var RESERVATIONS_PATH = "/v1/reservations";
|
|
80
|
+
var DECIDE_PATH = "/v1/decide";
|
|
81
|
+
var BALANCES_PATH = "/v1/balances";
|
|
82
|
+
var EVENTS_PATH = "/v1/events";
|
|
83
|
+
|
|
84
|
+
// src/mappers.ts
|
|
85
|
+
function stripUndefined(obj) {
|
|
86
|
+
const result = {};
|
|
87
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
88
|
+
if (value !== void 0) {
|
|
89
|
+
result[key] = value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
function metricsToWire(metrics) {
|
|
95
|
+
return stripUndefined({
|
|
96
|
+
tokens_input: metrics.tokensInput,
|
|
97
|
+
tokens_output: metrics.tokensOutput,
|
|
98
|
+
latency_ms: metrics.latencyMs,
|
|
99
|
+
model_version: metrics.modelVersion,
|
|
100
|
+
custom: metrics.custom
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function capsFromWire(wire) {
|
|
104
|
+
if (!wire) return void 0;
|
|
105
|
+
return stripUndefined({
|
|
106
|
+
maxTokens: wire.max_tokens,
|
|
107
|
+
maxStepsRemaining: wire.max_steps_remaining,
|
|
108
|
+
toolAllowlist: wire.tool_allowlist,
|
|
109
|
+
toolDenylist: wire.tool_denylist,
|
|
110
|
+
cooldownMs: wire.cooldown_ms
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function amountFromWire(wire) {
|
|
114
|
+
if (!wire) return void 0;
|
|
115
|
+
return { unit: wire.unit, amount: wire.amount };
|
|
116
|
+
}
|
|
117
|
+
function signedAmountFromWire(wire) {
|
|
118
|
+
if (!wire) return void 0;
|
|
119
|
+
return { unit: wire.unit, amount: wire.amount };
|
|
120
|
+
}
|
|
121
|
+
function balanceFromWire(wire) {
|
|
122
|
+
return {
|
|
123
|
+
scope: wire.scope,
|
|
124
|
+
scopePath: wire.scope_path,
|
|
125
|
+
remaining: signedAmountFromWire(wire.remaining),
|
|
126
|
+
reserved: amountFromWire(wire.reserved),
|
|
127
|
+
spent: amountFromWire(wire.spent),
|
|
128
|
+
allocated: amountFromWire(wire.allocated),
|
|
129
|
+
debt: amountFromWire(wire.debt),
|
|
130
|
+
overdraftLimit: amountFromWire(wire.overdraft_limit),
|
|
131
|
+
isOverLimit: wire.is_over_limit
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function balancesFromWire(wire) {
|
|
135
|
+
if (!wire) return void 0;
|
|
136
|
+
return wire.map((b) => balanceFromWire(b));
|
|
137
|
+
}
|
|
138
|
+
function reservationCreateResponseFromWire(wire) {
|
|
139
|
+
return {
|
|
140
|
+
decision: wire.decision,
|
|
141
|
+
reservationId: wire.reservation_id,
|
|
142
|
+
affectedScopes: wire.affected_scopes ?? [],
|
|
143
|
+
expiresAtMs: wire.expires_at_ms,
|
|
144
|
+
scopePath: wire.scope_path,
|
|
145
|
+
reserved: amountFromWire(wire.reserved),
|
|
146
|
+
caps: capsFromWire(wire.caps),
|
|
147
|
+
reasonCode: wire.reason_code,
|
|
148
|
+
retryAfterMs: wire.retry_after_ms,
|
|
149
|
+
balances: balancesFromWire(wire.balances)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function commitResponseFromWire(wire) {
|
|
153
|
+
return {
|
|
154
|
+
status: wire.status,
|
|
155
|
+
charged: amountFromWire(wire.charged),
|
|
156
|
+
released: amountFromWire(wire.released),
|
|
157
|
+
balances: balancesFromWire(wire.balances)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function releaseResponseFromWire(wire) {
|
|
161
|
+
return {
|
|
162
|
+
status: wire.status,
|
|
163
|
+
released: amountFromWire(wire.released),
|
|
164
|
+
balances: balancesFromWire(wire.balances)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function reservationExtendResponseFromWire(wire) {
|
|
168
|
+
return {
|
|
169
|
+
status: wire.status,
|
|
170
|
+
expiresAtMs: wire.expires_at_ms,
|
|
171
|
+
balances: balancesFromWire(wire.balances)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function decisionResponseFromWire(wire) {
|
|
175
|
+
return stripUndefined({
|
|
176
|
+
decision: wire.decision,
|
|
177
|
+
caps: capsFromWire(wire.caps),
|
|
178
|
+
reasonCode: wire.reason_code,
|
|
179
|
+
retryAfterMs: wire.retry_after_ms,
|
|
180
|
+
affectedScopes: wire.affected_scopes
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function eventCreateResponseFromWire(wire) {
|
|
184
|
+
return {
|
|
185
|
+
status: wire.status,
|
|
186
|
+
eventId: wire.event_id,
|
|
187
|
+
balances: balancesFromWire(wire.balances)
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function subjectFromWire(wire) {
|
|
191
|
+
const result = {};
|
|
192
|
+
if (wire.tenant !== void 0) result.tenant = wire.tenant;
|
|
193
|
+
if (wire.workspace !== void 0) result.workspace = wire.workspace;
|
|
194
|
+
if (wire.app !== void 0) result.app = wire.app;
|
|
195
|
+
if (wire.workflow !== void 0) result.workflow = wire.workflow;
|
|
196
|
+
if (wire.agent !== void 0) result.agent = wire.agent;
|
|
197
|
+
if (wire.toolset !== void 0) result.toolset = wire.toolset;
|
|
198
|
+
if (wire.dimensions !== void 0) result.dimensions = wire.dimensions;
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
function actionFromWire(wire) {
|
|
202
|
+
const result = {
|
|
203
|
+
kind: wire.kind,
|
|
204
|
+
name: wire.name
|
|
205
|
+
};
|
|
206
|
+
if (wire.tags !== void 0) result.tags = wire.tags;
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
function reservationDetailFromWire(wire) {
|
|
210
|
+
return {
|
|
211
|
+
reservationId: wire.reservation_id,
|
|
212
|
+
status: wire.status,
|
|
213
|
+
subject: subjectFromWire(wire.subject),
|
|
214
|
+
action: actionFromWire(wire.action),
|
|
215
|
+
reserved: amountFromWire(wire.reserved),
|
|
216
|
+
createdAtMs: wire.created_at_ms,
|
|
217
|
+
expiresAtMs: wire.expires_at_ms,
|
|
218
|
+
scopePath: wire.scope_path,
|
|
219
|
+
affectedScopes: wire.affected_scopes,
|
|
220
|
+
idempotencyKey: wire.idempotency_key,
|
|
221
|
+
committed: amountFromWire(wire.committed),
|
|
222
|
+
finalizedAtMs: wire.finalized_at_ms,
|
|
223
|
+
metadata: wire.metadata
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function reservationSummaryFromWire(wire) {
|
|
227
|
+
return {
|
|
228
|
+
reservationId: wire.reservation_id,
|
|
229
|
+
status: wire.status,
|
|
230
|
+
subject: subjectFromWire(wire.subject),
|
|
231
|
+
action: actionFromWire(wire.action),
|
|
232
|
+
reserved: amountFromWire(wire.reserved),
|
|
233
|
+
createdAtMs: wire.created_at_ms,
|
|
234
|
+
expiresAtMs: wire.expires_at_ms,
|
|
235
|
+
scopePath: wire.scope_path,
|
|
236
|
+
affectedScopes: wire.affected_scopes,
|
|
237
|
+
idempotencyKey: wire.idempotency_key
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function reservationListResponseFromWire(wire) {
|
|
241
|
+
const reservations = wire.reservations.map(
|
|
242
|
+
(r) => reservationSummaryFromWire(r)
|
|
243
|
+
);
|
|
244
|
+
return {
|
|
245
|
+
reservations,
|
|
246
|
+
nextCursor: wire.next_cursor,
|
|
247
|
+
hasMore: wire.has_more
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function balanceResponseFromWire(wire) {
|
|
251
|
+
return {
|
|
252
|
+
balances: balancesFromWire(wire.balances) ?? [],
|
|
253
|
+
nextCursor: wire.next_cursor,
|
|
254
|
+
hasMore: wire.has_more
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function errorResponseFromWire(wire) {
|
|
258
|
+
if (typeof wire.error !== "string" || typeof wire.message !== "string" || typeof wire.request_id !== "string") {
|
|
259
|
+
return void 0;
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
error: wire.error,
|
|
263
|
+
message: wire.message,
|
|
264
|
+
requestId: wire.request_id,
|
|
265
|
+
details: wire.details
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function actionToWire(action) {
|
|
269
|
+
return stripUndefined({
|
|
270
|
+
kind: action.kind,
|
|
271
|
+
name: action.name,
|
|
272
|
+
tags: action.tags
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
function subjectToWire(subject) {
|
|
276
|
+
return stripUndefined({
|
|
277
|
+
tenant: subject.tenant,
|
|
278
|
+
workspace: subject.workspace,
|
|
279
|
+
app: subject.app,
|
|
280
|
+
workflow: subject.workflow,
|
|
281
|
+
agent: subject.agent,
|
|
282
|
+
toolset: subject.toolset,
|
|
283
|
+
dimensions: subject.dimensions
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function reservationCreateRequestToWire(req) {
|
|
287
|
+
return stripUndefined({
|
|
288
|
+
idempotency_key: req.idempotencyKey,
|
|
289
|
+
subject: subjectToWire(req.subject),
|
|
290
|
+
action: actionToWire(req.action),
|
|
291
|
+
estimate: req.estimate,
|
|
292
|
+
ttl_ms: req.ttlMs,
|
|
293
|
+
grace_period_ms: req.gracePeriodMs,
|
|
294
|
+
overage_policy: req.overagePolicy,
|
|
295
|
+
dry_run: req.dryRun,
|
|
296
|
+
metadata: req.metadata
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
function commitRequestToWire(req) {
|
|
300
|
+
return stripUndefined({
|
|
301
|
+
idempotency_key: req.idempotencyKey,
|
|
302
|
+
actual: req.actual,
|
|
303
|
+
metrics: req.metrics ? metricsToWire(req.metrics) : void 0,
|
|
304
|
+
metadata: req.metadata
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
function releaseRequestToWire(req) {
|
|
308
|
+
return stripUndefined({
|
|
309
|
+
idempotency_key: req.idempotencyKey,
|
|
310
|
+
reason: req.reason
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function reservationExtendRequestToWire(req) {
|
|
314
|
+
return stripUndefined({
|
|
315
|
+
idempotency_key: req.idempotencyKey,
|
|
316
|
+
extend_by_ms: req.extendByMs,
|
|
317
|
+
metadata: req.metadata
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
function decisionRequestToWire(req) {
|
|
321
|
+
return stripUndefined({
|
|
322
|
+
idempotency_key: req.idempotencyKey,
|
|
323
|
+
subject: subjectToWire(req.subject),
|
|
324
|
+
action: actionToWire(req.action),
|
|
325
|
+
estimate: req.estimate,
|
|
326
|
+
metadata: req.metadata
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
function eventCreateRequestToWire(req) {
|
|
330
|
+
return stripUndefined({
|
|
331
|
+
idempotency_key: req.idempotencyKey,
|
|
332
|
+
subject: subjectToWire(req.subject),
|
|
333
|
+
action: actionToWire(req.action),
|
|
334
|
+
actual: req.actual,
|
|
335
|
+
overage_policy: req.overagePolicy,
|
|
336
|
+
metrics: req.metrics ? metricsToWire(req.metrics) : void 0,
|
|
337
|
+
client_time_ms: req.clientTimeMs,
|
|
338
|
+
metadata: req.metadata
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/response.ts
|
|
343
|
+
var CyclesResponse = class _CyclesResponse {
|
|
344
|
+
status;
|
|
345
|
+
body;
|
|
346
|
+
errorMessage;
|
|
347
|
+
headers;
|
|
348
|
+
_isTransportError;
|
|
349
|
+
transportError;
|
|
350
|
+
constructor(options) {
|
|
351
|
+
this.status = options.status;
|
|
352
|
+
this.body = options.body;
|
|
353
|
+
this.errorMessage = options.errorMessage;
|
|
354
|
+
this.headers = options.headers ?? {};
|
|
355
|
+
this._isTransportError = options.isTransportError ?? false;
|
|
356
|
+
this.transportError = options.transportError;
|
|
357
|
+
}
|
|
358
|
+
static success(status, body, headers) {
|
|
359
|
+
return new _CyclesResponse({ status, body, headers });
|
|
360
|
+
}
|
|
361
|
+
static httpError(status, errorMessage, body, headers) {
|
|
362
|
+
return new _CyclesResponse({ status, body, errorMessage, headers });
|
|
363
|
+
}
|
|
364
|
+
static transportError(err) {
|
|
365
|
+
return new _CyclesResponse({
|
|
366
|
+
status: -1,
|
|
367
|
+
errorMessage: err.message,
|
|
368
|
+
isTransportError: true,
|
|
369
|
+
transportError: err
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
get requestId() {
|
|
373
|
+
return this.headers["x-request-id"];
|
|
374
|
+
}
|
|
375
|
+
get rateLimitRemaining() {
|
|
376
|
+
const val = this.headers["x-ratelimit-remaining"];
|
|
377
|
+
return val !== void 0 ? parseInt(val, 10) : void 0;
|
|
378
|
+
}
|
|
379
|
+
get rateLimitReset() {
|
|
380
|
+
const val = this.headers["x-ratelimit-reset"];
|
|
381
|
+
return val !== void 0 ? parseInt(val, 10) : void 0;
|
|
382
|
+
}
|
|
383
|
+
get cyclesTenant() {
|
|
384
|
+
return this.headers["x-cycles-tenant"];
|
|
385
|
+
}
|
|
386
|
+
get isSuccess() {
|
|
387
|
+
return this.status >= 200 && this.status < 300;
|
|
388
|
+
}
|
|
389
|
+
get isClientError() {
|
|
390
|
+
return this.status >= 400 && this.status < 500;
|
|
391
|
+
}
|
|
392
|
+
get isServerError() {
|
|
393
|
+
return this.status >= 500 && this.status < 600;
|
|
394
|
+
}
|
|
395
|
+
get isTransportError() {
|
|
396
|
+
return this._isTransportError;
|
|
397
|
+
}
|
|
398
|
+
getBodyAttribute(key) {
|
|
399
|
+
if (this.body && key in this.body) {
|
|
400
|
+
return this.body[key];
|
|
401
|
+
}
|
|
402
|
+
return void 0;
|
|
403
|
+
}
|
|
404
|
+
getErrorResponse() {
|
|
405
|
+
if (this.body && typeof this.body === "object") {
|
|
406
|
+
return errorResponseFromWire(this.body);
|
|
407
|
+
}
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// src/client.ts
|
|
413
|
+
var RESPONSE_HEADERS = [
|
|
414
|
+
"x-request-id",
|
|
415
|
+
"x-ratelimit-remaining",
|
|
416
|
+
"x-ratelimit-reset",
|
|
417
|
+
"x-cycles-tenant"
|
|
418
|
+
];
|
|
419
|
+
var BALANCE_FILTER_PARAMS = /* @__PURE__ */ new Set([
|
|
420
|
+
"tenant",
|
|
421
|
+
"workspace",
|
|
422
|
+
"app",
|
|
423
|
+
"workflow",
|
|
424
|
+
"agent",
|
|
425
|
+
"toolset"
|
|
426
|
+
]);
|
|
427
|
+
function extractResponseHeaders(resp) {
|
|
428
|
+
const result = {};
|
|
429
|
+
for (const name of RESPONSE_HEADERS) {
|
|
430
|
+
const val = resp.headers.get(name);
|
|
431
|
+
if (val !== null) {
|
|
432
|
+
result[name] = val;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
var CyclesClient = class {
|
|
438
|
+
_config;
|
|
439
|
+
constructor(config) {
|
|
440
|
+
this._config = config;
|
|
441
|
+
}
|
|
442
|
+
get config() {
|
|
443
|
+
return this._config;
|
|
444
|
+
}
|
|
445
|
+
async createReservation(request) {
|
|
446
|
+
return this._post(RESERVATIONS_PATH, request);
|
|
447
|
+
}
|
|
448
|
+
async commitReservation(reservationId, request) {
|
|
449
|
+
return this._post(
|
|
450
|
+
`${RESERVATIONS_PATH}/${reservationId}/commit`,
|
|
451
|
+
request
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
async releaseReservation(reservationId, request) {
|
|
455
|
+
return this._post(
|
|
456
|
+
`${RESERVATIONS_PATH}/${reservationId}/release`,
|
|
457
|
+
request
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
async extendReservation(reservationId, request) {
|
|
461
|
+
return this._post(
|
|
462
|
+
`${RESERVATIONS_PATH}/${reservationId}/extend`,
|
|
463
|
+
request
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
async decide(request) {
|
|
467
|
+
return this._post(DECIDE_PATH, request);
|
|
468
|
+
}
|
|
469
|
+
async listReservations(params) {
|
|
470
|
+
return this._get(RESERVATIONS_PATH, params);
|
|
471
|
+
}
|
|
472
|
+
async getReservation(reservationId) {
|
|
473
|
+
return this._get(`${RESERVATIONS_PATH}/${reservationId}`);
|
|
474
|
+
}
|
|
475
|
+
async getBalances(params) {
|
|
476
|
+
const hasFilter = Object.keys(params).some(
|
|
477
|
+
(k) => BALANCE_FILTER_PARAMS.has(k)
|
|
478
|
+
);
|
|
479
|
+
if (!hasFilter) {
|
|
480
|
+
throw new Error(
|
|
481
|
+
"getBalances requires at least one subject filter (tenant, workspace, app, workflow, agent, or toolset)"
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
return this._get(BALANCES_PATH, params);
|
|
485
|
+
}
|
|
486
|
+
async createEvent(request) {
|
|
487
|
+
return this._post(EVENTS_PATH, request);
|
|
488
|
+
}
|
|
489
|
+
async _post(path, body) {
|
|
490
|
+
try {
|
|
491
|
+
const headers = {
|
|
492
|
+
"Content-Type": "application/json",
|
|
493
|
+
[API_KEY_HEADER]: this._config.apiKey
|
|
494
|
+
};
|
|
495
|
+
const idemKey = body.idempotency_key;
|
|
496
|
+
if (typeof idemKey === "string") {
|
|
497
|
+
headers[IDEMPOTENCY_KEY_HEADER] = idemKey;
|
|
498
|
+
}
|
|
499
|
+
const url = `${this._config.baseUrl}${path}`;
|
|
500
|
+
const resp = await fetch(url, {
|
|
501
|
+
method: "POST",
|
|
502
|
+
headers,
|
|
503
|
+
body: JSON.stringify(body),
|
|
504
|
+
signal: AbortSignal.timeout(
|
|
505
|
+
this._config.connectTimeout + this._config.readTimeout
|
|
506
|
+
)
|
|
507
|
+
});
|
|
508
|
+
return this._handleResponse(resp);
|
|
509
|
+
} catch (err) {
|
|
510
|
+
return CyclesResponse.transportError(
|
|
511
|
+
err instanceof Error ? err : new Error(String(err))
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async _get(path, params) {
|
|
516
|
+
try {
|
|
517
|
+
let url = `${this._config.baseUrl}${path}`;
|
|
518
|
+
if (params && Object.keys(params).length > 0) {
|
|
519
|
+
const qs = new URLSearchParams(params).toString();
|
|
520
|
+
url = `${url}?${qs}`;
|
|
521
|
+
}
|
|
522
|
+
const resp = await fetch(url, {
|
|
523
|
+
method: "GET",
|
|
524
|
+
headers: {
|
|
525
|
+
[API_KEY_HEADER]: this._config.apiKey
|
|
526
|
+
},
|
|
527
|
+
signal: AbortSignal.timeout(
|
|
528
|
+
this._config.connectTimeout + this._config.readTimeout
|
|
529
|
+
)
|
|
530
|
+
});
|
|
531
|
+
return this._handleResponse(resp);
|
|
532
|
+
} catch (err) {
|
|
533
|
+
return CyclesResponse.transportError(
|
|
534
|
+
err instanceof Error ? err : new Error(String(err))
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
async _handleResponse(resp) {
|
|
539
|
+
let body;
|
|
540
|
+
try {
|
|
541
|
+
body = await resp.json();
|
|
542
|
+
} catch {
|
|
543
|
+
body = void 0;
|
|
544
|
+
}
|
|
545
|
+
const headers = extractResponseHeaders(resp);
|
|
546
|
+
if (resp.status >= 200 && resp.status < 300) {
|
|
547
|
+
return CyclesResponse.success(resp.status, body ?? {}, headers);
|
|
548
|
+
}
|
|
549
|
+
let errorMsg;
|
|
550
|
+
if (body && typeof body === "object") {
|
|
551
|
+
errorMsg = body.message ?? body.error ?? void 0;
|
|
552
|
+
}
|
|
553
|
+
return CyclesResponse.httpError(
|
|
554
|
+
resp.status,
|
|
555
|
+
errorMsg ?? resp.statusText ?? "Unknown error",
|
|
556
|
+
body,
|
|
557
|
+
headers
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// src/config.ts
|
|
563
|
+
var CyclesConfig = class _CyclesConfig {
|
|
564
|
+
baseUrl;
|
|
565
|
+
apiKey;
|
|
566
|
+
tenant;
|
|
567
|
+
workspace;
|
|
568
|
+
app;
|
|
569
|
+
workflow;
|
|
570
|
+
agent;
|
|
571
|
+
toolset;
|
|
572
|
+
connectTimeout;
|
|
573
|
+
readTimeout;
|
|
574
|
+
retryEnabled;
|
|
575
|
+
retryMaxAttempts;
|
|
576
|
+
retryInitialDelay;
|
|
577
|
+
retryMultiplier;
|
|
578
|
+
retryMaxDelay;
|
|
579
|
+
constructor(options) {
|
|
580
|
+
this.baseUrl = options.baseUrl;
|
|
581
|
+
this.apiKey = options.apiKey;
|
|
582
|
+
this.tenant = options.tenant;
|
|
583
|
+
this.workspace = options.workspace;
|
|
584
|
+
this.app = options.app;
|
|
585
|
+
this.workflow = options.workflow;
|
|
586
|
+
this.agent = options.agent;
|
|
587
|
+
this.toolset = options.toolset;
|
|
588
|
+
this.connectTimeout = options.connectTimeout ?? 2e3;
|
|
589
|
+
this.readTimeout = options.readTimeout ?? 5e3;
|
|
590
|
+
this.retryEnabled = options.retryEnabled ?? true;
|
|
591
|
+
this.retryMaxAttempts = options.retryMaxAttempts ?? 5;
|
|
592
|
+
this.retryInitialDelay = options.retryInitialDelay ?? 500;
|
|
593
|
+
this.retryMultiplier = options.retryMultiplier ?? 2;
|
|
594
|
+
this.retryMaxDelay = options.retryMaxDelay ?? 3e4;
|
|
595
|
+
}
|
|
596
|
+
static fromEnv(prefix = "CYCLES_") {
|
|
597
|
+
const baseUrl = process.env[`${prefix}BASE_URL`];
|
|
598
|
+
const apiKey = process.env[`${prefix}API_KEY`];
|
|
599
|
+
if (!baseUrl) {
|
|
600
|
+
throw new Error(`${prefix}BASE_URL environment variable is required`);
|
|
601
|
+
}
|
|
602
|
+
if (!apiKey) {
|
|
603
|
+
throw new Error(`${prefix}API_KEY environment variable is required`);
|
|
604
|
+
}
|
|
605
|
+
return new _CyclesConfig({
|
|
606
|
+
baseUrl,
|
|
607
|
+
apiKey,
|
|
608
|
+
tenant: process.env[`${prefix}TENANT`],
|
|
609
|
+
workspace: process.env[`${prefix}WORKSPACE`],
|
|
610
|
+
app: process.env[`${prefix}APP`],
|
|
611
|
+
workflow: process.env[`${prefix}WORKFLOW`],
|
|
612
|
+
agent: process.env[`${prefix}AGENT`],
|
|
613
|
+
toolset: process.env[`${prefix}TOOLSET`],
|
|
614
|
+
connectTimeout: optionalFloat(process.env[`${prefix}CONNECT_TIMEOUT`], 2e3),
|
|
615
|
+
readTimeout: optionalFloat(process.env[`${prefix}READ_TIMEOUT`], 5e3),
|
|
616
|
+
retryEnabled: process.env[`${prefix}RETRY_ENABLED`]?.toLowerCase() !== "false",
|
|
617
|
+
retryMaxAttempts: optionalInt(process.env[`${prefix}RETRY_MAX_ATTEMPTS`], 5),
|
|
618
|
+
retryInitialDelay: optionalFloat(process.env[`${prefix}RETRY_INITIAL_DELAY`], 500),
|
|
619
|
+
retryMultiplier: optionalFloat(process.env[`${prefix}RETRY_MULTIPLIER`], 2),
|
|
620
|
+
retryMaxDelay: optionalFloat(process.env[`${prefix}RETRY_MAX_DELAY`], 3e4)
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
function optionalInt(val, fallback) {
|
|
625
|
+
return val !== void 0 ? parseInt(val, 10) : fallback;
|
|
626
|
+
}
|
|
627
|
+
function optionalFloat(val, fallback) {
|
|
628
|
+
return val !== void 0 ? parseFloat(val) : fallback;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/lifecycle.ts
|
|
632
|
+
var import_node_crypto = require("crypto");
|
|
633
|
+
|
|
634
|
+
// src/context.ts
|
|
635
|
+
var import_node_async_hooks = require("async_hooks");
|
|
636
|
+
var storage = new import_node_async_hooks.AsyncLocalStorage();
|
|
637
|
+
function getCyclesContext() {
|
|
638
|
+
return storage.getStore();
|
|
639
|
+
}
|
|
640
|
+
function runWithContext(ctx, fn) {
|
|
641
|
+
return storage.run(ctx, fn);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/exceptions.ts
|
|
645
|
+
var CyclesError = class extends Error {
|
|
646
|
+
constructor(message) {
|
|
647
|
+
super(message);
|
|
648
|
+
this.name = "CyclesError";
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
var CyclesProtocolError = class extends CyclesError {
|
|
652
|
+
status;
|
|
653
|
+
errorCode;
|
|
654
|
+
reasonCode;
|
|
655
|
+
retryAfterMs;
|
|
656
|
+
requestId;
|
|
657
|
+
details;
|
|
658
|
+
constructor(message, options = {}) {
|
|
659
|
+
super(message);
|
|
660
|
+
this.name = "CyclesProtocolError";
|
|
661
|
+
this.status = options.status ?? 0;
|
|
662
|
+
this.errorCode = options.errorCode;
|
|
663
|
+
this.reasonCode = options.reasonCode;
|
|
664
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
665
|
+
this.requestId = options.requestId;
|
|
666
|
+
this.details = options.details;
|
|
667
|
+
}
|
|
668
|
+
isBudgetExceeded() {
|
|
669
|
+
return this.errorCode === "BUDGET_EXCEEDED";
|
|
670
|
+
}
|
|
671
|
+
isOverdraftLimitExceeded() {
|
|
672
|
+
return this.errorCode === "OVERDRAFT_LIMIT_EXCEEDED";
|
|
673
|
+
}
|
|
674
|
+
isDebtOutstanding() {
|
|
675
|
+
return this.errorCode === "DEBT_OUTSTANDING";
|
|
676
|
+
}
|
|
677
|
+
isReservationExpired() {
|
|
678
|
+
return this.errorCode === "RESERVATION_EXPIRED";
|
|
679
|
+
}
|
|
680
|
+
isReservationFinalized() {
|
|
681
|
+
return this.errorCode === "RESERVATION_FINALIZED";
|
|
682
|
+
}
|
|
683
|
+
isIdempotencyMismatch() {
|
|
684
|
+
return this.errorCode === "IDEMPOTENCY_MISMATCH";
|
|
685
|
+
}
|
|
686
|
+
isUnitMismatch() {
|
|
687
|
+
return this.errorCode === "UNIT_MISMATCH";
|
|
688
|
+
}
|
|
689
|
+
isRetryable() {
|
|
690
|
+
return this.errorCode === "INTERNAL_ERROR" || this.errorCode === "UNKNOWN" || this.status >= 500;
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
var BudgetExceededError = class extends CyclesProtocolError {
|
|
694
|
+
constructor(message, options = {}) {
|
|
695
|
+
super(message, options);
|
|
696
|
+
this.name = "BudgetExceededError";
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
var OverdraftLimitExceededError = class extends CyclesProtocolError {
|
|
700
|
+
constructor(message, options = {}) {
|
|
701
|
+
super(message, options);
|
|
702
|
+
this.name = "OverdraftLimitExceededError";
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
var DebtOutstandingError = class extends CyclesProtocolError {
|
|
706
|
+
constructor(message, options = {}) {
|
|
707
|
+
super(message, options);
|
|
708
|
+
this.name = "DebtOutstandingError";
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
var ReservationExpiredError = class extends CyclesProtocolError {
|
|
712
|
+
constructor(message, options = {}) {
|
|
713
|
+
super(message, options);
|
|
714
|
+
this.name = "ReservationExpiredError";
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
var ReservationFinalizedError = class extends CyclesProtocolError {
|
|
718
|
+
constructor(message, options = {}) {
|
|
719
|
+
super(message, options);
|
|
720
|
+
this.name = "ReservationFinalizedError";
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
var CyclesTransportError = class extends CyclesError {
|
|
724
|
+
cause;
|
|
725
|
+
constructor(message, options) {
|
|
726
|
+
super(message);
|
|
727
|
+
this.name = "CyclesTransportError";
|
|
728
|
+
this.cause = options?.cause;
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// src/errors.ts
|
|
733
|
+
function buildProtocolException(prefix, response) {
|
|
734
|
+
const errorResp = response.getErrorResponse();
|
|
735
|
+
let errorCode;
|
|
736
|
+
let reasonCode;
|
|
737
|
+
let message = prefix;
|
|
738
|
+
let requestId;
|
|
739
|
+
let details;
|
|
740
|
+
if (errorResp) {
|
|
741
|
+
errorCode = errorResp.error;
|
|
742
|
+
requestId = errorResp.requestId;
|
|
743
|
+
details = errorResp.details;
|
|
744
|
+
if (errorResp.message) {
|
|
745
|
+
message = `${prefix}: ${errorResp.message}`;
|
|
746
|
+
}
|
|
747
|
+
} else {
|
|
748
|
+
const rawError = response.getBodyAttribute("error");
|
|
749
|
+
if (typeof rawError === "string") {
|
|
750
|
+
errorCode = rawError;
|
|
751
|
+
}
|
|
752
|
+
if (response.errorMessage) {
|
|
753
|
+
message = `${prefix}: ${response.errorMessage}`;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
reasonCode = response.getBodyAttribute("reason_code");
|
|
757
|
+
if (reasonCode === void 0 && errorCode !== void 0) {
|
|
758
|
+
reasonCode = errorCode;
|
|
759
|
+
}
|
|
760
|
+
const retryRaw = response.getBodyAttribute("retry_after_ms");
|
|
761
|
+
const retryAfterMs = retryRaw !== void 0 ? Number(retryRaw) : void 0;
|
|
762
|
+
const opts = {
|
|
763
|
+
status: response.status,
|
|
764
|
+
errorCode,
|
|
765
|
+
reasonCode,
|
|
766
|
+
retryAfterMs,
|
|
767
|
+
requestId,
|
|
768
|
+
details
|
|
769
|
+
};
|
|
770
|
+
switch (errorCode) {
|
|
771
|
+
case "BUDGET_EXCEEDED":
|
|
772
|
+
return new BudgetExceededError(message, opts);
|
|
773
|
+
case "OVERDRAFT_LIMIT_EXCEEDED":
|
|
774
|
+
return new OverdraftLimitExceededError(message, opts);
|
|
775
|
+
case "DEBT_OUTSTANDING":
|
|
776
|
+
return new DebtOutstandingError(message, opts);
|
|
777
|
+
case "RESERVATION_EXPIRED":
|
|
778
|
+
return new ReservationExpiredError(message, opts);
|
|
779
|
+
case "RESERVATION_FINALIZED":
|
|
780
|
+
return new ReservationFinalizedError(message, opts);
|
|
781
|
+
default:
|
|
782
|
+
return new CyclesProtocolError(message, opts);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/validation.ts
|
|
787
|
+
function validateSubject(subject) {
|
|
788
|
+
if (subject === void 0) return;
|
|
789
|
+
const hasField = !!(subject.tenant || subject.workspace || subject.app || subject.workflow || subject.agent || subject.toolset);
|
|
790
|
+
if (!hasField) {
|
|
791
|
+
throw new Error(
|
|
792
|
+
"Subject must have at least one standard field (tenant, workspace, app, workflow, agent, or toolset)"
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
function validateNonNegative(value, name) {
|
|
797
|
+
if (value < 0) {
|
|
798
|
+
throw new Error(`${name} must be non-negative, got ${value}`);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function validateTtlMs(ttlMs) {
|
|
802
|
+
if (ttlMs < 1e3 || ttlMs > 864e5) {
|
|
803
|
+
throw new Error(`ttl_ms must be between 1000 and 86400000, got ${ttlMs}`);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
function validateGracePeriodMs(gracePeriodMs) {
|
|
807
|
+
if (gracePeriodMs !== void 0 && (gracePeriodMs < 0 || gracePeriodMs > 6e4)) {
|
|
808
|
+
throw new Error(`grace_period_ms must be between 0 and 60000, got ${gracePeriodMs}`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function validateExtendByMs(extendByMs) {
|
|
812
|
+
if (extendByMs < 1 || extendByMs > 864e5) {
|
|
813
|
+
throw new Error(`extend_by_ms must be between 1 and 86400000, got ${extendByMs}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/lifecycle.ts
|
|
818
|
+
function evaluateAmount(expr, args) {
|
|
819
|
+
if (typeof expr === "function") {
|
|
820
|
+
return expr(...args);
|
|
821
|
+
}
|
|
822
|
+
return expr;
|
|
823
|
+
}
|
|
824
|
+
function evaluateActual(expr, result, estimate, useEstimateFallback) {
|
|
825
|
+
if (expr !== void 0) {
|
|
826
|
+
if (typeof expr === "function") {
|
|
827
|
+
return expr(result);
|
|
828
|
+
}
|
|
829
|
+
return expr;
|
|
830
|
+
}
|
|
831
|
+
if (useEstimateFallback) {
|
|
832
|
+
return estimate;
|
|
833
|
+
}
|
|
834
|
+
throw new Error(
|
|
835
|
+
"actual expression is required when useEstimateIfActualNotProvided is false"
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
function buildReservationBody(cfg, estimate, defaultSubject) {
|
|
839
|
+
validateNonNegative(estimate, "estimate");
|
|
840
|
+
const ttlMs = cfg.ttlMs ?? 6e4;
|
|
841
|
+
validateTtlMs(ttlMs);
|
|
842
|
+
const subject = {};
|
|
843
|
+
for (const field of [
|
|
844
|
+
"tenant",
|
|
845
|
+
"workspace",
|
|
846
|
+
"app",
|
|
847
|
+
"workflow",
|
|
848
|
+
"agent",
|
|
849
|
+
"toolset"
|
|
850
|
+
]) {
|
|
851
|
+
const val = cfg[field] ?? defaultSubject[field];
|
|
852
|
+
if (val) {
|
|
853
|
+
subject[field] = val;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (cfg.dimensions) {
|
|
857
|
+
subject.dimensions = cfg.dimensions;
|
|
858
|
+
}
|
|
859
|
+
validateSubject(subject);
|
|
860
|
+
const action = {
|
|
861
|
+
kind: cfg.actionKind ?? "unknown",
|
|
862
|
+
name: cfg.actionName ?? "unknown"
|
|
863
|
+
};
|
|
864
|
+
if (cfg.actionTags) {
|
|
865
|
+
action.tags = cfg.actionTags;
|
|
866
|
+
}
|
|
867
|
+
const unit = cfg.unit ?? "USD_MICROCENTS";
|
|
868
|
+
const body = {
|
|
869
|
+
idempotency_key: (0, import_node_crypto.randomUUID)(),
|
|
870
|
+
subject,
|
|
871
|
+
action,
|
|
872
|
+
estimate: { unit, amount: estimate },
|
|
873
|
+
ttl_ms: ttlMs,
|
|
874
|
+
overage_policy: cfg.overagePolicy ?? "REJECT"
|
|
875
|
+
};
|
|
876
|
+
validateGracePeriodMs(cfg.gracePeriodMs);
|
|
877
|
+
if (cfg.gracePeriodMs !== void 0) {
|
|
878
|
+
body.grace_period_ms = cfg.gracePeriodMs;
|
|
879
|
+
}
|
|
880
|
+
if (cfg.dryRun) {
|
|
881
|
+
body.dry_run = true;
|
|
882
|
+
}
|
|
883
|
+
return body;
|
|
884
|
+
}
|
|
885
|
+
function buildCommitBody(actual, unit, metrics, metadata) {
|
|
886
|
+
const body = {
|
|
887
|
+
idempotency_key: (0, import_node_crypto.randomUUID)(),
|
|
888
|
+
actual: { unit, amount: actual }
|
|
889
|
+
};
|
|
890
|
+
if (metrics && !isMetricsEmpty(metrics)) {
|
|
891
|
+
body.metrics = metricsToWire(metrics);
|
|
892
|
+
}
|
|
893
|
+
if (metadata) {
|
|
894
|
+
body.metadata = metadata;
|
|
895
|
+
}
|
|
896
|
+
return body;
|
|
897
|
+
}
|
|
898
|
+
function isMetricsEmpty(metrics) {
|
|
899
|
+
return metrics.tokensInput === void 0 && metrics.tokensOutput === void 0 && metrics.latencyMs === void 0 && metrics.modelVersion === void 0 && !metrics.custom;
|
|
900
|
+
}
|
|
901
|
+
function buildReleaseBody(reason) {
|
|
902
|
+
return { idempotency_key: (0, import_node_crypto.randomUUID)(), reason };
|
|
903
|
+
}
|
|
904
|
+
function buildExtendBody(extendByMs) {
|
|
905
|
+
validateExtendByMs(extendByMs);
|
|
906
|
+
return { idempotency_key: (0, import_node_crypto.randomUUID)(), extend_by_ms: extendByMs };
|
|
907
|
+
}
|
|
908
|
+
var AsyncCyclesLifecycle = class {
|
|
909
|
+
_client;
|
|
910
|
+
_retryEngine;
|
|
911
|
+
_defaultSubject;
|
|
912
|
+
constructor(client, retryEngine, defaultSubject) {
|
|
913
|
+
this._client = client;
|
|
914
|
+
this._retryEngine = retryEngine;
|
|
915
|
+
this._retryEngine.setClient(client);
|
|
916
|
+
this._defaultSubject = defaultSubject;
|
|
917
|
+
}
|
|
918
|
+
async execute(fn, args, cfg) {
|
|
919
|
+
const estimate = evaluateAmount(cfg.estimate, args);
|
|
920
|
+
const createBody = buildReservationBody(cfg, estimate, this._defaultSubject);
|
|
921
|
+
const resT1 = performance.now();
|
|
922
|
+
const resResponse = await this._client.createReservation(createBody);
|
|
923
|
+
if (!resResponse.isSuccess) {
|
|
924
|
+
throw buildProtocolException("Failed to create reservation", resResponse);
|
|
925
|
+
}
|
|
926
|
+
const resResult = reservationCreateResponseFromWire(
|
|
927
|
+
resResponse.body
|
|
928
|
+
);
|
|
929
|
+
const resT2 = performance.now();
|
|
930
|
+
const decision = resResult.decision;
|
|
931
|
+
const reservationId = resResult.reservationId;
|
|
932
|
+
const reasonCode = resResult.reasonCode;
|
|
933
|
+
if (cfg.dryRun) {
|
|
934
|
+
if (decision === "DENY") {
|
|
935
|
+
throw buildProtocolException("Dry-run denied", resResponse);
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
decision,
|
|
939
|
+
caps: resResult.caps,
|
|
940
|
+
affectedScopes: resResult.affectedScopes,
|
|
941
|
+
scopePath: resResult.scopePath,
|
|
942
|
+
reserved: resResult.reserved,
|
|
943
|
+
balances: resResult.balances,
|
|
944
|
+
reasonCode,
|
|
945
|
+
retryAfterMs: resResult.retryAfterMs
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
if (decision === "DENY") {
|
|
949
|
+
throw buildProtocolException("Reservation denied", resResponse);
|
|
950
|
+
}
|
|
951
|
+
if (!reservationId) {
|
|
952
|
+
throw new CyclesProtocolError(
|
|
953
|
+
"Reservation successful but reservation_id missing",
|
|
954
|
+
{ status: resResponse.status }
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
const unit = cfg.unit ?? "USD_MICROCENTS";
|
|
958
|
+
const ttlMs = cfg.ttlMs ?? 6e4;
|
|
959
|
+
const ctx = {
|
|
960
|
+
reservationId,
|
|
961
|
+
estimate,
|
|
962
|
+
decision,
|
|
963
|
+
caps: resResult.caps,
|
|
964
|
+
expiresAtMs: resResult.expiresAtMs,
|
|
965
|
+
affectedScopes: resResult.affectedScopes,
|
|
966
|
+
scopePath: resResult.scopePath,
|
|
967
|
+
reserved: resResult.reserved,
|
|
968
|
+
balances: resResult.balances
|
|
969
|
+
};
|
|
970
|
+
const heartbeatRef = this._startHeartbeat(reservationId, ttlMs, ctx);
|
|
971
|
+
try {
|
|
972
|
+
const result = await runWithContext(ctx, () => fn(...args));
|
|
973
|
+
const methodElapsed = Math.round(performance.now() - resT2);
|
|
974
|
+
const useEstimateFallback = cfg.useEstimateIfActualNotProvided !== false;
|
|
975
|
+
const actualAmount = evaluateActual(
|
|
976
|
+
cfg.actual,
|
|
977
|
+
result,
|
|
978
|
+
estimate,
|
|
979
|
+
useEstimateFallback
|
|
980
|
+
);
|
|
981
|
+
let metrics = ctx.metrics;
|
|
982
|
+
if (!metrics) {
|
|
983
|
+
metrics = {};
|
|
984
|
+
}
|
|
985
|
+
if (metrics.latencyMs === void 0) {
|
|
986
|
+
metrics = { ...metrics, latencyMs: methodElapsed };
|
|
987
|
+
}
|
|
988
|
+
const commitBody = buildCommitBody(
|
|
989
|
+
actualAmount,
|
|
990
|
+
unit,
|
|
991
|
+
metrics,
|
|
992
|
+
ctx.commitMetadata
|
|
993
|
+
);
|
|
994
|
+
await this._handleCommit(reservationId, commitBody);
|
|
995
|
+
return result;
|
|
996
|
+
} catch (err) {
|
|
997
|
+
await this._handleRelease(reservationId, "guarded_method_failed");
|
|
998
|
+
throw err;
|
|
999
|
+
} finally {
|
|
1000
|
+
if (heartbeatRef) {
|
|
1001
|
+
heartbeatRef.stop();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
async _handleCommit(reservationId, commitBody) {
|
|
1006
|
+
try {
|
|
1007
|
+
const response = await this._client.commitReservation(
|
|
1008
|
+
reservationId,
|
|
1009
|
+
commitBody
|
|
1010
|
+
);
|
|
1011
|
+
if (response.isSuccess) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
if (response.isTransportError || response.isServerError) {
|
|
1015
|
+
this._retryEngine.schedule(reservationId, commitBody);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
const errorResp = response.getErrorResponse();
|
|
1019
|
+
const errorCode = errorResp?.error;
|
|
1020
|
+
if (errorCode === "RESERVATION_FINALIZED" || errorCode === "RESERVATION_EXPIRED") {
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
if (errorCode === "IDEMPOTENCY_MISMATCH") {
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
if (response.isClientError) {
|
|
1027
|
+
await this._handleRelease(
|
|
1028
|
+
reservationId,
|
|
1029
|
+
`commit_rejected_${errorCode}`
|
|
1030
|
+
);
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
} catch {
|
|
1034
|
+
this._retryEngine.schedule(reservationId, commitBody);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
async _handleRelease(reservationId, reason) {
|
|
1038
|
+
try {
|
|
1039
|
+
const body = buildReleaseBody(reason);
|
|
1040
|
+
await this._client.releaseReservation(reservationId, body);
|
|
1041
|
+
} catch {
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
_startHeartbeat(reservationId, ttlMs, ctx) {
|
|
1045
|
+
if (ttlMs <= 0) return void 0;
|
|
1046
|
+
const intervalMs = Math.max(ttlMs / 2, 1e3);
|
|
1047
|
+
let stopped = false;
|
|
1048
|
+
let currentTimer;
|
|
1049
|
+
const tick = () => {
|
|
1050
|
+
if (stopped) return;
|
|
1051
|
+
currentTimer = setTimeout(() => {
|
|
1052
|
+
if (stopped) return;
|
|
1053
|
+
const body = buildExtendBody(ttlMs);
|
|
1054
|
+
void this._client.extendReservation(reservationId, body).then((response) => {
|
|
1055
|
+
if (response.isSuccess) {
|
|
1056
|
+
const newExpires = response.getBodyAttribute("expires_at_ms");
|
|
1057
|
+
if (typeof newExpires === "number") {
|
|
1058
|
+
ctx.expiresAtMs = newExpires;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}).catch(() => {
|
|
1062
|
+
}).finally(() => {
|
|
1063
|
+
tick();
|
|
1064
|
+
});
|
|
1065
|
+
}, intervalMs);
|
|
1066
|
+
};
|
|
1067
|
+
tick();
|
|
1068
|
+
return {
|
|
1069
|
+
stop: () => {
|
|
1070
|
+
stopped = true;
|
|
1071
|
+
clearTimeout(currentTimer);
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
// src/retry.ts
|
|
1078
|
+
function delay(ms) {
|
|
1079
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1080
|
+
}
|
|
1081
|
+
var CommitRetryEngine = class {
|
|
1082
|
+
_enabled;
|
|
1083
|
+
_maxAttempts;
|
|
1084
|
+
_initialDelay;
|
|
1085
|
+
_multiplier;
|
|
1086
|
+
_maxDelay;
|
|
1087
|
+
_client;
|
|
1088
|
+
constructor(config) {
|
|
1089
|
+
this._enabled = config.retryEnabled;
|
|
1090
|
+
this._maxAttempts = config.retryMaxAttempts;
|
|
1091
|
+
this._initialDelay = config.retryInitialDelay;
|
|
1092
|
+
this._multiplier = config.retryMultiplier;
|
|
1093
|
+
this._maxDelay = config.retryMaxDelay;
|
|
1094
|
+
}
|
|
1095
|
+
setClient(client) {
|
|
1096
|
+
this._client = client;
|
|
1097
|
+
}
|
|
1098
|
+
schedule(reservationId, commitBody) {
|
|
1099
|
+
if (!this._enabled) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
void this._retryLoop(reservationId, commitBody);
|
|
1103
|
+
}
|
|
1104
|
+
async _retryLoop(reservationId, commitBody) {
|
|
1105
|
+
for (let attempt = 0; attempt < this._maxAttempts; attempt++) {
|
|
1106
|
+
const backoff = Math.min(
|
|
1107
|
+
this._initialDelay * this._multiplier ** attempt,
|
|
1108
|
+
this._maxDelay
|
|
1109
|
+
);
|
|
1110
|
+
await delay(backoff);
|
|
1111
|
+
try {
|
|
1112
|
+
if (!this._client) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const response = await this._client.commitReservation(
|
|
1116
|
+
reservationId,
|
|
1117
|
+
commitBody
|
|
1118
|
+
);
|
|
1119
|
+
if (response.isSuccess) {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (response.isClientError) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
} catch {
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
// src/withCycles.ts
|
|
1132
|
+
var _defaultClient;
|
|
1133
|
+
var _defaultConfig;
|
|
1134
|
+
function setDefaultClient(client) {
|
|
1135
|
+
_defaultClient = client;
|
|
1136
|
+
}
|
|
1137
|
+
function setDefaultConfig(config) {
|
|
1138
|
+
_defaultConfig = config;
|
|
1139
|
+
}
|
|
1140
|
+
function getEffectiveClient(explicitClient) {
|
|
1141
|
+
if (explicitClient) return explicitClient;
|
|
1142
|
+
if (_defaultClient) return _defaultClient;
|
|
1143
|
+
if (_defaultConfig) {
|
|
1144
|
+
_defaultClient = new CyclesClient(_defaultConfig);
|
|
1145
|
+
return _defaultClient;
|
|
1146
|
+
}
|
|
1147
|
+
throw new Error(
|
|
1148
|
+
"No Cycles client available. Either pass client in options, call setDefaultClient(), or call setDefaultConfig()."
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
function withCycles(options, fn) {
|
|
1152
|
+
return async (...args) => {
|
|
1153
|
+
const client = getEffectiveClient(options.client);
|
|
1154
|
+
const config = client.config;
|
|
1155
|
+
const defaultSubject = {
|
|
1156
|
+
tenant: config.tenant,
|
|
1157
|
+
workspace: config.workspace,
|
|
1158
|
+
app: config.app,
|
|
1159
|
+
workflow: config.workflow,
|
|
1160
|
+
agent: config.agent,
|
|
1161
|
+
toolset: config.toolset
|
|
1162
|
+
};
|
|
1163
|
+
const retryEngine = new CommitRetryEngine(config);
|
|
1164
|
+
const lifecycle = new AsyncCyclesLifecycle(
|
|
1165
|
+
client,
|
|
1166
|
+
retryEngine,
|
|
1167
|
+
defaultSubject
|
|
1168
|
+
);
|
|
1169
|
+
return lifecycle.execute(
|
|
1170
|
+
fn,
|
|
1171
|
+
args,
|
|
1172
|
+
options
|
|
1173
|
+
);
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// src/streaming.ts
|
|
1178
|
+
var import_node_crypto2 = require("crypto");
|
|
1179
|
+
|
|
1180
|
+
// src/models.ts
|
|
1181
|
+
var Unit = /* @__PURE__ */ ((Unit2) => {
|
|
1182
|
+
Unit2["USD_MICROCENTS"] = "USD_MICROCENTS";
|
|
1183
|
+
Unit2["TOKENS"] = "TOKENS";
|
|
1184
|
+
Unit2["CREDITS"] = "CREDITS";
|
|
1185
|
+
Unit2["RISK_POINTS"] = "RISK_POINTS";
|
|
1186
|
+
return Unit2;
|
|
1187
|
+
})(Unit || {});
|
|
1188
|
+
var CommitOveragePolicy = /* @__PURE__ */ ((CommitOveragePolicy2) => {
|
|
1189
|
+
CommitOveragePolicy2["REJECT"] = "REJECT";
|
|
1190
|
+
CommitOveragePolicy2["ALLOW_IF_AVAILABLE"] = "ALLOW_IF_AVAILABLE";
|
|
1191
|
+
CommitOveragePolicy2["ALLOW_WITH_OVERDRAFT"] = "ALLOW_WITH_OVERDRAFT";
|
|
1192
|
+
return CommitOveragePolicy2;
|
|
1193
|
+
})(CommitOveragePolicy || {});
|
|
1194
|
+
var Decision = /* @__PURE__ */ ((Decision2) => {
|
|
1195
|
+
Decision2["ALLOW"] = "ALLOW";
|
|
1196
|
+
Decision2["ALLOW_WITH_CAPS"] = "ALLOW_WITH_CAPS";
|
|
1197
|
+
Decision2["DENY"] = "DENY";
|
|
1198
|
+
return Decision2;
|
|
1199
|
+
})(Decision || {});
|
|
1200
|
+
var ReservationStatus = /* @__PURE__ */ ((ReservationStatus2) => {
|
|
1201
|
+
ReservationStatus2["ACTIVE"] = "ACTIVE";
|
|
1202
|
+
ReservationStatus2["COMMITTED"] = "COMMITTED";
|
|
1203
|
+
ReservationStatus2["RELEASED"] = "RELEASED";
|
|
1204
|
+
ReservationStatus2["EXPIRED"] = "EXPIRED";
|
|
1205
|
+
return ReservationStatus2;
|
|
1206
|
+
})(ReservationStatus || {});
|
|
1207
|
+
var CommitStatus = /* @__PURE__ */ ((CommitStatus2) => {
|
|
1208
|
+
CommitStatus2["COMMITTED"] = "COMMITTED";
|
|
1209
|
+
return CommitStatus2;
|
|
1210
|
+
})(CommitStatus || {});
|
|
1211
|
+
var ReleaseStatus = /* @__PURE__ */ ((ReleaseStatus2) => {
|
|
1212
|
+
ReleaseStatus2["RELEASED"] = "RELEASED";
|
|
1213
|
+
return ReleaseStatus2;
|
|
1214
|
+
})(ReleaseStatus || {});
|
|
1215
|
+
var ExtendStatus = /* @__PURE__ */ ((ExtendStatus2) => {
|
|
1216
|
+
ExtendStatus2["ACTIVE"] = "ACTIVE";
|
|
1217
|
+
return ExtendStatus2;
|
|
1218
|
+
})(ExtendStatus || {});
|
|
1219
|
+
var EventStatus = /* @__PURE__ */ ((EventStatus2) => {
|
|
1220
|
+
EventStatus2["APPLIED"] = "APPLIED";
|
|
1221
|
+
return EventStatus2;
|
|
1222
|
+
})(EventStatus || {});
|
|
1223
|
+
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
1224
|
+
ErrorCode2["INVALID_REQUEST"] = "INVALID_REQUEST";
|
|
1225
|
+
ErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED";
|
|
1226
|
+
ErrorCode2["FORBIDDEN"] = "FORBIDDEN";
|
|
1227
|
+
ErrorCode2["NOT_FOUND"] = "NOT_FOUND";
|
|
1228
|
+
ErrorCode2["BUDGET_EXCEEDED"] = "BUDGET_EXCEEDED";
|
|
1229
|
+
ErrorCode2["RESERVATION_EXPIRED"] = "RESERVATION_EXPIRED";
|
|
1230
|
+
ErrorCode2["RESERVATION_FINALIZED"] = "RESERVATION_FINALIZED";
|
|
1231
|
+
ErrorCode2["IDEMPOTENCY_MISMATCH"] = "IDEMPOTENCY_MISMATCH";
|
|
1232
|
+
ErrorCode2["UNIT_MISMATCH"] = "UNIT_MISMATCH";
|
|
1233
|
+
ErrorCode2["OVERDRAFT_LIMIT_EXCEEDED"] = "OVERDRAFT_LIMIT_EXCEEDED";
|
|
1234
|
+
ErrorCode2["DEBT_OUTSTANDING"] = "DEBT_OUTSTANDING";
|
|
1235
|
+
ErrorCode2["INTERNAL_ERROR"] = "INTERNAL_ERROR";
|
|
1236
|
+
ErrorCode2["UNKNOWN"] = "UNKNOWN";
|
|
1237
|
+
return ErrorCode2;
|
|
1238
|
+
})(ErrorCode || {});
|
|
1239
|
+
function isAllowed(decision) {
|
|
1240
|
+
return decision === "ALLOW" /* ALLOW */ || decision === "ALLOW_WITH_CAPS" /* ALLOW_WITH_CAPS */;
|
|
1241
|
+
}
|
|
1242
|
+
function isDenied(decision) {
|
|
1243
|
+
return decision === "DENY" /* DENY */;
|
|
1244
|
+
}
|
|
1245
|
+
function isRetryableErrorCode(code) {
|
|
1246
|
+
return code === "INTERNAL_ERROR" /* INTERNAL_ERROR */ || code === "UNKNOWN" /* UNKNOWN */;
|
|
1247
|
+
}
|
|
1248
|
+
function errorCodeFromString(value) {
|
|
1249
|
+
if (value === void 0) return void 0;
|
|
1250
|
+
if (Object.values(ErrorCode).includes(value)) {
|
|
1251
|
+
return value;
|
|
1252
|
+
}
|
|
1253
|
+
return "UNKNOWN" /* UNKNOWN */;
|
|
1254
|
+
}
|
|
1255
|
+
function isToolAllowed(caps, tool) {
|
|
1256
|
+
if (caps.toolAllowlist && caps.toolAllowlist.length > 0) {
|
|
1257
|
+
return caps.toolAllowlist.includes(tool);
|
|
1258
|
+
}
|
|
1259
|
+
if (caps.toolDenylist && caps.toolDenylist.length > 0) {
|
|
1260
|
+
return !caps.toolDenylist.includes(tool);
|
|
1261
|
+
}
|
|
1262
|
+
return true;
|
|
1263
|
+
}
|
|
1264
|
+
function isMetricsEmpty2(metrics) {
|
|
1265
|
+
return metrics.tokensInput === void 0 && metrics.tokensOutput === void 0 && metrics.latencyMs === void 0 && metrics.modelVersion === void 0 && !metrics.custom;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// src/streaming.ts
|
|
1269
|
+
async function reserveForStream(options) {
|
|
1270
|
+
const {
|
|
1271
|
+
client,
|
|
1272
|
+
estimate,
|
|
1273
|
+
unit = "USD_MICROCENTS",
|
|
1274
|
+
actionKind = "unknown",
|
|
1275
|
+
actionName = "unknown",
|
|
1276
|
+
actionTags,
|
|
1277
|
+
ttlMs = 6e4,
|
|
1278
|
+
gracePeriodMs,
|
|
1279
|
+
overagePolicy = "REJECT",
|
|
1280
|
+
dimensions
|
|
1281
|
+
} = options;
|
|
1282
|
+
validateNonNegative(estimate, "estimate");
|
|
1283
|
+
validateTtlMs(ttlMs);
|
|
1284
|
+
validateGracePeriodMs(gracePeriodMs);
|
|
1285
|
+
const configDefaults = client.config;
|
|
1286
|
+
const subject = {};
|
|
1287
|
+
for (const field of ["tenant", "workspace", "app", "workflow", "agent", "toolset"]) {
|
|
1288
|
+
const val = options[field] ?? configDefaults[field];
|
|
1289
|
+
if (val) {
|
|
1290
|
+
subject[field] = val;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
if (dimensions) {
|
|
1294
|
+
subject.dimensions = dimensions;
|
|
1295
|
+
}
|
|
1296
|
+
validateSubject(subject);
|
|
1297
|
+
const action = { kind: actionKind, name: actionName };
|
|
1298
|
+
if (actionTags) {
|
|
1299
|
+
action.tags = actionTags;
|
|
1300
|
+
}
|
|
1301
|
+
const body = {
|
|
1302
|
+
idempotency_key: (0, import_node_crypto2.randomUUID)(),
|
|
1303
|
+
subject,
|
|
1304
|
+
action,
|
|
1305
|
+
estimate: { unit, amount: estimate },
|
|
1306
|
+
ttl_ms: ttlMs,
|
|
1307
|
+
overage_policy: overagePolicy
|
|
1308
|
+
};
|
|
1309
|
+
if (gracePeriodMs !== void 0) {
|
|
1310
|
+
body.grace_period_ms = gracePeriodMs;
|
|
1311
|
+
}
|
|
1312
|
+
const response = await client.createReservation(body);
|
|
1313
|
+
if (!response.isSuccess) {
|
|
1314
|
+
throw buildProtocolException("Failed to create reservation", response);
|
|
1315
|
+
}
|
|
1316
|
+
const parsed = reservationCreateResponseFromWire(
|
|
1317
|
+
response.body
|
|
1318
|
+
);
|
|
1319
|
+
if (parsed.decision === "DENY") {
|
|
1320
|
+
throw buildProtocolException("Reservation denied", response);
|
|
1321
|
+
}
|
|
1322
|
+
const reservationId = parsed.reservationId;
|
|
1323
|
+
if (!reservationId) {
|
|
1324
|
+
throw new CyclesProtocolError(
|
|
1325
|
+
"Reservation successful but reservation_id missing",
|
|
1326
|
+
{ status: response.status }
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
let heartbeatStopped = false;
|
|
1330
|
+
let finalized = false;
|
|
1331
|
+
let currentTimer;
|
|
1332
|
+
const stopHeartbeat = () => {
|
|
1333
|
+
if (!heartbeatStopped) {
|
|
1334
|
+
heartbeatStopped = true;
|
|
1335
|
+
clearTimeout(currentTimer);
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
const startHeartbeat = () => {
|
|
1339
|
+
if (ttlMs <= 0) return;
|
|
1340
|
+
const intervalMs = Math.max(ttlMs / 2, 1e3);
|
|
1341
|
+
const tick = () => {
|
|
1342
|
+
if (heartbeatStopped) return;
|
|
1343
|
+
currentTimer = setTimeout(() => {
|
|
1344
|
+
if (heartbeatStopped) return;
|
|
1345
|
+
validateExtendByMs(ttlMs);
|
|
1346
|
+
const extendBody = { idempotency_key: (0, import_node_crypto2.randomUUID)(), extend_by_ms: ttlMs };
|
|
1347
|
+
void client.extendReservation(reservationId, extendBody).catch(() => {
|
|
1348
|
+
}).finally(() => {
|
|
1349
|
+
tick();
|
|
1350
|
+
});
|
|
1351
|
+
}, intervalMs);
|
|
1352
|
+
};
|
|
1353
|
+
tick();
|
|
1354
|
+
};
|
|
1355
|
+
startHeartbeat();
|
|
1356
|
+
return {
|
|
1357
|
+
reservationId,
|
|
1358
|
+
decision: parsed.decision,
|
|
1359
|
+
caps: parsed.caps,
|
|
1360
|
+
get finalized() {
|
|
1361
|
+
return finalized;
|
|
1362
|
+
},
|
|
1363
|
+
async commit(actual, metrics, metadata) {
|
|
1364
|
+
if (finalized) {
|
|
1365
|
+
throw new CyclesError("StreamReservation already finalized");
|
|
1366
|
+
}
|
|
1367
|
+
finalized = true;
|
|
1368
|
+
stopHeartbeat();
|
|
1369
|
+
const commitBody = {
|
|
1370
|
+
idempotency_key: (0, import_node_crypto2.randomUUID)(),
|
|
1371
|
+
actual: { unit, amount: actual }
|
|
1372
|
+
};
|
|
1373
|
+
if (metrics && !isMetricsEmpty2(metrics)) {
|
|
1374
|
+
commitBody.metrics = metricsToWire(metrics);
|
|
1375
|
+
}
|
|
1376
|
+
if (metadata) {
|
|
1377
|
+
commitBody.metadata = metadata;
|
|
1378
|
+
}
|
|
1379
|
+
await client.commitReservation(reservationId, commitBody);
|
|
1380
|
+
},
|
|
1381
|
+
async release(reason) {
|
|
1382
|
+
if (finalized) return;
|
|
1383
|
+
finalized = true;
|
|
1384
|
+
stopHeartbeat();
|
|
1385
|
+
try {
|
|
1386
|
+
const releaseBody = { idempotency_key: (0, import_node_crypto2.randomUUID)(), reason: reason ?? "stream_aborted" };
|
|
1387
|
+
await client.releaseReservation(reservationId, releaseBody);
|
|
1388
|
+
} catch {
|
|
1389
|
+
}
|
|
1390
|
+
},
|
|
1391
|
+
dispose() {
|
|
1392
|
+
if (finalized) return;
|
|
1393
|
+
finalized = true;
|
|
1394
|
+
stopHeartbeat();
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1399
|
+
0 && (module.exports = {
|
|
1400
|
+
BudgetExceededError,
|
|
1401
|
+
CommitOveragePolicy,
|
|
1402
|
+
CommitStatus,
|
|
1403
|
+
CyclesClient,
|
|
1404
|
+
CyclesConfig,
|
|
1405
|
+
CyclesError,
|
|
1406
|
+
CyclesProtocolError,
|
|
1407
|
+
CyclesResponse,
|
|
1408
|
+
CyclesTransportError,
|
|
1409
|
+
DebtOutstandingError,
|
|
1410
|
+
Decision,
|
|
1411
|
+
ErrorCode,
|
|
1412
|
+
EventStatus,
|
|
1413
|
+
ExtendStatus,
|
|
1414
|
+
OverdraftLimitExceededError,
|
|
1415
|
+
ReleaseStatus,
|
|
1416
|
+
ReservationExpiredError,
|
|
1417
|
+
ReservationFinalizedError,
|
|
1418
|
+
ReservationStatus,
|
|
1419
|
+
Unit,
|
|
1420
|
+
balanceResponseFromWire,
|
|
1421
|
+
capsFromWire,
|
|
1422
|
+
commitRequestToWire,
|
|
1423
|
+
commitResponseFromWire,
|
|
1424
|
+
decisionRequestToWire,
|
|
1425
|
+
decisionResponseFromWire,
|
|
1426
|
+
errorCodeFromString,
|
|
1427
|
+
errorResponseFromWire,
|
|
1428
|
+
eventCreateRequestToWire,
|
|
1429
|
+
eventCreateResponseFromWire,
|
|
1430
|
+
getCyclesContext,
|
|
1431
|
+
isAllowed,
|
|
1432
|
+
isDenied,
|
|
1433
|
+
isMetricsEmpty,
|
|
1434
|
+
isRetryableErrorCode,
|
|
1435
|
+
isToolAllowed,
|
|
1436
|
+
metricsToWire,
|
|
1437
|
+
releaseRequestToWire,
|
|
1438
|
+
releaseResponseFromWire,
|
|
1439
|
+
reservationCreateRequestToWire,
|
|
1440
|
+
reservationCreateResponseFromWire,
|
|
1441
|
+
reservationDetailFromWire,
|
|
1442
|
+
reservationExtendRequestToWire,
|
|
1443
|
+
reservationExtendResponseFromWire,
|
|
1444
|
+
reservationListResponseFromWire,
|
|
1445
|
+
reservationSummaryFromWire,
|
|
1446
|
+
reserveForStream,
|
|
1447
|
+
setDefaultClient,
|
|
1448
|
+
setDefaultConfig,
|
|
1449
|
+
withCycles
|
|
1450
|
+
});
|