@oodarun/cli 0.1.13 → 0.1.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oodarun/cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Launch Claude Code on cloud dev environments",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -54,6 +54,7 @@
54
54
  "@types/react-dom": "^19.0.0",
55
55
  "@types/ws": "^8.18.1",
56
56
  "@vitejs/plugin-react": "^4.3.0",
57
+ "drizzle-orm": "^0.45.2",
57
58
  "esbuild": "^0.25.0",
58
59
  "husky": "^9.1.7",
59
60
  "react": "^19.0.0",
@@ -1,6 +0,0 @@
1
- import{r as a,j as e}from"./index-5I_ipmZc.js";import{b as E,d as D,g as H,a as J,c as U,s as Y}from"./api-DbuItvnH.js";function q(){const n=new URLSearchParams(window.location.search).get("email");return window.location.pathname==="/signup"&&n?{mode:"invite",email:n}:{mode:"login",email:""}}function z({onLogin:s}){const[n]=a.useState(q),[r]=a.useState(n.mode),[o,j]=a.useState(n.email),[d,y]=a.useState(""),[h,f]=a.useState(""),[g,u]=a.useState(null),[c,i]=a.useState([]),[t,x]=a.useState(!1),p="https://api.ooda.run",k=async v=>{v.preventDefault(),u(null),i([]),x(!0);try{const A=await fetch(`${p}/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:o,password:d})});if(!A.ok){u("Invalid email or password");return}const w=await A.json();s(w.user,w.orgs,w.accessToken,w.refreshToken)}catch{u("Could not reach the server")}finally{x(!1)}},b=async v=>{v.preventDefault(),u(null),i([]),x(!0);try{const A=await fetch(`${p}/auth/signup`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:o,password:d,name:h})}),w=await A.json();if(!A.ok){u(w.error||"Signup failed"),i(w.errors||[]);return}const I=await fetch(`${p}/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:o,password:d})});if(!I.ok){u("Account created but login failed. Try logging in.");return}const P=await I.json();s(P.user,P.orgs,P.accessToken,P.refreshToken)}catch{u("Could not reach the server")}finally{x(!1)}};return e.jsx("div",{className:"min-h-screen bg-surface flex items-center justify-center",children:e.jsxs("div",{className:"w-full max-w-sm p-8",children:[e.jsxs("div",{className:"text-center mb-8",children:[e.jsx("img",{src:"/logo.svg",alt:"ooda",className:"h-6 mx-auto mb-2"}),e.jsx("p",{className:"text-sm text-ink-muted",children:r==="invite"?"Accept your invite":"Sign in to your account"})]}),e.jsxs("form",{onSubmit:r==="invite"?b:k,className:"space-y-4",children:[r==="invite"&&e.jsxs("div",{children:[e.jsx("label",{htmlFor:"name",className:"block text-sm font-medium text-ink-muted mb-1",children:"Name"}),e.jsx("input",{id:"name",type:"text",value:h,onChange:v=>f(v.target.value),required:!0,autoComplete:"name",autoFocus:!0,className:"w-full px-3 py-2 border border-border text-sm bg-white focus:outline-none focus:ring-2 focus:ring-ink focus:border-transparent"})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"email",className:"block text-sm font-medium text-ink-muted mb-1",children:"Email"}),e.jsx("input",{id:"email",type:"email",value:o,onChange:v=>j(v.target.value),required:!0,autoComplete:"email",autoFocus:r==="login",disabled:r==="invite",className:"w-full px-3 py-2 border border-border text-sm bg-white focus:outline-none focus:ring-2 focus:ring-ink focus:border-transparent disabled:bg-gray-50 disabled:text-ink-muted"})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"password",className:"block text-sm font-medium text-ink-muted mb-1",children:r==="invite"?"Choose a password":"Password"}),e.jsx("input",{id:"password",type:"password",value:d,onChange:v=>y(v.target.value),required:!0,autoComplete:r==="invite"?"new-password":"current-password",className:"w-full px-3 py-2 border border-border text-sm bg-white focus:outline-none focus:ring-2 focus:ring-ink focus:border-transparent"})]}),g&&e.jsxs("div",{className:"text-sm text-red-600",children:[e.jsx("p",{children:g}),c.length>0&&e.jsx("ul",{className:"mt-1 list-disc list-inside text-xs text-red-500",children:c.map(v=>e.jsx("li",{children:v},v))})]}),e.jsx("button",{type:"submit",disabled:t,className:"w-full px-4 py-2 bg-ink text-surface text-sm font-medium hover:bg-ink/80 transition-colors disabled:opacity-50",children:t?"...":r==="invite"?"Accept invite":"Sign in"})]})]})})}function V({orgId:s,role:n="admin"}){const[r,o]=a.useState([]),[j,d]=a.useState(!0),[y,h]=a.useState(null),f=n==="admin",g=async()=>{d(!0);try{const c=f?await E(s,"/users"):await D(s,"/members");if(!c.ok){h("Failed to load members");return}const i=await c.json();o(i.users||[])}catch{h("Could not reach the server")}finally{d(!1)}};a.useEffect(()=>{g()},[s]);const u=async c=>{if(!confirm(`Remove ${c} from this organization?`))return;(await E(s,`/users/${encodeURIComponent(c)}`,{method:"DELETE"})).ok&&o(t=>t.filter(x=>x.email!==c))};return j?e.jsx("p",{className:"text-sm text-ink-muted",children:"Loading members..."}):y?e.jsx("p",{className:"text-sm text-red-600",children:y}):e.jsxs("div",{children:[e.jsxs("h2",{className:"text-lg font-medium text-ink mb-4",children:["Members",e.jsx("span",{className:"text-ink-faint font-normal ml-2",children:r.length})]}),r.length===0?e.jsx("p",{className:"text-sm text-ink-muted",children:"No members yet."}):e.jsx("div",{className:"border border-border divide-y divide-border",children:r.map(c=>e.jsxs("div",{className:"flex items-center justify-between px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-ink",children:c.name||c.email}),e.jsx("p",{className:"text-xs text-ink-faint",children:c.email})]}),c.role&&e.jsx("span",{className:`text-xs px-2 py-0.5 ${c.role==="admin"?"bg-ink text-surface":"bg-border text-ink-muted"}`,children:c.role})]}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("span",{className:"text-xs text-ink-faint",children:new Date(c.joinedAt).toLocaleDateString()}),f&&e.jsx("button",{onClick:()=>u(c.email),className:"text-xs text-red-500 hover:text-red-700 transition-colors",children:"Remove"})]})]},c.email))})]})}function R(s){return s.status==="pending"&&s.expiresAt&&new Date(s.expiresAt).getTime()<Date.now()?"expired":s.status}function G({orgId:s}){const[n,r]=a.useState([]),[o,j]=a.useState(!0),[d,y]=a.useState(null),[h,f]=a.useState(""),[g,u]=a.useState("member"),[c,i]=a.useState(!1),[t,x]=a.useState(null),p=async()=>{j(!0);try{const l=await E(s,"/invites");if(!l.ok){y("Failed to load invites");return}const C=await l.json();r(C.invites||[])}catch{y("Could not reach the server")}finally{j(!1)}};a.useEffect(()=>{p()},[s]);const k=async l=>{l.preventDefault(),x(null);const C=h.split(/[,\n]/).map(m=>m.trim()).filter(Boolean);if(C.length===0){x("Enter at least one email address");return}i(!0);try{const m=await E(s,"/invites",{method:"POST",body:JSON.stringify({emails:C,role:g})});if(!m.ok){const N=await m.json();x(N.error||"Failed to create invites");return}f(""),p()}catch{x("Could not reach the server")}finally{i(!1)}},b=async l=>{if(!confirm(`Revoke invite for ${l}?`))return;(await E(s,`/invites/${encodeURIComponent(l)}`,{method:"DELETE"})).ok&&p()},v=n.filter(l=>R(l)==="pending"),A=n.filter(l=>R(l)==="expired"),w=n.filter(l=>R(l)==="accepted"),I=n.filter(l=>R(l)==="revoked"),P=async l=>{i(!0);try{(await E(s,"/invites",{method:"POST",body:JSON.stringify({emails:[l],role:"member"})})).ok&&p()}catch{}finally{i(!1)}},$=l=>{switch(l){case"pending":return"bg-yellow-100 text-yellow-800";case"accepted":return"bg-green-100 text-green-800";case"revoked":return"bg-red-100 text-red-800";case"expired":return"bg-orange-100 text-orange-800";default:return"bg-border text-ink-muted"}};return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-medium text-ink mb-4",children:"Invite people"}),e.jsxs("form",{onSubmit:k,className:"space-y-3",children:[e.jsx("div",{children:e.jsx("textarea",{value:h,onChange:l=>f(l.target.value),placeholder:"Email addresses (comma or newline separated)",rows:3,className:"w-full px-3 py-2 border border-border text-sm bg-white focus:outline-none focus:ring-2 focus:ring-ink focus:border-transparent resize-none font-mono"})}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("select",{value:g,onChange:l=>u(l.target.value),className:"px-3 py-2 border border-border text-sm bg-white focus:outline-none focus:ring-2 focus:ring-ink",children:[e.jsx("option",{value:"member",children:"Member"}),e.jsx("option",{value:"admin",children:"Admin"})]}),e.jsx("button",{type:"submit",disabled:c,className:"px-4 py-2 bg-ink text-surface text-sm font-medium hover:bg-ink/80 transition-colors disabled:opacity-50",children:c?"Sending...":"Send invites"})]}),t&&e.jsx("p",{className:"text-sm text-red-600",children:t})]})]}),o?e.jsx("p",{className:"text-sm text-ink-muted",children:"Loading invites..."}):d?e.jsx("p",{className:"text-sm text-red-600",children:d}):e.jsxs("div",{children:[e.jsxs("h2",{className:"text-lg font-medium text-ink mb-4",children:["Invites",e.jsx("span",{className:"text-ink-faint font-normal ml-2",children:n.length})]}),n.length===0?e.jsx("p",{className:"text-sm text-ink-muted",children:"No invites yet."}):e.jsx("div",{className:"border border-border divide-y divide-border",children:[...v,...A,...w,...I].map(l=>{const C=R(l);return e.jsxs("div",{className:"flex items-center justify-between px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-ink",children:l.email}),e.jsx("p",{className:"text-xs text-ink-faint",children:new Date(l.createdAt).toLocaleDateString()})]}),e.jsx("span",{className:`text-xs px-2 py-0.5 ${$(C)}`,children:C}),e.jsx("span",{className:`text-xs px-2 py-0.5 ${l.role==="admin"?"bg-ink text-surface":"bg-border text-ink-muted"}`,children:l.role})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[C==="expired"&&e.jsx("button",{onClick:()=>P(l.email),disabled:c,className:"text-xs text-ink hover:text-ink/70 transition-colors disabled:opacity-50",children:"Re-send"}),C==="pending"&&e.jsx("button",{onClick:()=>b(l.email),className:"text-xs text-red-500 hover:text-red-700 transition-colors",children:"Revoke"})]})]},l.id)})})]})]})}function Q(s){const n=Math.max(0,Math.floor(Date.now()/1e3-s));return n<60?`${n}s ago`:n<3600?`${Math.floor(n/60)}m ago`:n<86400?`${Math.floor(n/3600)}h ago`:`${Math.floor(n/86400)}d ago`}function W(s){try{return atob(s.logTailBase64)}catch{return""}}function F(s){const n=W(s).trim();if(!n)return"";const r=n.split(`
2
- `).filter(o=>o.trim());return r[r.length-1]||""}function _(s){const n=Date.now()-new Date(s).getTime(),r=Math.floor(n/6e4);if(r<1)return"just now";if(r<60)return`${r}m ago`;const o=Math.floor(r/60);return o<24?`${o}h ago`:`${Math.floor(o/24)}d ago`}function X(s){const n=s.toLowerCase();return["running","started","ready"].includes(n)?"bg-green-100 text-green-800":["stopped","suspended"].includes(n)?"bg-red-100 text-red-800":n==="sleeping"?"bg-gray-100 text-gray-600":"bg-yellow-100 text-yellow-800"}function Z({orgId:s,role:n="admin"}){const[r,o]=a.useState([]),[j,d]=a.useState(!0),[y,h]=a.useState(null),f=n==="admin",g=f?E:D,u=async()=>{d(!0);try{const t=await g(s,"/projects");if(!t.ok){h("Failed to load projects");return}const x=await t.json();o(x.projects||[])}catch{h("Could not reach the server")}finally{d(!1)}};a.useEffect(()=>{u()},[s]);const c=async t=>{(await g(s,`/projects/${encodeURIComponent(t)}/stop`,{method:"POST"})).ok&&o(p=>p.map(k=>k.name===t?{...k,status:"stopped"}:k))},i=async t=>{if(!confirm(`Delete project "${t}"? This will destroy the running environment.`))return;(await g(s,`/projects/${encodeURIComponent(t)}`,{method:"DELETE"})).ok&&o(p=>p.filter(k=>k.name!==t))};return j?e.jsx("p",{className:"text-sm text-ink-muted",children:"Loading projects..."}):y?e.jsx("p",{className:"text-sm text-red-600",children:y}):e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsxs("h2",{className:"text-lg font-medium text-ink",children:["Projects",e.jsx("span",{className:"text-ink-faint font-normal ml-2",children:r.length})]}),e.jsx("button",{onClick:u,className:"text-xs text-ink-muted hover:text-ink transition-colors",children:"Refresh"})]}),r.length===0?e.jsxs("div",{className:"border border-border py-12 text-center",children:[e.jsx("p",{className:"text-sm text-ink-muted",children:"No projects created yet"}),e.jsx("p",{className:"text-xs text-ink-faint mt-1",children:"Projects will appear here when members deploy"})]}):e.jsx("div",{className:"border border-border divide-y divide-border",children:r.map(t=>{const x=f||t.isOwn,p=t.status&&["running","started","ready"].includes(t.status.toLowerCase());return e.jsxs("div",{className:"flex items-center justify-between px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-ink",children:t.name}),t.createdByName&&e.jsxs("p",{className:"text-xs text-ink-faint",children:[t.createdByName,t.isOwn&&!f&&e.jsx("span",{className:"text-ink-muted ml-1",children:"(you)"})]})]}),t.status&&e.jsx("span",{className:`text-xs px-2 py-0.5 ${X(t.status)}`,children:t.status}),t.devServerStatus&&e.jsx("span",{className:"text-xs px-2 py-0.5 bg-red-100 text-red-800 cursor-help",title:`Dev server auto-disabled ${Q(t.devServerStatus.disabledAt)} after ${t.devServerStatus.fails} rapid crashes.${F(t.devServerStatus)?`
3
-
4
- Last error: ${F(t.devServerStatus).slice(0,300)}`:""}
5
-
6
- The owner needs to reconnect with npx @oodarun/cli and run /start-server after fixing the underlying error.`,children:"dev server disabled"})]}),e.jsxs("div",{className:"flex items-center gap-4",children:[t.lastActiveAt?e.jsxs("span",{className:"text-xs text-ink-muted",title:`Last active: ${t.lastActiveAt}`,children:["active ",_(t.lastActiveAt)]}):e.jsxs("span",{className:"text-xs text-ink-faint",title:t.createdAt,children:["created ",_(t.createdAt)]}),x&&p&&e.jsx("button",{onClick:()=>c(t.name),className:"text-xs text-amber-600 hover:text-amber-800 transition-colors",children:"Stop"}),x&&e.jsx("button",{onClick:()=>i(t.name),className:"text-xs text-red-500 hover:text-red-700 transition-colors",children:"Delete"})]})]},t.name)})})]})}function ee(s){const n=Date.now()-new Date(s).getTime(),r=Math.floor(n/6e4);if(r<1)return"just now";if(r<60)return`${r}m ago`;const o=Math.floor(r/60);return o<24?`${o}h ago`:`${Math.floor(o/24)}d ago`}function te({orgId:s,role:n="admin"}){const[r,o]=a.useState([]),[j,d]=a.useState(!0),[y,h]=a.useState(null),f=n==="admin",g=f?E:D,u=async()=>{d(!0);try{const i=await g(s,"/sites");if(!i.ok){h("Failed to load sites");return}const t=await i.json();o(t.sites||[])}catch{h("Could not reach the server")}finally{d(!1)}};a.useEffect(()=>{u()},[s]);const c=async i=>{if(!confirm(`Unpublish "${i}"? This will remove the site from ooda.run.`))return;(await g(s,`/sites/${encodeURIComponent(i)}`,{method:"DELETE"})).ok&&o(x=>x.filter(p=>p.slug!==i))};return j?e.jsx("p",{className:"text-sm text-ink-muted",children:"Loading sites..."}):y?e.jsx("p",{className:"text-sm text-red-600",children:y}):e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsxs("h2",{className:"text-lg font-medium text-ink",children:["Published Sites",e.jsx("span",{className:"text-ink-faint font-normal ml-2",children:r.length})]}),e.jsx("button",{onClick:u,className:"text-xs text-ink-muted hover:text-ink transition-colors",children:"Refresh"})]}),r.length===0?e.jsxs("div",{className:"border border-border py-12 text-center",children:[e.jsx("p",{className:"text-sm text-ink-muted",children:"No sites published yet"}),e.jsx("p",{className:"text-xs text-ink-faint mt-1",children:"Published sites will appear here"})]}):e.jsx("div",{className:"border border-border divide-y divide-border",children:r.map(i=>{const t=f||i.isOwn;return e.jsxs("div",{className:"flex items-center justify-between px-4 py-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-ink",children:i.slug}),e.jsx("p",{className:"text-xs text-ink-faint",children:i.url}),i.publishedByName&&e.jsxs("p",{className:"text-xs text-ink-faint mt-0.5",children:["by ",i.publishedByName,i.isOwn&&!f&&e.jsx("span",{className:"text-ink-muted ml-1",children:"(you)"})]})]}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("span",{className:"text-xs text-ink-faint",title:i.publishedAt,children:ee(i.publishedAt)}),e.jsx("a",{href:i.url,target:"_blank",rel:"noopener noreferrer",className:"text-xs text-ink-muted hover:text-ink border border-border px-2 py-1 transition-colors",children:"Open"}),t&&e.jsx("button",{onClick:()=>c(i.slug),className:"text-xs text-red-500 hover:text-red-700 transition-colors",children:"Unpublish"})]})]},i.slug)})})]})}function se({orgId:s}){const[n,r]=a.useState(null),[o,j]=a.useState(!0),[d,y]=a.useState(!1),[h,f]=a.useState(null),[g,u]=a.useState(null),[c,i]=a.useState(""),[t,x]=a.useState(""),[p,k]=a.useState(""),[b,v]=a.useState(60),[A,w]=a.useState([]),I=async()=>{var m,N;j(!0),f(null);try{const S=await E(s,"/settings");if(!S.ok){f("Failed to load settings");return}const L=await S.json();r(L);const T=L.claude;if(T){i(((m=T.env)==null?void 0:m.ANTHROPIC_API_KEY)||""),x(((N=T.env)==null?void 0:N.ANTHROPIC_BASE_URL)||""),k(T.apiKeyHelper||""),v(Math.round((T.apiKeyHelperTtlMs||36e5)/6e4));const O=new Set(["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL"]),B=Object.entries(T.env||{}).filter(([M])=>!O.has(M)).map(([M,K])=>({key:M,value:K}));w(B)}}catch{f("Could not reach the server")}finally{j(!1)}};a.useEffect(()=>{I()},[s]);const P=async()=>{y(!0),f(null),u(null);const m={};c&&(m.ANTHROPIC_API_KEY=c),t&&(m.ANTHROPIC_BASE_URL=t);for(const S of A)S.key.trim()&&(m[S.key.trim()]=S.value);const N={};Object.keys(m).length>0&&(N.env=m),p.trim()&&(N.apiKeyHelper=p.trim(),N.apiKeyHelperTtlMs=b*6e4);try{const S={claude:Object.keys(N).length>0?N:null};if(!(await E(s,"/settings",{method:"PUT",body:JSON.stringify(S)})).ok){f("Failed to save settings");return}u("Settings saved"),setTimeout(()=>u(null),3e3)}catch{f("Could not reach the server")}finally{y(!1)}},$=()=>{w(m=>[...m,{key:"",value:""}])},l=m=>{w(N=>N.filter((S,L)=>L!==m))},C=(m,N,S)=>{w(L=>L.map((T,O)=>O===m?{...T,[N]:S}:T))};return o?e.jsx("p",{className:"text-sm text-ink-muted",children:"Loading settings..."}):h&&!n?e.jsx("p",{className:"text-sm text-red-600",children:h}):e.jsxs("div",{className:"space-y-8",children:[e.jsxs("section",{children:[e.jsx("h2",{className:"text-lg font-medium text-ink mb-1",children:"Claude Configuration"}),e.jsx("p",{className:"text-xs text-ink-faint mb-4",children:"Configure how Claude Code authenticates on cloud sandboxes in this organization."}),e.jsxs("div",{className:"border border-border divide-y divide-border",children:[e.jsxs("div",{className:"px-4 py-3",children:[e.jsx("label",{className:"block text-sm font-medium text-ink mb-1",children:"Anthropic API Key"}),e.jsx("p",{className:"text-xs text-ink-faint mb-2",children:"Encrypted at rest. Consider using an API key helper for automatic rotation."}),e.jsx("input",{type:"password",value:c,onChange:m=>i(m.target.value),placeholder:"sk-ant-api03-...",className:"w-full text-sm px-3 py-1.5 border border-border bg-surface text-ink placeholder:text-ink-faint focus:outline-none focus:border-ink"})]}),e.jsxs("div",{className:"px-4 py-3",children:[e.jsx("label",{className:"block text-sm font-medium text-ink mb-1",children:"Base URL"}),e.jsx("p",{className:"text-xs text-ink-faint mb-2",children:"Custom Anthropic API endpoint (leave blank for default)."}),e.jsx("input",{type:"text",value:t,onChange:m=>x(m.target.value),placeholder:"https://api.anthropic.com",className:"w-full text-sm px-3 py-1.5 border border-border bg-surface text-ink placeholder:text-ink-faint focus:outline-none focus:border-ink"})]}),e.jsxs("div",{className:"px-4 py-3",children:[e.jsx("label",{className:"block text-sm font-medium text-ink mb-1",children:"API Key Helper"}),e.jsx("p",{className:"text-xs text-ink-faint mb-2",children:"Path to a script that prints a valid API key to stdout. Used for token rotation."}),e.jsx("input",{type:"text",value:p,onChange:m=>k(m.target.value),placeholder:"/usr/local/bin/token-helper.sh",className:"w-full text-sm px-3 py-1.5 border border-border bg-surface text-ink placeholder:text-ink-faint focus:outline-none focus:border-ink"}),p&&e.jsxs("div",{className:"mt-2",children:[e.jsx("label",{className:"block text-xs text-ink-muted mb-1",children:"Refresh interval (minutes)"}),e.jsx("input",{type:"number",value:b,onChange:m=>v(parseInt(m.target.value,10)||60),min:1,className:"w-24 text-sm px-3 py-1.5 border border-border bg-surface text-ink focus:outline-none focus:border-ink"})]})]}),e.jsxs("div",{className:"px-4 py-3",children:[e.jsx("label",{className:"block text-sm font-medium text-ink mb-1",children:"Environment Variables"}),e.jsx("p",{className:"text-xs text-ink-faint mb-2",children:"Additional env vars injected into project sessions."}),A.map((m,N)=>e.jsxs("div",{className:"flex gap-2 mb-2",children:[e.jsx("input",{type:"text",value:m.key,onChange:S=>C(N,"key",S.target.value),placeholder:"KEY",className:"flex-1 text-sm px-3 py-1.5 border border-border bg-surface text-ink placeholder:text-ink-faint focus:outline-none focus:border-ink font-mono"}),e.jsx("input",{type:"text",value:m.value,onChange:S=>C(N,"value",S.target.value),placeholder:"value",className:"flex-[2] text-sm px-3 py-1.5 border border-border bg-surface text-ink placeholder:text-ink-faint focus:outline-none focus:border-ink font-mono"}),e.jsx("button",{onClick:()=>l(N),className:"text-xs text-red-500 hover:text-red-700 px-2",children:"Remove"})]},N)),e.jsx("button",{onClick:$,className:"text-xs text-ink-muted hover:text-ink transition-colors",children:"+ Add variable"})]})]})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("button",{onClick:P,disabled:d,className:"text-sm font-medium px-4 py-1.5 bg-ink text-surface hover:opacity-90 transition-opacity disabled:opacity-50",children:d?"Saving...":"Save changes"}),g&&e.jsx("span",{className:"text-xs text-green-600",children:g}),h&&e.jsx("span",{className:"text-xs text-red-600",children:h})]})]})}const ne=[["projects","Projects"],["sites","Published Sites"],["members","Members"],["invites","Invites"],["settings","Settings"]],ae=[["projects","Projects"],["sites","Published Sites"],["members","Members"]];function re({orgId:s,orgName:n,user:r,role:o,onLogout:j}){const[d,y]=a.useState("projects"),h=o==="admin",f=h?ne:ae;return e.jsxs("div",{className:"min-h-screen bg-surface",children:[e.jsxs("header",{className:"border-b border-border px-6 py-4 flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("img",{src:"/logo.svg",alt:"ooda",className:"h-5"}),e.jsx("span",{className:"text-ink-faint",children:"/"}),e.jsx("span",{className:"text-sm font-medium text-ink",children:n})]}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("span",{className:"text-xs text-ink-faint",children:r.email}),e.jsx("button",{onClick:j,className:"text-xs text-ink-muted hover:text-ink transition-colors",children:"Sign out"})]})]}),e.jsx("div",{className:"border-b border-border px-6",children:e.jsx("nav",{className:"flex gap-6",children:f.map(([g,u])=>e.jsx("button",{onClick:()=>y(g),className:`py-3 text-sm font-medium border-b-2 transition-colors ${d===g?"border-ink text-ink":"border-transparent text-ink-muted hover:text-ink"}`,children:u},g))})}),e.jsxs("main",{className:"max-w-4xl mx-auto px-6 py-8",children:[d==="projects"&&e.jsx(Z,{orgId:s,role:o}),d==="sites"&&e.jsx(te,{orgId:s,role:o}),d==="members"&&e.jsx(V,{orgId:s,role:o}),d==="invites"&&h&&e.jsx(G,{orgId:s}),d==="settings"&&h&&e.jsx(se,{orgId:s})]})]})}function le(){const[s,n]=a.useState(null),[r,o]=a.useState([]),[j,d]=a.useState(null),[y,h]=a.useState(!0);a.useEffect(()=>{(async()=>{if(!H()){h(!1);return}const t=await J();if(t){n(t.user);const x=localStorage.getItem("ooda-selected-org"),p=localStorage.getItem("ooda-selected-org-name"),k=t.orgs.find(b=>b.orgId===x);if(k)o(t.orgs.map(b=>({...b,orgName:b.orgId===x&&p||b.orgId}))),d({...k,orgName:p||k.orgId});else if(t.orgs.length>0){const b=t.orgs.map(v=>({...v,orgName:v.orgId}));o(b),d(b[0]),localStorage.setItem("ooda-selected-org",b[0].orgId),localStorage.setItem("ooda-selected-org-name",b[0].orgName)}}else U();h(!1)})()},[]);const f=(i,t,x,p)=>{Y(x,p),n(i),o(t);const b=t.find(v=>v.role==="admin")||t[0];b&&(d(b),localStorage.setItem("ooda-selected-org",b.orgId),localStorage.setItem("ooda-selected-org-name",b.orgName))},g=()=>{U(),localStorage.removeItem("ooda-selected-org"),localStorage.removeItem("ooda-selected-org-name"),n(null),o([]),d(null)};if(y)return e.jsx("div",{className:"min-h-screen bg-surface flex items-center justify-center",children:e.jsx("p",{className:"text-sm text-ink-muted",children:"Loading..."})});if(!s||!j)return e.jsx(z,{onLogin:f});const u=r.find(i=>i.orgId===j.orgId),c=(u==null?void 0:u.role)||"member";return e.jsx(re,{orgId:j.orgId,orgName:j.orgName,user:s,role:c,onLogout:g})}export{le as AdminApp};
@@ -1 +0,0 @@
1
- const s="https://api.ooda.run",c="ooda-access-token",h="ooda-refresh-token";function o(){return localStorage.getItem(c)}function f(){return localStorage.getItem(h)}function u(r,e){localStorage.setItem(c,r),localStorage.setItem(h,e)}function d(){localStorage.removeItem(c),localStorage.removeItem(h)}async function i(){const r=f();if(!r)return!1;const e=await fetch(`${s}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:r})});if(!e.ok)return!1;const t=await e.json();return u(t.accessToken,t.refreshToken),!0}async function l(){const r=o();if(!r)return null;let e=await fetch(`${s}/auth/me`,{headers:{Authorization:`Bearer ${r}`}});if(e.status===401){if(!await i())return null;e=await fetch(`${s}/auth/me`,{headers:{Authorization:`Bearer ${o()}`}})}return e.ok?e.json():null}async function k(r,e,t){const a=o(),n=new Headers(t==null?void 0:t.headers);return a&&n.set("Authorization",`Bearer ${a}`),n.set("Content-Type","application/json"),fetch(`${s}/org/${r}/admin${e}`,{...t,headers:n})}async function $(r,e){const t=o(),a=new Headers(e==null?void 0:e.headers);return t&&a.set("Authorization",`Bearer ${t}`),a.set("Content-Type","application/json"),fetch(`${s}/int${r}`,{...e,headers:a})}async function g(r,e,t){const a=o(),n=new Headers(t==null?void 0:t.headers);return a&&n.set("Authorization",`Bearer ${a}`),n.set("Content-Type","application/json"),fetch(`${s}/org/${r}/dashboard${e}`,{...t,headers:n})}export{l as a,k as b,d as c,g as d,o as g,$ as i,u as s};