@xopcai/xopc 0.0.57 → 0.0.59

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.
Files changed (46) hide show
  1. package/README.md +6 -0
  2. package/README.zh-CN.md +6 -0
  3. package/dist/extensions/telegram/xopc.extension.json +1 -1
  4. package/dist/gateway/static/root/assets/{agents-DEL6G6-C.js → agents-BgeuYTaG.js} +2 -2
  5. package/dist/gateway/static/root/assets/{agents-DEL6G6-C.js.map → agents-BgeuYTaG.js.map} +1 -1
  6. package/dist/gateway/static/root/assets/{apps-page-DXlDJBf0.js → apps-page-DXOoxQ3t.js} +2 -2
  7. package/dist/gateway/static/root/assets/{apps-page-DXlDJBf0.js.map → apps-page-DXOoxQ3t.js.map} +1 -1
  8. package/dist/gateway/static/root/assets/{channels-settings-IWwRmmCH.js → channels-settings-BtblELP8.js} +2 -2
  9. package/dist/gateway/static/root/assets/{channels-settings-IWwRmmCH.js.map → channels-settings-BtblELP8.js.map} +1 -1
  10. package/dist/gateway/static/root/assets/{cron-dreaming-jobs-DIwTeQzu.js → cron-dreaming-jobs-D5kvbjSY.js} +2 -2
  11. package/dist/gateway/static/root/assets/{cron-dreaming-jobs-DIwTeQzu.js.map → cron-dreaming-jobs-D5kvbjSY.js.map} +1 -1
  12. package/dist/gateway/static/root/assets/{cron-page-CcRCqnzx.js → cron-page-4hk422N-.js} +2 -2
  13. package/dist/gateway/static/root/assets/{cron-page-CcRCqnzx.js.map → cron-page-4hk422N-.js.map} +1 -1
  14. package/dist/gateway/static/root/assets/{dist-CgucsbDd.js → dist-CEmPWsYK.js} +2 -2
  15. package/dist/gateway/static/root/assets/{dist-CgucsbDd.js.map → dist-CEmPWsYK.js.map} +1 -1
  16. package/dist/gateway/static/root/assets/{extension-debug-page-BX1pf7rr.js → extension-debug-page-z0vfOyIu.js} +2 -2
  17. package/dist/gateway/static/root/assets/{extension-debug-page-BX1pf7rr.js.map → extension-debug-page-z0vfOyIu.js.map} +1 -1
  18. package/dist/gateway/static/root/assets/{extension-page-DPm5vzVN.js → extension-page-CFaT0yHk.js} +2 -2
  19. package/dist/gateway/static/root/assets/{extension-page-DPm5vzVN.js.map → extension-page-CFaT0yHk.js.map} +1 -1
  20. package/dist/gateway/static/root/assets/{extension-settings-page-DAly-C6g.js → extension-settings-page-7atlEJU0.js} +2 -2
  21. package/dist/gateway/static/root/assets/{extension-settings-page-DAly-C6g.js.map → extension-settings-page-7atlEJU0.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/{heartbeat-config-api-SijGy10-.js → heartbeat-config-api-DnmmSrTx.js} +2 -2
  23. package/dist/gateway/static/root/assets/{heartbeat-config-api-SijGy10-.js.map → heartbeat-config-api-DnmmSrTx.js.map} +1 -1
  24. package/dist/gateway/static/root/assets/{index-DHte7V8E.js → index-DvMI6Vqn.js} +4 -4
  25. package/dist/gateway/static/root/assets/{index-DHte7V8E.js.map → index-DvMI6Vqn.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{logs-page-DEnawdQ8.js → logs-page-XKXcjB3g.js} +2 -2
  27. package/dist/gateway/static/root/assets/{logs-page-DEnawdQ8.js.map → logs-page-XKXcjB3g.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{sessions-page-CGn5wKzh.js → sessions-page-1DyACBG7.js} +2 -2
  29. package/dist/gateway/static/root/assets/{sessions-page-CGn5wKzh.js.map → sessions-page-1DyACBG7.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{settings-page-BV20X-TN.js → settings-page-TrmEN1eQ.js} +2 -2
  31. package/dist/gateway/static/root/assets/{settings-page-BV20X-TN.js.map → settings-page-TrmEN1eQ.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/{skills-page-C1Ns0BHu.js → skills-page-Dcvjtm-1.js} +2 -2
  33. package/dist/gateway/static/root/assets/{skills-page-C1Ns0BHu.js.map → skills-page-Dcvjtm-1.js.map} +1 -1
  34. package/dist/gateway/static/root/assets/{use-image-provider-credentials-BZqTDErY.js → use-image-provider-credentials-D6TXcMU2.js} +2 -2
  35. package/dist/gateway/static/root/assets/{use-image-provider-credentials-BZqTDErY.js.map → use-image-provider-credentials-D6TXcMU2.js.map} +1 -1
  36. package/dist/gateway/static/root/index.html +1 -1
  37. package/dist/package.js +1 -1
  38. package/dist/src/agent/embedded/run-turn.js +6 -0
  39. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  40. package/dist/src/agent/embedded/xopc-auth-storage.d.ts +20 -0
  41. package/dist/src/agent/embedded/xopc-auth-storage.js +38 -0
  42. package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -0
  43. package/dist/src/agent/embedded/xopc-stream-bridge.d.ts +11 -0
  44. package/dist/src/agent/embedded/xopc-stream-bridge.js +24 -0
  45. package/dist/src/agent/embedded/xopc-stream-bridge.js.map +1 -0
  46. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"extension-page-DPm5vzVN.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 { extensionShellUiReachable, useExtensions } from './extension-provider';\n\nexport function ExtensionPage() {\n const { extensionId, pageId } = useParams<{ extensionId: string; pageId?: string }>();\n const extensions = useExtensions();\n const setPageHeader = usePageHeaderStore((s) => s.setPageHeader);\n const clearPageHeader = usePageHeaderStore((s) => s.clearPageHeader);\n\n const extension = extensionId\n ? extensions.find((ext) => ext.id === extensionId && extensionShellUiReachable(ext))\n : 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":"sMAcA,SAAgB,GAAgB,CAC9B,GAAM,CAAE,cAAa,UAAW,GAAqD,CAC/E,EAAa,GAAe,CAC5B,EAAgB,EAAoB,GAAM,EAAE,cAAc,CAC1D,EAAkB,EAAoB,GAAM,EAAE,gBAAgB,CAE9D,EAAY,EACd,EAAW,KAAM,GAAQ,EAAI,KAAO,GAAe,EAA0B,EAAI,CAAC,CAClF,IAAA,GACE,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
+ {"version":3,"file":"extension-page-CFaT0yHk.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 { extensionShellUiReachable, useExtensions } from './extension-provider';\n\nexport function ExtensionPage() {\n const { extensionId, pageId } = useParams<{ extensionId: string; pageId?: string }>();\n const extensions = useExtensions();\n const setPageHeader = usePageHeaderStore((s) => s.setPageHeader);\n const clearPageHeader = usePageHeaderStore((s) => s.clearPageHeader);\n\n const extension = extensionId\n ? extensions.find((ext) => ext.id === extensionId && extensionShellUiReachable(ext))\n : 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":"sMAcA,SAAgB,GAAgB,CAC9B,GAAM,CAAE,cAAa,UAAW,GAAqD,CAC/E,EAAa,GAAe,CAC5B,EAAgB,EAAoB,GAAM,EAAE,cAAc,CAC1D,EAAkB,EAAoB,GAAM,EAAE,gBAAgB,CAE9D,EAAY,EACd,EAAW,KAAM,GAAQ,EAAI,KAAO,GAAe,EAA0B,EAAI,CAAC,CAClF,IAAA,GACE,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{Ai as i,Ar as a,Cn as o,Ei as s,Ln as c,Mn as l,dn as u,gn as d,hn as f,jn as p,ln as m,n as h,vn as g}from"./index-DHte7V8E.js";import{i as _,n as v,r as y,t as b}from"./use-image-provider-credentials-BZqTDErY.js";var x=e(t(),1),S=n(),C=()=>u(_);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=o(e=>e.language),n=p(t),i=n.imageModelsSettings,s=g(e=>!!e.token),{data:c=[],isLoading:l}=r(s?C():null,y,{revalidateOnFocus:!1}),u=(0,x.useMemo)(()=>c.filter(t=>t.id===e),[c,e]),d=b(u),f=(0,x.useMemo)(()=>w(i),[i]),m=(0,x.useMemo)(()=>({getApiKey:n.providersSettings.getApiKey,getApiKeyIntl:n.providersSettings.getApiKeyIntl,getApiKeyCn:n.providersSettings.getApiKeyCn}),[n.providersSettings]);return s?l?(0,S.jsxs)(`div`,{className:`flex items-center gap-2 py-6 text-sm text-fg-muted`,children:[(0,S.jsx)(a,{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(i.credentialsSaveError),extensionIds:new Set,showExtensionLinks:!1,showImageModelsLink:!1,language:t,apiKeyLinkLabels:m,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 a=(0,x.useMemo)(()=>e.type===`object`?A(e):[],[e]),o=(0,x.useMemo)(()=>a.length===0?new Map:j(a),[a]),s=(0,x.useCallback)((e,r)=>{n({...t,[e]:r})},[n,t]);if(e.type!==`object`||!E(e.properties)||a.length===0)return null;let l=Array.from(o.keys()).sort((e,t)=>e===``?-1:t===``?1:e.localeCompare(t));return(0,S.jsx)(`div`,{className:c(`flex flex-col gap-4`,i),children:l.map(e=>{let n=(o.get(e)??[]).map(e=>(0,S.jsx)(I,{k:e.key,sub:e.sub,value:t[e.key],onValue:t=>s(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=p(o(e=>e.language)).agentSettings,n=g(e=>!!e.token),{data:i,error:a}=r(n&&e?`ext-detail-${e}`:null,()=>d(u(`/api/extensions/${encodeURIComponent(e)}`))),{data:s,mutate:c,error:m}=r(n&&e?`ext-cfg-${e}`:null,()=>d(u(`/api/extensions/${encodeURIComponent(e)}/config`))),h=i?.manifest?.configSchema,_=(0,x.useMemo)(()=>h&&h.type===`object`?R(h):{},[h]),v=(0,x.useMemo)(()=>({..._,...s??{}}),[_,s]),[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)(()=>{!h||h.type!==`object`||(b({...R(h)}),w(!0),O(null))},[h]),P=(0,x.useCallback)(async()=>{if(e){E(!0),O(null);try{let t=await f(u(`/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 c(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,c]);if(!n)return null;if(a||m){let e=a??m;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!h||h.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)(l,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,disabled:!C,onClick:M,children:t.discard}),(0,S.jsx)(l,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,onClick:N,children:`Reset to defaults`}),(0,S.jsx)(l,{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:h,values:y,onChange:j,disabled:T})]})}function B(){let e=p(o(e=>e.language)),t=e.extensionImageGen,{extensionId:n,panelId:r}=i(),a=m();if(!n)return(0,S.jsx)(V,{message:`No extension ID provided.`});let c=a.find(e=>e.id===n);if(!c)return(0,S.jsx)(V,{message:`Extension "${n}" not found or is not available in this workspace.`});let l=c.ui?.contributions?.settingsPanels,u=r?l?.find(e=>e.id===r||e.id===`${n}.${r}`):l?.[0],d=!!(u&&c.ui),f=!!c.hasConfigSchema,g=c.kind===`image-generation`;return!f&&!d&&!g?(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:u?.title??`${c.name} Settings`}),g?(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)(s,{to:`/settings/image-models`,className:`w-fit font-medium text-accent hover:underline`,title:e.imageModelsSettings.imageModelsLinkTitle,children:t.openImageModels})]}):null,g?(0,S.jsx)(T,{extensionId:n}):null,f?(0,S.jsx)(z,{extensionId:n}):null,d&&u&&c.ui?(0,S.jsx)(`div`,{className:`overflow-hidden rounded-xl border border-edge bg-surface-base`,children:(0,S.jsx)(h,{extensionId:n,extensionName:c.name,entrypoint:u.entrypoint,permissions:c.ui?.permissions,title:u.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-DAly-C6g.js.map
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{Ai as i,Ar as a,Cn as o,Ei as s,Ln as c,Mn as l,dn as u,gn as d,hn as f,jn as p,ln as m,n as h,vn as g}from"./index-DvMI6Vqn.js";import{i as _,n as v,r as y,t as b}from"./use-image-provider-credentials-D6TXcMU2.js";var x=e(t(),1),S=n(),C=()=>u(_);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=o(e=>e.language),n=p(t),i=n.imageModelsSettings,s=g(e=>!!e.token),{data:c=[],isLoading:l}=r(s?C():null,y,{revalidateOnFocus:!1}),u=(0,x.useMemo)(()=>c.filter(t=>t.id===e),[c,e]),d=b(u),f=(0,x.useMemo)(()=>w(i),[i]),m=(0,x.useMemo)(()=>({getApiKey:n.providersSettings.getApiKey,getApiKeyIntl:n.providersSettings.getApiKeyIntl,getApiKeyCn:n.providersSettings.getApiKeyCn}),[n.providersSettings]);return s?l?(0,S.jsxs)(`div`,{className:`flex items-center gap-2 py-6 text-sm text-fg-muted`,children:[(0,S.jsx)(a,{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(i.credentialsSaveError),extensionIds:new Set,showExtensionLinks:!1,showImageModelsLink:!1,language:t,apiKeyLinkLabels:m,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 a=(0,x.useMemo)(()=>e.type===`object`?A(e):[],[e]),o=(0,x.useMemo)(()=>a.length===0?new Map:j(a),[a]),s=(0,x.useCallback)((e,r)=>{n({...t,[e]:r})},[n,t]);if(e.type!==`object`||!E(e.properties)||a.length===0)return null;let l=Array.from(o.keys()).sort((e,t)=>e===``?-1:t===``?1:e.localeCompare(t));return(0,S.jsx)(`div`,{className:c(`flex flex-col gap-4`,i),children:l.map(e=>{let n=(o.get(e)??[]).map(e=>(0,S.jsx)(I,{k:e.key,sub:e.sub,value:t[e.key],onValue:t=>s(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=p(o(e=>e.language)).agentSettings,n=g(e=>!!e.token),{data:i,error:a}=r(n&&e?`ext-detail-${e}`:null,()=>d(u(`/api/extensions/${encodeURIComponent(e)}`))),{data:s,mutate:c,error:m}=r(n&&e?`ext-cfg-${e}`:null,()=>d(u(`/api/extensions/${encodeURIComponent(e)}/config`))),h=i?.manifest?.configSchema,_=(0,x.useMemo)(()=>h&&h.type===`object`?R(h):{},[h]),v=(0,x.useMemo)(()=>({..._,...s??{}}),[_,s]),[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)(()=>{!h||h.type!==`object`||(b({...R(h)}),w(!0),O(null))},[h]),P=(0,x.useCallback)(async()=>{if(e){E(!0),O(null);try{let t=await f(u(`/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 c(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,c]);if(!n)return null;if(a||m){let e=a??m;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!h||h.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)(l,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,disabled:!C,onClick:M,children:t.discard}),(0,S.jsx)(l,{type:`button`,variant:`ghost`,className:`h-8 text-xs`,onClick:N,children:`Reset to defaults`}),(0,S.jsx)(l,{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:h,values:y,onChange:j,disabled:T})]})}function B(){let e=p(o(e=>e.language)),t=e.extensionImageGen,{extensionId:n,panelId:r}=i(),a=m();if(!n)return(0,S.jsx)(V,{message:`No extension ID provided.`});let c=a.find(e=>e.id===n);if(!c)return(0,S.jsx)(V,{message:`Extension "${n}" not found or is not available in this workspace.`});let l=c.ui?.contributions?.settingsPanels,u=r?l?.find(e=>e.id===r||e.id===`${n}.${r}`):l?.[0],d=!!(u&&c.ui),f=!!c.hasConfigSchema,g=c.kind===`image-generation`;return!f&&!d&&!g?(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:u?.title??`${c.name} Settings`}),g?(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)(s,{to:`/settings/image-models`,className:`w-fit font-medium text-accent hover:underline`,title:e.imageModelsSettings.imageModelsLinkTitle,children:t.openImageModels})]}):null,g?(0,S.jsx)(T,{extensionId:n}):null,f?(0,S.jsx)(z,{extensionId:n}):null,d&&u&&c.ui?(0,S.jsx)(`div`,{className:`overflow-hidden rounded-xl border border-edge bg-surface-base`,children:(0,S.jsx)(h,{extensionId:n,extensionName:c.name,entrypoint:u.entrypoint,permissions:c.ui?.permissions,title:u.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-7atlEJU0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension-settings-page-DAly-C6g.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-7atlEJU0.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,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{o as r}from"./vendor-swr-B5fPo7KK.js";import{En as i,Ln as a,St as o,dn as s,gn as c,kn as l}from"./index-DHte7V8E.js";import{D as u,O as d}from"./cron-dreaming-jobs-DIwTeQzu.js";var f=e(t(),1),p=n(),m=a(l,`min-w-0 shrink text-xs sm:min-w-[7rem] sm:max-w-[11rem]`),h=a(`h-9 min-w-[6.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg`,i,`dark:bg-surface-panel`),g=a(`h-9 min-w-[9.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg`,i),_=a(`h-9 w-14 shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-center text-sm tabular-nums text-fg`,i),v=a(`min-h-[2.5rem] w-full rounded-lg border border-edge-subtle bg-surface-base px-3 py-2 font-mono text-xs text-fg`,i);function y({value:e,onChange:t,labels:n,disabled:r,showHeading:i=!0}){let o=(0,f.useMemo)(()=>d(e),[e]),[s,c]=(0,f.useState)(o.mode),[l,y]=(0,f.useState)(o.intervalKind),[b,x]=(0,f.useState)(o.onceDate),[S,C]=(0,f.useState)(o.intervalMinutes),[w,T]=(0,f.useState)(o.intervalHours),[E,D]=(0,f.useState)(o.minute),[O,k]=(0,f.useState)(o.hour),[A,j]=(0,f.useState)(o.weekDays),[M,N]=(0,f.useState)(o.dayOfMonth),[P,F]=(0,f.useState)(o.rawCron),[I,L]=(0,f.useState)(null),[R,z]=(0,f.useState)(null);(0,f.useEffect)(()=>{let t=d(e);c(t.mode),y(t.intervalKind),x(t.onceDate),C(t.intervalMinutes),T(t.intervalHours),D(t.minute),k(t.hour),j(t.weekDays),N(t.dayOfMonth),F(t.rawCron),L(null),z(null)},[e]);let B=(0,f.useCallback)(e=>{t(u({mode:s,intervalKind:l,onceDate:b,intervalMinutes:S,intervalHours:w,minute:E,hour:O,weekDays:A,dayOfMonth:M,rawCron:P,...e}))},[s,l,b,S,w,E,O,A,M,P,t]),V=`${String(O).padStart(2,`0`)}:${String(E).padStart(2,`0`)}`,H=e=>{let t=e.target.value;if(!t)return;let[n,r]=t.split(`:`).map(e=>parseInt(e,10));Number.isNaN(n)||Number.isNaN(r)||B({minute:r,hour:n})},U=(0,p.jsxs)(`div`,{className:`flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-0.5`,children:[(0,p.jsxs)(`select`,{className:m,disabled:r,value:s,onChange:e=>{let t=e.target.value;c(t),B({mode:t})},"aria-label":n.scheduleTimeLabel,children:[(0,p.jsx)(`option`,{value:`no_repeat`,children:n.modeNoRepeat}),(0,p.jsx)(`option`,{value:`interval`,children:n.modeInterval}),(0,p.jsx)(`option`,{value:`hourly`,children:n.modeHourly}),(0,p.jsx)(`option`,{value:`daily`,children:n.modeDaily}),(0,p.jsx)(`option`,{value:`weekly`,children:n.modeWeekly}),(0,p.jsx)(`option`,{value:`monthly`,children:n.modeMonthly}),(0,p.jsx)(`option`,{value:`custom`,children:n.modeCustom})]}),s===`no_repeat`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`input`,{type:`date`,disabled:r,className:g,value:b,onChange:e=>{let t=e.target.value;x(t),B({onceDate:t})},"aria-label":n.modeNoRepeat}),(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel})]}),s===`interval`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`select`,{className:a(m,`w-auto min-w-[5rem]`),disabled:r,value:l,onChange:e=>{let t=e.target.value;y(t),L(null),z(null),B({intervalKind:t})},"aria-label":n.modeInterval,children:[(0,p.jsx)(`option`,{value:`minutes`,children:n.intervalKindMinutes}),(0,p.jsx)(`option`,{value:`hours`,children:n.intervalKindHours})]}),l===`minutes`?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`input`,{type:`number`,min:1,max:59,disabled:r,className:_,value:I??String(S),onChange:e=>{let t=e.target.value;L(t);let n=parseInt(t,10);Number.isFinite(n)&&n>=1&&n<=59&&(C(n),B({intervalMinutes:n}))},onBlur:()=>{if(I===null)return;let e=parseInt(I,10),t=!Number.isFinite(e)||e<1?5:Math.min(59,Math.max(1,Math.round(e)));C(t),B({intervalMinutes:t}),L(null)},"aria-label":n.intervalMinutes}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.minuteUnit})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`input`,{type:`number`,min:1,max:23,disabled:r,className:_,value:R??String(w),onChange:e=>{let t=e.target.value;z(t);let n=parseInt(t,10);Number.isFinite(n)&&n>=1&&n<=23&&(T(n),B({intervalHours:n}))},onBlur:()=>{if(R===null)return;let e=parseInt(R,10),t=!Number.isFinite(e)||e<1?2:Math.min(23,Math.max(1,Math.round(e)));T(t),B({intervalHours:t}),z(null)},"aria-label":n.intervalHours}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.hourUnit}),(0,p.jsx)(`select`,{className:a(m,`w-auto min-w-[4rem]`),disabled:r,value:E,onChange:e=>{let t=parseInt(e.target.value,10);D(t),B({minute:t})},"aria-label":n.minuteAtHour,children:Array.from({length:60},(e,t)=>(0,p.jsx)(`option`,{value:t,children:String(t).padStart(2,`0`)},t))}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.minuteUnit})]})]}),s===`daily`&&(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel}),s===`weekly`&&(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel}),s===`monthly`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`select`,{className:a(m,`w-auto min-w-[4.5rem]`),disabled:r,value:M,onChange:e=>{let t=parseInt(e.target.value,10);N(t),B({dayOfMonth:t})},"aria-label":n.dayOfMonth,children:Array.from({length:31},(e,t)=>t+1).map(e=>(0,p.jsx)(`option`,{value:e,children:e},e))}),(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel})]}),s===`hourly`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`select`,{className:a(m,`w-auto min-w-[4rem]`),disabled:r,value:E,onChange:e=>{let t=parseInt(e.target.value,10);D(t),B({minute:t})},"aria-label":n.minuteAtHour,children:Array.from({length:60},(e,t)=>(0,p.jsx)(`option`,{value:t,children:String(t).padStart(2,`0`)},t))}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.minuteUnit})]})]}),W=s===`weekly`?(0,p.jsx)(`div`,{className:`flex flex-wrap gap-1.5 pt-1`,role:`group`,"aria-label":n.modeWeekly,children:n.weekdays.map((e,t)=>{let n=A[t];return(0,p.jsx)(`button`,{type:`button`,disabled:r,className:a(`flex size-9 select-none items-center justify-center rounded-full border text-xs font-medium transition-colors`,n?`border-fg bg-fg text-surface-panel`:`border-edge-subtle bg-surface-panel text-fg hover:border-edge`),"aria-pressed":n,onClick:()=>{let e=[...A];e[t]=!e[t],j(e),B({weekDays:e})},children:e},e)})}):null,G=s===`custom`?(0,p.jsxs)(`div`,{className:`pt-1`,children:[(0,p.jsx)(`textarea`,{disabled:r,className:v,rows:2,spellCheck:!1,value:P,placeholder:`*/5 * * * *`,onChange:e=>{let n=e.target.value;F(n),t(n.trim()||`*/5 * * * *`)},"aria-label":n.modeCustom}),(0,p.jsx)(`p`,{className:`mt-1 text-xs text-fg-muted`,children:n.customCronHint})]}):null;return(0,p.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[i?(0,p.jsx)(`span`,{className:`text-xs font-medium text-fg-muted`,children:n.scheduleTimeLabel}):null,U,W,G]})}function b(){return s(`/api/workspace/heartbeat-md`)}async function x(){let e=await c(b());return typeof e.payload?.content==`string`?e.payload.content:``}function S(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function C(e){let t=S(e)?e:{},n=S(t.gateway)?t.gateway:{},r=S(n.heartbeat)?n.heartbeat:{},i=r.activeHours,a=S(i)?i:null,o=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:o}}function w(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 T(e){await c(s(`/api/config`),{method:`PATCH`,body:JSON.stringify({gateway:{heartbeat:w(e)}})}),o()}async function E(e){await c(s(`/api/workspace/heartbeat-md`),{method:`PUT`,body:JSON.stringify({content:e})}),r(b())}async function D(e){await c(s(`/api/heartbeat/trigger`),{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e?{reason:e}:{})})}export{x as a,D as i,T as n,b as o,E as r,y as s,C as t};
2
- //# sourceMappingURL=heartbeat-config-api-SijGy10-.js.map
1
+ import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{i as t,t as n}from"./vendor-react-DbimaAId.js";import{o as r}from"./vendor-swr-B5fPo7KK.js";import{En as i,Ln as a,St as o,dn as s,gn as c,kn as l}from"./index-DvMI6Vqn.js";import{D as u,O as d}from"./cron-dreaming-jobs-D5kvbjSY.js";var f=e(t(),1),p=n(),m=a(l,`min-w-0 shrink text-xs sm:min-w-[7rem] sm:max-w-[11rem]`),h=a(`h-9 min-w-[6.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg`,i,`dark:bg-surface-panel`),g=a(`h-9 min-w-[9.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg`,i),_=a(`h-9 w-14 shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-center text-sm tabular-nums text-fg`,i),v=a(`min-h-[2.5rem] w-full rounded-lg border border-edge-subtle bg-surface-base px-3 py-2 font-mono text-xs text-fg`,i);function y({value:e,onChange:t,labels:n,disabled:r,showHeading:i=!0}){let o=(0,f.useMemo)(()=>d(e),[e]),[s,c]=(0,f.useState)(o.mode),[l,y]=(0,f.useState)(o.intervalKind),[b,x]=(0,f.useState)(o.onceDate),[S,C]=(0,f.useState)(o.intervalMinutes),[w,T]=(0,f.useState)(o.intervalHours),[E,D]=(0,f.useState)(o.minute),[O,k]=(0,f.useState)(o.hour),[A,j]=(0,f.useState)(o.weekDays),[M,N]=(0,f.useState)(o.dayOfMonth),[P,F]=(0,f.useState)(o.rawCron),[I,L]=(0,f.useState)(null),[R,z]=(0,f.useState)(null);(0,f.useEffect)(()=>{let t=d(e);c(t.mode),y(t.intervalKind),x(t.onceDate),C(t.intervalMinutes),T(t.intervalHours),D(t.minute),k(t.hour),j(t.weekDays),N(t.dayOfMonth),F(t.rawCron),L(null),z(null)},[e]);let B=(0,f.useCallback)(e=>{t(u({mode:s,intervalKind:l,onceDate:b,intervalMinutes:S,intervalHours:w,minute:E,hour:O,weekDays:A,dayOfMonth:M,rawCron:P,...e}))},[s,l,b,S,w,E,O,A,M,P,t]),V=`${String(O).padStart(2,`0`)}:${String(E).padStart(2,`0`)}`,H=e=>{let t=e.target.value;if(!t)return;let[n,r]=t.split(`:`).map(e=>parseInt(e,10));Number.isNaN(n)||Number.isNaN(r)||B({minute:r,hour:n})},U=(0,p.jsxs)(`div`,{className:`flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-0.5`,children:[(0,p.jsxs)(`select`,{className:m,disabled:r,value:s,onChange:e=>{let t=e.target.value;c(t),B({mode:t})},"aria-label":n.scheduleTimeLabel,children:[(0,p.jsx)(`option`,{value:`no_repeat`,children:n.modeNoRepeat}),(0,p.jsx)(`option`,{value:`interval`,children:n.modeInterval}),(0,p.jsx)(`option`,{value:`hourly`,children:n.modeHourly}),(0,p.jsx)(`option`,{value:`daily`,children:n.modeDaily}),(0,p.jsx)(`option`,{value:`weekly`,children:n.modeWeekly}),(0,p.jsx)(`option`,{value:`monthly`,children:n.modeMonthly}),(0,p.jsx)(`option`,{value:`custom`,children:n.modeCustom})]}),s===`no_repeat`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`input`,{type:`date`,disabled:r,className:g,value:b,onChange:e=>{let t=e.target.value;x(t),B({onceDate:t})},"aria-label":n.modeNoRepeat}),(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel})]}),s===`interval`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`select`,{className:a(m,`w-auto min-w-[5rem]`),disabled:r,value:l,onChange:e=>{let t=e.target.value;y(t),L(null),z(null),B({intervalKind:t})},"aria-label":n.modeInterval,children:[(0,p.jsx)(`option`,{value:`minutes`,children:n.intervalKindMinutes}),(0,p.jsx)(`option`,{value:`hours`,children:n.intervalKindHours})]}),l===`minutes`?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`input`,{type:`number`,min:1,max:59,disabled:r,className:_,value:I??String(S),onChange:e=>{let t=e.target.value;L(t);let n=parseInt(t,10);Number.isFinite(n)&&n>=1&&n<=59&&(C(n),B({intervalMinutes:n}))},onBlur:()=>{if(I===null)return;let e=parseInt(I,10),t=!Number.isFinite(e)||e<1?5:Math.min(59,Math.max(1,Math.round(e)));C(t),B({intervalMinutes:t}),L(null)},"aria-label":n.intervalMinutes}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.minuteUnit})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`input`,{type:`number`,min:1,max:23,disabled:r,className:_,value:R??String(w),onChange:e=>{let t=e.target.value;z(t);let n=parseInt(t,10);Number.isFinite(n)&&n>=1&&n<=23&&(T(n),B({intervalHours:n}))},onBlur:()=>{if(R===null)return;let e=parseInt(R,10),t=!Number.isFinite(e)||e<1?2:Math.min(23,Math.max(1,Math.round(e)));T(t),B({intervalHours:t}),z(null)},"aria-label":n.intervalHours}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.hourUnit}),(0,p.jsx)(`select`,{className:a(m,`w-auto min-w-[4rem]`),disabled:r,value:E,onChange:e=>{let t=parseInt(e.target.value,10);D(t),B({minute:t})},"aria-label":n.minuteAtHour,children:Array.from({length:60},(e,t)=>(0,p.jsx)(`option`,{value:t,children:String(t).padStart(2,`0`)},t))}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.minuteUnit})]})]}),s===`daily`&&(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel}),s===`weekly`&&(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel}),s===`monthly`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`select`,{className:a(m,`w-auto min-w-[4.5rem]`),disabled:r,value:M,onChange:e=>{let t=parseInt(e.target.value,10);N(t),B({dayOfMonth:t})},"aria-label":n.dayOfMonth,children:Array.from({length:31},(e,t)=>t+1).map(e=>(0,p.jsx)(`option`,{value:e,children:e},e))}),(0,p.jsx)(`input`,{type:`time`,step:60,disabled:r,className:h,value:V,onChange:H,"aria-label":n.scheduleTimeLabel})]}),s===`hourly`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`select`,{className:a(m,`w-auto min-w-[4rem]`),disabled:r,value:E,onChange:e=>{let t=parseInt(e.target.value,10);D(t),B({minute:t})},"aria-label":n.minuteAtHour,children:Array.from({length:60},(e,t)=>(0,p.jsx)(`option`,{value:t,children:String(t).padStart(2,`0`)},t))}),(0,p.jsx)(`span`,{className:`shrink-0 text-sm text-fg-muted`,children:n.minuteUnit})]})]}),W=s===`weekly`?(0,p.jsx)(`div`,{className:`flex flex-wrap gap-1.5 pt-1`,role:`group`,"aria-label":n.modeWeekly,children:n.weekdays.map((e,t)=>{let n=A[t];return(0,p.jsx)(`button`,{type:`button`,disabled:r,className:a(`flex size-9 select-none items-center justify-center rounded-full border text-xs font-medium transition-colors`,n?`border-fg bg-fg text-surface-panel`:`border-edge-subtle bg-surface-panel text-fg hover:border-edge`),"aria-pressed":n,onClick:()=>{let e=[...A];e[t]=!e[t],j(e),B({weekDays:e})},children:e},e)})}):null,G=s===`custom`?(0,p.jsxs)(`div`,{className:`pt-1`,children:[(0,p.jsx)(`textarea`,{disabled:r,className:v,rows:2,spellCheck:!1,value:P,placeholder:`*/5 * * * *`,onChange:e=>{let n=e.target.value;F(n),t(n.trim()||`*/5 * * * *`)},"aria-label":n.modeCustom}),(0,p.jsx)(`p`,{className:`mt-1 text-xs text-fg-muted`,children:n.customCronHint})]}):null;return(0,p.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[i?(0,p.jsx)(`span`,{className:`text-xs font-medium text-fg-muted`,children:n.scheduleTimeLabel}):null,U,W,G]})}function b(){return s(`/api/workspace/heartbeat-md`)}async function x(){let e=await c(b());return typeof e.payload?.content==`string`?e.payload.content:``}function S(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function C(e){let t=S(e)?e:{},n=S(t.gateway)?t.gateway:{},r=S(n.heartbeat)?n.heartbeat:{},i=r.activeHours,a=S(i)?i:null,o=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:o}}function w(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 T(e){await c(s(`/api/config`),{method:`PATCH`,body:JSON.stringify({gateway:{heartbeat:w(e)}})}),o()}async function E(e){await c(s(`/api/workspace/heartbeat-md`),{method:`PUT`,body:JSON.stringify({content:e})}),r(b())}async function D(e){await c(s(`/api/heartbeat/trigger`),{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e?{reason:e}:{})})}export{x as a,D as i,T as n,b as o,E as r,y as s,C as t};
2
+ //# sourceMappingURL=heartbeat-config-api-DnmmSrTx.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"heartbeat-config-api-SijGy10-.js","names":[],"sources":["../../../../../web/src/features/scheduling/cron/cron-schedule-picker.tsx","../../../../../web/src/features/settings/heartbeat-md-swr.ts","../../../../../web/src/features/settings/heartbeat-config-api.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type ChangeEvent } from 'react';\n\nimport {\n buildCronFromPickerState,\n cronExpressionToPickerState,\n type IntervalKind,\n type PickerState,\n type SchedulePickerMode,\n} from '@/features/scheduling/cron/cron-expression';\nimport { cn } from '@/lib/cn';\nimport { formControlBorderFocusClass, selectControlBaseClass } from '@/lib/form-field-width';\n\nexport type { IntervalKind, PickerState, SchedulePickerMode } from '@/features/scheduling/cron/cron-expression';\nexport { buildCronFromPickerState, cronExpressionToPickerState } from '@/features/scheduling/cron/cron-expression';\n\nexport type CronSchedulePickerLabels = {\n scheduleTimeLabel: string;\n modeNoRepeat: string;\n modeInterval: string;\n intervalKindMinutes: string;\n intervalKindHours: string;\n modeHourly: string;\n modeDaily: string;\n modeWeekly: string;\n modeMonthly: string;\n modeCustom: string;\n minuteUnit: string;\n minuteAtHour: string;\n intervalMinutes: string;\n intervalHours: string;\n hourUnit: string;\n dayOfMonth: string;\n customCronHint: string;\n weekdays: readonly string[];\n};\n\nconst pickerSelectClass = cn(\n selectControlBaseClass,\n 'min-w-0 shrink text-xs sm:min-w-[7rem] sm:max-w-[11rem]',\n);\n\nconst timeInputClass = cn(\n 'h-9 min-w-[6.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg',\n formControlBorderFocusClass,\n 'dark:bg-surface-panel',\n);\n\nconst dateInputClass = cn(\n 'h-9 min-w-[9.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg',\n formControlBorderFocusClass,\n);\n\nconst numberInputClass = cn(\n 'h-9 w-14 shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-center text-sm tabular-nums text-fg',\n formControlBorderFocusClass,\n);\n\nconst cronTextareaClass = cn(\n 'min-h-[2.5rem] w-full rounded-lg border border-edge-subtle bg-surface-base px-3 py-2 font-mono text-xs text-fg',\n formControlBorderFocusClass,\n);\n\ntype CronSchedulePickerProps = {\n value: string;\n onChange: (cron: string) => void;\n labels: CronSchedulePickerLabels;\n disabled?: boolean;\n /** When false, hides the top \"Scheduled time\" label (e.g. when wrapped by ScheduleField). */\n showHeading?: boolean;\n};\n\nexport function CronSchedulePicker({\n value,\n onChange,\n labels,\n disabled,\n showHeading = true,\n}: CronSchedulePickerProps) {\n const parsed = useMemo(() => cronExpressionToPickerState(value), [value]);\n\n const [mode, setMode] = useState<SchedulePickerMode>(parsed.mode);\n const [intervalKind, setIntervalKind] = useState<IntervalKind>(parsed.intervalKind);\n const [onceDate, setOnceDate] = useState(parsed.onceDate);\n const [intervalMinutes, setIntervalMinutes] = useState(parsed.intervalMinutes);\n const [intervalHours, setIntervalHours] = useState(parsed.intervalHours);\n const [minute, setMinute] = useState(parsed.minute);\n const [hour, setHour] = useState(parsed.hour);\n const [weekDays, setWeekDays] = useState<boolean[]>(parsed.weekDays);\n const [dayOfMonth, setDayOfMonth] = useState(parsed.dayOfMonth);\n const [rawCron, setRawCron] = useState(parsed.rawCron);\n const [intervalMinutesDraft, setIntervalMinutesDraft] = useState<string | null>(null);\n const [intervalHoursDraft, setIntervalHoursDraft] = useState<string | null>(null);\n\n useEffect(() => {\n const p = cronExpressionToPickerState(value);\n setMode(p.mode);\n setIntervalKind(p.intervalKind);\n setOnceDate(p.onceDate);\n setIntervalMinutes(p.intervalMinutes);\n setIntervalHours(p.intervalHours);\n setMinute(p.minute);\n setHour(p.hour);\n setWeekDays(p.weekDays);\n setDayOfMonth(p.dayOfMonth);\n setRawCron(p.rawCron);\n setIntervalMinutesDraft(null);\n setIntervalHoursDraft(null);\n }, [value]);\n\n const emit = useCallback(\n (patch: Partial<PickerState>) => {\n const next: PickerState = {\n mode,\n intervalKind,\n onceDate,\n intervalMinutes,\n intervalHours,\n minute,\n hour,\n weekDays,\n dayOfMonth,\n rawCron,\n ...patch,\n };\n onChange(buildCronFromPickerState(next));\n },\n [\n mode,\n intervalKind,\n onceDate,\n intervalMinutes,\n intervalHours,\n minute,\n hour,\n weekDays,\n dayOfMonth,\n rawCron,\n onChange,\n ],\n );\n\n const timeValue = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;\n\n const onTimeChange = (e: ChangeEvent<HTMLInputElement>) => {\n const v = e.target.value;\n if (!v) return;\n const [hh, mm] = v.split(':').map((x) => parseInt(x, 10));\n if (Number.isNaN(hh) || Number.isNaN(mm)) return;\n emit({ minute: mm, hour: hh });\n };\n\n const modeRow = (\n <div className=\"flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-0.5\">\n <select\n className={pickerSelectClass}\n disabled={disabled}\n value={mode}\n onChange={(e) => {\n const next = e.target.value as SchedulePickerMode;\n setMode(next);\n emit({ mode: next });\n }}\n aria-label={labels.scheduleTimeLabel}\n >\n <option value=\"no_repeat\">{labels.modeNoRepeat}</option>\n <option value=\"interval\">{labels.modeInterval}</option>\n <option value=\"hourly\">{labels.modeHourly}</option>\n <option value=\"daily\">{labels.modeDaily}</option>\n <option value=\"weekly\">{labels.modeWeekly}</option>\n <option value=\"monthly\">{labels.modeMonthly}</option>\n <option value=\"custom\">{labels.modeCustom}</option>\n </select>\n\n {mode === 'no_repeat' && (\n <>\n <input\n type=\"date\"\n disabled={disabled}\n className={dateInputClass}\n value={onceDate}\n onChange={(e) => {\n const v = e.target.value;\n setOnceDate(v);\n emit({ onceDate: v });\n }}\n aria-label={labels.modeNoRepeat}\n />\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n </>\n )}\n\n {mode === 'interval' && (\n <>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[5rem]')}\n disabled={disabled}\n value={intervalKind}\n onChange={(e) => {\n const k = e.target.value as IntervalKind;\n setIntervalKind(k);\n setIntervalMinutesDraft(null);\n setIntervalHoursDraft(null);\n emit({ intervalKind: k });\n }}\n aria-label={labels.modeInterval}\n >\n <option value=\"minutes\">{labels.intervalKindMinutes}</option>\n <option value=\"hours\">{labels.intervalKindHours}</option>\n </select>\n {intervalKind === 'minutes' ? (\n <>\n <input\n type=\"number\"\n min={1}\n max={59}\n disabled={disabled}\n className={numberInputClass}\n value={intervalMinutesDraft ?? String(intervalMinutes)}\n onChange={(e) => {\n const next = e.target.value;\n setIntervalMinutesDraft(next);\n const raw = parseInt(next, 10);\n if (Number.isFinite(raw) && raw >= 1 && raw <= 59) {\n setIntervalMinutes(raw);\n emit({ intervalMinutes: raw });\n }\n }}\n onBlur={() => {\n if (intervalMinutesDraft === null) return;\n const raw = parseInt(intervalMinutesDraft, 10);\n const v =\n !Number.isFinite(raw) || raw < 1\n ? 5\n : Math.min(59, Math.max(1, Math.round(raw)));\n setIntervalMinutes(v);\n emit({ intervalMinutes: v });\n setIntervalMinutesDraft(null);\n }}\n aria-label={labels.intervalMinutes}\n />\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.minuteUnit}</span>\n </>\n ) : (\n <>\n <input\n type=\"number\"\n min={1}\n max={23}\n disabled={disabled}\n className={numberInputClass}\n value={intervalHoursDraft ?? String(intervalHours)}\n onChange={(e) => {\n const next = e.target.value;\n setIntervalHoursDraft(next);\n const raw = parseInt(next, 10);\n if (Number.isFinite(raw) && raw >= 1 && raw <= 23) {\n setIntervalHours(raw);\n emit({ intervalHours: raw });\n }\n }}\n onBlur={() => {\n if (intervalHoursDraft === null) return;\n const raw = parseInt(intervalHoursDraft, 10);\n const v =\n !Number.isFinite(raw) || raw < 1\n ? 2\n : Math.min(23, Math.max(1, Math.round(raw)));\n setIntervalHours(v);\n emit({ intervalHours: v });\n setIntervalHoursDraft(null);\n }}\n aria-label={labels.intervalHours}\n />\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.hourUnit}</span>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[4rem]')}\n disabled={disabled}\n value={minute}\n onChange={(e) => {\n const mm = parseInt(e.target.value, 10);\n setMinute(mm);\n emit({ minute: mm });\n }}\n aria-label={labels.minuteAtHour}\n >\n {Array.from({ length: 60 }, (_, i) => (\n <option key={i} value={i}>\n {String(i).padStart(2, '0')}\n </option>\n ))}\n </select>\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.minuteUnit}</span>\n </>\n )}\n </>\n )}\n\n {mode === 'daily' && (\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n )}\n\n {mode === 'weekly' && (\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n )}\n\n {mode === 'monthly' && (\n <>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[4.5rem]')}\n disabled={disabled}\n value={dayOfMonth}\n onChange={(e) => {\n const d = parseInt(e.target.value, 10);\n setDayOfMonth(d);\n emit({ dayOfMonth: d });\n }}\n aria-label={labels.dayOfMonth}\n >\n {Array.from({ length: 31 }, (_, i) => i + 1).map((d) => (\n <option key={d} value={d}>\n {d}\n </option>\n ))}\n </select>\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n </>\n )}\n\n {mode === 'hourly' && (\n <>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[4rem]')}\n disabled={disabled}\n value={minute}\n onChange={(e) => {\n const mm = parseInt(e.target.value, 10);\n setMinute(mm);\n emit({ minute: mm });\n }}\n aria-label={labels.minuteAtHour}\n >\n {Array.from({ length: 60 }, (_, i) => (\n <option key={i} value={i}>\n {String(i).padStart(2, '0')}\n </option>\n ))}\n </select>\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.minuteUnit}</span>\n </>\n )}\n </div>\n );\n\n const weekRow =\n mode === 'weekly' ? (\n <div className=\"flex flex-wrap gap-1.5 pt-1\" role=\"group\" aria-label={labels.modeWeekly}>\n {labels.weekdays.map((label, i) => {\n const on = weekDays[i];\n return (\n <button\n key={label}\n type=\"button\"\n disabled={disabled}\n className={cn(\n 'flex size-9 select-none items-center justify-center rounded-full border text-xs font-medium transition-colors',\n on\n ? 'border-fg bg-fg text-surface-panel'\n : 'border-edge-subtle bg-surface-panel text-fg hover:border-edge',\n )}\n aria-pressed={on}\n onClick={() => {\n const next = [...weekDays];\n next[i] = !next[i];\n setWeekDays(next);\n emit({ weekDays: next });\n }}\n >\n {label}\n </button>\n );\n })}\n </div>\n ) : null;\n\n const customBlock =\n mode === 'custom' ? (\n <div className=\"pt-1\">\n <textarea\n disabled={disabled}\n className={cronTextareaClass}\n rows={2}\n spellCheck={false}\n value={rawCron}\n placeholder=\"*/5 * * * *\"\n onChange={(e) => {\n const v = e.target.value;\n setRawCron(v);\n onChange(v.trim() || '*/5 * * * *');\n }}\n aria-label={labels.modeCustom}\n />\n <p className=\"mt-1 text-xs text-fg-muted\">{labels.customCronHint}</p>\n </div>\n ) : null;\n\n return (\n <div className=\"flex flex-col gap-2\">\n {showHeading ? (\n <span className=\"text-xs font-medium text-fg-muted\">{labels.scheduleTimeLabel}</span>\n ) : null}\n {modeRow}\n {weekRow}\n {customBlock}\n </div>\n );\n}\n","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":"wTAoCM,EAAoB,EACxB,EACA,0DACD,CAEK,EAAiB,EACrB,gHACA,EACA,wBACD,CAEK,EAAiB,EACrB,gHACA,EACD,CAEK,EAAmB,EACvB,+HACA,EACD,CAEK,EAAoB,EACxB,iHACA,EACD,CAWD,SAAgB,EAAmB,CACjC,QACA,WACA,SACA,WACA,cAAc,IACY,CAC1B,IAAM,GAAA,EAAA,EAAA,aAAuB,EAA4B,EAAM,CAAE,CAAC,EAAM,CAAC,CAEnE,CAAC,EAAM,IAAA,EAAA,EAAA,UAAwC,EAAO,KAAK,CAC3D,CAAC,EAAc,IAAA,EAAA,EAAA,UAA0C,EAAO,aAAa,CAC7E,CAAC,EAAU,IAAA,EAAA,EAAA,UAAwB,EAAO,SAAS,CACnD,CAAC,EAAiB,IAAA,EAAA,EAAA,UAA+B,EAAO,gBAAgB,CACxE,CAAC,EAAe,IAAA,EAAA,EAAA,UAA6B,EAAO,cAAc,CAClE,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAsB,EAAO,OAAO,CAC7C,CAAC,EAAM,IAAA,EAAA,EAAA,UAAoB,EAAO,KAAK,CACvC,CAAC,EAAU,IAAA,EAAA,EAAA,UAAmC,EAAO,SAAS,CAC9D,CAAC,EAAY,IAAA,EAAA,EAAA,UAA0B,EAAO,WAAW,CACzD,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuB,EAAO,QAAQ,CAChD,CAAC,EAAsB,IAAA,EAAA,EAAA,UAAmD,KAAK,CAC/E,CAAC,EAAoB,IAAA,EAAA,EAAA,UAAiD,KAAK,EAEjF,EAAA,EAAA,eAAgB,CACd,IAAM,EAAI,EAA4B,EAAM,CAC5C,EAAQ,EAAE,KAAK,CACf,EAAgB,EAAE,aAAa,CAC/B,EAAY,EAAE,SAAS,CACvB,EAAmB,EAAE,gBAAgB,CACrC,EAAiB,EAAE,cAAc,CACjC,EAAU,EAAE,OAAO,CACnB,EAAQ,EAAE,KAAK,CACf,EAAY,EAAE,SAAS,CACvB,EAAc,EAAE,WAAW,CAC3B,EAAW,EAAE,QAAQ,CACrB,EAAwB,KAAK,CAC7B,EAAsB,KAAK,EAC1B,CAAC,EAAM,CAAC,CAEX,IAAM,GAAA,EAAA,EAAA,aACH,GAAgC,CAc/B,EAAS,EAAyB,CAZhC,OACA,eACA,WACA,kBACA,gBACA,SACA,OACA,WACA,aACA,UACA,GAAG,EAE6B,CAAK,CAAC,EAE1C,CACE,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACD,CACF,CAEK,EAAY,GAAG,OAAO,EAAK,CAAC,SAAS,EAAG,IAAI,CAAC,GAAG,OAAO,EAAO,CAAC,SAAS,EAAG,IAAI,GAE/E,EAAgB,GAAqC,CACzD,IAAM,EAAI,EAAE,OAAO,MACnB,GAAI,CAAC,EAAG,OACR,GAAM,CAAC,EAAI,GAAM,EAAE,MAAM,IAAI,CAAC,IAAK,GAAM,SAAS,EAAG,GAAG,CAAC,CACrD,OAAO,MAAM,EAAG,EAAI,OAAO,MAAM,EAAG,EACxC,EAAK,CAAE,OAAQ,EAAI,KAAM,EAAI,CAAC,EAG1B,GACJ,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,8EAAf,EACE,EAAA,EAAA,MAAC,SAAD,CACE,UAAW,EACD,WACV,MAAO,EACP,SAAW,GAAM,CACf,IAAM,EAAO,EAAE,OAAO,MACtB,EAAQ,EAAK,CACb,EAAK,CAAE,KAAM,EAAM,CAAC,EAEtB,aAAY,EAAO,2BATrB,EAWE,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,qBAAa,EAAO,aAAsB,CAAA,EACxD,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,oBAAY,EAAO,aAAsB,CAAA,EACvD,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,kBAAU,EAAO,WAAoB,CAAA,EACnD,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,iBAAS,EAAO,UAAmB,CAAA,EACjD,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,kBAAU,EAAO,WAAoB,CAAA,EACnD,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,mBAAW,EAAO,YAAqB,CAAA,EACrD,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,kBAAU,EAAO,WAAoB,CAAA,CAC5C,GAER,IAAS,cACR,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,OACK,WACV,UAAW,EACX,MAAO,EACP,SAAW,GAAM,CACf,IAAM,EAAI,EAAE,OAAO,MACnB,EAAY,EAAE,CACd,EAAK,CAAE,SAAU,EAAG,CAAC,EAEvB,aAAY,EAAO,aACnB,CAAA,EACF,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,OACL,KAAM,GACI,WACV,UAAW,EACX,MAAO,EACP,SAAU,EACV,aAAY,EAAO,kBACnB,CAAA,CACD,CAAA,CAAA,CAGJ,IAAS,aACR,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,MAAC,SAAD,CACE,UAAW,EAAG,EAAmB,sBAAsB,CAC7C,WACV,MAAO,EACP,SAAW,GAAM,CACf,IAAM,EAAI,EAAE,OAAO,MACnB,EAAgB,EAAE,CAClB,EAAwB,KAAK,CAC7B,EAAsB,KAAK,CAC3B,EAAK,CAAE,aAAc,EAAG,CAAC,EAE3B,aAAY,EAAO,sBAXrB,EAaE,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,mBAAW,EAAO,oBAA6B,CAAA,EAC7D,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,iBAAS,EAAO,kBAA2B,CAAA,CAClD,GACR,IAAiB,WAChB,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,SACL,IAAK,EACL,IAAK,GACK,WACV,UAAW,EACX,MAAO,GAAwB,OAAO,EAAgB,CACtD,SAAW,GAAM,CACf,IAAM,EAAO,EAAE,OAAO,MACtB,EAAwB,EAAK,CAC7B,IAAM,EAAM,SAAS,EAAM,GAAG,CAC1B,OAAO,SAAS,EAAI,EAAI,GAAO,GAAK,GAAO,KAC7C,EAAmB,EAAI,CACvB,EAAK,CAAE,gBAAiB,EAAK,CAAC,GAGlC,WAAc,CACZ,GAAI,IAAyB,KAAM,OACnC,IAAM,EAAM,SAAS,EAAsB,GAAG,CACxC,EACJ,CAAC,OAAO,SAAS,EAAI,EAAI,EAAM,EAC3B,EACA,KAAK,IAAI,GAAI,KAAK,IAAI,EAAG,KAAK,MAAM,EAAI,CAAC,CAAC,CAChD,EAAmB,EAAE,CACrB,EAAK,CAAE,gBAAiB,EAAG,CAAC,CAC5B,EAAwB,KAAK,EAE/B,aAAY,EAAO,gBACnB,CAAA,EACF,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0CAAkC,EAAO,WAAkB,CAAA,CAC1E,CAAA,CAAA,EAEH,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,SACL,IAAK,EACL,IAAK,GACK,WACV,UAAW,EACX,MAAO,GAAsB,OAAO,EAAc,CAClD,SAAW,GAAM,CACf,IAAM,EAAO,EAAE,OAAO,MACtB,EAAsB,EAAK,CAC3B,IAAM,EAAM,SAAS,EAAM,GAAG,CAC1B,OAAO,SAAS,EAAI,EAAI,GAAO,GAAK,GAAO,KAC7C,EAAiB,EAAI,CACrB,EAAK,CAAE,cAAe,EAAK,CAAC,GAGhC,WAAc,CACZ,GAAI,IAAuB,KAAM,OACjC,IAAM,EAAM,SAAS,EAAoB,GAAG,CACtC,EACJ,CAAC,OAAO,SAAS,EAAI,EAAI,EAAM,EAC3B,EACA,KAAK,IAAI,GAAI,KAAK,IAAI,EAAG,KAAK,MAAM,EAAI,CAAC,CAAC,CAChD,EAAiB,EAAE,CACnB,EAAK,CAAE,cAAe,EAAG,CAAC,CAC1B,EAAsB,KAAK,EAE7B,aAAY,EAAO,cACnB,CAAA,EACF,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0CAAkC,EAAO,SAAgB,CAAA,EACzE,EAAA,EAAA,KAAC,SAAD,CACE,UAAW,EAAG,EAAmB,sBAAsB,CAC7C,WACV,MAAO,EACP,SAAW,GAAM,CACf,IAAM,EAAK,SAAS,EAAE,OAAO,MAAO,GAAG,CACvC,EAAU,EAAG,CACb,EAAK,CAAE,OAAQ,EAAI,CAAC,EAEtB,aAAY,EAAO,sBAElB,MAAM,KAAK,CAAE,OAAQ,GAAI,EAAG,EAAG,KAC9B,EAAA,EAAA,KAAC,SAAD,CAAgB,MAAO,WACpB,OAAO,EAAE,CAAC,SAAS,EAAG,IAAI,CACpB,CAFI,EAEJ,CACT,CACK,CAAA,EACT,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0CAAkC,EAAO,WAAkB,CAAA,CAC1E,CAAA,CAAA,CAEJ,CAAA,CAAA,CAGJ,IAAS,UACR,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,OACL,KAAM,GACI,WACV,UAAW,EACX,MAAO,EACP,SAAU,EACV,aAAY,EAAO,kBACnB,CAAA,CAGH,IAAS,WACR,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,OACL,KAAM,GACI,WACV,UAAW,EACX,MAAO,EACP,SAAU,EACV,aAAY,EAAO,kBACnB,CAAA,CAGH,IAAS,YACR,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,SAAD,CACE,UAAW,EAAG,EAAmB,wBAAwB,CAC/C,WACV,MAAO,EACP,SAAW,GAAM,CACf,IAAM,EAAI,SAAS,EAAE,OAAO,MAAO,GAAG,CACtC,EAAc,EAAE,CAChB,EAAK,CAAE,WAAY,EAAG,CAAC,EAEzB,aAAY,EAAO,oBAElB,MAAM,KAAK,CAAE,OAAQ,GAAI,EAAG,EAAG,IAAM,EAAI,EAAE,CAAC,IAAK,IAChD,EAAA,EAAA,KAAC,SAAD,CAAgB,MAAO,WACpB,EACM,CAFI,EAEJ,CACT,CACK,CAAA,EACT,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,OACL,KAAM,GACI,WACV,UAAW,EACX,MAAO,EACP,SAAU,EACV,aAAY,EAAO,kBACnB,CAAA,CACD,CAAA,CAAA,CAGJ,IAAS,WACR,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,SAAD,CACE,UAAW,EAAG,EAAmB,sBAAsB,CAC7C,WACV,MAAO,EACP,SAAW,GAAM,CACf,IAAM,EAAK,SAAS,EAAE,OAAO,MAAO,GAAG,CACvC,EAAU,EAAG,CACb,EAAK,CAAE,OAAQ,EAAI,CAAC,EAEtB,aAAY,EAAO,sBAElB,MAAM,KAAK,CAAE,OAAQ,GAAI,EAAG,EAAG,KAC9B,EAAA,EAAA,KAAC,SAAD,CAAgB,MAAO,WACpB,OAAO,EAAE,CAAC,SAAS,EAAG,IAAI,CACpB,CAFI,EAEJ,CACT,CACK,CAAA,EACT,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0CAAkC,EAAO,WAAkB,CAAA,CAC1E,CAAA,CAAA,CAED,GAGF,EACJ,IAAS,UACP,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,8BAA8B,KAAK,QAAQ,aAAY,EAAO,oBAC1E,EAAO,SAAS,KAAK,EAAO,IAAM,CACjC,IAAM,EAAK,EAAS,GACpB,OACE,EAAA,EAAA,KAAC,SAAD,CAEE,KAAK,SACK,WACV,UAAW,EACT,gHACA,EACI,qCACA,gEACL,CACD,eAAc,EACd,YAAe,CACb,IAAM,EAAO,CAAC,GAAG,EAAS,CAC1B,EAAK,GAAK,CAAC,EAAK,GAChB,EAAY,EAAK,CACjB,EAAK,CAAE,SAAU,EAAM,CAAC,WAGzB,EACM,CAlBF,EAkBE,EAEX,CACE,CAAA,CACJ,KAEA,EACJ,IAAS,UACP,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,gBAAf,EACE,EAAA,EAAA,KAAC,WAAD,CACY,WACV,UAAW,EACX,KAAM,EACN,WAAY,GACZ,MAAO,EACP,YAAY,cACZ,SAAW,GAAM,CACf,IAAM,EAAI,EAAE,OAAO,MACnB,EAAW,EAAE,CACb,EAAS,EAAE,MAAM,EAAI,cAAc,EAErC,aAAY,EAAO,WACnB,CAAA,EACF,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAO,eAAmB,CAAA,CACjE,GACJ,KAEN,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,+BAAf,CACG,GACC,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,6CAAqC,EAAO,kBAAyB,CAAA,CACnF,KACH,EACA,EACA,EACG,GC1bV,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"}
1
+ {"version":3,"file":"heartbeat-config-api-DnmmSrTx.js","names":[],"sources":["../../../../../web/src/features/scheduling/cron/cron-schedule-picker.tsx","../../../../../web/src/features/settings/heartbeat-md-swr.ts","../../../../../web/src/features/settings/heartbeat-config-api.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type ChangeEvent } from 'react';\n\nimport {\n buildCronFromPickerState,\n cronExpressionToPickerState,\n type IntervalKind,\n type PickerState,\n type SchedulePickerMode,\n} from '@/features/scheduling/cron/cron-expression';\nimport { cn } from '@/lib/cn';\nimport { formControlBorderFocusClass, selectControlBaseClass } from '@/lib/form-field-width';\n\nexport type { IntervalKind, PickerState, SchedulePickerMode } from '@/features/scheduling/cron/cron-expression';\nexport { buildCronFromPickerState, cronExpressionToPickerState } from '@/features/scheduling/cron/cron-expression';\n\nexport type CronSchedulePickerLabels = {\n scheduleTimeLabel: string;\n modeNoRepeat: string;\n modeInterval: string;\n intervalKindMinutes: string;\n intervalKindHours: string;\n modeHourly: string;\n modeDaily: string;\n modeWeekly: string;\n modeMonthly: string;\n modeCustom: string;\n minuteUnit: string;\n minuteAtHour: string;\n intervalMinutes: string;\n intervalHours: string;\n hourUnit: string;\n dayOfMonth: string;\n customCronHint: string;\n weekdays: readonly string[];\n};\n\nconst pickerSelectClass = cn(\n selectControlBaseClass,\n 'min-w-0 shrink text-xs sm:min-w-[7rem] sm:max-w-[11rem]',\n);\n\nconst timeInputClass = cn(\n 'h-9 min-w-[6.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg',\n formControlBorderFocusClass,\n 'dark:bg-surface-panel',\n);\n\nconst dateInputClass = cn(\n 'h-9 min-w-[9.5rem] shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-sm text-fg',\n formControlBorderFocusClass,\n);\n\nconst numberInputClass = cn(\n 'h-9 w-14 shrink-0 rounded-lg border border-edge-subtle bg-surface-panel px-2 py-1.5 text-center text-sm tabular-nums text-fg',\n formControlBorderFocusClass,\n);\n\nconst cronTextareaClass = cn(\n 'min-h-[2.5rem] w-full rounded-lg border border-edge-subtle bg-surface-base px-3 py-2 font-mono text-xs text-fg',\n formControlBorderFocusClass,\n);\n\ntype CronSchedulePickerProps = {\n value: string;\n onChange: (cron: string) => void;\n labels: CronSchedulePickerLabels;\n disabled?: boolean;\n /** When false, hides the top \"Scheduled time\" label (e.g. when wrapped by ScheduleField). */\n showHeading?: boolean;\n};\n\nexport function CronSchedulePicker({\n value,\n onChange,\n labels,\n disabled,\n showHeading = true,\n}: CronSchedulePickerProps) {\n const parsed = useMemo(() => cronExpressionToPickerState(value), [value]);\n\n const [mode, setMode] = useState<SchedulePickerMode>(parsed.mode);\n const [intervalKind, setIntervalKind] = useState<IntervalKind>(parsed.intervalKind);\n const [onceDate, setOnceDate] = useState(parsed.onceDate);\n const [intervalMinutes, setIntervalMinutes] = useState(parsed.intervalMinutes);\n const [intervalHours, setIntervalHours] = useState(parsed.intervalHours);\n const [minute, setMinute] = useState(parsed.minute);\n const [hour, setHour] = useState(parsed.hour);\n const [weekDays, setWeekDays] = useState<boolean[]>(parsed.weekDays);\n const [dayOfMonth, setDayOfMonth] = useState(parsed.dayOfMonth);\n const [rawCron, setRawCron] = useState(parsed.rawCron);\n const [intervalMinutesDraft, setIntervalMinutesDraft] = useState<string | null>(null);\n const [intervalHoursDraft, setIntervalHoursDraft] = useState<string | null>(null);\n\n useEffect(() => {\n const p = cronExpressionToPickerState(value);\n setMode(p.mode);\n setIntervalKind(p.intervalKind);\n setOnceDate(p.onceDate);\n setIntervalMinutes(p.intervalMinutes);\n setIntervalHours(p.intervalHours);\n setMinute(p.minute);\n setHour(p.hour);\n setWeekDays(p.weekDays);\n setDayOfMonth(p.dayOfMonth);\n setRawCron(p.rawCron);\n setIntervalMinutesDraft(null);\n setIntervalHoursDraft(null);\n }, [value]);\n\n const emit = useCallback(\n (patch: Partial<PickerState>) => {\n const next: PickerState = {\n mode,\n intervalKind,\n onceDate,\n intervalMinutes,\n intervalHours,\n minute,\n hour,\n weekDays,\n dayOfMonth,\n rawCron,\n ...patch,\n };\n onChange(buildCronFromPickerState(next));\n },\n [\n mode,\n intervalKind,\n onceDate,\n intervalMinutes,\n intervalHours,\n minute,\n hour,\n weekDays,\n dayOfMonth,\n rawCron,\n onChange,\n ],\n );\n\n const timeValue = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;\n\n const onTimeChange = (e: ChangeEvent<HTMLInputElement>) => {\n const v = e.target.value;\n if (!v) return;\n const [hh, mm] = v.split(':').map((x) => parseInt(x, 10));\n if (Number.isNaN(hh) || Number.isNaN(mm)) return;\n emit({ minute: mm, hour: hh });\n };\n\n const modeRow = (\n <div className=\"flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-0.5\">\n <select\n className={pickerSelectClass}\n disabled={disabled}\n value={mode}\n onChange={(e) => {\n const next = e.target.value as SchedulePickerMode;\n setMode(next);\n emit({ mode: next });\n }}\n aria-label={labels.scheduleTimeLabel}\n >\n <option value=\"no_repeat\">{labels.modeNoRepeat}</option>\n <option value=\"interval\">{labels.modeInterval}</option>\n <option value=\"hourly\">{labels.modeHourly}</option>\n <option value=\"daily\">{labels.modeDaily}</option>\n <option value=\"weekly\">{labels.modeWeekly}</option>\n <option value=\"monthly\">{labels.modeMonthly}</option>\n <option value=\"custom\">{labels.modeCustom}</option>\n </select>\n\n {mode === 'no_repeat' && (\n <>\n <input\n type=\"date\"\n disabled={disabled}\n className={dateInputClass}\n value={onceDate}\n onChange={(e) => {\n const v = e.target.value;\n setOnceDate(v);\n emit({ onceDate: v });\n }}\n aria-label={labels.modeNoRepeat}\n />\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n </>\n )}\n\n {mode === 'interval' && (\n <>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[5rem]')}\n disabled={disabled}\n value={intervalKind}\n onChange={(e) => {\n const k = e.target.value as IntervalKind;\n setIntervalKind(k);\n setIntervalMinutesDraft(null);\n setIntervalHoursDraft(null);\n emit({ intervalKind: k });\n }}\n aria-label={labels.modeInterval}\n >\n <option value=\"minutes\">{labels.intervalKindMinutes}</option>\n <option value=\"hours\">{labels.intervalKindHours}</option>\n </select>\n {intervalKind === 'minutes' ? (\n <>\n <input\n type=\"number\"\n min={1}\n max={59}\n disabled={disabled}\n className={numberInputClass}\n value={intervalMinutesDraft ?? String(intervalMinutes)}\n onChange={(e) => {\n const next = e.target.value;\n setIntervalMinutesDraft(next);\n const raw = parseInt(next, 10);\n if (Number.isFinite(raw) && raw >= 1 && raw <= 59) {\n setIntervalMinutes(raw);\n emit({ intervalMinutes: raw });\n }\n }}\n onBlur={() => {\n if (intervalMinutesDraft === null) return;\n const raw = parseInt(intervalMinutesDraft, 10);\n const v =\n !Number.isFinite(raw) || raw < 1\n ? 5\n : Math.min(59, Math.max(1, Math.round(raw)));\n setIntervalMinutes(v);\n emit({ intervalMinutes: v });\n setIntervalMinutesDraft(null);\n }}\n aria-label={labels.intervalMinutes}\n />\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.minuteUnit}</span>\n </>\n ) : (\n <>\n <input\n type=\"number\"\n min={1}\n max={23}\n disabled={disabled}\n className={numberInputClass}\n value={intervalHoursDraft ?? String(intervalHours)}\n onChange={(e) => {\n const next = e.target.value;\n setIntervalHoursDraft(next);\n const raw = parseInt(next, 10);\n if (Number.isFinite(raw) && raw >= 1 && raw <= 23) {\n setIntervalHours(raw);\n emit({ intervalHours: raw });\n }\n }}\n onBlur={() => {\n if (intervalHoursDraft === null) return;\n const raw = parseInt(intervalHoursDraft, 10);\n const v =\n !Number.isFinite(raw) || raw < 1\n ? 2\n : Math.min(23, Math.max(1, Math.round(raw)));\n setIntervalHours(v);\n emit({ intervalHours: v });\n setIntervalHoursDraft(null);\n }}\n aria-label={labels.intervalHours}\n />\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.hourUnit}</span>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[4rem]')}\n disabled={disabled}\n value={minute}\n onChange={(e) => {\n const mm = parseInt(e.target.value, 10);\n setMinute(mm);\n emit({ minute: mm });\n }}\n aria-label={labels.minuteAtHour}\n >\n {Array.from({ length: 60 }, (_, i) => (\n <option key={i} value={i}>\n {String(i).padStart(2, '0')}\n </option>\n ))}\n </select>\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.minuteUnit}</span>\n </>\n )}\n </>\n )}\n\n {mode === 'daily' && (\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n )}\n\n {mode === 'weekly' && (\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n )}\n\n {mode === 'monthly' && (\n <>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[4.5rem]')}\n disabled={disabled}\n value={dayOfMonth}\n onChange={(e) => {\n const d = parseInt(e.target.value, 10);\n setDayOfMonth(d);\n emit({ dayOfMonth: d });\n }}\n aria-label={labels.dayOfMonth}\n >\n {Array.from({ length: 31 }, (_, i) => i + 1).map((d) => (\n <option key={d} value={d}>\n {d}\n </option>\n ))}\n </select>\n <input\n type=\"time\"\n step={60}\n disabled={disabled}\n className={timeInputClass}\n value={timeValue}\n onChange={onTimeChange}\n aria-label={labels.scheduleTimeLabel}\n />\n </>\n )}\n\n {mode === 'hourly' && (\n <>\n <select\n className={cn(pickerSelectClass, 'w-auto min-w-[4rem]')}\n disabled={disabled}\n value={minute}\n onChange={(e) => {\n const mm = parseInt(e.target.value, 10);\n setMinute(mm);\n emit({ minute: mm });\n }}\n aria-label={labels.minuteAtHour}\n >\n {Array.from({ length: 60 }, (_, i) => (\n <option key={i} value={i}>\n {String(i).padStart(2, '0')}\n </option>\n ))}\n </select>\n <span className=\"shrink-0 text-sm text-fg-muted\">{labels.minuteUnit}</span>\n </>\n )}\n </div>\n );\n\n const weekRow =\n mode === 'weekly' ? (\n <div className=\"flex flex-wrap gap-1.5 pt-1\" role=\"group\" aria-label={labels.modeWeekly}>\n {labels.weekdays.map((label, i) => {\n const on = weekDays[i];\n return (\n <button\n key={label}\n type=\"button\"\n disabled={disabled}\n className={cn(\n 'flex size-9 select-none items-center justify-center rounded-full border text-xs font-medium transition-colors',\n on\n ? 'border-fg bg-fg text-surface-panel'\n : 'border-edge-subtle bg-surface-panel text-fg hover:border-edge',\n )}\n aria-pressed={on}\n onClick={() => {\n const next = [...weekDays];\n next[i] = !next[i];\n setWeekDays(next);\n emit({ weekDays: next });\n }}\n >\n {label}\n </button>\n );\n })}\n </div>\n ) : null;\n\n const customBlock =\n mode === 'custom' ? (\n <div className=\"pt-1\">\n <textarea\n disabled={disabled}\n className={cronTextareaClass}\n rows={2}\n spellCheck={false}\n value={rawCron}\n placeholder=\"*/5 * * * *\"\n onChange={(e) => {\n const v = e.target.value;\n setRawCron(v);\n onChange(v.trim() || '*/5 * * * *');\n }}\n aria-label={labels.modeCustom}\n />\n <p className=\"mt-1 text-xs text-fg-muted\">{labels.customCronHint}</p>\n </div>\n ) : null;\n\n return (\n <div className=\"flex flex-col gap-2\">\n {showHeading ? (\n <span className=\"text-xs font-medium text-fg-muted\">{labels.scheduleTimeLabel}</span>\n ) : null}\n {modeRow}\n {weekRow}\n {customBlock}\n </div>\n );\n}\n","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":"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"}