@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hinto AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,429 @@
1
+ # @hintoai/cli
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@hintoai/cli.svg)](https://www.npmjs.com/package/@hintoai/cli)
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
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
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.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install -g @hintoai/cli
13
+ ```
14
+
15
+ Requires Node.js 18+.
16
+
17
+ ### Use it from an AI agent (Claude Code, Cursor, …)
18
+
19
+ Install the bundled skill into any supported agent with the universal [skills CLI](https://github.com/vercel-labs/skills):
20
+
21
+ ```bash
22
+ npx skills add hintoai/hinto-cli
23
+ # target specific agents:
24
+ npx skills add hintoai/hinto-cli -a claude-code -a cursor
25
+ ```
26
+
27
+ The skill bootstraps the CLI itself (`npm install -g @hintoai/cli`) on first use, so this one command is enough to go from zero to a working agent integration.
28
+
29
+ ## Authentication
30
+
31
+ Run `init` once with your API key to store credentials locally:
32
+
33
+ ```bash
34
+ hinto init --key <your-api-key>
35
+ ```
36
+
37
+ Credentials are stored in `~/.hinto/config.json` with `0600` permissions (owner-read only). You can override the stored key at any time with the `HINTO_API_KEY` environment variable — the env var always takes precedence.
38
+
39
+ ## Quickstart
40
+
41
+ ```bash
42
+ npm install -g @hintoai/cli
43
+ hinto init --key <your-api-key>
44
+ hinto videos upload --file ./demo.mp4 --json
45
+ hinto templates article --json
46
+ hinto generate start --video <videoId> --template <templateId> --wait --json
47
+ hinto publish now --json
48
+ ```
49
+
50
+ ## Shell completions
51
+
52
+ ```bash
53
+ # bash (add to ~/.bashrc)
54
+ eval "$(hinto completion bash)"
55
+
56
+ # zsh (add to ~/.zshrc)
57
+ eval "$(hinto completion zsh)"
58
+
59
+ # fish
60
+ hinto completion fish | source
61
+ ```
62
+
63
+ ## Global flags
64
+
65
+ | Flag | Description |
66
+ |------|-------------|
67
+ | `--api-url <url>` | Override the Hinto base URL (useful for local dev or self-hosted) |
68
+ | `--json` | Output raw JSON instead of human-readable tables / key-value pairs |
69
+ | `--version` | Print CLI version |
70
+ | `--help` | Print help for any command |
71
+
72
+ ## Commands
73
+
74
+ ### `hinto project`
75
+
76
+ ```bash
77
+ hinto project get
78
+ hinto project get --json
79
+ hinto project structure --json
80
+ hinto project update --name "New Name"
81
+ hinto project languages
82
+ hinto project retranslate --lang fr # fire-and-forget
83
+ hinto project retranslate --lang fr --wait # block until done
84
+ ```
85
+
86
+ `project get --json` — the response is wrapped in a `project` key:
87
+
88
+ ```json
89
+ {
90
+ "project": {
91
+ "id": "cc63bccf-4b76-4672-b46f-92a9c0a01567",
92
+ "name": "My Docs",
93
+ "url_slug": "my-docs",
94
+ "description": null,
95
+ "language": "en",
96
+ "is_published": true,
97
+ "logo_url": null,
98
+ "project_type": "docs",
99
+ "is_archived": false,
100
+ "created_at": "2026-01-10T09:00:00Z",
101
+ "custom_domain": null,
102
+ "custom_domain_verified": false
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### `hinto templates`
108
+
109
+ ```bash
110
+ hinto templates article # article templates for this project type
111
+ hinto templates article --json
112
+ hinto templates structure # structure templates for this project type
113
+ hinto templates structure --json
114
+ ```
115
+
116
+ Templates are automatically scoped to your project type.
117
+
118
+ `templates article --json` — `requires_video` tells you whether a template needs a video to generate from:
119
+
120
+ ```json
121
+ {
122
+ "templates": [
123
+ {
124
+ "id": 1,
125
+ "name": "Tutorial",
126
+ "description": "Step-by-step guide format",
127
+ "requires_video": true,
128
+ "image_url": "https://cdn.hinto.ai/templates/tutorial.png",
129
+ "sort_order": 1
130
+ }
131
+ ]
132
+ }
133
+ ```
134
+
135
+ ### `hinto folders`
136
+
137
+ ```bash
138
+ hinto folders list
139
+ hinto folders list --json
140
+ hinto folders get <id>
141
+ hinto folders get <id> --json
142
+ hinto folders create --name "Release Notes"
143
+ hinto folders create --name "Q2" --parent <parentId>
144
+ hinto folders update <id> --name "Q3"
145
+ hinto folders move <id> --parent <newParentId> # move into another folder
146
+ hinto folders move <id> # move to root
147
+ hinto folders delete <id>
148
+ ```
149
+
150
+ `folders create --json`:
151
+
152
+ ```json
153
+ { "id": 7, "name": "Release Notes", "parent_id": null }
154
+ ```
155
+
156
+ > **Note:** `folders move` does **not** return the updated folder — it returns a confirmation object:
157
+ >
158
+ > ```json
159
+ > { "message": "Folder moved", "parentId": null }
160
+ > ```
161
+
162
+ ### `hinto articles`
163
+
164
+ ```bash
165
+ hinto articles list
166
+ hinto articles list --folder <folderId>
167
+ hinto articles list --json
168
+
169
+ hinto articles get <id>
170
+ hinto articles get <id> --json
171
+
172
+ # --content is required; pass a Markdown string or a @filepath
173
+ hinto articles create --title "Getting Started" --content "# Hello\n\nWorld."
174
+ hinto articles create --title "From file" --content @path/to/article.md
175
+ hinto articles create --title "In a folder" --content "..." --folder <folderId>
176
+
177
+ hinto articles update <id> --title "New Title"
178
+ hinto articles update <id> --slug "new-slug"
179
+
180
+ hinto articles duplicate <id>
181
+ hinto articles move <id> --folder <folderId>
182
+ hinto articles regenerate <id>
183
+
184
+ hinto articles versions <id>
185
+ hinto articles restore <id> --vid <vId>
186
+
187
+ hinto articles translations <id>
188
+ hinto articles translate <id> --lang fr
189
+
190
+ hinto articles delete <id>
191
+ ```
192
+
193
+ `articles list --json` — includes a `pagination` object:
194
+
195
+ ```json
196
+ {
197
+ "articles": [
198
+ {
199
+ "id": 42,
200
+ "title": "Getting Started",
201
+ "slug": "getting-started",
202
+ "folder_id": null,
203
+ "inserted_at": "2026-01-15T10:00:00Z",
204
+ "updated_at": "2026-05-01T14:32:00Z"
205
+ }
206
+ ],
207
+ "pagination": { "limit": 50, "offset": 0, "count": 1 }
208
+ }
209
+ ```
210
+
211
+ `articles get <id> --json` — note the `metadata` wrapper for SEO fields, and that `content` is the full rendered output in the requested format:
212
+
213
+ ```json
214
+ {
215
+ "id": 42,
216
+ "title": "Getting Started",
217
+ "slug": "getting-started",
218
+ "format": "markdown",
219
+ "content": "# Getting Started\n\nWelcome to Hinto.",
220
+ "metadata": {
221
+ "metaDescription": null,
222
+ "metaKeywords": null,
223
+ "jsonLd": null,
224
+ "createdAt": "2026-01-15T10:00:00Z",
225
+ "updatedAt": "2026-05-01T14:32:00Z"
226
+ }
227
+ }
228
+ ```
229
+
230
+ `articles create --json` — returns only the essentials (`slug` may be `null` immediately after creation); fetch with `articles get` if you need the full article:
231
+
232
+ ```json
233
+ { "id": 42, "title": "Getting Started", "slug": null }
234
+ ```
235
+
236
+ > **Note:** `articles move` does **not** return the updated article — it returns a confirmation object:
237
+ >
238
+ > ```json
239
+ > { "message": "Article moved", "folderId": 7 }
240
+ > ```
241
+
242
+ ### `hinto videos`
243
+
244
+ ```bash
245
+ hinto videos list
246
+ hinto videos list --json
247
+ hinto videos get <videoId>
248
+ hinto videos status <videoId>
249
+ hinto videos import --url https://example.com/video.mp4
250
+ hinto videos delete <videoId>
251
+ ```
252
+
253
+ ### `hinto generate`
254
+
255
+ ```bash
256
+ # fire-and-forget with explicit template
257
+ hinto generate start --video <videoId> --template <templateId>
258
+
259
+ # fire-and-forget with auto-selected template (optional --template)
260
+ hinto generate start --video <videoId>
261
+
262
+ # block until the job completes
263
+ hinto generate start --video <videoId> --template <templateId> --wait --json
264
+
265
+ # without --template, server picks the default
266
+ hinto generate start --video <videoId> --wait --json
267
+
268
+ # check an existing job
269
+ hinto generate status <jobId> --json
270
+
271
+ # generate / refresh project structure
272
+ hinto generate structure --video <videoId>
273
+ hinto generate structure --video <videoId> --wait
274
+ ```
275
+
276
+ `generate start --json` (fire-and-forget) — use `jobId` to poll status:
277
+
278
+ ```json
279
+ {
280
+ "jobId": "job_a1b2c3d4",
281
+ "articleId": 42,
282
+ "status": "pending",
283
+ "message": "Article generation started. Poll the job status endpoint for updates."
284
+ }
285
+ ```
286
+
287
+ `generate status <jobId> --json` once complete:
288
+
289
+ ```json
290
+ {
291
+ "jobId": "job_a1b2c3d4",
292
+ "type": "generate_article",
293
+ "status": "completed",
294
+ "output": { "articleId": 42 },
295
+ "error": null,
296
+ "createdAt": "2026-05-16T10:00:00Z",
297
+ "completedAt": "2026-05-16T10:02:34Z"
298
+ }
299
+ ```
300
+
301
+ Possible `status` values: `pending` · `processing` · `completed` · `failed`.
302
+
303
+ ### `hinto export`
304
+
305
+ ```bash
306
+ hinto export article <id> --format md # print Markdown to stdout
307
+ hinto export article <id> --format html # print HTML to stdout
308
+ hinto export article <id> --format md --out article.md # write to file
309
+
310
+ hinto export folder <id> --out folder.zip
311
+ hinto export project --out project.zip
312
+ ```
313
+
314
+ ### `hinto publish`
315
+
316
+ ```bash
317
+ hinto publish status
318
+ hinto publish status --json
319
+ hinto publish now # fire-and-forget
320
+ hinto publish now --wait # block until live
321
+ hinto publish now --wait --json
322
+ hinto publish republish
323
+ hinto publish republish --wait
324
+ ```
325
+
326
+ `publish status --json` — the `status` field is a convenience alias derived from `isPublished`:
327
+
328
+ ```json
329
+ {
330
+ "isPublished": true,
331
+ "slug": "my-docs",
332
+ "url": "https://my-docs.hintoai.com",
333
+ "publicationId": "pub_xyz789",
334
+ "publishedAt": "2026-04-20T09:00:00Z",
335
+ "articlesCount": 12,
336
+ "foldersCount": 3,
337
+ "status": "published"
338
+ }
339
+ ```
340
+
341
+ When not yet published, `status` is `"unpublished"` and `url`, `publicationId`, `publishedAt`, `articlesCount`, `foldersCount` are all `null`.
342
+
343
+ ## Machine-readable output (`--json`)
344
+
345
+ Every command that returns data supports `--json`. Output is newline-terminated JSON suitable for piping into `jq` or consuming from scripts and AI agents:
346
+
347
+ ```bash
348
+ hinto articles list --json | jq '.articles[] | .title'
349
+ hinto project get --json | jq '.project.id'
350
+ hinto publish status --json | jq '.status'
351
+ hinto templates list --json | jq '[.templates[] | select(.requires_video) | .id]'
352
+ ```
353
+
354
+ When an error occurs with `--json`, stdout is always empty and the error message goes to stderr — so it is safe to parse stdout without guarding for error objects.
355
+
356
+ ## Exit codes
357
+
358
+ | Code | Meaning |
359
+ |------|---------|
360
+ | `0` | Success |
361
+ | `1` | Error (invalid API key, not found, network error, etc.) |
362
+
363
+ ## Async jobs (`--wait`)
364
+
365
+ Commands that start background jobs — `generate start`, `publish now`, `publish republish`, `project retranslate` — default to fire-and-forget: they print the job metadata and return exit 0 immediately. Pass `--wait` to poll until the job completes (or fails) and print its output.
366
+
367
+ ## Environment variables
368
+
369
+ | Variable | Description |
370
+ |----------|-------------|
371
+ | `HINTO_API_KEY` | Override the API key stored in `~/.hinto/config.json` |
372
+
373
+ ## Development
374
+
375
+ ### Setup
376
+
377
+ ```bash
378
+ git clone <repo-url>
379
+ cd hinto-cli
380
+ npm install
381
+ npm run build # compile TypeScript → dist/
382
+ npm test # run unit tests (nock-based, no network required)
383
+ ```
384
+
385
+ ### Running locally without installing globally
386
+
387
+ Use `node dist/index.js` directly — no `npm link` or global install needed:
388
+
389
+ ```bash
390
+ npm run build
391
+ node dist/index.js --api-url http://localhost:3000 project get
392
+ ```
393
+
394
+ Or set a shell alias for the session:
395
+
396
+ ```bash
397
+ alias hinto="node $(pwd)/dist/index.js"
398
+ hinto --api-url http://localhost:3000 videos list
399
+ ```
400
+
401
+ Set `HINTO_API_KEY` in your environment to avoid passing the key on every command:
402
+
403
+ ```bash
404
+ export HINTO_API_KEY=hinto_...
405
+ ```
406
+
407
+ ### Testing against a local server
408
+
409
+ Point the CLI at the Next.js dev server running on port 3000 (or the e2e port 3099):
410
+
411
+ ```bash
412
+ node dist/index.js --api-url http://localhost:3000 videos list --json
413
+ ```
414
+
415
+ Grab an API key from the local Supabase DB or from the e2e test setup.
416
+
417
+ ### Skill development
418
+
419
+ The Claude Code skill lives in `skills/hinto-cli/`. After editing any file there, copy it to Claude's global skills directory so it takes effect immediately:
420
+
421
+ ```bash
422
+ cp -r skills/hinto-cli/. ~/.claude/skills/hinto-cli/
423
+ ```
424
+
425
+ **Rule:** whenever you change a CLI flag or command behaviour, update **both**:
426
+ 1. The relevant `skills/hinto-cli/references/*.md` file
427
+ 2. The usage examples in this `README.md`
428
+
429
+ Then copy and commit together so the repo and the local skill stay in sync.
@@ -0,0 +1,101 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export interface Article {
3
+ id: number;
4
+ title: string;
5
+ slug: string | null;
6
+ folderId: number | null;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ }
10
+ export interface ArticleDetail {
11
+ id: number;
12
+ title: string;
13
+ slug: string | null;
14
+ folderId: number | null;
15
+ format: string;
16
+ content: string;
17
+ createdAt: string | null;
18
+ updatedAt: string | null;
19
+ metadata: {
20
+ metaDescription: string | null;
21
+ metaKeywords: string[] | null;
22
+ jsonLd: string | null;
23
+ };
24
+ }
25
+ export interface ArticleVersion {
26
+ id: string;
27
+ versionNumber: number;
28
+ createdAt: string;
29
+ createdBy: string;
30
+ changeDescription: string | null;
31
+ isAutoSave: boolean;
32
+ }
33
+ export interface ArticleTranslation {
34
+ languageCode: string;
35
+ status: string;
36
+ title: string | null;
37
+ slug: string | null;
38
+ metaDescription: string | null;
39
+ metaKeywords: string[] | null;
40
+ hasContent: boolean;
41
+ updatedAt: string;
42
+ }
43
+ export declare const articlesApi: (client: AxiosInstance) => {
44
+ list: (params?: {
45
+ folderId?: string;
46
+ offset?: number;
47
+ limit?: number;
48
+ }) => Promise<{
49
+ articles: Article[];
50
+ pagination: {
51
+ limit: number;
52
+ offset: number;
53
+ count: number;
54
+ };
55
+ }>;
56
+ get: (id: string, format?: "markdown" | "html") => Promise<ArticleDetail>;
57
+ create: (body: {
58
+ title: string;
59
+ content?: string;
60
+ folderId?: string;
61
+ }) => Promise<ArticleDetail>;
62
+ update: (id: string, body: {
63
+ title?: string;
64
+ slug?: string;
65
+ metaDescription?: string;
66
+ metaKeywords?: string[];
67
+ }) => Promise<ArticleDetail>;
68
+ delete: (id: string) => Promise<any>;
69
+ duplicate: (id: string) => Promise<ArticleDetail>;
70
+ move: (id: string, folderId: string | null) => Promise<ArticleDetail>;
71
+ regenerate: (id: string, callbackUrl?: string, callbackSecret?: string) => Promise<import("./generate").Job>;
72
+ createEmpty: (body: {
73
+ title?: string;
74
+ folderId?: number;
75
+ }) => Promise<ArticleDetail>;
76
+ listVersions: (id: string) => Promise<{
77
+ versions: ArticleVersion[];
78
+ }>;
79
+ restoreVersion: (id: string, versionId: string) => Promise<{
80
+ message: string;
81
+ articleId: number;
82
+ versionId: string;
83
+ }>;
84
+ listTranslations: (id: string) => Promise<{
85
+ translations: ArticleTranslation[];
86
+ }>;
87
+ getTranslation: (id: string, lang: string, format?: "markdown" | "html") => Promise<{
88
+ languageCode: string;
89
+ status: string;
90
+ title: string | null;
91
+ slug: string | null;
92
+ format: string;
93
+ content: string | null;
94
+ metadata: {
95
+ metaDescription: string | null;
96
+ metaKeywords: string[] | null;
97
+ updatedAt: string | null;
98
+ };
99
+ }>;
100
+ triggerTranslate: (id: string, lang: string, callbackUrl?: string, callbackSecret?: string) => Promise<import("./generate").Job>;
101
+ };
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.articlesApi = void 0;
4
+ const articlesApi = (client) => ({
5
+ list: (params) => client
6
+ .get('/articles', { params })
7
+ .then((r) => r.data),
8
+ get: (id, format) => client
9
+ .get(`/articles/${id}`, { params: format ? { format } : undefined })
10
+ .then((r) => r.data),
11
+ create: (body) => client.post('/articles', body).then((r) => r.data),
12
+ update: (id, body) => client.put(`/articles/${id}`, body).then((r) => r.data),
13
+ delete: (id) => client.delete(`/articles/${id}`).then((r) => r.data),
14
+ duplicate: (id) => client.post(`/articles/${id}/duplicate`).then((r) => r.data),
15
+ move: (id, folderId) => client.patch(`/articles/${id}/move`, { folderId }).then((r) => r.data),
16
+ regenerate: (id, callbackUrl, callbackSecret) => client
17
+ .post(`/articles/${id}/regenerate`, {
18
+ ...(callbackUrl ? { callbackUrl } : {}),
19
+ ...(callbackSecret ? { callbackSecret } : {}),
20
+ })
21
+ .then((r) => r.data),
22
+ createEmpty: (body) => client.post('/articles/empty', body).then((r) => r.data),
23
+ listVersions: (id) => client.get(`/articles/${id}/versions`).then((r) => r.data),
24
+ restoreVersion: (id, versionId) => client
25
+ .post(`/articles/${id}/versions/${versionId}/restore`)
26
+ .then((r) => r.data),
27
+ listTranslations: (id) => client
28
+ .get(`/articles/${id}/translations`)
29
+ .then((r) => r.data),
30
+ getTranslation: (id, lang, format) => client
31
+ .get(`/articles/${id}/translations/${lang}`, { params: format ? { format } : undefined })
32
+ .then((r) => r.data),
33
+ triggerTranslate: (id, lang, callbackUrl, callbackSecret) => client
34
+ .post(`/articles/${id}/translations/${lang}`, {
35
+ ...(callbackUrl ? { callbackUrl } : {}),
36
+ ...(callbackSecret ? { callbackSecret } : {}),
37
+ })
38
+ .then((r) => r.data),
39
+ });
40
+ exports.articlesApi = articlesApi;
@@ -0,0 +1,2 @@
1
+ import { type AxiosInstance } from 'axios';
2
+ export declare function createClient(apiKey: string, baseUrl: string): AxiosInstance;
@@ -0,0 +1,48 @@
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.createClient = createClient;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const errors_1 = require("../errors");
9
+ function createClient(apiKey, baseUrl) {
10
+ const instance = axios_1.default.create({
11
+ baseURL: `${baseUrl}/api/external/v2`,
12
+ headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
13
+ timeout: 30000,
14
+ });
15
+ instance.interceptors.response.use((res) => res, (err) => {
16
+ const apiError = err.response?.data?.error;
17
+ if (apiError) {
18
+ if (apiError.code === 'UNAUTHORIZED') {
19
+ throw new errors_1.CliError('UNAUTHORIZED', 'Invalid API key. Run `hinto init --key <your-api-key>` to authenticate.');
20
+ }
21
+ throw new errors_1.CliError(apiError.code, apiError.message);
22
+ }
23
+ if (!apiError && err.response?.data) {
24
+ try {
25
+ const raw = typeof err.response.data === 'string'
26
+ ? err.response.data
27
+ : Buffer.from(err.response.data).toString('utf-8');
28
+ const parsed = JSON.parse(raw);
29
+ if (parsed?.error) {
30
+ if (parsed.error.code === 'UNAUTHORIZED') {
31
+ throw new errors_1.CliError('UNAUTHORIZED', 'Invalid API key. Run `hinto init --key <your-api-key>` to authenticate.');
32
+ }
33
+ throw new errors_1.CliError(parsed.error.code, parsed.error.message ?? 'Unknown error');
34
+ }
35
+ }
36
+ catch (parseErr) {
37
+ if (parseErr instanceof errors_1.CliError)
38
+ throw parseErr;
39
+ // ignore parse failures — fall through to generic message
40
+ }
41
+ }
42
+ if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') {
43
+ throw new errors_1.CliError('NETWORK_ERROR', 'Could not reach Hinto API — check your connection');
44
+ }
45
+ throw new errors_1.CliError('UNKNOWN_ERROR', err.message ?? 'An unexpected error occurred');
46
+ });
47
+ return instance;
48
+ }
@@ -0,0 +1,6 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export declare const exportApi: (client: AxiosInstance) => {
3
+ article: (id: string, format?: "markdown" | "html" | "pdf", lang?: string) => Promise<string | Buffer>;
4
+ folder: (id: string) => Promise<Buffer<ArrayBufferLike>>;
5
+ project: (format?: "markdown" | "html" | "pdf" | "llm-text") => Promise<Buffer<ArrayBufferLike>>;
6
+ };
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exportApi = void 0;
4
+ const exportApi = (client) => ({
5
+ article: (id, format, lang) => {
6
+ const isPdf = format === 'pdf';
7
+ return client
8
+ .get(`/export/articles/${id}`, {
9
+ params: { ...(format ? { format } : {}), ...(lang ? { lang } : {}) },
10
+ responseType: isPdf ? 'arraybuffer' : 'text',
11
+ })
12
+ .then((r) => (isPdf ? Buffer.from(r.data) : r.data));
13
+ },
14
+ folder: (id) => client
15
+ .get(`/export/folders/${id}`, { responseType: 'arraybuffer' })
16
+ .then((r) => r.data),
17
+ project: (format) => client
18
+ .get('/export/project', {
19
+ params: format ? { format } : undefined,
20
+ responseType: 'arraybuffer',
21
+ })
22
+ .then((r) => r.data),
23
+ });
24
+ exports.exportApi = exportApi;
@@ -0,0 +1,18 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export interface Folder {
3
+ id: number;
4
+ name: string;
5
+ parentId: number | null;
6
+ createdAt: string;
7
+ updatedAt: string;
8
+ }
9
+ export declare const foldersApi: (client: AxiosInstance) => {
10
+ list: () => Promise<{
11
+ folders: Folder[];
12
+ }>;
13
+ get: (id: string) => Promise<Folder>;
14
+ create: (name: string, parentId?: string) => Promise<Folder>;
15
+ update: (id: string, name: string) => Promise<Folder>;
16
+ delete: (id: string) => Promise<any>;
17
+ move: (id: string, parentId: string | null) => Promise<Folder>;
18
+ };