@warlok-net/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/dist/index.js ADDED
@@ -0,0 +1,820 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ formatCliError: () => formatCliError,
34
+ mainHelpText: () => mainHelpText,
35
+ run: () => run,
36
+ runAgent: () => runAgent,
37
+ runDownload: () => runDownload,
38
+ runGenerate: () => runGenerate,
39
+ runList: () => runList,
40
+ runStatus: () => runStatus,
41
+ runTools: () => runTools
42
+ });
43
+ module.exports = __toCommonJS(index_exports);
44
+ var import_promises = require("fs/promises");
45
+ var import_node_os = __toESM(require("os"));
46
+ var import_node_path = __toESM(require("path"));
47
+ var import_node_process = require("process");
48
+ var import_promises2 = require("readline/promises");
49
+ var import_sdk = require("@warlok-net/sdk");
50
+ var SHORT_FLAG_MAP = {
51
+ p: "prompt",
52
+ d: "description",
53
+ i: "image",
54
+ e: "entity",
55
+ t: "entity",
56
+ n: "name",
57
+ o: "output",
58
+ k: "api-key",
59
+ u: "base-url"
60
+ };
61
+ var DEFAULT_API_URL = "https://api.warlok.net";
62
+ var DEFAULT_CONFIG_FILENAME = "config.json";
63
+ var GENERATION_STATUSES = /* @__PURE__ */ new Set([
64
+ "pending",
65
+ "preprocessing",
66
+ "generating",
67
+ "rigging",
68
+ "complete",
69
+ "failed"
70
+ ]);
71
+ var ENTITY_ALIASES = {
72
+ noun: "noun",
73
+ person: "person",
74
+ place: "place",
75
+ thing: "thing",
76
+ character: "person",
77
+ creature: "person",
78
+ environment: "place",
79
+ prop: "thing",
80
+ weapon: "thing",
81
+ vehicle: "thing",
82
+ material: "thing"
83
+ };
84
+ function parseArgs(args) {
85
+ const positional = [];
86
+ const options = {};
87
+ for (let i = 0; i < args.length; i += 1) {
88
+ const token = args[i];
89
+ if (token === "--") {
90
+ positional.push(...args.slice(i + 1));
91
+ break;
92
+ }
93
+ if (token.startsWith("--no-")) {
94
+ options[token.slice(5)] = false;
95
+ continue;
96
+ }
97
+ if (token.startsWith("--")) {
98
+ const [rawKey, inlineValue] = token.slice(2).split("=", 2);
99
+ if (inlineValue !== void 0) {
100
+ options[rawKey] = inlineValue;
101
+ continue;
102
+ }
103
+ const next = args[i + 1];
104
+ if (next && !next.startsWith("-")) {
105
+ options[rawKey] = next;
106
+ i += 1;
107
+ } else {
108
+ options[rawKey] = true;
109
+ }
110
+ continue;
111
+ }
112
+ if (token.startsWith("-") && token.length > 1) {
113
+ const raw = token.slice(1);
114
+ if (raw.length > 1) {
115
+ const alias2 = SHORT_FLAG_MAP[raw[0]];
116
+ if (alias2) {
117
+ options[alias2] = raw.slice(1);
118
+ continue;
119
+ }
120
+ }
121
+ const alias = SHORT_FLAG_MAP[raw];
122
+ if (!alias) {
123
+ positional.push(token);
124
+ continue;
125
+ }
126
+ const next = args[i + 1];
127
+ if (next && !next.startsWith("-")) {
128
+ options[alias] = next;
129
+ i += 1;
130
+ } else {
131
+ options[alias] = true;
132
+ }
133
+ continue;
134
+ }
135
+ positional.push(token);
136
+ }
137
+ return { positional, options };
138
+ }
139
+ function stringOption(options, key) {
140
+ const value = options[key];
141
+ if (typeof value === "string" && value.trim()) {
142
+ return value.trim();
143
+ }
144
+ return void 0;
145
+ }
146
+ function numberOption(options, key) {
147
+ const raw = stringOption(options, key);
148
+ if (!raw) {
149
+ return void 0;
150
+ }
151
+ const parsed = Number(raw);
152
+ return Number.isFinite(parsed) ? parsed : void 0;
153
+ }
154
+ function booleanOption(options, key, defaultValue) {
155
+ const value = options[key];
156
+ if (typeof value === "boolean") {
157
+ return value;
158
+ }
159
+ if (typeof value === "string") {
160
+ const normalized = value.toLowerCase();
161
+ if (normalized === "false" || normalized === "0" || normalized === "no") {
162
+ return false;
163
+ }
164
+ if (normalized === "true" || normalized === "1" || normalized === "yes") {
165
+ return true;
166
+ }
167
+ }
168
+ return defaultValue;
169
+ }
170
+ function isUrl(value) {
171
+ try {
172
+ const parsed = new URL(value);
173
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
174
+ } catch {
175
+ return false;
176
+ }
177
+ }
178
+ function normalizeEntityType(raw) {
179
+ if (!raw) {
180
+ return "thing";
181
+ }
182
+ const normalized = raw.toLowerCase();
183
+ const mapped = ENTITY_ALIASES[normalized];
184
+ if (!mapped) {
185
+ throw new Error(
186
+ `Unsupported entity type "${raw}". Use noun|person|place|thing.`
187
+ );
188
+ }
189
+ return mapped;
190
+ }
191
+ function resolveConfigPath(options) {
192
+ return stringOption(options || {}, "config") || process.env.WARLOK_CONFIG_PATH || import_node_path.default.join(import_node_os.default.homedir(), ".warlok", DEFAULT_CONFIG_FILENAME);
193
+ }
194
+ async function readCliConfig(options) {
195
+ const configPath = resolveConfigPath(options);
196
+ try {
197
+ const raw = await (0, import_promises.readFile)(configPath, "utf8");
198
+ const parsed = JSON.parse(raw);
199
+ if (!parsed || typeof parsed !== "object") {
200
+ return {};
201
+ }
202
+ return parsed;
203
+ } catch {
204
+ return {};
205
+ }
206
+ }
207
+ async function writeCliConfig(nextConfig, options) {
208
+ const configPath = resolveConfigPath(options);
209
+ const dir = import_node_path.default.dirname(configPath);
210
+ await (0, import_promises.mkdir)(dir, { recursive: true });
211
+ await (0, import_promises.writeFile)(configPath, `${JSON.stringify(nextConfig, null, 2)}
212
+ `, "utf8");
213
+ return configPath;
214
+ }
215
+ function resolveBaseUrl(options, config) {
216
+ return stringOption(options, "base-url") || process.env.WARLOK_API_URL || config?.apiUrl || DEFAULT_API_URL;
217
+ }
218
+ function isLocalBaseUrl(baseUrl) {
219
+ try {
220
+ const parsed = new URL(baseUrl);
221
+ return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname.endsWith(".local");
222
+ } catch {
223
+ return false;
224
+ }
225
+ }
226
+ function resolveApiKey(options, baseUrl, config) {
227
+ const provided = stringOption(options, "api-key") || process.env.WARLOK_API_KEY || config?.apiKey;
228
+ if (provided) {
229
+ return provided;
230
+ }
231
+ if (isLocalBaseUrl(baseUrl)) {
232
+ return "wk_dev_local";
233
+ }
234
+ throw new Error(
235
+ "Missing API key. Set WARLOK_API_KEY or pass --api-key <key>."
236
+ );
237
+ }
238
+ function createClient(options, config) {
239
+ const baseUrl = resolveBaseUrl(options, config);
240
+ const apiKey = resolveApiKey(options, baseUrl, config);
241
+ return new import_sdk.WarlokClient({ apiKey, baseUrl });
242
+ }
243
+ function shouldPrintJson(options) {
244
+ return booleanOption(options, "json", false);
245
+ }
246
+ function print(value, asJson) {
247
+ if (asJson) {
248
+ process.stdout.write(`${JSON.stringify(value, null, 2)}
249
+ `);
250
+ return;
251
+ }
252
+ if (typeof value === "string") {
253
+ process.stdout.write(`${value}
254
+ `);
255
+ return;
256
+ }
257
+ process.stdout.write(`${JSON.stringify(value, null, 2)}
258
+ `);
259
+ }
260
+ async function loadImageInput(imageInput) {
261
+ if (!imageInput) {
262
+ return {};
263
+ }
264
+ if (isUrl(imageInput)) {
265
+ return { imageUrl: imageInput };
266
+ }
267
+ const absolutePath = import_node_path.default.resolve(process.cwd(), imageInput);
268
+ const data = await (0, import_promises.readFile)(absolutePath);
269
+ return { imageBase64: data.toString("base64") };
270
+ }
271
+ function defaultOutputPath(id, format) {
272
+ return import_node_path.default.resolve(
273
+ process.cwd(),
274
+ format === "rigged" ? `${id}-rigged.glb` : `${id}.glb`
275
+ );
276
+ }
277
+ async function writeBlobToFile(blob, outputPath) {
278
+ const arrayBuffer = await blob.arrayBuffer();
279
+ const buffer = Buffer.from(arrayBuffer);
280
+ await (0, import_promises.writeFile)(outputPath, buffer);
281
+ }
282
+ function formatGenerationSummary(generation) {
283
+ return {
284
+ id: generation.id,
285
+ name: generation.name,
286
+ entityType: generation.entityType,
287
+ status: generation.status,
288
+ progress: generation.progress,
289
+ modelUrl: generation.outputs.modelUrl,
290
+ riggedModelUrl: generation.outputs.riggedModelUrl,
291
+ error: generation.error,
292
+ createdAt: generation.createdAt,
293
+ updatedAt: generation.updatedAt
294
+ };
295
+ }
296
+ async function runGenerate(args) {
297
+ const parsed = parseArgs(args);
298
+ const options = parsed.options;
299
+ const positional = [...parsed.positional];
300
+ const isCharacterSubcommand = positional[0]?.toLowerCase() === "character";
301
+ const descriptionPositional = isCharacterSubcommand ? positional.slice(1).join(" ").trim() : "";
302
+ const prompt = stringOption(options, "prompt") || stringOption(options, "description") || (descriptionPositional || void 0);
303
+ const image = stringOption(options, "image");
304
+ if (!prompt && !image) {
305
+ if (isCharacterSubcommand) {
306
+ throw new Error(
307
+ "Character generation requires a description and/or --image."
308
+ );
309
+ }
310
+ throw new Error("Generate requires --prompt/--description and/or --image.");
311
+ }
312
+ const requestedEntity = stringOption(options, "entity") || stringOption(options, "type");
313
+ const entityType = isCharacterSubcommand ? "person" : normalizeEntityType(requestedEntity);
314
+ if (isCharacterSubcommand && requestedEntity) {
315
+ const normalized = normalizeEntityType(requestedEntity);
316
+ if (normalized !== "person") {
317
+ throw new Error(
318
+ "Character subcommand always generates entity type person. Remove --entity/--type override."
319
+ );
320
+ }
321
+ }
322
+ const config = await readCliConfig(options);
323
+ const client = createClient(options, config);
324
+ const { imageUrl, imageBase64 } = await loadImageInput(image);
325
+ const generation = await client.generate({
326
+ name: stringOption(options, "name"),
327
+ entityType,
328
+ prompt,
329
+ imageUrl,
330
+ imageBase64,
331
+ options: {
332
+ rigForMixamo: booleanOption(options, "rig", entityType === "person"),
333
+ includeTpose: booleanOption(options, "tpose", entityType === "person"),
334
+ optimize: booleanOption(options, "optimize", true)
335
+ }
336
+ });
337
+ const wait = booleanOption(options, "wait", false);
338
+ const asJson = shouldPrintJson(options);
339
+ if (!wait) {
340
+ print(
341
+ {
342
+ ...formatGenerationSummary(generation),
343
+ message: "Generation queued. Use `warlok status <id>` to check progress, or pass --wait."
344
+ },
345
+ asJson
346
+ );
347
+ return;
348
+ }
349
+ const completed = await client.waitForCompletion(generation.id, {
350
+ pollingInterval: numberOption(options, "interval") || 2e3,
351
+ timeout: numberOption(options, "timeout") || 3e5
352
+ });
353
+ print(formatGenerationSummary(completed), asJson);
354
+ }
355
+ async function runStatus(args) {
356
+ const parsed = parseArgs(args);
357
+ const id = parsed.positional[0];
358
+ if (!id) {
359
+ throw new Error("Status requires a generation id: warlok status <id>");
360
+ }
361
+ const config = await readCliConfig(parsed.options);
362
+ const client = createClient(parsed.options, config);
363
+ const generation = await client.getGeneration(id);
364
+ print(formatGenerationSummary(generation), shouldPrintJson(parsed.options));
365
+ }
366
+ async function runList(args) {
367
+ const parsed = parseArgs(args);
368
+ const statusInput = stringOption(parsed.options, "status");
369
+ const status = statusInput ? statusInput : void 0;
370
+ if (status && !GENERATION_STATUSES.has(status)) {
371
+ throw new Error(
372
+ `Unsupported status "${statusInput}". Use pending|preprocessing|generating|rigging|complete|failed.`
373
+ );
374
+ }
375
+ const config = await readCliConfig(parsed.options);
376
+ const client = createClient(parsed.options, config);
377
+ const generations = await client.listGenerations({
378
+ limit: numberOption(parsed.options, "limit"),
379
+ status,
380
+ entityType: normalizeEntityType(stringOption(parsed.options, "entity"))
381
+ });
382
+ const rows = generations.map(formatGenerationSummary);
383
+ print({ total: rows.length, items: rows }, true);
384
+ }
385
+ async function runDownload(args) {
386
+ const parsed = parseArgs(args);
387
+ const id = parsed.positional[0];
388
+ if (!id) {
389
+ throw new Error("Download requires a generation id: warlok download <id>");
390
+ }
391
+ const format = booleanOption(
392
+ parsed.options,
393
+ "rigged",
394
+ false
395
+ ) ? "rigged" : booleanOption(parsed.options, "model", false) ? "model" : void 0;
396
+ const outputPath = stringOption(parsed.options, "output") || defaultOutputPath(id, format);
397
+ const config = await readCliConfig(parsed.options);
398
+ const client = createClient(parsed.options, config);
399
+ const blob = await client.downloadGeneration(id, format);
400
+ await writeBlobToFile(blob, outputPath);
401
+ print(
402
+ {
403
+ id,
404
+ format: format || "best_available",
405
+ output: outputPath
406
+ },
407
+ true
408
+ );
409
+ }
410
+ async function runTools(args) {
411
+ const parsed = parseArgs(args);
412
+ const config = await readCliConfig(parsed.options);
413
+ const baseUrl = resolveBaseUrl(parsed.options, config);
414
+ const apiKey = resolveApiKey(parsed.options, baseUrl, config);
415
+ const response = await fetch(`${baseUrl}/api/v1/mcp/tools`, {
416
+ headers: {
417
+ Authorization: `Bearer ${apiKey}`
418
+ }
419
+ });
420
+ const payload = await response.json();
421
+ if (!response.ok) {
422
+ throw new Error(
423
+ payload?.error?.message || `Failed to fetch tools (${response.status})`
424
+ );
425
+ }
426
+ print(payload?.data || payload, true);
427
+ }
428
+ async function runAgent(args) {
429
+ const [subcommand, ...rest] = args;
430
+ if (subcommand !== "run") {
431
+ throw new Error("Agent command requires `run`: warlok agent run <goal>");
432
+ }
433
+ const parsed = parseArgs(rest);
434
+ const goal = parsed.positional.join(" ").trim();
435
+ if (!goal) {
436
+ throw new Error("Agent goal is required: warlok agent run <goal>");
437
+ }
438
+ const config = await readCliConfig(parsed.options);
439
+ const baseUrl = resolveBaseUrl(parsed.options, config);
440
+ const apiKey = resolveApiKey(parsed.options, baseUrl, config);
441
+ const toolsResponse = await fetch(`${baseUrl}/api/v1/mcp/tools`, {
442
+ headers: {
443
+ Authorization: `Bearer ${apiKey}`
444
+ }
445
+ });
446
+ const toolsPayload = await toolsResponse.json();
447
+ const tools = toolsPayload?.data?.tools || [];
448
+ print(
449
+ {
450
+ goal,
451
+ message: "Use the listed MCP tools from your agent runtime to execute this goal.",
452
+ tools: tools.map((tool) => ({
453
+ name: tool.name,
454
+ description: tool.description
455
+ }))
456
+ },
457
+ true
458
+ );
459
+ }
460
+ function redactApiKey(key) {
461
+ if (!key) {
462
+ return void 0;
463
+ }
464
+ if (key.length <= 8) {
465
+ return "****";
466
+ }
467
+ return `${key.slice(0, 4)}...${key.slice(-4)}`;
468
+ }
469
+ async function promptForApiKey() {
470
+ const rl = (0, import_promises2.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
471
+ try {
472
+ const entered = (await rl.question("Enter WARLOK API key: ")).trim();
473
+ if (!entered) {
474
+ throw new Error("API key is required.");
475
+ }
476
+ return entered;
477
+ } finally {
478
+ rl.close();
479
+ }
480
+ }
481
+ async function verifyCredentials(apiKey, baseUrl) {
482
+ try {
483
+ const response = await fetch(`${baseUrl}/api/v1/generations`, {
484
+ headers: {
485
+ Authorization: `Bearer ${apiKey}`
486
+ }
487
+ });
488
+ if (response.ok) {
489
+ return { ok: true };
490
+ }
491
+ let message = `HTTP ${response.status}`;
492
+ try {
493
+ const payload = await response.json();
494
+ message = payload?.error?.message || payload?.error?.code || message;
495
+ } catch {
496
+ }
497
+ return { ok: false, reason: message };
498
+ } catch (error) {
499
+ return {
500
+ ok: false,
501
+ reason: error instanceof Error ? error.message : "request failed"
502
+ };
503
+ }
504
+ }
505
+ async function runDoctor(args) {
506
+ const parsed = parseArgs(args);
507
+ const asJson = shouldPrintJson(parsed.options);
508
+ const config = await readCliConfig(parsed.options);
509
+ const baseUrl = resolveBaseUrl(parsed.options, config);
510
+ const hasApiKey = Boolean(
511
+ stringOption(parsed.options, "api-key") || process.env.WARLOK_API_KEY || config.apiKey
512
+ );
513
+ const checks = [];
514
+ checks.push({
515
+ name: "base_url",
516
+ ok: isUrl(baseUrl),
517
+ detail: isUrl(baseUrl) ? baseUrl : `invalid URL: ${baseUrl}`
518
+ });
519
+ let healthOk = false;
520
+ try {
521
+ const response = await fetch(`${baseUrl}/api/v1/health`);
522
+ healthOk = response.ok;
523
+ checks.push({
524
+ name: "health",
525
+ ok: response.ok,
526
+ detail: response.ok ? "reachable" : `HTTP ${response.status}`
527
+ });
528
+ } catch (error) {
529
+ checks.push({
530
+ name: "health",
531
+ ok: false,
532
+ detail: error instanceof Error ? error.message : "request failed"
533
+ });
534
+ }
535
+ checks.push({
536
+ name: "api_key",
537
+ ok: hasApiKey,
538
+ detail: hasApiKey ? "configured" : "missing (WARLOK_API_KEY/config/--api-key)"
539
+ });
540
+ if (hasApiKey && healthOk) {
541
+ try {
542
+ const client = createClient(parsed.options, config);
543
+ await client.listGenerations({ limit: 1 });
544
+ checks.push({
545
+ name: "auth",
546
+ ok: true,
547
+ detail: "API key accepted"
548
+ });
549
+ } catch (error) {
550
+ checks.push({
551
+ name: "auth",
552
+ ok: false,
553
+ detail: formatCliError(error)
554
+ });
555
+ }
556
+ }
557
+ if (hasApiKey && healthOk) {
558
+ try {
559
+ const apiKey = resolveApiKey(parsed.options, baseUrl, config);
560
+ const response = await fetch(`${baseUrl}/api/v1/mcp/tools`, {
561
+ headers: {
562
+ Authorization: `Bearer ${apiKey}`
563
+ }
564
+ });
565
+ checks.push({
566
+ name: "mcp_tools",
567
+ ok: response.ok,
568
+ detail: response.ok ? "reachable" : `HTTP ${response.status}`
569
+ });
570
+ } catch (error) {
571
+ checks.push({
572
+ name: "mcp_tools",
573
+ ok: false,
574
+ detail: error instanceof Error ? error.message : "request failed"
575
+ });
576
+ }
577
+ }
578
+ const summary = {
579
+ ok: checks.every((check) => check.ok),
580
+ baseUrl,
581
+ configPath: resolveConfigPath(parsed.options),
582
+ checks
583
+ };
584
+ if (asJson) {
585
+ print(summary, true);
586
+ return;
587
+ }
588
+ process.stdout.write("Warlok doctor\n\n");
589
+ for (const check of checks) {
590
+ process.stdout.write(
591
+ `${check.ok ? "OK" : "FAIL"} ${check.name}: ${check.detail}
592
+ `
593
+ );
594
+ }
595
+ process.stdout.write(`
596
+ Overall: ${summary.ok ? "healthy" : "issues found"}
597
+ `);
598
+ }
599
+ async function runAuth(args) {
600
+ const [subcommand, ...rest] = args;
601
+ const parsed = parseArgs(rest);
602
+ const asJson = shouldPrintJson(parsed.options);
603
+ const config = await readCliConfig(parsed.options);
604
+ const baseUrl = resolveBaseUrl(parsed.options, config);
605
+ switch (subcommand) {
606
+ case "login": {
607
+ const apiKey = stringOption(parsed.options, "api-key") || process.env.WARLOK_API_KEY || await promptForApiKey();
608
+ const nextConfig = {
609
+ ...config,
610
+ apiUrl: baseUrl,
611
+ apiKey,
612
+ profile: config.profile || "default"
613
+ };
614
+ const verify = await verifyCredentials(apiKey, baseUrl);
615
+ if (!verify.ok) {
616
+ throw new Error(`Auth verification failed: ${verify.reason || "unknown"}`);
617
+ }
618
+ const savedPath = await writeCliConfig(nextConfig, parsed.options);
619
+ print(
620
+ {
621
+ message: "Login successful",
622
+ apiUrl: baseUrl,
623
+ apiKey: redactApiKey(apiKey),
624
+ configPath: savedPath
625
+ },
626
+ asJson
627
+ );
628
+ return;
629
+ }
630
+ case "whoami": {
631
+ const effectiveApiKey = stringOption(parsed.options, "api-key") || process.env.WARLOK_API_KEY || config.apiKey;
632
+ const verify = effectiveApiKey ? await verifyCredentials(effectiveApiKey, baseUrl) : { ok: false, reason: "no API key configured" };
633
+ print(
634
+ {
635
+ authenticated: verify.ok,
636
+ reason: verify.ok ? void 0 : verify.reason,
637
+ apiUrl: baseUrl,
638
+ apiKey: redactApiKey(effectiveApiKey),
639
+ configPath: resolveConfigPath(parsed.options),
640
+ source: {
641
+ apiKey: stringOption(parsed.options, "api-key") ? "flag" : process.env.WARLOK_API_KEY ? "env" : config.apiKey ? "config" : "none"
642
+ }
643
+ },
644
+ asJson
645
+ );
646
+ return;
647
+ }
648
+ case "logout": {
649
+ const keepBaseUrl = booleanOption(parsed.options, "keep-base-url", true);
650
+ const nextConfig = {
651
+ ...config,
652
+ apiKey: void 0,
653
+ apiUrl: keepBaseUrl ? config.apiUrl : void 0
654
+ };
655
+ const savedPath = await writeCliConfig(nextConfig, parsed.options);
656
+ print(
657
+ {
658
+ message: "Logged out",
659
+ configPath: savedPath,
660
+ keptBaseUrl: keepBaseUrl
661
+ },
662
+ asJson
663
+ );
664
+ return;
665
+ }
666
+ default:
667
+ throw new Error("Auth command requires login|whoami|logout");
668
+ }
669
+ }
670
+ async function runMcp(args) {
671
+ const [subcommand, ...rest] = args;
672
+ const parsed = parseArgs(rest);
673
+ const config = await readCliConfig(parsed.options);
674
+ const baseUrl = resolveBaseUrl(parsed.options, config);
675
+ const apiKey = resolveApiKey(parsed.options, baseUrl, config);
676
+ if (subcommand !== "print-config") {
677
+ throw new Error("MCP command requires `print-config`.");
678
+ }
679
+ const json = {
680
+ mcpServers: {
681
+ warlok: {
682
+ command: "pnpm",
683
+ args: ["dlx", "@warlok-net/mcp-server@latest"],
684
+ env: {
685
+ WARLOK_API_URL: baseUrl,
686
+ WARLOK_API_KEY: apiKey
687
+ }
688
+ }
689
+ }
690
+ };
691
+ const format = stringOption(parsed.options, "format") || "json";
692
+ if (format === "json") {
693
+ print(json, true);
694
+ return;
695
+ }
696
+ if (format === "env") {
697
+ process.stdout.write(`WARLOK_API_URL=${baseUrl}
698
+ `);
699
+ process.stdout.write(`WARLOK_API_KEY=${apiKey}
700
+ `);
701
+ process.stdout.write("pnpm dlx @warlok-net/mcp-server@latest\n");
702
+ return;
703
+ }
704
+ throw new Error("Unsupported MCP format. Use --format json|env.");
705
+ }
706
+ function mainHelpText() {
707
+ return `
708
+ Warlok CLI
709
+
710
+ Usage:
711
+ warlok <command> [options]
712
+
713
+ Commands:
714
+ generate Start a generation from prompt/image
715
+ status <id> Get generation status
716
+ list List generations
717
+ download <id> Download generated model
718
+ auth Login/logout and inspect CLI auth state
719
+ doctor Validate local CLI/API configuration
720
+ mcp MCP helper commands
721
+ tools List MCP-compatible tools from API
722
+ agent run Print agent execution context for a goal
723
+
724
+ Global options:
725
+ --api-key <key> API key (or set WARLOK_API_KEY)
726
+ --base-url <url> API base URL (or set WARLOK_API_URL)
727
+ --config <path> Config path (or set WARLOK_CONFIG_PATH)
728
+ --json JSON output where supported
729
+
730
+ Generate options:
731
+ generate character "<description>" [--image <path-or-url>] [--wait]
732
+ --prompt, -p <text>
733
+ --description, -d <text> Alias for --prompt
734
+ --image, -i <path-or-url>
735
+ --entity, -e <noun|person|place|thing> (aliases supported)
736
+ --name, -n <name>
737
+ --wait Poll until terminal status
738
+ --interval <ms> Poll interval when --wait (default 2000)
739
+ --timeout <ms> Timeout when --wait (default 300000)
740
+ --rig / --no-rig
741
+ --tpose / --no-tpose
742
+ --optimize / --no-optimize
743
+
744
+ Download options:
745
+ --rigged Prefer rigged model
746
+ --model Prefer base model
747
+ --output, -o <path> Save path (default: ./<id>.glb)
748
+
749
+ Auth:
750
+ auth login [--api-key <key>] [--base-url <url>]
751
+ auth whoami
752
+ auth logout [--keep-base-url=true]
753
+
754
+ Doctor:
755
+ doctor [--json]
756
+
757
+ MCP:
758
+ mcp print-config [--format json|env]
759
+ `.trimStart();
760
+ }
761
+ async function run(argv) {
762
+ const [command, ...rest] = argv;
763
+ if (!command || command === "help" || command === "--help" || command === "-h") {
764
+ process.stdout.write(mainHelpText());
765
+ process.stdout.write("\n");
766
+ return;
767
+ }
768
+ switch (command) {
769
+ case "generate":
770
+ await runGenerate(rest);
771
+ return;
772
+ case "status":
773
+ await runStatus(rest);
774
+ return;
775
+ case "list":
776
+ await runList(rest);
777
+ return;
778
+ case "download":
779
+ await runDownload(rest);
780
+ return;
781
+ case "auth":
782
+ await runAuth(rest);
783
+ return;
784
+ case "doctor":
785
+ await runDoctor(rest);
786
+ return;
787
+ case "mcp":
788
+ await runMcp(rest);
789
+ return;
790
+ case "tools":
791
+ await runTools(rest);
792
+ return;
793
+ case "agent":
794
+ await runAgent(rest);
795
+ return;
796
+ default:
797
+ throw new Error(`Unknown command: ${command}`);
798
+ }
799
+ }
800
+ function formatCliError(error) {
801
+ if (error instanceof import_sdk.WarlokError) {
802
+ return `${error.message} [${error.code}]`;
803
+ }
804
+ if (error instanceof Error) {
805
+ return error.message;
806
+ }
807
+ return "Unknown error";
808
+ }
809
+ // Annotate the CommonJS export names for ESM import in node:
810
+ 0 && (module.exports = {
811
+ formatCliError,
812
+ mainHelpText,
813
+ run,
814
+ runAgent,
815
+ runDownload,
816
+ runGenerate,
817
+ runList,
818
+ runStatus,
819
+ runTools
820
+ });