@mokuyoaxis/dsh-iris 0.2.6 → 0.3.0-alpha.1
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/CHANGELOG.md +103 -1
- package/README.en.md +104 -304
- package/README.md +101 -302
- package/bin/dsh-iris.js +446 -64
- package/docs/AGENT_CORE_QUICKSTART.md +64 -0
- package/docs/AGENT_QUICKSTART.md +7 -5
- package/docs/ARCHITECTURE.md +20 -4
- package/docs/AUDIO_FLOWS.md +71 -0
- package/docs/CLI_MANAGEMENT.md +1 -1
- package/docs/CONTACT_SHEET.md +62 -0
- package/docs/CORE_CONNECTIONS.md +99 -0
- package/docs/CORE_QUICKSTART.md +108 -0
- package/docs/CORE_RUNTIME_CONTRACT.md +41 -5
- package/docs/CORE_SERVICE.md +97 -0
- package/docs/CORE_SKILLS.md +94 -0
- package/docs/DSH_COMPATIBILITY.md +14 -4
- package/docs/DSH_CORE_MIGRATION.md +4 -2
- package/docs/DSH_PROVIDER_CONNECTIONS.md +1 -1
- package/docs/DSH_RC2_ADAPTATION.md +1 -1
- package/docs/DSH_RUNTIME.md +40 -0
- package/docs/FUNCTION_MODEL_POOLS.md +1 -1
- package/docs/HEADLESS_CLI.md +12 -4
- package/docs/HOST_DOCTOR.md +13 -1
- package/docs/IMAGE_FLOW.md +75 -0
- package/docs/IMAGE_GENERATION.md +127 -0
- package/docs/LOCAL_API.md +62 -0
- package/docs/LOCAL_API_CONTRACT.md +140 -0
- package/docs/LOCAL_CREATION.md +56 -0
- package/docs/LOCAL_OPERATIONS.md +46 -0
- package/docs/LOCAL_SELECTION.md +57 -0
- package/docs/LOCAL_WEB.md +201 -0
- package/docs/MCP.md +191 -0
- package/docs/MUSIC.md +2 -0
- package/docs/PANEL_EXTENSIONS.md +64 -0
- package/docs/PLUGINS.md +189 -0
- package/docs/PLUGIN_STARTUP.md +100 -0
- package/docs/PROVIDER_ADAPTER_CONTRACT.md +1 -1
- package/docs/PROVIDER_EXTENSIONS.md +78 -0
- package/docs/PROVIDER_HEALTH.md +4 -2
- package/docs/PROVIDER_SETTINGS.md +85 -0
- package/docs/PROVIDER_SUBMISSION_CONTRACT.md +6 -0
- package/docs/ROADMAP.md +28 -6
- package/docs/SECURITY.md +9 -1
- package/docs/SYNC_DELIVERY_RECOVERY.md +2 -0
- package/docs/VISION_CLI.md +2 -2
- package/docs/WEB_PROTOTYPE.md +62 -0
- package/docs/WEB_RECOVERY.md +46 -0
- package/docs/WORKBENCH_LOGIN.md +63 -0
- package/docs/assets/screenshots/core-connections-preview.webp +0 -0
- package/docs/assets/screenshots/core-model-pools-preview.webp +0 -0
- package/docs/assets/screenshots/core-overview-preview.webp +0 -0
- package/docs/assets/screenshots/core-spectrum-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-dark-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-mobile-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-preview.webp +0 -0
- package/docs/releases/0.2.7.md +62 -0
- package/docs/releases/0.3.0-alpha.1.md +51 -0
- package/docs/screenshots.md +41 -1
- package/lib/actions.js +132 -70
- package/lib/adapters.js +20 -11
- package/lib/api.js +38 -38
- package/lib/application-runtime.js +213 -0
- package/lib/audio-flow-input.js +37 -0
- package/lib/client.js +434 -55
- package/lib/comfyui-adapter.js +94 -0
- package/lib/comfyui-client.js +51 -0
- package/lib/comfyui-inspection.js +73 -0
- package/lib/comfyui-workflow.js +94 -0
- package/lib/config.js +36 -1
- package/lib/configured-image-runtime.js +50 -0
- package/lib/connection-skill.js +89 -0
- package/lib/connection-template.js +23 -0
- package/lib/core-artifact-store.js +21 -2
- package/lib/core-bridge-selection.js +31 -0
- package/lib/core-bridge-tools.js +83 -0
- package/lib/core-capability-catalog.js +182 -0
- package/lib/core-connection-diagnostics.js +49 -0
- package/lib/core-connection-store.js +277 -0
- package/lib/core-connections.js +225 -0
- package/lib/core-maintenance.js +31 -2
- package/lib/core-runtime.js +20 -0
- package/lib/core-service-client.js +170 -0
- package/lib/core-tasks.js +84 -13
- package/lib/core-writer-lease.js +16 -0
- package/lib/creation-request-store.js +133 -0
- package/lib/doctor.js +12 -1
- package/lib/dsh-core-adapter.js +666 -572
- package/lib/dsh-host-adapter.js +8 -2
- package/lib/dsh-service-bridge.js +66 -0
- package/lib/execution-ownership.js +43 -0
- package/lib/flow-store.js +290 -0
- package/lib/generation-input.js +10 -1
- package/lib/headless-vision.js +7 -7
- package/lib/image-edit-input.js +1 -1
- package/lib/image-flow.js +232 -0
- package/lib/index.js +94 -68
- package/lib/local-api-projection.js +32 -0
- package/lib/local-api-token-directory.js +31 -0
- package/lib/local-api.js +1083 -0
- package/lib/local-creation.js +150 -0
- package/lib/local-operations.js +36 -0
- package/lib/local-request-root.js +86 -0
- package/lib/local-web-assets.js +37 -0
- package/lib/mcp-bridge.js +58 -0
- package/lib/mcp-check.js +26 -0
- package/lib/mcp-config.js +42 -0
- package/lib/mcp-delivery.js +46 -0
- package/lib/mcp-http.js +101 -0
- package/lib/mcp-server.js +47 -0
- package/lib/model-directory-service.js +16 -6
- package/lib/model-directory.js +3 -3
- package/lib/models.js +2 -2
- package/lib/music-transport.js +2 -1
- package/lib/operation-runner.js +236 -0
- package/lib/operation-store.js +216 -0
- package/lib/panel-extension.js +36 -0
- package/lib/plugin-contract.js +194 -0
- package/lib/plugin-execution.js +51 -0
- package/lib/plugin-launch.js +86 -0
- package/lib/plugin-operation-store.js +101 -0
- package/lib/plugin-operations.js +28 -0
- package/lib/plugin-registry.js +109 -0
- package/lib/plugin-selection.js +15 -0
- package/lib/plugin-source-image.js +5 -0
- package/lib/plugin-web-assets.js +54 -0
- package/lib/plugins.d.ts +288 -0
- package/lib/plugins.js +58 -0
- package/lib/private-storage.js +12 -0
- package/lib/provider-adapter.js +5 -1
- package/lib/provider-adapters.js +56 -18
- package/lib/provider-catalog.js +40 -29
- package/lib/provider-config-service.js +105 -23
- package/lib/provider-diagnostics.js +56 -0
- package/lib/provider-extensions.js +29 -0
- package/lib/provider-health.js +8 -1
- package/lib/provider-monitor.js +79 -0
- package/lib/provider-protocol.js +70 -20
- package/lib/provider-registry.js +118 -0
- package/lib/provider-response.js +28 -0
- package/lib/provider-settings.js +134 -0
- package/lib/provider-task-runner.js +40 -7
- package/lib/render-address.js +35 -0
- package/lib/render.js +52 -13
- package/lib/skill-installation.js +211 -0
- package/lib/vision-model-routing.js +3 -0
- package/lib/web/app.js +642 -0
- package/lib/web/appearance.js +111 -0
- package/lib/web/comfyui.js +146 -0
- package/lib/web/connections.js +377 -0
- package/lib/web/creation.js +355 -0
- package/lib/web/flow-view.js +33 -0
- package/lib/web/gallery.js +132 -0
- package/lib/web/index.html +291 -0
- package/lib/web/media-client.js +204 -0
- package/lib/web/plugin-panel.js +221 -0
- package/lib/web/plugin-surface.css +17 -0
- package/lib/web/providers.js +492 -0
- package/lib/web/recovery.js +105 -0
- package/lib/web/startup.js +18 -0
- package/lib/web/styles.css +518 -0
- package/lib/web/transport.js +96 -0
- package/lib/web/workbench-login.js +165 -0
- package/lib/workbench-artifacts.js +1 -2
- package/lib/workbench-auth-cli.js +47 -0
- package/lib/workbench-auth.js +200 -0
- package/lib/workbench-management.js +8 -9
- package/package.json +22 -6
- package/plugins/contact-sheet/index.d.mts +4 -0
- package/plugins/contact-sheet/index.mjs +33 -0
- package/plugins/contact-sheet/web/board.css +14 -0
- package/plugins/contact-sheet/web/board.mjs +93 -0
- package/skills/iris-core/SKILL.md +44 -0
- package/skills/iris-core/agents/openai.yaml +4 -0
- package/skills/iris-core/references/cli.md +95 -0
- package/skills/iris-core/references/comfyui.md +69 -0
- package/skills/iris-core/references/tasks.md +60 -0
- package/user_guide.md +19 -9
package/lib/client.js
CHANGED
|
@@ -21,7 +21,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
21
21
|
var exports = module.exports;
|
|
22
22
|
|
|
23
23
|
var React = require('react');
|
|
24
|
-
var IRIS_CLIENT_VERSION = '0.
|
|
24
|
+
var IRIS_CLIENT_VERSION = '0.3.0-alpha.1';
|
|
25
25
|
var HOST_CLIENT_PROTOCOL_VERSION = 0;
|
|
26
26
|
var reportedSeats = [];
|
|
27
27
|
|
|
@@ -109,6 +109,27 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
109
109
|
'.iris-workbench-page :is(input, textarea, select) { font-family: inherit; accent-color: var(--iris-workbench-accent); }',
|
|
110
110
|
'.iris-workbench-page input:not([type=checkbox]):not([type=radio]):not([type=file]), .iris-workbench-page textarea, .iris-workbench-page select { min-width: 0; max-width: 100%; min-height: 34px; padding: 5px 8px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); font-size: 12px; }',
|
|
111
111
|
'.iris-workbench-page .iris-pm-note { line-height: 1.65; }',
|
|
112
|
+
'.iris-comfy-panel { position: relative; isolation: isolate; overflow: hidden; min-width: 0; display: grid; gap: 10px; padding: 12px; border: 1px solid color-mix(in srgb, #6F69B8 30%, var(--dsw-alias-border-l1, #3a3a44)); border-radius: 12px; background: linear-gradient(105deg, rgba(111,105,184,.07), rgba(229,155,118,.04)), var(--dsw-alias-bg-layer-1, #26262e); }',
|
|
113
|
+
'.iris-comfy-panel::before { content: ""; position: absolute; z-index: -1; pointer-events: none; inset: 0; background: linear-gradient(105deg, transparent 20%, #45317D18, #6F69B830, #BB78AD30, #E59B7630, #ED9C9120, #F0D09C30, transparent 80%); transform: translateX(110%); animation: iris-comfy-arrive .65s ease-out 1; }',
|
|
114
|
+
'@keyframes iris-comfy-arrive { from { transform: translateX(-110%); opacity: 0; } 25% { opacity: 1; } to { transform: translateX(110%); opacity: 0; } }',
|
|
115
|
+
'.iris-comfy-head { display: flex; gap: 8px; align-items: center; justify-content: space-between; flex-wrap: wrap; }',
|
|
116
|
+
'.iris-comfy-head strong { font-size: 13px; }',
|
|
117
|
+
'.iris-comfy-tag { border: 1px solid color-mix(in srgb, #BB78AD 30%, var(--dsw-alias-border-l1, #3a3a44)); border-radius: 20px; padding: 2px 8px; font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
118
|
+
'.iris-comfy-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }',
|
|
119
|
+
'.iris-comfy-panel label, .iris-comfy-grid label { display: grid; min-width: 0; gap: 5px; font-size: 12px; }',
|
|
120
|
+
'.iris-comfy-panel :is(input,select,textarea) { min-width: 0; max-width: 100%; width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; padding: 6px 8px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); font: inherit; }',
|
|
121
|
+
'.iris-comfy-panel input[type=checkbox] { width: auto; min-height: 0; }',
|
|
122
|
+
'.iris-comfy-panel summary { cursor: pointer; font-size: 12px; padding: 4px 0; }',
|
|
123
|
+
'.iris-comfy-panel details > div { margin-top: 8px; }',
|
|
124
|
+
'.iris-comfy-slots { display: grid; gap: 6px; max-height: 280px; overflow: auto; }',
|
|
125
|
+
'.iris-comfy-slot { display: flex; gap: 8px; align-items: center; min-width: 0; padding: 7px 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #3a3a44); }',
|
|
126
|
+
'.iris-comfy-slot label { flex: 1; display: flex; align-items: center; gap: 7px; overflow-wrap: anywhere; }',
|
|
127
|
+
'.iris-comfy-slot select { width: auto; max-width: 100px; }',
|
|
128
|
+
'.iris-comfy-result { font-size: 12px; line-height: 1.65; overflow-wrap: anywhere; }',
|
|
129
|
+
'.iris-comfy-result ul { padding-left: 20px; margin: 5px 0; }',
|
|
130
|
+
'.iris-comfy-panel .iris-act-run, .iris-comfy-card .iris-act-run { background: #6F69B8; color: #fff; border-color: transparent; }',
|
|
131
|
+
'@media (max-width: 640px) { .iris-comfy-grid { grid-template-columns: minmax(0, 1fr); } .iris-comfy-panel { padding: 10px; } }',
|
|
132
|
+
'@media (prefers-reduced-motion: reduce) { .iris-comfy-panel::before { animation: none; opacity: 0; } }',
|
|
112
133
|
'.iris-workbench-page .iris-pm-mrows { max-height: 400px; gap: 8px; }',
|
|
113
134
|
'.iris-workbench-page .iris-pm-mrow { min-width: 0; padding: 10px; gap: 6px; flex-wrap: wrap; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 10px; }',
|
|
114
135
|
'.iris-workbench-page .iris-pm-mname { flex-basis: 100%; white-space: normal; overflow-wrap: anywhere; font-size: 13px; }',
|
|
@@ -140,11 +161,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
140
161
|
'.iris-doctor-score { font-weight: 600; flex: 1; }',
|
|
141
162
|
'.iris-doctor-list { display: grid; gap: 4px; margin-top: 7px; }',
|
|
142
163
|
'.iris-doctor-row { display: grid; grid-template-columns: 14px 1fr; gap: 6px; font-size: 11px; }',
|
|
164
|
+
'.iris-doctor-row > span:last-child { min-width: 0; overflow-wrap: anywhere; }',
|
|
143
165
|
'.iris-doctor-row.ok { color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
144
166
|
'.iris-doctor-row.warn { color: var(--dsw-alias-state-warn-primary, #d9a941); }',
|
|
145
167
|
'.iris-doctor-row.error { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
146
168
|
'.iris-doctor-details { margin-top: 7px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
147
169
|
'.iris-doctor-details summary { cursor: pointer; font-size: 11px; }',
|
|
170
|
+
'.iris-browser-check { border-top: 1px solid var(--dsw-alias-border-l1, #ffffff12); margin-top: 10px; padding-top: 9px; min-width: 0; }',
|
|
171
|
+
'.iris-browser-check .iris-doctor-top { margin-bottom: 5px; }',
|
|
172
|
+
'.iris-browser-check img { display: block; width: 100%; max-height: 200px; object-fit: contain; margin-top: 7px; border-radius: 7px; background: var(--dsw-alias-bg-layer-2, #ffffff05); }',
|
|
173
|
+
'.iris-browser-check-note { font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }',
|
|
174
|
+
'.iris-browser-check-note.error { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
148
175
|
'.iris-core-runtime { cursor: default; padding: 0; overflow: hidden; }',
|
|
149
176
|
'.iris-core-runtime-head { display: flex; align-items: center; gap: 8px; padding: 9px 10px; }',
|
|
150
177
|
'.iris-core-runtime-title { min-width: 0; flex: 1; display: grid; gap: 1px; }',
|
|
@@ -177,7 +204,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
177
204
|
/* ---- 操作卡片组(阶段 5 GUI 直连) ---- */
|
|
178
205
|
'.iris-act-group { display: grid; gap: 8px; }',
|
|
179
206
|
'.iris-act-card { background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; overflow: hidden; }',
|
|
180
|
-
'.iris-act-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; cursor: pointer; user-select: none; }',
|
|
207
|
+
'.iris-act-head { display: flex; align-items: center; width:100%; gap: 8px; padding: 8px 10px; border:0; background:transparent; color:inherit; font:inherit; text-align:left; cursor: pointer; user-select: none; }',
|
|
181
208
|
'.iris-act-head:hover { background: var(--dsw-alias-bg-layer-2, #32323c); }',
|
|
182
209
|
'.iris-act-title { font-size: 13px; font-weight: 600; flex: 1; }',
|
|
183
210
|
'.iris-act-arrow { color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 10px; transition: transform 0.15s; }',
|
|
@@ -311,7 +338,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
311
338
|
'.iris-bubble-more { font-size: 11px; color: var(--dsw-alias-brand-primary, #7aa2f7); background: transparent; border: none; cursor: pointer; padding: 4px 2px; text-align: left; }',
|
|
312
339
|
/* 历史浏览器(设置页按日期分组折叠) */
|
|
313
340
|
'.iris-hist-group { margin-bottom: 4px; }',
|
|
314
|
-
'.iris-hist-head { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-secondary, #9a9a9a); cursor: pointer; padding: 4px 2px; }',
|
|
341
|
+
'.iris-hist-head { display: flex; align-items: center; width:100%; gap: 6px; border:0; background:transparent; text-align:left; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-secondary, #9a9a9a); cursor: pointer; padding: 4px 2px; }',
|
|
315
342
|
'.iris-hist-head:hover { color: var(--dsw-alias-label-primary, #e6e6e6); }',
|
|
316
343
|
/* 常用卡片选择器 + 清理区 */
|
|
317
344
|
'.iris-pick-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 10px; }',
|
|
@@ -390,7 +417,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
390
417
|
'.iris-ff-val { font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }',
|
|
391
418
|
'.iris-ff-val.set { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
392
419
|
'.iris-ff-list { display: grid; gap: 2px; max-height: 160px; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 6px; padding: 4px; background: var(--dsw-alias-bg-layer-2, #32323c); }',
|
|
393
|
-
'.iris-ff-item { display: flex; align-items: center; gap: 6px; font-size: 11px; cursor: pointer; padding: 2px 4px; border-radius: 4px; }',
|
|
420
|
+
'.iris-ff-item { display: flex; align-items: center; width:100%; gap: 6px; border:0; background:transparent; color:inherit; text-align:left; font-size: 11px; cursor: pointer; padding: 2px 4px; border-radius: 4px; }',
|
|
394
421
|
'.iris-ff-item:hover { background: var(--dsw-alias-bg-layer-1, #26262e); }',
|
|
395
422
|
'.iris-ff-item .iris-ff-src { color: var(--dsw-alias-brand-primary, #7aa2f7); font-size: 10px; }',
|
|
396
423
|
/* 对话框级提示词优化器:桌面浮层 + 窄屏底部 Sheet */
|
|
@@ -670,7 +697,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
670
697
|
: option.protocolSupported === false ? option.reason || '当前接口协议不支持此功能。'
|
|
671
698
|
: option.unavailable && !health.rateLimited && !health.modelUnavailable && !option.modelUnavailable ? option.reason || '连接尚未就绪,请先检查供应商设置。' : '';
|
|
672
699
|
return { ...option, providerName: provider ? provider.name : 'DSH 宿主', health: health,
|
|
673
|
-
taskOnly: row.taskOnly || host, reason: reason, canTest: !reason };
|
|
700
|
+
taskOnly: row.taskOnly || host || option.imageRouting && option.imageRouting.mediaProtocol === 'comfyui', reason: reason, canTest: !reason };
|
|
674
701
|
});
|
|
675
702
|
}
|
|
676
703
|
function CapabilityHealthOverview() {
|
|
@@ -739,7 +766,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
739
766
|
if (field) { revealWorkbenchItem(field); field.scrollIntoView({ block: 'center', behavior: 'smooth' }); field.focus({ preventScroll: true }); }
|
|
740
767
|
}
|
|
741
768
|
function openTask(row) {
|
|
742
|
-
var card = document.querySelector('[data-iris-action="' + row.action + '"]');
|
|
769
|
+
var card = document.querySelector('[data-iris-action="' + (row.cap === 'image-gen' ? 'image' : row.action) + '"]');
|
|
743
770
|
if (!card) return;
|
|
744
771
|
revealWorkbenchItem(card);
|
|
745
772
|
if (!card.classList.contains('open')) card.querySelector('.iris-act-head').click();
|
|
@@ -835,7 +862,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
835
862
|
React.createElement('button', { className: 'iris-health-btn' + (!option.taskOnly ? ' primary' : ''),
|
|
836
863
|
disabled: !fresh || !option.canTest || !!busy || !!(result && result.phase === 'unknown'),
|
|
837
864
|
onClick: function () { if (running.current) return; if (option.taskOnly) openTask(row); else confirmPair[1]({ cap: row.cap, ref: option.ref }); } },
|
|
838
|
-
option.taskOnly ? '前往功能验证 ↗' : result && result.phase === 'running' ? '正在实测…' : '实测此模型')),
|
|
865
|
+
option.taskOnly ? option.comfyWorkflow ? '前往画图选择工作流 ↗' : '前往功能验证 ↗' : result && result.phase === 'running' ? '正在实测…' : '实测此模型')),
|
|
839
866
|
option.reason ? React.createElement('p', { className: 'iris-health-caption' }, option.reason) : null,
|
|
840
867
|
option.ref === 'host:current-vision' ? React.createElement('p', { className: 'iris-health-caption' },
|
|
841
868
|
'宿主视觉通过真实图片任务验证,请在打开的表单中选择 DSH 当前默认模型。') : null,
|
|
@@ -1170,6 +1197,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1170
1197
|
React.createElement('option', { value: 'dashscope' }, 'DashScope'),
|
|
1171
1198
|
React.createElement('option', { value: 'openai-images' }, 'OpenAI Images'),
|
|
1172
1199
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
1200
|
+
p.mediaProtocol === 'comfyui' || m.imageProtocol === 'comfyui' ? React.createElement('option', { value: 'comfyui' }, 'ComfyUI · API 工作流') : null,
|
|
1173
1201
|
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')),
|
|
1174
1202
|
m.imageRouting && m.imageRouting.protocolInferred ? ' · 协议待确认' : null) : null,
|
|
1175
1203
|
!m.directoryOnly && caps.includes('music-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '音乐协议 ',
|
|
@@ -1184,9 +1212,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1184
1212
|
return React.createElement('div', { className: 'iris-pm-pool' },
|
|
1185
1213
|
React.createElement('div', { className: 'iris-pm-note' }, '模型池(' + models.filter(function (m) { return !m.directoryOnly; }).length + ')'
|
|
1186
1214
|
+ (models.some(function (m) { return m.directoryOnly; }) ? ' · 目录待确认 ' + models.filter(function (m) { return m.directoryOnly; }).length + ' 项' : '') + ':'),
|
|
1187
|
-
rows.length ? React.createElement('div', { className: 'iris-pm-mrows' }, rows) : React.createElement('div', { className: 'iris-pm-note' },
|
|
1188
|
-
|
|
1189
|
-
|
|
1215
|
+
rows.length ? React.createElement('div', { className: 'iris-pm-mrows' }, rows) : React.createElement('div', { className: 'iris-pm-note' }, p.mediaProtocol === 'comfyui'
|
|
1216
|
+
? '尚无工作流,请在上方导入 API 图并保存。' : '暂无模型 — 展开「模型目录」同步,或在下方手动添加'),
|
|
1217
|
+
p.mediaProtocol !== 'comfyui' ? React.createElement(ModelUsageForm, { provider: p, setupSuggested: props.setupSuggested,
|
|
1218
|
+
onNote: props.onNote, onDone: props.onDone }) : null);
|
|
1190
1219
|
}
|
|
1191
1220
|
|
|
1192
1221
|
function ModelUsageForm(props) {
|
|
@@ -1337,7 +1366,172 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1337
1366
|
React.createElement('div', { className: 'iris-wb-row' },
|
|
1338
1367
|
React.createElement('input', { value: value, placeholder: '留空则沿用 Base URL', onChange: function (e) { setValue(e.target.value); } }),
|
|
1339
1368
|
React.createElement('button', { className: 'iris-pm-btn', onClick: save }, '保存')),
|
|
1340
|
-
React.createElement('span', { className: 'iris-pm-note' },
|
|
1369
|
+
React.createElement('span', { className: 'iris-pm-note' }, p.mediaProtocol === 'comfyui' ? '留空沿用已保存地址;反向代理有独立媒体地址时才填写。'
|
|
1370
|
+
: '百炼 Workspace 可填 …maas.aliyuncs.com/compatible-mode/v1;视觉仍使用普通 Base URL。'));
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
function ProviderAuthenticationEditor(props) {
|
|
1374
|
+
var p = props.provider, authPair = React.useState(p.auth), keyPair = React.useState(''), notePair = React.useState('');
|
|
1375
|
+
var busyPair = React.useState(false), active = React.useRef(null), unknownPair = React.useState(false);
|
|
1376
|
+
React.useEffect(function () { authPair[1](p.auth); }, [p.auth]);
|
|
1377
|
+
React.useEffect(function () { return function () { if (active.current) active.current.abort(); }; }, []);
|
|
1378
|
+
async function save() {
|
|
1379
|
+
if (active.current || unknownPair[0]) return;
|
|
1380
|
+
var controller = new AbortController(), timer = setTimeout(function () { controller.abort(); }, 15000);
|
|
1381
|
+
active.current = controller; busyPair[1](true);
|
|
1382
|
+
try {
|
|
1383
|
+
var result = await postAction('providers_upsert', { id: p.id, auth: authPair[0], ...(authPair[0] === 'bearer' && keyPair[0].trim() ? { apiKey: keyPair[0].trim() } : {}) }, { signal: controller.signal });
|
|
1384
|
+
if (controller.signal.aborted) throw new Error('请求已结束');
|
|
1385
|
+
if (!result.ok) { notePair[1](result.d.error || '保存失败'); return; }
|
|
1386
|
+
keyPair[1](''); notePair[1]('认证设置已保存'); props.onDone();
|
|
1387
|
+
} catch (_) { unknownPair[1](true); notePair[1]('保存结果未知,请重新打开此供应商核对;未自动重试。'); }
|
|
1388
|
+
finally { clearTimeout(timer); active.current = null; busyPair[1](false); }
|
|
1389
|
+
}
|
|
1390
|
+
return React.createElement('details', {}, React.createElement('summary', {}, '认证设置'),
|
|
1391
|
+
React.createElement('fieldset', { disabled: busyPair[0] || unknownPair[0], style: { border: 0, padding: 0, margin: '8px 0', minWidth: 0 } },
|
|
1392
|
+
React.createElement('label', { className: 'iris-pm-field' }, '认证方式', React.createElement('select', { value: authPair[0], onChange: function (event) { authPair[1](event.target.value); } },
|
|
1393
|
+
React.createElement('option', { value: 'none' }, '无需认证'), React.createElement('option', { value: 'bearer' }, 'Bearer · API Key'))),
|
|
1394
|
+
authPair[0] === 'bearer' ? React.createElement('label', { className: 'iris-pm-field' }, '替换 API Key(留空保留)', React.createElement('input', { type: 'password', value: keyPair[0], onChange: function (event) { keyPair[1](event.target.value); } })) : null,
|
|
1395
|
+
React.createElement('p', { className: 'iris-pm-note' }, '无需认证时不会发送保留的 Key;更改认证会使旧的健康证据失效。'),
|
|
1396
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: save }, busyPair[0] ? '保存中…' : '保存认证')),
|
|
1397
|
+
notePair[0] ? React.createElement('div', { role: 'status', className: 'iris-pm-note' }, notePair[0]) : null);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
function ComfyCheckResult(props) {
|
|
1401
|
+
var report = props.report;
|
|
1402
|
+
if (!report) return null;
|
|
1403
|
+
return React.createElement('div', { className: 'iris-comfy-result', role: 'status' },
|
|
1404
|
+
React.createElement('strong', {}, report.ok ? '静态检查通过 · 未生成图片' : '需要修正 ' + report.issues.length + ' 项 · 未生成图片'),
|
|
1405
|
+
report.issues.length ? React.createElement('ul', {}, report.issues.map(function (issue, i) {
|
|
1406
|
+
return React.createElement('li', { key: i }, issue.message);
|
|
1407
|
+
})) : null,
|
|
1408
|
+
React.createElement('div', { className: 'iris-pm-note' }, '只读取节点定义;不证明 GPU、显存、模型家族或自定义节点能够实际运行。'));
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
function comfySlots(draft) {
|
|
1412
|
+
var result = [], labels = { lora_name: 'LoRA', strength_model: '模型权重', strength_clip: 'CLIP 权重',
|
|
1413
|
+
ckpt_name: 'Checkpoint', seed: '种子', steps: '步数', cfg: 'CFG', sampler_name: '采样器', scheduler: '调度器', text: '文本' };
|
|
1414
|
+
Object.entries(draft.graph).forEach(function (pair) {
|
|
1415
|
+
var id = pair[0], node = pair[1];
|
|
1416
|
+
Object.entries(node.inputs).forEach(function (field) {
|
|
1417
|
+
var input = field[0], value = field[1];
|
|
1418
|
+
if (!['string', 'boolean', 'number'].includes(typeof value) || id === draft.promptNode && input === 'text'
|
|
1419
|
+
|| id === draft.sizeNode && ['width', 'height'].includes(input)) return;
|
|
1420
|
+
var parameter = draft.parameters.find(function (item) { return item.node === id && item.input === input; });
|
|
1421
|
+
result.push({ key: id + '/' + input, value: value, selected: !!parameter, parameter: parameter || {
|
|
1422
|
+
id: ('n' + id.replace(/[^A-Za-z0-9_]/g, '_') + '_' + input.replace(/[^A-Za-z0-9_]/g, '_')).slice(0, 54) + '_' + result.length,
|
|
1423
|
+
label: (labels[input] || input) + ' · ' + node.class_type + ' #' + id, node: id, input: input,
|
|
1424
|
+
type: typeof value === 'number' ? Number.isSafeInteger(value) && !/^(strength_|cfg$|denoise$)/.test(input) ? 'integer' : 'number' : typeof value
|
|
1425
|
+
} });
|
|
1426
|
+
});
|
|
1427
|
+
});
|
|
1428
|
+
return result;
|
|
1429
|
+
}
|
|
1430
|
+
function comfyDraftWorkflow(draft) {
|
|
1431
|
+
if (!draft.promptNode || !draft.outputNode) throw new Error('请明确选择正向提示词与图片输出节点');
|
|
1432
|
+
var parameters = draft.parameters;
|
|
1433
|
+
return { version: parameters.length ? 1 : 0, graph: draft.graph, promptNode: draft.promptNode, outputNode: draft.outputNode,
|
|
1434
|
+
...(draft.sizeNode ? { sizeNode: draft.sizeNode } : {}), ...(parameters.length ? { parameters: parameters } : {}) };
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
function ComfyWorkflowEditor(props) {
|
|
1438
|
+
var p = props.provider, draftPair = React.useState(null), draft = draftPair[0], namePair = React.useState('');
|
|
1439
|
+
var autoPair = React.useState(props.setupSuggested === true), reportPair = React.useState(null), notePair = React.useState('');
|
|
1440
|
+
var busyPair = React.useState(''), unknownPair = React.useState(false), replacePair = React.useState(false);
|
|
1441
|
+
var active = React.useRef(null), importVersion = React.useRef(0), original = React.useRef({});
|
|
1442
|
+
React.useEffect(function () { return function () { importVersion.current++; if (active.current) active.current.abort(); }; }, []);
|
|
1443
|
+
function change(field, value) {
|
|
1444
|
+
var next = { ...draft, [field]: value };
|
|
1445
|
+
if (field === 'promptNode' || field === 'sizeNode') next.parameters = next.parameters.filter(function (item) {
|
|
1446
|
+
return !(item.node === next.promptNode && item.input === 'text' || item.node === next.sizeNode && ['width', 'height'].includes(item.input));
|
|
1447
|
+
});
|
|
1448
|
+
draftPair[1](next); reportPair[1](null); replacePair[1](false); notePair[1]('');
|
|
1449
|
+
}
|
|
1450
|
+
async function read(event) {
|
|
1451
|
+
var file = event.target.files[0], ticket = ++importVersion.current;
|
|
1452
|
+
if (!file) return;
|
|
1453
|
+
try {
|
|
1454
|
+
if (file.size > 40 * 1024) throw new Error('工作流最多 40 KiB');
|
|
1455
|
+
var raw = JSON.parse(await file.text()), template = raw && raw.graph && [0, 1].includes(raw.version) ? raw : null;
|
|
1456
|
+
if (ticket !== importVersion.current) return;
|
|
1457
|
+
var graph = template ? template.graph : raw, entries = graph && typeof graph === 'object' && !Array.isArray(graph) ? Object.entries(graph) : [];
|
|
1458
|
+
if (!entries.length || entries.length > 256 || entries.some(function (entry) { var node = entry[1]; return !node || typeof node.class_type !== 'string'
|
|
1459
|
+
|| !node.inputs || typeof node.inputs !== 'object' || Array.isArray(node.inputs); })) throw new Error('请使用 Export (API) 节点图或 Iris 参数模板,不支持画布 JSON');
|
|
1460
|
+
var outputs = entries.filter(function (entry) { return entry[1].class_type === 'SaveImage'; });
|
|
1461
|
+
if (template && (template.version === 0 && template.parameters !== undefined || template.version === 1 && (!Array.isArray(template.parameters) || !template.parameters.length))
|
|
1462
|
+
|| template && template.parameters !== undefined && (!Array.isArray(template.parameters) || template.parameters.some(function (item) {
|
|
1463
|
+
return !item || typeof item.id !== 'string' || typeof item.node !== 'string' || typeof item.input !== 'string' || !['string', 'integer', 'number', 'boolean'].includes(item.type);
|
|
1464
|
+
}))) throw new Error('参数声明格式不正确');
|
|
1465
|
+
draftPair[1]({ graph: graph, promptNode: template ? template.promptNode : '', outputNode: template ? template.outputNode : outputs.length === 1 ? outputs[0][0] : '',
|
|
1466
|
+
sizeNode: template && template.sizeNode || '', parameters: template && template.parameters || [] });
|
|
1467
|
+
original.current = Object.fromEntries((p.models || []).map(function (model) { return [model.id, model.comfyWorkflow && model.comfyWorkflow.sha256 || null]; }));
|
|
1468
|
+
namePair[1](file.name.replace(/\.json$/i, '').slice(0, 120)); reportPair[1](null); replacePair[1](false);
|
|
1469
|
+
notePair[1]('已读取 ' + entries.length + ' 个节点。请核对绑定,再勾选希望在画图时调整的参数。');
|
|
1470
|
+
} catch (error) { if (ticket === importVersion.current) notePair[1]('导入失败:' + error.message + ';原草稿保留。'); }
|
|
1471
|
+
}
|
|
1472
|
+
async function operate(kind, confirmed) {
|
|
1473
|
+
if (active.current || unknownPair[0]) return;
|
|
1474
|
+
var workflow, name = namePair[0].trim();
|
|
1475
|
+
try { workflow = comfyDraftWorkflow(draft); if (kind === 'save' && !name) throw new Error('请填写工作流名称'); }
|
|
1476
|
+
catch (error) { notePair[1](error.message); return; }
|
|
1477
|
+
var exists = Object.hasOwn(original.current, name);
|
|
1478
|
+
if (kind === 'save' && exists && !confirmed) { replacePair[1](true); return; }
|
|
1479
|
+
var controller = new AbortController(); active.current = controller; busyPair[1](kind); notePair[1]('');
|
|
1480
|
+
var timer = setTimeout(function () { controller.abort(); }, 20000);
|
|
1481
|
+
try {
|
|
1482
|
+
var result = await postAction(kind === 'check' ? 'providers_check_comfy_workflow' : 'providers_save_comfy_workflow', kind === 'check'
|
|
1483
|
+
? { id: p.id, workflow: workflow } : { id: p.id, model_id: name, workflow: workflow,
|
|
1484
|
+
expectedSha: exists ? original.current[name] : null, enableAutomatic: autoPair[0] }, { signal: controller.signal });
|
|
1485
|
+
if (controller.signal.aborted) throw new Error('请求已结束');
|
|
1486
|
+
if (!result.ok || result.d.ok !== true) { notePair[1](result.d.error || '操作未完成'); return; }
|
|
1487
|
+
if (kind === 'check') reportPair[1](result.d.report);
|
|
1488
|
+
else {
|
|
1489
|
+
original.current[name] = result.d.comfyWorkflow.sha256; replacePair[1](false); notePair[1](result.d.text);
|
|
1490
|
+
props.onDone(); window.dispatchEvent(new CustomEvent('iris-function-configuration-changed'));
|
|
1491
|
+
}
|
|
1492
|
+
} catch (_) {
|
|
1493
|
+
if (kind === 'save') { unknownPair[1](true); notePair[1]('保存结果未知,未自动重试。请关闭并重新打开此供应商,核对已保存工作流;草稿仍在此处。'); }
|
|
1494
|
+
else { reportPair[1](null); notePair[1]('检查未完成,请核对连接后重试;未发起生成。'); }
|
|
1495
|
+
} finally { clearTimeout(timer); active.current = null; busyPair[1](''); }
|
|
1496
|
+
}
|
|
1497
|
+
var entries = draft ? Object.entries(draft.graph) : [], slots = draft ? comfySlots(draft) : [];
|
|
1498
|
+
function select(field, label, options) {
|
|
1499
|
+
return React.createElement('label', {}, label, React.createElement('select', { value: draft[field], onChange: function (event) { change(field, event.target.value); } },
|
|
1500
|
+
React.createElement('option', { value: '' }, field === 'sizeNode' ? '沿用工作流尺寸' : '请选择'),
|
|
1501
|
+
options.map(function (entry) { return React.createElement('option', { key: entry[0], value: entry[0] }, '#' + entry[0] + ' · ' + entry[1].class_type); })));
|
|
1502
|
+
}
|
|
1503
|
+
return React.createElement('section', { className: 'iris-comfy-panel', 'aria-label': 'ComfyUI 工作流' },
|
|
1504
|
+
React.createElement('div', { className: 'iris-comfy-head' }, React.createElement('strong', {}, 'ComfyUI · 工作流'), React.createElement('span', { className: 'iris-comfy-tag' }, '已有服务 · 单张文生图')),
|
|
1505
|
+
React.createElement('p', { className: 'iris-pm-note' }, '导入 API 工作流,保留已有节点与 LoRA。检查和保存不会生成图片。'),
|
|
1506
|
+
React.createElement('fieldset', { disabled: !!busyPair[0] || unknownPair[0], style: { border: 0, padding: 0, margin: 0, minWidth: 0, display: 'grid', gap: 10 } },
|
|
1507
|
+
React.createElement('label', {}, '导入工作流 JSON', React.createElement('input', { type: 'file', accept: '.json,application/json', onChange: read, 'data-comfy-file': p.id })),
|
|
1508
|
+
draft ? React.createElement(React.Fragment, {},
|
|
1509
|
+
React.createElement('label', {}, '工作流名称', React.createElement('input', { value: namePair[0], maxLength: 120, onChange: function (event) { namePair[1](event.target.value); replacePair[1](false); } })),
|
|
1510
|
+
React.createElement('div', { className: 'iris-comfy-grid' },
|
|
1511
|
+
select('promptNode', '正向提示词节点', entries.filter(function (entry) { return typeof entry[1].inputs.text === 'string'; })),
|
|
1512
|
+
select('outputNode', '图片输出节点', entries),
|
|
1513
|
+
select('sizeNode', '尺寸节点', entries.filter(function (entry) { return ['width', 'height'].every(function (key) { return Number.isSafeInteger(entry[1].inputs[key]) && entry[1].inputs[key] > 0; }); }))),
|
|
1514
|
+
React.createElement('details', {}, React.createElement('summary', {}, '公开参数 · 已选 ' + slots.filter(function (slot) { return slot.selected; }).length + ' / 32'),
|
|
1515
|
+
React.createElement('div', { className: 'iris-comfy-slots' }, slots.map(function (slot) {
|
|
1516
|
+
return React.createElement('div', { key: slot.key, className: 'iris-comfy-slot' },
|
|
1517
|
+
React.createElement('label', {}, React.createElement('input', { type: 'checkbox', checked: slot.selected, onChange: function (event) {
|
|
1518
|
+
if (event.target.checked && draft.parameters.length >= 32) { notePair[1]('最多公开 32 个参数'); return; }
|
|
1519
|
+
change('parameters', draft.parameters.filter(function (item) { return item.node !== slot.parameter.node || item.input !== slot.parameter.input; }).concat(event.target.checked ? [slot.parameter] : []));
|
|
1520
|
+
} }), slot.parameter.label || slot.parameter.id),
|
|
1521
|
+
slot.selected ? React.createElement('select', { 'aria-label': (slot.parameter.label || slot.parameter.id) + '类型', value: slot.parameter.type, onChange: function (event) {
|
|
1522
|
+
change('parameters', draft.parameters.map(function (item) { return item.id === slot.parameter.id ? { id: item.id, node: item.node, input: item.input, label: item.label, type: event.target.value } : item; }));
|
|
1523
|
+
} }, (typeof slot.value === 'number' ? [['integer', '整数'], ['number', '数值']] : [[typeof slot.value, typeof slot.value === 'boolean' ? '开关' : '文本']]).map(function (pair) { return React.createElement('option', { key: pair[0], value: pair[0] }, pair[1]); })) : null);
|
|
1524
|
+
}))),
|
|
1525
|
+
React.createElement('label', { style: { display: 'flex', alignItems: 'center' } }, React.createElement('input', { type: 'checkbox', checked: autoPair[0], onChange: function (event) { autoPair[1](event.target.checked); } }), '保存时将图片功能设为自动池'),
|
|
1526
|
+
React.createElement('div', { className: 'iris-wb-row' },
|
|
1527
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { operate('check'); } }, busyPair[0] === 'check' ? '检查中…' : '只读检查'),
|
|
1528
|
+
React.createElement('button', { className: 'iris-act-run', onClick: function () { operate('save'); } }, busyPair[0] === 'save' ? '保存中…' : '保存工作流')),
|
|
1529
|
+
replacePair[0] ? React.createElement('div', { className: 'iris-comfy-result', role: 'group', 'aria-label': '确认替换工作流' },
|
|
1530
|
+
React.createElement('p', {}, '替换「' + namePair[0] + '」?旧任务不会改绑,更换模板可能影响旧任务的继续观察。'),
|
|
1531
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { operate('save', true); } }, '确认替换'),
|
|
1532
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { replacePair[1](false); } }, '取消')) : null) : null),
|
|
1533
|
+
notePair[0] ? React.createElement('div', { className: 'iris-comfy-result', role: 'status' }, notePair[0]) : null,
|
|
1534
|
+
React.createElement(ComfyCheckResult, { report: reportPair[0] }));
|
|
1341
1535
|
}
|
|
1342
1536
|
|
|
1343
1537
|
function ProviderManager() {
|
|
@@ -1349,7 +1543,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1349
1543
|
var addingPair = React.useState(false);
|
|
1350
1544
|
var adding = addingPair[0];
|
|
1351
1545
|
var setAdding = addingPair[1];
|
|
1352
|
-
var newForm = React.useState({ name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', type: 'openai', mediaProtocol: 'auto' });
|
|
1546
|
+
var newForm = React.useState({ name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', auth: 'bearer', type: 'openai', mediaProtocol: 'auto' });
|
|
1547
|
+
var savingRef = React.useRef(false), savingPair = React.useState(false), unknownPair = React.useState(false);
|
|
1353
1548
|
var hostConnections = React.useState([]);
|
|
1354
1549
|
var hostChoice = React.useState('');
|
|
1355
1550
|
var setupPair = React.useState(false);
|
|
@@ -1373,23 +1568,25 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1373
1568
|
return function () { clearInterval(timer); };
|
|
1374
1569
|
}, []);
|
|
1375
1570
|
function addProvider() {
|
|
1571
|
+
if (savingRef.current || unknownPair[0]) return;
|
|
1376
1572
|
var f = newForm[0];
|
|
1573
|
+
var payload;
|
|
1377
1574
|
if (hostChoice[0]) {
|
|
1378
1575
|
var connection = hostConnections[0].find(function (item) { return JSON.stringify([item.settingsNs, item.provider]) === hostChoice[0]; });
|
|
1379
1576
|
if (!connection || !connection.available) { setNote('请重新选择可用的 DSH 连接'); return; }
|
|
1380
|
-
|
|
1381
|
-
mediaProtocol: f.mediaProtocol, dshProvider: { provider: connection.provider, settingsNs: connection.settingsNs } }
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
});
|
|
1386
|
-
return;
|
|
1577
|
+
payload = { name: f.name || connection.name, type: 'openai', enabled: true,
|
|
1578
|
+
mediaProtocol: f.mediaProtocol, dshProvider: { provider: connection.provider, settingsNs: connection.settingsNs } };
|
|
1579
|
+
} else {
|
|
1580
|
+
if (!f.baseUrl.trim() || (f.auth !== 'none' && !f.apiKey.trim())) { setNote('请填写 Base URL;Bearer 认证还需要 API Key'); return; }
|
|
1581
|
+
payload = { ...f, ...(f.auth === 'none' ? { apiKey: '' } : {}) };
|
|
1387
1582
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1583
|
+
savingRef.current = true; savingPair[1](true);
|
|
1584
|
+
var controller = new AbortController(), timer = setTimeout(function () { controller.abort(); }, 15000);
|
|
1585
|
+
postAction('providers_upsert', payload, { signal: controller.signal }).then(function (r) {
|
|
1586
|
+
if (r.ok) { setAdding(false); openPair[1](r.d.providerId); setNote(f.mediaProtocol === 'comfyui' ? '连接已保存,请导入 API 工作流;尚未生成图片' : '已连接,请在下方确认模型用途'); refresh(); }
|
|
1391
1587
|
else setNote(r.d.error || '添加失败');
|
|
1392
|
-
});
|
|
1588
|
+
}).catch(function () { unknownPair[1](true); setNote('保存结果未知,请关闭添加表单并核对供应商列表;未自动重试。'); })
|
|
1589
|
+
.finally(function () { clearTimeout(timer); savingRef.current = false; savingPair[1](false); });
|
|
1393
1590
|
}
|
|
1394
1591
|
function toggle(p) {
|
|
1395
1592
|
postAction('providers_upsert', { id: p.id, enabled: !p.enabled }).then(function (r) {
|
|
@@ -1420,19 +1617,26 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1420
1617
|
var body = null;
|
|
1421
1618
|
if (open) {
|
|
1422
1619
|
body = React.createElement('div', { className: 'iris-pm-body', id: 'iris-provider-' + p.id },
|
|
1423
|
-
React.createElement('div', { className: 'iris-pm-note' }, 'Key ' + (p.apiKeyHint || '未配置') + ' · ' + (p.type || '') + ' · ' + (p.mediaProtocol || '')),
|
|
1620
|
+
React.createElement('div', { className: 'iris-pm-note' }, (p.auth === 'none' ? '无需认证' : 'Key ' + (p.apiKeyHint || '未配置')) + ' · ' + (p.type || '') + ' · ' + (p.mediaProtocol || '')),
|
|
1424
1621
|
p.dshProvider ? React.createElement('div', { className: 'iris-pm-note' }, '引用 DSH:' + p.dshProvider.provider
|
|
1425
1622
|
+ ' · 地址和密钥由宿主管理;' + (p.connection && p.connection.available ? '连接可用' : ((p.connection && p.connection.reason) || '连接不可用'))) : null,
|
|
1426
1623
|
p.protocolInferred ? React.createElement('div', { className: 'iris-pm-note' }, '协议为推断值,请确认') : null,
|
|
1427
|
-
React.createElement(
|
|
1428
|
-
|
|
1624
|
+
p.mediaProtocol === 'comfyui' ? React.createElement('details', {}, React.createElement('summary', {}, '连接设置'),
|
|
1625
|
+
React.createElement('div', { className: 'iris-pm-note' }, '服务地址:' + (p.mediaBaseUrl || p.baseUrl)),
|
|
1626
|
+
React.createElement(ProviderEndpointEditor, { provider: p, onNote: setNote, onDone: refresh }),
|
|
1627
|
+
!p.dshProvider ? React.createElement(ProviderAuthenticationEditor, { provider: p, onDone: refresh }) : null)
|
|
1628
|
+
: React.createElement(React.Fragment, {}, React.createElement(ProviderEndpointEditor, { provider: p, onNote: setNote, onDone: refresh }),
|
|
1629
|
+
!p.dshProvider ? React.createElement(ProviderAuthenticationEditor, { provider: p, onDone: refresh }) : null,
|
|
1630
|
+
React.createElement(VisionInputEditor, { provider: p, onNote: setNote, onDone: refresh })),
|
|
1429
1631
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体协议'), React.createElement('select', { value: p.protocolInferred ? 'auto' : (p.mediaProtocol || 'auto'), onChange: function (e) { setProtocol(p, e.target.value); } },
|
|
1430
1632
|
React.createElement('option', { value: 'auto' }, '自动'),
|
|
1431
|
-
p.mediaProtocol && !['dashscope', 'openai-images', 'openai-chat-images', 'openai-responses-images', 'auto'].includes(p.mediaProtocol) ? React.createElement('option', { value: p.mediaProtocol }, p.mediaProtocol + '(未支持)') : null,
|
|
1633
|
+
p.mediaProtocol && !['dashscope', 'openai-images', 'openai-chat-images', 'openai-responses-images', 'comfyui', 'auto'].includes(p.mediaProtocol) ? React.createElement('option', { value: p.mediaProtocol }, p.mediaProtocol + '(未支持)') : null,
|
|
1432
1634
|
React.createElement('option', { value: 'dashscope' }, 'DashScope(仅阿里云官方 HTTPS)'),
|
|
1433
1635
|
React.createElement('option', { value: 'openai-images' }, 'OpenAI Images 兼容'),
|
|
1434
1636
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
1435
|
-
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')
|
|
1637
|
+
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图'),
|
|
1638
|
+
!p.dshProvider ? React.createElement('option', { value: 'comfyui' }, 'ComfyUI · API 工作流') : null)),
|
|
1639
|
+
p.mediaProtocol === 'comfyui' ? React.createElement(ComfyWorkflowEditor, { key: p.id, provider: p, setupSuggested: setupPair[0], onDone: refresh }) : null,
|
|
1436
1640
|
React.createElement('div', { className: 'iris-pm-note' }, '能力: ' + ((p.capabilities || []).join(' / ') || '无')),
|
|
1437
1641
|
React.createElement(ProviderDirectory, { provider: p, onDone: refresh }),
|
|
1438
1642
|
React.createElement(ModelPool, { provider: p, setupSuggested: setupPair[0], onNote: setNote, onDone: refresh, testModel: testModel, removeModel: removeModel }),
|
|
@@ -1449,9 +1653,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1449
1653
|
});
|
|
1450
1654
|
var addBlock = null;
|
|
1451
1655
|
if (adding) {
|
|
1452
|
-
addBlock = React.createElement('
|
|
1656
|
+
addBlock = React.createElement('fieldset', { className: 'iris-pm-card' + (newForm[0].mediaProtocol === 'comfyui' ? ' iris-comfy-panel' : ''), disabled: savingPair[0] || unknownPair[0] },
|
|
1657
|
+
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '连接预设'),
|
|
1658
|
+
React.createElement('select', { value: newForm[0].mediaProtocol === 'comfyui' ? 'comfyui' : 'custom', onChange: function (e) {
|
|
1659
|
+
newForm[1](e.target.value === 'comfyui' ? { name: 'ComfyUI', baseUrl: 'http://127.0.0.1:8188', mediaBaseUrl: '', apiKey: '', type: 'openai', auth: 'none', mediaProtocol: 'comfyui' }
|
|
1660
|
+
: { name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', type: 'openai', auth: 'bearer', mediaProtocol: 'auto' });
|
|
1661
|
+
} }, React.createElement('option', { value: 'custom' }, '自定义供应商'), React.createElement('option', { value: 'comfyui' }, 'ComfyUI · 已有服务'))) : null,
|
|
1453
1662
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '连接来源'),
|
|
1454
|
-
React.createElement('select', { value: hostChoice[0], onChange: function (e) {
|
|
1663
|
+
React.createElement('select', { value: hostChoice[0], onChange: function (e) {
|
|
1664
|
+
hostChoice[1](e.target.value);
|
|
1665
|
+
if (e.target.value && newForm[0].mediaProtocol === 'comfyui') newForm[1]({ name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', auth: 'bearer', type: 'openai', mediaProtocol: 'auto' });
|
|
1666
|
+
} },
|
|
1455
1667
|
React.createElement('option', { value: '' }, '手动填写供应商'),
|
|
1456
1668
|
hostConnections[0].map(function (connection) { return React.createElement('option', {
|
|
1457
1669
|
key: JSON.stringify([connection.settingsNs, connection.provider]), value: JSON.stringify([connection.settingsNs, connection.provider]), disabled: !connection.available
|
|
@@ -1460,26 +1672,82 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1460
1672
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '名称'), React.createElement('input', { value: newForm[0].name, placeholder: '如 阿里云百炼', onChange: function (e) { newForm[1]({ ...newForm[0], name: e.target.value }); } })),
|
|
1461
1673
|
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'Base URL *'), React.createElement('input', { value: newForm[0].baseUrl, placeholder: 'https://dashscope.aliyuncs.com/compatible-mode/v1', onChange: function (e) { newForm[1]({ ...newForm[0], baseUrl: e.target.value }); } })) : null,
|
|
1462
1674
|
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体 Base URL(可选)'), React.createElement('input', { value: newForm[0].mediaBaseUrl, placeholder: 'Workspace 媒体端点;留空沿用 Base URL', onChange: function (e) { newForm[1]({ ...newForm[0], mediaBaseUrl: e.target.value }); } })) : null,
|
|
1463
|
-
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '
|
|
1675
|
+
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '认证方式'), React.createElement('select', { value: newForm[0].auth, onChange: function (e) { newForm[1]({ ...newForm[0], auth: e.target.value }); } },
|
|
1676
|
+
React.createElement('option', { value: 'bearer' }, 'Bearer · API Key'), React.createElement('option', { value: 'none' }, '无需认证(本地或内网服务)'))) : null,
|
|
1677
|
+
!hostChoice[0] && newForm[0].auth !== 'none' ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'API Key *'), React.createElement('input', { type: 'password', value: newForm[0].apiKey, placeholder: 'sk-...', onChange: function (e) { newForm[1]({ ...newForm[0], apiKey: e.target.value }); } })) : null,
|
|
1464
1678
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体协议'), React.createElement('select', { value: newForm[0].mediaProtocol, onChange: function (e) { newForm[1]({ ...newForm[0], mediaProtocol: e.target.value }); } },
|
|
1465
1679
|
React.createElement('option', { value: 'auto' }, hostChoice[0] ? '请选择该服务支持的媒体协议' : '自动(按媒体 Base URL 安全判断)'),
|
|
1466
1680
|
React.createElement('option', { value: 'dashscope' }, 'DashScope(阿里云官方)'),
|
|
1467
1681
|
React.createElement('option', { value: 'openai-images' }, 'OpenAI Images 兼容'),
|
|
1468
1682
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
1469
|
-
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')
|
|
1683
|
+
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图'),
|
|
1684
|
+
!hostChoice[0] ? React.createElement('option', { value: 'comfyui' }, 'ComfyUI · API 工作流') : null)),
|
|
1685
|
+
newForm[0].mediaProtocol === 'comfyui' ? React.createElement('p', { className: 'iris-pm-note' }, '连接已有 ComfyUI 服务。先保存连接,再导入 Export (API) 工作流;Iris 不安装服务或模型。') : null,
|
|
1470
1686
|
React.createElement('div', { className: 'iris-wb-row' },
|
|
1471
|
-
React.createElement('button', { className: 'iris-act-run', onClick: addProvider }, '保存'),
|
|
1687
|
+
React.createElement('button', { className: 'iris-act-run', onClick: addProvider }, savingPair[0] ? '保存中…' : '保存'),
|
|
1472
1688
|
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { setAdding(false); } }, '取消')));
|
|
1473
1689
|
}
|
|
1474
1690
|
return React.createElement('div', { className: 'iris-pm' },
|
|
1475
1691
|
React.createElement('div', { className: 'iris-pm-head' },
|
|
1476
1692
|
React.createElement('span', { className: 'iris-wb-muted' }, (list ? list.length : 0) + ' 个供应商'),
|
|
1477
|
-
React.createElement('button', { className: 'iris-pm-add', onClick: function () { if (!adding) { hostChoice[1](''); loadHostConnections(); } setAdding(!adding); } }, adding ? '收起' : '+ 添加供应商')),
|
|
1693
|
+
React.createElement('button', { className: 'iris-pm-add', disabled: savingPair[0], onClick: function () { if (!adding) { hostChoice[1](''); loadHostConnections(); } unknownPair[1](false); setAdding(!adding); } }, adding ? '收起' : '+ 添加供应商')),
|
|
1478
1694
|
addBlock,
|
|
1479
1695
|
cards.length ? cards : React.createElement('div', { className: 'iris-wb-empty' }, '暂无供应商 — 点右上角「+ 添加供应商」'),
|
|
1480
1696
|
note ? React.createElement('div', { className: 'iris-pm-act' }, note) : null);
|
|
1481
1697
|
}
|
|
1482
1698
|
|
|
1699
|
+
function comfyParameterLabel(parameter) {
|
|
1700
|
+
return parameter.label || ({ lora_name: 'LoRA', strength_model: '模型权重', strength_clip: 'CLIP 权重',
|
|
1701
|
+
ckpt_name: 'Checkpoint', seed: '种子', steps: '步数', sampler_name: '采样器', scheduler: '调度器', cfg: 'CFG' })[parameter.input]
|
|
1702
|
+
|| (parameter.id === 'negative' && parameter.input === 'text' ? '负面提示词' : parameter.id);
|
|
1703
|
+
}
|
|
1704
|
+
function ComfyParameters(props) {
|
|
1705
|
+
var option = props.option, parameters = option.comfyWorkflow.parameters || [];
|
|
1706
|
+
var reportPair = React.useState(null), choicesPair = React.useState([]), notePair = React.useState(''), busyPair = React.useState(false);
|
|
1707
|
+
var active = React.useRef(null), sequence = React.useRef(0);
|
|
1708
|
+
React.useEffect(function () { return function () { sequence.current++; if (active.current) active.current.abort(); }; }, []);
|
|
1709
|
+
function change(id, value) {
|
|
1710
|
+
sequence.current++; if (active.current) active.current.abort(); active.current = null; busyPair[1](false);
|
|
1711
|
+
reportPair[1](null); notePair[1]('参数已更改,提交前将重新预检。'); props.onChange({ ...props.values, [id]: value });
|
|
1712
|
+
}
|
|
1713
|
+
async function check() {
|
|
1714
|
+
if (active.current || props.disabled) return;
|
|
1715
|
+
var controller = new AbortController(), ticket = ++sequence.current; active.current = controller; busyPair[1](true); notePair[1]('');
|
|
1716
|
+
var timer = setTimeout(function () { controller.abort(); }, 20000);
|
|
1717
|
+
try {
|
|
1718
|
+
var result = await postAction('providers_check_comfy_workflow', { id: option.providerId, model_id: option.id,
|
|
1719
|
+
...(Object.keys(props.values).length ? { workflow_parameters: props.values } : {}) }, { signal: controller.signal });
|
|
1720
|
+
if (ticket !== sequence.current) return;
|
|
1721
|
+
if (!result.ok || result.d.ok !== true) { reportPair[1](null); notePair[1](result.d.error || '检查失败'); return; }
|
|
1722
|
+
reportPair[1](result.d.report); choicesPair[1](result.d.report.parameters || []);
|
|
1723
|
+
} catch (_) { if (ticket === sequence.current) { reportPair[1](null); notePair[1]('检查未完成;未发起生成。可核对服务后重试。'); } }
|
|
1724
|
+
finally { clearTimeout(timer); if (ticket === sequence.current) { active.current = null; busyPair[1](false); } }
|
|
1725
|
+
}
|
|
1726
|
+
return React.createElement('section', { className: 'iris-comfy-panel', 'aria-label': 'ComfyUI 画图参数' },
|
|
1727
|
+
React.createElement('div', { className: 'iris-comfy-head' }, React.createElement('strong', {}, 'ComfyUI · ' + option.id), React.createElement('span', { className: 'iris-comfy-tag' }, '单张文生图')),
|
|
1728
|
+
React.createElement('div', { className: 'iris-pm-note' }, '沿用工作流默认值,只调整已公开的参数。LoRA 候选可通过只读检查获取。'),
|
|
1729
|
+
parameters.length ? React.createElement('details', {}, React.createElement('summary', {}, '工作流参数 · ' + parameters.length),
|
|
1730
|
+
React.createElement('fieldset', { className: 'iris-comfy-grid', disabled: props.disabled, style: { border: 0, padding: 0, margin: '8px 0 0', minWidth: 0 } }, parameters.map(function (parameter) {
|
|
1731
|
+
var value = Object.hasOwn(props.values, parameter.id) ? props.values[parameter.id] : parameter.default;
|
|
1732
|
+
var checked = choicesPair[0].find(function (item) { return item.id === parameter.id; });
|
|
1733
|
+
var choices = checked && checked.availableChoices || parameter.choices, input;
|
|
1734
|
+
if (choices) input = React.createElement('select', { value: JSON.stringify(value), onChange: function (event) { change(parameter.id, JSON.parse(event.target.value)); } },
|
|
1735
|
+
!choices.includes(value) ? React.createElement('option', { value: JSON.stringify(value) }, String(value) + '(当前未列出)') : null,
|
|
1736
|
+
choices.map(function (choice) { return React.createElement('option', { key: JSON.stringify(choice), value: JSON.stringify(choice) }, String(choice)); }));
|
|
1737
|
+
else if (parameter.type === 'boolean') input = React.createElement('input', { type: 'checkbox', checked: value, onChange: function (event) { change(parameter.id, event.target.checked); } });
|
|
1738
|
+
else input = React.createElement(parameter.type === 'string' && parameter.input === 'text' ? 'textarea' : 'input', {
|
|
1739
|
+
type: parameter.type === 'string' ? 'text' : 'number', value: value, min: parameter.min, max: parameter.max, step: parameter.type === 'integer' ? 1 : 'any',
|
|
1740
|
+
onChange: function (event) { var text = event.target.value; change(parameter.id, parameter.type === 'string' || text === '' ? text : Number(text)); }
|
|
1741
|
+
});
|
|
1742
|
+
return React.createElement('label', { key: parameter.id }, comfyParameterLabel(parameter), input,
|
|
1743
|
+
checked && checked.choicesTruncated ? React.createElement('span', { className: 'iris-pm-note' }, '选项仅列前 256 项,可手填名称:',
|
|
1744
|
+
React.createElement('input', { type: 'text', value: value, onChange: function (event) { change(parameter.id, event.target.value); } })) : null);
|
|
1745
|
+
}))) : React.createElement('div', { className: 'iris-pm-note' }, '此模板未公开额外参数。'),
|
|
1746
|
+
React.createElement('div', {}, React.createElement('button', { className: 'iris-pm-btn', disabled: props.disabled || busyPair[0], onClick: check }, busyPair[0] ? '检查中…' : '只读检查并读取选项')),
|
|
1747
|
+
notePair[0] ? React.createElement('div', { className: 'iris-comfy-result', role: 'status' }, notePair[0]) : null,
|
|
1748
|
+
React.createElement(ComfyCheckResult, { report: reportPair[0] }));
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1483
1751
|
/* ---- 文件字段(阶段 10):让用户"看见并选文件",统一产出一个宿主路径 ---- */
|
|
1484
1752
|
function FileField(props) {
|
|
1485
1753
|
var value = props.value || '';
|
|
@@ -1540,7 +1808,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1540
1808
|
manual ? React.createElement('input', { className: 'iris-pm-madd', placeholder: '高级:粘贴 DSH 宿主上的绝对路径,如 /home/user/pic.png', value: value, onChange: function (e) { onChange(e.target.value); } }) : null,
|
|
1541
1809
|
menu === 'att' ? React.createElement('div', { className: 'iris-ff-list' },
|
|
1542
1810
|
(atts && atts.length) ? atts.map(function (a) {
|
|
1543
|
-
return React.createElement('
|
|
1811
|
+
return React.createElement('button', { type: 'button', key: a.attachmentId, className: 'iris-ff-item', onClick: function () { pickAttachment(a); } },
|
|
1544
1812
|
React.createElement('span', { className: 'iris-ff-src' }, a.source === 'iris' ? '🫧' : '🖼'),
|
|
1545
1813
|
React.createElement('span', {}, a.name || a.attachmentId.slice(0, 18)));
|
|
1546
1814
|
}) : React.createElement('div', { className: 'iris-wb-empty' }, '无可选附件(会话无图或 iris 未生成过)')) : null);
|
|
@@ -1564,6 +1832,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1564
1832
|
var modelPair = React.useState(null); // { assigned, options:[{id,providerId}], tested }
|
|
1565
1833
|
var modelInfo = modelPair[0];
|
|
1566
1834
|
var setModelInfo = modelPair[1];
|
|
1835
|
+
var submitting = React.useRef(false);
|
|
1836
|
+
var selectedOption = modelInfo && modelInfo.options.find(function (option) { return option.ref === vals.model; });
|
|
1837
|
+
var selectedComfy = action === 'image' && selectedOption && selectedOption.comfyWorkflow;
|
|
1567
1838
|
var irisState = useIrisState(5000);
|
|
1568
1839
|
function setVal(key, value) { var nv = {}; Object.keys(vals).forEach(function (k) { nv[k] = vals[k]; }); nv[key] = value; setVals(nv); }
|
|
1569
1840
|
function loadModels(event) {
|
|
@@ -1589,19 +1860,30 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1589
1860
|
return function () { window.removeEventListener('iris-function-configuration-changed', loadModels); };
|
|
1590
1861
|
}, [open]);
|
|
1591
1862
|
function submit() {
|
|
1592
|
-
if (run === 'running') return;
|
|
1863
|
+
if (submitting.current || run === 'running' || run && run.unknown) return;
|
|
1864
|
+
var payload = vals;
|
|
1865
|
+
if (selectedComfy) {
|
|
1866
|
+
var values = vals.workflow_parameters || {}, declared = selectedComfy.parameters || [];
|
|
1867
|
+
if (Object.keys(values).some(function (id) { var field = declared.find(function (item) { return item.id === id; }), value = values[id];
|
|
1868
|
+
return !field || (field.type === 'integer' ? !Number.isSafeInteger(value) : field.type === 'number' ? typeof value !== 'number' || !Number.isFinite(value) : typeof value !== field.type)
|
|
1869
|
+
|| field.min !== undefined && value < field.min || field.max !== undefined && value > field.max || field.choices && !field.choices.includes(value);
|
|
1870
|
+
})) { setRun({ ok: false, text: '请检查工作流参数的类型、范围和选项;尚未发起生成。' }); return; }
|
|
1871
|
+
payload = { ...vals, workflow_sha: selectedComfy.sha256 };
|
|
1872
|
+
}
|
|
1873
|
+
submitting.current = true;
|
|
1593
1874
|
setRun('running');
|
|
1594
1875
|
fetch('/iris/api/actions/' + action, {
|
|
1595
1876
|
method: 'POST',
|
|
1596
1877
|
headers: { 'Content-Type': 'application/json' },
|
|
1597
|
-
body: JSON.stringify(
|
|
1878
|
+
body: JSON.stringify(payload)
|
|
1598
1879
|
})
|
|
1599
1880
|
.then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })
|
|
1600
1881
|
.then(function (res) {
|
|
1601
1882
|
setRun(res.ok ? { ok: true, text: res.d.text || '', img: res.d.imageUrl || res.d.imageDataUrl || null, audio: res.d.audioUrl || null } : { ok: false, text: res.d.error || '执行失败' });
|
|
1602
1883
|
if (res.ok && res.d && res.d.storage === 'core') window.dispatchEvent(new CustomEvent('iris-core-artifacts-changed'));
|
|
1603
1884
|
})
|
|
1604
|
-
.catch(function () { setRun({ ok: false, text: '网络错误' }); })
|
|
1885
|
+
.catch(function () { setRun({ ok: false, unknown: !!selectedComfy, text: selectedComfy ? '提交结果未知,请到任务列表核对;未自动重试。' : '网络错误' }); })
|
|
1886
|
+
.finally(function () { submitting.current = false; });
|
|
1605
1887
|
}
|
|
1606
1888
|
var cardHealth = capability ? capabilityHealth(irisState, capability) : { status: 'verified' };
|
|
1607
1889
|
// Host references can be ready without raw endpoint/key fields in the state snapshot.
|
|
@@ -1614,7 +1896,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1614
1896
|
var inputs = fields.map(function (f) {
|
|
1615
1897
|
var node;
|
|
1616
1898
|
if (f.key === 'model' && capability) {
|
|
1617
|
-
node = React.createElement('select', { value: vals.model || '',
|
|
1899
|
+
node = React.createElement('select', { value: vals.model || '', disabled: run === 'running', onChange: function (e) {
|
|
1900
|
+
var next = { ...vals, model: e.target.value }; delete next.workflow_parameters; delete next.workflow_sha; setVals(next);
|
|
1901
|
+
} },
|
|
1618
1902
|
React.createElement('option', { value: '' }, modelInfo && modelInfo.mode === 'auto' ? '按功能池顺序自动选择' : '按所选模型顺序'),
|
|
1619
1903
|
((modelInfo && modelInfo.options) || []).map(function (option) {
|
|
1620
1904
|
return React.createElement('option', { key: option.ref, value: option.ref, disabled: option.unavailable }, option.label + (option.unavailable ? ' · 当前不可用' : ''));
|
|
@@ -1672,16 +1956,18 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1672
1956
|
body = React.createElement('div', { className: 'iris-act-body' },
|
|
1673
1957
|
modelBar,
|
|
1674
1958
|
inputs,
|
|
1675
|
-
React.createElement(
|
|
1676
|
-
run === 'running'
|
|
1959
|
+
selectedComfy ? React.createElement(ComfyParameters, { key: selectedOption.ref + '@' + selectedComfy.sha256, option: selectedOption,
|
|
1960
|
+
values: vals.workflow_parameters || {}, disabled: run === 'running', onChange: function (values) { setVal('workflow_parameters', values); } }) : null,
|
|
1961
|
+
React.createElement('button', { className: 'iris-act-run', disabled: run === 'running' || !!(run && run.unknown), onClick: submit },
|
|
1962
|
+
run === 'running' ? '执行中…' : (props.submitLabel || (selectedComfy ? '生成图片' : '执行'))),
|
|
1677
1963
|
result);
|
|
1678
1964
|
}
|
|
1679
1965
|
var headStatus = capability ? cardHealth.status : 'verified';
|
|
1680
1966
|
var headTitle = capability ? healthText(cardHealth) : '本地功能';
|
|
1681
1967
|
return React.createElement('div', {
|
|
1682
|
-
className: 'iris-act-card' + (open ? ' open' : '') + (capability ? ' health-' + headStatus : ''), 'data-iris-action': action
|
|
1968
|
+
className: 'iris-act-card' + (open ? ' open' : '') + (selectedComfy ? ' iris-comfy-card' : '') + (capability ? ' health-' + headStatus : ''), 'data-iris-action': action
|
|
1683
1969
|
},
|
|
1684
|
-
React.createElement('
|
|
1970
|
+
React.createElement('button', { type: 'button', className: 'iris-act-head', 'aria-expanded': open, onClick: function () { setOpen(!open); } },
|
|
1685
1971
|
React.createElement('span', { className: 'iris-act-title' }, title),
|
|
1686
1972
|
capability ? React.createElement('span', {
|
|
1687
1973
|
className: 'iris-pm-dot ' + healthClass(cardHealth),
|
|
@@ -1981,8 +2267,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1981
2267
|
if (!rows.length) return null;
|
|
1982
2268
|
var isCollapsed = !!collapsed[name];
|
|
1983
2269
|
return React.createElement('div', { key: name, className: 'iris-hist-group' },
|
|
1984
|
-
React.createElement('
|
|
1985
|
-
className: 'iris-hist-head',
|
|
2270
|
+
React.createElement('button', {
|
|
2271
|
+
type: 'button', className: 'iris-hist-head', 'aria-expanded': !isCollapsed,
|
|
1986
2272
|
onClick: function () { var n = {}; Object.keys(collapsed).forEach(function (k) { n[k] = collapsed[k]; }); n[name] = !isCollapsed; setCollapsed(n); }
|
|
1987
2273
|
}, React.createElement('span', {}, isCollapsed ? '▶' : '▼'), name + ' · ' + rows.length),
|
|
1988
2274
|
isCollapsed ? null : React.createElement('div', { className: 'iris-wb-box' },
|
|
@@ -2064,11 +2350,12 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2064
2350
|
var pair = React.useState([]);
|
|
2065
2351
|
var setItems = pair[1];
|
|
2066
2352
|
React.useEffect(function () {
|
|
2067
|
-
var alive = true;
|
|
2353
|
+
var alive = true, sequence = 0;
|
|
2068
2354
|
function load() {
|
|
2355
|
+
var current = ++sequence;
|
|
2069
2356
|
fetch('/iris/api/core/snapshot?limit=' + Math.max(1, Math.min(200, Number(limit) || 6)))
|
|
2070
2357
|
.then(function (res) { return res.ok ? res.json() : null; })
|
|
2071
|
-
.then(function (data) { if (alive && data) setItems(coreGalleryItems(data, limit)); })
|
|
2358
|
+
.then(function (data) { if (alive && current === sequence && data) setItems(coreGalleryItems(data, limit)); })
|
|
2072
2359
|
.catch(function () { /* Core 不可用不影响旧作品 */ });
|
|
2073
2360
|
}
|
|
2074
2361
|
function changed() { load(); }
|
|
@@ -2584,12 +2871,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2584
2871
|
var pair = React.useState(EMPTY_CORE_USER);
|
|
2585
2872
|
var setData = pair[1];
|
|
2586
2873
|
React.useEffect(function () {
|
|
2587
|
-
var alive = true;
|
|
2874
|
+
var alive = true, sequence = 0;
|
|
2588
2875
|
function load() {
|
|
2876
|
+
var current = ++sequence;
|
|
2589
2877
|
fetch('/iris/api/core/snapshot?limit=200')
|
|
2590
2878
|
.then(function (res) { return res.ok ? res.json() : null; })
|
|
2591
2879
|
.then(function (data) {
|
|
2592
|
-
if (!alive || !data || !Array.isArray(data.userTasks)) return;
|
|
2880
|
+
if (!alive || current !== sequence || !data || !Array.isArray(data.userTasks)) return;
|
|
2593
2881
|
setData({
|
|
2594
2882
|
rows: data.userTasks,
|
|
2595
2883
|
available: !!data.available,
|
|
@@ -2879,17 +3167,20 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2879
3167
|
var copyPair = React.useState('');
|
|
2880
3168
|
var copyNote = copyPair[0];
|
|
2881
3169
|
var setCopyNote = copyPair[1];
|
|
3170
|
+
var requestRef = React.useRef({ active: false, sequence: 0 });
|
|
2882
3171
|
function load() {
|
|
2883
|
-
|
|
3172
|
+
var request = requestRef.current;
|
|
3173
|
+
if (busy || !request.active) return;
|
|
3174
|
+
var current = ++request.sequence;
|
|
2884
3175
|
setBusy(true);
|
|
2885
3176
|
setError('');
|
|
2886
3177
|
fetch('/iris/api/core/snapshot?limit=12')
|
|
2887
3178
|
.then(function (res) {
|
|
2888
3179
|
if (res.status === 404) return { available: false, backendReloadRequired: true, tasks: { total: 0, recent: [] }, artifacts: { total: 0, recent: [] } };
|
|
2889
3180
|
return res.ok ? res.json() : Promise.reject(new Error('读取失败(HTTP ' + res.status + ')'));
|
|
2890
|
-
}).then(setData)
|
|
2891
|
-
.catch(function (caught) { setError('Core 运行事实读取失败:' + caught.message); })
|
|
2892
|
-
.finally(function () { setBusy(false); });
|
|
3181
|
+
}).then(function (value) { if (request.active && current === request.sequence) setData(value); })
|
|
3182
|
+
.catch(function (caught) { if (request.active && current === request.sequence) setError('Core 运行事实读取失败:' + caught.message); })
|
|
3183
|
+
.finally(function () { if (request.active && current === request.sequence) setBusy(false); });
|
|
2893
3184
|
}
|
|
2894
3185
|
function copyCoreId(value, label) {
|
|
2895
3186
|
setCopyNote('');
|
|
@@ -2903,11 +3194,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2903
3194
|
.catch(function () { setCopyNote('复制失败;请长按上方 ID 复制。'); });
|
|
2904
3195
|
}
|
|
2905
3196
|
React.useEffect(function () {
|
|
3197
|
+
requestRef.current.active = true;
|
|
2906
3198
|
function changed() { load(); }
|
|
2907
3199
|
load();
|
|
2908
3200
|
window.addEventListener('iris-core-artifacts-changed', changed);
|
|
2909
3201
|
window.addEventListener('iris-core-refresh-tick', changed);
|
|
2910
3202
|
return function () {
|
|
3203
|
+
requestRef.current.active = false;
|
|
3204
|
+
requestRef.current.sequence++;
|
|
2911
3205
|
window.removeEventListener('iris-core-artifacts-changed', changed);
|
|
2912
3206
|
window.removeEventListener('iris-core-refresh-tick', changed);
|
|
2913
3207
|
};
|
|
@@ -3136,6 +3430,80 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
3136
3430
|
note ? React.createElement('div', { className: 'iris-clean-note' }, note) : null);
|
|
3137
3431
|
}
|
|
3138
3432
|
|
|
3433
|
+
function BrowserCheckPanel(props) {
|
|
3434
|
+
var statePair = React.useState({ status: 'idle', message: '尚未实测' });
|
|
3435
|
+
var state = statePair[0], setState = statePair[1];
|
|
3436
|
+
var request = React.useRef(null);
|
|
3437
|
+
React.useEffect(function () { return function () {
|
|
3438
|
+
var operation = request.current;
|
|
3439
|
+
request.current = null;
|
|
3440
|
+
if (operation) { clearTimeout(operation.timer); operation.controller.abort(); }
|
|
3441
|
+
}; }, []);
|
|
3442
|
+
|
|
3443
|
+
function stop(timedOut) {
|
|
3444
|
+
var operation = request.current;
|
|
3445
|
+
if (!operation) return;
|
|
3446
|
+
request.current = null;
|
|
3447
|
+
clearTimeout(operation.timer);
|
|
3448
|
+
operation.controller.abort();
|
|
3449
|
+
setState({ status: 'canceled', message: timedOut
|
|
3450
|
+
? '截图自检超过 60 秒,已停止等待。若恰好完成保存,可在作品库查看。'
|
|
3451
|
+
: '已取消本次自检等待。若恰好完成保存,可在作品库查看。' });
|
|
3452
|
+
}
|
|
3453
|
+
function start() {
|
|
3454
|
+
if (request.current || !props.available) return;
|
|
3455
|
+
var operation = { controller: new AbortController(), started: Date.now() };
|
|
3456
|
+
request.current = operation;
|
|
3457
|
+
setState({ status: 'running', message: '正在打开浏览器并生成测试截图…' });
|
|
3458
|
+
operation.timer = setTimeout(function () { if (request.current === operation) stop(true); }, 60000);
|
|
3459
|
+
fetch('/iris/api/actions/html', {
|
|
3460
|
+
method: 'POST', headers: { 'content-type': 'application/json' }, credentials: 'same-origin',
|
|
3461
|
+
signal: operation.controller.signal,
|
|
3462
|
+
body: JSON.stringify({ fullPage: false, width: 480, height: 240,
|
|
3463
|
+
html: '<style>body{margin:0;background:#edf3f1;color:#183e34;font-family:system-ui,sans-serif}.iris-test{padding:32px}h1{font-size:26px;margin:0 0 12px}p{font-size:16px;margin:8px 0}</style><div class="iris-test"><h1>Iris · 截图自检</h1><p>中文与 HTML 渲染测试</p><p>本页只使用内联样式,不调用模型。</p></div>' })
|
|
3464
|
+
}).then(function (res) { return res.json().then(function (data) {
|
|
3465
|
+
if (!res.ok || data.ok !== true) throw Object.assign(new Error(data.error || '截图自检失败'), { code: data.code });
|
|
3466
|
+
var id = data.artifactIds && data.artifactIds[0];
|
|
3467
|
+
if (data.storage !== 'core' || !/^artifact_[a-f0-9]{24}$/.test(id || '')) throw new Error('服务端未返回有效截图作品,请核对前后端版本。');
|
|
3468
|
+
return id;
|
|
3469
|
+
}); }).then(function (id) {
|
|
3470
|
+
if (request.current !== operation || operation.controller.signal.aborted) return;
|
|
3471
|
+
setState({ status: 'passed', artifactId: id, message: '本次截图已保存 · ' + ((Date.now() - operation.started) / 1000).toFixed(1) + ' 秒 · ' + new Date().toLocaleTimeString() });
|
|
3472
|
+
window.dispatchEvent(new CustomEvent('iris-core-artifacts-changed'));
|
|
3473
|
+
}).catch(function (error) {
|
|
3474
|
+
if (request.current !== operation || operation.controller.signal.aborted) return;
|
|
3475
|
+
var code = String(error && error.code || '');
|
|
3476
|
+
var detail = String(error && error.message || error);
|
|
3477
|
+
var message = '截图自检未通过。请查看错误详情。';
|
|
3478
|
+
if (code === 'IRIS_RENDER_ADDRESS_INVALID') message = '截图地址配置有误,请核对 DSH_WEB_BASE。';
|
|
3479
|
+
else if (code === 'IRIS_CORE_DATA_ROOT_BUSY') message = '作品库正在被其他操作写入,请稍后重试。';
|
|
3480
|
+
else if (/^IRIS_HOST_CAPABILITY_/.test(code)) message = '宿主浏览器接口不可用,请检查浏览器插件是否启用。';
|
|
3481
|
+
else if (/Missing X server|DISPLAY|BROWSER_HOST_/.test(detail + code)) message = '浏览器进程未能启动,请检查运行环境与显示服务。';
|
|
3482
|
+
else if (/BROWSER_NAVIGATION|ERR_CONNECTION_REFUSED|ERR_NAME_NOT_RESOLVED/.test(detail + code)) message = '浏览器未能打开渲染页面,请核对上方截图地址与宿主监听状态。';
|
|
3483
|
+
else if (/BROWSER_SCREENSHOT/.test(code)) message = '浏览器未能生成截图,请查看错误详情。';
|
|
3484
|
+
setState({ status: 'failed', message: message, detail: detail, code: code });
|
|
3485
|
+
}).finally(function () {
|
|
3486
|
+
clearTimeout(operation.timer);
|
|
3487
|
+
if (request.current === operation) request.current = null;
|
|
3488
|
+
});
|
|
3489
|
+
}
|
|
3490
|
+
var busy = state.status === 'running';
|
|
3491
|
+
return React.createElement('div', { className: 'iris-browser-check', 'data-iris-browser-check': state.status },
|
|
3492
|
+
React.createElement('div', { className: 'iris-doctor-top' },
|
|
3493
|
+
React.createElement('strong', { className: 'iris-doctor-score' }, 'HTML 截图'),
|
|
3494
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: busy || !props.available, onClick: start }, busy ? '检测中…' : '截图自检'),
|
|
3495
|
+
busy ? React.createElement('button', { className: 'iris-pm-btn', onClick: function () { stop(false); } }, '取消') : null),
|
|
3496
|
+
React.createElement('div', { className: 'iris-browser-check-note' + (state.status === 'failed' ? ' error' : ''), role: 'status', 'aria-live': 'polite' }, state.message),
|
|
3497
|
+
props.address ? React.createElement('div', { className: 'iris-browser-check-note iris-wb-muted' }, props.address) : null,
|
|
3498
|
+
React.createElement('div', { className: 'iris-wb-muted' }, '点击后打开浏览器,生成一张本地测试作品;不调用模型。'),
|
|
3499
|
+
state.artifactId ? React.createElement('details', { className: 'iris-doctor-details' },
|
|
3500
|
+
React.createElement('summary', {}, '查看测试截图'),
|
|
3501
|
+
React.createElement('img', { src: '/iris/api/core/artifact/' + state.artifactId + '/media', alt: 'Iris HTML 截图自检结果', loading: 'lazy' })) : null,
|
|
3502
|
+
state.detail ? React.createElement('details', { className: 'iris-doctor-details' },
|
|
3503
|
+
React.createElement('summary', {}, '错误详情'),
|
|
3504
|
+
React.createElement('div', { className: 'iris-browser-check-note' }, (state.code ? state.code + ' · ' : '') + state.detail)) : null);
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3139
3507
|
function HostDoctorPanel() {
|
|
3140
3508
|
var pair = React.useState(null);
|
|
3141
3509
|
var report = pair[0];
|
|
@@ -3160,12 +3528,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
3160
3528
|
|
|
3161
3529
|
var checks = report && Array.isArray(report.checks) ? report.checks : [];
|
|
3162
3530
|
var issues = checks.filter(function (item) { return item.status !== 'ok'; });
|
|
3531
|
+
var renderAddress = checks.find(function (item) { return item.id === 'render-address'; });
|
|
3163
3532
|
var mark = { ok: '✓', warn: '!', error: '×' };
|
|
3164
3533
|
function rows(list) {
|
|
3165
3534
|
return React.createElement('div', { className: 'iris-doctor-list' }, list.map(function (item) {
|
|
3166
3535
|
return React.createElement('div', { key: item.id, className: 'iris-doctor-row ' + item.status },
|
|
3167
3536
|
React.createElement('span', {}, mark[item.status] || '·'),
|
|
3168
|
-
React.createElement('span', {}, item.summary
|
|
3537
|
+
React.createElement('span', {}, item.summary,
|
|
3538
|
+
item.status !== 'ok' && item.suggestion ? React.createElement('div', { className: 'iris-wb-muted' }, item.suggestion) : null));
|
|
3169
3539
|
}));
|
|
3170
3540
|
}
|
|
3171
3541
|
var score = report
|
|
@@ -3181,7 +3551,11 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
3181
3551
|
checks.length ? React.createElement('details', { className: 'iris-doctor-details' },
|
|
3182
3552
|
React.createElement('summary', {}, '查看全部 ' + checks.length + ' 项'),
|
|
3183
3553
|
rows(checks)) : null,
|
|
3184
|
-
React.createElement('div', { className: 'iris-wb-muted' }, '
|
|
3554
|
+
React.createElement('div', { className: 'iris-wb-muted' }, '刷新只读装载证据,不启动浏览器;接口已提供不代表截图已通过。'),
|
|
3555
|
+
React.createElement(BrowserCheckPanel, {
|
|
3556
|
+
available: Boolean(report && report.capabilities && report.capabilities.browser && report.capabilities.browser.status === 'available'),
|
|
3557
|
+
address: renderAddress && renderAddress.status === 'ok' ? renderAddress.summary : ''
|
|
3558
|
+
}));
|
|
3185
3559
|
}
|
|
3186
3560
|
|
|
3187
3561
|
function revealWorkbenchItem(node) {
|
|
@@ -4032,6 +4406,11 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
4032
4406
|
|
|
4033
4407
|
return React.createElement('div', {
|
|
4034
4408
|
className: 'iris-bubble health-' + overallHealth + (dragRef.current ? ' dragging' : ''),
|
|
4409
|
+
role: 'button', tabIndex: 0, 'aria-label': 'Iris 快捷面板', 'aria-expanded': open,
|
|
4410
|
+
onKeyDown: function (event) {
|
|
4411
|
+
if (event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' ')) return;
|
|
4412
|
+
event.preventDefault(); setOpen(!open);
|
|
4413
|
+
},
|
|
4035
4414
|
style: style,
|
|
4036
4415
|
onPointerDown: onPointerDown,
|
|
4037
4416
|
onPointerMove: onPointerMove,
|