@askdb/studio 0.2.0-beta.41 → 0.2.0-beta.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -61,6 +61,6 @@ Configuration, in `askdb.config.*` (bind any value to an environment variable of
61
61
  | `studio.listen.port` | Bind port. Defaults to `5556`. `--port` overrides it. |
62
62
  | `dev.mockSql` | Deterministic generated SQL for tests or offline demos. |
63
63
 
64
- Security: every `/api/*` request must carry the per-launch session token that Studio injects into the page it serves (`x-askdb-studio-token` header), come with an allowed `Host` (`localhost`, `127.0.0.1`, `[::1]`, or the bound host), and, for writes, be same-origin JSON. Programmatic callers of `createStudioServer()` can read the token from `server.sessionToken`. Binding to a non-loopback host exposes query execution and schema writes to anyone on that network who can load the page. Only do that on a trusted network. See [Studio → Security model](https://askdb.tools/studio/#security-model).
64
+ Security: every `/api/*` request must carry the per-launch session token that Studio injects into the page it serves (`x-askdb-studio-token` header), come with an allowed `Host` (`localhost`, `127.0.0.1`, `[::1]`, or the bound host), and, for writes, be same-origin JSON. Programmatic callers of `createStudioServer()` can read the token from `server.sessionToken`. Binding to a non-loopback host exposes query execution and schema writes to anyone on that network who can load the page. Only do that on a trusted network. See [Studio → Security model](https://askdb.tools/reference/studio/#security-model).
65
65
 
66
66
  Studio uses the active `rag.store` branch from `askdb.config.*`, and embeds with `rag.embedder`: `mock`, a local lexical embedder that needs no key, or `ai`, the `ai.embedding` model, built from that section's connection only (see the [configuration reference](https://askdb.tools/reference/config/#aiembedding)). For `pgvector`, make sure `rag.storeConfig.pgvector.databaseUrl` resolves; there's no need to create the table. The first index build creates it, and the `vector` extension, at the width the embedding model returns (or `ai.embedding.dimensions`). A table that already exists at another width is refused, with a message saying how to resolve it. If Studio's database role can't create extensions, create `vector` first.
@@ -97,4 +97,4 @@ const { sql${l&&i===`sql-params`?`, tenantParams`:``} } = await ask({
97
97
  `).map((e,t)=>t===0?e:n+e).join(`
98
98
  `)}var Mg=`h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring`,Ng={border:0,padding:0,margin:0};function Pg(e){return`${e}-${globalThis.crypto?.randomUUID?.()??`${Date.now()}-${Math.random().toString(36).slice(2)}`}`}function Fg(){let{workspace:e}=vr(),{ragAvailable:t}=jr(),{askQuestion:n,setAskQuestion:r,askMode:i,setAskMode:a,askMessage:o,askResult:s,askTenantEnabled:c,setAskTenantEnabled:l,askTenantAccessKind:u,setAskTenantAccessKind:d,askTenantRoot:f,setAskTenantRoot:p,askTenantIdsText:m,setAskTenantIdsText:h,askTenantMultiRootRows:g,setAskTenantMultiRootRows:_,askTenantGlobalReason:v,setAskTenantGlobalReason:y,askTenantContext:b,setAskTenantContext:x,askTenantContextAttributes:S,setAskTenantContextAttributes:C,generatedTenantScopeJson:w,tenantScopeValidationError:T,askTenantSqlMode:E,setAskTenantSqlMode:D,executeResult:O,executeMessage:k,executeStatus:A,historyEntries:j,busy:N,handleAsk:ee,handleExecute:te,loadHistoryEntry:P,handleDeleteHistory:ne,handleInstallExecuteDriver:re}=Vr(),ie=(0,M.useCallback)(e=>{(e.metaKey||e.ctrlKey)&&e.key===`Enter`&&!N.has(`ask`)&&(e.preventDefault(),ee())},[N,ee]);if((0,M.useEffect)(()=>(document.addEventListener(`keydown`,ie),()=>document.removeEventListener(`keydown`,ie)),[ie]),!e)return null;let ae=!!e.tenantPolicy,oe=e.tenantPolicy?.roots??[],se=oe[0]?.id??``;return(0,R.jsxs)(`main`,{className:`main-pane`,children:[(0,R.jsx)(`div`,{className:`main-hd`,children:(0,R.jsxs)(`div`,{className:`main-title`,children:[(0,R.jsxs)(`h1`,{children:[(0,R.jsx)(ea,{size:18,style:{display:`inline`,marginRight:8}}),`Query Playground`]}),(0,R.jsx)(`div`,{className:`main-sub`,children:`Generate SQL from natural language and execute it against your database`})]})}),(0,R.jsx)(`div`,{className:`main-body`,style:{overflow:`hidden`,padding:0},children:(0,R.jsxs)(`div`,{style:{display:`grid`,gridTemplateColumns:`2fr 3fr`,height:`100%`},children:[(0,R.jsxs)(`div`,{style:{borderRight:`1px solid var(--border)`,display:`flex`,flexDirection:`column`,overflow:`auto`},children:[(0,R.jsx)(`div`,{style:{padding:`var(--pad-y) var(--pad-x)`},children:(0,R.jsxs)(`div`,{style:{display:`grid`,gap:12},children:[(0,R.jsx)($,{label:`Natural language question`,children:(0,R.jsx)(Hh,{value:n,onChange:e=>r(e.target.value),placeholder:`How many users placed orders last month?`,rows:4})}),(0,R.jsxs)(`div`,{style:{display:`grid`,gap:6},children:[(0,R.jsx)(`span`,{className:`muted`,style:{fontSize:11,fontWeight:600,textTransform:`uppercase`,letterSpacing:`0.04em`},children:`Retrieval mode`}),(0,R.jsxs)(`fieldset`,{className:`toggle-seg`,style:Ng,children:[(0,R.jsx)(`legend`,{className:`sr-only`,children:`Retrieval mode`}),(0,R.jsx)(`button`,{className:i===`full`?`active`:``,onClick:()=>a(`full`),children:`Full schema`}),(0,R.jsxs)(`button`,{className:i===`rag`?`active`:``,disabled:!t,title:t?void 0:`Build the RAG index first to query with retrieval.`,onClick:()=>a(`rag`),children:[!t&&(0,R.jsx)(Ki,{size:12,style:{display:`inline`,marginRight:4}}),`RAG`]})]})]}),ae&&(0,R.jsxs)(`div`,{style:{display:`grid`,gap:10},children:[(0,R.jsxs)(`label`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,R.jsx)(`input`,{type:`checkbox`,checked:c,onChange:e=>l(e.target.checked)}),(0,R.jsxs)(`span`,{className:`muted`,style:{fontSize:11,fontWeight:600},children:[(0,R.jsx)(ha,{size:12,style:{display:`inline`,marginRight:4}}),`Tenant scope`]})]}),c&&(0,R.jsxs)(`div`,{style:{display:`grid`,gap:10},children:[(0,R.jsxs)(`div`,{style:{display:`grid`,gap:6},children:[(0,R.jsx)(`span`,{className:`muted`,style:{fontSize:11,fontWeight:600,textTransform:`uppercase`,letterSpacing:`0.04em`},children:`Access kind`}),(0,R.jsxs)(`fieldset`,{className:`toggle-seg`,style:Ng,children:[(0,R.jsx)(`legend`,{className:`sr-only`,children:`Tenant scope access kind`}),(0,R.jsx)(`button`,{className:u===`ids`?`active`:``,onClick:()=>d(`ids`),children:`IDs`}),(0,R.jsx)(`button`,{className:u===`multi_root`?`active`:``,onClick:()=>d(`multi_root`),children:`Multi-root`}),(0,R.jsx)(`button`,{className:u===`global`?`active`:``,onClick:()=>d(`global`),children:`Super / global`})]})]}),(u===`ids`||u===`subtree`)&&(0,R.jsxs)(`div`,{style:{display:`grid`,gap:8},children:[(0,R.jsx)($,{label:`Tenant root`,children:(0,R.jsx)(`select`,{className:Mg,value:f,onChange:e=>p(e.target.value),children:oe.map(e=>(0,R.jsxs)(`option`,{value:e.id,children:[e.label,` (`,e.id,`)`]},e.id))})}),(0,R.jsx)($,{label:u===`ids`?`Tenant IDs`:`Subtree root IDs`,description:`Comma-separated IDs from your app's auth context.`,children:(0,R.jsx)(Vm,{value:m,onChange:e=>h(e.target.value),placeholder:u===`ids`?`org-1, org-2`:`region-1`})})]}),u===`multi_root`&&(0,R.jsxs)(`div`,{style:{display:`grid`,gap:8},children:[g.map((e,t)=>(0,R.jsxs)(`div`,{style:{display:`grid`,gridTemplateColumns:`minmax(0, 1fr) minmax(0, 1fr) auto`,gap:8,alignItems:`end`},children:[(0,R.jsx)($,{label:`Tenant root`,className:`min-w-0`,children:(0,R.jsx)(`select`,{className:Mg,value:e.tenantRoot,onChange:n=>{let r=[...g];r[t]={...e,tenantRoot:n.target.value},_(r)},children:oe.map(e=>(0,R.jsxs)(`option`,{value:e.id,children:[e.label,` (`,e.id,`)`]},e.id))})}),(0,R.jsx)($,{label:`IDs`,className:`min-w-0`,children:(0,R.jsx)(Vm,{value:e.idsText,onChange:n=>{let r=[...g];r[t]={...e,idsText:n.target.value},_(r)},placeholder:`org-1, org-2`})}),(0,R.jsx)(`button`,{className:`btn ghost sm`,disabled:g.length===1,onClick:()=>_(g.filter((e,n)=>n!==t)),title:`Remove scope row`,children:(0,R.jsx)(Ca,{size:14})})]},e.id)),(0,R.jsx)(`div`,{children:(0,R.jsxs)(`button`,{className:`btn ghost sm`,onClick:()=>_([...g,{id:Pg(`multi-root`),tenantRoot:se,idsText:``}]),children:[(0,R.jsx)(na,{size:14}),`Add root`]})})]}),u===`global`&&(0,R.jsx)($,{label:`Super / global reason`,description:`Required for audit context.`,children:(0,R.jsx)(Vm,{value:v,onChange:e=>y(e.target.value),placeholder:`superuser access for support investigation`})}),(0,R.jsxs)(`details`,{children:[(0,R.jsx)(`summary`,{className:`muted`,style:{cursor:`pointer`,fontSize:11,fontWeight:600,textTransform:`uppercase`,letterSpacing:`0.04em`},children:`Advisory context`}),(0,R.jsxs)(`div`,{style:{display:`grid`,gap:8,marginTop:8},children:[(0,R.jsxs)(`div`,{style:{display:`grid`,gridTemplateColumns:`1fr 1fr`,gap:8},children:[(0,R.jsx)($,{label:`Role`,children:(0,R.jsx)(Vm,{value:b.role,onChange:e=>x({...b,role:e.target.value}),placeholder:`regional_manager`})}),(0,R.jsx)($,{label:`Label`,children:(0,R.jsx)(Vm,{value:b.label,onChange:e=>x({...b,label:e.target.value}),placeholder:`Jane Smith, Northeast`})}),(0,R.jsx)($,{label:`Department`,children:(0,R.jsx)(Vm,{value:b.department,onChange:e=>x({...b,department:e.target.value}),placeholder:`operations`})}),(0,R.jsx)($,{label:`Region`,children:(0,R.jsx)(Vm,{value:b.region,onChange:e=>x({...b,region:e.target.value}),placeholder:`northeast`})})]}),(0,R.jsx)($,{label:`Description`,children:(0,R.jsx)(Hh,{value:b.description,onChange:e=>x({...b,description:e.target.value}),placeholder:`Business context for this user's scope.`,rows:2})}),S.map((e,t)=>(0,R.jsxs)(`div`,{style:{display:`grid`,gridTemplateColumns:`minmax(0, 1fr) minmax(0, 1fr) auto`,gap:8,alignItems:`end`},children:[(0,R.jsx)($,{label:`Attribute key`,className:`min-w-0`,children:(0,R.jsx)(Vm,{value:e.key,onChange:n=>{let r=[...S];r[t]={...e,key:n.target.value},C(r)},placeholder:`team`})}),(0,R.jsx)($,{label:`Attribute value`,className:`min-w-0`,children:(0,R.jsx)(Vm,{value:e.value,onChange:n=>{let r=[...S];r[t]={...e,value:n.target.value},C(r)},placeholder:`field-sales`})}),(0,R.jsx)(`button`,{className:`btn ghost sm`,onClick:()=>C(S.filter((e,n)=>n!==t)),title:`Remove attribute`,children:(0,R.jsx)(Ca,{size:14})})]},e.id)),(0,R.jsx)(`div`,{children:(0,R.jsxs)(`button`,{className:`btn ghost sm`,onClick:()=>C([...S,{id:Pg(`context-attribute`),key:``,value:``}]),children:[(0,R.jsx)(na,{size:14}),`Add attribute`]})})]})]}),(0,R.jsxs)(`div`,{style:{display:`grid`,gap:6},children:[(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:8},children:[(0,R.jsx)(`span`,{className:`muted`,style:{fontSize:11,fontWeight:600,textTransform:`uppercase`,letterSpacing:`0.04em`},children:`tenantScope JSON preview`}),w&&(0,R.jsx)(Tg,{value:w})]}),(0,R.jsx)(`pre`,{className:`mono`,style:{minHeight:116,margin:0,overflow:`auto`,whiteSpace:`pre-wrap`,border:`1px solid var(--border)`,borderRadius:6,padding:10,fontSize:11,background:`var(--surface-2)`},children:w||T||`Complete the tenant scope fields to preview the JSON passed to ask().`}),T&&(0,R.jsx)(`span`,{style:{color:`var(--red-600)`,fontSize:12},children:T})]}),(0,R.jsxs)(`div`,{style:{display:`grid`,gap:6},children:[(0,R.jsx)(`span`,{className:`muted`,style:{fontSize:11,fontWeight:600,textTransform:`uppercase`,letterSpacing:`0.04em`},children:`SQL output mode`}),(0,R.jsxs)(`fieldset`,{className:`toggle-seg`,style:Ng,children:[(0,R.jsx)(`legend`,{className:`sr-only`,children:`SQL output mode`}),(0,R.jsx)(`button`,{className:E===`sql-only`?`active`:``,onClick:()=>D(`sql-only`),children:`Inline literals`}),(0,R.jsx)(`button`,{className:E===`sql-params`?`active`:``,onClick:()=>D(`sql-params`),children:`$N parameters`})]})]})]})]}),(0,R.jsxs)(`button`,{className:`btn primary`,disabled:N.has(`ask`),onClick:()=>void ee(),title:`Generate SQL (⌘↵)`,children:[N.has(`ask`)?(0,R.jsx)(Wi,{size:14,className:`animate-spin`}):(0,R.jsx)(_a,{size:14}),`Generate SQL`,(0,R.jsx)(`kbd`,{className:`kbd`,style:{marginLeft:4,opacity:.7},children:`⌘↵`})]}),o&&(0,R.jsx)(Ah,{status:o})]})}),j.length>0&&(0,R.jsxs)(`div`,{style:{borderTop:`1px solid var(--border)`,flex:1,overflow:`auto`},children:[(0,R.jsx)(`div`,{style:{padding:`8px var(--pad-x)`},children:(0,R.jsxs)(`span`,{className:`muted`,style:{fontSize:11,fontWeight:600,textTransform:`uppercase`,letterSpacing:`0.04em`},children:[`History (`,j.length,`)`]})}),(0,R.jsx)(`div`,{children:j.slice(0,20).map(e=>(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:8,padding:`6px var(--pad-x)`,cursor:`pointer`,fontSize:12,borderBottom:`1px solid var(--border)`},onClick:()=>P(e),children:[(0,R.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`,flex:1},children:e.question}),(0,R.jsx)(`span`,{className:`chip ${e.mode===`rag`?`red`:``}`,style:{flexShrink:0},children:e.mode}),(0,R.jsx)(`button`,{className:`btn ghost sm`,onClick:t=>{t.stopPropagation(),ne(e.id)},title:`Delete`,style:{flexShrink:0,padding:2},children:`×`})]},e.id))})]})]}),(0,R.jsx)(`div`,{style:{overflow:`auto`},children:s?(0,R.jsxs)(`div`,{style:{display:`grid`,gap:0},children:[(0,R.jsxs)(`section`,{style:{padding:`var(--pad-y) var(--pad-x)`,borderBottom:`1px solid var(--border)`},children:[(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,marginBottom:8},children:[(0,R.jsx)(`h3`,{style:{fontSize:13,fontWeight:600},children:`Generated SQL`}),s.sql&&(0,R.jsx)(Tg,{value:s.sql})]}),s.sql?(0,R.jsx)(`pre`,{className:`sql-block`,children:s.sql}):(0,R.jsx)(vg,{text:`No SQL generated.`})]}),s.explain!==null&&s.explain!==void 0&&(0,R.jsx)(`section`,{style:{padding:`var(--pad-y) var(--pad-x)`,borderBottom:`1px solid var(--border)`},children:(0,R.jsx)(zg,{explain:s.explain})}),s.tenant?.enabled&&(0,R.jsxs)(`section`,{style:{padding:`var(--pad-y) var(--pad-x)`,borderBottom:`1px solid var(--border)`},children:[(0,R.jsx)(`h3`,{style:{fontSize:13,fontWeight:600,marginBottom:8},children:`Tenant Params`}),s.tenant.params.length>0?(0,R.jsxs)(`div`,{style:{padding:8,border:`1px solid var(--border)`,borderRadius:`var(--radius)`,fontSize:12},children:[(0,R.jsx)(`span`,{style:{fontWeight:600},children:`Positional params:`}),` `,(0,R.jsx)(`code`,{children:JSON.stringify(s.tenant.params)})]}):(0,R.jsx)(vg,{text:`No tenant params.`})]}),(0,R.jsxs)(`div`,{style:{padding:`var(--pad-y) var(--pad-x)`,borderBottom:`1px solid var(--border)`,display:`flex`,flexDirection:`column`,gap:8},children:[A&&!A.enabled&&(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:12,margin:0},children:A.disabledReason}),A?.enabled&&(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8,flexWrap:`wrap`},children:[(0,R.jsx)(`span`,{className:`muted`,style:{fontSize:11,fontWeight:600},children:A.label}),A.configured?(0,R.jsx)(`span`,{className:`chip`,style:{fontSize:11},children:A.connectionKind===`file`?`file configured`:`URL configured`}):(0,R.jsx)(`span`,{className:`chip red`,style:{fontSize:11},children:A.connectionKind===`file`?`no file`:`no URL`}),A.installed?(0,R.jsxs)(`span`,{className:`chip`,style:{fontSize:11},children:[A.packageName,` ready`]}):(0,R.jsxs)(R.Fragment,{children:[(0,R.jsxs)(`span`,{className:`chip red`,style:{fontSize:11},children:[A.packageName,` missing`]}),A.canInstallFromStudio&&(0,R.jsxs)(`button`,{className:`btn ghost sm`,disabled:N.has(`install-driver`),onClick:()=>void re(),style:{fontSize:11},children:[N.has(`install-driver`)?(0,R.jsx)(Wi,{size:12,className:`animate-spin`}):null,`Install `,A.packageName]}),!A.canInstallFromStudio&&A.manualInstallReason&&(0,R.jsx)(`span`,{className:`muted`,style:{fontSize:11},children:A.installCommand})]})]}),A?.enabled&&A.disabledReason&&(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:12,margin:0},children:A.disabledReason}),A?.enabled&&(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12},children:[(0,R.jsxs)(`button`,{className:`btn primary`,disabled:N.has(`execute`)||!s.sql||!A.configured||!A.installed,onClick:()=>void te(),title:`Runs read-only · ${Math.round(A.timeoutMs/1e3)}s timeout · first ${A.maxRows} rows`,children:[N.has(`execute`)?(0,R.jsx)(Wi,{size:14,className:`animate-spin`}):(0,R.jsx)(ea,{size:14}),`Execute Query`]}),k&&(0,R.jsx)(Ah,{status:k})]})]}),(0,R.jsx)(Ag,{}),O?.ok===!0&&(0,R.jsxs)(`section`,{style:{padding:`var(--pad-y) var(--pad-x)`},children:[(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,marginBottom:8},children:[(0,R.jsx)(`h3`,{style:{fontSize:13,fontWeight:600},children:`Results`}),O.truncated&&(0,R.jsxs)(`span`,{className:`chip amber`,children:[`Showing first `,O.rowLimit,` rows`]})]}),O.columns&&O.rows?(0,R.jsx)(`div`,{style:{overflowX:`auto`},children:(0,R.jsxs)(`table`,{className:`tbl`,children:[(0,R.jsx)(`thead`,{children:(0,R.jsx)(`tr`,{children:O.columns.map(e=>(0,R.jsx)(`th`,{children:e},e))})}),(0,R.jsx)(`tbody`,{children:O.rows.map((e,t)=>(0,R.jsx)(`tr`,{children:e.map((e,t)=>(0,R.jsx)(`td`,{className:`mono`,style:{fontSize:11},children:e==null?(0,R.jsx)(`span`,{className:`muted`,children:`NULL`}):String(e)},t))},t))})]})}):(0,R.jsx)(vg,{text:`No results.`})]}),s.usage&&(0,R.jsx)(`section`,{style:{padding:`var(--pad-y) var(--pad-x)`,borderTop:`1px solid var(--border)`},children:(0,R.jsx)(Sg,{title:`Token usage`,usage:s.usage})})]}):(0,R.jsx)(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`center`,height:`100%`,padding:32},children:(0,R.jsx)(vg,{text:`Ask a question to generate SQL`})})})]})})]})}function Ig(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}function Lg(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.statementKind==`string`&&Array.isArray(t.checksVerified)&&t.checksVerified.every(e=>typeof e==`string`)&&typeof t.remediationNote==`string`}function Rg(e){return e.replace(/_/g,` `)}function zg({explain:e}){let[t,n]=(0,M.useState)(!1);return(0,R.jsxs)(`div`,{children:[(0,R.jsxs)(`button`,{type:`button`,className:`collapsible-btn`,onClick:()=>n(!t),children:[t?(0,R.jsx)(vi,{size:14}):(0,R.jsx)(bi,{size:14}),`Explain`]}),t&&(Lg(e)?(0,R.jsxs)(`div`,{style:{display:`grid`,gap:8,fontSize:12},children:[(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:6},children:[(0,R.jsx)(`span`,{className:`muted`,style:{fontWeight:600},children:`Statement kind`}),(0,R.jsx)(`span`,{className:`chip`,children:e.statementKind})]}),(0,R.jsxs)(`div`,{style:{display:`grid`,gap:4},children:[(0,R.jsx)(`span`,{className:`muted`,style:{fontWeight:600},children:`Checks verified`}),(0,R.jsx)(`ul`,{style:{listStyle:`none`,margin:0,padding:0,display:`grid`,gap:4},children:e.checksVerified.map(e=>(0,R.jsxs)(`li`,{style:{display:`flex`,alignItems:`center`,gap:6},children:[(0,R.jsx)(gi,{size:13,style:{color:`var(--green-500)`,flexShrink:0}}),Rg(e)]},e))})]}),(0,R.jsx)(`p`,{className:`muted`,style:{margin:0,fontStyle:`italic`},children:e.remediationNote})]}):(0,R.jsx)(`pre`,{className:`plain-block`,children:Ig(e)}))]})}function Bg(){let{workspace:e}=vr(),{ragStatus:t}=jr();return e?(0,R.jsxs)(`main`,{className:`main-pane`,children:[(0,R.jsx)(`div`,{className:`main-hd`,children:(0,R.jsxs)(`div`,{className:`main-title`,children:[(0,R.jsxs)(`h1`,{children:[(0,R.jsx)(pa,{size:18,style:{display:`inline`,marginRight:8}}),`Settings`]}),(0,R.jsx)(`div`,{className:`main-sub`,children:`Workspace configuration and diagnostics`})]})}),(0,R.jsx)(`div`,{className:`main-body`,children:(0,R.jsxs)(`div`,{className:`stack`,style:{padding:`var(--pad-y) var(--pad-x)`},children:[(0,R.jsxs)(`section`,{className:`card`,children:[(0,R.jsx)(`div`,{className:`card-hd`,children:(0,R.jsx)(`h3`,{children:`Workspace`})}),(0,R.jsx)(`div`,{className:`card-bd`,children:(0,R.jsxs)(`dl`,{className:`definition-list`,children:[(0,R.jsx)(`dt`,{children:`Schema ID`}),(0,R.jsx)(`dd`,{children:e.schemaId}),(0,R.jsx)(`dt`,{children:`Schema path`}),(0,R.jsx)(`dd`,{children:e.schemaDir}),(0,R.jsx)(`dt`,{children:`AI provider`}),(0,R.jsx)(`dd`,{children:e.aiProvider}),(0,R.jsx)(`dt`,{children:`Model`}),(0,R.jsx)(`dd`,{children:e.model}),(0,R.jsx)(`dt`,{children:`AI suggestions`}),(0,R.jsx)(`dd`,{children:e.aiConfigured?`Configured`:`Not configured`})]})})]}),(0,R.jsxs)(`section`,{className:`card`,children:[(0,R.jsx)(`div`,{className:`card-hd`,children:(0,R.jsx)(`h3`,{children:`RAG Store`})}),(0,R.jsx)(`div`,{className:`card-bd`,children:t?(0,R.jsxs)(`dl`,{className:`definition-list`,children:[(0,R.jsx)(`dt`,{children:`Embedder`}),(0,R.jsx)(`dd`,{children:t.embedder.kind===`mock`?`mock (local lexical)`:`${t.embedder.provider??`unknown provider`} · ${t.embedder.model??`no model`}`}),(0,R.jsx)(`dt`,{children:`Store`}),(0,R.jsx)(`dd`,{children:t.store.kind}),(0,R.jsx)(`dt`,{children:`Lock file`}),(0,R.jsx)(`dd`,{children:t.files.lock?`present`:`missing`}),t.store.kind===`file`&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`dt`,{children:`Base path`}),(0,R.jsx)(`dd`,{children:t.store.basePath??`n/a`}),(0,R.jsx)(`dt`,{children:`Embeddings JSON`}),(0,R.jsx)(`dd`,{children:t.files.embeddingsJson?`present`:`missing`}),(0,R.jsx)(`dt`,{children:`Embeddings binary`}),(0,R.jsx)(`dd`,{children:t.files.embeddingsBin?`present`:`missing`})]}),t.store.kind===`pgvector`&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`dt`,{children:`Table`}),(0,R.jsx)(`dd`,{children:t.store.table??`askdb_rag_chunks`}),(0,R.jsx)(`dt`,{children:`Index strategy`}),(0,R.jsx)(`dd`,{children:t.store.indexStrategy??`default`})]}),t.store.kind===`memory`&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`dt`,{children:`Persistence`}),(0,R.jsx)(`dd`,{children:`In-process only`})]})]}):(0,R.jsx)(vg,{text:`RAG status is unavailable.`})})]}),(0,R.jsxs)(`section`,{className:`card`,children:[(0,R.jsx)(`div`,{className:`card-hd`,children:(0,R.jsx)(`h3`,{children:`Schema Warnings`})}),(0,R.jsx)(`div`,{className:`card-bd`,children:e.warnings.length>0?(0,R.jsx)(`div`,{style:{display:`grid`,gap:8},children:e.warnings.map((e,t)=>(0,R.jsx)(`pre`,{className:`warning-block`,children:typeof e==`string`?e:JSON.stringify(e,null,2)},t))}):(0,R.jsx)(vg,{text:`No schema warnings.`})})]})]})})]}):null}function Vg({label:e,description:t,value:n,onChange:r,disabled:i}){let[a,o]=(0,M.useState)(!1);return a&&!i?(0,R.jsx)($,{label:e,description:t,children:(0,R.jsx)(Vm,{autoFocus:!0,value:n,onChange:e=>r(e.target.value.toUpperCase()),onBlur:()=>o(!1),onKeyDown:e=>{e.key===`Enter`&&e.currentTarget.blur()}})}):(0,R.jsx)($,{label:e,description:t,children:(0,R.jsxs)(`button`,{type:`button`,className:`env-var-field`,disabled:i,onClick:()=>o(!0),children:[(0,R.jsx)(`span`,{className:`mono env-var-field-value`,children:n}),!i&&(0,R.jsx)(Qi,{size:12,className:`muted`})]})})}var Hg=[{value:`postgres`,label:`PostgreSQL`},{value:`mysql`,label:`MySQL`},{value:`sqlite`,label:`SQLite`},{value:`sqlserver`,label:`SQL Server`},{value:`prisma`,label:`Prisma schema file (no live database)`}],Ug=[{value:`openai`,label:`OpenAI`,keyEnv:`OPENAI_API_KEY`,modelEnv:`OPENAI_MODEL`},{value:`anthropic`,label:`Anthropic`,keyEnv:`ANTHROPIC_API_KEY`,modelEnv:`ANTHROPIC_MODEL`},{value:`google`,label:`Google (Gemini)`,keyEnv:`GOOGLE_GENERATIVE_AI_API_KEY`,modelEnv:`GOOGLE_AI_MODEL`},{value:`azure`,label:`Azure OpenAI`,keyEnv:`AZURE_OPENAI_API_KEY`,modelEnv:`AZURE_OPENAI_DEPLOYMENT`},{value:`foundry`,label:`Azure AI Foundry`,keyEnv:`AZURE_OPENAI_API_KEY`,modelEnv:`AZURE_OPENAI_DEPLOYMENT`},{value:`gateway`,label:`Vercel AI Gateway`,keyEnv:`AI_GATEWAY_API_KEY`,modelEnv:`ASKDB_AI_MODEL`}],Wg=[{value:`file`,label:`File (default, no setup required)`},{value:`memory`,label:`Memory (fast, non-persistent)`},{value:`pgvector`,label:`pgvector (Postgres vector store)`}],Gg=[{value:`postgres`,label:`PostgreSQL`},{value:`mysql`,label:`MySQL`},{value:`sqlite`,label:`SQLite`},{value:`sqlserver`,label:`SQL Server`}],Kg={postgres:`DATABASE_URL`,mysql:`DATABASE_URL`,sqlserver:`DATABASE_URL`,sqlite:``,prisma:``},qg={postgres:`DATABASE_URL`,mysql:`DATABASE_URL`,sqlserver:`DATABASE_URL`,sqlite:``},Jg=`h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring`;function Yg({stepNumber:e,configResult:t,busy:n,database:r,onDatabaseChange:i,needsConnectionEnv:a,connectionEnv:o,onConnectionEnvChange:s,sqliteFile:c,onSqliteFileChange:l,prismaSchema:u,onPrismaSchemaChange:d,aiProvider:f,onAiProviderChange:p,aiKeyEnv:m,onAiKeyEnvChange:h,aiModelEnv:g,onAiModelEnvChange:_,schemaOut:v,onSchemaOutChange:y,ragStore:b,onRagStoreChange:x,pgvectorEnv:S,onPgvectorEnvChange:C,studioExecuteEnabled:w,onStudioExecuteEnabledChange:T,studioExecuteProvider:E,onStudioExecuteProviderChange:D,studioExecuteConnectionEnv:O,onStudioExecuteConnectionEnvChange:k,studioExecuteSqliteFile:A,onStudioExecuteSqliteFileChange:j,onWriteConfig:M}){let N=!!t;return(0,R.jsxs)(`div`,{className:`card`,children:[(0,R.jsxs)(`div`,{className:`card-hd`,children:[(0,R.jsxs)(`h3`,{children:[(0,R.jsx)(Mi,{size:14}),` `,e,` · Database & model provider`]}),t&&(0,R.jsxs)(`span`,{className:`chip green`,children:[(0,R.jsx)(gi,{size:11}),` config written`]})]}),(0,R.jsxs)(`div`,{className:`card-bd`,style:{display:`grid`,gap:12},children:[(0,R.jsx)($,{label:`Database engine`,children:(0,R.jsx)(`select`,{className:Jg,value:r,disabled:N,onChange:e=>i(e.target.value),children:Hg.map(e=>(0,R.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),a&&(0,R.jsx)(Vg,{label:`Connection URL env var name`,description:`The NAME of the environment variable — the actual URL goes in .env, never here.`,value:o,disabled:N,onChange:s}),r===`sqlite`&&(0,R.jsx)($,{label:`SQLite file path`,description:`Relative to the project root.`,children:(0,R.jsx)(Vm,{value:c,disabled:N,onChange:e=>l(e.target.value)})}),r===`prisma`&&(0,R.jsx)($,{label:`Prisma schema path`,description:`Leave empty to auto-discover prisma/schema.prisma.`,children:(0,R.jsx)(Vm,{value:u,disabled:N,onChange:e=>d(e.target.value),placeholder:`./prisma/schema.prisma`})}),(0,R.jsx)($,{label:`AI provider`,children:(0,R.jsx)(`select`,{className:Jg,value:f,disabled:N,onChange:e=>p(e.target.value),children:Ug.map(e=>(0,R.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(0,R.jsx)(Vg,{label:`API key env var name`,description:`The NAME of the environment variable holding your model API key.`,value:m,disabled:N,onChange:h}),(0,R.jsx)(Vg,{label:`Model env var name (optional)`,description:`The NAME of the environment variable for a model override. Leave empty to use the provider default.`,value:g,disabled:N,onChange:_}),(0,R.jsx)($,{label:`Schema artifact directory`,description:`Where introspection writes the schema artifact.`,children:(0,R.jsx)(Vm,{value:v,disabled:N,onChange:e=>y(e.target.value)})}),(0,R.jsx)($,{label:`RAG store`,description:`Where table/relationship embeddings are indexed for retrieval.`,children:(0,R.jsx)(`select`,{className:Jg,value:b,disabled:N,onChange:e=>x(e.target.value),children:Wg.map(e=>(0,R.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),b===`pgvector`&&(0,R.jsx)(Vg,{label:`pgvector connection URL env var name`,description:`The NAME of the environment variable — the actual URL goes in .env, never here.`,value:S,disabled:N,onChange:C}),(0,R.jsxs)(`label`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,R.jsx)(`input`,{type:`checkbox`,checked:w,disabled:N,onChange:e=>T(e.target.checked)}),(0,R.jsx)(`span`,{style:{fontSize:13},children:`Enable Studio execute (run queries from the browser playground)`})]}),(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:12,margin:0},children:`Off by default. When on, Studio runs generated SELECTs read-only with a timeout and row cap. Point it at a read-only database role.`}),w&&r===`prisma`&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)($,{label:`Studio execute database`,description:`Prisma has no live database of its own — pick one for the browser playground to run queries against.`,children:(0,R.jsx)(`select`,{className:Jg,value:E,disabled:N,onChange:e=>D(e.target.value),children:Gg.map(e=>(0,R.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),E===`sqlite`?(0,R.jsx)($,{label:`SQLite file path`,description:`Relative to the project root.`,children:(0,R.jsx)(Vm,{value:A,disabled:N,onChange:e=>j(e.target.value)})}):(0,R.jsx)(Vg,{label:`Connection URL env var name`,description:`The NAME of the environment variable — the actual URL goes in .env, never here.`,value:O,disabled:N,onChange:k})]}),!t&&(0,R.jsx)(`div`,{children:(0,R.jsxs)(`button`,{type:`button`,className:`btn primary`,disabled:n===`config`,onClick:M,children:[n===`config`?(0,R.jsx)(Wi,{size:14,className:`animate-spin`}):(0,R.jsx)(gi,{size:14}),`Write askdb.config.ts`]})}),t&&(0,R.jsxs)(`div`,{style:{display:`grid`,gap:6},children:[(0,R.jsxs)(`p`,{className:`muted`,style:{fontSize:12.5},children:[`Wrote `,(0,R.jsx)(`code`,{className:`mono`,children:t.configPath}),t.envExamplePath?(0,R.jsxs)(R.Fragment,{children:[` and `,(0,R.jsx)(`code`,{className:`mono`,children:t.envExamplePath})]}):null,t.packageJsonCreated?(0,R.jsxs)(R.Fragment,{children:[` (plus a minimal `,(0,R.jsx)(`code`,{className:`mono`,children:`package.json`}),`)`]}):null,`.`]}),t.installed&&t.installed.length>0&&(0,R.jsxs)(`p`,{className:`muted`,style:{fontSize:12.5},children:[`Installed into your project: `,(0,R.jsx)(`code`,{className:`mono`,children:t.installed.join(` `)}),`.`]}),t.manualInstallCommand&&(0,R.jsxs)(`div`,{style:{display:`grid`,gap:6,border:`1px solid var(--amber-500)`,borderRadius:6,padding:10},children:[(0,R.jsx)(`span`,{style:{fontSize:12.5,fontWeight:600},children:`One manual step: install the dependencies`}),(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:12},children:`The wizard couldn't install packages automatically. Run this in your project, then continue below:`}),(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,R.jsx)(`code`,{className:`mono`,style:{flex:1,fontSize:12,border:`1px solid var(--border)`,borderRadius:6,padding:`6px 8px`,background:`var(--surface-2)`},children:t.manualInstallCommand}),(0,R.jsx)(Tg,{value:t.manualInstallCommand})]})]})]})]})]})}function Xg({stepNumber:e,envVars:t}){let n=t.map(e=>`${e.name}=`).join(`
99
99
  `);return(0,R.jsxs)(`div`,{className:`card`,children:[(0,R.jsx)(`div`,{className:`card-hd`,children:(0,R.jsxs)(`h3`,{children:[(0,R.jsx)(Ii,{size:14}),` `,e,` · Add your secrets to .env`]})}),(0,R.jsxs)(`div`,{className:`card-bd`,style:{display:`grid`,gap:10},children:[(0,R.jsxs)(`p`,{className:`muted`,style:{fontSize:12.5,lineHeight:1.6},children:[`Create a `,(0,R.jsx)(`code`,{className:`mono`,children:`.env`}),` file next to `,(0,R.jsx)(`code`,{className:`mono`,children:`askdb.config.ts`}),` and fill in the values. Studio never asks for secret values — they stay on disk, outside this UI.`]}),(0,R.jsxs)(`div`,{style:{display:`flex`,alignItems:`flex-start`,gap:8},children:[(0,R.jsx)(`pre`,{className:`mono`,style:{flex:1,margin:0,border:`1px solid var(--border)`,borderRadius:6,padding:10,fontSize:12,background:`var(--surface-2)`},children:t.map(e=>`${e.name}= # ${e.purpose}`).join(`
100
- `)}),(0,R.jsx)(Tg,{value:n})]})]})]})}function Zg({stepNumber:e,busy:t,onIntrospect:n}){return(0,R.jsxs)(`div`,{className:`card`,children:[(0,R.jsx)(`div`,{className:`card-hd`,children:(0,R.jsxs)(`h3`,{children:[(0,R.jsx)(_a,{size:14}),` `,e,` · Introspect your database`]})}),(0,R.jsxs)(`div`,{className:`card-bd`,style:{display:`grid`,gap:10},children:[(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,lineHeight:1.6},children:`Reads your schema structure (tables, columns, relationships — never rows) and writes the schema artifact. When it finishes, Studio opens on the Overview.`}),(0,R.jsx)(`div`,{children:(0,R.jsxs)(`button`,{type:`button`,className:`btn primary`,disabled:t===`introspect`,onClick:n,children:[t===`introspect`?(0,R.jsx)(Wi,{size:14,className:`animate-spin`}):(0,R.jsx)(Mi,{size:14}),`Run introspection`]})})]})]})}function Qg({status:e,onComplete:t}){let n=e.reason===`no-artifact`,[r,i]=(0,M.useState)(`postgres`),[a,o]=(0,M.useState)(`DATABASE_URL`),s=(0,M.useRef)(!1),[c,l]=(0,M.useState)(`./data.db`),[u,d]=(0,M.useState)(``),[f,p]=(0,M.useState)(`openai`),[m,h]=(0,M.useState)(`OPENAI_API_KEY`),g=(0,M.useRef)(!1),[_,v]=(0,M.useState)(`OPENAI_MODEL`),y=(0,M.useRef)(!1),[b,x]=(0,M.useState)(`./askdb`),[S,C]=(0,M.useState)(`file`),[w,T]=(0,M.useState)(`ASKDB_PGVECTOR_URL`),[E,D]=(0,M.useState)(!1),[O,k]=(0,M.useState)(`postgres`),[A,j]=(0,M.useState)(`DATABASE_URL`),N=(0,M.useRef)(!1),[ee,te]=(0,M.useState)(`./data.db`),[P,ne]=(0,M.useState)(null),[re,ie]=(0,M.useState)(null),[ae,oe]=(0,M.useState)(null),se=r===`postgres`||r===`mysql`||r===`sqlserver`,ce=n||P!==null,le=(0,M.useMemo)(()=>{if(P)return P.envVars;let e=new Map,t=t=>{t.name&&!e.has(t.name)&&e.set(t.name,t)};if(t({name:m,purpose:`${f} API key`,requiredForIntrospection:!1}),_&&t({name:_,purpose:`${f} model override`,requiredForIntrospection:!1}),se&&t({name:a,purpose:`${r} connection URL`,requiredForIntrospection:!0}),S===`pgvector`&&t({name:w,purpose:`pgvector connection URL`,requiredForIntrospection:!1}),E){let e=r===`prisma`?O:r;e!==`sqlite`&&t({name:r===`prisma`?A:a,purpose:`${e} connection URL (Studio execute)`,requiredForIntrospection:!1})}return Array.from(e.values())},[P,m,f,_,se,a,r,S,w,E,O,A]),ue=(0,M.useCallback)(e=>{i(e),s.current||o(Kg[e]||`DATABASE_URL`)},[]),de=(0,M.useCallback)(e=>{p(e);let t=Ug.find(t=>t.value===e);g.current||h(t?.keyEnv??`OPENAI_API_KEY`),y.current||v(t?.modelEnv??``)},[]),fe=(0,M.useCallback)(e=>{k(e),N.current||j(qg[e]||`DATABASE_URL`)},[]),pe=(0,M.useCallback)(async()=>{ie(`config`),oe(null);try{let e=r===`prisma`?O:r,t=await or({database:r,aiProvider:f,...se&&a?{connectionEnv:a}:{},...r===`sqlite`&&c?{sqliteFile:c}:{},...r===`prisma`&&u?{prismaSchema:u}:{},...m?{aiKeyEnv:m}:{},..._?{aiModelEnv:_}:{},...b?{schemaOut:b}:{},ragStore:S,...S===`pgvector`&&w?{pgvectorEnv:w}:{},studioExecute:E,...E?{studioExecuteProvider:e,...e===`sqlite`?{studioExecuteSqliteFile:r===`prisma`?ee:c}:{studioExecuteConnectionEnv:r===`prisma`?A:a}}:{}});ne(t)}catch(e){oe(e instanceof Error?e.message:String(e))}finally{ie(null)}},[r,f,se,a,c,u,m,_,b,S,w,E,O,A,ee]),me=(0,M.useCallback)(async()=>{ie(`introspect`),oe(null);try{await sr(),t()}catch(e){oe(e instanceof Error?e.message:String(e))}finally{ie(null)}},[t]),he=n?1:2,F=n?2:3;return(0,R.jsx)(`div`,{className:`app-shell`,style:{display:`flex`,alignItems:`flex-start`,justifyContent:`center`,overflow:`auto`,padding:`48px 16px`},children:(0,R.jsxs)(`div`,{style:{width:`100%`,maxWidth:640,display:`grid`,gap:16},children:[(0,R.jsxs)(`div`,{children:[(0,R.jsxs)(`h1`,{style:{fontSize:20,fontWeight:700,display:`flex`,alignItems:`center`,gap:8},children:[(0,R.jsx)(Ta,{size:18}),` Set up AskDB`]}),(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:13,marginTop:6,lineHeight:1.6},children:n?(0,R.jsxs)(R.Fragment,{children:[`Config found at `,(0,R.jsx)(`code`,{className:`mono`,children:e.configPath}),`, but no schema artifact yet. Run introspection to create it.`]}):(0,R.jsxs)(R.Fragment,{children:[`No `,(0,R.jsx)(`code`,{className:`mono`,children:`askdb.config.ts`}),` found in `,(0,R.jsx)(`code`,{className:`mono`,children:e.projectDir}),`. Answer two questions and Studio writes it for you — your secrets stay in `,(0,R.jsx)(`code`,{className:`mono`,children:`.env`}),`, on your machine.`]})})]}),!n&&(0,R.jsx)(Yg,{stepNumber:1,configResult:P,busy:re,database:r,onDatabaseChange:ue,needsConnectionEnv:se,connectionEnv:a,onConnectionEnvChange:e=>{o(e),s.current=!0},sqliteFile:c,onSqliteFileChange:l,prismaSchema:u,onPrismaSchemaChange:d,aiProvider:f,onAiProviderChange:de,aiKeyEnv:m,onAiKeyEnvChange:e=>{h(e),g.current=!0},aiModelEnv:_,onAiModelEnvChange:e=>{v(e),y.current=!0},schemaOut:b,onSchemaOutChange:x,ragStore:S,onRagStoreChange:C,pgvectorEnv:w,onPgvectorEnvChange:T,studioExecuteEnabled:E,onStudioExecuteEnabledChange:D,studioExecuteProvider:O,onStudioExecuteProviderChange:fe,studioExecuteConnectionEnv:A,onStudioExecuteConnectionEnvChange:e=>{j(e),N.current=!0},studioExecuteSqliteFile:ee,onStudioExecuteSqliteFileChange:te,onWriteConfig:()=>void pe()}),ce&&(0,R.jsx)(Xg,{stepNumber:he,envVars:le}),ce&&(0,R.jsx)(Zg,{stepNumber:F,busy:re,onIntrospect:()=>void me()}),ae&&(0,R.jsx)(`div`,{className:`card`,style:{borderColor:`var(--red-600)`},children:(0,R.jsx)(`div`,{className:`card-bd`,children:(0,R.jsx)(`p`,{style:{color:`var(--red-600)`,fontSize:12.5,whiteSpace:`pre-wrap`},children:ae})})}),(0,R.jsxs)(`p`,{className:`muted tiny`,style:{lineHeight:1.6},children:[`Prefer the terminal? `,(0,R.jsx)(`code`,{className:`mono`,children:`npx askdb init`}),` runs the same setup as a CLI wizard.`]})]})})}function $g(){return(0,R.jsx)(Vn,{children:(0,R.jsx)(xr,{children:(0,R.jsx)(Pr,{children:(0,R.jsx)(e_,{})})})})}function e_(){let{load:e,loadState:t,setupStatus:n,suggestionDialog:r,setSuggestionDialog:i,applySuggestion:a}=vr(),{refreshRagStatus:o,ragAvailable:s}=jr();return(0,M.useEffect)(()=>{e().then(()=>void o())},[e,o]),t.kind===`setup`&&n?(0,R.jsx)(Qg,{status:n,onComplete:()=>void e().then(()=>void o())}):t.kind===`loading`?(0,R.jsx)(`div`,{className:`app-shell`,style:{display:`flex`,alignItems:`center`,justifyContent:`center`},children:(0,R.jsx)(`p`,{style:{color:`var(--ink-400)`,fontSize:13},children:`Loading workspace…`})}):t.kind===`error`?(0,R.jsx)(`div`,{className:`app-shell`,style:{display:`flex`,alignItems:`center`,justifyContent:`center`},children:(0,R.jsxs)(`div`,{style:{textAlign:`center`},children:[(0,R.jsx)(`p`,{style:{color:`var(--red-600)`,fontWeight:600,marginBottom:8},children:`Failed to load workspace`}),(0,R.jsx)(`p`,{style:{color:`var(--ink-400)`,fontSize:13},children:t.message})]})}):(0,R.jsxs)(Hr,{ragAvailable:s,children:[(0,R.jsxs)(ch,{defaultOpen:!0,className:`!min-h-0 h-svh`,style:{"--sidebar-width":`var(--nav-w)`},children:[(0,R.jsx)(Dh,{}),(0,R.jsxs)(dh,{className:`min-h-0 overflow-hidden`,children:[(0,R.jsxs)(`header`,{className:`topbar`,children:[(0,R.jsx)(uh,{className:`-ml-1`}),(0,R.jsx)(Hm,{orientation:`vertical`,className:`mr-2 !h-4`}),(0,R.jsx)(Ch,{})]}),(0,R.jsx)(`div`,{className:`app-content`,children:(0,R.jsxs)(un,{children:[(0,R.jsx)(cn,{path:`/`,element:(0,R.jsx)(on,{to:`/overview`,replace:!0})}),(0,R.jsx)(cn,{path:`/overview`,element:(0,R.jsx)(jh,{})}),(0,R.jsx)(cn,{path:`/tables`,element:(0,R.jsx)(Ih,{}),children:(0,R.jsxs)(cn,{path:`:schema/:name`,element:(0,R.jsx)(Lh,{}),children:[(0,R.jsx)(cn,{index:!0,element:(0,R.jsx)(on,{to:`enrichment`,replace:!0})}),(0,R.jsx)(cn,{path:`enrichment`,element:(0,R.jsx)(Gh,{})}),(0,R.jsx)(cn,{path:`schema`,element:(0,R.jsx)(Jh,{})}),(0,R.jsx)(cn,{path:`sensitivity`,element:(0,R.jsx)(Yh,{})})]})}),(0,R.jsx)(cn,{path:`/concepts`,element:(0,R.jsx)(eg,{})}),(0,R.jsx)(cn,{path:`/tenancy`,element:(0,R.jsx)(og,{})}),(0,R.jsx)(cn,{path:`/rag-index`,element:(0,R.jsx)(Cg,{})}),(0,R.jsx)(cn,{path:`/playground`,element:(0,R.jsx)(Fg,{})}),(0,R.jsx)(cn,{path:`/settings`,element:(0,R.jsx)(Bg,{})})]})})]})]}),r&&(0,R.jsx)(Oh,{dialog:r,onApply:a,onClose:()=>i(null)})]})}var t_=document.getElementById(`root`);if(!t_)throw Error(`Missing #root element.`);(0,Ln.createRoot)(t_).render((0,R.jsx)(M.StrictMode,{children:(0,R.jsx)(Tn,{children:(0,R.jsx)($g,{})})}));
100
+ `)}),(0,R.jsx)(Tg,{value:n})]})]})]})}function Zg({stepNumber:e,busy:t,onIntrospect:n}){return(0,R.jsxs)(`div`,{className:`card`,children:[(0,R.jsx)(`div`,{className:`card-hd`,children:(0,R.jsxs)(`h3`,{children:[(0,R.jsx)(_a,{size:14}),` `,e,` · Introspect your database`]})}),(0,R.jsxs)(`div`,{className:`card-bd`,style:{display:`grid`,gap:10},children:[(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:12.5,lineHeight:1.6},children:`Reads your schema structure (tables, columns, relationships — never rows) and writes the schema artifact. When it finishes, Studio opens on the Overview.`}),(0,R.jsx)(`div`,{children:(0,R.jsxs)(`button`,{type:`button`,className:`btn primary`,disabled:t===`introspect`,onClick:n,children:[t===`introspect`?(0,R.jsx)(Wi,{size:14,className:`animate-spin`}):(0,R.jsx)(Mi,{size:14}),`Run introspection`]})})]})]})}function Qg({status:e,onComplete:t}){let n=e.reason===`no-artifact`,[r,i]=(0,M.useState)(`postgres`),[a,o]=(0,M.useState)(`DATABASE_URL`),s=(0,M.useRef)(!1),[c,l]=(0,M.useState)(`./data.db`),[u,d]=(0,M.useState)(``),[f,p]=(0,M.useState)(`openai`),[m,h]=(0,M.useState)(`OPENAI_API_KEY`),g=(0,M.useRef)(!1),[_,v]=(0,M.useState)(`OPENAI_MODEL`),y=(0,M.useRef)(!1),[b,x]=(0,M.useState)(`./askdb`),[S,C]=(0,M.useState)(`file`),[w,T]=(0,M.useState)(`ASKDB_PGVECTOR_URL`),[E,D]=(0,M.useState)(!1),[O,k]=(0,M.useState)(`postgres`),[A,j]=(0,M.useState)(`DATABASE_URL`),N=(0,M.useRef)(!1),[ee,te]=(0,M.useState)(`./data.db`),[P,ne]=(0,M.useState)(null),[re,ie]=(0,M.useState)(null),[ae,oe]=(0,M.useState)(null),se=Kg[r]!==``,ce=n||P!==null,le=(0,M.useMemo)(()=>{if(P)return P.envVars;let e=new Map,t=t=>{t.name&&!e.has(t.name)&&e.set(t.name,t)};if(t({name:m,purpose:`${f} API key`,requiredForIntrospection:!1}),_&&t({name:_,purpose:`${f} model override`,requiredForIntrospection:!1}),se&&t({name:a,purpose:`${r} connection URL`,requiredForIntrospection:!0}),S===`pgvector`&&t({name:w,purpose:`pgvector connection URL`,requiredForIntrospection:!1}),E){let e=r===`prisma`?O:r;e!==`sqlite`&&t({name:r===`prisma`?A:a,purpose:`${e} connection URL (Studio execute)`,requiredForIntrospection:!1})}return Array.from(e.values())},[P,m,f,_,se,a,r,S,w,E,O,A]),ue=(0,M.useCallback)(e=>{i(e),s.current||o(Kg[e]||`DATABASE_URL`)},[]),de=(0,M.useCallback)(e=>{p(e);let t=Ug.find(t=>t.value===e);g.current||h(t?.keyEnv??`OPENAI_API_KEY`),y.current||v(t?.modelEnv??``)},[]),fe=(0,M.useCallback)(e=>{k(e),N.current||j(qg[e]||`DATABASE_URL`)},[]),pe=(0,M.useCallback)(async()=>{ie(`config`),oe(null);try{let e=r===`prisma`?O:r,t=await or({database:r,aiProvider:f,...se&&a?{connectionEnv:a}:{},...r===`sqlite`&&c?{sqliteFile:c}:{},...r===`prisma`&&u?{prismaSchema:u}:{},...m?{aiKeyEnv:m}:{},..._?{aiModelEnv:_}:{},...b?{schemaOut:b}:{},ragStore:S,...S===`pgvector`&&w?{pgvectorEnv:w}:{},studioExecute:E,...E?{studioExecuteProvider:e,...e===`sqlite`?{studioExecuteSqliteFile:r===`prisma`?ee:c}:{studioExecuteConnectionEnv:r===`prisma`?A:a}}:{}});ne(t)}catch(e){oe(e instanceof Error?e.message:String(e))}finally{ie(null)}},[r,f,se,a,c,u,m,_,b,S,w,E,O,A,ee]),me=(0,M.useCallback)(async()=>{ie(`introspect`),oe(null);try{await sr(),t()}catch(e){oe(e instanceof Error?e.message:String(e))}finally{ie(null)}},[t]),he=n?1:2,F=n?2:3;return(0,R.jsx)(`div`,{className:`app-shell`,style:{display:`flex`,alignItems:`flex-start`,justifyContent:`center`,overflow:`auto`,padding:`48px 16px`},children:(0,R.jsxs)(`div`,{style:{width:`100%`,maxWidth:640,display:`grid`,gap:16},children:[(0,R.jsxs)(`div`,{children:[(0,R.jsxs)(`h1`,{style:{fontSize:20,fontWeight:700,display:`flex`,alignItems:`center`,gap:8},children:[(0,R.jsx)(Ta,{size:18}),` Set up AskDB`]}),(0,R.jsx)(`p`,{className:`muted`,style:{fontSize:13,marginTop:6,lineHeight:1.6},children:n?(0,R.jsxs)(R.Fragment,{children:[`Config found at `,(0,R.jsx)(`code`,{className:`mono`,children:e.configPath}),`, but no schema artifact yet. Run introspection to create it.`]}):(0,R.jsxs)(R.Fragment,{children:[`No `,(0,R.jsx)(`code`,{className:`mono`,children:`askdb.config.ts`}),` found in `,(0,R.jsx)(`code`,{className:`mono`,children:e.projectDir}),`. Answer two questions and Studio writes it for you — your secrets stay in `,(0,R.jsx)(`code`,{className:`mono`,children:`.env`}),`, on your machine.`]})})]}),!n&&(0,R.jsx)(Yg,{stepNumber:1,configResult:P,busy:re,database:r,onDatabaseChange:ue,needsConnectionEnv:se,connectionEnv:a,onConnectionEnvChange:e=>{o(e),s.current=!0},sqliteFile:c,onSqliteFileChange:l,prismaSchema:u,onPrismaSchemaChange:d,aiProvider:f,onAiProviderChange:de,aiKeyEnv:m,onAiKeyEnvChange:e=>{h(e),g.current=!0},aiModelEnv:_,onAiModelEnvChange:e=>{v(e),y.current=!0},schemaOut:b,onSchemaOutChange:x,ragStore:S,onRagStoreChange:C,pgvectorEnv:w,onPgvectorEnvChange:T,studioExecuteEnabled:E,onStudioExecuteEnabledChange:D,studioExecuteProvider:O,onStudioExecuteProviderChange:fe,studioExecuteConnectionEnv:A,onStudioExecuteConnectionEnvChange:e=>{j(e),N.current=!0},studioExecuteSqliteFile:ee,onStudioExecuteSqliteFileChange:te,onWriteConfig:()=>void pe()}),ce&&(0,R.jsx)(Xg,{stepNumber:he,envVars:le}),ce&&(0,R.jsx)(Zg,{stepNumber:F,busy:re,onIntrospect:()=>void me()}),ae&&(0,R.jsx)(`div`,{className:`card`,style:{borderColor:`var(--red-600)`},children:(0,R.jsx)(`div`,{className:`card-bd`,children:(0,R.jsx)(`p`,{style:{color:`var(--red-600)`,fontSize:12.5,whiteSpace:`pre-wrap`},children:ae})})}),(0,R.jsxs)(`p`,{className:`muted tiny`,style:{lineHeight:1.6},children:[`Prefer the terminal? `,(0,R.jsx)(`code`,{className:`mono`,children:`npx askdb init`}),` runs the same setup as a CLI wizard.`]})]})})}function $g(){return(0,R.jsx)(Vn,{children:(0,R.jsx)(xr,{children:(0,R.jsx)(Pr,{children:(0,R.jsx)(e_,{})})})})}function e_(){let{load:e,loadState:t,setupStatus:n,suggestionDialog:r,setSuggestionDialog:i,applySuggestion:a}=vr(),{refreshRagStatus:o,ragAvailable:s}=jr();return(0,M.useEffect)(()=>{e().then(()=>void o())},[e,o]),t.kind===`setup`&&n?(0,R.jsx)(Qg,{status:n,onComplete:()=>void e().then(()=>void o())}):t.kind===`loading`?(0,R.jsx)(`div`,{className:`app-shell`,style:{display:`flex`,alignItems:`center`,justifyContent:`center`},children:(0,R.jsx)(`p`,{style:{color:`var(--ink-400)`,fontSize:13},children:`Loading workspace…`})}):t.kind===`error`?(0,R.jsx)(`div`,{className:`app-shell`,style:{display:`flex`,alignItems:`center`,justifyContent:`center`},children:(0,R.jsxs)(`div`,{style:{textAlign:`center`},children:[(0,R.jsx)(`p`,{style:{color:`var(--red-600)`,fontWeight:600,marginBottom:8},children:`Failed to load workspace`}),(0,R.jsx)(`p`,{style:{color:`var(--ink-400)`,fontSize:13},children:t.message})]})}):(0,R.jsxs)(Hr,{ragAvailable:s,children:[(0,R.jsxs)(ch,{defaultOpen:!0,className:`!min-h-0 h-svh`,style:{"--sidebar-width":`var(--nav-w)`},children:[(0,R.jsx)(Dh,{}),(0,R.jsxs)(dh,{className:`min-h-0 overflow-hidden`,children:[(0,R.jsxs)(`header`,{className:`topbar`,children:[(0,R.jsx)(uh,{className:`-ml-1`}),(0,R.jsx)(Hm,{orientation:`vertical`,className:`mr-2 !h-4`}),(0,R.jsx)(Ch,{})]}),(0,R.jsx)(`div`,{className:`app-content`,children:(0,R.jsxs)(un,{children:[(0,R.jsx)(cn,{path:`/`,element:(0,R.jsx)(on,{to:`/overview`,replace:!0})}),(0,R.jsx)(cn,{path:`/overview`,element:(0,R.jsx)(jh,{})}),(0,R.jsx)(cn,{path:`/tables`,element:(0,R.jsx)(Ih,{}),children:(0,R.jsxs)(cn,{path:`:schema/:name`,element:(0,R.jsx)(Lh,{}),children:[(0,R.jsx)(cn,{index:!0,element:(0,R.jsx)(on,{to:`enrichment`,replace:!0})}),(0,R.jsx)(cn,{path:`enrichment`,element:(0,R.jsx)(Gh,{})}),(0,R.jsx)(cn,{path:`schema`,element:(0,R.jsx)(Jh,{})}),(0,R.jsx)(cn,{path:`sensitivity`,element:(0,R.jsx)(Yh,{})})]})}),(0,R.jsx)(cn,{path:`/concepts`,element:(0,R.jsx)(eg,{})}),(0,R.jsx)(cn,{path:`/tenancy`,element:(0,R.jsx)(og,{})}),(0,R.jsx)(cn,{path:`/rag-index`,element:(0,R.jsx)(Cg,{})}),(0,R.jsx)(cn,{path:`/playground`,element:(0,R.jsx)(Fg,{})}),(0,R.jsx)(cn,{path:`/settings`,element:(0,R.jsx)(Bg,{})})]})})]})]}),r&&(0,R.jsx)(Oh,{dialog:r,onApply:a,onClose:()=>i(null)})]})}var t_=document.getElementById(`root`);if(!t_)throw Error(`Missing #root element.`);(0,Ln.createRoot)(t_).render((0,R.jsx)(M.StrictMode,{children:(0,R.jsx)(Tn,{children:(0,R.jsx)($g,{})})}));
@@ -6,7 +6,7 @@
6
6
  <title>AskDB Studio</title>
7
7
  <link rel="icon" type="image/png" media="(prefers-color-scheme: light)" href="/assets/brand/light-icon.png" />
8
8
  <link rel="icon" type="image/png" media="(prefers-color-scheme: dark)" href="/assets/brand/dark-icon.png" />
9
- <script type="module" crossorigin src="/assets/index-CGC5sKu1.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-DX7u2yPd.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-Bl9jqOjj.css">
11
11
  </head>
12
12
  <body>
@@ -1 +1 @@
1
- {"version":3,"file":"introspection.d.ts","sourceRoot":"","sources":["../src/introspection.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AA+B3B,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;;;GAKG;AACH,wBAAsB,8BAA8B,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAevF;AAaD,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAoClC"}
1
+ {"version":3,"file":"introspection.d.ts","sourceRoot":"","sources":["../src/introspection.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AA+B3B,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;;;GAKG;AACH,wBAAsB,8BAA8B,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAevF;AAaD,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAqClC"}
@@ -90,6 +90,7 @@ export async function runStudioIntrospection(options) {
90
90
  outDir: options.outDir,
91
91
  schemaId,
92
92
  existingArtifactDir: options.hasExistingArtifact ? options.outDir : undefined,
93
+ filters: connectorConfig.filters,
93
94
  }, { connector });
94
95
  return {
95
96
  engine: plan.engine,
@@ -1 +1 @@
1
- {"version":3,"file":"introspection.js","sourceRoot":"","sources":["../src/introspection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"introspection.js","sourceRoot":"","sources":["../src/introspection.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The builds pnpm must approve to install `@askdb/prisma`, an optional peer (#259):
3
3
  * `@prisma/engines` has a postinstall script, and pnpm 11 exits 1 on an unapproved one.
4
- * Read by guided setup's install plan and the introspection plan's missing-package hint, so the two stay in step. `askdb init`'s copy is `apps/cli/src/prisma-install.ts`.
4
+ * Read by the introspection plan's missing-package hint. Guided setup installs from the scaffold planner in `@askdb/config/scaffold`, which approves the same build (`setup-engines.test.ts` checks they match). `askdb init`'s copy is `apps/cli/src/prisma-install.ts`.
5
5
  */
6
6
  export declare const PRISMA_BUILD_APPROVALS: readonly string[];
7
7
  //# sourceMappingURL=prisma-install.d.ts.map
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The builds pnpm must approve to install `@askdb/prisma`, an optional peer (#259):
3
3
  * `@prisma/engines` has a postinstall script, and pnpm 11 exits 1 on an unapproved one.
4
- * Read by guided setup's install plan and the introspection plan's missing-package hint, so the two stay in step. `askdb init`'s copy is `apps/cli/src/prisma-install.ts`.
4
+ * Read by the introspection plan's missing-package hint. Guided setup installs from the scaffold planner in `@askdb/config/scaffold`, which approves the same build (`setup-engines.test.ts` checks they match). `askdb init`'s copy is `apps/cli/src/prisma-install.ts`.
5
5
  */
6
6
  export const PRISMA_BUILD_APPROVALS = ["@prisma/engines"];
7
7
  //# sourceMappingURL=prisma-install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAyCjG,OAAO,EAQL,mBAAmB,EAUpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAcL,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAYV,WAAW,EAKX,kBAAkB,EAGnB,MAAM,iBAAiB,CAAC;AA+BzB,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAExE;AAMD,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAClC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAsEF,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,OAAO,mBAAmB,GAAG,SAAS,GAC9C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAuKvE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,kBAAkB,CA+C3E;AAwsDD,gDAAgD;AAChD,eAAO,MAAM,mBAAmB,QAAc,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAgDjG,OAAO,EAQL,mBAAmB,EAUpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAcL,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAYV,WAAW,EAKX,kBAAkB,EAGnB,MAAM,iBAAiB,CAAC;AA0BzB,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAExE;AAMD,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAClC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAsEF,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,OAAO,mBAAmB,GAAG,SAAS,GAC9C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAuKvE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,kBAAkB,CA+C3E;AAksDD,gDAAgD;AAChD,eAAO,MAAM,mBAAmB,QAAc,CAAC"}
package/dist/server.js CHANGED
@@ -1,17 +1,18 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
3
3
  import { createServer } from "node:http";
4
- import { extname, relative, join, resolve, dirname, sep } from "node:path";
4
+ import { extname, relative, join, resolve, sep } from "node:path";
5
5
  import { spawn } from "node:child_process";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { generateText as defaultGenerateText } from "ai";
8
8
  import { ASKDB_AI_PROVIDERS, bootstrapAskDbEnv, getAskDbRuntimeConfig, } from "@askdb/config";
9
- import { createAiRegistry, resolveReasoningEffort, } from "@askdb/ai";
9
+ import { findNearestPackageJsonDir, lockfilePackageManager, packageManagerAddArgs, packageManagerSpawnSpec, } from "@askdb/config/scaffold";
10
+ import { createAiRegistry, resolveReasoningEffortFromSection, } from "@askdb/ai";
10
11
  import { ask, DEFAULT_TENANT_SQL_MODE, formatSchemaV2ForNlToSql, formatSensitiveReference, isBuiltInDialectId, loadSchema, saveSchemaFile, schemaHasSensitiveIdentifiers, SensitiveEnrichmentTargetError, SqlValidationError, validateSensitiveReferences, suggestEnrichment, tenantScopeSchema, SchemaParseError, tenantPolicyFrontmatterSchema, } from "@askdb/core";
12
+ import { EmptyIntrospectionError } from "@askdb/introspect";
11
13
  import { buildSchemaIndex, chunkContentHash, chunkSchema, aiSdkEmbedderId, createAiSdkEmbedder, createFileStore, createMemoryStore, createPgvectorStore, createRetriever, detectEmbeddingDimensions, loadChunkerSourcesFromDir, PgvectorDimensionMismatchError, } from "@askdb/rag";
12
14
  import { buildDefaultTableBody, buildFrontmatter, buildSuggestionContext, buildSuggestionTarget, buildTableDraft, loadWorkspace, replaceH2Section, replaceTableDescription, saveConcepts, saveTable, saveTenantPolicy, } from "@askdb/enrich";
13
15
  import { EXECUTE_DRIVER_REGISTRY, executeDialectFor, isDriverInstalled, isStudioExecuteProvider, validateExecuteSql, } from "./execute-registry.js";
14
- import { lockfilePackageManager, packageManagerAddArgs, packageManagerSpawnSpec, } from "./package-manager.js";
15
16
  import { resolveStudioIntrospectionPlan, runStudioIntrospection } from "./introspection.js";
16
17
  import { probeSetupState, writeSetupConfig, SetupError } from "./setup.js";
17
18
  import { checkApiRequest, checkHost, createStudioSessionToken, injectSessionToken, } from "./request-guard.js";
@@ -203,11 +204,11 @@ export function serializeWorkspace(workspace) {
203
204
  const rt = getAskDbRuntimeConfig();
204
205
  const aiConfig = (() => {
205
206
  try {
206
- return ai.resolveAiConfig(rt.ai.aiEnv);
207
+ return ai.resolveAiConfigFromSection(rt.ai.language);
207
208
  }
208
209
  catch {
209
- // A misconfigured AI env (e.g. azure without resourceName) shouldn't crash the workspace
210
- // listing — surface it as "not configured" in the UI and let the user fix .env.
210
+ // A misconfigured AI section (e.g. azure without resourceName) shouldn't crash the workspace
211
+ // listing — surface it as "not configured" in the UI and let the user fix askdb.config.
211
212
  return undefined;
212
213
  }
213
214
  })();
@@ -320,7 +321,7 @@ async function handleSetupIntrospect(state) {
320
321
  "Install the project dependencies shown in setup step 1, then retry.");
321
322
  }
322
323
  const outDir = probe.outputDir;
323
- const run = await runStudioIntrospection({
324
+ const run = await introspectForRequest({
324
325
  outDir,
325
326
  hasExistingArtifact: existsSync(join(outDir, "schema.json")),
326
327
  });
@@ -345,7 +346,7 @@ async function handleResync(state) {
345
346
  catch {
346
347
  // Keep the snapshot the server started with when the config disappeared mid-session.
347
348
  }
348
- const run = await runStudioIntrospection({
349
+ const run = await introspectForRequest({
349
350
  outDir: state.schemaDir,
350
351
  schemaId: workspace.physical.schemaId,
351
352
  hasExistingArtifact: true,
@@ -467,12 +468,12 @@ function saveDraft(state, tableId, draft) {
467
468
  }
468
469
  async function suggestForSource(state, source) {
469
470
  const rt = getAskDbRuntimeConfig();
470
- const aiConfig = ai.resolveAiConfig(rt.ai.aiEnv);
471
+ const aiConfig = ai.resolveAiConfigFromSection(rt.ai.language);
471
472
  if (!aiConfig) {
472
473
  throw new StudioHttpError(400, ai.keyMissingMessage("AI enrichment suggestions"));
473
474
  }
474
475
  const model = await ai.createLanguageModel(aiConfig);
475
- const reasoningEffort = resolveReasoningEffort(rt.ai.aiEnv, "enrichment");
476
+ const reasoningEffort = resolveReasoningEffortFromSection(rt.ai.language.reasoning, "enrichment");
476
477
  const providerOptions = reasoningEffort
477
478
  ? ai.resolveProviderOptions(aiConfig, { reasoningEffort })
478
479
  : undefined;
@@ -572,7 +573,7 @@ function tenantPolicySchemaForPrompt(schema, omitSensitive) {
572
573
  }
573
574
  async function suggestTenantPolicyDraft(state) {
574
575
  const rt = getAskDbRuntimeConfig();
575
- const model = await ai.createLanguageModelFromEnv(rt.ai.aiEnv);
576
+ const model = await ai.createLanguageModelFromSection(rt.ai.language);
576
577
  if (!model) {
577
578
  throw new StudioHttpError(400, ai.keyMissingMessage("AI tenant policy suggestion"));
578
579
  }
@@ -641,11 +642,11 @@ If the schema has no clear multi-tenant pattern, still make your best guess at w
641
642
  async function askSampleQuestion(state, options) {
642
643
  const rt = getAskDbRuntimeConfig();
643
644
  const mockSql = rt.dev.mockSql;
644
- const aiConfig = mockSql ? undefined : ai.resolveAiConfig(rt.ai.aiEnv);
645
+ const aiConfig = mockSql ? undefined : ai.resolveAiConfigFromSection(rt.ai.language);
645
646
  if (!mockSql && !aiConfig) {
646
647
  throw new StudioHttpError(400, `${ai.keyMissingMessage("Sample NL-to-SQL generation")} Set dev.mockSql in askdb.config to bypass the live model.`);
647
648
  }
648
- const reasoningEffort = mockSql ? undefined : resolveReasoningEffort(rt.ai.aiEnv, "nlToSql");
649
+ const reasoningEffort = mockSql ? undefined : resolveReasoningEffortFromSection(rt.ai.language.reasoning, "nlToSql");
649
650
  const providerOptions = aiConfig && reasoningEffort ? ai.resolveProviderOptions(aiConfig, { reasoningEffort }) : undefined;
650
651
  const schema = loadSchema(state.schemaDir);
651
652
  const retrievedChunks = [];
@@ -655,7 +656,7 @@ async function askSampleQuestion(state, options) {
655
656
  : undefined;
656
657
  const model = mockSql
657
658
  ? undefined
658
- : (await ai.createLanguageModelFromEnv(rt.ai.aiEnv));
659
+ : (await ai.createLanguageModel(aiConfig));
659
660
  const schemaProvider = "provider" in schema && typeof schema.provider === "string"
660
661
  ? schema.provider
661
662
  : undefined;
@@ -990,8 +991,8 @@ function resolveStudioRagEmbedderConfig() {
990
991
  label: "Mock lexical",
991
992
  };
992
993
  }
993
- // The env holds the embedding connection only, so no other provider's key can reach it (#345).
994
- const aiConfig = ai.resolveEmbeddingConfig(embedding.env);
994
+ // The section holds the embedding connection only, so no other provider's key can reach it (#345).
995
+ const aiConfig = ai.resolveEmbeddingConfigFromSection(embedding);
995
996
  // The adapter's canonical name (`foundry` resolves to `azure`) keeps existing index ids stable.
996
997
  const provider = aiConfig?.provider ?? embedding.provider;
997
998
  const model = embedding.model;
@@ -1000,7 +1001,7 @@ function resolveStudioRagEmbedderConfig() {
1000
1001
  kind: "ai-sdk",
1001
1002
  provider,
1002
1003
  configuredProvider: embedding.provider,
1003
- connection: embedding.connection,
1004
+ connection: embedding.connection.name,
1004
1005
  embedderId: aiSdkEmbedderId({ provider, model, dimensions }),
1005
1006
  dimensions,
1006
1007
  configured: Boolean(aiConfig),
@@ -1366,7 +1367,7 @@ async function getExecuteStatus(schemaDir) {
1366
1367
  const exec = rt.studio.execute;
1367
1368
  const { provider, databaseUrl, file } = exec;
1368
1369
  const def = EXECUTE_DRIVER_REGISTRY[provider];
1369
- const projectRoot = findProjectRoot(schemaDir) ?? schemaDir;
1370
+ const projectRoot = findNearestPackageJsonDir(schemaDir) ?? schemaDir;
1370
1371
  const installed = isDriverInstalled(def.packageName, projectRoot);
1371
1372
  const connectionKind = provider === "sqlite" ? "file" : "url";
1372
1373
  const configured = connectionKind === "file" ? Boolean(file) : Boolean(databaseUrl);
@@ -1406,7 +1407,7 @@ async function installExecuteDriver(body, schemaDir) {
1406
1407
  provider = body.provider;
1407
1408
  }
1408
1409
  const def = EXECUTE_DRIVER_REGISTRY[provider];
1409
- const detection = detectPackageManager(schemaDir, def.packageName);
1410
+ const detection = detectExecuteDriverInstaller(schemaDir, def.packageName);
1410
1411
  if (detection.manualReason !== null) {
1411
1412
  return {
1412
1413
  ok: false,
@@ -1431,9 +1432,14 @@ async function installExecuteDriver(body, schemaDir) {
1431
1432
  installed,
1432
1433
  };
1433
1434
  }
1434
- function detectPackageManager(cwd, packageName) {
1435
- // Walk up from schemaDir to find the project root's lockfile.
1436
- const projectRoot = findProjectRoot(cwd);
1435
+ /**
1436
+ * How to install the execute driver: with the manager of the lockfile in the nearest
1437
+ * `package.json` directory above `cwd`. Unlike setup's detection it doesn't walk further
1438
+ * up or guess, so Studio never installs into a parent project or with a manager the
1439
+ * project hasn't chosen.
1440
+ */
1441
+ function detectExecuteDriverInstaller(cwd, packageName) {
1442
+ const projectRoot = findNearestPackageJsonDir(cwd);
1437
1443
  if (!projectRoot) {
1438
1444
  return { manualReason: `Could not locate a package.json above ${cwd}. Run the install manually.` };
1439
1445
  }
@@ -1448,18 +1454,6 @@ function detectPackageManager(cwd, packageName) {
1448
1454
  spawnSpec: packageManagerSpawnSpec(pm, packageManagerAddArgs(pm, [packageName])),
1449
1455
  };
1450
1456
  }
1451
- function findProjectRoot(startDir) {
1452
- let current = resolve(startDir);
1453
- for (let i = 0; i < 20; i++) {
1454
- if (existsSync(join(current, "package.json")))
1455
- return current;
1456
- const parent = dirname(current);
1457
- if (parent === current)
1458
- return null;
1459
- current = parent;
1460
- }
1461
- return null;
1462
- }
1463
1457
  function spawnCommand(spec, cwd) {
1464
1458
  return new Promise((resolve) => {
1465
1459
  const proc = spawn(spec.command, spec.args, { cwd, shell: spec.shell, stdio: ["ignore", "pipe", "pipe"] });
@@ -1506,7 +1500,7 @@ async function executeQuery(body, schemaDir) {
1506
1500
  throw error;
1507
1501
  }
1508
1502
  const warnings = sensitiveExecuteWarnings(sql, schemaDir, executeDialectFor(exec.provider, rt.nlToSql.dialect));
1509
- const projectRoot = findProjectRoot(schemaDir) ?? schemaDir;
1503
+ const projectRoot = findNearestPackageJsonDir(schemaDir) ?? schemaDir;
1510
1504
  const def = EXECUTE_DRIVER_REGISTRY[exec.provider];
1511
1505
  const result = await def.execute({
1512
1506
  connectionString: exec.databaseUrl,
@@ -1859,6 +1853,17 @@ function contentTypeFor(path) {
1859
1853
  function isRecord(value) {
1860
1854
  return typeof value === "object" && value !== null && !Array.isArray(value);
1861
1855
  }
1856
+ /** `runStudioIntrospection` for an HTTP route: an empty result is the user's to fix (ADR 0037), so it becomes a 422, not a 500. */
1857
+ async function introspectForRequest(options) {
1858
+ try {
1859
+ return await runStudioIntrospection(options);
1860
+ }
1861
+ catch (error) {
1862
+ if (error instanceof EmptyIntrospectionError)
1863
+ throw new StudioHttpError(422, error.message);
1864
+ throw error;
1865
+ }
1866
+ }
1862
1867
  class StudioHttpError extends Error {
1863
1868
  status;
1864
1869
  constructor(status, message) {