@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
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=20260713-1" />
|
|
11
11
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?v=20260713-1" />
|
|
12
12
|
<link rel="manifest" href="/site.webmanifest?v=20260713-1" />
|
|
13
|
-
<title>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<title>XOPC — Turn goals into loops. Keep what matters moving.</title>
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-B24B26og.js"></script>
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-aKtaBQYM.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-zJ_50EbN.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BOUWij0V.js">
|
|
@@ -24,17 +24,17 @@
|
|
|
24
24
|
<link rel="modulepreload" crossorigin href="/assets/check-DUBLcKHA.js">
|
|
25
25
|
<link rel="modulepreload" crossorigin href="/assets/circle-alert-BEjgurr2.js">
|
|
26
26
|
<link rel="modulepreload" crossorigin href="/assets/middleware-Pd0e8Igk.js">
|
|
27
|
-
<link rel="modulepreload" crossorigin href="/assets/messages-
|
|
28
|
-
<link rel="modulepreload" crossorigin href="/assets/locale-store-
|
|
27
|
+
<link rel="modulepreload" crossorigin href="/assets/messages-B1EG4gdu.js">
|
|
28
|
+
<link rel="modulepreload" crossorigin href="/assets/locale-store-tnRLWdx2.js">
|
|
29
29
|
<link rel="modulepreload" crossorigin href="/assets/cn-JdwAhgNB.js">
|
|
30
30
|
<link rel="modulepreload" crossorigin href="/assets/schemas-DgQq6Z5S.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="/assets/product-delivery-BTRT9kCS.js">
|
|
32
32
|
<link rel="modulepreload" crossorigin href="/assets/config-reload-event-Bjh2zoKp.js">
|
|
33
33
|
<link rel="modulepreload" crossorigin href="/assets/copy-to-clipboard-HOJ79TWv.js">
|
|
34
|
-
<link rel="modulepreload" crossorigin href="/assets/gateway-startup-retry-
|
|
35
|
-
<link rel="modulepreload" crossorigin href="/assets/activity-store-
|
|
36
|
-
<link rel="modulepreload" crossorigin href="/assets/theme-store-
|
|
37
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
34
|
+
<link rel="modulepreload" crossorigin href="/assets/gateway-startup-retry-qnEmj-vL.js">
|
|
35
|
+
<link rel="modulepreload" crossorigin href="/assets/activity-store-DjEDu38T.js">
|
|
36
|
+
<link rel="modulepreload" crossorigin href="/assets/theme-store-H9T49Iar.js">
|
|
37
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DfE2j0g8.css">
|
|
38
38
|
</head>
|
|
39
39
|
<body>
|
|
40
40
|
<div id="root"></div>
|
package/dist/package.js
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ImageGenerationProviderConfig, ProviderConfig } from '../../../config/models-json.js';
|
|
2
|
+
import type { ImageGenerationProvider } from './types.js';
|
|
3
|
+
export declare function buildCustomOpenAiImagesProvider(params: {
|
|
4
|
+
providerId: string;
|
|
5
|
+
provider: ProviderConfig;
|
|
6
|
+
imageGeneration: ImageGenerationProviderConfig;
|
|
7
|
+
}): ImageGenerationProvider;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { resolveAuthProfileForProvider } from "../../../providers/auth-runtime/resolve-auth.js";
|
|
2
|
+
import { isProviderApiKeyConfigured } from "../../../providers/auth-runtime/is-configured.js";
|
|
3
|
+
import "../../../providers/auth-runtime/index.js";
|
|
4
|
+
import { createOpenAiImagesProvider } from "./openai-images-provider.js";
|
|
5
|
+
//#region src/agent/image/generation/custom-openai-images-provider.ts
|
|
6
|
+
function buildCustomOpenAiImagesProvider(params) {
|
|
7
|
+
const { providerId, provider, imageGeneration } = params;
|
|
8
|
+
if (!provider.baseUrl) throw new Error(`Custom image provider "${providerId}" requires baseUrl.`);
|
|
9
|
+
const models = imageGeneration.models.map((model) => model.id);
|
|
10
|
+
const modelCapabilities = Object.fromEntries(imageGeneration.models.map((model) => [model.id, model.capabilities]));
|
|
11
|
+
const defaultCapabilities = modelCapabilities[imageGeneration.defaultModel];
|
|
12
|
+
if (!defaultCapabilities) throw new Error(`Custom image provider "${providerId}" default model is not in its model catalog.`);
|
|
13
|
+
const auth = imageGeneration.auth;
|
|
14
|
+
const isConfigured = (ctx) => auth.type === "none" || isProviderApiKeyConfigured({
|
|
15
|
+
providerId,
|
|
16
|
+
cfg: ctx.cfg,
|
|
17
|
+
agentId: ctx.agentId
|
|
18
|
+
});
|
|
19
|
+
const executors = new Map(imageGeneration.models.map((model) => {
|
|
20
|
+
const executor = createOpenAiImagesProvider({
|
|
21
|
+
id: providerId,
|
|
22
|
+
label: imageGeneration.name,
|
|
23
|
+
defaultModel: model.id,
|
|
24
|
+
models: [model.id],
|
|
25
|
+
capabilities: model.capabilities,
|
|
26
|
+
isConfigured,
|
|
27
|
+
resolveApiKey: (req) => {
|
|
28
|
+
if (auth.type === "none") return null;
|
|
29
|
+
return resolveAuthProfileForProvider({
|
|
30
|
+
providerId,
|
|
31
|
+
cfg: req.cfg,
|
|
32
|
+
agentId: req.agentId,
|
|
33
|
+
store: req.authStore
|
|
34
|
+
}).apiKey;
|
|
35
|
+
},
|
|
36
|
+
resolveEndpoint: () => ({
|
|
37
|
+
baseUrl: provider.baseUrl,
|
|
38
|
+
headers: provider.headers,
|
|
39
|
+
generationsPath: imageGeneration.paths?.generations,
|
|
40
|
+
editsPath: imageGeneration.paths?.edits,
|
|
41
|
+
authorization: auth.type === "none" ? { kind: "none" } : auth.type === "header" ? {
|
|
42
|
+
kind: "header",
|
|
43
|
+
headerName: auth.headerName
|
|
44
|
+
} : { kind: "bearer" },
|
|
45
|
+
privateNetworkPolicy: imageGeneration.network ? {
|
|
46
|
+
allowPrivate: true,
|
|
47
|
+
allowHosts: imageGeneration.network.allowedHosts
|
|
48
|
+
} : {}
|
|
49
|
+
}),
|
|
50
|
+
defaultCount: model.defaults?.count,
|
|
51
|
+
defaultSize: model.defaults?.size
|
|
52
|
+
});
|
|
53
|
+
return [model.id, {
|
|
54
|
+
executor,
|
|
55
|
+
outputFormat: model.defaults?.outputFormat
|
|
56
|
+
}];
|
|
57
|
+
}));
|
|
58
|
+
return {
|
|
59
|
+
id: providerId,
|
|
60
|
+
label: imageGeneration.name,
|
|
61
|
+
source: "custom",
|
|
62
|
+
credentialMode: auth.type === "none" ? "none" : "api-key",
|
|
63
|
+
documentationUrl: imageGeneration.documentationUrl,
|
|
64
|
+
apiKeyUrl: imageGeneration.apiKeyUrl,
|
|
65
|
+
defaultModel: imageGeneration.defaultModel,
|
|
66
|
+
models,
|
|
67
|
+
capabilities: defaultCapabilities,
|
|
68
|
+
modelCapabilities,
|
|
69
|
+
isConfigured,
|
|
70
|
+
async generateImage(req) {
|
|
71
|
+
const selected = executors.get(req.model);
|
|
72
|
+
if (!selected) throw new Error(`Image model is not configured for ${providerId}: ${req.model}`);
|
|
73
|
+
return selected.executor.generateImage({
|
|
74
|
+
...req,
|
|
75
|
+
outputFormat: req.outputFormat ?? selected.outputFormat
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { buildCustomOpenAiImagesProvider };
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Many image providers (OpenAI, Azure OpenAI, OpenAI-compatible gateways)
|
|
5
|
-
* speak the same `/images/generations` and `/images/edits` REST shape.
|
|
6
|
-
* This factory wraps that wire format on top of shared provider HTTP, normalization,
|
|
7
|
-
* and auth-runtime so vendor modules only need to declare:
|
|
8
|
-
* - id / models / capabilities
|
|
9
|
-
* - baseUrl resolution
|
|
10
|
-
* - apiKey resolution
|
|
11
|
-
* - optional request body / response transforms
|
|
12
|
-
*
|
|
13
|
-
*/
|
|
1
|
+
/** Strict implementation of the OpenAI Images REST protocol. */
|
|
2
|
+
import type { PrivateNetworkPolicy } from '../../../media-shared/http/index.js';
|
|
14
3
|
import type { GeneratedImageAsset, ImageGenerationProvider, ImageGenerationProviderCapabilities, ImageGenerationProviderConfiguredContext, ImageGenerationRequest } from './types.js';
|
|
15
4
|
/** Subset of the official OpenAI image response we map. */
|
|
16
|
-
export interface
|
|
5
|
+
export interface OpenAiImagesResponse {
|
|
17
6
|
data?: Array<{
|
|
18
7
|
b64_json?: string;
|
|
19
8
|
revised_prompt?: string;
|
|
@@ -21,7 +10,7 @@ export interface OpenAiCompatibleImageResponse {
|
|
|
21
10
|
}>;
|
|
22
11
|
[k: string]: unknown;
|
|
23
12
|
}
|
|
24
|
-
export interface
|
|
13
|
+
export interface OpenAiImagesEndpointResolution {
|
|
25
14
|
/** Base URL without trailing slash, e.g. `https://api.openai.com/v1`. */
|
|
26
15
|
baseUrl: string;
|
|
27
16
|
/** Optional override for the generations sub-path; defaults to `/images/generations`. */
|
|
@@ -42,8 +31,10 @@ export interface OpenAiCompatibleEndpointResolution {
|
|
|
42
31
|
kind: 'header';
|
|
43
32
|
headerName: string;
|
|
44
33
|
};
|
|
34
|
+
/** Explicit private-host allowlist for this endpoint. Omit to use the global policy. */
|
|
35
|
+
privateNetworkPolicy?: PrivateNetworkPolicy;
|
|
45
36
|
}
|
|
46
|
-
export interface
|
|
37
|
+
export interface OpenAiImagesProviderOptions {
|
|
47
38
|
id: string;
|
|
48
39
|
label: string;
|
|
49
40
|
defaultModel: string;
|
|
@@ -54,24 +45,12 @@ export interface OpenAiCompatibleImageProviderOptions {
|
|
|
54
45
|
/** Resolve API key for one request. May return null when using OAuth/header auth. */
|
|
55
46
|
resolveApiKey: (req: ImageGenerationRequest) => string | null | undefined;
|
|
56
47
|
/** Resolve endpoint info per-request (region, baseUrl override, Azure deployment, …). */
|
|
57
|
-
resolveEndpoint: (req: ImageGenerationRequest) =>
|
|
48
|
+
resolveEndpoint: (req: ImageGenerationRequest) => OpenAiImagesEndpointResolution;
|
|
58
49
|
/** Default per-call timeout. Combined with provider-level config in provider-http. */
|
|
59
50
|
defaultTimeoutMs?: number;
|
|
60
|
-
/**
|
|
61
|
-
* Hook to enrich the JSON body sent to `/images/generations`. Mutate the
|
|
62
|
-
* returned object freely; the factory keeps standard fields stable.
|
|
63
|
-
*/
|
|
64
|
-
buildGenerateRequestBody?: (req: ImageGenerationRequest, base: Record<string, unknown>) => Record<string, unknown>;
|
|
65
|
-
/** Hook to enrich the multipart form for `/images/edits`. */
|
|
66
|
-
buildEditFormFields?: (req: ImageGenerationRequest, fields: Record<string, string>) => Record<string, string>;
|
|
67
|
-
/**
|
|
68
|
-
* Optional response mapper. Default: `mapDefaultOpenAiResponse`. Vendor
|
|
69
|
-
* implementations can override to handle non-standard response shapes.
|
|
70
|
-
*/
|
|
71
|
-
mapResponse?: (raw: unknown, req: ImageGenerationRequest) => GeneratedImageAsset[];
|
|
72
51
|
/** Default count clamp. Provider may also enforce via `capabilities.generate.maxCount`. */
|
|
73
52
|
defaultCount?: number;
|
|
74
53
|
defaultSize?: string;
|
|
75
54
|
}
|
|
76
|
-
export declare function
|
|
77
|
-
export declare function
|
|
55
|
+
export declare function createOpenAiImagesProvider(options: OpenAiImagesProviderOptions): ImageGenerationProvider;
|
|
56
|
+
export declare function mapOpenAiImagesResponse(raw: unknown, req: ImageGenerationRequest): GeneratedImageAsset[];
|
|
@@ -4,14 +4,17 @@ import { imageAssetFromBase64, imageFileExtensionForMimeType } from "./image-ass
|
|
|
4
4
|
import { pickTimeoutMsOrFallback } from "../../../media-shared/http/deadline.js";
|
|
5
5
|
import { privateNetworkPolicyToSsrfGuardOptions } from "../../../media-shared/http/private-network.js";
|
|
6
6
|
import { resolveProviderHttpRequestConfig } from "../../../media-shared/http/resolve-provider-http-request-config.js";
|
|
7
|
-
import { postJsonRequest, postMultipartRequest } from "../../../media-shared/http/provider-http.js";
|
|
7
|
+
import { postJsonRequest, postMultipartRequest, readJsonResponseLimited } from "../../../media-shared/http/provider-http.js";
|
|
8
8
|
import "../../../media-shared/http/index.js";
|
|
9
|
-
//#region src/agent/image/generation/openai-
|
|
9
|
+
//#region src/agent/image/generation/openai-images-provider.ts
|
|
10
10
|
init_logger();
|
|
11
|
-
const log = createLogger("ImageGen:
|
|
11
|
+
const log = createLogger("ImageGen:OpenAIImages");
|
|
12
12
|
const DEFAULT_OUTPUT_MIME = "image/png";
|
|
13
13
|
const DEFAULT_TIMEOUT_MS = 12e4;
|
|
14
|
-
|
|
14
|
+
const MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
15
|
+
const MAX_IMAGE_BYTES = 32 * 1024 * 1024;
|
|
16
|
+
const MAX_TOTAL_IMAGE_BYTES = 64 * 1024 * 1024;
|
|
17
|
+
function createOpenAiImagesProvider(options) {
|
|
15
18
|
const defaultMaxCount = options.capabilities.generate?.maxCount ?? 4;
|
|
16
19
|
const defaultCount = options.defaultCount ?? 1;
|
|
17
20
|
const defaultSize = options.defaultSize ?? "1024x1024";
|
|
@@ -39,6 +42,7 @@ function createOpenAiCompatibleImageProvider(options) {
|
|
|
39
42
|
endpoint,
|
|
40
43
|
apiKey
|
|
41
44
|
});
|
|
45
|
+
const ssrfGuardOptions = privateNetworkPolicyToSsrfGuardOptions(endpoint.privateNetworkPolicy);
|
|
42
46
|
const isEdit = (req.inputImages?.length ?? 0) > 0;
|
|
43
47
|
const count = clampCount(req.count, defaultCount, defaultMaxCount);
|
|
44
48
|
const size = req.size?.trim() || defaultSize;
|
|
@@ -49,8 +53,8 @@ function createOpenAiCompatibleImageProvider(options) {
|
|
|
49
53
|
phase: isEdit ? "edit_request" : "generate_request",
|
|
50
54
|
count,
|
|
51
55
|
size
|
|
52
|
-
}, `OpenAI
|
|
53
|
-
const
|
|
56
|
+
}, `OpenAI Images ${isEdit ? "edit" : "generate"} request`);
|
|
57
|
+
const mapped = mapOpenAiImagesResponse(isEdit ? await postEditRequest({
|
|
54
58
|
providerId: options.id,
|
|
55
59
|
req,
|
|
56
60
|
url,
|
|
@@ -58,7 +62,7 @@ function createOpenAiCompatibleImageProvider(options) {
|
|
|
58
62
|
count,
|
|
59
63
|
size,
|
|
60
64
|
httpDefaults,
|
|
61
|
-
|
|
65
|
+
ssrfGuardOptions
|
|
62
66
|
}) : await postGenerateRequest({
|
|
63
67
|
providerId: options.id,
|
|
64
68
|
req,
|
|
@@ -67,9 +71,8 @@ function createOpenAiCompatibleImageProvider(options) {
|
|
|
67
71
|
count,
|
|
68
72
|
size,
|
|
69
73
|
httpDefaults,
|
|
70
|
-
|
|
71
|
-
});
|
|
72
|
-
const mapped = (options.mapResponse ?? mapDefaultOpenAiResponse)(responseJson, req);
|
|
74
|
+
ssrfGuardOptions
|
|
75
|
+
}), req);
|
|
73
76
|
if (mapped.length === 0) throw new Error(`${options.id} returned no images.`);
|
|
74
77
|
return {
|
|
75
78
|
images: mapped,
|
|
@@ -79,24 +82,23 @@ function createOpenAiCompatibleImageProvider(options) {
|
|
|
79
82
|
};
|
|
80
83
|
}
|
|
81
84
|
async function postGenerateRequest(params) {
|
|
82
|
-
const
|
|
85
|
+
const body = {
|
|
83
86
|
model: params.req.model,
|
|
84
87
|
prompt: params.req.prompt,
|
|
85
88
|
n: params.count,
|
|
86
89
|
size: params.size,
|
|
87
90
|
response_format: "b64_json"
|
|
88
91
|
};
|
|
89
|
-
applyOpenAiOptions(
|
|
90
|
-
const body = params.buildGenerateRequestBody ? params.buildGenerateRequestBody(params.req, baseBody) : baseBody;
|
|
92
|
+
applyOpenAiOptions(body, params.req);
|
|
91
93
|
const timeoutMs = pickTimeoutMsOrFallback(params.req.timeoutMs, params.httpDefaults.timeoutMs, DEFAULT_TIMEOUT_MS);
|
|
92
|
-
return await (await postJsonRequest(params.url, {
|
|
94
|
+
return await readJsonResponseLimited(await postJsonRequest(params.url, {
|
|
93
95
|
label: params.providerId,
|
|
94
96
|
timeoutMs,
|
|
95
97
|
signal: params.req.signal,
|
|
96
98
|
body,
|
|
97
99
|
headers: params.headers,
|
|
98
|
-
...
|
|
99
|
-
}))
|
|
100
|
+
...params.ssrfGuardOptions
|
|
101
|
+
}), MAX_RESPONSE_BYTES);
|
|
100
102
|
}
|
|
101
103
|
async function postEditRequest(params) {
|
|
102
104
|
const inputImages = params.req.inputImages ?? [];
|
|
@@ -107,9 +109,8 @@ async function postEditRequest(params) {
|
|
|
107
109
|
size: params.size,
|
|
108
110
|
response_format: "b64_json"
|
|
109
111
|
};
|
|
110
|
-
const merged = params.buildEditFormFields ? params.buildEditFormFields(params.req, fields) : fields;
|
|
111
112
|
const form = new FormData();
|
|
112
|
-
for (const [k, v] of Object.entries(
|
|
113
|
+
for (const [k, v] of Object.entries(fields)) if (typeof v === "string") form.append(k, v);
|
|
113
114
|
for (let idx = 0; idx < inputImages.length; idx++) {
|
|
114
115
|
const img = inputImages[idx];
|
|
115
116
|
const field = idx === 0 ? "image" : `image[${idx}]`;
|
|
@@ -121,24 +122,29 @@ async function postEditRequest(params) {
|
|
|
121
122
|
const headers = new Headers(params.headers);
|
|
122
123
|
headers.delete("content-type");
|
|
123
124
|
const timeoutMs = pickTimeoutMsOrFallback(params.req.timeoutMs, params.httpDefaults.timeoutMs, DEFAULT_TIMEOUT_MS);
|
|
124
|
-
return await (await postMultipartRequest(params.url, {
|
|
125
|
+
return await readJsonResponseLimited(await postMultipartRequest(params.url, {
|
|
125
126
|
label: params.providerId,
|
|
126
127
|
timeoutMs,
|
|
127
128
|
signal: params.req.signal,
|
|
128
129
|
body: form,
|
|
129
130
|
headers,
|
|
130
|
-
...
|
|
131
|
-
}))
|
|
131
|
+
...params.ssrfGuardOptions
|
|
132
|
+
}), MAX_RESPONSE_BYTES);
|
|
132
133
|
}
|
|
133
|
-
function
|
|
134
|
+
function mapOpenAiImagesResponse(raw, req) {
|
|
134
135
|
if (!raw || typeof raw !== "object") return [];
|
|
135
136
|
const data = raw.data;
|
|
136
137
|
if (!Array.isArray(data)) return [];
|
|
137
138
|
const ext = imageFileExtensionForMimeType(req.outputFormat ? `image/${req.outputFormat}` : DEFAULT_OUTPUT_MIME);
|
|
138
139
|
const out = [];
|
|
140
|
+
let totalBytes = 0;
|
|
139
141
|
data.forEach((entry, index) => {
|
|
140
142
|
const b64 = entry?.b64_json;
|
|
141
143
|
if (typeof b64 !== "string" || b64.length === 0) return;
|
|
144
|
+
const estimatedBytes = Math.ceil(b64.replace(/\s+/g, "").length * 3 / 4);
|
|
145
|
+
if (estimatedBytes > MAX_IMAGE_BYTES) throw new Error(`OpenAI Images response image exceeds ${MAX_IMAGE_BYTES} bytes.`);
|
|
146
|
+
totalBytes += estimatedBytes;
|
|
147
|
+
if (totalBytes > MAX_TOTAL_IMAGE_BYTES) throw new Error(`OpenAI Images response images exceed ${MAX_TOTAL_IMAGE_BYTES} bytes total.`);
|
|
142
148
|
const asset = imageAssetFromBase64({
|
|
143
149
|
base64: b64,
|
|
144
150
|
mimeType: req.outputFormat ? `image/${req.outputFormat}` : void 0,
|
|
@@ -155,12 +161,19 @@ function applyAuthorizationHeader(params) {
|
|
|
155
161
|
const auth = params.endpoint.authorization ?? { kind: "bearer" };
|
|
156
162
|
if (auth.kind === "none") return;
|
|
157
163
|
if (!params.apiKey) return;
|
|
164
|
+
assertHttpHeaderValue(params.apiKey, "API key");
|
|
158
165
|
if (auth.kind === "bearer") {
|
|
159
166
|
params.headers["authorization"] = `Bearer ${params.apiKey}`;
|
|
160
167
|
return;
|
|
161
168
|
}
|
|
162
169
|
if (auth.kind === "header") params.headers[auth.headerName.toLowerCase()] = params.apiKey;
|
|
163
170
|
}
|
|
171
|
+
function assertHttpHeaderValue(value, label) {
|
|
172
|
+
for (let index = 0; index < value.length; index++) {
|
|
173
|
+
const code = value.charCodeAt(index);
|
|
174
|
+
if (code > 255 || code < 32 && code !== 9 || code === 127) throw new Error(`${label} contains characters that cannot be sent in an HTTP header.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
164
177
|
function applyOpenAiOptions(body, req) {
|
|
165
178
|
if (req.quality) body.quality = req.quality;
|
|
166
179
|
if (req.outputFormat) body.output_format = req.outputFormat;
|
|
@@ -186,4 +199,4 @@ function joinPath(baseUrl, subPath) {
|
|
|
186
199
|
return `${baseUrl.replace(/\/+$/, "")}${subPath.startsWith("/") ? subPath : `/${subPath}`}`;
|
|
187
200
|
}
|
|
188
201
|
//#endregion
|
|
189
|
-
export {
|
|
202
|
+
export { createOpenAiImagesProvider, mapOpenAiImagesResponse };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { ModelsJsonConfig } from '../../../config/models-json.js';
|
|
1
2
|
import type { ImageGenerationProvider, ImageGenerationProviderSummary } from './types.js';
|
|
3
|
+
export declare function reloadImageGenerationProviders(config?: ModelsJsonConfig): void;
|
|
2
4
|
export declare function getImageGenerationProvider(providerId: string): ImageGenerationProvider | undefined;
|
|
3
5
|
export declare function listImageGenerationProviders(): ImageGenerationProvider[];
|
|
4
6
|
export declare function listImageGenerationProvidersSummary(): ImageGenerationProviderSummary[];
|
|
@@ -1,31 +1,77 @@
|
|
|
1
|
+
import { createLogger } from "../../../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../../../utils/logger.js";
|
|
3
|
+
import { init_models_json, loadModelsJson } from "../../../config/models-json.js";
|
|
4
|
+
import { buildCustomOpenAiImagesProvider } from "./custom-openai-images-provider.js";
|
|
1
5
|
import { buildDashScopeImageGenerationProvider } from "./providers/dashscope.js";
|
|
2
6
|
import { buildFalImageGenerationProvider } from "./providers/fal.js";
|
|
3
7
|
import { buildGoogleImageGenerationProvider } from "./providers/google.js";
|
|
4
8
|
import { buildMinimaxImageGenerationProvider } from "./providers/minimax.js";
|
|
5
9
|
import { buildOpenAIImageGenerationProvider } from "./providers/openai.js";
|
|
6
10
|
//#region src/agent/image/generation/provider-registry.ts
|
|
7
|
-
|
|
11
|
+
init_models_json();
|
|
12
|
+
init_logger();
|
|
13
|
+
const log = createLogger("ImageGenerationRegistry");
|
|
14
|
+
const builtInProviders = [
|
|
8
15
|
buildOpenAIImageGenerationProvider(),
|
|
9
16
|
buildDashScopeImageGenerationProvider(),
|
|
10
17
|
buildMinimaxImageGenerationProvider(),
|
|
11
18
|
buildGoogleImageGenerationProvider(),
|
|
12
19
|
buildFalImageGenerationProvider()
|
|
13
20
|
];
|
|
14
|
-
|
|
21
|
+
let providers = [...builtInProviders];
|
|
22
|
+
let providersById = new Map(providers.map((provider) => [provider.id, provider]));
|
|
23
|
+
let customProvidersLoaded = false;
|
|
24
|
+
function ensureCustomProvidersLoaded() {
|
|
25
|
+
if (!customProvidersLoaded) reloadImageGenerationProviders();
|
|
26
|
+
}
|
|
27
|
+
function reloadImageGenerationProviders(config) {
|
|
28
|
+
let resolved = config;
|
|
29
|
+
if (!resolved) {
|
|
30
|
+
const loaded = loadModelsJson();
|
|
31
|
+
if (loaded.error) {
|
|
32
|
+
log.warn({ errorMessage: loaded.error }, `Custom image providers not loaded: ${loaded.error}`);
|
|
33
|
+
providers = [...builtInProviders];
|
|
34
|
+
providersById = new Map(providers.map((provider) => [provider.id, provider]));
|
|
35
|
+
customProvidersLoaded = true;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
resolved = loaded.config;
|
|
39
|
+
}
|
|
40
|
+
const builtInIds = new Set(builtInProviders.map((provider) => provider.id));
|
|
41
|
+
const customEntries = Object.entries(resolved.providers).filter((entry) => Boolean(entry[1].imageGeneration));
|
|
42
|
+
const conflictingId = customEntries.find(([providerId]) => builtInIds.has(providerId))?.[0];
|
|
43
|
+
if (conflictingId) throw new Error(`Custom image generation cannot override built-in provider "${conflictingId}".`);
|
|
44
|
+
const customProviders = customEntries.map(([providerId, provider]) => buildCustomOpenAiImagesProvider({
|
|
45
|
+
providerId,
|
|
46
|
+
provider,
|
|
47
|
+
imageGeneration: provider.imageGeneration
|
|
48
|
+
}));
|
|
49
|
+
providers = [...builtInProviders, ...customProviders];
|
|
50
|
+
providersById = new Map(providers.map((provider) => [provider.id, provider]));
|
|
51
|
+
customProvidersLoaded = true;
|
|
52
|
+
}
|
|
15
53
|
function getImageGenerationProvider(providerId) {
|
|
54
|
+
ensureCustomProvidersLoaded();
|
|
16
55
|
return providersById.get(providerId);
|
|
17
56
|
}
|
|
18
57
|
function listImageGenerationProviders() {
|
|
58
|
+
ensureCustomProvidersLoaded();
|
|
19
59
|
return [...providers];
|
|
20
60
|
}
|
|
21
61
|
function listImageGenerationProvidersSummary() {
|
|
62
|
+
ensureCustomProvidersLoaded();
|
|
22
63
|
return providers.map((provider) => ({
|
|
23
64
|
id: provider.id,
|
|
24
65
|
label: provider.label,
|
|
66
|
+
source: provider.source ?? "builtin",
|
|
67
|
+
credentialMode: provider.credentialMode ?? "api-key",
|
|
68
|
+
...provider.documentationUrl ? { documentationUrl: provider.documentationUrl } : {},
|
|
69
|
+
...provider.apiKeyUrl ? { apiKeyUrl: provider.apiKeyUrl } : {},
|
|
25
70
|
defaultModel: provider.defaultModel,
|
|
26
71
|
models: [...provider.models],
|
|
27
|
-
capabilities: provider.capabilities
|
|
72
|
+
capabilities: provider.capabilities,
|
|
73
|
+
...provider.modelCapabilities ? { modelCapabilities: structuredClone(provider.modelCapabilities) } : {}
|
|
28
74
|
}));
|
|
29
75
|
}
|
|
30
76
|
//#endregion
|
|
31
|
-
export { getImageGenerationProvider, listImageGenerationProviders, listImageGenerationProvidersSummary };
|
|
77
|
+
export { getImageGenerationProvider, listImageGenerationProviders, listImageGenerationProvidersSummary, reloadImageGenerationProviders };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveAuthProfileForProvider } from "../../../../providers/auth-runtime/resolve-auth.js";
|
|
2
2
|
import { isProviderApiKeyConfigured } from "../../../../providers/auth-runtime/is-configured.js";
|
|
3
3
|
import "../../../../providers/auth-runtime/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createOpenAiImagesProvider } from "../openai-images-provider.js";
|
|
5
5
|
//#region src/agent/image/generation/providers/openai.ts
|
|
6
6
|
/**
|
|
7
7
|
* Built-in OpenAI image-generation provider.
|
|
@@ -93,7 +93,7 @@ function buildAzureEndpoint(azure) {
|
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
95
|
function buildOpenAIImageGenerationProvider() {
|
|
96
|
-
return
|
|
96
|
+
return createOpenAiImagesProvider({
|
|
97
97
|
id: "openai",
|
|
98
98
|
label: "OpenAI",
|
|
99
99
|
defaultModel: OPENAI_DEFAULT_IMAGE_MODEL,
|
|
@@ -51,8 +51,12 @@ async function generateImage(params, deps = {}) {
|
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
53
|
try {
|
|
54
|
+
const modelCapabilities = provider.modelCapabilities?.[candidate.model];
|
|
54
55
|
const sanitized = resolveImageGenerationOverrides({
|
|
55
|
-
provider
|
|
56
|
+
provider: modelCapabilities ? {
|
|
57
|
+
...provider,
|
|
58
|
+
capabilities: modelCapabilities
|
|
59
|
+
} : provider,
|
|
56
60
|
size: params.size,
|
|
57
61
|
aspectRatio: params.aspectRatio,
|
|
58
62
|
resolution: params.resolution,
|
|
@@ -51,18 +51,29 @@ export interface ImageGenerationProviderConfiguredContext {
|
|
|
51
51
|
export interface ImageGenerationProvider {
|
|
52
52
|
id: string;
|
|
53
53
|
label: string;
|
|
54
|
+
source?: 'builtin' | 'custom';
|
|
55
|
+
credentialMode?: 'api-key' | 'none';
|
|
56
|
+
documentationUrl?: string;
|
|
57
|
+
apiKeyUrl?: string;
|
|
54
58
|
defaultModel: string;
|
|
55
59
|
models: string[];
|
|
56
60
|
capabilities: ImageGenerationProviderCapabilities;
|
|
61
|
+
/** Per-model capability declarations when models on one endpoint differ. */
|
|
62
|
+
modelCapabilities?: Record<string, ImageGenerationProviderCapabilities>;
|
|
57
63
|
isConfigured(ctx: ImageGenerationProviderConfiguredContext): boolean;
|
|
58
64
|
generateImage(req: ImageGenerationRequest): Promise<ImageGenerationResult>;
|
|
59
65
|
}
|
|
60
66
|
export interface ImageGenerationProviderSummary {
|
|
61
67
|
id: string;
|
|
62
68
|
label: string;
|
|
69
|
+
source: 'builtin' | 'custom';
|
|
70
|
+
credentialMode: 'api-key' | 'none';
|
|
71
|
+
documentationUrl?: string;
|
|
72
|
+
apiKeyUrl?: string;
|
|
63
73
|
defaultModel: string;
|
|
64
74
|
models: string[];
|
|
65
75
|
capabilities: ImageGenerationProviderCapabilities;
|
|
76
|
+
modelCapabilities?: Record<string, ImageGenerationProviderCapabilities>;
|
|
66
77
|
}
|
|
67
78
|
export interface ImageGenerationOpenAIOptions {
|
|
68
79
|
background?: ImageGenerationBackground;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { buildChannelCatalogForConfig, init_channel_catalog_service } from "../../channels/catalog/channel-catalog-service.js";
|
|
2
2
|
import { init_loader, loadConfig, saveConfig } from "../../config/loader.js";
|
|
3
|
-
import { formatExamples, register } from "../registry.js";
|
|
4
3
|
import { listChannelPairingState } from "../../channels/pairing/pairing-service.js";
|
|
5
4
|
import { approveChannelPairingFromCli } from "../../channels/pairing/approve-cli.js";
|
|
5
|
+
import { formatExamples, register } from "../registry.js";
|
|
6
6
|
import { colors } from "../utils/colors.js";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
//#region src/cli/commands/channels.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { init_loader, loadConfig, saveConfig } from "../../config/loader.js";
|
|
2
2
|
import { getStoreConnectorInstallPlan, installStoreConnector, listStoreConnectors } from "../../capabilities/store-connector.js";
|
|
3
3
|
import { register } from "../registry.js";
|
|
4
|
-
import { Command } from "commander";
|
|
5
4
|
import { confirm } from "@inquirer/prompts";
|
|
5
|
+
import { Command } from "commander";
|
|
6
6
|
//#region src/cli/commands/connectors.ts
|
|
7
7
|
init_loader();
|
|
8
8
|
function parsePairs(values, option) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { resolveModelsJsonPath } from "../../../../config/paths.js";
|
|
2
|
+
import { init_models_json, loadModelsJson } from "../../../../config/models-json.js";
|
|
3
|
+
import { CredentialResolver, init_credentials } from "../../../../auth/credentials.js";
|
|
4
|
+
import { classifyHost } from "../../../../media-shared/http/private-network.js";
|
|
5
|
+
import "../../../../media-shared/http/index.js";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
//#region src/cli/commands/doctor/checks/image-providers.ts
|
|
8
|
+
init_credentials();
|
|
9
|
+
init_models_json();
|
|
10
|
+
async function checkImageProviders(_ctx) {
|
|
11
|
+
const path = resolveModelsJsonPath();
|
|
12
|
+
if (!existsSync(path)) return {
|
|
13
|
+
id: "image-providers",
|
|
14
|
+
label: "Image providers",
|
|
15
|
+
status: "skip",
|
|
16
|
+
message: "No models.json file; custom image providers are not configured.",
|
|
17
|
+
hints: []
|
|
18
|
+
};
|
|
19
|
+
const loaded = loadModelsJson(path);
|
|
20
|
+
if (loaded.error) return {
|
|
21
|
+
id: "image-providers",
|
|
22
|
+
label: "Image providers",
|
|
23
|
+
status: "fail",
|
|
24
|
+
message: "models.json image provider configuration is invalid.",
|
|
25
|
+
hints: [loaded.error, path]
|
|
26
|
+
};
|
|
27
|
+
const entries = Object.entries(loaded.config.providers).filter((entry) => Boolean(entry[1].imageGeneration));
|
|
28
|
+
if (entries.length === 0) return {
|
|
29
|
+
id: "image-providers",
|
|
30
|
+
label: "Image providers",
|
|
31
|
+
status: "skip",
|
|
32
|
+
message: "No custom image providers are configured.",
|
|
33
|
+
hints: [path]
|
|
34
|
+
};
|
|
35
|
+
const blockedEndpoints = [];
|
|
36
|
+
const missingCredentials = [];
|
|
37
|
+
const resolver = new CredentialResolver();
|
|
38
|
+
for (const [providerId, provider] of entries) {
|
|
39
|
+
const hostname = new URL(provider.baseUrl).hostname.toLowerCase();
|
|
40
|
+
const hostClass = classifyHost(hostname);
|
|
41
|
+
const allowedHosts = (provider.imageGeneration.network?.allowedHosts ?? []).map((entry) => entry.toLowerCase());
|
|
42
|
+
if (hostClass !== "public" && !allowedHosts.includes(hostname)) blockedEndpoints.push(`${providerId}: ${hostname}`);
|
|
43
|
+
if (provider.imageGeneration.auth.type !== "none" && !await resolver.resolveApiKey(providerId)) missingCredentials.push(providerId);
|
|
44
|
+
}
|
|
45
|
+
if (blockedEndpoints.length > 0) return {
|
|
46
|
+
id: "image-providers",
|
|
47
|
+
label: "Image providers",
|
|
48
|
+
status: "fail",
|
|
49
|
+
message: `${blockedEndpoints.length} custom image provider endpoint(s) are blocked by private-network policy.`,
|
|
50
|
+
hints: [...blockedEndpoints, "Add each exact private hostname to imageGeneration.network.allowedHosts only when you trust it."]
|
|
51
|
+
};
|
|
52
|
+
if (missingCredentials.length > 0) return {
|
|
53
|
+
id: "image-providers",
|
|
54
|
+
label: "Image providers",
|
|
55
|
+
status: "warn",
|
|
56
|
+
message: `${missingCredentials.length} custom image provider(s) need an API key.`,
|
|
57
|
+
hints: missingCredentials.map((providerId) => `Configure a credential for ${providerId}.`)
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
id: "image-providers",
|
|
61
|
+
label: "Image providers",
|
|
62
|
+
status: "pass",
|
|
63
|
+
message: `${entries.length} custom image provider(s) validate and are ready.`,
|
|
64
|
+
hints: [path]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { checkImageProviders };
|
|
@@ -14,6 +14,7 @@ import { checkWorkspaceStatus } from "./checks/workspace-status.js";
|
|
|
14
14
|
import { checkVersionUpdate } from "./checks/version-check.js";
|
|
15
15
|
import { checkChannelPlugins } from "./checks/channel-plugins.js";
|
|
16
16
|
import { checkMigrations } from "./checks/migrations.js";
|
|
17
|
+
import { checkImageProviders } from "./checks/image-providers.js";
|
|
17
18
|
//#region src/cli/commands/doctor/flow.ts
|
|
18
19
|
const DOCTOR_CHECKS = [
|
|
19
20
|
checkVersionUpdate,
|
|
@@ -23,6 +24,7 @@ const DOCTOR_CHECKS = [
|
|
|
23
24
|
checkStateIntegrity,
|
|
24
25
|
checkDatabaseSchema,
|
|
25
26
|
checkProviderAuth,
|
|
27
|
+
checkImageProviders,
|
|
26
28
|
checkChannelConfig,
|
|
27
29
|
checkChannelPairingPending,
|
|
28
30
|
checkSecurityAudit,
|
|
@@ -12,8 +12,8 @@ import { join, resolve } from "node:path";
|
|
|
12
12
|
import { existsSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
13
13
|
import { execSync } from "node:child_process";
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
|
-
import { Command } from "commander";
|
|
16
15
|
import { confirm } from "@inquirer/prompts";
|
|
16
|
+
import { Command } from "commander";
|
|
17
17
|
import semver from "semver";
|
|
18
18
|
//#region src/cli/commands/extension-marketplace.ts
|
|
19
19
|
init_loader();
|
|
@@ -8,8 +8,8 @@ import "../../config/index.js";
|
|
|
8
8
|
import { prepareUpdateGlobalDefaults } from "../../gateway/global-defaults-admin.js";
|
|
9
9
|
import { colors } from "../utils/colors.js";
|
|
10
10
|
import { runCliOAuthLogin } from "../utils/oauth-login.js";
|
|
11
|
-
import { Command } from "commander";
|
|
12
11
|
import { input } from "@inquirer/prompts";
|
|
12
|
+
import { Command } from "commander";
|
|
13
13
|
//#region src/cli/commands/models.ts
|
|
14
14
|
init_credentials();
|
|
15
15
|
init_schema();
|