@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.
- package/LICENSE +21 -0
- package/README.md +429 -0
- package/dist/api/articles.d.ts +101 -0
- package/dist/api/articles.js +40 -0
- package/dist/api/client.d.ts +2 -0
- package/dist/api/client.js +48 -0
- package/dist/api/export.d.ts +6 -0
- package/dist/api/export.js +24 -0
- package/dist/api/folders.d.ts +18 -0
- package/dist/api/folders.js +14 -0
- package/dist/api/generate.d.ts +15 -0
- package/dist/api/generate.js +23 -0
- package/dist/api/project.d.ts +43 -0
- package/dist/api/project.js +19 -0
- package/dist/api/publish.d.ts +31 -0
- package/dist/api/publish.js +23 -0
- package/dist/api/templates.d.ts +17 -0
- package/dist/api/templates.js +8 -0
- package/dist/api/videos.d.ts +48 -0
- package/dist/api/videos.js +30 -0
- package/dist/commands/articles.d.ts +3 -0
- package/dist/commands/articles.js +297 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +73 -0
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.js +76 -0
- package/dist/commands/folders.d.ts +3 -0
- package/dist/commands/folders.js +108 -0
- package/dist/commands/generate-batch.d.ts +3 -0
- package/dist/commands/generate-batch.js +158 -0
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.js +83 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +25 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.js +112 -0
- package/dist/commands/publish.d.ts +3 -0
- package/dist/commands/publish.js +74 -0
- package/dist/commands/templates.d.ts +3 -0
- package/dist/commands/templates.js +50 -0
- package/dist/commands/videos.d.ts +3 -0
- package/dist/commands/videos.js +176 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +31 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +56 -0
- package/dist/output.d.ts +3 -0
- package/dist/output.js +43 -0
- package/dist/poll.d.ts +2 -0
- package/dist/poll.js +23 -0
- 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,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,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
|
+
}
|