@doufunao123/asset-gateway 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/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @doufunao123/asset-gateway
2
+
3
+ Lightweight npm CLI client for the universal asset generation gateway.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @doufunao123/asset-gateway
9
+ ```
10
+
11
+ Or run directly:
12
+
13
+ ```bash
14
+ npx @doufunao123/asset-gateway provider list
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ # 1. Save your API token
21
+ asset-gateway auth set agk_xxx
22
+
23
+ # 2. Generate assets
24
+ asset-gateway generate image --prompt "a red dragon"
25
+ asset-gateway generate video --prompt "ocean waves at sunset"
26
+ ```
27
+
28
+ ## Authentication
29
+
30
+ Supports `agk_` admin tokens or plain API keys.
31
+
32
+ **Resolution order:**
33
+
34
+ 1. `--token <token>` CLI flag
35
+ 2. `ASSET_GATEWAY_TOKEN` environment variable
36
+ 3. `~/.config/asset-gateway/auth.json` (saved via `auth set`)
37
+
38
+ Config file format:
39
+
40
+ ```json
41
+ {
42
+ "token": "agk_...",
43
+ "gateway_url": "https://assets.xiaomao.chat"
44
+ }
45
+ ```
46
+
47
+ ## Commands
48
+
49
+ ```bash
50
+ # Auth
51
+ asset-gateway auth set <token> # Save token locally
52
+ asset-gateway auth status # Show current auth state
53
+ asset-gateway auth clear # Remove saved credentials
54
+
55
+ # Generate
56
+ asset-gateway generate image --prompt "a cat" --size 1024x1024
57
+ asset-gateway generate image --prompt "icon" --transparent --provider flux
58
+ asset-gateway generate video --prompt "ocean waves"
59
+ asset-gateway generate audio --prompt "epic battle" --type bgm --duration 30
60
+ asset-gateway generate model --image https://example.com/ref.png
61
+ asset-gateway generate text --prompt "describe a forest" --model gpt-5.4
62
+
63
+ # Providers
64
+ asset-gateway provider list
65
+ asset-gateway provider health
66
+ asset-gateway provider health flux
67
+
68
+ # Jobs
69
+ asset-gateway job list
70
+ asset-gateway job list --status pending --limit 10
71
+ asset-gateway job status <id>
72
+ asset-gateway job cancel <id>
73
+
74
+ # Self-describe (JSON Schema)
75
+ asset-gateway describe
76
+ asset-gateway describe generate
77
+ ```
78
+
79
+ ## Output
80
+
81
+ JSON by default. Use `--human` for readable output, `--fields` to filter:
82
+
83
+ ```bash
84
+ asset-gateway provider list --human
85
+ asset-gateway job status abc123 --fields "status,created_at"
86
+ ```
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,688 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command as Command6 } from "commander";
5
+
6
+ // src/commands/auth.ts
7
+ import { existsSync as existsSync2, unlinkSync } from "fs";
8
+ import { Command } from "commander";
9
+
10
+ // src/config.ts
11
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
12
+ import { homedir } from "os";
13
+ import { dirname, join } from "path";
14
+
15
+ // src/errors.ts
16
+ var GatewayError = class extends Error {
17
+ code;
18
+ exitCode;
19
+ suggestion;
20
+ constructor(message, options) {
21
+ super(message);
22
+ this.name = "GatewayError";
23
+ this.code = options.code;
24
+ this.exitCode = options.exitCode ?? 1;
25
+ this.suggestion = options.suggestion;
26
+ }
27
+ };
28
+ function configError(message, suggestion) {
29
+ return new GatewayError(message, {
30
+ code: "CONFIG_ERROR",
31
+ exitCode: 1,
32
+ suggestion: suggestion ?? "Run asset-gateway auth set <token> to configure credentials"
33
+ });
34
+ }
35
+ function notFoundError(message) {
36
+ return new GatewayError(message, {
37
+ code: "NOT_FOUND",
38
+ exitCode: 1
39
+ });
40
+ }
41
+ function apiError(message, suggestion) {
42
+ return new GatewayError(message, {
43
+ code: "GATEWAY_API_ERROR",
44
+ exitCode: 3,
45
+ suggestion: suggestion ?? "Check if the gateway is running: asset-gateway provider health"
46
+ });
47
+ }
48
+ function httpClientError(message, suggestion) {
49
+ return new GatewayError(message, {
50
+ code: "HTTP_CLIENT_ERROR",
51
+ exitCode: 3,
52
+ suggestion: suggestion ?? "Check network connectivity to the gateway"
53
+ });
54
+ }
55
+ function internalError(message) {
56
+ return new GatewayError(message, {
57
+ code: "INTERNAL_ERROR",
58
+ exitCode: 2
59
+ });
60
+ }
61
+ function normalizeError(error2) {
62
+ if (error2 instanceof GatewayError) {
63
+ return error2;
64
+ }
65
+ if (error2 instanceof Error) {
66
+ return internalError(error2.message);
67
+ }
68
+ return internalError(String(error2));
69
+ }
70
+
71
+ // src/meta.ts
72
+ var CLI_NAME = "asset-gateway";
73
+ var CLI_VERSION = "0.1.0";
74
+ var CLI_DESCRIPTION = "Universal asset generation gateway CLI";
75
+ var DEFAULT_GATEWAY_URL = "https://assets.xiaomao.chat";
76
+
77
+ // src/config.ts
78
+ function configDir() {
79
+ return join(homedir(), ".config", "asset-gateway");
80
+ }
81
+ function configPath() {
82
+ return join(configDir(), "auth.json");
83
+ }
84
+ function loadAuthConfig() {
85
+ const path = configPath();
86
+ if (!existsSync(path)) {
87
+ return {};
88
+ }
89
+ const content = readFileSync(path, "utf8");
90
+ if (!content.trim()) {
91
+ return {};
92
+ }
93
+ try {
94
+ const parsed = JSON.parse(content);
95
+ return { token: parsed.token, gateway_url: parsed.gateway_url };
96
+ } catch (error2) {
97
+ throw configError(
98
+ `Failed to parse auth config at ${path}: ${error2 instanceof Error ? error2.message : String(error2)}`
99
+ );
100
+ }
101
+ }
102
+ function saveAuthConfig(config) {
103
+ const path = configPath();
104
+ mkdirSync(dirname(path), { recursive: true });
105
+ writeFileSync(path, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
106
+ chmodSync(path, 384);
107
+ }
108
+ function resolveGatewayUrl(overrides) {
109
+ return overrides?.gatewayUrl ?? process.env.ASSET_GATEWAY_URL ?? loadAuthConfig().gateway_url ?? DEFAULT_GATEWAY_URL;
110
+ }
111
+ function resolveToken(overrides) {
112
+ return overrides?.token ?? process.env.ASSET_GATEWAY_TOKEN ?? loadAuthConfig().token;
113
+ }
114
+
115
+ // src/output.ts
116
+ function success(command, data) {
117
+ return { ok: true, command, data };
118
+ }
119
+ function error(command, code, message, suggestion) {
120
+ return {
121
+ ok: false,
122
+ command,
123
+ error: { code, message, ...suggestion ? { suggestion } : {} }
124
+ };
125
+ }
126
+ function output(result, human = false) {
127
+ if (human) {
128
+ if (result.ok) {
129
+ console.log(`\u2713 ${result.command}`);
130
+ console.log(formatHuman(result.data));
131
+ } else {
132
+ console.error(`\u2717 ${result.command}: ${result.error.message}`);
133
+ if (result.error.suggestion) {
134
+ console.error(` \u2192 ${result.error.suggestion}`);
135
+ }
136
+ }
137
+ } else {
138
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
139
+ }
140
+ }
141
+ function filterFields(data, fields) {
142
+ if (!fields) return data;
143
+ const allowed = new Set(fields.split(",").map((f) => f.trim()));
144
+ const filtered = {};
145
+ for (const key of allowed) {
146
+ if (key in data) {
147
+ filtered[key] = data[key];
148
+ }
149
+ }
150
+ return filtered;
151
+ }
152
+ function formatHuman(data, indent = 0) {
153
+ if (data === null || data === void 0) return "";
154
+ if (typeof data === "string") return " ".repeat(indent) + data;
155
+ if (typeof data !== "object") return " ".repeat(indent) + String(data);
156
+ const pad = " ".repeat(indent);
157
+ if (Array.isArray(data)) {
158
+ return data.map((item) => formatHuman(item, indent + 2)).join("\n");
159
+ }
160
+ return Object.entries(data).map(([key, val]) => {
161
+ if (typeof val === "object" && val !== null) {
162
+ return `${pad}${key}:
163
+ ${formatHuman(val, indent + 2)}`;
164
+ }
165
+ return `${pad}${key}: ${val}`;
166
+ }).join("\n");
167
+ }
168
+
169
+ // src/client.ts
170
+ var GatewayClient = class {
171
+ constructor(baseUrl, token) {
172
+ this.baseUrl = baseUrl;
173
+ this.token = token;
174
+ }
175
+ async get(path) {
176
+ return this.request("GET", path);
177
+ }
178
+ async post(path, body) {
179
+ return this.request("POST", path, { body });
180
+ }
181
+ async put(path, body) {
182
+ return this.request("PUT", path, { body });
183
+ }
184
+ async request(method, path, options = {}) {
185
+ const url = new URL(path, ensureTrailingSlash(this.baseUrl));
186
+ const headers = new Headers(options.headers);
187
+ if (this.token) {
188
+ headers.set("authorization", `Bearer ${this.token}`);
189
+ }
190
+ let body;
191
+ if (options.body !== void 0) {
192
+ headers.set("content-type", "application/json");
193
+ body = JSON.stringify(options.body);
194
+ }
195
+ let response;
196
+ try {
197
+ response = await fetch(url, { method, headers, body });
198
+ } catch (error2) {
199
+ throw httpClientError(
200
+ error2 instanceof Error ? error2.message : String(error2)
201
+ );
202
+ }
203
+ const text = await response.text();
204
+ const payload = parseResponse(text);
205
+ if (!response.ok) {
206
+ const preview = typeof payload === "string" ? payload : JSON.stringify(payload);
207
+ if (response.status === 404) {
208
+ throw notFoundError(`HTTP 404 - ${truncate(preview, 512)}`);
209
+ }
210
+ throw apiError(`HTTP ${response.status} - ${truncate(preview, 512)}`);
211
+ }
212
+ if (isEnvelope(payload)) {
213
+ if (!payload.ok) {
214
+ const errMsg = typeof payload.error === "object" && payload.error !== null ? JSON.stringify(payload.error) : String(payload.error);
215
+ throw apiError(errMsg);
216
+ }
217
+ return payload.data;
218
+ }
219
+ return payload;
220
+ }
221
+ };
222
+ function isEnvelope(value) {
223
+ return typeof value === "object" && value !== null && "ok" in value;
224
+ }
225
+ function ensureTrailingSlash(url) {
226
+ return url.endsWith("/") ? url : `${url}/`;
227
+ }
228
+ function parseResponse(text) {
229
+ if (!text) {
230
+ return null;
231
+ }
232
+ try {
233
+ return JSON.parse(text);
234
+ } catch {
235
+ return text;
236
+ }
237
+ }
238
+ function truncate(value, maxLength) {
239
+ if (value.length <= maxLength) {
240
+ return value;
241
+ }
242
+ return `${value.slice(0, maxLength)}...[truncated]`;
243
+ }
244
+
245
+ // src/commands/common.ts
246
+ function getGlobalOptions(command) {
247
+ return command.optsWithGlobals();
248
+ }
249
+ function createContext(command, requireAuth = true) {
250
+ const globals = getGlobalOptions(command);
251
+ const url = resolveGatewayUrl({ gatewayUrl: globals.gatewayUrl });
252
+ const token = resolveToken({ token: globals.token });
253
+ if (requireAuth && !token) {
254
+ throw configError(
255
+ "Authentication required. Use --token, set ASSET_GATEWAY_TOKEN, or run asset-gateway auth set <token>.",
256
+ "asset-gateway auth set <token>"
257
+ );
258
+ }
259
+ return {
260
+ client: new GatewayClient(url, token),
261
+ human: Boolean(globals.human),
262
+ fields: globals.fields
263
+ };
264
+ }
265
+ function printSuccess(commandName, data, ctx) {
266
+ const filtered = isRecord(data) ? filterFields(data, ctx.fields) : data;
267
+ output(success(commandName, filtered), ctx.human);
268
+ }
269
+ function printError(commandName, err, human = false) {
270
+ const normalized = normalizeError(err);
271
+ output(
272
+ error(commandName, normalized.code, normalized.message, normalized.suggestion),
273
+ human
274
+ );
275
+ process.exit(normalized.exitCode);
276
+ }
277
+ function isRecord(value) {
278
+ return typeof value === "object" && value !== null && !Array.isArray(value);
279
+ }
280
+
281
+ // src/commands/auth.ts
282
+ function createAuthCommand() {
283
+ const command = new Command("auth").description("Credential management");
284
+ command.addCommand(
285
+ new Command("set").description("Save token and gateway URL locally").argument("<token>", "API token (agk_ prefix for admin, or any API key)").action(function(token) {
286
+ const globals = this.optsWithGlobals();
287
+ try {
288
+ const current = loadAuthConfig();
289
+ const gatewayUrl = resolveGatewayUrl({ gatewayUrl: globals.gatewayUrl });
290
+ saveAuthConfig({ ...current, token, gateway_url: gatewayUrl });
291
+ output(
292
+ success("auth.set", { saved: true, gateway_url: gatewayUrl }),
293
+ Boolean(globals.human)
294
+ );
295
+ } catch (error2) {
296
+ printError("auth.set", error2);
297
+ }
298
+ })
299
+ );
300
+ command.addCommand(
301
+ new Command("status").description("Show current authentication status").action(function() {
302
+ const globals = this.optsWithGlobals();
303
+ const config = loadAuthConfig();
304
+ const path = configPath();
305
+ output(
306
+ success("auth.status", {
307
+ config_path: path,
308
+ has_token: Boolean(config.token),
309
+ token_preview: config.token ? mask(config.token) : null,
310
+ gateway_url: config.gateway_url ?? null
311
+ }),
312
+ Boolean(globals.human)
313
+ );
314
+ })
315
+ );
316
+ command.addCommand(
317
+ new Command("clear").description("Remove saved credentials").action(function() {
318
+ const globals = this.optsWithGlobals();
319
+ const path = configPath();
320
+ if (existsSync2(path)) {
321
+ unlinkSync(path);
322
+ }
323
+ output(success("auth.clear", { cleared: true }), Boolean(globals.human));
324
+ })
325
+ );
326
+ return command;
327
+ }
328
+ function mask(token) {
329
+ if (token.length <= 12) return "***";
330
+ return `${token.slice(0, 8)}...${token.slice(-4)}`;
331
+ }
332
+
333
+ // src/commands/describe.ts
334
+ import { Command as Command2 } from "commander";
335
+ var SCHEMAS = {
336
+ auth: {
337
+ description: "Credential management",
338
+ subcommands: {
339
+ set: {
340
+ description: "Save token and gateway URL locally",
341
+ params: { token: { type: "string", required: true, description: "API token (agk_ prefix for admin, or any API key)" } }
342
+ },
343
+ status: { description: "Show current authentication status" },
344
+ clear: { description: "Remove saved credentials" }
345
+ }
346
+ },
347
+ generate: {
348
+ description: "Generate assets via the gateway",
349
+ subcommands: {
350
+ image: {
351
+ description: "Generate an image from a text prompt",
352
+ params: {
353
+ "--prompt": { type: "string", required: true, description: "Image description prompt" },
354
+ "--provider": { type: "string", required: false, description: "Provider to use" },
355
+ "--transparent": { type: "bool", default: false, description: "Request transparent background" },
356
+ "--model": { type: "string", required: false, description: "Model to use" },
357
+ "--size": { type: "string", required: false, description: "Image size (e.g. 1024x1024)" },
358
+ "--output-dir": { type: "string", default: ".", description: "Directory to save output" }
359
+ }
360
+ },
361
+ video: {
362
+ description: "Generate a video from a text prompt",
363
+ params: {
364
+ "--prompt": { type: "string", required: true, description: "Video description prompt" },
365
+ "--provider": { type: "string", required: false, description: "Provider to use" },
366
+ "--output-dir": { type: "string", default: ".", description: "Directory to save output" }
367
+ }
368
+ },
369
+ audio: {
370
+ description: "Generate audio from a text prompt",
371
+ params: {
372
+ "--prompt": { type: "string", required: true, description: "Audio description prompt" },
373
+ "--type": { type: "string", required: false, description: "Audio type: bgm or sfx" },
374
+ "--duration": { type: "number", required: false, description: "Duration in seconds" },
375
+ "--output-dir": { type: "string", default: ".", description: "Directory to save output" }
376
+ }
377
+ },
378
+ model: {
379
+ description: "Generate a 3D model",
380
+ params: {
381
+ "--image": { type: "string", required: false, description: "Reference image URL" },
382
+ "--prompt": { type: "string", required: false, description: "Model description prompt" },
383
+ "--output-dir": { type: "string", default: ".", description: "Directory to save output" }
384
+ }
385
+ },
386
+ text: {
387
+ description: "Generate text via LLM",
388
+ params: {
389
+ "--prompt": { type: "string", required: true, description: "Text prompt" },
390
+ "--model": { type: "string", required: false, description: "Model to use" },
391
+ "--max-tokens": { type: "number", required: false, description: "Maximum tokens" },
392
+ "--output-dir": { type: "string", default: ".", description: "Directory to save output" }
393
+ }
394
+ }
395
+ }
396
+ },
397
+ provider: {
398
+ description: "Provider management",
399
+ subcommands: {
400
+ list: { description: "List available providers" },
401
+ health: {
402
+ description: "Check provider health",
403
+ params: { name: { type: "string", required: false, description: "Specific provider name" } }
404
+ }
405
+ }
406
+ },
407
+ job: {
408
+ description: "Job management",
409
+ subcommands: {
410
+ list: {
411
+ description: "List jobs",
412
+ params: {
413
+ "--status": { type: "string", required: false, description: "Filter by status" },
414
+ "--limit": { type: "number", required: false, description: "Maximum number of jobs to return" }
415
+ }
416
+ },
417
+ status: {
418
+ description: "Get job status",
419
+ params: { id: { type: "string", required: true, description: "Job ID" } }
420
+ },
421
+ cancel: {
422
+ description: "Cancel a job",
423
+ params: { id: { type: "string", required: true, description: "Job ID" } }
424
+ }
425
+ }
426
+ },
427
+ describe: {
428
+ description: "Self-describe available commands (JSON Schema)",
429
+ params: {
430
+ command: { type: "string", required: false, description: "Specific command to describe" }
431
+ }
432
+ }
433
+ };
434
+ function createDescribeCommand() {
435
+ return new Command2("describe").description("Self-describe available commands (JSON Schema)").argument("[command]", "Specific command to describe").action(function(commandArg) {
436
+ const globals = this.optsWithGlobals();
437
+ if (!commandArg) {
438
+ output(
439
+ success("describe", {
440
+ name: CLI_NAME,
441
+ version: CLI_VERSION,
442
+ description: CLI_DESCRIPTION,
443
+ commands: SCHEMAS
444
+ }),
445
+ globals.human
446
+ );
447
+ return;
448
+ }
449
+ const schema = SCHEMAS[commandArg];
450
+ if (!schema) {
451
+ output(
452
+ success("describe", {
453
+ error: `Unknown command: ${commandArg}`,
454
+ available: Object.keys(SCHEMAS)
455
+ }),
456
+ globals.human
457
+ );
458
+ return;
459
+ }
460
+ output(success("describe", { command: commandArg, schema }), globals.human);
461
+ });
462
+ }
463
+
464
+ // src/commands/generate.ts
465
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
466
+ import { join as join2 } from "path";
467
+ import { Command as Command3 } from "commander";
468
+ function inferExtension(assetType) {
469
+ const map = { image: "png", audio: "mp3", video: "mp4", model3d: "glb", text: "txt" };
470
+ return map[assetType] ?? "bin";
471
+ }
472
+ function stripDataUri(data) {
473
+ const idx = data.indexOf(";base64,");
474
+ return idx >= 0 ? data.slice(idx + 8) : data;
475
+ }
476
+ async function saveOutput(result, assetType, outputDir) {
477
+ const ext = inferExtension(assetType);
478
+ const timestamp = Date.now();
479
+ const filename = `${assetType}_${timestamp}.${ext}`;
480
+ mkdirSync2(outputDir, { recursive: true });
481
+ const filePath = join2(outputDir, filename);
482
+ if (result.output_data) {
483
+ const raw = String(result.output_data);
484
+ if (assetType === "text") {
485
+ writeFileSync2(filePath, raw, "utf8");
486
+ } else {
487
+ const base64 = stripDataUri(raw);
488
+ writeFileSync2(filePath, Buffer.from(base64, "base64"));
489
+ }
490
+ return filePath;
491
+ }
492
+ if (result.output_url) {
493
+ const response = await fetch(String(result.output_url));
494
+ if (!response.ok) {
495
+ return null;
496
+ }
497
+ const buffer = Buffer.from(await response.arrayBuffer());
498
+ writeFileSync2(filePath, buffer);
499
+ return filePath;
500
+ }
501
+ return null;
502
+ }
503
+ function createGenerateCommand() {
504
+ const command = new Command3("generate").description("Generate assets via the gateway");
505
+ command.addCommand(
506
+ new Command3("image").description("Generate an image from a text prompt").requiredOption("--prompt <text>", "Image description prompt").option("--provider <id>", "Provider to use").option("--transparent", "Request transparent background").option("--model <model>", "Model to use").option("--size <size>", "Image size (e.g. 1024x1024)").option("--output-dir <dir>", "Directory to save output", ".").action(async function(options) {
507
+ try {
508
+ const ctx = createContext(this);
509
+ const body = {
510
+ asset_type: "image",
511
+ prompt: options.prompt
512
+ };
513
+ if (options.provider) body.provider = options.provider;
514
+ if (options.transparent) body.transparent = true;
515
+ if (options.model) body.model = options.model;
516
+ if (options.size) body.size = options.size;
517
+ const data = await ctx.client.post("/api/generate", body);
518
+ const localPath = await saveOutput(data, "image", options.outputDir);
519
+ if (localPath) data.local_path = localPath;
520
+ printSuccess("generate.image", data, ctx);
521
+ } catch (error2) {
522
+ printError("generate.image", error2);
523
+ }
524
+ })
525
+ );
526
+ command.addCommand(
527
+ new Command3("video").description("Generate a video from a text prompt").requiredOption("--prompt <text>", "Video description prompt").option("--provider <id>", "Provider to use").option("--output-dir <dir>", "Directory to save output", ".").action(async function(options) {
528
+ try {
529
+ const ctx = createContext(this);
530
+ const body = {
531
+ asset_type: "video",
532
+ prompt: options.prompt
533
+ };
534
+ if (options.provider) body.provider = options.provider;
535
+ const data = await ctx.client.post("/api/generate", body);
536
+ const localPath = await saveOutput(data, "video", options.outputDir);
537
+ if (localPath) data.local_path = localPath;
538
+ printSuccess("generate.video", data, ctx);
539
+ } catch (error2) {
540
+ printError("generate.video", error2);
541
+ }
542
+ })
543
+ );
544
+ command.addCommand(
545
+ new Command3("audio").description("Generate audio from a text prompt").requiredOption("--prompt <text>", "Audio description prompt").option("--type <type>", "Audio type: bgm or sfx").option("--duration <seconds>", "Duration in seconds").option("--output-dir <dir>", "Directory to save output", ".").action(async function(options) {
546
+ try {
547
+ const ctx = createContext(this);
548
+ const body = {
549
+ asset_type: "audio",
550
+ prompt: options.prompt
551
+ };
552
+ if (options.type) body.audio_type = options.type;
553
+ if (options.duration) body.duration = Number(options.duration);
554
+ const data = await ctx.client.post("/api/generate", body);
555
+ const localPath = await saveOutput(data, "audio", options.outputDir);
556
+ if (localPath) data.local_path = localPath;
557
+ printSuccess("generate.audio", data, ctx);
558
+ } catch (error2) {
559
+ printError("generate.audio", error2);
560
+ }
561
+ })
562
+ );
563
+ command.addCommand(
564
+ new Command3("model").description("Generate a 3D model").option("--image <url>", "Reference image URL").option("--prompt <text>", "Model description prompt").option("--output-dir <dir>", "Directory to save output", ".").action(async function(options) {
565
+ try {
566
+ const ctx = createContext(this);
567
+ const body = {
568
+ asset_type: "model3d"
569
+ };
570
+ if (options.image) body.image_url = options.image;
571
+ if (options.prompt) body.prompt = options.prompt;
572
+ const data = await ctx.client.post("/api/generate", body);
573
+ const localPath = await saveOutput(data, "model3d", options.outputDir);
574
+ if (localPath) data.local_path = localPath;
575
+ printSuccess("generate.model", data, ctx);
576
+ } catch (error2) {
577
+ printError("generate.model", error2);
578
+ }
579
+ })
580
+ );
581
+ command.addCommand(
582
+ new Command3("text").description("Generate text via LLM").requiredOption("--prompt <text>", "Text prompt").option("--model <model>", "Model to use").option("--max-tokens <n>", "Maximum tokens").option("--output-dir <dir>", "Directory to save output", ".").action(async function(options) {
583
+ try {
584
+ const ctx = createContext(this);
585
+ const body = {
586
+ asset_type: "text",
587
+ prompt: options.prompt
588
+ };
589
+ if (options.model) body.model = options.model;
590
+ if (options.maxTokens) body.max_tokens = Number(options.maxTokens);
591
+ const data = await ctx.client.post("/api/generate", body);
592
+ const localPath = await saveOutput(data, "text", options.outputDir);
593
+ if (localPath) data.local_path = localPath;
594
+ printSuccess("generate.text", data, ctx);
595
+ } catch (error2) {
596
+ printError("generate.text", error2);
597
+ }
598
+ })
599
+ );
600
+ return command;
601
+ }
602
+
603
+ // src/commands/job.ts
604
+ import { Command as Command4 } from "commander";
605
+ function createJobCommand() {
606
+ const command = new Command4("job").description("Job management");
607
+ command.addCommand(
608
+ new Command4("list").description("List jobs").option("--status <status>", "Filter by status").option("--limit <n>", "Maximum number of jobs to return").action(async function(options) {
609
+ try {
610
+ const ctx = createContext(this);
611
+ const params = new URLSearchParams();
612
+ if (options.status) params.set("status", options.status);
613
+ if (options.limit) params.set("limit", options.limit);
614
+ const query = params.toString();
615
+ const path = query ? `/api/jobs?${query}` : "/api/jobs";
616
+ const data = await ctx.client.get(path);
617
+ printSuccess("job.list", data, ctx);
618
+ } catch (error2) {
619
+ printError("job.list", error2);
620
+ }
621
+ })
622
+ );
623
+ command.addCommand(
624
+ new Command4("status").description("Get job status").argument("<id>", "Job ID").action(async function(id) {
625
+ try {
626
+ const ctx = createContext(this);
627
+ const data = await ctx.client.get(`/api/jobs/${encodeURIComponent(id)}`);
628
+ printSuccess("job.status", data, ctx);
629
+ } catch (error2) {
630
+ printError("job.status", error2);
631
+ }
632
+ })
633
+ );
634
+ command.addCommand(
635
+ new Command4("cancel").description("Cancel a job").argument("<id>", "Job ID").action(async function(id) {
636
+ try {
637
+ const ctx = createContext(this);
638
+ const data = await ctx.client.post(`/api/jobs/${encodeURIComponent(id)}/cancel`);
639
+ printSuccess("job.cancel", data, ctx);
640
+ } catch (error2) {
641
+ printError("job.cancel", error2);
642
+ }
643
+ })
644
+ );
645
+ return command;
646
+ }
647
+
648
+ // src/commands/provider.ts
649
+ import { Command as Command5 } from "commander";
650
+ function createProviderCommand() {
651
+ const command = new Command5("provider").description("Provider management");
652
+ command.addCommand(
653
+ new Command5("list").description("List available providers").action(async function() {
654
+ try {
655
+ const ctx = createContext(this);
656
+ const data = await ctx.client.get("/api/providers");
657
+ printSuccess("provider.list", data, ctx);
658
+ } catch (error2) {
659
+ printError("provider.list", error2);
660
+ }
661
+ })
662
+ );
663
+ command.addCommand(
664
+ new Command5("health").description("Check provider health").argument("[name]", "Specific provider name").action(async function(name) {
665
+ try {
666
+ const ctx = createContext(this);
667
+ const path = name ? `/api/providers/${encodeURIComponent(name)}/health` : "/api/providers/health";
668
+ const data = await ctx.client.get(path);
669
+ printSuccess("provider.health", data, ctx);
670
+ } catch (error2) {
671
+ printError("provider.health", error2);
672
+ }
673
+ })
674
+ );
675
+ return command;
676
+ }
677
+
678
+ // src/index.ts
679
+ var program = new Command6().name("asset-gateway").description("Universal asset generation gateway CLI").version(CLI_VERSION).option(
680
+ "--gateway-url <url>",
681
+ `Gateway URL (default: $ASSET_GATEWAY_URL, auth config, or ${DEFAULT_GATEWAY_URL})`
682
+ ).option("--token <token>", "API token for authentication").option("--human", "Human-readable output instead of JSON").option("--fields <fields>", "Comma-separated list of output fields");
683
+ program.addCommand(createAuthCommand());
684
+ program.addCommand(createGenerateCommand());
685
+ program.addCommand(createProviderCommand());
686
+ program.addCommand(createJobCommand());
687
+ program.addCommand(createDescribeCommand());
688
+ await program.parseAsync(process.argv);
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@doufunao123/asset-gateway",
3
+ "version": "0.1.0",
4
+ "description": "Universal asset generation gateway CLI",
5
+ "type": "module",
6
+ "bin": {
7
+ "asset-gateway": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/fran0220/agent-skills.git",
16
+ "directory": "asset-gateway/npm"
17
+ },
18
+ "author": "doufunao123",
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm --dts --clean",
21
+ "dev": "tsup src/index.ts --format esm --watch",
22
+ "lint": "tsc --noEmit",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "dependencies": {
29
+ "commander": "^13.1.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22.15.0",
33
+ "tsup": "^8.4.0",
34
+ "typescript": "^5.8.0"
35
+ }
36
+ }