@cartanova/qgrid-cli 2.4.0 → 2.4.1

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 (39) hide show
  1. package/bundle/dist/application/qgrid/conv-routing.js +21 -1
  2. package/bundle/dist/application/qgrid/qgrid-response-format.js +50 -3
  3. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +3 -14
  4. package/bundle/dist/application/qgrid/qgrid.frame.js +3 -14
  5. package/bundle/dist/application/qgrid/qgrid.types.js +11 -2
  6. package/bundle/dist/application/sonamu.generated.js +11 -3
  7. package/bundle/dist/application/sonamu.generated.sso.js +9 -2
  8. package/bundle/src/application/qgrid/conv-routing.test.ts +31 -0
  9. package/bundle/src/application/qgrid/conv-routing.ts +13 -0
  10. package/bundle/src/application/qgrid/qgrid-response-format.test.ts +77 -0
  11. package/bundle/src/application/qgrid/qgrid-response-format.ts +79 -6
  12. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +11 -21
  13. package/bundle/src/application/qgrid/qgrid.frame.test.ts +36 -0
  14. package/bundle/src/application/qgrid/qgrid.frame.ts +14 -22
  15. package/bundle/src/application/qgrid/qgrid.types.ts +14 -0
  16. package/bundle/src/application/request-log-step/request-log-step.entity.json +2 -1
  17. package/bundle/src/application/sonamu.generated.http +18 -0
  18. package/bundle/src/application/sonamu.generated.sso.ts +7 -0
  19. package/bundle/src/application/sonamu.generated.ts +7 -1
  20. package/bundle/web-dist/client/assets/{index-DVdnvcvo.js → index-CHRDevod.js} +3 -3
  21. package/bundle/web-dist/client/assets/index-Dc-qeEPm.css +1 -0
  22. package/bundle/web-dist/client/assets/logs-CFw7oJgw.js +1 -0
  23. package/bundle/web-dist/client/assets/{routes-CvCEcpZ7.js → routes-vhw9Wsg6.js} +1 -1
  24. package/bundle/web-dist/client/assets/{show-DWRh--ML.js → show-CKIx8CD_.js} +3 -3
  25. package/bundle/web-dist/client/assets/{tokens-DOu_ZFUL.js → tokens-Di4RowSm.js} +1 -1
  26. package/bundle/web-dist/client/index.html +2 -2
  27. package/bundle/web-dist/server/assets/{logs-B_ZLzpwY.js → logs-Bk9C0ELo.js} +1 -0
  28. package/bundle/web-dist/server/assets/{show-CGlX9OYt.js → show-3GokIDEJ.js} +94 -12
  29. package/bundle/web-dist/server/entry-server.generated.js +3 -3
  30. package/package.json +1 -1
  31. package/postinstall.mjs +14 -2
  32. package/skills/qgrid/references/ai-sdk-provider-contract.md +4 -0
  33. package/skills/qgrid/references/decision-rationale.md +2 -2
  34. package/skills/qgrid/references/openai-codex-runtime.md +2 -0
  35. package/skills/qgrid/references/request-log-run-lifecycle.md +2 -0
  36. package/skills/qgrid/references/sonamu-api-web-flow.md +2 -0
  37. package/skills/qgrid/references/verification-and-debugging.md +4 -0
  38. package/bundle/web-dist/client/assets/index-VE_24EJf.css +0 -1
  39. package/bundle/web-dist/client/assets/logs-B82PGnxD.js +0 -1
@@ -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,r as a}from"./index-DVdnvcvo.js";import{i as o,t as s}from"./services.generated-AsEvHbJ7.js";var c=e(n()),l=r(),u=e=>(0,l.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,l.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M5 12h14m-7-7v14`})});function d(){let[e,n]=(0,c.useState)(!1),[r,o]=(0,c.useState)(!1),[d,f]=(0,c.useState)(``),p=t(),m=s.useOauthStartMutation(),h=s.useOauthStartOpenAIMutation(),g=async e=>{if(d.trim())if(e===`openai`){let e=window.open(`about:blank`,`_blank`);o(!0);try{let{authUrl:t}=await h.mutateAsync({name:d.trim()});e?e.location.href=t:window.open(t,`_blank`);let n=setInterval(async()=>{await p.invalidateQueries({queryKey:[`Token`]}),await p.invalidateQueries({queryKey:[`Qgrid`]})},3e3);setTimeout(()=>{clearInterval(n),o(!1)},3e5)}catch(e){console.error(`OAuth start failed:`,e),o(!1)}}else{o(!0);try{let{authUrl:e}=await m.mutateAsync({name:d.trim()});window.location.href=e}catch(e){console.error(`OAuth start failed:`,e),o(!1)}}},_=()=>{n(!1),f(``),o(!1)};return(0,l.jsxs)(l.Fragment,{children:[(0,l.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:()=>n(!0),children:[(0,l.jsx)(u,{className:`size-3.5`}),`Add Token`]}),e&&(0,l.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,l.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:_,onKeyDown:()=>{}}),(0,l.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-md mx-4`,children:[(0,l.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/50`,children:(0,l.jsx)(`h2`,{className:`text-base font-medium text-sand-900`,children:`Add Token`})}),(0,l.jsxs)(`div`,{className:`px-5 py-4 space-y-4`,children:[(0,l.jsxs)(`div`,{children:[(0,l.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name *`}),(0,l.jsx)(a,{value:d,onChange:e=>f(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`})]}),(0,l.jsxs)(`div`,{className:`space-y-2`,children:[(0,l.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:!d.trim()||r,onClick:()=>g(`openai`),children:r?(0,l.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,l.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin`}),`Waiting for login...`]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(i,{className:`size-4`}),`Login with OpenAI`]})}),(0,l.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,l.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`}),(0,l.jsx)(`span`,{className:`text-[10px] text-sand-400 uppercase`,children:`or`}),(0,l.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`})]}),(0,l.jsxs)(`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:!d.trim()||r,onClick:()=>g(`anthropic`),children:[(0,l.jsx)(i,{className:`size-4`}),`Login with Claude`]})]})]}),(0,l.jsx)(`div`,{className:`px-5 py-3 border-t border-sand-100/50 flex items-center justify-end gap-2`,children:(0,l.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:_,children:`Cancel`})})]})]})]})}var f=e=>(0,l.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,l.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`})}),p=e=>(0,l.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,l.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 m(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 h(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 g(e){let t=e.credentials.accessToken;return t.length<=12?t:`${t.slice(0,8)}...${t.slice(-4)}`}function _({data:e,isLoading:n}){let[r,i]=(0,c.useState)(null),[o,u]=(0,c.useState)(null),[d,_]=(0,c.useState)(``),v=t(),y=s.useRemoveTokenMutation(),b=s.useUpdateTokenMutation(),x=s.useToggleTokenMutation(),S=()=>Promise.all([v.invalidateQueries({queryKey:[`Token`]}),v.invalidateQueries({queryKey:[`Qgrid`]})]),C=async e=>{await x.mutateAsync({id:e.id}),await S()},w=async()=>{r&&(await y.mutateAsync({id:r.id}),await S(),i(null))},T=e=>{u(e),_(e.name??``)},E=async()=>{o&&(await b.mutateAsync({id:o.id,name:d.trim(),quotaThreshold:o.quota_threshold}),await S(),u(null))};if(n)return(0,l.jsx)(`div`,{className:`space-y-2`,children:Array.from({length:3}).map((e,t)=>(0,l.jsx)(`div`,{className:`h-12 bg-sand-100 rounded-md animate-pulse`},`skeleton-${t}`))});let D=e??[];return D.length===0?(0,l.jsxs)(`div`,{className:`text-sand-400 text-center py-16 text-sm`,children:[`No tokens registered. Click `,(0,l.jsx)(`strong`,{className:`text-sand-600`,children:`Add Token`}),` to get started.`]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`panel overflow-hidden`,children:(0,l.jsxs)(`table`,{className:`w-full text-[13px]`,children:[(0,l.jsx)(`thead`,{children:(0,l.jsxs)(`tr`,{className:`panel-header`,children:[(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Provider`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Name`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Token`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Status`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Expires`}),(0,l.jsx)(`th`,{className:`w-20 px-3 py-2.5`})]})}),(0,l.jsx)(`tbody`,{className:`divide-y divide-sand-100/80`,children:D.map(e=>{let t=h(m(e));return(0,l.jsxs)(`tr`,{className:`transition-colors duration-150 hover:bg-sand-100/60 ${e.active?``:`opacity-50`}`,children:[(0,l.jsx)(`td`,{className:`px-5 py-3`,children:(0,l.jsx)(`span`,{className:`text-xs font-medium text-sand-500 uppercase`,children:e.provider})}),(0,l.jsx)(`td`,{className:`px-5 py-3`,children:e.name?(0,l.jsx)(`span`,{className:`text-sm text-sand-700`,children:e.name}):(0,l.jsx)(`span`,{className:`text-[11px] text-sand-400`,children:`—`})}),(0,l.jsx)(`td`,{className:`px-5 py-3`,children:(0,l.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:g(e)})}),(0,l.jsx)(`td`,{className:`px-5 py-3`,children:(0,l.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:()=>C(e),disabled:x.isPending,children:(0,l.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,l.jsx)(`td`,{className:`px-5 py-3`,children:t.text&&(0,l.jsx)(`span`,{className:`text-xs ${t.color}`,children:t.text})}),(0,l.jsx)(`td`,{className:`px-3 py-3`,children:(0,l.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,l.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-sienna-500 transition-colors duration-150`,onClick:()=>T(e),children:(0,l.jsx)(f,{className:`size-4`})}),(0,l.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-red-500 transition-colors duration-150`,onClick:()=>i(e),children:(0,l.jsx)(p,{className:`size-4`})})]})})]},e.id)})})]})}),o&&(0,l.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,l.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>u(null),onKeyDown:()=>{}}),(0,l.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,l.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/60`,children:(0,l.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Edit Token`})}),(0,l.jsx)(`div`,{className:`px-5 py-4 space-y-3`,children:(0,l.jsxs)(`div`,{children:[(0,l.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name`}),(0,l.jsx)(a,{value:d,onChange:e=>_(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,l.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,l.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:()=>u(null),children:`Cancel`}),(0,l.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:b.isPending,onClick:E,children:b.isPending?`Saving...`:`Save`})]})]})]}),r&&(0,l.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,l.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>i(null),onKeyDown:()=>{}}),(0,l.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,l.jsxs)(`div`,{className:`px-5 py-4`,children:[(0,l.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Remove Token`}),(0,l.jsxs)(`p`,{className:`text-sm text-sand-700 mt-2`,children:[`Are you sure you want to remove`,` `,(0,l.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:g(r)}),r.name&&(0,l.jsxs)(`span`,{className:`text-sand-500`,children:[` (`,r.name,`)`]}),`?`]})]}),(0,l.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,l.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:()=>i(null),children:`Cancel`}),(0,l.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:y.isPending,onClick:w,children:y.isPending?`Removing...`:`Remove`})]})]})]})]})}function v(){let{data:e,isLoading:t}=o.useTokens(`A`);return(0,l.jsxs)(`div`,{className:`max-w-6xl mx-auto space-y-4`,children:[(0,l.jsx)(`div`,{className:`flex items-center justify-end`,children:(0,l.jsx)(d,{})}),(0,l.jsx)(_,{data:e?.rows,isLoading:t})]})}export{v 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,r as a}from"./index-CHRDevod.js";import{i as o,t as s}from"./services.generated-AsEvHbJ7.js";var c=e(n()),l=r(),u=e=>(0,l.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,l.jsx)(`path`,{fill:`none`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M5 12h14m-7-7v14`})});function d(){let[e,n]=(0,c.useState)(!1),[r,o]=(0,c.useState)(!1),[d,f]=(0,c.useState)(``),p=t(),m=s.useOauthStartMutation(),h=s.useOauthStartOpenAIMutation(),g=async e=>{if(d.trim())if(e===`openai`){let e=window.open(`about:blank`,`_blank`);o(!0);try{let{authUrl:t}=await h.mutateAsync({name:d.trim()});e?e.location.href=t:window.open(t,`_blank`);let n=setInterval(async()=>{await p.invalidateQueries({queryKey:[`Token`]}),await p.invalidateQueries({queryKey:[`Qgrid`]})},3e3);setTimeout(()=>{clearInterval(n),o(!1)},3e5)}catch(e){console.error(`OAuth start failed:`,e),o(!1)}}else{o(!0);try{let{authUrl:e}=await m.mutateAsync({name:d.trim()});window.location.href=e}catch(e){console.error(`OAuth start failed:`,e),o(!1)}}},_=()=>{n(!1),f(``),o(!1)};return(0,l.jsxs)(l.Fragment,{children:[(0,l.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:()=>n(!0),children:[(0,l.jsx)(u,{className:`size-3.5`}),`Add Token`]}),e&&(0,l.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,l.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:_,onKeyDown:()=>{}}),(0,l.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-md mx-4`,children:[(0,l.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/50`,children:(0,l.jsx)(`h2`,{className:`text-base font-medium text-sand-900`,children:`Add Token`})}),(0,l.jsxs)(`div`,{className:`px-5 py-4 space-y-4`,children:[(0,l.jsxs)(`div`,{children:[(0,l.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name *`}),(0,l.jsx)(a,{value:d,onChange:e=>f(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`})]}),(0,l.jsxs)(`div`,{className:`space-y-2`,children:[(0,l.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:!d.trim()||r,onClick:()=>g(`openai`),children:r?(0,l.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,l.jsx)(`span`,{className:`w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin`}),`Waiting for login...`]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(i,{className:`size-4`}),`Login with OpenAI`]})}),(0,l.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,l.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`}),(0,l.jsx)(`span`,{className:`text-[10px] text-sand-400 uppercase`,children:`or`}),(0,l.jsx)(`div`,{className:`flex-1 h-px bg-sand-200`})]}),(0,l.jsxs)(`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:!d.trim()||r,onClick:()=>g(`anthropic`),children:[(0,l.jsx)(i,{className:`size-4`}),`Login with Claude`]})]})]}),(0,l.jsx)(`div`,{className:`px-5 py-3 border-t border-sand-100/50 flex items-center justify-end gap-2`,children:(0,l.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:_,children:`Cancel`})})]})]})]})}var f=e=>(0,l.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,l.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`})}),p=e=>(0,l.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`1.2em`,height:`1.2em`,...e,children:(0,l.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 m(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 h(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 g(e){let t=e.credentials.accessToken;return t.length<=12?t:`${t.slice(0,8)}...${t.slice(-4)}`}function _({data:e,isLoading:n}){let[r,i]=(0,c.useState)(null),[o,u]=(0,c.useState)(null),[d,_]=(0,c.useState)(``),v=t(),y=s.useRemoveTokenMutation(),b=s.useUpdateTokenMutation(),x=s.useToggleTokenMutation(),S=()=>Promise.all([v.invalidateQueries({queryKey:[`Token`]}),v.invalidateQueries({queryKey:[`Qgrid`]})]),C=async e=>{await x.mutateAsync({id:e.id}),await S()},w=async()=>{r&&(await y.mutateAsync({id:r.id}),await S(),i(null))},T=e=>{u(e),_(e.name??``)},E=async()=>{o&&(await b.mutateAsync({id:o.id,name:d.trim(),quotaThreshold:o.quota_threshold}),await S(),u(null))};if(n)return(0,l.jsx)(`div`,{className:`space-y-2`,children:Array.from({length:3}).map((e,t)=>(0,l.jsx)(`div`,{className:`h-12 bg-sand-100 rounded-md animate-pulse`},`skeleton-${t}`))});let D=e??[];return D.length===0?(0,l.jsxs)(`div`,{className:`text-sand-400 text-center py-16 text-sm`,children:[`No tokens registered. Click `,(0,l.jsx)(`strong`,{className:`text-sand-600`,children:`Add Token`}),` to get started.`]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`panel overflow-hidden`,children:(0,l.jsxs)(`table`,{className:`w-full text-[13px]`,children:[(0,l.jsx)(`thead`,{children:(0,l.jsxs)(`tr`,{className:`panel-header`,children:[(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Provider`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Name`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Token`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Status`}),(0,l.jsx)(`th`,{className:`text-left px-5 py-2.5 text-[10px] uppercase tracking-wider text-sand-400 font-medium`,children:`Expires`}),(0,l.jsx)(`th`,{className:`w-20 px-3 py-2.5`})]})}),(0,l.jsx)(`tbody`,{className:`divide-y divide-sand-100/80`,children:D.map(e=>{let t=h(m(e));return(0,l.jsxs)(`tr`,{className:`transition-colors duration-150 hover:bg-sand-100/60 ${e.active?``:`opacity-50`}`,children:[(0,l.jsx)(`td`,{className:`px-5 py-3`,children:(0,l.jsx)(`span`,{className:`text-xs font-medium text-sand-500 uppercase`,children:e.provider})}),(0,l.jsx)(`td`,{className:`px-5 py-3`,children:e.name?(0,l.jsx)(`span`,{className:`text-sm text-sand-700`,children:e.name}):(0,l.jsx)(`span`,{className:`text-[11px] text-sand-400`,children:`—`})}),(0,l.jsx)(`td`,{className:`px-5 py-3`,children:(0,l.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:g(e)})}),(0,l.jsx)(`td`,{className:`px-5 py-3`,children:(0,l.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:()=>C(e),disabled:x.isPending,children:(0,l.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,l.jsx)(`td`,{className:`px-5 py-3`,children:t.text&&(0,l.jsx)(`span`,{className:`text-xs ${t.color}`,children:t.text})}),(0,l.jsx)(`td`,{className:`px-3 py-3`,children:(0,l.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,l.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-sienna-500 transition-colors duration-150`,onClick:()=>T(e),children:(0,l.jsx)(f,{className:`size-4`})}),(0,l.jsx)(`button`,{type:`button`,className:`p-1 rounded text-sand-400 hover:text-red-500 transition-colors duration-150`,onClick:()=>i(e),children:(0,l.jsx)(p,{className:`size-4`})})]})})]},e.id)})})]})}),o&&(0,l.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,l.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>u(null),onKeyDown:()=>{}}),(0,l.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,l.jsx)(`div`,{className:`px-5 py-4 border-b border-sand-100/60`,children:(0,l.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Edit Token`})}),(0,l.jsx)(`div`,{className:`px-5 py-4 space-y-3`,children:(0,l.jsxs)(`div`,{children:[(0,l.jsx)(`label`,{htmlFor:`token-name`,className:`text-[10px] uppercase tracking-wider text-sand-500 font-medium`,children:`Name`}),(0,l.jsx)(a,{value:d,onChange:e=>_(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,l.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,l.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:()=>u(null),children:`Cancel`}),(0,l.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:b.isPending,onClick:E,children:b.isPending?`Saving...`:`Save`})]})]})]}),r&&(0,l.jsxs)(`div`,{className:`fixed inset-0 z-50 flex items-start justify-center pt-[15vh]`,children:[(0,l.jsx)(`div`,{className:`absolute inset-0 bg-sand-900/8 backdrop-blur-sm`,onClick:()=>i(null),onKeyDown:()=>{}}),(0,l.jsxs)(`div`,{className:`relative panel shadow-xl w-full max-w-sm mx-4`,children:[(0,l.jsxs)(`div`,{className:`px-5 py-4`,children:[(0,l.jsx)(`h3`,{className:`text-base font-medium text-sand-900`,children:`Remove Token`}),(0,l.jsxs)(`p`,{className:`text-sm text-sand-700 mt-2`,children:[`Are you sure you want to remove`,` `,(0,l.jsx)(`code`,{className:`text-[13px] font-mono text-sand-800`,children:g(r)}),r.name&&(0,l.jsxs)(`span`,{className:`text-sand-500`,children:[` (`,r.name,`)`]}),`?`]})]}),(0,l.jsxs)(`div`,{className:`px-5 py-3 border-t border-sand-100/60 flex items-center justify-end gap-2`,children:[(0,l.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:()=>i(null),children:`Cancel`}),(0,l.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:y.isPending,onClick:w,children:y.isPending?`Removing...`:`Remove`})]})]})]})]})}function v(){let{data:e,isLoading:t}=o.useTokens(`A`);return(0,l.jsxs)(`div`,{className:`max-w-6xl mx-auto space-y-4`,children:[(0,l.jsx)(`div`,{className:`flex items-center justify-end`,children:(0,l.jsx)(d,{})}),(0,l.jsx)(_,{data:e?.rows,isLoading:t})]})}export{v as component};
@@ -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-DVdnvcvo.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-CHRDevod.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
13
  <link rel="modulepreload" crossorigin href="/assets/sd.generated-BrUnRt0B.js">
14
- <link rel="stylesheet" crossorigin href="/assets/index-VE_24EJf.css">
14
+ <link rel="stylesheet" crossorigin href="/assets/index-Dc-qeEPm.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -53,6 +53,7 @@ function formatDuration(ms) {
53
53
  return m > 0 ? `${m}m ${s}s` : `${s}s`;
54
54
  }
55
55
  function formatTtft(ms) {
56
+ if (ms <= 0) return "—";
56
57
  return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
57
58
  }
58
59
  var COLUMNS = [
@@ -25210,7 +25210,7 @@ function ImageAwareContent({ text, markdown }) {
25210
25210
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideX, { className: "size-5" })
25211
25211
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
25212
25212
  src: viewerSrc,
25213
- alt: "generated image preview",
25213
+ alt: "generated preview",
25214
25214
  className: "max-h-full max-w-full object-contain rounded-md bg-white",
25215
25215
  onClick: (e) => e.stopPropagation()
25216
25216
  })]
@@ -25342,7 +25342,7 @@ function MetricsPanel({ data, toolCallCount }) {
25342
25342
  }),
25343
25343
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25344
25344
  label: "TTFT",
25345
- value: formatDurationMs(data.ttft_ms)
25345
+ value: formatRunTtftMs(data.ttft_ms)
25346
25346
  }),
25347
25347
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25348
25348
  label: "Tokens/sec",
@@ -25392,6 +25392,10 @@ function formatDurationMs(ms) {
25392
25392
  if (ms === null || ms === void 0) return "—";
25393
25393
  return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
25394
25394
  }
25395
+ function formatRunTtftMs(ms) {
25396
+ if (ms === null || ms === void 0 || ms <= 0) return "—";
25397
+ return formatDurationMs(ms);
25398
+ }
25395
25399
  function detectTokenRateProvider(source) {
25396
25400
  const modelName = source.model_name?.trim().toLowerCase();
25397
25401
  if (modelName) {
@@ -25409,7 +25413,7 @@ function isTokensPerSecEnabled(source) {
25409
25413
  return provider !== null && TOKENS_PER_SEC_PROVIDERS.has(provider);
25410
25414
  }
25411
25415
  function formatTokensPerSec(outputTokens, durationMs, ttftMs, enabled) {
25412
- if (!enabled || outputTokens === null || outputTokens === void 0 || durationMs === null || durationMs === void 0 || ttftMs === null || ttftMs === void 0) return "—";
25416
+ if (!enabled || outputTokens === null || outputTokens === void 0 || durationMs === null || durationMs === void 0 || ttftMs === null || ttftMs === void 0 || ttftMs <= 0) return "—";
25413
25417
  const generationMs = durationMs - ttftMs;
25414
25418
  if (outputTokens <= 0 || generationMs <= 0) return "—";
25415
25419
  return `${Math.round(outputTokens / (generationMs / 1e3)).toLocaleString()} tok/s`;
@@ -25423,11 +25427,85 @@ function CompactMetric({ label, value }) {
25423
25427
  children: value
25424
25428
  })] });
25425
25429
  }
25430
+ function readToolInputImages(args) {
25431
+ if (!args || typeof args !== "object" || Array.isArray(args)) return [];
25432
+ const inputImages = args.inputImages;
25433
+ return Array.isArray(inputImages) ? inputImages : void 0;
25434
+ }
25435
+ function getToolInputImages(args) {
25436
+ return (readToolInputImages(args) ?? []).filter((item) => {
25437
+ if (!item || typeof item !== "object" || Array.isArray(item)) return false;
25438
+ const image = item;
25439
+ return typeof image.url === "string" && image.url.length > 0 || typeof image.data === "string" && image.data.length > 0;
25440
+ });
25441
+ }
25442
+ function hideToolInputImages(args) {
25443
+ if (!args || typeof args !== "object" || Array.isArray(args)) return args;
25444
+ const record = args;
25445
+ const inputImages = readToolInputImages(record);
25446
+ if (!inputImages) return args;
25447
+ return {
25448
+ ...record,
25449
+ inputImages: inputImages.map((item) => {
25450
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
25451
+ const image = item;
25452
+ const maskedUrl = typeof image.url === "string" && image.url.toLowerCase().startsWith("data:") ? `[data-url ${image.url.length} chars]` : image.url;
25453
+ return {
25454
+ mediaType: image.mediaType,
25455
+ byteSize: image.byteSize,
25456
+ ...maskedUrl ? { url: maskedUrl } : {},
25457
+ ...image.data ? { data: `[base64 ${image.data.length} chars]` } : {}
25458
+ };
25459
+ })
25460
+ };
25461
+ }
25462
+ function ToolInputImages({ images }) {
25463
+ const renderableImages = images.map((image, index) => {
25464
+ const urlSrc = image.url && !image.url.startsWith("[") ? image.url : void 0;
25465
+ const dataSrc = image.data ? `data:${image.mediaType ?? "image/png"};base64,${image.data}` : "";
25466
+ const src = urlSrc ?? dataSrc;
25467
+ return src ? {
25468
+ image,
25469
+ index,
25470
+ src
25471
+ } : null;
25472
+ }).filter((item) => item !== null);
25473
+ if (renderableImages.length === 0) return null;
25474
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
25475
+ className: "text-[10px] uppercase tracking-wider text-sand-400 font-medium px-2",
25476
+ children: "Input Images"
25477
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
25478
+ className: "mt-1 grid grid-cols-1 sm:grid-cols-2 gap-2",
25479
+ children: renderableImages.map(({ image, index, src }) => {
25480
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25481
+ className: "rounded-md border border-sand-200 bg-white overflow-hidden",
25482
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
25483
+ src,
25484
+ alt: `reference ${index + 1}`,
25485
+ className: "max-h-64 w-full object-contain bg-white",
25486
+ loading: "lazy"
25487
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25488
+ className: "px-2 py-1 text-[10px] text-sand-500 font-mono border-t border-sand-100",
25489
+ children: [
25490
+ image.mediaType ?? "image",
25491
+ " ·",
25492
+ " ",
25493
+ image.byteSize !== void 0 ? `${image.byteSize.toLocaleString()} bytes` : "url"
25494
+ ]
25495
+ })]
25496
+ }, `${image.mediaType ?? "image"}-${index}`);
25497
+ })
25498
+ })] });
25499
+ }
25500
+ function RequestInputImages({ stepId }) {
25501
+ const { data: detail } = RequestLogStepService.useRequestLogStep("I", stepId ?? 0, { enabled: stepId !== null });
25502
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolInputImages, { images: getToolInputImages(detail ? safeParseJson(detail.tool_args) : null) });
25503
+ }
25426
25504
  function ToolCallItem({ entry }) {
25427
25505
  const hasError = !!entry.error;
25428
25506
  const [opened, setOpened] = (0, import_react.useState)(false);
25429
25507
  const { data: detail } = RequestLogStepService.useRequestLogStep("A", entry.id, { enabled: opened });
25430
- const args = detail ? safeParseJson(detail.tool_args) : null;
25508
+ const displayArgs = hideToolInputImages(detail ? safeParseJson(detail.tool_args) : null);
25431
25509
  const result = detail ? safeParseJson(detail.tool_result) : null;
25432
25510
  const error = detail?.error ?? entry.error;
25433
25511
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", {
@@ -25462,7 +25540,7 @@ function ToolCallItem({ entry }) {
25462
25540
  children: "Request"
25463
25541
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
25464
25542
  className: "mt-1 rounded-md bg-sand-50 p-3 overflow-auto",
25465
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormattedContent, { text: detail ? JSON.stringify(args) : "Loading..." })
25543
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormattedContent, { text: detail ? JSON.stringify(displayArgs) : "Loading..." })
25466
25544
  })] }),
25467
25545
  error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
25468
25546
  className: "text-[10px] uppercase tracking-wider text-red-500 font-medium px-2",
@@ -25562,7 +25640,7 @@ function StepTreeItem({ entry, tokensPerSecEnabled }) {
25562
25640
  }),
25563
25641
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
25564
25642
  label: "TTFT",
25565
- value: formatDurationMs(generate.ttftMs)
25643
+ value: formatRunTtftMs(generate.ttftMs)
25566
25644
  }),
25567
25645
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
25568
25646
  label: "Tokens/sec",
@@ -25712,6 +25790,7 @@ function RequestDetail({ id }) {
25712
25790
  page: 1,
25713
25791
  orderBy: "id-asc"
25714
25792
  });
25793
+ const stepTree = buildStepTree(stepsData?.rows ?? []);
25715
25794
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25716
25795
  className: "max-w-6xl mx-auto space-y-4",
25717
25796
  children: [
@@ -25731,8 +25810,8 @@ function RequestDetail({ id }) {
25731
25810
  children: "Request not found."
25732
25811
  })]
25733
25812
  });
25734
- const stepTree = buildStepTree(stepsData?.rows ?? []);
25735
25813
  const toolCalls = stepTree.flatMap((step) => step.toolCalls);
25814
+ const firstImageGenerationStepId = toolCalls.find((toolCall) => toolCall.toolName === "image_generation")?.id ?? null;
25736
25815
  const hasSteps = stepTree.length > 0;
25737
25816
  const tokensPerSecEnabled = isTokensPerSecEnabled(data);
25738
25817
  const history = data.history ?? null;
@@ -25751,11 +25830,14 @@ function RequestDetail({ id }) {
25751
25830
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, {
25752
25831
  title: "User",
25753
25832
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25754
- className: "relative",
25755
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, { text: data.user_prompt ?? "null" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormattedContent, {
25756
- text: data.user_prompt ?? "null",
25757
- markdown: true
25758
- })]
25833
+ className: "space-y-3",
25834
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25835
+ className: "relative",
25836
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, { text: data.user_prompt ?? "null" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormattedContent, {
25837
+ text: data.user_prompt ?? "null",
25838
+ markdown: true
25839
+ })]
25840
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RequestInputImages, { stepId: firstImageGenerationStepId })]
25759
25841
  })
25760
25842
  })]
25761
25843
  });
@@ -22679,7 +22679,7 @@ var sections = [{
22679
22679
  icon: lucideFileText
22680
22680
  }]
22681
22681
  }];
22682
- var qgridVersion = "2.4.0";
22682
+ var qgridVersion = "2.4.1";
22683
22683
  function Sidebar({ className }) {
22684
22684
  const pathname = useRouterState({ select: (s) => s.location.pathname });
22685
22685
  const isActive = (path) => {
@@ -44563,7 +44563,7 @@ var $$splitComponentImporter$3 = () => import("./assets/tokens-Cjw6cQxZ.js");
44563
44563
  var Route$3 = createFileRoute("/tokens")({ component: lazyRouteComponent($$splitComponentImporter$3, "component") });
44564
44564
  //#endregion
44565
44565
  //#region src/routes/logs.tsx
44566
- var $$splitComponentImporter$2 = () => import("./assets/logs-B_ZLzpwY.js");
44566
+ var $$splitComponentImporter$2 = () => import("./assets/logs-Bk9C0ELo.js");
44567
44567
  var logsSearchSchema = object({
44568
44568
  token: string().optional(),
44569
44569
  project: string().optional(),
@@ -44579,7 +44579,7 @@ var $$splitComponentImporter$1 = () => import("./assets/routes-D9OiKoqa.js");
44579
44579
  var Route$1 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter$1, "component") });
44580
44580
  //#endregion
44581
44581
  //#region src/routes/requests/show.tsx
44582
- var $$splitComponentImporter = () => import("./assets/show-CGlX9OYt.js");
44582
+ var $$splitComponentImporter = () => import("./assets/show-3GokIDEJ.js");
44583
44583
  var showSearchSchema = object({ id: number() });
44584
44584
  var Route = createFileRoute("/requests/show")({
44585
44585
  validateSearch: showSearchSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartanova/qgrid-cli",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cartanova-ai/qgrid"
package/postinstall.mjs CHANGED
@@ -42,6 +42,11 @@ function defaultProjectRoot() {
42
42
  return findProjectRoot(cwd);
43
43
  }
44
44
 
45
+ function isInside(parentDir, childDir) {
46
+ const relative = path.relative(path.resolve(parentDir), path.resolve(childDir));
47
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
48
+ }
49
+
45
50
  function defaultTargets() {
46
51
  const isGlobalInstall =
47
52
  process.env.npm_config_global === "true" || process.env.npm_config_location === "global";
@@ -61,6 +66,11 @@ function defaultTargets() {
61
66
 
62
67
  const projectRoot = defaultProjectRoot();
63
68
  if (!projectRoot) return null;
69
+ const isInstalledPackage = packageDir.split(path.sep).includes("node_modules");
70
+ if (!isInstalledPackage && isInside(projectRoot, sourceSkillsDir)) {
71
+ console.log("qgrid skill sync skipped: running inside qgrid repository.");
72
+ return [];
73
+ }
64
74
 
65
75
  return [
66
76
  {
@@ -104,7 +114,7 @@ async function syncSkill({ label, target, useSymlink }) {
104
114
 
105
115
  const targetConfigs = defaultTargets();
106
116
 
107
- if (!targetConfigs) {
117
+ if (targetConfigs === null) {
108
118
  console.log("qgrid skill sync skipped: project root not found.");
109
119
  process.exit(0);
110
120
  }
@@ -114,6 +124,8 @@ try {
114
124
  await syncSkill(targetConfig);
115
125
  }
116
126
  } catch (error) {
117
- console.error(`qgrid skill sync failed: ${error instanceof Error ? error.message : String(error)}`);
127
+ console.error(
128
+ `qgrid skill sync failed: ${error instanceof Error ? error.message : String(error)}`,
129
+ );
118
130
  process.exit(1);
119
131
  }
@@ -64,6 +64,8 @@ Payload responsibilities:
64
64
  - Preserve and resend `runContext` for tool-call follow-ups.
65
65
  - Store/resend OpenAI `threadCoord` by `sessionKey`.
66
66
  - Send `imageGeneration` and `imageGenerationOptions` when configured.
67
+ - Send AI SDK multimodal image/file message parts as qgrid `input` only when `imageGeneration` is enabled. Normal non-image-generation calls keep `user_prompt` text-only and do not forward image input.
68
+ - Reject oversized reference-image data URLs before the request leaves the SDK. Reference images travel as JSON data URLs; callers should compress/resize large photos, preferably to WebP/JPEG.
67
69
 
68
70
  Tools and `jsonSchema` cannot be used together at qgrid dispatcher level.
69
71
 
@@ -71,6 +73,8 @@ When tools are present, qgrid sends tool definitions to the server as `tools`; i
71
73
 
72
74
  Image generation is different from AI SDK client tools: it is an opt-in Codex-hosted tool exposed by qgrid through `providerOptions.qgrid.imageGeneration`. Use `generateText`; `streamText` rejects image generation before opening the stream.
73
75
 
76
+ Reference images for image generation use normal AI SDK multimodal message parts. The provider wraps image/file base64 as `data:${mediaType};base64,...` qgrid `input` items. This is intentionally scoped to image-generation requests, not general vision/chat behavior.
77
+
74
78
  ## Response mapping
75
79
 
76
80
  qgrid response content maps to AI SDK content:
@@ -172,8 +172,8 @@ Key decisions:
172
172
  - It is non-stream only. Codex returns completed base64 image payloads, not useful image deltas.
173
173
  - Image turns are cold one-shot threads and are excluded from thread reuse. This prevents base64 payloads from entering reusable conversation state and cache prefixes.
174
174
  - qgrid performs preflight capability/model gates and postflight "image count must be greater than zero" checks. Codex can otherwise silently return text when image generation is unavailable or unused.
175
- - Returned images are inline base64 content parts for consumers and AI SDK `file` parts. qgrid does not promise aspect ratio, edits, or reference-image control on this path.
175
+ - Returned images are inline base64 content parts for consumers and AI SDK `file` parts. Reference images are accepted through AI SDK multimodal message parts only on the `imageGeneration` path, and are transported as JSON data URLs with SDK-side size guarding.
176
176
  - `imageGenerationOptions` currently supports quality and size. The worker instruction and request-log pricing assumption use `gpt-image-2`, with defaults `medium` and `1536x1024`.
177
- - qgrid does not manage generated images as durable assets through a `generated_images` table or object-storage layer. Current request logs can still contain inline image data URLs for inspection and synthetic `image_generation` tool-call steps.
177
+ - qgrid does not manage generated or reference images as durable assets through a `generated_images` table or object-storage layer. Current request logs can still contain inline image data URLs for inspection and synthetic `image_generation` tool-call steps; reference input images live in the first synthetic step's `tool_args.inputImages`.
178
178
  - Image cost is stored separately in `request_logs.image_cost_usd`; `request_logs.cost_usd` remains the Codex driver model token cost. Because Codex does not expose exact image tool usage, `image_cost_usd` is a price-table estimate and may be inaccurate.
179
179
  - Inspect current plans, tests, and implementation before changing behavior; the Codex tool surface can change underneath qgrid.
@@ -145,9 +145,11 @@ Image generation is OpenAI/Codex-only and implemented as an opt-in Codex `image_
145
145
  - It enables `features.image_generation` only on that thread; global config remains disabled.
146
146
  - It swaps the normal "text only" base instruction for an image-permitting instruction on that thread.
147
147
  - It gates on provider capability and model multimodality.
148
+ - AI SDK reference images are accepted only on this image-generation path. They arrive as qgrid `input` image data URLs and should be compressed/resized by callers before JSON transport.
148
149
  - Failure kinds are `gate`, `not_called`, and `incomplete`.
149
150
  - Codex notifications expose image items through `item/started` and `item/completed`; qgrid treats non-empty base64 `result` as the completion signal, not the item `status` string.
150
151
  - Completed images are surfaced as qgrid `image` content parts and AI SDK `file` parts with `mediaType: "image/png"`.
151
152
  - qgrid uses `gpt-image-2` as the image-tool pricing/model assumption. Codex does not expose exact image tool token usage, so `image_cost_usd` is an estimate from the public price table, not exact billing.
153
+ - Codex may return multiple completed image outputs. qgrid maps each one to a separate image content part and a synthetic `image_generation` request-log tool step.
152
154
 
153
155
  Before modifying this area, inspect latest docs/plans/brainstorms and current tests.
@@ -58,6 +58,8 @@ Anthropic: normalize native mutually exclusive categories by summing input + cac
58
58
 
59
59
  For image requests, keep `cost_usd` as the Codex driver model token cost. Image output cost is separate because qgrid observes Codex's `image_generation` result, not the OpenAI Images API usage object. Treat `image_cost_usd` as a price-table estimate that may be inaccurate if Codex changes its underlying image accounting.
60
60
 
61
+ Reference images for image generation are not stored in `request_logs.user_prompt`; that field remains the text prompt. For inspection in the detail view, qgrid stores reference image metadata/base64 in the synthetic `image_generation` step's `tool_args.inputImages`. If Codex returns multiple image outputs, store `inputImages` only on the first synthetic image tool step so multi-output logs do not duplicate the same input payload on every output.
62
+
61
63
  ## Legacy normalization
62
64
 
63
65
  `RequestLogModel` normalizes legacy Anthropic rows where stored `input_tokens` may not include cache read/write. Keep this in mind when changing cost or display logic.
@@ -37,6 +37,8 @@ Check list and detail views. Avoid loading large text columns in list views unle
37
37
 
38
38
  Image-generation request logs add `is_image_generation`, `image_cost_usd`, and `image_cost_method`. The list view displays driver plus image cost as the total cost cell; the detail view shows driver cost and image cost separately and renders generated image data URLs from response/tool-step content.
39
39
 
40
+ Reference input images for image generation render in the detail view under the user prompt from the first synthetic `image_generation` step's `tool_args.inputImages`. Keep the prompt itself text-only, redact large base64 in request JSON views, and avoid rendering duplicate input previews for multi-output image turns.
41
+
40
42
  ## Verification
41
43
 
42
44
  After API shape changes:
@@ -164,6 +164,10 @@ Other scripts under `scripts/smoke-test-*` and `scripts/debug-*` are ad hoc prob
164
164
 
165
165
  - AI SDK version-skew guard. The client requested image generation, but the server returned text-only content. Check server version and the OpenAI image-generation path.
166
166
 
167
+ `reference image input is too large for JSON transport`:
168
+
169
+ - AI SDK reference-image guard. The client passed a base64 image data URL large enough to likely exceed qgrid's JSON request body transport. Compress or resize the image before calling `generateText`, preferably as WebP/JPEG.
170
+
167
171
  OpenAI `ImageGenerationError` kinds:
168
172
 
169
173
  - `gate`: capability/model check failed before the turn.