@aigne/afs-aignehub 1.11.0-beta.10

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.
@@ -0,0 +1,1635 @@
1
+ import { aigneHubAuth } from "./auth.mjs";
2
+ import { afsAigneHubOptionsSchema } from "./types.mjs";
3
+ import { __decorate } from "./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs";
4
+ import { AFSBaseProvider, AFSNotFoundError, Actions, Explain, List, Meta, Read, Stat } from "@aigne/afs";
5
+ import { zodParse } from "@aigne/afs/utils/zod";
6
+ import { AIGNEHubChatModel, AIGNEHubImageModel, AIGNEHubVideoModel, AIGNE_HUB_URL } from "@aigne/aigne-hub";
7
+ import { joinURL } from "ufo";
8
+ import { z } from "zod";
9
+
10
+ //#region src/aignehub.ts
11
+ const MODEL_TYPES = [
12
+ "chat",
13
+ "image",
14
+ "video"
15
+ ];
16
+ const CHAT_INPUT_PROPERTIES = {
17
+ prompt: {
18
+ type: "string",
19
+ description: "Your question or instruction (shorthand for single user message)"
20
+ },
21
+ image: {
22
+ type: "string",
23
+ description: "Image URL or base64 data URI for vision (used with prompt)"
24
+ },
25
+ messages: {
26
+ type: "array",
27
+ items: {
28
+ type: "object",
29
+ properties: {
30
+ role: {
31
+ type: "string",
32
+ description: "Message role (user, assistant, system)"
33
+ },
34
+ content: {
35
+ type: "string",
36
+ description: "Message content"
37
+ }
38
+ },
39
+ required: ["role", "content"]
40
+ },
41
+ description: "Full conversation messages array (takes priority over prompt)"
42
+ },
43
+ temperature: {
44
+ type: "number",
45
+ description: "Sampling temperature (0-2)"
46
+ },
47
+ topP: {
48
+ type: "number",
49
+ description: "Nucleus sampling parameter (0-1)"
50
+ },
51
+ frequencyPenalty: {
52
+ type: "number",
53
+ description: "Frequency penalty (-2 to 2)"
54
+ },
55
+ presencePenalty: {
56
+ type: "number",
57
+ description: "Presence penalty (-2 to 2)"
58
+ },
59
+ reasoningEffort: {
60
+ type: "string",
61
+ enum: [
62
+ "low",
63
+ "medium",
64
+ "high"
65
+ ],
66
+ description: "Reasoning effort level for supported models"
67
+ },
68
+ responseFormat: {
69
+ type: "object",
70
+ properties: {
71
+ type: {
72
+ type: "string",
73
+ enum: ["text", "json_schema"]
74
+ },
75
+ jsonSchema: {
76
+ type: "object",
77
+ description: "JSON Schema when type is json_schema"
78
+ }
79
+ },
80
+ description: "Response format configuration"
81
+ },
82
+ tools: {
83
+ type: "array",
84
+ items: { type: "object" },
85
+ description: "Function tool definitions for tool-calling models"
86
+ },
87
+ toolChoice: {
88
+ type: "string",
89
+ enum: [
90
+ "auto",
91
+ "none",
92
+ "required"
93
+ ],
94
+ description: "Tool choice strategy"
95
+ },
96
+ outputFileType: {
97
+ type: "string",
98
+ enum: [
99
+ "url",
100
+ "file",
101
+ "local"
102
+ ],
103
+ description: "Output file type for generated content (url, file, or local)"
104
+ }
105
+ };
106
+ const CHAT_SCHEMA = {
107
+ type: "object",
108
+ properties: CHAT_INPUT_PROPERTIES
109
+ };
110
+ const GENERATE_IMAGE_SCHEMA = {
111
+ type: "object",
112
+ properties: {
113
+ prompt: {
114
+ type: "string",
115
+ description: "Image generation prompt"
116
+ },
117
+ n: {
118
+ type: "number",
119
+ description: "Number of images to generate"
120
+ },
121
+ size: {
122
+ type: "string",
123
+ description: "Image size (e.g. 1024x1024, 1024x1792)"
124
+ },
125
+ quality: {
126
+ type: "string",
127
+ description: "Image quality (e.g. standard, hd)"
128
+ },
129
+ style: {
130
+ type: "string",
131
+ description: "Image style (e.g. natural, vivid)"
132
+ },
133
+ outputFileType: {
134
+ type: "string",
135
+ enum: [
136
+ "url",
137
+ "file",
138
+ "local"
139
+ ],
140
+ description: "Output file type for generated images (url, file, or local)"
141
+ }
142
+ },
143
+ required: ["prompt"]
144
+ };
145
+ const GENERATE_VIDEO_SCHEMA = {
146
+ type: "object",
147
+ properties: {
148
+ prompt: {
149
+ type: "string",
150
+ description: "Video generation prompt"
151
+ },
152
+ size: {
153
+ type: "string",
154
+ description: "Video resolution (e.g. 1920x1080)"
155
+ },
156
+ seconds: {
157
+ type: "string",
158
+ description: "Video duration"
159
+ },
160
+ outputFileType: {
161
+ type: "string",
162
+ enum: [
163
+ "url",
164
+ "file",
165
+ "local"
166
+ ],
167
+ description: "Output file type for generated videos (url, file, or local)"
168
+ }
169
+ },
170
+ required: ["prompt"]
171
+ };
172
+ const ROOT_CHAT_SCHEMA = {
173
+ type: "object",
174
+ properties: {
175
+ model: {
176
+ type: "string",
177
+ description: "Model name (e.g. gpt-4o)"
178
+ },
179
+ ...CHAT_INPUT_PROPERTIES
180
+ },
181
+ required: ["model"]
182
+ };
183
+ const ROOT_GENERATE_IMAGE_SCHEMA = {
184
+ type: "object",
185
+ properties: {
186
+ model: {
187
+ type: "string",
188
+ description: "Model name (e.g. gpt-image-1)"
189
+ },
190
+ ...GENERATE_IMAGE_SCHEMA.properties
191
+ },
192
+ required: ["model", "prompt"]
193
+ };
194
+ const ROOT_GENERATE_VIDEO_SCHEMA = {
195
+ type: "object",
196
+ properties: {
197
+ model: {
198
+ type: "string",
199
+ description: "Model name (e.g. sora-2)"
200
+ },
201
+ ...GENERATE_VIDEO_SCHEMA.properties
202
+ },
203
+ required: ["model", "prompt"]
204
+ };
205
+ const SET_DEFAULT_SCHEMA = {
206
+ type: "object",
207
+ properties: {}
208
+ };
209
+ const SET_DEFAULT_MODEL_SCHEMA = {
210
+ type: "object",
211
+ properties: {
212
+ type: {
213
+ type: "string",
214
+ enum: [
215
+ "chat",
216
+ "image",
217
+ "video"
218
+ ],
219
+ description: "Model type"
220
+ },
221
+ model: {
222
+ type: "string",
223
+ description: "Model name to set as default"
224
+ }
225
+ },
226
+ required: ["type", "model"]
227
+ };
228
+ const SET_DEFAULT_ACTION = {
229
+ name: "setDefault",
230
+ description: "Set this model as the default for its type",
231
+ schema: SET_DEFAULT_SCHEMA
232
+ };
233
+ function getActionsForType(type) {
234
+ if (type === "chat") return [{
235
+ name: "chat",
236
+ description: "Chat inference (prompt shorthand or full messages array)",
237
+ schema: CHAT_SCHEMA
238
+ }, SET_DEFAULT_ACTION];
239
+ if (type === "image") return [{
240
+ name: "generateImage",
241
+ description: "Generate images from a text prompt",
242
+ schema: GENERATE_IMAGE_SCHEMA
243
+ }, SET_DEFAULT_ACTION];
244
+ return [{
245
+ name: "generateVideo",
246
+ description: "Generate video from a text prompt",
247
+ schema: GENERATE_VIDEO_SCHEMA
248
+ }, SET_DEFAULT_ACTION];
249
+ }
250
+ /** Get the primary action name for a model type */
251
+ function primaryActionForType(type) {
252
+ if (type === "chat") return "chat";
253
+ if (type === "image") return "generateImage";
254
+ return "generateVideo";
255
+ }
256
+ /** Root-level actions (includes model param as required) */
257
+ const ROOT_ACTIONS = [
258
+ {
259
+ name: "chat",
260
+ description: "Chat inference (prompt shorthand or full messages array)",
261
+ schema: ROOT_CHAT_SCHEMA
262
+ },
263
+ {
264
+ name: "generateImage",
265
+ description: "Generate images from a text prompt",
266
+ schema: ROOT_GENERATE_IMAGE_SCHEMA
267
+ },
268
+ {
269
+ name: "generateVideo",
270
+ description: "Generate video from a text prompt",
271
+ schema: ROOT_GENERATE_VIDEO_SCHEMA
272
+ },
273
+ {
274
+ name: "setDefaultModel",
275
+ description: "Set the default model for a given type",
276
+ schema: SET_DEFAULT_MODEL_SCHEMA
277
+ },
278
+ {
279
+ name: "refresh",
280
+ description: "Clear model catalog cache and reload on next access",
281
+ schema: {
282
+ type: "object",
283
+ properties: {}
284
+ }
285
+ }
286
+ ];
287
+ function missingParam(name) {
288
+ return {
289
+ success: false,
290
+ error: {
291
+ code: "MISSING_PARAM",
292
+ message: `The '${name}' parameter is required`
293
+ }
294
+ };
295
+ }
296
+ function inferenceError(modelName, err) {
297
+ return {
298
+ success: false,
299
+ error: {
300
+ code: "INFERENCE_ERROR",
301
+ message: `Inference failed for ${modelName}: ${err instanceof Error ? err.message : String(err)}`
302
+ }
303
+ };
304
+ }
305
+ /** Extract last segment of model name for use as a path segment */
306
+ function modelPathSegment(model) {
307
+ const idx = model.model.lastIndexOf("/");
308
+ return idx >= 0 ? model.model.slice(idx + 1) : model.model;
309
+ }
310
+ /** Build multimodal message content from prompt + optional image */
311
+ function buildMessageContent(prompt, image) {
312
+ if (!image) return prompt;
313
+ const parts = [{
314
+ type: "text",
315
+ text: prompt
316
+ }];
317
+ if (image.startsWith("data:")) parts.push({
318
+ type: "file",
319
+ data: image
320
+ });
321
+ else parts.push({
322
+ type: "url",
323
+ url: image
324
+ });
325
+ return parts;
326
+ }
327
+ /** Build SDK ChatModelInput from flat args */
328
+ function buildChatSDKInput(args) {
329
+ const input = {};
330
+ const image = args.image;
331
+ if (Array.isArray(args.messages) && args.messages.length > 0) input.messages = args.messages;
332
+ else if (args.prompt !== void 0 && args.prompt !== null) input.messages = [{
333
+ role: "user",
334
+ content: buildMessageContent(String(args.prompt), image)
335
+ }];
336
+ const modelOptions = {};
337
+ for (const key of [
338
+ "temperature",
339
+ "topP",
340
+ "frequencyPenalty",
341
+ "presencePenalty",
342
+ "reasoningEffort"
343
+ ]) if (args[key] !== void 0) modelOptions[key] = args[key];
344
+ if (Object.keys(modelOptions).length > 0) input.modelOptions = modelOptions;
345
+ if (args.responseFormat !== void 0) input.responseFormat = args.responseFormat;
346
+ if (args.tools !== void 0) input.tools = args.tools;
347
+ if (args.toolChoice !== void 0) input.toolChoice = args.toolChoice;
348
+ input.outputFileType = args.outputFileType ?? "url";
349
+ return input;
350
+ }
351
+ var AFSAigneHub = class AFSAigneHub extends AFSBaseProvider {
352
+ static schema() {
353
+ return afsAigneHubOptionsSchema;
354
+ }
355
+ static manifest() {
356
+ return {
357
+ name: "aignehub",
358
+ description: "AigneHub AI Model Gateway — discover and run AI models",
359
+ uriTemplate: "aignehub://{host?}",
360
+ category: "ai",
361
+ schema: z.object({
362
+ host: z.string().optional(),
363
+ apiKey: z.string().meta({ sensitive: true })
364
+ }),
365
+ tags: [
366
+ "ai",
367
+ "llm",
368
+ "model",
369
+ "aignehub"
370
+ ]
371
+ };
372
+ }
373
+ static async load({ config } = {}) {
374
+ const cfg = { ...config ?? {} };
375
+ if (cfg.host && !cfg.url) cfg.url = `https://${cfg.host}`;
376
+ return new AFSAigneHub(await AFSAigneHub.schema().parseAsync(cfg));
377
+ }
378
+ static async auth(context) {
379
+ const host = context.resolved?.host;
380
+ return aigneHubAuth(context, host ? `https://${host}` : void 0);
381
+ }
382
+ name;
383
+ description;
384
+ accessMode = "readwrite";
385
+ apiKey;
386
+ baseURL;
387
+ needsURLDiscovery;
388
+ catalog = null;
389
+ /** Config-level defaults (from constructor options) */
390
+ configDefaults;
391
+ /** Runtime overrides (set via setDefault action, takes highest priority) */
392
+ runtimeDefaults = {};
393
+ /** Mount path captured from onMount (for config persistence) */
394
+ mountPath;
395
+ /** Injectable callback for persisting config changes */
396
+ updateConfigFn;
397
+ constructor(options) {
398
+ super();
399
+ const parsed = zodParse(afsAigneHubOptionsSchema, options);
400
+ this.name = parsed.name;
401
+ this.description = parsed.description;
402
+ this.apiKey = parsed.apiKey;
403
+ this.baseURL = parsed.url || AIGNE_HUB_URL;
404
+ this.needsURLDiscovery = !parsed.url;
405
+ this.configDefaults = {
406
+ ...parsed.defaultChat ? { chat: parsed.defaultChat } : {},
407
+ ...parsed.defaultImage ? { image: parsed.defaultImage } : {},
408
+ ...parsed.defaultVideo ? { video: parsed.defaultVideo } : {}
409
+ };
410
+ }
411
+ async ready() {
412
+ if (this.needsURLDiscovery) {
413
+ const { getAIGNEHubMountPoint: getAIGNEHubMountPoint$1 } = await import("@aigne/aigne-hub");
414
+ const { AIGNE_HUB_BLOCKLET_DID } = await import("./auth.mjs");
415
+ this.baseURL = await getAIGNEHubMountPoint$1(this.baseURL, AIGNE_HUB_BLOCKLET_DID);
416
+ }
417
+ }
418
+ onMount(root, mountPath) {
419
+ this.mountPath = mountPath;
420
+ this.updateConfigFn = root.updateProviderConfig;
421
+ }
422
+ async ensureCatalog() {
423
+ if (this.catalog) return this.catalog;
424
+ const [chat, image, video] = await Promise.all([
425
+ this.fetchModels("chat"),
426
+ this.fetchModels("image"),
427
+ this.fetchModels("video")
428
+ ]);
429
+ const catalog = {
430
+ chat,
431
+ image,
432
+ video
433
+ };
434
+ this.catalog = catalog;
435
+ return catalog;
436
+ }
437
+ async fetchModels(type) {
438
+ const url = joinURL(this.baseURL, "/api/ai/models");
439
+ const response = await fetch(`${url}?type=${type}`, { headers: { Authorization: `Bearer ${this.apiKey}` } });
440
+ if (!response.ok) throw new Error(`Failed to fetch ${type} models: ${response.status} ${response.statusText}`);
441
+ return (await response.json()).map((m) => ({
442
+ type,
443
+ model: m.model,
444
+ provider: m.provider,
445
+ inputCreditsPerToken: m.input_credits_per_token,
446
+ outputCreditsPerToken: m.output_credits_per_token,
447
+ providerDisplayName: m.providerDisplayName,
448
+ status: m.status,
449
+ modelMetadata: m.modelMetadata
450
+ }));
451
+ }
452
+ /** Get unique providers with their model counts */
453
+ getProviderStats(catalog) {
454
+ const map = /* @__PURE__ */ new Map();
455
+ for (const type of MODEL_TYPES) for (const m of catalog[type]) {
456
+ const existing = map.get(m.provider);
457
+ if (existing) existing.count++;
458
+ else map.set(m.provider, {
459
+ displayName: m.providerDisplayName,
460
+ count: 1
461
+ });
462
+ }
463
+ return Array.from(map.entries()).map(([name, v]) => ({
464
+ name,
465
+ displayName: v.displayName,
466
+ count: v.count
467
+ }));
468
+ }
469
+ /** Get all models for a specific provider */
470
+ getModelsForProvider(catalog, provider) {
471
+ return MODEL_TYPES.flatMap((type) => catalog[type].filter((m) => m.provider === provider));
472
+ }
473
+ /** Get default model for a type. Priority: runtime override > config > API first */
474
+ getDefaultModel(catalog, type) {
475
+ const models = catalog[type];
476
+ const name = this.runtimeDefaults[type] ?? this.configDefaults[type];
477
+ if (name) {
478
+ const found = models.find((m) => m.model === name);
479
+ if (found) return found;
480
+ }
481
+ return models[0];
482
+ }
483
+ /** Validate type param, throw AFSNotFoundError if invalid */
484
+ validateType(type) {
485
+ if (MODEL_TYPES.includes(type)) return type;
486
+ throw new AFSNotFoundError(joinURL("/types", type), `Type '${type}' not found. Available types: ${MODEL_TYPES.join(", ")}`);
487
+ }
488
+ async listRoot(_ctx) {
489
+ const catalog = await this.ensureCatalog();
490
+ const providerCount = this.getProviderStats(catalog).length;
491
+ return { data: [
492
+ this.buildEntry("/defaults", { meta: {
493
+ childrenCount: MODEL_TYPES.length,
494
+ kind: "aignehub:directory"
495
+ } }),
496
+ this.buildEntry("/providers", { meta: {
497
+ childrenCount: providerCount,
498
+ kind: "aignehub:directory"
499
+ } }),
500
+ this.buildEntry("/types", { meta: {
501
+ childrenCount: MODEL_TYPES.length,
502
+ kind: "aignehub:directory"
503
+ } })
504
+ ] };
505
+ }
506
+ async readRoot(_ctx) {
507
+ return this.buildEntry("/", {
508
+ content: {
509
+ provider: "aignehub",
510
+ description: "AigneHub AI Model Gateway"
511
+ },
512
+ meta: {
513
+ childrenCount: 3,
514
+ kind: "aignehub:root"
515
+ }
516
+ });
517
+ }
518
+ async rootMeta(_ctx) {
519
+ return this.buildEntry("/.meta", {
520
+ content: {
521
+ provider: "aignehub",
522
+ baseURL: this.baseURL,
523
+ description: "AigneHub AI Model Gateway"
524
+ },
525
+ meta: {
526
+ childrenCount: 3,
527
+ kind: "aignehub:root"
528
+ }
529
+ });
530
+ }
531
+ async readCapabilities(_ctx) {
532
+ const operations = this.getOperationsDeclaration();
533
+ const manifest = {
534
+ schemaVersion: 1,
535
+ provider: this.name,
536
+ description: this.description || "AigneHub AI Model Gateway",
537
+ tools: [],
538
+ operations,
539
+ actions: [{
540
+ description: "Root-level actions",
541
+ catalog: [
542
+ {
543
+ name: "chat",
544
+ description: "Chat inference (prompt shorthand or full messages array)",
545
+ inputSchema: ROOT_CHAT_SCHEMA
546
+ },
547
+ {
548
+ name: "generateImage",
549
+ description: "Generate images from a text prompt",
550
+ inputSchema: ROOT_GENERATE_IMAGE_SCHEMA
551
+ },
552
+ {
553
+ name: "generateVideo",
554
+ description: "Generate video from a text prompt",
555
+ inputSchema: ROOT_GENERATE_VIDEO_SCHEMA
556
+ },
557
+ {
558
+ name: "refresh",
559
+ description: "Clear model catalog cache",
560
+ inputSchema: {
561
+ type: "object",
562
+ properties: {}
563
+ }
564
+ }
565
+ ],
566
+ discovery: {
567
+ pathTemplate: "/.actions",
568
+ note: "Root actions: chat, generateImage, generateVideo, refresh"
569
+ }
570
+ }, {
571
+ description: "Model-level actions (varies by model type)",
572
+ catalog: [
573
+ {
574
+ name: "chat",
575
+ description: "Chat inference (chat models)",
576
+ inputSchema: CHAT_SCHEMA
577
+ },
578
+ {
579
+ name: "generateImage",
580
+ description: "Generate images (image models)",
581
+ inputSchema: GENERATE_IMAGE_SCHEMA
582
+ },
583
+ {
584
+ name: "generateVideo",
585
+ description: "Generate video (video models)",
586
+ inputSchema: GENERATE_VIDEO_SCHEMA
587
+ }
588
+ ],
589
+ discovery: {
590
+ pathTemplate: "/providers/:provider/:model/.actions",
591
+ note: "Actions depend on model type. Chat: chat; Image: generateImage; Video: generateVideo. Also accessible via /defaults/:type/.actions and /types/:type/:model/.actions."
592
+ }
593
+ }]
594
+ };
595
+ return this.buildEntry("/.meta/.capabilities", {
596
+ content: manifest,
597
+ meta: {
598
+ kind: "afs:capabilities",
599
+ ...operations
600
+ }
601
+ });
602
+ }
603
+ async explainRoot(_ctx) {
604
+ const catalog = await this.ensureCatalog();
605
+ const providers = this.getProviderStats(catalog);
606
+ return {
607
+ content: [
608
+ "# AigneHub AI Model Gateway",
609
+ "",
610
+ `Unified access to ${MODEL_TYPES.reduce((sum, t) => sum + catalog[t].length, 0)} AI models from ${providers.length} providers.`,
611
+ "",
612
+ "## Quick Start",
613
+ "",
614
+ "- Chat: `exec /defaults/chat/.actions/chat { \"prompt\": \"Hello\" }`",
615
+ "- Generate image: `exec /defaults/image/.actions/generateImage { \"prompt\": \"A sunset\" }`",
616
+ "- Generate video: `exec /defaults/video/.actions/generateVideo { \"prompt\": \"Ocean waves\" }`",
617
+ "",
618
+ "## Browse Models",
619
+ "",
620
+ "- `list /defaults` — Default models for quick access",
621
+ "- `list /providers` — Browse by provider",
622
+ "- `list /types` — Browse by capability (chat, image, video)",
623
+ "",
624
+ "## Root Actions",
625
+ "",
626
+ "- `exec /.actions/chat { \"model\": \"gpt-4o\", \"prompt\": \"Hello\" }`",
627
+ "- `exec /.actions/generateImage { \"model\": \"gpt-image-1\", \"prompt\": \"A cat\" }`",
628
+ "- `exec /.actions/refresh` — Reload model catalog"
629
+ ].join("\n"),
630
+ format: "markdown"
631
+ };
632
+ }
633
+ async statRoot(_ctx) {
634
+ return { data: this.buildEntry("/", { meta: {
635
+ childrenCount: 3,
636
+ kind: "aignehub:root"
637
+ } }) };
638
+ }
639
+ async listDefaults(_ctx) {
640
+ const catalog = await this.ensureCatalog();
641
+ return { data: MODEL_TYPES.map((type) => {
642
+ const model = this.getDefaultModel(catalog, type);
643
+ return this.buildEntry(joinURL("/defaults", type), { meta: {
644
+ childrenCount: 0,
645
+ kind: `aignehub:default-${type}`,
646
+ type,
647
+ ...model ? {
648
+ model: model.model,
649
+ provider: model.provider
650
+ } : {}
651
+ } });
652
+ }) };
653
+ }
654
+ async readDefaults(_ctx) {
655
+ const catalog = await this.ensureCatalog();
656
+ const defaults = {};
657
+ for (const type of MODEL_TYPES) defaults[type] = this.getDefaultModel(catalog, type)?.model ?? null;
658
+ return this.buildEntry("/defaults", {
659
+ content: { defaults },
660
+ meta: {
661
+ childrenCount: MODEL_TYPES.length,
662
+ kind: "aignehub:directory"
663
+ }
664
+ });
665
+ }
666
+ async defaultsMeta(_ctx) {
667
+ return this.buildEntry("/defaults/.meta", {
668
+ content: { description: "Default model shortcuts" },
669
+ meta: {
670
+ childrenCount: MODEL_TYPES.length,
671
+ kind: "aignehub:directory"
672
+ }
673
+ });
674
+ }
675
+ async statDefaults(_ctx) {
676
+ return { data: this.buildEntry("/defaults", { meta: {
677
+ childrenCount: MODEL_TYPES.length,
678
+ kind: "aignehub:directory"
679
+ } }) };
680
+ }
681
+ async explainDefaults(_ctx) {
682
+ const catalog = await this.ensureCatalog();
683
+ const lines = [
684
+ "# Default Models",
685
+ "",
686
+ "Pre-selected models for quick access — one per type.",
687
+ "",
688
+ "## Examples",
689
+ ""
690
+ ];
691
+ for (const type of MODEL_TYPES) {
692
+ const model = this.getDefaultModel(catalog, type);
693
+ const action = primaryActionForType(type);
694
+ if (model) lines.push(`- **${type}** (${model.model}): \`exec /defaults/${type}/.actions/${action} { "prompt": "..." }\``);
695
+ }
696
+ lines.push("", "## Navigation", "");
697
+ for (const type of MODEL_TYPES) lines.push(`- \`explain /defaults/${type}\` — Default ${type} model details`);
698
+ return {
699
+ content: lines.join("\n"),
700
+ format: "markdown"
701
+ };
702
+ }
703
+ async listDefault(ctx) {
704
+ this.validateType(ctx.params.type);
705
+ return { data: [] };
706
+ }
707
+ async readDefault(ctx) {
708
+ const type = this.validateType(ctx.params.type);
709
+ const catalog = await this.ensureCatalog();
710
+ const model = this.getDefaultModel(catalog, type);
711
+ if (!model) return this.buildEntry(joinURL("/defaults", type), {
712
+ content: {
713
+ type,
714
+ model: null,
715
+ message: `No ${type} models available`
716
+ },
717
+ meta: {
718
+ childrenCount: 0,
719
+ kind: `aignehub:default-${type}`
720
+ }
721
+ });
722
+ return this.buildModelEntry(joinURL("/defaults", type), model);
723
+ }
724
+ async statDefault(ctx) {
725
+ const type = this.validateType(ctx.params.type);
726
+ const catalog = await this.ensureCatalog();
727
+ const model = this.getDefaultModel(catalog, type);
728
+ return { data: this.buildEntry(joinURL("/defaults", type), { meta: {
729
+ childrenCount: 0,
730
+ kind: `aignehub:default-${type}`,
731
+ type,
732
+ ...model ? { model: model.model } : {}
733
+ } }) };
734
+ }
735
+ async defaultTypeMeta(ctx) {
736
+ const type = this.validateType(ctx.params.type);
737
+ const catalog = await this.ensureCatalog();
738
+ const model = this.getDefaultModel(catalog, type);
739
+ return this.buildEntry(joinURL("/defaults", type, ".meta"), {
740
+ content: {
741
+ type,
742
+ model: model?.model ?? null
743
+ },
744
+ meta: {
745
+ childrenCount: 0,
746
+ kind: `aignehub:default-${type}`,
747
+ type,
748
+ ...model ? { model: model.model } : {}
749
+ }
750
+ });
751
+ }
752
+ async explainDefault(ctx) {
753
+ const type = this.validateType(ctx.params.type);
754
+ const catalog = await this.ensureCatalog();
755
+ const model = this.getDefaultModel(catalog, type);
756
+ if (!model) return {
757
+ content: `No default ${type} model available.`,
758
+ format: "text"
759
+ };
760
+ return this.buildModelExplain(joinURL("/defaults", type), model);
761
+ }
762
+ async listDefaultActions(ctx) {
763
+ const type = this.validateType(ctx.params.type);
764
+ const basePath = joinURL("/defaults", type);
765
+ return { data: getActionsForType(type).map((action) => this.buildActionEntry(joinURL(basePath, ".actions", action.name), action.name, action.description, action.schema)) };
766
+ }
767
+ async execDefaultChat(ctx, args) {
768
+ const type = this.validateType(ctx.params.type);
769
+ if (type !== "chat") throw new AFSNotFoundError(joinURL("/defaults", type, ".actions/chat"), `Action 'chat' is not available for ${type} models.`);
770
+ const catalog = await this.ensureCatalog();
771
+ const model = this.getDefaultModel(catalog, type);
772
+ if (!model) return {
773
+ success: false,
774
+ error: {
775
+ code: "NO_DEFAULT",
776
+ message: `No default ${type} model available`
777
+ }
778
+ };
779
+ return this.runChat(model.model, args);
780
+ }
781
+ async execDefaultGenerateImage(ctx, args) {
782
+ const type = this.validateType(ctx.params.type);
783
+ if (type !== "image") throw new AFSNotFoundError(joinURL("/defaults", type, ".actions/generateImage"), `Action 'generateImage' is not available for ${type} models.`);
784
+ const catalog = await this.ensureCatalog();
785
+ const model = this.getDefaultModel(catalog, type);
786
+ if (!model) return {
787
+ success: false,
788
+ error: {
789
+ code: "NO_DEFAULT",
790
+ message: `No default ${type} model available`
791
+ }
792
+ };
793
+ return this.runImage(model.model, args);
794
+ }
795
+ async execDefaultGenerateVideo(ctx, args) {
796
+ const type = this.validateType(ctx.params.type);
797
+ if (type !== "video") throw new AFSNotFoundError(joinURL("/defaults", type, ".actions/generateVideo"), `Action 'generateVideo' is not available for ${type} models.`);
798
+ const catalog = await this.ensureCatalog();
799
+ const model = this.getDefaultModel(catalog, type);
800
+ if (!model) return {
801
+ success: false,
802
+ error: {
803
+ code: "NO_DEFAULT",
804
+ message: `No default ${type} model available`
805
+ }
806
+ };
807
+ return this.runVideo(model.model, args);
808
+ }
809
+ async listProviders(_ctx) {
810
+ const catalog = await this.ensureCatalog();
811
+ return { data: this.getProviderStats(catalog).map((p) => this.buildEntry(joinURL("/providers", p.name), { meta: {
812
+ childrenCount: p.count,
813
+ kind: "aignehub:provider",
814
+ providerDisplayName: p.displayName
815
+ } })) };
816
+ }
817
+ async readProviders(_ctx) {
818
+ const catalog = await this.ensureCatalog();
819
+ const providers = this.getProviderStats(catalog);
820
+ const totalModels = MODEL_TYPES.reduce((sum, t) => sum + catalog[t].length, 0);
821
+ return this.buildEntry("/providers", {
822
+ content: {
823
+ providers: providers.map((p) => p.name),
824
+ totalModels
825
+ },
826
+ meta: {
827
+ childrenCount: providers.length,
828
+ kind: "aignehub:directory"
829
+ }
830
+ });
831
+ }
832
+ async providersMeta(_ctx) {
833
+ const catalog = await this.ensureCatalog();
834
+ const providers = this.getProviderStats(catalog);
835
+ const totalModels = MODEL_TYPES.reduce((sum, t) => sum + catalog[t].length, 0);
836
+ return this.buildEntry("/providers/.meta", {
837
+ content: {
838
+ providers: providers.map((p) => p.name),
839
+ totalModels
840
+ },
841
+ meta: {
842
+ childrenCount: providers.length,
843
+ kind: "aignehub:directory"
844
+ }
845
+ });
846
+ }
847
+ async statProviders(_ctx) {
848
+ const catalog = await this.ensureCatalog();
849
+ const providers = this.getProviderStats(catalog);
850
+ return { data: this.buildEntry("/providers", { meta: {
851
+ childrenCount: providers.length,
852
+ kind: "aignehub:directory"
853
+ } }) };
854
+ }
855
+ async explainProviders(_ctx) {
856
+ const catalog = await this.ensureCatalog();
857
+ const providers = this.getProviderStats(catalog);
858
+ const totalModels = MODEL_TYPES.reduce((sum, t) => sum + catalog[t].length, 0);
859
+ return {
860
+ content: [
861
+ "# Providers",
862
+ "",
863
+ `${providers.length} providers, ${totalModels} models total.`,
864
+ "",
865
+ "## Available Providers",
866
+ "",
867
+ ...providers.map((p) => `- **${p.displayName}** (\`${p.name}\`): ${p.count} models`),
868
+ "",
869
+ "## Example",
870
+ "",
871
+ `\`list /providers/${providers[0]?.name ?? "openai"}\` — List models from a provider`
872
+ ].join("\n"),
873
+ format: "markdown"
874
+ };
875
+ }
876
+ async listProvider(ctx) {
877
+ const catalog = await this.ensureCatalog();
878
+ const models = this.getModelsForProvider(catalog, ctx.params.provider);
879
+ if (models.length === 0) throw new AFSNotFoundError(joinURL("/providers", ctx.params.provider), `Provider '${ctx.params.provider}' not found.`);
880
+ return { data: models.map((m) => this.buildEntry(joinURL("/providers", ctx.params.provider, modelPathSegment(m)), { meta: {
881
+ childrenCount: 0,
882
+ kind: `aignehub:${m.type}-model`,
883
+ type: m.type
884
+ } })) };
885
+ }
886
+ async readProvider(ctx) {
887
+ const catalog = await this.ensureCatalog();
888
+ const models = this.getModelsForProvider(catalog, ctx.params.provider);
889
+ if (models.length === 0) throw new AFSNotFoundError(joinURL("/providers", ctx.params.provider), `Provider '${ctx.params.provider}' not found.`);
890
+ const displayName = models[0].providerDisplayName;
891
+ return this.buildEntry(joinURL("/providers", ctx.params.provider), {
892
+ content: {
893
+ provider: ctx.params.provider,
894
+ providerDisplayName: displayName,
895
+ modelCount: models.length,
896
+ models: models.map((m) => ({
897
+ model: m.model,
898
+ type: m.type
899
+ }))
900
+ },
901
+ meta: {
902
+ childrenCount: models.length,
903
+ kind: "aignehub:provider",
904
+ providerDisplayName: displayName
905
+ }
906
+ });
907
+ }
908
+ async providerMeta(ctx) {
909
+ const catalog = await this.ensureCatalog();
910
+ const models = this.getModelsForProvider(catalog, ctx.params.provider);
911
+ if (models.length === 0) throw new AFSNotFoundError(joinURL("/providers", ctx.params.provider), `Provider '${ctx.params.provider}' not found.`);
912
+ const displayName = models[0].providerDisplayName;
913
+ return this.buildEntry(joinURL("/providers", ctx.params.provider, ".meta"), {
914
+ content: {
915
+ provider: ctx.params.provider,
916
+ providerDisplayName: displayName,
917
+ modelCount: models.length
918
+ },
919
+ meta: {
920
+ childrenCount: models.length,
921
+ kind: "aignehub:provider",
922
+ providerDisplayName: displayName
923
+ }
924
+ });
925
+ }
926
+ async statProvider(ctx) {
927
+ const catalog = await this.ensureCatalog();
928
+ const models = this.getModelsForProvider(catalog, ctx.params.provider);
929
+ if (models.length === 0) throw new AFSNotFoundError(joinURL("/providers", ctx.params.provider), `Provider '${ctx.params.provider}' not found.`);
930
+ return { data: this.buildEntry(joinURL("/providers", ctx.params.provider), { meta: {
931
+ childrenCount: models.length,
932
+ kind: "aignehub:provider"
933
+ } }) };
934
+ }
935
+ async explainProvider(ctx) {
936
+ const catalog = await this.ensureCatalog();
937
+ const models = this.getModelsForProvider(catalog, ctx.params.provider);
938
+ if (models.length === 0) throw new AFSNotFoundError(joinURL("/providers", ctx.params.provider), `Provider '${ctx.params.provider}' not found.`);
939
+ const displayName = models[0].providerDisplayName;
940
+ const chatModels = models.filter((m) => m.type === "chat");
941
+ const imageModels = models.filter((m) => m.type === "image");
942
+ const videoModels = models.filter((m) => m.type === "video");
943
+ const parts = [
944
+ chatModels.length ? `${chatModels.length} chat` : null,
945
+ imageModels.length ? `${imageModels.length} image` : null,
946
+ videoModels.length ? `${videoModels.length} video` : null
947
+ ].filter(Boolean);
948
+ const firstChat = chatModels[0];
949
+ const lines = [
950
+ `# ${displayName}`,
951
+ "",
952
+ `${parts.join(", ")} models available.`,
953
+ "",
954
+ "## Models",
955
+ "",
956
+ ...models.map((m) => `- **${modelPathSegment(m)}** (${m.type})`),
957
+ "",
958
+ "## Example",
959
+ ""
960
+ ];
961
+ if (firstChat) lines.push(`\`exec /providers/${ctx.params.provider}/${modelPathSegment(firstChat)}/.actions/chat { "prompt": "Hello" }\``);
962
+ return {
963
+ content: lines.join("\n"),
964
+ format: "markdown"
965
+ };
966
+ }
967
+ async listProviderModel(ctx) {
968
+ await this.findModelByPath(ctx.params.provider, ctx.params.model);
969
+ return { data: [] };
970
+ }
971
+ async readProviderModel(ctx) {
972
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
973
+ return this.buildModelEntry(joinURL("/providers", ctx.params.provider, ctx.params.model), model);
974
+ }
975
+ async providerModelMeta(ctx) {
976
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
977
+ return this.buildEntry(joinURL("/providers", ctx.params.provider, ctx.params.model, ".meta"), {
978
+ content: {
979
+ model: model.model,
980
+ provider: model.provider,
981
+ type: model.type
982
+ },
983
+ meta: {
984
+ childrenCount: 0,
985
+ kind: `aignehub:${model.type}-model`,
986
+ type: model.type
987
+ }
988
+ });
989
+ }
990
+ async statProviderModel(ctx) {
991
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
992
+ return { data: this.buildEntry(joinURL("/providers", ctx.params.provider, ctx.params.model), { meta: {
993
+ childrenCount: 0,
994
+ kind: `aignehub:${model.type}-model`,
995
+ type: model.type
996
+ } }) };
997
+ }
998
+ async explainProviderModel(ctx) {
999
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
1000
+ return this.buildModelExplain(joinURL("/providers", ctx.params.provider, ctx.params.model), model);
1001
+ }
1002
+ async listProviderModelActions(ctx) {
1003
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
1004
+ const basePath = joinURL("/providers", ctx.params.provider, ctx.params.model);
1005
+ return { data: getActionsForType(model.type).map((action) => this.buildActionEntry(joinURL(basePath, ".actions", action.name), action.name, action.description, action.schema)) };
1006
+ }
1007
+ async execProviderChat(ctx, args) {
1008
+ const model = await this.findModelOfTypeByPath(ctx.params.provider, ctx.params.model, "chat");
1009
+ return this.runChat(model.model, args);
1010
+ }
1011
+ async execProviderGenerateImage(ctx, args) {
1012
+ const model = await this.findModelOfTypeByPath(ctx.params.provider, ctx.params.model, "image");
1013
+ return this.runImage(model.model, args);
1014
+ }
1015
+ async execProviderGenerateVideo(ctx, args) {
1016
+ const model = await this.findModelOfTypeByPath(ctx.params.provider, ctx.params.model, "video");
1017
+ return this.runVideo(model.model, args);
1018
+ }
1019
+ async execProviderSetDefault(ctx) {
1020
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
1021
+ return await this.setDefault(model);
1022
+ }
1023
+ async listTypes(_ctx) {
1024
+ const catalog = await this.ensureCatalog();
1025
+ return { data: MODEL_TYPES.map((type) => this.buildEntry(joinURL("/types", type), { meta: {
1026
+ childrenCount: catalog[type].length,
1027
+ kind: `aignehub:type-${type}`
1028
+ } })) };
1029
+ }
1030
+ async readTypes(_ctx) {
1031
+ const catalog = await this.ensureCatalog();
1032
+ const types = MODEL_TYPES.map((type) => ({
1033
+ type,
1034
+ count: catalog[type].length
1035
+ }));
1036
+ return this.buildEntry("/types", {
1037
+ content: { types },
1038
+ meta: {
1039
+ childrenCount: MODEL_TYPES.length,
1040
+ kind: "aignehub:directory"
1041
+ }
1042
+ });
1043
+ }
1044
+ async typesMeta(_ctx) {
1045
+ const catalog = await this.ensureCatalog();
1046
+ const types = MODEL_TYPES.map((type) => ({
1047
+ type,
1048
+ count: catalog[type].length
1049
+ }));
1050
+ return this.buildEntry("/types/.meta", {
1051
+ content: { types },
1052
+ meta: {
1053
+ childrenCount: MODEL_TYPES.length,
1054
+ kind: "aignehub:directory"
1055
+ }
1056
+ });
1057
+ }
1058
+ async statTypes(_ctx) {
1059
+ return { data: this.buildEntry("/types", { meta: {
1060
+ childrenCount: MODEL_TYPES.length,
1061
+ kind: "aignehub:directory"
1062
+ } }) };
1063
+ }
1064
+ async explainTypes(_ctx) {
1065
+ const catalog = await this.ensureCatalog();
1066
+ return {
1067
+ content: [
1068
+ "# Model Types",
1069
+ "",
1070
+ "Browse models by capability.",
1071
+ "",
1072
+ ...MODEL_TYPES.map((type) => `- **${type}**: ${catalog[type].length} models`),
1073
+ "",
1074
+ "## Example",
1075
+ "",
1076
+ "`list /types/chat` — List all chat models",
1077
+ "",
1078
+ "## Navigation",
1079
+ "",
1080
+ "- By provider: `list /providers`"
1081
+ ].join("\n"),
1082
+ format: "markdown"
1083
+ };
1084
+ }
1085
+ async listType(ctx) {
1086
+ const type = this.validateType(ctx.params.type);
1087
+ return { data: (await this.ensureCatalog())[type].map((m) => this.buildEntry(joinURL("/types", type, modelPathSegment(m)), { meta: {
1088
+ childrenCount: 0,
1089
+ kind: `aignehub:${type}-model`,
1090
+ type,
1091
+ provider: m.provider
1092
+ } })) };
1093
+ }
1094
+ async readType(ctx) {
1095
+ const type = this.validateType(ctx.params.type);
1096
+ const catalog = await this.ensureCatalog();
1097
+ return this.buildEntry(joinURL("/types", type), {
1098
+ content: {
1099
+ type,
1100
+ modelCount: catalog[type].length,
1101
+ models: catalog[type].map((m) => ({
1102
+ model: m.model,
1103
+ provider: m.provider
1104
+ }))
1105
+ },
1106
+ meta: {
1107
+ childrenCount: catalog[type].length,
1108
+ kind: `aignehub:type-${type}`
1109
+ }
1110
+ });
1111
+ }
1112
+ async typeMeta(ctx) {
1113
+ const type = this.validateType(ctx.params.type);
1114
+ const catalog = await this.ensureCatalog();
1115
+ return this.buildEntry(joinURL("/types", type, ".meta"), {
1116
+ content: {
1117
+ type,
1118
+ modelCount: catalog[type].length
1119
+ },
1120
+ meta: {
1121
+ childrenCount: catalog[type].length,
1122
+ kind: `aignehub:type-${type}`
1123
+ }
1124
+ });
1125
+ }
1126
+ async statType(ctx) {
1127
+ const type = this.validateType(ctx.params.type);
1128
+ const catalog = await this.ensureCatalog();
1129
+ return { data: this.buildEntry(joinURL("/types", type), { meta: {
1130
+ childrenCount: catalog[type].length,
1131
+ kind: `aignehub:type-${type}`
1132
+ } }) };
1133
+ }
1134
+ async explainType(ctx) {
1135
+ const type = this.validateType(ctx.params.type);
1136
+ const models = (await this.ensureCatalog())[type];
1137
+ const action = primaryActionForType(type);
1138
+ const lines = [
1139
+ `# ${type} models`,
1140
+ "",
1141
+ `${models.length} models available.`,
1142
+ "",
1143
+ "## Models",
1144
+ "",
1145
+ ...models.map((m) => `- **${modelPathSegment(m)}** (${m.providerDisplayName})`),
1146
+ "",
1147
+ "## Example",
1148
+ ""
1149
+ ];
1150
+ if (models.length > 0) {
1151
+ const first = models[0];
1152
+ lines.push(`\`exec /types/${type}/${modelPathSegment(first)}/.actions/${action} { "prompt": "..." }\``);
1153
+ }
1154
+ lines.push("", "## Navigation", "", "- By provider: `list /providers`");
1155
+ return {
1156
+ content: lines.join("\n"),
1157
+ format: "markdown"
1158
+ };
1159
+ }
1160
+ async listTypeModel(ctx) {
1161
+ const type = this.validateType(ctx.params.type);
1162
+ await this.findModelByTypeAndSegment(type, ctx.params.model);
1163
+ return { data: [] };
1164
+ }
1165
+ async readTypeModel(ctx) {
1166
+ const type = this.validateType(ctx.params.type);
1167
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1168
+ return this.buildModelEntry(joinURL("/types", type, ctx.params.model), model);
1169
+ }
1170
+ async statTypeModel(ctx) {
1171
+ const type = this.validateType(ctx.params.type);
1172
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1173
+ return { data: this.buildEntry(joinURL("/types", type, ctx.params.model), { meta: {
1174
+ childrenCount: 0,
1175
+ kind: `aignehub:${model.type}-model`,
1176
+ type: model.type
1177
+ } }) };
1178
+ }
1179
+ async typeModelMeta(ctx) {
1180
+ const type = this.validateType(ctx.params.type);
1181
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1182
+ return this.buildEntry(joinURL("/types", type, ctx.params.model, ".meta"), {
1183
+ content: {
1184
+ model: model.model,
1185
+ provider: model.provider,
1186
+ type: model.type
1187
+ },
1188
+ meta: {
1189
+ childrenCount: 0,
1190
+ kind: `aignehub:${model.type}-model`,
1191
+ type: model.type
1192
+ }
1193
+ });
1194
+ }
1195
+ async explainTypeModel(ctx) {
1196
+ const type = this.validateType(ctx.params.type);
1197
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1198
+ return this.buildModelExplain(joinURL("/types", type, ctx.params.model), model);
1199
+ }
1200
+ async listTypeModelActions(ctx) {
1201
+ const type = this.validateType(ctx.params.type);
1202
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1203
+ const basePath = joinURL("/types", type, ctx.params.model);
1204
+ return { data: getActionsForType(model.type).map((action) => this.buildActionEntry(joinURL(basePath, ".actions", action.name), action.name, action.description, action.schema)) };
1205
+ }
1206
+ async execTypeChat(ctx, args) {
1207
+ const type = this.validateType(ctx.params.type);
1208
+ if (type !== "chat") throw new AFSNotFoundError(joinURL("/types", type, ctx.params.model, ".actions/chat"), `Action 'chat' is not available for ${type} models.`);
1209
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1210
+ return this.runChat(model.model, args);
1211
+ }
1212
+ async execTypeGenerateImage(ctx, args) {
1213
+ const type = this.validateType(ctx.params.type);
1214
+ if (type !== "image") throw new AFSNotFoundError(joinURL("/types", type, ctx.params.model, ".actions/generateImage"), `Action 'generateImage' is not available for ${type} models.`);
1215
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1216
+ return this.runImage(model.model, args);
1217
+ }
1218
+ async execTypeGenerateVideo(ctx, args) {
1219
+ const type = this.validateType(ctx.params.type);
1220
+ if (type !== "video") throw new AFSNotFoundError(joinURL("/types", type, ctx.params.model, ".actions/generateVideo"), `Action 'generateVideo' is not available for ${type} models.`);
1221
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1222
+ return this.runVideo(model.model, args);
1223
+ }
1224
+ async execTypeSetDefault(ctx) {
1225
+ const type = this.validateType(ctx.params.type);
1226
+ const model = await this.findModelByTypeAndSegment(type, ctx.params.model);
1227
+ return await this.setDefault(model);
1228
+ }
1229
+ async listRootActions(_ctx) {
1230
+ return { data: [
1231
+ this.buildActionEntry("/.actions/chat", "chat", "Chat inference (prompt shorthand or full messages array)", ROOT_CHAT_SCHEMA),
1232
+ this.buildActionEntry("/.actions/generateImage", "generateImage", "Generate images from a text prompt", ROOT_GENERATE_IMAGE_SCHEMA),
1233
+ this.buildActionEntry("/.actions/generateVideo", "generateVideo", "Generate video from a text prompt", ROOT_GENERATE_VIDEO_SCHEMA),
1234
+ this.buildActionEntry("/.actions/setDefaultModel", "setDefaultModel", "Set the default model for a given type", SET_DEFAULT_MODEL_SCHEMA),
1235
+ this.buildActionEntry("/.actions/refresh", "refresh", "Clear model catalog cache and reload on next access", {
1236
+ type: "object",
1237
+ properties: {}
1238
+ })
1239
+ ] };
1240
+ }
1241
+ async execRootChat(_ctx, args) {
1242
+ const modelName = args.model;
1243
+ if (!modelName) return missingParam("model");
1244
+ await this.findModelOfType(modelName, "chat");
1245
+ return this.runChat(modelName, args);
1246
+ }
1247
+ async execRootGenerateImage(_ctx, args) {
1248
+ const modelName = args.model;
1249
+ if (!modelName) return missingParam("model");
1250
+ await this.findModelOfType(modelName, "image");
1251
+ return this.runImage(modelName, args);
1252
+ }
1253
+ async execRootGenerateVideo(_ctx, args) {
1254
+ const modelName = args.model;
1255
+ if (!modelName) return missingParam("model");
1256
+ await this.findModelOfType(modelName, "video");
1257
+ return this.runVideo(modelName, args);
1258
+ }
1259
+ async execRootSetDefaultModel(_ctx, args) {
1260
+ const typeName = args.type;
1261
+ const modelName = args.model;
1262
+ if (!typeName) return missingParam("type");
1263
+ if (!modelName) return missingParam("model");
1264
+ const type = this.validateType(typeName);
1265
+ const model = await this.findModelOfType(modelName, type);
1266
+ return await this.setDefault(model);
1267
+ }
1268
+ async execRefresh(_ctx, _args) {
1269
+ this.catalog = null;
1270
+ return {
1271
+ success: true,
1272
+ data: { message: "Model catalog cache cleared" }
1273
+ };
1274
+ }
1275
+ async setDefault(model) {
1276
+ const type = model.type;
1277
+ this.runtimeDefaults[type] = model.model;
1278
+ if (this.updateConfigFn && this.mountPath) {
1279
+ const key = type === "chat" ? "defaultChat" : type === "image" ? "defaultImage" : "defaultVideo";
1280
+ await this.updateConfigFn(this.mountPath, { [key]: model.model });
1281
+ }
1282
+ return {
1283
+ success: true,
1284
+ data: {
1285
+ message: `Default ${type} model set to ${model.model}`,
1286
+ type,
1287
+ model: model.model,
1288
+ provider: model.provider
1289
+ }
1290
+ };
1291
+ }
1292
+ buildActionsListExplain(basePath, actions) {
1293
+ return {
1294
+ content: [
1295
+ `# Actions for ${basePath || "/"}`,
1296
+ "",
1297
+ "Available actions:",
1298
+ "",
1299
+ ...actions.map((a) => `- **${a.name}** — ${a.description}`),
1300
+ "",
1301
+ `Use \`explain ${basePath}/.actions/<name>\` for input schema and usage.`
1302
+ ].join("\n"),
1303
+ format: "markdown"
1304
+ };
1305
+ }
1306
+ buildActionExplain(basePath, actionName, actions) {
1307
+ const action = actions.find((a) => a.name === actionName);
1308
+ if (!action) throw new AFSNotFoundError(joinURL(basePath, ".actions", actionName));
1309
+ return {
1310
+ content: [
1311
+ `# ${action.name}`,
1312
+ "",
1313
+ action.description,
1314
+ "",
1315
+ "## Input Schema",
1316
+ "",
1317
+ "```json",
1318
+ JSON.stringify(action.schema, null, 2),
1319
+ "```",
1320
+ "",
1321
+ "## Example",
1322
+ "",
1323
+ `\`exec ${basePath}/.actions/${action.name} ${JSON.stringify(Object.fromEntries((Array.isArray(action.schema.required) ? action.schema.required : []).map((k) => [k, `<${k}>`])))}\``
1324
+ ].join("\n"),
1325
+ format: "markdown"
1326
+ };
1327
+ }
1328
+ async explainRootActionsList(_ctx) {
1329
+ return this.buildActionsListExplain("", ROOT_ACTIONS);
1330
+ }
1331
+ async explainRootAction(ctx) {
1332
+ return this.buildActionExplain("", ctx.params.action, ROOT_ACTIONS);
1333
+ }
1334
+ async explainDefaultActionsList(ctx) {
1335
+ const type = this.validateType(ctx.params.type);
1336
+ return this.buildActionsListExplain(joinURL("/defaults", type), getActionsForType(type));
1337
+ }
1338
+ async explainDefaultAction(ctx) {
1339
+ const type = this.validateType(ctx.params.type);
1340
+ return this.buildActionExplain(joinURL("/defaults", type), ctx.params.action, getActionsForType(type));
1341
+ }
1342
+ async explainProviderModelActionsList(ctx) {
1343
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
1344
+ return this.buildActionsListExplain(joinURL("/providers", ctx.params.provider, ctx.params.model), getActionsForType(model.type));
1345
+ }
1346
+ async explainProviderModelAction(ctx) {
1347
+ const model = await this.findModelByPath(ctx.params.provider, ctx.params.model);
1348
+ return this.buildActionExplain(joinURL("/providers", ctx.params.provider, ctx.params.model), ctx.params.action, getActionsForType(model.type));
1349
+ }
1350
+ async explainTypeModelActionsList(ctx) {
1351
+ const type = this.validateType(ctx.params.type);
1352
+ return this.buildActionsListExplain(joinURL("/types", type, ctx.params.model), getActionsForType(type));
1353
+ }
1354
+ async explainTypeModelAction(ctx) {
1355
+ const type = this.validateType(ctx.params.type);
1356
+ return this.buildActionExplain(joinURL("/types", type, ctx.params.model), ctx.params.action, getActionsForType(type));
1357
+ }
1358
+ buildActionEntry(path, name, description, inputSchema) {
1359
+ return this.buildEntry(path, {
1360
+ id: name,
1361
+ content: {
1362
+ name,
1363
+ description,
1364
+ inputSchema
1365
+ },
1366
+ meta: {
1367
+ name,
1368
+ kind: "afs:executable",
1369
+ description,
1370
+ inputSchema
1371
+ }
1372
+ });
1373
+ }
1374
+ buildModelEntry(path, model) {
1375
+ return this.buildEntry(path, {
1376
+ content: {
1377
+ model: model.model,
1378
+ provider: model.provider,
1379
+ providerDisplayName: model.providerDisplayName,
1380
+ type: model.type,
1381
+ inputCreditsPerToken: model.inputCreditsPerToken,
1382
+ outputCreditsPerToken: model.outputCreditsPerToken,
1383
+ available: model.status?.available ?? true
1384
+ },
1385
+ meta: {
1386
+ childrenCount: 0,
1387
+ kind: `aignehub:${model.type}-model`,
1388
+ type: model.type
1389
+ }
1390
+ });
1391
+ }
1392
+ buildModelExplain(modelPath, model) {
1393
+ const actions = getActionsForType(model.type);
1394
+ const action = actions[0];
1395
+ const actionNames = actions.map((a) => a.name).join(", ");
1396
+ const lines = [
1397
+ `# ${model.model}`,
1398
+ "",
1399
+ `- **Type**: ${model.type}`,
1400
+ `- **Provider**: ${model.providerDisplayName} (${model.provider})`,
1401
+ `- **Input Credits**: ${model.inputCreditsPerToken} per token`,
1402
+ `- **Output Credits**: ${model.outputCreditsPerToken} per token`,
1403
+ `- **Available**: ${model.status?.available ?? true ? "Yes" : "No"}`,
1404
+ `- **Actions**: ${actionNames}`,
1405
+ "",
1406
+ "## Example",
1407
+ "",
1408
+ `\`exec ${modelPath}/.actions/${action.name} { "prompt": "..." }\``
1409
+ ];
1410
+ for (const a of actions) lines.push("", `## ${a.name} Input Schema`, "", "```json", JSON.stringify(a.schema, null, 2), "```");
1411
+ lines.push("", "## Navigation", "", `- Same type: \`list /types/${model.type}\``, `- Same provider: \`list /providers/${model.provider}\``);
1412
+ return {
1413
+ content: lines.join("\n"),
1414
+ format: "markdown"
1415
+ };
1416
+ }
1417
+ async runChat(modelName, args) {
1418
+ const input = buildChatSDKInput(args);
1419
+ if (!input.messages) return {
1420
+ success: false,
1421
+ error: {
1422
+ code: "MISSING_PARAM",
1423
+ message: "Either 'messages' or 'prompt' is required for chat"
1424
+ }
1425
+ };
1426
+ try {
1427
+ const output = await new AIGNEHubChatModel({
1428
+ baseURL: this.baseURL,
1429
+ apiKey: this.apiKey,
1430
+ model: modelName
1431
+ }).invoke(input);
1432
+ const usage = output.usage;
1433
+ const data = { model: modelName };
1434
+ if (output.text !== void 0 && output.text !== "") data.text = output.text;
1435
+ if (output.json !== void 0) data.json = output.json;
1436
+ if (output.toolCalls !== void 0) data.toolCalls = output.toolCalls;
1437
+ if (output.thoughts !== void 0) data.thoughts = output.thoughts;
1438
+ if (output.files !== void 0) data.files = output.files;
1439
+ if (usage) {
1440
+ data.inputTokens = usage.inputTokens;
1441
+ data.outputTokens = usage.outputTokens;
1442
+ }
1443
+ return {
1444
+ success: true,
1445
+ data
1446
+ };
1447
+ } catch (err) {
1448
+ return inferenceError(modelName, err);
1449
+ }
1450
+ }
1451
+ async runImage(modelName, args) {
1452
+ const prompt = args.prompt;
1453
+ if (prompt === void 0 || prompt === null) return missingParam("prompt");
1454
+ try {
1455
+ const baseOptions = {
1456
+ baseURL: this.baseURL,
1457
+ apiKey: this.apiKey,
1458
+ model: modelName
1459
+ };
1460
+ const modelOptions = {};
1461
+ if (args.size !== void 0) modelOptions.size = args.size;
1462
+ if (args.quality !== void 0) modelOptions.quality = args.quality;
1463
+ if (args.style !== void 0) modelOptions.style = args.style;
1464
+ const input = { prompt: String(prompt) };
1465
+ if (args.n !== void 0) input.n = args.n;
1466
+ input.outputFileType = args.outputFileType ?? "url";
1467
+ if (Object.keys(modelOptions).length > 0) input.modelOptions = modelOptions;
1468
+ const output = await new AIGNEHubImageModel(baseOptions).invoke(input);
1469
+ const data = {
1470
+ images: output.images ?? [],
1471
+ model: modelName
1472
+ };
1473
+ if (output.usage) data.usage = output.usage;
1474
+ return {
1475
+ success: true,
1476
+ data
1477
+ };
1478
+ } catch (err) {
1479
+ return inferenceError(modelName, err);
1480
+ }
1481
+ }
1482
+ async runVideo(modelName, args) {
1483
+ const prompt = args.prompt;
1484
+ if (prompt === void 0 || prompt === null) return missingParam("prompt");
1485
+ try {
1486
+ const baseOptions = {
1487
+ baseURL: this.baseURL,
1488
+ apiKey: this.apiKey,
1489
+ model: modelName
1490
+ };
1491
+ const input = { prompt: String(prompt) };
1492
+ if (args.size !== void 0) input.size = args.size;
1493
+ if (args.seconds !== void 0) input.seconds = args.seconds;
1494
+ input.outputFileType = args.outputFileType ?? "url";
1495
+ const output = await new AIGNEHubVideoModel(baseOptions).invoke(input);
1496
+ const data = {
1497
+ videos: output.videos ?? [],
1498
+ model: modelName
1499
+ };
1500
+ if (output.seconds !== void 0) data.seconds = output.seconds;
1501
+ if (output.usage) data.usage = output.usage;
1502
+ return {
1503
+ success: true,
1504
+ data
1505
+ };
1506
+ } catch (err) {
1507
+ return inferenceError(modelName, err);
1508
+ }
1509
+ }
1510
+ /** Find model by provider + path segment across all types */
1511
+ async findModelByPath(provider, segment) {
1512
+ const catalog = await this.ensureCatalog();
1513
+ for (const type of MODEL_TYPES) {
1514
+ const model = catalog[type].find((m) => m.provider === provider && modelPathSegment(m) === segment);
1515
+ if (model) return model;
1516
+ }
1517
+ const providerModels = this.getModelsForProvider(catalog, provider);
1518
+ if (providerModels.length === 0) throw new AFSNotFoundError(joinURL("/providers", provider, segment), `Provider '${provider}' not found.`);
1519
+ const available = providerModels.map((m) => modelPathSegment(m));
1520
+ throw new AFSNotFoundError(joinURL("/providers", provider, segment), `Model '${segment}' not found under provider '${provider}'. Available: ${available.slice(0, 8).join(", ")}${available.length > 8 ? "..." : ""}`);
1521
+ }
1522
+ /** Find model by provider + path segment in a specific type catalog */
1523
+ async findModelOfTypeByPath(provider, segment, type) {
1524
+ const catalog = await this.ensureCatalog();
1525
+ const model = catalog[type].find((m) => m.provider === provider && modelPathSegment(m) === segment);
1526
+ if (model) return model;
1527
+ for (const otherType of MODEL_TYPES) {
1528
+ if (otherType === type) continue;
1529
+ if (catalog[otherType].find((m) => m.provider === provider && modelPathSegment(m) === segment)) throw new AFSNotFoundError(joinURL("/providers", provider, segment), `Model '${segment}' (${provider}) is not a ${type} model. It is listed under ${otherType} models.`);
1530
+ }
1531
+ throw new AFSNotFoundError(joinURL("/providers", provider, segment), `Model '${segment}' not found in ${type} models for provider '${provider}'.`);
1532
+ }
1533
+ /** Find model by type + path segment (for /types/:type/:model) */
1534
+ async findModelByTypeAndSegment(type, segment) {
1535
+ const catalog = await this.ensureCatalog();
1536
+ const model = catalog[type].find((m) => modelPathSegment(m) === segment);
1537
+ if (model) return model;
1538
+ for (const otherType of MODEL_TYPES) {
1539
+ if (otherType === type) continue;
1540
+ if (catalog[otherType].find((m) => modelPathSegment(m) === segment)) throw new AFSNotFoundError(joinURL("/types", type, segment), `Model '${segment}' is not a ${type} model. It is listed under ${otherType} models.`);
1541
+ }
1542
+ const available = catalog[type].map((m) => modelPathSegment(m));
1543
+ throw new AFSNotFoundError(joinURL("/types", type, segment), `Model '${segment}' not found in ${type} models. Available: ${available.slice(0, 8).join(", ")}${available.length > 8 ? "..." : ""}`);
1544
+ }
1545
+ /** Find model by full model name in a specific type catalog (for root exec) */
1546
+ async findModelOfType(modelName, type) {
1547
+ const catalog = await this.ensureCatalog();
1548
+ const model = catalog[type].find((m) => m.model === modelName);
1549
+ if (model) return model;
1550
+ for (const otherType of MODEL_TYPES) {
1551
+ if (otherType === type) continue;
1552
+ if (catalog[otherType].find((m) => m.model === modelName)) throw new AFSNotFoundError(modelName, `Model '${modelName}' is not a ${type} model. It is listed under ${otherType} models.`);
1553
+ }
1554
+ const available = catalog[type].map((m) => m.model);
1555
+ throw new AFSNotFoundError(modelName, `Model '${modelName}' not found in ${type} models. Available: ${available.slice(0, 8).join(", ")}${available.length > 8 ? "..." : ""}`);
1556
+ }
1557
+ };
1558
+ __decorate([List("/")], AFSAigneHub.prototype, "listRoot", null);
1559
+ __decorate([Read("/")], AFSAigneHub.prototype, "readRoot", null);
1560
+ __decorate([Meta("/")], AFSAigneHub.prototype, "rootMeta", null);
1561
+ __decorate([Read("/.meta/.capabilities")], AFSAigneHub.prototype, "readCapabilities", null);
1562
+ __decorate([Explain("/")], AFSAigneHub.prototype, "explainRoot", null);
1563
+ __decorate([Stat("/")], AFSAigneHub.prototype, "statRoot", null);
1564
+ __decorate([List("/defaults")], AFSAigneHub.prototype, "listDefaults", null);
1565
+ __decorate([Read("/defaults")], AFSAigneHub.prototype, "readDefaults", null);
1566
+ __decorate([Meta("/defaults")], AFSAigneHub.prototype, "defaultsMeta", null);
1567
+ __decorate([Stat("/defaults")], AFSAigneHub.prototype, "statDefaults", null);
1568
+ __decorate([Explain("/defaults")], AFSAigneHub.prototype, "explainDefaults", null);
1569
+ __decorate([List("/defaults/:type")], AFSAigneHub.prototype, "listDefault", null);
1570
+ __decorate([Read("/defaults/:type")], AFSAigneHub.prototype, "readDefault", null);
1571
+ __decorate([Stat("/defaults/:type")], AFSAigneHub.prototype, "statDefault", null);
1572
+ __decorate([Meta("/defaults/:type")], AFSAigneHub.prototype, "defaultTypeMeta", null);
1573
+ __decorate([Explain("/defaults/:type")], AFSAigneHub.prototype, "explainDefault", null);
1574
+ __decorate([Actions("/defaults/:type")], AFSAigneHub.prototype, "listDefaultActions", null);
1575
+ __decorate([Actions.Exec("/defaults/:type", "chat")], AFSAigneHub.prototype, "execDefaultChat", null);
1576
+ __decorate([Actions.Exec("/defaults/:type", "generateImage")], AFSAigneHub.prototype, "execDefaultGenerateImage", null);
1577
+ __decorate([Actions.Exec("/defaults/:type", "generateVideo")], AFSAigneHub.prototype, "execDefaultGenerateVideo", null);
1578
+ __decorate([List("/providers")], AFSAigneHub.prototype, "listProviders", null);
1579
+ __decorate([Read("/providers")], AFSAigneHub.prototype, "readProviders", null);
1580
+ __decorate([Meta("/providers")], AFSAigneHub.prototype, "providersMeta", null);
1581
+ __decorate([Stat("/providers")], AFSAigneHub.prototype, "statProviders", null);
1582
+ __decorate([Explain("/providers")], AFSAigneHub.prototype, "explainProviders", null);
1583
+ __decorate([List("/providers/:provider")], AFSAigneHub.prototype, "listProvider", null);
1584
+ __decorate([Read("/providers/:provider")], AFSAigneHub.prototype, "readProvider", null);
1585
+ __decorate([Meta("/providers/:provider")], AFSAigneHub.prototype, "providerMeta", null);
1586
+ __decorate([Stat("/providers/:provider")], AFSAigneHub.prototype, "statProvider", null);
1587
+ __decorate([Explain("/providers/:provider")], AFSAigneHub.prototype, "explainProvider", null);
1588
+ __decorate([List("/providers/:provider/:model")], AFSAigneHub.prototype, "listProviderModel", null);
1589
+ __decorate([Read("/providers/:provider/:model")], AFSAigneHub.prototype, "readProviderModel", null);
1590
+ __decorate([Meta("/providers/:provider/:model")], AFSAigneHub.prototype, "providerModelMeta", null);
1591
+ __decorate([Stat("/providers/:provider/:model")], AFSAigneHub.prototype, "statProviderModel", null);
1592
+ __decorate([Explain("/providers/:provider/:model")], AFSAigneHub.prototype, "explainProviderModel", null);
1593
+ __decorate([Actions("/providers/:provider/:model")], AFSAigneHub.prototype, "listProviderModelActions", null);
1594
+ __decorate([Actions.Exec("/providers/:provider/:model", "chat")], AFSAigneHub.prototype, "execProviderChat", null);
1595
+ __decorate([Actions.Exec("/providers/:provider/:model", "generateImage")], AFSAigneHub.prototype, "execProviderGenerateImage", null);
1596
+ __decorate([Actions.Exec("/providers/:provider/:model", "generateVideo")], AFSAigneHub.prototype, "execProviderGenerateVideo", null);
1597
+ __decorate([Actions.Exec("/providers/:provider/:model", "setDefault")], AFSAigneHub.prototype, "execProviderSetDefault", null);
1598
+ __decorate([List("/types")], AFSAigneHub.prototype, "listTypes", null);
1599
+ __decorate([Read("/types")], AFSAigneHub.prototype, "readTypes", null);
1600
+ __decorate([Meta("/types")], AFSAigneHub.prototype, "typesMeta", null);
1601
+ __decorate([Stat("/types")], AFSAigneHub.prototype, "statTypes", null);
1602
+ __decorate([Explain("/types")], AFSAigneHub.prototype, "explainTypes", null);
1603
+ __decorate([List("/types/:type")], AFSAigneHub.prototype, "listType", null);
1604
+ __decorate([Read("/types/:type")], AFSAigneHub.prototype, "readType", null);
1605
+ __decorate([Meta("/types/:type")], AFSAigneHub.prototype, "typeMeta", null);
1606
+ __decorate([Stat("/types/:type")], AFSAigneHub.prototype, "statType", null);
1607
+ __decorate([Explain("/types/:type")], AFSAigneHub.prototype, "explainType", null);
1608
+ __decorate([List("/types/:type/:model")], AFSAigneHub.prototype, "listTypeModel", null);
1609
+ __decorate([Read("/types/:type/:model")], AFSAigneHub.prototype, "readTypeModel", null);
1610
+ __decorate([Stat("/types/:type/:model")], AFSAigneHub.prototype, "statTypeModel", null);
1611
+ __decorate([Meta("/types/:type/:model")], AFSAigneHub.prototype, "typeModelMeta", null);
1612
+ __decorate([Explain("/types/:type/:model")], AFSAigneHub.prototype, "explainTypeModel", null);
1613
+ __decorate([Actions("/types/:type/:model")], AFSAigneHub.prototype, "listTypeModelActions", null);
1614
+ __decorate([Actions.Exec("/types/:type/:model", "chat")], AFSAigneHub.prototype, "execTypeChat", null);
1615
+ __decorate([Actions.Exec("/types/:type/:model", "generateImage")], AFSAigneHub.prototype, "execTypeGenerateImage", null);
1616
+ __decorate([Actions.Exec("/types/:type/:model", "generateVideo")], AFSAigneHub.prototype, "execTypeGenerateVideo", null);
1617
+ __decorate([Actions.Exec("/types/:type/:model", "setDefault")], AFSAigneHub.prototype, "execTypeSetDefault", null);
1618
+ __decorate([Actions("/")], AFSAigneHub.prototype, "listRootActions", null);
1619
+ __decorate([Actions.Exec("/", "chat")], AFSAigneHub.prototype, "execRootChat", null);
1620
+ __decorate([Actions.Exec("/", "generateImage")], AFSAigneHub.prototype, "execRootGenerateImage", null);
1621
+ __decorate([Actions.Exec("/", "generateVideo")], AFSAigneHub.prototype, "execRootGenerateVideo", null);
1622
+ __decorate([Actions.Exec("/", "setDefaultModel")], AFSAigneHub.prototype, "execRootSetDefaultModel", null);
1623
+ __decorate([Actions.Exec("/", "refresh")], AFSAigneHub.prototype, "execRefresh", null);
1624
+ __decorate([Explain("/.actions")], AFSAigneHub.prototype, "explainRootActionsList", null);
1625
+ __decorate([Explain("/.actions/:action")], AFSAigneHub.prototype, "explainRootAction", null);
1626
+ __decorate([Explain("/defaults/:type/.actions")], AFSAigneHub.prototype, "explainDefaultActionsList", null);
1627
+ __decorate([Explain("/defaults/:type/.actions/:action")], AFSAigneHub.prototype, "explainDefaultAction", null);
1628
+ __decorate([Explain("/providers/:provider/:model/.actions")], AFSAigneHub.prototype, "explainProviderModelActionsList", null);
1629
+ __decorate([Explain("/providers/:provider/:model/.actions/:action")], AFSAigneHub.prototype, "explainProviderModelAction", null);
1630
+ __decorate([Explain("/types/:type/:model/.actions")], AFSAigneHub.prototype, "explainTypeModelActionsList", null);
1631
+ __decorate([Explain("/types/:type/:model/.actions/:action")], AFSAigneHub.prototype, "explainTypeModelAction", null);
1632
+
1633
+ //#endregion
1634
+ export { AFSAigneHub };
1635
+ //# sourceMappingURL=aignehub.mjs.map