@clipwright/cli 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 +21 -0
- package/README.md +35 -0
- package/dist/format.js +92 -0
- package/dist/format.js.map +1 -0
- package/dist/index.js +141 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dimantika Sp. z o.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @clipwright/cli
|
|
2
|
+
|
|
3
|
+
Command-line client for the [Clipwright](https://clipwright.io) UGC video API.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
export CLIPWRIGHT_API_KEY=cw_...
|
|
7
|
+
|
|
8
|
+
npx -y -p @clipwright/cli clipwright quote --script "Hello from Clipwright."
|
|
9
|
+
npx -y -p @clipwright/cli clipwright make --script "Hello from Clipwright."
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Command | What it does |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `quote --script <text>` | Estimates the credit cost. Spends nothing. |
|
|
15
|
+
| `make --script <text>` | Renders a clip and waits for it. **Costs credits.** Prints the video URL to stdout, progress and warnings to stderr. |
|
|
16
|
+
| `runs <id>` | Prints the state of one run. |
|
|
17
|
+
| `voices` | Lists the curated voice catalog for `--voice`. |
|
|
18
|
+
|
|
19
|
+
Pick a voice with `--voice <name>` from that catalog, or `--voice-id <id>` for a
|
|
20
|
+
vendor voice outside it (a cloned one, for instance). Set neither and the default
|
|
21
|
+
voice already matched to the avatar is used.
|
|
22
|
+
|
|
23
|
+
`make` retries are explicit: `--retry 2` deliberately starts a *new* run for the
|
|
24
|
+
same script. Without it a repeated call returns the run you already paid for,
|
|
25
|
+
because every request carries an idempotency key derived from the input and from
|
|
26
|
+
this installation's id. A non-numeric `--retry` is rejected rather than coerced:
|
|
27
|
+
the value ends up in that key, so a silent `NaN` would buy you a second render.
|
|
28
|
+
|
|
29
|
+
Set `CLIPWRIGHT_CLIENT_ID` to a stable per-install value, or leave `~/.clipwright/`
|
|
30
|
+
writable so the client can create one on first use. It refuses to invent a
|
|
31
|
+
throwaway id, because a per-process id would disable duplicate protection
|
|
32
|
+
silently — and the duplicate it fails to catch is a second paid render.
|
|
33
|
+
|
|
34
|
+
`CLIPWRIGHT_API_URL` overrides the API base URL; the default points at
|
|
35
|
+
production.
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { FAILED_STATE, isTerminal } from "@clipwright/core";
|
|
2
|
+
/**
|
|
3
|
+
* Чистый форматтер вывода `clipwright runs <id>` — вынесен из команды, чтобы
|
|
4
|
+
* тестировать терминальные/нетерминальные/неизвестные стадии без сети
|
|
5
|
+
* (US-502, заметка Critic №1: выделенный тест CLI-поверхности, а не только
|
|
6
|
+
* транзитивное покрытие через SDK).
|
|
7
|
+
*
|
|
8
|
+
* Терминальность определяется через `isTerminal`/`FAILED_STATE` из core, а
|
|
9
|
+
* не строковыми литералами: неизвестная стадия (расширение enum на сервере,
|
|
10
|
+
* напр. `"publishing"`) просто печатается как есть и НЕ роняет команду.
|
|
11
|
+
*/
|
|
12
|
+
export function formatRun(run) {
|
|
13
|
+
const lines = [];
|
|
14
|
+
if (isTerminal(run.state)) {
|
|
15
|
+
const outcome = run.state === FAILED_STATE ? "FAILED" : "SUCCEEDED";
|
|
16
|
+
lines.push(`run ${run.run_id}: ${outcome} (state: ${run.state})`);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
lines.push(`run ${run.run_id}: state: ${run.state}`);
|
|
20
|
+
}
|
|
21
|
+
if (run.final_output?.video_url) {
|
|
22
|
+
lines.push(`video_url: ${run.final_output.video_url}`);
|
|
23
|
+
}
|
|
24
|
+
if (run.warnings.length > 0) {
|
|
25
|
+
lines.push(`warnings: ${run.warnings.join(", ")}`);
|
|
26
|
+
}
|
|
27
|
+
if (run.error) {
|
|
28
|
+
lines.push(`error: ${run.error}`);
|
|
29
|
+
}
|
|
30
|
+
return lines.join("\n");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Чистый форматтер `clipwright voices` (US-516) — тестируемый без сети, тот
|
|
34
|
+
* же паттерн, что `formatRun`. Каталог курируемый по дизайну (план §5): это
|
|
35
|
+
* не полный список голосов вендора, остальные достижимы сырым `voice_id`.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Отказ API для ЧЕЛОВЕКА (US-622) — из того же развёртывания, что видит агент.
|
|
39
|
+
*
|
|
40
|
+
* ЧИСЛА БЕРУТСЯ ИЗ `report()`, А НЕ СОБИРАЮТСЯ ЗДЕСЬ ЗАНОВО. Соблазн написать
|
|
41
|
+
* `Balance: ${err.failure.balanceCredits}` велик и стоил бы третьего источника
|
|
42
|
+
* правды о том, что значит отказ: MCP говорил бы агенту одно, CLI человеку —
|
|
43
|
+
* другое, и разошлись бы они молча, при первой же правке формулировки.
|
|
44
|
+
*
|
|
45
|
+
* ЧТО ДОБАВЛЯЕТ CLI СВЕРХ ОТЧЁТА — только оформление: код отказа отдельной
|
|
46
|
+
* строкой (человек ищет его в доках) и `next_action` как совет. Стек-трейс не
|
|
47
|
+
* печатается: у отказа сервера он показывает наши внутренности вместо ответа на
|
|
48
|
+
* вопрос «что делать».
|
|
49
|
+
*/
|
|
50
|
+
export function formatApiError(error) {
|
|
51
|
+
const report = error.report();
|
|
52
|
+
const lines = [`error: ${report.message}`, `code: ${report.code} (HTTP ${error.status})`];
|
|
53
|
+
if (report.next_action)
|
|
54
|
+
lines.push(report.next_action);
|
|
55
|
+
return lines.join("\n");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Отказ СХЕМЫ для человека (US-622, внешнее ревью круга 8).
|
|
59
|
+
*
|
|
60
|
+
* Вход отвергается схемой ещё до сети: `--script ""` короче минимума,
|
|
61
|
+
* `--voice-id nosuch` не проходит формат вендорского идентификатора. До этой
|
|
62
|
+
* ветки такой отказ долетал до пользователя необработанным `ZodError` — стеком
|
|
63
|
+
* с путями `core/dist/idempotency.js` и внутренностями регулярок. Человек
|
|
64
|
+
* спрашивал про видео, а получал наши кадры стека.
|
|
65
|
+
*
|
|
66
|
+
* ОПОЗНАЁТСЯ ПО ИМЕНИ, А НЕ ЧЕРЕЗ `instanceof`, и это не небрежность. `zod`
|
|
67
|
+
* приходит сюда транзитивно через два пакета, и при разных копиях модуля в
|
|
68
|
+
* дереве `instanceof` вернул бы `false` — проверка молча перестала бы работать
|
|
69
|
+
* ровно там, где её труднее всего заметить: у потребителя после установки, а не
|
|
70
|
+
* у нас. Имя класса и форма `issues` — часть публичного контракта zod.
|
|
71
|
+
*/
|
|
72
|
+
export function formatSchemaError(error) {
|
|
73
|
+
if (!(error instanceof Error) || error.name !== "ZodError")
|
|
74
|
+
return undefined;
|
|
75
|
+
const issues = error.issues;
|
|
76
|
+
if (!Array.isArray(issues) || issues.length === 0)
|
|
77
|
+
return undefined;
|
|
78
|
+
const lines = issues.map((issue) => {
|
|
79
|
+
const where = Array.isArray(issue.path) && issue.path.length > 0 ? issue.path.join(".") : "input";
|
|
80
|
+
return ` ${where}: ${issue.message ?? "invalid"}`;
|
|
81
|
+
});
|
|
82
|
+
return ["error: the request was rejected before it was sent", ...lines].join("\n");
|
|
83
|
+
}
|
|
84
|
+
export function formatVoices(voices) {
|
|
85
|
+
return voices
|
|
86
|
+
.map((v) => {
|
|
87
|
+
const gender = v.gender ? `, ${v.gender}` : "";
|
|
88
|
+
return `${v.name} (${v.language}${gender}): ${v.description}`;
|
|
89
|
+
})
|
|
90
|
+
.join("\n");
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Option } from "commander";
|
|
3
|
+
import { VOICE_PRESET_NAMES } from "@clipwright/core";
|
|
4
|
+
import { ClipwrightApiError, ClipwrightClient, ClipwrightResponseError } from "@clipwright/sdk";
|
|
5
|
+
import { formatApiError, formatRun, formatSchemaError, formatVoices } from "./format.js";
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program.name("clipwright").description("UGC video platform CLI").version("0.2.0");
|
|
8
|
+
function client() {
|
|
9
|
+
const apiKey = process.env.CLIPWRIGHT_API_KEY;
|
|
10
|
+
if (!apiKey) {
|
|
11
|
+
console.error("CLIPWRIGHT_API_KEY env var is required (login flow lands in stage C)");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
return new ClipwrightClient({ apiKey, baseUrl: process.env.CLIPWRIGHT_API_URL });
|
|
15
|
+
}
|
|
16
|
+
program
|
|
17
|
+
.command("quote")
|
|
18
|
+
.description("estimate credits for a script without spending them")
|
|
19
|
+
.requiredOption("--script <text>", "the spoken script")
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const quote = await client().quoteUgc({ script: opts.script });
|
|
22
|
+
console.log(JSON.stringify(quote, null, 2));
|
|
23
|
+
});
|
|
24
|
+
program
|
|
25
|
+
.command("make")
|
|
26
|
+
.description("generate a UGC video and wait for the result")
|
|
27
|
+
.requiredOption("--script <text>", "the spoken script")
|
|
28
|
+
.option("--person <description>", "who says it, in words")
|
|
29
|
+
// `--character` СНЯТ вместе с прокидыванием: диспозиция поля — `rejected`
|
|
30
|
+
// (реестр в @clipwright/core), сервер отбивает его четырёхсотым. Флаг,
|
|
31
|
+
// оставшийся в `--help`, предлагал бы пользователю то, за что мы его
|
|
32
|
+
// накажем (блокер B4). Вернётся вместе с персистентными персонажами.
|
|
33
|
+
.option("--captions", "burn in word-synced captions")
|
|
34
|
+
// Каталог пресетов даёт `clipwright voices`. Без этих двух флагов команда
|
|
35
|
+
// `voices` предлагала бы выбор, которым нельзя воспользоваться (нашло внешнее
|
|
36
|
+
// ревью круга 5): её собственное описание ссылалось на `--voice`, а флага не
|
|
37
|
+
// было. `--voice-id` — escape hatch для голосов вне каталога.
|
|
38
|
+
//
|
|
39
|
+
// Имя пресета сверяется С КАТАЛОГОМ ЗДЕСЬ, а не только сервером: опечатка
|
|
40
|
+
// ловится до сетевого вызова, и человек сразу видит список допустимых имён.
|
|
41
|
+
// Множество берётся из `@clipwright/core`, поэтому новый пресет доезжает сюда
|
|
42
|
+
// сам (правило проекта №2).
|
|
43
|
+
.option("--voice <name>", `curated voice preset: ${VOICE_PRESET_NAMES.join(", ")}`, (raw) => {
|
|
44
|
+
if (!VOICE_PRESET_NAMES.includes(raw)) {
|
|
45
|
+
console.error(`unknown --voice ${raw}; run "clipwright voices" for the catalog`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
return raw;
|
|
49
|
+
})
|
|
50
|
+
// Взаимную исключительность ловит Commander, а НЕ сервер (внешнее ревью,
|
|
51
|
+
// круг 6). Пропусти мы оба флага дальше — `makeUgcInput.parse` отверг бы их
|
|
52
|
+
// `ZodError`, который CLI не обрабатывает, и человек вместо «эти два флага
|
|
53
|
+
// несовместимы» получил бы стек.
|
|
54
|
+
.addOption(new Option("--voice-id <id>", "raw vendor voice id, for voices outside the catalog").conflicts("voice"))
|
|
55
|
+
// --retry — НОМЕР попытки (не булев флаг): булев упёрся бы в attempt=2 навсегда
|
|
56
|
+
// и второй ретрай вернул бы тот же ран.
|
|
57
|
+
//
|
|
58
|
+
// РАЗБОР СТРОГИЙ, И ЭТО ДЕНЕЖНЫЙ ВОПРОС. Прежний `parseInt` молча возвращал
|
|
59
|
+
// `NaN` на `--retry banana` и `2` на `--retry 2junk`. `NaN` доезжал до
|
|
60
|
+
// суффикса ключа идемпотентности, ключ отличался от обычного, и сервер
|
|
61
|
+
// запускал ВТОРОЙ платный рендер вместо того, чтобы отбить опечатку.
|
|
62
|
+
.option("--retry <n>", "retry attempt number (forces a fresh run)", (raw) => {
|
|
63
|
+
// ПРОВЕРЯЕТСЯ И ФОРМА ЗАПИСИ, И САМО ЧИСЛО — граница здесь ровно та же, что
|
|
64
|
+
// в `attemptSuffix` у SDK, и разъехаться им нельзя: расхождение означает
|
|
65
|
+
// стек вместо отказа.
|
|
66
|
+
// `[1-9]` в начале, а не `\d` — иначе ноль проезжал регулярку и падал уже
|
|
67
|
+
// в SDK (внешнее ревью, круг 6);
|
|
68
|
+
// `isSafeInteger` после разбора — иначе строка из трёхсот девяток давала
|
|
69
|
+
// `Infinity`, а соседние огромные целые сливались в одно и переставали
|
|
70
|
+
// различать попытки (круг 7).
|
|
71
|
+
const parsed = Number.parseInt(raw, 10);
|
|
72
|
+
if (!/^[1-9]\d*$/.test(raw) || !Number.isSafeInteger(parsed)) {
|
|
73
|
+
console.error(`--retry expects a whole number of 1 or more, got ${raw}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
})
|
|
78
|
+
.action(async (opts) => {
|
|
79
|
+
const run = await client().makeUgc({
|
|
80
|
+
script: opts.script,
|
|
81
|
+
person: opts.person,
|
|
82
|
+
captions: opts.captions ?? false,
|
|
83
|
+
voice: opts.voice,
|
|
84
|
+
voice_id: opts.voiceId,
|
|
85
|
+
}, { attempt: opts.retry }, (r) => console.error(`state: ${r.state}`));
|
|
86
|
+
// Warnings — в stderr, ролик — в stdout: несоблюдённый параметр обязан
|
|
87
|
+
// быть виден человеку, но не должен попадать в пайп, из которого читают
|
|
88
|
+
// URL. Молча их проглатывать нельзя (правило проекта №3): CLI —
|
|
89
|
+
// единственная поверхность, где их больше никто не покажет.
|
|
90
|
+
for (const warning of run.warnings) {
|
|
91
|
+
console.error(`warning: ${warning}`);
|
|
92
|
+
}
|
|
93
|
+
console.log(run.final_output?.video_url);
|
|
94
|
+
});
|
|
95
|
+
program
|
|
96
|
+
.command("runs <id>")
|
|
97
|
+
.description("check the status of a run")
|
|
98
|
+
.action(async (id) => {
|
|
99
|
+
const run = await client().getRun(id);
|
|
100
|
+
console.log(formatRun(run));
|
|
101
|
+
});
|
|
102
|
+
program
|
|
103
|
+
.command("voices")
|
|
104
|
+
.description("list the curated voice catalog for --voice on make")
|
|
105
|
+
.action(async () => {
|
|
106
|
+
const voices = await client().listVoices();
|
|
107
|
+
console.log(formatVoices(voices));
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* Отказ API печатается ответом, а не стек-трейсом (US-622).
|
|
111
|
+
*
|
|
112
|
+
* До этой обработки `clipwright make` на 402 падал необработанным промисом: имя
|
|
113
|
+
* класса, стек до `node:internal`, код выхода 1. Числа отказа в этой куче есть,
|
|
114
|
+
* но найти их человек не обязан — он спрашивал про видео, а не про наши кадры
|
|
115
|
+
* стека. Прочие ошибки (сеть, отсутствующий `CLIPWRIGHT_CLIENT_ID`) намеренно
|
|
116
|
+
* летят как раньше: у них стек и есть полезная часть.
|
|
117
|
+
*/
|
|
118
|
+
program.parseAsync().catch((error) => {
|
|
119
|
+
if (error instanceof ClipwrightApiError) {
|
|
120
|
+
console.error(formatApiError(error));
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Битый ответ сервера — НЕ отказ ввода, и путать их нельзя (внешнее ревью,
|
|
125
|
+
// круг 9): на `POST /run` запрос уже ушёл, ран мог быть создан и оплачен.
|
|
126
|
+
// Собственное сообщение класса это и говорит; стек тут ничего не добавит.
|
|
127
|
+
if (error instanceof ClipwrightResponseError) {
|
|
128
|
+
console.error(`error: ${error.message}`);
|
|
129
|
+
process.exitCode = 1;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Отказ схемы — тоже ответ пользователю, а не наш баг (внешнее ревью, круг 8).
|
|
133
|
+
const schema = formatSchemaError(error);
|
|
134
|
+
if (schema !== undefined) {
|
|
135
|
+
console.error(schema);
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
});
|
|
141
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clipwright/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Command-line client for the Clipwright UGC video API",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"ugc",
|
|
8
|
+
"video",
|
|
9
|
+
"text-to-video"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "Dimantika Sp. z o.o.",
|
|
13
|
+
"homepage": "https://clipwright.io",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"clipwright": "dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"commander": "^15.0.0",
|
|
29
|
+
"@clipwright/core": "0.2.0",
|
|
30
|
+
"@clipwright/sdk": "0.2.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^26.1.1"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.json",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
38
|
+
"dev": "tsx src/index.ts"
|
|
39
|
+
}
|
|
40
|
+
}
|