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.
- package/README.md +249 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +31 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/account.d.ts +12 -0
- package/dist/commands/account.js +48 -0
- package/dist/commands/account.js.map +1 -0
- package/dist/commands/auth.d.ts +14 -0
- package/dist/commands/auth.js +152 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/characters.d.ts +3 -0
- package/dist/commands/characters.js +261 -0
- package/dist/commands/characters.js.map +1 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.js +69 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/credits.d.ts +2 -0
- package/dist/commands/credits.js +117 -0
- package/dist/commands/credits.js.map +1 -0
- package/dist/commands/hooks.d.ts +2 -0
- package/dist/commands/hooks.js +32 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/images.d.ts +3 -0
- package/dist/commands/images.js +257 -0
- package/dist/commands/images.js.map +1 -0
- package/dist/commands/videos.d.ts +2 -0
- package/dist/commands/videos.js +272 -0
- package/dist/commands/videos.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/services/api.d.ts +40 -0
- package/dist/services/api.js +139 -0
- package/dist/services/api.js.map +1 -0
- package/dist/services/characters.service.d.ts +135 -0
- package/dist/services/characters.service.js +276 -0
- package/dist/services/characters.service.js.map +1 -0
- package/dist/services/hooks.service.d.ts +9 -0
- package/dist/services/hooks.service.js +23 -0
- package/dist/services/hooks.service.js.map +1 -0
- package/dist/services/images.service.d.ts +95 -0
- package/dist/services/images.service.js +191 -0
- package/dist/services/images.service.js.map +1 -0
- package/dist/services/upload.service.d.ts +46 -0
- package/dist/services/upload.service.js +134 -0
- package/dist/services/upload.service.js.map +1 -0
- package/dist/services/user.service.d.ts +80 -0
- package/dist/services/user.service.js +35 -0
- package/dist/services/user.service.js.map +1 -0
- package/dist/services/videos.service.d.ts +100 -0
- package/dist/services/videos.service.js +187 -0
- package/dist/services/videos.service.js.map +1 -0
- package/dist/types/index.d.ts +47 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config.d.ts +18 -0
- package/dist/utils/config.js +65 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/errors.d.ts +52 -0
- package/dist/utils/errors.js +74 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.js +28 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/output.d.ts +16 -0
- package/dist/utils/output.js +53 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/poll.d.ts +37 -0
- package/dist/utils/poll.js +94 -0
- package/dist/utils/poll.js.map +1 -0
- package/dist/utils/prompt.d.ts +4 -0
- package/dist/utils/prompt.js +55 -0
- package/dist/utils/prompt.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ApiClient } from './api.js';
|
|
2
|
+
import type { Paginated, Pagination } from '../types/index.js';
|
|
3
|
+
export declare const MAX_PROMPT_LENGTH = 1500;
|
|
4
|
+
export declare const MAX_SCENE_PROMPT_LENGTH = 600;
|
|
5
|
+
export declare const MAX_HOOK_TEXT_LENGTH = 150;
|
|
6
|
+
export declare const MAX_PER_PAGE = 50;
|
|
7
|
+
export declare const ALLOWED_DURATIONS: readonly [5, 10];
|
|
8
|
+
/** A character video as returned by /character-videos endpoints. */
|
|
9
|
+
export interface CharacterVideo {
|
|
10
|
+
id: number | string;
|
|
11
|
+
status: string;
|
|
12
|
+
kind?: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
prompt?: string | null;
|
|
15
|
+
scene_prompt?: string | null;
|
|
16
|
+
duration?: number;
|
|
17
|
+
is_merged?: boolean;
|
|
18
|
+
/** processing | completed | failed | null (never merged). */
|
|
19
|
+
merge_status?: string | null;
|
|
20
|
+
created_at?: string;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
/** Payload of GET /character-videos/{id}/check-status. */
|
|
24
|
+
export interface VideoStatusCheck {
|
|
25
|
+
status: string;
|
|
26
|
+
failure_reason?: string | null;
|
|
27
|
+
error_message?: string | null;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface ImageToVideoInput {
|
|
31
|
+
/** Exactly one of characterReferenceImageId / sourceImageKey is required. */
|
|
32
|
+
characterReferenceImageId?: string;
|
|
33
|
+
sourceImageKey?: string;
|
|
34
|
+
prompt?: string;
|
|
35
|
+
/** Extra scene description — turns the clip into a scene-staged clip (9 credits). */
|
|
36
|
+
scenePrompt?: string;
|
|
37
|
+
/** 5 or 10 seconds (default 5). A 5s clip costs 7 credits, a 10s clip 13. */
|
|
38
|
+
duration?: number;
|
|
39
|
+
keepOriginalSound?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface MotionControlInput extends Omit<ImageToVideoInput, 'scenePrompt' | 'duration'> {
|
|
42
|
+
/** Storage key of the uploaded driver video (purpose driver_video). */
|
|
43
|
+
referenceVideoKey: string;
|
|
44
|
+
scenePrompt?: string;
|
|
45
|
+
duration?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface MergeInput {
|
|
48
|
+
/** Storage key of the uploaded app screen recording (purpose app_video). */
|
|
49
|
+
appVideoKey: string;
|
|
50
|
+
/** Hook text overlaid on the final video (required, max 150 chars). */
|
|
51
|
+
hookText: string;
|
|
52
|
+
/** Optional storage key of background music (purpose music). */
|
|
53
|
+
musicKey?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Tolerant list extractor: the API may return the array raw or wrapped under
|
|
57
|
+
* a known key. Pagination, when present, lives at data.pagination.
|
|
58
|
+
*/
|
|
59
|
+
export declare function extractList<T>(data: unknown): Paginated<T>;
|
|
60
|
+
export interface ListVideosOptions {
|
|
61
|
+
page?: number;
|
|
62
|
+
perPage?: number;
|
|
63
|
+
/** Only videos of this AI character (ai_character_id filter). */
|
|
64
|
+
aiCharacterId?: string;
|
|
65
|
+
/** Only completed, unmerged clips — ready for `videos merge` (mergeable=1). */
|
|
66
|
+
mergeable?: boolean;
|
|
67
|
+
/** Only merged final videos (finals=1). */
|
|
68
|
+
finals?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** GET /character-videos — paginated list. */
|
|
71
|
+
export declare function listVideos(api: ApiClient, opts?: ListVideosOptions): Promise<{
|
|
72
|
+
items: CharacterVideo[];
|
|
73
|
+
pagination?: Pagination;
|
|
74
|
+
raw: unknown;
|
|
75
|
+
}>;
|
|
76
|
+
/** POST /character-videos/image-to-video — animate a still image into a talking clip. */
|
|
77
|
+
export declare function createImageToVideo(api: ApiClient, input: ImageToVideoInput): Promise<CharacterVideo>;
|
|
78
|
+
/** POST /character-videos/motion-control — drive the character with a reference video. */
|
|
79
|
+
export declare function createMotionControl(api: ApiClient, input: MotionControlInput): Promise<CharacterVideo>;
|
|
80
|
+
/** POST /character-videos/{id}/merge — merge app recording + hook text (+ music) into the final UGC video. */
|
|
81
|
+
export declare function mergeVideo(api: ApiClient, id: string, input: MergeInput): Promise<CharacterVideo>;
|
|
82
|
+
/** POST /character-videos/{id}/retry. */
|
|
83
|
+
export declare function retryVideo(api: ApiClient, id: string): Promise<CharacterVideo>;
|
|
84
|
+
/** GET /character-videos/{id}. */
|
|
85
|
+
export declare function getVideo(api: ApiClient, id: string): Promise<CharacterVideo>;
|
|
86
|
+
/** GET /character-videos/{id}/check-status. */
|
|
87
|
+
export declare function checkVideoStatus(api: ApiClient, id: string): Promise<VideoStatusCheck>;
|
|
88
|
+
/** DELETE /character-videos/{id}. Returns the envelope message when present. */
|
|
89
|
+
export declare function deleteVideo(api: ApiClient, id: string): Promise<{
|
|
90
|
+
data: unknown;
|
|
91
|
+
message: string | null;
|
|
92
|
+
}>;
|
|
93
|
+
/**
|
|
94
|
+
* GET /character-videos/{id}/download → data.download_url, then fetch that URL
|
|
95
|
+
* and write the bytes to disk. Returns the destination path.
|
|
96
|
+
*/
|
|
97
|
+
export declare function downloadVideo(api: ApiClient, id: string, opts?: {
|
|
98
|
+
output?: string;
|
|
99
|
+
quiet?: boolean;
|
|
100
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { ApiError, NetworkError, ValidationError } from '../utils/errors.js';
|
|
5
|
+
export const MAX_PROMPT_LENGTH = 1500;
|
|
6
|
+
export const MAX_SCENE_PROMPT_LENGTH = 600;
|
|
7
|
+
export const MAX_HOOK_TEXT_LENGTH = 150;
|
|
8
|
+
export const MAX_PER_PAGE = 50;
|
|
9
|
+
export const ALLOWED_DURATIONS = [5, 10];
|
|
10
|
+
/**
|
|
11
|
+
* Tolerant list extractor: the API may return the array raw or wrapped under
|
|
12
|
+
* a known key. Pagination, when present, lives at data.pagination.
|
|
13
|
+
*/
|
|
14
|
+
export function extractList(data) {
|
|
15
|
+
if (Array.isArray(data)) {
|
|
16
|
+
return { items: data };
|
|
17
|
+
}
|
|
18
|
+
if (data && typeof data === 'object') {
|
|
19
|
+
const record = data;
|
|
20
|
+
const pagination = record.pagination ?? undefined;
|
|
21
|
+
for (const key of ['videos', 'character_videos', 'items', 'data', 'results']) {
|
|
22
|
+
if (Array.isArray(record[key])) {
|
|
23
|
+
return { items: record[key], pagination };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { items: [] };
|
|
28
|
+
}
|
|
29
|
+
function validatePrompts(prompt, scenePrompt) {
|
|
30
|
+
if (prompt !== undefined && prompt.length > MAX_PROMPT_LENGTH) {
|
|
31
|
+
throw new ValidationError(`--prompt is too long (${prompt.length} chars, max ${MAX_PROMPT_LENGTH}).`);
|
|
32
|
+
}
|
|
33
|
+
if (scenePrompt !== undefined && scenePrompt.length > MAX_SCENE_PROMPT_LENGTH) {
|
|
34
|
+
throw new ValidationError(`--scene is too long (${scenePrompt.length} chars, max ${MAX_SCENE_PROMPT_LENGTH}).`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function validateDuration(duration) {
|
|
38
|
+
if (!ALLOWED_DURATIONS.includes(duration)) {
|
|
39
|
+
throw new ValidationError(`--duration must be 5 or 10 (got ${duration}).`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function validateImageSource(input) {
|
|
43
|
+
const hasLook = Boolean(input.characterReferenceImageId);
|
|
44
|
+
const hasKey = Boolean(input.sourceImageKey);
|
|
45
|
+
if (hasLook === hasKey) {
|
|
46
|
+
throw new ValidationError('Provide exactly one image source: --image <lookId> (a generated character look) or --photo <file> (your own photo).');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function baseVideoPayload(input) {
|
|
50
|
+
const payload = {};
|
|
51
|
+
if (input.characterReferenceImageId)
|
|
52
|
+
payload.character_reference_image_id = input.characterReferenceImageId;
|
|
53
|
+
if (input.sourceImageKey)
|
|
54
|
+
payload.source_image_key = input.sourceImageKey;
|
|
55
|
+
if (input.prompt !== undefined && input.prompt !== '')
|
|
56
|
+
payload.prompt = input.prompt;
|
|
57
|
+
if (input.scenePrompt !== undefined && input.scenePrompt !== '')
|
|
58
|
+
payload.scene_prompt = input.scenePrompt;
|
|
59
|
+
if (input.keepOriginalSound !== undefined)
|
|
60
|
+
payload.keep_original_sound = input.keepOriginalSound;
|
|
61
|
+
return payload;
|
|
62
|
+
}
|
|
63
|
+
/** GET /character-videos — paginated list. */
|
|
64
|
+
export async function listVideos(api, opts = {}) {
|
|
65
|
+
if (opts.perPage !== undefined && (!Number.isInteger(opts.perPage) || opts.perPage < 1 || opts.perPage > MAX_PER_PAGE)) {
|
|
66
|
+
throw new ValidationError(`--per-page must be an integer between 1 and ${MAX_PER_PAGE}.`);
|
|
67
|
+
}
|
|
68
|
+
if (opts.page !== undefined && (!Number.isInteger(opts.page) || opts.page < 1)) {
|
|
69
|
+
throw new ValidationError('--page must be a positive integer.');
|
|
70
|
+
}
|
|
71
|
+
if (opts.mergeable && opts.finals) {
|
|
72
|
+
throw new ValidationError('--mergeable and --finals are mutually exclusive — pick one.');
|
|
73
|
+
}
|
|
74
|
+
const raw = await api.get('/character-videos', {
|
|
75
|
+
query: {
|
|
76
|
+
page: opts.page,
|
|
77
|
+
per_page: opts.perPage,
|
|
78
|
+
ai_character_id: opts.aiCharacterId,
|
|
79
|
+
mergeable: opts.mergeable ? 1 : undefined,
|
|
80
|
+
finals: opts.finals ? 1 : undefined,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const { items, pagination } = extractList(raw);
|
|
84
|
+
return { items, pagination, raw };
|
|
85
|
+
}
|
|
86
|
+
/** POST /character-videos/image-to-video — animate a still image into a talking clip. */
|
|
87
|
+
export async function createImageToVideo(api, input) {
|
|
88
|
+
validateImageSource(input);
|
|
89
|
+
validatePrompts(input.prompt, input.scenePrompt);
|
|
90
|
+
const duration = input.duration ?? 5;
|
|
91
|
+
validateDuration(duration);
|
|
92
|
+
const payload = baseVideoPayload(input);
|
|
93
|
+
payload.duration = duration;
|
|
94
|
+
return api.post('/character-videos/image-to-video', { body: payload });
|
|
95
|
+
}
|
|
96
|
+
/** POST /character-videos/motion-control — drive the character with a reference video. */
|
|
97
|
+
export async function createMotionControl(api, input) {
|
|
98
|
+
validateImageSource(input);
|
|
99
|
+
if (!input.referenceVideoKey) {
|
|
100
|
+
throw new ValidationError('A driver video is required (--driver <videoFile>).');
|
|
101
|
+
}
|
|
102
|
+
validatePrompts(input.prompt, input.scenePrompt);
|
|
103
|
+
const payload = baseVideoPayload(input);
|
|
104
|
+
payload.reference_video_key = input.referenceVideoKey;
|
|
105
|
+
if (input.duration !== undefined) {
|
|
106
|
+
validateDuration(input.duration);
|
|
107
|
+
payload.duration = input.duration;
|
|
108
|
+
}
|
|
109
|
+
return api.post('/character-videos/motion-control', { body: payload });
|
|
110
|
+
}
|
|
111
|
+
/** POST /character-videos/{id}/merge — merge app recording + hook text (+ music) into the final UGC video. */
|
|
112
|
+
export async function mergeVideo(api, id, input) {
|
|
113
|
+
if (!input.appVideoKey) {
|
|
114
|
+
throw new ValidationError('An app screen recording is required (--app-video <file>).');
|
|
115
|
+
}
|
|
116
|
+
if (!input.hookText || input.hookText.trim() === '') {
|
|
117
|
+
throw new ValidationError('Hook text is required (--hook "your hook text").');
|
|
118
|
+
}
|
|
119
|
+
if (input.hookText.length > MAX_HOOK_TEXT_LENGTH) {
|
|
120
|
+
throw new ValidationError(`--hook is too long (${input.hookText.length} chars, max ${MAX_HOOK_TEXT_LENGTH}).`);
|
|
121
|
+
}
|
|
122
|
+
const payload = {
|
|
123
|
+
app_video_key: input.appVideoKey,
|
|
124
|
+
hook_text: input.hookText,
|
|
125
|
+
};
|
|
126
|
+
if (input.musicKey)
|
|
127
|
+
payload.music_key = input.musicKey;
|
|
128
|
+
return api.post(`/character-videos/${encodeURIComponent(id)}/merge`, { body: payload });
|
|
129
|
+
}
|
|
130
|
+
/** POST /character-videos/{id}/retry. */
|
|
131
|
+
export async function retryVideo(api, id) {
|
|
132
|
+
return api.post(`/character-videos/${encodeURIComponent(id)}/retry`);
|
|
133
|
+
}
|
|
134
|
+
/** GET /character-videos/{id}. */
|
|
135
|
+
export async function getVideo(api, id) {
|
|
136
|
+
return api.get(`/character-videos/${encodeURIComponent(id)}`);
|
|
137
|
+
}
|
|
138
|
+
/** GET /character-videos/{id}/check-status. */
|
|
139
|
+
export async function checkVideoStatus(api, id) {
|
|
140
|
+
return api.get(`/character-videos/${encodeURIComponent(id)}/check-status`);
|
|
141
|
+
}
|
|
142
|
+
/** DELETE /character-videos/{id}. Returns the envelope message when present. */
|
|
143
|
+
export async function deleteVideo(api, id) {
|
|
144
|
+
const result = await api.request('DELETE', `/character-videos/${encodeURIComponent(id)}`);
|
|
145
|
+
return { data: result.data, message: result.message };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* GET /character-videos/{id}/download → data.download_url, then fetch that URL
|
|
149
|
+
* and write the bytes to disk. Returns the destination path.
|
|
150
|
+
*/
|
|
151
|
+
export async function downloadVideo(api, id, opts = {}) {
|
|
152
|
+
const data = await api.get(`/character-videos/${encodeURIComponent(id)}/download`);
|
|
153
|
+
const url = data?.download_url;
|
|
154
|
+
if (!url) {
|
|
155
|
+
throw new ApiError('The API did not return a download URL. Is the video completed? Check `clipugc videos status`.');
|
|
156
|
+
}
|
|
157
|
+
const dest = opts.output || `clipugc-video-${id}.mp4`;
|
|
158
|
+
const spinner = opts.quiet ? null : ora(`Downloading to ${dest}`).start();
|
|
159
|
+
try {
|
|
160
|
+
let response;
|
|
161
|
+
try {
|
|
162
|
+
response = await fetch(url);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
166
|
+
throw new NetworkError(`Download failed: ${reason}`);
|
|
167
|
+
}
|
|
168
|
+
if (!response.ok) {
|
|
169
|
+
throw new NetworkError(`Download failed (HTTP ${response.status}). The link may have expired — try again.`);
|
|
170
|
+
}
|
|
171
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
172
|
+
// Create missing parent directories so -o can target workspace paths
|
|
173
|
+
// like clipugc/influencers/<id>/clips/… directly.
|
|
174
|
+
const dir = path.dirname(dest);
|
|
175
|
+
if (dir && dir !== '.')
|
|
176
|
+
await fs.mkdir(dir, { recursive: true });
|
|
177
|
+
await fs.writeFile(dest, bytes);
|
|
178
|
+
spinner?.succeed(`Saved ${dest} (${(bytes.length / (1024 * 1024)).toFixed(1)} MB)`);
|
|
179
|
+
return dest;
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
if (spinner?.isSpinning)
|
|
183
|
+
spinner.fail(`Download to ${dest} failed`);
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=videos.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videos.service.js","sourceRoot":"","sources":["../../src/services/videos.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the ClipUGC CLI.
|
|
3
|
+
*
|
|
4
|
+
* NOTE (subagent contract): group-specific request/response types live in the
|
|
5
|
+
* corresponding service file (e.g. src/services/characters.service.ts).
|
|
6
|
+
* Only genuinely shared, cross-group types belong here.
|
|
7
|
+
*/
|
|
8
|
+
/** Persisted CLI configuration (~/.config/clipugc/config.json). */
|
|
9
|
+
export interface ClipUgcConfig {
|
|
10
|
+
/** API base URL, e.g. https://clipugc.com/api/v1 */
|
|
11
|
+
apiBaseUrl: string;
|
|
12
|
+
/** Sanctum personal access token created from the ClipUGC dashboard. */
|
|
13
|
+
apiKey: string;
|
|
14
|
+
/** Email of the authenticated user (cached at login for `auth status`). */
|
|
15
|
+
email: string;
|
|
16
|
+
}
|
|
17
|
+
/** Standard ClipUGC API response envelope. Every response is HTTP 200. */
|
|
18
|
+
export interface ApiEnvelope<T = unknown> {
|
|
19
|
+
statusCode: number;
|
|
20
|
+
errorMessage: string | null;
|
|
21
|
+
data: T;
|
|
22
|
+
message: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** Pagination block returned inside `data.pagination`. */
|
|
25
|
+
export interface Pagination {
|
|
26
|
+
current_page: number;
|
|
27
|
+
last_page: number;
|
|
28
|
+
per_page: number;
|
|
29
|
+
total: number;
|
|
30
|
+
has_more_pages: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface Paginated<T> {
|
|
33
|
+
items: T[];
|
|
34
|
+
pagination?: Pagination;
|
|
35
|
+
}
|
|
36
|
+
/** Long-running resource statuses. */
|
|
37
|
+
export type ResourceStatus = 'pending' | 'processing' | 'completed' | 'failed';
|
|
38
|
+
/** App-level status codes used by the API envelope. */
|
|
39
|
+
export declare const APP_CODES: {
|
|
40
|
+
readonly OK: 200;
|
|
41
|
+
readonly CREATED: 201;
|
|
42
|
+
readonly VALIDATION: 400;
|
|
43
|
+
readonly UNAUTHORIZED: 401;
|
|
44
|
+
readonly NOT_FOUND: 404;
|
|
45
|
+
readonly PREMIUM_REQUIRED: 1002;
|
|
46
|
+
readonly INSUFFICIENT_CREDITS: 1003;
|
|
47
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the ClipUGC CLI.
|
|
3
|
+
*
|
|
4
|
+
* NOTE (subagent contract): group-specific request/response types live in the
|
|
5
|
+
* corresponding service file (e.g. src/services/characters.service.ts).
|
|
6
|
+
* Only genuinely shared, cross-group types belong here.
|
|
7
|
+
*/
|
|
8
|
+
/** App-level status codes used by the API envelope. */
|
|
9
|
+
export const APP_CODES = {
|
|
10
|
+
OK: 200,
|
|
11
|
+
CREATED: 201,
|
|
12
|
+
VALIDATION: 400,
|
|
13
|
+
UNAUTHORIZED: 401,
|
|
14
|
+
NOT_FOUND: 404,
|
|
15
|
+
PREMIUM_REQUIRED: 1002,
|
|
16
|
+
INSUFFICIENT_CREDITS: 1003,
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqCH,uDAAuD;AACvD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,EAAE,EAAE,GAAG;IACP,OAAO,EAAE,GAAG;IACZ,UAAU,EAAE,GAAG;IACf,YAAY,EAAE,GAAG;IACjB,SAAS,EAAE,GAAG;IACd,gBAAgB,EAAE,IAAI;IACtB,oBAAoB,EAAE,IAAI;CAClB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ClipUgcConfig } from '../types/index.js';
|
|
2
|
+
export declare const DEFAULT_API_BASE_URL = "https://clipugc.com/api/v1";
|
|
3
|
+
export declare function getConfigDir(): string;
|
|
4
|
+
export declare function getConfigPath(): string;
|
|
5
|
+
export declare function getDefaultConfig(): ClipUgcConfig;
|
|
6
|
+
export declare function loadConfig(): Promise<ClipUgcConfig>;
|
|
7
|
+
export declare function saveConfig(config: ClipUgcConfig): Promise<void>;
|
|
8
|
+
export declare function getConfigValue(key: keyof ClipUgcConfig): Promise<string>;
|
|
9
|
+
export declare function setConfigValue(key: keyof ClipUgcConfig, value: string): Promise<void>;
|
|
10
|
+
export declare function isValidConfigKey(key: string): key is keyof ClipUgcConfig;
|
|
11
|
+
export declare function getConfigKeys(): (keyof ClipUgcConfig)[];
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the effective API key: env var CLIPUGC_API_KEY wins, else config file.
|
|
14
|
+
* Returns empty string when not set.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveApiKey(): Promise<string>;
|
|
17
|
+
/** Resolve the effective API base URL: env CLIPUGC_API_BASE_URL wins, else config. */
|
|
18
|
+
export declare function resolveApiBaseUrl(): Promise<string>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), '.config', 'clipugc');
|
|
5
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
6
|
+
export const DEFAULT_API_BASE_URL = 'https://clipugc.com/api/v1';
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
9
|
+
apiKey: '',
|
|
10
|
+
email: '',
|
|
11
|
+
};
|
|
12
|
+
export function getConfigDir() {
|
|
13
|
+
return CONFIG_DIR;
|
|
14
|
+
}
|
|
15
|
+
export function getConfigPath() {
|
|
16
|
+
return process.env.CLIPUGC_CONFIG_PATH ?? CONFIG_FILE;
|
|
17
|
+
}
|
|
18
|
+
export function getDefaultConfig() {
|
|
19
|
+
return { ...DEFAULT_CONFIG };
|
|
20
|
+
}
|
|
21
|
+
export async function loadConfig() {
|
|
22
|
+
try {
|
|
23
|
+
const file = getConfigPath();
|
|
24
|
+
if (await fs.pathExists(file)) {
|
|
25
|
+
const raw = await fs.readJson(file);
|
|
26
|
+
return { ...DEFAULT_CONFIG, ...raw };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Corrupt or unreadable config — fall back to defaults
|
|
31
|
+
}
|
|
32
|
+
return { ...DEFAULT_CONFIG };
|
|
33
|
+
}
|
|
34
|
+
export async function saveConfig(config) {
|
|
35
|
+
const file = getConfigPath();
|
|
36
|
+
await fs.ensureDir(path.dirname(file));
|
|
37
|
+
await fs.writeJson(file, config, { spaces: 2 });
|
|
38
|
+
}
|
|
39
|
+
export async function getConfigValue(key) {
|
|
40
|
+
const config = await loadConfig();
|
|
41
|
+
return config[key];
|
|
42
|
+
}
|
|
43
|
+
export async function setConfigValue(key, value) {
|
|
44
|
+
const config = await loadConfig();
|
|
45
|
+
config[key] = value;
|
|
46
|
+
await saveConfig(config);
|
|
47
|
+
}
|
|
48
|
+
export function isValidConfigKey(key) {
|
|
49
|
+
return key in DEFAULT_CONFIG;
|
|
50
|
+
}
|
|
51
|
+
export function getConfigKeys() {
|
|
52
|
+
return Object.keys(DEFAULT_CONFIG);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the effective API key: env var CLIPUGC_API_KEY wins, else config file.
|
|
56
|
+
* Returns empty string when not set.
|
|
57
|
+
*/
|
|
58
|
+
export async function resolveApiKey() {
|
|
59
|
+
return process.env.CLIPUGC_API_KEY || (await loadConfig()).apiKey || '';
|
|
60
|
+
}
|
|
61
|
+
/** Resolve the effective API base URL: env CLIPUGC_API_BASE_URL wins, else config. */
|
|
62
|
+
export async function resolveApiBaseUrl() {
|
|
63
|
+
return process.env.CLIPUGC_API_BASE_URL || (await loadConfig()).apiBaseUrl || DEFAULT_API_BASE_URL;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,UAAU,CAAC;AAG1B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AACjE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,oBAAoB,GAAG,4BAA4B,CAAC;AAEjE,MAAM,cAAc,GAAkB;IACpC,UAAU,EAAE,oBAAoB;IAChC,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE;CACV,CAAC;AAEF,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,WAAW,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;QAC7B,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpC,OAAO,EAAE,GAAG,cAAc,EAAE,GAAG,GAAG,EAAE,CAAC;QACvC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;IACzD,CAAC;IACD,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAqB;IACpD,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAwB;IAC3D,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAwB,EAAE,KAAa;IAC1E,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACpB,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG,IAAI,cAAc,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,cAAc,CAA4B,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC;AAC1E,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,oBAAoB,CAAC;AACrG,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed errors thrown by services. Services NEVER call process.exit —
|
|
3
|
+
* only src/index.ts maps errors to exit codes.
|
|
4
|
+
*/
|
|
5
|
+
/** Exit codes used by the CLI entry point. */
|
|
6
|
+
export declare const EXIT_CODES: {
|
|
7
|
+
readonly OK: 0;
|
|
8
|
+
readonly GENERIC: 1;
|
|
9
|
+
readonly VALIDATION: 2;
|
|
10
|
+
readonly AUTH: 3;
|
|
11
|
+
readonly NOT_FOUND: 4;
|
|
12
|
+
readonly PREMIUM_REQUIRED: 5;
|
|
13
|
+
readonly INSUFFICIENT_CREDITS: 6;
|
|
14
|
+
readonly NETWORK: 7;
|
|
15
|
+
};
|
|
16
|
+
export declare class CliError extends Error {
|
|
17
|
+
readonly exitCode: number;
|
|
18
|
+
constructor(message: string, exitCode?: number);
|
|
19
|
+
}
|
|
20
|
+
/** Generic API failure (unexpected status code or malformed response). */
|
|
21
|
+
export declare class ApiError extends CliError {
|
|
22
|
+
readonly statusCode: number | undefined;
|
|
23
|
+
constructor(message: string, statusCode?: number, exitCode?: number);
|
|
24
|
+
}
|
|
25
|
+
/** 400 — request validation failed. */
|
|
26
|
+
export declare class ValidationError extends ApiError {
|
|
27
|
+
constructor(message: string);
|
|
28
|
+
}
|
|
29
|
+
/** 401 — missing/invalid API key. */
|
|
30
|
+
export declare class AuthError extends ApiError {
|
|
31
|
+
constructor(message?: string);
|
|
32
|
+
}
|
|
33
|
+
/** 404 — resource not found. */
|
|
34
|
+
export declare class NotFoundError extends ApiError {
|
|
35
|
+
constructor(message?: string);
|
|
36
|
+
}
|
|
37
|
+
/** 1002 — premium subscription required. */
|
|
38
|
+
export declare class PremiumRequiredError extends ApiError {
|
|
39
|
+
constructor(message?: string);
|
|
40
|
+
}
|
|
41
|
+
/** 1003 — insufficient credits. */
|
|
42
|
+
export declare class InsufficientCreditsError extends ApiError {
|
|
43
|
+
constructor(message?: string);
|
|
44
|
+
}
|
|
45
|
+
/** Network-level failure (server unreachable, DNS, timeout). */
|
|
46
|
+
export declare class NetworkError extends CliError {
|
|
47
|
+
constructor(message: string);
|
|
48
|
+
}
|
|
49
|
+
/** User aborted an interactive confirmation. */
|
|
50
|
+
export declare class AbortedError extends CliError {
|
|
51
|
+
constructor(message?: string);
|
|
52
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed errors thrown by services. Services NEVER call process.exit —
|
|
3
|
+
* only src/index.ts maps errors to exit codes.
|
|
4
|
+
*/
|
|
5
|
+
/** Exit codes used by the CLI entry point. */
|
|
6
|
+
export const EXIT_CODES = {
|
|
7
|
+
OK: 0,
|
|
8
|
+
GENERIC: 1,
|
|
9
|
+
VALIDATION: 2,
|
|
10
|
+
AUTH: 3,
|
|
11
|
+
NOT_FOUND: 4,
|
|
12
|
+
PREMIUM_REQUIRED: 5,
|
|
13
|
+
INSUFFICIENT_CREDITS: 6,
|
|
14
|
+
NETWORK: 7,
|
|
15
|
+
};
|
|
16
|
+
export class CliError extends Error {
|
|
17
|
+
exitCode;
|
|
18
|
+
constructor(message, exitCode = EXIT_CODES.GENERIC) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = new.target.name;
|
|
21
|
+
this.exitCode = exitCode;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Generic API failure (unexpected status code or malformed response). */
|
|
25
|
+
export class ApiError extends CliError {
|
|
26
|
+
statusCode;
|
|
27
|
+
constructor(message, statusCode, exitCode = EXIT_CODES.GENERIC) {
|
|
28
|
+
super(message, exitCode);
|
|
29
|
+
this.statusCode = statusCode;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** 400 — request validation failed. */
|
|
33
|
+
export class ValidationError extends ApiError {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message, 400, EXIT_CODES.VALIDATION);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** 401 — missing/invalid API key. */
|
|
39
|
+
export class AuthError extends ApiError {
|
|
40
|
+
constructor(message = 'Unauthorized. Run `clipugc auth login` with a valid API key.') {
|
|
41
|
+
super(message, 401, EXIT_CODES.AUTH);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** 404 — resource not found. */
|
|
45
|
+
export class NotFoundError extends ApiError {
|
|
46
|
+
constructor(message = 'Resource not found.') {
|
|
47
|
+
super(message, 404, EXIT_CODES.NOT_FOUND);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** 1002 — premium subscription required. */
|
|
51
|
+
export class PremiumRequiredError extends ApiError {
|
|
52
|
+
constructor(message = 'This feature requires a premium subscription. Upgrade at https://clipugc.com') {
|
|
53
|
+
super(message, 1002, EXIT_CODES.PREMIUM_REQUIRED);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** 1003 — insufficient credits. */
|
|
57
|
+
export class InsufficientCreditsError extends ApiError {
|
|
58
|
+
constructor(message = 'Insufficient credits. Top up at https://clipugc.com') {
|
|
59
|
+
super(message, 1003, EXIT_CODES.INSUFFICIENT_CREDITS);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Network-level failure (server unreachable, DNS, timeout). */
|
|
63
|
+
export class NetworkError extends CliError {
|
|
64
|
+
constructor(message) {
|
|
65
|
+
super(message, EXIT_CODES.NETWORK);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** User aborted an interactive confirmation. */
|
|
69
|
+
export class AbortedError extends CliError {
|
|
70
|
+
constructor(message = 'Aborted.') {
|
|
71
|
+
super(message, EXIT_CODES.OK);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,8CAA8C;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,CAAC;IACL,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,IAAI,EAAE,CAAC;IACP,SAAS,EAAE,CAAC;IACZ,gBAAgB,EAAE,CAAC;IACnB,oBAAoB,EAAE,CAAC;IACvB,OAAO,EAAE,CAAC;CACF,CAAC;AAEX,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,QAAQ,CAAS;IAE1B,YAAY,OAAe,EAAE,WAAmB,UAAU,CAAC,OAAO;QAChE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,0EAA0E;AAC1E,MAAM,OAAO,QAAS,SAAQ,QAAQ;IAC3B,UAAU,CAAqB;IAExC,YAAY,OAAe,EAAE,UAAmB,EAAE,WAAmB,UAAU,CAAC,OAAO;QACrF,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,uCAAuC;AACvC,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;IAC7C,CAAC;CACF;AAED,qCAAqC;AACrC,MAAM,OAAO,SAAU,SAAQ,QAAQ;IACrC,YAAY,OAAO,GAAG,8DAA8D;QAClF,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;CACF;AAED,gCAAgC;AAChC,MAAM,OAAO,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAO,GAAG,qBAAqB;QACzC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;CACF;AAED,4CAA4C;AAC5C,MAAM,OAAO,oBAAqB,SAAQ,QAAQ;IAChD,YAAY,OAAO,GAAG,8EAA8E;QAClG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACpD,CAAC;CACF;AAED,mCAAmC;AACnC,MAAM,OAAO,wBAAyB,SAAQ,QAAQ;IACpD,YAAY,OAAO,GAAG,qDAAqD;QACzE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC;IACxD,CAAC;CACF;AAED,gEAAgE;AAChE,MAAM,OAAO,YAAa,SAAQ,QAAQ;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;CACF;AAED,gDAAgD;AAChD,MAAM,OAAO,YAAa,SAAQ,QAAQ;IACxC,YAAY,OAAO,GAAG,UAAU;QAC9B,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const logger: {
|
|
2
|
+
success(message: string): void;
|
|
3
|
+
info(message: string): void;
|
|
4
|
+
warn(message: string): void;
|
|
5
|
+
error(message: string): void;
|
|
6
|
+
plain(message: string): void;
|
|
7
|
+
/** Dim helper hint, e.g. next-command suggestions. */
|
|
8
|
+
hint(message: string): void;
|
|
9
|
+
/** Key/value detail line used in `show` commands. */
|
|
10
|
+
kv(key: string, value: string | number | boolean | null | undefined): void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export const logger = {
|
|
3
|
+
success(message) {
|
|
4
|
+
console.log(chalk.green('✔ ') + message);
|
|
5
|
+
},
|
|
6
|
+
info(message) {
|
|
7
|
+
console.log(chalk.gray('- ') + message);
|
|
8
|
+
},
|
|
9
|
+
warn(message) {
|
|
10
|
+
console.log(chalk.yellow('! ') + message);
|
|
11
|
+
},
|
|
12
|
+
error(message) {
|
|
13
|
+
console.error(chalk.red('✖ ') + message);
|
|
14
|
+
},
|
|
15
|
+
plain(message) {
|
|
16
|
+
console.log(message);
|
|
17
|
+
},
|
|
18
|
+
/** Dim helper hint, e.g. next-command suggestions. */
|
|
19
|
+
hint(message) {
|
|
20
|
+
console.log(chalk.dim(message));
|
|
21
|
+
},
|
|
22
|
+
/** Key/value detail line used in `show` commands. */
|
|
23
|
+
kv(key, value) {
|
|
24
|
+
const v = value === null || value === undefined || value === '' ? chalk.dim('—') : String(value);
|
|
25
|
+
console.log(` ${chalk.cyan(key.padEnd(18))} ${v}`);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,OAAO,CAAC,OAAe;QACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,sDAAsD;IACtD,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,qDAAqD;IACrD,EAAE,CAAC,GAAW,EAAE,KAAmD;QACjE,MAAM,CAAC,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACjG,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { Pagination } from '../types/index.js';
|
|
3
|
+
/** True when the global --json flag is set (for scripting). */
|
|
4
|
+
export declare function isJsonMode(command: Command): boolean;
|
|
5
|
+
/** Print raw JSON data (the `data` part of the envelope) for --json mode. */
|
|
6
|
+
export declare function printJson(data: unknown): void;
|
|
7
|
+
export interface Column<T> {
|
|
8
|
+
header: string;
|
|
9
|
+
value: (row: T) => string | number | boolean | null | undefined;
|
|
10
|
+
}
|
|
11
|
+
/** Render a simple aligned text table for humans. */
|
|
12
|
+
export declare function printTable<T>(rows: T[], columns: Column<T>[]): void;
|
|
13
|
+
/** Print a "page X of Y (N total)" footer for paginated lists. */
|
|
14
|
+
export declare function printPagination(pagination: Pagination | undefined): void;
|
|
15
|
+
/** Colorize a pending|processing|completed|failed status. */
|
|
16
|
+
export declare function formatStatus(status: string): string;
|