agnes-ai-cli 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jomeswang
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,60 @@
1
+ # agnes-ai-cli
2
+
3
+ CLI and JS API for Agnes text, image, and video workflows.
4
+
5
+ ## Install
6
+
7
+ Current verified distribution path:
8
+
9
+ ```bash
10
+ npm install -g github:jomeswang/agnes-ai-cli
11
+ ```
12
+
13
+ Ad hoc execution without a global install:
14
+
15
+ ```bash
16
+ npx -y github:jomeswang/agnes-ai-cli --help
17
+ ```
18
+
19
+ Planned npm package name:
20
+
21
+ ```bash
22
+ agnes-ai-cli
23
+ ```
24
+
25
+ Once the npm publish path is cleared, the package will also be installable as:
26
+
27
+ ```bash
28
+ npm install -g agnes-ai-cli
29
+ ```
30
+
31
+ ## CLI
32
+
33
+ ```bash
34
+ agnes --help
35
+ agnes auth check
36
+ agnes media url ./input.png
37
+ agnes text chat --prompt "say pong"
38
+ agnes image text2img --prompt "a polished product photo of a perfume bottle"
39
+ agnes image img2img --image ./input.png --prompt "turn this into a clean editorial poster"
40
+ agnes video text2video --prompt "a cinematic drone shot of waves at dusk"
41
+ agnes video img2video --image ./frame.png --prompt "animate subtle rain and drifting fog"
42
+ agnes video multivideo --image ./frame-a.png --image ./frame-b.png --prompt "blend these references into one motion concept"
43
+ agnes video keyframes --image ./frame-a.png --image ./frame-b.png --prompt "morph between the two scenes"
44
+ agnes video poll <task-id>
45
+ ```
46
+
47
+ ## JS API
48
+
49
+ ```js
50
+ import { createAgnesClient } from "agnes-ai-cli";
51
+
52
+ const agnes = createAgnesClient({ apiKey: process.env.AGNES_API_KEY });
53
+
54
+ const image = await agnes.image.generate({
55
+ mode: "text2img",
56
+ prompt: "A cinematic product shot of a silver watch on black stone."
57
+ });
58
+
59
+ console.log(image.url);
60
+ ```
package/bin/agnes.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCli } from "../dist/cli.js";
4
+
5
+ void runCli(process.argv);
@@ -0,0 +1,7 @@
1
+ export declare function checkAuth(config?: {
2
+ env?: NodeJS.ProcessEnv;
3
+ }): {
4
+ readonly ok: boolean;
5
+ readonly configured: boolean;
6
+ readonly source: "env" | "missing";
7
+ };
@@ -0,0 +1,10 @@
1
+ import { resolveConfig } from "../config.js";
2
+ export function checkAuth(config) {
3
+ const resolved = resolveConfig({ env: config?.env });
4
+ const configured = Boolean(resolved.apiKey);
5
+ return {
6
+ ok: configured,
7
+ configured,
8
+ source: configured ? "env" : "missing",
9
+ };
10
+ }
@@ -0,0 +1,12 @@
1
+ export interface SaveKeyOptions {
2
+ shell?: string;
3
+ homeDir?: string;
4
+ env?: NodeJS.ProcessEnv;
5
+ }
6
+ export interface SaveKeyResult {
7
+ ok: true;
8
+ rcFile: string;
9
+ variable: "AGNES_API_KEY";
10
+ }
11
+ export declare function resolveRcFile(shellName: string | undefined, homeDir: string): string;
12
+ export declare function saveKeyToShell(key: string, options?: SaveKeyOptions): Promise<SaveKeyResult>;
@@ -0,0 +1,43 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { AgnesCliError } from "../errors.js";
4
+ export function resolveRcFile(shellName, homeDir) {
5
+ if (shellName === "zsh")
6
+ return join(homeDir, ".zshrc");
7
+ if (shellName === "bash")
8
+ return join(homeDir, ".bashrc");
9
+ return join(homeDir, ".profile");
10
+ }
11
+ export async function saveKeyToShell(key, options = {}) {
12
+ if (!key || !key.trim()) {
13
+ throw new AgnesCliError("INVALID_KEY", "Agnes API key is required.");
14
+ }
15
+ const env = options.env ?? process.env;
16
+ const shellName = options.shell ?? (env.SHELL ? env.SHELL.split("/").pop() : undefined);
17
+ const homeDir = options.homeDir ?? env.HOME;
18
+ if (!homeDir) {
19
+ throw new AgnesCliError("HOME_MISSING", "HOME is not available, so the shell rc file cannot be resolved.");
20
+ }
21
+ const rcFile = resolveRcFile(shellName, homeDir);
22
+ let existing = "";
23
+ try {
24
+ existing = await fs.readFile(rcFile, "utf8");
25
+ }
26
+ catch (error) {
27
+ if (error.code !== "ENOENT")
28
+ throw error;
29
+ }
30
+ const filtered = existing
31
+ .split("\n")
32
+ .filter((line) => !line.startsWith("export AGNES_API_KEY="))
33
+ .join("\n")
34
+ .replace(/\n*$/, "\n");
35
+ const escaped = shellEscape(key);
36
+ const next = `${filtered}\nexport AGNES_API_KEY=${escaped}\n`;
37
+ await fs.writeFile(rcFile, next, "utf8");
38
+ process.env.AGNES_API_KEY = key;
39
+ return { ok: true, rcFile, variable: "AGNES_API_KEY" };
40
+ }
41
+ function shellEscape(value) {
42
+ return `'${value.replace(/'/g, `'\"'\"'`)}'`;
43
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function runCli(argv?: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,431 @@
1
+ import { Command } from "commander";
2
+ import { fileURLToPath } from "node:url";
3
+ import { z } from "zod";
4
+ import { saveKeyToShell } from "./auth/saveKey.js";
5
+ import { createAgnesClient } from "./index.js";
6
+ import { isAgnesCliError } from "./errors.js";
7
+ import { printJson, printLines } from "./output.js";
8
+ export async function runCli(argv = process.argv) {
9
+ const program = new Command();
10
+ program
11
+ .name("agnes")
12
+ .description("CLI for Agnes AI text, image, and video workflows, with auth and media URL bridging")
13
+ .version("0.1.0")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ agnes auth check
17
+ agnes media url ./input.png
18
+ agnes image img2img --image ./input.png --prompt "Turn this into an editorial campaign"
19
+ agnes video keyframes --image ./a.png --image ./b.png --prompt "Transition between these frames"
20
+
21
+ Use this CLI when you want explicit Agnes request-shape handling, local-file URL bridging, or stable JSON output for agents.
22
+ `);
23
+ const client = createAgnesClient();
24
+ const auth = program.command("auth").description("Inspect and configure Agnes auth");
25
+ auth.addHelpText("after", `
26
+ Examples:
27
+ agnes auth check
28
+ agnes auth save-key --key sk-...
29
+
30
+ Use this group to verify AGNES_API_KEY or persist it into the active shell rc file.
31
+ `);
32
+ auth
33
+ .command("check")
34
+ .description("Check whether AGNES_API_KEY is configured")
35
+ .option("--json", "Output JSON")
36
+ .action((options) => {
37
+ const result = client.auth.check();
38
+ if (options.json) {
39
+ printJson(result);
40
+ return;
41
+ }
42
+ printLines([
43
+ result.configured ? "AGNES_API_KEY is configured." : "AGNES_API_KEY is missing.",
44
+ `source: ${result.source}`,
45
+ ]);
46
+ });
47
+ auth
48
+ .command("save-key")
49
+ .description("Persist AGNES_API_KEY into the current shell rc file")
50
+ .requiredOption("--key <value>", "Agnes API key to persist")
51
+ .option("--json", "Output JSON")
52
+ .action(async (options) => {
53
+ const result = await saveKeyToShell(options.key);
54
+ if (options.json) {
55
+ printJson(result);
56
+ return;
57
+ }
58
+ printLines([
59
+ "AGNES_API_KEY saved.",
60
+ `rc file: ${result.rcFile}`,
61
+ ]);
62
+ });
63
+ const media = program.command("media").description("Resolve Agnes media inputs");
64
+ media.addHelpText("after", `
65
+ Examples:
66
+ agnes media url ./frame.png
67
+ agnes media url https://example.com/frame.png
68
+
69
+ Use this group when Agnes requires a public image URL and your source asset is a local file path.
70
+ `);
71
+ media
72
+ .command("url")
73
+ .description("Return a public URL for a local file or pass through an existing URL")
74
+ .argument("<file-or-url>", "Local file path or http(s) URL")
75
+ .option("--ttl <ttl>", "Litterbox TTL (1h, 12h, 24h, 72h)", "1h")
76
+ .option("--json", "Output JSON")
77
+ .action(async (input, options) => {
78
+ const result = await client.media.toPublicUrl(input, { ttl: ttlSchema.parse(options.ttl) });
79
+ if (options.json) {
80
+ printJson(result);
81
+ return;
82
+ }
83
+ console.log(result.url);
84
+ });
85
+ const text = program.command("text").description("Run Agnes text workflows");
86
+ text.addHelpText("after", `
87
+ Examples:
88
+ agnes text chat --prompt "Reply with exactly pong."
89
+
90
+ Use this group for Agnes chat/completions request shapes when you want a minimal one-shot text call through the CLI.
91
+ `);
92
+ text
93
+ .command("chat")
94
+ .description("Run a one-shot Agnes chat completion")
95
+ .requiredOption("--prompt <text>", "Prompt text")
96
+ .option("--model <model>", "Agnes text model", "agnes-2.0-flash")
97
+ .option("--json", "Output JSON")
98
+ .addHelpText("after", `
99
+ Example:
100
+ agnes text chat --prompt "Reply with exactly pong."
101
+
102
+ Request shape:
103
+ Sends a single-user-message Agnes /chat/completions request with agnes-2.0-flash by default.
104
+ `)
105
+ .action(async (options) => {
106
+ const result = await client.text.complete({
107
+ prompt: options.prompt,
108
+ model: options.model,
109
+ });
110
+ if (options.json) {
111
+ printJson(result);
112
+ return;
113
+ }
114
+ console.log(result.text);
115
+ });
116
+ const image = program.command("image").description("Generate Agnes images");
117
+ image.addHelpText("after", `
118
+ Examples:
119
+ agnes image text2img --prompt "A luminous city at sunrise"
120
+ agnes image img2img --image ./input.png --prompt "Restyle this frame for a premium campaign"
121
+ agnes image compose --image ./a.png --image ./b.png --prompt "Blend these references"
122
+
123
+ Use this group for /images/generations request shapes. text2img sends prompt-only payloads. img2img sends a single image URL. compose sends an image URL array.
124
+ `);
125
+ image
126
+ .command("text2img")
127
+ .description("Generate an image from text")
128
+ .requiredOption("--prompt <text>", "Prompt text")
129
+ .option("--model <model>", "Agnes image model")
130
+ .option("--size <size>", "Image size, e.g. 1024x768")
131
+ .option("--seed <number>", "Seed", parseInteger)
132
+ .option("--json", "Output JSON")
133
+ .addHelpText("after", `
134
+ Example:
135
+ agnes image text2img --prompt "A luminous city at sunrise" --size 1024x1024
136
+
137
+ Request shape:
138
+ Sends prompt-only generation to Agnes /images/generations with agnes-image-2.1-flash by default.
139
+ `)
140
+ .action(async (options) => {
141
+ const result = await client.image.generate({
142
+ mode: "text2img",
143
+ prompt: options.prompt,
144
+ model: options.model,
145
+ size: options.size,
146
+ seed: options.seed,
147
+ });
148
+ if (options.json) {
149
+ printJson(result);
150
+ return;
151
+ }
152
+ console.log(result.url);
153
+ });
154
+ image
155
+ .command("img2img")
156
+ .description("Generate an image from one input image")
157
+ .requiredOption("--image <path-or-url>", "Input image path or URL")
158
+ .requiredOption("--prompt <text>", "Prompt text")
159
+ .option("--model <model>", "Agnes image model")
160
+ .option("--size <size>", "Image size, e.g. 1024x768")
161
+ .option("--seed <number>", "Seed", parseInteger)
162
+ .option("--ttl <ttl>", "Temporary upload TTL", "1h")
163
+ .option("--json", "Output JSON")
164
+ .addHelpText("after", `
165
+ Example:
166
+ agnes image img2img --image ./input.png --prompt "Turn this into an editorial travel poster"
167
+
168
+ Request shape:
169
+ Resolves the local file to a public URL when needed, then sends a single Agnes image URL for image-to-image generation.
170
+ `)
171
+ .action(async (options) => {
172
+ const result = await client.image.generate({
173
+ mode: "img2img",
174
+ image: options.image,
175
+ prompt: options.prompt,
176
+ model: options.model,
177
+ size: options.size,
178
+ seed: options.seed,
179
+ ttl: ttlSchema.parse(options.ttl),
180
+ });
181
+ if (options.json) {
182
+ printJson(result);
183
+ return;
184
+ }
185
+ console.log(result.url);
186
+ });
187
+ image
188
+ .command("compose")
189
+ .description("Generate an image from multiple input images")
190
+ .requiredOption("--image <path-or-url...>", "Repeatable image path or URL")
191
+ .requiredOption("--prompt <text>", "Prompt text")
192
+ .option("--model <model>", "Agnes image model")
193
+ .option("--size <size>", "Image size, e.g. 1024x768")
194
+ .option("--seed <number>", "Seed", parseInteger)
195
+ .option("--ttl <ttl>", "Temporary upload TTL", "1h")
196
+ .option("--json", "Output JSON")
197
+ .addHelpText("after", `
198
+ Example:
199
+ agnes image compose --image ./a.png --image ./b.png --prompt "Blend these references into one campaign frame"
200
+
201
+ Request shape:
202
+ Resolves each input to a public URL, preserves image order, and sends an Agnes image URL array for multi-image composition.
203
+ `)
204
+ .action(async (options) => {
205
+ const images = toArray(options.image);
206
+ const result = await client.image.generate({
207
+ mode: "compose",
208
+ images,
209
+ prompt: options.prompt,
210
+ model: options.model,
211
+ size: options.size,
212
+ seed: options.seed,
213
+ ttl: ttlSchema.parse(options.ttl),
214
+ });
215
+ if (options.json) {
216
+ printJson(result);
217
+ return;
218
+ }
219
+ console.log(result.url);
220
+ });
221
+ const video = program.command("video").description("Generate Agnes videos");
222
+ video.addHelpText("after", `
223
+ Examples:
224
+ agnes video text2video --prompt "A cinematic beach scene at sunset"
225
+ agnes video img2video --image ./frame.png --prompt "Add gentle wind and a soft push-in"
226
+ agnes video keyframes --image ./a.png --image ./b.png --prompt "Transition between the frames"
227
+ agnes video poll task_123
228
+
229
+ Use this group for Agnes video task creation and polling. Video creation is asynchronous; poll returns the final result.
230
+ `);
231
+ buildVideoGenerateCommand(video, "text2video", "Generate a video from text", async (client, options) => client.video.generate({
232
+ mode: "text2video",
233
+ prompt: options.prompt,
234
+ width: options.width,
235
+ height: options.height,
236
+ numFrames: options.numFrames,
237
+ frameRate: options.frameRate,
238
+ seed: options.seed,
239
+ negativePrompt: options.negativePrompt,
240
+ }));
241
+ buildVideoGenerateCommand(video, "img2video", "Generate a video from one input image", async (client, options) => client.video.generate({
242
+ mode: "img2video",
243
+ image: Array.isArray(options.image) ? options.image[0] : options.image,
244
+ prompt: options.prompt,
245
+ width: options.width,
246
+ height: options.height,
247
+ numFrames: options.numFrames,
248
+ frameRate: options.frameRate,
249
+ seed: options.seed,
250
+ negativePrompt: options.negativePrompt,
251
+ ttl: ttlSchema.parse(options.ttl),
252
+ }), "single");
253
+ buildVideoGenerateCommand(video, "multivideo", "Generate a video from multiple input images", async (client, options) => client.video.generate({
254
+ mode: "multivideo",
255
+ images: toArray(options.image),
256
+ prompt: options.prompt,
257
+ width: options.width,
258
+ height: options.height,
259
+ numFrames: options.numFrames,
260
+ frameRate: options.frameRate,
261
+ seed: options.seed,
262
+ negativePrompt: options.negativePrompt,
263
+ ttl: ttlSchema.parse(options.ttl),
264
+ }), "multi");
265
+ buildVideoGenerateCommand(video, "keyframes", "Generate a keyframe video from multiple images", async (client, options) => client.video.generate({
266
+ mode: "keyframes",
267
+ images: toArray(options.image),
268
+ prompt: options.prompt,
269
+ width: options.width,
270
+ height: options.height,
271
+ numFrames: options.numFrames,
272
+ frameRate: options.frameRate,
273
+ seed: options.seed,
274
+ negativePrompt: options.negativePrompt,
275
+ ttl: ttlSchema.parse(options.ttl),
276
+ }), "multi");
277
+ video
278
+ .command("poll")
279
+ .description("Poll an Agnes video task until it finishes")
280
+ .argument("<task-id>", "Task id returned by Agnes video creation")
281
+ .option("--interval <seconds>", "Polling interval seconds", parseInteger, 3)
282
+ .option("--timeout <seconds>", "Polling timeout seconds", parseInteger, 600)
283
+ .option("--json", "Output JSON")
284
+ .addHelpText("after", `
285
+ Example:
286
+ agnes video poll task_123 --interval 3 --timeout 600
287
+
288
+ Request shape:
289
+ Polls Agnes /videos/{task_id} until the task completes, fails, or times out. Use this after any asynchronous video creation command.
290
+ `)
291
+ .action(async (taskId, options) => {
292
+ const result = await client.video.poll(taskId, {
293
+ intervalSeconds: options.interval,
294
+ timeoutSeconds: options.timeout,
295
+ });
296
+ if (options.json) {
297
+ printJson(result);
298
+ return;
299
+ }
300
+ printLines([
301
+ result.videoUrl,
302
+ `status: ${result.status}`,
303
+ result.seconds !== undefined ? `seconds: ${result.seconds}` : undefined,
304
+ result.size ? `size: ${result.size}` : undefined,
305
+ ]);
306
+ });
307
+ try {
308
+ await program.parseAsync(argv);
309
+ }
310
+ catch (error) {
311
+ handleCliError(error, argv);
312
+ }
313
+ }
314
+ function buildVideoGenerateCommand(video, name, description, fn, imageMode = false) {
315
+ let command = video
316
+ .command(name)
317
+ .description(description)
318
+ .requiredOption("--prompt <text>", "Prompt text")
319
+ .option("--width <number>", "Video width", parseInteger)
320
+ .option("--height <number>", "Video height", parseInteger)
321
+ .option("--num-frames <number>", "Video frame count, must satisfy 8n + 1", parseInteger)
322
+ .option("--frame-rate <number>", "Frame rate (1-60)", parseInteger)
323
+ .option("--seed <number>", "Seed", parseInteger)
324
+ .option("--negative-prompt <text>", "Negative prompt")
325
+ .option("--json", "Output JSON");
326
+ if (imageMode === "single") {
327
+ command = command
328
+ .requiredOption("--image <path-or-url>", "Input image path or URL")
329
+ .option("--ttl <ttl>", "Temporary upload TTL", "1h");
330
+ }
331
+ else if (imageMode === "multi") {
332
+ command = command
333
+ .requiredOption("--image <path-or-url...>", "Repeatable image path or URL")
334
+ .option("--ttl <ttl>", "Temporary upload TTL", "1h");
335
+ }
336
+ command.addHelpText("after", buildVideoHelp(name, imageMode));
337
+ command.action(async (options) => {
338
+ const client = createAgnesClient();
339
+ const result = await fn(client, options);
340
+ if (options.json) {
341
+ printJson(result);
342
+ return;
343
+ }
344
+ const task = result;
345
+ printLines([
346
+ `taskId: ${task.taskId}`,
347
+ `status: ${task.status}`,
348
+ ]);
349
+ });
350
+ }
351
+ function handleCliError(error, argv) {
352
+ const wantsJson = argv.includes("--json");
353
+ if (isAgnesCliError(error)) {
354
+ if (wantsJson) {
355
+ printJson({
356
+ ok: false,
357
+ code: error.code,
358
+ message: error.message,
359
+ ...toJsonDetails(error.details),
360
+ });
361
+ process.exit(error.exitCode);
362
+ }
363
+ console.error(error.message);
364
+ process.exit(error.exitCode);
365
+ }
366
+ if (error instanceof z.ZodError) {
367
+ if (wantsJson) {
368
+ printJson({
369
+ ok: false,
370
+ code: "INVALID_ARGUMENTS",
371
+ message: error.issues.map((issue) => issue.message).join("\n"),
372
+ });
373
+ process.exit(1);
374
+ }
375
+ console.error(error.issues.map((issue) => issue.message).join("\n"));
376
+ process.exit(1);
377
+ }
378
+ if (wantsJson) {
379
+ printJson({
380
+ ok: false,
381
+ code: "UNEXPECTED_ERROR",
382
+ message: error instanceof Error ? error.message : String(error),
383
+ });
384
+ process.exit(1);
385
+ }
386
+ console.error(error instanceof Error ? error.message : String(error));
387
+ process.exit(1);
388
+ }
389
+ function parseInteger(value) {
390
+ return Number.parseInt(value, 10);
391
+ }
392
+ function toArray(value) {
393
+ return Array.isArray(value) ? value : [value];
394
+ }
395
+ const ttlSchema = z.enum(["1h", "12h", "24h", "72h"]);
396
+ function toJsonDetails(details) {
397
+ if (!details || typeof details !== "object" || Array.isArray(details)) {
398
+ return {};
399
+ }
400
+ return details;
401
+ }
402
+ function buildVideoHelp(name, imageMode) {
403
+ const examples = {
404
+ text2video: 'agnes video text2video --prompt "A cinematic beach scene at sunset"',
405
+ img2video: 'agnes video img2video --image ./frame.png --prompt "Add gentle wind and a soft push-in"',
406
+ multivideo: 'agnes video multivideo --image ./a.png --image ./b.png --prompt "Blend these references into one motion concept"',
407
+ keyframes: 'agnes video keyframes --image ./a.png --image ./b.png --prompt "Transition between these frames"',
408
+ };
409
+ const shapeNotes = {
410
+ text2video: "Sends a prompt-only Agnes video task payload.",
411
+ img2video: "Resolves the input file to a public URL when needed, then sends a single top-level image field.",
412
+ multivideo: "Resolves each input to a public URL and sends an ordered Agnes image URL array in extra_body.image.",
413
+ keyframes: "Uses Agnes keyframes mode. Pass --image at least twice; each image keeps its original order and is sent in extra_body.image with extra_body.mode=keyframes.",
414
+ };
415
+ const imageNote = imageMode === "multi"
416
+ ? "Required image input: repeat --image two or more times."
417
+ : imageMode === "single"
418
+ ? "Required image input: provide exactly one --image."
419
+ : "No image input is required for this mode.";
420
+ return `
421
+ Example:
422
+ ${examples[name] ?? `agnes video ${name} --help`}
423
+
424
+ Request shape:
425
+ ${shapeNotes[name] ?? "Creates an Agnes video task."}
426
+ ${imageNote}
427
+ `;
428
+ }
429
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
430
+ void runCli(process.argv);
431
+ }
@@ -0,0 +1,70 @@
1
+ export type Ttl = "1h" | "12h" | "24h" | "72h";
2
+ export type FetchLike = typeof fetch;
3
+ export interface AgnesClientConfig {
4
+ apiKey?: string;
5
+ baseUrl?: string;
6
+ fetchImpl?: FetchLike;
7
+ defaultMediaTtl?: Ttl;
8
+ mediaProvider?: MediaUrlProvider;
9
+ env?: NodeJS.ProcessEnv;
10
+ }
11
+ export interface MediaUrlProvider {
12
+ upload(localPath: string, options?: {
13
+ ttl?: Ttl;
14
+ }): Promise<string>;
15
+ }
16
+ export interface ResolvedConfig {
17
+ apiKey?: string;
18
+ baseUrl: string;
19
+ fetchImpl: FetchLike;
20
+ defaultMediaTtl: Ttl;
21
+ mediaProvider?: MediaUrlProvider;
22
+ env: NodeJS.ProcessEnv;
23
+ }
24
+ export declare const DEFAULT_BASE_URL = "https://apihub.agnes-ai.com/v1";
25
+ export declare const DEFAULT_MEDIA_TTL: Ttl;
26
+ export declare const DEFAULT_VIDEO_DIMENSIONS: {
27
+ width: number;
28
+ height: number;
29
+ };
30
+ export declare const DEFAULT_VIDEO_TEMPORAL: {
31
+ numFrames: number;
32
+ frameRate: number;
33
+ };
34
+ export declare function resolveConfig(config?: AgnesClientConfig): ResolvedConfig;
35
+ export interface AuthCheckResult {
36
+ ok: boolean;
37
+ configured: boolean;
38
+ source: "env" | "missing";
39
+ }
40
+ export interface PublicUrlResult {
41
+ ok: true;
42
+ url: string;
43
+ source: "passthrough" | "litterbox" | "provider";
44
+ }
45
+ export type AgnesStatus = "queued" | "in_progress" | "completed" | "failed" | "timed_out";
46
+ export interface NormalizedVideoTask {
47
+ ok: true;
48
+ taskId: string;
49
+ status: AgnesStatus;
50
+ rawStatus?: string;
51
+ model: string;
52
+ raw: unknown;
53
+ }
54
+ export interface NormalizedVideoResult {
55
+ ok: true;
56
+ taskId: string;
57
+ status: AgnesStatus;
58
+ rawStatus?: string;
59
+ model: string;
60
+ videoUrl: string;
61
+ seconds?: number;
62
+ size?: string;
63
+ raw: unknown;
64
+ }
65
+ export interface ImageGenerationResult {
66
+ ok: true;
67
+ model: string;
68
+ url: string;
69
+ raw: unknown;
70
+ }