@cartanova/qgrid-cli 2.6.2 → 2.6.5

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 (72) hide show
  1. package/README.ko.md +2 -0
  2. package/README.md +2 -0
  3. package/bundle/dist/application/qgrid/expired-token-digest.js +78 -0
  4. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +40 -6
  5. package/bundle/dist/application/qgrid/qgrid.frame.js +22 -7
  6. package/bundle/dist/application/qgrid/qgrid.types.js +13 -3
  7. package/bundle/dist/application/qgrid/token-death.js +29 -4
  8. package/bundle/dist/application/queries.generated.js +2 -1
  9. package/bundle/dist/application/request-log/request-log.model.js +37 -5
  10. package/bundle/dist/application/request-log-step/request-log-step.model.js +1 -1
  11. package/bundle/dist/application/sonamu.generated.js +41 -3
  12. package/bundle/dist/application/token/token.model.js +44 -14
  13. package/bundle/dist/i18n/en.js +3 -1
  14. package/bundle/dist/i18n/ko.js +3 -1
  15. package/bundle/dist/i18n/sd.generated.js +18 -1
  16. package/bundle/dist/migrations/20260807164057_alter_request_logs_alter1.js +13 -0
  17. package/bundle/dist/server-error-handler.js +5 -3
  18. package/bundle/dist/sonamu.config.js +8 -1
  19. package/bundle/dist/utils/slack-notify.js +2 -2
  20. package/bundle/src/application/qgrid/expired-token-digest.test.ts +49 -0
  21. package/bundle/src/application/qgrid/expired-token-digest.ts +93 -0
  22. package/bundle/src/application/qgrid/qgrid-oauth-redirect.test.ts +0 -3
  23. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +30 -2
  24. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +44 -6
  25. package/bundle/src/application/qgrid/qgrid.frame.ts +13 -4
  26. package/bundle/src/application/qgrid/qgrid.types.ts +10 -0
  27. package/bundle/src/application/qgrid/token-death.test.ts +33 -3
  28. package/bundle/src/application/qgrid/token-death.ts +32 -3
  29. package/bundle/src/application/queries.generated.ts +3 -0
  30. package/bundle/src/application/request-log/request-log.entity.json +26 -1
  31. package/bundle/src/application/request-log/request-log.model.ts +50 -10
  32. package/bundle/src/application/sonamu.generated.http +6 -0
  33. package/bundle/src/application/sonamu.generated.ts +42 -2
  34. package/bundle/src/application/token/token.model.test.ts +13 -3
  35. package/bundle/src/application/token/token.model.ts +34 -17
  36. package/bundle/src/i18n/en.ts +3 -0
  37. package/bundle/src/i18n/ko.ts +3 -0
  38. package/bundle/src/i18n/sd.generated.ts +17 -0
  39. package/bundle/src/migrations/20260807164057_alter_request_logs_alter1.ts +13 -0
  40. package/bundle/src/server-error-handler.test.ts +58 -0
  41. package/bundle/src/server-error-handler.ts +16 -5
  42. package/bundle/src/sonamu.config.ts +13 -0
  43. package/bundle/src/utils/slack-notify.test.ts +6 -4
  44. package/bundle/src/utils/slack-notify.ts +10 -3
  45. package/bundle/web-dist/client/assets/index-BGQSGMcK.css +1 -0
  46. package/bundle/web-dist/client/assets/index-DTy-s79t.js +78 -0
  47. package/bundle/web-dist/client/assets/logs-DYqUSqg-.js +1 -0
  48. package/bundle/web-dist/client/assets/{monit-BSa5lSZp.js → monit-GnFS0_Xa.js} +1 -1
  49. package/bundle/web-dist/client/assets/routes-CBwpn4d-.js +5 -0
  50. package/bundle/web-dist/client/assets/{sd.generated-CjW3rfJ8.js → sd.generated-DYdwSB2I.js} +1 -1
  51. package/bundle/web-dist/client/assets/{services.generated-B-dMUtEu.js → services.generated-BsAvotjG.js} +1 -1
  52. package/bundle/web-dist/client/assets/{show-C1jEPhuS.js → show-DDgukGuY.js} +3 -3
  53. package/bundle/web-dist/client/assets/{tokens-1e66MAC0.js → tokens-Co1wPrXX.js} +1 -1
  54. package/bundle/web-dist/client/assets/{use-update-token-mutation-DueEppqt.js → use-update-token-mutation-DqEpK7It.js} +1 -1
  55. package/bundle/web-dist/client/index.html +3 -3
  56. package/bundle/web-dist/server/assets/{lazyRouteComponent-PRxXWW3t.js → lazyRouteComponent-BaZaveNc.js} +1 -1
  57. package/bundle/web-dist/server/assets/{logs-CwoOsveV.js → logs-DMOw3SjD.js} +82 -40
  58. package/bundle/web-dist/server/assets/{monit-DIsEsDJN.js → monit-UmcqHroX.js} +2 -2
  59. package/bundle/web-dist/server/assets/{routes-Bts-KJ9W.js → routes-Sv_4FjtH.js} +47 -45
  60. package/bundle/web-dist/server/assets/{sd.generated-ek-tz-7H.js → sd.generated-B0xqnHEo.js} +21 -0
  61. package/bundle/web-dist/server/assets/{services.generated-Bh7Vs8Ds.js → services.generated-DqAKskAS.js} +16 -1
  62. package/bundle/web-dist/server/assets/{show-DJfaCNUw.js → show-C-oSdzHX.js} +31 -25
  63. package/bundle/web-dist/server/assets/{tokens-BA4vwDat.js → tokens-h-2A3wfD.js} +112 -109
  64. package/bundle/web-dist/server/assets/{use-update-token-mutation-BQRzEPy2.js → use-update-token-mutation-DHCaa7Ku.js} +1 -1
  65. package/bundle/web-dist/server/entry-server.generated.js +70 -29
  66. package/package.json +1 -1
  67. package/skills/qgrid/references/cli-env-and-server-boot.md +8 -0
  68. package/skills/qgrid/references/token-auth-quota-lifecycle.md +3 -2
  69. package/bundle/web-dist/client/assets/index-2cAgaLOm.css +0 -1
  70. package/bundle/web-dist/client/assets/index-BgWzzjvj.js +0 -78
  71. package/bundle/web-dist/client/assets/logs-Lyv8OfHE.js +0 -1
  72. package/bundle/web-dist/client/assets/routes-Cp0re-Nn.js +0 -5
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{y as t}from"./vendor-tanstack-DJRQAbrY.js";import{i as n,r}from"./vendor-react-DH4kUfK2.js";import{i,o as a}from"./index-BgWzzjvj.js";import{a as o,n as s}from"./services.generated-B-dMUtEu.js";import{t as c}from"./use-update-token-mutation-DueEppqt.js";var l=e(n()),u=r(),d=e=>(0,u.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,u.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M5 12h14m-7-7v14`})});function f({isPending:e,isError:t,onSubmit:n,onRestart:r}){let[a,o]=(0,l.useState)(``);return(0,u.jsxs)(`div`,{className:`space-y-2`,children:[(0,u.jsx)(`p`,{className:`text-[12px] text-sand-600 leading-relaxed`,children:`새 탭에서 Claude 로그인을 완료하면 코드가 표시됩니다. 코드 전체를 아래에 붙여넣으세요.`}),(0,u.jsx)(i,{value:a,onChange:e=>o(e.target.value),placeholder:`code#state`,className:`w-full border border-sand-200 rounded-md px-3 py-2 text-sm font-mono text-sand-900 bg-white placeholder:text-sand-300 focus:outline-none focus:border-sienna-300`}),t&&(0,u.jsx)(`p`,{className:`text-[11px] text-red-500`,children:`코드 확인에 실패했습니다 — 표시된 코드 전체를 다시 붙여넣거나 로그인을 다시 시작하세요.`}),(0,u.jsx)(`button`,{type:`button`,className:`w-full py-2.5 text-sm font-medium rounded-md bg-sand-900 text-white hover:bg-sand-800 disabled:opacity-50 transition-colors duration-150 flex items-center justify-center gap-2`,disabled:!a.trim()||e,onClick:()=>n(a),children:e?(0,u.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,u.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin`}),`Verifying...`]}):`Complete login`}),(0,u.jsx)(`button`,{type:`button`,className:`w-full py-1.5 text-[12px] text-sand-500 hover:text-sand-700 transition-colors duration-150`,onClick:()=>{o(``),r()},children:`처음부터 다시 시작`})]})}var p=3e3,m=3e5;function h(e){let t=0;for(let n of e.getQueryCache().findAll({queryKey:[`Token`]})){let e=n.state.data?.rows;Array.isArray(e)&&(t=Math.max(t,e.length))}return t}function g(){let[e,n]=(0,l.useState)(null),[r,i]=(0,l.useState)(!1),a=t(),o=s.useOauthStartMutation(),c=s.useOauthStartOpenAIMutation(),u=s.useOauthCompleteMutation(),d=(0,l.useRef)({}),f=()=>{d.current.interval!==void 0&&clearInterval(d.current.interval),d.current.timeout!==void 0&&clearTimeout(d.current.timeout),d.current={}};(0,l.useEffect)(()=>f,[]);let g=()=>Promise.all([a.invalidateQueries({queryKey:[`Token`]}),a.invalidateQueries({queryKey:[`Qgrid`]})]);return{loadingProvider:e,codeEntry:r,start:async(e,t)=>{let r=t.trim();if(!r)return;let s=window.open(`about:blank`,`_blank`);n(e);try{if(e===`openai`){let{authUrl:e}=await c.mutateAsync({name:r});s?s.location.href=e:window.open(e,`_blank`);let t=h(a);f(),d.current.interval=window.setInterval(()=>{g().then(()=>{h(a)>t&&(f(),n(null))})},p),d.current.timeout=window.setTimeout(()=>{f(),n(null)},m);return}let{authUrl:t,mode:l}=await o.mutateAsync({name:r});l===`code`?(s?s.location.href=t:window.open(t,`_blank`),i(!0),n(null)):(s?.close(),window.location.href=t)}catch(e){s?.close(),console.error(`OAuth start failed:`,e),n(null)}},submitCode:async e=>{let t=e.trim();if(!t)return!1;try{await u.mutateAsync({pastedCode:t})}catch(e){return console.error(`OAuth complete failed:`,e),!1}return await g(),!0},reset:()=>{f(),n(null),i(!1),u.reset()},completeMutation:u}}function _(){let[e,t]=(0,l.useState)(!1),[n,r]=(0,l.useState)(``),o=g(),s=()=>{t(!1),r(``),o.reset()},c=async e=>{await o.submitCode(e)&&s()};return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`button`,{type:`button`,className:`px-3.5 py-1.5 text-[12px] font-medium rounded-lg bg-sienna-500 text-white hover:bg-sienna-600 disabled:opacity-50 transition-all duration-150 active:scale-[0.97] flex items-center gap-1.5 shadow-sm`,onClick:()=>t(!0),children:[(0,u.jsx)(d,{className:`size-3.5`}),`Add Token`]}),e&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:s,onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-md mx-4`,children:[(0,u.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/50`,children:(0,u.jsx)(`h2`,{className:`text-base font-medium text-sand-900`,children:`Add Token`})}),(0,u.jsxs)(`div`,{className:`px-5 py-4 space-y-4`,children:[(0,u.jsxs)(`div`,{children:[(0,u.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name *`}),(0,u.jsx)(i,{value:n,onChange:e=>r(e.target.value),placeholder:`e.g. your-token-name`,className:`mt-1 w-full border border-sand-200 rounded-md px-3 py-2 text-sm text-sand-900 bg-white placeholder:text-sand-300 focus:outline-none focus:border-sienna-300`})]}),o.codeEntry?(0,u.jsx)(f,{isPending:o.completeMutation.isPending,isError:o.completeMutation.isError,onSubmit:e=>void c(e),onRestart:o.reset}):(0,u.jsxs)(`div`,{className:`space-y-2`,children:[(0,u.jsx)(`button`,{type:`button`,className:`w-full py-2.5 text-sm font-medium rounded-md bg-sand-900 text-white hover:bg-sand-800 disabled:opacity-50 transition-colors duration-150 flex items-center justify-center gap-2`,disabled:!n.trim()||o.loadingProvider!==null,onClick:()=>void o.start(`openai`,n),children:o.loadingProvider===`openai`?(0,u.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,u.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin`}),`Waiting for login...`]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(a,{className:`size-4`}),`Login with OpenAI`]})}),(0,u.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,u.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`}),(0,u.jsx)(`span`,{className:`text-[10px] text-sand-400 uppercase`,children:`or`}),(0,u.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`})]}),(0,u.jsx)(`button`,{type:`button`,className:`w-full py-2.5 text-sm font-medium rounded-md border border-sand-300 text-sand-700 hover:bg-sand-100 disabled:opacity-50 transition-colors duration-150 flex items-center justify-center gap-2`,disabled:!n.trim()||o.loadingProvider!==null,onClick:()=>void o.start(`anthropic`,n),children:o.loadingProvider===`anthropic`?(0,u.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,u.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-sand-400 border-t-transparent rounded-full animate-spin`}),`Waiting for login...`]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(a,{className:`size-4`}),`Login with Claude`]})})]})]}),(0,u.jsx)(`div`,{className:`px-5 py-3 border-t border-sand-100/50 flex items-center justify-end gap-2`,children:(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md border border-sand-200 text-sand-600 hover:bg-sand-100 transition-colors duration-150`,onClick:s,children:`Cancel`})})]})]})]})}var v=e=>(0,u.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,u.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497zM15 5l4 4`})}),y=e=>(0,u.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,u.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M10 11v6m4-6v6m5-11v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`})});function b(e){let t=e.credentials;return`expiresAt`in t&&typeof t.expiresAt==`number`?t.expiresAt:`accessTokenExpiresAt`in t&&typeof t.accessTokenExpiresAt==`number`?t.accessTokenExpiresAt:null}function x(e){if(!e)return{text:``,color:``};let t=e-Date.now();if(t<=0)return{text:`expired`,color:`text-red-500`};let n=Math.floor(t/36e5),r=Math.floor(t%36e5/6e4),i=n>0?`${n}h ${r}m`:`${r}m`;return t<36e5?{text:i,color:`text-amber-500`}:{text:i,color:`text-sand-400`}}function S(e){let t=e.credentials.accessToken;return t.length<=12?t:`${t.slice(0,8)}...${t.slice(-4)}`}function C({data:e,isLoading:n}){let[r,a]=(0,l.useState)(null),[o,d]=(0,l.useState)(null),[p,m]=(0,l.useState)(``),h=t(),_=s.useRemoveTokenMutation(),C=c(),w=s.useToggleTokenMutation(),T=g(),E=()=>Promise.all([h.invalidateQueries({queryKey:[`Token`]}),h.invalidateQueries({queryKey:[`Qgrid`]})]),D=async e=>{await w.mutateAsync({id:e.id}),await E()},O=async()=>{r&&(await _.mutateAsync({id:r.id}),await E(),a(null))},k=async e=>{await T.submitCode(e)&&T.reset()},A=e=>{d(e),m(e.name??``)},j=async()=>{o&&(await C.mutateAsync({id:o.id,name:p.trim()}),await E(),d(null))};if(n)return(0,u.jsx)(`div`,{className:`space-y-2`,children:Array.from({length:3}).map((e,t)=>(0,u.jsx)(`div`,{className:`h-12 bg-sand-100 rounded-md animate-pulse`},`skeleton-${t}`))});let M=e??[];return M.length===0?(0,u.jsxs)(`div`,{className:`text-sand-400 text-center py-16 text-sm`,children:[`No tokens registered. Click `,(0,u.jsx)(`strong`,{className:`text-sand-600`,children:`Add Token`}),` to get started.`]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(`div`,{className:`panel overflow-hidden`,children:(0,u.jsxs)(`table`,{className:`w-full text-[13px]`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsxs)(`tr`,{className:`panel-header`,children:[(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Provider`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Name`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Token`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Status`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Expires`}),(0,u.jsx)(`th`,{className:`w-20 px-3 py-2.5`})]})}),(0,u.jsx)(`tbody`,{className:`divide-y divide-sand-100/80`,children:M.map(e=>{let t=x(b(e));return(0,u.jsxs)(`tr`,{className:`transition-colors duration-150 hover:bg-sand-100/60 ${e.active?``:`opacity-50`}`,children:[(0,u.jsx)(`td`,{className:`px-5 py-3`,children:(0,u.jsx)(`span`,{className:`text-xs font-medium text-sand-500 uppercase`,children:e.provider})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:e.name?(0,u.jsx)(`span`,{className:`text-sm text-sand-700`,children:e.name}):(0,u.jsx)(`span`,{className:`text-[11px] text-sand-400`,children:`—`})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:(0,u.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:S(e)})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:(0,u.jsx)(`button`,{type:`button`,className:`relative inline-flex h-5 w-9 items-center rounded-full transition-colors duration-200 ${e.active?`bg-sage-400`:`bg-sand-300`}`,onClick:()=>D(e),disabled:w.isPending,children:(0,u.jsx)(`span`,{className:`inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform duration-200 ${e.active?`translate-x-4.5`:`translate-x-0.5`}`})})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:t.text&&(0,u.jsx)(`span`,{className:`text-xs ${t.color}`,children:t.text})}),(0,u.jsx)(`td`,{className:`px-3 py-3`,children:(0,u.jsxs)(`div`,{className:`flex items-center gap-1`,children:[!e.active&&(0,u.jsx)(`button`,{type:`button`,title:`기존 이름으로 다시 로그인`,className:`px-2 py-0.5 mr-1 text-[11px] font-medium rounded-md border border-sand-300 text-sand-600 hover:bg-sand-100 disabled:opacity-50 transition-colors duration-150 inline-flex items-center gap-1`,disabled:T.loadingProvider!==null,onClick:()=>void T.start(e.provider,e.name??``),children:T.loadingProvider===e.provider?(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(`span`,{className:`w-3 h-3 border-2 border-sand-400 border-t-transparent rounded-full animate-spin`}),`로그인 대기`]}):`재로그인`}),(0,u.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-sienna-500 transition-colors duration-150`,onClick:()=>A(e),children:(0,u.jsx)(v,{className:`size-4`})}),(0,u.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-red-500 transition-colors duration-150`,onClick:()=>a(e),children:(0,u.jsx)(y,{className:`size-4`})})]})})]},e.id)})})]})}),T.codeEntry&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:T.reset,onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-md mx-4`,children:[(0,u.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/60`,children:(0,u.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`재로그인`})}),(0,u.jsx)(`div`,{className:`px-5 py-4`,children:(0,u.jsx)(f,{isPending:T.completeMutation.isPending,isError:T.completeMutation.isError,onSubmit:e=>void k(e),onRestart:T.reset})})]})]}),o&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>d(null),onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,u.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/60`,children:(0,u.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Edit Token`})}),(0,u.jsx)(`div`,{className:`px-5 py-4 space-y-3`,children:(0,u.jsxs)(`div`,{children:[(0,u.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name`}),(0,u.jsx)(i,{value:p,onChange:e=>m(e.target.value),placeholder:`e.g. prod, dev, team-a`,className:`mt-1 w-full border border-sand-200 rounded-md px-3 py-2 text-sm text-sand-900 bg-white placeholder:text-sand-300 focus:outline-none focus:border-sienna-300`})]})}),(0,u.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md border border-sand-200 text-sand-600 hover:bg-sand-100 transition-colors duration-150`,onClick:()=>d(null),children:`Cancel`}),(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md bg-sienna-400 text-white hover:bg-sienna-500 disabled:opacity-50 transition-colors duration-150`,disabled:C.isPending,onClick:j,children:C.isPending?`Saving...`:`Save`})]})]})]}),r&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>a(null),onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,u.jsxs)(`div`,{className:`px-5 py-4`,children:[(0,u.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Remove Token`}),(0,u.jsxs)(`p`,{className:`text-sm text-sand-700 mt-2`,children:[`Are you sure you want to remove`,` `,(0,u.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:S(r)}),r.name&&(0,u.jsxs)(`span`,{className:`text-sand-500`,children:[` (`,r.name,`)`]}),`?`]})]}),(0,u.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md border border-sand-200 text-sand-600 hover:bg-sand-100 transition-colors duration-150`,onClick:()=>a(null),children:`Cancel`}),(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md bg-red-500 text-white hover:bg-red-600 disabled:opacity-50 transition-colors duration-150`,disabled:_.isPending,onClick:O,children:_.isPending?`Removing...`:`Remove`})]})]})]})]})}function w(){let{data:e,isLoading:t}=o.useTokens(`A`);return(0,u.jsxs)(`div`,{className:`max-w-6xl mx-auto space-y-4`,children:[(0,u.jsx)(`div`,{className:`flex items-center justify-end`,children:(0,u.jsx)(_,{})}),(0,u.jsx)(C,{data:e?.rows,isLoading:t})]})}export{w as component};
1
+ import{r as e}from"./rolldown-runtime-Dw2cE7zH.js";import{y as t}from"./vendor-tanstack-DJRQAbrY.js";import{i as n,r}from"./vendor-react-DH4kUfK2.js";import{i,o as a}from"./index-DTy-s79t.js";import{a as o,n as s}from"./services.generated-BsAvotjG.js";import{t as c}from"./use-update-token-mutation-DqEpK7It.js";var l=e(n()),u=r(),d=e=>(0,u.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,u.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M5 12h14m-7-7v14`})});function f({isPending:e,isError:t,onSubmit:n,onRestart:r}){let[a,o]=(0,l.useState)(``);return(0,u.jsxs)(`div`,{className:`space-y-2`,children:[(0,u.jsx)(`p`,{className:`text-[12px] text-sand-600 leading-relaxed`,children:`새 탭에서 Claude 로그인을 완료하면 코드가 표시됩니다. 코드 전체를 아래에 붙여넣으세요.`}),(0,u.jsx)(i,{value:a,onChange:e=>o(e.target.value),placeholder:`code#state`,className:`w-full border border-sand-200 rounded-md px-3 py-2 text-sm font-mono text-sand-900 bg-white placeholder:text-sand-300 focus:outline-none focus:border-sienna-300`}),t&&(0,u.jsx)(`p`,{className:`text-[11px] text-red-500`,children:`코드 확인에 실패했습니다 — 표시된 코드 전체를 다시 붙여넣거나 로그인을 다시 시작하세요.`}),(0,u.jsx)(`button`,{type:`button`,className:`w-full py-2.5 text-sm font-medium rounded-md bg-sand-900 text-white hover:bg-sand-800 disabled:opacity-50 transition-colors duration-150 flex items-center justify-center gap-2`,disabled:!a.trim()||e,onClick:()=>n(a),children:e?(0,u.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,u.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin`}),`Verifying...`]}):`Complete login`}),(0,u.jsx)(`button`,{type:`button`,className:`w-full py-1.5 text-[12px] text-sand-500 hover:text-sand-700 transition-colors duration-150`,onClick:()=>{o(``),r()},children:`처음부터 다시 시작`})]})}var p=3e3,m=3e5;function h(e){let t=0;for(let n of e.getQueryCache().findAll({queryKey:[`Token`]})){let e=n.state.data?.rows;Array.isArray(e)&&(t=Math.max(t,e.length))}return t}function g(){let[e,n]=(0,l.useState)(null),[r,i]=(0,l.useState)(!1),a=t(),o=s.useOauthStartMutation(),c=s.useOauthStartOpenAIMutation(),u=s.useOauthCompleteMutation(),d=(0,l.useRef)({}),f=()=>{d.current.interval!==void 0&&clearInterval(d.current.interval),d.current.timeout!==void 0&&clearTimeout(d.current.timeout),d.current={}};(0,l.useEffect)(()=>f,[]);let g=()=>Promise.all([a.invalidateQueries({queryKey:[`Token`]}),a.invalidateQueries({queryKey:[`Qgrid`]})]);return{loadingProvider:e,codeEntry:r,start:async(e,t)=>{let r=t.trim();if(!r)return;let s=window.open(`about:blank`,`_blank`);n(e);try{if(e===`openai`){let{authUrl:e}=await c.mutateAsync({name:r});s?s.location.href=e:window.open(e,`_blank`);let t=h(a);f(),d.current.interval=window.setInterval(()=>{g().then(()=>{h(a)>t&&(f(),n(null))})},p),d.current.timeout=window.setTimeout(()=>{f(),n(null)},m);return}let{authUrl:t,mode:l}=await o.mutateAsync({name:r});l===`code`?(s?s.location.href=t:window.open(t,`_blank`),i(!0),n(null)):(s?.close(),window.location.href=t)}catch(e){s?.close(),console.error(`OAuth start failed:`,e),n(null)}},submitCode:async e=>{let t=e.trim();if(!t)return!1;try{await u.mutateAsync({pastedCode:t})}catch(e){return console.error(`OAuth complete failed:`,e),!1}return await g(),!0},reset:()=>{f(),n(null),i(!1),u.reset()},completeMutation:u}}function _(){let[e,t]=(0,l.useState)(!1),[n,r]=(0,l.useState)(``),o=g(),s=()=>{t(!1),r(``),o.reset()},c=async e=>{await o.submitCode(e)&&s()};return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`button`,{type:`button`,className:`px-3.5 py-1.5 text-[12px] font-medium rounded-lg bg-sienna-500 text-white hover:bg-sienna-600 disabled:opacity-50 transition-all duration-150 active:scale-[0.97] flex items-center gap-1.5 shadow-sm`,onClick:()=>t(!0),children:[(0,u.jsx)(d,{className:`size-3.5`}),`Add Token`]}),e&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:s,onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-md mx-4`,children:[(0,u.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/50`,children:(0,u.jsx)(`h2`,{className:`text-base font-medium text-sand-900`,children:`Add Token`})}),(0,u.jsxs)(`div`,{className:`px-5 py-4 space-y-4`,children:[(0,u.jsxs)(`div`,{children:[(0,u.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name *`}),(0,u.jsx)(i,{value:n,onChange:e=>r(e.target.value),placeholder:`e.g. your-token-name`,className:`mt-1 w-full border border-sand-200 rounded-md px-3 py-2 text-sm text-sand-900 bg-white placeholder:text-sand-300 focus:outline-none focus:border-sienna-300`})]}),o.codeEntry?(0,u.jsx)(f,{isPending:o.completeMutation.isPending,isError:o.completeMutation.isError,onSubmit:e=>void c(e),onRestart:o.reset}):(0,u.jsxs)(`div`,{className:`space-y-2`,children:[(0,u.jsx)(`button`,{type:`button`,className:`w-full py-2.5 text-sm font-medium rounded-md bg-sand-900 text-white hover:bg-sand-800 disabled:opacity-50 transition-colors duration-150 flex items-center justify-center gap-2`,disabled:!n.trim()||o.loadingProvider!==null,onClick:()=>void o.start(`openai`,n),children:o.loadingProvider===`openai`?(0,u.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,u.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin`}),`Waiting for login...`]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(a,{className:`size-4`}),`Login with OpenAI`]})}),(0,u.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,u.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`}),(0,u.jsx)(`span`,{className:`text-[10px] text-sand-400 uppercase`,children:`or`}),(0,u.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`})]}),(0,u.jsx)(`button`,{type:`button`,className:`w-full py-2.5 text-sm font-medium rounded-md border border-sand-300 text-sand-700 hover:bg-sand-100 disabled:opacity-50 transition-colors duration-150 flex items-center justify-center gap-2`,disabled:!n.trim()||o.loadingProvider!==null,onClick:()=>void o.start(`anthropic`,n),children:o.loadingProvider===`anthropic`?(0,u.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,u.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-sand-400 border-t-transparent rounded-full animate-spin`}),`Waiting for login...`]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(a,{className:`size-4`}),`Login with Claude`]})})]})]}),(0,u.jsx)(`div`,{className:`px-5 py-3 border-t border-sand-100/50 flex items-center justify-end gap-2`,children:(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md border border-sand-200 text-sand-600 hover:bg-sand-100 transition-colors duration-150`,onClick:s,children:`Cancel`})})]})]})]})}var v=e=>(0,u.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,u.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497zM15 5l4 4`})}),y=e=>(0,u.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,u.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M10 11v6m4-6v6m5-11v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`})});function b(e){let t=e.credentials;return`expiresAt`in t&&typeof t.expiresAt==`number`?t.expiresAt:`accessTokenExpiresAt`in t&&typeof t.accessTokenExpiresAt==`number`?t.accessTokenExpiresAt:null}function x(e){if(!e)return{text:``,color:``};let t=e-Date.now();if(t<=0)return{text:`expired`,color:`text-red-500`};let n=Math.floor(t/36e5),r=Math.floor(t%36e5/6e4),i=n>0?`${n}h ${r}m`:`${r}m`;return t<36e5?{text:i,color:`text-amber-500`}:{text:i,color:`text-sand-400`}}function S(e){let t=e.credentials.accessToken;return t.length<=12?t:`${t.slice(0,8)}...${t.slice(-4)}`}function C({data:e,isLoading:n}){let[r,a]=(0,l.useState)(null),[o,d]=(0,l.useState)(null),[p,m]=(0,l.useState)(``),h=t(),_=s.useRemoveTokenMutation(),C=c(),w=s.useToggleTokenMutation(),T=g(),E=()=>Promise.all([h.invalidateQueries({queryKey:[`Token`]}),h.invalidateQueries({queryKey:[`Qgrid`]})]),D=async e=>{await w.mutateAsync({id:e.id}),await E()},O=async()=>{r&&(await _.mutateAsync({id:r.id}),await E(),a(null))},k=async e=>{await T.submitCode(e)&&T.reset()},A=e=>{d(e),m(e.name??``)},j=async()=>{o&&(await C.mutateAsync({id:o.id,name:p.trim()}),await E(),d(null))};if(n)return(0,u.jsx)(`div`,{className:`space-y-2`,children:Array.from({length:3}).map((e,t)=>(0,u.jsx)(`div`,{className:`h-12 bg-sand-100 rounded-md animate-pulse`},`skeleton-${t}`))});let M=e??[];return M.length===0?(0,u.jsxs)(`div`,{className:`text-sand-400 text-center py-16 text-sm`,children:[`No tokens registered. Click `,(0,u.jsx)(`strong`,{className:`text-sand-600`,children:`Add Token`}),` to get started.`]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(`div`,{className:`panel overflow-hidden`,children:(0,u.jsx)(`div`,{className:`overflow-x-auto`,children:(0,u.jsxs)(`table`,{className:`w-full min-w-[640px] text-[13px]`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsxs)(`tr`,{className:`panel-header`,children:[(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Provider`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Name`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Token`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Status`}),(0,u.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Expires`}),(0,u.jsx)(`th`,{className:`w-20 px-3 py-2.5`})]})}),(0,u.jsx)(`tbody`,{className:`divide-y divide-sand-100/80`,children:M.map(e=>{let t=x(b(e));return(0,u.jsxs)(`tr`,{className:`transition-colors duration-150 hover:bg-sand-100/60 ${e.active?``:`opacity-50`}`,children:[(0,u.jsx)(`td`,{className:`px-5 py-3`,children:(0,u.jsx)(`span`,{className:`text-xs font-medium text-sand-500 uppercase`,children:e.provider})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:e.name?(0,u.jsx)(`span`,{className:`text-sm text-sand-700`,children:e.name}):(0,u.jsx)(`span`,{className:`text-[11px] text-sand-400`,children:`—`})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:(0,u.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:S(e)})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:(0,u.jsx)(`button`,{type:`button`,className:`relative inline-flex h-5 w-9 items-center rounded-full transition-colors duration-200 ${e.active?`bg-sage-400`:`bg-sand-300`}`,onClick:()=>D(e),disabled:w.isPending,children:(0,u.jsx)(`span`,{className:`inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform duration-200 ${e.active?`translate-x-4.5`:`translate-x-0.5`}`})})}),(0,u.jsx)(`td`,{className:`px-5 py-3`,children:t.text&&(0,u.jsx)(`span`,{className:`text-xs ${t.color}`,children:t.text})}),(0,u.jsx)(`td`,{className:`px-3 py-3`,children:(0,u.jsxs)(`div`,{className:`flex items-center gap-1`,children:[!e.active&&(0,u.jsx)(`button`,{type:`button`,title:`기존 이름으로 다시 로그인`,className:`px-2 py-0.5 mr-1 text-[11px] font-medium rounded-md border border-sand-300 text-sand-600 hover:bg-sand-100 disabled:opacity-50 transition-colors duration-150 inline-flex items-center gap-1`,disabled:T.loadingProvider!==null,onClick:()=>void T.start(e.provider,e.name??``),children:T.loadingProvider===e.provider?(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(`span`,{className:`w-3 h-3 border-2 border-sand-400 border-t-transparent rounded-full animate-spin`}),`로그인 대기`]}):`재로그인`}),(0,u.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-sienna-500 transition-colors duration-150`,onClick:()=>A(e),children:(0,u.jsx)(v,{className:`size-4`})}),(0,u.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-red-500 transition-colors duration-150`,onClick:()=>a(e),children:(0,u.jsx)(y,{className:`size-4`})})]})})]},e.id)})})]})})}),T.codeEntry&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:T.reset,onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-md mx-4`,children:[(0,u.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/60`,children:(0,u.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`재로그인`})}),(0,u.jsx)(`div`,{className:`px-5 py-4`,children:(0,u.jsx)(f,{isPending:T.completeMutation.isPending,isError:T.completeMutation.isError,onSubmit:e=>void k(e),onRestart:T.reset})})]})]}),o&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>d(null),onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,u.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/60`,children:(0,u.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Edit Token`})}),(0,u.jsx)(`div`,{className:`px-5 py-4 space-y-3`,children:(0,u.jsxs)(`div`,{children:[(0,u.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name`}),(0,u.jsx)(i,{value:p,onChange:e=>m(e.target.value),placeholder:`e.g. prod, dev, team-a`,className:`mt-1 w-full border border-sand-200 rounded-md px-3 py-2 text-sm text-sand-900 bg-white placeholder:text-sand-300 focus:outline-none focus:border-sienna-300`})]})}),(0,u.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md border border-sand-200 text-sand-600 hover:bg-sand-100 transition-colors duration-150`,onClick:()=>d(null),children:`Cancel`}),(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md bg-sienna-400 text-white hover:bg-sienna-500 disabled:opacity-50 transition-colors duration-150`,disabled:C.isPending,onClick:j,children:C.isPending?`Saving...`:`Save`})]})]})]}),r&&(0,u.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,u.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>a(null),onKeyDown:()=>{}}),(0,u.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,u.jsxs)(`div`,{className:`px-5 py-4`,children:[(0,u.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Remove Token`}),(0,u.jsxs)(`p`,{className:`text-sm text-sand-700 mt-2`,children:[`Are you sure you want to remove`,` `,(0,u.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:S(r)}),r.name&&(0,u.jsxs)(`span`,{className:`text-sand-500`,children:[` (`,r.name,`)`]}),`?`]})]}),(0,u.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md border border-sand-200 text-sand-600 hover:bg-sand-100 transition-colors duration-150`,onClick:()=>a(null),children:`Cancel`}),(0,u.jsx)(`button`,{type:`button`,className:`px-3 py-1 text-xs font-medium rounded-md bg-red-500 text-white hover:bg-red-600 disabled:opacity-50 transition-colors duration-150`,disabled:_.isPending,onClick:O,children:_.isPending?`Removing...`:`Remove`})]})]})]})]})}function w(){let{data:e,isLoading:t}=o.useTokens(`A`);return(0,u.jsxs)(`div`,{className:`max-w-6xl mx-auto space-y-4`,children:[(0,u.jsx)(`div`,{className:`flex items-center justify-end`,children:(0,u.jsx)(_,{})}),(0,u.jsx)(C,{data:e?.rows,isLoading:t})]})}export{w as component};
@@ -1 +1 @@
1
- import{m as e}from"./vendor-tanstack-DJRQAbrY.js";import{n as t}from"./services.generated-B-dMUtEu.js";function n(){return e({mutationFn:({id:e,name:n,quotaThreshold:r,weight:i})=>t.updateToken(e,n,r,i)})}export{n as t};
1
+ import{m as e}from"./vendor-tanstack-DJRQAbrY.js";import{n as t}from"./services.generated-BsAvotjG.js";function n(){return e({mutationFn:({id:e,name:n,quotaThreshold:r,weight:i})=>t.updateToken(e,n,r,i)})}export{n as t};
@@ -5,13 +5,13 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/sonamu.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Qgrid</title>
8
- <script type="module" crossorigin src="/assets/index-BgWzzjvj.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-DTy-s79t.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dw2cE7zH.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-ca-nBW7U.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DH4kUfK2.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-DJRQAbrY.js">
13
- <link rel="modulepreload" crossorigin href="/assets/sd.generated-CjW3rfJ8.js">
14
- <link rel="stylesheet" crossorigin href="/assets/index-2cAgaLOm.css">
13
+ <link rel="modulepreload" crossorigin href="/assets/sd.generated-DYdwSB2I.js">
14
+ <link rel="stylesheet" crossorigin href="/assets/index-BGQSGMcK.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -1,4 +1,4 @@
1
- import { J as __commonJSMin, Z as __toESM, _ as require_jsx_runtime, m as require_shim, p as require_react_dom, v as require_react } from "./sd.generated-ek-tz-7H.js";
1
+ import { J as __commonJSMin, Z as __toESM, _ as require_jsx_runtime, m as require_shim, p as require_react_dom, v as require_react } from "./sd.generated-B0xqnHEo.js";
2
2
  //#region ../../node_modules/.pnpm/@tanstack+router-core@1.143.6/node_modules/@tanstack/router-core/dist/esm/utils.js
3
3
  function last(arr) {
4
4
  return arr[arr.length - 1];
@@ -1,6 +1,6 @@
1
- import { _ as require_jsx_runtime, d as string, l as number, u as object, v as require_react } from "./sd.generated-ek-tz-7H.js";
2
- import { a as TokenService, n as QgridService, r as RequestLogService } from "./services.generated-Bh7Vs8Ds.js";
3
- import { a as Link, o as useNavigate } from "./lazyRouteComponent-PRxXWW3t.js";
1
+ import { _ as require_jsx_runtime, d as string, l as number, u as object, v as require_react } from "./sd.generated-B0xqnHEo.js";
2
+ import { a as TokenService, n as QgridService, r as RequestLogService } from "./services.generated-DqAKskAS.js";
3
+ import { a as Link, o as useNavigate } from "./lazyRouteComponent-BaZaveNc.js";
4
4
  import { n as Route } from "../entry-server.generated.js";
5
5
  import { n as formatMicroUsd, r as formatUsd, t as cacheHitRate } from "./cost-CSH_t17I.js";
6
6
  require_react();
@@ -82,12 +82,14 @@ var COLUMNS = [
82
82
  {
83
83
  label: "ID",
84
84
  align: "left",
85
- width: "w-12"
85
+ width: "w-12",
86
+ sortKey: "id"
86
87
  },
87
88
  {
88
89
  label: "Date",
89
90
  align: "left",
90
- width: "w-20"
91
+ width: "w-20",
92
+ sortKey: "created_at"
91
93
  },
92
94
  {
93
95
  label: "Project",
@@ -106,48 +108,55 @@ var COLUMNS = [
106
108
  },
107
109
  {
108
110
  label: "TTFT",
109
- align: "left",
110
- width: "w-16"
111
+ align: "right",
112
+ width: "w-16",
113
+ sortKey: "ttft_ms"
111
114
  },
112
115
  {
113
116
  label: "Duration",
114
- align: "left",
115
- width: "w-20"
117
+ align: "right",
118
+ width: "w-20",
119
+ sortKey: "duration_ms"
116
120
  },
117
121
  {
118
122
  label: "In",
119
- align: "left",
120
- width: "w-16"
123
+ align: "right",
124
+ width: "w-16",
125
+ sortKey: "input_tokens"
121
126
  },
122
127
  {
123
128
  label: "Out",
124
- align: "left",
125
- width: "w-20"
129
+ align: "right",
130
+ width: "w-20",
131
+ sortKey: "output_tokens"
126
132
  },
127
133
  {
128
134
  label: "C.Read",
129
- align: "left",
130
- width: "w-20"
135
+ align: "right",
136
+ width: "w-20",
137
+ sortKey: "cache_read_tokens"
131
138
  },
132
139
  {
133
140
  label: "C.Write",
134
- align: "left",
135
- width: "w-20"
141
+ align: "right",
142
+ width: "w-20",
143
+ sortKey: "cache_creation_tokens"
136
144
  },
137
145
  {
138
146
  label: "Hit",
139
- align: "left",
147
+ align: "right",
140
148
  width: "w-14"
141
149
  },
142
150
  {
143
151
  label: "Tools",
144
- align: "left",
152
+ align: "right",
145
153
  width: "w-14"
146
154
  },
147
155
  {
148
156
  label: "Cost",
149
- align: "left",
150
- width: "w-20"
157
+ align: "right",
158
+ width: "w-20",
159
+ sortKey: "cost_usd"
151
160
  }
152
161
  ];
153
162
  function RequestLogTable({ search, onSearchChange }) {
@@ -155,10 +164,13 @@ function RequestLogTable({ search, onSearchChange }) {
155
164
  const page = search.page ?? 1;
156
165
  const tokenFilter = search.token ?? "";
157
166
  const projectFilter = search.project ?? "";
167
+ const modelFilter = search.model ?? "";
158
168
  const { data: tokensData } = TokenService.useTokens("A");
159
169
  const tokenNames = (tokensData?.rows ?? []).map((t) => t.name).filter(Boolean);
160
170
  const { data: projectData } = QgridService.useProjectNames();
161
171
  const projectNames = projectData?.names ?? [];
172
+ const { data: modelData } = QgridService.useModelNames();
173
+ const modelNames = modelData?.names ?? [];
162
174
  const projectFilterParam = (() => {
163
175
  if (projectFilter === UNASSIGNED) return { project_name_is_null: true };
164
176
  if (projectFilter) return { project_name: projectFilter };
@@ -166,12 +178,17 @@ function RequestLogTable({ search, onSearchChange }) {
166
178
  })();
167
179
  const listFilters = {
168
180
  ...tokenFilter ? { token_name: tokenFilter } : {},
169
- ...projectFilterParam
181
+ ...projectFilterParam,
182
+ ...modelFilter ? { model_name: modelFilter } : {}
170
183
  };
184
+ const sort = search.sort ?? "id-desc";
185
+ const separator = sort.lastIndexOf("-");
186
+ const sortColumn = sort.slice(0, separator);
187
+ const sortDirection = sort.slice(separator + 1) === "asc" ? "asc" : "desc";
171
188
  const { data, isLoading } = RequestLogService.useRequestLogs("P", {
172
189
  num: PAGE_SIZE,
173
190
  page,
174
- orderBy: "id-desc",
191
+ orderBy: sort,
175
192
  ...listFilters
176
193
  });
177
194
  const { data: costData } = QgridService.useTotalCost({
@@ -192,15 +209,16 @@ function RequestLogTable({ search, onSearchChange }) {
192
209
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
193
210
  className: "panel overflow-hidden",
194
211
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
195
- className: "panel-header flex items-center gap-3 px-5 py-2.5 flex-wrap",
212
+ className: "panel-header flex items-center gap-2 px-3 py-2.5 flex-wrap sm:gap-3 sm:px-5",
196
213
  children: [
197
- tokenNames.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
198
215
  value: tokenFilter,
199
216
  onChange: (e) => updateFilter({ token: e.target.value || void 0 }),
200
- className: "border border-sand-200/80 rounded-lg px-2.5 py-1.5 text-[11px] text-sand-700 bg-sand-50/50 focus:outline-none focus:border-sienna-300",
217
+ disabled: tokenNames.length === 0,
218
+ className: "border border-sand-200/80 rounded-lg px-2.5 py-1.5 text-[11px] text-sand-700 bg-sand-50/50 focus:outline-none focus:border-sienna-300 disabled:text-sand-300 disabled:cursor-not-allowed",
201
219
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
202
220
  value: "",
203
- children: "All Tokens"
221
+ children: tokenNames.length > 0 ? "All Tokens" : "Tokens —"
204
222
  }), tokenNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
205
223
  value: name,
206
224
  children: name
@@ -225,6 +243,19 @@ function RequestLogTable({ search, onSearchChange }) {
225
243
  }, name))
226
244
  ]
227
245
  }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
247
+ value: modelFilter,
248
+ onChange: (e) => updateFilter({ model: e.target.value || void 0 }),
249
+ disabled: modelNames.length === 0,
250
+ className: "border border-sand-200/80 rounded-lg px-2.5 py-1.5 text-[11px] text-sand-700 bg-sand-50/50 focus:outline-none focus:border-sienna-300 disabled:text-sand-300 disabled:cursor-not-allowed",
251
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
252
+ value: "",
253
+ children: modelNames.length > 0 ? "All Models" : "Models —"
254
+ }), modelNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
255
+ value: name,
256
+ children: name
257
+ }, name))]
258
+ }),
228
259
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" }),
229
260
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
230
261
  className: "text-[11px] text-sand-400",
@@ -242,16 +273,25 @@ function RequestLogTable({ search, onSearchChange }) {
242
273
  className: "text-sand-400 text-center py-12 text-sm",
243
274
  children: "No requests yet."
244
275
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
245
- className: "overflow-x-auto px-5 py-3",
276
+ className: "overflow-x-auto px-3 py-3 sm:px-5",
246
277
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
247
- className: "w-full text-[13px]",
278
+ className: "w-full min-w-[900px] text-[13px]",
248
279
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", {
249
280
  className: "border-b border-sand-100/60",
250
281
  children: [COLUMNS.map((col) => {
251
282
  const padX = col.label === "Duration" ? "pl-5 pr-3" : col.width ? "px-4" : "px-3";
283
+ const active = col.sortKey === sortColumn;
252
284
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
253
- className: `text-${col.align} ${col.width ?? ""} ${col.width ? "whitespace-nowrap" : ""} ${padX} py-1.5 text-[10px] uppercase text-sand-400 font-medium`,
254
- children: col.label
285
+ className: `${col.align === "right" ? "text-right" : "text-left"} ${col.width ?? ""} ${col.width ? "whitespace-nowrap" : ""} ${padX} py-1.5 text-[10px] uppercase font-medium ${active ? "text-sienna-600" : "text-sand-400"}`,
286
+ children: col.sortKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
287
+ type: "button",
288
+ onClick: () => updateFilter({ sort: `${col.sortKey}-${active && sortDirection === "desc" ? "asc" : "desc"}` }),
289
+ className: `inline-flex items-center gap-0.5 text-[10px] uppercase font-medium hover:text-sienna-500 transition-colors duration-150 ${col.align === "right" ? "justify-end w-full" : ""}`,
290
+ children: [col.label, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
291
+ className: active ? "" : "text-sand-300",
292
+ children: active ? sortDirection === "desc" ? "↓" : "↑" : "↕"
293
+ })]
294
+ }) : col.label
255
295
  }, col.label);
256
296
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: "w-8" })]
257
297
  }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", {
@@ -310,39 +350,39 @@ function RequestLogTable({ search, onSearchChange }) {
310
350
  })
311
351
  }),
312
352
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
313
- className: "px-4 py-1.5 text-left tabular-nums text-sand-500 whitespace-nowrap",
353
+ className: "px-4 py-1.5 text-right tabular-nums text-sand-500 whitespace-nowrap",
314
354
  children: formatTtft(row.ttft_ms)
315
355
  }),
316
356
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
317
- className: "pl-5 pr-3 py-1.5 text-left tabular-nums text-sand-500 whitespace-nowrap",
357
+ className: "pl-5 pr-3 py-1.5 text-right tabular-nums text-sand-500 whitespace-nowrap",
318
358
  children: formatDuration(row.duration_ms)
319
359
  }),
320
360
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
321
- className: "px-3 py-1.5 text-left tabular-nums text-sand-700",
361
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
322
362
  children: formatNum(row.input_tokens)
323
363
  }),
324
364
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
325
- className: "px-3 py-1.5 text-left tabular-nums text-sand-700",
365
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
326
366
  children: formatNum(row.output_tokens)
327
367
  }),
328
368
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
329
- className: "px-3 py-1.5 text-left tabular-nums text-sand-700",
369
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
330
370
  children: formatNum(row.cache_read_tokens)
331
371
  }),
332
372
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
333
- className: "px-3 py-1.5 text-left tabular-nums text-sand-700",
373
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
334
374
  children: formatNum(row.cache_creation_tokens)
335
375
  }),
336
376
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
337
- className: "px-3 py-1.5 text-left tabular-nums text-sand-700",
377
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
338
378
  children: cacheHitRate(row)
339
379
  }),
340
380
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
341
- className: "px-3 py-1.5 text-left tabular-nums text-sand-500",
381
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-500",
342
382
  children: row.tool_call_count > 0 ? row.tool_call_count : "—"
343
383
  }),
344
384
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
345
- className: "px-3 py-1.5 text-left tabular-nums text-sand-700",
385
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
346
386
  children: row.image_cost_usd !== null ? formatMicroUsd((row.cost_usd ?? 0) + row.image_cost_usd) : row.cost_usd !== null ? formatMicroUsd(row.cost_usd) : "—"
347
387
  }),
348
388
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
@@ -393,6 +433,8 @@ function RequestLogTable({ search, onSearchChange }) {
393
433
  object({
394
434
  token: string().optional(),
395
435
  project: string().optional(),
436
+ model: string().optional(),
437
+ sort: string().optional(),
396
438
  page: number().optional().default(1)
397
439
  });
398
440
  function LogsPage() {
@@ -1,5 +1,5 @@
1
- import { Z as __toESM, _ as require_jsx_runtime, c as _enum, p as require_react_dom, u as object, v as require_react } from "./sd.generated-ek-tz-7H.js";
2
- import { s as useQuery, t as MonitService } from "./services.generated-Bh7Vs8Ds.js";
1
+ import { Z as __toESM, _ as require_jsx_runtime, c as _enum, p as require_react_dom, u as object, v as require_react } from "./sd.generated-B0xqnHEo.js";
2
+ import { s as useQuery, t as MonitService } from "./services.generated-DqAKskAS.js";
3
3
  import { a as clsx, r as Route } from "../entry-server.generated.js";
4
4
  //#region ../../node_modules/.pnpm/@tanstack+virtual-core@3.17.4/node_modules/@tanstack/virtual-core/dist/esm/lazy-measurements.js
5
5
  var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
@@ -1,8 +1,8 @@
1
- import { Z as __toESM, _ as require_jsx_runtime, g as useQueryClient, p as require_react_dom, v as require_react } from "./sd.generated-ek-tz-7H.js";
2
- import { a as TokenService, n as QgridService } from "./services.generated-Bh7Vs8Ds.js";
1
+ import { Z as __toESM, _ as require_jsx_runtime, g as useQueryClient, p as require_react_dom, v as require_react } from "./sd.generated-B0xqnHEo.js";
2
+ import { a as TokenService, n as QgridService } from "./services.generated-DqAKskAS.js";
3
3
  import { i as Q } from "../entry-server.generated.js";
4
4
  import { r as formatUsd } from "./cost-CSH_t17I.js";
5
- import { t as useUpdateTokenMutation } from "./use-update-token-mutation-BQRzEPy2.js";
5
+ import { t as useUpdateTokenMutation } from "./use-update-token-mutation-DHCaa7Ku.js";
6
6
  //#region ../../node_modules/.pnpm/@dnd-kit+utilities@3.2.2_react@19.2.4/node_modules/@dnd-kit/utilities/dist/utilities.esm.js
7
7
  var import_react_dom = /* @__PURE__ */ __toESM(require_react_dom());
8
8
  var import_react = /* @__PURE__ */ __toESM(require_react());
@@ -2387,14 +2387,14 @@ function UsageRow({ label, utilization, resetsAt, windowDurationMins, theme, thr
2387
2387
  const pct = utilization ?? 0;
2388
2388
  const hasThreshold = threshold !== void 0 && threshold !== null;
2389
2389
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2390
- className: "flex items-center gap-3",
2390
+ className: "flex items-center gap-2",
2391
2391
  children: [
2392
2392
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2393
- className: "text-xs text-sand-600 w-20 shrink-0",
2393
+ className: "text-xs text-sand-600 w-7 shrink-0",
2394
2394
  children: formatWindowLabel(label, windowDurationMins)
2395
2395
  }),
2396
2396
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2397
- className: "relative flex-1 h-2 bg-sand-200 rounded-full overflow-hidden",
2397
+ className: "relative flex-1 min-w-0 h-2 bg-sand-200 rounded-full overflow-hidden",
2398
2398
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2399
2399
  className: `h-full rounded-full transition-all duration-300 ${barColor(pct, theme)}`,
2400
2400
  style: { width: `${Math.min(pct, 100)}%` }
@@ -2404,11 +2404,12 @@ function UsageRow({ label, utilization, resetsAt, windowDurationMins, theme, thr
2404
2404
  })]
2405
2405
  }),
2406
2406
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2407
- className: "text-xs tabular-nums text-sand-700 w-10 text-right",
2407
+ className: "text-xs tabular-nums text-sand-700 w-9 text-right shrink-0",
2408
2408
  children: [pct, "%"]
2409
2409
  }),
2410
2410
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2411
- className: "text-[10px] text-sand-400 w-24 text-right",
2411
+ className: "hidden sm:inline text-[10px] text-sand-400 w-24 text-right shrink-0",
2412
+ title: formatResets(resetsAt),
2412
2413
  children: formatResets(resetsAt)
2413
2414
  })
2414
2415
  ]
@@ -2456,9 +2457,6 @@ function TokenUsage({ token, theme }) {
2456
2457
  })]
2457
2458
  });
2458
2459
  }
2459
- function stopDragPropagation(e) {
2460
- e.stopPropagation();
2461
- }
2462
2460
  function ThresholdControl({ token }) {
2463
2461
  const [pos, setPos] = (0, import_react.useState)(null);
2464
2462
  const [value, setValue] = (0, import_react.useState)("");
@@ -2506,8 +2504,6 @@ function ThresholdControl({ token }) {
2506
2504
  };
2507
2505
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2508
2506
  className: "inline-flex",
2509
- onPointerDown: stopDragPropagation,
2510
- onClick: stopDragPropagation,
2511
2507
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
2512
2508
  ref: triggerRef,
2513
2509
  type: "button",
@@ -2623,8 +2619,6 @@ function WeightControl({ token }) {
2623
2619
  };
2624
2620
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2625
2621
  className: "inline-flex",
2626
- onPointerDown: stopDragPropagation,
2627
- onClick: stopDragPropagation,
2628
2622
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
2629
2623
  ref: triggerRef,
2630
2624
  type: "button",
@@ -2713,27 +2707,50 @@ function SortableTokenCard({ token }) {
2713
2707
  opacity: isDragging ? .5 : 1,
2714
2708
  zIndex: isDragging ? 10 : void 0
2715
2709
  },
2716
- ...attributes,
2717
- ...listeners,
2718
- className: "relative rounded-lg bg-white border border-sand-200/80 overflow-hidden select-none cursor-grab active:cursor-grabbing touch-none shadow-sm hover:shadow-md transition-shadow duration-200",
2710
+ className: "relative rounded-lg bg-white border border-sand-200/80 overflow-hidden shadow-sm hover:shadow-md transition-shadow duration-200",
2719
2711
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `absolute inset-y-0 left-0 w-1 ${theme.stripe}` }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2720
- className: "pl-4 pr-4 py-3",
2712
+ className: "pl-4 pr-3 py-3",
2721
2713
  children: [
2722
2714
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2723
- className: "flex items-center gap-2 mb-2",
2724
- children: [
2725
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideGripVertical, { className: "size-3.5 text-sand-300 shrink-0" }),
2726
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2727
- className: "text-[13px] font-medium text-sand-800 truncate",
2715
+ className: "flex items-start justify-between gap-2 mb-2",
2716
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2717
+ className: "flex items-center gap-1.5 min-w-0",
2718
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
2719
+ type: "button",
2720
+ "aria-label": "순서 변경 핸들",
2721
+ ...attributes,
2722
+ ...listeners,
2723
+ className: "shrink-0 -m-1 p-1 text-sand-300 hover:text-sand-500 cursor-grab active:cursor-grabbing touch-none",
2724
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideGripVertical, { className: "size-3.5" })
2725
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2726
+ className: "text-[13px] font-medium text-sand-800 truncate select-none",
2728
2727
  children: token.name ?? "Unnamed"
2729
- }),
2728
+ })]
2729
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2730
+ className: `text-[11px] tabular-nums font-medium shrink-0 ${theme.cost}`,
2731
+ title: "이 토큰의 누적 비용",
2732
+ children: formatUsd(costData?.usd ?? 0)
2733
+ })]
2734
+ }),
2735
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2736
+ className: "flex flex-wrap items-center gap-1 mb-2",
2737
+ children: [
2730
2738
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2731
- className: `text-[10px] px-1.5 py-0.5 rounded-full shrink-0 uppercase ${theme.badge}`,
2739
+ className: `text-[10px] px-1.5 py-0.5 rounded-full uppercase ${theme.badge}`,
2732
2740
  children: token.provider
2733
2741
  }),
2734
2742
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2735
- className: `text-[10px] px-1.5 py-0.5 rounded-full shrink-0 ${token.active ? "bg-sage-100 text-sage-600" : "bg-sand-200 text-sand-500"}`,
2743
+ className: `text-[10px] px-1.5 py-0.5 rounded-full ${token.active ? "bg-sage-100 text-sage-600" : "bg-sand-200 text-sand-500"}`,
2736
2744
  children: token.active ? "Active" : "Inactive"
2745
+ }),
2746
+ QUOTA_THRESHOLD_PROVIDERS.has(token.provider) && token.quota_threshold !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2747
+ className: "px-1.5 py-0.5 rounded-full bg-sienna-50 text-[10px] tabular-nums font-medium text-sienna-600",
2748
+ title: `사용률 ${token.quota_threshold}% 이상이면 이 토큰 제외`,
2749
+ children: [
2750
+ "≤ ",
2751
+ token.quota_threshold,
2752
+ "%"
2753
+ ]
2737
2754
  })
2738
2755
  ]
2739
2756
  }),
@@ -2742,23 +2759,8 @@ function SortableTokenCard({ token }) {
2742
2759
  theme
2743
2760
  }),
2744
2761
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2745
- className: "mt-2 flex items-center justify-end gap-1.5",
2762
+ className: "mt-2 flex flex-wrap items-center justify-end gap-1.5",
2746
2763
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(WeightControl, { token }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThresholdControl, { token })]
2747
- }),
2748
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2749
- className: "absolute top-2.5 right-3 flex items-center gap-1.5",
2750
- children: [QUOTA_THRESHOLD_PROVIDERS.has(token.provider) && token.quota_threshold !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2751
- className: "px-1.5 py-0.5 rounded-md bg-sienna-50 text-[11px] tabular-nums font-medium border border-sienna-200 text-sienna-600",
2752
- title: `사용률 ${token.quota_threshold}% 이상이면 이 토큰 제외`,
2753
- children: [
2754
- "≤ ",
2755
- token.quota_threshold,
2756
- "%"
2757
- ]
2758
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2759
- className: `px-2 py-0.5 rounded-md bg-sand-50 text-[11px] tabular-nums font-medium border border-sand-200/80 ${theme.cost}`,
2760
- children: formatUsd(costData?.usd ?? 0)
2761
- })]
2762
2764
  })
2763
2765
  ]
2764
2766
  })]
@@ -2808,7 +2810,7 @@ function UsageCard() {
2808
2810
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2809
2811
  className: "panel p-5",
2810
2812
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2811
- className: "grid grid-cols-2 gap-3",
2813
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-3",
2812
2814
  children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2813
2815
  className: "rounded-lg bg-sand-50 px-4 py-3 animate-pulse",
2814
2816
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-3 w-16 bg-sand-200 rounded mb-3" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-2 w-full bg-sand-200 rounded-full" })]
@@ -2850,7 +2852,7 @@ function UsageCard() {
2850
2852
  items: filteredTokens.map((t) => String(t.id)),
2851
2853
  strategy: rectSortingStrategy,
2852
2854
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2853
- className: "grid grid-cols-2 gap-3",
2855
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-3",
2854
2856
  children: filteredTokens.map((token) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SortableTokenCard, { token }, token.id))
2855
2857
  })
2856
2858
  })
@@ -22308,6 +22308,8 @@ var en_default = {
22308
22308
  "error.internalServerError": "Internal server error",
22309
22309
  "error.notFound": "Not found",
22310
22310
  "error.serviceUnavailable": "Service unavailable",
22311
+ "qgrid.dispatcherFailed": (provider) => `${provider} dispatcher failed to initialize`,
22312
+ "qgrid.dispatcherStarting": (provider) => `${provider} dispatcher is starting up. Please retry shortly`,
22311
22313
  "error.unauthorized": "Unauthorized",
22312
22314
  notFound: (name, id) => `${name} ID ${id} not found`,
22313
22315
  "validation.email": "Invalid email format",
@@ -22368,6 +22370,8 @@ var ko_default = {
22368
22370
  "error.internalServerError": "서버 오류가 발생했습니다",
22369
22371
  "error.notFound": "찾을 수 없습니다",
22370
22372
  "error.serviceUnavailable": "서비스를 사용할 수 없습니다",
22373
+ "qgrid.dispatcherFailed": (provider) => `${provider} dispatcher 초기화에 실패했습니다`,
22374
+ "qgrid.dispatcherStarting": (provider) => `${provider} dispatcher 기동 중입니다. 잠시 후 다시 시도해주세요`,
22371
22375
  "error.unauthorized": "인증이 필요합니다",
22372
22376
  "error.unknownSearchField": (field) => `구현되지 않은 검색 필드 ${field}`,
22373
22377
  notFound: (name, id) => `존재하지 않는 ${name} ID ${id}`,
@@ -22564,6 +22568,23 @@ var entityLabels = {
22564
22568
  "entity.RequestLog.tool_call_count": "tool call 횟수",
22565
22569
  "entity.RequestLog.is_image_generation": "이미지 생성 turn 여부 (quota 귀속용)",
22566
22570
  "enum.RequestLogOrderBy.id-desc": "ID최신순",
22571
+ "enum.RequestLogOrderBy.id-asc": "ID오래된순",
22572
+ "enum.RequestLogOrderBy.created_at-desc": "등록일시최신순",
22573
+ "enum.RequestLogOrderBy.created_at-asc": "등록일시오래된순",
22574
+ "enum.RequestLogOrderBy.ttft_ms-desc": "TTFT긴순",
22575
+ "enum.RequestLogOrderBy.ttft_ms-asc": "TTFT짧은순",
22576
+ "enum.RequestLogOrderBy.duration_ms-desc": "소요시간긴순",
22577
+ "enum.RequestLogOrderBy.duration_ms-asc": "소요시간짧은순",
22578
+ "enum.RequestLogOrderBy.input_tokens-desc": "입력토큰많은순",
22579
+ "enum.RequestLogOrderBy.input_tokens-asc": "입력토큰적은순",
22580
+ "enum.RequestLogOrderBy.output_tokens-desc": "출력토큰많은순",
22581
+ "enum.RequestLogOrderBy.output_tokens-asc": "출력토큰적은순",
22582
+ "enum.RequestLogOrderBy.cache_read_tokens-desc": "캐시읽기많은순",
22583
+ "enum.RequestLogOrderBy.cache_read_tokens-asc": "캐시읽기적은순",
22584
+ "enum.RequestLogOrderBy.cache_creation_tokens-desc": "캐시생성많은순",
22585
+ "enum.RequestLogOrderBy.cache_creation_tokens-asc": "캐시생성적은순",
22586
+ "enum.RequestLogOrderBy.cost_usd-desc": "비용높은순",
22587
+ "enum.RequestLogOrderBy.cost_usd-asc": "비용낮은순",
22567
22588
  "enum.RequestLogSearchField.id": "ID",
22568
22589
  "enum.RequestLogSearchField.token_name": "토큰이름",
22569
22590
  "enum.RequestLogSearchField.user_prompt": "사용자 프롬프트",
@@ -1,4 +1,4 @@
1
- import { B as resolveStaleTime, C as infiniteQueryBehavior, D as notifyManager, G as timeoutManager, H as shouldThrowError, I as noop, K as focusManager, N as isValidTimeout, O as pendingThenable, R as replaceData, S as hasPreviousPage, T as fetchState, V as shallowEqualObjects, W as timeUntilStale, Z as __toESM, _ as require_jsx_runtime, a as fetch, b as getDefaultState, f as require_lib, g as useQueryClient, i as dedupeAndFlatten, j as hashKey, k as environmentManager, o as useRefreshable, q as Subscribable, s as useSSEStream, v as require_react, x as hasNextPage, z as resolveEnabled } from "./sd.generated-ek-tz-7H.js";
1
+ import { B as resolveStaleTime, C as infiniteQueryBehavior, D as notifyManager, G as timeoutManager, H as shouldThrowError, I as noop, K as focusManager, N as isValidTimeout, O as pendingThenable, R as replaceData, S as hasPreviousPage, T as fetchState, V as shallowEqualObjects, W as timeUntilStale, Z as __toESM, _ as require_jsx_runtime, a as fetch, b as getDefaultState, f as require_lib, g as useQueryClient, i as dedupeAndFlatten, j as hashKey, k as environmentManager, o as useRefreshable, q as Subscribable, s as useSSEStream, v as require_react, x as hasNextPage, z as resolveEnabled } from "./sd.generated-B0xqnHEo.js";
2
2
  //#region ../../node_modules/.pnpm/@tanstack+query-core@5.95.2/node_modules/@tanstack/query-core/build/modern/queryObserver.js
3
3
  var QueryObserver = class extends Subscribable {
4
4
  constructor(client, options) {
@@ -1023,6 +1023,21 @@ var QgridService;
1023
1023
  ...projectNamesQueryOptions(),
1024
1024
  ...options
1025
1025
  }));
1026
+ async function modelNames() {
1027
+ return fetch({
1028
+ method: "GET",
1029
+ url: `/api/qgrid/modelNames`
1030
+ });
1031
+ }
1032
+ _QgridService.modelNames = modelNames;
1033
+ const modelNamesQueryOptions = _QgridService.modelNamesQueryOptions = () => queryOptions({
1034
+ queryKey: ["Qgrid", "modelNames"],
1035
+ queryFn: () => modelNames()
1036
+ });
1037
+ _QgridService.useModelNames = (options) => useRefreshable(useQuery({
1038
+ ...modelNamesQueryOptions(),
1039
+ ...options
1040
+ }));
1026
1041
  async function addToken(provider, credentials, name) {
1027
1042
  return fetch({
1028
1043
  method: "POST",