ai-zero-token 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +13 -5
  3. package/README.zh-CN.md +13 -5
  4. package/admin-ui/dist/assets/InfoRow-0ULI9iI3.js +1 -0
  5. package/admin-ui/dist/assets/accounts-DymL4WIa.js +2 -0
  6. package/admin-ui/dist/assets/activity-D21-Xrc4.js +1 -0
  7. package/admin-ui/dist/assets/app-mark-nsRs4vo7.svg +8 -0
  8. package/admin-ui/dist/assets/circle-check-ZYtn9GqY.js +1 -0
  9. package/admin-ui/dist/assets/clock-3-BzDANsVk.js +1 -0
  10. package/admin-ui/dist/assets/docs-BRNWAMPw.css +1 -0
  11. package/admin-ui/dist/assets/docs-DtO-AOWU.js +1735 -0
  12. package/admin-ui/dist/assets/earth-DFdZaQIi.js +1 -0
  13. package/admin-ui/dist/assets/image-bed-yIVQ4dKs.js +1 -0
  14. package/admin-ui/dist/assets/index-By4r-wy3.css +1 -0
  15. package/admin-ui/dist/assets/index-DRe-tByu.js +10 -0
  16. package/admin-ui/dist/assets/jsx-runtime-DqpGtLhh.js +1 -0
  17. package/admin-ui/dist/assets/launch-CQXYrl-h.js +1 -0
  18. package/admin-ui/dist/assets/logs-awABDg1C.js +1 -0
  19. package/admin-ui/dist/assets/network-detect-sSrnwZqf.js +1 -0
  20. package/admin-ui/dist/assets/overview-BbSON0Jl.js +1 -0
  21. package/admin-ui/dist/assets/profiles-DMOjJORP.js +1 -0
  22. package/admin-ui/dist/assets/refresh-cw-CAAH2rqe.js +1 -0
  23. package/admin-ui/dist/assets/search-B2hz41D3.js +1 -0
  24. package/admin-ui/dist/assets/server-BrjJPb9D.js +1 -0
  25. package/admin-ui/dist/assets/settings-DvRiHS7i.js +1 -0
  26. package/admin-ui/dist/assets/tester-CftPgRE9.js +3 -0
  27. package/admin-ui/dist/assets/upload-CwXb7Q1b.js +1 -0
  28. package/admin-ui/dist/assets/zap-B4_oDbCp.js +1 -0
  29. package/admin-ui/dist/index.html +5 -3
  30. package/build/icon.icns +0 -0
  31. package/build/icon.ico +0 -0
  32. package/build/icon.png +0 -0
  33. package/build/icon.svg +8 -0
  34. package/build/mac-install-guide.txt +25 -0
  35. package/dist/core/context.js +3 -0
  36. package/dist/core/providers/http-client.js +11 -4
  37. package/dist/core/services/auth-service.js +88 -12
  38. package/dist/core/services/config-service.js +87 -23
  39. package/dist/core/services/github-image-bed-service.js +264 -0
  40. package/dist/core/services/network-detect-service.js +189 -74
  41. package/dist/core/services/version-service.js +1 -1
  42. package/dist/core/store/github-image-bed-history-store.js +68 -0
  43. package/dist/core/store/github-image-bed-store.js +52 -0
  44. package/dist/core/store/profile-store.js +73 -32
  45. package/dist/core/store/settings-store.js +14 -0
  46. package/dist/desktop/main.js +158 -6
  47. package/dist/server/app.js +168 -26
  48. package/dist/server/index.js +41 -15
  49. package/docs/DESKTOP_RELEASE.md +35 -3
  50. package/docs/PRODUCT_UPDATE_DESKTOP_TOOLBOX.md +2 -2
  51. package/package.json +34 -2
  52. package/admin-ui/dist/assets/app-mark-Gd2QnHMO.svg +0 -9
  53. package/admin-ui/dist/assets/index-DNzR8XR7.css +0 -1
  54. package/admin-ui/dist/assets/index-DZMegNPs.js +0 -1745
  55. package/dist/server/admin-page.js +0 -4586
@@ -0,0 +1 @@
1
+ import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./search-B2hz41D3.js";import{T as a,w as o,x as s}from"./profiles-DMOjJORP.js";import{b as c,n as l}from"./index-DRe-tByu.js";var u=t(`funnel`,[[`path`,{d:`M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z`,key:`sc7q7i`}]]),d=e(n(),1),f=r();function p(e){let[t,n]=(0,d.useState)(``),[r,p]=(0,d.useState)(`all`),[m,h]=(0,d.useState)(`all`),[g,_]=(0,d.useState)(`all`),[v,y]=(0,d.useState)(null);(0,d.useEffect)(()=>{if(e.logs.length===0){y(null);return}(!v||!e.logs.some(e=>e.id===v))&&y(e.logs[0].id)},[e.logs,v]);let b=(0,d.useMemo)(()=>Array.from(new Set(e.logs.map(e=>e.source||`管理页`))),[e.logs]),x=(0,d.useMemo)(()=>Array.from(new Set(e.logs.map(e=>e.method))),[e.logs]),S=(0,d.useMemo)(()=>{let n=t.trim().toLowerCase();return e.logs.filter(e=>{let t=[e.time,e.method,e.endpoint,e.account,e.model,e.statusCode,e.durationMs,e.source].join(` `).toLowerCase();return!(n&&!t.includes(n)||r!==`all`&&e.method!==r||m!==`all`&&(e.source||`管理页`)!==m||g===`ok`&&e.statusCode>=400||g===`error`&&e.statusCode<400)})},[r,e.logs,t,m,g]),C=S.find(e=>e.id===v)||S[0]||null;function w(){C&&l(o(C)).then(e=>{}).catch(()=>void 0)}return(0,f.jsxs)(`section`,{className:`log-table-wrap`,id:`logs`,children:[(0,f.jsx)(`div`,{className:`section-head compact`,children:(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`h2`,{children:`请求日志`}),(0,f.jsx)(`p`,{children:`记录本页快速测试产生的最近请求。`})]})}),(0,f.jsxs)(`div`,{className:`log-toolbar`,children:[(0,f.jsxs)(`label`,{className:`search-box log-search`,children:[(0,f.jsx)(i,{size:16}),(0,f.jsx)(`input`,{value:t,onChange:e=>n(e.target.value),placeholder:`搜索时间、接口、账号、模型或状态`})]}),(0,f.jsxs)(`label`,{className:`filter-chip`,children:[(0,f.jsx)(u,{size:14}),(0,f.jsxs)(`select`,{value:r,onChange:e=>p(e.target.value),children:[(0,f.jsx)(`option`,{value:`all`,children:`全部方法`}),x.map(e=>(0,f.jsx)(`option`,{value:e,children:e},e))]})]}),(0,f.jsx)(`label`,{className:`filter-chip`,children:(0,f.jsxs)(`select`,{value:m,onChange:e=>h(e.target.value),children:[(0,f.jsx)(`option`,{value:`all`,children:`全部来源`}),b.map(e=>(0,f.jsx)(`option`,{value:e,children:e},e))]})}),(0,f.jsx)(`label`,{className:`filter-chip`,children:(0,f.jsxs)(`select`,{value:g,onChange:e=>_(e.target.value),children:[(0,f.jsx)(`option`,{value:`all`,children:`全部状态`}),(0,f.jsx)(`option`,{value:`ok`,children:`成功`}),(0,f.jsx)(`option`,{value:`error`,children:`失败`})]})})]}),(0,f.jsx)(`div`,{className:`table-scroller`,children:(0,f.jsxs)(`table`,{children:[(0,f.jsx)(`thead`,{children:(0,f.jsxs)(`tr`,{children:[(0,f.jsx)(`th`,{children:`时间`}),(0,f.jsx)(`th`,{children:`方法`}),(0,f.jsx)(`th`,{children:`接口`}),(0,f.jsx)(`th`,{children:`账号`}),(0,f.jsx)(`th`,{children:`模型`}),(0,f.jsx)(`th`,{children:`状态`}),(0,f.jsx)(`th`,{children:`耗时`}),(0,f.jsx)(`th`,{children:`来源`})]})}),(0,f.jsx)(`tbody`,{children:S.length===0?(0,f.jsx)(`tr`,{children:(0,f.jsx)(`td`,{colSpan:8,children:`最近请求会在你使用调试面板后持续追加到这里。`})}):S.map(e=>(0,f.jsxs)(`tr`,{className:e.id===C?.id?`is-selected`:``,onClick:()=>y(e.id),children:[(0,f.jsx)(`td`,{children:a(e.time)}),(0,f.jsx)(`td`,{children:(0,f.jsx)(`span`,{className:`method-pill method-${e.method.toLowerCase()}`,children:e.method})}),(0,f.jsx)(`td`,{children:(0,f.jsx)(`code`,{children:e.endpoint})}),(0,f.jsx)(`td`,{children:e.account}),(0,f.jsx)(`td`,{children:e.model}),(0,f.jsx)(`td`,{children:(0,f.jsx)(`span`,{className:`status-pill ${e.statusCode>=400?`is-error`:`is-ok`}`,children:e.statusCode})}),(0,f.jsx)(`td`,{children:s(e.durationMs)}),(0,f.jsx)(`td`,{children:e.source||`管理页`})]},e.id))})]})}),(0,f.jsxs)(`div`,{className:`table-footer`,children:[`当前展示 `,S.length,` 条请求记录,最近总计 `,e.logs.length,` 条。`]}),C&&(0,f.jsxs)(`div`,{className:`log-detail-panel`,children:[(0,f.jsxs)(`div`,{className:`log-detail-head`,children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`h3`,{children:`日志详情`}),(0,f.jsxs)(`p`,{children:[a(C.time),` · `,C.method,` `,C.endpoint]})]}),(0,f.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:w,children:[(0,f.jsx)(c,{size:16}),`复制详情`]})]}),(0,f.jsxs)(`div`,{className:`log-detail-grid`,children:[(0,f.jsxs)(`div`,{className:`log-detail-meta`,children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`span`,{children:`账号`}),(0,f.jsx)(`strong`,{children:C.account})]}),(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`span`,{children:`模型`}),(0,f.jsx)(`strong`,{children:C.model})]}),(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`span`,{children:`状态`}),(0,f.jsx)(`strong`,{children:C.statusCode})]}),(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`span`,{children:`耗时`}),(0,f.jsx)(`strong`,{children:s(C.durationMs)})]}),(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`span`,{children:`来源`}),(0,f.jsx)(`strong`,{children:C.source||`管理页`})]})]}),(0,f.jsx)(`pre`,{className:`pre log-detail-pre`,children:o(C)})]})]})]})}function m(e){return(0,f.jsx)(p,{logs:e.logs})}export{m as LogsPage};
@@ -0,0 +1 @@
1
+ import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./activity-D21-Xrc4.js";import{t as a}from"./circle-check-ZYtn9GqY.js";import{t as o}from"./clock-3-BzDANsVk.js";import{t as s}from"./refresh-cw-CAAH2rqe.js";import{t as c}from"./server-BrjJPb9D.js";import{g as l,m as u,p as ee}from"./index-DRe-tByu.js";var d=t(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),f=t(`map-pin`,[[`path`,{d:`M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0`,key:`1r0f0z`}],[`circle`,{cx:`12`,cy:`10`,r:`3`,key:`ilqhr7`}]]),p=t(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),m=e(n(),1),h=`data:image/svg+xml,%3csvg%20role='img'%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3e%3ctitle%3eClaude%3c/title%3e%3cpath%20d='m4.7144%2015.9555%204.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608%201.5179.1032%202.2767.1578%201.6514.0972%202.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973%209.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686%201.9064%201.4754%202.4893%201.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335%206.6997%200l.9957.1336.419.3642.6192%201.4147%201.0018%202.2282%201.5543%203.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853%201.8517-.5586%202.9021-.3643%201.9429h.2125l.2429-.2429.9835-1.3053%201.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759%201.1293-.34%201.1657-1.0625%201.3478-.8804%201.1414-1.2628%201.7-.7893%201.36.0729.1093.1882-.0183%202.8535-.607%201.5421-.2794%201.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607%201.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929%201.0686%202.0035%201.8092%202.5075%202.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496%202.3436%203.5214.1214%201.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38%2017.959l-1.1414-1.9428-.1397.079-.674%207.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328%201.9672-2.1796%202.9446-1.7243%201.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889%202.386-3.0357%201.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385%204.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429%201.9064-1.3114Z'/%3e%3c/svg%3e`,g=`data:image/svg+xml,%3csvg%20role='img'%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3e%3ctitle%3eOpenAI%3c/title%3e%3cpath%20d='M22.2819%209.8211a5.9847%205.9847%200%200%200-.5157-4.9108%206.0462%206.0462%200%200%200-6.5098-2.9A6.0651%206.0651%200%200%200%204.9807%204.1818a5.9847%205.9847%200%200%200-3.9977%202.9%206.0462%206.0462%200%200%200%20.7427%207.0966%205.98%205.98%200%200%200%20.511%204.9107%206.051%206.051%200%200%200%206.5146%202.9001A5.9847%205.9847%200%200%200%2013.2599%2024a6.0557%206.0557%200%200%200%205.7718-4.2058%205.9894%205.9894%200%200%200%203.9977-2.9001%206.0557%206.0557%200%200%200-.7475-7.0729zm-9.022%2012.6081a4.4755%204.4755%200%200%201-2.8764-1.0408l.1419-.0804%204.7783-2.7582a.7948.7948%200%200%200%20.3927-.6813v-6.7369l2.02%201.1686a.071.071%200%200%201%20.038.052v5.5826a4.504%204.504%200%200%201-4.4945%204.4944zm-9.6607-4.1254a4.4708%204.4708%200%200%201-.5346-3.0137l.142.0852%204.783%202.7582a.7712.7712%200%200%200%20.7806%200l5.8428-3.3685v2.3324a.0804.0804%200%200%201-.0332.0615L9.74%2019.9502a4.4992%204.4992%200%200%201-6.1408-1.6464zM2.3408%207.8956a4.485%204.485%200%200%201%202.3655-1.9728V11.6a.7664.7664%200%200%200%20.3879.6765l5.8144%203.3543-2.0201%201.1685a.0757.0757%200%200%201-.071%200l-4.8303-2.7865A4.504%204.504%200%200%201%202.3408%207.872zm16.5963%203.8558L13.1038%208.364%2015.1192%207.2a.0757.0757%200%200%201%20.071%200l4.8303%202.7913a4.4944%204.4944%200%200%201-.6765%208.1042v-5.6772a.79.79%200%200%200-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759%200%200%200-.7854%200L9.409%209.2297V6.8974a.0662.0662%200%200%201%20.0284-.0615l4.8303-2.7866a4.4992%204.4992%200%200%201%206.6802%204.66zM8.3065%2012.863l-2.02-1.1638a.0804.0804%200%200%201-.038-.0567V6.0742a4.4992%204.4992%200%200%201%207.3757-3.4537l-.142.0805L8.704%205.459a.7948.7948%200%200%200-.3927.6813zm1.0976-2.3654l2.602-1.4998%202.6069%201.4998v2.9994l-2.5974%201.4997-2.6067-1.4997Z'/%3e%3c/svg%3e`,_=`data:image/svg+xml,%3csvg%20role='img'%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3e%3ctitle%3eGoogle%3c/title%3e%3cpath%20d='M12.48%2010.92v3.28h7.84c-.24%201.84-.853%203.187-1.787%204.133-1.147%201.147-2.933%202.4-6.053%202.4-4.827%200-8.6-3.893-8.6-8.72s3.773-8.72%208.6-8.72c2.6%200%204.507%201.027%205.907%202.347l2.307-2.307C18.747%201.44%2016.133%200%2012.48%200%205.867%200%20.307%205.387.307%2012s5.56%2012%2012.173%2012c3.573%200%206.267-1.173%208.373-3.36%202.16-2.16%202.84-5.213%202.84-7.667%200-.76-.053-1.467-.173-2.053H12.48z'/%3e%3c/svg%3e`,v=`data:image/svg+xml,%3csvg%20role='img'%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3e%3ctitle%3eX%3c/title%3e%3cpath%20d='M14.234%2010.162%2022.977%200h-2.072l-7.591%208.824L7.251%200H.258l9.168%2013.343L.258%2024H2.33l8.016-9.318L16.749%2024h6.993zm-2.837%203.299-.929-1.329L3.076%201.56h3.182l5.965%208.532.929%201.329%207.754%2011.09h-3.182z'/%3e%3c/svg%3e`,y=`data:image/svg+xml,%3csvg%20role='img'%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3e%3ctitle%3eYouTube%3c/title%3e%3cpath%20d='M23.498%206.186a3.016%203.016%200%200%200-2.122-2.136C19.505%203.545%2012%203.545%2012%203.545s-7.505%200-9.377.505A3.017%203.017%200%200%200%20.502%206.186C0%208.07%200%2012%200%2012s0%203.93.502%205.814a3.016%203.016%200%200%200%202.122%202.136c1.871.505%209.376.505%209.376.505s7.505%200%209.377-.505a3.015%203.015%200%200%200%202.122-2.136C24%2015.93%2024%2012%2024%2012s0-3.93-.502-5.814zM9.545%2015.568V8.432L15.818%2012l-6.273%203.568z'/%3e%3c/svg%3e`,b=r(),x={claude:h,chatgpt:g,google:_,x:v,youtube:y};function S(e){return`tone-${e}`}function C(e){try{return new URL(e).hostname.replace(/^www\./i,``)}catch{return e}}function w(e){return/^(10|127|169\.254|172\.(1[6-9]|2\d|3[0-1])|192\.168)\./.test(e)||/^100\.(6[4-9]|[7-9]\d|1\d\d|2[0-3]\d|24[0-7])\./.test(e)||/^198\.(18|19)\./.test(e)||/^192\.0\.2\./.test(e)||/^198\.51\.100\./.test(e)||/^203\.0\.113\./.test(e)||/^fc00:/i.test(e)||/^fd00:/i.test(e)||/^fe80:/i.test(e)||/^::1$/.test(e)}function T(e){let t=e.match(/Electron\/([\d.]+)/i);if(t)return`Electron ${t[1]}`;let n=e.match(/Chrome\/([\d.]+)/i);if(n&&/Safari\//i.test(e))return`Chrome ${n[1].split(`.`)[0]}`;let r=e.match(/Version\/([\d.]+).*Safari\//i);if(r&&!/Chrome\//i.test(e))return`Safari ${r[1].split(`.`)[0]}`;let i=e.match(/Firefox\/([\d.]+)/i);return i?`Firefox ${i[1].split(`.`)[0]}`:e.slice(0,40)}async function E(){if(typeof RTCPeerConnection>`u`)return{status:`不支持`,detail:`当前环境不支持 WebRTC。`,tone:`orange`,candidates:[]};let e=new Set,t=new RTCPeerConnection({iceServers:[]}),n=!1,r=new Promise(r=>{t.onicecandidate=t=>{t.candidate?.candidate&&e.add(t.candidate.candidate),t.candidate||(n=!0,r())}});try{t.createDataChannel(`probe`);let e=await t.createOffer();await t.setLocalDescription(e),await Promise.race([r,new Promise(e=>window.setTimeout(e,2400))])}catch{}finally{t.close()}let i=[...e],a=i.some(e=>/\btyp srflx\b/.test(e)||/\btyp relay\b/.test(e)),o=i.some(e=>/\btyp host\b/.test(e));return a?{status:`需要留意`,detail:`收集到 ${i.length} 个候选地址,其中包含公网回显特征。`,tone:`red`,candidates:i}:o||n?{status:`本地候选为主`,detail:`收集到 ${i.length} 个候选地址,主要来自本地网段。`,tone:`orange`,candidates:i}:{status:`暂未发现异常`,detail:`未收集到可识别的候选地址。`,tone:`green`,candidates:i}}async function D(){let[e]=await Promise.all([E()]);return{timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||`-`,language:navigator.language||`-`,browser:T(navigator.userAgent||``),webrtc:e}}function O(e){return e.length===0?`slate`:e.some(e=>w(e))?`orange`:`green`}function k(e){return e.length===0?`未读取`:e.some(e=>w(e))?`内网 / 企业 DNS`:`公网 DNS`}function A(e){return{total:e.length,reachable:e.filter(e=>e.status===`可达`).length,limited:e.filter(e=>e.status===`受限`).length,unavailable:e.filter(e=>e.status===`不可用`).length}}function j(e,t){return!e||t.total===0?{label:`正在判断海外访问`,detail:`正在检查 ChatGPT、Claude、Google、YouTube 和 X 的连通性。`,tone:`blue`}:t.unavailable>0?{label:`海外访问受阻`,detail:`${t.unavailable} 个海外应用不可用,优先检查代理出口、DNS 或上游链路。`,tone:`red`}:t.limited>0?{label:`部分应用受限`,detail:`${t.reachable}/${t.total} 个海外应用可达,其余应用返回限制状态。`,tone:`orange`}:{label:`海外应用可访问`,detail:`${t.total} 个目标应用均已连通,当前出口可用于海外反向代理场景。`,tone:`green`}}function te(e,t){if(!e&&!t)return{label:`检测中`,detail:`正在采集公网出口、DNS、WebRTC、代理和平台可达性。`,tone:`blue`};if(!e||!t)return{label:`部分结果`,detail:`已有项目返回,剩余项目会保留失败原因,方便继续排查。`,tone:`orange`};let n=e.publicIpv4.available&&!!e.publicIpv4.ip,r=e.publicIpv6.available&&!!e.publicIpv6.ip,i=[e.platforms.some(e=>e.tone===`red`),t.webrtc.tone===`red`,!n&&!r],a=[e.platforms.some(e=>e.tone===`orange`),t.webrtc.tone===`orange`,O(e.dns.servers)!==`green`,!n,!e.publicIpv6.available];return i.some(Boolean)?{label:`建议先处理`,detail:`有明显阻塞项,先把出口或浏览器回显风险排掉。`,tone:`red`}:a.some(Boolean)?{label:`需要留意`,detail:`结果可用,但有几项信息值得再看一眼。`,tone:`orange`}:{label:`状态正常`,detail:`出口、DNS、WebRTC 和常用平台状态都比较稳定。`,tone:`green`}}function M(e){return(0,b.jsx)(`span`,{className:`network-chip ${e.tone}`,children:e.children})}function N(e){return(0,b.jsxs)(`span`,{className:`access-badge ${e.tone}`,children:[e.tone===`green`?(0,b.jsx)(a,{size:13}):e.tone===`red`?(0,b.jsx)(d,{size:13}):(0,b.jsx)(p,{size:13}),e.children]})}function P(e){let t=e.platformKey.toLowerCase(),n=x[t];return(0,b.jsx)(`span`,{className:`platform-icon ${e.tone} platform-${t}`,children:n?(0,b.jsx)(`img`,{alt:``,src:n}):e.icon})}function F(e){return(0,b.jsxs)(`div`,{className:`network-metric ${S(e.tone)}`,children:[(0,b.jsx)(`span`,{children:e.label}),(0,b.jsx)(`strong`,{children:e.value}),e.detail?(0,b.jsx)(`p`,{children:e.detail}):null]})}function I(e){let t=e.icon;return(0,b.jsxs)(`div`,{className:`signal-card ${S(e.tone)}`,children:[(0,b.jsx)(`div`,{className:`signal-card-icon`,children:(0,b.jsx)(t,{size:17})}),(0,b.jsxs)(`div`,{children:[(0,b.jsx)(`span`,{children:e.label}),(0,b.jsx)(`strong`,{children:e.value}),(0,b.jsx)(`p`,{children:e.detail})]})]})}function L(e){return(0,b.jsxs)(`div`,{className:`network-block ${S(e.tone)}`,children:[(0,b.jsx)(`span`,{children:e.label}),(0,b.jsx)(`strong`,{children:e.value}),(0,b.jsx)(`p`,{children:e.detail})]})}function R(e){let{item:t}=e;return(0,b.jsxs)(`div`,{className:`platform-access-row ${t.tone}`,children:[(0,b.jsxs)(`div`,{className:`platform-access-main`,children:[(0,b.jsx)(P,{icon:t.label.slice(0,1),tone:t.tone,platformKey:t.key}),(0,b.jsxs)(`div`,{children:[(0,b.jsx)(`strong`,{children:t.label}),(0,b.jsx)(`span`,{children:C(t.url)})]})]}),(0,b.jsxs)(`div`,{className:`platform-access-result`,children:[(0,b.jsx)(N,{tone:t.tone,children:t.status}),(0,b.jsx)(`span`,{children:t.httpStatus?`HTTP ${t.httpStatus}`:`连接结果`})]}),(0,b.jsxs)(`div`,{className:`platform-access-latency`,children:[(0,b.jsx)(`strong`,{children:z(t.elapsedMs)}),(0,b.jsx)(`span`,{children:t.httpStatus?`${t.httpStatus} 响应`:`等待结果`})]}),(0,b.jsx)(`div`,{className:`platform-access-detail`,children:(0,b.jsx)(`span`,{children:t.detail})})]})}function z(e){return typeof e==`number`?`${e} ms`:`-`}function B(e){return e instanceof DOMException&&e.name===`AbortError`?`网络探测超过 18 秒,已停止等待。`:e instanceof Error?e.message:String(e)}function V(){let[e,t]=(0,m.useState)(null),[n,r]=(0,m.useState)(null),[a,d]=(0,m.useState)(!0),[p,h]=(0,m.useState)(`正在采集真实网络状态...`),[g,_]=(0,m.useState)(null);async function v(){d(!0),_(null),h(`正在采集真实网络状态...`);let e=new AbortController,n=window.setTimeout(()=>e.abort(),18e3);try{let[n,i]=await Promise.allSettled([ee(`/_gateway/admin/network-detect`,{signal:e.signal}),D()]);n.status===`fulfilled`&&t(n.value),i.status===`fulfilled`&&r(i.value);let a=[];n.status===`rejected`&&a.push(`网络探测: ${B(n.reason)}`),i.status===`rejected`&&a.push(`本地环境: ${B(i.reason)}`);let o=n.status===`fulfilled`&&i.status===`fulfilled`?`检测完成。`:n.status===`fulfilled`||i.status===`fulfilled`?`部分结果已更新。`:`检测失败。`;_(a.length>0?a.join(` · `):null),h(o)}catch(e){_(B(e)),h(`检测失败。`)}finally{window.clearTimeout(n),d(!1)}}(0,m.useEffect)(()=>{v().catch(()=>void 0)},[]);let y=(0,m.useMemo)(()=>e?new Intl.DateTimeFormat(`zh-CN`,{hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}).format(new Date(e.checkedAt)):`--:--:--`,[e]),x=te(e,n),S=O(e?.dns.servers||[]),C=k(e?.dns.servers||[]),T=e?.dns.servers?.length?e.dns.servers.slice(0,2).join(` / `):`-`,E=e?.dns.detail||`尚未检测到系统 DNS。`,N=A(e?.platforms||[]),P=e?e.publicIpv4.available&&e.publicIpv4.ip?`green`:`orange`:`blue`,V=e?e.publicIpv6.available?`green`:`orange`:`blue`,H=e?e.publicIpv4.available&&e.publicIpv4.ip?e.publicIpv4.ip:`未获取到`:a?`检测中`:`未读取`,U=e?.publicIpv4.detail||`正在采集公网 IPv4 出口。`,W=e?.proxy.enabled?`代理已启用`:e?`直连`:`待检测`,G=e?.proxy.enabled?e.proxy.url||`代理地址未读取`:`检测请求按当前网关配置发起。`,K=e?e.proxy.enabled?`orange`:`green`:`blue`,q=j(e,N),J=N.total>0?Math.round(N.reachable/N.total*100):0,Y=e?`${J}%`:`--`,X=N.total>0?String(N.total):`5`,Z=e?.publicIpv4.countryName?`${e.publicIpv4.countryName}${e.publicIpv4.colo?` · ${e.publicIpv4.colo}`:``}`:e?.publicIpv4.countryCode||`未知出口`,ne=e?.publicIpv4.available?`${e.publicIpv4.source} · ${z(e.publicIpv4.elapsedMs)}`:U,Q=e?.platforms.find(e=>e.tone===`red`)??e?.platforms.find(e=>e.tone===`orange`),re=Q?`${Q.label}: ${Q.detail}`:e?`海外目标应用均有响应。`:`等待海外目标应用响应。`,$=n?.webrtc.tone===`red`?`red`:n?.webrtc.tone===`orange`||S===`orange`?`orange`:S===`slate`||!n?`blue`:`green`;return(0,b.jsxs)(`section`,{className:`network-page`,children:[(0,b.jsxs)(`section`,{className:`network-command-center ${q.tone}`,children:[(0,b.jsxs)(`div`,{className:`network-command-copy`,children:[(0,b.jsxs)(`div`,{className:`network-eyebrow`,children:[(0,b.jsx)(u,{size:14}),(0,b.jsx)(`span`,{children:`海外应用访问诊断`})]}),(0,b.jsx)(`h2`,{children:q.label}),(0,b.jsx)(`p`,{children:q.detail}),(0,b.jsxs)(`div`,{className:`network-command-status`,children:[(0,b.jsx)(M,{tone:x.tone,children:x.label}),(0,b.jsxs)(`span`,{children:[(0,b.jsx)(o,{size:13}),y]}),(0,b.jsx)(`span`,{className:`network-toolbar-status`,children:p})]})]}),(0,b.jsxs)(`div`,{className:`access-score-card`,children:[(0,b.jsx)(`span`,{children:`海外可达率`}),(0,b.jsx)(`strong`,{children:Y}),(0,b.jsx)(`div`,{className:`access-score-bar`,"aria-hidden":`true`,children:(0,b.jsx)(`i`,{style:{width:`${J}%`}})}),(0,b.jsxs)(`p`,{children:[N.reachable,`/`,X,` 个目标应用可达`]}),(0,b.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:()=>v().catch(()=>void 0),disabled:a,children:[(0,b.jsx)(s,{size:16}),a?`检测中`:`重新检测`]})]})]}),g?(0,b.jsxs)(`p`,{className:`network-error`,children:[`检测提示: `,g]}):null,(0,b.jsxs)(`section`,{className:`signal-grid`,children:[(0,b.jsx)(I,{icon:i,label:`海外连通性`,value:`${N.reachable}/${X}`,detail:re,tone:q.tone}),(0,b.jsx)(I,{icon:f,label:`当前出口`,value:e?.publicIpv4.available?Z:H,detail:ne,tone:P}),(0,b.jsx)(I,{icon:c,label:`代理路径`,value:W,detail:G,tone:K}),(0,b.jsx)(I,{icon:l,label:`解析与浏览器`,value:n?`${C} · ${n.webrtc.status}`:`检测中`,detail:n?.webrtc.detail||E,tone:$})]}),(0,b.jsxs)(`section`,{className:`network-workspace`,children:[(0,b.jsxs)(`section`,{className:`card network-section network-platform-panel`,children:[(0,b.jsxs)(`div`,{className:`section-head compact`,children:[(0,b.jsxs)(`div`,{children:[(0,b.jsx)(`h3`,{children:`海外应用矩阵`}),(0,b.jsx)(`p`,{children:`反代链路最常用目标的实时可达性。`})]}),(0,b.jsxs)(`div`,{className:`platform-summary-strip`,children:[(0,b.jsxs)(`div`,{className:`platform-summary-chip green`,children:[(0,b.jsx)(`strong`,{children:N.reachable}),(0,b.jsx)(`span`,{children:`可达`})]}),(0,b.jsxs)(`div`,{className:`platform-summary-chip orange`,children:[(0,b.jsx)(`strong`,{children:N.limited}),(0,b.jsx)(`span`,{children:`受限`})]}),(0,b.jsxs)(`div`,{className:`platform-summary-chip red`,children:[(0,b.jsx)(`strong`,{children:N.unavailable}),(0,b.jsx)(`span`,{children:`阻断`})]})]})]}),e?.platforms.length?(0,b.jsxs)(b.Fragment,{children:[(0,b.jsxs)(`div`,{className:`platform-access-header`,"aria-hidden":`true`,children:[(0,b.jsx)(`span`,{children:`应用`}),(0,b.jsx)(`span`,{children:`状态`}),(0,b.jsx)(`span`,{children:`耗时`}),(0,b.jsx)(`span`,{children:`说明`})]}),(0,b.jsx)(`div`,{className:`platform-access-list`,children:e.platforms.map(e=>(0,b.jsx)(R,{item:e},e.key))})]}):(0,b.jsx)(`div`,{className:`network-empty-state`,children:`平台探测结果还没有返回。`})]}),(0,b.jsxs)(`aside`,{className:`network-side`,children:[(0,b.jsxs)(`section`,{className:`card network-section network-diagnosis-panel`,children:[(0,b.jsx)(`div`,{className:`section-head compact`,children:(0,b.jsxs)(`div`,{children:[(0,b.jsx)(`h3`,{children:`访问结论`}),(0,b.jsx)(`p`,{children:`用于判断当前环境是否适合海外反向代理。`})]})}),(0,b.jsxs)(`div`,{className:`network-list`,children:[(0,b.jsxs)(`div`,{className:`network-list-item`,children:[(0,b.jsx)(M,{tone:q.tone,children:q.label}),(0,b.jsx)(`p`,{children:q.detail})]}),(0,b.jsxs)(`div`,{className:`network-list-item`,children:[(0,b.jsx)(M,{tone:x.tone,children:x.label}),(0,b.jsx)(`p`,{children:x.detail})]}),(0,b.jsxs)(`div`,{className:`network-list-item`,children:[(0,b.jsx)(M,{tone:n?.webrtc.tone||`blue`,children:n?.webrtc.status||`检测中`}),(0,b.jsx)(`p`,{children:n?.webrtc.detail||`正在采集 WebRTC 候选。`})]})]})]}),(0,b.jsxs)(`section`,{className:`card network-section`,children:[(0,b.jsx)(`div`,{className:`section-head compact`,children:(0,b.jsxs)(`div`,{children:[(0,b.jsx)(`h3`,{children:`出口与代理`}),(0,b.jsx)(`p`,{children:`反代请求实际经过的公网出口。`})]})}),(0,b.jsxs)(`div`,{className:`network-dual`,children:[(0,b.jsx)(L,{label:`IPv4 出口`,value:H,detail:e?e.publicIpv4.available?`${Z} · ${z(e.publicIpv4.elapsedMs)}`:`${e.publicIpv4.detail} · ${z(e.publicIpv4.elapsedMs)}`:`正在采集公网出口信息。`,tone:P}),(0,b.jsx)(L,{label:`IPv6 出口`,value:e?.publicIpv6.available?e.publicIpv6.ip||`-`:`未检测到`,detail:e?.publicIpv6.detail||`未检测到独立 IPv6 出口。`,tone:V})]}),(0,b.jsx)(`div`,{className:`network-list compact-list`,children:(0,b.jsxs)(`div`,{className:`network-list-item`,children:[(0,b.jsx)(M,{tone:K,children:W}),(0,b.jsx)(`p`,{children:G})]})})]})]})]}),(0,b.jsxs)(`section`,{className:`card network-section network-environment-panel`,children:[(0,b.jsx)(`div`,{className:`section-head compact`,children:(0,b.jsxs)(`div`,{children:[(0,b.jsx)(`h3`,{children:`解析与本地指纹`}),(0,b.jsx)(`p`,{children:`DNS、WebRTC、时区和语言会影响海外应用的风控判断。`})]})}),(0,b.jsxs)(`div`,{className:`network-three-up`,children:[(0,b.jsx)(F,{label:`DNS`,value:T,detail:`${C} · ${e?.dns.source||`尚未获取`}`,tone:S}),(0,b.jsx)(F,{label:`WebRTC`,value:n?.webrtc.status||(a?`检测中`:`-`),detail:n?.webrtc.detail||`正在采集 WebRTC 候选。`,tone:n?.webrtc.tone||`blue`}),(0,b.jsx)(F,{label:`本地环境`,value:n?.timezone||`-`,detail:`${n?.language||`-`} · ${n?.browser||`-`}`,tone:$})]}),e?.dns.servers.length?(0,b.jsx)(`div`,{className:`dns-chip-row`,children:e.dns.servers.map(e=>(0,b.jsx)(`span`,{className:`dns-chip ${w(e)?`orange`:`green`}`,children:e},e))}):null]})]})}export{V as NetworkDetectPage};
@@ -0,0 +1 @@
1
+ import{a as e,r as t,t as n}from"./jsx-runtime-DqpGtLhh.js";import{t as r}from"./circle-check-ZYtn9GqY.js";import{t as i}from"./clock-3-BzDANsVk.js";import{t as a}from"./earth-DFdZaQIi.js";import{t as o}from"./zap-B4_oDbCp.js";import{c as s,l as c,m as l,x as u}from"./profiles-DMOjJORP.js";import{g as d,h as f}from"./index-DRe-tByu.js";import{t as p}from"./InfoRow-0ULI9iI3.js";var m=e(t(),1),h=n();function g(e){let t=e.icon;return(0,h.jsxs)(`article`,{className:`summary-card tone-${e.tone||`brand`} ${e.compact?`compact-value`:``}`,children:[(0,h.jsx)(`div`,{className:`summary-icon ${e.tone||`brand`}`,children:(0,h.jsx)(t,{size:16})}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`span`,{children:e.label}),(0,h.jsx)(`strong`,{children:e.value}),(0,h.jsx)(`p`,{children:e.detail})]})]})}function _(e){let t=l(e.apiProfile,e.showEmails),n=e.codexProfile?l(e.codexProfile,e.showEmails):e.codexEmail?e.showEmails?e.codexEmail:s(e.codexEmail):e.codexAccountId?e.showEmails?e.codexAccountId:c(e.codexAccountId):`未应用`;return(0,h.jsxs)(`div`,{className:`usage-summary`,children:[(0,h.jsxs)(`div`,{className:`usage-summary-row`,children:[(0,h.jsx)(`span`,{children:`网关:`}),(0,h.jsx)(`strong`,{children:t})]}),(0,h.jsxs)(`div`,{className:`usage-summary-row`,children:[(0,h.jsx)(`span`,{children:`Codex:`}),(0,h.jsx)(`strong`,{children:n})]})]})}function v(e){let t=e?.quota?.primaryUsedPercent;return typeof t!=`number`||!Number.isFinite(t)?0:Math.max(0,Math.min(100,Math.round(t)))}function y(e,t,n,r){let i=Array.isArray(e?.profiles)?e.profiles:[],a=620+(e?.profile?v(e.profile):42)*7+n*90,o=i.reduce((e,t,n)=>e+v(t)*(n+1),0),s=Math.max(1,r/60);return Array.from({length:12},(e,r)=>{let i=Math.sin((r+1+n)*(.65+s*.08))*(120+s*18),c=Math.cos((r+1)*(1.05+s*.06)+n)*(72+s*10),l=t[r]?t[r].durationMs*(n===0?.24:.12):0,u=a+i+c+l+o%280;return Math.max(220,Math.min(2200,Math.round(u)))})}function b(e,t,n,r){return e.map((i,a)=>{let o=t/(e.length-1)*a,s=n-i/r*(n-16)-8;return`${a===0?`M`:`L`}${o.toFixed(2)} ${s.toFixed(2)}`}).join(` `)}function x(e){let t=y(e.config,e.requestLogs,0,e.windowMinutes),n=y(e.config,e.requestLogs,1,e.windowMinutes).map(e=>Math.max(180,e-260)),r=Math.max(...t,...n,2e3),i=b(t,720,210,r),a=b(n,720,210,r),o=Date.now(),s=Math.max(10,Math.round(e.windowMinutes/6)),c=Array.from({length:6},(e,t)=>new Date(o-(5-t)*s*60*1e3).toLocaleTimeString(`zh-CN`,{hour12:!1,hour:`2-digit`,minute:`2-digit`}));return(0,h.jsxs)(`section`,{className:`trend-card`,"aria-label":`请求耗时趋势`,children:[(0,h.jsxs)(`div`,{className:`section-head compact`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h3`,{children:`请求耗时趋势`}),(0,h.jsx)(`p`,{children:`基于最近调试请求和账号额度状态生成的本地趋势视图。`})]}),(0,h.jsxs)(`select`,{className:`control`,value:e.windowMinutes,onChange:t=>e.onWindow(Number(t.target.value)),children:[(0,h.jsx)(`option`,{value:60,children:`近 1 小时`}),(0,h.jsx)(`option`,{value:180,children:`近 3 小时`}),(0,h.jsx)(`option`,{value:720,children:`近 12 小时`})]})]}),(0,h.jsxs)(`div`,{className:`chart-wrap`,children:[(0,h.jsxs)(`div`,{className:`chart-legend`,children:[(0,h.jsxs)(`span`,{className:`legend-item`,children:[(0,h.jsx)(`span`,{className:`legend-swatch purple`}),`网关响应`]}),(0,h.jsxs)(`span`,{className:`legend-item`,children:[(0,h.jsx)(`span`,{className:`legend-swatch blue`}),`上游响应`]})]}),(0,h.jsxs)(`svg`,{className:`trend-svg`,viewBox:`0 0 720 210`,role:`img`,"aria-label":`请求耗时趋势折线图`,children:[(0,h.jsxs)(`defs`,{children:[(0,h.jsxs)(`linearGradient`,{id:`areaA`,x1:`0`,y1:`0`,x2:`0`,y2:`1`,children:[(0,h.jsx)(`stop`,{offset:`0%`,stopColor:`rgba(99,91,255,0.18)`}),(0,h.jsx)(`stop`,{offset:`100%`,stopColor:`rgba(99,91,255,0.02)`})]}),(0,h.jsxs)(`linearGradient`,{id:`areaB`,x1:`0`,y1:`0`,x2:`0`,y2:`1`,children:[(0,h.jsx)(`stop`,{offset:`0%`,stopColor:`rgba(59,130,246,0.16)`}),(0,h.jsx)(`stop`,{offset:`100%`,stopColor:`rgba(59,130,246,0.02)`})]})]}),[1,2,3,4].map(e=>(0,h.jsx)(`line`,{x1:`0`,y1:e*42,x2:720,y2:e*42,stroke:`#e2e8f0`,strokeWidth:`1`},e)),(0,h.jsx)(`path`,{d:`${i} L 720 210 L 0 210 Z`,fill:`url(#areaA)`,stroke:`none`}),(0,h.jsx)(`path`,{d:`${a} L 720 210 L 0 210 Z`,fill:`url(#areaB)`,stroke:`none`}),(0,h.jsx)(`path`,{d:i,fill:`none`,stroke:`#635bff`,strokeWidth:`2.4`,strokeLinecap:`round`}),(0,h.jsx)(`path`,{d:a,fill:`none`,stroke:`#3b82f6`,strokeWidth:`2.4`,strokeLinecap:`round`})]}),(0,h.jsx)(`div`,{className:`trend-labels`,children:c.map((e,t)=>(0,h.jsx)(`span`,{children:e},`${e}-${t}`))})]})]})}function S(e){return(0,h.jsxs)(`section`,{className:`card service-card endpoint-card`,children:[(0,h.jsx)(`div`,{className:`section-head compact`,children:(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h3`,{children:`网关信息`}),(0,h.jsx)(`p`,{children:`桌面端与 CLI 共享同一套本地服务。`})]})}),(0,h.jsxs)(`div`,{className:`service-list compact-grid`,children:[(0,h.jsx)(p,{label:`管理页`,value:e.config?.adminUrl||`-`,code:!0}),(0,h.jsx)(p,{label:`Base URL`,value:e.config?.baseUrl||`-`,code:!0}),(0,h.jsx)(p,{label:`默认模型`,value:e.config?.settings.defaultModel||`-`}),(0,h.jsx)(p,{label:`生图模型`,value:`gpt-image-2`}),(0,h.jsx)(p,{label:`兼容接口`,value:e.config?.supportedEndpoints.map(e=>e.path).join(`,`)||`-`}),(0,h.jsx)(p,{label:`令牌预览`,value:e.config?.profile?.accessTokenPreview||`未登录`,code:!0}),(0,h.jsx)(p,{label:`模型来源`,value:e.config?.modelCatalog.source||`-`})]})]})}function C(e){let[t,n]=(0,m.useState)(60),s=e.requestLogs.length?e.requestLogs.reduce((e,t)=>e+t.durationMs,0)/e.requestLogs.length:0;return(0,h.jsxs)(h.Fragment,{children:[(0,h.jsxs)(`section`,{className:`summary-grid desktop-summary-grid overview-summary-grid`,children:[(0,h.jsx)(g,{icon:f,label:`账号总数`,value:String(e.config?.status.profileCount||0),detail:`已保存到本地账号池`,tone:`blue`}),(0,h.jsx)(g,{icon:a,label:`当前账号状态`,value:(0,h.jsx)(_,{apiProfile:e.activeProfile,codexProfile:e.codexProfile,codexEmail:e.codexEmail,codexAccountId:e.codexAccountId,showEmails:e.showEmails}),detail:e.config?.status.loggedIn||e.codexProfile?``:`需要先登录或导入账号`,tone:e.config?.status.loggedIn||e.codexProfile?`green`:`orange`,compact:!0}),(0,h.jsx)(g,{icon:o,label:`今日请求数`,value:String(e.requestLogs.length),detail:`基于本页最近测试记录`,tone:`blue`}),(0,h.jsx)(g,{icon:i,label:`平均耗时`,value:e.requestLogs.length?u(s):`-`,detail:`统计最近 ${e.requestLogs.length} 次`,tone:`orange`}),(0,h.jsx)(g,{icon:d,label:`服务状态`,value:e.config?.status.loggedIn?`运行中`:`等待登录`,detail:`网关可转发请求`,tone:e.config?.status.loggedIn?`green`:`orange`}),(0,h.jsx)(g,{icon:r,label:`默认模型`,value:e.config?.settings.defaultModel||`-`,detail:`未显式指定 model 时生效`,tone:`green`})]}),(0,h.jsxs)(`section`,{className:`overview-grid`,children:[(0,h.jsx)(x,{config:e.config,requestLogs:e.requestLogs,windowMinutes:t,onWindow:n}),(0,h.jsx)(S,{config:e.config})]})]})}export{C as OverviewPage};
@@ -0,0 +1 @@
1
+ function e(e){return JSON.stringify(e,null,2)}function t(e){return e?new Intl.DateTimeFormat(`zh-CN`,{month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`}).format(new Date(e)):`-`}function n(e){if(!(typeof e!=`number`||!Number.isFinite(e)||e<=0))return e<0xe8d4a51000?e*1e3:e}function r(e){return e?new Intl.DateTimeFormat(`zh-CN`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`}).format(new Date(e)):`-`}function i(e){return Number.isFinite(e)?e<1e3?`${Math.round(e)} ms`:`${(e/1e3).toFixed(e<1e4?2:1)} s`:`-`}function a(e){if(!Number.isFinite(e)||e<=0)return`0 B`;let t=[`B`,`KiB`,`MiB`,`GiB`],n=Math.min(Math.floor(Math.log(e)/Math.log(1024)),t.length-1),r=e/1024**n;return`${r.toFixed(n===0?0:r>=10?1:2)} ${t[n]}`}function o(e){return typeof e!=`number`||!Number.isFinite(e)?0:Math.max(0,Math.min(100,Math.round(e)))}function s(e){return e?.quota?.planType||`unknown`}function c(e){let t=s(e).toLowerCase();return t.includes(`enterprise`)||t.includes(`business`)?60:t.includes(`team`)?50:t.includes(`pro`)||t.includes(`premium`)?40:t.includes(`plus`)?30:t.includes(`free`)?10:0}function l(e){let t=s(e).toLowerCase();return t.includes(`plus`)?`plus`:t.includes(`pro`)?`pro`:t.includes(`enterprise`)||t.includes(`business`)?`enterprise`:t.includes(`team`)?`team`:t.includes(`free`)?`free`:t.includes(`premium`)?`premium`:`unknown`}function u(e){let[t,n]=e.split(`@`);return n?`${t.slice(0,2)}${`*`.repeat(Math.max(3,Math.min(5,t.length)))}@${n}`:d(e)}function d(e){return e?e.length<=10?e:`${e.slice(0,4)}...${e.slice(-4)}`:`未提供`}function f(e,t){return e?e.email?t?e.email:u(e.email):t?e.accountId||e.profileId:d(e.accountId||e.profileId):`未登录`}function p(e){return(e.email||e.accountId||e.profileId||`A`).trim().slice(0,1).toUpperCase()}function m(e){return o(e.quota?.primaryUsedPercent)}function h(e){return 100-m(e)}function g(e){return o(e.quota?.secondaryUsedPercent)}function _(e){return e>=95?`red`:e>=75?`orange`:`blue`}function v(e,t){return e.isActive&&t?{className:`dual`,label:`API + Codex`}:e.isActive?{className:`api-only`,label:`API`}:t?{className:`codex-only`,label:`Codex`}:null}function y(e){return m(e)>=100||g(e)>=100}function b(e){return e.authStatus?.state===`token_invalidated`||e.authStatus?.state===`auth_error`}function x(e){return b(e)||!!(e.expiresAt&&e.expiresAt<=Date.now())}function S(e,t){return e.isActive||t&&e.accountId===t?0:x(e)?2:1}function C(e){let t=e.authStatus;return!t||t.state===`ok`?t?.checkedAt?`正常 · ${r(t.checkedAt)}`:`正常`:`${t.state===`token_invalidated`?`登录失效`:`认证异常`}${t.code||t.httpStatus?` (${t.code||t.httpStatus})`:``} · ${r(t.checkedAt)}`}function w(e){return e.authStatus?.state===`token_invalidated`?{key:`invalid`,label:`登录失效`,tone:`red`}:e.authStatus?.state===`auth_error`?{key:`invalid`,label:`认证异常`,tone:`red`}:e.expiresAt&&e.expiresAt<=Date.now()?{key:`expired`,label:`已过期`,tone:`red`}:y(e)?{key:`exhausted`,label:`额度耗尽`,tone:`orange`}:m(e)>=75||g(e)>=75?{key:`warning`,label:`即将耗尽`,tone:`orange`}:{key:`healthy`,label:`健康`,tone:`green`}}function T(e,t){let n=t===`primary`?e.quota?.primaryWindowMinutes:e.quota?.secondaryWindowMinutes;return n?n<60?`${n} 分钟重置`:n<1440?`${Math.round(n/60)} 小时重置`:`${Math.round(n/60/24)} 天重置`:t===`primary`?`主额度重置`:`周重置`}function E(e,r){let i=r===`primary`?e.quota?.primaryResetAt:e.quota?.secondaryResetAt,a=r===`primary`?e.quota?.primaryResetAfterSeconds:e.quota?.secondaryResetAfterSeconds,o=n(i);return o?t(o):typeof a==`number`&&a>0?t((n(e.quota?.capturedAt)||Date.now())+a*1e3):`-`}function D(e){return e?e.authStatus?.state===`token_invalidated`||e.authStatus?.state===`auth_error`?{ok:!1,detail:`账号认证已失效,请重新登录后再测试图片接口。`}:y(e)?{ok:!0,detail:`账号额度可能不足,实际以接口返回为准。`}:{ok:!0,detail:`当前账号可使用图片生成接口。`}:{ok:!1,detail:`登录后可测试图片接口。`}}export{r as C,a as S,t as T,T as _,D as a,v as b,u as c,m as d,w as f,_ as g,S as h,s as i,d as l,f as m,l as n,b as o,p,c as r,y as s,C as t,h as u,E as v,e as w,i as x,g as y};
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]);export{t};
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`search`,[[`path`,{d:`m21 21-4.34-4.34`,key:`14j7rj`}],[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u`}]]);export{t};
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`server`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`2`,rx:`2`,ry:`2`,key:`ngkwjq`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,ry:`2`,key:`iecqi9`}],[`line`,{x1:`6`,x2:`6.01`,y1:`6`,y2:`6`,key:`16zg32`}],[`line`,{x1:`6`,x2:`6.01`,y1:`18`,y2:`18`,key:`nzw8ys`}]]);export{t};
@@ -0,0 +1 @@
1
+ import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./refresh-cw-CAAH2rqe.js";import{w as a}from"./profiles-DMOjJORP.js";import{_ as o,p as s,r as c}from"./index-DRe-tByu.js";var l=t(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]),u=e(n(),1),d=r();function f(e){return{defaultModel:e.settings.defaultModel,proxyEnabled:e.settings.networkProxy.enabled,proxyUrl:e.settings.networkProxy.url,proxyNoProxy:e.settings.networkProxy.noProxy||`localhost,127.0.0.1,::1`,autoSwitchEnabled:e.settings.autoSwitch.enabled,quotaSyncConcurrency:String(e.settings.runtime?.quotaSyncConcurrency||16),serverPort:String(e.settings.server.port||8787)}}function p(e){let[t,n]=(0,u.useState)({defaultModel:``,proxyEnabled:!1,proxyUrl:``,proxyNoProxy:`localhost,127.0.0.1,::1`,autoSwitchEnabled:!1,quotaSyncConcurrency:`16`,serverPort:`8787`}),[r,p]=(0,u.useState)(!1);(0,u.useEffect)(()=>{!e.config||r||n(f(e.config))},[e.config,r]);function m(e){n(t=>({...t,...e})),p(!0)}async function h(n){let r=Number.parseInt(t.serverPort,10);if(!Number.isInteger(r)||r<1||r>65535){e.setStatus(`端口必须是 1 到 65535 之间的整数。`);return}let i=Number.parseInt(t.quotaSyncConcurrency,10);if(!Number.isInteger(i)||i<1||i>32){e.setStatus(`全局额度刷新并发数必须是 1 到 32 之间的整数。`);return}let o=n?.restart?`restart`:`settings`;e.setBusy(o);try{let o=await s(`/_gateway/admin/settings`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:a({defaultModel:t.defaultModel,networkProxy:{enabled:t.proxyEnabled,url:t.proxyUrl,noProxy:t.proxyNoProxy},autoSwitch:{enabled:t.autoSwitchEnabled},runtime:{quotaSyncConcurrency:i},server:{port:r}})});e.setConfig(o),p(!1),n?.restart?(e.setStatus(`设置已保存,正在重启本地网关...`),await s(`/_gateway/admin/restart`,{method:`POST`}),e.setStatus(`本地网关正在重启,页面会自动恢复。`)):e.setStatus(`设置已保存。`)}catch(t){e.setStatus(c(t))}finally{e.setBusy(null)}}async function g(){e.setBusy(`proxy`);try{let n=await s(`/_gateway/admin/settings/proxy-test`,{method:`POST`,headers:{"Content-Type":`application/json`},body:a({networkProxy:{enabled:t.proxyEnabled,url:t.proxyUrl,noProxy:t.proxyNoProxy}})});e.setStatus(`代理测试通过: HTTP ${n.status},耗时 ${n.elapsedMs} ms。`)}catch(t){e.setStatus(`代理测试失败: ${c(t)}`)}finally{e.setBusy(null)}}async function _(){e.setBusy(`models`);try{await s(`/_gateway/models/refresh`,{method:`POST`}),await e.refreshConfig({silent:!0}),e.setStatus(`Codex 模型列表已同步。`)}catch(t){e.setStatus(c(t))}finally{e.setBusy(null)}}return(0,d.jsxs)(`section`,{className:`settings-page`,children:[(0,d.jsxs)(`div`,{className:`settings-page-head`,children:[(0,d.jsxs)(`div`,{children:[(0,d.jsxs)(`div`,{className:`settings-page-kicker`,children:[(0,d.jsx)(l,{size:14}),`系统设置`]}),(0,d.jsx)(`h2`,{children:`本地网关和运行策略`}),(0,d.jsx)(`p`,{children:`设置会保存到本地状态目录,CLI、桌面端和本地服务共享。`})]}),(0,d.jsx)(`div`,{className:`settings-page-actions`,children:(0,d.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:_,disabled:e.busy===`models`,children:[e.busy===`models`?(0,d.jsx)(o,{className:`spin`,size:16}):(0,d.jsx)(i,{size:16}),`同步 Codex 模型`]})})]}),(0,d.jsxs)(`div`,{className:`settings-grid`,children:[(0,d.jsxs)(`section`,{className:`settings-section`,children:[(0,d.jsx)(`h4`,{children:`模型`}),(0,d.jsxs)(`label`,{className:`field`,children:[(0,d.jsx)(`span`,{children:`默认文本模型`}),(0,d.jsx)(`select`,{className:`control`,value:t.defaultModel,onChange:e=>m({defaultModel:e.target.value}),children:(e.config?.models||[]).map(e=>(0,d.jsx)(`option`,{value:e.id,children:e.id},e.id))})]}),(0,d.jsxs)(`p`,{className:`hint`,children:[`模型列表来源:`,e.config?.modelCatalog.source||`-`,`,共 `,e.config?.modelCatalog.modelCount||0,` 个。`]})]}),(0,d.jsxs)(`section`,{className:`settings-section`,children:[(0,d.jsx)(`h4`,{children:`上游代理`}),(0,d.jsxs)(`label`,{className:`switch-line`,children:[(0,d.jsx)(`input`,{type:`checkbox`,checked:t.proxyEnabled,onChange:e=>m({proxyEnabled:e.target.checked})}),(0,d.jsx)(`span`,{children:`启用 OAuth、模型刷新和接口转发代理`})]}),(0,d.jsxs)(`label`,{className:`field`,children:[(0,d.jsx)(`span`,{children:`代理地址`}),(0,d.jsx)(`input`,{className:`input`,value:t.proxyUrl,onChange:e=>m({proxyUrl:e.target.value}),placeholder:`http://127.0.0.1:7890`})]}),(0,d.jsxs)(`label`,{className:`field`,children:[(0,d.jsx)(`span`,{children:`No Proxy`}),(0,d.jsx)(`input`,{className:`input`,value:t.proxyNoProxy,onChange:e=>m({proxyNoProxy:e.target.value})})]}),(0,d.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:g,disabled:e.busy===`proxy`,children:`测试代理`})]}),(0,d.jsxs)(`section`,{className:`settings-section`,children:[(0,d.jsx)(`h4`,{children:`端口`}),(0,d.jsxs)(`label`,{className:`field`,children:[(0,d.jsx)(`span`,{children:`网关端口`}),(0,d.jsx)(`input`,{className:`input`,inputMode:`numeric`,type:`number`,min:1,max:65535,value:t.serverPort,onChange:e=>m({serverPort:e.target.value})})]}),(0,d.jsx)(`p`,{className:`hint`,children:`修改后重启本地网关生效,桌面窗口不会退出。若端口被占用,启动时会自动顺延到下一个可用端口。`})]}),(0,d.jsxs)(`section`,{className:`settings-section`,children:[(0,d.jsx)(`h4`,{children:`账号运行策略`}),(0,d.jsxs)(`label`,{className:`switch-line`,children:[(0,d.jsx)(`input`,{type:`checkbox`,checked:t.autoSwitchEnabled,onChange:e=>m({autoSwitchEnabled:e.target.checked})}),(0,d.jsx)(`span`,{children:`当前 API 账号额度耗尽后自动切换到下一个仍有额度的账号`})]}),(0,d.jsxs)(`label`,{className:`field`,children:[(0,d.jsx)(`span`,{children:`全局额度刷新并发数`}),(0,d.jsx)(`input`,{className:`input`,inputMode:`numeric`,max:32,min:1,type:`number`,value:t.quotaSyncConcurrency,onChange:e=>m({quotaSyncConcurrency:e.target.value})})]}),(0,d.jsx)(`p`,{className:`hint`,children:`手动刷新全部账号额度时使用,默认 16。账号很多可以调高,遇到限流或失败增多时调低。`}),(0,d.jsx)(`p`,{className:`hint`,children:e.status})]}),(0,d.jsxs)(`section`,{className:`settings-section`,children:[(0,d.jsx)(`h4`,{children:`显示`}),(0,d.jsxs)(`label`,{className:`switch-line`,children:[(0,d.jsx)(`input`,{type:`checkbox`,checked:e.showEmails,onChange:t=>e.setShowEmails(t.target.checked)}),(0,d.jsx)(`span`,{children:`脱敏模式`})]}),(0,d.jsx)(`p`,{className:`hint`,children:`开启后账号邮箱将以脱敏形式展示。`})]})]}),(0,d.jsxs)(`div`,{className:`settings-page-actions settings-page-footer-actions`,children:[(0,d.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:()=>void h(),disabled:e.busy===`settings`||e.busy===`restart`||!r,children:`保存设置`}),(0,d.jsx)(`button`,{className:`btn-primary`,type:`button`,onClick:()=>void h({restart:!0}),disabled:e.busy===`settings`||e.busy===`restart`||!r||!e.config?.restartSupported,children:`保存并重启网关`})]})]})}export{p as SettingsPage};
@@ -0,0 +1,3 @@
1
+ import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./upload-CwXb7Q1b.js";import{t as a}from"./zap-B4_oDbCp.js";import{S as o,m as s,w as c,x as l}from"./profiles-DMOjJORP.js";import{_ as u,a as d,b as f,c as p,i as m,l as h,n as g,o as _,p as v,r as y,s as b,t as x,u as S}from"./index-DRe-tByu.js";var C=t(`rotate-ccw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]),w=e(n(),1),T=r();function E(e){let t=e.endpoint.startsWith(`/v1/images/`);function n(t){let n=t.currentTarget.files?.[0];t.currentTarget.value=``,n&&e.onImageUpload(n,e.imageUploadMode).catch(()=>void 0)}return(0,T.jsxs)(`section`,{className:`card tester-card`,id:`tester`,children:[(0,T.jsxs)(`div`,{className:`section-head compact`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`h2`,{children:`快速测试`}),(0,T.jsx)(`p`,{children:`页面直接调用当前网关暴露的 OpenAI 风格接口。`})]}),(0,T.jsx)(`span`,{className:`badge brand`,children:e.busy===`test`?`请求中`:`准备就绪`})]}),(0,T.jsx)(`div`,{className:`tester-tabs`,children:e.endpoints.map(t=>(0,T.jsx)(`button`,{className:`tab-btn ${e.endpoint===t.path?`is-active`:``}`,type:`button`,onClick:()=>e.onEndpoint(t.path),children:S[t.path]||t.path},t.path))}),(0,T.jsxs)(`div`,{className:`tester-workbench`,children:[(0,T.jsxs)(`div`,{className:`tester-pane tester-request-pane`,children:[(0,T.jsxs)(`label`,{className:`field`,children:[(0,T.jsx)(`span`,{children:`接口`}),(0,T.jsx)(`select`,{className:`control`,value:e.endpoint,onChange:t=>e.onEndpoint(t.target.value),children:e.endpoints.map(e=>(0,T.jsxs)(`option`,{value:e.path,children:[e.method,` `,e.path]},e.path))})]}),(0,T.jsxs)(`div`,{className:`tester-copy-row tester-copy-row-top`,children:[(0,T.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onCopyRequest,children:[(0,T.jsx)(f,{size:16}),`复制请求`]}),(0,T.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onCopyResponse,children:[(0,T.jsx)(f,{size:16}),`复制响应`]}),(0,T.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onCopyTiming,children:[(0,T.jsx)(f,{size:16}),`复制日志`]}),(0,T.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onResetExample,children:[(0,T.jsx)(C,{size:16}),`重置示例`]})]}),(0,T.jsxs)(`label`,{className:`field tester-body-field`,children:[(0,T.jsx)(`span`,{children:`请求体 JSON`}),(0,T.jsx)(`textarea`,{className:`textarea tester-textarea`,value:e.requestBody,onChange:t=>e.onRequestBody(t.target.value),disabled:e.activeEndpoint.method===`GET`,spellCheck:!1})]}),e.endpoint===`/v1/images/edits`&&(0,T.jsxs)(`div`,{className:`edit-upload-row`,children:[(0,T.jsxs)(`div`,{className:`edit-upload-mode`,role:`group`,"aria-label":`图片写入方式`,children:[(0,T.jsx)(`span`,{children:`写入方式`}),(0,T.jsxs)(`div`,{className:`edit-upload-toggle`,children:[(0,T.jsx)(`button`,{className:`tab-btn ${e.imageUploadMode===`base64`?`is-active`:``}`,type:`button`,onClick:()=>e.onImageUploadMode(`base64`),children:`Base64`}),(0,T.jsx)(`button`,{className:`tab-btn ${e.imageUploadMode===`image-bed`?`is-active`:``}`,type:`button`,onClick:()=>e.onImageUploadMode(`image-bed`),children:`图床`})]})]}),(0,T.jsxs)(`label`,{className:`btn-secondary upload-btn`,title:e.imageUploadMode===`base64`?`上传图片并写入 base64 data URL`:`上传图片到图床并写入公网链接`,children:[e.imageUploadMode===`image-bed`&&e.busy===`image-bed-upload`?(0,T.jsx)(u,{className:`spin`,size:16}):(0,T.jsx)(i,{size:16}),e.imageUploadMode===`base64`?`上传并写入 Base64`:`上传并写入图床链接`,(0,T.jsx)(`input`,{type:`file`,accept:`image/*`,onChange:n})]}),(0,T.jsxs)(`span`,{children:[`目标字段:images[0].image_url · `,e.imageUploadMode===`base64`?`直接写入 data URL`:`先上传图床再写入公网链接`]})]}),(0,T.jsx)(`p`,{className:`hint`,children:t?e.capability.detail:e.activeEndpoint.description||`GET /v1/models 无需请求体。`}),(0,T.jsxs)(`div`,{className:`tester-actions-bar`,children:[(0,T.jsx)(`div`,{className:`tester-actions-group`,children:(0,T.jsx)(`div`,{className:`example-row`,children:p.map(t=>(0,T.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:()=>e.onEndpoint(t),disabled:!e.endpoints.some(e=>e.path===t),children:[`示例 `,S[t]||t]},t))})}),(0,T.jsxs)(`button`,{className:`btn-primary`,type:`button`,onClick:e.onRun,disabled:e.busy===`test`||t&&!e.config?.profile,children:[e.busy===`test`?(0,T.jsx)(u,{className:`spin`,size:16}):(0,T.jsx)(a,{size:16}),`发送请求`]})]})]}),(0,T.jsxs)(`div`,{className:`tester-pane tester-response-pane`,children:[(0,T.jsxs)(`div`,{className:`tester-result-head`,children:[(0,T.jsxs)(`div`,{className:`tester-result-tabs`,children:[(0,T.jsx)(`button`,{className:`tab-btn ${e.resultTab===`response`?`is-active`:``}`,type:`button`,onClick:()=>e.onResultTab(`response`),children:`响应 JSON`}),(0,T.jsx)(`button`,{className:`tab-btn ${e.resultTab===`timing`?`is-active`:``}`,type:`button`,onClick:()=>e.onResultTab(`timing`),children:`耗时日志`}),(0,T.jsx)(`button`,{className:`tab-btn ${e.resultTab===`preview`?`is-active`:``}`,type:`button`,onClick:()=>e.onResultTab(`preview`),children:`图片预览`})]}),(0,T.jsx)(`p`,{className:`status-inline`,children:e.status})]}),e.resultTab===`response`&&(0,T.jsx)(`pre`,{className:`pre`,children:e.responseBody}),e.resultTab===`timing`&&(0,T.jsx)(`pre`,{className:`pre`,children:e.timingBody}),e.resultTab===`preview`&&(0,T.jsx)(`div`,{className:`preview-panel`,children:e.previewImages.length===0?(0,T.jsx)(`div`,{className:`preview-empty`,children:`图片结果会显示在这里。点击缩略图可查看大图。`}):(0,T.jsx)(`div`,{className:`preview-grid`,children:e.previewImages.map(t=>(0,T.jsxs)(`figure`,{className:`preview-card`,children:[(0,T.jsx)(`button`,{type:`button`,onClick:()=>e.onPreview({src:t.src,meta:t.meta,filename:t.filename}),children:(0,T.jsx)(`img`,{src:t.src,alt:t.meta})}),(0,T.jsx)(`figcaption`,{children:t.meta}),(0,T.jsx)(`div`,{className:`preview-actions`,children:(0,T.jsx)(`a`,{href:t.src,download:t.filename,children:`下载`})})]},t.filename))})})]})]})]})}function D(e){let[t,n]=(0,w.useState)(`/v1/models`),[r,i]=(0,w.useState)(``),[a,u]=(0,w.useState)(`等待请求...`),[f,S]=(0,w.useState)(`等待请求...`),[C,D]=(0,w.useState)(`response`),[O,k]=(0,w.useState)([]),[A,j]=(0,w.useState)(`base64`),M=(0,w.useMemo)(()=>[...e.config?.supportedEndpoints||[]].sort(h),[e.config?.supportedEndpoints]),N=(0,w.useMemo)(()=>M.find(e=>e.path===t)||M[0]||{method:`GET`,path:`/v1/models`,description:``},[t,M]);(0,w.useEffect)(()=>{if(!e.config)return;let r=p.find(t=>e.config?.supportedEndpoints.some(e=>e.path===t)),a=e.config.supportedEndpoints.some(e=>e.path===t)?t:r||`/v1/models`;n(a),i(x(a,e.config.settings.defaultModel))},[e.config]);function P(t){n(t),i(x(t,e.config?.settings.defaultModel||`gpt-5.4`)),k([])}function F(){P(t)}function I(){g(r||x(t,e.config?.settings.defaultModel||`gpt-5.4`)).then(t=>e.setStatus(t?`请求体已复制。`:`请求体复制失败。`)).catch(()=>e.setStatus(`请求体复制失败。`))}function L(){g(a).then(t=>e.setStatus(t?`响应内容已复制。`:`响应内容复制失败。`)).catch(()=>e.setStatus(`响应内容复制失败。`))}function R(){g(f).then(t=>e.setStatus(t?`耗时日志已复制。`:`耗时日志复制失败。`)).catch(()=>e.setStatus(`耗时日志复制失败。`))}async function z(){let t=N,n=performance.now(),i=[];e.setBusy(`test`),D(`response`),u(`请求发送中...`),S(`请求发送中...`),k([]);try{let a=null,o={method:t.method,headers:{}};if(t.method!==`GET`){let e=performance.now();a=r.trim()?JSON.parse(r):{},i.push(`解析请求体: ${l(performance.now()-e)}`),o.headers[`Content-Type`]=`application/json`,o.body=c(a)}let d=performance.now(),f=await fetch(t.path,o);i.push(`等待响应头: ${l(performance.now()-d)}`);let p=performance.now(),h=await f.text();i.push(`读取响应体: ${l(performance.now()-p)}`);let g=performance.now(),_=h;try{_=h?JSON.parse(h):null}catch{_=h}i.push(`解析响应体: ${l(performance.now()-g)}`);let v=m(_);k(v),v.length>0&&D(`preview`),u(typeof _==`string`?_:c(b(_))),S([`${t.method} ${t.path}`,`HTTP 状态: ${f.status} ${f.statusText}`,...i].join(`
2
+ `)),e.setStatus(`${f.ok?`成功`:`失败`}: HTTP ${f.status} ${t.method} ${t.path}`),e.setRequestLogs(r=>[{id:crypto.randomUUID(),time:Date.now(),method:t.method,endpoint:t.path,account:s(e.config?.profile,e.showEmails),model:typeof a==`object`&&a&&`model`in a?String(a.model||e.config?.settings.defaultModel||`-`):e.config?.settings.defaultModel||`-`,statusCode:f.status,durationMs:performance.now()-n,source:`管理台`},...r].slice(0,20)),e.config?.profile&&e.refreshConfig({silent:!0}).catch(()=>void 0)}catch(n){let r=y(n);u(r),S([`${t.method} ${t.path}(失败)`,...i,`错误: ${r}`].join(`
3
+ `)),e.setStatus(`请求失败。`)}finally{e.setBusy(null)}}async function B(t,n){if(!t.type.startsWith(`image/`)){e.setStatus(`请选择图片文件。`);return}try{if(n===`image-bed`){e.setBusy(`image-bed-upload`);let n=await _(t);i(d(r,(await v(`/_gateway/image-bed/upload`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({filename:t.name,dataUrl:n})})).url,e.config?.settings.defaultModel||`gpt-image-2`)),e.setStatus(`已上传到图床并写入公网链接(${t.name},${o(t.size)})。`);return}i(d(r,await _(t),e.config?.settings.defaultModel||`gpt-image-2`)),e.setStatus(`已将 ${t.name} 转成 base64 data URL,并写入请求体 images[0].image_url(${o(t.size)})。`)}catch(t){e.setStatus(`图片写入失败: ${y(t)}`)}finally{n===`image-bed`&&e.setBusy(null)}}return(0,T.jsx)(E,{config:e.config,endpoints:M,activeEndpoint:N,endpoint:t,requestBody:r,responseBody:a,timingBody:f,resultTab:C,status:e.status,busy:e.busy,previewImages:O,capability:e.capability,imageUploadMode:A,onEndpoint:P,onRequestBody:i,onResultTab:D,onRun:z,onResetExample:F,onCopyRequest:I,onCopyResponse:L,onCopyTiming:R,onImageUploadMode:j,onPreview:e.setPreviewImage,onImageUpload:B})}export{D as TesterPage};
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]);export{t};
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`zap`,[[`path`,{d:`M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z`,key:`1xq2db`}]]);export{t};
@@ -3,10 +3,12 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <link rel="icon" type="image/svg+xml" href="/assets/app-mark-Gd2QnHMO.svg" />
6
+ <link rel="icon" type="image/svg+xml" href="/assets/app-mark-nsRs4vo7.svg" />
7
7
  <title>AI Zero Token</title>
8
- <script type="module" crossorigin src="/assets/index-DZMegNPs.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DNzR8XR7.css">
8
+ <script type="module" crossorigin src="/assets/index-DRe-tByu.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-DqpGtLhh.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/profiles-DMOjJORP.js">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-By4r-wy3.css">
10
12
  </head>
11
13
  <body>
12
14
  <div id="root"></div>
package/build/icon.icns CHANGED
Binary file
package/build/icon.ico CHANGED
Binary file
package/build/icon.png CHANGED
Binary file
package/build/icon.svg ADDED
@@ -0,0 +1,8 @@
1
+ <svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="1024" height="1024" fill="none"/>
3
+ <rect x="96" y="96" width="832" height="832" rx="192" fill="#F8FAFC"/>
4
+ <text x="512" y="610" text-anchor="middle" font-family="Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" font-size="258" font-weight="900" letter-spacing="-19" fill="#111827">azt</text>
5
+ <path d="M250 748c84 38 440 38 524 0" stroke="#2DD4BF" stroke-width="38" stroke-linecap="round"/>
6
+ <circle cx="332" cy="324" r="24" fill="#3B82F6"/>
7
+ <circle cx="692" cy="324" r="24" fill="#F97316"/>
8
+ </svg>
@@ -0,0 +1,25 @@
1
+ AI Zero Token macOS 安装说明
2
+
3
+ 如果打开 App 时看到“Apple 无法验证 AI Zero Token 是否包含可能危害 Mac 安全或泄漏隐私的恶意软件”,这是因为当前安装包尚未接入 Apple Developer ID 公证。
4
+
5
+ 临时打开方式:
6
+
7
+ 1. 将 AI Zero Token 拖入 Applications。
8
+ 2. 打开“系统设置”。
9
+ 3. 进入“隐私与安全性”。
10
+ 4. 在安全提示区域点击“仍要打开”。
11
+ 5. 再次确认打开 AI Zero Token。
12
+
13
+ 也可以使用:
14
+
15
+ 1. 在 Applications 中找到 AI Zero Token。
16
+ 2. 按住 Control 并点击 App,选择“打开”。
17
+ 3. 在确认窗口中再次点击“打开”。
18
+
19
+ 安全说明:
20
+
21
+ - 这是 macOS Gatekeeper 对未公证应用的标准提示。
22
+ - 这不代表当前安装包一定有问题。
23
+ - 如果你从 GitHub Releases 官方页面下载,并且版本号与发布说明一致,可以按上面的步骤打开。
24
+ - 后续版本接入 Apple Developer ID 签名和 notarization 后,这个提示会消失。
25
+
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { ConfigService } from "./services/config-service.js";
3
3
  import { AuthService } from "./services/auth-service.js";
4
+ import { GithubImageBedService } from "./services/github-image-bed-service.js";
4
5
  import { ChatService } from "./services/chat-service.js";
5
6
  import { ImageService } from "./services/image-service.js";
6
7
  import { ModelService } from "./services/model-service.js";
@@ -12,6 +13,7 @@ function createGatewayContext() {
12
13
  const modelService = new ModelService(configService);
13
14
  const versionService = new VersionService();
14
15
  const networkDetectService = new NetworkDetectService();
16
+ const githubImageBedService = new GithubImageBedService();
15
17
  const chatService = new ChatService({
16
18
  authService,
17
19
  modelService
@@ -26,6 +28,7 @@ function createGatewayContext() {
26
28
  modelService,
27
29
  versionService,
28
30
  networkDetectService,
31
+ githubImageBedService,
29
32
  chatService,
30
33
  imageService
31
34
  };
@@ -65,6 +65,7 @@ function normalizeCurlHeaders(value) {
65
65
  async function runCurlRequest(init, params) {
66
66
  const requestId = params?.requestId ?? nextRequestId();
67
67
  const startedAt = performance.now();
68
+ const timeoutSeconds = typeof params?.timeoutMs === "number" && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? Math.max(1, Math.ceil(params.timeoutMs / 1e3)) : void 0;
68
69
  const args = [
69
70
  "--silent",
70
71
  "--show-error",
@@ -75,6 +76,10 @@ async function runCurlRequest(init, params) {
75
76
  "--write-out",
76
77
  `${CURL_STATUS_MARKER}%{http_code}${CURL_HEADERS_MARKER}%{header_json}`
77
78
  ];
79
+ if (typeof timeoutSeconds === "number") {
80
+ args.push("--connect-timeout", String(Math.min(timeoutSeconds, 10)));
81
+ args.push("--max-time", String(timeoutSeconds));
82
+ }
78
83
  if (params?.proxy?.enabled && params.proxy.url.trim()) {
79
84
  args.push("--proxy", params.proxy.url.trim());
80
85
  if (params.proxy.noProxy.trim()) {
@@ -174,13 +179,13 @@ async function loadNetworkProxySettings() {
174
179
  }
175
180
  async function requestText(init) {
176
181
  const requestId = nextRequestId();
182
+ const requestStartedAt = performance.now();
177
183
  const proxy = init.ignoreProxy ? void 0 : init.proxyOverride ?? await loadNetworkProxySettings();
178
184
  const useCurlOnly = process.env.OAUTH_DEMO_USE_CURL === "1";
179
185
  const useConfiguredProxy = !!proxy?.enabled && !!proxy.url.trim();
180
186
  const timeoutMs = init.timeoutMs;
181
187
  const signal = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : void 0;
182
188
  if (!useCurlOnly && !useConfiguredProxy) {
183
- const startedAt = performance.now();
184
189
  const phases = {};
185
190
  try {
186
191
  const fetchStartedAt = performance.now();
@@ -194,7 +199,7 @@ async function requestText(init) {
194
199
  const readBodyStartedAt = performance.now();
195
200
  const body = await response.text();
196
201
  phases.readBodyMs = performance.now() - readBodyStartedAt;
197
- const timing = finalizeTiming(startedAt, phases);
202
+ const timing = finalizeTiming(requestStartedAt, phases);
198
203
  logHttpTiming({
199
204
  requestId,
200
205
  method: init.method,
@@ -218,15 +223,17 @@ async function requestText(init) {
218
223
  requestId,
219
224
  method: init.method,
220
225
  url: init.url,
221
- elapsedMs: roundMs(performance.now() - startedAt),
226
+ elapsedMs: roundMs(performance.now() - requestStartedAt),
222
227
  error: message
223
228
  });
224
229
  }
225
230
  }
231
+ const remainingTimeoutMs = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.max(1e3, timeoutMs - (performance.now() - requestStartedAt)) : void 0;
226
232
  return runCurlRequest(init, {
227
233
  requestId,
228
234
  fallbackFrom: useCurlOnly || useConfiguredProxy ? void 0 : "fetch",
229
- proxy
235
+ proxy,
236
+ timeoutMs: remainingTimeoutMs
230
237
  });
231
238
  }
232
239
  export {
@@ -4,6 +4,7 @@ import {
4
4
  getActiveProfile,
5
5
  listProfiles,
6
6
  removeProfile,
7
+ removeProfiles as removeStoredProfiles,
7
8
  saveProfile,
8
9
  setActiveProfile,
9
10
  updateProfile
@@ -24,6 +25,28 @@ import {
24
25
  importProfileFromJson,
25
26
  importProfilesFromJson
26
27
  } from "../store/profile-transfer.js";
28
+ const DEFAULT_QUOTA_SYNC_CONCURRENCY = 16;
29
+ function getQuotaSyncConcurrency(configured) {
30
+ const raw = process.env.AZT_QUOTA_SYNC_CONCURRENCY;
31
+ const parsed = raw ? Number.parseInt(raw, 10) : configured ?? DEFAULT_QUOTA_SYNC_CONCURRENCY;
32
+ if (!Number.isFinite(parsed)) {
33
+ return configured ?? DEFAULT_QUOTA_SYNC_CONCURRENCY;
34
+ }
35
+ return Math.min(32, Math.max(1, parsed));
36
+ }
37
+ async function mapWithConcurrency(items, concurrency, worker) {
38
+ const results = new Array(items.length);
39
+ let nextIndex = 0;
40
+ const workerCount = Math.min(Math.max(1, concurrency), items.length);
41
+ await Promise.all(Array.from({ length: workerCount }, async () => {
42
+ while (nextIndex < items.length) {
43
+ const index = nextIndex;
44
+ nextIndex += 1;
45
+ results[index] = await worker(items[index]);
46
+ }
47
+ }));
48
+ return results;
49
+ }
27
50
  class AuthService {
28
51
  constructor(configService) {
29
52
  this.configService = configService;
@@ -51,7 +74,32 @@ class AuthService {
51
74
  accessTokenPreview: this.maskSecret(profile.access),
52
75
  refreshTokenPreview: this.maskSecret(profile.refresh),
53
76
  isActive: profile.profileId === activeProfileId,
54
- authStatus: profile.authStatus
77
+ authStatus: profile.authStatus,
78
+ exportAudit: profile.exportAudit
79
+ };
80
+ }
81
+ buildExportAudit(current, kind, exportedAt) {
82
+ return {
83
+ exported: true,
84
+ count: Math.max(0, current?.count ?? 0) + 1,
85
+ firstExportedAt: current?.firstExportedAt ?? exportedAt,
86
+ lastExportedAt: exportedAt,
87
+ lastExportKind: kind
88
+ };
89
+ }
90
+ async recordProfileExport(profile, kind, exportedAt) {
91
+ const updated = await updateProfile(profile.profileId, (current) => {
92
+ if (current.provider !== profile.provider) {
93
+ return current;
94
+ }
95
+ return {
96
+ ...current,
97
+ exportAudit: this.buildExportAudit(current.exportAudit, kind, exportedAt)
98
+ };
99
+ });
100
+ return updated ?? {
101
+ ...profile,
102
+ exportAudit: this.buildExportAudit(profile.exportAudit, kind, exportedAt)
55
103
  };
56
104
  }
57
105
  createOkAuthStatus() {
@@ -228,6 +276,12 @@ class AuthService {
228
276
  await saveProfile(profile);
229
277
  return this.toManagedProfile(profile);
230
278
  }
279
+ validateProfilesImport(value, provider = "openai-codex") {
280
+ if (provider !== "openai-codex") {
281
+ throw new Error(`\u6682\u4E0D\u652F\u6301 provider: ${provider}`);
282
+ }
283
+ return importProfilesFromJson(value);
284
+ }
231
285
  async importProfiles(value, provider = "openai-codex") {
232
286
  if (provider !== "openai-codex") {
233
287
  throw new Error(`\u6682\u4E0D\u652F\u6301 provider: ${provider}`);
@@ -246,16 +300,21 @@ class AuthService {
246
300
  if (!profile) {
247
301
  throw new Error(targetProfileId ? `\u6CA1\u6709\u627E\u5230\u53EF\u5BFC\u51FA\u7684\u8D26\u53F7: ${targetProfileId}` : "\u6CA1\u6709\u53EF\u5BFC\u51FA\u7684\u5F53\u524D\u8D26\u53F7\u3002");
248
302
  }
249
- return exportProfileToJson(profile);
303
+ const exportedAt = Date.now();
304
+ const exportedProfile = await this.recordProfileExport(profile, "single", exportedAt);
305
+ return exportProfileToJson(exportedProfile);
250
306
  }
251
- async exportProfiles(profileIds, provider = "openai-codex") {
307
+ async exportProfiles(profileIds, provider = "openai-codex", exportKind) {
252
308
  const profiles = await listProfiles();
253
309
  const idSet = profileIds && profileIds.length > 0 ? new Set(profileIds.map((item) => item.trim()).filter(Boolean)) : null;
254
310
  const selected = profiles.filter((item) => item.provider === provider).filter((item) => !idSet || idSet.has(item.profileId));
255
311
  if (selected.length === 0) {
256
312
  throw new Error("\u6CA1\u6709\u627E\u5230\u53EF\u5BFC\u51FA\u7684\u8D26\u53F7\u3002");
257
313
  }
258
- return exportProfilesToJson(selected);
314
+ const exportedAt = Date.now();
315
+ const kind = exportKind ?? (idSet ? "batch" : "all");
316
+ const exportedProfiles = await Promise.all(selected.map((profile) => this.recordProfileExport(profile, kind, exportedAt)));
317
+ return exportProfilesToJson(exportedProfiles);
259
318
  }
260
319
  getProfileImportTemplate() {
261
320
  return getProfileImportTemplate();
@@ -310,6 +369,18 @@ class AuthService {
310
369
  }
311
370
  await removeProfile(profileId);
312
371
  }
372
+ async removeProfiles(profileIds, provider = "openai-codex") {
373
+ const idSet = new Set(profileIds.map((id) => id.trim()).filter(Boolean));
374
+ if (idSet.size === 0) {
375
+ throw new Error("\u6CA1\u6709\u9009\u62E9\u8981\u5220\u9664\u7684\u8D26\u53F7\u3002");
376
+ }
377
+ const profiles = await listProfiles();
378
+ const matched = profiles.filter((profile) => profile.provider === provider && idSet.has(profile.profileId));
379
+ if (matched.length === 0) {
380
+ throw new Error("\u6CA1\u6709\u627E\u5230\u8981\u5220\u9664\u7684\u8D26\u53F7\u3002");
381
+ }
382
+ return removeStoredProfiles(matched.map((profile) => profile.profileId));
383
+ }
313
384
  async requireUsableProfile(provider = "openai-codex", options) {
314
385
  const activeProfile = await this.getActiveProfile(provider);
315
386
  const profile = activeProfile ? options?.skipAutoSwitch ? activeProfile : await this.maybeAutoSwitchProfile(activeProfile, provider) : null;
@@ -364,18 +435,23 @@ class AuthService {
364
435
  await this.syncQuotaForProfile(profile, model, provider, options);
365
436
  }
366
437
  async syncAllProfileQuotas(provider = "openai-codex", options) {
367
- const [profiles, activeProfile, model] = await Promise.all([
438
+ const [profiles, activeProfile, model, settings] = await Promise.all([
368
439
  listProfiles(),
369
440
  this.getActiveProfile(provider),
370
- this.configService.getDefaultModel(provider)
441
+ this.configService.getDefaultModel(provider),
442
+ this.configService.getSettings()
371
443
  ]);
372
444
  const providerProfiles = profiles.filter((profile) => profile.provider === provider);
373
- const skipped = providerProfiles.filter((profile) => this.hasInvalidAuthStatus(profile)).length;
374
- const targets = providerProfiles.filter((profile) => !this.hasInvalidAuthStatus(profile)).sort((left, right) => Number(left.profileId === activeProfile?.profileId) - Number(right.profileId === activeProfile?.profileId));
375
- const results = [];
376
- for (const profile of targets) {
377
- results.push(await this.syncQuotaForProfile(profile, model, provider, options));
378
- }
445
+ const now = Date.now();
446
+ const targets = providerProfiles.filter((profile) => !this.hasInvalidAuthStatus(profile)).filter((profile) => {
447
+ if (!options?.staleAfterMs) {
448
+ return true;
449
+ }
450
+ const capturedAt = profile.quota?.capturedAt;
451
+ return !capturedAt || now - capturedAt >= options.staleAfterMs;
452
+ }).sort((left, right) => Number(right.profileId === activeProfile?.profileId) - Number(left.profileId === activeProfile?.profileId));
453
+ const skipped = providerProfiles.length - targets.length;
454
+ const results = await mapWithConcurrency(targets, getQuotaSyncConcurrency(settings.runtime.quotaSyncConcurrency), (profile) => this.syncQuotaForProfile(profile, model, provider, options));
379
455
  const failed = results.filter((item) => !item.ok).length;
380
456
  return {
381
457
  total: providerProfiles.length,
@@ -3,8 +3,34 @@ import { getPreferredCodexModel, hasCodexModel } from "../models/openai-codex-mo
3
3
  import {
4
4
  createDefaultSettings,
5
5
  loadSettings,
6
+ normalizeQuotaSyncConcurrency,
6
7
  saveSettings
7
8
  } from "../store/settings-store.js";
9
+ function normalizeNetworkProxy(settings, params) {
10
+ const requestedUrl = params.url?.trim() ?? "";
11
+ const url = requestedUrl || (!params.enabled ? settings.networkProxy.url : "");
12
+ const noProxy = params.noProxy?.trim() || settings.networkProxy.noProxy || "localhost,127.0.0.1,::1";
13
+ if (params.enabled) {
14
+ if (!url) {
15
+ throw new Error("\u542F\u7528\u4EE3\u7406\u65F6\u5FC5\u987B\u586B\u5199\u4EE3\u7406\u5730\u5740\u3002");
16
+ }
17
+ let parsed;
18
+ try {
19
+ parsed = new URL(url);
20
+ } catch {
21
+ throw new Error("\u4EE3\u7406\u5730\u5740\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u586B\u5199\u5B8C\u6574\u7684\u4EE3\u7406 URL\u3002");
22
+ }
23
+ const supportedProtocols = /* @__PURE__ */ new Set(["http:", "https:", "socks4:", "socks4a:", "socks5:", "socks5h:"]);
24
+ if (!supportedProtocols.has(parsed.protocol)) {
25
+ throw new Error("\u4EE3\u7406\u5730\u5740\u4EC5\u652F\u6301 http\u3001https\u3001socks4\u3001socks4a\u3001socks5 \u6216 socks5h\u3002");
26
+ }
27
+ }
28
+ return {
29
+ enabled: params.enabled,
30
+ url,
31
+ noProxy
32
+ };
33
+ }
8
34
  class ConfigService {
9
35
  async getSettings() {
10
36
  return this.ensureSettings();
@@ -43,31 +69,9 @@ class ConfigService {
43
69
  }
44
70
  async setNetworkProxy(params) {
45
71
  const settings = await this.getSettings();
46
- const requestedUrl = params.url?.trim() ?? "";
47
- const url = requestedUrl || (!params.enabled ? settings.networkProxy.url : "");
48
- const noProxy = params.noProxy?.trim() || settings.networkProxy.noProxy || "localhost,127.0.0.1,::1";
49
- if (params.enabled) {
50
- if (!url) {
51
- throw new Error("\u542F\u7528\u4EE3\u7406\u65F6\u5FC5\u987B\u586B\u5199\u4EE3\u7406\u5730\u5740\u3002");
52
- }
53
- let parsed;
54
- try {
55
- parsed = new URL(url);
56
- } catch {
57
- throw new Error("\u4EE3\u7406\u5730\u5740\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u586B\u5199\u5B8C\u6574\u7684\u4EE3\u7406 URL\u3002");
58
- }
59
- const supportedProtocols = /* @__PURE__ */ new Set(["http:", "https:", "socks4:", "socks4a:", "socks5:", "socks5h:"]);
60
- if (!supportedProtocols.has(parsed.protocol)) {
61
- throw new Error("\u4EE3\u7406\u5730\u5740\u4EC5\u652F\u6301 http\u3001https\u3001socks4\u3001socks4a\u3001socks5 \u6216 socks5h\u3002");
62
- }
63
- }
64
72
  const next = {
65
73
  ...settings,
66
- networkProxy: {
67
- enabled: params.enabled,
68
- url,
69
- noProxy
70
- }
74
+ networkProxy: normalizeNetworkProxy(settings, params)
71
75
  };
72
76
  await saveSettings(next);
73
77
  return next;
@@ -83,6 +87,18 @@ class ConfigService {
83
87
  await saveSettings(next);
84
88
  return next;
85
89
  }
90
+ async setRuntimeConfig(params) {
91
+ const settings = await this.getSettings();
92
+ const next = {
93
+ ...settings,
94
+ runtime: {
95
+ ...settings.runtime,
96
+ quotaSyncConcurrency: normalizeQuotaSyncConcurrency(params.quotaSyncConcurrency, settings.runtime.quotaSyncConcurrency)
97
+ }
98
+ };
99
+ await saveSettings(next);
100
+ return next;
101
+ }
86
102
  async getServerConfig() {
87
103
  const settings = await this.getSettings();
88
104
  return settings.server;
@@ -99,6 +115,54 @@ class ConfigService {
99
115
  await saveSettings(next);
100
116
  return next;
101
117
  }
118
+ async updateSettings(params) {
119
+ const settings = await this.getSettings();
120
+ let next = { ...settings };
121
+ if (params.defaultModel) {
122
+ if (!await hasCodexModel(params.defaultModel)) {
123
+ throw new Error(`\u5F53\u524D\u7F51\u5173\u672A\u627E\u5230\u53EF\u7528\u6A21\u578B: ${params.defaultModel}`);
124
+ }
125
+ next = {
126
+ ...next,
127
+ defaultProvider: "openai-codex",
128
+ defaultModel: params.defaultModel
129
+ };
130
+ }
131
+ if (params.networkProxy) {
132
+ next = {
133
+ ...next,
134
+ networkProxy: normalizeNetworkProxy(next, params.networkProxy)
135
+ };
136
+ }
137
+ if (params.autoSwitch) {
138
+ next = {
139
+ ...next,
140
+ autoSwitch: {
141
+ enabled: params.autoSwitch.enabled
142
+ }
143
+ };
144
+ }
145
+ if (params.runtime) {
146
+ next = {
147
+ ...next,
148
+ runtime: {
149
+ ...next.runtime,
150
+ quotaSyncConcurrency: normalizeQuotaSyncConcurrency(params.runtime.quotaSyncConcurrency, next.runtime.quotaSyncConcurrency)
151
+ }
152
+ };
153
+ }
154
+ if (params.server) {
155
+ next = {
156
+ ...next,
157
+ server: {
158
+ ...next.server,
159
+ port: params.server.port
160
+ }
161
+ };
162
+ }
163
+ await saveSettings(next);
164
+ return next;
165
+ }
102
166
  async resetSettings() {
103
167
  const defaults = createDefaultSettings();
104
168
  await saveSettings(defaults);