@xopcai/xopc 0.0.66 → 0.0.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agents-JiJR38n1.js → agents-CmhOU0fB.js} +2 -2
- package/dist/gateway/static/root/assets/{agents-JiJR38n1.js.map → agents-CmhOU0fB.js.map} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-aBDyr7Im.js → apps-page-CzS9A_6L.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-aBDyr7Im.js.map → apps-page-CzS9A_6L.js.map} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-DXxfgG4N.js → channels-settings-Bn0YSztA.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-DXxfgG4N.js.map → channels-settings-Bn0YSztA.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-dreaming-jobs-3--QJSP5.js → cron-dreaming-jobs-bAYsiHdF.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-dreaming-jobs-3--QJSP5.js.map → cron-dreaming-jobs-bAYsiHdF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-CCn-JwBy.js → cron-page-CKqDtScu.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-CCn-JwBy.js.map → cron-page-CKqDtScu.js.map} +1 -1
- package/dist/gateway/static/root/assets/{dist-BS-cwfHQ.js → dist-CUMhnSuX.js} +2 -2
- package/dist/gateway/static/root/assets/{dist-BS-cwfHQ.js.map → dist-CUMhnSuX.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-M8JS4Jid.js → extension-debug-page-BDls5xp3.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-M8JS4Jid.js.map → extension-debug-page-BDls5xp3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-C8gjVE_t.js → extension-page-BSfv53OO.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-C8gjVE_t.js.map → extension-page-BSfv53OO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DeY2M2di.js → extension-settings-page-B0nkVsLi.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-DeY2M2di.js.map → extension-settings-page-B0nkVsLi.js.map} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-DWmeyjyR.js → heartbeat-config-api-P5zdbIBw.js} +2 -2
- package/dist/gateway/static/root/assets/{heartbeat-config-api-DWmeyjyR.js.map → heartbeat-config-api-P5zdbIBw.js.map} +1 -1
- package/dist/gateway/static/root/assets/{index-Cc57jhuG.js → index-DAglRwh4.js} +5 -5
- package/dist/gateway/static/root/assets/{index-Cc57jhuG.js.map → index-DAglRwh4.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-DHj3Cf9B.css +1 -0
- package/dist/gateway/static/root/assets/{logs-page-kIsSDMqb.js → logs-page-CfGDv4k7.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-kIsSDMqb.js.map → logs-page-CfGDv4k7.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-Da9gjMh7.js → sessions-page-DoGE8N-O.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-Da9gjMh7.js.map → sessions-page-DoGE8N-O.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-C5VrwDoO.js +3 -0
- package/dist/gateway/static/root/assets/settings-page-C5VrwDoO.js.map +1 -0
- package/dist/gateway/static/root/assets/{skills-page-B_msZcLx.js → skills-page-ADUi69Fa.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-B_msZcLx.js.map → skills-page-ADUi69Fa.js.map} +1 -1
- package/dist/gateway/static/root/assets/{use-image-provider-credentials-Bs8xl2E3.js → use-image-provider-credentials-C4x4dNv2.js} +2 -2
- package/dist/gateway/static/root/assets/{use-image-provider-credentials-Bs8xl2E3.js.map → use-image-provider-credentials-C4x4dNv2.js.map} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/cli/commands/tunnel.js +69 -8
- package/dist/src/cli/commands/tunnel.js.map +1 -1
- package/dist/src/config/schema.d.ts +2 -0
- package/dist/src/config/schema.js +2 -0
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +2 -5
- package/dist/src/gateway/hono/lib/config-payload.js +3 -5
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/routes/tunnel.js +12 -10
- package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
- package/dist/src/tunnel/acme-client.d.ts +11 -0
- package/dist/src/tunnel/acme-client.js +54 -10
- package/dist/src/tunnel/acme-client.js.map +1 -1
- package/dist/src/tunnel/env.d.ts +14 -3
- package/dist/src/tunnel/env.js +34 -5
- package/dist/src/tunnel/env.js.map +1 -1
- package/dist/src/tunnel/frpc-binary.d.ts +6 -1
- package/dist/src/tunnel/frpc-binary.js +66 -40
- package/dist/src/tunnel/frpc-binary.js.map +1 -1
- package/dist/src/tunnel/frpc-extract.d.ts +10 -0
- package/dist/src/tunnel/frpc-extract.js +129 -0
- package/dist/src/tunnel/frpc-extract.js.map +1 -0
- package/dist/src/tunnel/gateway-lifecycle.d.ts +3 -1
- package/dist/src/tunnel/gateway-lifecycle.js +6 -4
- package/dist/src/tunnel/gateway-lifecycle.js.map +1 -1
- package/dist/src/tunnel/index.d.ts +3 -1
- package/dist/src/tunnel/index.js +2 -2
- package/dist/src/tunnel/tls-server.js +5 -0
- package/dist/src/tunnel/tls-server.js.map +1 -1
- package/dist/src/tunnel/tunnel-config.js +11 -1
- package/dist/src/tunnel/tunnel-config.js.map +1 -1
- package/dist/src/tunnel/tunnel-e2e-config.d.ts +4 -1
- package/dist/src/tunnel/tunnel-e2e-config.js +10 -3
- package/dist/src/tunnel/tunnel-e2e-config.js.map +1 -1
- package/dist/src/tunnel/tunnel-service.d.ts +4 -0
- package/dist/src/tunnel/tunnel-service.js +66 -5
- package/dist/src/tunnel/tunnel-service.js.map +1 -1
- package/dist/src/tunnel/tunnel-types.d.ts +16 -0
- package/package.json +3 -3
- package/dist/gateway/static/root/assets/index-B5gp15_q.css +0 -1
- package/dist/gateway/static/root/assets/settings-page-B0spIRVS.js +0 -3
- package/dist/gateway/static/root/assets/settings-page-B0spIRVS.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{a as t,n,t as r}from"./vendor-react-vzx_MT5D.js";import{o as i}from"./vendor-swr-CQnUjdeQ.js";import{Bn as a,En as o,Hn as s,Mt as c,Nt as l,On as u,S as d,Si as f,Sr as p,Vr as m,ci as h,h as g,li as _,mi as v,ot as y,qn as b,si as x,x as S,xn as C,zi as w}from"./index-Cc57jhuG.js";var T=`/api/image/providers`;async function E(){return(await o(C(`/api/image/providers`)))?.payload?.providers??[]}var D=e(t(),1),O=n();function k(){return{apiKey:``,region:``,baseUrl:``,imageBaseUrl:``}}function A(e){return e?.apiKey?`••••••••••••`:``}function j(e,t){let n=(()=>{if(!e||typeof e!=`object`||!(`providersConfig`in e))return;let t=e.providersConfig;if(!(!t||typeof t!=`object`||Array.isArray(t)))return t})(),r={};for(let e of t){let t=n?.[e];r[e]={apiKey:A(t),region:t?.region??``,baseUrl:t?.baseUrl??``,imageBaseUrl:t?.imageBaseUrl??``}}return r}function M(e,t,n){let r=e[n].trim();if(r!==t[n].trim())return r||null}function N(e,t){let n=e.trim(),r=t.trim();if(n!==r&&!(g(n)&&g(r)))return n||(r?null:void 0)}function P(e,t,n){let r={};for(let i of e){let e=t[i]??k(),a=n[i]??k();if(JSON.stringify(e)===JSON.stringify(a))continue;let o={},s=N(e.apiKey,a.apiKey);s!==void 0&&(o.apiKey=s);let c=M(e,a,`region`);c!==void 0&&(o.region=c);let l=M(e,a,`baseUrl`);l!==void 0&&(o.baseUrl=l);let u=M(e,a,`imageBaseUrl`);u!==void 0&&(o.imageBaseUrl=u),Object.keys(o).length>0&&(r[i]=o)}return r}async function F(e){let t=await o(C(`/api/image/providers/${encodeURIComponent(e)}/reveal-api-key`),{method:`POST`,headers:{"Content-Type":`application/json`},body:`{}`});if(!t.ok||!t.payload)throw Error(t.error?.message??`Reveal failed`);return t.payload}async function I(e){Object.keys(e).length!==0&&(await o(C(`/api/config`),{method:`PATCH`,body:JSON.stringify({providersConfig:e})}),await c())}var L=r();function ee({providerId:e,value:t,onChange:n,labels:r,apiKeyLinks:i,apiKeyLinkLabels:o}){let[s,c]=(0,D.useState)(!1),[l,u]=(0,D.useState)(void 0),[p,S]=(0,D.useState)(!1),[C,w]=(0,D.useState)(null),[T,E]=(0,D.useState)(!1),O=g(t);(0,D.useEffect)(()=>{O||(u(void 0),w(null))},[O,t]);let k=O&&s&&typeof l==`string`?l:t,A=!O||O&&s&&typeof l==`string`?`text`:`password`,j=!O&&t.trim().length>0&&!g(t)||!!s&&typeof l==`string`&&l.length>0,M=(0,D.useCallback)(async()=>{let e=!O&&t.trim()&&!g(t)?t.trim():typeof l==`string`&&l.length>0?l:``;if(e)try{await navigator.clipboard.writeText(e),E(!0),window.setTimeout(()=>E(!1),2e3)}catch{}},[O,l,t]),N=(0,D.useCallback)(async()=>{if(w(null),!O){c(e=>!e);return}if(l!==void 0){c(e=>!e);return}S(!0);try{u((await F(e)).apiKey??null),c(!0)}catch(e){w(e instanceof Error?e.message:r.loadFailed),u(null)}finally{S(!1)}},[O,e,l,r.loadFailed]);return(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-key-${e}`,children:r.apiKeyLabel}),i.length>0?(0,L.jsx)(`div`,{className:`flex flex-col gap-1`,children:i.map(e=>(0,L.jsxs)(`a`,{href:e.href,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,children:[d(e.kind,o),(0,L.jsx)(_,{className:`size-3`,"aria-hidden":!0})]},`${e.kind}-${e.href}`))}):null,O?(0,L.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:r.maskedHelp}):null,(0,L.jsxs)(`div`,{className:`relative min-w-0`,children:[(0,L.jsx)(`input`,{id:`img-cred-key-${e}`,type:A,autoComplete:`off`,spellCheck:!1,className:b(`w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg`,`placeholder:text-fg-subtle`,a),value:k,placeholder:O?`••••••••`:r.optionalPlaceholder,onChange:e=>{let t=e.target.value;O&&typeof l==`string`&&s&&t!==l&&(u(void 0),c(!1)),n(t)}}),(0,L.jsxs)(`div`,{className:`absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5`,children:[j?(0,L.jsx)(`button`,{type:`button`,className:b(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg`,y.transition,y.press,y.focusRingPanel),title:T?r.copied:r.copy,"aria-label":T?r.copied:r.copy,onClick:()=>void M(),children:T?(0,L.jsx)(f,{className:`size-4`}):(0,L.jsx)(v,{className:`size-4`})}):null,(0,L.jsx)(`button`,{type:`button`,className:b(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40`,y.transition,y.press,y.focusRingPanel),title:s?r.hide:r.show,"aria-label":s?r.hide:r.show,disabled:p,onClick:()=>void N(),children:p?(0,L.jsx)(m,{className:`size-4 animate-spin`,"aria-hidden":!0}):s?(0,L.jsx)(h,{className:`size-4`,"aria-hidden":!0}):(0,L.jsx)(x,{className:`size-4`,"aria-hidden":!0})})]})]}),O&&s&&l===null&&!C?(0,L.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:r.notInConfigFile}):null,C?(0,L.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:C}):null]})}function R(){return b(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,a)}function z(){return b(R(),`appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9`)}var B=`__custom__`;function V(e,t){if(!e.region.trim()&&!e.imageBaseUrl.trim())return``;let n=e.region.trim().toLowerCase();return t.some(e=>e.value===n)?n:B}function H(e,t){let n=e.baseUrl.trim().replace(/\/+$/,``);if(!n)return``;let r=t.map(e=>e.value.replace(/\/+$/,``)).indexOf(n);return r>=0?t[r].value:B}function te(e,t,n){return t===`beijing`?e.dashscopeRegion_beijing:t===`singapore`?e.dashscopeRegion_singapore:t===`us`?e.dashscopeRegion_us:n}function ne(e,t){return t===`minimax`?e.minimaxClusterLabel:t===`fal`?e.falQueueBaseLabel:e.baseUrlLabel}function re(e,t){return t===`minimax`?e.minimaxClusterHint:t===`fal`?e.falQueueBaseHint:null}function U(e){let t=(0,O.c)(70),{summaries:n,credDraft:r,credDirty:i,credSaving:a,credError:o,credSavedFlash:c,credNoopFlash:l,updateCredRow:u,onDiscardCredentials:d,onSaveCredentials:f,extensionIds:h,showExtensionLinks:g,showImageModelsLink:v,language:y,apiKeyLinkLabels:x,messages:C}=e;if(n.length===0)return null;let T;t[0]===n?T=t[1]:(T=n.some(oe),t[0]=n,t[1]=T);let E=T,D;t[2]===n?D=t[3]:(D=n.some(ae),t[2]=n,t[3]=D);let A=D,j;t[4]===C.credentialsIntro?j=t[5]:(j=(0,L.jsx)(`p`,{children:C.credentialsIntro}),t[4]=C.credentialsIntro,t[5]=j);let M;t[6]!==E||t[7]!==C.regionHint?(M=E?(0,L.jsx)(`p`,{className:`text-fg-subtle`,children:C.regionHint}):null,t[6]=E,t[7]=C.regionHint,t[8]=M):M=t[8];let N;t[9]!==A||t[10]!==C.endpointPresetsHint?(N=A?(0,L.jsx)(`p`,{className:`text-fg-subtle`,children:C.endpointPresetsHint}):null,t[9]=A,t[10]=C.endpointPresetsHint,t[11]=N):N=t[11];let P;t[12]!==v||t[13]!==C.imageModelsLinkTitle||t[14]!==C.openImageModelsPage?(P=v?(0,L.jsx)(`p`,{children:(0,L.jsx)(w,{to:`/settings/image-models`,className:`font-medium text-accent hover:underline`,title:C.imageModelsLinkTitle,children:C.openImageModelsPage})}):null,t[12]=v,t[13]=C.imageModelsLinkTitle,t[14]=C.openImageModelsPage,t[15]=P):P=t[15];let F;t[16]!==j||t[17]!==M||t[18]!==N||t[19]!==P?(F=(0,L.jsxs)(`div`,{className:`flex flex-col gap-1 text-xs leading-relaxed text-fg-muted`,children:[j,M,N,P]}),t[16]=j,t[17]=M,t[18]=N,t[19]=P,t[20]=F):F=t[20];let I;t[21]===o?I=t[22]:(I=o?(0,L.jsx)(`div`,{className:`rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300`,children:o}):null,t[21]=o,t[22]=I);let U;t[23]!==c||t[24]!==C.credentialsSaved?(U=c?(0,L.jsx)(`span`,{className:`text-sm text-fg-muted`,children:C.credentialsSaved}):null,t[23]=c,t[24]=C.credentialsSaved,t[25]=U):U=t[25];let W;t[26]!==l||t[27]!==C.credentialsNothingToSave?(W=l?(0,L.jsx)(`span`,{className:`text-sm text-fg-muted`,children:C.credentialsNothingToSave}):null,t[26]=l,t[27]=C.credentialsNothingToSave,t[28]=W):W=t[28];let G=!i||a,K;t[29]!==d||t[30]!==C.discardCredentials||t[31]!==G?(K=(0,L.jsx)(s,{type:`button`,variant:`secondary`,onClick:d,disabled:G,children:C.discardCredentials}),t[29]=d,t[30]=C.discardCredentials,t[31]=G,t[32]=K):K=t[32];let q=!i||a,J;t[33]!==a||t[34]!==C.saveCredentials||t[35]!==C.savingCredentials?(J=a?(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(m,{className:`size-3.5 animate-spin`}),(0,L.jsx)(`span`,{className:`ml-1.5`,children:C.savingCredentials})]}):(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(p,{className:`size-3.5`}),(0,L.jsx)(`span`,{className:`ml-1.5`,children:C.saveCredentials})]}),t[33]=a,t[34]=C.saveCredentials,t[35]=C.savingCredentials,t[36]=J):J=t[36];let Y;t[37]!==f||t[38]!==q||t[39]!==J?(Y=(0,L.jsx)(s,{type:`button`,variant:`primary`,onClick:f,disabled:q,children:J}),t[37]=f,t[38]=q,t[39]=J,t[40]=Y):Y=t[40];let X;t[41]!==W||t[42]!==K||t[43]!==Y||t[44]!==U?(X=(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[U,W,K,Y]}),t[41]=W,t[42]=K,t[43]=Y,t[44]=U,t[45]=X):X=t[45];let Z;if(t[46]!==x||t[47]!==r||t[48]!==h||t[49]!==y||t[50]!==g||t[51]!==n||t[52]!==C||t[53]!==u){let e;t[55]!==x||t[56]!==r||t[57]!==h||t[58]!==y||t[59]!==g||t[60]!==C||t[61]!==u?(e=e=>{let t=r[e.id]??k(),n=e.ui,i=g&&h.has(e.id)?`/settings/ext/${encodeURIComponent(e.id)}`:null;return(0,L.jsxs)(`div`,{className:`rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{className:`text-sm font-semibold text-fg`,children:e.label??e.id}),(0,L.jsxs)(`span`,{className:`text-xs text-fg-subtle`,children:[`(`,e.id,`)`]}),i?(0,L.jsxs)(w,{to:i,className:`inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline`,title:C.extensionSettingsLinkTitle,children:[(0,L.jsx)(_,{className:`size-3`}),C.openExtensionSettings]}):null]}),e.configured?(0,L.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg`,children:C.configured}):(0,L.jsx)(`span`,{className:`rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300`,children:C.missingKey})]}),e.defaultModel?(0,L.jsxs)(`p`,{className:`mt-1 text-xs text-fg-subtle`,children:[(0,L.jsxs)(`span`,{className:`text-fg-muted`,children:[C.defaultModel,`:`]}),` `,e.id,`/`,e.defaultModel]}):null,e.models.length>0?(0,L.jsxs)(`p`,{className:`mt-0.5 text-xs text-fg-subtle`,children:[(0,L.jsxs)(`span`,{className:`text-fg-muted`,children:[C.modelsLabel,`:`]}),` `,e.models.map(t=>`${e.id}/${t}`).join(`, `)]}):null,(0,L.jsxs)(`div`,{className:`mt-4 grid gap-3 sm:grid-cols-2`,children:[(0,L.jsx)(ee,{providerId:e.id,value:t.apiKey,onChange:t=>u(e.id,{apiKey:t}),apiKeyLinks:S(e.id,y),apiKeyLinkLabels:x,labels:{apiKeyLabel:C.apiKeyLabel,optionalPlaceholder:C.optionalPlaceholder,maskedHelp:C.apiKeyMaskedHelp,copy:C.apiKeyCopy,copied:C.apiKeyCopied,show:C.apiKeyShow,hide:C.apiKeyHide,notInConfigFile:C.apiKeyNotInConfigFile,loadFailed:C.apiKeyRevealFailed}}),n?.regions?.length?(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-region-preset-${e.id}`,children:C.regionLabel}),(0,L.jsxs)(`select`,{id:`img-cred-region-preset-${e.id}`,className:z(),value:V(t,n.regions),onChange:t=>{let r=t.target.value;if(r===``){u(e.id,{region:``,imageBaseUrl:``});return}if(r===B){u(e.id,{region:``,imageBaseUrl:``});return}let i=n.regions.find(e=>e.value===r);i&&u(e.id,{region:i.value,imageBaseUrl:i.imageBaseUrl})},children:[(0,L.jsx)(`option`,{value:``,children:C.regionPresetDefault}),n.regions.map(e=>(0,L.jsx)(`option`,{value:e.value,children:te(C,e.value,e.label)},e.value)),(0,L.jsx)(`option`,{value:B,children:C.regionPresetCustom})]}),V(t,n.regions)===B?(0,L.jsxs)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:[(0,L.jsx)(`input`,{type:`text`,className:R(),value:t.region,placeholder:`region`,onChange:t=>u(e.id,{region:t.target.value})}),(0,L.jsx)(`input`,{type:`url`,className:R(),value:t.imageBaseUrl,placeholder:C.imageBaseUrlLabel,onChange:t=>u(e.id,{imageBaseUrl:t.target.value})})]}):null]}):null,n?.baseUrlPresets?.length?(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-base-preset-${e.id}`,children:ne(C,n.baseUrlPresetKind)}),re(C,n.baseUrlPresetKind)?(0,L.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:re(C,n.baseUrlPresetKind)}):null,(0,L.jsxs)(`select`,{id:`img-cred-base-preset-${e.id}`,className:z(),value:H(t,n.baseUrlPresets),onChange:t=>{let n=t.target.value;if(n===``){u(e.id,{baseUrl:``});return}if(n===B){u(e.id,{baseUrl:``});return}u(e.id,{baseUrl:n.replace(/\/+$/,``)})},children:[(0,L.jsx)(`option`,{value:``,children:C.baseUrlPresetDefault}),n.baseUrlPresets.map(ie),(0,L.jsx)(`option`,{value:B,children:C.baseUrlPresetCustom})]}),H(t,n.baseUrlPresets)===B?(0,L.jsx)(`input`,{type:`url`,className:b(R(),`mt-2`),value:t.baseUrl,placeholder:`https://…`,onChange:t=>u(e.id,{baseUrl:t.target.value})}):null]}):null,n?.regions?.length&&V(t,n.regions)!==B?(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-imgbase-ro-${e.id}`,children:C.imageBaseUrlLabel}),(0,L.jsx)(`input`,{id:`img-cred-imgbase-ro-${e.id}`,type:`url`,readOnly:!0,className:b(R(),`cursor-not-allowed opacity-90`),value:t.imageBaseUrl,title:C.imageBaseUrlPresetHint}),(0,L.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:C.imageBaseUrlPresetHint})]}):null]})]},e.id)},t[55]=x,t[56]=r,t[57]=h,t[58]=y,t[59]=g,t[60]=C,t[61]=u,t[62]=e):e=t[62],Z=n.map(e),t[46]=x,t[47]=r,t[48]=h,t[49]=y,t[50]=g,t[51]=n,t[52]=C,t[53]=u,t[54]=Z}else Z=t[54];let Q;t[63]===Z?Q=t[64]:(Q=(0,L.jsx)(`div`,{className:`flex flex-col gap-4`,children:Z}),t[63]=Z,t[64]=Q);let $;return t[65]!==X||t[66]!==Q||t[67]!==F||t[68]!==I?($=(0,L.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[F,I,X,Q]}),t[65]=X,t[66]=Q,t[67]=F,t[68]=I,t[69]=$):$=t[69],$}function ie(e){return(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value)}function ae(e){return(e.ui?.baseUrlPresets?.length??0)>0}function oe(e){return(e.ui?.regions?.length??0)>0}function W(e){let t=l(u(e=>!!e.token)),n=t.data,r=(0,D.useMemo)(()=>e.map(e=>e.id),[e]),[a,o]=(0,D.useState)({}),[s,c]=(0,D.useState)({}),[d,f]=(0,D.useState)(!1),[p,m]=(0,D.useState)(null),[h,g]=(0,D.useState)(!1),[_,v]=(0,D.useState)(!1),y=(0,D.useMemo)(()=>j(n?.payload?.config,r),[n?.payload?.config,r]),b=(0,D.useMemo)(()=>JSON.stringify(a)!==JSON.stringify(s),[a,s]);return(0,D.useEffect)(()=>{b||(o(structuredClone(y)),c(structuredClone(y)))},[y,b]),{gwSwr:t,credDraft:a,credBaseline:s,credDirty:b,credSaving:d,credError:p,credSavedFlash:h,credNoopFlash:_,updateCredRow:(0,D.useCallback)((e,t)=>{o(n=>{let r=n[e]??k();return{...n,[e]:{...r,...t}}})},[]),onDiscardCredentials:(0,D.useCallback)(()=>{o(structuredClone(s)),m(null),g(!1),v(!1)},[s]),saveCredentials:(0,D.useCallback)(async e=>{let n=P(r,a,s);if(Object.keys(n).length===0){v(!0),window.setTimeout(()=>v(!1),2200);return}f(!0),m(null),g(!1);try{await I(n);let e=await t.mutate?.();i(C(T));let a=j(e?.payload?.config,r);o(structuredClone(a)),c(structuredClone(a)),g(!0),window.setTimeout(()=>g(!1),2e3)}catch(t){m(t instanceof Error?t.message:e)}finally{f(!1)}},[r,a,s,t])}}export{T as i,U as n,E as r,W as t};
|
|
2
|
-
//# sourceMappingURL=use-image-provider-credentials-
|
|
1
|
+
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{a as t,n,t as r}from"./vendor-react-vzx_MT5D.js";import{o as i}from"./vendor-swr-CQnUjdeQ.js";import{Bn as a,En as o,Hn as s,Mt as c,Nt as l,On as u,S as d,Si as f,Sr as p,Vr as m,ci as h,h as g,li as _,mi as v,ot as y,qn as b,si as x,x as S,xn as C,zi as w}from"./index-DAglRwh4.js";var T=`/api/image/providers`;async function E(){return(await o(C(`/api/image/providers`)))?.payload?.providers??[]}var D=e(t(),1),O=n();function k(){return{apiKey:``,region:``,baseUrl:``,imageBaseUrl:``}}function A(e){return e?.apiKey?`••••••••••••`:``}function j(e,t){let n=(()=>{if(!e||typeof e!=`object`||!(`providersConfig`in e))return;let t=e.providersConfig;if(!(!t||typeof t!=`object`||Array.isArray(t)))return t})(),r={};for(let e of t){let t=n?.[e];r[e]={apiKey:A(t),region:t?.region??``,baseUrl:t?.baseUrl??``,imageBaseUrl:t?.imageBaseUrl??``}}return r}function M(e,t,n){let r=e[n].trim();if(r!==t[n].trim())return r||null}function N(e,t){let n=e.trim(),r=t.trim();if(n!==r&&!(g(n)&&g(r)))return n||(r?null:void 0)}function P(e,t,n){let r={};for(let i of e){let e=t[i]??k(),a=n[i]??k();if(JSON.stringify(e)===JSON.stringify(a))continue;let o={},s=N(e.apiKey,a.apiKey);s!==void 0&&(o.apiKey=s);let c=M(e,a,`region`);c!==void 0&&(o.region=c);let l=M(e,a,`baseUrl`);l!==void 0&&(o.baseUrl=l);let u=M(e,a,`imageBaseUrl`);u!==void 0&&(o.imageBaseUrl=u),Object.keys(o).length>0&&(r[i]=o)}return r}async function F(e){let t=await o(C(`/api/image/providers/${encodeURIComponent(e)}/reveal-api-key`),{method:`POST`,headers:{"Content-Type":`application/json`},body:`{}`});if(!t.ok||!t.payload)throw Error(t.error?.message??`Reveal failed`);return t.payload}async function I(e){Object.keys(e).length!==0&&(await o(C(`/api/config`),{method:`PATCH`,body:JSON.stringify({providersConfig:e})}),await c())}var L=r();function ee({providerId:e,value:t,onChange:n,labels:r,apiKeyLinks:i,apiKeyLinkLabels:o}){let[s,c]=(0,D.useState)(!1),[l,u]=(0,D.useState)(void 0),[p,S]=(0,D.useState)(!1),[C,w]=(0,D.useState)(null),[T,E]=(0,D.useState)(!1),O=g(t);(0,D.useEffect)(()=>{O||(u(void 0),w(null))},[O,t]);let k=O&&s&&typeof l==`string`?l:t,A=!O||O&&s&&typeof l==`string`?`text`:`password`,j=!O&&t.trim().length>0&&!g(t)||!!s&&typeof l==`string`&&l.length>0,M=(0,D.useCallback)(async()=>{let e=!O&&t.trim()&&!g(t)?t.trim():typeof l==`string`&&l.length>0?l:``;if(e)try{await navigator.clipboard.writeText(e),E(!0),window.setTimeout(()=>E(!1),2e3)}catch{}},[O,l,t]),N=(0,D.useCallback)(async()=>{if(w(null),!O){c(e=>!e);return}if(l!==void 0){c(e=>!e);return}S(!0);try{u((await F(e)).apiKey??null),c(!0)}catch(e){w(e instanceof Error?e.message:r.loadFailed),u(null)}finally{S(!1)}},[O,e,l,r.loadFailed]);return(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-key-${e}`,children:r.apiKeyLabel}),i.length>0?(0,L.jsx)(`div`,{className:`flex flex-col gap-1`,children:i.map(e=>(0,L.jsxs)(`a`,{href:e.href,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,children:[d(e.kind,o),(0,L.jsx)(_,{className:`size-3`,"aria-hidden":!0})]},`${e.kind}-${e.href}`))}):null,O?(0,L.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:r.maskedHelp}):null,(0,L.jsxs)(`div`,{className:`relative min-w-0`,children:[(0,L.jsx)(`input`,{id:`img-cred-key-${e}`,type:A,autoComplete:`off`,spellCheck:!1,className:b(`w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg`,`placeholder:text-fg-subtle`,a),value:k,placeholder:O?`••••••••`:r.optionalPlaceholder,onChange:e=>{let t=e.target.value;O&&typeof l==`string`&&s&&t!==l&&(u(void 0),c(!1)),n(t)}}),(0,L.jsxs)(`div`,{className:`absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5`,children:[j?(0,L.jsx)(`button`,{type:`button`,className:b(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg`,y.transition,y.press,y.focusRingPanel),title:T?r.copied:r.copy,"aria-label":T?r.copied:r.copy,onClick:()=>void M(),children:T?(0,L.jsx)(f,{className:`size-4`}):(0,L.jsx)(v,{className:`size-4`})}):null,(0,L.jsx)(`button`,{type:`button`,className:b(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40`,y.transition,y.press,y.focusRingPanel),title:s?r.hide:r.show,"aria-label":s?r.hide:r.show,disabled:p,onClick:()=>void N(),children:p?(0,L.jsx)(m,{className:`size-4 animate-spin`,"aria-hidden":!0}):s?(0,L.jsx)(h,{className:`size-4`,"aria-hidden":!0}):(0,L.jsx)(x,{className:`size-4`,"aria-hidden":!0})})]})]}),O&&s&&l===null&&!C?(0,L.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:r.notInConfigFile}):null,C?(0,L.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:C}):null]})}function R(){return b(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,a)}function z(){return b(R(),`appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9`)}var B=`__custom__`;function V(e,t){if(!e.region.trim()&&!e.imageBaseUrl.trim())return``;let n=e.region.trim().toLowerCase();return t.some(e=>e.value===n)?n:B}function H(e,t){let n=e.baseUrl.trim().replace(/\/+$/,``);if(!n)return``;let r=t.map(e=>e.value.replace(/\/+$/,``)).indexOf(n);return r>=0?t[r].value:B}function te(e,t,n){return t===`beijing`?e.dashscopeRegion_beijing:t===`singapore`?e.dashscopeRegion_singapore:t===`us`?e.dashscopeRegion_us:n}function ne(e,t){return t===`minimax`?e.minimaxClusterLabel:t===`fal`?e.falQueueBaseLabel:e.baseUrlLabel}function re(e,t){return t===`minimax`?e.minimaxClusterHint:t===`fal`?e.falQueueBaseHint:null}function U(e){let t=(0,O.c)(70),{summaries:n,credDraft:r,credDirty:i,credSaving:a,credError:o,credSavedFlash:c,credNoopFlash:l,updateCredRow:u,onDiscardCredentials:d,onSaveCredentials:f,extensionIds:h,showExtensionLinks:g,showImageModelsLink:v,language:y,apiKeyLinkLabels:x,messages:C}=e;if(n.length===0)return null;let T;t[0]===n?T=t[1]:(T=n.some(oe),t[0]=n,t[1]=T);let E=T,D;t[2]===n?D=t[3]:(D=n.some(ae),t[2]=n,t[3]=D);let A=D,j;t[4]===C.credentialsIntro?j=t[5]:(j=(0,L.jsx)(`p`,{children:C.credentialsIntro}),t[4]=C.credentialsIntro,t[5]=j);let M;t[6]!==E||t[7]!==C.regionHint?(M=E?(0,L.jsx)(`p`,{className:`text-fg-subtle`,children:C.regionHint}):null,t[6]=E,t[7]=C.regionHint,t[8]=M):M=t[8];let N;t[9]!==A||t[10]!==C.endpointPresetsHint?(N=A?(0,L.jsx)(`p`,{className:`text-fg-subtle`,children:C.endpointPresetsHint}):null,t[9]=A,t[10]=C.endpointPresetsHint,t[11]=N):N=t[11];let P;t[12]!==v||t[13]!==C.imageModelsLinkTitle||t[14]!==C.openImageModelsPage?(P=v?(0,L.jsx)(`p`,{children:(0,L.jsx)(w,{to:`/settings/image-models`,className:`font-medium text-accent hover:underline`,title:C.imageModelsLinkTitle,children:C.openImageModelsPage})}):null,t[12]=v,t[13]=C.imageModelsLinkTitle,t[14]=C.openImageModelsPage,t[15]=P):P=t[15];let F;t[16]!==j||t[17]!==M||t[18]!==N||t[19]!==P?(F=(0,L.jsxs)(`div`,{className:`flex flex-col gap-1 text-xs leading-relaxed text-fg-muted`,children:[j,M,N,P]}),t[16]=j,t[17]=M,t[18]=N,t[19]=P,t[20]=F):F=t[20];let I;t[21]===o?I=t[22]:(I=o?(0,L.jsx)(`div`,{className:`rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300`,children:o}):null,t[21]=o,t[22]=I);let U;t[23]!==c||t[24]!==C.credentialsSaved?(U=c?(0,L.jsx)(`span`,{className:`text-sm text-fg-muted`,children:C.credentialsSaved}):null,t[23]=c,t[24]=C.credentialsSaved,t[25]=U):U=t[25];let W;t[26]!==l||t[27]!==C.credentialsNothingToSave?(W=l?(0,L.jsx)(`span`,{className:`text-sm text-fg-muted`,children:C.credentialsNothingToSave}):null,t[26]=l,t[27]=C.credentialsNothingToSave,t[28]=W):W=t[28];let G=!i||a,K;t[29]!==d||t[30]!==C.discardCredentials||t[31]!==G?(K=(0,L.jsx)(s,{type:`button`,variant:`secondary`,onClick:d,disabled:G,children:C.discardCredentials}),t[29]=d,t[30]=C.discardCredentials,t[31]=G,t[32]=K):K=t[32];let q=!i||a,J;t[33]!==a||t[34]!==C.saveCredentials||t[35]!==C.savingCredentials?(J=a?(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(m,{className:`size-3.5 animate-spin`}),(0,L.jsx)(`span`,{className:`ml-1.5`,children:C.savingCredentials})]}):(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(p,{className:`size-3.5`}),(0,L.jsx)(`span`,{className:`ml-1.5`,children:C.saveCredentials})]}),t[33]=a,t[34]=C.saveCredentials,t[35]=C.savingCredentials,t[36]=J):J=t[36];let Y;t[37]!==f||t[38]!==q||t[39]!==J?(Y=(0,L.jsx)(s,{type:`button`,variant:`primary`,onClick:f,disabled:q,children:J}),t[37]=f,t[38]=q,t[39]=J,t[40]=Y):Y=t[40];let X;t[41]!==W||t[42]!==K||t[43]!==Y||t[44]!==U?(X=(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[U,W,K,Y]}),t[41]=W,t[42]=K,t[43]=Y,t[44]=U,t[45]=X):X=t[45];let Z;if(t[46]!==x||t[47]!==r||t[48]!==h||t[49]!==y||t[50]!==g||t[51]!==n||t[52]!==C||t[53]!==u){let e;t[55]!==x||t[56]!==r||t[57]!==h||t[58]!==y||t[59]!==g||t[60]!==C||t[61]!==u?(e=e=>{let t=r[e.id]??k(),n=e.ui,i=g&&h.has(e.id)?`/settings/ext/${encodeURIComponent(e.id)}`:null;return(0,L.jsxs)(`div`,{className:`rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{className:`text-sm font-semibold text-fg`,children:e.label??e.id}),(0,L.jsxs)(`span`,{className:`text-xs text-fg-subtle`,children:[`(`,e.id,`)`]}),i?(0,L.jsxs)(w,{to:i,className:`inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline`,title:C.extensionSettingsLinkTitle,children:[(0,L.jsx)(_,{className:`size-3`}),C.openExtensionSettings]}):null]}),e.configured?(0,L.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg`,children:C.configured}):(0,L.jsx)(`span`,{className:`rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300`,children:C.missingKey})]}),e.defaultModel?(0,L.jsxs)(`p`,{className:`mt-1 text-xs text-fg-subtle`,children:[(0,L.jsxs)(`span`,{className:`text-fg-muted`,children:[C.defaultModel,`:`]}),` `,e.id,`/`,e.defaultModel]}):null,e.models.length>0?(0,L.jsxs)(`p`,{className:`mt-0.5 text-xs text-fg-subtle`,children:[(0,L.jsxs)(`span`,{className:`text-fg-muted`,children:[C.modelsLabel,`:`]}),` `,e.models.map(t=>`${e.id}/${t}`).join(`, `)]}):null,(0,L.jsxs)(`div`,{className:`mt-4 grid gap-3 sm:grid-cols-2`,children:[(0,L.jsx)(ee,{providerId:e.id,value:t.apiKey,onChange:t=>u(e.id,{apiKey:t}),apiKeyLinks:S(e.id,y),apiKeyLinkLabels:x,labels:{apiKeyLabel:C.apiKeyLabel,optionalPlaceholder:C.optionalPlaceholder,maskedHelp:C.apiKeyMaskedHelp,copy:C.apiKeyCopy,copied:C.apiKeyCopied,show:C.apiKeyShow,hide:C.apiKeyHide,notInConfigFile:C.apiKeyNotInConfigFile,loadFailed:C.apiKeyRevealFailed}}),n?.regions?.length?(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-region-preset-${e.id}`,children:C.regionLabel}),(0,L.jsxs)(`select`,{id:`img-cred-region-preset-${e.id}`,className:z(),value:V(t,n.regions),onChange:t=>{let r=t.target.value;if(r===``){u(e.id,{region:``,imageBaseUrl:``});return}if(r===B){u(e.id,{region:``,imageBaseUrl:``});return}let i=n.regions.find(e=>e.value===r);i&&u(e.id,{region:i.value,imageBaseUrl:i.imageBaseUrl})},children:[(0,L.jsx)(`option`,{value:``,children:C.regionPresetDefault}),n.regions.map(e=>(0,L.jsx)(`option`,{value:e.value,children:te(C,e.value,e.label)},e.value)),(0,L.jsx)(`option`,{value:B,children:C.regionPresetCustom})]}),V(t,n.regions)===B?(0,L.jsxs)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:[(0,L.jsx)(`input`,{type:`text`,className:R(),value:t.region,placeholder:`region`,onChange:t=>u(e.id,{region:t.target.value})}),(0,L.jsx)(`input`,{type:`url`,className:R(),value:t.imageBaseUrl,placeholder:C.imageBaseUrlLabel,onChange:t=>u(e.id,{imageBaseUrl:t.target.value})})]}):null]}):null,n?.baseUrlPresets?.length?(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-base-preset-${e.id}`,children:ne(C,n.baseUrlPresetKind)}),re(C,n.baseUrlPresetKind)?(0,L.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:re(C,n.baseUrlPresetKind)}):null,(0,L.jsxs)(`select`,{id:`img-cred-base-preset-${e.id}`,className:z(),value:H(t,n.baseUrlPresets),onChange:t=>{let n=t.target.value;if(n===``){u(e.id,{baseUrl:``});return}if(n===B){u(e.id,{baseUrl:``});return}u(e.id,{baseUrl:n.replace(/\/+$/,``)})},children:[(0,L.jsx)(`option`,{value:``,children:C.baseUrlPresetDefault}),n.baseUrlPresets.map(ie),(0,L.jsx)(`option`,{value:B,children:C.baseUrlPresetCustom})]}),H(t,n.baseUrlPresets)===B?(0,L.jsx)(`input`,{type:`url`,className:b(R(),`mt-2`),value:t.baseUrl,placeholder:`https://…`,onChange:t=>u(e.id,{baseUrl:t.target.value})}):null]}):null,n?.regions?.length&&V(t,n.regions)!==B?(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,L.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-imgbase-ro-${e.id}`,children:C.imageBaseUrlLabel}),(0,L.jsx)(`input`,{id:`img-cred-imgbase-ro-${e.id}`,type:`url`,readOnly:!0,className:b(R(),`cursor-not-allowed opacity-90`),value:t.imageBaseUrl,title:C.imageBaseUrlPresetHint}),(0,L.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:C.imageBaseUrlPresetHint})]}):null]})]},e.id)},t[55]=x,t[56]=r,t[57]=h,t[58]=y,t[59]=g,t[60]=C,t[61]=u,t[62]=e):e=t[62],Z=n.map(e),t[46]=x,t[47]=r,t[48]=h,t[49]=y,t[50]=g,t[51]=n,t[52]=C,t[53]=u,t[54]=Z}else Z=t[54];let Q;t[63]===Z?Q=t[64]:(Q=(0,L.jsx)(`div`,{className:`flex flex-col gap-4`,children:Z}),t[63]=Z,t[64]=Q);let $;return t[65]!==X||t[66]!==Q||t[67]!==F||t[68]!==I?($=(0,L.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[F,I,X,Q]}),t[65]=X,t[66]=Q,t[67]=F,t[68]=I,t[69]=$):$=t[69],$}function ie(e){return(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value)}function ae(e){return(e.ui?.baseUrlPresets?.length??0)>0}function oe(e){return(e.ui?.regions?.length??0)>0}function W(e){let t=l(u(e=>!!e.token)),n=t.data,r=(0,D.useMemo)(()=>e.map(e=>e.id),[e]),[a,o]=(0,D.useState)({}),[s,c]=(0,D.useState)({}),[d,f]=(0,D.useState)(!1),[p,m]=(0,D.useState)(null),[h,g]=(0,D.useState)(!1),[_,v]=(0,D.useState)(!1),y=(0,D.useMemo)(()=>j(n?.payload?.config,r),[n?.payload?.config,r]),b=(0,D.useMemo)(()=>JSON.stringify(a)!==JSON.stringify(s),[a,s]);return(0,D.useEffect)(()=>{b||(o(structuredClone(y)),c(structuredClone(y)))},[y,b]),{gwSwr:t,credDraft:a,credBaseline:s,credDirty:b,credSaving:d,credError:p,credSavedFlash:h,credNoopFlash:_,updateCredRow:(0,D.useCallback)((e,t)=>{o(n=>{let r=n[e]??k();return{...n,[e]:{...r,...t}}})},[]),onDiscardCredentials:(0,D.useCallback)(()=>{o(structuredClone(s)),m(null),g(!1),v(!1)},[s]),saveCredentials:(0,D.useCallback)(async e=>{let n=P(r,a,s);if(Object.keys(n).length===0){v(!0),window.setTimeout(()=>v(!1),2200);return}f(!0),m(null),g(!1);try{await I(n);let e=await t.mutate?.();i(C(T));let a=j(e?.payload?.config,r);o(structuredClone(a)),c(structuredClone(a)),g(!0),window.setTimeout(()=>g(!1),2e3)}catch(t){m(t instanceof Error?t.message:e)}finally{f(!1)}},[r,a,s,t])}}export{T as i,U as n,E as r,W as t};
|
|
2
|
+
//# sourceMappingURL=use-image-provider-credentials-C4x4dNv2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-image-provider-credentials-Bs8xl2E3.js","names":["IMAGE_PROVIDERS_SWR_KEY","fetchJson","apiUrl","IMAGE_PROVIDERS_SWR_KEY","ImageGenProviderCredentialSummary","fetchImageProvidersList","Promise","res","ok","payload","providers","isMaskedKey","revalidateGatewayConfig","fetchJson","apiUrl","ImageProviderCredRow","apiKey","region","baseUrl","imageBaseUrl","emptyImageProviderCredRow","SafeProviderAuthEntry","maskedApiKeyDisplay","safe","imageProviderCredRowsFromConfigRoot","config","imageProviderIds","Record","pc","undefined","v","providersConfig","Array","isArray","out","id","optionalStringField","draft","baseline","key","Pick","d","trim","b","apiKeyPatchValue","draftKey","baselineKey","buildImageProvidersConfigPatch","patch","JSON","stringify","entry","keyDelta","Object","keys","length","RevealImageProviderApiKeyPayload","source","revealImageProviderConfigApiKey","providerId","Promise","data","ok","payload","error","message","encodeURIComponent","method","headers","body","Error","patchImageProvidersConfig","CheckCircle2","Copy","ExternalLink","Eye","EyeOff","Loader2","useCallback","useEffect","useState","revealImageProviderConfigApiKey","ApiKeyLinkKind","providerApiKeyLinkLabel","isMaskedKey","ProvidersSettingsMessages","settingsInputFocusClass","interaction","cn","ImageProviderApiKeyFieldLabels","apiKeyLabel","optionalPlaceholder","maskedHelp","copy","copied","show","hide","notInConfigFile","loadFailed","ImageProviderApiKeyField","providerId","value","onChange","labels","apiKeyLinks","apiKeyLinkLabels","next","href","kind","Pick","showKey","setShowKey","revealed","setRevealed","undefined","revealLoading","setRevealLoading","revealErr","setRevealErr","setCopied","masked","inputValue","inputType","const","copyEnabled","trim","length","Boolean","copyKey","text","navigator","clipboard","writeText","window","setTimeout","toggleEye","s","payload","apiKey","e","Error","message","map","link","target","transition","press","focusRingPanel","ExternalLink","Loader2","Save","Link","Button","ImageProviderApiKeyField","emptyImageProviderCredRow","ImageProviderCredRow","getOrderedApiKeyLinks","ImageGenProviderCredentialSummary","ImageProviderUiMetadata","ProvidersSettingsMessages","settingsInputFocusClass","StoredLanguage","cn","inputClass","selectClass","CUSTOM_SENTINEL","dashscopeSelectValue","row","regions","NonNullable","region","trim","imageBaseUrl","r","toLowerCase","some","x","value","baseUrlSelectValue","presets","b","baseUrl","replace","norm","map","p","idx","indexOf","ImageProviderCredentialsPanelMessages","credentialsIntro","regionHint","endpointPresetsHint","apiKeyLabel","optionalPlaceholder","regionLabel","baseUrlLabel","imageBaseUrlLabel","saveCredentials","savingCredentials","credentialsSaved","discardCredentials","credentialsNothingToSave","credentialsSaveError","regionPresetDefault","regionPresetCustom","baseUrlPresetDefault","baseUrlPresetCustom","openExtensionSettings","openImageModelsPage","extensionSettingsLinkTitle","imageModelsLinkTitle","configured","missingKey","defaultModel","modelsLabel","imageBaseUrlPresetHint","dashscopeRegion_beijing","dashscopeRegion_singapore","dashscopeRegion_us","apiKeyMaskedHelp","apiKeyCopy","apiKeyCopied","apiKeyShow","apiKeyHide","apiKeyNotInConfigFile","apiKeyRevealFailed","minimaxClusterLabel","minimaxClusterHint","falQueueBaseLabel","falQueueBaseHint","translateDashscopeRegion","m","serverLabel","baseUrlPresetBlockTitle","t","kind","baseUrlPresetBlockHint","ImageProviderCredentialsPanel","t0","$","_c","summaries","credDraft","credDirty","credSaving","credError","credSavedFlash","credNoopFlash","updateCredRow","onDiscardCredentials","onSaveCredentials","extensionIds","showExtensionLinks","showImageModelsLink","language","apiKeyLinkLabels","messages","empty","length","t1","_temp","anyRegionUi","t2","_temp2","anyBaseUrlPresets","t3","t4","t5","t6","t7","t8","t9","t10","t11","t12","t13","t14","t15","t16","t17","t18","id","ui","extPath","has","encodeURIComponent","label","models","mm","join","apiKey","next","maskedHelp","copy","copied","show","hide","notInConfigFile","loadFailed","e","v","target","opt","find","e_0","e_1","baseUrlPresets","baseUrlPresetKind","e_2","v_0","_temp3","e_3","t19","s_0","s","useCallback","useEffect","useMemo","useState","mutate","useGatewayConfigSwr","buildImageProvidersConfigPatch","emptyImageProviderCredRow","imageProviderCredRowsFromConfigRoot","patchImageProvidersConfig","ImageProviderCredRow","IMAGE_PROVIDERS_SWR_KEY","apiUrl","useGatewayStore","ImageProviderUiRegionOption","value","label","imageBaseUrl","ImageProviderUiBaseUrlPreset","ImageProviderUiMetadata","regions","baseUrlPresets","baseUrlPresetKind","ImageGenProviderCredentialSummary","id","defaultModel","models","configured","ui","useImageProviderCredentials","summaries","hasToken","s","Boolean","token","gwSwr","gwCfg","data","ids","map","credDraft","setCredDraft","Record","credBaseline","setCredBaseline","credSaving","setCredSaving","credError","setCredError","credSavedFlash","setCredSavedFlash","credNoopFlash","setCredNoopFlash","credRowsFromServer","payload","config","credDirty","JSON","stringify","structuredClone","updateCredRow","patch","Partial","prev","base","onDiscardCredentials","saveCredentials","errorFallback","Object","keys","length","window","setTimeout","updated","nextRows","e","Error","message"],"sources":["../../../../../web/src/features/settings/image-providers-swr-key.ts","../../../../../web/src/features/settings/fetch-image-providers.ts","../../../../../web/src/features/settings/image-providers-config-api.ts","../../../../../web/src/features/settings/image-provider-api-key-field.tsx","../../../../../web/src/features/settings/image-provider-credentials-panel.tsx","../../../../../web/src/features/settings/use-image-provider-credentials.ts"],"sourcesContent":["/** Shared SWR key for GET `/api/image/providers` (image settings + extension image pages). */\nexport const IMAGE_PROVIDERS_SWR_KEY = '/api/image/providers';\n","import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport type { ImageGenProviderCredentialSummary } from '@/features/settings/use-image-provider-credentials';\n\nexport async function fetchImageProvidersList(): Promise<ImageGenProviderCredentialSummary[]> {\n const res = await fetchJson<{\n ok?: boolean;\n payload?: { providers?: ImageGenProviderCredentialSummary[] };\n }>(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n return res?.payload?.providers ?? [];\n}\n","import { isMaskedKey } from '@/features/settings/providers-api';\nimport { revalidateGatewayConfig } from '@/features/gateway/gateway-config-swr';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\n/** One row of image-provider credential fields (matches PATCH `providersConfig` subset). */\nexport type ImageProviderCredRow = {\n apiKey: string;\n region: string;\n baseUrl: string;\n imageBaseUrl: string;\n};\n\nexport function emptyImageProviderCredRow(): ImageProviderCredRow {\n return { apiKey: '', region: '', baseUrl: '', imageBaseUrl: '' };\n}\n\nexport type SafeProviderAuthEntry = {\n apiKey: string;\n region?: string;\n baseUrl?: string;\n imageBaseUrl?: string;\n};\n\nfunction maskedApiKeyDisplay(safe?: SafeProviderAuthEntry): string {\n if (!safe?.apiKey) return '';\n return '••••••••••••';\n}\n\n/** Read `payload.config.providersConfig` from GET /api/config (masked). */\nexport function imageProviderCredRowsFromConfigRoot(\n config: unknown,\n imageProviderIds: string[],\n): Record<string, ImageProviderCredRow> {\n const pc = (() => {\n if (!config || typeof config !== 'object' || !('providersConfig' in config)) return undefined;\n const v = (config as { providersConfig?: unknown }).providersConfig;\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n return v as Record<string, SafeProviderAuthEntry>;\n })();\n\n const out: Record<string, ImageProviderCredRow> = {};\n for (const id of imageProviderIds) {\n const safe = pc?.[id];\n out[id] = {\n apiKey: maskedApiKeyDisplay(safe),\n region: safe?.region ?? '',\n baseUrl: safe?.baseUrl ?? '',\n imageBaseUrl: safe?.imageBaseUrl ?? '',\n };\n }\n return out;\n}\n\nfunction optionalStringField(\n draft: ImageProviderCredRow,\n baseline: ImageProviderCredRow,\n key: keyof Pick<ImageProviderCredRow, 'region' | 'baseUrl' | 'imageBaseUrl'>,\n): string | null | undefined {\n const d = draft[key].trim();\n const b = baseline[key].trim();\n if (d === b) return undefined;\n if (!d) return null;\n return d;\n}\n\nfunction apiKeyPatchValue(draftKey: string, baselineKey: string): string | null | undefined {\n const d = draftKey.trim();\n const b = baselineKey.trim();\n if (d === b) return undefined;\n if (isMaskedKey(d) && isMaskedKey(b)) return undefined;\n if (!d) {\n if (!b) return undefined;\n return null;\n }\n return d;\n}\n\n/**\n * Build `providersConfig` PATCH entries only for image providers whose row changed.\n * Omits `apiKey` when unchanged (still masked); sends `null` to clear stored key.\n */\nexport function buildImageProvidersConfigPatch(\n imageProviderIds: string[],\n draft: Record<string, ImageProviderCredRow>,\n baseline: Record<string, ImageProviderCredRow>,\n): Record<string, Record<string, unknown>> {\n const patch: Record<string, Record<string, unknown>> = {};\n for (const id of imageProviderIds) {\n const d = draft[id] ?? emptyImageProviderCredRow();\n const b = baseline[id] ?? emptyImageProviderCredRow();\n if (JSON.stringify(d) === JSON.stringify(b)) continue;\n\n const entry: Record<string, unknown> = {};\n const keyDelta = apiKeyPatchValue(d.apiKey, b.apiKey);\n if (keyDelta !== undefined) {\n entry.apiKey = keyDelta;\n }\n const region = optionalStringField(d, b, 'region');\n if (region !== undefined) entry.region = region;\n const baseUrl = optionalStringField(d, b, 'baseUrl');\n if (baseUrl !== undefined) entry.baseUrl = baseUrl;\n const imageBaseUrl = optionalStringField(d, b, 'imageBaseUrl');\n if (imageBaseUrl !== undefined) entry.imageBaseUrl = imageBaseUrl;\n\n if (Object.keys(entry).length > 0) {\n patch[id] = entry;\n }\n }\n return patch;\n}\n\nexport type RevealImageProviderApiKeyPayload = {\n id: string;\n apiKey: string | null;\n source: 'config' | 'none';\n};\n\n/** POST /api/image/providers/:id/reveal-api-key — plaintext only when stored in config file. */\nexport async function revealImageProviderConfigApiKey(providerId: string): Promise<RevealImageProviderApiKeyPayload> {\n const data = await fetchJson<{\n ok?: boolean;\n payload?: RevealImageProviderApiKeyPayload;\n error?: { message?: string };\n }>(apiUrl(`/api/image/providers/${encodeURIComponent(providerId)}/reveal-api-key`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n if (!data.ok || !data.payload) {\n throw new Error(data.error?.message ?? 'Reveal failed');\n }\n return data.payload;\n}\n\nexport async function patchImageProvidersConfig(\n patch: Record<string, Record<string, unknown>>,\n): Promise<void> {\n if (Object.keys(patch).length === 0) return;\n await fetchJson(apiUrl('/api/config'), {\n method: 'PATCH',\n body: JSON.stringify({ providersConfig: patch }),\n });\n await revalidateGatewayConfig();\n}\n","import { CheckCircle2, Copy, ExternalLink, Eye, EyeOff, Loader2 } from 'lucide-react';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport { revealImageProviderConfigApiKey } from '@/features/settings/image-providers-config-api';\nimport type { ApiKeyLinkKind } from '@/features/settings/provider-enrichment';\nimport { providerApiKeyLinkLabel } from '@/features/settings/provider-enrichment';\nimport { isMaskedKey } from '@/features/settings/providers-api';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport { interaction } from '@/lib/interaction';\nimport { cn } from '@/lib/cn';\n\nexport type ImageProviderApiKeyFieldLabels = {\n apiKeyLabel: string;\n optionalPlaceholder: string;\n maskedHelp: string;\n copy: string;\n copied: string;\n show: string;\n hide: string;\n notInConfigFile: string;\n loadFailed: string;\n};\n\nexport function ImageProviderApiKeyField({\n providerId,\n value,\n onChange,\n labels,\n apiKeyLinks,\n apiKeyLinkLabels,\n}: {\n providerId: string;\n value: string;\n onChange: (next: string) => void;\n labels: ImageProviderApiKeyFieldLabels;\n apiKeyLinks: { href: string; kind: ApiKeyLinkKind }[];\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n}) {\n const [showKey, setShowKey] = useState(false);\n /** `undefined` = not fetched; `null` = fetched, not in config file; string = plaintext from config */\n const [revealed, setRevealed] = useState<string | null | undefined>(undefined);\n const [revealLoading, setRevealLoading] = useState(false);\n const [revealErr, setRevealErr] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const masked = isMaskedKey(value);\n\n useEffect(() => {\n if (!masked) {\n setRevealed(undefined);\n setRevealErr(null);\n }\n }, [masked, value]);\n\n const inputValue = (() => {\n if (!masked) return value;\n if (showKey && typeof revealed === 'string') return revealed;\n return value;\n })();\n\n const inputType =\n !masked || (masked && showKey && typeof revealed === 'string') ? ('text' as const) : ('password' as const);\n\n const copyEnabled =\n (!masked && value.trim().length > 0 && !isMaskedKey(value)) ||\n (Boolean(showKey) && typeof revealed === 'string' && revealed.length > 0);\n\n const copyKey = useCallback(async () => {\n const text =\n !masked && value.trim() && !isMaskedKey(value)\n ? value.trim()\n : typeof revealed === 'string' && revealed.length > 0\n ? revealed\n : '';\n if (!text) return;\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 2000);\n } catch {\n /* ignore */\n }\n }, [masked, revealed, value]);\n\n const toggleEye = useCallback(async () => {\n setRevealErr(null);\n if (!masked) {\n setShowKey((s) => !s);\n return;\n }\n if (revealed !== undefined) {\n setShowKey((s) => !s);\n return;\n }\n setRevealLoading(true);\n try {\n const payload = await revealImageProviderConfigApiKey(providerId);\n setRevealed(payload.apiKey ?? null);\n setShowKey(true);\n } catch (e) {\n setRevealErr(e instanceof Error ? e.message : labels.loadFailed);\n setRevealed(null);\n } finally {\n setRevealLoading(false);\n }\n }, [masked, providerId, revealed, labels.loadFailed]);\n\n return (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-key-${providerId}`}>\n {labels.apiKeyLabel}\n </label>\n {apiKeyLinks.length > 0 ? (\n <div className=\"flex flex-col gap-1\">\n {apiKeyLinks.map((link) => (\n <a\n key={`${link.kind}-${link.href}`}\n href={link.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n >\n {providerApiKeyLinkLabel(link.kind, apiKeyLinkLabels)}\n <ExternalLink className=\"size-3\" aria-hidden />\n </a>\n ))}\n </div>\n ) : null}\n {masked ? <p className=\"text-[11px] text-fg-subtle\">{labels.maskedHelp}</p> : null}\n <div className=\"relative min-w-0\">\n <input\n id={`img-cred-key-${providerId}`}\n type={inputType}\n autoComplete=\"off\"\n spellCheck={false}\n className={cn(\n 'w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n )}\n value={inputValue}\n placeholder={masked ? '••••••••' : labels.optionalPlaceholder}\n onChange={(e) => {\n const next = e.target.value;\n if (masked && typeof revealed === 'string' && showKey && next !== revealed) {\n setRevealed(undefined);\n setShowKey(false);\n }\n onChange(next);\n }}\n />\n <div className=\"absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5\">\n {copyEnabled ? (\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={copied ? labels.copied : labels.copy}\n aria-label={copied ? labels.copied : labels.copy}\n onClick={() => void copyKey()}\n >\n {copied ? <CheckCircle2 className=\"size-4\" /> : <Copy className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={showKey ? labels.hide : labels.show}\n aria-label={showKey ? labels.hide : labels.show}\n disabled={revealLoading}\n onClick={() => void toggleEye()}\n >\n {revealLoading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : showKey ? (\n <EyeOff className=\"size-4\" aria-hidden />\n ) : (\n <Eye className=\"size-4\" aria-hidden />\n )}\n </button>\n </div>\n </div>\n {masked && showKey && revealed === null && !revealErr ? (\n <p className=\"text-xs text-amber-700 dark:text-amber-400/90\">{labels.notInConfigFile}</p>\n ) : null}\n {revealErr ? <p className=\"text-xs text-red-600 dark:text-red-400\">{revealErr}</p> : null}\n </div>\n );\n}\n","import { ExternalLink, Loader2, Save } from 'lucide-react';\nimport { Link } from 'react-router-dom';\n\nimport { Button } from '@/components/ui/button';\nimport { ImageProviderApiKeyField } from '@/features/settings/image-provider-api-key-field';\nimport { emptyImageProviderCredRow, type ImageProviderCredRow } from '@/features/settings/image-providers-config-api';\nimport { getOrderedApiKeyLinks } from '@/features/settings/provider-enrichment';\nimport type {\n ImageGenProviderCredentialSummary,\n ImageProviderUiMetadata,\n} from '@/features/settings/use-image-provider-credentials';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport type { StoredLanguage } from '@/lib/storage';\nimport { cn } from '@/lib/cn';\n\nfunction inputClass(): string {\n return cn(\n 'w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n );\n}\n\nfunction selectClass(): string {\n return cn(inputClass(), 'appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9');\n}\n\nconst CUSTOM_SENTINEL = '__custom__';\n\nfunction dashscopeSelectValue(\n row: ImageProviderCredRow,\n regions: NonNullable<ImageProviderUiMetadata['regions']>,\n): string {\n if (!row.region.trim() && !row.imageBaseUrl.trim()) return '';\n const r = row.region.trim().toLowerCase();\n if (regions.some((x) => x.value === r)) return r;\n return CUSTOM_SENTINEL;\n}\n\nfunction baseUrlSelectValue(\n row: ImageProviderCredRow,\n presets: NonNullable<ImageProviderUiMetadata['baseUrlPresets']>,\n): string {\n const b = row.baseUrl.trim().replace(/\\/+$/, '');\n if (!b) return '';\n const norm = presets.map((p) => p.value.replace(/\\/+$/, ''));\n const idx = norm.indexOf(b);\n if (idx >= 0) return presets[idx].value;\n return CUSTOM_SENTINEL;\n}\n\nexport type ImageProviderCredentialsPanelMessages = {\n credentialsIntro: string;\n regionHint: string;\n endpointPresetsHint: string;\n apiKeyLabel: string;\n optionalPlaceholder: string;\n regionLabel: string;\n baseUrlLabel: string;\n imageBaseUrlLabel: string;\n saveCredentials: string;\n savingCredentials: string;\n credentialsSaved: string;\n discardCredentials: string;\n credentialsNothingToSave: string;\n credentialsSaveError: string;\n regionPresetDefault: string;\n regionPresetCustom: string;\n baseUrlPresetDefault: string;\n baseUrlPresetCustom: string;\n openExtensionSettings: string;\n openImageModelsPage: string;\n extensionSettingsLinkTitle: string;\n imageModelsLinkTitle: string;\n configured: string;\n missingKey: string;\n defaultModel: string;\n modelsLabel: string;\n imageBaseUrlPresetHint: string;\n dashscopeRegion_beijing: string;\n dashscopeRegion_singapore: string;\n dashscopeRegion_us: string;\n apiKeyMaskedHelp: string;\n apiKeyCopy: string;\n apiKeyCopied: string;\n apiKeyShow: string;\n apiKeyHide: string;\n apiKeyNotInConfigFile: string;\n apiKeyRevealFailed: string;\n minimaxClusterLabel: string;\n minimaxClusterHint: string;\n falQueueBaseLabel: string;\n falQueueBaseHint: string;\n};\n\nfunction translateDashscopeRegion(m: ImageProviderCredentialsPanelMessages, value: string, serverLabel: string) {\n if (value === 'beijing') return m.dashscopeRegion_beijing;\n if (value === 'singapore') return m.dashscopeRegion_singapore;\n if (value === 'us') return m.dashscopeRegion_us;\n return serverLabel;\n}\n\nfunction baseUrlPresetBlockTitle(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string {\n if (kind === 'minimax') return t.minimaxClusterLabel;\n if (kind === 'fal') return t.falQueueBaseLabel;\n return t.baseUrlLabel;\n}\n\nfunction baseUrlPresetBlockHint(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string | null {\n if (kind === 'minimax') return t.minimaxClusterHint;\n if (kind === 'fal') return t.falQueueBaseHint;\n return null;\n}\n\nexport function ImageProviderCredentialsPanel({\n summaries,\n credDraft,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n onSaveCredentials,\n extensionIds,\n showExtensionLinks,\n showImageModelsLink,\n language,\n apiKeyLinkLabels,\n messages: t,\n}: {\n summaries: ImageGenProviderCredentialSummary[];\n credDraft: Record<string, ImageProviderCredRow>;\n credDirty: boolean;\n credSaving: boolean;\n credError: string | null;\n credSavedFlash: boolean;\n credNoopFlash: boolean;\n updateCredRow: (id: string, patch: Partial<ImageProviderCredRow>) => void;\n onDiscardCredentials: () => void;\n onSaveCredentials: () => void;\n /** Extension ids present in gateway discovery (for deep links). */\n extensionIds: Set<string>;\n showExtensionLinks: boolean;\n showImageModelsLink: boolean;\n language: StoredLanguage;\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n messages: ImageProviderCredentialsPanelMessages;\n}) {\n const empty = summaries.length === 0;\n\n if (empty) {\n return null;\n }\n\n const anyRegionUi = summaries.some((s) => (s.ui?.regions?.length ?? 0) > 0);\n const anyBaseUrlPresets = summaries.some((s) => (s.ui?.baseUrlPresets?.length ?? 0) > 0);\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1 text-xs leading-relaxed text-fg-muted\">\n <p>{t.credentialsIntro}</p>\n {anyRegionUi ? <p className=\"text-fg-subtle\">{t.regionHint}</p> : null}\n {anyBaseUrlPresets ? <p className=\"text-fg-subtle\">{t.endpointPresetsHint}</p> : null}\n {showImageModelsLink ? (\n <p>\n <Link\n to=\"/settings/image-models\"\n className=\"font-medium text-accent hover:underline\"\n title={t.imageModelsLinkTitle}\n >\n {t.openImageModelsPage}\n </Link>\n </p>\n ) : null}\n </div>\n {credError ? (\n <div className=\"rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300\">\n {credError}\n </div>\n ) : null}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {credSavedFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsSaved}</span>\n ) : null}\n {credNoopFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsNothingToSave}</span>\n ) : null}\n <Button type=\"button\" variant=\"secondary\" onClick={onDiscardCredentials} disabled={!credDirty || credSaving}>\n {t.discardCredentials}\n </Button>\n <Button type=\"button\" variant=\"primary\" onClick={onSaveCredentials} disabled={!credDirty || credSaving}>\n {credSaving ? (\n <>\n <Loader2 className=\"size-3.5 animate-spin\" />\n <span className=\"ml-1.5\">{t.savingCredentials}</span>\n </>\n ) : (\n <>\n <Save className=\"size-3.5\" />\n <span className=\"ml-1.5\">{t.saveCredentials}</span>\n </>\n )}\n </Button>\n </div>\n <div className=\"flex flex-col gap-4\">\n {summaries.map((p) => {\n const row = credDraft[p.id] ?? emptyImageProviderCredRow();\n const ui = p.ui;\n const extPath =\n showExtensionLinks && extensionIds.has(p.id)\n ? `/settings/ext/${encodeURIComponent(p.id)}`\n : null;\n return (\n <div\n key={p.id}\n className=\"rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-fg\">{p.label ?? p.id}</span>\n <span className=\"text-xs text-fg-subtle\">({p.id})</span>\n {extPath ? (\n <Link\n to={extPath}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline\"\n title={t.extensionSettingsLinkTitle}\n >\n <ExternalLink className=\"size-3\" />\n {t.openExtensionSettings}\n </Link>\n ) : null}\n </div>\n {p.configured ? (\n <span className=\"rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg\">\n {t.configured}\n </span>\n ) : (\n <span className=\"rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300\">\n {t.missingKey}\n </span>\n )}\n </div>\n {p.defaultModel ? (\n <p className=\"mt-1 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.defaultModel}:</span> {p.id}/{p.defaultModel}\n </p>\n ) : null}\n {p.models.length > 0 ? (\n <p className=\"mt-0.5 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.modelsLabel}:</span>{' '}\n {p.models.map((mm) => `${p.id}/${mm}`).join(', ')}\n </p>\n ) : null}\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <ImageProviderApiKeyField\n providerId={p.id}\n value={row.apiKey}\n onChange={(next) => updateCredRow(p.id, { apiKey: next })}\n apiKeyLinks={getOrderedApiKeyLinks(p.id, language)}\n apiKeyLinkLabels={apiKeyLinkLabels}\n labels={{\n apiKeyLabel: t.apiKeyLabel,\n optionalPlaceholder: t.optionalPlaceholder,\n maskedHelp: t.apiKeyMaskedHelp,\n copy: t.apiKeyCopy,\n copied: t.apiKeyCopied,\n show: t.apiKeyShow,\n hide: t.apiKeyHide,\n notInConfigFile: t.apiKeyNotInConfigFile,\n loadFailed: t.apiKeyRevealFailed,\n }}\n />\n\n {ui?.regions?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-region-preset-${p.id}`}>\n {t.regionLabel}\n </label>\n <select\n id={`img-cred-region-preset-${p.id}`}\n className={selectClass()}\n value={dashscopeSelectValue(row, ui.regions)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n const opt = ui.regions!.find((x) => x.value === v);\n if (opt) {\n updateCredRow(p.id, { region: opt.value, imageBaseUrl: opt.imageBaseUrl });\n }\n }}\n >\n <option value=\"\">{t.regionPresetDefault}</option>\n {ui.regions.map((r) => (\n <option key={r.value} value={r.value}>\n {translateDashscopeRegion(t, r.value, r.label)}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.regionPresetCustom}</option>\n </select>\n {dashscopeSelectValue(row, ui.regions) === CUSTOM_SENTINEL ? (\n <div className=\"mt-2 grid gap-2 sm:grid-cols-2\">\n <input\n type=\"text\"\n className={inputClass()}\n value={row.region}\n placeholder=\"region\"\n onChange={(e) => updateCredRow(p.id, { region: e.target.value })}\n />\n <input\n type=\"url\"\n className={inputClass()}\n value={row.imageBaseUrl}\n placeholder={t.imageBaseUrlLabel}\n onChange={(e) => updateCredRow(p.id, { imageBaseUrl: e.target.value })}\n />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {ui?.baseUrlPresets?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-base-preset-${p.id}`}>\n {baseUrlPresetBlockTitle(t, ui.baseUrlPresetKind)}\n </label>\n {baseUrlPresetBlockHint(t, ui.baseUrlPresetKind) ? (\n <p className=\"text-[11px] text-fg-subtle\">{baseUrlPresetBlockHint(t, ui.baseUrlPresetKind)}</p>\n ) : null}\n <select\n id={`img-cred-base-preset-${p.id}`}\n className={selectClass()}\n value={baseUrlSelectValue(row, ui.baseUrlPresets)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n updateCredRow(p.id, { baseUrl: v.replace(/\\/+$/, '') });\n }}\n >\n <option value=\"\">{t.baseUrlPresetDefault}</option>\n {ui.baseUrlPresets.map((b) => (\n <option key={b.value} value={b.value}>\n {b.label}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.baseUrlPresetCustom}</option>\n </select>\n {baseUrlSelectValue(row, ui.baseUrlPresets) === CUSTOM_SENTINEL ? (\n <input\n type=\"url\"\n className={cn(inputClass(), 'mt-2')}\n value={row.baseUrl}\n placeholder=\"https://…\"\n onChange={(e) => updateCredRow(p.id, { baseUrl: e.target.value })}\n />\n ) : null}\n </div>\n ) : null}\n\n {ui?.regions?.length && dashscopeSelectValue(row, ui.regions) !== CUSTOM_SENTINEL ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-imgbase-ro-${p.id}`}>\n {t.imageBaseUrlLabel}\n </label>\n <input\n id={`img-cred-imgbase-ro-${p.id}`}\n type=\"url\"\n readOnly\n className={cn(inputClass(), 'cursor-not-allowed opacity-90')}\n value={row.imageBaseUrl}\n title={t.imageBaseUrlPresetHint}\n />\n <p className=\"text-[11px] text-fg-subtle\">{t.imageBaseUrlPresetHint}</p>\n </div>\n ) : null}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport { mutate } from 'swr';\n\nimport { useGatewayConfigSwr } from '@/features/gateway/gateway-config-swr';\nimport {\n buildImageProvidersConfigPatch,\n emptyImageProviderCredRow,\n imageProviderCredRowsFromConfigRoot,\n patchImageProvidersConfig,\n type ImageProviderCredRow,\n} from '@/features/settings/image-providers-config-api';\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport { apiUrl } from '@/lib/url';\nimport { useGatewayStore } from '@/stores/gateway-store';\n\nexport type ImageProviderUiRegionOption = {\n value: string;\n label: string;\n imageBaseUrl: string;\n};\n\nexport type ImageProviderUiBaseUrlPreset = {\n value: string;\n label: string;\n};\n\nexport type ImageProviderUiMetadata = {\n regions?: ImageProviderUiRegionOption[];\n baseUrlPresets?: ImageProviderUiBaseUrlPreset[];\n baseUrlPresetKind?: 'fal' | 'minimax' | 'google' | 'openai';\n};\n\nexport type ImageGenProviderCredentialSummary = {\n id: string;\n label?: string;\n defaultModel?: string;\n models: string[];\n configured?: boolean;\n ui?: ImageProviderUiMetadata;\n};\n\nexport function useImageProviderCredentials(summaries: ImageGenProviderCredentialSummary[]) {\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const gwSwr = useGatewayConfigSwr(hasToken);\n const gwCfg = gwSwr.data;\n\n const ids = useMemo(() => summaries.map((s) => s.id), [summaries]);\n\n const [credDraft, setCredDraft] = useState<Record<string, ImageProviderCredRow>>({});\n const [credBaseline, setCredBaseline] = useState<Record<string, ImageProviderCredRow>>({});\n const [credSaving, setCredSaving] = useState(false);\n const [credError, setCredError] = useState<string | null>(null);\n const [credSavedFlash, setCredSavedFlash] = useState(false);\n const [credNoopFlash, setCredNoopFlash] = useState(false);\n\n const credRowsFromServer = useMemo(\n () => imageProviderCredRowsFromConfigRoot(gwCfg?.payload?.config, ids),\n [gwCfg?.payload?.config, ids],\n );\n\n const credDirty = useMemo(\n () => JSON.stringify(credDraft) !== JSON.stringify(credBaseline),\n [credDraft, credBaseline],\n );\n\n useEffect(() => {\n if (!credDirty) {\n setCredDraft(structuredClone(credRowsFromServer));\n setCredBaseline(structuredClone(credRowsFromServer));\n }\n }, [credRowsFromServer, credDirty]);\n\n const updateCredRow = useCallback((id: string, patch: Partial<ImageProviderCredRow>) => {\n setCredDraft((prev) => {\n const base = prev[id] ?? emptyImageProviderCredRow();\n return { ...prev, [id]: { ...base, ...patch } };\n });\n }, []);\n\n const onDiscardCredentials = useCallback(() => {\n setCredDraft(structuredClone(credBaseline));\n setCredError(null);\n setCredSavedFlash(false);\n setCredNoopFlash(false);\n }, [credBaseline]);\n\n const saveCredentials = useCallback(\n async (errorFallback: string) => {\n const patch = buildImageProvidersConfigPatch(ids, credDraft, credBaseline);\n if (Object.keys(patch).length === 0) {\n setCredNoopFlash(true);\n window.setTimeout(() => setCredNoopFlash(false), 2200);\n return;\n }\n setCredSaving(true);\n setCredError(null);\n setCredSavedFlash(false);\n try {\n await patchImageProvidersConfig(patch);\n const updated = await gwSwr.mutate?.();\n void mutate(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n const nextRows = imageProviderCredRowsFromConfigRoot(updated?.payload?.config, ids);\n setCredDraft(structuredClone(nextRows));\n setCredBaseline(structuredClone(nextRows));\n setCredSavedFlash(true);\n window.setTimeout(() => setCredSavedFlash(false), 2000);\n } catch (e) {\n setCredError(e instanceof Error ? e.message : errorFallback);\n } finally {\n setCredSaving(false);\n }\n },\n [ids, credDraft, credBaseline, gwSwr],\n );\n\n return {\n gwSwr,\n credDraft,\n credBaseline,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n saveCredentials,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-image-provider-credentials-C4x4dNv2.js","names":["IMAGE_PROVIDERS_SWR_KEY","fetchJson","apiUrl","IMAGE_PROVIDERS_SWR_KEY","ImageGenProviderCredentialSummary","fetchImageProvidersList","Promise","res","ok","payload","providers","isMaskedKey","revalidateGatewayConfig","fetchJson","apiUrl","ImageProviderCredRow","apiKey","region","baseUrl","imageBaseUrl","emptyImageProviderCredRow","SafeProviderAuthEntry","maskedApiKeyDisplay","safe","imageProviderCredRowsFromConfigRoot","config","imageProviderIds","Record","pc","undefined","v","providersConfig","Array","isArray","out","id","optionalStringField","draft","baseline","key","Pick","d","trim","b","apiKeyPatchValue","draftKey","baselineKey","buildImageProvidersConfigPatch","patch","JSON","stringify","entry","keyDelta","Object","keys","length","RevealImageProviderApiKeyPayload","source","revealImageProviderConfigApiKey","providerId","Promise","data","ok","payload","error","message","encodeURIComponent","method","headers","body","Error","patchImageProvidersConfig","CheckCircle2","Copy","ExternalLink","Eye","EyeOff","Loader2","useCallback","useEffect","useState","revealImageProviderConfigApiKey","ApiKeyLinkKind","providerApiKeyLinkLabel","isMaskedKey","ProvidersSettingsMessages","settingsInputFocusClass","interaction","cn","ImageProviderApiKeyFieldLabels","apiKeyLabel","optionalPlaceholder","maskedHelp","copy","copied","show","hide","notInConfigFile","loadFailed","ImageProviderApiKeyField","providerId","value","onChange","labels","apiKeyLinks","apiKeyLinkLabels","next","href","kind","Pick","showKey","setShowKey","revealed","setRevealed","undefined","revealLoading","setRevealLoading","revealErr","setRevealErr","setCopied","masked","inputValue","inputType","const","copyEnabled","trim","length","Boolean","copyKey","text","navigator","clipboard","writeText","window","setTimeout","toggleEye","s","payload","apiKey","e","Error","message","map","link","target","transition","press","focusRingPanel","ExternalLink","Loader2","Save","Link","Button","ImageProviderApiKeyField","emptyImageProviderCredRow","ImageProviderCredRow","getOrderedApiKeyLinks","ImageGenProviderCredentialSummary","ImageProviderUiMetadata","ProvidersSettingsMessages","settingsInputFocusClass","StoredLanguage","cn","inputClass","selectClass","CUSTOM_SENTINEL","dashscopeSelectValue","row","regions","NonNullable","region","trim","imageBaseUrl","r","toLowerCase","some","x","value","baseUrlSelectValue","presets","b","baseUrl","replace","norm","map","p","idx","indexOf","ImageProviderCredentialsPanelMessages","credentialsIntro","regionHint","endpointPresetsHint","apiKeyLabel","optionalPlaceholder","regionLabel","baseUrlLabel","imageBaseUrlLabel","saveCredentials","savingCredentials","credentialsSaved","discardCredentials","credentialsNothingToSave","credentialsSaveError","regionPresetDefault","regionPresetCustom","baseUrlPresetDefault","baseUrlPresetCustom","openExtensionSettings","openImageModelsPage","extensionSettingsLinkTitle","imageModelsLinkTitle","configured","missingKey","defaultModel","modelsLabel","imageBaseUrlPresetHint","dashscopeRegion_beijing","dashscopeRegion_singapore","dashscopeRegion_us","apiKeyMaskedHelp","apiKeyCopy","apiKeyCopied","apiKeyShow","apiKeyHide","apiKeyNotInConfigFile","apiKeyRevealFailed","minimaxClusterLabel","minimaxClusterHint","falQueueBaseLabel","falQueueBaseHint","translateDashscopeRegion","m","serverLabel","baseUrlPresetBlockTitle","t","kind","baseUrlPresetBlockHint","ImageProviderCredentialsPanel","t0","$","_c","summaries","credDraft","credDirty","credSaving","credError","credSavedFlash","credNoopFlash","updateCredRow","onDiscardCredentials","onSaveCredentials","extensionIds","showExtensionLinks","showImageModelsLink","language","apiKeyLinkLabels","messages","empty","length","t1","_temp","anyRegionUi","t2","_temp2","anyBaseUrlPresets","t3","t4","t5","t6","t7","t8","t9","t10","t11","t12","t13","t14","t15","t16","t17","t18","id","ui","extPath","has","encodeURIComponent","label","models","mm","join","apiKey","next","maskedHelp","copy","copied","show","hide","notInConfigFile","loadFailed","e","v","target","opt","find","e_0","e_1","baseUrlPresets","baseUrlPresetKind","e_2","v_0","_temp3","e_3","t19","s_0","s","useCallback","useEffect","useMemo","useState","mutate","useGatewayConfigSwr","buildImageProvidersConfigPatch","emptyImageProviderCredRow","imageProviderCredRowsFromConfigRoot","patchImageProvidersConfig","ImageProviderCredRow","IMAGE_PROVIDERS_SWR_KEY","apiUrl","useGatewayStore","ImageProviderUiRegionOption","value","label","imageBaseUrl","ImageProviderUiBaseUrlPreset","ImageProviderUiMetadata","regions","baseUrlPresets","baseUrlPresetKind","ImageGenProviderCredentialSummary","id","defaultModel","models","configured","ui","useImageProviderCredentials","summaries","hasToken","s","Boolean","token","gwSwr","gwCfg","data","ids","map","credDraft","setCredDraft","Record","credBaseline","setCredBaseline","credSaving","setCredSaving","credError","setCredError","credSavedFlash","setCredSavedFlash","credNoopFlash","setCredNoopFlash","credRowsFromServer","payload","config","credDirty","JSON","stringify","structuredClone","updateCredRow","patch","Partial","prev","base","onDiscardCredentials","saveCredentials","errorFallback","Object","keys","length","window","setTimeout","updated","nextRows","e","Error","message"],"sources":["../../../../../web/src/features/settings/image-providers-swr-key.ts","../../../../../web/src/features/settings/fetch-image-providers.ts","../../../../../web/src/features/settings/image-providers-config-api.ts","../../../../../web/src/features/settings/image-provider-api-key-field.tsx","../../../../../web/src/features/settings/image-provider-credentials-panel.tsx","../../../../../web/src/features/settings/use-image-provider-credentials.ts"],"sourcesContent":["/** Shared SWR key for GET `/api/image/providers` (image settings + extension image pages). */\nexport const IMAGE_PROVIDERS_SWR_KEY = '/api/image/providers';\n","import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport type { ImageGenProviderCredentialSummary } from '@/features/settings/use-image-provider-credentials';\n\nexport async function fetchImageProvidersList(): Promise<ImageGenProviderCredentialSummary[]> {\n const res = await fetchJson<{\n ok?: boolean;\n payload?: { providers?: ImageGenProviderCredentialSummary[] };\n }>(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n return res?.payload?.providers ?? [];\n}\n","import { isMaskedKey } from '@/features/settings/providers-api';\nimport { revalidateGatewayConfig } from '@/features/gateway/gateway-config-swr';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\n/** One row of image-provider credential fields (matches PATCH `providersConfig` subset). */\nexport type ImageProviderCredRow = {\n apiKey: string;\n region: string;\n baseUrl: string;\n imageBaseUrl: string;\n};\n\nexport function emptyImageProviderCredRow(): ImageProviderCredRow {\n return { apiKey: '', region: '', baseUrl: '', imageBaseUrl: '' };\n}\n\nexport type SafeProviderAuthEntry = {\n apiKey: string;\n region?: string;\n baseUrl?: string;\n imageBaseUrl?: string;\n};\n\nfunction maskedApiKeyDisplay(safe?: SafeProviderAuthEntry): string {\n if (!safe?.apiKey) return '';\n return '••••••••••••';\n}\n\n/** Read `payload.config.providersConfig` from GET /api/config (masked). */\nexport function imageProviderCredRowsFromConfigRoot(\n config: unknown,\n imageProviderIds: string[],\n): Record<string, ImageProviderCredRow> {\n const pc = (() => {\n if (!config || typeof config !== 'object' || !('providersConfig' in config)) return undefined;\n const v = (config as { providersConfig?: unknown }).providersConfig;\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n return v as Record<string, SafeProviderAuthEntry>;\n })();\n\n const out: Record<string, ImageProviderCredRow> = {};\n for (const id of imageProviderIds) {\n const safe = pc?.[id];\n out[id] = {\n apiKey: maskedApiKeyDisplay(safe),\n region: safe?.region ?? '',\n baseUrl: safe?.baseUrl ?? '',\n imageBaseUrl: safe?.imageBaseUrl ?? '',\n };\n }\n return out;\n}\n\nfunction optionalStringField(\n draft: ImageProviderCredRow,\n baseline: ImageProviderCredRow,\n key: keyof Pick<ImageProviderCredRow, 'region' | 'baseUrl' | 'imageBaseUrl'>,\n): string | null | undefined {\n const d = draft[key].trim();\n const b = baseline[key].trim();\n if (d === b) return undefined;\n if (!d) return null;\n return d;\n}\n\nfunction apiKeyPatchValue(draftKey: string, baselineKey: string): string | null | undefined {\n const d = draftKey.trim();\n const b = baselineKey.trim();\n if (d === b) return undefined;\n if (isMaskedKey(d) && isMaskedKey(b)) return undefined;\n if (!d) {\n if (!b) return undefined;\n return null;\n }\n return d;\n}\n\n/**\n * Build `providersConfig` PATCH entries only for image providers whose row changed.\n * Omits `apiKey` when unchanged (still masked); sends `null` to clear stored key.\n */\nexport function buildImageProvidersConfigPatch(\n imageProviderIds: string[],\n draft: Record<string, ImageProviderCredRow>,\n baseline: Record<string, ImageProviderCredRow>,\n): Record<string, Record<string, unknown>> {\n const patch: Record<string, Record<string, unknown>> = {};\n for (const id of imageProviderIds) {\n const d = draft[id] ?? emptyImageProviderCredRow();\n const b = baseline[id] ?? emptyImageProviderCredRow();\n if (JSON.stringify(d) === JSON.stringify(b)) continue;\n\n const entry: Record<string, unknown> = {};\n const keyDelta = apiKeyPatchValue(d.apiKey, b.apiKey);\n if (keyDelta !== undefined) {\n entry.apiKey = keyDelta;\n }\n const region = optionalStringField(d, b, 'region');\n if (region !== undefined) entry.region = region;\n const baseUrl = optionalStringField(d, b, 'baseUrl');\n if (baseUrl !== undefined) entry.baseUrl = baseUrl;\n const imageBaseUrl = optionalStringField(d, b, 'imageBaseUrl');\n if (imageBaseUrl !== undefined) entry.imageBaseUrl = imageBaseUrl;\n\n if (Object.keys(entry).length > 0) {\n patch[id] = entry;\n }\n }\n return patch;\n}\n\nexport type RevealImageProviderApiKeyPayload = {\n id: string;\n apiKey: string | null;\n source: 'config' | 'none';\n};\n\n/** POST /api/image/providers/:id/reveal-api-key — plaintext only when stored in config file. */\nexport async function revealImageProviderConfigApiKey(providerId: string): Promise<RevealImageProviderApiKeyPayload> {\n const data = await fetchJson<{\n ok?: boolean;\n payload?: RevealImageProviderApiKeyPayload;\n error?: { message?: string };\n }>(apiUrl(`/api/image/providers/${encodeURIComponent(providerId)}/reveal-api-key`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n if (!data.ok || !data.payload) {\n throw new Error(data.error?.message ?? 'Reveal failed');\n }\n return data.payload;\n}\n\nexport async function patchImageProvidersConfig(\n patch: Record<string, Record<string, unknown>>,\n): Promise<void> {\n if (Object.keys(patch).length === 0) return;\n await fetchJson(apiUrl('/api/config'), {\n method: 'PATCH',\n body: JSON.stringify({ providersConfig: patch }),\n });\n await revalidateGatewayConfig();\n}\n","import { CheckCircle2, Copy, ExternalLink, Eye, EyeOff, Loader2 } from 'lucide-react';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport { revealImageProviderConfigApiKey } from '@/features/settings/image-providers-config-api';\nimport type { ApiKeyLinkKind } from '@/features/settings/provider-enrichment';\nimport { providerApiKeyLinkLabel } from '@/features/settings/provider-enrichment';\nimport { isMaskedKey } from '@/features/settings/providers-api';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport { interaction } from '@/lib/interaction';\nimport { cn } from '@/lib/cn';\n\nexport type ImageProviderApiKeyFieldLabels = {\n apiKeyLabel: string;\n optionalPlaceholder: string;\n maskedHelp: string;\n copy: string;\n copied: string;\n show: string;\n hide: string;\n notInConfigFile: string;\n loadFailed: string;\n};\n\nexport function ImageProviderApiKeyField({\n providerId,\n value,\n onChange,\n labels,\n apiKeyLinks,\n apiKeyLinkLabels,\n}: {\n providerId: string;\n value: string;\n onChange: (next: string) => void;\n labels: ImageProviderApiKeyFieldLabels;\n apiKeyLinks: { href: string; kind: ApiKeyLinkKind }[];\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n}) {\n const [showKey, setShowKey] = useState(false);\n /** `undefined` = not fetched; `null` = fetched, not in config file; string = plaintext from config */\n const [revealed, setRevealed] = useState<string | null | undefined>(undefined);\n const [revealLoading, setRevealLoading] = useState(false);\n const [revealErr, setRevealErr] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const masked = isMaskedKey(value);\n\n useEffect(() => {\n if (!masked) {\n setRevealed(undefined);\n setRevealErr(null);\n }\n }, [masked, value]);\n\n const inputValue = (() => {\n if (!masked) return value;\n if (showKey && typeof revealed === 'string') return revealed;\n return value;\n })();\n\n const inputType =\n !masked || (masked && showKey && typeof revealed === 'string') ? ('text' as const) : ('password' as const);\n\n const copyEnabled =\n (!masked && value.trim().length > 0 && !isMaskedKey(value)) ||\n (Boolean(showKey) && typeof revealed === 'string' && revealed.length > 0);\n\n const copyKey = useCallback(async () => {\n const text =\n !masked && value.trim() && !isMaskedKey(value)\n ? value.trim()\n : typeof revealed === 'string' && revealed.length > 0\n ? revealed\n : '';\n if (!text) return;\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 2000);\n } catch {\n /* ignore */\n }\n }, [masked, revealed, value]);\n\n const toggleEye = useCallback(async () => {\n setRevealErr(null);\n if (!masked) {\n setShowKey((s) => !s);\n return;\n }\n if (revealed !== undefined) {\n setShowKey((s) => !s);\n return;\n }\n setRevealLoading(true);\n try {\n const payload = await revealImageProviderConfigApiKey(providerId);\n setRevealed(payload.apiKey ?? null);\n setShowKey(true);\n } catch (e) {\n setRevealErr(e instanceof Error ? e.message : labels.loadFailed);\n setRevealed(null);\n } finally {\n setRevealLoading(false);\n }\n }, [masked, providerId, revealed, labels.loadFailed]);\n\n return (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-key-${providerId}`}>\n {labels.apiKeyLabel}\n </label>\n {apiKeyLinks.length > 0 ? (\n <div className=\"flex flex-col gap-1\">\n {apiKeyLinks.map((link) => (\n <a\n key={`${link.kind}-${link.href}`}\n href={link.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n >\n {providerApiKeyLinkLabel(link.kind, apiKeyLinkLabels)}\n <ExternalLink className=\"size-3\" aria-hidden />\n </a>\n ))}\n </div>\n ) : null}\n {masked ? <p className=\"text-[11px] text-fg-subtle\">{labels.maskedHelp}</p> : null}\n <div className=\"relative min-w-0\">\n <input\n id={`img-cred-key-${providerId}`}\n type={inputType}\n autoComplete=\"off\"\n spellCheck={false}\n className={cn(\n 'w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n )}\n value={inputValue}\n placeholder={masked ? '••••••••' : labels.optionalPlaceholder}\n onChange={(e) => {\n const next = e.target.value;\n if (masked && typeof revealed === 'string' && showKey && next !== revealed) {\n setRevealed(undefined);\n setShowKey(false);\n }\n onChange(next);\n }}\n />\n <div className=\"absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5\">\n {copyEnabled ? (\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={copied ? labels.copied : labels.copy}\n aria-label={copied ? labels.copied : labels.copy}\n onClick={() => void copyKey()}\n >\n {copied ? <CheckCircle2 className=\"size-4\" /> : <Copy className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n className={cn(\n 'rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40',\n interaction.transition,\n interaction.press,\n interaction.focusRingPanel,\n )}\n title={showKey ? labels.hide : labels.show}\n aria-label={showKey ? labels.hide : labels.show}\n disabled={revealLoading}\n onClick={() => void toggleEye()}\n >\n {revealLoading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : showKey ? (\n <EyeOff className=\"size-4\" aria-hidden />\n ) : (\n <Eye className=\"size-4\" aria-hidden />\n )}\n </button>\n </div>\n </div>\n {masked && showKey && revealed === null && !revealErr ? (\n <p className=\"text-xs text-amber-700 dark:text-amber-400/90\">{labels.notInConfigFile}</p>\n ) : null}\n {revealErr ? <p className=\"text-xs text-red-600 dark:text-red-400\">{revealErr}</p> : null}\n </div>\n );\n}\n","import { ExternalLink, Loader2, Save } from 'lucide-react';\nimport { Link } from 'react-router-dom';\n\nimport { Button } from '@/components/ui/button';\nimport { ImageProviderApiKeyField } from '@/features/settings/image-provider-api-key-field';\nimport { emptyImageProviderCredRow, type ImageProviderCredRow } from '@/features/settings/image-providers-config-api';\nimport { getOrderedApiKeyLinks } from '@/features/settings/provider-enrichment';\nimport type {\n ImageGenProviderCredentialSummary,\n ImageProviderUiMetadata,\n} from '@/features/settings/use-image-provider-credentials';\nimport type { ProvidersSettingsMessages } from '@/i18n/messages';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport type { StoredLanguage } from '@/lib/storage';\nimport { cn } from '@/lib/cn';\n\nfunction inputClass(): string {\n return cn(\n 'w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n );\n}\n\nfunction selectClass(): string {\n return cn(inputClass(), 'appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9');\n}\n\nconst CUSTOM_SENTINEL = '__custom__';\n\nfunction dashscopeSelectValue(\n row: ImageProviderCredRow,\n regions: NonNullable<ImageProviderUiMetadata['regions']>,\n): string {\n if (!row.region.trim() && !row.imageBaseUrl.trim()) return '';\n const r = row.region.trim().toLowerCase();\n if (regions.some((x) => x.value === r)) return r;\n return CUSTOM_SENTINEL;\n}\n\nfunction baseUrlSelectValue(\n row: ImageProviderCredRow,\n presets: NonNullable<ImageProviderUiMetadata['baseUrlPresets']>,\n): string {\n const b = row.baseUrl.trim().replace(/\\/+$/, '');\n if (!b) return '';\n const norm = presets.map((p) => p.value.replace(/\\/+$/, ''));\n const idx = norm.indexOf(b);\n if (idx >= 0) return presets[idx].value;\n return CUSTOM_SENTINEL;\n}\n\nexport type ImageProviderCredentialsPanelMessages = {\n credentialsIntro: string;\n regionHint: string;\n endpointPresetsHint: string;\n apiKeyLabel: string;\n optionalPlaceholder: string;\n regionLabel: string;\n baseUrlLabel: string;\n imageBaseUrlLabel: string;\n saveCredentials: string;\n savingCredentials: string;\n credentialsSaved: string;\n discardCredentials: string;\n credentialsNothingToSave: string;\n credentialsSaveError: string;\n regionPresetDefault: string;\n regionPresetCustom: string;\n baseUrlPresetDefault: string;\n baseUrlPresetCustom: string;\n openExtensionSettings: string;\n openImageModelsPage: string;\n extensionSettingsLinkTitle: string;\n imageModelsLinkTitle: string;\n configured: string;\n missingKey: string;\n defaultModel: string;\n modelsLabel: string;\n imageBaseUrlPresetHint: string;\n dashscopeRegion_beijing: string;\n dashscopeRegion_singapore: string;\n dashscopeRegion_us: string;\n apiKeyMaskedHelp: string;\n apiKeyCopy: string;\n apiKeyCopied: string;\n apiKeyShow: string;\n apiKeyHide: string;\n apiKeyNotInConfigFile: string;\n apiKeyRevealFailed: string;\n minimaxClusterLabel: string;\n minimaxClusterHint: string;\n falQueueBaseLabel: string;\n falQueueBaseHint: string;\n};\n\nfunction translateDashscopeRegion(m: ImageProviderCredentialsPanelMessages, value: string, serverLabel: string) {\n if (value === 'beijing') return m.dashscopeRegion_beijing;\n if (value === 'singapore') return m.dashscopeRegion_singapore;\n if (value === 'us') return m.dashscopeRegion_us;\n return serverLabel;\n}\n\nfunction baseUrlPresetBlockTitle(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string {\n if (kind === 'minimax') return t.minimaxClusterLabel;\n if (kind === 'fal') return t.falQueueBaseLabel;\n return t.baseUrlLabel;\n}\n\nfunction baseUrlPresetBlockHint(\n t: ImageProviderCredentialsPanelMessages,\n kind: ImageProviderUiMetadata['baseUrlPresetKind'],\n): string | null {\n if (kind === 'minimax') return t.minimaxClusterHint;\n if (kind === 'fal') return t.falQueueBaseHint;\n return null;\n}\n\nexport function ImageProviderCredentialsPanel({\n summaries,\n credDraft,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n onSaveCredentials,\n extensionIds,\n showExtensionLinks,\n showImageModelsLink,\n language,\n apiKeyLinkLabels,\n messages: t,\n}: {\n summaries: ImageGenProviderCredentialSummary[];\n credDraft: Record<string, ImageProviderCredRow>;\n credDirty: boolean;\n credSaving: boolean;\n credError: string | null;\n credSavedFlash: boolean;\n credNoopFlash: boolean;\n updateCredRow: (id: string, patch: Partial<ImageProviderCredRow>) => void;\n onDiscardCredentials: () => void;\n onSaveCredentials: () => void;\n /** Extension ids present in gateway discovery (for deep links). */\n extensionIds: Set<string>;\n showExtensionLinks: boolean;\n showImageModelsLink: boolean;\n language: StoredLanguage;\n apiKeyLinkLabels: Pick<ProvidersSettingsMessages, 'getApiKey' | 'getApiKeyIntl' | 'getApiKeyCn'>;\n messages: ImageProviderCredentialsPanelMessages;\n}) {\n const empty = summaries.length === 0;\n\n if (empty) {\n return null;\n }\n\n const anyRegionUi = summaries.some((s) => (s.ui?.regions?.length ?? 0) > 0);\n const anyBaseUrlPresets = summaries.some((s) => (s.ui?.baseUrlPresets?.length ?? 0) > 0);\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1 text-xs leading-relaxed text-fg-muted\">\n <p>{t.credentialsIntro}</p>\n {anyRegionUi ? <p className=\"text-fg-subtle\">{t.regionHint}</p> : null}\n {anyBaseUrlPresets ? <p className=\"text-fg-subtle\">{t.endpointPresetsHint}</p> : null}\n {showImageModelsLink ? (\n <p>\n <Link\n to=\"/settings/image-models\"\n className=\"font-medium text-accent hover:underline\"\n title={t.imageModelsLinkTitle}\n >\n {t.openImageModelsPage}\n </Link>\n </p>\n ) : null}\n </div>\n {credError ? (\n <div className=\"rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300\">\n {credError}\n </div>\n ) : null}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {credSavedFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsSaved}</span>\n ) : null}\n {credNoopFlash ? (\n <span className=\"text-sm text-fg-muted\">{t.credentialsNothingToSave}</span>\n ) : null}\n <Button type=\"button\" variant=\"secondary\" onClick={onDiscardCredentials} disabled={!credDirty || credSaving}>\n {t.discardCredentials}\n </Button>\n <Button type=\"button\" variant=\"primary\" onClick={onSaveCredentials} disabled={!credDirty || credSaving}>\n {credSaving ? (\n <>\n <Loader2 className=\"size-3.5 animate-spin\" />\n <span className=\"ml-1.5\">{t.savingCredentials}</span>\n </>\n ) : (\n <>\n <Save className=\"size-3.5\" />\n <span className=\"ml-1.5\">{t.saveCredentials}</span>\n </>\n )}\n </Button>\n </div>\n <div className=\"flex flex-col gap-4\">\n {summaries.map((p) => {\n const row = credDraft[p.id] ?? emptyImageProviderCredRow();\n const ui = p.ui;\n const extPath =\n showExtensionLinks && extensionIds.has(p.id)\n ? `/settings/ext/${encodeURIComponent(p.id)}`\n : null;\n return (\n <div\n key={p.id}\n className=\"rounded-lg border border-edge bg-surface-panel px-4 py-3 shadow-sm dark:shadow-none\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-fg\">{p.label ?? p.id}</span>\n <span className=\"text-xs text-fg-subtle\">({p.id})</span>\n {extPath ? (\n <Link\n to={extPath}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline\"\n title={t.extensionSettingsLinkTitle}\n >\n <ExternalLink className=\"size-3\" />\n {t.openExtensionSettings}\n </Link>\n ) : null}\n </div>\n {p.configured ? (\n <span className=\"rounded-full bg-accent-soft px-2 py-0.5 text-xs font-medium text-accent-fg\">\n {t.configured}\n </span>\n ) : (\n <span className=\"rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300\">\n {t.missingKey}\n </span>\n )}\n </div>\n {p.defaultModel ? (\n <p className=\"mt-1 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.defaultModel}:</span> {p.id}/{p.defaultModel}\n </p>\n ) : null}\n {p.models.length > 0 ? (\n <p className=\"mt-0.5 text-xs text-fg-subtle\">\n <span className=\"text-fg-muted\">{t.modelsLabel}:</span>{' '}\n {p.models.map((mm) => `${p.id}/${mm}`).join(', ')}\n </p>\n ) : null}\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <ImageProviderApiKeyField\n providerId={p.id}\n value={row.apiKey}\n onChange={(next) => updateCredRow(p.id, { apiKey: next })}\n apiKeyLinks={getOrderedApiKeyLinks(p.id, language)}\n apiKeyLinkLabels={apiKeyLinkLabels}\n labels={{\n apiKeyLabel: t.apiKeyLabel,\n optionalPlaceholder: t.optionalPlaceholder,\n maskedHelp: t.apiKeyMaskedHelp,\n copy: t.apiKeyCopy,\n copied: t.apiKeyCopied,\n show: t.apiKeyShow,\n hide: t.apiKeyHide,\n notInConfigFile: t.apiKeyNotInConfigFile,\n loadFailed: t.apiKeyRevealFailed,\n }}\n />\n\n {ui?.regions?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-region-preset-${p.id}`}>\n {t.regionLabel}\n </label>\n <select\n id={`img-cred-region-preset-${p.id}`}\n className={selectClass()}\n value={dashscopeSelectValue(row, ui.regions)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { region: '', imageBaseUrl: '' });\n return;\n }\n const opt = ui.regions!.find((x) => x.value === v);\n if (opt) {\n updateCredRow(p.id, { region: opt.value, imageBaseUrl: opt.imageBaseUrl });\n }\n }}\n >\n <option value=\"\">{t.regionPresetDefault}</option>\n {ui.regions.map((r) => (\n <option key={r.value} value={r.value}>\n {translateDashscopeRegion(t, r.value, r.label)}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.regionPresetCustom}</option>\n </select>\n {dashscopeSelectValue(row, ui.regions) === CUSTOM_SENTINEL ? (\n <div className=\"mt-2 grid gap-2 sm:grid-cols-2\">\n <input\n type=\"text\"\n className={inputClass()}\n value={row.region}\n placeholder=\"region\"\n onChange={(e) => updateCredRow(p.id, { region: e.target.value })}\n />\n <input\n type=\"url\"\n className={inputClass()}\n value={row.imageBaseUrl}\n placeholder={t.imageBaseUrlLabel}\n onChange={(e) => updateCredRow(p.id, { imageBaseUrl: e.target.value })}\n />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {ui?.baseUrlPresets?.length ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-base-preset-${p.id}`}>\n {baseUrlPresetBlockTitle(t, ui.baseUrlPresetKind)}\n </label>\n {baseUrlPresetBlockHint(t, ui.baseUrlPresetKind) ? (\n <p className=\"text-[11px] text-fg-subtle\">{baseUrlPresetBlockHint(t, ui.baseUrlPresetKind)}</p>\n ) : null}\n <select\n id={`img-cred-base-preset-${p.id}`}\n className={selectClass()}\n value={baseUrlSelectValue(row, ui.baseUrlPresets)}\n onChange={(e) => {\n const v = e.target.value;\n if (v === '') {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n if (v === CUSTOM_SENTINEL) {\n updateCredRow(p.id, { baseUrl: '' });\n return;\n }\n updateCredRow(p.id, { baseUrl: v.replace(/\\/+$/, '') });\n }}\n >\n <option value=\"\">{t.baseUrlPresetDefault}</option>\n {ui.baseUrlPresets.map((b) => (\n <option key={b.value} value={b.value}>\n {b.label}\n </option>\n ))}\n <option value={CUSTOM_SENTINEL}>{t.baseUrlPresetCustom}</option>\n </select>\n {baseUrlSelectValue(row, ui.baseUrlPresets) === CUSTOM_SENTINEL ? (\n <input\n type=\"url\"\n className={cn(inputClass(), 'mt-2')}\n value={row.baseUrl}\n placeholder=\"https://…\"\n onChange={(e) => updateCredRow(p.id, { baseUrl: e.target.value })}\n />\n ) : null}\n </div>\n ) : null}\n\n {ui?.regions?.length && dashscopeSelectValue(row, ui.regions) !== CUSTOM_SENTINEL ? (\n <div className=\"flex min-w-0 flex-col gap-1 sm:col-span-2\">\n <label className=\"text-xs font-medium text-fg-muted\" htmlFor={`img-cred-imgbase-ro-${p.id}`}>\n {t.imageBaseUrlLabel}\n </label>\n <input\n id={`img-cred-imgbase-ro-${p.id}`}\n type=\"url\"\n readOnly\n className={cn(inputClass(), 'cursor-not-allowed opacity-90')}\n value={row.imageBaseUrl}\n title={t.imageBaseUrlPresetHint}\n />\n <p className=\"text-[11px] text-fg-subtle\">{t.imageBaseUrlPresetHint}</p>\n </div>\n ) : null}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport { mutate } from 'swr';\n\nimport { useGatewayConfigSwr } from '@/features/gateway/gateway-config-swr';\nimport {\n buildImageProvidersConfigPatch,\n emptyImageProviderCredRow,\n imageProviderCredRowsFromConfigRoot,\n patchImageProvidersConfig,\n type ImageProviderCredRow,\n} from '@/features/settings/image-providers-config-api';\nimport { IMAGE_PROVIDERS_SWR_KEY } from '@/features/settings/image-providers-swr-key';\nimport { apiUrl } from '@/lib/url';\nimport { useGatewayStore } from '@/stores/gateway-store';\n\nexport type ImageProviderUiRegionOption = {\n value: string;\n label: string;\n imageBaseUrl: string;\n};\n\nexport type ImageProviderUiBaseUrlPreset = {\n value: string;\n label: string;\n};\n\nexport type ImageProviderUiMetadata = {\n regions?: ImageProviderUiRegionOption[];\n baseUrlPresets?: ImageProviderUiBaseUrlPreset[];\n baseUrlPresetKind?: 'fal' | 'minimax' | 'google' | 'openai';\n};\n\nexport type ImageGenProviderCredentialSummary = {\n id: string;\n label?: string;\n defaultModel?: string;\n models: string[];\n configured?: boolean;\n ui?: ImageProviderUiMetadata;\n};\n\nexport function useImageProviderCredentials(summaries: ImageGenProviderCredentialSummary[]) {\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const gwSwr = useGatewayConfigSwr(hasToken);\n const gwCfg = gwSwr.data;\n\n const ids = useMemo(() => summaries.map((s) => s.id), [summaries]);\n\n const [credDraft, setCredDraft] = useState<Record<string, ImageProviderCredRow>>({});\n const [credBaseline, setCredBaseline] = useState<Record<string, ImageProviderCredRow>>({});\n const [credSaving, setCredSaving] = useState(false);\n const [credError, setCredError] = useState<string | null>(null);\n const [credSavedFlash, setCredSavedFlash] = useState(false);\n const [credNoopFlash, setCredNoopFlash] = useState(false);\n\n const credRowsFromServer = useMemo(\n () => imageProviderCredRowsFromConfigRoot(gwCfg?.payload?.config, ids),\n [gwCfg?.payload?.config, ids],\n );\n\n const credDirty = useMemo(\n () => JSON.stringify(credDraft) !== JSON.stringify(credBaseline),\n [credDraft, credBaseline],\n );\n\n useEffect(() => {\n if (!credDirty) {\n setCredDraft(structuredClone(credRowsFromServer));\n setCredBaseline(structuredClone(credRowsFromServer));\n }\n }, [credRowsFromServer, credDirty]);\n\n const updateCredRow = useCallback((id: string, patch: Partial<ImageProviderCredRow>) => {\n setCredDraft((prev) => {\n const base = prev[id] ?? emptyImageProviderCredRow();\n return { ...prev, [id]: { ...base, ...patch } };\n });\n }, []);\n\n const onDiscardCredentials = useCallback(() => {\n setCredDraft(structuredClone(credBaseline));\n setCredError(null);\n setCredSavedFlash(false);\n setCredNoopFlash(false);\n }, [credBaseline]);\n\n const saveCredentials = useCallback(\n async (errorFallback: string) => {\n const patch = buildImageProvidersConfigPatch(ids, credDraft, credBaseline);\n if (Object.keys(patch).length === 0) {\n setCredNoopFlash(true);\n window.setTimeout(() => setCredNoopFlash(false), 2200);\n return;\n }\n setCredSaving(true);\n setCredError(null);\n setCredSavedFlash(false);\n try {\n await patchImageProvidersConfig(patch);\n const updated = await gwSwr.mutate?.();\n void mutate(apiUrl(IMAGE_PROVIDERS_SWR_KEY));\n const nextRows = imageProviderCredRowsFromConfigRoot(updated?.payload?.config, ids);\n setCredDraft(structuredClone(nextRows));\n setCredBaseline(structuredClone(nextRows));\n setCredSavedFlash(true);\n window.setTimeout(() => setCredSavedFlash(false), 2000);\n } catch (e) {\n setCredError(e instanceof Error ? e.message : errorFallback);\n } finally {\n setCredSaving(false);\n }\n },\n [ids, credDraft, credBaseline, gwSwr],\n );\n\n return {\n gwSwr,\n credDraft,\n credBaseline,\n credDirty,\n credSaving,\n credError,\n credSavedFlash,\n credNoopFlash,\n updateCredRow,\n onDiscardCredentials,\n saveCredentials,\n };\n}\n"],"mappings":"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"}
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
10
10
|
<link rel="apple-touch-icon" href="/logo.svg" />
|
|
11
11
|
<title>xopc</title>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-DAglRwh4.js"></script>
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-DWdDZTNf.js">
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-CXAvob9m.js">
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-vzx_MT5D.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/vendor-swr-CQnUjdeQ.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/assets/attachment-utils-core-Dt6UxMPV.js">
|
|
18
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
18
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DHj3Cf9B.css">
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
21
21
|
<div id="root"></div>
|
package/dist/package.js
CHANGED
|
@@ -9,7 +9,7 @@ import { ensureFrpcBinary } from "../../tunnel/frpc-binary.js";
|
|
|
9
9
|
import { loadTunnelState } from "../../tunnel/tunnel-state.js";
|
|
10
10
|
import { getTunnelService } from "../../tunnel/tunnel-service.js";
|
|
11
11
|
import { resolveFrpSubdomainHost, resolveTunnelE2eConfig } from "../../tunnel/tunnel-e2e-config.js";
|
|
12
|
-
import { applyTunnelConsentToConfig, setTunnelEnabledInConfig } from "../../tunnel/tunnel-config.js";
|
|
12
|
+
import { applyTunnelConsentToConfig, mergeTunnelConfigPatch, setTunnelEnabledInConfig } from "../../tunnel/tunnel-config.js";
|
|
13
13
|
import "../../tunnel/index.js";
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
//#region src/cli/commands/tunnel.ts
|
|
@@ -33,13 +33,14 @@ function resolveGatewayToken(config) {
|
|
|
33
33
|
}
|
|
34
34
|
function configureTunnel(ctx) {
|
|
35
35
|
const config = loadConfig(ctx.configPath);
|
|
36
|
-
const { host } = resolveGatewayPortHost(config);
|
|
36
|
+
const { port, host } = resolveGatewayPortHost(config);
|
|
37
|
+
const brokerUrl = resolveTunnelBrokerUrl(config.tunnel?.brokerUrl);
|
|
37
38
|
getTunnelService().configure({
|
|
38
|
-
brokerUrl
|
|
39
|
-
registrationSecret: resolveTunnelRegistrationSecret(),
|
|
39
|
+
brokerUrl,
|
|
40
|
+
registrationSecret: resolveTunnelRegistrationSecret(process.env, brokerUrl, config.tunnel?.registrationSecret),
|
|
40
41
|
autoStart: config.tunnel?.autoStart ?? false,
|
|
41
42
|
gatewayHost: host,
|
|
42
|
-
e2e: resolveTunnelE2eConfig(config.tunnel),
|
|
43
|
+
e2e: resolveTunnelE2eConfig(config.tunnel, port),
|
|
43
44
|
frpSubdomainHost: resolveFrpSubdomainHost(resolveTunnelBrokerUrl(config.tunnel?.brokerUrl))
|
|
44
45
|
});
|
|
45
46
|
}
|
|
@@ -65,6 +66,33 @@ async function ensureCliTunnelConsent(ctx, opts) {
|
|
|
65
66
|
applyTunnelConsentToConfig(config);
|
|
66
67
|
await saveConfig(config, ctx.configPath);
|
|
67
68
|
}
|
|
69
|
+
async function readTunnelRegistrationSecretFromCli(opts) {
|
|
70
|
+
const fromArg = opts.secretArg?.trim();
|
|
71
|
+
if (fromArg) return fromArg;
|
|
72
|
+
if (opts.stdin) {
|
|
73
|
+
const chunks = [];
|
|
74
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
75
|
+
const value = Buffer.concat(chunks).toString("utf8").trim();
|
|
76
|
+
if (!value) throw new Error("Empty registration secret from stdin.");
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
if (isInteractive()) {
|
|
80
|
+
const { password } = await import("@inquirer/prompts");
|
|
81
|
+
const value = await password({
|
|
82
|
+
message: "Tunnel broker registration secret",
|
|
83
|
+
mask: "*"
|
|
84
|
+
});
|
|
85
|
+
if (!value?.trim()) throw new Error("Registration secret is required.");
|
|
86
|
+
return value.trim();
|
|
87
|
+
}
|
|
88
|
+
throw new Error("Provide the secret as an argument, pass --stdin, or run in an interactive terminal.");
|
|
89
|
+
}
|
|
90
|
+
async function saveTunnelRegistrationSecret(ctx, secret) {
|
|
91
|
+
const config = loadConfig(ctx.configPath);
|
|
92
|
+
const result = mergeTunnelConfigPatch(config, { registrationSecret: secret });
|
|
93
|
+
if (result.ok === false) throw new Error(result.message);
|
|
94
|
+
await saveConfig(config, ctx.configPath);
|
|
95
|
+
}
|
|
68
96
|
function createTunnelCommand(ctx) {
|
|
69
97
|
const cmd = new Command("tunnel").description("Manage FRP remote access tunnel").addHelpText("after", formatExamples([
|
|
70
98
|
"xopc tunnel start",
|
|
@@ -73,11 +101,22 @@ function createTunnelCommand(ctx) {
|
|
|
73
101
|
"xopc tunnel status",
|
|
74
102
|
"xopc tunnel qr",
|
|
75
103
|
"xopc tunnel consent",
|
|
76
|
-
"xopc tunnel prefetch"
|
|
104
|
+
"xopc tunnel prefetch",
|
|
105
|
+
"xopc tunnel secret set",
|
|
106
|
+
"xopc tunnel secret set --stdin"
|
|
77
107
|
]));
|
|
78
108
|
cmd.command("prefetch").description("Download frpc to the state bin directory without starting the tunnel").action(async () => {
|
|
79
109
|
try {
|
|
80
|
-
const path = await ensureFrpcBinary()
|
|
110
|
+
const path = await ensureFrpcBinary({ onProgress: (progress) => {
|
|
111
|
+
if (!process.stderr.isTTY) return;
|
|
112
|
+
if (progress.phase === "extracting") {
|
|
113
|
+
process.stderr.write("\rExtracting frpc… \n");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (progress.percent != null) process.stderr.write(`\rDownloading frpc… ${progress.percent}%`);
|
|
117
|
+
else if (progress.bytesReceived != null) process.stderr.write(`\rDownloading frpc… ${progress.bytesReceived} bytes`);
|
|
118
|
+
} });
|
|
119
|
+
if (process.stderr.isTTY) process.stderr.write("\n");
|
|
81
120
|
console.log(`frpc ready at ${path}`);
|
|
82
121
|
} catch (err) {
|
|
83
122
|
const em = err instanceof Error ? err.message : String(err);
|
|
@@ -92,6 +131,24 @@ function createTunnelCommand(ctx) {
|
|
|
92
131
|
});
|
|
93
132
|
console.log("Tunnel security consent recorded.");
|
|
94
133
|
});
|
|
134
|
+
cmd.command("secret").description("Manage tunnel broker registration secret in config").command("set").description("Save tunnel.registrationSecret to xopc.json (env XOPC_TUNNEL_REGISTRATION_SECRET overrides at runtime)").argument("[secret]", "Registration secret (prompts securely when omitted in a TTY)").option("--stdin", "Read secret from stdin (single line, trimmed)").action(async (secretArg, opts) => {
|
|
135
|
+
try {
|
|
136
|
+
await saveTunnelRegistrationSecret(ctx, await readTunnelRegistrationSecretFromCli({
|
|
137
|
+
secretArg,
|
|
138
|
+
stdin: opts.stdin
|
|
139
|
+
}));
|
|
140
|
+
if (process.env.XOPC_TUNNEL_REGISTRATION_SECRET?.trim()) console.log("Note: XOPC_TUNNEL_REGISTRATION_SECRET is set in the environment and overrides the saved config at runtime.");
|
|
141
|
+
console.log(`Saved tunnel registration secret to ${ctx.configPath}.`);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
const em = err instanceof Error ? err.message : String(err);
|
|
144
|
+
log.error({
|
|
145
|
+
err,
|
|
146
|
+
phase: "tunnel_secret_set"
|
|
147
|
+
}, `Tunnel secret set failed: ${em}`);
|
|
148
|
+
console.error(em);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
95
152
|
cmd.command("start").description("Register tunnel, start frpc, print connection info").option("--yes", "Skip interactive consent prompt when consent is already recorded").option("--accept-risk", "Record security consent and start (non-interactive; read risks in docs/tunnel-security.md first)").action(async (opts) => {
|
|
96
153
|
configureTunnel(ctx);
|
|
97
154
|
await ensureCliTunnelConsent(ctx, {
|
|
@@ -174,7 +231,11 @@ register({
|
|
|
174
231
|
factory: createTunnelCommand,
|
|
175
232
|
metadata: {
|
|
176
233
|
category: "runtime",
|
|
177
|
-
examples: [
|
|
234
|
+
examples: [
|
|
235
|
+
"xopc tunnel start",
|
|
236
|
+
"xopc tunnel status",
|
|
237
|
+
"xopc tunnel secret set"
|
|
238
|
+
]
|
|
178
239
|
}
|
|
179
240
|
});
|
|
180
241
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel.js","names":[],"sources":["../../../../src/cli/commands/tunnel.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { loadConfig, saveConfig } from '../../config/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n assertTunnelMayStart,\n hasValidTunnelConsent,\n TUNNEL_RISK_SUMMARY_LINES,\n TunnelConsentError,\n} from '../../tunnel/consent.js';\nimport { resolveTunnelBrokerUrl, resolveTunnelRegistrationSecret } from '../../tunnel/env.js';\nimport { ensureFrpcBinary, getTunnelService } from '../../tunnel/index.js';\nimport { resolveFrpSubdomainHost, resolveTunnelE2eConfig } from '../../tunnel/tunnel-e2e-config.js';\nimport { applyTunnelConsentToConfig, setTunnelEnabledInConfig } from '../../tunnel/tunnel-config.js';\nimport { loadTunnelState } from '../../tunnel/tunnel-state.js';\nimport { formatExamples, register, type CLIContext } from '../registry.js';\n\nconst log = createLogger('TunnelCommand');\n\nfunction isInteractive(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n\nfunction resolveGatewayPortHost(config: ReturnType<typeof loadConfig>): { port: number; host: string } {\n return {\n port: config.gateway.port ?? 18790,\n host: config.gateway.host ?? '127.0.0.1',\n };\n}\n\nfunction resolveGatewayToken(config: ReturnType<typeof loadConfig>): string {\n const fromEnv = process.env.XOPC_GATEWAY_TOKEN?.trim();\n if (fromEnv) return fromEnv;\n const token = config.gateway.auth?.token?.trim();\n if (token) return token;\n throw new Error('Gateway token not configured. Set gateway.auth.token or XOPC_GATEWAY_TOKEN.');\n}\n\nfunction configureTunnel(ctx: CLIContext): void {\n const config = loadConfig(ctx.configPath);\n const { host } = resolveGatewayPortHost(config);\n getTunnelService().configure({\n brokerUrl: resolveTunnelBrokerUrl(config.tunnel?.brokerUrl),\n registrationSecret: resolveTunnelRegistrationSecret(),\n autoStart: config.tunnel?.autoStart ?? false,\n gatewayHost: host,\n e2e: resolveTunnelE2eConfig(config.tunnel),\n frpSubdomainHost: resolveFrpSubdomainHost(resolveTunnelBrokerUrl(config.tunnel?.brokerUrl)),\n });\n}\n\nasync function ensureCliTunnelConsent(\n ctx: CLIContext,\n opts: { acceptRisk?: boolean; yes?: boolean },\n): Promise<void> {\n const config = loadConfig(ctx.configPath);\n if (hasValidTunnelConsent(config)) return;\n\n if (opts.acceptRisk) {\n applyTunnelConsentToConfig(config);\n await saveConfig(config, ctx.configPath);\n return;\n }\n\n if (opts.yes) {\n throw new TunnelConsentError(\n 'Security consent not recorded. Run without --yes and confirm, or pass --accept-risk after reading the risks.',\n );\n }\n\n if (!isInteractive()) {\n throw new TunnelConsentError(\n 'Security consent required. Run interactively, use --accept-risk, or accept in gateway settings.',\n );\n }\n\n const { confirm } = await import('@inquirer/prompts');\n console.log('');\n console.log('⚠️ Remote access security notice');\n for (const line of TUNNEL_RISK_SUMMARY_LINES) {\n console.log(` • ${line}`);\n }\n console.log('');\n const accepted = await confirm({\n message: 'I understand these risks and want to enable remote access',\n default: false,\n });\n if (!accepted) {\n throw new TunnelConsentError('Remote access not started (consent declined).');\n }\n applyTunnelConsentToConfig(config);\n await saveConfig(config, ctx.configPath);\n}\n\nfunction createTunnelCommand(ctx: CLIContext): Command {\n const cmd = new Command('tunnel')\n .description('Manage FRP remote access tunnel')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc tunnel start',\n 'xopc tunnel start --accept-risk',\n 'xopc tunnel stop',\n 'xopc tunnel status',\n 'xopc tunnel qr',\n 'xopc tunnel consent',\n 'xopc tunnel prefetch',\n ]),\n );\n\n cmd\n .command('prefetch')\n .description('Download frpc to the state bin directory without starting the tunnel')\n .action(async () => {\n try {\n const path = await ensureFrpcBinary();\n console.log(`frpc ready at ${path}`);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err }, `frpc prefetch failed: ${em}`);\n process.exit(1);\n }\n });\n\n cmd\n .command('consent')\n .description('Record acceptance of the remote access security notice')\n .option('--accept-risk', 'Non-interactive: record consent without prompts')\n .action(async (opts: { acceptRisk?: boolean }) => {\n await ensureCliTunnelConsent(ctx, { acceptRisk: opts.acceptRisk, yes: false });\n console.log('Tunnel security consent recorded.');\n });\n\n cmd\n .command('start')\n .description('Register tunnel, start frpc, print connection info')\n .option('--yes', 'Skip interactive consent prompt when consent is already recorded')\n .option(\n '--accept-risk',\n 'Record security consent and start (non-interactive; read risks in docs/tunnel-security.md first)',\n )\n .action(async (opts: { yes?: boolean; acceptRisk?: boolean }) => {\n configureTunnel(ctx);\n await ensureCliTunnelConsent(ctx, { acceptRisk: opts.acceptRisk, yes: opts.yes });\n\n const config = loadConfig(ctx.configPath);\n assertTunnelMayStart(config);\n\n const { port, host } = resolveGatewayPortHost(config);\n const token = resolveGatewayToken(config);\n const tunnel = getTunnelService();\n\n console.log('🚀 Starting tunnel...');\n try {\n const qr = await tunnel.start(port, token);\n setTunnelEnabledInConfig(config, true);\n await saveConfig(config, ctx.configPath);\n\n const status = tunnel.getStatus();\n console.log('');\n console.log('✅ Tunnel is active');\n if (status.publicUrl) console.log(` URL: ${status.publicUrl}`);\n if (qr.lanUrl) console.log(` LAN: ${qr.lanUrl}`);\n console.log('');\n console.log('📱 Mobile connect QR payload:');\n console.log(qr.qrPayload);\n console.log('');\n console.log(\n `💡 Gateway must be running at ${host}:${port}. Keep frpc alive or use gateway / Web console.`,\n );\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err }, `Tunnel start failed: ${em}`);\n process.exit(1);\n }\n });\n\n cmd\n .command('stop')\n .description('Stop frpc (keeps broker registration for stable subdomain)')\n .option(\n '--release',\n 'Deregister from broker and clear saved subdomain (next start gets a new public URL)',\n )\n .option('--yes', 'Skip confirmation when using --release')\n .action(async (opts: { release?: boolean; yes?: boolean }) => {\n configureTunnel(ctx);\n if (opts.release) {\n if (!opts.yes && isInteractive()) {\n const { confirm } = await import('@inquirer/prompts');\n const ok = await confirm({\n message:\n 'Release will revoke the public URL and subdomain on the broker. Continue?',\n default: false,\n });\n if (!ok) {\n console.log('Cancelled.');\n return;\n }\n }\n }\n const { released } = await getTunnelService().stop({ release: opts.release });\n const config = loadConfig(ctx.configPath);\n setTunnelEnabledInConfig(config, false);\n await saveConfig(config, ctx.configPath);\n console.log(released ? 'Tunnel stopped and broker registration released.' : 'Tunnel stopped.');\n });\n\n cmd\n .command('status')\n .description('Show tunnel status')\n .action(() => {\n configureTunnel(ctx);\n const config = loadConfig(ctx.configPath);\n const status = getTunnelService().getStatus();\n const persisted = loadTunnelState();\n console.log(\n JSON.stringify(\n {\n ...status,\n persistedSubdomain: persisted?.subdomain ?? null,\n consentValid: hasValidTunnelConsent(config),\n },\n null,\n 2,\n ),\n );\n });\n\n cmd\n .command('qr')\n .description('Print mobile connect QR payload (tunnel must be active)')\n .action(() => {\n configureTunnel(ctx);\n const config = loadConfig(ctx.configPath);\n const { port, host } = resolveGatewayPortHost(config);\n const qr = getTunnelService().buildQr(port, host);\n if (!qr.qrPayload) {\n console.error('No active tunnel. Run: xopc tunnel start');\n process.exit(1);\n }\n console.log(qr.qrPayload);\n });\n\n return cmd;\n}\n\nregister({\n id: 'tunnel',\n name: 'tunnel',\n description: 'FRP remote access tunnel',\n factory: createTunnelCommand,\n metadata: {\n category: 'runtime',\n examples: ['xopc tunnel start', 'xopc tunnel status'],\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;aAGqD;AAcrD,MAAM,MAAM,aAAa,gBAAgB;AAEzC,SAAS,gBAAyB;AAChC,QAAO,QAAQ,QAAQ,MAAM,SAAS,QAAQ,OAAO,MAAM;;AAG7D,SAAS,uBAAuB,QAAuE;AACrG,QAAO;EACL,MAAM,OAAO,QAAQ,QAAQ;EAC7B,MAAM,OAAO,QAAQ,QAAQ;EAC9B;;AAGH,SAAS,oBAAoB,QAA+C;CAC1E,MAAM,UAAU,QAAQ,IAAI,oBAAoB,MAAM;AACtD,KAAI,QAAS,QAAO;CACpB,MAAM,QAAQ,OAAO,QAAQ,MAAM,OAAO,MAAM;AAChD,KAAI,MAAO,QAAO;AAClB,OAAM,IAAI,MAAM,8EAA8E;;AAGhG,SAAS,gBAAgB,KAAuB;CAC9C,MAAM,SAAS,WAAW,IAAI,WAAW;CACzC,MAAM,EAAE,SAAS,uBAAuB,OAAO;AAC/C,mBAAkB,CAAC,UAAU;EAC3B,WAAW,uBAAuB,OAAO,QAAQ,UAAU;EAC3D,oBAAoB,iCAAiC;EACrD,WAAW,OAAO,QAAQ,aAAa;EACvC,aAAa;EACb,KAAK,uBAAuB,OAAO,OAAO;EAC1C,kBAAkB,wBAAwB,uBAAuB,OAAO,QAAQ,UAAU,CAAC;EAC5F,CAAC;;AAGJ,eAAe,uBACb,KACA,MACe;CACf,MAAM,SAAS,WAAW,IAAI,WAAW;AACzC,KAAI,sBAAsB,OAAO,CAAE;AAEnC,KAAI,KAAK,YAAY;AACnB,6BAA2B,OAAO;AAClC,QAAM,WAAW,QAAQ,IAAI,WAAW;AACxC;;AAGF,KAAI,KAAK,IACP,OAAM,IAAI,mBACR,+GACD;AAGH,KAAI,CAAC,eAAe,CAClB,OAAM,IAAI,mBACR,kGACD;CAGH,MAAM,EAAE,YAAY,MAAM,OAAO;AACjC,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,oCAAoC;AAChD,MAAK,MAAM,QAAQ,0BACjB,SAAQ,IAAI,QAAQ,OAAO;AAE7B,SAAQ,IAAI,GAAG;AAKf,KAAI,CAAC,MAJkB,QAAQ;EAC7B,SAAS;EACT,SAAS;EACV,CAAC,CAEA,OAAM,IAAI,mBAAmB,gDAAgD;AAE/E,4BAA2B,OAAO;AAClC,OAAM,WAAW,QAAQ,IAAI,WAAW;;AAG1C,SAAS,oBAAoB,KAA0B;CACrD,MAAM,MAAM,IAAI,QAAQ,SAAS,CAC9B,YAAY,kCAAkC,CAC9C,YACC,SACA,eAAe;EACb;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,CACH;AAEH,KACG,QAAQ,WAAW,CACnB,YAAY,uEAAuE,CACnF,OAAO,YAAY;AAClB,MAAI;GACF,MAAM,OAAO,MAAM,kBAAkB;AACrC,WAAQ,IAAI,iBAAiB,OAAO;WAC7B,KAAK;GACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAI,MAAM,EAAE,KAAK,EAAE,yBAAyB,KAAK;AACjD,WAAQ,KAAK,EAAE;;GAEjB;AAEJ,KACG,QAAQ,UAAU,CAClB,YAAY,yDAAyD,CACrE,OAAO,iBAAiB,kDAAkD,CAC1E,OAAO,OAAO,SAAmC;AAChD,QAAM,uBAAuB,KAAK;GAAE,YAAY,KAAK;GAAY,KAAK;GAAO,CAAC;AAC9E,UAAQ,IAAI,oCAAoC;GAChD;AAEJ,KACG,QAAQ,QAAQ,CAChB,YAAY,qDAAqD,CACjE,OAAO,SAAS,mEAAmE,CACnF,OACC,iBACA,mGACD,CACA,OAAO,OAAO,SAAkD;AAC/D,kBAAgB,IAAI;AACpB,QAAM,uBAAuB,KAAK;GAAE,YAAY,KAAK;GAAY,KAAK,KAAK;GAAK,CAAC;EAEjF,MAAM,SAAS,WAAW,IAAI,WAAW;AACzC,uBAAqB,OAAO;EAE5B,MAAM,EAAE,MAAM,SAAS,uBAAuB,OAAO;EACrD,MAAM,QAAQ,oBAAoB,OAAO;EACzC,MAAM,SAAS,kBAAkB;AAEjC,UAAQ,IAAI,wBAAwB;AACpC,MAAI;GACF,MAAM,KAAK,MAAM,OAAO,MAAM,MAAM,MAAM;AAC1C,4BAAyB,QAAQ,KAAK;AACtC,SAAM,WAAW,QAAQ,IAAI,WAAW;GAExC,MAAM,SAAS,OAAO,WAAW;AACjC,WAAQ,IAAI,GAAG;AACf,WAAQ,IAAI,qBAAqB;AACjC,OAAI,OAAO,UAAW,SAAQ,IAAI,WAAW,OAAO,YAAY;AAChE,OAAI,GAAG,OAAQ,SAAQ,IAAI,WAAW,GAAG,SAAS;AAClD,WAAQ,IAAI,GAAG;AACf,WAAQ,IAAI,gCAAgC;AAC5C,WAAQ,IAAI,GAAG,UAAU;AACzB,WAAQ,IAAI,GAAG;AACf,WAAQ,IACN,iCAAiC,KAAK,GAAG,KAAK,iDAC/C;WACM,KAAK;GACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAI,MAAM,EAAE,KAAK,EAAE,wBAAwB,KAAK;AAChD,WAAQ,KAAK,EAAE;;GAEjB;AAEJ,KACG,QAAQ,OAAO,CACf,YAAY,6DAA6D,CACzE,OACC,aACA,sFACD,CACA,OAAO,SAAS,yCAAyC,CACzD,OAAO,OAAO,SAA+C;AAC5D,kBAAgB,IAAI;AACpB,MAAI,KAAK;OACH,CAAC,KAAK,OAAO,eAAe,EAAE;IAChC,MAAM,EAAE,YAAY,MAAM,OAAO;AAMjC,QAAI,CAAC,MALY,QAAQ;KACvB,SACE;KACF,SAAS;KACV,CAAC,EACO;AACP,aAAQ,IAAI,aAAa;AACzB;;;;EAIN,MAAM,EAAE,aAAa,MAAM,kBAAkB,CAAC,KAAK,EAAE,SAAS,KAAK,SAAS,CAAC;EAC7E,MAAM,SAAS,WAAW,IAAI,WAAW;AACzC,2BAAyB,QAAQ,MAAM;AACvC,QAAM,WAAW,QAAQ,IAAI,WAAW;AACxC,UAAQ,IAAI,WAAW,qDAAqD,kBAAkB;GAC9F;AAEJ,KACG,QAAQ,SAAS,CACjB,YAAY,qBAAqB,CACjC,aAAa;AACZ,kBAAgB,IAAI;EACpB,MAAM,SAAS,WAAW,IAAI,WAAW;EACzC,MAAM,SAAS,kBAAkB,CAAC,WAAW;EAC7C,MAAM,YAAY,iBAAiB;AACnC,UAAQ,IACN,KAAK,UACH;GACE,GAAG;GACH,oBAAoB,WAAW,aAAa;GAC5C,cAAc,sBAAsB,OAAO;GAC5C,EACD,MACA,EACD,CACF;GACD;AAEJ,KACG,QAAQ,KAAK,CACb,YAAY,0DAA0D,CACtE,aAAa;AACZ,kBAAgB,IAAI;EAEpB,MAAM,EAAE,MAAM,SAAS,uBADR,WAAW,IAAI,WACsB,CAAC;EACrD,MAAM,KAAK,kBAAkB,CAAC,QAAQ,MAAM,KAAK;AACjD,MAAI,CAAC,GAAG,WAAW;AACjB,WAAQ,MAAM,2CAA2C;AACzD,WAAQ,KAAK,EAAE;;AAEjB,UAAQ,IAAI,GAAG,UAAU;GACzB;AAEJ,QAAO;;AAGT,SAAS;CACP,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS;CACT,UAAU;EACR,UAAU;EACV,UAAU,CAAC,qBAAqB,qBAAqB;EACtD;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"tunnel.js","names":[],"sources":["../../../../src/cli/commands/tunnel.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { loadConfig, saveConfig } from '../../config/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n assertTunnelMayStart,\n hasValidTunnelConsent,\n TUNNEL_RISK_SUMMARY_LINES,\n TunnelConsentError,\n} from '../../tunnel/consent.js';\nimport { resolveTunnelBrokerUrl, resolveTunnelRegistrationSecret } from '../../tunnel/env.js';\nimport { ensureFrpcBinary, getTunnelService } from '../../tunnel/index.js';\nimport { resolveFrpSubdomainHost, resolveTunnelE2eConfig } from '../../tunnel/tunnel-e2e-config.js';\nimport { applyTunnelConsentToConfig, mergeTunnelConfigPatch, setTunnelEnabledInConfig } from '../../tunnel/tunnel-config.js';\nimport { loadTunnelState } from '../../tunnel/tunnel-state.js';\nimport { formatExamples, register, type CLIContext } from '../registry.js';\n\nconst log = createLogger('TunnelCommand');\n\nfunction isInteractive(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n\nfunction resolveGatewayPortHost(config: ReturnType<typeof loadConfig>): { port: number; host: string } {\n return {\n port: config.gateway.port ?? 18790,\n host: config.gateway.host ?? '127.0.0.1',\n };\n}\n\nfunction resolveGatewayToken(config: ReturnType<typeof loadConfig>): string {\n const fromEnv = process.env.XOPC_GATEWAY_TOKEN?.trim();\n if (fromEnv) return fromEnv;\n const token = config.gateway.auth?.token?.trim();\n if (token) return token;\n throw new Error('Gateway token not configured. Set gateway.auth.token or XOPC_GATEWAY_TOKEN.');\n}\n\nfunction configureTunnel(ctx: CLIContext): void {\n const config = loadConfig(ctx.configPath);\n const { port, host } = resolveGatewayPortHost(config);\n const brokerUrl = resolveTunnelBrokerUrl(config.tunnel?.brokerUrl);\n getTunnelService().configure({\n brokerUrl,\n registrationSecret: resolveTunnelRegistrationSecret(\n process.env,\n brokerUrl,\n config.tunnel?.registrationSecret,\n ),\n autoStart: config.tunnel?.autoStart ?? false,\n gatewayHost: host,\n e2e: resolveTunnelE2eConfig(config.tunnel, port),\n frpSubdomainHost: resolveFrpSubdomainHost(resolveTunnelBrokerUrl(config.tunnel?.brokerUrl)),\n });\n}\n\nasync function ensureCliTunnelConsent(\n ctx: CLIContext,\n opts: { acceptRisk?: boolean; yes?: boolean },\n): Promise<void> {\n const config = loadConfig(ctx.configPath);\n if (hasValidTunnelConsent(config)) return;\n\n if (opts.acceptRisk) {\n applyTunnelConsentToConfig(config);\n await saveConfig(config, ctx.configPath);\n return;\n }\n\n if (opts.yes) {\n throw new TunnelConsentError(\n 'Security consent not recorded. Run without --yes and confirm, or pass --accept-risk after reading the risks.',\n );\n }\n\n if (!isInteractive()) {\n throw new TunnelConsentError(\n 'Security consent required. Run interactively, use --accept-risk, or accept in gateway settings.',\n );\n }\n\n const { confirm } = await import('@inquirer/prompts');\n console.log('');\n console.log('⚠️ Remote access security notice');\n for (const line of TUNNEL_RISK_SUMMARY_LINES) {\n console.log(` • ${line}`);\n }\n console.log('');\n const accepted = await confirm({\n message: 'I understand these risks and want to enable remote access',\n default: false,\n });\n if (!accepted) {\n throw new TunnelConsentError('Remote access not started (consent declined).');\n }\n applyTunnelConsentToConfig(config);\n await saveConfig(config, ctx.configPath);\n}\n\nasync function readTunnelRegistrationSecretFromCli(opts: {\n secretArg?: string;\n stdin?: boolean;\n}): Promise<string> {\n const fromArg = opts.secretArg?.trim();\n if (fromArg) return fromArg;\n\n if (opts.stdin) {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n const value = Buffer.concat(chunks).toString('utf8').trim();\n if (!value) {\n throw new Error('Empty registration secret from stdin.');\n }\n return value;\n }\n\n if (isInteractive()) {\n const { password } = await import('@inquirer/prompts');\n const value = await password({\n message: 'Tunnel broker registration secret',\n mask: '*',\n });\n if (!value?.trim()) {\n throw new Error('Registration secret is required.');\n }\n return value.trim();\n }\n\n throw new Error(\n 'Provide the secret as an argument, pass --stdin, or run in an interactive terminal.',\n );\n}\n\nasync function saveTunnelRegistrationSecret(ctx: CLIContext, secret: string): Promise<void> {\n const config = loadConfig(ctx.configPath);\n const result = mergeTunnelConfigPatch(config, { registrationSecret: secret });\n if (result.ok === false) {\n throw new Error(result.message);\n }\n await saveConfig(config, ctx.configPath);\n}\n\nfunction createTunnelCommand(ctx: CLIContext): Command {\n const cmd = new Command('tunnel')\n .description('Manage FRP remote access tunnel')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc tunnel start',\n 'xopc tunnel start --accept-risk',\n 'xopc tunnel stop',\n 'xopc tunnel status',\n 'xopc tunnel qr',\n 'xopc tunnel consent',\n 'xopc tunnel prefetch',\n 'xopc tunnel secret set',\n 'xopc tunnel secret set --stdin',\n ]),\n );\n\n cmd\n .command('prefetch')\n .description('Download frpc to the state bin directory without starting the tunnel')\n .action(async () => {\n try {\n const path = await ensureFrpcBinary({\n onProgress: (progress) => {\n if (!process.stderr.isTTY) return;\n if (progress.phase === 'extracting') {\n process.stderr.write('\\rExtracting frpc… \\n');\n return;\n }\n if (progress.percent != null) {\n process.stderr.write(`\\rDownloading frpc… ${progress.percent}%`);\n } else if (progress.bytesReceived != null) {\n process.stderr.write(`\\rDownloading frpc… ${progress.bytesReceived} bytes`);\n }\n },\n });\n if (process.stderr.isTTY) process.stderr.write('\\n');\n console.log(`frpc ready at ${path}`);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err }, `frpc prefetch failed: ${em}`);\n process.exit(1);\n }\n });\n\n cmd\n .command('consent')\n .description('Record acceptance of the remote access security notice')\n .option('--accept-risk', 'Non-interactive: record consent without prompts')\n .action(async (opts: { acceptRisk?: boolean }) => {\n await ensureCliTunnelConsent(ctx, { acceptRisk: opts.acceptRisk, yes: false });\n console.log('Tunnel security consent recorded.');\n });\n\n const secretCmd = cmd.command('secret').description('Manage tunnel broker registration secret in config');\n\n secretCmd\n .command('set')\n .description('Save tunnel.registrationSecret to xopc.json (env XOPC_TUNNEL_REGISTRATION_SECRET overrides at runtime)')\n .argument('[secret]', 'Registration secret (prompts securely when omitted in a TTY)')\n .option('--stdin', 'Read secret from stdin (single line, trimmed)')\n .action(async (secretArg: string | undefined, opts: { stdin?: boolean }) => {\n try {\n const secret = await readTunnelRegistrationSecretFromCli({\n secretArg,\n stdin: opts.stdin,\n });\n await saveTunnelRegistrationSecret(ctx, secret);\n if (process.env.XOPC_TUNNEL_REGISTRATION_SECRET?.trim()) {\n console.log(\n 'Note: XOPC_TUNNEL_REGISTRATION_SECRET is set in the environment and overrides the saved config at runtime.',\n );\n }\n console.log(`Saved tunnel registration secret to ${ctx.configPath}.`);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, phase: 'tunnel_secret_set' }, `Tunnel secret set failed: ${em}`);\n console.error(em);\n process.exit(1);\n }\n });\n\n cmd\n .command('start')\n .description('Register tunnel, start frpc, print connection info')\n .option('--yes', 'Skip interactive consent prompt when consent is already recorded')\n .option(\n '--accept-risk',\n 'Record security consent and start (non-interactive; read risks in docs/tunnel-security.md first)',\n )\n .action(async (opts: { yes?: boolean; acceptRisk?: boolean }) => {\n configureTunnel(ctx);\n await ensureCliTunnelConsent(ctx, { acceptRisk: opts.acceptRisk, yes: opts.yes });\n\n const config = loadConfig(ctx.configPath);\n assertTunnelMayStart(config);\n\n const { port, host } = resolveGatewayPortHost(config);\n const token = resolveGatewayToken(config);\n const tunnel = getTunnelService();\n\n console.log('🚀 Starting tunnel...');\n try {\n const qr = await tunnel.start(port, token);\n setTunnelEnabledInConfig(config, true);\n await saveConfig(config, ctx.configPath);\n\n const status = tunnel.getStatus();\n console.log('');\n console.log('✅ Tunnel is active');\n if (status.publicUrl) console.log(` URL: ${status.publicUrl}`);\n if (qr.lanUrl) console.log(` LAN: ${qr.lanUrl}`);\n console.log('');\n console.log('📱 Mobile connect QR payload:');\n console.log(qr.qrPayload);\n console.log('');\n console.log(\n `💡 Gateway must be running at ${host}:${port}. Keep frpc alive or use gateway / Web console.`,\n );\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err }, `Tunnel start failed: ${em}`);\n process.exit(1);\n }\n });\n\n cmd\n .command('stop')\n .description('Stop frpc (keeps broker registration for stable subdomain)')\n .option(\n '--release',\n 'Deregister from broker and clear saved subdomain (next start gets a new public URL)',\n )\n .option('--yes', 'Skip confirmation when using --release')\n .action(async (opts: { release?: boolean; yes?: boolean }) => {\n configureTunnel(ctx);\n if (opts.release) {\n if (!opts.yes && isInteractive()) {\n const { confirm } = await import('@inquirer/prompts');\n const ok = await confirm({\n message:\n 'Release will revoke the public URL and subdomain on the broker. Continue?',\n default: false,\n });\n if (!ok) {\n console.log('Cancelled.');\n return;\n }\n }\n }\n const { released } = await getTunnelService().stop({ release: opts.release });\n const config = loadConfig(ctx.configPath);\n setTunnelEnabledInConfig(config, false);\n await saveConfig(config, ctx.configPath);\n console.log(released ? 'Tunnel stopped and broker registration released.' : 'Tunnel stopped.');\n });\n\n cmd\n .command('status')\n .description('Show tunnel status')\n .action(() => {\n configureTunnel(ctx);\n const config = loadConfig(ctx.configPath);\n const status = getTunnelService().getStatus();\n const persisted = loadTunnelState();\n console.log(\n JSON.stringify(\n {\n ...status,\n persistedSubdomain: persisted?.subdomain ?? null,\n consentValid: hasValidTunnelConsent(config),\n },\n null,\n 2,\n ),\n );\n });\n\n cmd\n .command('qr')\n .description('Print mobile connect QR payload (tunnel must be active)')\n .action(() => {\n configureTunnel(ctx);\n const config = loadConfig(ctx.configPath);\n const { port, host } = resolveGatewayPortHost(config);\n const qr = getTunnelService().buildQr(port, host);\n if (!qr.qrPayload) {\n console.error('No active tunnel. Run: xopc tunnel start');\n process.exit(1);\n }\n console.log(qr.qrPayload);\n });\n\n return cmd;\n}\n\nregister({\n id: 'tunnel',\n name: 'tunnel',\n description: 'FRP remote access tunnel',\n factory: createTunnelCommand,\n metadata: {\n category: 'runtime',\n examples: ['xopc tunnel start', 'xopc tunnel status', 'xopc tunnel secret set'],\n },\n});\n"],"mappings":"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"}
|