@hintoai/cli 0.2.0 → 0.3.1

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 CHANGED
@@ -4,7 +4,9 @@
4
4
  [![CI](https://github.com/hintoai/hinto-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/hintoai/hinto-cli/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
6
 
7
- Command-line interface for the [Hinto AI](https://hinto.ai) API. Manage videos, articles, folders, templates, and publishing from your terminal or from AI agents and scripts.
7
+ Command-line interface for [**Hinto AI**](https://hintoai.com) — turn videos into articles, docs, and published content. Manage videos, articles, folders, templates, and publishing from your terminal or from AI agents and scripts.
8
+
9
+ > New to Hinto AI? Create a project and grab your API key at **[hintoai.com](https://hintoai.com)**.
8
10
 
9
11
  ## Installation
10
12
 
@@ -427,3 +429,14 @@ cp -r skills/hinto-cli/. ~/.claude/skills/hinto-cli/
427
429
  2. The usage examples in this `README.md`
428
430
 
429
431
  Then copy and commit together so the repo and the local skill stay in sync.
432
+
433
+ ---
434
+
435
+ ## About Hinto AI
436
+
437
+ [Hinto AI](https://hintoai.com) turns videos into SEO-ready articles, documentation, and published content sites. This CLI is the terminal interface to the [Hinto AI API](https://hintoai.com).
438
+
439
+ - 🌐 Website: **[hintoai.com](https://hintoai.com)**
440
+ - 📦 npm: [@hintoai/cli](https://www.npmjs.com/package/@hintoai/cli)
441
+ - 🤖 Use from an AI agent: `npx skills add hintoai/hinto-cli`
442
+ - 📄 License: [MIT](./LICENSE)
@@ -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 [ "\$COMP_CWORD" -eq 1 ]; then
25
- COMPREPLY=( \$(compgen -W "\$groups" -- "\$cur") )
24
+ if [ "$COMP_CWORD" -eq 1 ]; then
25
+ COMPREPLY=( $(compgen -W "$groups" -- "$cur") )
26
26
  else
27
- COMPREPLY=( \$(compgen -W "\$flags" -- "\$cur") )
27
+ COMPREPLY=( $(compgen -W "$flags" -- "$cur") )
28
28
  fi
29
29
  }
30
30
  complete -F _hinto hinto
@@ -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) => table.push(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.2.0",
3
+ "version": "0.3.1",
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,10 +10,10 @@
10
10
  "dist/"
11
11
  ],
12
12
  "engines": {
13
- "node": ">=18"
13
+ "node": ">=20"
14
14
  },
15
15
  "license": "MIT",
16
- "homepage": "https://github.com/hintoai/hinto-cli#readme",
16
+ "homepage": "https://hintoai.com",
17
17
  "repository": {
18
18
  "type": "git",
19
19
  "url": "git+https://github.com/hintoai/hinto-cli.git"
@@ -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": "^12.1.0",
54
+ "commander": "^15.0.0",
55
55
  "ora": "^8.1.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@biomejs/biome": "1.9.4",
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,3 +0,0 @@
1
- import { Command } from 'commander';
2
- import { AxiosInstance } from 'axios';
3
- export declare function registerGenerateBatch(generate: Command, client: AxiosInstance): void;
@@ -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
- }