@xopcai/xopc 0.0.46 → 0.0.47
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/README.md +17 -2
- package/README.zh-CN.md +8 -2
- package/dist/extensions/dingtalk/src/plugin.js +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +2 -2
- package/dist/extensions/weixin/src/auth/accounts.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
- package/dist/gateway/static/root/assets/{agents-9tPOBNVa.js → agents-DdWPgyn-.js} +2 -2
- package/dist/gateway/static/root/assets/{agents-9tPOBNVa.js.map → agents-DdWPgyn-.js.map} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-yjXMjZEY.js → apps-page-BTi_W1y1.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-yjXMjZEY.js.map → apps-page-BTi_W1y1.js.map} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-B-vOBJmr.js → channels-settings-CjUmKQrC.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-B-vOBJmr.js.map → channels-settings-CjUmKQrC.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-dreaming-jobs-CmdPJHC8.js → cron-dreaming-jobs-DinMur-Z.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-dreaming-jobs-CmdPJHC8.js.map → cron-dreaming-jobs-DinMur-Z.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-aS5nCGTE.js → cron-page-Bu05Z2oL.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-aS5nCGTE.js.map → cron-page-Bu05Z2oL.js.map} +1 -1
- package/dist/gateway/static/root/assets/{dist-COrWk0va.js → dist-wDej8fSi.js} +2 -2
- package/dist/gateway/static/root/assets/{dist-COrWk0va.js.map → dist-wDej8fSi.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-p7Aufdiu.js → extension-debug-page-CZBu7-zM.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-p7Aufdiu.js.map → extension-debug-page-CZBu7-zM.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Ca_H5bLy.js → extension-page-CnOyLPrh.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-Ca_H5bLy.js.map → extension-page-CnOyLPrh.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-f8jERrXK.js → extension-settings-page-BOHn3S1a.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-f8jERrXK.js.map → extension-settings-page-BOHn3S1a.js.map} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-BC5dh16N.js → heartbeat-config-api-OqFXdrMO.js} +2 -2
- package/dist/gateway/static/root/assets/{heartbeat-config-api-BC5dh16N.js.map → heartbeat-config-api-OqFXdrMO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{index-DRnjltLC.js → index-DeELk--t.js} +4 -4
- package/dist/gateway/static/root/assets/{index-DRnjltLC.js.map → index-DeELk--t.js.map} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-CBHnie4a.js → logs-page-DiN42-yE.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-CBHnie4a.js.map → logs-page-DiN42-yE.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-29vp15ci.js → sessions-page-B5oxRfRm.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-29vp15ci.js.map → sessions-page-B5oxRfRm.js.map} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Bd1rO8dz.js → settings-page-DaRY3XEp.js} +2 -2
- package/dist/gateway/static/root/assets/{settings-page-Bd1rO8dz.js.map → settings-page-DaRY3XEp.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BYppQ88L.js → skills-page-BGDLiQZ6.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-BYppQ88L.js.map → skills-page-BGDLiQZ6.js.map} +1 -1
- package/dist/gateway/static/root/assets/{use-image-provider-credentials-DlWCe2d_.js → use-image-provider-credentials-DcP2SYn3.js} +2 -2
- package/dist/gateway/static/root/assets/{use-image-provider-credentials-DlWCe2d_.js.map → use-image-provider-credentials-DcP2SYn3.js.map} +1 -1
- package/dist/gateway/static/root/index.html +1 -1
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +6 -6
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/goals/post-turn.js +1 -1
- package/dist/src/agent/image/load-image-media.js +1 -1
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +2 -2
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
- package/dist/src/agent/memory/dreaming/events.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
- package/dist/src/agent/memory/dreaming/preview.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
- package/dist/src/agent/service.js +5 -5
- package/dist/src/agent/skills/config.js +1 -1
- package/dist/src/agent/skills/hub-hash.js +2 -2
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/auth/credentials.js +3 -3
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/outbound/persist-store.js +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +1 -1
- package/dist/src/channels/pairing/pairing-store.js +2 -2
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/bin.d.ts +7 -0
- package/dist/src/cli/bin.js +4 -0
- package/dist/src/cli/bootstrap-extensions.js +5 -1
- package/dist/src/cli/bootstrap-extensions.js.map +1 -1
- package/dist/src/cli/cli-log-level-preset.d.ts +7 -0
- package/dist/src/cli/cli-log-level-preset.js +13 -0
- package/dist/src/cli/cli-log-level-preset.js.map +1 -0
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/agents.js +1 -1
- package/dist/src/cli/commands/config.js +2 -2
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway.js +1 -1
- package/dist/src/cli/commands/image.js +1 -1
- package/dist/src/cli/commands/init.js +4 -4
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/update.js +1 -1
- package/dist/src/cli/index.d.ts +0 -1
- package/dist/src/cli/index.js +1 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/utils/init-workspace.js +2 -2
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/profile.js +2 -2
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.d.ts +5 -0
- package/dist/src/extensions/loader.js +19 -3
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +2 -2
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/hono/lib/extension-store.js +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/config.js +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +1 -1
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +3 -3
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/lock.js +2 -2
- package/dist/src/gateway/service/run-gateway-agent.js +2 -2
- package/dist/src/gateway/service.js +5 -5
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-lock.js +3 -3
- package/dist/src/infra/update-runner.js +2 -2
- package/dist/src/infra/update-runner.js.map +1 -1
- package/dist/src/infra/update-startup.js +2 -2
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/config-store.js +2 -2
- package/dist/src/session/search-index-cache.js +1 -1
- package/dist/src/session/search-index.js +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.js +5 -5
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/tui/tui.js +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +1 -1
- package/package.json +7 -7
- package/dist/src/cli/agent-chat-log-level-preset.d.ts +0 -8
- package/dist/src/cli/agent-chat-log-level-preset.js +0 -25
- package/dist/src/cli/agent-chat-log-level-preset.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-page-
|
|
1
|
+
{"version":3,"file":"extension-page-CnOyLPrh.js","names":[],"sources":["../../../../../web/src/features/extensions/extension-page.tsx"],"sourcesContent":["/**\n * ExtensionPage — renders a full-page extension UI via ExtensionIframeHost.\n *\n * Mounted at /apps/:extensionId (or /apps/:extensionId/:pageId for multi-page extensions).\n */\n\nimport { useLayoutEffect } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { usePageHeaderStore } from '@/stores/page-header-store';\n\nimport { ExtensionIframeHost } from './extension-iframe-host';\nimport { useUiExtensions } from './extension-provider';\n\nexport function ExtensionPage() {\n const { extensionId, pageId } = useParams<{ extensionId: string; pageId?: string }>();\n const uiExtensions = useUiExtensions();\n const setPageHeader = usePageHeaderStore((s) => s.setPageHeader);\n const clearPageHeader = usePageHeaderStore((s) => s.clearPageHeader);\n\n const extension = extensionId ? uiExtensions.find((ext) => ext.id === extensionId) : undefined;\n const pages = extension?.ui?.contributions?.pages;\n const page =\n extensionId && pages?.length\n ? pageId\n ? pages.find((p) => p.id === pageId || p.id === `${extensionId}.${pageId}`)\n : pages[0]\n : undefined;\n\n useLayoutEffect(() => {\n if (!extensionId || !extension || !page) {\n clearPageHeader();\n return () => clearPageHeader();\n }\n const headline = page.title?.trim() || extension.name || extensionId;\n setPageHeader({\n startExtra: null,\n main: (\n <div className=\"w-full min-w-0 px-3 sm:px-5 xl:px-6\">\n <h1\n className=\"min-w-0 truncate text-base font-semibold tracking-tight text-fg\"\n title={headline}\n >\n {headline}\n </h1>\n </div>\n ),\n end: null,\n });\n return () => clearPageHeader();\n }, [clearPageHeader, extension, extensionId, page, setPageHeader]);\n\n if (!extensionId) {\n return <ExtensionPageNotFound message=\"No extension ID provided.\" />;\n }\n\n if (!extension) {\n return (\n <ExtensionPageNotFound message={`Extension \"${extensionId}\" not found or has no UI.`} />\n );\n }\n\n if (!pages?.length) {\n return (\n <ExtensionPageNotFound message={`Extension \"${extensionId}\" has no page contributions.`} />\n );\n }\n\n if (!page) {\n return (\n <ExtensionPageNotFound message={`Page \"${pageId}\" not found in extension \"${extensionId}\".`} />\n );\n }\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <ExtensionIframeHost\n extensionId={extensionId}\n extensionName={extension.name}\n entrypoint={page.entrypoint}\n permissions={extension.ui?.permissions}\n title={page.title}\n className=\"min-h-0 flex-1\"\n fixedHeight={undefined}\n maxHeight={99999}\n />\n </div>\n );\n}\n\nfunction ExtensionPageNotFound({ message }: { message: string }) {\n return (\n <div className=\"flex min-h-[min(40vh,16rem)] flex-1 items-center justify-center\">\n <div className=\"text-center\">\n <p className=\"text-sm text-fg-muted\">{message}</p>\n </div>\n </div>\n );\n}\n"],"mappings":"8LAcA,SAAgB,GAAgB,CAC9B,GAAM,CAAE,cAAa,UAAW,GAAqD,CAC/E,EAAe,GAAiB,CAChC,EAAgB,EAAoB,GAAM,EAAE,cAAc,CAC1D,EAAkB,EAAoB,GAAM,EAAE,gBAAgB,CAE9D,EAAY,EAAc,EAAa,KAAM,GAAQ,EAAI,KAAO,EAAY,CAAG,IAAA,GAC/E,EAAQ,GAAW,IAAI,eAAe,MACtC,EACJ,GAAe,GAAO,OAClB,EACE,EAAM,KAAM,GAAM,EAAE,KAAO,GAAU,EAAE,KAAO,GAAG,EAAY,GAAG,IAAS,CACzE,EAAM,GACR,IAAA,GA+CN,OA7CA,EAAA,EAAA,qBAAsB,CACpB,GAAI,CAAC,GAAe,CAAC,GAAa,CAAC,EAEjC,OADA,GAAiB,KACJ,GAAiB,CAEhC,IAAM,EAAW,EAAK,OAAO,MAAM,EAAI,EAAU,MAAQ,EAezD,OAdA,EAAc,CACZ,WAAY,KACZ,MACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,gDACb,EAAA,EAAA,KAAC,KAAD,CACE,UAAU,kEACV,MAAO,WAEN,EACE,CAAA,CACD,CAAA,CAER,IAAK,KACN,CAAC,KACW,GAAiB,EAC7B,CAAC,EAAiB,EAAW,EAAa,EAAM,EAAc,CAAC,CAE7D,EAIA,EAMA,GAAO,OAMP,GAOH,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,yCACb,EAAA,EAAA,KAAC,EAAD,CACe,cACb,cAAe,EAAU,KACzB,WAAY,EAAK,WACjB,YAAa,EAAU,IAAI,YAC3B,MAAO,EAAK,MACZ,UAAU,iBACV,YAAa,IAAA,GACb,UAAW,MACX,CAAA,CACE,CAAA,EAhBJ,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,SAAS,EAAO,4BAA4B,EAAY,IAAO,CAAA,EAN/F,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,cAAc,EAAY,8BAAiC,CAAA,EAN3F,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAS,cAAc,EAAY,2BAA8B,CAAA,EALnF,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAQ,4BAA8B,CAAA,CAqCxE,SAAS,EAAsB,CAAE,WAAgC,CAC/D,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,4EACb,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,wBACb,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCAAyB,EAAY,CAAA,CAC9C,CAAA,CACF,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{i as t,t as n}from"./vendor-react-DbimaAId.js";import{i as r}from"./vendor-swr-B5fPo7KK.js";import{Cn as i,In as a,Mn as o,Oi as s,Or as c,cn as l,d as u,dn as d,gn as f,hn as p,jn as m,vn as h,wi as g}from"./index-DRnjltLC.js";import{i as _,n as v,r as y,t as b}from"./use-image-provider-credentials-DlWCe2d_.js";var x=e(t(),1),S=n(),C=()=>d(_);function w(e){return{credentialsIntro:e.credentialsIntro,regionHint:e.regionHint,endpointPresetsHint:e.endpointPresetsHint,apiKeyLabel:e.apiKeyLabel,optionalPlaceholder:e.optionalPlaceholder,regionLabel:e.regionLabel,baseUrlLabel:e.baseUrlLabel,imageBaseUrlLabel:e.imageBaseUrlLabel,saveCredentials:e.saveCredentials,savingCredentials:e.savingCredentials,credentialsSaved:e.credentialsSaved,discardCredentials:e.discardCredentials,credentialsNothingToSave:e.credentialsNothingToSave,credentialsSaveError:e.credentialsSaveError,regionPresetDefault:e.regionPresetDefault,regionPresetCustom:e.regionPresetCustom,baseUrlPresetDefault:e.baseUrlPresetDefault,baseUrlPresetCustom:e.baseUrlPresetCustom,openExtensionSettings:e.openExtensionSettings,openImageModelsPage:e.openImageModelsPage,extensionSettingsLinkTitle:e.extensionSettingsLinkTitle,imageModelsLinkTitle:e.imageModelsLinkTitle,configured:e.configured,missingKey:e.missingKey,defaultModel:e.defaultModel,modelsLabel:e.modelsLabel,imageBaseUrlPresetHint:e.imageBaseUrlPresetHint,dashscopeRegion_beijing:e.dashscopeRegion_beijing,dashscopeRegion_singapore:e.dashscopeRegion_singapore,dashscopeRegion_us:e.dashscopeRegion_us,apiKeyMaskedHelp:e.apiKeyMaskedHelp,apiKeyCopy:e.apiKeyCopy,apiKeyCopied:e.apiKeyCopied,apiKeyShow:e.apiKeyShow,apiKeyHide:e.apiKeyHide,apiKeyNotInConfigFile:e.apiKeyNotInConfigFile,apiKeyRevealFailed:e.apiKeyRevealFailed,minimaxClusterLabel:e.minimaxClusterLabel,minimaxClusterHint:e.minimaxClusterHint,falQueueBaseLabel:e.falQueueBaseLabel,falQueueBaseHint:e.falQueueBaseHint}}function T({extensionId:e}){let t=i(e=>e.language),n=m(t),a=n.imageModelsSettings,o=h(e=>!!e.token),{data:s=[],isLoading:l}=r(o?C():null,y,{revalidateOnFocus:!1}),u=(0,x.useMemo)(()=>s.filter(t=>t.id===e),[s,e]),d=b(u),f=(0,x.useMemo)(()=>w(a),[a]),p=(0,x.useMemo)(()=>({getApiKey:n.providersSettings.getApiKey,getApiKeyIntl:n.providersSettings.getApiKeyIntl,getApiKeyCn:n.providersSettings.getApiKeyCn}),[n.providersSettings]);return o?l?(0,S.jsxs)(`div`,{className:`flex items-center gap-2 py-6 text-sm text-fg-muted`,children:[(0,S.jsx)(c,{className:`size-4 animate-spin`}),`…`]}):u.length===0?(0,S.jsx)(`p`,{className:`text-sm text-fg-muted`,children:t===`zh`?`网关未注册该图像 Provider,或扩展已被禁用。`:`This image provider is not registered on the gateway, or the extension is disabled.`}):(0,S.jsx)(v,{summaries:u,credDraft:d.credDraft,credDirty:d.credDirty,credSaving:d.credSaving,credError:d.credError,credSavedFlash:d.credSavedFlash,credNoopFlash:d.credNoopFlash,updateCredRow:d.updateCredRow,onDiscardCredentials:d.onDiscardCredentials,onSaveCredentials:()=>void d.saveCredentials(a.credentialsSaveError),extensionIds:new Set,showExtensionLinks:!1,showImageModelsLink:!1,language:t,apiKeyLinkLabels:p,messages:f}):null}function E(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function D(e){let t=e[`x-order`];return typeof t==`number`&&!Number.isNaN(t)?t:999}function O(e){let t=e[`x-group`];return typeof t==`string`&&t.length>0?t:``}function k(e){return e[`x-hidden`]===!0}function A(e){let t=e.properties;if(!E(t))return[];let n=[];for(let[e,r]of Object.entries(t))E(r)&&(k(r)||n.push({key:e,sub:r,order:D(r),group:O(r),hidden:!1}));return n.sort((e,t)=>e.order-t.order||e.key.localeCompare(t.key)),n}function j(e){let t=new Map;for(let n of e){let e=n.group;t.has(e)||t.set(e,[]),t.get(e).push(n)}return t}function M({name:e,s:t,value:n,onChange:r,disabled:i}){let a=typeof t.description==`string`?t.description:void 0,o=(typeof t[`x-placeholder`]==`string`?t[`x-placeholder`]:null)||a,s=t.format;return Array.isArray(t.enum)&&t.enum.every(e=>typeof e==`string`)?(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[a?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:a}):null,(0,S.jsx)(`select`,{name:e,className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2 text-sm text-fg`,value:n,disabled:i,onChange:e=>r(e.target.value),children:t.enum.map(e=>(0,S.jsx)(`option`,{value:e,children:e},e))})]}):(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[a?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:a}):null,(0,S.jsx)(`input`,{name:e,type:s===`password`?`password`:`text`,className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg placeholder:text-fg-muted/70`,value:n,placeholder:o,disabled:i,onChange:e=>r(e.target.value)})]})}function N({s:e,value:t,onChange:n,disabled:r}){let i=typeof e.description==`string`?e.description:void 0;return(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[i?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:i}):null,(0,S.jsx)(`input`,{type:`number`,className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg`,value:Number.isFinite(t)?t:0,disabled:r,onChange:e=>n(Number(e.target.value))})]})}function P({s:e,value:t,onChange:n,disabled:r}){let i=(typeof e.description==`string`?e.description:void 0)??(typeof e.title==`string`&&e.title.length>0?e.title:`Enable`);return(0,S.jsxs)(`label`,{className:`flex items-center gap-2 text-sm text-fg`,children:[(0,S.jsx)(`input`,{type:`checkbox`,className:`h-4 w-4 rounded border border-edge`,checked:t,disabled:r,onChange:e=>n(e.target.checked)}),(0,S.jsx)(`span`,{children:i})]})}function F({s:e,value:t,onChange:n,disabled:r}){let i=typeof e.description==`string`?e.description:void 0,a=e.items;if(!(E(a)&&a.type===`string`))return(0,S.jsx)(`p`,{className:`text-xs text-fg-muted`,children:`Unsupported array type`});let o=e=>{let r=e.trim();!r||t.includes(r)||n([...t,r])};return(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[i?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:i}):null,(0,S.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:t.map(e=>(0,S.jsxs)(`span`,{className:`inline-flex items-center gap-1 rounded-md border border-edge bg-surface-panel px-2 py-0.5 text-sm`,children:[e,(0,S.jsx)(`button`,{type:`button`,className:`text-fg-muted hover:text-fg`,disabled:r,onClick:()=>n(t.filter(t=>t!==e)),children:`×`})]},e))}),(0,S.jsx)(`input`,{className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm`,disabled:r,placeholder:`Add and press Enter`,onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),o(e.target.value),e.target.value=``)}})]})}function I({k:e,sub:t,value:n,onValue:r,disabled:i}){let a=t.type,o=(typeof t.title==`string`&&t.title.length>0?t.title:null)??e;if(a===`boolean`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(P,{s:t,value:n===!0,disabled:i,onChange:e=>r(e)})]});if(a===`number`||a===`integer`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(N,{s:t,value:typeof n==`number`?n:0,disabled:i,onChange:r})]});if(a===`string`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(M,{name:e,s:t,value:typeof n==`string`?n:``,disabled:i,onChange:r})]});if(a===`array`){let e=t.items;if(E(e)&&e.type===`string`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(F,{s:t,value:Array.isArray(n)&&n.every(e=>typeof e==`string`)?n:[],disabled:i,onChange:r})]})}return(0,S.jsxs)(`p`,{className:`text-xs text-fg-muted`,children:[o,`: unsupported field type`,typeof a==`string`?` (${a})`:``]})}function L({schema:e,values:t,onChange:n,disabled:r=!1,className:i}){let o=(0,x.useMemo)(()=>e.type===`object`?A(e):[],[e]),s=(0,x.useMemo)(()=>o.length===0?new Map:j(o),[o]),c=(0,x.useCallback)((e,r)=>{n({...t,[e]:r})},[n,t]);if(e.type!==`object`||!E(e.properties)||o.length===0)return null;let l=Array.from(s.keys()).sort((e,t)=>e===``?-1:t===``?1:e.localeCompare(t));return(0,S.jsx)(`div`,{className:a(`flex flex-col gap-4`,i),children:l.map(e=>{let n=(s.get(e)??[]).map(e=>(0,S.jsx)(I,{k:e.key,sub:e.sub,value:t[e.key],onValue:t=>c(e.key,t),disabled:r},e.key));return e===``?(0,S.jsx)(`div`,{children:n},`default`):(0,S.jsxs)(`details`,{className:`group rounded-lg border border-edge bg-surface-panel/40 open:bg-surface-base`,open:!0,children:[(0,S.jsx)(`summary`,{className:`cursor-pointer select-none px-3 py-2 text-sm font-medium text-fg group-open:rounded-b-none`,children:e}),(0,S.jsx)(`div`,{className:`space-y-4 border-t border-edge p-3`,children:n})]},e)})})}function R(e){let t={};if(e.type!==`object`||!E(e.properties))return t;for(let[n,r]of Object.entries(e.properties))E(r)&&Object.prototype.hasOwnProperty.call(r,`default`)&&(t[n]=r.default);return t}function z({extensionId:e}){let t=m(i(e=>e.language)).agentSettings,n=h(e=>!!e.token),{data:a,error:s}=r(n&&e?`ext-detail-${e}`:null,()=>f(d(`/api/extensions/${encodeURIComponent(e)}`))),{data:c,mutate:l,error:u}=r(n&&e?`ext-cfg-${e}`:null,()=>f(d(`/api/extensions/${encodeURIComponent(e)}/config`))),g=a?.manifest?.configSchema,_=(0,x.useMemo)(()=>g&&g.type===`object`?R(g):{},[g]),v=(0,x.useMemo)(()=>({..._,...c??{}}),[_,c]),[y,b]=(0,x.useState)({}),[C,w]=(0,x.useState)(!1),[T,E]=(0,x.useState)(!1),[D,O]=(0,x.useState)(null),[k,A]=(0,x.useState)(!1);(0,x.useEffect)(()=>{C||b(v)},[C,v]);let j=(0,x.useCallback)(e=>{b(e),w(!0),O(null)},[]),M=(0,x.useCallback)(()=>{b(v),w(!1),O(null)},[v]),N=(0,x.useCallback)(()=>{!g||g.type!==`object`||(b({...R(g)}),w(!0),O(null))},[g]),P=(0,x.useCallback)(async()=>{if(e){E(!0),O(null);try{let t=await p(d(`/api/extensions/${encodeURIComponent(e)}/config`),{method:`PATCH`,body:JSON.stringify(y)});if(!t.ok){let e=await t.json().catch(()=>({}));throw Error(e.error?.message??t.statusText)}await l(y,!1),w(!1),A(!0),window.setTimeout(()=>A(!1),3e3)}catch(e){O(e instanceof Error?e.message:String(e))}finally{E(!1)}}},[e,y,l]);if(!n)return null;if(s||u){let e=s??u;return(0,S.jsxs)(`p`,{className:`text-sm text-fg-muted`,children:[`Could not load extension settings: `,e instanceof Error?e.message:String(e)]})}return!g||g.type!==`object`?null:(0,S.jsxs)(`div`,{className:`mb-6 flex flex-col gap-3 rounded-xl border border-edge bg-surface-base p-4`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[(0,S.jsx)(`h2`,{className:`text-sm font-semibold text-fg`,children:`Configuration`}),(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[k?(0,S.jsx)(`span`,{className:`text-xs text-emerald-600 dark:text-emerald-400`,children:t.saved}):null,D?(0,S.jsx)(`span`,{className:`text-xs text-red-600 dark:text-red-400`,children:D}):null,(0,S.jsx)(o,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,disabled:!C,onClick:M,children:t.discard}),(0,S.jsx)(o,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,onClick:N,children:`Reset to defaults`}),(0,S.jsx)(o,{type:`button`,variant:`primary`,className:`h-8 text-xs`,disabled:!C||T,onClick:()=>void P(),children:T?t.saving:t.save})]})]}),(0,S.jsx)(L,{schema:g,values:y,onChange:j,disabled:T})]})}function B(){let e=m(i(e=>e.language)),t=e.extensionImageGen,{extensionId:n,panelId:r}=s(),a=l();if(!n)return(0,S.jsx)(V,{message:`No extension ID provided.`});let o=a.find(e=>e.id===n);if(!o)return(0,S.jsx)(V,{message:`Extension "${n}" not found or is not available in this workspace.`});let c=o.ui?.contributions?.settingsPanels,d=r?c?.find(e=>e.id===r||e.id===`${n}.${r}`):c?.[0],f=!!(d&&o.ui),p=!!o.hasConfigSchema,h=o.kind===`image-generation`;return!p&&!f&&!h?(0,S.jsx)(V,{message:`Extension "${n}" has no settings panels or config schema.`}):(0,S.jsxs)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:[(0,S.jsx)(`h1`,{className:`text-lg font-semibold text-fg`,children:d?.title??`${o.name} Settings`}),h?(0,S.jsxs)(`div`,{className:`flex flex-col gap-2 rounded-lg border border-edge-subtle bg-surface-base px-4 py-3 text-sm`,children:[(0,S.jsx)(`p`,{className:`leading-relaxed text-fg-muted`,children:t.banner}),(0,S.jsx)(g,{to:`/settings/image-models`,className:`w-fit font-medium text-accent hover:underline`,title:e.imageModelsSettings.imageModelsLinkTitle,children:t.openImageModels})]}):null,h?(0,S.jsx)(T,{extensionId:n}):null,p?(0,S.jsx)(z,{extensionId:n}):null,f&&d&&o.ui?(0,S.jsx)(`div`,{className:`overflow-hidden rounded-xl border border-edge bg-surface-base`,children:(0,S.jsx)(u,{extensionId:n,extensionName:o.name,entrypoint:d.entrypoint,permissions:o.ui?.permissions,title:d.title,className:`w-full`,minHeight:120,maxHeight:2e3})}):null]})}function V({message:e}){return(0,S.jsx)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:(0,S.jsx)(`p`,{className:`text-sm text-fg-muted`,children:e})})}export{B as ExtensionSettingsPage};
|
|
2
|
-
//# sourceMappingURL=extension-settings-page-
|
|
1
|
+
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{i as t,t as n}from"./vendor-react-DbimaAId.js";import{i as r}from"./vendor-swr-B5fPo7KK.js";import{Cn as i,In as a,Mn as o,Oi as s,Or as c,cn as l,d as u,dn as d,gn as f,hn as p,jn as m,vn as h,wi as g}from"./index-DeELk--t.js";import{i as _,n as v,r as y,t as b}from"./use-image-provider-credentials-DcP2SYn3.js";var x=e(t(),1),S=n(),C=()=>d(_);function w(e){return{credentialsIntro:e.credentialsIntro,regionHint:e.regionHint,endpointPresetsHint:e.endpointPresetsHint,apiKeyLabel:e.apiKeyLabel,optionalPlaceholder:e.optionalPlaceholder,regionLabel:e.regionLabel,baseUrlLabel:e.baseUrlLabel,imageBaseUrlLabel:e.imageBaseUrlLabel,saveCredentials:e.saveCredentials,savingCredentials:e.savingCredentials,credentialsSaved:e.credentialsSaved,discardCredentials:e.discardCredentials,credentialsNothingToSave:e.credentialsNothingToSave,credentialsSaveError:e.credentialsSaveError,regionPresetDefault:e.regionPresetDefault,regionPresetCustom:e.regionPresetCustom,baseUrlPresetDefault:e.baseUrlPresetDefault,baseUrlPresetCustom:e.baseUrlPresetCustom,openExtensionSettings:e.openExtensionSettings,openImageModelsPage:e.openImageModelsPage,extensionSettingsLinkTitle:e.extensionSettingsLinkTitle,imageModelsLinkTitle:e.imageModelsLinkTitle,configured:e.configured,missingKey:e.missingKey,defaultModel:e.defaultModel,modelsLabel:e.modelsLabel,imageBaseUrlPresetHint:e.imageBaseUrlPresetHint,dashscopeRegion_beijing:e.dashscopeRegion_beijing,dashscopeRegion_singapore:e.dashscopeRegion_singapore,dashscopeRegion_us:e.dashscopeRegion_us,apiKeyMaskedHelp:e.apiKeyMaskedHelp,apiKeyCopy:e.apiKeyCopy,apiKeyCopied:e.apiKeyCopied,apiKeyShow:e.apiKeyShow,apiKeyHide:e.apiKeyHide,apiKeyNotInConfigFile:e.apiKeyNotInConfigFile,apiKeyRevealFailed:e.apiKeyRevealFailed,minimaxClusterLabel:e.minimaxClusterLabel,minimaxClusterHint:e.minimaxClusterHint,falQueueBaseLabel:e.falQueueBaseLabel,falQueueBaseHint:e.falQueueBaseHint}}function T({extensionId:e}){let t=i(e=>e.language),n=m(t),a=n.imageModelsSettings,o=h(e=>!!e.token),{data:s=[],isLoading:l}=r(o?C():null,y,{revalidateOnFocus:!1}),u=(0,x.useMemo)(()=>s.filter(t=>t.id===e),[s,e]),d=b(u),f=(0,x.useMemo)(()=>w(a),[a]),p=(0,x.useMemo)(()=>({getApiKey:n.providersSettings.getApiKey,getApiKeyIntl:n.providersSettings.getApiKeyIntl,getApiKeyCn:n.providersSettings.getApiKeyCn}),[n.providersSettings]);return o?l?(0,S.jsxs)(`div`,{className:`flex items-center gap-2 py-6 text-sm text-fg-muted`,children:[(0,S.jsx)(c,{className:`size-4 animate-spin`}),`…`]}):u.length===0?(0,S.jsx)(`p`,{className:`text-sm text-fg-muted`,children:t===`zh`?`网关未注册该图像 Provider,或扩展已被禁用。`:`This image provider is not registered on the gateway, or the extension is disabled.`}):(0,S.jsx)(v,{summaries:u,credDraft:d.credDraft,credDirty:d.credDirty,credSaving:d.credSaving,credError:d.credError,credSavedFlash:d.credSavedFlash,credNoopFlash:d.credNoopFlash,updateCredRow:d.updateCredRow,onDiscardCredentials:d.onDiscardCredentials,onSaveCredentials:()=>void d.saveCredentials(a.credentialsSaveError),extensionIds:new Set,showExtensionLinks:!1,showImageModelsLink:!1,language:t,apiKeyLinkLabels:p,messages:f}):null}function E(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function D(e){let t=e[`x-order`];return typeof t==`number`&&!Number.isNaN(t)?t:999}function O(e){let t=e[`x-group`];return typeof t==`string`&&t.length>0?t:``}function k(e){return e[`x-hidden`]===!0}function A(e){let t=e.properties;if(!E(t))return[];let n=[];for(let[e,r]of Object.entries(t))E(r)&&(k(r)||n.push({key:e,sub:r,order:D(r),group:O(r),hidden:!1}));return n.sort((e,t)=>e.order-t.order||e.key.localeCompare(t.key)),n}function j(e){let t=new Map;for(let n of e){let e=n.group;t.has(e)||t.set(e,[]),t.get(e).push(n)}return t}function M({name:e,s:t,value:n,onChange:r,disabled:i}){let a=typeof t.description==`string`?t.description:void 0,o=(typeof t[`x-placeholder`]==`string`?t[`x-placeholder`]:null)||a,s=t.format;return Array.isArray(t.enum)&&t.enum.every(e=>typeof e==`string`)?(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[a?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:a}):null,(0,S.jsx)(`select`,{name:e,className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2 text-sm text-fg`,value:n,disabled:i,onChange:e=>r(e.target.value),children:t.enum.map(e=>(0,S.jsx)(`option`,{value:e,children:e},e))})]}):(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[a?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:a}):null,(0,S.jsx)(`input`,{name:e,type:s===`password`?`password`:`text`,className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg placeholder:text-fg-muted/70`,value:n,placeholder:o,disabled:i,onChange:e=>r(e.target.value)})]})}function N({s:e,value:t,onChange:n,disabled:r}){let i=typeof e.description==`string`?e.description:void 0;return(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[i?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:i}):null,(0,S.jsx)(`input`,{type:`number`,className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg`,value:Number.isFinite(t)?t:0,disabled:r,onChange:e=>n(Number(e.target.value))})]})}function P({s:e,value:t,onChange:n,disabled:r}){let i=(typeof e.description==`string`?e.description:void 0)??(typeof e.title==`string`&&e.title.length>0?e.title:`Enable`);return(0,S.jsxs)(`label`,{className:`flex items-center gap-2 text-sm text-fg`,children:[(0,S.jsx)(`input`,{type:`checkbox`,className:`h-4 w-4 rounded border border-edge`,checked:t,disabled:r,onChange:e=>n(e.target.checked)}),(0,S.jsx)(`span`,{children:i})]})}function F({s:e,value:t,onChange:n,disabled:r}){let i=typeof e.description==`string`?e.description:void 0,a=e.items;if(!(E(a)&&a.type===`string`))return(0,S.jsx)(`p`,{className:`text-xs text-fg-muted`,children:`Unsupported array type`});let o=e=>{let r=e.trim();!r||t.includes(r)||n([...t,r])};return(0,S.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[i?(0,S.jsx)(`label`,{className:`text-xs text-fg-muted`,children:i}):null,(0,S.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:t.map(e=>(0,S.jsxs)(`span`,{className:`inline-flex items-center gap-1 rounded-md border border-edge bg-surface-panel px-2 py-0.5 text-sm`,children:[e,(0,S.jsx)(`button`,{type:`button`,className:`text-fg-muted hover:text-fg`,disabled:r,onClick:()=>n(t.filter(t=>t!==e)),children:`×`})]},e))}),(0,S.jsx)(`input`,{className:`ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm`,disabled:r,placeholder:`Add and press Enter`,onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),o(e.target.value),e.target.value=``)}})]})}function I({k:e,sub:t,value:n,onValue:r,disabled:i}){let a=t.type,o=(typeof t.title==`string`&&t.title.length>0?t.title:null)??e;if(a===`boolean`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(P,{s:t,value:n===!0,disabled:i,onChange:e=>r(e)})]});if(a===`number`||a===`integer`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(N,{s:t,value:typeof n==`number`?n:0,disabled:i,onChange:r})]});if(a===`string`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(M,{name:e,s:t,value:typeof n==`string`?n:``,disabled:i,onChange:r})]});if(a===`array`){let e=t.items;if(E(e)&&e.type===`string`)return(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:o}),(0,S.jsx)(F,{s:t,value:Array.isArray(n)&&n.every(e=>typeof e==`string`)?n:[],disabled:i,onChange:r})]})}return(0,S.jsxs)(`p`,{className:`text-xs text-fg-muted`,children:[o,`: unsupported field type`,typeof a==`string`?` (${a})`:``]})}function L({schema:e,values:t,onChange:n,disabled:r=!1,className:i}){let o=(0,x.useMemo)(()=>e.type===`object`?A(e):[],[e]),s=(0,x.useMemo)(()=>o.length===0?new Map:j(o),[o]),c=(0,x.useCallback)((e,r)=>{n({...t,[e]:r})},[n,t]);if(e.type!==`object`||!E(e.properties)||o.length===0)return null;let l=Array.from(s.keys()).sort((e,t)=>e===``?-1:t===``?1:e.localeCompare(t));return(0,S.jsx)(`div`,{className:a(`flex flex-col gap-4`,i),children:l.map(e=>{let n=(s.get(e)??[]).map(e=>(0,S.jsx)(I,{k:e.key,sub:e.sub,value:t[e.key],onValue:t=>c(e.key,t),disabled:r},e.key));return e===``?(0,S.jsx)(`div`,{children:n},`default`):(0,S.jsxs)(`details`,{className:`group rounded-lg border border-edge bg-surface-panel/40 open:bg-surface-base`,open:!0,children:[(0,S.jsx)(`summary`,{className:`cursor-pointer select-none px-3 py-2 text-sm font-medium text-fg group-open:rounded-b-none`,children:e}),(0,S.jsx)(`div`,{className:`space-y-4 border-t border-edge p-3`,children:n})]},e)})})}function R(e){let t={};if(e.type!==`object`||!E(e.properties))return t;for(let[n,r]of Object.entries(e.properties))E(r)&&Object.prototype.hasOwnProperty.call(r,`default`)&&(t[n]=r.default);return t}function z({extensionId:e}){let t=m(i(e=>e.language)).agentSettings,n=h(e=>!!e.token),{data:a,error:s}=r(n&&e?`ext-detail-${e}`:null,()=>f(d(`/api/extensions/${encodeURIComponent(e)}`))),{data:c,mutate:l,error:u}=r(n&&e?`ext-cfg-${e}`:null,()=>f(d(`/api/extensions/${encodeURIComponent(e)}/config`))),g=a?.manifest?.configSchema,_=(0,x.useMemo)(()=>g&&g.type===`object`?R(g):{},[g]),v=(0,x.useMemo)(()=>({..._,...c??{}}),[_,c]),[y,b]=(0,x.useState)({}),[C,w]=(0,x.useState)(!1),[T,E]=(0,x.useState)(!1),[D,O]=(0,x.useState)(null),[k,A]=(0,x.useState)(!1);(0,x.useEffect)(()=>{C||b(v)},[C,v]);let j=(0,x.useCallback)(e=>{b(e),w(!0),O(null)},[]),M=(0,x.useCallback)(()=>{b(v),w(!1),O(null)},[v]),N=(0,x.useCallback)(()=>{!g||g.type!==`object`||(b({...R(g)}),w(!0),O(null))},[g]),P=(0,x.useCallback)(async()=>{if(e){E(!0),O(null);try{let t=await p(d(`/api/extensions/${encodeURIComponent(e)}/config`),{method:`PATCH`,body:JSON.stringify(y)});if(!t.ok){let e=await t.json().catch(()=>({}));throw Error(e.error?.message??t.statusText)}await l(y,!1),w(!1),A(!0),window.setTimeout(()=>A(!1),3e3)}catch(e){O(e instanceof Error?e.message:String(e))}finally{E(!1)}}},[e,y,l]);if(!n)return null;if(s||u){let e=s??u;return(0,S.jsxs)(`p`,{className:`text-sm text-fg-muted`,children:[`Could not load extension settings: `,e instanceof Error?e.message:String(e)]})}return!g||g.type!==`object`?null:(0,S.jsxs)(`div`,{className:`mb-6 flex flex-col gap-3 rounded-xl border border-edge bg-surface-base p-4`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[(0,S.jsx)(`h2`,{className:`text-sm font-semibold text-fg`,children:`Configuration`}),(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[k?(0,S.jsx)(`span`,{className:`text-xs text-emerald-600 dark:text-emerald-400`,children:t.saved}):null,D?(0,S.jsx)(`span`,{className:`text-xs text-red-600 dark:text-red-400`,children:D}):null,(0,S.jsx)(o,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,disabled:!C,onClick:M,children:t.discard}),(0,S.jsx)(o,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,onClick:N,children:`Reset to defaults`}),(0,S.jsx)(o,{type:`button`,variant:`primary`,className:`h-8 text-xs`,disabled:!C||T,onClick:()=>void P(),children:T?t.saving:t.save})]})]}),(0,S.jsx)(L,{schema:g,values:y,onChange:j,disabled:T})]})}function B(){let e=m(i(e=>e.language)),t=e.extensionImageGen,{extensionId:n,panelId:r}=s(),a=l();if(!n)return(0,S.jsx)(V,{message:`No extension ID provided.`});let o=a.find(e=>e.id===n);if(!o)return(0,S.jsx)(V,{message:`Extension "${n}" not found or is not available in this workspace.`});let c=o.ui?.contributions?.settingsPanels,d=r?c?.find(e=>e.id===r||e.id===`${n}.${r}`):c?.[0],f=!!(d&&o.ui),p=!!o.hasConfigSchema,h=o.kind===`image-generation`;return!p&&!f&&!h?(0,S.jsx)(V,{message:`Extension "${n}" has no settings panels or config schema.`}):(0,S.jsxs)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:[(0,S.jsx)(`h1`,{className:`text-lg font-semibold text-fg`,children:d?.title??`${o.name} Settings`}),h?(0,S.jsxs)(`div`,{className:`flex flex-col gap-2 rounded-lg border border-edge-subtle bg-surface-base px-4 py-3 text-sm`,children:[(0,S.jsx)(`p`,{className:`leading-relaxed text-fg-muted`,children:t.banner}),(0,S.jsx)(g,{to:`/settings/image-models`,className:`w-fit font-medium text-accent hover:underline`,title:e.imageModelsSettings.imageModelsLinkTitle,children:t.openImageModels})]}):null,h?(0,S.jsx)(T,{extensionId:n}):null,p?(0,S.jsx)(z,{extensionId:n}):null,f&&d&&o.ui?(0,S.jsx)(`div`,{className:`overflow-hidden rounded-xl border border-edge bg-surface-base`,children:(0,S.jsx)(u,{extensionId:n,extensionName:o.name,entrypoint:d.entrypoint,permissions:o.ui?.permissions,title:d.title,className:`w-full`,minHeight:120,maxHeight:2e3})}):null]})}function V({message:e}){return(0,S.jsx)(`div`,{className:`mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8`,children:(0,S.jsx)(`p`,{className:`text-sm text-fg-muted`,children:e})})}export{B as ExtensionSettingsPage};
|
|
2
|
+
//# sourceMappingURL=extension-settings-page-BOHn3S1a.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-settings-page-f8jERrXK.js","names":[],"sources":["../../../../../web/src/features/settings/extension-image-provider-settings.tsx","../../../../../web/src/components/ui/schema-form.tsx","../../../../../web/src/features/extensions/extension-auto-settings.tsx","../../../../../web/src/features/extensions/extension-settings-page.tsx"],"sourcesContent":["import { Loader2 } from 'lucide-react';\nimport { useMemo } from 'react';\nimport useSWR from 'swr';\n\nimport { fetchImageProvidersList } from '@/features/settings/fetch-image-providers';\nimport {\n ImageProviderCredentialsPanel,\n type ImageProviderCredentialsPanelMessages,\n} from '@/features/settings/image-provider-credentials-panel';\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport { useImageProviderCredentials } from '@/features/settings/use-image-provider-credentials';\nimport { apiUrl } from '@/lib/url';\nimport { messages, type MessageBundle } from '@/i18n/messages';\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { useLocaleStore } from '@/stores/locale-store';\n\nconst imageProvidersSwrKey = () => apiUrl(IMAGE_PROVIDERS_SWR_KEY);\n\nfunction panelMessagesFromBundle(t: MessageBundle['imageModelsSettings']): ImageProviderCredentialsPanelMessages {\n return {\n credentialsIntro: t.credentialsIntro,\n regionHint: t.regionHint,\n endpointPresetsHint: t.endpointPresetsHint,\n apiKeyLabel: t.apiKeyLabel,\n optionalPlaceholder: t.optionalPlaceholder,\n regionLabel: t.regionLabel,\n baseUrlLabel: t.baseUrlLabel,\n imageBaseUrlLabel: t.imageBaseUrlLabel,\n saveCredentials: t.saveCredentials,\n savingCredentials: t.savingCredentials,\n credentialsSaved: t.credentialsSaved,\n discardCredentials: t.discardCredentials,\n credentialsNothingToSave: t.credentialsNothingToSave,\n credentialsSaveError: t.credentialsSaveError,\n regionPresetDefault: t.regionPresetDefault,\n regionPresetCustom: t.regionPresetCustom,\n baseUrlPresetDefault: t.baseUrlPresetDefault,\n baseUrlPresetCustom: t.baseUrlPresetCustom,\n openExtensionSettings: t.openExtensionSettings,\n openImageModelsPage: t.openImageModelsPage,\n extensionSettingsLinkTitle: t.extensionSettingsLinkTitle,\n imageModelsLinkTitle: t.imageModelsLinkTitle,\n configured: t.configured,\n missingKey: t.missingKey,\n defaultModel: t.defaultModel,\n modelsLabel: t.modelsLabel,\n imageBaseUrlPresetHint: t.imageBaseUrlPresetHint,\n dashscopeRegion_beijing: t.dashscopeRegion_beijing,\n dashscopeRegion_singapore: t.dashscopeRegion_singapore,\n dashscopeRegion_us: t.dashscopeRegion_us,\n apiKeyMaskedHelp: t.apiKeyMaskedHelp,\n apiKeyCopy: t.apiKeyCopy,\n apiKeyCopied: t.apiKeyCopied,\n apiKeyShow: t.apiKeyShow,\n apiKeyHide: t.apiKeyHide,\n apiKeyNotInConfigFile: t.apiKeyNotInConfigFile,\n apiKeyRevealFailed: t.apiKeyRevealFailed,\n minimaxClusterLabel: t.minimaxClusterLabel,\n minimaxClusterHint: t.minimaxClusterHint,\n falQueueBaseLabel: t.falQueueBaseLabel,\n falQueueBaseHint: t.falQueueBaseHint,\n };\n}\n\nexport function ExtensionImageProviderSettings({ extensionId }: { extensionId: string }) {\n const language = useLocaleStore((s) => s.language);\n const m = messages(language);\n const t = m.imageModelsSettings;\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n\n const { data: all = [], isLoading } = useSWR(\n hasToken ? imageProvidersSwrKey() : null,\n fetchImageProvidersList,\n { revalidateOnFocus: false },\n );\n\n const summaries = useMemo(\n () => all.filter((p) => p.id === extensionId),\n [all, extensionId],\n );\n\n const cred = useImageProviderCredentials(summaries);\n const panelMsg = useMemo(() => panelMessagesFromBundle(t), [t]);\n const apiKeyLinkLabels = useMemo(\n () => ({\n getApiKey: m.providersSettings.getApiKey,\n getApiKeyIntl: m.providersSettings.getApiKeyIntl,\n getApiKeyCn: m.providersSettings.getApiKeyCn,\n }),\n [m.providersSettings],\n );\n\n if (!hasToken) {\n return null;\n }\n\n if (isLoading) {\n return (\n <div className=\"flex items-center gap-2 py-6 text-sm text-fg-muted\">\n <Loader2 className=\"size-4 animate-spin\" />\n …\n </div>\n );\n }\n\n if (summaries.length === 0) {\n return (\n <p className=\"text-sm text-fg-muted\">\n {language === 'zh'\n ? '网关未注册该图像 Provider,或扩展已被禁用。'\n : 'This image provider is not registered on the gateway, or the extension is disabled.'}\n </p>\n );\n }\n\n return (\n <ImageProviderCredentialsPanel\n summaries={summaries}\n credDraft={cred.credDraft}\n credDirty={cred.credDirty}\n credSaving={cred.credSaving}\n credError={cred.credError}\n credSavedFlash={cred.credSavedFlash}\n credNoopFlash={cred.credNoopFlash}\n updateCredRow={cred.updateCredRow}\n onDiscardCredentials={cred.onDiscardCredentials}\n onSaveCredentials={() => void cred.saveCredentials(t.credentialsSaveError)}\n extensionIds={new Set()}\n showExtensionLinks={false}\n showImageModelsLink={false}\n language={language}\n apiKeyLinkLabels={apiKeyLinkLabels}\n messages={panelMsg}\n />\n );\n}\n","import { useCallback, useMemo } from 'react';\n\nimport { cn } from '@/lib/cn';\n\nexport type JsonSchema = Record<string, unknown>;\n\ntype SchemaFormProps = {\n schema: JsonSchema;\n values: Record<string, unknown>;\n onChange: (values: Record<string, unknown>) => void;\n disabled?: boolean;\n className?: string;\n};\n\nfunction isRecord(x: unknown): x is Record<string, unknown> {\n return typeof x === 'object' && x !== null && !Array.isArray(x);\n}\n\ntype FieldDef = {\n key: string;\n sub: JsonSchema;\n order: number;\n group: string;\n hidden: boolean;\n};\n\nfunction getXOrder(s: JsonSchema): number {\n const v = s['x-order'];\n return typeof v === 'number' && !Number.isNaN(v) ? v : 999;\n}\n\nfunction getXGroup(s: JsonSchema): string {\n const v = s['x-group'];\n return typeof v === 'string' && v.length > 0 ? v : '';\n}\n\nfunction isHidden(s: JsonSchema): boolean {\n return s['x-hidden'] === true;\n}\n\nfunction sortedFields(schema: JsonSchema): FieldDef[] {\n const props = schema.properties;\n if (!isRecord(props)) return [];\n const out: FieldDef[] = [];\n for (const [key, sub] of Object.entries(props)) {\n if (!isRecord(sub)) continue;\n if (isHidden(sub)) continue;\n out.push({\n key,\n sub: sub,\n order: getXOrder(sub),\n group: getXGroup(sub),\n hidden: false,\n });\n }\n out.sort((a, b) => a.order - b.order || a.key.localeCompare(b.key));\n return out;\n}\n\nfunction groupFields(fields: FieldDef[]): Map<string, FieldDef[]> {\n const m = new Map<string, FieldDef[]>();\n for (const f of fields) {\n const g = f.group;\n if (!m.has(g)) m.set(g, []);\n m.get(g)!.push(f);\n }\n return m;\n}\n\nfunction StringField({\n name,\n s,\n value,\n onChange,\n disabled,\n}: {\n name: string;\n s: JsonSchema;\n value: string;\n onChange: (v: string) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n const placeholder =\n (typeof s['x-placeholder'] === 'string' ? s['x-placeholder'] : null) || desc;\n const fmt = s.format;\n if (Array.isArray(s.enum) && s.enum.every((x) => typeof x === 'string')) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <select\n name={name}\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2 text-sm text-fg\"\n value={value}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n >\n {(s.enum).map((op) => (\n <option key={op} value={op}>\n {op}\n </option>\n ))}\n </select>\n </div>\n );\n }\n const inputType = fmt === 'password' ? 'password' : 'text';\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <input\n name={name}\n type={inputType}\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg placeholder:text-fg-muted/70\"\n value={value}\n placeholder={placeholder}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n />\n </div>\n );\n}\n\nfunction NumberField({\n s,\n value,\n onChange,\n disabled,\n}: {\n s: JsonSchema;\n value: number;\n onChange: (v: number) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <input\n type=\"number\"\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg\"\n value={Number.isFinite(value) ? value : 0}\n disabled={disabled}\n onChange={(e) => onChange(Number(e.target.value))}\n />\n </div>\n );\n}\n\nfunction BooleanField({\n s,\n value,\n onChange,\n disabled,\n}: {\n s: JsonSchema;\n value: boolean;\n onChange: (v: boolean) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n const labelText =\n desc ?? (typeof s.title === 'string' && s.title.length > 0 ? s.title : 'Enable');\n return (\n <label className=\"flex items-center gap-2 text-sm text-fg\">\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border border-edge\"\n checked={value}\n disabled={disabled}\n onChange={(e) => onChange(e.target.checked)}\n />\n <span>{labelText}</span>\n </label>\n );\n}\n\nfunction ArrayStringField({\n s,\n value,\n onChange,\n disabled,\n}: {\n s: JsonSchema;\n value: string[];\n onChange: (v: string[]) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n const items = s.items;\n const isStringItems = isRecord(items) && items.type === 'string';\n if (!isStringItems) {\n return <p className=\"text-xs text-fg-muted\">Unsupported array type</p>;\n }\n const add = (t: string) => {\n const n = t.trim();\n if (!n || value.includes(n)) return;\n onChange([...value, n]);\n };\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <div className=\"flex flex-wrap gap-1\">\n {value.map((t) => (\n <span\n key={t}\n className=\"inline-flex items-center gap-1 rounded-md border border-edge bg-surface-panel px-2 py-0.5 text-sm\"\n >\n {t}\n <button\n type=\"button\"\n className=\"text-fg-muted hover:text-fg\"\n disabled={disabled}\n onClick={() => onChange(value.filter((x) => x !== t))}\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm\"\n disabled={disabled}\n placeholder=\"Add and press Enter\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n add((e.target as HTMLInputElement).value);\n (e.target as HTMLInputElement).value = '';\n }\n }}\n />\n </div>\n );\n}\n\nfunction FieldRow({\n k,\n sub,\n value,\n onValue,\n disabled,\n}: {\n k: string;\n sub: JsonSchema;\n value: unknown;\n onValue: (next: unknown) => void;\n disabled: boolean;\n}) {\n const t = sub.type;\n const title =\n (typeof sub.title === 'string' && sub.title.length > 0 ? sub.title : null) ?? k;\n if (t === 'boolean') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <BooleanField\n s={sub}\n value={value === true}\n disabled={disabled}\n onChange={(b) => onValue(b)}\n />\n </div>\n );\n }\n if (t === 'number' || t === 'integer') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <NumberField\n s={sub}\n value={typeof value === 'number' ? value : 0}\n disabled={disabled}\n onChange={onValue}\n />\n </div>\n );\n }\n if (t === 'string') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <StringField\n name={k}\n s={sub}\n value={typeof value === 'string' ? value : ''}\n disabled={disabled}\n onChange={onValue}\n />\n </div>\n );\n }\n if (t === 'array') {\n const items = sub.items;\n if (isRecord(items) && items.type === 'string') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <ArrayStringField\n s={sub}\n value={Array.isArray(value) && value.every((x) => typeof x === 'string') ? value : []}\n disabled={disabled}\n onChange={onValue}\n />\n </div>\n );\n }\n }\n return (\n <p className=\"text-xs text-fg-muted\">\n {title}: unsupported field type\n {typeof t === 'string' ? ` (${t})` : ''}\n </p>\n );\n}\n\n/**\n * Renders a JSON Schema (type: object) as a form using Gateway Console design tokens.\n */\nexport function SchemaForm({ schema, values, onChange, disabled = false, className }: SchemaFormProps) {\n const fields = useMemo(() => {\n if (schema.type !== 'object') return [];\n return sortedFields(schema);\n }, [schema]);\n\n const grouped = useMemo(() => {\n if (fields.length === 0) return new Map<string, FieldDef[]>();\n return groupFields(fields);\n }, [fields]);\n\n const setKey = useCallback(\n (key: string, next: unknown) => {\n onChange({ ...values, [key]: next });\n },\n [onChange, values],\n );\n\n if (schema.type !== 'object' || !isRecord(schema.properties) || fields.length === 0) {\n return null;\n }\n\n const groupKeys = Array.from(grouped.keys()).sort((a, b) => {\n if (a === '') return -1;\n if (b === '') return 1;\n return a.localeCompare(b);\n });\n\n return (\n <div className={cn('flex flex-col gap-4', className)}>\n {groupKeys.map((gk) => {\n const list = grouped.get(gk) ?? [];\n const block = list.map((f) => (\n <FieldRow\n key={f.key}\n k={f.key}\n sub={f.sub}\n value={values[f.key]}\n onValue={(v) => setKey(f.key, v)}\n disabled={disabled}\n />\n ));\n if (gk === '') {\n return <div key=\"default\">{block}</div>;\n }\n return (\n <details\n key={gk}\n className=\"group rounded-lg border border-edge bg-surface-panel/40 open:bg-surface-base\"\n open\n >\n <summary className=\"cursor-pointer select-none px-3 py-2 text-sm font-medium text-fg group-open:rounded-b-none\">\n {gk}\n </summary>\n <div className=\"space-y-4 border-t border-edge p-3\">{block}</div>\n </details>\n );\n })}\n </div>\n );\n}\n\n/** Extract per-key defaults from a JSON object schema for \"Reset\" actions. */\nexport function extractObjectDefaults(\n schema: JsonSchema,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (schema.type !== 'object' || !isRecord(schema.properties)) {\n return out;\n }\n for (const [k, sub] of Object.entries(schema.properties)) {\n if (!isRecord(sub)) continue;\n if (Object.prototype.hasOwnProperty.call(sub, 'default')) {\n out[k] = sub.default;\n }\n }\n return out;\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport useSWR from 'swr';\n\nimport { Button } from '@/components/ui/button';\nimport { extractObjectDefaults, SchemaForm, type JsonSchema } from '@/components/ui/schema-form';\nimport { apiFetch, fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { messages } from '@/i18n/messages';\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { useLocaleStore } from '@/stores/locale-store';\n\ntype ExtensionDetailResponse = {\n manifest: { configSchema?: JsonSchema };\n};\n\nexport function ExtensionAutoSettings({ extensionId }: { extensionId: string }) {\n const language = useLocaleStore((s) => s.language);\n const a = messages(language).agentSettings;\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const { data: detail, error: detailError } = useSWR(\n hasToken && extensionId ? `ext-detail-${extensionId}` : null,\n () => fetchJson<ExtensionDetailResponse>(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}`)),\n );\n\n const { data: remoteConfig, mutate: mutateConfig, error: configError } = useSWR(\n hasToken && extensionId ? `ext-cfg-${extensionId}` : null,\n () =>\n fetchJson<Record<string, unknown>>(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`),\n ),\n );\n\n const schema = detail?.manifest?.configSchema;\n const defaults = useMemo(\n () => (schema && schema.type === 'object' ? extractObjectDefaults(schema) : {}),\n [schema],\n );\n\n const savedValues = useMemo(\n () => ({ ...defaults, ...(remoteConfig ?? {}) }),\n [defaults, remoteConfig],\n );\n\n const [localValues, setLocalValues] = useState<Record<string, unknown>>({});\n const [isDirty, setIsDirty] = useState(false);\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saveSuccess, setSaveSuccess] = useState(false);\n\n useEffect(() => {\n if (isDirty) return;\n setLocalValues(savedValues);\n }, [isDirty, savedValues]);\n\n const onChange = useCallback((next: Record<string, unknown>) => {\n setLocalValues(next);\n setIsDirty(true);\n setSaveError(null);\n }, []);\n\n const handleDiscard = useCallback(() => {\n setLocalValues(savedValues);\n setIsDirty(false);\n setSaveError(null);\n }, [savedValues]);\n\n const handleResetDefaults = useCallback(() => {\n if (!schema || schema.type !== 'object') return;\n setLocalValues({ ...extractObjectDefaults(schema) });\n setIsDirty(true);\n setSaveError(null);\n }, [schema]);\n\n const handleSave = useCallback(async () => {\n if (!extensionId) return;\n setSaving(true);\n setSaveError(null);\n try {\n const res = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`),\n { method: 'PATCH', body: JSON.stringify(localValues) },\n );\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n throw new Error(body.error?.message ?? res.statusText);\n }\n await mutateConfig(localValues, false);\n setIsDirty(false);\n setSaveSuccess(true);\n window.setTimeout(() => setSaveSuccess(false), 3000);\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : String(e));\n } finally {\n setSaving(false);\n }\n }, [extensionId, localValues, mutateConfig]);\n\n if (!hasToken) {\n return null;\n }\n\n if (detailError || configError) {\n const err = (detailError ?? configError) as Error;\n return (\n <p className=\"text-sm text-fg-muted\">\n Could not load extension settings: {err instanceof Error ? err.message : String(err)}\n </p>\n );\n }\n\n if (!schema || schema.type !== 'object') {\n return null;\n }\n\n return (\n <div className=\"mb-6 flex flex-col gap-3 rounded-xl border border-edge bg-surface-base p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-fg\">Configuration</h2>\n <div className=\"flex flex-wrap items-center gap-2\">\n {saveSuccess ? (\n <span className=\"text-xs text-emerald-600 dark:text-emerald-400\">{a.saved}</span>\n ) : null}\n {saveError ? <span className=\"text-xs text-red-600 dark:text-red-400\">{saveError}</span> : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"h-8 text-xs\"\n disabled={!isDirty}\n onClick={handleDiscard}\n >\n {a.discard}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"h-8 text-xs\"\n onClick={handleResetDefaults}\n >\n Reset to defaults\n </Button>\n <Button\n type=\"button\"\n variant=\"primary\"\n className=\"h-8 text-xs\"\n disabled={!isDirty || saving}\n onClick={() => void handleSave()}\n >\n {saving ? a.saving : a.save}\n </Button>\n </div>\n </div>\n <SchemaForm schema={schema} values={localValues} onChange={onChange} disabled={saving} />\n </div>\n );\n}\n","/**\n * Extension settings: auto-generated config form (configSchema) + optional iframe (settingsPanels).\n *\n * Routes: /settings/ext/:extensionId, /settings/ext/:extensionId/:panelId\n */\n\nimport { Link, useParams } from 'react-router-dom';\n\nimport { ExtensionImageProviderSettings } from '@/features/settings/extension-image-provider-settings';\nimport { messages } from '@/i18n/messages';\nimport { useLocaleStore } from '@/stores/locale-store';\n\nimport { ExtensionAutoSettings } from './extension-auto-settings';\nimport { ExtensionIframeHost } from './extension-iframe-host';\nimport { useExtensions } from './extension-provider';\n\nexport function ExtensionSettingsPage() {\n const language = useLocaleStore((s) => s.language);\n const m = messages(language);\n const xm = m.extensionImageGen;\n const { extensionId, panelId } = useParams<{ extensionId: string; panelId?: string }>();\n const extensions = useExtensions();\n\n if (!extensionId) {\n return <SettingsPanelNotFound message=\"No extension ID provided.\" />;\n }\n\n const extension = extensions.find((ext) => ext.id === extensionId);\n if (!extension) {\n return (\n <SettingsPanelNotFound\n message={`Extension \"${extensionId}\" not found or is not available in this workspace.`}\n />\n );\n }\n\n const panels = extension.ui?.contributions?.settingsPanels;\n const panel = panelId\n ? panels?.find((p) => p.id === panelId || p.id === `${extensionId}.${panelId}`)\n : panels?.[0];\n\n const hasIframe = Boolean(panel && extension.ui);\n const hasAutoForm = Boolean(extension.hasConfigSchema);\n const isImageGeneration = extension.kind === 'image-generation';\n\n if (!hasAutoForm && !hasIframe && !isImageGeneration) {\n return (\n <SettingsPanelNotFound\n message={`Extension \"${extensionId}\" has no settings panels or config schema.`}\n />\n );\n }\n\n const title = panel?.title ?? `${extension.name} Settings`;\n\n return (\n <div className=\"mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8\">\n <h1 className=\"text-lg font-semibold text-fg\">{title}</h1>\n {isImageGeneration ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-edge-subtle bg-surface-base px-4 py-3 text-sm\">\n <p className=\"leading-relaxed text-fg-muted\">{xm.banner}</p>\n <Link\n to=\"/settings/image-models\"\n className=\"w-fit font-medium text-accent hover:underline\"\n title={m.imageModelsSettings.imageModelsLinkTitle}\n >\n {xm.openImageModels}\n </Link>\n </div>\n ) : null}\n {isImageGeneration ? <ExtensionImageProviderSettings extensionId={extensionId} /> : null}\n {hasAutoForm ? <ExtensionAutoSettings extensionId={extensionId} /> : null}\n {hasIframe && panel && extension.ui ? (\n <div className=\"overflow-hidden rounded-xl border border-edge bg-surface-base\">\n <ExtensionIframeHost\n extensionId={extensionId}\n extensionName={extension.name}\n entrypoint={panel.entrypoint}\n permissions={extension.ui?.permissions}\n title={panel.title}\n className=\"w-full\"\n minHeight={120}\n maxHeight={2000}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction SettingsPanelNotFound({ message }: { message: string }) {\n return (\n <div className=\"mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8\">\n <p className=\"text-sm text-fg-muted\">{message}</p>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension-settings-page-BOHn3S1a.js","names":[],"sources":["../../../../../web/src/features/settings/extension-image-provider-settings.tsx","../../../../../web/src/components/ui/schema-form.tsx","../../../../../web/src/features/extensions/extension-auto-settings.tsx","../../../../../web/src/features/extensions/extension-settings-page.tsx"],"sourcesContent":["import { Loader2 } from 'lucide-react';\nimport { useMemo } from 'react';\nimport useSWR from 'swr';\n\nimport { fetchImageProvidersList } from '@/features/settings/fetch-image-providers';\nimport {\n ImageProviderCredentialsPanel,\n type ImageProviderCredentialsPanelMessages,\n} from '@/features/settings/image-provider-credentials-panel';\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport { useImageProviderCredentials } from '@/features/settings/use-image-provider-credentials';\nimport { apiUrl } from '@/lib/url';\nimport { messages, type MessageBundle } from '@/i18n/messages';\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { useLocaleStore } from '@/stores/locale-store';\n\nconst imageProvidersSwrKey = () => apiUrl(IMAGE_PROVIDERS_SWR_KEY);\n\nfunction panelMessagesFromBundle(t: MessageBundle['imageModelsSettings']): ImageProviderCredentialsPanelMessages {\n return {\n credentialsIntro: t.credentialsIntro,\n regionHint: t.regionHint,\n endpointPresetsHint: t.endpointPresetsHint,\n apiKeyLabel: t.apiKeyLabel,\n optionalPlaceholder: t.optionalPlaceholder,\n regionLabel: t.regionLabel,\n baseUrlLabel: t.baseUrlLabel,\n imageBaseUrlLabel: t.imageBaseUrlLabel,\n saveCredentials: t.saveCredentials,\n savingCredentials: t.savingCredentials,\n credentialsSaved: t.credentialsSaved,\n discardCredentials: t.discardCredentials,\n credentialsNothingToSave: t.credentialsNothingToSave,\n credentialsSaveError: t.credentialsSaveError,\n regionPresetDefault: t.regionPresetDefault,\n regionPresetCustom: t.regionPresetCustom,\n baseUrlPresetDefault: t.baseUrlPresetDefault,\n baseUrlPresetCustom: t.baseUrlPresetCustom,\n openExtensionSettings: t.openExtensionSettings,\n openImageModelsPage: t.openImageModelsPage,\n extensionSettingsLinkTitle: t.extensionSettingsLinkTitle,\n imageModelsLinkTitle: t.imageModelsLinkTitle,\n configured: t.configured,\n missingKey: t.missingKey,\n defaultModel: t.defaultModel,\n modelsLabel: t.modelsLabel,\n imageBaseUrlPresetHint: t.imageBaseUrlPresetHint,\n dashscopeRegion_beijing: t.dashscopeRegion_beijing,\n dashscopeRegion_singapore: t.dashscopeRegion_singapore,\n dashscopeRegion_us: t.dashscopeRegion_us,\n apiKeyMaskedHelp: t.apiKeyMaskedHelp,\n apiKeyCopy: t.apiKeyCopy,\n apiKeyCopied: t.apiKeyCopied,\n apiKeyShow: t.apiKeyShow,\n apiKeyHide: t.apiKeyHide,\n apiKeyNotInConfigFile: t.apiKeyNotInConfigFile,\n apiKeyRevealFailed: t.apiKeyRevealFailed,\n minimaxClusterLabel: t.minimaxClusterLabel,\n minimaxClusterHint: t.minimaxClusterHint,\n falQueueBaseLabel: t.falQueueBaseLabel,\n falQueueBaseHint: t.falQueueBaseHint,\n };\n}\n\nexport function ExtensionImageProviderSettings({ extensionId }: { extensionId: string }) {\n const language = useLocaleStore((s) => s.language);\n const m = messages(language);\n const t = m.imageModelsSettings;\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n\n const { data: all = [], isLoading } = useSWR(\n hasToken ? imageProvidersSwrKey() : null,\n fetchImageProvidersList,\n { revalidateOnFocus: false },\n );\n\n const summaries = useMemo(\n () => all.filter((p) => p.id === extensionId),\n [all, extensionId],\n );\n\n const cred = useImageProviderCredentials(summaries);\n const panelMsg = useMemo(() => panelMessagesFromBundle(t), [t]);\n const apiKeyLinkLabels = useMemo(\n () => ({\n getApiKey: m.providersSettings.getApiKey,\n getApiKeyIntl: m.providersSettings.getApiKeyIntl,\n getApiKeyCn: m.providersSettings.getApiKeyCn,\n }),\n [m.providersSettings],\n );\n\n if (!hasToken) {\n return null;\n }\n\n if (isLoading) {\n return (\n <div className=\"flex items-center gap-2 py-6 text-sm text-fg-muted\">\n <Loader2 className=\"size-4 animate-spin\" />\n …\n </div>\n );\n }\n\n if (summaries.length === 0) {\n return (\n <p className=\"text-sm text-fg-muted\">\n {language === 'zh'\n ? '网关未注册该图像 Provider,或扩展已被禁用。'\n : 'This image provider is not registered on the gateway, or the extension is disabled.'}\n </p>\n );\n }\n\n return (\n <ImageProviderCredentialsPanel\n summaries={summaries}\n credDraft={cred.credDraft}\n credDirty={cred.credDirty}\n credSaving={cred.credSaving}\n credError={cred.credError}\n credSavedFlash={cred.credSavedFlash}\n credNoopFlash={cred.credNoopFlash}\n updateCredRow={cred.updateCredRow}\n onDiscardCredentials={cred.onDiscardCredentials}\n onSaveCredentials={() => void cred.saveCredentials(t.credentialsSaveError)}\n extensionIds={new Set()}\n showExtensionLinks={false}\n showImageModelsLink={false}\n language={language}\n apiKeyLinkLabels={apiKeyLinkLabels}\n messages={panelMsg}\n />\n );\n}\n","import { useCallback, useMemo } from 'react';\n\nimport { cn } from '@/lib/cn';\n\nexport type JsonSchema = Record<string, unknown>;\n\ntype SchemaFormProps = {\n schema: JsonSchema;\n values: Record<string, unknown>;\n onChange: (values: Record<string, unknown>) => void;\n disabled?: boolean;\n className?: string;\n};\n\nfunction isRecord(x: unknown): x is Record<string, unknown> {\n return typeof x === 'object' && x !== null && !Array.isArray(x);\n}\n\ntype FieldDef = {\n key: string;\n sub: JsonSchema;\n order: number;\n group: string;\n hidden: boolean;\n};\n\nfunction getXOrder(s: JsonSchema): number {\n const v = s['x-order'];\n return typeof v === 'number' && !Number.isNaN(v) ? v : 999;\n}\n\nfunction getXGroup(s: JsonSchema): string {\n const v = s['x-group'];\n return typeof v === 'string' && v.length > 0 ? v : '';\n}\n\nfunction isHidden(s: JsonSchema): boolean {\n return s['x-hidden'] === true;\n}\n\nfunction sortedFields(schema: JsonSchema): FieldDef[] {\n const props = schema.properties;\n if (!isRecord(props)) return [];\n const out: FieldDef[] = [];\n for (const [key, sub] of Object.entries(props)) {\n if (!isRecord(sub)) continue;\n if (isHidden(sub)) continue;\n out.push({\n key,\n sub: sub,\n order: getXOrder(sub),\n group: getXGroup(sub),\n hidden: false,\n });\n }\n out.sort((a, b) => a.order - b.order || a.key.localeCompare(b.key));\n return out;\n}\n\nfunction groupFields(fields: FieldDef[]): Map<string, FieldDef[]> {\n const m = new Map<string, FieldDef[]>();\n for (const f of fields) {\n const g = f.group;\n if (!m.has(g)) m.set(g, []);\n m.get(g)!.push(f);\n }\n return m;\n}\n\nfunction StringField({\n name,\n s,\n value,\n onChange,\n disabled,\n}: {\n name: string;\n s: JsonSchema;\n value: string;\n onChange: (v: string) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n const placeholder =\n (typeof s['x-placeholder'] === 'string' ? s['x-placeholder'] : null) || desc;\n const fmt = s.format;\n if (Array.isArray(s.enum) && s.enum.every((x) => typeof x === 'string')) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <select\n name={name}\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2 text-sm text-fg\"\n value={value}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n >\n {(s.enum).map((op) => (\n <option key={op} value={op}>\n {op}\n </option>\n ))}\n </select>\n </div>\n );\n }\n const inputType = fmt === 'password' ? 'password' : 'text';\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <input\n name={name}\n type={inputType}\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg placeholder:text-fg-muted/70\"\n value={value}\n placeholder={placeholder}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n />\n </div>\n );\n}\n\nfunction NumberField({\n s,\n value,\n onChange,\n disabled,\n}: {\n s: JsonSchema;\n value: number;\n onChange: (v: number) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <input\n type=\"number\"\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm text-fg\"\n value={Number.isFinite(value) ? value : 0}\n disabled={disabled}\n onChange={(e) => onChange(Number(e.target.value))}\n />\n </div>\n );\n}\n\nfunction BooleanField({\n s,\n value,\n onChange,\n disabled,\n}: {\n s: JsonSchema;\n value: boolean;\n onChange: (v: boolean) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n const labelText =\n desc ?? (typeof s.title === 'string' && s.title.length > 0 ? s.title : 'Enable');\n return (\n <label className=\"flex items-center gap-2 text-sm text-fg\">\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border border-edge\"\n checked={value}\n disabled={disabled}\n onChange={(e) => onChange(e.target.checked)}\n />\n <span>{labelText}</span>\n </label>\n );\n}\n\nfunction ArrayStringField({\n s,\n value,\n onChange,\n disabled,\n}: {\n s: JsonSchema;\n value: string[];\n onChange: (v: string[]) => void;\n disabled: boolean;\n}) {\n const desc = typeof s.description === 'string' ? s.description : undefined;\n const items = s.items;\n const isStringItems = isRecord(items) && items.type === 'string';\n if (!isStringItems) {\n return <p className=\"text-xs text-fg-muted\">Unsupported array type</p>;\n }\n const add = (t: string) => {\n const n = t.trim();\n if (!n || value.includes(n)) return;\n onChange([...value, n]);\n };\n return (\n <div className=\"flex flex-col gap-1.5\">\n {desc ? <label className=\"text-xs text-fg-muted\">{desc}</label> : null}\n <div className=\"flex flex-wrap gap-1\">\n {value.map((t) => (\n <span\n key={t}\n className=\"inline-flex items-center gap-1 rounded-md border border-edge bg-surface-panel px-2 py-0.5 text-sm\"\n >\n {t}\n <button\n type=\"button\"\n className=\"text-fg-muted hover:text-fg\"\n disabled={disabled}\n onClick={() => onChange(value.filter((x) => x !== t))}\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n className=\"ui-input h-9 rounded-md border border-edge bg-surface-base px-2.5 text-sm\"\n disabled={disabled}\n placeholder=\"Add and press Enter\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n add((e.target as HTMLInputElement).value);\n (e.target as HTMLInputElement).value = '';\n }\n }}\n />\n </div>\n );\n}\n\nfunction FieldRow({\n k,\n sub,\n value,\n onValue,\n disabled,\n}: {\n k: string;\n sub: JsonSchema;\n value: unknown;\n onValue: (next: unknown) => void;\n disabled: boolean;\n}) {\n const t = sub.type;\n const title =\n (typeof sub.title === 'string' && sub.title.length > 0 ? sub.title : null) ?? k;\n if (t === 'boolean') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <BooleanField\n s={sub}\n value={value === true}\n disabled={disabled}\n onChange={(b) => onValue(b)}\n />\n </div>\n );\n }\n if (t === 'number' || t === 'integer') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <NumberField\n s={sub}\n value={typeof value === 'number' ? value : 0}\n disabled={disabled}\n onChange={onValue}\n />\n </div>\n );\n }\n if (t === 'string') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <StringField\n name={k}\n s={sub}\n value={typeof value === 'string' ? value : ''}\n disabled={disabled}\n onChange={onValue}\n />\n </div>\n );\n }\n if (t === 'array') {\n const items = sub.items;\n if (isRecord(items) && items.type === 'string') {\n return (\n <div className=\"space-y-1.5\">\n <p className=\"text-sm font-medium text-fg\">{title}</p>\n <ArrayStringField\n s={sub}\n value={Array.isArray(value) && value.every((x) => typeof x === 'string') ? value : []}\n disabled={disabled}\n onChange={onValue}\n />\n </div>\n );\n }\n }\n return (\n <p className=\"text-xs text-fg-muted\">\n {title}: unsupported field type\n {typeof t === 'string' ? ` (${t})` : ''}\n </p>\n );\n}\n\n/**\n * Renders a JSON Schema (type: object) as a form using Gateway Console design tokens.\n */\nexport function SchemaForm({ schema, values, onChange, disabled = false, className }: SchemaFormProps) {\n const fields = useMemo(() => {\n if (schema.type !== 'object') return [];\n return sortedFields(schema);\n }, [schema]);\n\n const grouped = useMemo(() => {\n if (fields.length === 0) return new Map<string, FieldDef[]>();\n return groupFields(fields);\n }, [fields]);\n\n const setKey = useCallback(\n (key: string, next: unknown) => {\n onChange({ ...values, [key]: next });\n },\n [onChange, values],\n );\n\n if (schema.type !== 'object' || !isRecord(schema.properties) || fields.length === 0) {\n return null;\n }\n\n const groupKeys = Array.from(grouped.keys()).sort((a, b) => {\n if (a === '') return -1;\n if (b === '') return 1;\n return a.localeCompare(b);\n });\n\n return (\n <div className={cn('flex flex-col gap-4', className)}>\n {groupKeys.map((gk) => {\n const list = grouped.get(gk) ?? [];\n const block = list.map((f) => (\n <FieldRow\n key={f.key}\n k={f.key}\n sub={f.sub}\n value={values[f.key]}\n onValue={(v) => setKey(f.key, v)}\n disabled={disabled}\n />\n ));\n if (gk === '') {\n return <div key=\"default\">{block}</div>;\n }\n return (\n <details\n key={gk}\n className=\"group rounded-lg border border-edge bg-surface-panel/40 open:bg-surface-base\"\n open\n >\n <summary className=\"cursor-pointer select-none px-3 py-2 text-sm font-medium text-fg group-open:rounded-b-none\">\n {gk}\n </summary>\n <div className=\"space-y-4 border-t border-edge p-3\">{block}</div>\n </details>\n );\n })}\n </div>\n );\n}\n\n/** Extract per-key defaults from a JSON object schema for \"Reset\" actions. */\nexport function extractObjectDefaults(\n schema: JsonSchema,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (schema.type !== 'object' || !isRecord(schema.properties)) {\n return out;\n }\n for (const [k, sub] of Object.entries(schema.properties)) {\n if (!isRecord(sub)) continue;\n if (Object.prototype.hasOwnProperty.call(sub, 'default')) {\n out[k] = sub.default;\n }\n }\n return out;\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport useSWR from 'swr';\n\nimport { Button } from '@/components/ui/button';\nimport { extractObjectDefaults, SchemaForm, type JsonSchema } from '@/components/ui/schema-form';\nimport { apiFetch, fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { messages } from '@/i18n/messages';\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { useLocaleStore } from '@/stores/locale-store';\n\ntype ExtensionDetailResponse = {\n manifest: { configSchema?: JsonSchema };\n};\n\nexport function ExtensionAutoSettings({ extensionId }: { extensionId: string }) {\n const language = useLocaleStore((s) => s.language);\n const a = messages(language).agentSettings;\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const { data: detail, error: detailError } = useSWR(\n hasToken && extensionId ? `ext-detail-${extensionId}` : null,\n () => fetchJson<ExtensionDetailResponse>(apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}`)),\n );\n\n const { data: remoteConfig, mutate: mutateConfig, error: configError } = useSWR(\n hasToken && extensionId ? `ext-cfg-${extensionId}` : null,\n () =>\n fetchJson<Record<string, unknown>>(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`),\n ),\n );\n\n const schema = detail?.manifest?.configSchema;\n const defaults = useMemo(\n () => (schema && schema.type === 'object' ? extractObjectDefaults(schema) : {}),\n [schema],\n );\n\n const savedValues = useMemo(\n () => ({ ...defaults, ...(remoteConfig ?? {}) }),\n [defaults, remoteConfig],\n );\n\n const [localValues, setLocalValues] = useState<Record<string, unknown>>({});\n const [isDirty, setIsDirty] = useState(false);\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saveSuccess, setSaveSuccess] = useState(false);\n\n useEffect(() => {\n if (isDirty) return;\n setLocalValues(savedValues);\n }, [isDirty, savedValues]);\n\n const onChange = useCallback((next: Record<string, unknown>) => {\n setLocalValues(next);\n setIsDirty(true);\n setSaveError(null);\n }, []);\n\n const handleDiscard = useCallback(() => {\n setLocalValues(savedValues);\n setIsDirty(false);\n setSaveError(null);\n }, [savedValues]);\n\n const handleResetDefaults = useCallback(() => {\n if (!schema || schema.type !== 'object') return;\n setLocalValues({ ...extractObjectDefaults(schema) });\n setIsDirty(true);\n setSaveError(null);\n }, [schema]);\n\n const handleSave = useCallback(async () => {\n if (!extensionId) return;\n setSaving(true);\n setSaveError(null);\n try {\n const res = await apiFetch(\n apiUrl(`/api/extensions/${encodeURIComponent(extensionId)}/config`),\n { method: 'PATCH', body: JSON.stringify(localValues) },\n );\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n throw new Error(body.error?.message ?? res.statusText);\n }\n await mutateConfig(localValues, false);\n setIsDirty(false);\n setSaveSuccess(true);\n window.setTimeout(() => setSaveSuccess(false), 3000);\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : String(e));\n } finally {\n setSaving(false);\n }\n }, [extensionId, localValues, mutateConfig]);\n\n if (!hasToken) {\n return null;\n }\n\n if (detailError || configError) {\n const err = (detailError ?? configError) as Error;\n return (\n <p className=\"text-sm text-fg-muted\">\n Could not load extension settings: {err instanceof Error ? err.message : String(err)}\n </p>\n );\n }\n\n if (!schema || schema.type !== 'object') {\n return null;\n }\n\n return (\n <div className=\"mb-6 flex flex-col gap-3 rounded-xl border border-edge bg-surface-base p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-fg\">Configuration</h2>\n <div className=\"flex flex-wrap items-center gap-2\">\n {saveSuccess ? (\n <span className=\"text-xs text-emerald-600 dark:text-emerald-400\">{a.saved}</span>\n ) : null}\n {saveError ? <span className=\"text-xs text-red-600 dark:text-red-400\">{saveError}</span> : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"h-8 text-xs\"\n disabled={!isDirty}\n onClick={handleDiscard}\n >\n {a.discard}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"h-8 text-xs\"\n onClick={handleResetDefaults}\n >\n Reset to defaults\n </Button>\n <Button\n type=\"button\"\n variant=\"primary\"\n className=\"h-8 text-xs\"\n disabled={!isDirty || saving}\n onClick={() => void handleSave()}\n >\n {saving ? a.saving : a.save}\n </Button>\n </div>\n </div>\n <SchemaForm schema={schema} values={localValues} onChange={onChange} disabled={saving} />\n </div>\n );\n}\n","/**\n * Extension settings: auto-generated config form (configSchema) + optional iframe (settingsPanels).\n *\n * Routes: /settings/ext/:extensionId, /settings/ext/:extensionId/:panelId\n */\n\nimport { Link, useParams } from 'react-router-dom';\n\nimport { ExtensionImageProviderSettings } from '@/features/settings/extension-image-provider-settings';\nimport { messages } from '@/i18n/messages';\nimport { useLocaleStore } from '@/stores/locale-store';\n\nimport { ExtensionAutoSettings } from './extension-auto-settings';\nimport { ExtensionIframeHost } from './extension-iframe-host';\nimport { useExtensions } from './extension-provider';\n\nexport function ExtensionSettingsPage() {\n const language = useLocaleStore((s) => s.language);\n const m = messages(language);\n const xm = m.extensionImageGen;\n const { extensionId, panelId } = useParams<{ extensionId: string; panelId?: string }>();\n const extensions = useExtensions();\n\n if (!extensionId) {\n return <SettingsPanelNotFound message=\"No extension ID provided.\" />;\n }\n\n const extension = extensions.find((ext) => ext.id === extensionId);\n if (!extension) {\n return (\n <SettingsPanelNotFound\n message={`Extension \"${extensionId}\" not found or is not available in this workspace.`}\n />\n );\n }\n\n const panels = extension.ui?.contributions?.settingsPanels;\n const panel = panelId\n ? panels?.find((p) => p.id === panelId || p.id === `${extensionId}.${panelId}`)\n : panels?.[0];\n\n const hasIframe = Boolean(panel && extension.ui);\n const hasAutoForm = Boolean(extension.hasConfigSchema);\n const isImageGeneration = extension.kind === 'image-generation';\n\n if (!hasAutoForm && !hasIframe && !isImageGeneration) {\n return (\n <SettingsPanelNotFound\n message={`Extension \"${extensionId}\" has no settings panels or config schema.`}\n />\n );\n }\n\n const title = panel?.title ?? `${extension.name} Settings`;\n\n return (\n <div className=\"mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8\">\n <h1 className=\"text-lg font-semibold text-fg\">{title}</h1>\n {isImageGeneration ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-edge-subtle bg-surface-base px-4 py-3 text-sm\">\n <p className=\"leading-relaxed text-fg-muted\">{xm.banner}</p>\n <Link\n to=\"/settings/image-models\"\n className=\"w-fit font-medium text-accent hover:underline\"\n title={m.imageModelsSettings.imageModelsLinkTitle}\n >\n {xm.openImageModels}\n </Link>\n </div>\n ) : null}\n {isImageGeneration ? <ExtensionImageProviderSettings extensionId={extensionId} /> : null}\n {hasAutoForm ? <ExtensionAutoSettings extensionId={extensionId} /> : null}\n {hasIframe && panel && extension.ui ? (\n <div className=\"overflow-hidden rounded-xl border border-edge bg-surface-base\">\n <ExtensionIframeHost\n extensionId={extensionId}\n extensionName={extension.name}\n entrypoint={panel.entrypoint}\n permissions={extension.ui?.permissions}\n title={panel.title}\n className=\"w-full\"\n minHeight={120}\n maxHeight={2000}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction SettingsPanelNotFound({ message }: { message: string }) {\n return (\n <div className=\"mx-auto flex w-full max-w-app-main flex-col gap-3 px-4 py-8\">\n <p className=\"text-sm text-fg-muted\">{message}</p>\n </div>\n );\n}\n"],"mappings":"yYAgBM,MAA6B,EAAO,EAAwB,CAElE,SAAS,EAAwB,EAAgF,CAC/G,MAAO,CACL,iBAAkB,EAAE,iBACpB,WAAY,EAAE,WACd,oBAAqB,EAAE,oBACvB,YAAa,EAAE,YACf,oBAAqB,EAAE,oBACvB,YAAa,EAAE,YACf,aAAc,EAAE,aAChB,kBAAmB,EAAE,kBACrB,gBAAiB,EAAE,gBACnB,kBAAmB,EAAE,kBACrB,iBAAkB,EAAE,iBACpB,mBAAoB,EAAE,mBACtB,yBAA0B,EAAE,yBAC5B,qBAAsB,EAAE,qBACxB,oBAAqB,EAAE,oBACvB,mBAAoB,EAAE,mBACtB,qBAAsB,EAAE,qBACxB,oBAAqB,EAAE,oBACvB,sBAAuB,EAAE,sBACzB,oBAAqB,EAAE,oBACvB,2BAA4B,EAAE,2BAC9B,qBAAsB,EAAE,qBACxB,WAAY,EAAE,WACd,WAAY,EAAE,WACd,aAAc,EAAE,aAChB,YAAa,EAAE,YACf,uBAAwB,EAAE,uBAC1B,wBAAyB,EAAE,wBAC3B,0BAA2B,EAAE,0BAC7B,mBAAoB,EAAE,mBACtB,iBAAkB,EAAE,iBACpB,WAAY,EAAE,WACd,aAAc,EAAE,aAChB,WAAY,EAAE,WACd,WAAY,EAAE,WACd,sBAAuB,EAAE,sBACzB,mBAAoB,EAAE,mBACtB,oBAAqB,EAAE,oBACvB,mBAAoB,EAAE,mBACtB,kBAAmB,EAAE,kBACrB,iBAAkB,EAAE,iBACrB,CAGH,SAAgB,EAA+B,CAAE,eAAwC,CACvF,IAAM,EAAW,EAAgB,GAAM,EAAE,SAAS,CAC5C,EAAI,EAAS,EAAS,CACtB,EAAI,EAAE,oBACN,EAAW,EAAiB,GAAM,EAAQ,EAAE,MAAO,CAEnD,CAAE,KAAM,EAAM,EAAE,CAAE,aAAc,EACpC,EAAW,GAAsB,CAAG,KACpC,EACA,CAAE,kBAAmB,GAAO,CAC7B,CAEK,GAAA,EAAA,EAAA,aACE,EAAI,OAAQ,GAAM,EAAE,KAAO,EAAY,CAC7C,CAAC,EAAK,EAAY,CACnB,CAEK,EAAO,EAA4B,EAAU,CAC7C,GAAA,EAAA,EAAA,aAAyB,EAAwB,EAAE,CAAE,CAAC,EAAE,CAAC,CACzD,GAAA,EAAA,EAAA,cACG,CACL,UAAW,EAAE,kBAAkB,UAC/B,cAAe,EAAE,kBAAkB,cACnC,YAAa,EAAE,kBAAkB,YAClC,EACD,CAAC,EAAE,kBAAkB,CACtB,CAyBD,OAvBK,EAID,GAEA,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,8DAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAS,UAAU,sBAAwB,CAAA,CAAA,IAEvC,GAIN,EAAU,SAAW,GAErB,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCACV,IAAa,KACV,6BACA,sFACF,CAAA,EAKN,EAAA,EAAA,KAAC,EAAD,CACa,YACX,UAAW,EAAK,UAChB,UAAW,EAAK,UAChB,WAAY,EAAK,WACjB,UAAW,EAAK,UAChB,eAAgB,EAAK,eACrB,cAAe,EAAK,cACpB,cAAe,EAAK,cACpB,qBAAsB,EAAK,qBAC3B,sBAAyB,KAAK,EAAK,gBAAgB,EAAE,qBAAqB,CAC1E,aAAc,IAAI,IAClB,mBAAoB,GACpB,oBAAqB,GACX,WACQ,mBAClB,SAAU,EACV,CAAA,CAxCK,KC/EX,SAAS,EAAS,EAA0C,CAC1D,OAAO,OAAO,GAAM,YAAY,GAAc,CAAC,MAAM,QAAQ,EAAE,CAWjE,SAAS,EAAU,EAAuB,CACxC,IAAM,EAAI,EAAE,WACZ,OAAO,OAAO,GAAM,UAAY,CAAC,OAAO,MAAM,EAAE,CAAG,EAAI,IAGzD,SAAS,EAAU,EAAuB,CACxC,IAAM,EAAI,EAAE,WACZ,OAAO,OAAO,GAAM,UAAY,EAAE,OAAS,EAAI,EAAI,GAGrD,SAAS,EAAS,EAAwB,CACxC,OAAO,EAAE,cAAgB,GAG3B,SAAS,EAAa,EAAgC,CACpD,IAAM,EAAQ,EAAO,WACrB,GAAI,CAAC,EAAS,EAAM,CAAE,MAAO,EAAE,CAC/B,IAAM,EAAkB,EAAE,CAC1B,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,EAAM,CACvC,EAAS,EAAI,GACd,EAAS,EAAI,EACjB,EAAI,KAAK,CACP,MACK,MACL,MAAO,EAAU,EAAI,CACrB,MAAO,EAAU,EAAI,CACrB,OAAQ,GACT,CAAC,EAGJ,OADA,EAAI,MAAM,EAAG,IAAM,EAAE,MAAQ,EAAE,OAAS,EAAE,IAAI,cAAc,EAAE,IAAI,CAAC,CAC5D,EAGT,SAAS,EAAY,EAA6C,CAChE,IAAM,EAAI,IAAI,IACd,IAAK,IAAM,KAAK,EAAQ,CACtB,IAAM,EAAI,EAAE,MACP,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAG,EAAE,CAAC,CAC3B,EAAE,IAAI,EAAE,CAAE,KAAK,EAAE,CAEnB,OAAO,EAGT,SAAS,EAAY,CACnB,OACA,IACA,QACA,WACA,YAOC,CACD,IAAM,EAAO,OAAO,EAAE,aAAgB,SAAW,EAAE,YAAc,IAAA,GAC3D,GACH,OAAO,EAAE,kBAAqB,SAAW,EAAE,iBAAmB,OAAS,EACpE,EAAM,EAAE,OAsBd,OArBI,MAAM,QAAQ,EAAE,KAAK,EAAI,EAAE,KAAK,MAAO,GAAM,OAAO,GAAM,SAAS,EAEnE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,iCAAf,CACG,GAAO,EAAA,EAAA,KAAC,QAAD,CAAO,UAAU,iCAAyB,EAAa,CAAA,CAAG,MAClE,EAAA,EAAA,KAAC,SAAD,CACQ,OACN,UAAU,kFACH,QACG,WACV,SAAW,GAAM,EAAS,EAAE,OAAO,MAAM,UAEvC,EAAE,KAAM,IAAK,IACb,EAAA,EAAA,KAAC,SAAD,CAAiB,MAAO,WACrB,EACM,CAFI,EAEJ,CACT,CACK,CAAA,CACL,IAKR,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,iCAAf,CACG,GAAO,EAAA,EAAA,KAAC,QAAD,CAAO,UAAU,iCAAyB,EAAa,CAAA,CAAG,MAClE,EAAA,EAAA,KAAC,QAAD,CACQ,OACN,KANY,IAAQ,WAAa,WAAa,OAO9C,UAAU,iHACH,QACM,cACH,WACV,SAAW,GAAM,EAAS,EAAE,OAAO,MAAM,CACzC,CAAA,CACE,GAIV,SAAS,EAAY,CACnB,IACA,QACA,WACA,YAMC,CACD,IAAM,EAAO,OAAO,EAAE,aAAgB,SAAW,EAAE,YAAc,IAAA,GACjE,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,iCAAf,CACG,GAAO,EAAA,EAAA,KAAC,QAAD,CAAO,UAAU,iCAAyB,EAAa,CAAA,CAAG,MAClE,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,SACL,UAAU,oFACV,MAAO,OAAO,SAAS,EAAM,CAAG,EAAQ,EAC9B,WACV,SAAW,GAAM,EAAS,OAAO,EAAE,OAAO,MAAM,CAAC,CACjD,CAAA,CACE,GAIV,SAAS,EAAa,CACpB,IACA,QACA,WACA,YAMC,CAED,IAAM,GADO,OAAO,EAAE,aAAgB,SAAW,EAAE,YAAc,IAAA,MAEtD,OAAO,EAAE,OAAU,UAAY,EAAE,MAAM,OAAS,EAAI,EAAE,MAAQ,UACzE,OACE,EAAA,EAAA,MAAC,QAAD,CAAO,UAAU,mDAAjB,EACE,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,WACL,UAAU,qCACV,QAAS,EACC,WACV,SAAW,GAAM,EAAS,EAAE,OAAO,QAAQ,CAC3C,CAAA,EACF,EAAA,EAAA,KAAC,OAAD,CAAA,SAAO,EAAiB,CAAA,CAClB,GAIZ,SAAS,EAAiB,CACxB,IACA,QACA,WACA,YAMC,CACD,IAAM,EAAO,OAAO,EAAE,aAAgB,SAAW,EAAE,YAAc,IAAA,GAC3D,EAAQ,EAAE,MAEhB,GAAI,EADkB,EAAS,EAAM,EAAI,EAAM,OAAS,UAEtD,OAAO,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCAAwB,yBAA0B,CAAA,CAExE,IAAM,EAAO,GAAc,CACzB,IAAM,EAAI,EAAE,MAAM,CACd,CAAC,GAAK,EAAM,SAAS,EAAE,EAC3B,EAAS,CAAC,GAAG,EAAO,EAAE,CAAC,EAEzB,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,iCAAf,CACG,GAAO,EAAA,EAAA,KAAC,QAAD,CAAO,UAAU,iCAAyB,EAAa,CAAA,CAAG,MAClE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,gCACZ,EAAM,IAAK,IACV,EAAA,EAAA,MAAC,OAAD,CAEE,UAAU,6GAFZ,CAIG,GACD,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,UAAU,8BACA,WACV,YAAe,EAAS,EAAM,OAAQ,GAAM,IAAM,EAAE,CAAC,UACtD,IAEQ,CAAA,CACJ,EAZA,EAYA,CACP,CACE,CAAA,EACN,EAAA,EAAA,KAAC,QAAD,CACE,UAAU,4EACA,WACV,YAAY,sBACZ,UAAY,GAAM,CACZ,EAAE,MAAQ,UACZ,EAAE,gBAAgB,CAClB,EAAK,EAAE,OAA4B,MAAM,CACxC,EAAE,OAA4B,MAAQ,KAG3C,CAAA,CACE,GAIV,SAAS,EAAS,CAChB,IACA,MACA,QACA,UACA,YAOC,CACD,IAAM,EAAI,EAAI,KACR,GACH,OAAO,EAAI,OAAU,UAAY,EAAI,MAAM,OAAS,EAAI,EAAI,MAAQ,OAAS,EAChF,GAAI,IAAM,UACR,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,EACE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,uCAA+B,EAAU,CAAA,EACtD,EAAA,EAAA,KAAC,EAAD,CACE,EAAG,EACH,MAAO,IAAU,GACP,WACV,SAAW,GAAM,EAAQ,EAAE,CAC3B,CAAA,CACE,GAGV,GAAI,IAAM,UAAY,IAAM,UAC1B,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,EACE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,uCAA+B,EAAU,CAAA,EACtD,EAAA,EAAA,KAAC,EAAD,CACE,EAAG,EACH,MAAO,OAAO,GAAU,SAAW,EAAQ,EACjC,WACV,SAAU,EACV,CAAA,CACE,GAGV,GAAI,IAAM,SACR,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,EACE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,uCAA+B,EAAU,CAAA,EACtD,EAAA,EAAA,KAAC,EAAD,CACE,KAAM,EACN,EAAG,EACH,MAAO,OAAO,GAAU,SAAW,EAAQ,GACjC,WACV,SAAU,EACV,CAAA,CACE,GAGV,GAAI,IAAM,QAAS,CACjB,IAAM,EAAQ,EAAI,MAClB,GAAI,EAAS,EAAM,EAAI,EAAM,OAAS,SACpC,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,EACE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,uCAA+B,EAAU,CAAA,EACtD,EAAA,EAAA,KAAC,EAAD,CACE,EAAG,EACH,MAAO,MAAM,QAAQ,EAAM,EAAI,EAAM,MAAO,GAAM,OAAO,GAAM,SAAS,CAAG,EAAQ,EAAE,CAC3E,WACV,SAAU,EACV,CAAA,CACE,GAIZ,OACE,EAAA,EAAA,MAAC,IAAD,CAAG,UAAU,iCAAb,CACG,EAAM,2BACN,OAAO,GAAM,SAAW,KAAK,EAAE,GAAK,GACnC,GAOR,SAAgB,EAAW,CAAE,SAAQ,SAAQ,WAAU,WAAW,GAAO,aAA8B,CACrG,IAAM,GAAA,EAAA,EAAA,aACA,EAAO,OAAS,SACb,EAAa,EAAO,CADU,EAAE,CAEtC,CAAC,EAAO,CAAC,CAEN,GAAA,EAAA,EAAA,aACA,EAAO,SAAW,EAAU,IAAI,IAC7B,EAAY,EAAO,CACzB,CAAC,EAAO,CAAC,CAEN,GAAA,EAAA,EAAA,cACH,EAAa,IAAkB,CAC9B,EAAS,CAAE,GAAG,GAAS,GAAM,EAAM,CAAC,EAEtC,CAAC,EAAU,EAAO,CACnB,CAED,GAAI,EAAO,OAAS,UAAY,CAAC,EAAS,EAAO,WAAW,EAAI,EAAO,SAAW,EAChF,OAAO,KAGT,IAAM,EAAY,MAAM,KAAK,EAAQ,MAAM,CAAC,CAAC,MAAM,EAAG,IAChD,IAAM,GAAW,GACjB,IAAM,GAAW,EACd,EAAE,cAAc,EAAE,CACzB,CAEF,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAW,EAAG,sBAAuB,EAAU,UACjD,EAAU,IAAK,GAAO,CAErB,IAAM,GADO,EAAQ,IAAI,EAAG,EAAI,EAAE,EACf,IAAK,IACtB,EAAA,EAAA,KAAC,EAAD,CAEE,EAAG,EAAE,IACL,IAAK,EAAE,IACP,MAAO,EAAO,EAAE,KAChB,QAAU,GAAM,EAAO,EAAE,IAAK,EAAE,CACtB,WACV,CANK,EAAE,IAMP,CACF,CAIF,OAHI,IAAO,IACF,EAAA,EAAA,KAAC,MAAD,CAAA,SAAoB,EAAY,CAAvB,UAAuB,EAGvC,EAAA,EAAA,MAAC,UAAD,CAEE,UAAU,+EACV,KAAA,YAHF,EAKE,EAAA,EAAA,KAAC,UAAD,CAAS,UAAU,sGAChB,EACO,CAAA,EACV,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,8CAAsC,EAAY,CAAA,CACzD,EARH,EAQG,EAEZ,CACE,CAAA,CAKV,SAAgB,EACd,EACyB,CACzB,IAAM,EAA+B,EAAE,CACvC,GAAI,EAAO,OAAS,UAAY,CAAC,EAAS,EAAO,WAAW,CAC1D,OAAO,EAET,IAAK,GAAM,CAAC,EAAG,KAAQ,OAAO,QAAQ,EAAO,WAAW,CACjD,EAAS,EAAI,EACd,OAAO,UAAU,eAAe,KAAK,EAAK,UAAU,GACtD,EAAI,GAAK,EAAI,SAGjB,OAAO,EC5XT,SAAgB,EAAsB,CAAE,eAAwC,CAE9E,IAAM,EAAI,EADO,EAAgB,GAAM,EAAE,SACtB,CAAS,CAAC,cACvB,EAAW,EAAiB,GAAM,EAAQ,EAAE,MAAO,CACnD,CAAE,KAAM,EAAQ,MAAO,GAAgB,EAC3C,GAAY,EAAc,cAAc,IAAgB,SAClD,EAAmC,EAAO,mBAAmB,mBAAmB,EAAY,GAAG,CAAC,CACvG,CAEK,CAAE,KAAM,EAAc,OAAQ,EAAc,MAAO,GAAgB,EACvE,GAAY,EAAc,WAAW,IAAgB,SAEnD,EACE,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,SAAS,CACpE,CACJ,CAEK,EAAS,GAAQ,UAAU,aAC3B,GAAA,EAAA,EAAA,aACG,GAAU,EAAO,OAAS,SAAW,EAAsB,EAAO,CAAG,EAAE,CAC9E,CAAC,EAAO,CACT,CAEK,GAAA,EAAA,EAAA,cACG,CAAE,GAAG,EAAU,GAAI,GAAgB,EAAE,CAAG,EAC/C,CAAC,EAAU,EAAa,CACzB,CAEK,CAAC,EAAa,IAAA,EAAA,EAAA,UAAoD,EAAE,CAAC,CACrE,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuB,GAAM,CACvC,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAsB,GAAM,CACrC,CAAC,EAAW,IAAA,EAAA,EAAA,UAAwC,KAAK,CACzD,CAAC,EAAa,IAAA,EAAA,EAAA,UAA2B,GAAM,EAErD,EAAA,EAAA,eAAgB,CACV,GACJ,EAAe,EAAY,EAC1B,CAAC,EAAS,EAAY,CAAC,CAE1B,IAAM,GAAA,EAAA,EAAA,aAAwB,GAAkC,CAC9D,EAAe,EAAK,CACpB,EAAW,GAAK,CAChB,EAAa,KAAK,EACjB,EAAE,CAAC,CAEA,GAAA,EAAA,EAAA,iBAAkC,CACtC,EAAe,EAAY,CAC3B,EAAW,GAAM,CACjB,EAAa,KAAK,EACjB,CAAC,EAAY,CAAC,CAEX,GAAA,EAAA,EAAA,iBAAwC,CACxC,CAAC,GAAU,EAAO,OAAS,WAC/B,EAAe,CAAE,GAAG,EAAsB,EAAO,CAAE,CAAC,CACpD,EAAW,GAAK,CAChB,EAAa,KAAK,GACjB,CAAC,EAAO,CAAC,CAEN,GAAA,EAAA,EAAA,aAAyB,SAAY,CACpC,KAEL,CADA,EAAU,GAAK,CACf,EAAa,KAAK,CAClB,GAAI,CACF,IAAM,EAAM,MAAM,EAChB,EAAO,mBAAmB,mBAAmB,EAAY,CAAC,SAAS,CACnE,CAAE,OAAQ,QAAS,KAAM,KAAK,UAAU,EAAY,CAAE,CACvD,CACD,GAAI,CAAC,EAAI,GAAI,CACX,IAAM,EAAQ,MAAM,EAAI,MAAM,CAAC,WAAa,EAAE,EAAE,CAChD,MAAU,MAAM,EAAK,OAAO,SAAW,EAAI,WAAW,CAExD,MAAM,EAAa,EAAa,GAAM,CACtC,EAAW,GAAM,CACjB,EAAe,GAAK,CACpB,OAAO,eAAiB,EAAe,GAAM,CAAE,IAAK,OAC7C,EAAG,CACV,EAAa,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAC,QAChD,CACR,EAAU,GAAM,IAEjB,CAAC,EAAa,EAAa,EAAa,CAAC,CAE5C,GAAI,CAAC,EACH,OAAO,KAGT,GAAI,GAAe,EAAa,CAC9B,IAAM,EAAO,GAAe,EAC5B,OACE,EAAA,EAAA,MAAC,IAAD,CAAG,UAAU,iCAAb,CAAqC,sCACC,aAAe,MAAQ,EAAI,QAAU,OAAO,EAAI,CAClF,GAQR,MAJI,CAAC,GAAU,EAAO,OAAS,SACtB,MAIP,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,sFAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6DAAf,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAgC,gBAAkB,CAAA,EAChE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,CACG,GACC,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0DAAkD,EAAE,MAAa,CAAA,CAC/E,KACH,GAAY,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,kDAA0C,EAAiB,CAAA,CAAG,MAC3F,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,SACL,QAAQ,QACR,UAAU,cACV,SAAU,CAAC,EACX,QAAS,WAER,EAAE,QACI,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,SACL,QAAQ,QACR,UAAU,cACV,QAAS,WACV,oBAEQ,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,SACL,QAAQ,UACR,UAAU,cACV,SAAU,CAAC,GAAW,EACtB,YAAe,KAAK,GAAY,UAE/B,EAAS,EAAE,OAAS,EAAE,KAChB,CAAA,CACL,GACF,IACN,EAAA,EAAA,KAAC,EAAD,CAAoB,SAAQ,OAAQ,EAAuB,WAAU,SAAU,EAAU,CAAA,CACrF,GCxIV,SAAgB,GAAwB,CAEtC,IAAM,EAAI,EADO,EAAgB,GAAM,EAAE,SACtB,CAAS,CACtB,EAAK,EAAE,kBACP,CAAE,cAAa,WAAY,GAAsD,CACjF,EAAa,GAAe,CAElC,GAAI,CAAC,EACH,OAAO,EAAA,EAAA,KAAC,EAAD,CAAuB,QAAQ,4BAA8B,CAAA,CAGtE,IAAM,EAAY,EAAW,KAAM,GAAQ,EAAI,KAAO,EAAY,CAClE,GAAI,CAAC,EACH,OACE,EAAA,EAAA,KAAC,EAAD,CACE,QAAS,cAAc,EAAY,oDACnC,CAAA,CAIN,IAAM,EAAS,EAAU,IAAI,eAAe,eACtC,EAAQ,EACV,GAAQ,KAAM,GAAM,EAAE,KAAO,GAAW,EAAE,KAAO,GAAG,EAAY,GAAG,IAAU,CAC7E,IAAS,GAEP,EAAY,GAAQ,GAAS,EAAU,IACvC,EAAc,EAAQ,EAAU,gBAChC,EAAoB,EAAU,OAAS,mBAY7C,MAVI,CAAC,GAAe,CAAC,GAAa,CAAC,GAE/B,EAAA,EAAA,KAAC,EAAD,CACE,QAAS,cAAc,EAAY,4CACnC,CAAA,EAOJ,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uEAAf,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAJJ,GAAO,OAAS,GAAG,EAAU,KAAK,WAIc,CAAA,CACzD,GACC,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,sGAAf,EACE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,yCAAiC,EAAG,OAAW,CAAA,EAC5D,EAAA,EAAA,KAAC,EAAD,CACE,GAAG,yBACH,UAAU,gDACV,MAAO,EAAE,oBAAoB,8BAE5B,EAAG,gBACC,CAAA,CACH,GACJ,KACH,GAAoB,EAAA,EAAA,KAAC,EAAD,CAA6C,cAAe,CAAA,CAAG,KACnF,GAAc,EAAA,EAAA,KAAC,EAAD,CAAoC,cAAe,CAAA,CAAG,KACpE,GAAa,GAAS,EAAU,IAC/B,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,0EACb,EAAA,EAAA,KAAC,EAAD,CACe,cACb,cAAe,EAAU,KACzB,WAAY,EAAM,WAClB,YAAa,EAAU,IAAI,YAC3B,MAAO,EAAM,MACb,UAAU,SACV,UAAW,IACX,UAAW,IACX,CAAA,CACE,CAAA,CACJ,KACA,GAIV,SAAS,EAAsB,CAAE,WAAgC,CAC/D,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,wEACb,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCAAyB,EAAY,CAAA,CAC9C,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{o as e}from"./vendor-swr-B5fPo7KK.js";import{dn as t,gn as n,xt as r}from"./index-
|
|
2
|
-
//# sourceMappingURL=heartbeat-config-api-
|
|
1
|
+
import{o as e}from"./vendor-swr-B5fPo7KK.js";import{dn as t,gn as n,xt as r}from"./index-DeELk--t.js";function i(){return t(`/api/workspace/heartbeat-md`)}async function a(){let e=await n(i());return typeof e.payload?.content==`string`?e.payload.content:``}function o(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function s(e){let t=o(e)?e:{},n=o(t.gateway)?t.gateway:{},r=o(n.heartbeat)?n.heartbeat:{},i=r.activeHours,a=o(i)?i:null,s=a&&typeof a.start==`string`&&typeof a.end==`string`&&a.start&&a.end?{start:a.start,end:a.end,timezone:typeof a.timezone==`string`?a.timezone:``}:null;return{enabled:!!(r.enabled??!0),intervalMs:typeof r.intervalMs==`number`&&Number.isFinite(r.intervalMs)?r.intervalMs:18e5,target:typeof r.target==`string`?r.target:``,targetChatId:typeof r.targetChatId==`string`?r.targetChatId:``,prompt:typeof r.prompt==`string`?r.prompt:``,ackMaxChars:typeof r.ackMaxChars==`number`&&Number.isFinite(r.ackMaxChars)?r.ackMaxChars:``,isolatedSession:!!r.isolatedSession,activeHours:s}}function c(e){let t={enabled:e.enabled,intervalMs:e.intervalMs};return e.target.trim()?t.target=e.target.trim():t.target=null,e.targetChatId.trim()?t.targetChatId=e.targetChatId.trim():t.targetChatId=null,e.prompt.trim()?t.prompt=e.prompt.trim():t.prompt=null,e.ackMaxChars===``||e.ackMaxChars===void 0?t.ackMaxChars=null:t.ackMaxChars=e.ackMaxChars,e.isolatedSession?t.isolatedSession=!0:t.isolatedSession=null,e.activeHours?.start?.trim()&&e.activeHours?.end?.trim()?t.activeHours={start:e.activeHours.start.trim(),end:e.activeHours.end.trim(),...e.activeHours.timezone.trim()?{timezone:e.activeHours.timezone.trim()}:{}}:t.activeHours=null,t}async function l(e){await n(t(`/api/config`),{method:`PATCH`,body:JSON.stringify({gateway:{heartbeat:c(e)}})}),r()}async function u(r){await n(t(`/api/workspace/heartbeat-md`),{method:`PUT`,body:JSON.stringify({content:r})}),e(i())}async function d(e){await n(t(`/api/heartbeat/trigger`),{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e?{reason:e}:{})})}export{a,d as i,l as n,i as o,u as r,s as t};
|
|
2
|
+
//# sourceMappingURL=heartbeat-config-api-OqFXdrMO.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat-config-api-
|
|
1
|
+
{"version":3,"file":"heartbeat-config-api-OqFXdrMO.js","names":[],"sources":["../../../../../web/src/features/settings/heartbeat-md-swr.ts","../../../../../web/src/features/settings/heartbeat-config-api.ts"],"sourcesContent":["import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nexport function heartbeatMdSwrKey(): string {\n return apiUrl('/api/workspace/heartbeat-md');\n}\n\nexport async function fetchHeartbeatMdSwr(): Promise<string> {\n const res = await fetchJson<{ ok?: boolean; payload?: { content?: string } }>(heartbeatMdSwrKey());\n return typeof res.payload?.content === 'string' ? res.payload.content : '';\n}\n","import { revalidateGatewayConfig } from '@/features/gateway/gateway-config-swr';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { heartbeatMdSwrKey } from '@/features/settings/heartbeat-md-swr';\nimport { mutate } from 'swr';\n\nimport type { HeartbeatSettingsState } from './heartbeat-settings.types';\n\nexport type { HeartbeatSettingsState } from './heartbeat-settings.types';\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v);\n}\n\nexport function normalizeHeartbeatFromConfig(config: unknown): HeartbeatSettingsState {\n const c = isRecord(config) ? config : {};\n const gw = isRecord(c.gateway) ? c.gateway : {};\n const hb = isRecord(gw.heartbeat) ? gw.heartbeat : {};\n const ahRaw = hb.activeHours;\n const ah = isRecord(ahRaw) ? ahRaw : null;\n const activeHours =\n ah && typeof ah.start === 'string' && typeof ah.end === 'string' && ah.start && ah.end\n ? {\n start: ah.start,\n end: ah.end,\n timezone: typeof ah.timezone === 'string' ? ah.timezone : '',\n }\n : null;\n return {\n enabled: Boolean(hb.enabled ?? true),\n intervalMs: typeof hb.intervalMs === 'number' && Number.isFinite(hb.intervalMs) ? hb.intervalMs : 1_800_000,\n target: typeof hb.target === 'string' ? hb.target : '',\n targetChatId: typeof hb.targetChatId === 'string' ? hb.targetChatId : '',\n prompt: typeof hb.prompt === 'string' ? hb.prompt : '',\n ackMaxChars:\n typeof hb.ackMaxChars === 'number' && Number.isFinite(hb.ackMaxChars) ? hb.ackMaxChars : '',\n isolatedSession: Boolean(hb.isolatedSession),\n activeHours,\n };\n}\n\nfunction buildHeartbeatPayload(state: HeartbeatSettingsState): Record<string, unknown> {\n const p: Record<string, unknown> = {\n enabled: state.enabled,\n intervalMs: state.intervalMs,\n };\n if (state.target.trim()) p.target = state.target.trim();\n else p.target = null;\n if (state.targetChatId.trim()) p.targetChatId = state.targetChatId.trim();\n else p.targetChatId = null;\n if (state.prompt.trim()) p.prompt = state.prompt.trim();\n else p.prompt = null;\n if (state.ackMaxChars === '' || state.ackMaxChars === undefined) {\n p.ackMaxChars = null;\n } else {\n p.ackMaxChars = state.ackMaxChars;\n }\n if (state.isolatedSession) p.isolatedSession = true;\n else p.isolatedSession = null;\n if (state.activeHours?.start?.trim() && state.activeHours?.end?.trim()) {\n p.activeHours = {\n start: state.activeHours.start.trim(),\n end: state.activeHours.end.trim(),\n ...(state.activeHours.timezone.trim()\n ? { timezone: state.activeHours.timezone.trim() }\n : {}),\n };\n } else {\n p.activeHours = null;\n }\n return p;\n}\n\nexport async function patchHeartbeatSettings(state: HeartbeatSettingsState): Promise<void> {\n await fetchJson(apiUrl('/api/config'), {\n method: 'PATCH',\n body: JSON.stringify({\n gateway: {\n heartbeat: buildHeartbeatPayload(state),\n },\n }),\n });\n void revalidateGatewayConfig();\n}\n\nexport async function fetchHeartbeatMd(): Promise<string> {\n const res = await fetchJson<{ ok?: boolean; payload?: { content?: string } }>(\n apiUrl('/api/workspace/heartbeat-md'),\n );\n return typeof res.payload?.content === 'string' ? res.payload.content : '';\n}\n\nexport async function putHeartbeatMd(content: string): Promise<void> {\n await fetchJson(apiUrl('/api/workspace/heartbeat-md'), {\n method: 'PUT',\n body: JSON.stringify({ content }),\n });\n void mutate(heartbeatMdSwrKey());\n}\n\n/** Queue one heartbeat run (same path as the interval timer). */\nexport async function triggerHeartbeat(reason?: string): Promise<void> {\n await fetchJson(apiUrl('/api/heartbeat/trigger'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(reason ? { reason } : {}),\n });\n}\n"],"mappings":"sGAGA,SAAgB,GAA4B,CAC1C,OAAO,EAAO,8BAA8B,CAG9C,eAAsB,GAAuC,CAC3D,IAAM,EAAM,MAAM,EAA4D,GAAmB,CAAC,CAClG,OAAO,OAAO,EAAI,SAAS,SAAY,SAAW,EAAI,QAAQ,QAAU,GCC1E,SAAS,EAAS,EAA0C,CAC1D,OAAqB,OAAO,GAAM,YAA3B,GAAuC,CAAC,MAAM,QAAQ,EAAE,CAGjE,SAAgB,EAA6B,EAAyC,CACpF,IAAM,EAAI,EAAS,EAAO,CAAG,EAAS,EAAE,CAClC,EAAK,EAAS,EAAE,QAAQ,CAAG,EAAE,QAAU,EAAE,CACzC,EAAK,EAAS,EAAG,UAAU,CAAG,EAAG,UAAY,EAAE,CAC/C,EAAQ,EAAG,YACX,EAAK,EAAS,EAAM,CAAG,EAAQ,KAC/B,EACJ,GAAM,OAAO,EAAG,OAAU,UAAY,OAAO,EAAG,KAAQ,UAAY,EAAG,OAAS,EAAG,IAC/E,CACE,MAAO,EAAG,MACV,IAAK,EAAG,IACR,SAAU,OAAO,EAAG,UAAa,SAAW,EAAG,SAAW,GAC3D,CACD,KACN,MAAO,CACL,QAAS,GAAQ,EAAG,SAAW,IAC/B,WAAY,OAAO,EAAG,YAAe,UAAY,OAAO,SAAS,EAAG,WAAW,CAAG,EAAG,WAAa,KAClG,OAAQ,OAAO,EAAG,QAAW,SAAW,EAAG,OAAS,GACpD,aAAc,OAAO,EAAG,cAAiB,SAAW,EAAG,aAAe,GACtE,OAAQ,OAAO,EAAG,QAAW,SAAW,EAAG,OAAS,GACpD,YACE,OAAO,EAAG,aAAgB,UAAY,OAAO,SAAS,EAAG,YAAY,CAAG,EAAG,YAAc,GAC3F,gBAAiB,EAAQ,EAAG,gBAC5B,cACD,CAGH,SAAS,EAAsB,EAAwD,CACrF,IAAM,EAA6B,CACjC,QAAS,EAAM,QACf,WAAY,EAAM,WACnB,CAyBD,OAxBI,EAAM,OAAO,MAAM,CAAE,EAAE,OAAS,EAAM,OAAO,MAAM,CAClD,EAAE,OAAS,KACZ,EAAM,aAAa,MAAM,CAAE,EAAE,aAAe,EAAM,aAAa,MAAM,CACpE,EAAE,aAAe,KAClB,EAAM,OAAO,MAAM,CAAE,EAAE,OAAS,EAAM,OAAO,MAAM,CAClD,EAAE,OAAS,KACZ,EAAM,cAAgB,IAAM,EAAM,cAAgB,IAAA,GACpD,EAAE,YAAc,KAEhB,EAAE,YAAc,EAAM,YAEpB,EAAM,gBAAiB,EAAE,gBAAkB,GAC1C,EAAE,gBAAkB,KACrB,EAAM,aAAa,OAAO,MAAM,EAAI,EAAM,aAAa,KAAK,MAAM,CACpE,EAAE,YAAc,CACd,MAAO,EAAM,YAAY,MAAM,MAAM,CACrC,IAAK,EAAM,YAAY,IAAI,MAAM,CACjC,GAAI,EAAM,YAAY,SAAS,MAAM,CACjC,CAAE,SAAU,EAAM,YAAY,SAAS,MAAM,CAAE,CAC/C,EAAE,CACP,CAED,EAAE,YAAc,KAEX,EAGT,eAAsB,EAAuB,EAA8C,CACzF,MAAM,EAAU,EAAO,cAAc,CAAE,CACrC,OAAQ,QACR,KAAM,KAAK,UAAU,CACnB,QAAS,CACP,UAAW,EAAsB,EAAM,CACxC,CACF,CAAC,CACH,CAAC,CACG,GAAyB,CAUhC,eAAsB,EAAe,EAAgC,CACnE,MAAM,EAAU,EAAO,8BAA8B,CAAE,CACrD,OAAQ,MACR,KAAM,KAAK,UAAU,CAAE,UAAS,CAAC,CAClC,CAAC,CACG,EAAO,GAAmB,CAAC,CAIlC,eAAsB,EAAiB,EAAgC,CACrE,MAAM,EAAU,EAAO,yBAAyB,CAAE,CAChD,OAAQ,OACR,QAAS,CAAE,eAAgB,mBAAoB,CAC/C,KAAM,KAAK,UAAU,EAAS,CAAE,SAAQ,CAAG,EAAE,CAAC,CAC/C,CAAC"}
|