@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,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerTemplates = registerTemplates;
4
+ const templates_1 = require("../api/templates");
5
+ const errors_1 = require("../errors");
6
+ const output_1 = require("../output");
7
+ function registerTemplates(program, client) {
8
+ const templates = program.command('templates').description('Browse templates');
9
+ const api = (0, templates_1.templatesApi)(client);
10
+ templates
11
+ .command('article')
12
+ .description('List article generation templates for this project type')
13
+ .option('--json', 'Output as JSON')
14
+ .action(async (opts) => {
15
+ try {
16
+ const data = await api.articleTemplates();
17
+ if (opts.json)
18
+ return (0, output_1.printJson)(data);
19
+ (0, output_1.printTable)(['ID', 'Name', 'Requires Video', 'Description'], data.templates.map((t) => [
20
+ String(t.id),
21
+ t.name,
22
+ t.requires_video ? 'yes' : 'no',
23
+ t.description ?? '—',
24
+ ]));
25
+ }
26
+ catch (e) {
27
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
28
+ }
29
+ });
30
+ templates
31
+ .command('structure')
32
+ .description('List structure generation templates for this project type')
33
+ .option('--json', 'Output as JSON')
34
+ .action(async (opts) => {
35
+ try {
36
+ const data = await api.structureTemplates();
37
+ if (opts.json)
38
+ return (0, output_1.printJson)(data);
39
+ (0, output_1.printTable)(['ID', 'Name', 'Requires Video', 'Description'], data.templates.map((t) => [
40
+ String(t.id),
41
+ t.name,
42
+ t.requires_video ? 'yes' : 'no',
43
+ t.description ?? '—',
44
+ ]));
45
+ }
46
+ catch (e) {
47
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
48
+ }
49
+ });
50
+ }
@@ -0,0 +1,3 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ import type { Command } from 'commander';
3
+ export declare function registerVideos(program: Command, client: AxiosInstance): void;
@@ -0,0 +1,176 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.registerVideos = registerVideos;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const axios_1 = __importDefault(require("axios"));
43
+ const videos_1 = require("../api/videos");
44
+ const errors_1 = require("../errors");
45
+ const output_1 = require("../output");
46
+ function registerVideos(program, client) {
47
+ const videos = program.command('videos').description('Manage videos');
48
+ const api = (0, videos_1.videosApi)(client);
49
+ videos
50
+ .command('list')
51
+ .description('List all videos')
52
+ .option('--offset <n>', 'Number of videos to skip', '0')
53
+ .option('--limit <n>', 'Results per page', '20')
54
+ .option('--json', 'Output as JSON')
55
+ .action(async (opts) => {
56
+ try {
57
+ const data = await api.list({ offset: Number(opts.offset), limit: Number(opts.limit) });
58
+ if (opts.json)
59
+ return (0, output_1.printJson)(data);
60
+ (0, output_1.printTable)(['Video ID', 'Filename', 'Status', 'Created'], data.videos.map((v) => [v.videoId, v.filename ?? '—', v.status, v.createdAt]));
61
+ }
62
+ catch (e) {
63
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
64
+ }
65
+ });
66
+ videos
67
+ .command('import')
68
+ .description('Import a video from a URL')
69
+ .requiredOption('--url <url>', 'Video URL to import')
70
+ .option('--name <name>', 'Display name for the imported video')
71
+ .option('--callback-url <url>', 'URL to receive a webhook when the import job completes')
72
+ .option('--callback-secret <secret>', 'HMAC-SHA256 signing secret for the callback webhook')
73
+ .option('--json', 'Output as JSON')
74
+ .action(async (opts) => {
75
+ try {
76
+ const data = await api.import(opts.url, opts.name, opts.callbackUrl, opts.callbackSecret);
77
+ if (opts.json)
78
+ return (0, output_1.printJson)(data);
79
+ (0, output_1.printKeyValue)(data);
80
+ }
81
+ catch (e) {
82
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
83
+ }
84
+ });
85
+ videos
86
+ .command('get <videoId>')
87
+ .description('Get a video by ID')
88
+ .option('--json', 'Output as JSON')
89
+ .action(async (videoId, opts) => {
90
+ try {
91
+ const data = await api.get(videoId);
92
+ if (opts.json)
93
+ return (0, output_1.printJson)(data);
94
+ (0, output_1.printKeyValue)(data);
95
+ }
96
+ catch (e) {
97
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
98
+ }
99
+ });
100
+ videos
101
+ .command('status <videoId>')
102
+ .description('Get video processing status')
103
+ .option('--json', 'Output as JSON')
104
+ .action(async (videoId, opts) => {
105
+ try {
106
+ const data = await api.status(videoId);
107
+ if (opts.json)
108
+ return (0, output_1.printJson)(data);
109
+ (0, output_1.printKeyValue)(data);
110
+ }
111
+ catch (e) {
112
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
113
+ }
114
+ });
115
+ videos
116
+ .command('delete <videoId>')
117
+ .description('Delete a video')
118
+ .option('--json', 'Output as JSON')
119
+ .action(async (videoId, opts) => {
120
+ try {
121
+ await api.delete(videoId);
122
+ if (opts.json)
123
+ return (0, output_1.printJson)({ deleted: true });
124
+ process.stdout.write(`Video ${videoId} deleted.\n`);
125
+ }
126
+ catch (e) {
127
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
128
+ }
129
+ });
130
+ videos
131
+ .command('upload')
132
+ .description('Upload a video file from disk (3-step presigned flow)')
133
+ .requiredOption('--file <path>', 'Path to the video file')
134
+ .option('--json', 'Output as JSON')
135
+ .action(async (opts) => {
136
+ try {
137
+ const filePath = path.resolve(opts.file);
138
+ if (!fs.existsSync(filePath)) {
139
+ (0, errors_1.exitWithError)(`File not found: ${filePath}`);
140
+ return;
141
+ }
142
+ const filename = path.basename(filePath);
143
+ const ext = path.extname(filename).toLowerCase();
144
+ const contentTypeMap = {
145
+ '.mp4': 'video/mp4',
146
+ '.mov': 'video/quicktime',
147
+ '.webm': 'video/webm',
148
+ '.avi': 'video/avi',
149
+ '.mkv': 'video/x-matroska',
150
+ };
151
+ const contentType = contentTypeMap[ext] ?? 'video/mp4';
152
+ // Step 1: get presigned URL
153
+ process.stderr.write('Requesting presigned upload URL...\n');
154
+ const { videoId, uploadUrl, key: s3Key } = await api.uploadPresigned(filename, contentType);
155
+ // Step 2: PUT file to S3
156
+ process.stderr.write(`Uploading ${filename}...\n`);
157
+ const fileStream = fs.createReadStream(filePath);
158
+ const { size } = fs.statSync(filePath);
159
+ await axios_1.default.put(uploadUrl, fileStream, {
160
+ headers: { 'Content-Type': contentType, 'Content-Length': size },
161
+ maxBodyLength: Number.POSITIVE_INFINITY,
162
+ maxContentLength: Number.POSITIVE_INFINITY,
163
+ });
164
+ // Step 3: complete upload
165
+ process.stderr.write('Completing upload...\n');
166
+ const result = await api.uploadComplete(videoId, s3Key, filename);
167
+ if (opts.json)
168
+ return (0, output_1.printJson)(result);
169
+ process.stdout.write(`Uploaded: videoId=${result.videoId} status=pending\n`);
170
+ process.stdout.write(`Track: hinto videos status ${result.videoId}\n`);
171
+ }
172
+ catch (e) {
173
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
174
+ }
175
+ });
176
+ }
@@ -0,0 +1,7 @@
1
+ export interface HintoConfig {
2
+ apiKey: string;
3
+ baseUrl: string;
4
+ }
5
+ export declare const CONFIG_PATH: string;
6
+ export declare function saveConfig(config: HintoConfig): void;
7
+ export declare function loadConfig(): HintoConfig;
package/dist/config.js ADDED
@@ -0,0 +1,31 @@
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.CONFIG_PATH = void 0;
7
+ exports.saveConfig = saveConfig;
8
+ exports.loadConfig = loadConfig;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const path_1 = __importDefault(require("path"));
12
+ exports.CONFIG_PATH = path_1.default.join(os_1.default.homedir(), '.hinto', 'config.json');
13
+ function saveConfig(config) {
14
+ const dir = path_1.default.dirname(exports.CONFIG_PATH);
15
+ if (!fs_1.default.existsSync(dir))
16
+ fs_1.default.mkdirSync(dir, { recursive: true, mode: 0o700 });
17
+ fs_1.default.writeFileSync(exports.CONFIG_PATH, JSON.stringify(config, null, 2), {
18
+ encoding: 'utf-8',
19
+ mode: 0o600,
20
+ });
21
+ }
22
+ function loadConfig() {
23
+ if (!fs_1.default.existsSync(exports.CONFIG_PATH)) {
24
+ throw new Error('Run `hinto init --key <your-api-key>` to get started.');
25
+ }
26
+ const raw = JSON.parse(fs_1.default.readFileSync(exports.CONFIG_PATH, 'utf-8'));
27
+ return {
28
+ ...raw,
29
+ apiKey: process.env.HINTO_API_KEY ?? raw.apiKey,
30
+ };
31
+ }
@@ -0,0 +1,5 @@
1
+ export declare class CliError extends Error {
2
+ readonly code: string;
3
+ constructor(code: string, message: string);
4
+ }
5
+ export declare function exitWithError(message: string): never;
package/dist/errors.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliError = void 0;
4
+ exports.exitWithError = exitWithError;
5
+ class CliError extends Error {
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.code = code;
9
+ this.name = 'CliError';
10
+ }
11
+ }
12
+ exports.CliError = CliError;
13
+ function exitWithError(message) {
14
+ process.stderr.write(`Error: ${message}\n`);
15
+ process.exit(1);
16
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const client_1 = require("./api/client");
6
+ const articles_1 = require("./commands/articles");
7
+ const completion_1 = require("./commands/completion");
8
+ const export_1 = require("./commands/export");
9
+ const folders_1 = require("./commands/folders");
10
+ const generate_1 = require("./commands/generate");
11
+ const init_1 = require("./commands/init");
12
+ const project_1 = require("./commands/project");
13
+ const publish_1 = require("./commands/publish");
14
+ const templates_1 = require("./commands/templates");
15
+ const videos_1 = require("./commands/videos");
16
+ const config_1 = require("./config");
17
+ const errors_1 = require("./errors");
18
+ // Read version from package.json so `hinto --version` never drifts from the
19
+ // published package version (package.json sits one level above dist/index.js).
20
+ const { version } = require('../package.json');
21
+ const program = new commander_1.Command();
22
+ program
23
+ .name('hinto')
24
+ .description('Hinto AI CLI — manage videos, articles, and publishing')
25
+ .version(version)
26
+ .option('--api-url <url>', 'Override the Hinto base URL');
27
+ // init and completion don't need auth — register first
28
+ (0, init_1.registerInit)(program);
29
+ (0, completion_1.registerCompletion)(program);
30
+ // Load config eagerly. If missing, fall back to empty key so --help still
31
+ // works on all subcommands. The client's interceptor returns a helpful
32
+ // UNAUTHORIZED message when a command is actually invoked without a valid key.
33
+ const config = (() => {
34
+ try {
35
+ return (0, config_1.loadConfig)();
36
+ }
37
+ catch {
38
+ return { apiKey: process.env.HINTO_API_KEY ?? '', baseUrl: 'https://app.hinto.ai' };
39
+ }
40
+ })();
41
+ const apiUrl = (() => {
42
+ const idx = process.argv.indexOf('--api-url');
43
+ return idx !== -1 ? (process.argv[idx + 1] ?? config.baseUrl) : config.baseUrl;
44
+ })();
45
+ const client = (0, client_1.createClient)(config.apiKey, apiUrl);
46
+ (0, videos_1.registerVideos)(program, client);
47
+ (0, articles_1.registerArticles)(program, client);
48
+ (0, folders_1.registerFolders)(program, client);
49
+ (0, generate_1.registerGenerate)(program, client);
50
+ (0, project_1.registerProject)(program, client);
51
+ (0, publish_1.registerPublish)(program, client);
52
+ (0, templates_1.registerTemplates)(program, client);
53
+ (0, export_1.registerExport)(program, client);
54
+ program.parseAsync(process.argv).catch((e) => {
55
+ (0, errors_1.exitWithError)(e instanceof Error ? e.message : String(e));
56
+ });
@@ -0,0 +1,3 @@
1
+ export declare function printJson(data: unknown): void;
2
+ export declare function printTable(headers: string[], rows: string[][]): void;
3
+ export declare function printKeyValue(obj: Record<string, unknown> | unknown): void;
package/dist/output.js ADDED
@@ -0,0 +1,43 @@
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.printJson = printJson;
7
+ exports.printTable = printTable;
8
+ exports.printKeyValue = printKeyValue;
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const cli_table3_1 = __importDefault(require("cli-table3"));
11
+ function printJson(data) {
12
+ process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
13
+ }
14
+ function printTable(headers, rows) {
15
+ const table = new cli_table3_1.default({ head: headers.map((h) => chalk_1.default.bold(h)) });
16
+ rows.forEach((row) => table.push(row));
17
+ process.stdout.write(`${table.toString()}\n`);
18
+ }
19
+ function printKeyValue(obj) {
20
+ if (obj == null || typeof obj !== 'object') {
21
+ process.stdout.write(`${String(obj)}\n`);
22
+ return;
23
+ }
24
+ const lines = Object.entries(obj)
25
+ .map(([k, v]) => `${chalk_1.default.bold(k)}: ${formatValue(k, v)}`)
26
+ .join('\n');
27
+ process.stdout.write(`${lines}\n`);
28
+ }
29
+ function formatValue(key, value) {
30
+ if ((key === 'status' || key === 'ingest_status') && typeof value === 'string') {
31
+ if (['ready', 'completed'].includes(value))
32
+ return chalk_1.default.green(value);
33
+ if (['pending', 'processing'].includes(value))
34
+ return chalk_1.default.yellow(value);
35
+ if (value === 'failed')
36
+ return chalk_1.default.red(value);
37
+ }
38
+ if (value === null || value === undefined)
39
+ return chalk_1.default.dim('—');
40
+ if (typeof value === 'object')
41
+ return JSON.stringify(value, null, 2);
42
+ return String(value);
43
+ }
package/dist/poll.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export declare function pollJob(client: AxiosInstance, jobId: string, intervalMs?: number, timeoutMs?: number): Promise<unknown>;
package/dist/poll.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pollJob = pollJob;
4
+ async function pollJob(client, jobId, intervalMs = 2000, timeoutMs = 300000) {
5
+ process.stderr.write(`Waiting for job ${jobId}…\n`);
6
+ const deadline = Date.now() + timeoutMs;
7
+ while (Date.now() < deadline) {
8
+ const { data } = await client.get(`/generate/${jobId}`);
9
+ if (data.status === 'completed') {
10
+ process.stderr.write(`Job ${jobId} completed\n`);
11
+ return data.output;
12
+ }
13
+ if (data.status === 'failed') {
14
+ process.stderr.write(`Job ${jobId} failed\n`);
15
+ throw new Error(data.error ?? 'Job failed with no error message');
16
+ }
17
+ await sleep(intervalMs);
18
+ }
19
+ throw new Error(`Job ${jobId} timed out after ${timeoutMs / 1000}s`);
20
+ }
21
+ function sleep(ms) {
22
+ return new Promise((resolve) => setTimeout(resolve, ms));
23
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@hintoai/cli",
3
+ "version": "0.2.0",
4
+ "description": "Hinto AI CLI — manage videos, articles, and publishing via the Hinto API",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "hinto": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist/"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "license": "MIT",
16
+ "homepage": "https://github.com/hintoai/hinto-cli#readme",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/hintoai/hinto-cli.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/hintoai/hinto-cli/issues"
23
+ },
24
+ "keywords": [
25
+ "hinto",
26
+ "cli",
27
+ "video",
28
+ "articles",
29
+ "publishing",
30
+ "ai",
31
+ "agent-skill"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "provenance": true
36
+ },
37
+ "scripts": {
38
+ "build": "tsc",
39
+ "dev": "ts-node src/index.ts",
40
+ "test": "jest",
41
+ "test:watch": "jest --watch",
42
+ "lint": "biome check .",
43
+ "format": "biome format --write .",
44
+ "validate:skill": "node scripts/validate-skill.mjs",
45
+ "changeset": "changeset",
46
+ "version-packages": "changeset version",
47
+ "release": "npm run build && changeset publish",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "dependencies": {
51
+ "axios": "^1.7.0",
52
+ "chalk": "^4.1.2",
53
+ "cli-table3": "^0.6.5",
54
+ "commander": "^12.1.0",
55
+ "ora": "^8.1.0"
56
+ },
57
+ "devDependencies": {
58
+ "@biomejs/biome": "1.9.4",
59
+ "@changesets/cli": "^2.31.0",
60
+ "@types/jest": "^30.0.0",
61
+ "@types/node": "^25.9.2",
62
+ "jest": "^30.4.2",
63
+ "nock": "^13.5.0",
64
+ "ts-jest": "^29.4.11",
65
+ "ts-node": "^10.9.0",
66
+ "typescript": "^5.5.0"
67
+ }
68
+ }