clipugc 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.
Files changed (77) hide show
  1. package/README.md +249 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +31 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/account.d.ts +12 -0
  6. package/dist/commands/account.js +48 -0
  7. package/dist/commands/account.js.map +1 -0
  8. package/dist/commands/auth.d.ts +14 -0
  9. package/dist/commands/auth.js +152 -0
  10. package/dist/commands/auth.js.map +1 -0
  11. package/dist/commands/characters.d.ts +3 -0
  12. package/dist/commands/characters.js +261 -0
  13. package/dist/commands/characters.js.map +1 -0
  14. package/dist/commands/config.d.ts +5 -0
  15. package/dist/commands/config.js +69 -0
  16. package/dist/commands/config.js.map +1 -0
  17. package/dist/commands/credits.d.ts +2 -0
  18. package/dist/commands/credits.js +117 -0
  19. package/dist/commands/credits.js.map +1 -0
  20. package/dist/commands/hooks.d.ts +2 -0
  21. package/dist/commands/hooks.js +32 -0
  22. package/dist/commands/hooks.js.map +1 -0
  23. package/dist/commands/images.d.ts +3 -0
  24. package/dist/commands/images.js +257 -0
  25. package/dist/commands/images.js.map +1 -0
  26. package/dist/commands/videos.d.ts +2 -0
  27. package/dist/commands/videos.js +272 -0
  28. package/dist/commands/videos.js.map +1 -0
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.js +24 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/services/api.d.ts +40 -0
  33. package/dist/services/api.js +139 -0
  34. package/dist/services/api.js.map +1 -0
  35. package/dist/services/characters.service.d.ts +135 -0
  36. package/dist/services/characters.service.js +276 -0
  37. package/dist/services/characters.service.js.map +1 -0
  38. package/dist/services/hooks.service.d.ts +9 -0
  39. package/dist/services/hooks.service.js +23 -0
  40. package/dist/services/hooks.service.js.map +1 -0
  41. package/dist/services/images.service.d.ts +95 -0
  42. package/dist/services/images.service.js +191 -0
  43. package/dist/services/images.service.js.map +1 -0
  44. package/dist/services/upload.service.d.ts +46 -0
  45. package/dist/services/upload.service.js +134 -0
  46. package/dist/services/upload.service.js.map +1 -0
  47. package/dist/services/user.service.d.ts +80 -0
  48. package/dist/services/user.service.js +35 -0
  49. package/dist/services/user.service.js.map +1 -0
  50. package/dist/services/videos.service.d.ts +100 -0
  51. package/dist/services/videos.service.js +187 -0
  52. package/dist/services/videos.service.js.map +1 -0
  53. package/dist/types/index.d.ts +47 -0
  54. package/dist/types/index.js +18 -0
  55. package/dist/types/index.js.map +1 -0
  56. package/dist/utils/config.d.ts +18 -0
  57. package/dist/utils/config.js +65 -0
  58. package/dist/utils/config.js.map +1 -0
  59. package/dist/utils/errors.d.ts +52 -0
  60. package/dist/utils/errors.js +74 -0
  61. package/dist/utils/errors.js.map +1 -0
  62. package/dist/utils/logger.d.ts +11 -0
  63. package/dist/utils/logger.js +28 -0
  64. package/dist/utils/logger.js.map +1 -0
  65. package/dist/utils/output.d.ts +16 -0
  66. package/dist/utils/output.js +53 -0
  67. package/dist/utils/output.js.map +1 -0
  68. package/dist/utils/poll.d.ts +37 -0
  69. package/dist/utils/poll.js +94 -0
  70. package/dist/utils/poll.js.map +1 -0
  71. package/dist/utils/prompt.d.ts +4 -0
  72. package/dist/utils/prompt.js +55 -0
  73. package/dist/utils/prompt.js.map +1 -0
  74. package/dist/version.d.ts +1 -0
  75. package/dist/version.js +12 -0
  76. package/dist/version.js.map +1 -0
  77. package/package.json +49 -0
@@ -0,0 +1,261 @@
1
+ import { logger } from '../utils/logger.js';
2
+ import { isJsonMode, printJson, printTable, printPagination, formatStatus } from '../utils/output.js';
3
+ import { confirm } from '../utils/prompt.js';
4
+ import { AbortedError, ValidationError } from '../utils/errors.js';
5
+ import { waitForCompletion } from '../utils/poll.js';
6
+ import { checkImageStatus } from '../services/images.service.js';
7
+ import { MAX_PER_PAGE, createCharacter, deleteCharacter, getCharacter, getDisplayName, getSelectedLookId, listCharacters, parseDnaJson, renameCharacter, setCharacterVisibility, } from '../services/characters.service.js';
8
+ /** "Character created: Isabel Romero, 26 (id: 6)" — name/age fall back gracefully. */
9
+ function characterHeadline(c) {
10
+ const name = getDisplayName(c);
11
+ const age = typeof c.age === 'number' ? String(c.age) : undefined;
12
+ const label = [name, age].filter(Boolean).join(', ');
13
+ return `Character created: ${label} (id: ${c.id})`;
14
+ }
15
+ /** The staged first clip returned by create with --make-video, when present. */
16
+ function stagedVideo(c) {
17
+ const video = c.character_video;
18
+ return video && typeof video === 'object' && video.id !== undefined ? video : undefined;
19
+ }
20
+ /** Print the staged first clip's id + status with a follow-up hint. */
21
+ function printStagedVideo(video) {
22
+ logger.info(`First clip queued (video id: ${video.id}, status: ${formatStatus(String(video.status ?? 'pending'))}).`);
23
+ logger.hint(`Follow it with \`clipugc videos status ${video.id}\` (or download it once completed).`);
24
+ }
25
+ function parseIntStrict(value, label) {
26
+ const n = Number(value);
27
+ if (!Number.isInteger(n)) {
28
+ throw new ValidationError(`${label} must be an integer (got "${value}").`);
29
+ }
30
+ return n;
31
+ }
32
+ function yesNo(value) {
33
+ if (value === undefined || value === null)
34
+ return undefined;
35
+ return value ? 'yes' : 'no';
36
+ }
37
+ /** Register the `clipugc characters` command group (AI characters CRUD). */
38
+ export function registerCharactersCommands(program) {
39
+ const characters = program
40
+ .command('characters')
41
+ .description('Manage AI characters (create, list, publish, delete)');
42
+ characters
43
+ .command('list')
44
+ .description('List AI characters (your own by default, or the public discover feed)')
45
+ .option('--discover', 'Browse public characters instead of your own')
46
+ .option('--mine', 'List your own characters (default)')
47
+ .option('--feed', 'Combined feed: your characters first (newest), then public ones in unlock order')
48
+ .option('--search <q>', 'Search by name')
49
+ .option('--page <n>', 'Page number')
50
+ .option('--per-page <n>', `Results per page (max ${MAX_PER_PAGE})`)
51
+ .action(async (options, cmd) => {
52
+ const json = isJsonMode(cmd);
53
+ let scope = 'mine';
54
+ if (options.discover && !options.mine)
55
+ scope = 'discover';
56
+ else if (options.feed && !options.mine)
57
+ scope = 'feed';
58
+ let perPage = options.perPage !== undefined ? parseIntStrict(options.perPage, '--per-page') : undefined;
59
+ if (perPage !== undefined && perPage > MAX_PER_PAGE) {
60
+ if (!json)
61
+ logger.warn(`--per-page is capped at ${MAX_PER_PAGE}; using ${MAX_PER_PAGE}.`);
62
+ perPage = MAX_PER_PAGE;
63
+ }
64
+ const page = options.page !== undefined ? parseIntStrict(options.page, '--page') : undefined;
65
+ const result = await listCharacters({ scope, search: options.search, page, perPage });
66
+ if (json) {
67
+ printJson(result.raw);
68
+ return;
69
+ }
70
+ const columns = [
71
+ { header: 'ID', value: (c) => c.id },
72
+ { header: 'Name', value: (c) => getDisplayName(c) },
73
+ { header: 'Age', value: (c) => c.age },
74
+ { header: 'Gender', value: (c) => c.gender },
75
+ { header: 'Public', value: (c) => yesNo(c.is_public) },
76
+ ];
77
+ if (result.items.some((c) => c.is_locked !== undefined && c.is_locked !== null)) {
78
+ columns.push({ header: 'Locked', value: (c) => yesNo(c.is_locked) });
79
+ }
80
+ if (result.items.some((c) => c.status !== undefined && c.status !== null)) {
81
+ columns.push({ header: 'Status', value: (c) => (c.status ? formatStatus(String(c.status)) : undefined) });
82
+ }
83
+ if (result.items.some((c) => getSelectedLookId(c) !== undefined)) {
84
+ columns.push({ header: 'Selected Look', value: (c) => getSelectedLookId(c) });
85
+ }
86
+ printTable(result.items, columns);
87
+ printPagination(result.pagination);
88
+ });
89
+ characters
90
+ .command('create')
91
+ .description('Create a new AI character from a text description (the first look is generated automatically, 2 credits)')
92
+ .option('--description <text>', 'Describe your character in plain words, 10-1000 chars (e.g. "playful italian street musician woman in her 20s")')
93
+ .option('--scene <text>', 'Optional scene/pose for the first look, up to 600 chars')
94
+ .option('--inspiration <files...>', 'Optional inspiration images (up to 6 files)')
95
+ .option('--private', 'Keep the character private (default: public/discoverable, like the web)')
96
+ .option('--make-video', 'Also stage the character\'s first video clip right after the first look')
97
+ .option('--motion-prompt <text>', 'Motion/action prompt for that first clip (requires --make-video)')
98
+ .option('--wait', 'Wait for the first look to finish generating')
99
+ // Advanced: raw structured DNA instead of a description (rarely needed).
100
+ .option('--name <full name>', '[advanced] Full name (2-120 chars) — structured create without a description')
101
+ .option('--age <n>', '[advanced] Age (18-99)')
102
+ .option('--gender <gender>', '[advanced] Gender (e.g. male, female, other)')
103
+ .option('--dna-json <file-or-json>', '[advanced] Appearance DNA fields: inline JSON object or a JSON file path')
104
+ .action(async (options, cmd) => {
105
+ const json = isJsonMode(cmd);
106
+ if (!options.description && !options.name) {
107
+ throw new ValidationError('Describe your character with --description "…" (or use the advanced --name/--dna-json structured path).');
108
+ }
109
+ const dna = options.dnaJson !== undefined ? await parseDnaJson(options.dnaJson) : undefined;
110
+ const age = options.age !== undefined ? parseIntStrict(options.age, '--age') : undefined;
111
+ const character = await createCharacter({
112
+ description: options.description,
113
+ scene: options.scene,
114
+ inspirationFiles: options.inspiration,
115
+ name: options.name,
116
+ age,
117
+ gender: options.gender,
118
+ // Web parity: public by default; --private opts out. (Structured path keeps
119
+ // the server default unless --private is passed.)
120
+ isPublic: options.description ? !options.private : options.private ? false : undefined,
121
+ dna,
122
+ makeVideo: options.makeVideo,
123
+ motionPrompt: options.motionPrompt,
124
+ });
125
+ const rawLooks = character.reference_images;
126
+ const looks = Array.isArray(rawLooks)
127
+ ? rawLooks
128
+ : [];
129
+ const firstLook = looks[0];
130
+ const firstClip = stagedVideo(character);
131
+ if (options.wait && firstLook?.id !== undefined) {
132
+ const final = await waitForCompletion(() => checkImageStatus(String(firstLook.id)), {
133
+ label: 'first look',
134
+ quiet: json,
135
+ });
136
+ // The name/age/DNA are extracted asynchronously (in the first-look job), so the
137
+ // create response had them null — re-fetch now that the look is done.
138
+ const resolved = await getCharacter(String(character.id)).catch(() => character);
139
+ if (json) {
140
+ printJson({ ...resolved, reference_images: [final] });
141
+ return;
142
+ }
143
+ logger.success(characterHeadline(resolved));
144
+ const clip = stagedVideo(resolved) ?? firstClip;
145
+ if (clip) {
146
+ printStagedVideo(clip);
147
+ }
148
+ else {
149
+ logger.hint(`Make a video: clipugc videos create --image ${firstLook.id} --wait`);
150
+ }
151
+ return;
152
+ }
153
+ if (json) {
154
+ printJson(character);
155
+ return;
156
+ }
157
+ logger.success(`Character created (id: ${character.id})`);
158
+ if (firstLook?.id !== undefined && !options.wait) {
159
+ logger.info('First look is generating; its name & appearance fill in when it finishes.');
160
+ logger.hint(`Check it with: clipugc images status ${firstLook.id} --wait`);
161
+ }
162
+ else if (firstLook?.id === undefined) {
163
+ logger.hint(`Next: clipugc images generate --character ${character.id}`);
164
+ }
165
+ if (firstClip)
166
+ printStagedVideo(firstClip);
167
+ });
168
+ characters
169
+ .command('show <id>')
170
+ .description('Show a character in detail')
171
+ .action(async (id, _options, cmd) => {
172
+ const json = isJsonMode(cmd);
173
+ const character = await getCharacter(id);
174
+ if (json) {
175
+ printJson(character);
176
+ return;
177
+ }
178
+ for (const [key, value] of Object.entries(character)) {
179
+ if (value === null || value === undefined || value === '')
180
+ continue;
181
+ if (key === 'preview_clip' && typeof value === 'object') {
182
+ const clip = value;
183
+ // Locked public rows now return clip_url: null but keep the thumbnail —
184
+ // render whatever is present instead of dumping the raw object.
185
+ const url = clip.clip_url ?? clip.url;
186
+ const thumbnail = clip.clip_thumbnail_url ?? clip.thumbnail_url;
187
+ if (url)
188
+ logger.kv('preview_clip', url);
189
+ if (thumbnail)
190
+ logger.kv('preview_thumbnail', thumbnail);
191
+ if (url || thumbnail)
192
+ continue;
193
+ }
194
+ if (typeof value === 'object') {
195
+ logger.kv(key, JSON.stringify(value));
196
+ }
197
+ else if (key === 'status') {
198
+ logger.kv(key, formatStatus(String(value)));
199
+ }
200
+ else {
201
+ logger.kv(key, String(value));
202
+ }
203
+ }
204
+ });
205
+ characters
206
+ .command('rename <id>')
207
+ .description('Rename a character')
208
+ .requiredOption('--name <new name>', 'New full name (2-120 characters)')
209
+ .action(async (id, options, cmd) => {
210
+ const json = isJsonMode(cmd);
211
+ const character = await renameCharacter(id, options.name);
212
+ if (json) {
213
+ printJson(character);
214
+ return;
215
+ }
216
+ logger.success(`Character ${id} renamed to "${character.display_name ?? character.full_name ?? options.name}".`);
217
+ });
218
+ characters
219
+ .command('publish <id>')
220
+ .description('Make a character public (discoverable by others)')
221
+ .action(async (id, _options, cmd) => {
222
+ const json = isJsonMode(cmd);
223
+ const character = await setCharacterVisibility(id, true);
224
+ if (json) {
225
+ printJson(character);
226
+ return;
227
+ }
228
+ logger.success(`Character ${id} is now public.`);
229
+ });
230
+ characters
231
+ .command('unpublish <id>')
232
+ .description('Make a character private')
233
+ .action(async (id, _options, cmd) => {
234
+ const json = isJsonMode(cmd);
235
+ const character = await setCharacterVisibility(id, false);
236
+ if (json) {
237
+ printJson(character);
238
+ return;
239
+ }
240
+ logger.success(`Character ${id} is now private.`);
241
+ });
242
+ characters
243
+ .command('delete <id>')
244
+ .description('Delete a character')
245
+ .option('--yes', 'Skip the confirmation prompt')
246
+ .action(async (id, options, cmd) => {
247
+ const json = isJsonMode(cmd);
248
+ if (!options.yes) {
249
+ const ok = await confirm(`Delete character ${id}? This cannot be undone.`);
250
+ if (!ok)
251
+ throw new AbortedError('Delete cancelled.');
252
+ }
253
+ const result = await deleteCharacter(id);
254
+ if (json) {
255
+ printJson(result ?? { deleted: true, id });
256
+ return;
257
+ }
258
+ logger.success(`Character ${id} deleted.`);
259
+ });
260
+ }
261
+ //# sourceMappingURL=characters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"characters.js","sourceRoot":"","sources":["../../src/commands/characters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Command } from 'commander';
2
+ import type { ClipUgcConfig } from '../types/index.js';
3
+ /** Validate a config key or throw a ValidationError listing the valid keys. */
4
+ export declare function assertValidConfigKey(key: string): asserts key is keyof ClipUgcConfig;
5
+ export declare function registerConfigCommands(program: Command): void;
@@ -0,0 +1,69 @@
1
+ import { getConfigKeys, getConfigPath, getConfigValue, isValidConfigKey, loadConfig, setConfigValue, } from '../utils/config.js';
2
+ import { ValidationError } from '../utils/errors.js';
3
+ import { logger } from '../utils/logger.js';
4
+ import { isJsonMode, printJson } from '../utils/output.js';
5
+ import { maskApiKey } from './auth.js';
6
+ /** Validate a config key or throw a ValidationError listing the valid keys. */
7
+ export function assertValidConfigKey(key) {
8
+ if (!isValidConfigKey(key)) {
9
+ throw new ValidationError(`Unknown config key "${key}". Valid keys: ${getConfigKeys().join(', ')}`);
10
+ }
11
+ }
12
+ /** Mask secret values (apiKey) for display; other values pass through. */
13
+ function displayValue(key, value) {
14
+ return key === 'apiKey' ? maskApiKey(value) : value;
15
+ }
16
+ export function registerConfigCommands(program) {
17
+ const config = program
18
+ .command('config')
19
+ .description(`Manage CLI configuration (keys: ${getConfigKeys().join(', ')})`);
20
+ config
21
+ .command('list')
22
+ .description('Show all configuration values (apiKey is masked)')
23
+ .action(async (_options, cmd) => {
24
+ const values = await loadConfig();
25
+ const masked = Object.fromEntries(getConfigKeys().map((key) => [key, displayValue(key, values[key])]));
26
+ if (isJsonMode(cmd)) {
27
+ printJson(masked);
28
+ return;
29
+ }
30
+ for (const key of getConfigKeys()) {
31
+ logger.kv(key, masked[key]);
32
+ }
33
+ });
34
+ config
35
+ .command('get <key>')
36
+ .description(`Print a single configuration value (keys: ${getConfigKeys().join(', ')})`)
37
+ .action(async (key, _options, cmd) => {
38
+ assertValidConfigKey(key);
39
+ const value = await getConfigValue(key);
40
+ if (isJsonMode(cmd)) {
41
+ printJson({ key, value });
42
+ return;
43
+ }
44
+ logger.plain(value);
45
+ });
46
+ config
47
+ .command('set <key> <value>')
48
+ .description(`Set a configuration value (keys: ${getConfigKeys().join(', ')})`)
49
+ .action(async (key, value, _options, cmd) => {
50
+ assertValidConfigKey(key);
51
+ await setConfigValue(key, value);
52
+ if (isJsonMode(cmd)) {
53
+ printJson({ key, value: displayValue(key, value) });
54
+ return;
55
+ }
56
+ logger.success(`Set ${key} = ${displayValue(key, value)}`);
57
+ });
58
+ config
59
+ .command('path')
60
+ .description('Print the path of the config file')
61
+ .action(async (_options, cmd) => {
62
+ if (isJsonMode(cmd)) {
63
+ printJson({ path: getConfigPath() });
64
+ return;
65
+ }
66
+ logger.plain(getConfigPath());
67
+ });
68
+ }
69
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerCreditsCommands(program: Command): void;
@@ -0,0 +1,117 @@
1
+ import chalk from 'chalk';
2
+ import { createApiClient } from '../services/api.js';
3
+ import { getCredits, getCreditTransactions } from '../services/user.service.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import { isJsonMode, printJson, printPagination, printTable } from '../utils/output.js';
6
+ /**
7
+ * Human-readable labels for the per-action cost keys returned by GET /credits.
8
+ * Unknown keys fall back to a humanized version of the raw key.
9
+ */
10
+ const COST_LABELS = {
11
+ image: 'image',
12
+ clip: 'clip (5s)',
13
+ clip_10s: 'clip (10s)',
14
+ motion_per_second: 'motion control (per second)',
15
+ scene_staged: 'scene-staged clip',
16
+ merge: 'merge',
17
+ };
18
+ /** Preferred display order for known cost keys; unknown keys follow, sorted. */
19
+ const COST_ORDER = ['image', 'clip', 'clip_10s', 'scene_staged', 'motion_per_second', 'merge'];
20
+ function costLabel(action) {
21
+ return COST_LABELS[action] ?? action.replace(/_/g, ' ');
22
+ }
23
+ /** Order cost entries: known keys by COST_ORDER, then any unknown keys sorted. */
24
+ function orderedCosts(costs) {
25
+ const entries = Object.entries(costs).filter((e) => typeof e[1] === 'number');
26
+ return entries.sort(([a], [b]) => {
27
+ const ia = COST_ORDER.indexOf(a);
28
+ const ib = COST_ORDER.indexOf(b);
29
+ if (ia !== -1 && ib !== -1)
30
+ return ia - ib;
31
+ if (ia !== -1)
32
+ return -1;
33
+ if (ib !== -1)
34
+ return 1;
35
+ return a.localeCompare(b);
36
+ }).map(([action, cost]) => ({ action, cost }));
37
+ }
38
+ /** "+3" for a top-up/refund, "-7" for a spend (server already signs the amount). */
39
+ function signedAmount(amount) {
40
+ if (typeof amount !== 'number')
41
+ return undefined;
42
+ return amount > 0 ? `+${amount}` : String(amount);
43
+ }
44
+ export function registerCreditsCommands(program) {
45
+ const credits = program
46
+ .command('credits')
47
+ .description('Show your credit balance and the credit cost of each action')
48
+ .action(async (_options, cmd) => {
49
+ const api = await createApiClient();
50
+ const info = await getCredits(api);
51
+ if (isJsonMode(cmd)) {
52
+ printJson(info);
53
+ return;
54
+ }
55
+ logger.plain(`\n Balance: ${chalk.bold(String(info.balance))} credits\n`);
56
+ const rows = orderedCosts(info.costs ?? {});
57
+ printTable(rows, [
58
+ { header: 'action', value: (row) => costLabel(row.action) },
59
+ { header: 'credits', value: (row) => row.cost },
60
+ ]);
61
+ logger.hint('See your ledger: clipugc credits history · Buy more: clipugc credits packs');
62
+ });
63
+ credits
64
+ .command('history')
65
+ .description('Show your credit transaction history (spends, top-ups, refunds)')
66
+ .option('--per-page <n>', 'Results per page (max 100)')
67
+ .option('--page <n>', 'Page number')
68
+ .action(async (opts, cmd) => {
69
+ const api = await createApiClient();
70
+ const perPage = opts.perPage !== undefined ? Number.parseInt(opts.perPage, 10) : undefined;
71
+ const page = opts.page !== undefined ? Number.parseInt(opts.page, 10) : undefined;
72
+ const result = await getCreditTransactions(api, { perPage, page });
73
+ if (isJsonMode(cmd)) {
74
+ printJson(result);
75
+ return;
76
+ }
77
+ printTable(result.transactions, [
78
+ { header: 'Date', value: (t) => t.created_at },
79
+ { header: 'Description', value: (t) => t.description ?? t.action ?? t.type },
80
+ { header: 'Amount', value: (t) => signedAmount(t.amount) },
81
+ { header: 'Balance', value: (t) => t.balance_after },
82
+ ]);
83
+ printPagination(result.pagination);
84
+ });
85
+ credits
86
+ .command('packs')
87
+ .description('List purchasable credit packs (buy on the web dashboard)')
88
+ .action(async (_options, cmd) => {
89
+ const api = await createApiClient();
90
+ const data = await api.get('/credits/packs');
91
+ const packs = Array.isArray(data?.packs) ? data.packs : [];
92
+ if (isJsonMode(cmd)) {
93
+ printJson(packs);
94
+ return;
95
+ }
96
+ if (packs.length === 0) {
97
+ logger.info('No credit packs are available yet.');
98
+ return;
99
+ }
100
+ printTable(packs, [
101
+ { header: 'Credits', value: (p) => (p.credits !== undefined ? String(p.credits) : undefined) },
102
+ { header: 'Name', value: (p) => p.name },
103
+ {
104
+ header: 'Price',
105
+ value: (p) => {
106
+ const price = p.prices?.[0];
107
+ return price?.amount !== undefined
108
+ ? `${(price.amount / 100).toFixed(2)} ${price.currency ?? ''}`.trim()
109
+ : undefined;
110
+ },
111
+ },
112
+ { header: 'Buy at', value: (p) => p.checkout_url },
113
+ ]);
114
+ logger.hint('Open the "Buy at" link in your browser to complete the purchase; then check `clipugc credits`.');
115
+ });
116
+ }
117
+ //# sourceMappingURL=credits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credits.js","sourceRoot":"","sources":["../../src/commands/credits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAExF;;;GAGG;AACH,MAAM,WAAW,GAA2B;IAC1C,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,WAAW;IACjB,QAAQ,EAAE,YAAY;IACtB,iBAAiB,EAAE,6BAA6B;IAChD,YAAY,EAAE,mBAAmB;IACjC,KAAK,EAAE,OAAO;CACf,CAAC;AAEF,gFAAgF;AAChF,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAC;AAE/F,SAAS,SAAS,CAAC,MAAc;IAC/B,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,kFAAkF;AAClF,SAAS,YAAY,CAAC,KAAyC;IAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAC1C,CAAC,CAAC,EAAyB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CACvD,CAAC;IACF,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;QAC/B,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAC3C,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC;QACzB,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;QACxB,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,oFAAoF;AACpF,SAAS,YAAY,CAAC,MAA0B;IAC9C,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACjD,OAAO,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACpD,CAAC;AAWD,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,6DAA6D,CAAC;SAC1E,MAAM,CAAC,KAAK,EAAE,QAA+B,EAAE,GAAY,EAAE,EAAE;QAC9D,MAAM,GAAG,GAAG,MAAM,eAAe,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;QAEnC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,OAAO;QACT,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,gBAAgB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;QAE3E,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5C,UAAU,CAAC,IAAI,EAAE;YACf,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;YAC3D,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE;SAChD,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,iEAAiE,CAAC;SAC9E,MAAM,CAAC,gBAAgB,EAAE,4BAA4B,CAAC;SACtD,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC;SACnC,MAAM,CAAC,KAAK,EAAE,IAAyC,EAAE,GAAY,EAAE,EAAE;QACxE,MAAM,GAAG,GAAG,MAAM,eAAe,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAClF,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAEnE,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,SAAS,CAAC,MAAM,CAAC,CAAC;YAClB,OAAO;QACT,CAAC;QAED,UAAU,CAAC,MAAM,CAAC,YAAY,EAAE;YAC9B,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE;YAC9C,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE;YAC5E,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;YAC1D,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE;SACrD,CAAC,CAAC;QACH,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,0DAA0D,CAAC;SACvE,MAAM,CAAC,KAAK,EAAE,QAA+B,EAAE,GAAY,EAAE,EAAE;QAC9D,MAAM,GAAG,GAAG,MAAM,eAAe,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,GAAG,CAA2B,gBAAgB,CAAC,CAAC;QACvE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAE3D,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,SAAS,CAAC,KAAK,CAAC,CAAC;YACjB,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,UAAU,CAAC,KAAK,EAAE;YAChB,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE;YAC9F,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE;YACxC;gBACE,MAAM,EAAE,OAAO;gBACf,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE;oBACX,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;oBAC5B,OAAO,KAAK,EAAE,MAAM,KAAK,SAAS;wBAChC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;wBACrE,CAAC,CAAC,SAAS,CAAC;gBAChB,CAAC;aACF;YACD,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE;SACnD,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,gGAAgG,CAAC,CAAC;IAChH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerHooksCommands(program: Command): void;
@@ -0,0 +1,32 @@
1
+ import chalk from 'chalk';
2
+ import { createApiClient } from '../services/api.js';
3
+ import { suggestHooks } from '../services/hooks.service.js';
4
+ import { isJsonMode, printJson } from '../utils/output.js';
5
+ import { logger } from '../utils/logger.js';
6
+ export function registerHooksCommands(program) {
7
+ const hooks = program
8
+ .command('hooks')
9
+ .description('AI-generated hook text ideas for your UGC videos');
10
+ hooks
11
+ .command('suggest')
12
+ .description('Suggest hook texts (short attention-grabbing overlays) for your app')
13
+ .option('--context <text>', 'Describe your app for tailored hooks, e.g. "my app is a habit tracker"')
14
+ .action(async (opts, cmd) => {
15
+ const json = isJsonMode(cmd);
16
+ const api = await createApiClient();
17
+ const { hooks: suggestions, raw } = await suggestHooks(api, opts.context);
18
+ if (json) {
19
+ printJson(raw);
20
+ return;
21
+ }
22
+ if (suggestions.length === 0) {
23
+ logger.info('No hook suggestions returned. Try again with --context "describe your app".');
24
+ return;
25
+ }
26
+ for (const [index, hook] of suggestions.entries()) {
27
+ logger.plain(`${chalk.cyan(String(index + 1).padStart(2))}. ${hook}`);
28
+ }
29
+ logger.hint('Use one with `clipugc videos merge <videoId> --app-video <file> --hook "..."`');
30
+ });
31
+ }
32
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,KAAK,GAAG,OAAO;SAClB,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,kDAAkD,CAAC,CAAC;IAEnE,KAAK;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,qEAAqE,CAAC;SAClF,MAAM,CAAC,kBAAkB,EAAE,wEAAwE,CAAC;SACpG,MAAM,CAAC,KAAK,EAAE,IAA0B,EAAE,GAAY,EAAE,EAAE;QACzD,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,MAAM,eAAe,EAAE,CAAC;QACpC,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1E,IAAI,IAAI,EAAE,CAAC;YACT,SAAS,CAAC,GAAG,CAAC,CAAC;YACf,OAAO;QACT,CAAC;QACD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;YAC3F,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;YAClD,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ /** Register the `clipugc images` command group (character looks). */
3
+ export declare function registerImagesCommands(program: Command): void;