@puppetry.com/sdk 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/README.md +372 -0
- package/dist/chunk-IP47SEN7.mjs +1638 -0
- package/dist/chunk-QK5VPBL7.mjs +1500 -0
- package/dist/index.d.mts +1105 -0
- package/dist/index.d.ts +1105 -0
- package/dist/index.js +3163 -0
- package/dist/index.mjs +28 -0
- package/dist/mcp-ChYQVbVe.d.mts +947 -0
- package/dist/mcp-ChYQVbVe.d.ts +947 -0
- package/dist/mcp-stdio.d.mts +1 -0
- package/dist/mcp-stdio.d.ts +1 -0
- package/dist/mcp-stdio.js +3174 -0
- package/dist/mcp-stdio.mjs +65 -0
- package/dist/mcp.d.mts +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +1451 -0
- package/dist/mcp.mjs +6 -0
- package/package.json +61 -0
|
@@ -0,0 +1,1638 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PuppetryAuthError,
|
|
3
|
+
PuppetryError,
|
|
4
|
+
PuppetryNetworkError,
|
|
5
|
+
PuppetryRateLimitError,
|
|
6
|
+
PuppetryTimeoutError,
|
|
7
|
+
puppetryErrorData
|
|
8
|
+
} from "./chunk-QK5VPBL7.mjs";
|
|
9
|
+
|
|
10
|
+
// src/client.ts
|
|
11
|
+
var DEFAULT_BASE_URL = "https://www.puppetry.com/api/v1";
|
|
12
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
13
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
14
|
+
var DEFAULT_RETRY_DELAY_MS = 500;
|
|
15
|
+
var SDK_VERSION = "0.1.0";
|
|
16
|
+
var IDEMPOTENCY_KEY_MAX_LENGTH = 200;
|
|
17
|
+
var EMPTY_HEADERS = { get: () => null };
|
|
18
|
+
var VIDEO_STATUS_ALIASES = {
|
|
19
|
+
active: "processing",
|
|
20
|
+
complete: "completed",
|
|
21
|
+
completed: "completed",
|
|
22
|
+
delayed: "queued",
|
|
23
|
+
done: "completed",
|
|
24
|
+
error: "failed",
|
|
25
|
+
errored: "failed",
|
|
26
|
+
failed: "failed",
|
|
27
|
+
failure: "failed",
|
|
28
|
+
finished: "completed",
|
|
29
|
+
"in-progress": "processing",
|
|
30
|
+
in_progress: "processing",
|
|
31
|
+
paused: "queued",
|
|
32
|
+
prioritized: "queued",
|
|
33
|
+
process: "processing",
|
|
34
|
+
processing: "processing",
|
|
35
|
+
queued: "queued",
|
|
36
|
+
rendering: "processing",
|
|
37
|
+
retrying: "processing",
|
|
38
|
+
running: "processing",
|
|
39
|
+
started: "processing",
|
|
40
|
+
success: "completed",
|
|
41
|
+
succeeded: "completed",
|
|
42
|
+
wait: "queued",
|
|
43
|
+
waiting: "queued",
|
|
44
|
+
"waiting-children": "queued"
|
|
45
|
+
};
|
|
46
|
+
function normalizeVideoStatusAlias(status) {
|
|
47
|
+
if (typeof status !== "string") return void 0;
|
|
48
|
+
return VIDEO_STATUS_ALIASES[status.trim().toLowerCase()];
|
|
49
|
+
}
|
|
50
|
+
function normalizeVideoJobSource(source) {
|
|
51
|
+
return source === "text" || source === "audio" ? source : void 0;
|
|
52
|
+
}
|
|
53
|
+
var Puppetry = class {
|
|
54
|
+
constructor(config) {
|
|
55
|
+
// ── Videos ─────────────────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Create a video from text (text-to-speech + lip sync).
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const job = await puppetry.videos.createFromText({
|
|
62
|
+
* text: "Hello, world!",
|
|
63
|
+
* image_url: "https://example.com/photo.jpg",
|
|
64
|
+
* voice_id: "puppetry-af_heart",
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
this.videos = {
|
|
69
|
+
createFromText: async (params) => this.requestWithResponse(
|
|
70
|
+
"POST",
|
|
71
|
+
"/videos/text",
|
|
72
|
+
this.textVideoBody(params),
|
|
73
|
+
{
|
|
74
|
+
idempotencyKey: this.idempotencyKeyFromParams(params)
|
|
75
|
+
}
|
|
76
|
+
).then(({ data, headers }) => this.normalizeVideoJob(data, headers)),
|
|
77
|
+
createFromAudio: async (params) => this.requestWithResponse(
|
|
78
|
+
"POST",
|
|
79
|
+
"/videos/audio",
|
|
80
|
+
this.audioVideoBody(params),
|
|
81
|
+
{
|
|
82
|
+
idempotencyKey: this.idempotencyKeyFromParams(params)
|
|
83
|
+
}
|
|
84
|
+
).then(({ data, headers }) => this.normalizeVideoJob(data, headers)),
|
|
85
|
+
get: async (jobId) => this.getWithResponse(this.videoJobPath(jobId)).then(
|
|
86
|
+
({ data, headers }) => this.normalizeVideoJob(data, headers)
|
|
87
|
+
),
|
|
88
|
+
/**
|
|
89
|
+
* Return the current Developer API video readiness contract.
|
|
90
|
+
*
|
|
91
|
+
* Use this before reserving uploads or queueing a video when an agent needs
|
|
92
|
+
* to fail fast on credits or active slot limits.
|
|
93
|
+
*/
|
|
94
|
+
getReadiness: () => this.getVideoGenerationReadiness(),
|
|
95
|
+
/**
|
|
96
|
+
* Assert that this API key can create a video right now.
|
|
97
|
+
*
|
|
98
|
+
* Throws PuppetryRateLimitError with retryAfter for active-slot blockers,
|
|
99
|
+
* and PuppetryError(402) for credit blockers.
|
|
100
|
+
*/
|
|
101
|
+
ensureReady: () => this.ensureVideoGenerationReady(),
|
|
102
|
+
/**
|
|
103
|
+
* Wait for a video job to complete, polling at the given interval.
|
|
104
|
+
*
|
|
105
|
+
* @param jobId - The job ID to watch
|
|
106
|
+
* @param options.intervalMs - Polling interval (default: 2000)
|
|
107
|
+
* @param options.timeoutMs - Max wait time (default: 300000 = 5 min)
|
|
108
|
+
* @returns The completed VideoJob
|
|
109
|
+
* @throws PuppetryTimeoutError if the job doesn't complete in time
|
|
110
|
+
*/
|
|
111
|
+
waitForCompletion: async (jobId, options) => {
|
|
112
|
+
const interval = options?.intervalMs ?? 2e3;
|
|
113
|
+
const timeout = options?.timeoutMs ?? 3e5;
|
|
114
|
+
const deadline = Date.now() + timeout;
|
|
115
|
+
const initialDelayMs = this.optionDelayMs(options?.initialDelayMs);
|
|
116
|
+
let currentJobId = jobId;
|
|
117
|
+
if (initialDelayMs !== void 0 && initialDelayMs > 0) {
|
|
118
|
+
await this.sleepWithinDeadline(initialDelayMs, deadline);
|
|
119
|
+
}
|
|
120
|
+
while (Date.now() < deadline) {
|
|
121
|
+
try {
|
|
122
|
+
const { data: job, headers } = await this.getWithResponse(
|
|
123
|
+
this.videoJobPath(currentJobId)
|
|
124
|
+
);
|
|
125
|
+
const normalizedJob = this.normalizeVideoJob(job, headers);
|
|
126
|
+
currentJobId = this.videoJobIdFromStatusUrl(
|
|
127
|
+
normalizedJob.statusUrl ?? normalizedJob.status_url
|
|
128
|
+
) ?? normalizedJob.id;
|
|
129
|
+
await options?.onPoll?.(normalizedJob);
|
|
130
|
+
if (normalizedJob.status === "completed" || normalizedJob.status === "failed") {
|
|
131
|
+
return normalizedJob;
|
|
132
|
+
}
|
|
133
|
+
const delayMs = this.pollingDelayMs(normalizedJob, headers, interval);
|
|
134
|
+
await this.sleep(
|
|
135
|
+
Math.min(delayMs, Math.max(deadline - Date.now(), 0))
|
|
136
|
+
);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (!(err instanceof PuppetryError)) throw err;
|
|
139
|
+
const delayMs = this.retryableStatusErrorDelayMs(err, interval);
|
|
140
|
+
if (delayMs === void 0) throw err;
|
|
141
|
+
const retryingEvent = this.videoRetryingEvent(err, currentJobId);
|
|
142
|
+
currentJobId = retryingEvent.jobId;
|
|
143
|
+
await options?.onRetrying?.(retryingEvent);
|
|
144
|
+
await this.sleepWithinDeadline(delayMs, deadline);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
throw new PuppetryTimeoutError(timeout);
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* Stream progress-like events by polling the live v1 job status endpoint.
|
|
151
|
+
*
|
|
152
|
+
* @param jobId - The job ID to stream
|
|
153
|
+
* @param options.intervalMs - Polling interval (default: 2000)
|
|
154
|
+
* @param options.timeoutMs - Max stream time (default: 300000 = 5 min)
|
|
155
|
+
* @returns AsyncGenerator yielding VideoEvent objects
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* for await (const event of puppetry.videos.stream("job-123")) {
|
|
160
|
+
* console.log(`${event.type}: ${event.progress}%`);
|
|
161
|
+
* if (event.type === "completed") break;
|
|
162
|
+
* }
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
stream: (jobId, options) => this.streamPolledVideoEvents(jobId, options)
|
|
166
|
+
};
|
|
167
|
+
// ── Jobs ───────────────────────────────────────────────────────────
|
|
168
|
+
this.jobs = {
|
|
169
|
+
/**
|
|
170
|
+
* Fetch a generated video job by ID.
|
|
171
|
+
*
|
|
172
|
+
* This is an alias for `videos.get()` because Puppetry's public v1 API
|
|
173
|
+
* exposes generated video job state under `/videos/{jobId}`.
|
|
174
|
+
*/
|
|
175
|
+
get: (jobId) => this.videos.get(jobId),
|
|
176
|
+
/**
|
|
177
|
+
* Wait for a generated video job to complete, polling at the given interval.
|
|
178
|
+
*/
|
|
179
|
+
waitForCompletion: (jobId, options) => this.videos.waitForCompletion(jobId, options)
|
|
180
|
+
};
|
|
181
|
+
// ── Voices ─────────────────────────────────────────────────────────
|
|
182
|
+
this.voices = {
|
|
183
|
+
list: (params) => {
|
|
184
|
+
const query = new URLSearchParams();
|
|
185
|
+
if (params?.language) query.set("language", params.language);
|
|
186
|
+
if (params?.gender) query.set("gender", params.gender);
|
|
187
|
+
if (params?.cursor) query.set("cursor", params.cursor);
|
|
188
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
189
|
+
const qs = query.toString();
|
|
190
|
+
return this.get(`/voices${qs ? "?" + qs : ""}`);
|
|
191
|
+
},
|
|
192
|
+
listPuppetry: () => this.get("/voices/puppetry")
|
|
193
|
+
};
|
|
194
|
+
// ── Text to Speech ───────────────────────────────────────────────────
|
|
195
|
+
this.tts = {
|
|
196
|
+
createPuppetry: (params) => this.post(
|
|
197
|
+
"/tts/puppetry",
|
|
198
|
+
params
|
|
199
|
+
)
|
|
200
|
+
};
|
|
201
|
+
// ── Uploads ───────────────────────────────────────────────────────────
|
|
202
|
+
this.uploads = {
|
|
203
|
+
/**
|
|
204
|
+
* Create a signed PUT URL for hosting audio that can be used by Puppetry API
|
|
205
|
+
* workflows.
|
|
206
|
+
*/
|
|
207
|
+
createAudioUrl: async (params) => this.post(
|
|
208
|
+
"/uploads/audio-url",
|
|
209
|
+
this.compactBody({
|
|
210
|
+
mime_type: this.resolveUploadStringAliases([
|
|
211
|
+
{ name: "mime_type", value: params.mime_type },
|
|
212
|
+
{ name: "mimeType", value: params.mimeType },
|
|
213
|
+
{ name: "contentType", value: params.contentType }
|
|
214
|
+
]),
|
|
215
|
+
content_length: this.resolveNumberAliases([
|
|
216
|
+
{ name: "content_length", value: params.content_length },
|
|
217
|
+
{ name: "contentLength", value: params.contentLength },
|
|
218
|
+
{ name: "sizeBytes", value: params.sizeBytes },
|
|
219
|
+
{ name: "file_size", value: params.file_size },
|
|
220
|
+
{ name: "fileSize", value: params.fileSize }
|
|
221
|
+
]),
|
|
222
|
+
require_video_readiness: params.require_video_readiness ?? params.requireVideoReadiness
|
|
223
|
+
}),
|
|
224
|
+
{ idempotencyKey: this.idempotencyKeyFromParams(params) }
|
|
225
|
+
).then((upload) => this.normalizeAudioUpload(upload)),
|
|
226
|
+
/**
|
|
227
|
+
* Create a signed audio upload URL and PUT the provided bytes to it.
|
|
228
|
+
*
|
|
229
|
+
* The returned `read_url` can be passed to `videos.createFromAudio()`.
|
|
230
|
+
*/
|
|
231
|
+
uploadAudio: async (params) => {
|
|
232
|
+
const upload = await this.uploads.createAudioUrl(params);
|
|
233
|
+
const controller = new AbortController();
|
|
234
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
235
|
+
try {
|
|
236
|
+
const res = await this.fetchFn(upload.upload_url, {
|
|
237
|
+
method: upload.method,
|
|
238
|
+
headers: upload.headers,
|
|
239
|
+
body: params.body,
|
|
240
|
+
signal: controller.signal
|
|
241
|
+
});
|
|
242
|
+
if (!res.ok) {
|
|
243
|
+
throw new PuppetryError(
|
|
244
|
+
res.status,
|
|
245
|
+
"upload_failed",
|
|
246
|
+
res.statusText || "Failed to upload audio bytes"
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
} catch (err) {
|
|
250
|
+
if (err instanceof PuppetryError) throw err;
|
|
251
|
+
if (this.isAbortError(err)) {
|
|
252
|
+
throw new PuppetryTimeoutError(this.timeout);
|
|
253
|
+
}
|
|
254
|
+
if (this.isNetworkError(err)) {
|
|
255
|
+
throw new PuppetryNetworkError(this.transportErrorMessage(err));
|
|
256
|
+
}
|
|
257
|
+
throw err;
|
|
258
|
+
} finally {
|
|
259
|
+
clearTimeout(timer);
|
|
260
|
+
}
|
|
261
|
+
return this.normalizeAudioUpload(upload);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
// ── Puppets ────────────────────────────────────────────────────────
|
|
265
|
+
this.puppets = {
|
|
266
|
+
list: (params) => {
|
|
267
|
+
const query = new URLSearchParams();
|
|
268
|
+
if (params?.cursor) query.set("cursor", params.cursor);
|
|
269
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
270
|
+
const qs = query.toString();
|
|
271
|
+
return this.get(
|
|
272
|
+
`/puppets${qs ? "?" + qs : ""}`
|
|
273
|
+
);
|
|
274
|
+
},
|
|
275
|
+
create: (params) => this.post(
|
|
276
|
+
"/puppets",
|
|
277
|
+
params
|
|
278
|
+
),
|
|
279
|
+
delete: (puppetId) => this.delete(`/puppets/${encodeURIComponent(puppetId)}`)
|
|
280
|
+
};
|
|
281
|
+
// ── Usage ──────────────────────────────────────────────────────────
|
|
282
|
+
this.usage = {
|
|
283
|
+
get: async () => this.normalizeUsage(await this.get("/usage"))
|
|
284
|
+
};
|
|
285
|
+
// ── Quota ──────────────────────────────────────────────────────────
|
|
286
|
+
this.quota = {
|
|
287
|
+
get: () => this.usage.get()
|
|
288
|
+
};
|
|
289
|
+
const apiKey = config.apiKey.trim();
|
|
290
|
+
if (!apiKey) {
|
|
291
|
+
throw new PuppetryAuthError("apiKey is required");
|
|
292
|
+
}
|
|
293
|
+
this.apiKey = apiKey;
|
|
294
|
+
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
295
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
296
|
+
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
297
|
+
this.retryDelayMs = config.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
298
|
+
this.onRetry = config.onRetry;
|
|
299
|
+
this.fetchFn = config.fetch || globalThis.fetch;
|
|
300
|
+
}
|
|
301
|
+
// ── HTTP Layer ─────────────────────────────────────────────────────
|
|
302
|
+
async requestWithResponse(method, path, body, options) {
|
|
303
|
+
const url = `${this.baseUrl}${path}`;
|
|
304
|
+
const requestBody = body ? JSON.stringify(body) : void 0;
|
|
305
|
+
const headers = {
|
|
306
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
307
|
+
"Content-Type": "application/json",
|
|
308
|
+
"User-Agent": `@puppetry.com/sdk/${SDK_VERSION}`
|
|
309
|
+
};
|
|
310
|
+
const idempotencyKey = this.normalizeIdempotencyKey(
|
|
311
|
+
options?.idempotencyKey
|
|
312
|
+
);
|
|
313
|
+
if (idempotencyKey) {
|
|
314
|
+
headers["Idempotency-Key"] = idempotencyKey;
|
|
315
|
+
}
|
|
316
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
317
|
+
const controller = new AbortController();
|
|
318
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
319
|
+
try {
|
|
320
|
+
const res = await this.fetchFn(url, {
|
|
321
|
+
method,
|
|
322
|
+
headers,
|
|
323
|
+
body: requestBody,
|
|
324
|
+
signal: controller.signal
|
|
325
|
+
});
|
|
326
|
+
if (!res.ok) {
|
|
327
|
+
const errorBody = await res.json().catch(() => ({
|
|
328
|
+
error: "unknown",
|
|
329
|
+
message: res.statusText
|
|
330
|
+
}));
|
|
331
|
+
const responseHeaders = this.responseHeaders(res);
|
|
332
|
+
const retryAfter = this.responseRetryAfter(res.headers, errorBody);
|
|
333
|
+
const errorOptions = {
|
|
334
|
+
...this.videoCreditErrorOptions(responseHeaders),
|
|
335
|
+
...this.videoStatusErrorOptions(
|
|
336
|
+
method,
|
|
337
|
+
path,
|
|
338
|
+
responseHeaders,
|
|
339
|
+
retryAfter
|
|
340
|
+
)
|
|
341
|
+
};
|
|
342
|
+
const isVideoStatusRetry = this.isRetryableVideoStatusError(
|
|
343
|
+
method,
|
|
344
|
+
path,
|
|
345
|
+
errorBody
|
|
346
|
+
);
|
|
347
|
+
if (!isVideoStatusRetry && this.shouldRetry(method, res.status, Boolean(idempotencyKey)) && attempt < this.maxRetries) {
|
|
348
|
+
const delayMs = this.retryDelay(attempt, retryAfter);
|
|
349
|
+
const retryError = res.status === 429 ? PuppetryRateLimitError.fromRateLimitResponse(errorBody, {
|
|
350
|
+
...errorOptions
|
|
351
|
+
}) : PuppetryError.fromResponse(
|
|
352
|
+
res.status,
|
|
353
|
+
errorBody,
|
|
354
|
+
errorOptions
|
|
355
|
+
);
|
|
356
|
+
await this.notifyRetry(
|
|
357
|
+
this.requestRetryEvent(
|
|
358
|
+
method,
|
|
359
|
+
path,
|
|
360
|
+
url,
|
|
361
|
+
idempotencyKey,
|
|
362
|
+
attempt,
|
|
363
|
+
delayMs,
|
|
364
|
+
retryError
|
|
365
|
+
)
|
|
366
|
+
);
|
|
367
|
+
await this.sleep(delayMs);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (res.status === 401)
|
|
371
|
+
throw new PuppetryAuthError(errorBody.message);
|
|
372
|
+
if (res.status === 429) {
|
|
373
|
+
throw PuppetryRateLimitError.fromRateLimitResponse(errorBody, {
|
|
374
|
+
...errorOptions
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
throw PuppetryError.fromResponse(res.status, errorBody, errorOptions);
|
|
378
|
+
}
|
|
379
|
+
if (res.status === 204) {
|
|
380
|
+
return { data: void 0, headers: this.responseHeaders(res) };
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
data: await res.json(),
|
|
384
|
+
headers: this.responseHeaders(res)
|
|
385
|
+
};
|
|
386
|
+
} catch (err) {
|
|
387
|
+
if (err instanceof PuppetryError) throw err;
|
|
388
|
+
if (this.isAbortError(err)) {
|
|
389
|
+
if (this.shouldRetryTransport(method, Boolean(idempotencyKey), attempt)) {
|
|
390
|
+
const delayMs = this.retryDelay(attempt);
|
|
391
|
+
await this.notifyRetry(
|
|
392
|
+
this.requestRetryEvent(
|
|
393
|
+
method,
|
|
394
|
+
path,
|
|
395
|
+
url,
|
|
396
|
+
idempotencyKey,
|
|
397
|
+
attempt,
|
|
398
|
+
delayMs,
|
|
399
|
+
new PuppetryTimeoutError(this.timeout)
|
|
400
|
+
)
|
|
401
|
+
);
|
|
402
|
+
await this.sleep(delayMs);
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
throw new PuppetryTimeoutError(this.timeout);
|
|
406
|
+
}
|
|
407
|
+
if (this.isNetworkError(err)) {
|
|
408
|
+
if (this.shouldRetryTransport(method, Boolean(idempotencyKey), attempt)) {
|
|
409
|
+
const delayMs = this.retryDelay(attempt);
|
|
410
|
+
await this.notifyRetry(
|
|
411
|
+
this.requestRetryEvent(
|
|
412
|
+
method,
|
|
413
|
+
path,
|
|
414
|
+
url,
|
|
415
|
+
idempotencyKey,
|
|
416
|
+
attempt,
|
|
417
|
+
delayMs,
|
|
418
|
+
new PuppetryNetworkError(this.transportErrorMessage(err))
|
|
419
|
+
)
|
|
420
|
+
);
|
|
421
|
+
await this.sleep(delayMs);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
throw new PuppetryNetworkError(this.transportErrorMessage(err));
|
|
425
|
+
}
|
|
426
|
+
throw err;
|
|
427
|
+
} finally {
|
|
428
|
+
clearTimeout(timer);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
throw new PuppetryError(
|
|
432
|
+
500,
|
|
433
|
+
"retry_exhausted",
|
|
434
|
+
"Request retries exhausted"
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
requestRetryEvent(method, path, url, idempotencyKey, attempt, delayMs, err) {
|
|
438
|
+
return {
|
|
439
|
+
method,
|
|
440
|
+
path,
|
|
441
|
+
url,
|
|
442
|
+
...idempotencyKey ? {
|
|
443
|
+
idempotency_key: idempotencyKey,
|
|
444
|
+
idempotencyKey
|
|
445
|
+
} : {},
|
|
446
|
+
attempt: attempt + 1,
|
|
447
|
+
maxRetries: this.maxRetries,
|
|
448
|
+
delayMs,
|
|
449
|
+
...puppetryErrorData(err)
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
async notifyRetry(event) {
|
|
453
|
+
if (!this.onRetry) return;
|
|
454
|
+
try {
|
|
455
|
+
await this.onRetry(event);
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
async request(method, path, body, options) {
|
|
460
|
+
return (await this.requestWithResponse(method, path, body, options)).data;
|
|
461
|
+
}
|
|
462
|
+
get(path) {
|
|
463
|
+
return this.request("GET", path);
|
|
464
|
+
}
|
|
465
|
+
getWithResponse(path) {
|
|
466
|
+
return this.requestWithResponse("GET", path);
|
|
467
|
+
}
|
|
468
|
+
post(path, body, options) {
|
|
469
|
+
return this.request("POST", path, body, options);
|
|
470
|
+
}
|
|
471
|
+
delete(path) {
|
|
472
|
+
return this.request("DELETE", path);
|
|
473
|
+
}
|
|
474
|
+
shouldRetry(method, status, hasIdempotencyKey) {
|
|
475
|
+
if (status !== 429 && status < 500) return false;
|
|
476
|
+
return this.isRetrySafeRequest(method, hasIdempotencyKey);
|
|
477
|
+
}
|
|
478
|
+
isRetryableVideoStatusError(method, path, body) {
|
|
479
|
+
if (!this.isVideoStatusRequest(method, path)) return false;
|
|
480
|
+
return body.retryable === true || body.details?.retryable === true;
|
|
481
|
+
}
|
|
482
|
+
isVideoStatusRequest(method, path) {
|
|
483
|
+
if (method.toUpperCase() !== "GET") return false;
|
|
484
|
+
const pathname = path.split("?")[0];
|
|
485
|
+
return /^\/videos\/[^/]+\/?$/.test(pathname);
|
|
486
|
+
}
|
|
487
|
+
shouldRetryTransport(method, hasIdempotencyKey, attempt) {
|
|
488
|
+
return attempt < this.maxRetries && this.isRetrySafeRequest(method, hasIdempotencyKey);
|
|
489
|
+
}
|
|
490
|
+
isRetrySafeRequest(method, hasIdempotencyKey) {
|
|
491
|
+
const normalizedMethod = method.toUpperCase();
|
|
492
|
+
if (normalizedMethod === "GET" || normalizedMethod === "HEAD" || normalizedMethod === "OPTIONS") {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
return hasIdempotencyKey;
|
|
496
|
+
}
|
|
497
|
+
isAbortError(err) {
|
|
498
|
+
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
499
|
+
}
|
|
500
|
+
isNetworkError(err) {
|
|
501
|
+
return err instanceof TypeError;
|
|
502
|
+
}
|
|
503
|
+
transportErrorMessage(err) {
|
|
504
|
+
if (err instanceof Error && err.message) return err.message;
|
|
505
|
+
return "Network request failed";
|
|
506
|
+
}
|
|
507
|
+
parseRetryAfter(value) {
|
|
508
|
+
if (!value) return void 0;
|
|
509
|
+
const trimmedValue = value.trim();
|
|
510
|
+
if (/^\d+$/.test(trimmedValue)) {
|
|
511
|
+
const seconds = Number(trimmedValue);
|
|
512
|
+
return Number.isSafeInteger(seconds) ? seconds : void 0;
|
|
513
|
+
}
|
|
514
|
+
const retryAtMs = Date.parse(trimmedValue);
|
|
515
|
+
if (!Number.isFinite(retryAtMs)) return void 0;
|
|
516
|
+
return Math.max(0, Math.ceil((retryAtMs - Date.now()) / 1e3));
|
|
517
|
+
}
|
|
518
|
+
responseRetryAfter(headers, body) {
|
|
519
|
+
return this.parseRetryAfter(this.readHeader(headers, "Retry-After")) ?? this.retryAfterFromErrorBody(body) ?? this.retryAfterFromDetails(body.details);
|
|
520
|
+
}
|
|
521
|
+
retryAfterFromErrorBody(body) {
|
|
522
|
+
return this.normalizeRetryAfterSeconds(
|
|
523
|
+
body.retry_after_seconds ?? body.retryAfterSeconds ?? body.retryAfter
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
retryAfterFromDetails(details) {
|
|
527
|
+
const directRetryAfter = this.normalizeRetryAfterSeconds(
|
|
528
|
+
details?.retry_after_seconds ?? details?.retryAfterSeconds ?? details?.retryAfter
|
|
529
|
+
);
|
|
530
|
+
if (directRetryAfter !== void 0) return directRetryAfter;
|
|
531
|
+
const nestedReadiness = details && typeof details === "object" ? details.video_generation ?? details.videoGeneration : void 0;
|
|
532
|
+
if (!nestedReadiness || typeof nestedReadiness !== "object") {
|
|
533
|
+
return void 0;
|
|
534
|
+
}
|
|
535
|
+
const readiness = nestedReadiness;
|
|
536
|
+
return this.normalizeRetryAfterSeconds(
|
|
537
|
+
readiness.retry_after_seconds ?? readiness.retryAfterSeconds ?? readiness.retryAfter
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
normalizeRetryAfterSeconds(value) {
|
|
541
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
542
|
+
return void 0;
|
|
543
|
+
}
|
|
544
|
+
return value;
|
|
545
|
+
}
|
|
546
|
+
retryDelay(attempt, retryAfterSeconds) {
|
|
547
|
+
if (retryAfterSeconds !== void 0) {
|
|
548
|
+
return retryAfterSeconds * 1e3;
|
|
549
|
+
}
|
|
550
|
+
return this.retryDelayMs * 2 ** attempt;
|
|
551
|
+
}
|
|
552
|
+
sleep(ms) {
|
|
553
|
+
if (ms <= 0) return Promise.resolve();
|
|
554
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
555
|
+
}
|
|
556
|
+
responseHeaders(response) {
|
|
557
|
+
return response.headers ?? EMPTY_HEADERS;
|
|
558
|
+
}
|
|
559
|
+
readHeader(headers, name) {
|
|
560
|
+
const reader = headers;
|
|
561
|
+
return reader?.get?.(name) ?? reader?.get?.(name.toLowerCase()) ?? null;
|
|
562
|
+
}
|
|
563
|
+
readResponseHeader(headers, name) {
|
|
564
|
+
return headers?.get?.(name) ?? headers?.get?.(name.toLowerCase()) ?? null;
|
|
565
|
+
}
|
|
566
|
+
compactBody(body) {
|
|
567
|
+
return Object.fromEntries(
|
|
568
|
+
Object.entries(body).filter(([, value]) => value !== void 0)
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
normalizeIdempotencyKey(value) {
|
|
572
|
+
if (value === void 0) return void 0;
|
|
573
|
+
const key = value.trim();
|
|
574
|
+
if (!key) {
|
|
575
|
+
throw new PuppetryError(
|
|
576
|
+
400,
|
|
577
|
+
"invalid_request",
|
|
578
|
+
"Idempotency-Key must not be blank"
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
if (key.length > IDEMPOTENCY_KEY_MAX_LENGTH) {
|
|
582
|
+
throw new PuppetryError(
|
|
583
|
+
400,
|
|
584
|
+
"invalid_request",
|
|
585
|
+
`Idempotency-Key must be ${IDEMPOTENCY_KEY_MAX_LENGTH} characters or fewer`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
return key;
|
|
589
|
+
}
|
|
590
|
+
idempotencyKeyFromParams(params) {
|
|
591
|
+
const aliases = [
|
|
592
|
+
{ name: "idempotencyKey", value: params.idempotencyKey },
|
|
593
|
+
{ name: "idempotency_key", value: params.idempotency_key }
|
|
594
|
+
];
|
|
595
|
+
const provided = aliases.filter((alias) => alias.value !== void 0 && alias.value !== null).map((alias) => {
|
|
596
|
+
if (typeof alias.value !== "string") {
|
|
597
|
+
throw new PuppetryError(
|
|
598
|
+
400,
|
|
599
|
+
"invalid_request",
|
|
600
|
+
`${alias.name} must be a string`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
return { ...alias, value: alias.value };
|
|
604
|
+
});
|
|
605
|
+
const first = provided[0];
|
|
606
|
+
if (!first) return void 0;
|
|
607
|
+
const conflictingAlias = provided.find(
|
|
608
|
+
(alias) => alias.value !== first.value
|
|
609
|
+
);
|
|
610
|
+
if (conflictingAlias) {
|
|
611
|
+
throw new PuppetryError(
|
|
612
|
+
400,
|
|
613
|
+
"invalid_request",
|
|
614
|
+
`Pass one of ${aliases.map((alias) => alias.name).join(", ")}, not conflicting values`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
return first.value;
|
|
618
|
+
}
|
|
619
|
+
resolveAlias(preferred, alias, preferredName, aliasName) {
|
|
620
|
+
if (preferred && alias && preferred !== alias) {
|
|
621
|
+
throw new PuppetryError(
|
|
622
|
+
400,
|
|
623
|
+
"invalid_request",
|
|
624
|
+
`Pass either ${preferredName} or ${aliasName}, not conflicting values`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
return preferred ?? alias;
|
|
628
|
+
}
|
|
629
|
+
resolveRequiredStringAliases(aliases) {
|
|
630
|
+
const provided = aliases.flatMap((alias) => {
|
|
631
|
+
if (alias.value === void 0 || alias.value === null) return [];
|
|
632
|
+
if (typeof alias.value !== "string") {
|
|
633
|
+
throw new PuppetryError(
|
|
634
|
+
400,
|
|
635
|
+
"invalid_request",
|
|
636
|
+
`${alias.name} must be a string`
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
const value = alias.value.trim();
|
|
640
|
+
if (!value) {
|
|
641
|
+
throw new PuppetryError(
|
|
642
|
+
400,
|
|
643
|
+
"invalid_request",
|
|
644
|
+
`${alias.name} must not be blank`
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
return [{ name: alias.name, value }];
|
|
648
|
+
});
|
|
649
|
+
const first = provided[0];
|
|
650
|
+
if (!first) return void 0;
|
|
651
|
+
const conflictingAlias = provided.find(
|
|
652
|
+
(alias) => alias.value !== first.value
|
|
653
|
+
);
|
|
654
|
+
if (conflictingAlias) {
|
|
655
|
+
throw new PuppetryError(
|
|
656
|
+
400,
|
|
657
|
+
"invalid_request",
|
|
658
|
+
`Pass one of ${aliases.map((alias) => alias.name).join(", ")}, not conflicting values`
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
return first.value;
|
|
662
|
+
}
|
|
663
|
+
resolveOptionalStringAliases(aliases) {
|
|
664
|
+
const provided = aliases.flatMap((alias) => {
|
|
665
|
+
if (alias.value === void 0 || alias.value === null) return [];
|
|
666
|
+
if (typeof alias.value !== "string") {
|
|
667
|
+
throw new PuppetryError(
|
|
668
|
+
400,
|
|
669
|
+
"invalid_request",
|
|
670
|
+
`${alias.name} must be a string`
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
const value = alias.value.trim();
|
|
674
|
+
return value ? [{ name: alias.name, value }] : [];
|
|
675
|
+
});
|
|
676
|
+
const first = provided[0];
|
|
677
|
+
if (!first) return void 0;
|
|
678
|
+
const conflictingAlias = provided.find(
|
|
679
|
+
(alias) => alias.value !== first.value
|
|
680
|
+
);
|
|
681
|
+
if (conflictingAlias) {
|
|
682
|
+
throw new PuppetryError(
|
|
683
|
+
400,
|
|
684
|
+
"invalid_request",
|
|
685
|
+
`Pass one of ${aliases.map((alias) => alias.name).join(", ")}, not conflicting values`
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
return first.value;
|
|
689
|
+
}
|
|
690
|
+
resolveUploadStringAliases(aliases) {
|
|
691
|
+
const provided = aliases.flatMap((alias) => {
|
|
692
|
+
if (alias.value === void 0 || alias.value === null) return [];
|
|
693
|
+
if (typeof alias.value !== "string") {
|
|
694
|
+
throw new PuppetryError(
|
|
695
|
+
400,
|
|
696
|
+
"invalid_request",
|
|
697
|
+
`${alias.name} must be a string`
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
const value = alias.value.trim();
|
|
701
|
+
return value ? [{ name: alias.name, value }] : [];
|
|
702
|
+
});
|
|
703
|
+
const first = provided[0];
|
|
704
|
+
if (!first) return void 0;
|
|
705
|
+
const conflictingAlias = provided.find(
|
|
706
|
+
(alias) => alias.value !== first.value
|
|
707
|
+
);
|
|
708
|
+
if (conflictingAlias) {
|
|
709
|
+
throw new PuppetryError(
|
|
710
|
+
400,
|
|
711
|
+
"invalid_request",
|
|
712
|
+
`Pass one of ${aliases.map((alias) => alias.name).join(", ")}, not conflicting values`
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
return first.value;
|
|
716
|
+
}
|
|
717
|
+
resolveNumberAliases(aliases) {
|
|
718
|
+
const provided = aliases.flatMap((alias) => {
|
|
719
|
+
if (alias.value === void 0 || alias.value === null) return [];
|
|
720
|
+
if (typeof alias.value === "string" && alias.value.trim() === "") {
|
|
721
|
+
return [];
|
|
722
|
+
}
|
|
723
|
+
if (typeof alias.value !== "number") {
|
|
724
|
+
throw new PuppetryError(
|
|
725
|
+
400,
|
|
726
|
+
"invalid_request",
|
|
727
|
+
`${alias.name} must be a number`
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
return [{ name: alias.name, value: alias.value }];
|
|
731
|
+
});
|
|
732
|
+
const first = provided[0];
|
|
733
|
+
if (!first) return void 0;
|
|
734
|
+
const conflictingAlias = provided.find(
|
|
735
|
+
(alias) => alias.value !== first.value
|
|
736
|
+
);
|
|
737
|
+
if (conflictingAlias) {
|
|
738
|
+
throw new PuppetryError(
|
|
739
|
+
400,
|
|
740
|
+
"invalid_request",
|
|
741
|
+
`Pass one of ${aliases.map((alias) => alias.name).join(", ")}, not conflicting values`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
return first.value;
|
|
745
|
+
}
|
|
746
|
+
firstDefinedNullable(...values) {
|
|
747
|
+
for (const value of values) {
|
|
748
|
+
if (value !== void 0 && value !== null) return value;
|
|
749
|
+
}
|
|
750
|
+
return values.some((value) => value === null) ? null : void 0;
|
|
751
|
+
}
|
|
752
|
+
textVideoBody(params) {
|
|
753
|
+
return this.compactBody({
|
|
754
|
+
image_url: this.resolveRequiredStringAliases([
|
|
755
|
+
{ name: "image_url", value: params.image_url },
|
|
756
|
+
{ name: "imageUrl", value: params.imageUrl },
|
|
757
|
+
{ name: "image", value: params.image },
|
|
758
|
+
{ name: "portrait", value: params.portrait },
|
|
759
|
+
{ name: "portrait_url", value: params.portrait_url },
|
|
760
|
+
{ name: "portraitUrl", value: params.portraitUrl },
|
|
761
|
+
{ name: "avatar", value: params.avatar },
|
|
762
|
+
{ name: "avatarUrl", value: params.avatarUrl },
|
|
763
|
+
{ name: "avatar_url", value: params.avatar_url },
|
|
764
|
+
{ name: "photo", value: params.photo },
|
|
765
|
+
{ name: "photoUrl", value: params.photoUrl },
|
|
766
|
+
{ name: "photo_url", value: params.photo_url }
|
|
767
|
+
]),
|
|
768
|
+
text: this.resolveRequiredStringAliases([
|
|
769
|
+
{ name: "text", value: params.text },
|
|
770
|
+
{ name: "script", value: params.script },
|
|
771
|
+
{ name: "prompt", value: params.prompt }
|
|
772
|
+
]),
|
|
773
|
+
voice_id: this.resolveOptionalStringAliases([
|
|
774
|
+
{ name: "voice_id", value: params.voice_id },
|
|
775
|
+
{ name: "voiceId", value: params.voiceId },
|
|
776
|
+
{ name: "voice", value: params.voice }
|
|
777
|
+
]),
|
|
778
|
+
language: params.language,
|
|
779
|
+
expressiveness: params.expressiveness
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
audioVideoBody(params) {
|
|
783
|
+
return this.compactBody({
|
|
784
|
+
image_url: this.resolveRequiredStringAliases([
|
|
785
|
+
{ name: "image_url", value: params.image_url },
|
|
786
|
+
{ name: "imageUrl", value: params.imageUrl },
|
|
787
|
+
{ name: "image", value: params.image },
|
|
788
|
+
{ name: "portrait", value: params.portrait },
|
|
789
|
+
{ name: "portrait_url", value: params.portrait_url },
|
|
790
|
+
{ name: "portraitUrl", value: params.portraitUrl },
|
|
791
|
+
{ name: "avatar", value: params.avatar },
|
|
792
|
+
{ name: "avatarUrl", value: params.avatarUrl },
|
|
793
|
+
{ name: "avatar_url", value: params.avatar_url },
|
|
794
|
+
{ name: "photo", value: params.photo },
|
|
795
|
+
{ name: "photoUrl", value: params.photoUrl },
|
|
796
|
+
{ name: "photo_url", value: params.photo_url }
|
|
797
|
+
]),
|
|
798
|
+
audio_url: this.resolveRequiredStringAliases([
|
|
799
|
+
{ name: "audio_url", value: params.audio_url },
|
|
800
|
+
{ name: "audioUrl", value: params.audioUrl },
|
|
801
|
+
{ name: "audio", value: params.audio }
|
|
802
|
+
]),
|
|
803
|
+
expressiveness: params.expressiveness
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
videoJobPath(jobId) {
|
|
807
|
+
return `/videos/${encodeURIComponent(jobId)}`;
|
|
808
|
+
}
|
|
809
|
+
normalizeVideoJob(job, headers) {
|
|
810
|
+
const jobId = job.id ?? job.jobId ?? job.job_id ?? job.task_id ?? job.taskId;
|
|
811
|
+
if (!jobId) {
|
|
812
|
+
throw new PuppetryError(
|
|
813
|
+
502,
|
|
814
|
+
"invalid_response",
|
|
815
|
+
"Puppetry API returned a video job without an id"
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
const retryAfter = job.retryAfter ?? job.retryAfterSeconds ?? job.retry_after_seconds ?? this.parseRetryAfter(this.readResponseHeader(headers, "Retry-After"));
|
|
819
|
+
const nextPollAt = job.nextPollAt ?? job.next_poll_at ?? this.videoNextPollAtHeader(headers);
|
|
820
|
+
const videoUrl = this.firstDefinedNullable(
|
|
821
|
+
job.videoUrl,
|
|
822
|
+
job.video_url,
|
|
823
|
+
job.resultUrl,
|
|
824
|
+
job.result_url,
|
|
825
|
+
job.downloadUrl,
|
|
826
|
+
job.download_url,
|
|
827
|
+
job.outputUrl,
|
|
828
|
+
job.output_url,
|
|
829
|
+
job.url
|
|
830
|
+
);
|
|
831
|
+
const durationSeconds = this.firstDefinedNullable(
|
|
832
|
+
job.durationSeconds,
|
|
833
|
+
job.duration_seconds,
|
|
834
|
+
job.duration
|
|
835
|
+
);
|
|
836
|
+
const retryable = typeof job.retryable === "boolean" ? job.retryable : void 0;
|
|
837
|
+
const retryBlockedReason = job.retryBlockedReason ?? job.retry_blocked_reason;
|
|
838
|
+
const retryBlockCode = job.retryBlockCode ?? job.retry_block_code;
|
|
839
|
+
const diagnostics = job.diagnostics ?? this.videoJobResultDiagnostics(job.result);
|
|
840
|
+
const createdAt = job.createdAt ?? job.created_at;
|
|
841
|
+
const expiresAt = job.expiresAt ?? job.expires_at;
|
|
842
|
+
const completedAt = this.firstDefinedNullable(
|
|
843
|
+
job.completedAt,
|
|
844
|
+
job.completed_at
|
|
845
|
+
);
|
|
846
|
+
const thumbnailUrl = this.firstDefinedNullable(
|
|
847
|
+
job.thumbnailUrl,
|
|
848
|
+
job.thumbnail_url
|
|
849
|
+
);
|
|
850
|
+
const { statusUrl, contentLocation } = this.videoJobPollLocationAliases(job, headers);
|
|
851
|
+
const statusCredits = this.videoStatusCredits(job.credits);
|
|
852
|
+
const creationCredits = this.videoCreationCredits(job, headers);
|
|
853
|
+
const operationId = job.operationId ?? job.operation_id ?? this.videoOperationIdHeader(headers);
|
|
854
|
+
const requestId = job.requestId ?? job.request_id ?? this.videoRequestIdHeader(headers);
|
|
855
|
+
const idempotentReplay = job.idempotentReplay ?? job.idempotent_replay;
|
|
856
|
+
const source = normalizeVideoJobSource(job.source) ?? normalizeVideoJobSource(job.request_source) ?? normalizeVideoJobSource(job.requestSource);
|
|
857
|
+
const status = normalizeVideoStatusAlias(job.status) ?? normalizeVideoStatusAlias(job.state) ?? normalizeVideoStatusAlias(job.task_status) ?? normalizeVideoStatusAlias(job.taskStatus) ?? "processing";
|
|
858
|
+
const progress = this.videoProgress(job);
|
|
859
|
+
const {
|
|
860
|
+
credits: _credits,
|
|
861
|
+
creation_credits: _creationCredits,
|
|
862
|
+
creationCredits: _creationCreditsCamel,
|
|
863
|
+
...jobWithoutCreditAliases
|
|
864
|
+
} = job;
|
|
865
|
+
return this.attachVideoJobHelpers({
|
|
866
|
+
...jobWithoutCreditAliases,
|
|
867
|
+
id: jobId,
|
|
868
|
+
job_id: job.job_id ?? jobId,
|
|
869
|
+
jobId: job.jobId ?? jobId,
|
|
870
|
+
operation_id: job.operation_id ?? operationId ?? jobId,
|
|
871
|
+
operationId: job.operationId ?? operationId ?? jobId,
|
|
872
|
+
...requestId !== void 0 ? { request_id: requestId, requestId } : {},
|
|
873
|
+
task_id: job.task_id ?? jobId,
|
|
874
|
+
taskId: job.taskId ?? jobId,
|
|
875
|
+
status,
|
|
876
|
+
state: normalizeVideoStatusAlias(job.state) ?? status,
|
|
877
|
+
task_status: normalizeVideoStatusAlias(job.task_status) ?? status,
|
|
878
|
+
taskStatus: normalizeVideoStatusAlias(job.taskStatus) ?? status,
|
|
879
|
+
...source !== void 0 ? { source, request_source: source, requestSource: source } : {},
|
|
880
|
+
...progress !== void 0 ? {
|
|
881
|
+
progress,
|
|
882
|
+
percent: job.percent ?? progress,
|
|
883
|
+
percentage: job.percentage ?? progress,
|
|
884
|
+
progress_percent: job.progress_percent ?? progress,
|
|
885
|
+
progressPercent: job.progressPercent ?? progress
|
|
886
|
+
} : {},
|
|
887
|
+
...createdAt !== void 0 ? { created_at: createdAt, createdAt } : {},
|
|
888
|
+
...expiresAt !== void 0 ? { expires_at: expiresAt, expiresAt } : {},
|
|
889
|
+
...completedAt !== void 0 ? { completed_at: completedAt, completedAt } : {},
|
|
890
|
+
...videoUrl !== void 0 ? {
|
|
891
|
+
url: videoUrl,
|
|
892
|
+
video_url: videoUrl,
|
|
893
|
+
videoUrl,
|
|
894
|
+
result_url: videoUrl,
|
|
895
|
+
resultUrl: videoUrl,
|
|
896
|
+
download_url: videoUrl,
|
|
897
|
+
downloadUrl: videoUrl,
|
|
898
|
+
output_url: videoUrl,
|
|
899
|
+
outputUrl: videoUrl
|
|
900
|
+
} : {},
|
|
901
|
+
...thumbnailUrl !== void 0 ? { thumbnail_url: thumbnailUrl, thumbnailUrl } : {},
|
|
902
|
+
duration: job.duration ?? durationSeconds,
|
|
903
|
+
duration_seconds: job.duration_seconds ?? durationSeconds,
|
|
904
|
+
durationSeconds,
|
|
905
|
+
...statusUrl !== void 0 ? { status_url: statusUrl, statusUrl } : {},
|
|
906
|
+
...contentLocation !== void 0 ? { content_location: contentLocation, contentLocation } : {},
|
|
907
|
+
...statusCredits !== void 0 ? { credits: statusCredits } : {},
|
|
908
|
+
...creationCredits !== void 0 ? { creation_credits: creationCredits, creationCredits } : {},
|
|
909
|
+
...idempotentReplay !== void 0 ? { idempotent_replay: idempotentReplay, idempotentReplay } : {},
|
|
910
|
+
...retryable !== void 0 ? { retryable } : {},
|
|
911
|
+
...retryBlockedReason ? {
|
|
912
|
+
retry_blocked_reason: retryBlockedReason,
|
|
913
|
+
retryBlockedReason
|
|
914
|
+
} : {},
|
|
915
|
+
...retryBlockCode ? {
|
|
916
|
+
retry_block_code: retryBlockCode,
|
|
917
|
+
retryBlockCode
|
|
918
|
+
} : {},
|
|
919
|
+
...diagnostics ? { diagnostics } : {},
|
|
920
|
+
...retryAfter !== void 0 ? { retryAfter } : {},
|
|
921
|
+
...retryAfter !== void 0 ? { retry_after_seconds: retryAfter } : {},
|
|
922
|
+
...retryAfter !== void 0 ? { retryAfterSeconds: retryAfter } : {},
|
|
923
|
+
...nextPollAt !== void 0 ? { next_poll_at: nextPollAt, nextPollAt } : {}
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
videoJobStatusUrlHeader(headers) {
|
|
927
|
+
return this.videoLocationHeader(headers) ?? this.videoContentLocationHeader(headers);
|
|
928
|
+
}
|
|
929
|
+
videoJobPollLocationAliases(job, headers) {
|
|
930
|
+
const bodyStatusUrl = job.statusUrl ?? job.status_url;
|
|
931
|
+
const bodyContentLocation = job.contentLocation ?? job.content_location;
|
|
932
|
+
const headerLocation = this.videoLocationHeader(headers);
|
|
933
|
+
const headerContentLocation = this.videoContentLocationHeader(headers);
|
|
934
|
+
return {
|
|
935
|
+
statusUrl: bodyStatusUrl ?? bodyContentLocation ?? headerLocation ?? headerContentLocation,
|
|
936
|
+
contentLocation: bodyContentLocation ?? bodyStatusUrl ?? headerContentLocation ?? headerLocation
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
videoLocationHeader(headers) {
|
|
940
|
+
const value = headers?.get("Location") ?? headers?.get("location");
|
|
941
|
+
if (!value) return void 0;
|
|
942
|
+
const trimmed = value.trim();
|
|
943
|
+
return trimmed ? trimmed : void 0;
|
|
944
|
+
}
|
|
945
|
+
videoContentLocationHeader(headers) {
|
|
946
|
+
const value = headers?.get("Content-Location") ?? headers?.get("content-location");
|
|
947
|
+
if (!value) return void 0;
|
|
948
|
+
const trimmed = value.trim();
|
|
949
|
+
return trimmed ? trimmed : void 0;
|
|
950
|
+
}
|
|
951
|
+
videoOperationIdHeader(headers) {
|
|
952
|
+
const value = headers?.get("Puppetry-Operation-Id") ?? headers?.get("puppetry-operation-id") ?? headers?.get("X-Puppetry-Operation-Id") ?? headers?.get("x-puppetry-operation-id");
|
|
953
|
+
if (!value) return void 0;
|
|
954
|
+
const trimmed = value.trim();
|
|
955
|
+
return trimmed ? trimmed : void 0;
|
|
956
|
+
}
|
|
957
|
+
videoRequestIdHeader(headers) {
|
|
958
|
+
const value = headers?.get("Puppetry-Request-Id") ?? headers?.get("puppetry-request-id") ?? headers?.get("X-Puppetry-Request-Id") ?? headers?.get("x-puppetry-request-id");
|
|
959
|
+
if (!value) return void 0;
|
|
960
|
+
const trimmed = value.trim();
|
|
961
|
+
return trimmed ? trimmed : void 0;
|
|
962
|
+
}
|
|
963
|
+
videoNextPollAtHeader(headers) {
|
|
964
|
+
const value = headers?.get("X-Puppetry-Next-Poll-At") ?? headers?.get("x-puppetry-next-poll-at");
|
|
965
|
+
if (!value) return void 0;
|
|
966
|
+
const trimmed = value.trim();
|
|
967
|
+
return trimmed ? trimmed : void 0;
|
|
968
|
+
}
|
|
969
|
+
videoStatusErrorOptions(method, path, headers, retryAfter) {
|
|
970
|
+
const statusUrl = this.videoJobStatusUrlHeader(headers);
|
|
971
|
+
const contentLocation = this.videoContentLocationHeader(headers);
|
|
972
|
+
const jobId = this.videoJobIdFromStatusUrl(statusUrl ?? contentLocation);
|
|
973
|
+
const operationId = this.videoOperationIdHeader(headers);
|
|
974
|
+
const requestId = this.videoRequestIdHeader(headers);
|
|
975
|
+
const nextPollAt = this.videoNextPollAtHeader(headers);
|
|
976
|
+
const creationCredits = this.isVideoStatusRequest(method, path) ? this.videoCreationCreditsFromHeaders(headers) : void 0;
|
|
977
|
+
return {
|
|
978
|
+
retryAfter,
|
|
979
|
+
...jobId ? { id: jobId, job_id: jobId, jobId } : {},
|
|
980
|
+
...statusUrl ? { status_url: statusUrl, statusUrl } : {},
|
|
981
|
+
...contentLocation ? { content_location: contentLocation, contentLocation } : {},
|
|
982
|
+
...operationId ? { operation_id: operationId, operationId } : {},
|
|
983
|
+
...requestId ? { request_id: requestId, requestId } : {},
|
|
984
|
+
...nextPollAt ? { next_poll_at: nextPollAt, nextPollAt } : {},
|
|
985
|
+
...creationCredits ? { creation_credits: creationCredits, creationCredits } : {}
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
videoCreditErrorOptions(headers) {
|
|
989
|
+
const requiredCredits = this.videoCreditHeaderNumber(
|
|
990
|
+
headers,
|
|
991
|
+
"X-Puppetry-Credits-Required"
|
|
992
|
+
);
|
|
993
|
+
const creditsRemaining = this.videoCreditHeaderNumber(
|
|
994
|
+
headers,
|
|
995
|
+
"X-Puppetry-Credits-Remaining"
|
|
996
|
+
);
|
|
997
|
+
return {
|
|
998
|
+
...requiredCredits !== void 0 ? { required_credits: requiredCredits, requiredCredits } : {},
|
|
999
|
+
...creditsRemaining !== void 0 ? {
|
|
1000
|
+
credits_remaining: creditsRemaining,
|
|
1001
|
+
creditsRemaining,
|
|
1002
|
+
remaining_credits: creditsRemaining,
|
|
1003
|
+
remainingCredits: creditsRemaining
|
|
1004
|
+
} : {}
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
videoCreditHeaderNumber(headers, name) {
|
|
1008
|
+
const raw = this.readResponseHeader(headers, name);
|
|
1009
|
+
if (raw === null) return void 0;
|
|
1010
|
+
const trimmed = raw.trim();
|
|
1011
|
+
if (!/^\d+$/.test(trimmed)) return void 0;
|
|
1012
|
+
const value = Number(trimmed);
|
|
1013
|
+
return Number.isSafeInteger(value) ? value : void 0;
|
|
1014
|
+
}
|
|
1015
|
+
videoJobIdFromStatusUrl(statusUrl) {
|
|
1016
|
+
if (!statusUrl) return void 0;
|
|
1017
|
+
try {
|
|
1018
|
+
const url = new URL(statusUrl, this.baseUrl);
|
|
1019
|
+
const match = url.pathname.match(/\/videos\/([^/]+)\/?$/);
|
|
1020
|
+
if (!match) return void 0;
|
|
1021
|
+
return decodeURIComponent(match[1]);
|
|
1022
|
+
} catch {
|
|
1023
|
+
return void 0;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
retryableStatusErrorJobId(err) {
|
|
1027
|
+
return err.jobId ?? this.videoJobIdFromStatusUrl(err.statusUrl ?? err.status_url);
|
|
1028
|
+
}
|
|
1029
|
+
videoJobResultDiagnostics(result) {
|
|
1030
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
1031
|
+
return void 0;
|
|
1032
|
+
}
|
|
1033
|
+
return result.diagnostics;
|
|
1034
|
+
}
|
|
1035
|
+
videoStatusCredits(credits) {
|
|
1036
|
+
if (!credits || typeof credits !== "object") return void 0;
|
|
1037
|
+
if (typeof credits.charged !== "number" || !Number.isFinite(credits.charged)) {
|
|
1038
|
+
return void 0;
|
|
1039
|
+
}
|
|
1040
|
+
return {
|
|
1041
|
+
charged: credits.charged,
|
|
1042
|
+
remaining: typeof credits.remaining === "number" && Number.isFinite(credits.remaining) ? credits.remaining : null
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
videoCreationCredits(job, headers) {
|
|
1046
|
+
const raw = job.creationCredits ?? job.creation_credits;
|
|
1047
|
+
if (raw && typeof raw === "object") {
|
|
1048
|
+
if (typeof raw.charged !== "number" || !Number.isFinite(raw.charged)) {
|
|
1049
|
+
return this.videoCreationCreditsFromHeaders(headers);
|
|
1050
|
+
}
|
|
1051
|
+
const remainingAfterDebit = raw.remainingAfterDebit ?? raw.remaining_after_debit ?? null;
|
|
1052
|
+
const normalizedRemainingAfterDebit = typeof remainingAfterDebit === "number" && Number.isFinite(remainingAfterDebit) ? remainingAfterDebit : null;
|
|
1053
|
+
return {
|
|
1054
|
+
charged: raw.charged,
|
|
1055
|
+
remaining_after_debit: normalizedRemainingAfterDebit,
|
|
1056
|
+
remainingAfterDebit: normalizedRemainingAfterDebit
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
return this.videoCreationCreditsFromHeaders(headers);
|
|
1060
|
+
}
|
|
1061
|
+
videoCreationCreditsFromHeaders(headers) {
|
|
1062
|
+
const charged = this.videoCreditHeaderNumber(
|
|
1063
|
+
headers,
|
|
1064
|
+
"X-Puppetry-Credits-Required"
|
|
1065
|
+
);
|
|
1066
|
+
if (charged === void 0) return void 0;
|
|
1067
|
+
const remainingAfterDebit = this.videoCreditHeaderNumber(
|
|
1068
|
+
headers,
|
|
1069
|
+
"X-Puppetry-Credits-Remaining"
|
|
1070
|
+
) ?? null;
|
|
1071
|
+
return {
|
|
1072
|
+
charged,
|
|
1073
|
+
remaining_after_debit: remainingAfterDebit,
|
|
1074
|
+
remainingAfterDebit
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
videoProgress(job) {
|
|
1078
|
+
const progress = job.progress ?? job.percent ?? job.percentage ?? job.progressPercent ?? job.progress_percent;
|
|
1079
|
+
return typeof progress === "number" || progress === null ? progress : void 0;
|
|
1080
|
+
}
|
|
1081
|
+
attachVideoJobHelpers(job) {
|
|
1082
|
+
const sdkJob = job;
|
|
1083
|
+
Object.defineProperty(sdkJob, "waitForCompletion", {
|
|
1084
|
+
value: (options) => {
|
|
1085
|
+
if (sdkJob.status === "completed" || sdkJob.status === "failed") {
|
|
1086
|
+
return Promise.resolve(sdkJob);
|
|
1087
|
+
}
|
|
1088
|
+
return this.videos.waitForCompletion(
|
|
1089
|
+
this.videoJobIdFromStatusUrl(sdkJob.statusUrl ?? sdkJob.status_url) ?? sdkJob.id,
|
|
1090
|
+
this.waitOptionsWithInitialDelay(
|
|
1091
|
+
options,
|
|
1092
|
+
sdkJob.retryAfter,
|
|
1093
|
+
sdkJob.nextPollAt ?? sdkJob.next_poll_at
|
|
1094
|
+
)
|
|
1095
|
+
);
|
|
1096
|
+
},
|
|
1097
|
+
enumerable: false,
|
|
1098
|
+
configurable: true
|
|
1099
|
+
});
|
|
1100
|
+
return sdkJob;
|
|
1101
|
+
}
|
|
1102
|
+
waitOptionsWithInitialDelay(options, retryAfterSeconds, nextPollAt) {
|
|
1103
|
+
if (options?.initialDelayMs !== void 0) return options;
|
|
1104
|
+
const initialDelayMs = this.retryAfterDelayMs(retryAfterSeconds) ?? this.nextPollDelayMs(nextPollAt);
|
|
1105
|
+
return initialDelayMs === void 0 ? options : { ...options, initialDelayMs };
|
|
1106
|
+
}
|
|
1107
|
+
retryAfterDelayMs(value) {
|
|
1108
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
1109
|
+
return void 0;
|
|
1110
|
+
}
|
|
1111
|
+
return value * 1e3;
|
|
1112
|
+
}
|
|
1113
|
+
optionDelayMs(value) {
|
|
1114
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
1115
|
+
return void 0;
|
|
1116
|
+
}
|
|
1117
|
+
return value;
|
|
1118
|
+
}
|
|
1119
|
+
nextPollDelayMs(value) {
|
|
1120
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
1121
|
+
return void 0;
|
|
1122
|
+
}
|
|
1123
|
+
const timestamp = Date.parse(value);
|
|
1124
|
+
if (!Number.isFinite(timestamp)) {
|
|
1125
|
+
return void 0;
|
|
1126
|
+
}
|
|
1127
|
+
return Math.max(timestamp - Date.now(), 0);
|
|
1128
|
+
}
|
|
1129
|
+
async sleepWithinDeadline(ms, deadline) {
|
|
1130
|
+
const remainingMs = Math.max(deadline - Date.now(), 0);
|
|
1131
|
+
await this.sleep(Math.min(ms, remainingMs));
|
|
1132
|
+
}
|
|
1133
|
+
pollingDelayMs(job, headers, intervalMs) {
|
|
1134
|
+
const retryAfterSeconds = this.parseRetryAfter(this.readResponseHeader(headers, "Retry-After")) ?? job.retryAfter;
|
|
1135
|
+
const retryAfterDelayMs = this.retryAfterDelayMs(retryAfterSeconds);
|
|
1136
|
+
if (retryAfterDelayMs !== void 0) {
|
|
1137
|
+
return retryAfterDelayMs;
|
|
1138
|
+
}
|
|
1139
|
+
return this.nextPollDelayMs(job.nextPollAt ?? job.next_poll_at) ?? intervalMs;
|
|
1140
|
+
}
|
|
1141
|
+
retryableStatusErrorDelayMs(err, intervalMs) {
|
|
1142
|
+
if (err.retryable !== true) return void 0;
|
|
1143
|
+
return this.retryAfterDelayMs(err.retryAfter) ?? this.nextPollDelayMs(err.nextPollAt ?? err.next_poll_at) ?? intervalMs;
|
|
1144
|
+
}
|
|
1145
|
+
normalizeUsage(usage) {
|
|
1146
|
+
const videoCredits = usage.video_credits ?? usage.videoCredits;
|
|
1147
|
+
if (!videoCredits) {
|
|
1148
|
+
return usage;
|
|
1149
|
+
}
|
|
1150
|
+
const rawCurrentApiKey = videoCredits.current_api_key ?? videoCredits.currentApiKey ?? {
|
|
1151
|
+
used: 0,
|
|
1152
|
+
refunded: 0,
|
|
1153
|
+
net_used: 0,
|
|
1154
|
+
transactions: 0
|
|
1155
|
+
};
|
|
1156
|
+
const currentApiKeyNetUsed = rawCurrentApiKey.net_used ?? rawCurrentApiKey.netUsed ?? 0;
|
|
1157
|
+
const currentApiKey = {
|
|
1158
|
+
...rawCurrentApiKey,
|
|
1159
|
+
keyId: rawCurrentApiKey.keyId ?? rawCurrentApiKey.key_id,
|
|
1160
|
+
keyPrefix: rawCurrentApiKey.keyPrefix ?? rawCurrentApiKey.key_prefix,
|
|
1161
|
+
net_used: currentApiKeyNetUsed,
|
|
1162
|
+
netUsed: rawCurrentApiKey.netUsed ?? currentApiKeyNetUsed
|
|
1163
|
+
};
|
|
1164
|
+
const videoCreditNetUsed = videoCredits.net_used ?? videoCredits.netUsed ?? 0;
|
|
1165
|
+
const resetAt = videoCredits.reset_at ?? videoCredits.resetAt;
|
|
1166
|
+
const normalizedVideoCredits = {
|
|
1167
|
+
...videoCredits,
|
|
1168
|
+
reset_at: resetAt,
|
|
1169
|
+
resetAt: videoCredits.resetAt ?? resetAt,
|
|
1170
|
+
net_used: videoCreditNetUsed,
|
|
1171
|
+
netUsed: videoCredits.netUsed ?? videoCreditNetUsed,
|
|
1172
|
+
current_api_key: currentApiKey,
|
|
1173
|
+
currentApiKey: videoCredits.currentApiKey ?? currentApiKey
|
|
1174
|
+
};
|
|
1175
|
+
const videoGeneration = this.normalizeVideoGenerationReadiness(
|
|
1176
|
+
usage.video_generation ?? usage.videoGeneration,
|
|
1177
|
+
videoCredits.balance
|
|
1178
|
+
);
|
|
1179
|
+
return {
|
|
1180
|
+
...usage,
|
|
1181
|
+
video_credits: normalizedVideoCredits,
|
|
1182
|
+
videoCredits: usage.videoCredits ?? normalizedVideoCredits,
|
|
1183
|
+
video_generation: videoGeneration,
|
|
1184
|
+
videoGeneration: usage.videoGeneration ?? videoGeneration,
|
|
1185
|
+
plan: usage.plan ?? "developer_api",
|
|
1186
|
+
creditsTotal: usage.creditsTotal ?? Math.max(
|
|
1187
|
+
videoCredits.balance + videoCreditNetUsed,
|
|
1188
|
+
videoCredits.balance
|
|
1189
|
+
),
|
|
1190
|
+
creditsUsed: usage.creditsUsed ?? videoCreditNetUsed,
|
|
1191
|
+
creditsRemaining: usage.creditsRemaining ?? videoCredits.balance,
|
|
1192
|
+
periodStart: usage.periodStart ?? `${videoCredits.month}-01`,
|
|
1193
|
+
periodEnd: usage.periodEnd ?? resetAt
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
normalizeVideoGenerationReadiness(readiness, balance) {
|
|
1197
|
+
const requiredCredits = readiness?.required_credits ?? readiness?.requiredCredits ?? 1;
|
|
1198
|
+
const creditsRemaining = readiness?.credits_remaining ?? readiness?.creditsRemaining ?? balance;
|
|
1199
|
+
const concurrentJobs = readiness?.concurrent_jobs ?? readiness?.concurrentJobs;
|
|
1200
|
+
const activeJobs = readiness?.active_jobs ?? readiness?.activeJobs;
|
|
1201
|
+
const availableSlots = readiness?.available_slots ?? readiness?.availableSlots;
|
|
1202
|
+
const activeJobExpiresInSeconds = readiness?.active_job_expires_in_seconds ?? readiness?.activeJobExpiresInSeconds;
|
|
1203
|
+
const canCreate = readiness?.can_create ?? readiness?.canCreate ?? (creditsRemaining >= requiredCredits && (availableSlots === void 0 || availableSlots > 0));
|
|
1204
|
+
const reason = readiness?.reason ?? (creditsRemaining < requiredCredits ? "insufficient_video_credits" : availableSlots !== void 0 && availableSlots <= 0 ? "concurrent_job_limit_reached" : "available");
|
|
1205
|
+
const retryAfterSeconds = readiness?.retry_after_seconds ?? readiness?.retryAfterSeconds ?? readiness?.retryAfter ?? (reason === "concurrent_job_limit_reached" ? activeJobExpiresInSeconds : void 0);
|
|
1206
|
+
const nextPollAt = readiness?.nextPollAt ?? readiness?.next_poll_at;
|
|
1207
|
+
const blockers = this.normalizeVideoGenerationBlockers(
|
|
1208
|
+
readiness?.blockers,
|
|
1209
|
+
{
|
|
1210
|
+
reason,
|
|
1211
|
+
requiredCredits,
|
|
1212
|
+
creditsRemaining,
|
|
1213
|
+
concurrentJobs,
|
|
1214
|
+
activeJobs,
|
|
1215
|
+
availableSlots,
|
|
1216
|
+
activeJobExpiresInSeconds,
|
|
1217
|
+
retryAfterSeconds
|
|
1218
|
+
}
|
|
1219
|
+
);
|
|
1220
|
+
return {
|
|
1221
|
+
can_create: canCreate,
|
|
1222
|
+
canCreate: readiness?.canCreate ?? canCreate,
|
|
1223
|
+
required_credits: requiredCredits,
|
|
1224
|
+
requiredCredits: readiness?.requiredCredits ?? requiredCredits,
|
|
1225
|
+
credits_remaining: creditsRemaining,
|
|
1226
|
+
creditsRemaining: readiness?.creditsRemaining ?? creditsRemaining,
|
|
1227
|
+
...concurrentJobs === void 0 ? {} : {
|
|
1228
|
+
concurrent_jobs: concurrentJobs,
|
|
1229
|
+
concurrentJobs: readiness?.concurrentJobs ?? concurrentJobs
|
|
1230
|
+
},
|
|
1231
|
+
...activeJobs === void 0 ? {} : {
|
|
1232
|
+
active_jobs: activeJobs,
|
|
1233
|
+
activeJobs: readiness?.activeJobs ?? activeJobs
|
|
1234
|
+
},
|
|
1235
|
+
...availableSlots === void 0 ? {} : {
|
|
1236
|
+
available_slots: availableSlots,
|
|
1237
|
+
availableSlots: readiness?.availableSlots ?? availableSlots
|
|
1238
|
+
},
|
|
1239
|
+
...activeJobExpiresInSeconds === void 0 ? {} : {
|
|
1240
|
+
active_job_expires_in_seconds: activeJobExpiresInSeconds,
|
|
1241
|
+
activeJobExpiresInSeconds: readiness?.activeJobExpiresInSeconds ?? activeJobExpiresInSeconds
|
|
1242
|
+
},
|
|
1243
|
+
...retryAfterSeconds === void 0 ? {} : {
|
|
1244
|
+
retry_after_seconds: retryAfterSeconds,
|
|
1245
|
+
retryAfter: readiness?.retryAfter ?? retryAfterSeconds,
|
|
1246
|
+
retryAfterSeconds: readiness?.retryAfterSeconds ?? retryAfterSeconds
|
|
1247
|
+
},
|
|
1248
|
+
...nextPollAt === void 0 ? {} : { next_poll_at: nextPollAt, nextPollAt },
|
|
1249
|
+
...blockers.length === 0 ? {} : { blockers },
|
|
1250
|
+
reason
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
normalizeVideoGenerationBlockers(blockers, fallback) {
|
|
1254
|
+
if (Array.isArray(blockers) && blockers.length > 0) {
|
|
1255
|
+
return blockers.map(
|
|
1256
|
+
(blocker) => this.normalizeVideoGenerationBlocker(blocker)
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
return this.derivedVideoGenerationBlockers(fallback);
|
|
1260
|
+
}
|
|
1261
|
+
normalizeVideoGenerationBlocker(blocker) {
|
|
1262
|
+
const record = blocker;
|
|
1263
|
+
const code = this.videoGenerationReadinessReason(record.code) ?? this.videoGenerationReadinessReason(record.reason);
|
|
1264
|
+
if (code === "insufficient_video_credits") {
|
|
1265
|
+
const requiredCredits = this.readinessNumber(record.required_credits) ?? this.readinessNumber(record.requiredCredits) ?? 1;
|
|
1266
|
+
const creditsRemaining = this.readinessNumber(record.credits_remaining) ?? this.readinessNumber(record.creditsRemaining) ?? 0;
|
|
1267
|
+
const retryBlockCode = this.readinessString(record.retry_block_code) ?? this.readinessString(record.retryBlockCode);
|
|
1268
|
+
return {
|
|
1269
|
+
code,
|
|
1270
|
+
reason: code,
|
|
1271
|
+
retryable: false,
|
|
1272
|
+
required_credits: requiredCredits,
|
|
1273
|
+
requiredCredits,
|
|
1274
|
+
credits_remaining: creditsRemaining,
|
|
1275
|
+
creditsRemaining,
|
|
1276
|
+
...retryBlockCode === void 0 ? {} : {
|
|
1277
|
+
retry_block_code: retryBlockCode,
|
|
1278
|
+
retryBlockCode
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
const retryAfterSeconds = this.readinessNumber(record.retry_after_seconds) ?? this.readinessNumber(record.retryAfterSeconds) ?? this.readinessNumber(record.retryAfter);
|
|
1283
|
+
const nextPollAt = this.readinessString(record.nextPollAt) ?? this.readinessString(record.next_poll_at);
|
|
1284
|
+
return {
|
|
1285
|
+
code: "concurrent_job_limit_reached",
|
|
1286
|
+
reason: "concurrent_job_limit_reached",
|
|
1287
|
+
retryable: true,
|
|
1288
|
+
...this.activeJobReadinessAliases(record),
|
|
1289
|
+
...retryAfterSeconds === void 0 ? {} : {
|
|
1290
|
+
retry_after_seconds: retryAfterSeconds,
|
|
1291
|
+
retryAfter: retryAfterSeconds,
|
|
1292
|
+
retryAfterSeconds
|
|
1293
|
+
},
|
|
1294
|
+
...nextPollAt === void 0 ? {} : { next_poll_at: nextPollAt, nextPollAt }
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
derivedVideoGenerationBlockers(params) {
|
|
1298
|
+
const blockers = [];
|
|
1299
|
+
if (params.reason === "insufficient_video_credits" || params.creditsRemaining < params.requiredCredits) {
|
|
1300
|
+
blockers.push({
|
|
1301
|
+
code: "insufficient_video_credits",
|
|
1302
|
+
reason: "insufficient_video_credits",
|
|
1303
|
+
retryable: false,
|
|
1304
|
+
required_credits: params.requiredCredits,
|
|
1305
|
+
requiredCredits: params.requiredCredits,
|
|
1306
|
+
credits_remaining: params.creditsRemaining,
|
|
1307
|
+
creditsRemaining: params.creditsRemaining
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
if (params.reason === "concurrent_job_limit_reached" || params.availableSlots !== void 0 && params.availableSlots <= 0) {
|
|
1311
|
+
blockers.push({
|
|
1312
|
+
code: "concurrent_job_limit_reached",
|
|
1313
|
+
reason: "concurrent_job_limit_reached",
|
|
1314
|
+
retryable: true,
|
|
1315
|
+
...params.concurrentJobs === void 0 ? {} : {
|
|
1316
|
+
concurrent_jobs: params.concurrentJobs,
|
|
1317
|
+
concurrentJobs: params.concurrentJobs
|
|
1318
|
+
},
|
|
1319
|
+
...params.activeJobs === void 0 ? {} : {
|
|
1320
|
+
active_jobs: params.activeJobs,
|
|
1321
|
+
activeJobs: params.activeJobs
|
|
1322
|
+
},
|
|
1323
|
+
...params.availableSlots === void 0 ? {} : {
|
|
1324
|
+
available_slots: params.availableSlots,
|
|
1325
|
+
availableSlots: params.availableSlots
|
|
1326
|
+
},
|
|
1327
|
+
...params.activeJobExpiresInSeconds === void 0 ? {} : {
|
|
1328
|
+
active_job_expires_in_seconds: params.activeJobExpiresInSeconds,
|
|
1329
|
+
activeJobExpiresInSeconds: params.activeJobExpiresInSeconds
|
|
1330
|
+
},
|
|
1331
|
+
...params.retryAfterSeconds === void 0 ? {} : {
|
|
1332
|
+
retry_after_seconds: params.retryAfterSeconds,
|
|
1333
|
+
retryAfter: params.retryAfterSeconds,
|
|
1334
|
+
retryAfterSeconds: params.retryAfterSeconds
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
return blockers;
|
|
1339
|
+
}
|
|
1340
|
+
activeJobReadinessAliases(record) {
|
|
1341
|
+
const concurrentJobs = this.readinessNumber(record.concurrent_jobs) ?? this.readinessNumber(record.concurrentJobs);
|
|
1342
|
+
const activeJobs = this.readinessNumber(record.active_jobs) ?? this.readinessNumber(record.activeJobs);
|
|
1343
|
+
const availableSlots = this.readinessNumber(record.available_slots) ?? this.readinessNumber(record.availableSlots);
|
|
1344
|
+
const activeJobExpiresInSeconds = this.readinessNumber(record.active_job_expires_in_seconds) ?? this.readinessNumber(record.activeJobExpiresInSeconds);
|
|
1345
|
+
return {
|
|
1346
|
+
...concurrentJobs === void 0 ? {} : { concurrent_jobs: concurrentJobs, concurrentJobs },
|
|
1347
|
+
...activeJobs === void 0 ? {} : { active_jobs: activeJobs, activeJobs },
|
|
1348
|
+
...availableSlots === void 0 ? {} : { available_slots: availableSlots, availableSlots },
|
|
1349
|
+
...activeJobExpiresInSeconds === void 0 ? {} : {
|
|
1350
|
+
active_job_expires_in_seconds: activeJobExpiresInSeconds,
|
|
1351
|
+
activeJobExpiresInSeconds
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
videoGenerationReadinessReason(value) {
|
|
1356
|
+
return value === "available" || value === "insufficient_video_credits" || value === "concurrent_job_limit_reached" ? value : void 0;
|
|
1357
|
+
}
|
|
1358
|
+
readinessNumber(value) {
|
|
1359
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1360
|
+
}
|
|
1361
|
+
readinessString(value) {
|
|
1362
|
+
return typeof value === "string" && value ? value : void 0;
|
|
1363
|
+
}
|
|
1364
|
+
async getVideoGenerationReadiness() {
|
|
1365
|
+
const usage = await this.usage.get();
|
|
1366
|
+
const readiness = usage.video_generation ?? usage.videoGeneration;
|
|
1367
|
+
if (!readiness) {
|
|
1368
|
+
throw new PuppetryError(
|
|
1369
|
+
502,
|
|
1370
|
+
"invalid_response",
|
|
1371
|
+
"Puppetry API usage response did not include video_generation readiness"
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
return readiness;
|
|
1375
|
+
}
|
|
1376
|
+
async ensureVideoGenerationReady() {
|
|
1377
|
+
const readiness = await this.getVideoGenerationReadiness();
|
|
1378
|
+
if (readiness.can_create !== false) return readiness;
|
|
1379
|
+
const retryAfter = readiness.retry_after_seconds ?? readiness.retryAfterSeconds ?? readiness.retryAfter ?? readiness.active_job_expires_in_seconds ?? readiness.activeJobExpiresInSeconds;
|
|
1380
|
+
const nextPollAt = readiness.nextPollAt ?? readiness.next_poll_at;
|
|
1381
|
+
const details = { video_generation: readiness };
|
|
1382
|
+
const errorOptions = this.videoGenerationReadinessErrorOptions(readiness);
|
|
1383
|
+
if (readiness.reason === "concurrent_job_limit_reached") {
|
|
1384
|
+
throw new PuppetryRateLimitError(
|
|
1385
|
+
retryAfter,
|
|
1386
|
+
"Video generation is at the active job limit",
|
|
1387
|
+
details,
|
|
1388
|
+
"rate_limited",
|
|
1389
|
+
{
|
|
1390
|
+
...errorOptions,
|
|
1391
|
+
...nextPollAt === void 0 ? {} : { next_poll_at: nextPollAt, nextPollAt }
|
|
1392
|
+
}
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
throw new PuppetryError(
|
|
1396
|
+
402,
|
|
1397
|
+
readiness.reason,
|
|
1398
|
+
"Video generation is not ready for this API key",
|
|
1399
|
+
details,
|
|
1400
|
+
{
|
|
1401
|
+
...errorOptions,
|
|
1402
|
+
retryAfter,
|
|
1403
|
+
...nextPollAt === void 0 ? {} : { next_poll_at: nextPollAt, nextPollAt }
|
|
1404
|
+
}
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
videoGenerationReadinessErrorOptions(readiness) {
|
|
1408
|
+
const requiredCredits = readiness.required_credits ?? readiness.requiredCredits;
|
|
1409
|
+
const creditsRemaining = readiness.credits_remaining ?? readiness.creditsRemaining;
|
|
1410
|
+
const blocker = readiness.blockers?.find(
|
|
1411
|
+
(item) => item.reason === readiness.reason || item.code === readiness.reason
|
|
1412
|
+
);
|
|
1413
|
+
const retryBlockCode = readiness.reason === "insufficient_video_credits" ? (blocker && "retry_block_code" in blocker ? blocker.retry_block_code ?? blocker.retryBlockCode : void 0) ?? "INSUFFICIENT_VIDEO_CREDITS" : void 0;
|
|
1414
|
+
const retryBlockedReason = readiness.reason === "insufficient_video_credits" ? `Developer API video generation needs ${requiredCredits ?? 1} credit, but only ${creditsRemaining ?? 0} remain.` : readiness.reason;
|
|
1415
|
+
return {
|
|
1416
|
+
retryable: readiness.reason === "concurrent_job_limit_reached",
|
|
1417
|
+
...requiredCredits === void 0 ? {} : { required_credits: requiredCredits, requiredCredits },
|
|
1418
|
+
...creditsRemaining === void 0 ? {} : {
|
|
1419
|
+
credits_remaining: creditsRemaining,
|
|
1420
|
+
creditsRemaining,
|
|
1421
|
+
remaining_credits: creditsRemaining,
|
|
1422
|
+
remainingCredits: creditsRemaining
|
|
1423
|
+
},
|
|
1424
|
+
retry_blocked_reason: retryBlockedReason,
|
|
1425
|
+
retryBlockedReason,
|
|
1426
|
+
...retryBlockCode === void 0 ? {} : { retry_block_code: retryBlockCode, retryBlockCode }
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
normalizeAudioUploadLimits(limits) {
|
|
1430
|
+
const maxFileBytes = limits.max_file_bytes ?? limits.maxFileBytes;
|
|
1431
|
+
const monthlyUploads = limits.monthly_uploads ?? limits.monthlyUploads;
|
|
1432
|
+
const storageBytes = limits.storage_bytes ?? limits.storageBytes;
|
|
1433
|
+
const remainingMonthlyUploads = limits.remaining_monthly_uploads ?? limits.remainingMonthlyUploads;
|
|
1434
|
+
const remainingStorageBytes = limits.remaining_storage_bytes ?? limits.remainingStorageBytes;
|
|
1435
|
+
return {
|
|
1436
|
+
...limits,
|
|
1437
|
+
max_file_bytes: maxFileBytes,
|
|
1438
|
+
maxFileBytes,
|
|
1439
|
+
monthly_uploads: monthlyUploads,
|
|
1440
|
+
monthlyUploads,
|
|
1441
|
+
storage_bytes: storageBytes,
|
|
1442
|
+
storageBytes,
|
|
1443
|
+
remaining_monthly_uploads: remainingMonthlyUploads,
|
|
1444
|
+
remainingMonthlyUploads,
|
|
1445
|
+
remaining_storage_bytes: remainingStorageBytes,
|
|
1446
|
+
remainingStorageBytes
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
normalizeAudioUpload(upload) {
|
|
1450
|
+
const uploadUrl = upload.upload_url ?? upload.uploadUrl;
|
|
1451
|
+
const readUrl = upload.read_url ?? upload.readUrl;
|
|
1452
|
+
if (!uploadUrl || !readUrl) {
|
|
1453
|
+
throw new PuppetryError(
|
|
1454
|
+
502,
|
|
1455
|
+
"invalid_response",
|
|
1456
|
+
"Puppetry API returned an upload response without upload/read URLs"
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
const readUrlExpiresIn = upload.read_url_expires_in ?? upload.readUrlExpiresIn;
|
|
1460
|
+
const readUrlExpiresAt = upload.read_url_expires_at ?? upload.readUrlExpiresAt;
|
|
1461
|
+
const uploadUrlExpiresIn = upload.upload_url_expires_in ?? upload.uploadUrlExpiresIn;
|
|
1462
|
+
const idempotentReplay = upload.idempotent_replay ?? upload.idempotentReplay;
|
|
1463
|
+
return {
|
|
1464
|
+
...upload,
|
|
1465
|
+
upload_url: uploadUrl,
|
|
1466
|
+
uploadUrl,
|
|
1467
|
+
read_url: readUrl,
|
|
1468
|
+
readUrl,
|
|
1469
|
+
read_url_expires_in: readUrlExpiresIn,
|
|
1470
|
+
readUrlExpiresIn,
|
|
1471
|
+
read_url_expires_at: readUrlExpiresAt,
|
|
1472
|
+
readUrlExpiresAt,
|
|
1473
|
+
upload_url_expires_in: uploadUrlExpiresIn,
|
|
1474
|
+
uploadUrlExpiresIn,
|
|
1475
|
+
idempotent_replay: idempotentReplay,
|
|
1476
|
+
idempotentReplay,
|
|
1477
|
+
limits: this.normalizeAudioUploadLimits(upload.limits)
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
// ── Polled event helper ────────────────────────────────────────────
|
|
1481
|
+
videoJobEvent(job, headers) {
|
|
1482
|
+
const normalizedJob = this.normalizeVideoJob(job, headers);
|
|
1483
|
+
const progress = typeof normalizedJob.progress === "number" ? normalizedJob.progress : normalizedJob.status === "completed" ? 100 : void 0;
|
|
1484
|
+
const videoUrl = normalizedJob.videoUrl ?? void 0;
|
|
1485
|
+
const type = normalizedJob.status === "processing" && progress !== void 0 ? "progress" : normalizedJob.status;
|
|
1486
|
+
const statusUrl = normalizedJob.statusUrl ?? normalizedJob.status_url;
|
|
1487
|
+
const contentLocation = normalizedJob.contentLocation ?? normalizedJob.content_location;
|
|
1488
|
+
const operationId = job.operationId ?? job.operation_id ?? this.videoOperationIdHeader(headers);
|
|
1489
|
+
const requestId = job.requestId ?? job.request_id ?? this.videoRequestIdHeader(headers);
|
|
1490
|
+
const source = normalizeVideoJobSource(job.source) ?? normalizeVideoJobSource(job.request_source) ?? normalizeVideoJobSource(job.requestSource);
|
|
1491
|
+
const retryAfter = normalizedJob.retryAfter ?? normalizedJob.retryAfterSeconds ?? normalizedJob.retry_after_seconds;
|
|
1492
|
+
const nextPollAt = normalizedJob.nextPollAt ?? normalizedJob.next_poll_at;
|
|
1493
|
+
const creationCredits = this.videoCreationCredits(normalizedJob);
|
|
1494
|
+
return {
|
|
1495
|
+
type,
|
|
1496
|
+
id: normalizedJob.id,
|
|
1497
|
+
job_id: normalizedJob.job_id,
|
|
1498
|
+
jobId: normalizedJob.id,
|
|
1499
|
+
task_id: normalizedJob.task_id,
|
|
1500
|
+
taskId: normalizedJob.taskId,
|
|
1501
|
+
...operationId ? { operation_id: operationId, operationId } : {},
|
|
1502
|
+
...requestId ? { request_id: requestId, requestId } : {},
|
|
1503
|
+
...source ? { source, request_source: source, requestSource: source } : {},
|
|
1504
|
+
...statusUrl ? { status_url: statusUrl, statusUrl } : {},
|
|
1505
|
+
...contentLocation ? { content_location: contentLocation, contentLocation } : {},
|
|
1506
|
+
...progress !== void 0 ? { progress } : {},
|
|
1507
|
+
...videoUrl ? { video_url: videoUrl, videoUrl } : {},
|
|
1508
|
+
...normalizedJob.error ? { error: normalizedJob.error } : {},
|
|
1509
|
+
...normalizedJob.retryable !== void 0 ? { retryable: normalizedJob.retryable } : {},
|
|
1510
|
+
...normalizedJob.credits ? { credits: normalizedJob.credits } : {},
|
|
1511
|
+
...creationCredits ? {
|
|
1512
|
+
creation_credits: creationCredits,
|
|
1513
|
+
creationCredits
|
|
1514
|
+
} : {},
|
|
1515
|
+
...normalizedJob.retryBlockedReason ? {
|
|
1516
|
+
retry_blocked_reason: normalizedJob.retryBlockedReason,
|
|
1517
|
+
retryBlockedReason: normalizedJob.retryBlockedReason
|
|
1518
|
+
} : {},
|
|
1519
|
+
...normalizedJob.retryBlockCode ? {
|
|
1520
|
+
retry_block_code: normalizedJob.retryBlockCode,
|
|
1521
|
+
retryBlockCode: normalizedJob.retryBlockCode
|
|
1522
|
+
} : {},
|
|
1523
|
+
...retryAfter !== void 0 ? {
|
|
1524
|
+
retry_after_seconds: retryAfter,
|
|
1525
|
+
retryAfter,
|
|
1526
|
+
retryAfterSeconds: retryAfter
|
|
1527
|
+
} : {},
|
|
1528
|
+
...nextPollAt ? {
|
|
1529
|
+
next_poll_at: nextPollAt,
|
|
1530
|
+
nextPollAt
|
|
1531
|
+
} : {},
|
|
1532
|
+
...normalizedJob.createdAt ? {
|
|
1533
|
+
created_at: normalizedJob.createdAt,
|
|
1534
|
+
createdAt: normalizedJob.createdAt
|
|
1535
|
+
} : {},
|
|
1536
|
+
...normalizedJob.expiresAt ? {
|
|
1537
|
+
expires_at: normalizedJob.expiresAt,
|
|
1538
|
+
expiresAt: normalizedJob.expiresAt
|
|
1539
|
+
} : {},
|
|
1540
|
+
...normalizedJob.completedAt !== void 0 ? {
|
|
1541
|
+
completed_at: normalizedJob.completedAt,
|
|
1542
|
+
completedAt: normalizedJob.completedAt
|
|
1543
|
+
} : {},
|
|
1544
|
+
timestamp: normalizedJob.completedAt ?? normalizedJob.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
videoRetryingEvent(err, fallbackJobId) {
|
|
1548
|
+
const jobId = this.retryableStatusErrorJobId(err) ?? fallbackJobId;
|
|
1549
|
+
const source = normalizeVideoJobSource(err.source);
|
|
1550
|
+
const requestSource = normalizeVideoJobSource(
|
|
1551
|
+
err.requestSource ?? err.request_source
|
|
1552
|
+
);
|
|
1553
|
+
return {
|
|
1554
|
+
type: "retrying",
|
|
1555
|
+
id: err.id ?? jobId,
|
|
1556
|
+
job_id: err.job_id ?? jobId,
|
|
1557
|
+
jobId,
|
|
1558
|
+
task_id: err.task_id ?? jobId,
|
|
1559
|
+
taskId: err.taskId ?? jobId,
|
|
1560
|
+
...err.operationId ? { operation_id: err.operationId, operationId: err.operationId } : {},
|
|
1561
|
+
...err.requestId ? { request_id: err.requestId, requestId: err.requestId } : {},
|
|
1562
|
+
...source || requestSource ? {
|
|
1563
|
+
source: source ?? requestSource,
|
|
1564
|
+
request_source: requestSource ?? source,
|
|
1565
|
+
requestSource: requestSource ?? source
|
|
1566
|
+
} : {},
|
|
1567
|
+
...err.statusUrl ? { status_url: err.statusUrl, statusUrl: err.statusUrl } : {},
|
|
1568
|
+
...err.contentLocation ? {
|
|
1569
|
+
content_location: err.contentLocation,
|
|
1570
|
+
contentLocation: err.contentLocation
|
|
1571
|
+
} : {},
|
|
1572
|
+
error: err.message,
|
|
1573
|
+
code: err.code,
|
|
1574
|
+
status: err.status,
|
|
1575
|
+
statusCode: err.status,
|
|
1576
|
+
...err.retryable !== void 0 ? { retryable: err.retryable } : {},
|
|
1577
|
+
...err.credits ? { credits: err.credits } : {},
|
|
1578
|
+
...err.creationCredits ? {
|
|
1579
|
+
creation_credits: err.creationCredits,
|
|
1580
|
+
creationCredits: err.creationCredits
|
|
1581
|
+
} : {},
|
|
1582
|
+
...err.retryBlockedReason ? {
|
|
1583
|
+
retry_blocked_reason: err.retryBlockedReason,
|
|
1584
|
+
retryBlockedReason: err.retryBlockedReason
|
|
1585
|
+
} : {},
|
|
1586
|
+
...err.retryBlockCode ? {
|
|
1587
|
+
retry_block_code: err.retryBlockCode,
|
|
1588
|
+
retryBlockCode: err.retryBlockCode
|
|
1589
|
+
} : {},
|
|
1590
|
+
...err.retryAfter !== void 0 ? {
|
|
1591
|
+
retry_after_seconds: err.retryAfter,
|
|
1592
|
+
retryAfter: err.retryAfter,
|
|
1593
|
+
retryAfterSeconds: err.retryAfter
|
|
1594
|
+
} : {},
|
|
1595
|
+
...err.nextPollAt ? { next_poll_at: err.nextPollAt, nextPollAt: err.nextPollAt } : {},
|
|
1596
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
async *streamPolledVideoEvents(jobId, options) {
|
|
1600
|
+
const interval = options?.intervalMs ?? 2e3;
|
|
1601
|
+
const timeout = options?.timeoutMs ?? 3e5;
|
|
1602
|
+
const deadline = Date.now() + timeout;
|
|
1603
|
+
const initialDelayMs = this.optionDelayMs(options?.initialDelayMs);
|
|
1604
|
+
let currentJobId = jobId;
|
|
1605
|
+
if (initialDelayMs !== void 0 && initialDelayMs > 0) {
|
|
1606
|
+
await this.sleepWithinDeadline(initialDelayMs, deadline);
|
|
1607
|
+
}
|
|
1608
|
+
while (Date.now() < deadline) {
|
|
1609
|
+
try {
|
|
1610
|
+
const { data: job, headers } = await this.getWithResponse(
|
|
1611
|
+
this.videoJobPath(currentJobId)
|
|
1612
|
+
);
|
|
1613
|
+
const normalizedJob = this.normalizeVideoJob(job, headers);
|
|
1614
|
+
currentJobId = this.videoJobIdFromStatusUrl(
|
|
1615
|
+
normalizedJob.statusUrl ?? normalizedJob.status_url
|
|
1616
|
+
) ?? normalizedJob.id;
|
|
1617
|
+
yield this.videoJobEvent(job, headers);
|
|
1618
|
+
if (normalizedJob.status === "completed" || normalizedJob.status === "failed") {
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const delayMs = this.pollingDelayMs(normalizedJob, headers, interval);
|
|
1622
|
+
await this.sleep(Math.min(delayMs, Math.max(deadline - Date.now(), 0)));
|
|
1623
|
+
} catch (err) {
|
|
1624
|
+
if (!(err instanceof PuppetryError)) throw err;
|
|
1625
|
+
const delayMs = this.retryableStatusErrorDelayMs(err, interval);
|
|
1626
|
+
if (delayMs === void 0) throw err;
|
|
1627
|
+
currentJobId = this.retryableStatusErrorJobId(err) ?? currentJobId;
|
|
1628
|
+
yield this.videoRetryingEvent(err, currentJobId);
|
|
1629
|
+
await this.sleepWithinDeadline(delayMs, deadline);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
throw new PuppetryTimeoutError(timeout);
|
|
1633
|
+
}
|
|
1634
|
+
};
|
|
1635
|
+
|
|
1636
|
+
export {
|
|
1637
|
+
Puppetry
|
|
1638
|
+
};
|