@hintoai/cli 0.2.0 → 0.3.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.
|
@@ -21,10 +21,10 @@ _hinto() {
|
|
|
21
21
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
22
22
|
groups="${GROUPS.join(' ')}"
|
|
23
23
|
flags="${GLOBAL_FLAGS.join(' ')}"
|
|
24
|
-
if [ "
|
|
25
|
-
COMPREPLY=(
|
|
24
|
+
if [ "$COMP_CWORD" -eq 1 ]; then
|
|
25
|
+
COMPREPLY=( $(compgen -W "$groups" -- "$cur") )
|
|
26
26
|
else
|
|
27
|
-
COMPREPLY=(
|
|
27
|
+
COMPREPLY=( $(compgen -W "$flags" -- "$cur") )
|
|
28
28
|
fi
|
|
29
29
|
}
|
|
30
30
|
complete -F _hinto hinto
|
package/dist/commands/videos.js
CHANGED
|
@@ -37,9 +37,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.registerVideos = registerVideos;
|
|
40
|
+
const axios_1 = __importDefault(require("axios"));
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
41
42
|
const path = __importStar(require("path"));
|
|
42
|
-
const axios_1 = __importDefault(require("axios"));
|
|
43
43
|
const videos_1 = require("../api/videos");
|
|
44
44
|
const errors_1 = require("../errors");
|
|
45
45
|
const output_1 = require("../output");
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/output.js
CHANGED
|
@@ -13,7 +13,9 @@ function printJson(data) {
|
|
|
13
13
|
}
|
|
14
14
|
function printTable(headers, rows) {
|
|
15
15
|
const table = new cli_table3_1.default({ head: headers.map((h) => chalk_1.default.bold(h)) });
|
|
16
|
-
rows.forEach((row) =>
|
|
16
|
+
rows.forEach((row) => {
|
|
17
|
+
table.push(row);
|
|
18
|
+
});
|
|
17
19
|
process.stdout.write(`${table.toString()}\n`);
|
|
18
20
|
}
|
|
19
21
|
function printKeyValue(obj) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hintoai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Hinto AI CLI — manage videos, articles, and publishing via the Hinto API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"dist/"
|
|
11
11
|
],
|
|
12
12
|
"engines": {
|
|
13
|
-
"node": ">=
|
|
13
|
+
"node": ">=20"
|
|
14
14
|
},
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"homepage": "https://github.com/hintoai/hinto-cli#readme",
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"axios": "^1.7.0",
|
|
52
52
|
"chalk": "^4.1.2",
|
|
53
53
|
"cli-table3": "^0.6.5",
|
|
54
|
-
"commander": "^
|
|
54
|
+
"commander": "^15.0.0",
|
|
55
55
|
"ora": "^8.1.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@biomejs/biome": "
|
|
58
|
+
"@biomejs/biome": "2.4.16",
|
|
59
59
|
"@changesets/cli": "^2.31.0",
|
|
60
60
|
"@types/jest": "^30.0.0",
|
|
61
61
|
"@types/node": "^25.9.2",
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.registerGenerateBatch = registerGenerateBatch;
|
|
37
|
-
const fs = __importStar(require("fs"));
|
|
38
|
-
const path = __importStar(require("path"));
|
|
39
|
-
const crypto = __importStar(require("crypto"));
|
|
40
|
-
const output_1 = require("../output");
|
|
41
|
-
const errors_1 = require("../errors");
|
|
42
|
-
function makeClientKey(filePath) {
|
|
43
|
-
const stat = fs.statSync(filePath);
|
|
44
|
-
return crypto
|
|
45
|
-
.createHash('sha256')
|
|
46
|
-
.update(`${path.resolve(filePath)}:${stat.size}:${stat.mtimeMs}`)
|
|
47
|
-
.digest('hex');
|
|
48
|
-
}
|
|
49
|
-
async function uploadFile(client, filePath, clientKey) {
|
|
50
|
-
const filename = path.basename(filePath);
|
|
51
|
-
const contentType = 'video/mp4';
|
|
52
|
-
const { data: presigned } = await client.post('/videos/upload/presigned', { filename, contentType, clientKey });
|
|
53
|
-
if (presigned.alreadyExists) {
|
|
54
|
-
process.stderr.write(` (reused existing video for ${filename})\n`);
|
|
55
|
-
return presigned.videoId;
|
|
56
|
-
}
|
|
57
|
-
const fileBuffer = fs.readFileSync(filePath);
|
|
58
|
-
const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default;
|
|
59
|
-
await axios.put(presigned.uploadUrl, fileBuffer, {
|
|
60
|
-
headers: { 'Content-Type': contentType },
|
|
61
|
-
maxBodyLength: Infinity,
|
|
62
|
-
timeout: 300000,
|
|
63
|
-
});
|
|
64
|
-
const { data: completed } = await client.post('/videos/upload/complete', { key: presigned.key, fileId: presigned.videoId, filename, clientKey });
|
|
65
|
-
return completed.videoId;
|
|
66
|
-
}
|
|
67
|
-
function registerGenerateBatch(generate, client) {
|
|
68
|
-
generate
|
|
69
|
-
.command('batch')
|
|
70
|
-
.description('Upload video files and generate structure for each in batch')
|
|
71
|
-
.option('-f, --file <path>', 'video file to include (repeatable)', (v, acc) => { acc.push(v); return acc; }, [])
|
|
72
|
-
.option('-v, --video <id>', 'already-uploaded video ID (repeatable)', (v, acc) => { acc.push(v); return acc; }, [])
|
|
73
|
-
.option('-t, --template <id>', 'article template ID')
|
|
74
|
-
.option('-s, --structure-template <id>', 'structure template ID')
|
|
75
|
-
.option('--wait', 'poll until batch completes')
|
|
76
|
-
.option('--json', 'output JSON')
|
|
77
|
-
.option('--callback-url <url>', 'webhook URL')
|
|
78
|
-
.option('--callback-secret <secret>', 'webhook HMAC secret')
|
|
79
|
-
.action(async (opts) => {
|
|
80
|
-
try {
|
|
81
|
-
const files = opts.file ?? [];
|
|
82
|
-
const videoIds = opts.video ?? [];
|
|
83
|
-
if (!files.length && !videoIds.length) {
|
|
84
|
-
(0, errors_1.exitWithError)('Provide at least one --file or --video');
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const templateId = opts.template ? Number(opts.template) : undefined;
|
|
88
|
-
const structureTemplateId = opts.structureTemplate ? Number(opts.structureTemplate) : undefined;
|
|
89
|
-
const jobs = [];
|
|
90
|
-
for (const filePath of files) {
|
|
91
|
-
if (!fs.existsSync(filePath)) {
|
|
92
|
-
(0, errors_1.exitWithError)(`File not found: ${filePath}`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const clientKey = makeClientKey(filePath);
|
|
96
|
-
process.stderr.write(`Uploading ${path.basename(filePath)}...\n`);
|
|
97
|
-
const videoId = await uploadFile(client, filePath, clientKey);
|
|
98
|
-
jobs.push({
|
|
99
|
-
type: 'generate/structure',
|
|
100
|
-
input: {
|
|
101
|
-
videoId,
|
|
102
|
-
...(templateId !== undefined ? { templateId } : {}),
|
|
103
|
-
...(structureTemplateId !== undefined ? { structureTemplateId } : {}),
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
for (const videoId of videoIds) {
|
|
108
|
-
jobs.push({
|
|
109
|
-
type: 'generate/structure',
|
|
110
|
-
input: {
|
|
111
|
-
videoId,
|
|
112
|
-
...(templateId !== undefined ? { templateId } : {}),
|
|
113
|
-
...(structureTemplateId !== undefined ? { structureTemplateId } : {}),
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
const { data: batchRes } = await client.post('/batches', {
|
|
118
|
-
jobs,
|
|
119
|
-
...(opts.callbackUrl ? { callbackUrl: opts.callbackUrl } : {}),
|
|
120
|
-
...(opts.callbackSecret ? { callbackSecret: opts.callbackSecret } : {}),
|
|
121
|
-
});
|
|
122
|
-
const { jobId } = batchRes;
|
|
123
|
-
if (!opts.wait) {
|
|
124
|
-
if (opts.json) {
|
|
125
|
-
(0, output_1.printJson)({ jobId });
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
process.stdout.write(`Batch started: ${jobId}\n`);
|
|
129
|
-
process.stdout.write(`${jobs.length} job(s) queued.\n`);
|
|
130
|
-
process.stdout.write(`Poll status: hinto generate status ${jobId}\n`);
|
|
131
|
-
}
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
process.stderr.write(`Waiting for batch ${jobId}...\n`);
|
|
135
|
-
const result = await pollBatch(client, jobId);
|
|
136
|
-
if (opts.json) {
|
|
137
|
-
(0, output_1.printJson)(result);
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
process.stdout.write(`Batch ${result.status}.\n`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
catch (e) {
|
|
144
|
-
(0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
async function pollBatch(client, jobId, intervalMs = 3000, timeoutMs = 600000) {
|
|
149
|
-
const deadline = Date.now() + timeoutMs;
|
|
150
|
-
while (Date.now() < deadline) {
|
|
151
|
-
const { data } = await client.get(`/jobs/${jobId}`);
|
|
152
|
-
if (data.status === 'completed' || data.status === 'failed') {
|
|
153
|
-
return data;
|
|
154
|
-
}
|
|
155
|
-
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
156
|
-
}
|
|
157
|
-
throw new Error(`Batch ${jobId} timed out after ${timeoutMs / 1000}s`);
|
|
158
|
-
}
|