@clipwright/sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Dimantika LLC
3
+ Copyright (c) 2026 Dimantika Sp. z o.o.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type MakeUgcInput, type MakeUgcInputArgs, type QuoteResponse, type Run, type RunRead, type VoicePreset, type VoicePresetName } from "@clipwright/core";
1
+ import { type AgentFailureReport, type ApiFailure, type MakeUgcInput, type MakeUgcInputArgs, type QuoteResponse, type Run, type RunRead, type VoicePreset, type VoicePresetName } from "@clipwright/core";
2
2
  export interface ClipwrightClientOptions {
3
3
  apiKey: string;
4
4
  baseUrl?: string | undefined;
@@ -18,10 +18,68 @@ export interface MakeUgcOptions {
18
18
  /** Дедлайн ожидания терминального состояния, мс (дефолт 10 мин). */
19
19
  maxWaitMs?: number | undefined;
20
20
  }
21
+ /**
22
+ * ОТВЕТ СЕРВЕРА НЕ РАЗОБРАЛСЯ — ЭТО НЕ ТО ЖЕ, ЧТО ОТКАЗ ВВОДА (US-622).
23
+ *
24
+ * Обе беды приходят из zod и до этого класса были неотличимы снаружи: и
25
+ * `makeUgcInput.parse` на входе, и `run.parse` на ответе бросали `ZodError`.
26
+ * Внешнее ревью (круг 9) показало цену этой неразличимости. CLI печатал любую
27
+ * такую ошибку как «запрос отвергнут ДО отправки» — а на битом ответе `POST
28
+ * /run` это ложь в самую дорогую сторону: запрос ушёл, ран создан и, возможно,
29
+ * уже оплачен, но человеку сказано, что ничего не произошло, и `run_id` он не
30
+ * получил.
31
+ *
32
+ * Поэтому провал разбора ОТВЕТА получает собственный класс и собственный текст,
33
+ * который прямо называет, мог ли вызов иметь последствия.
34
+ */
35
+ export declare class ClipwrightResponseError extends Error {
36
+ #private;
37
+ /** Путь, ответ которого не разобрался. */
38
+ readonly path: string;
39
+ /** Мог ли запрос уже что-то изменить (создать ран, потратить деньги). */
40
+ readonly mayHaveTakenEffect: boolean;
41
+ /**
42
+ * ПОДТВЕРЖДЕНА ли доставка запроса. Имя выбрано точно (внешнее ревью, круг 12):
43
+ * прежнее `delivered` читалось как «не доставлено», хотя `false` означает лишь
44
+ * ОТСУТСТВИЕ ПОДТВЕРЖДЕНИЯ. Отличить «не дошло» от «дошло, а ответ потерян»
45
+ * у HTTP способа нет, и потребитель, прочитавший поле как отрицание, запустил
46
+ * бы `attempt: 2` и оплатил второй рендер.
47
+ */
48
+ readonly deliveryConfirmed: boolean;
49
+ constructor(path: string, mayHaveTakenEffect: boolean, cause: unknown, deliveryConfirmed?: boolean);
50
+ }
21
51
  /** Один пресет каталога `/v1/voices`, с именем — как отдаёт GET /v1/voices. */
22
52
  export interface VoiceCatalogEntry extends VoicePreset {
23
53
  name: VoicePresetName;
24
54
  }
55
+ /**
56
+ * ОТКАЗ API, ДОЕХАВШИЙ ДО ВЫЗЫВАТЕЛЯ ЦЕЛИКОМ (US-622).
57
+ *
58
+ * ЧТО БЫЛО ДО. `#request` сводил любой не-2xx к `new Error(message)`: статус,
59
+ * код и назначенная сервером пауза терялись на границе SDK. Вызыватель мог
60
+ * только напечатать текст — отличить «подожди две секунды» от «пополни счёт»
61
+ * было нечем, и поллинг умирал на первом же 429.
62
+ *
63
+ * ЧЕГО ЗДЕСЬ НАМЕРЕННО НЕТ — СВОЕЙ ФОРМЫ ОТКАЗА. Класс не объявляет ни кодов,
64
+ * ни полей с числами: всё это лежит в `ApiFailure` из `@clipwright/core`, и
65
+ * `code`/`report()` выводятся оттуда же (правило проекта №2). Объяви SDK свою
66
+ * копию — и MCP с CLI получили бы вторую, а «повторять или нет» стало бы
67
+ * вопросом, на который три поверхности отвечают порознь.
68
+ */
69
+ export declare class ClipwrightApiError extends Error {
70
+ /** Разобранный отказ: вид, числа, пауза. Форма — из core. */
71
+ readonly failure: ApiFailure;
72
+ readonly status: number;
73
+ /** Код отказа. Для чужих тел без кода — `http_<status>`. */
74
+ readonly code: string;
75
+ /** Пауза в секундах, назначенная сервером. `undefined` — он её не назвал. */
76
+ readonly retryAfterSeconds: number | undefined;
77
+ constructor(failure: ApiFailure);
78
+ /** Имеет ли смысл повторить тот же вызов. Решает core, а не эта поверхность. */
79
+ get retryable(): boolean;
80
+ /** Отказ, развёрнутый для агента: числа, императив, признак повтора. */
81
+ report(): AgentFailureReport;
82
+ }
25
83
  export declare class ClipwrightClient {
26
84
  #private;
27
85
  constructor(opts: ClipwrightClientOptions);
@@ -64,4 +122,5 @@ export declare class ClipwrightClient {
64
122
  makeUgc(input: MakeUgcInputArgs, opts?: MakeUgcOptions, onProgress?: (run: RunRead) => void): Promise<RunRead>;
65
123
  }
66
124
  export { resolveClientId } from "./client-id.js";
125
+ export type { AgentFailureReport, ApiFailure };
67
126
  export type { MakeUgcInput, MakeUgcInputArgs, QuoteResponse, Run, RunRead };
package/dist/index.js CHANGED
@@ -1,10 +1,223 @@
1
- import { run as runSchema, runRead as runReadSchema, isTerminal, FAILED_STATE, quoteResponse, } from "@clipwright/core";
1
+ import { agentFailureReport, isRetryableFailure, parseApiFailure, retryDelayMs, run as runSchema, runRead as runReadSchema, isTerminal, FAILED_STATE, quoteResponse, RETRY_AFTER_HEADER, } from "@clipwright/core";
2
2
  import { idempotencyKeyFor } from "@clipwright/core/idempotency";
3
3
  import { resolveClientId } from "./client-id.js";
4
4
  /** Прод-API по умолчанию: SDK «просто работает» без явного baseUrl. */
5
5
  const DEFAULT_BASE_URL = "https://api-production-7e69.up.railway.app";
6
6
  /** Дефолтный дедлайн поллинга: ≥5× над проекцией рендера (~113 с, docs/13). */
7
7
  const DEFAULT_MAX_WAIT_MS = 600_000;
8
+ /**
9
+ * Потолок ОДНОГО `setTimeout` — 2^31−1 мс (примерно 24.8 суток).
10
+ *
11
+ * ЧТО БЫВАЕТ БЕЗ УЧЁТА ЭТОГО ПРЕДЕЛА, проверено запуском:
12
+ * `setTimeout(fn, 2_592_000_000)` (тридцать суток) печатает
13
+ * `TimeoutOverflowWarning` и стреляет ЧЕРЕЗ 0 мс. То есть самая длинная пауза
14
+ * превращается в её полное отсутствие, и клиент, которому сервер велел
15
+ * подождать тридцать суток, начинает долбить его без пауз.
16
+ *
17
+ * Живёт этот предел в SDK, а не в `@clipwright/core`, потому что он — свойство
18
+ * платформы, а не контракта: `retryDelayMs` обязан честно возвращать то, что
19
+ * назначил сервер.
20
+ */
21
+ const MAX_TIMER_DELAY_MS = 2_147_483_647;
22
+ /**
23
+ * Длительность в миллисекундах — или внятный отказ на входе.
24
+ *
25
+ * ОТКАЗ ГРОМКИЙ, А НЕ ТИХАЯ ПОДСТАНОВКА ДЕФОЛТА. `Number(process.env.POLL_MS)`
26
+ * на незаданной переменной даёт `NaN`, и молчаливая замена его пятью секундами
27
+ * скрыла бы от вызывателя, что его настройка не применилась. Здесь он узнаёт об
28
+ * этом на первой же строке, а не по странному поведению поллинга.
29
+ *
30
+ * `undefined` — законный вход: это «не задавал», и тогда берётся дефолт.
31
+ */
32
+ function positiveMs(value, fallback, name) {
33
+ if (value === undefined)
34
+ return fallback;
35
+ if (!Number.isFinite(value) || value <= 0) {
36
+ throw new TypeError(`${name} must be a positive number of milliseconds, got ${String(value)}`);
37
+ }
38
+ return value;
39
+ }
40
+ /**
41
+ * ОТВЕТ СЕРВЕРА НЕ РАЗОБРАЛСЯ — ЭТО НЕ ТО ЖЕ, ЧТО ОТКАЗ ВВОДА (US-622).
42
+ *
43
+ * Обе беды приходят из zod и до этого класса были неотличимы снаружи: и
44
+ * `makeUgcInput.parse` на входе, и `run.parse` на ответе бросали `ZodError`.
45
+ * Внешнее ревью (круг 9) показало цену этой неразличимости. CLI печатал любую
46
+ * такую ошибку как «запрос отвергнут ДО отправки» — а на битом ответе `POST
47
+ * /run` это ложь в самую дорогую сторону: запрос ушёл, ран создан и, возможно,
48
+ * уже оплачен, но человеку сказано, что ничего не произошло, и `run_id` он не
49
+ * получил.
50
+ *
51
+ * Поэтому провал разбора ОТВЕТА получает собственный класс и собственный текст,
52
+ * который прямо называет, мог ли вызов иметь последствия.
53
+ */
54
+ export class ClipwrightResponseError extends Error {
55
+ /** Путь, ответ которого не разобрался. */
56
+ path;
57
+ /** Мог ли запрос уже что-то изменить (создать ран, потратить деньги). */
58
+ mayHaveTakenEffect;
59
+ /**
60
+ * ПОДТВЕРЖДЕНА ли доставка запроса. Имя выбрано точно (внешнее ревью, круг 12):
61
+ * прежнее `delivered` читалось как «не доставлено», хотя `false` означает лишь
62
+ * ОТСУТСТВИЕ ПОДТВЕРЖДЕНИЯ. Отличить «не дошло» от «дошло, а ответ потерян»
63
+ * у HTTP способа нет, и потребитель, прочитавший поле как отрицание, запустил
64
+ * бы `attempt: 2` и оплатил второй рендер.
65
+ */
66
+ deliveryConfirmed;
67
+ constructor(path, mayHaveTakenEffect, cause, deliveryConfirmed = true) {
68
+ super(ClipwrightResponseError.#describe(path, mayHaveTakenEffect, deliveryConfirmed), { cause });
69
+ this.name = "ClipwrightResponseError";
70
+ this.path = path;
71
+ this.mayHaveTakenEffect = mayHaveTakenEffect;
72
+ this.deliveryConfirmed = deliveryConfirmed;
73
+ }
74
+ /**
75
+ * Три разных новости, а не одна (внешнее ревью, круги 9-11).
76
+ *
77
+ * Разница между ними — деньги. Сказать «ничего не произошло» там, где ран уже
78
+ * создан, значит подтолкнуть человека к `--retry 2`, то есть ко второму
79
+ * платному рендеру. Сказать «возможно, вы заплатили» на неудачном `quote`
80
+ * значит пугать без причины и обесценить предупреждение там, где оно нужно.
81
+ *
82
+ * НИ ОДИН ИЗ ТРЁХ ТЕКСТОВ НЕ НАЗЫВАЕТ ВИНОВНИКА. Ответ не по контракту с тем
83
+ * же успехом мог прислать шлюз или CDN, а не наш сервер; ровно за эту
84
+ * бездоказательную атрибуцию внешнее ревью сняло две прежние формулировки
85
+ * (круги 13 и 14), и здесь она повторена не будет.
86
+ */
87
+ static #describe(path, mayHaveTakenEffect, deliveryConfirmed) {
88
+ if (!mayHaveTakenEffect) {
89
+ return deliveryConfirmed
90
+ ? `The response to ${path} did not match the Clipwright contract. ` +
91
+ "Nothing was created; retrying is safe."
92
+ : `The request to ${path} failed before an answer arrived. Nothing was created; retrying is safe.`;
93
+ }
94
+ const tail = "Check your recent runs before retrying: a retry with a new idempotency key would pay twice.";
95
+ return deliveryConfirmed
96
+ ? `The response to ${path} did not match the Clipwright contract. ` +
97
+ `The request was already sent, so a run may have been created and charged. ${tail}`
98
+ : `The request to ${path} failed before an answer arrived, so it is unknown whether it was ` +
99
+ `delivered — a run may have been created and charged. ${tail}`;
100
+ }
101
+ }
102
+ function parseResponse(schema, body, path, mayHaveTakenEffect) {
103
+ try {
104
+ return schema.parse(body);
105
+ }
106
+ catch (cause) {
107
+ throw new ClipwrightResponseError(path, mayHaveTakenEffect, cause);
108
+ }
109
+ }
110
+ /**
111
+ * Номер попытки → суффикс ключа идемпотентности. С отказом на мусоре.
112
+ *
113
+ * ЗДЕСЬ ПРОХОДИТ ДЕНЕЖНАЯ ГРАНИЦА, и молчаливого прохода на ней быть не должно.
114
+ * Суффикс участвует в ключе, а РАЗНЫЙ ключ — это РАЗНЫЙ платный рендер. Пока
115
+ * проверки не было, `?? 1` ловил только `null`/`undefined`, и `NaN` доезжал
116
+ * до ключа как строка `:NaN`: ключ отличался от `:1`, сервер видел новый
117
+ * запрос и запускал вторую платную генерацию. Внешнее ревью (круг 5) нашло
118
+ * живой путь до этого входа — `clipwright make --retry banana`, где
119
+ * `parseInt("banana")` даёт `NaN`.
120
+ *
121
+ * Отказ громкий, потому что тихий здесь стоит денег: пользователь, опечатавшийся
122
+ * в номере попытки, получит сообщение, а не счёт.
123
+ */
124
+ function attemptSuffix(attempt) {
125
+ if (attempt === undefined)
126
+ return 1;
127
+ // `isSafeInteger`, а НЕ `isInteger` (внешнее ревью, круг 7). За пределом
128
+ // безопасного диапазона разные числа сливаются в одно: `9007199254740993`
129
+ // становится `…992` (проверено запуском). Два РАЗНЫХ номера попытки дали бы
130
+ // один суффикс, то есть один ключ, — и запрошенный новый платный рендер молча
131
+ // вернул бы прежний ран.
132
+ if (!Number.isSafeInteger(attempt) || attempt < 1) {
133
+ throw new TypeError(`attempt must be a positive integer (1, 2, 3, …), got ${String(attempt)}. ` +
134
+ "It selects which paid run to deduplicate against, so a wrong value can start a second render.");
135
+ }
136
+ return attempt;
137
+ }
138
+ /**
139
+ * Сон произвольной длины — ЦЕПОЧКОЙ таймеров, а не одним обрезанным.
140
+ *
141
+ * ПОЧЕМУ НЕ ПРОСТО ОБРЕЗАТЬ. Первая редакция ограничивала сон потолком выше и
142
+ * объясняла это тем, что цикл всё равно перепроверит условия. Внешнее ревью
143
+ * (круг 2) показало, что объяснение неверно: сразу после сна цикл делает
144
+ * `getRun()`, поэтому обрезка — это не «более ранняя перепроверка», а более
145
+ * ранний ЗАПРОС. На паузе в тридцать суток второй запрос уходил бы через 24.86
146
+ * суток — на пять суток раньше разрешённого сервером времени, то есть ровно то
147
+ * нарушение, ради недопущения которого пауза и соблюдается.
148
+ *
149
+ * Цепочка сохраняет и предел платформы, и назначенную длительность.
150
+ */
151
+ function sleep(ms) {
152
+ return new Promise((resolve) => {
153
+ // НЕ-ЧИСЛО ОБЯЗАНО ЗАВЕРШИТЬ СОН, А НЕ ПРОДЛИТЬ ЕГО НАВСЕГДА (внешнее ревью,
154
+ // круг 3). С `NaN` условие `left <= 0` ложно ВСЕГДА, `setTimeout(tick, NaN)`
155
+ // Node трактует как одну миллисекунду, и цикл планирует таймеры до
156
+ // скончания века: промис не завершается, дедлайн больше не проверяется, а
157
+ // таймер держит процесс Node живым — `clipwright make` не вернул бы
158
+ // управление вовсе. Сюда `NaN` попадает из `pollIntervalMs`, вычисленного
159
+ // вызывателем (`Number(process.env.…)`); конструктор такой ввод теперь
160
+ // отвергает, но защита стоит и здесь: у цикла с таймерами цена ошибки —
161
+ // зависший процесс, а не неверное число.
162
+ let left = Number.isFinite(ms) ? Math.max(ms, 0) : 0;
163
+ const tick = () => {
164
+ if (left <= 0) {
165
+ resolve();
166
+ return;
167
+ }
168
+ const step = Math.min(left, MAX_TIMER_DELAY_MS);
169
+ left -= step;
170
+ setTimeout(tick, step);
171
+ };
172
+ tick();
173
+ });
174
+ }
175
+ /**
176
+ * ОТКАЗ API, ДОЕХАВШИЙ ДО ВЫЗЫВАТЕЛЯ ЦЕЛИКОМ (US-622).
177
+ *
178
+ * ЧТО БЫЛО ДО. `#request` сводил любой не-2xx к `new Error(message)`: статус,
179
+ * код и назначенная сервером пауза терялись на границе SDK. Вызыватель мог
180
+ * только напечатать текст — отличить «подожди две секунды» от «пополни счёт»
181
+ * было нечем, и поллинг умирал на первом же 429.
182
+ *
183
+ * ЧЕГО ЗДЕСЬ НАМЕРЕННО НЕТ — СВОЕЙ ФОРМЫ ОТКАЗА. Класс не объявляет ни кодов,
184
+ * ни полей с числами: всё это лежит в `ApiFailure` из `@clipwright/core`, и
185
+ * `code`/`report()` выводятся оттуда же (правило проекта №2). Объяви SDK свою
186
+ * копию — и MCP с CLI получили бы вторую, а «повторять или нет» стало бы
187
+ * вопросом, на который три поверхности отвечают порознь.
188
+ */
189
+ export class ClipwrightApiError extends Error {
190
+ /** Разобранный отказ: вид, числа, пауза. Форма — из core. */
191
+ failure;
192
+ status;
193
+ /** Код отказа. Для чужих тел без кода — `http_<status>`. */
194
+ code;
195
+ /** Пауза в секундах, назначенная сервером. `undefined` — он её не назвал. */
196
+ retryAfterSeconds;
197
+ constructor(failure) {
198
+ super(failure.message);
199
+ this.name = "ClipwrightApiError";
200
+ this.failure = failure;
201
+ this.status = failure.status;
202
+ // И КОД, И ПАУЗА БЕРУТСЯ ИЗ ОБЩЕГО РАЗВЁРТЫВАНИЯ, а не выводятся здесь по
203
+ // второму разу. Разъехавшись, `error.code` в CLI и `code` в ответе MCP
204
+ // говорили бы об одном отказе разными словами. Плюс следствие, которое
205
+ // поймал сканер источников: перечисляя виды отказа тут (`failure.kind ===
206
+ // …`), эта поверхность заводила бы у себя копию их названий — то есть
207
+ // ровно тот второй источник, который правило проекта №2 запрещает.
208
+ const report = agentFailureReport(failure);
209
+ this.code = report.code;
210
+ this.retryAfterSeconds = report.retry_after_seconds;
211
+ }
212
+ /** Имеет ли смысл повторить тот же вызов. Решает core, а не эта поверхность. */
213
+ get retryable() {
214
+ return isRetryableFailure(this.failure);
215
+ }
216
+ /** Отказ, развёрнутый для агента: числа, императив, признак повтора. */
217
+ report() {
218
+ return agentFailureReport(this.failure);
219
+ }
220
+ }
8
221
  export class ClipwrightClient {
9
222
  #apiKey;
10
223
  #baseUrl;
@@ -20,7 +233,7 @@ export class ClipwrightClient {
20
233
  constructor(opts) {
21
234
  this.#apiKey = opts.apiKey;
22
235
  this.#baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;
23
- this.#pollIntervalMs = opts.pollIntervalMs ?? 5_000;
236
+ this.#pollIntervalMs = positiveMs(opts.pollIntervalMs, 5_000, "pollIntervalMs");
24
237
  }
25
238
  /** Ленивый резолв + кэш идентичности установки (см. `#clientId`). */
26
239
  #getClientId() {
@@ -29,28 +242,95 @@ export class ClipwrightClient {
29
242
  }
30
243
  return this.#clientId;
31
244
  }
32
- async #request(path, init) {
33
- const res = await fetch(`${this.#baseUrl}${path}`, {
34
- ...init,
35
- headers: {
36
- Authorization: `Bearer ${this.#apiKey}`,
37
- "Content-Type": "application/json",
38
- ...init?.headers,
39
- },
40
- });
41
- const body = (await res.json());
245
+ /**
246
+ * Один запрос к API.
247
+ *
248
+ * ТЕЛО ЧИТАЕТСЯ ТЕКСТОМ, А НЕ `res.json()`, и это не стилистика (US-622).
249
+ * `res.json()` на не-JSON бросает `SyntaxError` ДО того, как мы посмотрели на
250
+ * статус, — и вызыватель видит «Unexpected token <» вместо «слишком часто».
251
+ * Отдать HTML-страницу вместо нашего тела может любой прокси между клиентом и
252
+ * API: edge тарифа, корпоративный шлюз, страница обслуживания. Разбор текста
253
+ * в `parseApiFailure` эту ветку закрывает, потому что решение принимается по
254
+ * статусу, а тело лишь уточняет причину.
255
+ */
256
+ async #request(path,
257
+ /**
258
+ * Мог ли ЭТОТ вызов что-то изменить на сервере. Знает это только вызыватель,
259
+ * и знание обязано дойти сюда: провал разбора успешного ответа на `POST
260
+ * /run` и на `GET /voices` — разные новости, и молчать о разнице дороже
261
+ * всего именно там, где ран уже мог быть создан и оплачен.
262
+ */
263
+ mayHaveTakenEffect, init) {
264
+ // ВСЁ, ЧТО МОЖЕТ УПАСТЬ ПОСЛЕ ОТПРАВКИ, ЛОВИТСЯ ОДИНАКОВО (внешнее ревью,
265
+ // круг 11). Обрыв на чтении тела бросал голый `TypeError` мимо
266
+ // предупреждения про деньги — то есть ровно там, где ран уже создан, клиент
267
+ // молчал о последствиях. Сюда же отнесён и провал самого `fetch`: доставку
268
+ // он не опровергает, и на платном пути честнее сказать «неизвестно».
269
+ let res;
270
+ try {
271
+ res = await fetch(`${this.#baseUrl}${path}`, {
272
+ ...init,
273
+ headers: {
274
+ Authorization: `Bearer ${this.#apiKey}`,
275
+ "Content-Type": "application/json",
276
+ ...init?.headers,
277
+ },
278
+ });
279
+ }
280
+ catch (cause) {
281
+ // На бесплатных путях сетевой сбой остаётся собой: своё сообщение (DNS,
282
+ // отказ соединения) полезнее нашей обёртки, а денег там нет.
283
+ if (!mayHaveTakenEffect)
284
+ throw cause;
285
+ throw new ClipwrightResponseError(path, true, cause, false);
286
+ }
287
+ // ЗАГОЛОВКИ ЧИТАЮТСЯ ДО ТЕЛА, потому что тело может и не дочитаться.
288
+ const retryAfterHeader = res.headers.get(RETRY_AFTER_HEADER);
289
+ let bodyText;
290
+ try {
291
+ bodyText = await res.text();
292
+ }
293
+ catch (cause) {
294
+ // ОБРЫВ ЧТЕНИЯ НЕ СМЕЕТ СТИРАТЬ УЖЕ ИЗВЕСТНЫЙ СТАТУС (внешнее ревью,
295
+ // круг 12). Первая редакция объявляла любой такой обрыв нарушением
296
+ // контракта — и 429 на поллинге переставал быть ретрайным: `makeUgc`
297
+ // бросал уже оплаченный ран вместо того, чтобы выждать паузу. Статус и
298
+ // заголовки к этому моменту получены целиком, их и достаточно.
299
+ if (!res.ok) {
300
+ throw new ClipwrightApiError(
301
+ // `undefined`, а не пустая строка: тело не пустое, оно НЕДОСТУПНО.
302
+ parseApiFailure({ status: res.status, bodyText: undefined, retryAfterHeader }));
303
+ }
304
+ throw new ClipwrightResponseError(path, mayHaveTakenEffect, cause);
305
+ }
42
306
  if (!res.ok) {
43
- throw new Error(body.error?.message ?? `HTTP ${res.status} on ${path}`);
307
+ throw new ClipwrightApiError(parseApiFailure({
308
+ status: res.status,
309
+ bodyText,
310
+ retryAfterHeader,
311
+ }));
312
+ }
313
+ try {
314
+ return JSON.parse(bodyText);
315
+ }
316
+ catch (cause) {
317
+ // ТОТ ЖЕ КЛАСС, ЧТО И У ПРОВАЛА СХЕМЫ (внешнее ревью, круг 10). Первая
318
+ // редакция бросала здесь обычный `Error`, и он проскакивал МИМО
319
+ // предупреждения «ран мог быть создан и оплачен»: сервер, ответивший на
320
+ // `POST /run` пустым телом или HTML, оставлял человека в уверенности, что
321
+ // ничего не произошло, — и следующий шаг `--retry 2` оплачивал второй
322
+ // рендер. Тело не-JSON и тело не по схеме — одна и та же беда, «ответ не
323
+ // соответствует контракту», и говорить о ней надо одинаково.
324
+ throw new ClipwrightResponseError(path, mayHaveTakenEffect, cause);
44
325
  }
45
- return body;
46
326
  }
47
327
  /** Оценка стоимости без списания кредитов. */
48
328
  async quoteUgc(input) {
49
- const body = await this.#request("/v1/skills/make_ugc/quote", {
329
+ const body = await this.#request("/v1/skills/make_ugc/quote", false, {
50
330
  method: "POST",
51
331
  body: JSON.stringify(input),
52
332
  });
53
- return quoteResponse.parse(body);
333
+ return parseResponse(quoteResponse, body, "/v1/skills/make_ugc/quote", false);
54
334
  }
55
335
  /**
56
336
  * Запуск генерации; возвращает ран сразу (state=queued).
@@ -60,14 +340,15 @@ export class ClipwrightClient {
60
340
  * `<hash>`, а CLI `<hash>:1` — межповерхностный шов, в котором дедуп ломается.
61
341
  */
62
342
  async startUgc(input, opts) {
63
- const key = opts?.idempotencyKey ??
64
- `${idempotencyKeyFor(input, this.#getClientId())}:${opts?.attempt ?? 1}`;
65
- const body = await this.#request("/v1/skills/make_ugc/run", {
343
+ const key = opts?.idempotencyKey ?? `${idempotencyKeyFor(input, this.#getClientId())}:${attemptSuffix(opts?.attempt)}`;
344
+ // `true`: запрос уже ушёл, ран мог быть создан и оплачен.
345
+ const body = await this.#request("/v1/skills/make_ugc/run", true, {
66
346
  method: "POST",
67
347
  body: JSON.stringify(input),
68
348
  headers: { "Idempotency-Key": key },
69
349
  });
70
- return runSchema.parse(body);
350
+ // `hasSideEffect: true` — запрос уже ушёл и ран мог быть создан и оплачен.
351
+ return parseResponse(runSchema, body, "/v1/skills/make_ugc/run", true);
71
352
  }
72
353
  /**
73
354
  * Курируемый каталог голосов для `voice`/`voice_id` (US-516). Отдаёт то же,
@@ -76,8 +357,16 @@ export class ClipwrightClient {
76
357
  * не полный список голосов вендора — остальные достижимы сырым `voice_id`.
77
358
  */
78
359
  async listVoices() {
79
- const body = (await this.#request("/v1/voices"));
80
- return body.voices;
360
+ const body = await this.#request("/v1/voices", false);
361
+ // Форма проверяется, а не предполагается: голое приведение типа отдавало бы
362
+ // `undefined` как массив, и падал бы уже вызыватель — на `.map`, вдали от
363
+ // причины. Каталог статический, отдельной схемы у него нет, поэтому
364
+ // проверяется то единственное, на что опирается потребитель.
365
+ const voices = body.voices;
366
+ if (!Array.isArray(voices)) {
367
+ throw new ClipwrightResponseError("/v1/voices", false, body);
368
+ }
369
+ return voices;
81
370
  }
82
371
  /**
83
372
  * Чтение рана в ТОЛЕРАНТНОЙ проекции (`runRead`, `state: string`): сервер мог
@@ -89,7 +378,7 @@ export class ClipwrightClient {
89
378
  * приняли бы `state` за закрытый enum.
90
379
  */
91
380
  async getRun(runId) {
92
- const body = await this.#request(`/v1/runs/${runId}`);
381
+ const body = await this.#request(`/v1/runs/${runId}`, false);
93
382
  const parsed = runReadSchema.safeParse(body);
94
383
  return parsed.success ? parsed.data : body;
95
384
  }
@@ -103,23 +392,54 @@ export class ClipwrightClient {
103
392
  * бросок случался вовремя, а не через целый интервал поллинга.
104
393
  */
105
394
  async makeUgc(input, opts, onProgress) {
106
- const maxWaitMs = opts?.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;
395
+ const maxWaitMs = positiveMs(opts?.maxWaitMs, DEFAULT_MAX_WAIT_MS, "maxWaitMs");
107
396
  const deadline = Date.now() + maxWaitMs;
108
397
  // Начальный ран приходит строгим `Run` из `startUgc`; расширяем тип до
109
398
  // `RunRead`, т.к. дальше присваиваем сюда толерантный результат `getRun`.
110
399
  let current = await this.startUgc(input, { attempt: opts?.attempt });
400
+ // Пауза перед СЛЕДУЮЩИМ опросом. Обычно это интервал поллинга; отбитый
401
+ // опрос заменяет её паузой, назначенной сервером (US-622).
402
+ let nextDelayMs = this.#pollIntervalMs;
403
+ // Последний отбитый опрос — только чтобы объяснить таймаут. Ран, который
404
+ // упёрся в дедлайн из-за 429, и ран, который просто долго рендерится, — это
405
+ // разные новости, и человеку они нужны разными.
406
+ let lastRetried;
111
407
  // Терминальность — через разделяемый `isTerminal`, а не литералы: неизвестный
112
408
  // ТЕРМИНАЛЬНЫЙ статус сервера (`isTerminal=false`) осознанно поллится до
113
409
  // `maxWaitMs` и даёт чистый таймаут с `run_id`, а не вечный цикл (PM4).
114
410
  while (!isTerminal(current.state)) {
411
+ // ДЕДЛАЙН ОДИН НА ВЕСЬ ВЫЗОВ И НЕ СБРАСЫВАЕТСЯ РЕТРАЕМ. `deadline`
412
+ // посчитан ДО цикла и здесь только читается: пересчитай его при отказе —
413
+ // и цепочка 429 продлевала бы ожидание бесконечно, ровно вопреки тому,
414
+ // ради чего `maxWaitMs` существует (US-207).
115
415
  const remaining = deadline - Date.now();
116
416
  if (remaining <= 0) {
117
417
  throw new Error(`makeUgc timed out after ${maxWaitMs}ms; run ${current.run_id} is still ` +
118
- `${current.state}. Poll it later with getRun("${current.run_id}").`);
418
+ `${current.state}. Poll it later with getRun("${current.run_id}").` +
419
+ (lastRetried === undefined
420
+ ? ""
421
+ : ` The last status check was refused with HTTP ${lastRetried.status}: ` +
422
+ `${lastRetried.message}`));
423
+ }
424
+ await sleep(Math.min(nextDelayMs, remaining));
425
+ try {
426
+ current = await this.getRun(current.run_id);
427
+ nextDelayMs = this.#pollIntervalMs;
428
+ onProgress?.(current);
429
+ }
430
+ catch (error) {
431
+ // ОТБИТЫЙ ОПРОС — НЕ ПРОВАЛ РАНА. Ран уже запущен и уже оплачен: 429 на
432
+ // `GET /v1/runs/:id` до US-622 убивал `makeUgc` целиком, то есть бросал
433
+ // работу, за которую заплатили. Повторяем — но только то, что повторять
434
+ // ИМЕЕТ СМЫСЛ, и решает это core: 402 и прочие 4xx летят наружу
435
+ // немедленно, потому что повтор даст тот же ответ, а агент тем временем
436
+ // выест лимит частоты вместо того, чтобы сказать человеку правду.
437
+ if (!(error instanceof ClipwrightApiError) || !isRetryableFailure(error.failure)) {
438
+ throw error;
439
+ }
440
+ lastRetried = error;
441
+ nextDelayMs = retryDelayMs(error.failure, this.#pollIntervalMs);
119
442
  }
120
- await new Promise((r) => setTimeout(r, Math.min(this.#pollIntervalMs, remaining)));
121
- current = await this.getRun(current.run_id);
122
- onProgress?.(current);
123
443
  }
124
444
  // Терминально: различение исхода — именованной константой core (не
125
445
  // позиционным индексом массива: переупорядочивание молча инвертировало бы
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@clipwright/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "TypeScript client for the Clipwright UGC video API",
5
5
  "license": "MIT",
6
- "author": "Dimantika LLC",
6
+ "author": "Dimantika Sp. z o.o.",
7
7
  "homepage": "https://clipwright.io",
8
8
  "type": "module",
9
9
  "publishConfig": {
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "zod": "^4.4.3",
26
- "@clipwright/core": "0.1.0"
26
+ "@clipwright/core": "0.2.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^26.1.1"