@xopcai/xopc 0.0.166 → 0.0.167
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-DkALQFJT.js → activity-store-DjEDu38T.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Tr2hgn4j.js → agent-avatar-dicebear-DQULz7qY.js} +1 -1
- package/dist/gateway/static/root/assets/agents-CnzkBgKC.js +2 -0
- package/dist/gateway/static/root/assets/{api-DPIXEcIq.js → api-DwVCuPOb.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-D7Nrbhez.js → app-management-settings-panel-DOjWALVF.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-DNNbABFj.js → appearance-settings-C31COUIC.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-0zXIeywq.js → apps-page-lSC486eu.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-CizqFy4G.js → archive-plugin-DQPvLqfx.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-DU8KjTua.js → attachment-load-DwT1x1rc.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-3pqyIveW.js → automation-suggestion-card-ByYBiiMR.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-DSz3xDsi.js → automations-page-iZTnc1YZ.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-CipUnsOr.js → binary-plugins-CRXiHoZS.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-XoEq6qiv.js → block-editor-CHO6ILiz.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-COYvq3y5.js → browser-settings-page-C-OOPZcJ.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-DRH995XI.js → browser-workflow-inputs-3UaarfXS.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-65edjhpz.js → browser-workflows-page-BFaa-N_D.js} +1 -1
- package/dist/gateway/static/root/assets/capabilities-settings-panel-BayH0Uom.js +7 -0
- package/dist/gateway/static/root/assets/capability-presets-api-DdCHSzcA.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-JlhjAhT7.js +2 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-B5rLegwk.js → channel-recipient-api-BOkLI89e.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-D8nH0mve.js → channels-settings-C-Spmwr5.js} +1 -1
- package/dist/gateway/static/root/assets/channels-status-swr-ZrqadU60.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-n4Hg41iD.js → connector-logo-CbcC0T6i.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-jvRWZn3Y.js → connectors-page-DpDmJ3Nx.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-D4OzBOwm.js → desktop-pet-CR7RGQff.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-CDGezbaf.js → desktop-pet-settings-BudmCcPE.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DmL4AUXQ.js → directory-picker-path-field-BLG2Oe6z.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-ClUcBKnd.js → dreaming-settings-BwBGC0SG.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DsV_KUDM.js → extension-debug-page-Pr65_hLC.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DJv_G1jY.js → extension-page-BuJDcoKn.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DUAlJ4CU.js → extension-settings-page-B3802tn5.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-SVYgzqv7.js → gateway-config-api-D-NVkfts.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-YL6f2C_F.js → gateway-settings-DIdsKVXv.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-ulwTJovP.js → gateway-startup-retry-qnEmj-vL.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-CJJe-4Co.js → goal-create-dialog-51j_DxgV.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-detail-page-Da9VMWRd.js → goal-detail-page-CeqMr5GF.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-config-api-st_Itxa9.js → goals-config-api-BkVBh7ct.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-B6pC7PYZ.js → goals-page-lf3oM4Xa.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-settings-BEZePZ5m.js → goals-settings-Cg7MJLvx.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-DpeD9GQ9.js → heartbeat-settings-BV3kJOcX.js} +1 -1
- package/dist/gateway/static/root/assets/{index-BhQMnKH1.js → index-B24B26og.js} +9 -9
- package/dist/gateway/static/root/assets/index-DfE2j0g8.css +1 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-B9K5rTZZ.js +1 -0
- package/dist/gateway/static/root/assets/{local-app-workbench-page-Ctia6lSG.js → local-app-workbench-page-ehzr9CBG.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-DWF9rpWQ.js → local-apps-page-BBx6mfAD.js} +1 -1
- package/dist/gateway/static/root/assets/locale-store-tnRLWdx2.js +1 -0
- package/dist/gateway/static/root/assets/{logs-page-D3gL6vHX.js → logs-page-xuwEkusZ.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-BuduJ0Ev.js → markdown-split-DRDddIt8.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-CLASZjhT.js → media-plugins-Dn3hxhOV.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-C96Zjb0V.js → messages-B1EG4gdu.js} +2 -4
- package/dist/gateway/static/root/assets/{notes-page-ve3XiBDF.js → notes-page-BtKHSb4z.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-workbench-oHU532lp.js → notes-workbench-DXGXn5XF.js} +2 -2
- package/dist/gateway/static/root/assets/{page-tabs-DU85jn98.js → page-tabs-P2BEd1D8.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CtchTuRQ.js → preview-open-alternatives-DrkjmW0F.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-DFT6Fx6y.js → product-open-page-CGzMEowb.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-Ceu0NoC1.js → project-detail-page-IiE2ddWb.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-CdpqOmO8.js → projects-page-DGOVhaYI.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-D1NKLyiR.js → remote-access-hub-l_y_Shgl.js} +1 -1
- package/dist/gateway/static/root/assets/{save-bar-controls-CmcrHpMc.js → save-bar-controls-BkGrufxl.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-DmIkP6JV.js → schedule-field-A8tDpL-9.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Dkk9blN0.js → schema-form-N6_hDWgx.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-CMj_JaOk.js → sessions-page-CAX4ipPv.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-eVlH9brU.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-CuQzWvg5.js → settings-form-section-Bb6yYt5f.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-C5FNbyej.js → settings-loading-skeleton-Q_Vh6HEl.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-a-A2yTGE.js → settings-page-CjOtQpbR.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-MuXpamWD.js → settings-page-layout-CtESNNdx.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-BUdh8FKY.js → setup-status-panel-CGxHXgJB.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-BfzzmTgg.js → share-preview-page-C65IsFmd.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-8a9qnLF8.js → shares-settings-Ct3fcbHr.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-ElA5kabh.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-CmHj-CKY.js → skills-page-FeieOEn3.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-CyhRkWMi.js → system-settings-panel-CdUl2I0D.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-D37XLD_C.js → theme-store-H9T49Iar.js} +1 -1
- package/dist/gateway/static/root/assets/{time-picker-WEPp3HrB.js → time-picker-EkhqT3fS.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-Bi1uoxFR.js → typed-models-lib-5xRRUE9Y.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-DBh0dOC9.js → use-channel-catalog-UACJTyH0.js} +1 -1
- package/dist/gateway/static/root/assets/{user-context-page-BObEyiim.js → user-context-page-DHiRvd4i.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-Gvgodhec.js → voice-api-key-field-Bql0SPCp.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-page-BHnjvGH1.js → work-discovery-page-BcmZux7y.js} +1 -1
- package/dist/gateway/static/root/assets/{work-item-detail-page-CQ8ybkxx.js → work-item-detail-page-D94Q5zVD.js} +1 -1
- package/dist/gateway/static/root/assets/{work-page-D_U_En23.js → work-page-DdFBMDcB.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-B-Sk6gFn.js → workflow-run-setup-panel-Dow7ZztR.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-DT0pzfkz.js → workflows-page-wOVSoduy.js} +1 -1
- package/dist/gateway/static/root/index.html +8 -8
- package/dist/package.js +1 -1
- package/dist/src/agent/image/generation/custom-openai-images-provider.d.ts +7 -0
- package/dist/src/agent/image/generation/custom-openai-images-provider.js +81 -0
- package/dist/src/agent/image/generation/{openai-compatible-image-provider.d.ts → openai-images-provider.d.ts} +10 -31
- package/dist/src/agent/image/generation/{openai-compatible-image-provider.js → openai-images-provider.js} +36 -23
- package/dist/src/agent/image/generation/provider-registry.d.ts +2 -0
- package/dist/src/agent/image/generation/provider-registry.js +50 -4
- package/dist/src/agent/image/generation/providers/openai.js +2 -2
- package/dist/src/agent/image/generation/runtime.js +5 -1
- package/dist/src/agent/image/generation/types.d.ts +11 -0
- package/dist/src/cli/commands/channels.js +1 -1
- package/dist/src/cli/commands/connectors.js +1 -1
- package/dist/src/cli/commands/doctor/checks/image-providers.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/image-providers.js +68 -0
- package/dist/src/cli/commands/doctor/flow.js +2 -0
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/models-json.d.ts +325 -0
- package/dist/src/config/models-json.js +139 -4
- package/dist/src/extensions/sdk/index.js +1 -1
- package/dist/src/gateway/custom-image-providers.d.ts +89 -0
- package/dist/src/gateway/custom-image-providers.js +59 -0
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/routes/models.js +182 -1
- package/dist/src/gateway/hono/routes/workflows.js +1 -1
- package/dist/src/gateway/image-generation-setup.js +7 -1
- package/dist/src/gateway/service/config-coordinator.js +2 -0
- package/dist/src/gateway/service.js +2 -2
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/media-shared/http/index.d.ts +1 -1
- package/dist/src/media-shared/http/index.js +2 -2
- package/dist/src/media-shared/http/provider-http.d.ts +2 -0
- package/dist/src/media-shared/http/provider-http.js +43 -4
- package/dist/src/providers/model-reference-auditor.d.ts +1 -0
- package/dist/src/providers/model-reference-auditor.js +23 -13
- package/package.json +2 -2
- package/dist/gateway/static/root/assets/agents-DZ69tC4K.js +0 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-LOQyfdsM.js +0 -2
- package/dist/gateway/static/root/assets/capability-presets-api-XL4h7dCK.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-KPJFkt1h.js +0 -2
- package/dist/gateway/static/root/assets/channels-status-swr-DZZq0upi.js +0 -1
- package/dist/gateway/static/root/assets/index-BXtJvaQl.css +0 -1
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-fhssr2-J.js +0 -1
- package/dist/gateway/static/root/assets/locale-store-C_ax27uc.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DR9Q2ZyF.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-da5gLCer.js +0 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type ModelsJsonConfig } from '../config/models-json.js';
|
|
3
|
+
export declare const CustomImageProviderInputSchema: z.ZodObject<{
|
|
4
|
+
baseUrl: z.ZodString;
|
|
5
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
6
|
+
imageGeneration: z.ZodObject<{
|
|
7
|
+
api: z.ZodLiteral<"openai-images">;
|
|
8
|
+
name: z.ZodString;
|
|
9
|
+
documentationUrl: z.ZodOptional<z.ZodString>;
|
|
10
|
+
apiKeyUrl: z.ZodOptional<z.ZodString>;
|
|
11
|
+
defaultModel: z.ZodString;
|
|
12
|
+
auth: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
13
|
+
type: z.ZodLiteral<"bearer">;
|
|
14
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
15
|
+
type: z.ZodLiteral<"header">;
|
|
16
|
+
headerName: z.ZodString;
|
|
17
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
18
|
+
type: z.ZodLiteral<"none">;
|
|
19
|
+
}, z.core.$strict>], "type">;
|
|
20
|
+
paths: z.ZodOptional<z.ZodObject<{
|
|
21
|
+
generations: z.ZodOptional<z.ZodString>;
|
|
22
|
+
edits: z.ZodOptional<z.ZodString>;
|
|
23
|
+
}, z.core.$strict>>;
|
|
24
|
+
network: z.ZodOptional<z.ZodObject<{
|
|
25
|
+
allowedHosts: z.ZodArray<z.ZodString>;
|
|
26
|
+
}, z.core.$strict>>;
|
|
27
|
+
models: z.ZodArray<z.ZodObject<{
|
|
28
|
+
id: z.ZodString;
|
|
29
|
+
name: z.ZodOptional<z.ZodString>;
|
|
30
|
+
capabilities: z.ZodObject<{
|
|
31
|
+
generate: z.ZodOptional<z.ZodObject<{
|
|
32
|
+
maxCount: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
supportsSize: z.ZodOptional<z.ZodBoolean>;
|
|
34
|
+
supportsAspectRatio: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
+
supportsResolution: z.ZodOptional<z.ZodBoolean>;
|
|
36
|
+
}, z.core.$strict>>;
|
|
37
|
+
edit: z.ZodOptional<z.ZodObject<{
|
|
38
|
+
enabled: z.ZodBoolean;
|
|
39
|
+
maxInputImages: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
supportsSize: z.ZodOptional<z.ZodBoolean>;
|
|
41
|
+
supportsAspectRatio: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
+
}, z.core.$strict>>;
|
|
43
|
+
geometry: z.ZodOptional<z.ZodObject<{
|
|
44
|
+
sizes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
45
|
+
aspectRatios: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
46
|
+
resolutions: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
47
|
+
"1K": "1K";
|
|
48
|
+
"2K": "2K";
|
|
49
|
+
"4K": "4K";
|
|
50
|
+
}>>>;
|
|
51
|
+
}, z.core.$strict>>;
|
|
52
|
+
output: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
qualities: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
54
|
+
low: "low";
|
|
55
|
+
high: "high";
|
|
56
|
+
auto: "auto";
|
|
57
|
+
medium: "medium";
|
|
58
|
+
}>>>;
|
|
59
|
+
formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
60
|
+
png: "png";
|
|
61
|
+
jpeg: "jpeg";
|
|
62
|
+
webp: "webp";
|
|
63
|
+
}>>>;
|
|
64
|
+
backgrounds: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
65
|
+
auto: "auto";
|
|
66
|
+
transparent: "transparent";
|
|
67
|
+
opaque: "opaque";
|
|
68
|
+
}>>>;
|
|
69
|
+
}, z.core.$strict>>;
|
|
70
|
+
}, z.core.$strict>;
|
|
71
|
+
defaults: z.ZodOptional<z.ZodObject<{
|
|
72
|
+
count: z.ZodOptional<z.ZodNumber>;
|
|
73
|
+
size: z.ZodOptional<z.ZodString>;
|
|
74
|
+
outputFormat: z.ZodOptional<z.ZodEnum<{
|
|
75
|
+
png: "png";
|
|
76
|
+
jpeg: "jpeg";
|
|
77
|
+
webp: "webp";
|
|
78
|
+
}>>;
|
|
79
|
+
}, z.core.$strict>>;
|
|
80
|
+
}, z.core.$strict>>;
|
|
81
|
+
}, z.core.$strict>;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
export type CustomImageProviderInput = z.infer<typeof CustomImageProviderInputSchema>;
|
|
84
|
+
export interface CustomImageProviderRecord extends CustomImageProviderInput {
|
|
85
|
+
providerId: string;
|
|
86
|
+
}
|
|
87
|
+
export declare function listCustomImageProviders(config: ModelsJsonConfig): CustomImageProviderRecord[];
|
|
88
|
+
export declare function upsertCustomImageProvider(config: ModelsJsonConfig, providerIdRaw: string, input: unknown): ModelsJsonConfig;
|
|
89
|
+
export declare function deleteCustomImageProvider(config: ModelsJsonConfig, providerIdRaw: string): ModelsJsonConfig;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ImageGenerationProviderSchema, init_models_json, validateModelsConfig } from "../config/models-json.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/gateway/custom-image-providers.ts
|
|
4
|
+
init_models_json();
|
|
5
|
+
const PROVIDER_ID_REGEX = /^[a-z0-9]([a-z0-9-_]*[a-z0-9])?$/;
|
|
6
|
+
const CustomImageProviderInputSchema = z.object({
|
|
7
|
+
baseUrl: z.string().url(),
|
|
8
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
9
|
+
imageGeneration: ImageGenerationProviderSchema
|
|
10
|
+
}).strict();
|
|
11
|
+
function listCustomImageProviders(config) {
|
|
12
|
+
return Object.entries(config.providers).flatMap(([providerId, provider]) => {
|
|
13
|
+
if (!provider.imageGeneration) return [];
|
|
14
|
+
return [{
|
|
15
|
+
providerId,
|
|
16
|
+
baseUrl: provider.baseUrl,
|
|
17
|
+
...provider.headers ? { headers: { ...provider.headers } } : {},
|
|
18
|
+
imageGeneration: structuredClone(provider.imageGeneration)
|
|
19
|
+
}];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function upsertCustomImageProvider(config, providerIdRaw, input) {
|
|
23
|
+
const providerId = normalizeProviderId(providerIdRaw);
|
|
24
|
+
const parsed = CustomImageProviderInputSchema.safeParse(input);
|
|
25
|
+
if (!parsed.success) throw new Error(parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
|
|
26
|
+
const { headers: _currentHeaders, ...currentWithoutHeaders } = config.providers[providerId] ?? {};
|
|
27
|
+
const next = structuredClone(config);
|
|
28
|
+
next.providers[providerId] = {
|
|
29
|
+
...currentWithoutHeaders,
|
|
30
|
+
baseUrl: parsed.data.baseUrl,
|
|
31
|
+
...parsed.data.headers ? { headers: parsed.data.headers } : {},
|
|
32
|
+
imageGeneration: parsed.data.imageGeneration
|
|
33
|
+
};
|
|
34
|
+
assertValid(next);
|
|
35
|
+
return next;
|
|
36
|
+
}
|
|
37
|
+
function deleteCustomImageProvider(config, providerIdRaw) {
|
|
38
|
+
const providerId = normalizeProviderId(providerIdRaw);
|
|
39
|
+
const current = config.providers[providerId];
|
|
40
|
+
if (!current?.imageGeneration) throw new Error(`Custom image provider not found: ${providerId}`);
|
|
41
|
+
const next = structuredClone(config);
|
|
42
|
+
if (Boolean(current.api || current.apiKey || current.authHeader !== void 0 || current.models?.length || current.modelOverrides && Object.keys(current.modelOverrides).length > 0 || current.modelDiscovery)) {
|
|
43
|
+
const { imageGeneration: _removed, ...remaining } = current;
|
|
44
|
+
next.providers[providerId] = remaining;
|
|
45
|
+
} else delete next.providers[providerId];
|
|
46
|
+
assertValid(next);
|
|
47
|
+
return next;
|
|
48
|
+
}
|
|
49
|
+
function normalizeProviderId(value) {
|
|
50
|
+
const providerId = value.trim();
|
|
51
|
+
if (!PROVIDER_ID_REGEX.test(providerId)) throw new Error("Provider ID must start/end with alphanumeric and contain only lowercase letters, numbers, hyphens, and underscores");
|
|
52
|
+
return providerId;
|
|
53
|
+
}
|
|
54
|
+
function assertValid(config) {
|
|
55
|
+
const validation = validateModelsConfig(config);
|
|
56
|
+
if (!validation.valid) throw new Error(validation.errors.filter((entry) => entry.severity === "error").map((entry) => `${entry.path}: ${entry.message}`).join("; "));
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { CustomImageProviderInputSchema, deleteCustomImageProvider, listCustomImageProviders, upsertCustomImageProvider };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { shouldSilence, stripHeartbeatToken } from "../../heartbeat/tokens.js";
|
|
4
|
+
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
4
5
|
import { isWithinActiveHours } from "../../heartbeat/active-hours.js";
|
|
5
6
|
import { isHeartbeatContentEmpty } from "../../heartbeat/content-check.js";
|
|
6
|
-
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
7
7
|
import { createHeartbeatWake } from "../../heartbeat/wake.js";
|
|
8
8
|
import { resolveHeartbeatMdPath } from "../workspace-heartbeat-path.js";
|
|
9
9
|
import { readFile } from "fs/promises";
|
|
@@ -7,7 +7,9 @@ import { CredentialResolver, init_credentials } from "../../../auth/credentials.
|
|
|
7
7
|
import "../../../providers/constants.js";
|
|
8
8
|
import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
|
|
9
9
|
import { PROVIDER_META, getAllModels, getAllProviders, getAvailableModels, getProviderAuthState, init_providers, isProviderConfigured } from "../../../providers/index.js";
|
|
10
|
+
import { getImageGenerationProvider, reloadImageGenerationProviders } from "../../../agent/image/generation/provider-registry.js";
|
|
10
11
|
import { SessionConfigStore } from "../../../session/config-store.js";
|
|
12
|
+
import { deleteCustomImageProvider, listCustomImageProviders, upsertCustomImageProvider } from "../../custom-image-providers.js";
|
|
11
13
|
import { getDomesticProviderPreset, getDomesticProviderPresetIds } from "../../../providers/domestic-presets.js";
|
|
12
14
|
import { getOnboardingFeaturedProviders, getProviderHint, getRecommendedModelsForProvider, isRecommendedModel, sortModelsForPicker, sortProvidersForPicker } from "../../../providers/presentation.js";
|
|
13
15
|
import { getAgentImageGenerationConfig, getImageGenerationCatalog, prepareImageGenerationSetup, verifyImageGenerationCredential } from "../../image-generation-setup.js";
|
|
@@ -119,6 +121,7 @@ function registerModelsRoutes(authenticated, deps) {
|
|
|
119
121
|
}, 400);
|
|
120
122
|
const registry = getModelRegistry();
|
|
121
123
|
registry.refresh();
|
|
124
|
+
reloadImageGenerationProviders(config);
|
|
122
125
|
service.emit("models-json.updated", { modelCount: registry.getAll().length });
|
|
123
126
|
return c.json({
|
|
124
127
|
ok: true,
|
|
@@ -131,6 +134,7 @@ function registerModelsRoutes(authenticated, deps) {
|
|
|
131
134
|
authenticated.post("/api/models-json/reload", async (c) => {
|
|
132
135
|
const registry = getModelRegistry();
|
|
133
136
|
registry.refresh();
|
|
137
|
+
reloadImageGenerationProviders();
|
|
134
138
|
const error = registry.getError();
|
|
135
139
|
const models = registry.getAll();
|
|
136
140
|
service.emit("models-json.reloaded", {
|
|
@@ -239,6 +243,182 @@ function registerModelsRoutes(authenticated, deps) {
|
|
|
239
243
|
payload: { providers: getImageGenerationCatalog(service.currentConfig) }
|
|
240
244
|
});
|
|
241
245
|
});
|
|
246
|
+
authenticated.get("/api/image-generation/custom-providers", (c) => {
|
|
247
|
+
const loaded = loadModelsJson(resolveModelsJsonPath());
|
|
248
|
+
if (loaded.error) return c.json({
|
|
249
|
+
ok: false,
|
|
250
|
+
error: { message: loaded.error }
|
|
251
|
+
}, 409);
|
|
252
|
+
return c.json({
|
|
253
|
+
ok: true,
|
|
254
|
+
payload: { providers: listCustomImageProviders(loaded.config) }
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
authenticated.put("/api/image-generation/custom-providers/:providerId", strictRateLimitMiddleware, async (c) => {
|
|
258
|
+
const loaded = loadModelsJson(resolveModelsJsonPath());
|
|
259
|
+
if (loaded.error) return c.json({
|
|
260
|
+
ok: false,
|
|
261
|
+
error: { message: loaded.error }
|
|
262
|
+
}, 409);
|
|
263
|
+
try {
|
|
264
|
+
const body = await c.req.json();
|
|
265
|
+
const next = upsertCustomImageProvider(loaded.config, c.req.param("providerId"), body);
|
|
266
|
+
const saved = saveModelsJson(resolveModelsJsonPath(), next);
|
|
267
|
+
if (!saved.success) return c.json({
|
|
268
|
+
ok: false,
|
|
269
|
+
error: { message: saved.error }
|
|
270
|
+
}, 400);
|
|
271
|
+
getModelRegistry().refresh();
|
|
272
|
+
reloadImageGenerationProviders(next);
|
|
273
|
+
service.emit("models-json.updated", { modelCount: getModelRegistry().getAll().length });
|
|
274
|
+
return c.json({
|
|
275
|
+
ok: true,
|
|
276
|
+
payload: {
|
|
277
|
+
provider: listCustomImageProviders(next).find((entry) => entry.providerId === c.req.param("providerId")),
|
|
278
|
+
providers: getImageGenerationCatalog(service.currentConfig)
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
} catch (error) {
|
|
282
|
+
return c.json({
|
|
283
|
+
ok: false,
|
|
284
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
285
|
+
}, 400);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
authenticated.delete("/api/image-generation/custom-providers/:providerId", strictRateLimitMiddleware, (c) => {
|
|
289
|
+
const loaded = loadModelsJson(resolveModelsJsonPath());
|
|
290
|
+
if (loaded.error) return c.json({
|
|
291
|
+
ok: false,
|
|
292
|
+
error: { message: loaded.error }
|
|
293
|
+
}, 409);
|
|
294
|
+
try {
|
|
295
|
+
const next = deleteCustomImageProvider(loaded.config, c.req.param("providerId"));
|
|
296
|
+
const saved = saveModelsJson(resolveModelsJsonPath(), next);
|
|
297
|
+
if (!saved.success) return c.json({
|
|
298
|
+
ok: false,
|
|
299
|
+
error: { message: saved.error }
|
|
300
|
+
}, 400);
|
|
301
|
+
getModelRegistry().refresh();
|
|
302
|
+
reloadImageGenerationProviders(next);
|
|
303
|
+
service.emit("models-json.updated", { modelCount: getModelRegistry().getAll().length });
|
|
304
|
+
return c.json({
|
|
305
|
+
ok: true,
|
|
306
|
+
payload: {
|
|
307
|
+
deleted: c.req.param("providerId"),
|
|
308
|
+
providers: getImageGenerationCatalog(service.currentConfig)
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
} catch (error) {
|
|
312
|
+
return c.json({
|
|
313
|
+
ok: false,
|
|
314
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
315
|
+
}, 400);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
authenticated.put("/api/image-generation/providers/:providerId/credential", strictRateLimitMiddleware, async (c) => {
|
|
319
|
+
const providerId = c.req.param("providerId").trim();
|
|
320
|
+
const provider = getImageGenerationProvider(providerId);
|
|
321
|
+
if (!provider) return c.json({
|
|
322
|
+
ok: false,
|
|
323
|
+
error: { message: `Unknown image provider: ${providerId}` }
|
|
324
|
+
}, 404);
|
|
325
|
+
if (provider.credentialMode === "none") return c.json({
|
|
326
|
+
ok: false,
|
|
327
|
+
error: { message: "This provider does not use an API key" }
|
|
328
|
+
}, 400);
|
|
329
|
+
const body = await c.req.json().catch(() => null);
|
|
330
|
+
const apiKey = typeof body?.apiKey === "string" ? body.apiKey.trim() : "";
|
|
331
|
+
if (!apiKey) return c.json({
|
|
332
|
+
ok: false,
|
|
333
|
+
error: { message: "API key is required" }
|
|
334
|
+
}, 400);
|
|
335
|
+
await new CredentialResolver().saveApiKey(providerId, apiKey, { profileName: "default" });
|
|
336
|
+
return c.json({
|
|
337
|
+
ok: true,
|
|
338
|
+
payload: {
|
|
339
|
+
providerId,
|
|
340
|
+
saved: true
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
authenticated.post("/api/image-generation/providers/:providerId/reveal-api-key", strictRateLimitMiddleware, async (c) => {
|
|
345
|
+
const providerId = c.req.param("providerId").trim();
|
|
346
|
+
if (!getImageGenerationProvider(providerId)) return c.json({
|
|
347
|
+
ok: false,
|
|
348
|
+
error: { message: `Unknown image provider: ${providerId}` }
|
|
349
|
+
}, 404);
|
|
350
|
+
const apiKey = await new CredentialResolver().revealGatewayStoredApiKey(providerId);
|
|
351
|
+
return c.json({
|
|
352
|
+
ok: true,
|
|
353
|
+
payload: {
|
|
354
|
+
providerId,
|
|
355
|
+
apiKey: apiKey ?? null,
|
|
356
|
+
source: apiKey ? "credential" : "none"
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
authenticated.delete("/api/image-generation/providers/:providerId/credential", strictRateLimitMiddleware, async (c) => {
|
|
361
|
+
const providerId = c.req.param("providerId").trim();
|
|
362
|
+
if (!getImageGenerationProvider(providerId)) return c.json({
|
|
363
|
+
ok: false,
|
|
364
|
+
error: { message: `Unknown image provider: ${providerId}` }
|
|
365
|
+
}, 404);
|
|
366
|
+
await new CredentialResolver().deleteProviderCredential(providerId);
|
|
367
|
+
return c.json({
|
|
368
|
+
ok: true,
|
|
369
|
+
payload: {
|
|
370
|
+
providerId,
|
|
371
|
+
deleted: true
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
authenticated.post("/api/image-generation/providers/:providerId/test", strictRateLimitMiddleware, async (c) => {
|
|
376
|
+
const providerId = c.req.param("providerId").trim();
|
|
377
|
+
const provider = getImageGenerationProvider(providerId);
|
|
378
|
+
if (!provider) return c.json({
|
|
379
|
+
ok: false,
|
|
380
|
+
error: { message: `Unknown image provider: ${providerId}` }
|
|
381
|
+
}, 404);
|
|
382
|
+
if (!provider.isConfigured({ cfg: service.currentConfig })) return c.json({
|
|
383
|
+
ok: false,
|
|
384
|
+
error: { message: "Provider credential is not configured" }
|
|
385
|
+
}, 400);
|
|
386
|
+
const body = await c.req.json().catch(() => ({}));
|
|
387
|
+
const modelId = typeof body.modelId === "string" && body.modelId.trim() ? body.modelId.trim() : provider.defaultModel;
|
|
388
|
+
if (!provider.models.includes(modelId)) return c.json({
|
|
389
|
+
ok: false,
|
|
390
|
+
error: { message: `Unknown ${providerId} image model: ${modelId}` }
|
|
391
|
+
}, 400);
|
|
392
|
+
const prompt = typeof body.prompt === "string" && body.prompt.trim() ? body.prompt.trim() : "A simple blue circle centered on a white background.";
|
|
393
|
+
try {
|
|
394
|
+
const result = await provider.generateImage({
|
|
395
|
+
provider: providerId,
|
|
396
|
+
model: modelId,
|
|
397
|
+
prompt,
|
|
398
|
+
count: 1,
|
|
399
|
+
cfg: service.currentConfig,
|
|
400
|
+
timeoutMs: 12e4
|
|
401
|
+
});
|
|
402
|
+
return c.json({
|
|
403
|
+
ok: true,
|
|
404
|
+
payload: {
|
|
405
|
+
providerId,
|
|
406
|
+
modelId: result.model ?? modelId,
|
|
407
|
+
images: result.images.map((image) => ({
|
|
408
|
+
mimeType: image.mimeType,
|
|
409
|
+
fileName: image.fileName,
|
|
410
|
+
dataUrl: `data:${image.mimeType};base64,${image.buffer.toString("base64")}`,
|
|
411
|
+
...image.revisedPrompt ? { revisedPrompt: image.revisedPrompt } : {}
|
|
412
|
+
}))
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
} catch (error) {
|
|
416
|
+
return c.json({
|
|
417
|
+
ok: false,
|
|
418
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
419
|
+
}, 502);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
242
422
|
authenticated.get("/api/agents/:agentId/image-generation", (c) => {
|
|
243
423
|
try {
|
|
244
424
|
return c.json({
|
|
@@ -271,7 +451,8 @@ function registerModelsRoutes(authenticated, deps) {
|
|
|
271
451
|
const resolver = new CredentialResolver();
|
|
272
452
|
const previousKey = await resolver.revealGatewayStoredApiKey(prepared.providerId);
|
|
273
453
|
const suppliedKey = typeof body.apiKey === "string" ? body.apiKey.trim() : "";
|
|
274
|
-
|
|
454
|
+
const effectiveKey = suppliedKey || await resolver.resolveApiKey(prepared.providerId);
|
|
455
|
+
if (getImageGenerationProvider(prepared.providerId).credentialMode !== "none" && !effectiveKey) return c.json({
|
|
275
456
|
ok: false,
|
|
276
457
|
error: { message: "API key is required" }
|
|
277
458
|
}, 400);
|
|
@@ -7,9 +7,9 @@ import { messagesToClientHistory } from "../../../session/client-history.js";
|
|
|
7
7
|
import "../../../workflows/domain/index.js";
|
|
8
8
|
import { resolveWorkflowRunArtifactsDir } from "../../../workflows/store/paths.js";
|
|
9
9
|
import { WorkflowDraftService } from "../../../workflows/draft/workflow-draft-service.js";
|
|
10
|
-
import "../../../workflows/draft/index.js";
|
|
11
10
|
import { WorkflowDraftConflictError, WorkflowDraftStore } from "../../../workflows/authoring/workflow-draft-store.js";
|
|
12
11
|
import "../../../workflows/authoring/index.js";
|
|
12
|
+
import "../../../workflows/draft/index.js";
|
|
13
13
|
import { basename, join } from "node:path";
|
|
14
14
|
import { readFile, stat } from "node:fs/promises";
|
|
15
15
|
//#region src/gateway/hono/routes/workflows.ts
|
|
@@ -64,7 +64,13 @@ function prepareImageGenerationSetup(config, agentIdRaw, input) {
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
const entry = next.agents.list[index];
|
|
67
|
-
entry.models
|
|
67
|
+
entry.models = {
|
|
68
|
+
...entry.models ?? {
|
|
69
|
+
defaultRole: resolveEffectiveAgentManifestForAgent(config, agentId).models.defaultRole,
|
|
70
|
+
roles: {}
|
|
71
|
+
},
|
|
72
|
+
imageGenerationModel: { primary: `${providerId}/${modelId}` }
|
|
73
|
+
};
|
|
68
74
|
const parsed = ConfigSchema.safeParse(next);
|
|
69
75
|
if (!parsed.success) return {
|
|
70
76
|
ok: false,
|
|
@@ -3,6 +3,7 @@ import { init_logger } from "../../utils/logger.js";
|
|
|
3
3
|
import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
4
4
|
import { getModelRegistry } from "../../providers/model-registry.js";
|
|
5
5
|
import { init_providers } from "../../providers/index.js";
|
|
6
|
+
import { reloadImageGenerationProviders } from "../../agent/image/generation/provider-registry.js";
|
|
6
7
|
import { disposeAllSessionMcpRuntimes } from "../../agent/mcp/bundle-mcp-runtime.js";
|
|
7
8
|
import "../../agent/mcp/bundle-mcp-tools.js";
|
|
8
9
|
import { ConfigHotReloader } from "../../config/reload.js";
|
|
@@ -214,6 +215,7 @@ var GatewayConfigCoordinator = class {
|
|
|
214
215
|
log.debug("Reloading models config...");
|
|
215
216
|
this.opts.setConfig(newConfig);
|
|
216
217
|
getModelRegistry().refresh();
|
|
218
|
+
reloadImageGenerationProviders();
|
|
217
219
|
this.opts.emit("config.reload", { section: "models" });
|
|
218
220
|
log.debug("Models config reloaded");
|
|
219
221
|
}
|
|
@@ -37,11 +37,11 @@ import { AgentService } from "../agent/service.js";
|
|
|
37
37
|
import { ensureStarterAgentsInitialized } from "../agent/starter-agents.js";
|
|
38
38
|
import { AutomationService } from "../automations/service/automation-service.js";
|
|
39
39
|
import "../automations/index.js";
|
|
40
|
-
import { getWorkspacePath } from "../config/workspace-path-helpers.js";
|
|
41
|
-
import "../config/index.js";
|
|
42
40
|
import { setPairingBroadcastSink } from "../channels/pairing/pairing-events.js";
|
|
43
41
|
import { registerClarifyBridge } from "./clarify-runtime.js";
|
|
44
42
|
import { ChannelManager } from "../channels/manager.js";
|
|
43
|
+
import { getWorkspacePath } from "../config/workspace-path-helpers.js";
|
|
44
|
+
import "../config/index.js";
|
|
45
45
|
import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
|
|
46
46
|
import "../knowledge/index.js";
|
|
47
47
|
import { startConnectorMemorySyncCoordinator } from "../connectors/memory-sync-coordinator.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, shouldSilence, stripHeartbeatToken } from "./tokens.js";
|
|
2
|
+
import { appendCronEventLines } from "./event-prompt.js";
|
|
2
3
|
import { isWithinActiveHours } from "./active-hours.js";
|
|
3
4
|
import { isHeartbeatContentEmpty } from "./content-check.js";
|
|
4
|
-
import { appendCronEventLines } from "./event-prompt.js";
|
|
5
5
|
import { createHeartbeatWake } from "./wake.js";
|
|
6
6
|
export { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, appendCronEventLines, createHeartbeatWake, isHeartbeatContentEmpty, isWithinActiveHours, shouldSilence, stripHeartbeatToken };
|
|
@@ -7,5 +7,5 @@ export { TimeoutAbortError, isTimeoutAbortError, pickEffectiveTimeoutMs, pickTim
|
|
|
7
7
|
export { BlockedPrivateNetworkError, assertNotPrivateNetwork, classifyHost, defaultPolicy as defaultPrivateNetworkPolicy, privateNetworkPolicyToSsrfGuardOptions, type HostClass, type PrivateNetworkPolicy, } from './private-network.js';
|
|
8
8
|
export { resolveProviderHttpRequestConfig, type ResolvedProviderHttpDefaults, type ResolveProviderHttpRequestConfigOptions, } from './resolve-provider-http-request-config.js';
|
|
9
9
|
export { ProviderHttpError, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, createProviderHttpError, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, formatProviderErrorPayload, formatProviderHttpErrorMessage, readResponseTextLimited, truncateErrorDetail, type ProviderHttpErrorParts, } from './provider-http-errors.js';
|
|
10
|
-
export { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, waitProviderOperationPollInterval, type FetchWithGuardOptions, type GetJsonRequestOptions, type PostJsonRequestOptions, type PostMultipartRequestOptions, type ProviderHttpRequestConfig, type ProviderOperationDeadline, } from './provider-http.js';
|
|
10
|
+
export { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, readJsonResponseLimited, waitProviderOperationPollInterval, type FetchWithGuardOptions, type GetJsonRequestOptions, type PostJsonRequestOptions, type PostMultipartRequestOptions, type ProviderHttpRequestConfig, type ProviderOperationDeadline, } from './provider-http.js';
|
|
11
11
|
export { SsrfBlockedError, assertSafeUrl, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6, type ResolvedSsrfTarget, type SsrfGuardOptions, } from './ssrf-guard.js';
|
|
@@ -4,5 +4,5 @@ import { BlockedPrivateNetworkError, assertNotPrivateNetwork, classifyHost, defa
|
|
|
4
4
|
import { resolveProviderHttpRequestConfig } from "./resolve-provider-http-request-config.js";
|
|
5
5
|
import { ProviderHttpError, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, createProviderHttpError, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, formatProviderErrorPayload, formatProviderHttpErrorMessage, readResponseTextLimited, truncateErrorDetail } from "./provider-http-errors.js";
|
|
6
6
|
import { SsrfBlockedError, assertSafeUrl, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6 } from "./ssrf-guard.js";
|
|
7
|
-
import { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, waitProviderOperationPollInterval } from "./provider-http.js";
|
|
8
|
-
export { BlockedPrivateNetworkError, ProviderHttpError, SsrfBlockedError, TimeoutAbortError, assertNotPrivateNetwork, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, assertSafeUrl, classifyHost, collectProviderApiKeysForExecution, createProviderHttpError, createProviderOperationDeadline, defaultPolicy as defaultPrivateNetworkPolicy, executeWithApiKeyRotation, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, fetchWithTimeoutGuarded, formatProviderErrorPayload, formatProviderHttpErrorMessage, getJsonRequest, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6, isRotatableAuthFailure, isTimeoutAbortError, normalizeBaseUrl, pickEffectiveTimeoutMs, pickTimeoutMsOrFallback, postJsonRequest, postMultipartRequest, privateNetworkPolicyToSsrfGuardOptions, readResponseTextLimited, resolveDeadline, resolveProviderHttpRequestConfig, truncateErrorDetail, waitProviderOperationPollInterval };
|
|
7
|
+
import { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, readJsonResponseLimited, waitProviderOperationPollInterval } from "./provider-http.js";
|
|
8
|
+
export { BlockedPrivateNetworkError, ProviderHttpError, SsrfBlockedError, TimeoutAbortError, assertNotPrivateNetwork, assertOk, assertOkOrThrowHttpError, assertOkOrThrowProviderError, assertSafeUrl, classifyHost, collectProviderApiKeysForExecution, createProviderHttpError, createProviderOperationDeadline, defaultPolicy as defaultPrivateNetworkPolicy, executeWithApiKeyRotation, extractProviderErrorDetail, extractProviderRequestId, extractVendorErrorFields, fetchWithTimeoutGuarded, formatProviderErrorPayload, formatProviderHttpErrorMessage, getJsonRequest, isPrivateIpAddress, isPrivateIpv4, isPrivateIpv6, isRotatableAuthFailure, isTimeoutAbortError, normalizeBaseUrl, pickEffectiveTimeoutMs, pickTimeoutMsOrFallback, postJsonRequest, postMultipartRequest, privateNetworkPolicyToSsrfGuardOptions, readJsonResponseLimited, readResponseTextLimited, resolveDeadline, resolveProviderHttpRequestConfig, truncateErrorDetail, waitProviderOperationPollInterval };
|
|
@@ -39,6 +39,8 @@ export interface GetJsonRequestOptions extends ProviderHttpRequestConfig {
|
|
|
39
39
|
headers?: Record<string, string> | Headers;
|
|
40
40
|
}
|
|
41
41
|
export declare function getJsonRequest(url: string | URL, options: GetJsonRequestOptions): Promise<Response>;
|
|
42
|
+
/** Read and parse a JSON response without allowing an unbounded body allocation. */
|
|
43
|
+
export declare function readJsonResponseLimited(response: Response, maxBytes: number): Promise<unknown>;
|
|
42
44
|
/**
|
|
43
45
|
* Normalize a base URL: trim trailing slash, force http(s) scheme.
|
|
44
46
|
*/
|
|
@@ -49,7 +49,8 @@ async function postJsonRequest(url, options) {
|
|
|
49
49
|
init: {
|
|
50
50
|
method: "POST",
|
|
51
51
|
headers,
|
|
52
|
-
body: JSON.stringify(options.body)
|
|
52
|
+
body: JSON.stringify(options.body),
|
|
53
|
+
redirect: "error"
|
|
53
54
|
}
|
|
54
55
|
});
|
|
55
56
|
await assertOkOrThrowProviderError(response, options.label);
|
|
@@ -62,7 +63,8 @@ async function postMultipartRequest(url, options) {
|
|
|
62
63
|
init: {
|
|
63
64
|
method: "POST",
|
|
64
65
|
headers,
|
|
65
|
-
body: options.body
|
|
66
|
+
body: options.body,
|
|
67
|
+
redirect: "error"
|
|
66
68
|
}
|
|
67
69
|
});
|
|
68
70
|
await assertOkOrThrowProviderError(response, options.label);
|
|
@@ -75,12 +77,49 @@ async function getJsonRequest(url, options) {
|
|
|
75
77
|
...options,
|
|
76
78
|
init: {
|
|
77
79
|
method: "GET",
|
|
78
|
-
headers
|
|
80
|
+
headers,
|
|
81
|
+
redirect: "error"
|
|
79
82
|
}
|
|
80
83
|
});
|
|
81
84
|
await assertOkOrThrowProviderError(response, options.label);
|
|
82
85
|
return response;
|
|
83
86
|
}
|
|
87
|
+
/** Read and parse a JSON response without allowing an unbounded body allocation. */
|
|
88
|
+
async function readJsonResponseLimited(response, maxBytes) {
|
|
89
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) throw new Error("JSON response byte limit must be a positive safe integer.");
|
|
90
|
+
const declaredLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
91
|
+
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) throw new Error(`Provider JSON response exceeds ${maxBytes} bytes.`);
|
|
92
|
+
const reader = response.body?.getReader();
|
|
93
|
+
if (!reader) throw new Error("Provider returned an empty JSON response.");
|
|
94
|
+
const chunks = [];
|
|
95
|
+
let total = 0;
|
|
96
|
+
try {
|
|
97
|
+
while (true) {
|
|
98
|
+
const { value, done } = await reader.read();
|
|
99
|
+
if (done) break;
|
|
100
|
+
if (!value?.byteLength) continue;
|
|
101
|
+
total += value.byteLength;
|
|
102
|
+
if (total > maxBytes) {
|
|
103
|
+
await reader.cancel().catch(() => {});
|
|
104
|
+
throw new Error(`Provider JSON response exceeds ${maxBytes} bytes.`);
|
|
105
|
+
}
|
|
106
|
+
chunks.push(value);
|
|
107
|
+
}
|
|
108
|
+
} finally {
|
|
109
|
+
reader.releaseLock();
|
|
110
|
+
}
|
|
111
|
+
const body = new Uint8Array(total);
|
|
112
|
+
let offset = 0;
|
|
113
|
+
for (const chunk of chunks) {
|
|
114
|
+
body.set(chunk, offset);
|
|
115
|
+
offset += chunk.byteLength;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
return JSON.parse(new TextDecoder().decode(body));
|
|
119
|
+
} catch {
|
|
120
|
+
throw new Error("Provider returned invalid JSON.");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
84
123
|
/**
|
|
85
124
|
* Normalize a base URL: trim trailing slash, force http(s) scheme.
|
|
86
125
|
*/
|
|
@@ -107,4 +146,4 @@ async function waitProviderOperationPollInterval(intervalMs, signal) {
|
|
|
107
146
|
await setTimeout$1(intervalMs, void 0, { signal });
|
|
108
147
|
}
|
|
109
148
|
//#endregion
|
|
110
|
-
export { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, waitProviderOperationPollInterval };
|
|
149
|
+
export { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, readJsonResponseLimited, waitProviderOperationPollInterval };
|
|
@@ -11,4 +11,5 @@ export interface ModelReferenceHealth {
|
|
|
11
11
|
export declare function auditModelReferences(config: Config, sessionConfigs?: ReadonlyMap<string, SessionAgentConfig>, deps?: {
|
|
12
12
|
registry?: ModelRegistry;
|
|
13
13
|
catalog?: ModelCatalogSnapshot;
|
|
14
|
+
resolveImageGenerationModel?: (ref: string) => boolean;
|
|
14
15
|
}): ModelReferenceHealth[];
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { getModelCatalogStore, init_model_catalog_store } from "./model-catalog-store.js";
|
|
2
2
|
import { getModelRegistry, init_model_registry } from "./model-registry.js";
|
|
3
|
+
import { parseImageGenerationModelRef } from "../agent/image/generation/model-ref.js";
|
|
4
|
+
import { getImageGenerationProvider } from "../agent/image/generation/provider-registry.js";
|
|
3
5
|
//#region src/providers/model-reference-auditor.ts
|
|
4
6
|
init_model_catalog_store();
|
|
5
7
|
init_model_registry();
|
|
6
8
|
function asRecord(value) {
|
|
7
9
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
8
10
|
}
|
|
9
|
-
function addRef(out, value, location) {
|
|
11
|
+
function addRef(out, value, location, kind = "text") {
|
|
10
12
|
if (typeof value === "string" && value.trim()) out.push({
|
|
11
13
|
ref: value.trim(),
|
|
12
|
-
location
|
|
14
|
+
location,
|
|
15
|
+
kind
|
|
13
16
|
});
|
|
14
17
|
}
|
|
15
|
-
function collectToolModel(out, value, location) {
|
|
18
|
+
function collectToolModel(out, value, location, kind = "text") {
|
|
16
19
|
const model = asRecord(value);
|
|
17
20
|
if (!model) return;
|
|
18
|
-
addRef(out, model.primary, `${location}.primary
|
|
19
|
-
if (Array.isArray(model.fallbacks)) model.fallbacks.forEach((ref, index) => addRef(out, ref, `${location}.fallbacks[${index}]
|
|
21
|
+
addRef(out, model.primary, `${location}.primary`, kind);
|
|
22
|
+
if (Array.isArray(model.fallbacks)) model.fallbacks.forEach((ref, index) => addRef(out, ref, `${location}.fallbacks[${index}]`, kind));
|
|
20
23
|
}
|
|
21
24
|
function collectModelPolicy(out, value, location) {
|
|
22
25
|
const policy = asRecord(value);
|
|
@@ -29,7 +32,7 @@ function collectModelPolicy(out, value, location) {
|
|
|
29
32
|
if (Array.isArray(role.fallbacks)) role.fallbacks.forEach((ref, index) => addRef(out, ref, `${location}.roles.${roleId}.fallbacks[${index}]`));
|
|
30
33
|
}
|
|
31
34
|
collectToolModel(out, policy.imageModel, `${location}.imageModel`);
|
|
32
|
-
collectToolModel(out, policy.imageGenerationModel, `${location}.imageGenerationModel
|
|
35
|
+
collectToolModel(out, policy.imageGenerationModel, `${location}.imageGenerationModel`, "image-generation");
|
|
33
36
|
}
|
|
34
37
|
function collectReferences(config, sessionConfigs) {
|
|
35
38
|
const out = [];
|
|
@@ -47,22 +50,29 @@ function suggestedRef(ref, catalog) {
|
|
|
47
50
|
const first = source.models.find((model) => model.availability === "available");
|
|
48
51
|
return first ? `${providerId}/${first.id}` : void 0;
|
|
49
52
|
}
|
|
53
|
+
function hasImageGenerationModel(ref) {
|
|
54
|
+
const parsed = parseImageGenerationModelRef(ref);
|
|
55
|
+
if (!parsed) return false;
|
|
56
|
+
return getImageGenerationProvider(parsed.provider)?.models.includes(parsed.model) ?? false;
|
|
57
|
+
}
|
|
50
58
|
function auditModelReferences(config, sessionConfigs = /* @__PURE__ */ new Map(), deps = {}) {
|
|
51
59
|
const registry = deps.registry ?? getModelRegistry();
|
|
52
60
|
const catalog = deps.catalog ?? getModelCatalogStore().load();
|
|
61
|
+
const resolveImageGenerationModel = deps.resolveImageGenerationModel ?? hasImageGenerationModel;
|
|
53
62
|
const grouped = /* @__PURE__ */ new Map();
|
|
54
63
|
for (const reference of collectReferences(config, sessionConfigs)) {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
grouped.set(reference.ref,
|
|
64
|
+
const references = grouped.get(reference.ref) ?? [];
|
|
65
|
+
references.push(reference);
|
|
66
|
+
grouped.set(reference.ref, references);
|
|
58
67
|
}
|
|
59
|
-
return [...grouped.entries()].map(([ref,
|
|
60
|
-
const availability = registry.resolve(ref) ? "available" : "unavailable";
|
|
61
|
-
const
|
|
68
|
+
return [...grouped.entries()].map(([ref, references]) => {
|
|
69
|
+
const availability = references.every((reference) => reference.kind === "image-generation" ? resolveImageGenerationModel(ref) : Boolean(registry.resolve(ref))) ? "available" : "unavailable";
|
|
70
|
+
const textOnly = references.every((reference) => reference.kind === "text");
|
|
71
|
+
const suggestion = availability === "unavailable" && textOnly ? suggestedRef(ref, catalog) : void 0;
|
|
62
72
|
return {
|
|
63
73
|
ref,
|
|
64
74
|
availability,
|
|
65
|
-
locations,
|
|
75
|
+
locations: references.map((reference) => reference.location),
|
|
66
76
|
...suggestion ? { suggestedRef: suggestion } : {}
|
|
67
77
|
};
|
|
68
78
|
}).sort((a, b) => a.ref.localeCompare(b.ref));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.167",
|
|
4
4
|
"description": "Local-first AI system that remembers context, coordinates AI, and sustains long-term progress.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"semver": "^7.8.1",
|
|
101
101
|
"silk-wasm": "^3.7.1",
|
|
102
102
|
"thread-stream": "^4.2.0",
|
|
103
|
-
"undici": "^8.
|
|
103
|
+
"undici": "^8.9.0",
|
|
104
104
|
"uuid": "^14.0.0",
|
|
105
105
|
"ws": "^8.21.0",
|
|
106
106
|
"zod": "^4.4.3"
|