@hintoai/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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +429 -0
  3. package/dist/api/articles.d.ts +101 -0
  4. package/dist/api/articles.js +40 -0
  5. package/dist/api/client.d.ts +2 -0
  6. package/dist/api/client.js +48 -0
  7. package/dist/api/export.d.ts +6 -0
  8. package/dist/api/export.js +24 -0
  9. package/dist/api/folders.d.ts +18 -0
  10. package/dist/api/folders.js +14 -0
  11. package/dist/api/generate.d.ts +15 -0
  12. package/dist/api/generate.js +23 -0
  13. package/dist/api/project.d.ts +43 -0
  14. package/dist/api/project.js +19 -0
  15. package/dist/api/publish.d.ts +31 -0
  16. package/dist/api/publish.js +23 -0
  17. package/dist/api/templates.d.ts +17 -0
  18. package/dist/api/templates.js +8 -0
  19. package/dist/api/videos.d.ts +48 -0
  20. package/dist/api/videos.js +30 -0
  21. package/dist/commands/articles.d.ts +3 -0
  22. package/dist/commands/articles.js +297 -0
  23. package/dist/commands/completion.d.ts +2 -0
  24. package/dist/commands/completion.js +73 -0
  25. package/dist/commands/export.d.ts +3 -0
  26. package/dist/commands/export.js +76 -0
  27. package/dist/commands/folders.d.ts +3 -0
  28. package/dist/commands/folders.js +108 -0
  29. package/dist/commands/generate-batch.d.ts +3 -0
  30. package/dist/commands/generate-batch.js +158 -0
  31. package/dist/commands/generate.d.ts +3 -0
  32. package/dist/commands/generate.js +83 -0
  33. package/dist/commands/init.d.ts +3 -0
  34. package/dist/commands/init.js +25 -0
  35. package/dist/commands/project.d.ts +3 -0
  36. package/dist/commands/project.js +112 -0
  37. package/dist/commands/publish.d.ts +3 -0
  38. package/dist/commands/publish.js +74 -0
  39. package/dist/commands/templates.d.ts +3 -0
  40. package/dist/commands/templates.js +50 -0
  41. package/dist/commands/videos.d.ts +3 -0
  42. package/dist/commands/videos.js +176 -0
  43. package/dist/config.d.ts +7 -0
  44. package/dist/config.js +31 -0
  45. package/dist/errors.d.ts +5 -0
  46. package/dist/errors.js +16 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +56 -0
  49. package/dist/output.d.ts +3 -0
  50. package/dist/output.js +43 -0
  51. package/dist/poll.d.ts +2 -0
  52. package/dist/poll.js +23 -0
  53. package/package.json +68 -0
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.foldersApi = void 0;
4
+ const foldersApi = (client) => ({
5
+ list: () => client.get('/folders').then((r) => r.data),
6
+ get: (id) => client.get(`/folders/${id}`).then((r) => r.data),
7
+ create: (name, parentId) => client
8
+ .post('/folders', { name, ...(parentId !== undefined ? { parentId } : {}) })
9
+ .then((r) => r.data),
10
+ update: (id, name) => client.put(`/folders/${id}`, { name }).then((r) => r.data),
11
+ delete: (id) => client.delete(`/folders/${id}`).then((r) => r.data),
12
+ move: (id, parentId) => client.patch(`/folders/${id}/move`, { parentId }).then((r) => r.data),
13
+ });
14
+ exports.foldersApi = foldersApi;
@@ -0,0 +1,15 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export interface Job {
3
+ jobId: string;
4
+ type: string;
5
+ status: 'pending' | 'processing' | 'completed' | 'failed';
6
+ output: unknown | null;
7
+ error?: string | null;
8
+ createdAt: string;
9
+ completedAt: string | null;
10
+ }
11
+ export declare const generateApi: (client: AxiosInstance) => {
12
+ start: (videoId: string, templateId?: number, callbackUrl?: string, callbackSecret?: string) => Promise<Job>;
13
+ status: (jobId: string) => Promise<Job>;
14
+ structure: (videoId: string, templateId?: number, callbackUrl?: string, callbackSecret?: string) => Promise<Job>;
15
+ };
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateApi = void 0;
4
+ const generateApi = (client) => ({
5
+ start: (videoId, templateId, callbackUrl, callbackSecret) => client
6
+ .post('/generate', {
7
+ videoId,
8
+ ...(templateId !== undefined ? { templateId } : {}),
9
+ ...(callbackUrl ? { callbackUrl } : {}),
10
+ ...(callbackSecret ? { callbackSecret } : {}),
11
+ })
12
+ .then((r) => r.data),
13
+ status: (jobId) => client.get(`/generate/${jobId}`).then((r) => r.data),
14
+ structure: (videoId, templateId, callbackUrl, callbackSecret) => client
15
+ .post('/generate/structure', {
16
+ videoId,
17
+ ...(templateId !== undefined ? { templateId } : {}),
18
+ ...(callbackUrl ? { callbackUrl } : {}),
19
+ ...(callbackSecret ? { callbackSecret } : {}),
20
+ })
21
+ .then((r) => r.data),
22
+ });
23
+ exports.generateApi = generateApi;
@@ -0,0 +1,43 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export interface Project {
3
+ id: string;
4
+ name: string;
5
+ urlSlug: string;
6
+ description: string | null;
7
+ language: string;
8
+ isPublished: boolean;
9
+ logoUrl: string | null;
10
+ projectType: string;
11
+ isArchived: boolean;
12
+ createdAt: string;
13
+ customDomain: string | null;
14
+ customDomainVerified: boolean;
15
+ }
16
+ export interface Language {
17
+ code: string;
18
+ label: string;
19
+ createdAt: string;
20
+ translationRules: unknown | null;
21
+ totalArticles: number;
22
+ translatedArticles: number;
23
+ isTranslating: boolean;
24
+ }
25
+ export declare const projectApi: (client: AxiosInstance) => {
26
+ get: () => Promise<{
27
+ project: Project;
28
+ }>;
29
+ update: (body: {
30
+ name?: string;
31
+ }) => Promise<{
32
+ project: Project;
33
+ }>;
34
+ structure: () => Promise<unknown>;
35
+ listLanguages: () => Promise<{
36
+ languages: Language[];
37
+ }>;
38
+ retranslate: (code: string, callbackUrl?: string, callbackSecret?: string) => Promise<import("./generate").Job>;
39
+ addLanguage: (languageCode: string) => Promise<{
40
+ languageCode: string;
41
+ message: string;
42
+ }>;
43
+ };
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectApi = void 0;
4
+ const projectApi = (client) => ({
5
+ get: () => client.get('/project').then((r) => r.data),
6
+ update: (body) => client.patch('/project', body).then((r) => r.data),
7
+ structure: () => client.get('/project/structure').then((r) => r.data),
8
+ listLanguages: () => client.get('/project/languages').then((r) => r.data),
9
+ retranslate: (code, callbackUrl, callbackSecret) => client
10
+ .post(`/project/languages/${code}/retranslate`, {
11
+ ...(callbackUrl ? { callbackUrl } : {}),
12
+ ...(callbackSecret ? { callbackSecret } : {}),
13
+ })
14
+ .then((r) => r.data),
15
+ addLanguage: (languageCode) => client
16
+ .post('/project/languages', { languageCode })
17
+ .then((r) => r.data),
18
+ });
19
+ exports.projectApi = projectApi;
@@ -0,0 +1,31 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ interface PublishStatusResponse {
3
+ isPublished: boolean;
4
+ slug: string | null;
5
+ url: string | null;
6
+ publicationId: string | null;
7
+ publishedAt: string | null;
8
+ articlesCount: number | null;
9
+ foldersCount: number | null;
10
+ }
11
+ export interface PublishStatus extends PublishStatusResponse {
12
+ status: 'published' | 'unpublished';
13
+ }
14
+ export declare const publishApi: (client: AxiosInstance) => {
15
+ now: (callbackUrl?: string, callbackSecret?: string) => Promise<import("./generate").Job>;
16
+ republish: (callbackUrl?: string, callbackSecret?: string) => Promise<import("./generate").Job>;
17
+ status: () => Promise<{
18
+ status: "published" | "unpublished";
19
+ isPublished: boolean;
20
+ slug: string | null;
21
+ url: string | null;
22
+ publicationId: string | null;
23
+ publishedAt: string | null;
24
+ articlesCount: number | null;
25
+ foldersCount: number | null;
26
+ }>;
27
+ unpublish: () => Promise<{
28
+ message: string;
29
+ }>;
30
+ };
31
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.publishApi = void 0;
4
+ const publishApi = (client) => ({
5
+ now: (callbackUrl, callbackSecret) => client
6
+ .post('/publish', {
7
+ ...(callbackUrl ? { callbackUrl } : {}),
8
+ ...(callbackSecret ? { callbackSecret } : {}),
9
+ })
10
+ .then((r) => r.data),
11
+ republish: (callbackUrl, callbackSecret) => client
12
+ .post('/publish/republish', {
13
+ ...(callbackUrl ? { callbackUrl } : {}),
14
+ ...(callbackSecret ? { callbackSecret } : {}),
15
+ })
16
+ .then((r) => r.data),
17
+ status: () => client.get('/publish/status').then((r) => ({
18
+ ...r.data,
19
+ status: (r.data.isPublished ? 'published' : 'unpublished'),
20
+ })),
21
+ unpublish: () => client.delete('/publish').then((r) => r.data),
22
+ });
23
+ exports.publishApi = publishApi;
@@ -0,0 +1,17 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export interface Template {
3
+ id: number;
4
+ name: string;
5
+ description?: string | null;
6
+ requires_video: boolean;
7
+ image_url?: string | null;
8
+ sort_order?: number | null;
9
+ }
10
+ export declare const templatesApi: (client: AxiosInstance) => {
11
+ articleTemplates: () => Promise<{
12
+ templates: Template[];
13
+ }>;
14
+ structureTemplates: () => Promise<{
15
+ templates: Template[];
16
+ }>;
17
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.templatesApi = void 0;
4
+ const templatesApi = (client) => ({
5
+ articleTemplates: () => client.get('/templates/article').then((r) => r.data),
6
+ structureTemplates: () => client.get('/templates/structure').then((r) => r.data),
7
+ });
8
+ exports.templatesApi = templatesApi;
@@ -0,0 +1,48 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export interface VideoListItem {
3
+ videoId: string;
4
+ filename?: string | null;
5
+ durationSeconds?: number | null;
6
+ status: string;
7
+ createdAt: string;
8
+ }
9
+ export interface VideoListParams {
10
+ offset?: number;
11
+ limit?: number;
12
+ }
13
+ export declare const videosApi: (client: AxiosInstance) => {
14
+ list: (params?: VideoListParams) => Promise<{
15
+ videos: VideoListItem[];
16
+ pagination: {
17
+ limit: number;
18
+ offset: number;
19
+ count: number;
20
+ };
21
+ }>;
22
+ get: (videoId: string) => Promise<{
23
+ videoId: string;
24
+ filename?: string | null;
25
+ status: string;
26
+ durationSeconds?: number | null;
27
+ createdAt: string;
28
+ }>;
29
+ status: (videoId: string) => Promise<{
30
+ videoId: string;
31
+ filename?: string | null;
32
+ status: string;
33
+ durationSeconds?: number | null;
34
+ createdAt: string;
35
+ }>;
36
+ import: (url: string, name?: string, callbackUrl?: string, callbackSecret?: string) => Promise<import("./generate").Job>;
37
+ uploadPresigned: (filename: string, contentType: string) => Promise<{
38
+ videoId: string;
39
+ uploadUrl: string;
40
+ s3Url: string;
41
+ key: string;
42
+ expiresIn: number;
43
+ }>;
44
+ uploadComplete: (videoId: string, key: string, filename: string) => Promise<{
45
+ videoId: string;
46
+ }>;
47
+ delete: (videoId: string) => Promise<any>;
48
+ };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.videosApi = void 0;
4
+ const videosApi = (client) => ({
5
+ list: (params) => client
6
+ .get('/videos', { params })
7
+ .then((r) => r.data),
8
+ get: (videoId) => client
9
+ .get(`/videos/${videoId}`)
10
+ .then((r) => r.data),
11
+ status: (videoId) => client
12
+ .get(`/videos/${videoId}/status`)
13
+ .then((r) => r.data),
14
+ import: (url, name, callbackUrl, callbackSecret) => client
15
+ .post('/videos/import', {
16
+ url,
17
+ ...(name ? { name } : {}),
18
+ ...(callbackUrl ? { callbackUrl } : {}),
19
+ ...(callbackSecret ? { callbackSecret } : {}),
20
+ })
21
+ .then((r) => r.data),
22
+ uploadPresigned: (filename, contentType) => client
23
+ .post('/videos/upload/presigned', { filename, contentType })
24
+ .then((r) => r.data),
25
+ uploadComplete: (videoId, key, filename) => client
26
+ .post('/videos/upload/complete', { key, fileId: videoId, filename })
27
+ .then((r) => r.data),
28
+ delete: (videoId) => client.delete(`/videos/${videoId}`).then((r) => r.data),
29
+ });
30
+ exports.videosApi = videosApi;
@@ -0,0 +1,3 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ import type { Command } from 'commander';
3
+ export declare function registerArticles(program: Command, client: AxiosInstance): void;
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerArticles = registerArticles;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const articles_1 = require("../api/articles");
10
+ const errors_1 = require("../errors");
11
+ const output_1 = require("../output");
12
+ function resolveContent(raw) {
13
+ if (!raw.startsWith('@'))
14
+ return raw;
15
+ const filePath = path_1.default.resolve(raw.slice(1));
16
+ return fs_1.default.readFileSync(filePath, 'utf-8');
17
+ }
18
+ function registerArticles(program, client) {
19
+ const articles = program.command('articles').description('Manage articles');
20
+ const api = (0, articles_1.articlesApi)(client);
21
+ articles
22
+ .command('list')
23
+ .description('List articles')
24
+ .option('--folder <id>', 'Filter by folder ID')
25
+ .option('--offset <n>', 'Number of articles to skip', '0')
26
+ .option('--limit <n>', 'Results per page', '20')
27
+ .option('--json', 'Output as JSON')
28
+ .action(async (opts) => {
29
+ try {
30
+ const data = await api.list({
31
+ folderId: opts.folder,
32
+ offset: Number(opts.offset),
33
+ limit: Number(opts.limit),
34
+ });
35
+ if (opts.json)
36
+ return (0, output_1.printJson)(data);
37
+ (0, output_1.printTable)(['ID', 'Title', 'Slug', 'Folder', 'Updated'], data.articles.map((a) => [
38
+ String(a.id),
39
+ a.title,
40
+ a.slug ?? '(none)',
41
+ a.folderId != null ? String(a.folderId) : '—',
42
+ a.updatedAt,
43
+ ]));
44
+ }
45
+ catch (e) {
46
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
47
+ }
48
+ });
49
+ articles
50
+ .command('get <id>')
51
+ .description('Get an article by ID')
52
+ .option('--format <fmt>', 'Content format: markdown (default) or html', 'markdown')
53
+ .option('--json', 'Output as JSON')
54
+ .action(async (id, opts) => {
55
+ try {
56
+ const fmt = opts.format === 'html' ? 'html' : 'markdown';
57
+ const data = await api.get(id, fmt);
58
+ if (opts.json)
59
+ return (0, output_1.printJson)(data);
60
+ (0, output_1.printKeyValue)(data);
61
+ }
62
+ catch (e) {
63
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
64
+ }
65
+ });
66
+ articles
67
+ .command('create')
68
+ .description('Create an article')
69
+ .requiredOption('--title <title>', 'Article title')
70
+ .requiredOption('--content <content>', 'Markdown content string or @filepath')
71
+ .option('--folder <id>', 'Folder ID')
72
+ .option('--json', 'Output as JSON')
73
+ .action(async (opts) => {
74
+ try {
75
+ const data = await api.create({
76
+ title: opts.title,
77
+ content: resolveContent(opts.content),
78
+ folderId: opts.folder,
79
+ });
80
+ if (opts.json)
81
+ return (0, output_1.printJson)(data);
82
+ (0, output_1.printKeyValue)(data);
83
+ }
84
+ catch (e) {
85
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
86
+ }
87
+ });
88
+ articles
89
+ .command('create-empty')
90
+ .description('Create an empty article (no content required)')
91
+ .option('--title <title>', 'Article title')
92
+ .option('--folder <id>', 'Folder ID')
93
+ .option('--json', 'Output as JSON')
94
+ .action(async (opts) => {
95
+ try {
96
+ const data = await api.createEmpty({
97
+ ...(opts.title !== undefined && { title: opts.title }),
98
+ ...(opts.folder !== undefined && { folderId: Number(opts.folder) }),
99
+ });
100
+ if (opts.json)
101
+ return (0, output_1.printJson)(data);
102
+ (0, output_1.printKeyValue)(data);
103
+ }
104
+ catch (e) {
105
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
106
+ }
107
+ });
108
+ articles
109
+ .command('update <id>')
110
+ .description('Update an article')
111
+ .option('--title <title>', 'New title')
112
+ .option('--slug <slug>', 'New slug')
113
+ .option('--meta-description <text>', 'SEO meta description')
114
+ .option('--meta-keywords <keywords>', 'Comma-separated SEO keywords')
115
+ .option('--json', 'Output as JSON')
116
+ .action(async (id, opts) => {
117
+ try {
118
+ if (!opts.title && !opts.slug && !opts.metaDescription && !opts.metaKeywords) {
119
+ (0, errors_1.exitWithError)('Provide at least one field to update: --title, --slug, --meta-description, or --meta-keywords');
120
+ return;
121
+ }
122
+ const data = await api.update(id, {
123
+ ...(opts.title !== undefined && { title: opts.title }),
124
+ ...(opts.slug !== undefined && { slug: opts.slug }),
125
+ ...(opts.metaDescription !== undefined && { metaDescription: opts.metaDescription }),
126
+ ...(opts.metaKeywords !== undefined && {
127
+ metaKeywords: opts.metaKeywords
128
+ .split(',')
129
+ .map((k) => k.trim())
130
+ .filter(Boolean),
131
+ }),
132
+ });
133
+ if (opts.json)
134
+ return (0, output_1.printJson)(data);
135
+ (0, output_1.printKeyValue)(data);
136
+ }
137
+ catch (e) {
138
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
139
+ }
140
+ });
141
+ articles
142
+ .command('delete <id>')
143
+ .description('Delete an article')
144
+ .option('--json', 'Output as JSON')
145
+ .action(async (id, opts) => {
146
+ try {
147
+ await api.delete(id);
148
+ if (opts.json)
149
+ return (0, output_1.printJson)({ deleted: true });
150
+ process.stdout.write(`Article ${id} deleted.\n`);
151
+ }
152
+ catch (e) {
153
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
154
+ }
155
+ });
156
+ articles
157
+ .command('duplicate <id>')
158
+ .description('Duplicate an article')
159
+ .option('--json', 'Output as JSON')
160
+ .action(async (id, opts) => {
161
+ try {
162
+ const data = await api.duplicate(id);
163
+ if (opts.json)
164
+ return (0, output_1.printJson)(data);
165
+ (0, output_1.printKeyValue)(data);
166
+ }
167
+ catch (e) {
168
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
169
+ }
170
+ });
171
+ articles
172
+ .command('move <id>')
173
+ .description('Move an article to a folder, or to root if --folder is omitted')
174
+ .option('--folder <folderId>', 'Destination folder ID — omit to move to root')
175
+ .option('--json', 'Output as JSON')
176
+ .action(async (id, opts) => {
177
+ try {
178
+ const data = await api.move(id, opts.folder ?? null);
179
+ if (opts.json)
180
+ return (0, output_1.printJson)(data);
181
+ (0, output_1.printKeyValue)(data);
182
+ }
183
+ catch (e) {
184
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
185
+ }
186
+ });
187
+ articles
188
+ .command('regenerate <id>')
189
+ .description('Regenerate an article')
190
+ .option('--callback-url <url>', 'URL to receive a webhook when the job completes')
191
+ .option('--callback-secret <secret>', 'HMAC-SHA256 signing secret for the callback webhook')
192
+ .option('--json', 'Output as JSON')
193
+ .action(async (id, opts) => {
194
+ try {
195
+ const data = await api.regenerate(id, opts.callbackUrl, opts.callbackSecret);
196
+ if (opts.json)
197
+ return (0, output_1.printJson)(data);
198
+ (0, output_1.printKeyValue)(data);
199
+ }
200
+ catch (e) {
201
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
202
+ }
203
+ });
204
+ articles
205
+ .command('versions <id>')
206
+ .description('List article versions')
207
+ .option('--json', 'Output as JSON')
208
+ .action(async (id, opts) => {
209
+ try {
210
+ const data = await api.listVersions(id);
211
+ if (opts.json)
212
+ return (0, output_1.printJson)(data);
213
+ (0, output_1.printTable)(['Version ID', 'Version #', 'Created', 'Auto-save'], data.versions.map((v) => [
214
+ v.id,
215
+ String(v.versionNumber),
216
+ v.createdAt,
217
+ v.isAutoSave ? 'yes' : 'no',
218
+ ]));
219
+ }
220
+ catch (e) {
221
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
222
+ }
223
+ });
224
+ articles
225
+ .command('restore <id>')
226
+ .description('Restore an article version')
227
+ .requiredOption('--vid <vId>', 'Version ID to restore')
228
+ .option('--json', 'Output as JSON')
229
+ .action(async (id, opts) => {
230
+ try {
231
+ const data = await api.restoreVersion(id, opts.vid);
232
+ if (opts.json)
233
+ return (0, output_1.printJson)(data);
234
+ (0, output_1.printKeyValue)(data);
235
+ }
236
+ catch (e) {
237
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
238
+ }
239
+ });
240
+ articles
241
+ .command('translations <id>')
242
+ .description('List article translations')
243
+ .option('--json', 'Output as JSON')
244
+ .action(async (id, opts) => {
245
+ try {
246
+ const data = await api.listTranslations(id);
247
+ if (opts.json)
248
+ return (0, output_1.printJson)(data);
249
+ (0, output_1.printTable)(['Language', 'Status', 'Title', 'Has Content', 'Updated'], data.translations.map((t) => [
250
+ t.languageCode,
251
+ t.status,
252
+ t.title ?? '—',
253
+ t.hasContent ? 'yes' : 'no',
254
+ t.updatedAt,
255
+ ]));
256
+ }
257
+ catch (e) {
258
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
259
+ }
260
+ });
261
+ articles
262
+ .command('translate <id>')
263
+ .description('Get a specific translation')
264
+ .requiredOption('--lang <code>', 'Language code (e.g. en, fr)')
265
+ .option('--format <format>', 'Content format: markdown or html', /^(markdown|html)$/, 'markdown')
266
+ .option('--json', 'Output as JSON')
267
+ .action(async (id, opts) => {
268
+ try {
269
+ const fmt = opts.format === 'html' ? 'html' : 'markdown';
270
+ const data = await api.getTranslation(id, opts.lang, fmt);
271
+ if (opts.json)
272
+ return (0, output_1.printJson)(data);
273
+ (0, output_1.printKeyValue)(data);
274
+ }
275
+ catch (e) {
276
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
277
+ }
278
+ });
279
+ articles
280
+ .command('trigger-translate <id>')
281
+ .description('Trigger translation of an article to a language')
282
+ .requiredOption('--lang <code>', 'Target language code (e.g. fr, de, es)')
283
+ .option('--callback-url <url>', 'URL to receive a webhook when the job completes')
284
+ .option('--callback-secret <secret>', 'HMAC-SHA256 signing secret for the callback webhook')
285
+ .option('--json', 'Output as JSON')
286
+ .action(async (id, opts) => {
287
+ try {
288
+ const data = await api.triggerTranslate(id, opts.lang, opts.callbackUrl, opts.callbackSecret);
289
+ if (opts.json)
290
+ return (0, output_1.printJson)(data);
291
+ process.stdout.write(`Translation triggered. Job ID: ${data.jobId}\n`);
292
+ }
293
+ catch (e) {
294
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
295
+ }
296
+ });
297
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerCompletion(program: Command): void;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerCompletion = registerCompletion;
4
+ const GROUPS = [
5
+ 'videos',
6
+ 'articles',
7
+ 'folders',
8
+ 'generate',
9
+ 'project',
10
+ 'publish',
11
+ 'templates',
12
+ 'export',
13
+ 'init',
14
+ 'completion',
15
+ ];
16
+ const GLOBAL_FLAGS = ['--json', '--api-url', '--help', '--version'];
17
+ function bashScript() {
18
+ return `# hinto bash completion
19
+ _hinto() {
20
+ local cur groups flags
21
+ cur="\${COMP_WORDS[COMP_CWORD]}"
22
+ groups="${GROUPS.join(' ')}"
23
+ flags="${GLOBAL_FLAGS.join(' ')}"
24
+ if [ "\$COMP_CWORD" -eq 1 ]; then
25
+ COMPREPLY=( \$(compgen -W "\$groups" -- "\$cur") )
26
+ else
27
+ COMPREPLY=( \$(compgen -W "\$flags" -- "\$cur") )
28
+ fi
29
+ }
30
+ complete -F _hinto hinto
31
+ `;
32
+ }
33
+ function zshScript() {
34
+ return `#compdef hinto
35
+ # hinto zsh completion
36
+ _hinto() {
37
+ local -a groups
38
+ groups=(${GROUPS.join(' ')})
39
+ if (( CURRENT == 2 )); then
40
+ compadd -- \${groups[@]}
41
+ else
42
+ compadd -- ${GLOBAL_FLAGS.join(' ')}
43
+ fi
44
+ }
45
+ compdef _hinto hinto
46
+ `;
47
+ }
48
+ function fishScript() {
49
+ const groupLines = GROUPS.map((g) => `complete -c hinto -n "__fish_use_subcommand" -a "${g}"`).join('\n');
50
+ const flagLines = GLOBAL_FLAGS.map((f) => `complete -c hinto -l "${f.replace(/^--/, '')}"`).join('\n');
51
+ return `# hinto fish completion\n${groupLines}\n${flagLines}\n`;
52
+ }
53
+ function registerCompletion(program) {
54
+ program
55
+ .command('completion <shell>')
56
+ .description('Output a shell completion script (bash | zsh | fish)')
57
+ .action((shell) => {
58
+ switch (shell) {
59
+ case 'bash':
60
+ process.stdout.write(bashScript());
61
+ break;
62
+ case 'zsh':
63
+ process.stdout.write(zshScript());
64
+ break;
65
+ case 'fish':
66
+ process.stdout.write(fishScript());
67
+ break;
68
+ default:
69
+ process.stderr.write(`Unsupported shell: ${shell}. Use bash, zsh, or fish.\n`);
70
+ process.exit(1);
71
+ }
72
+ });
73
+ }
@@ -0,0 +1,3 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ import type { Command } from 'commander';
3
+ export declare function registerExport(program: Command, client: AxiosInstance): void;