velloo 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/BUN-LICENSE.md +4 -3
  2. package/README.md +8 -1
  3. package/THIRD-PARTY-NOTICES.md +3 -3
  4. package/canvas/assets/index-CkVteRZ9.css +2 -0
  5. package/canvas/assets/index-DFOlwAKW.js +31 -0
  6. package/canvas/assets/lucide-all-CezGNjGW.js +1 -0
  7. package/canvas/index.html +2 -2
  8. package/{chunk-qrnkrkkt.js → chunk-00n2d68z.js} +1 -1
  9. package/{chunk-mdgwdqnq.js → chunk-02gpgqj4.js} +8 -8
  10. package/{chunk-qp3wahg4.js → chunk-0ztxyzac.js} +1 -1
  11. package/{chunk-4y2xy1kf.js → chunk-2x5rw009.js} +3 -3
  12. package/{chunk-abr2hgay.js → chunk-3f2p94wa.js} +1 -1
  13. package/{chunk-srp5gyr1.js → chunk-3jqbybmp.js} +1 -1
  14. package/{chunk-0vaff40g.js → chunk-5tatkt2j.js} +1 -1
  15. package/{chunk-asfgvzse.js → chunk-640swnrm.js} +1 -1
  16. package/{chunk-bms1sg4c.js → chunk-6600wkmq.js} +1 -1
  17. package/{chunk-7gyzh0vq.js → chunk-6adv9eke.js} +1 -1
  18. package/{chunk-fvezyap4.js → chunk-6wfvb07c.js} +1 -1
  19. package/chunk-8a2agf28.js +22 -0
  20. package/{chunk-82x2jkfq.js → chunk-8kn6tp4b.js} +2 -2
  21. package/{chunk-fmcm06xb.js → chunk-9b4akcfp.js} +1 -1
  22. package/{chunk-0pxm5v0b.js → chunk-9kvbe53e.js} +3 -3
  23. package/{chunk-gfppsgvv.js → chunk-a5mxgvcz.js} +1 -1
  24. package/{chunk-g7d065ek.js → chunk-as4rn21e.js} +1 -1
  25. package/{chunk-xg71h7nb.js → chunk-avewjans.js} +4 -4
  26. package/chunk-bpb97rsk.js +17 -0
  27. package/{chunk-j306gr0c.js → chunk-bsn761jv.js} +1 -1
  28. package/{chunk-kkvf0xk2.js → chunk-c11n1tve.js} +2 -2
  29. package/{chunk-sme6hv6f.js → chunk-cfksd3zh.js} +1 -1
  30. package/{chunk-zv17t1n7.js → chunk-ck3bg8rf.js} +1 -1
  31. package/{chunk-d93kpv7s.js → chunk-cy1nmr0f.js} +1 -1
  32. package/{chunk-qqrac4va.js → chunk-dqy6a75x.js} +1 -1
  33. package/{chunk-xzf52wvn.js → chunk-f8jchnwe.js} +1 -1
  34. package/{chunk-s84vxcdv.js → chunk-fejhav0p.js} +1 -1
  35. package/{chunk-ykpctq15.js → chunk-fzqt4rsz.js} +1 -1
  36. package/{chunk-3m28pvzr.js → chunk-g7pbfad5.js} +1 -1
  37. package/{chunk-b8vcbbrg.js → chunk-gv209d5j.js} +1 -1
  38. package/chunk-j4rt31qb.js +2 -0
  39. package/{chunk-d0j8dg1e.js → chunk-kmsbcb3q.js} +1 -1
  40. package/{chunk-5s1wjhtj.js → chunk-ktps9v0k.js} +1 -1
  41. package/{chunk-667ag8ts.js → chunk-ktsdzzgx.js} +1 -1
  42. package/{chunk-0e5j6nqs.js → chunk-kvxn1cyx.js} +1 -1
  43. package/{chunk-ex36z3b3.js → chunk-n70zfv0m.js} +906 -906
  44. package/{chunk-rqd4swxx.js → chunk-n8pr0kc8.js} +1 -1
  45. package/{chunk-eh2xp4v6.js → chunk-pasvz8xw.js} +1 -1
  46. package/{chunk-j4fn2a2b.js → chunk-qewarcqf.js} +1 -1
  47. package/{chunk-dvh0b2r4.js → chunk-qf1c6jxh.js} +1 -1
  48. package/chunk-qn05hn7a.js +5 -0
  49. package/{chunk-gcce587w.js → chunk-qvdvxqs0.js} +1 -1
  50. package/{chunk-pjkkp7xa.js → chunk-r014f76h.js} +1 -1
  51. package/{chunk-acsv6m3n.js → chunk-seaar5na.js} +1 -1
  52. package/{chunk-84n18xgx.js → chunk-tax7fxav.js} +2 -2
  53. package/{chunk-gbq5wv7x.js → chunk-tmjs6xas.js} +1 -1
  54. package/{chunk-vs8j3x6t.js → chunk-v6w8akph.js} +1 -1
  55. package/{chunk-5x4nw0zz.js → chunk-w40pqmyz.js} +4 -4
  56. package/{chunk-vdbr81a3.js → chunk-x05grjd9.js} +1 -1
  57. package/{chunk-md4mpvqc.js → chunk-x61f42fj.js} +2 -2
  58. package/{chunk-531xzfq5.js → chunk-xc6mats8.js} +1 -1
  59. package/chunk-yp39f93e.js +815 -0
  60. package/{chunk-5yhnnhkd.js → chunk-ysf9388d.js} +1 -1
  61. package/{chunk-64pex3mw.js → chunk-z3r8px3d.js} +1 -1
  62. package/{chunk-ejppcfa5.js → chunk-z9kcdgj8.js} +1 -1
  63. package/chunk-zexzy6zv.js +2 -0
  64. package/chunk-zjbbd87h.js +2 -0
  65. package/cli.js +1 -1
  66. package/package.json +9 -9
  67. package/pkgs/provider-antd/src/index.ts +1 -0
  68. package/pkgs/provider-html/src/index.ts +1 -0
  69. package/pkgs/provider-mui/src/index.ts +10 -0
  70. package/pkgs/provider-none/src/index.ts +1 -0
  71. package/pkgs/schema/src/board.ts +13 -1
  72. package/pkgs/schema/src/config.ts +34 -0
  73. package/pkgs/schema/src/flow.ts +230 -0
  74. package/pkgs/schema/src/frame.ts +7 -0
  75. package/pkgs/schema/src/index.ts +22 -0
  76. package/pkgs/schema/src/node-identity.ts +48 -0
  77. package/pkgs/schema/src/node.ts +80 -9
  78. package/pkgs/schema/src/snippet-resolve.ts +43 -0
  79. package/pkgs/schema/src/theme.ts +56 -0
  80. package/plugins/claude/velloo/agents/velloo-designer.md +7 -4
  81. package/plugins/claude/velloo/commands/setup.md +13 -0
  82. package/plugins/gemini/GEMINI.md +9 -5
  83. package/plugins/gemini/commands/velloo/setup.toml +11 -0
  84. package/skills/velloo-design/SKILL.md +11 -6
  85. package/skills/velloo-setup/SKILL.md +179 -36
  86. package/canvas/assets/index-BQ5710lu.js +0 -29
  87. package/canvas/assets/index-GNLd0pCG.css +0 -2
  88. package/canvas/assets/lucide-all-DIRZdJPK.js +0 -1
  89. package/chunk-brnkprt5.js +0 -723
  90. package/chunk-bys7pqdb.js +0 -2
  91. package/chunk-g8xrwjvq.js +0 -22
  92. package/chunk-jbgde2v9.js +0 -2
  93. package/chunk-rxkvjyst.js +0 -18
  94. package/chunk-sjjnm4a1.js +0 -2
  95. package/chunk-snak5zpr.js +0 -5
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{signInRequired,boardLimitReached,topUpTokens,createServer}from"./chunk-brnkprt5.js";import"./chunk-5x4nw0zz.js";import"./chunk-ex36z3b3.js";import{ok,err,GUESTS_UNAVAILABLE,GuestsResponseSchema,GuestInviteResponseSchema,GuestLinkResponseSchema,unreachable,httpFailureFrom,boardLimitFrom,describeCloudError,cloudFetch}from"./chunk-bys7pqdb.js";import{loadCredential,saveCredential,deleteCredential,fetchAccount,revokeCredential,performDeviceLogin}from"./chunk-kkvf0xk2.js";import{listPublishedDesigns,unpublishDesign,listPublishDestinations,describePublishError,PUBLISH_VIEWPORT,gitContext,publishableTeams,resolvePublishTeam,listTeams,publishDesign}from"./chunk-snak5zpr.js";import{TOOL_VERSION}from"./chunk-3m28pvzr.js";import{updateStatus,upgradeInstalledVelloo}from"./chunk-s84vxcdv.js";import{assertLoopbackHost,daemonRoot,writeLock,removeLock,readRememberedPort,rememberPort,portCandidates,registryUpsert,registryRemove}from"./chunk-xzf52wvn.js";import"./chunk-zv17t1n7.js";import{defaultCloudUrl,fetchCloudAppUrl}from"./chunk-abr2hgay.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{existsSync as existsSync2}from"fs";import{existsSync}from"fs";import{dirname,join}from"path";import{fileURLToPath}from"url";var here=dirname(fileURLToPath(import.meta.url));function resolveCanvasDist(){let devLayout=join(here,"..","dist");return[process.env.VELLOO_CANVAS_DIST,join(here,"canvas"),devLayout].filter((p)=>Boolean(p)).find((p)=>existsSync(join(p,"index.html")))??devLayout}var canvasDistPath=resolveCanvasDist();var ACCOUNT_TTL_MS=60000,ACCOUNT_TIMEOUT_MS=5000;function createCanvasAuth(cloudUrl,dependencies={}){let runDeviceLogin=dependencies.performDeviceLogin??performDeviceLogin,login={state:"idle"},pending=null,cached=null,cachedApp=null,describe=async(token,email)=>{if(cached&&cached.token===token&&Date.now()-cached.at<ACCOUNT_TTL_MS)return{account:cached.account,verified:cached.verified};let found=await fetchAccount(cloudUrl,token,ACCOUNT_TIMEOUT_MS),resolved=found.status==="ok"?{account:found.account,verified:!0}:{account:{email},verified:found.status==="rejected"?!1:null};return cached={token,at:Date.now(),...resolved},resolved},resolveAppUrl=async()=>{if(cachedApp&&Date.now()-cachedApp.at<ACCOUNT_TTL_MS)return cachedApp.url;let url=await fetchCloudAppUrl(cloudUrl,ACCOUNT_TIMEOUT_MS);return cachedApp={at:Date.now(),url},url},status=async()=>{let cred=await loadCredential(cloudUrl),appUrlP=resolveAppUrl();if(!cred?.token)return{loggedIn:!1,cloudUrl,appUrl:await appUrlP,verified:null,login};let[{account,verified},appUrl]=await Promise.all([describe(cred.token,cred.email),appUrlP]);return{loggedIn:!0,cloudUrl,appUrl,verified,login,...account?{account}:{}}};return{status,async beginLogin(){if(login.state==="pending")return status();let attempt=new AbortController;pending=attempt,login={state:"idle"};let settle=()=>{},ready=new Promise((resolve)=>{settle=resolve});return runDeviceLogin(cloudUrl,({verificationUrl,userCode,expiresIn})=>{login={state:"pending",userCode,verificationUrl,expiresAt:new Date(Date.now()+expiresIn*1000).toISOString()},settle()},attempt.signal).then(async(result)=>{await saveCredential(cloudUrl,result),cached=null,login={state:"idle"},await topUpTokens({url:cloudUrl,token:result.token},10).catch(()=>{return})}).catch((err)=>{login=attempt.signal.aborted?{state:"idle"}:{state:"error",message:err instanceof Error?err.message:String(err)}}).finally(()=>{if(pending===attempt)pending=null;settle()}),await ready,status()},async cancelLogin(){pending?.abort(),pending=null,login={state:"idle"}},async logout(){pending?.abort(),pending=null,login={state:"idle"},cached=null;let stored=await loadCredential(cloudUrl);if(stored)await revokeCredential(cloudUrl,stored.token);await deleteCredential(cloudUrl)}}}var guestsUrl=({baseUrl,slug},rest="")=>`${baseUrl}/v1/links/${encodeURIComponent(slug)}/guests${rest}`,json=(body)=>({method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(body)});async function listGuests(target){let body=await cloudFetch(guestsUrl(target),GuestsResponseSchema,{operation:"listing guests",token:target.token});return body.ok?ok(body.value.guests):body}function inviteGuest(target,guest){return cloudFetch(guestsUrl(target),GuestInviteResponseSchema,{operation:"inviting a guest",token:target.token,...json({email:guest.email,...guest.name?{name:guest.name}:{}})})}function resendGuestLink(target,guestId){return cloudFetch(guestsUrl(target,`/${encodeURIComponent(guestId)}/resend`),GuestInviteResponseSchema,{operation:"resending a guest's link",token:target.token,...json({})})}async function mintGuestLink(target,guestId){let body=await cloudFetch(guestsUrl(target,`/${encodeURIComponent(guestId)}/link`),GuestLinkResponseSchema,{operation:"making a guest link",token:target.token,...json({})});return body.ok?ok(body.value.guestUrl):body}async function removeGuest(target,guestId){let res=await fetch(guestsUrl(target,`/${encodeURIComponent(guestId)}`),{method:"DELETE",headers:{authorization:`Bearer ${target.token}`}}).catch((error)=>error);if(!(res instanceof Response))return err(unreachable(res,{url:target.baseUrl}));if(!res.ok)return err(await httpFailureFrom("removing a guest",res));return ok(void 0)}function createCanvasPublish(cloudUrl,auth){let tokenFor=async()=>(await loadCredential(cloudUrl))?.token,access=async()=>{if(!await tokenFor())return{state:"signed-out"};return(await auth.status().catch(()=>null))?.verified===!1?{state:"expired"}:{state:"ready"}},rejection=(error)=>error.kind==="LoggedOut"?signInRequired("expired",describeCloudError(error)):Error(describeCloudError(error)),guestRejection=(error)=>error.kind==="HttpFailure"&&error.status===403&&/\bguests requires\b/.test(error.detail)?Error(GUESTS_UNAVAILABLE):rejection(error),guestTarget=async(slug)=>{let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");return{baseUrl:cloudUrl,token,slug}},settled=(result)=>{if(!result.ok)throw guestRejection(result.error);return result.value},publishedAt=(value)=>{let parsed=value?Date.parse(value):Number.NaN;return Number.isFinite(parsed)?parsed:Number.NEGATIVE_INFINITY},soleTeamId=async(token)=>{let listed=await listTeams(cloudUrl,token);if(!listed.ok)return null;let resolved=resolvePublishTeam(listed.value);return resolved.ok&&resolved.value.kind==="team"?resolved.value.team.id:null};return{access,async teams(){let token=await tokenFor();if(!token)return[];let listed=await listTeams(cloudUrl,token);return listed.ok?publishableTeams(listed.value):[]},async blocked(){let token=await tokenFor();if(!token)return null;let listed=await listTeams(cloudUrl,token);if(!listed.ok)return null;let resolved=resolvePublishTeam(listed.value);return!resolved.ok&&resolved.error.kind==="NoPublishableTeam"?describePublishError(resolved.error):null},async published(){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let listed=await listPublishedDesigns({baseUrl:cloudUrl,token});if(!listed.ok)throw rejection(listed.error);return listed.value.map((design)=>({slug:design.slug,title:design.title??"",url:design.url,visibility:design.visibility,passwordProtected:design.passwordProtected,canManage:design.canManage,lastPublishedAt:design.lastPublishedAt,...design.guestCount!==void 0?{guestCount:design.guestCount}:{},...design.visibility==="private"&&design.teamName&&design.audience?.some((entry)=>entry.type==="team")?{onlyTeam:design.teamName}:{}})).sort((left,right)=>publishedAt(right.lastPublishedAt)-publishedAt(left.lastPublishedAt))},async unpublish(slug){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let removed=await unpublishDesign({baseUrl:cloudUrl,token,slug});if(!removed.ok)throw rejection(removed.error)},guests:{async list(slug){return settled(await listGuests(await guestTarget(slug))).map(canvasGuest)},async invite(slug,guest){return canvasInvite(settled(await inviteGuest(await guestTarget(slug),guest)))},async resend(slug,guestId){return canvasInvite(settled(await resendGuestLink(await guestTarget(slug),guestId)))},async link(slug,guestId){return settled(await mintGuestLink(await guestTarget(slug),guestId))},async remove(slug,guestId){settled(await removeGuest(await guestTarget(slug),guestId))}},async destinations(host){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let provenance=gitContext(host.folder.root),folderId=host.folder.config.folderId;if(!folderId){let teams=await listTeams(cloudUrl,token);if(!teams.ok)throw rejection(teams.error);let publishable=publishableTeams(teams.value);return{effectiveTeamId:publishable.length===1?publishable[0]?.id??null:null,provenance,slots:[]}}let listed=await listPublishDestinations({baseUrl:cloudUrl,token,folderId});if(!listed.ok)throw rejection(listed.error);return{...listed.value,provenance}},async run(host,request,onProgress,onWarning){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud \u2014 sign in from the account menu first.");let teamOnlyId=request.teamOnly?request.teamId??await soleTeamId(token):null;if(request.teamOnly&&!teamOnlyId)throw Error("a team-only link needs a team to publish into \u2014 choose one");let step="start",message="starting",published=await publishDesign({baseUrl:cloudUrl,token},{folder:host.folder,providers:host.providers,defaultProvider:host.defaultProvider,snapshotCss:host.snapshotCss},{boardIds:request.boardIds,...request.title?{title:request.title}:{},visibility:teamOnlyId?"private":request.visibility,...request.password?{password:request.password}:{},destination:request.destination,...request.teamId?{teamId:request.teamId}:{},...teamOnlyId?{audience:[{type:"team",id:teamOnlyId}]}:{},...request.publicComments!==void 0?{publicComments:request.publicComments}:{},provenance:gitContext(host.folder.root),viewport:PUBLISH_VIEWPORT},(event)=>{if(event.kind==="warn"){onWarning(event.message);return}if(event.kind==="note"){onWarning(event.message);return}if(event.kind==="info")return;if(event.kind==="capture"){onProgress({step,message,capture:{done:event.done,total:event.total}});return}step=event.step,message=event.message,onProgress({step,message})});if(!published.ok){let reason=describePublishError(published.error);if(published.error.kind==="LoggedOut")throw signInRequired("expired",reason);let limit=published.error.kind==="HttpFailure"?boardLimitFrom(published.error):null;if(limit)throw boardLimitReached(limit,reason);throw Error(reason)}let outcome=published.value,teamAudience=outcome.audience?.find((entry)=>entry.type==="team");return{shareUrl:outcome.shareUrl,slug:outcome.slug,visibility:outcome.visibility,passwordProtected:outcome.passwordProtected,...teamAudience?{onlyTeam:teamAudience.name||"your team"}:{},files:outcome.files,bytes:outcome.bytes,screenshots:outcome.screenshots,boards:outcome.boards,screens:outcome.screens,created:outcome.created,...outcome.tier!==void 0?{tier:outcome.tier}:{},...outcome.history!==void 0?{history:outcome.history}:{}}}}}function canvasGuest(guest){return{id:guest.id,name:guest.name,...guest.email!==void 0?{email:guest.email}:{},createdAt:guest.createdAt,lastSeenAt:guest.lastSeenAt,linkExpiresAt:guest.linkExpiresAt??null}}function canvasInvite(invite){return{guest:canvasGuest(invite.guest),emailed:invite.delivery.sent,...invite.delivery.reason?{reason:invite.delivery.reason}:{},...invite.guestUrl?{guestUrl:invite.guestUrl}:{}}}function createCanvasUpdates(){return{async status(opts={}){let status=await updateStatus(opts);return{current:status.current,latest:status.latest,available:status.available,method:status.method,channel:status.channel,upgradable:status.upgradable,...status.reason===void 0?{}:{reason:status.reason}}},async upgrade(){let outcome=await upgradeInstalledVelloo();return{upgraded:outcome.upgraded,from:outcome.from,to:outcome.to,restartRequired:outcome.upgraded}}}}var IDLE_MS=300000;function portFree(port,host){try{return Bun.serve({port,hostname:host,fetch:()=>new Response("")}).stop(!0),!0}catch{return!1}}var daemon_default=defineCommand({meta:{name:"__daemon",hidden:!0,description:"(internal) Run the persistent canvas daemon. Spawned by `velloo run` / `velloo mcp`."},args:{folder:{type:"positional",required:!0,description:"Design folder (absolute)"},port:{type:"string",description:"Preferred canvas port"},host:{type:"string",description:"Bind hostname (default 127.0.0.1)"}},async run({args}){let root=daemonRoot(args.folder),host=args.host??"127.0.0.1";assertLoopbackHost(host);let cloudUrl=defaultCloudUrl(),cred=await loadCredential(cloudUrl),cloud={url:cloudUrl,token:cred?.token,resolveToken:async()=>(await loadCredential(cloudUrl))?.token},auth=createCanvasAuth(cloudUrl),publish=createCanvasPublish(cloudUrl,auth),updates=createCanvasUpdates(),canvasPort=portCandidates({explicit:args.port?Number(args.port):void 0,remembered:await readRememberedPort(root),root}).find((port)=>portFree(port,host))??0,handle;try{handle=await createServer({folder:root,port:canvasPort,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}catch(err){if(canvasPort===0)throw err;handle=await createServer({folder:root,port:0,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}let rec={root,pid:process.pid,canvasUrl:handle.url,canvasPort:handle.port,mcpUrl:handle.mcpUrl??"",mcpPort:handle.mcpPort??0,cloudUrl,version:TOOL_VERSION,startedAt:new Date().toISOString()};await writeLock(rec),await registryUpsert(rec),await rememberPort(root,handle.port).catch(()=>{return}),console.error(`velloo: canvas daemon up at ${handle.url} (mcp ${handle.mcpUrl}), pid ${process.pid}`);let stopped=!1,shutdown=async()=>{if(stopped)return;stopped=!0,removeLock(root),await registryRemove(root).catch(()=>{return}),await handle.close(),process.exit(0)};for(let signal of["SIGTERM","SIGINT"])process.on(signal,()=>{console.error(`velloo: received ${signal} \u2014 shutting down.`),shutdown()});let rootMissing=0;setInterval(()=>{if(existsSync2(root)){rootMissing=0;return}if(rootMissing+=1,rootMissing>=2)console.error(`velloo: design folder ${root} is gone \u2014 shutting down.`),shutdown()},1e4);let idleSince=Date.now();setInterval(()=>{let{canvas,mcp}=handle.connections();if(canvas+mcp>0){idleSince=null;return}if(idleSince===null){idleSince=Date.now();return}if(Date.now()-idleSince>=IDLE_MS)console.error("velloo: canvas idle for 5 min \u2014 shutting down."),shutdown()},30000),await new Promise(()=>{})}});export{daemon_default as default};
2
+ import{signInRequired,boardLimitReached,topUpTokens,createServer}from"./chunk-yp39f93e.js";import"./chunk-w40pqmyz.js";import"./chunk-n70zfv0m.js";import{ok,err,GUESTS_UNAVAILABLE,GuestsResponseSchema,GuestInviteResponseSchema,GuestLinkResponseSchema,unreachable,httpFailureFrom,boardLimitFrom,describeCloudError,cloudFetch}from"./chunk-zexzy6zv.js";import{loadCredential,saveCredential,deleteCredential,fetchAccount,revokeCredential,performDeviceLogin}from"./chunk-c11n1tve.js";import{listPublishedDesigns,unpublishDesign,listPublishDestinations,describePublishError,PUBLISH_VIEWPORT,gitContext,publishableTeams,resolvePublishTeam,listTeams,publishDesign}from"./chunk-qn05hn7a.js";import{TOOL_VERSION}from"./chunk-g7pbfad5.js";import{updateStatus,upgradeInstalledVelloo}from"./chunk-fejhav0p.js";import{assertLoopbackHost,daemonRoot,writeLock,removeLock,readRememberedPort,rememberPort,portCandidates,registryUpsert,registryRemove}from"./chunk-f8jchnwe.js";import"./chunk-ck3bg8rf.js";import{defaultCloudUrl,fetchCloudAppUrl}from"./chunk-3f2p94wa.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{existsSync as existsSync2}from"fs";import{existsSync}from"fs";import{dirname,join}from"path";import{fileURLToPath}from"url";var here=dirname(fileURLToPath(import.meta.url));function resolveCanvasDist(){let devLayout=join(here,"..","dist");return[process.env.VELLOO_CANVAS_DIST,join(here,"canvas"),devLayout].filter((p)=>Boolean(p)).find((p)=>existsSync(join(p,"index.html")))??devLayout}var canvasDistPath=resolveCanvasDist();var ACCOUNT_TTL_MS=60000,ACCOUNT_TIMEOUT_MS=5000;function createCanvasAuth(cloudUrl,dependencies={}){let runDeviceLogin=dependencies.performDeviceLogin??performDeviceLogin,login={state:"idle"},pending=null,cached=null,cachedApp=null,describe=async(token,email)=>{if(cached&&cached.token===token&&Date.now()-cached.at<ACCOUNT_TTL_MS)return{account:cached.account,verified:cached.verified};let found=await fetchAccount(cloudUrl,token,ACCOUNT_TIMEOUT_MS),resolved=found.status==="ok"?{account:found.account,verified:!0}:{account:{email},verified:found.status==="rejected"?!1:null};return cached={token,at:Date.now(),...resolved},resolved},resolveAppUrl=async()=>{if(cachedApp&&Date.now()-cachedApp.at<ACCOUNT_TTL_MS)return cachedApp.url;let url=await fetchCloudAppUrl(cloudUrl,ACCOUNT_TIMEOUT_MS);return cachedApp={at:Date.now(),url},url},status=async()=>{let cred=await loadCredential(cloudUrl),appUrlP=resolveAppUrl();if(!cred?.token)return{loggedIn:!1,cloudUrl,appUrl:await appUrlP,verified:null,login};let[{account,verified},appUrl]=await Promise.all([describe(cred.token,cred.email),appUrlP]);return{loggedIn:!0,cloudUrl,appUrl,verified,login,...account?{account}:{}}};return{status,async beginLogin(){if(login.state==="pending")return status();let attempt=new AbortController;pending=attempt,login={state:"idle"};let settle=()=>{},ready=new Promise((resolve)=>{settle=resolve});return runDeviceLogin(cloudUrl,({verificationUrl,userCode,expiresIn})=>{login={state:"pending",userCode,verificationUrl,expiresAt:new Date(Date.now()+expiresIn*1000).toISOString()},settle()},attempt.signal).then(async(result)=>{await saveCredential(cloudUrl,result),cached=null,login={state:"idle"},await topUpTokens({url:cloudUrl,token:result.token},10).catch(()=>{return})}).catch((err)=>{login=attempt.signal.aborted?{state:"idle"}:{state:"error",message:err instanceof Error?err.message:String(err)}}).finally(()=>{if(pending===attempt)pending=null;settle()}),await ready,status()},async cancelLogin(){pending?.abort(),pending=null,login={state:"idle"}},async logout(){pending?.abort(),pending=null,login={state:"idle"},cached=null;let stored=await loadCredential(cloudUrl);if(stored)await revokeCredential(cloudUrl,stored.token);await deleteCredential(cloudUrl)}}}var guestsUrl=({baseUrl,slug},rest="")=>`${baseUrl}/v1/links/${encodeURIComponent(slug)}/guests${rest}`,json=(body)=>({method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(body)});async function listGuests(target){let body=await cloudFetch(guestsUrl(target),GuestsResponseSchema,{operation:"listing guests",token:target.token});return body.ok?ok(body.value.guests):body}function inviteGuest(target,guest){return cloudFetch(guestsUrl(target),GuestInviteResponseSchema,{operation:"inviting a guest",token:target.token,...json({email:guest.email,...guest.name?{name:guest.name}:{}})})}function resendGuestLink(target,guestId){return cloudFetch(guestsUrl(target,`/${encodeURIComponent(guestId)}/resend`),GuestInviteResponseSchema,{operation:"resending a guest's link",token:target.token,...json({})})}async function mintGuestLink(target,guestId){let body=await cloudFetch(guestsUrl(target,`/${encodeURIComponent(guestId)}/link`),GuestLinkResponseSchema,{operation:"making a guest link",token:target.token,...json({})});return body.ok?ok(body.value.guestUrl):body}async function removeGuest(target,guestId){let res=await fetch(guestsUrl(target,`/${encodeURIComponent(guestId)}`),{method:"DELETE",headers:{authorization:`Bearer ${target.token}`}}).catch((error)=>error);if(!(res instanceof Response))return err(unreachable(res,{url:target.baseUrl}));if(!res.ok)return err(await httpFailureFrom("removing a guest",res));return ok(void 0)}function createCanvasPublish(cloudUrl,auth){let tokenFor=async()=>(await loadCredential(cloudUrl))?.token,access=async()=>{if(!await tokenFor())return{state:"signed-out"};return(await auth.status().catch(()=>null))?.verified===!1?{state:"expired"}:{state:"ready"}},rejection=(error)=>error.kind==="LoggedOut"?signInRequired("expired",describeCloudError(error)):Error(describeCloudError(error)),guestRejection=(error)=>error.kind==="HttpFailure"&&error.status===403&&/\bguests requires\b/.test(error.detail)?Error(GUESTS_UNAVAILABLE):rejection(error),guestTarget=async(slug)=>{let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");return{baseUrl:cloudUrl,token,slug}},settled=(result)=>{if(!result.ok)throw guestRejection(result.error);return result.value},publishedAt=(value)=>{let parsed=value?Date.parse(value):Number.NaN;return Number.isFinite(parsed)?parsed:Number.NEGATIVE_INFINITY},soleTeamId=async(token)=>{let listed=await listTeams(cloudUrl,token);if(!listed.ok)return null;let resolved=resolvePublishTeam(listed.value);return resolved.ok&&resolved.value.kind==="team"?resolved.value.team.id:null};return{access,async teams(){let token=await tokenFor();if(!token)return[];let listed=await listTeams(cloudUrl,token);return listed.ok?publishableTeams(listed.value):[]},async blocked(){let token=await tokenFor();if(!token)return null;let listed=await listTeams(cloudUrl,token);if(!listed.ok)return null;let resolved=resolvePublishTeam(listed.value);return!resolved.ok&&resolved.error.kind==="NoPublishableTeam"?describePublishError(resolved.error):null},async published(){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let listed=await listPublishedDesigns({baseUrl:cloudUrl,token});if(!listed.ok)throw rejection(listed.error);return listed.value.map((design)=>({slug:design.slug,title:design.title??"",url:design.url,visibility:design.visibility,passwordProtected:design.passwordProtected,canManage:design.canManage,lastPublishedAt:design.lastPublishedAt,...design.guestCount!==void 0?{guestCount:design.guestCount}:{},...design.visibility==="private"&&design.teamName&&design.audience?.some((entry)=>entry.type==="team")?{onlyTeam:design.teamName}:{}})).sort((left,right)=>publishedAt(right.lastPublishedAt)-publishedAt(left.lastPublishedAt))},async unpublish(slug){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let removed=await unpublishDesign({baseUrl:cloudUrl,token,slug});if(!removed.ok)throw rejection(removed.error)},guests:{async list(slug){return settled(await listGuests(await guestTarget(slug))).map(canvasGuest)},async invite(slug,guest){return canvasInvite(settled(await inviteGuest(await guestTarget(slug),guest)))},async resend(slug,guestId){return canvasInvite(settled(await resendGuestLink(await guestTarget(slug),guestId)))},async link(slug,guestId){return settled(await mintGuestLink(await guestTarget(slug),guestId))},async remove(slug,guestId){settled(await removeGuest(await guestTarget(slug),guestId))}},async destinations(host){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let provenance=gitContext(host.folder.root),folderId=host.folder.config.folderId;if(!folderId){let teams=await listTeams(cloudUrl,token);if(!teams.ok)throw rejection(teams.error);let publishable=publishableTeams(teams.value);return{effectiveTeamId:publishable.length===1?publishable[0]?.id??null:null,provenance,slots:[]}}let listed=await listPublishDestinations({baseUrl:cloudUrl,token,folderId});if(!listed.ok)throw rejection(listed.error);return{...listed.value,provenance}},async run(host,request,onProgress,onWarning){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud \u2014 sign in from the account menu first.");let teamOnlyId=request.teamOnly?request.teamId??await soleTeamId(token):null;if(request.teamOnly&&!teamOnlyId)throw Error("a team-only link needs a team to publish into \u2014 choose one");let step="start",message="starting",published=await publishDesign({baseUrl:cloudUrl,token},{folder:host.folder,providers:host.providers,defaultProvider:host.defaultProvider,snapshotCss:host.snapshotCss},{boardIds:request.boardIds,...request.title?{title:request.title}:{},visibility:teamOnlyId?"private":request.visibility,...request.password?{password:request.password}:{},destination:request.destination,...request.teamId?{teamId:request.teamId}:{},...teamOnlyId?{audience:[{type:"team",id:teamOnlyId}]}:{},...request.publicComments!==void 0?{publicComments:request.publicComments}:{},provenance:gitContext(host.folder.root),viewport:PUBLISH_VIEWPORT},(event)=>{if(event.kind==="warn"){onWarning(event.message);return}if(event.kind==="note"){onWarning(event.message);return}if(event.kind==="info")return;if(event.kind==="capture"){onProgress({step,message,capture:{done:event.done,total:event.total}});return}step=event.step,message=event.message,onProgress({step,message})});if(!published.ok){let reason=describePublishError(published.error);if(published.error.kind==="LoggedOut")throw signInRequired("expired",reason);let limit=published.error.kind==="HttpFailure"?boardLimitFrom(published.error):null;if(limit)throw boardLimitReached(limit,reason);throw Error(reason)}let outcome=published.value,teamAudience=outcome.audience?.find((entry)=>entry.type==="team");return{shareUrl:outcome.shareUrl,slug:outcome.slug,visibility:outcome.visibility,passwordProtected:outcome.passwordProtected,...teamAudience?{onlyTeam:teamAudience.name||"your team"}:{},files:outcome.files,bytes:outcome.bytes,screenshots:outcome.screenshots,boards:outcome.boards,screens:outcome.screens,created:outcome.created,...outcome.tier!==void 0?{tier:outcome.tier}:{},...outcome.history!==void 0?{history:outcome.history}:{}}}}}function canvasGuest(guest){return{id:guest.id,name:guest.name,...guest.email!==void 0?{email:guest.email}:{},createdAt:guest.createdAt,lastSeenAt:guest.lastSeenAt,linkExpiresAt:guest.linkExpiresAt??null}}function canvasInvite(invite){return{guest:canvasGuest(invite.guest),emailed:invite.delivery.sent,...invite.delivery.reason?{reason:invite.delivery.reason}:{},...invite.guestUrl?{guestUrl:invite.guestUrl}:{}}}function createCanvasUpdates(){return{async status(opts={}){let status=await updateStatus(opts);return{current:status.current,latest:status.latest,available:status.available,method:status.method,channel:status.channel,upgradable:status.upgradable,...status.reason===void 0?{}:{reason:status.reason}}},async upgrade(){let outcome=await upgradeInstalledVelloo();return{upgraded:outcome.upgraded,from:outcome.from,to:outcome.to,restartRequired:outcome.upgraded}}}}var IDLE_MS=300000;function portFree(port,host){try{return Bun.serve({port,hostname:host,fetch:()=>new Response("")}).stop(!0),!0}catch{return!1}}var daemon_default=defineCommand({meta:{name:"__daemon",hidden:!0,description:"(internal) Run the persistent canvas daemon. Spawned by `velloo run` / `velloo mcp`."},args:{folder:{type:"positional",required:!0,description:"Design folder (absolute)"},port:{type:"string",description:"Preferred canvas port"},host:{type:"string",description:"Bind hostname (default 127.0.0.1)"}},async run({args}){let root=daemonRoot(args.folder),host=args.host??"127.0.0.1";assertLoopbackHost(host);let cloudUrl=defaultCloudUrl(),cred=await loadCredential(cloudUrl),cloud={url:cloudUrl,token:cred?.token,resolveToken:async()=>(await loadCredential(cloudUrl))?.token},auth=createCanvasAuth(cloudUrl),publish=createCanvasPublish(cloudUrl,auth),updates=createCanvasUpdates(),canvasPort=portCandidates({explicit:args.port?Number(args.port):void 0,remembered:await readRememberedPort(root),root}).find((port)=>portFree(port,host))??0,handle;try{handle=await createServer({folder:root,port:canvasPort,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}catch(err){if(canvasPort===0)throw err;handle=await createServer({folder:root,port:0,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}let rec={root,pid:process.pid,canvasUrl:handle.url,canvasPort:handle.port,mcpUrl:handle.mcpUrl??"",mcpPort:handle.mcpPort??0,cloudUrl,version:TOOL_VERSION,startedAt:new Date().toISOString()};await writeLock(rec),await registryUpsert(rec),await rememberPort(root,handle.port).catch(()=>{return}),console.error(`velloo: canvas daemon up at ${handle.url} (mcp ${handle.mcpUrl}), pid ${process.pid}`);let stopped=!1,shutdown=async()=>{if(stopped)return;stopped=!0,removeLock(root),await registryRemove(root).catch(()=>{return}),await handle.close(),process.exit(0)};for(let signal of["SIGTERM","SIGINT"])process.on(signal,()=>{console.error(`velloo: received ${signal} \u2014 shutting down.`),shutdown()});let rootMissing=0;setInterval(()=>{if(existsSync2(root)){rootMissing=0;return}if(rootMissing+=1,rootMissing>=2)console.error(`velloo: design folder ${root} is gone \u2014 shutting down.`),shutdown()},1e4);let idleSince=Date.now();setInterval(()=>{let{canvas,mcp}=handle.connections();if(canvas+mcp>0){idleSince=null;return}if(idleSince===null){idleSince=Date.now();return}if(Date.now()-idleSince>=IDLE_MS)console.error("velloo: canvas idle for 5 min \u2014 shutting down."),shutdown()},30000),await new Promise(()=>{})}});export{daemon_default as default};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{string,number,object,CURRENT_SCHEMA_VERSION,schemaVersionOf}from"./chunk-5x4nw0zz.js";import{writeJsonAtomic}from"./chunk-brnkprt5.js";import{TOOL_VERSION}from"./chunk-3m28pvzr.js";import{defaultCloudUrl}from"./chunk-abr2hgay.js";import{selfCommand}from"./chunk-skk6yvnw.js";function isLoopbackHost(host){let normalized=host.trim().toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"");if(normalized==="localhost"||normalized==="::1")return!0;let octets=normalized.split(".");return octets.length===4&&octets[0]==="127"&&octets.every((part)=>/^\d+$/.test(part))}function assertLoopbackHost(host){if(isLoopbackHost(host))return;throw Error(`refusing to bind ${JSON.stringify(host)}: the canvas and MCP endpoints have no authentication and only serve loopback (use 127.0.0.1, localhost or ::1)`)}import{closeSync,existsSync,mkdirSync,openSync,readFileSync,realpathSync,statSync,unlinkSync,writeSync}from"fs";import{mkdir,readFile}from"fs/promises";import{homedir}from"os";import{dirname,join,resolve}from"path";function spawnOutliving(cmd,output){let proc=Bun.spawn(cmd,{stdin:"ignore",stdout:output,stderr:output,detached:!0,windowsHide:!0});return proc.unref(),proc}var DaemonRecordSchema=object({root:string().min(1),pid:number().int().positive(),canvasUrl:string().min(1),canvasPort:number().int().positive(),mcpUrl:string().min(1),mcpPort:number().int().positive(),cloudUrl:string().min(1),version:string().min(1),startedAt:string().min(1)});function daemonMatchesRuntime(rec,cloudUrl=defaultCloudUrl()){return rec.version===TOOL_VERSION&&rec.cloudUrl===cloudUrl}var MUTEX_STALE_MS=15000,HEALTHY_TIMEOUT_MS=15000,cacheDir=(root)=>join(root,".design","cache"),lockPath=(root)=>join(cacheDir(root),"runtime.json"),mutexPath=(root)=>join(cacheDir(root),"runtime.lock"),logPath=(root)=>join(cacheDir(root),"daemon.log"),portMemoPath=(root)=>join(cacheDir(root),"port.json"),registryPath=()=>process.env.VELLOO_DAEMONS_PATH??join(homedir(),".velloo","daemons.json");function daemonRoot(folder){let abs=resolve(folder);return existsSync(abs)?realpathSync(abs):abs}class DesignFolderFormatError extends Error{root;found;current;constructor(message,root,found,current){super(message);this.root=root;this.found=found;this.current=current;this.name="DesignFolderFormatError"}}function assertFolderFormatCurrent(root){let raw;try{raw=JSON.parse(readFileSync(join(root,".design","config.json"),"utf8"))}catch{return}let found=schemaVersionOf(raw);if(found===CURRENT_SCHEMA_VERSION)return;let message=found>CURRENT_SCHEMA_VERSION?`velloo: ${root} uses design-folder schema version ${found}, but this velloo only knows version ${CURRENT_SCHEMA_VERSION}. Upgrade velloo to open it.`:`velloo: ${root} uses design-folder schema version ${found} (current: ${CURRENT_SCHEMA_VERSION}). Run \`velloo upgrade ${root}\` to migrate it.`;throw new DesignFolderFormatError(message,root,found,CURRENT_SCHEMA_VERSION)}async function readLock(root){try{let parsed=DaemonRecordSchema.safeParse(JSON.parse(await readFile(lockPath(root),"utf8")));return parsed.success?parsed.data:null}catch{return null}}async function writeLock(rec){await writeJsonAtomic(lockPath(rec.root),rec)}function removeLock(root){try{unlinkSync(lockPath(root))}catch{}}var DEFAULT_CANVAS_PORT=7300,DERIVED_BAND={start:7310,size:90},PortMemoSchema=object({canvasPort:number().int().min(1).max(65535)});async function readRememberedPort(root){try{let parsed=PortMemoSchema.safeParse(JSON.parse(await readFile(portMemoPath(root),"utf8")));return parsed.success?parsed.data.canvasPort:null}catch{return null}}async function rememberPort(root,canvasPort){await writeJsonAtomic(portMemoPath(root),{canvasPort})}function derivedPort(root){let digest=Bun.CryptoHasher.hash("sha256",root,"hex");return DERIVED_BAND.start+Number.parseInt(digest.slice(0,8),16)%DERIVED_BAND.size}function portCandidates(opts){if(opts.explicit!==void 0)return[opts.explicit];let wanted=[opts.remembered,DEFAULT_CANVAS_PORT,derivedPort(opts.root)];return[...new Set(wanted.filter((p)=>typeof p==="number"))]}var HealthSchema=object({app:string().optional(),root:string().optional()});async function isLive(rec){try{let res=await fetch(`http://127.0.0.1:${rec.canvasPort}/api/health`,{signal:AbortSignal.timeout(1000)});if(!res.ok)return!1;let body=await res.json(),health=HealthSchema.safeParse(body);return health.success&&health.data.app==="velloo"&&health.data.root===rec.root}catch{return!1}}async function registryRead(){try{let raw=JSON.parse(await readFile(registryPath(),"utf8"));if(!Array.isArray(raw))return[];return raw.flatMap((entry)=>{let parsed=DaemonRecordSchema.safeParse(entry);return parsed.success?[parsed.data]:[]})}catch{return[]}}async function registryWrite(records){let p=registryPath();await mkdir(dirname(p),{recursive:!0}),await writeJsonAtomic(p,records)}async function registryUpsert(rec){let others=(await registryRead()).filter((r)=>r.root!==rec.root);await registryWrite([...others,rec])}async function registryRemove(root){await registryWrite((await registryRead()).filter((r)=>r.root!==root))}async function listDaemons(){let all=await registryRead(),live=[];for(let rec of all)if(await isLive(rec))live.push(rec);if(live.length!==all.length)await registryWrite(live);return live}function daemonSpawnCmd(root,preferredPort,host){let args=["__daemon",root,"--host",host];if(preferredPort!==void 0)args.push("--port",String(preferredPort));return selfCommand(args)}function spawnDetached(root,preferredPort,host){mkdirSync(cacheDir(root),{recursive:!0});let logStart=0;try{logStart=statSync(logPath(root)).size}catch{}let log=openSync(logPath(root),"a");return{exited:spawnOutliving(daemonSpawnCmd(root,preferredPort,host),log).exited,logStart}}function claimMutex(root){let path=mutexPath(root);try{let fd=openSync(path,"wx");return writeSync(fd,String(process.pid)),closeSync(fd),!0}catch{try{if(Date.now()-statSync(path).mtimeMs>MUTEX_STALE_MS)return unlinkSync(path),claimMutex(root)}catch{}return!1}}function releaseMutex(root){try{unlinkSync(mutexPath(root))}catch{}}function startupFailure(root,logStart){let appended="";try{appended=readFileSync(logPath(root),"utf8").slice(logStart)}catch{}let lines=appended.split(`
2
+ import{string,number,object,CURRENT_SCHEMA_VERSION,schemaVersionOf}from"./chunk-w40pqmyz.js";import{writeJsonAtomic}from"./chunk-yp39f93e.js";import{TOOL_VERSION}from"./chunk-g7pbfad5.js";import{defaultCloudUrl}from"./chunk-3f2p94wa.js";import{selfCommand}from"./chunk-skk6yvnw.js";import{closeSync,existsSync,mkdirSync,openSync,readFileSync,realpathSync,statSync,unlinkSync,writeSync}from"fs";import{mkdir,readFile}from"fs/promises";import{homedir}from"os";import{dirname,join,resolve}from"path";function isLoopbackHost(host){let normalized=host.trim().toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"");if(normalized==="localhost"||normalized==="::1")return!0;let octets=normalized.split(".");return octets.length===4&&octets[0]==="127"&&octets.every((part)=>/^\d+$/.test(part))}function assertLoopbackHost(host){if(isLoopbackHost(host))return;throw Error(`refusing to bind ${JSON.stringify(host)}: the canvas and MCP endpoints have no authentication and only serve loopback (use 127.0.0.1, localhost or ::1)`)}function spawnOutliving(cmd,output){let proc=Bun.spawn(cmd,{stdin:"ignore",stdout:output,stderr:output,detached:!0,windowsHide:!0});return proc.unref(),proc}var DaemonRecordSchema=object({root:string().min(1),pid:number().int().positive(),canvasUrl:string().min(1),canvasPort:number().int().positive(),mcpUrl:string().min(1),mcpPort:number().int().positive(),cloudUrl:string().min(1),version:string().min(1),startedAt:string().min(1)});function daemonMatchesRuntime(rec,cloudUrl=defaultCloudUrl()){return rec.version===TOOL_VERSION&&rec.cloudUrl===cloudUrl}var MUTEX_STALE_MS=15000,HEALTHY_TIMEOUT_MS=15000,cacheDir=(root)=>join(root,".design","cache"),lockPath=(root)=>join(cacheDir(root),"runtime.json"),mutexPath=(root)=>join(cacheDir(root),"runtime.lock"),logPath=(root)=>join(cacheDir(root),"daemon.log"),portMemoPath=(root)=>join(cacheDir(root),"port.json"),registryPath=()=>process.env.VELLOO_DAEMONS_PATH??join(homedir(),".velloo","daemons.json");function daemonRoot(folder){let abs=resolve(folder);return existsSync(abs)?realpathSync(abs):abs}class DesignFolderFormatError extends Error{root;found;current;constructor(message,root,found,current){super(message);this.root=root;this.found=found;this.current=current;this.name="DesignFolderFormatError"}}function assertFolderFormatCurrent(root){let raw;try{raw=JSON.parse(readFileSync(join(root,".design","config.json"),"utf8"))}catch{return}let found=schemaVersionOf(raw);if(found===CURRENT_SCHEMA_VERSION)return;let message=found>CURRENT_SCHEMA_VERSION?`velloo: ${root} uses design-folder schema version ${found}, but this velloo only knows version ${CURRENT_SCHEMA_VERSION}. Upgrade velloo to open it.`:`velloo: ${root} uses design-folder schema version ${found} (current: ${CURRENT_SCHEMA_VERSION}). Run \`velloo upgrade ${root}\` to migrate it.`;throw new DesignFolderFormatError(message,root,found,CURRENT_SCHEMA_VERSION)}async function readLock(root){try{let parsed=DaemonRecordSchema.safeParse(JSON.parse(await readFile(lockPath(root),"utf8")));return parsed.success?parsed.data:null}catch{return null}}async function writeLock(rec){await writeJsonAtomic(lockPath(rec.root),rec)}function removeLock(root){try{unlinkSync(lockPath(root))}catch{}}var DEFAULT_CANVAS_PORT=7300,DERIVED_BAND={start:7310,size:90},PortMemoSchema=object({canvasPort:number().int().min(1).max(65535)});async function readRememberedPort(root){try{let parsed=PortMemoSchema.safeParse(JSON.parse(await readFile(portMemoPath(root),"utf8")));return parsed.success?parsed.data.canvasPort:null}catch{return null}}async function rememberPort(root,canvasPort){await writeJsonAtomic(portMemoPath(root),{canvasPort})}function derivedPort(root){let digest=Bun.CryptoHasher.hash("sha256",root,"hex");return DERIVED_BAND.start+Number.parseInt(digest.slice(0,8),16)%DERIVED_BAND.size}function portCandidates(opts){if(opts.explicit!==void 0)return[opts.explicit];let wanted=[opts.remembered,DEFAULT_CANVAS_PORT,derivedPort(opts.root)];return[...new Set(wanted.filter((p)=>typeof p==="number"))]}var HealthSchema=object({app:string().optional(),root:string().optional()});async function isLive(rec){try{let res=await fetch(`http://127.0.0.1:${rec.canvasPort}/api/health`,{signal:AbortSignal.timeout(1000)});if(!res.ok)return!1;let body=await res.json(),health=HealthSchema.safeParse(body);return health.success&&health.data.app==="velloo"&&health.data.root===rec.root}catch{return!1}}async function registryRead(){try{let raw=JSON.parse(await readFile(registryPath(),"utf8"));if(!Array.isArray(raw))return[];return raw.flatMap((entry)=>{let parsed=DaemonRecordSchema.safeParse(entry);return parsed.success?[parsed.data]:[]})}catch{return[]}}async function registryWrite(records){let p=registryPath();await mkdir(dirname(p),{recursive:!0}),await writeJsonAtomic(p,records)}async function registryUpsert(rec){let others=(await registryRead()).filter((r)=>r.root!==rec.root);await registryWrite([...others,rec])}async function registryRemove(root){await registryWrite((await registryRead()).filter((r)=>r.root!==root))}async function listDaemons(){let all=await registryRead(),live=[];for(let rec of all)if(await isLive(rec))live.push(rec);if(live.length!==all.length)await registryWrite(live);return live}function daemonSpawnCmd(root,preferredPort,host){let args=["__daemon",root,"--host",host];if(preferredPort!==void 0)args.push("--port",String(preferredPort));return selfCommand(args)}function spawnDetached(root,preferredPort,host){mkdirSync(cacheDir(root),{recursive:!0});let logStart=0;try{logStart=statSync(logPath(root)).size}catch{}let log=openSync(logPath(root),"a");return{exited:spawnOutliving(daemonSpawnCmd(root,preferredPort,host),log).exited,logStart}}function claimMutex(root){let path=mutexPath(root);try{let fd=openSync(path,"wx");return writeSync(fd,String(process.pid)),closeSync(fd),!0}catch{try{if(Date.now()-statSync(path).mtimeMs>MUTEX_STALE_MS)return unlinkSync(path),claimMutex(root)}catch{}return!1}}function releaseMutex(root){try{unlinkSync(mutexPath(root))}catch{}}function startupFailure(root,logStart){let appended="";try{appended=readFileSync(logPath(root),"utf8").slice(logStart)}catch{}let lines=appended.split(`
3
3
  `).map((l)=>l.replace(/^velloo __daemon: /,"").trimEnd()).filter((l)=>l.trim().length>0).slice(-8);if(lines.length===0)return`velloo: the canvas daemon for ${root} exited during startup with no output \u2014 see ${logPath(root)}`;return`velloo: the canvas daemon for ${root} exited during startup:
4
4
  ${lines.join(`
5
5
  `)}`}async function waitForHealthy(root,spawned){let deadline=Date.now()+HEALTHY_TIMEOUT_MS,died=!1;if(spawned)spawned.exited.then(()=>died=!0);while(Date.now()<deadline){let rec=await readLock(root);if(rec&&await isLive(rec))return rec;if(died)throw Error(startupFailure(root,spawned?.logStart??0));await new Promise((r)=>setTimeout(r,150))}throw Error(`velloo: canvas daemon for ${root} didn't come up \u2014 see ${logPath(root)}`)}async function ensureDaemon(folder,opts={}){let root=daemonRoot(folder);assertFolderFormatCurrent(root);let host=opts.host??"127.0.0.1";assertLoopbackHost(host),mkdirSync(cacheDir(root),{recursive:!0});let deadline=Date.now()+HEALTHY_TIMEOUT_MS+5000;while(Date.now()<deadline){let existing=await readLock(root);if(existing&&await isLive(existing)){if(daemonMatchesRuntime(existing))return existing;await stopDaemon(root)}if(claimMutex(root))try{let again=await readLock(root);if(again&&await isLive(again)){if(daemonMatchesRuntime(again))return again;await stopDaemon(root)}let spawned=spawnDetached(root,opts.preferredPort,host);return opts.onSpawn?.(),await waitForHealthy(root,spawned)}finally{releaseMutex(root)}await new Promise((r)=>setTimeout(r,200))}throw Error(`velloo: timed out ensuring a canvas daemon for ${root}`)}async function stopDaemon(root){let rec=await readLock(root);if(!rec)return!1;try{process.kill(rec.pid,"SIGTERM")}catch{}for(let i=0;i<30;i++){if(!await isLive(rec))break;await new Promise((r)=>setTimeout(r,100))}return removeLock(root),await registryRemove(root),!0}
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{PACKAGE_VERSION,TOOL_VERSION,BUILD_TIME_MS}from"./chunk-3m28pvzr.js";import{selfCommand}from"./chunk-skk6yvnw.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM}from"./chunk-2smsg2ct.js";import{existsSync,realpathSync}from"fs";import{readFile}from"fs/promises";import{homedir}from"os";import{dirname,join}from"path";var DEFAULT_DOWNLOAD_BASE={stable:"https://get.velloo.design",dev:"https://get.dev.velloo.design"},NPM_LATEST_URL="https://registry.npmjs.org/velloo/latest";function localReleasePath(){return process.env.VELLOO_LOCAL_RELEASE??join(homedir(),".velloo","local-release.json")}async function readLocalRelease(){try{let raw=JSON.parse(await readFile(localReleasePath(),"utf8"));if(typeof raw!=="object"||raw===null)return null;let value=raw;if(typeof value.builtAt!=="number"||typeof value.tarball!=="string")return null;return{version:typeof value.version==="string"?value.version:PACKAGE_VERSION,build:typeof value.build==="string"?value.build:value.version??PACKAGE_VERSION,builtAt:value.builtAt,tarball:value.tarball,repo:typeof value.repo==="string"?value.repo:dirname(value.tarball)}}catch{return null}}function looksLikeHomebrew(env){let entry=env.VELLOO_LAUNCHER_PATH??Bun.main;if(!entry)return!1;try{return/[/\\]Cellar[/\\]velloo[/\\]/.test(realpathSync(entry))}catch{return/[/\\]Cellar[/\\]velloo[/\\]/.test(entry)}}var EXPLICIT=["npm","direct","homebrew","local","source"];function installMethod(env=process.env){let declared=env.VELLOO_INSTALL_METHOD;if(declared&&EXPLICIT.includes(declared)){if(declared==="npm"&&looksLikeHomebrew(env))return"homebrew";return declared}if(looksLikeHomebrew(env))return"homebrew";return"unknown"}function selfUpgradable(method){return method==="npm"||method==="direct"||method==="homebrew"||method==="local"}function releaseChannel(env=process.env){let declared=env.VELLOO_RELEASE_CHANNEL??bakedChannel();return declared==="dev"||declared==="local"?declared:"stable"}function bakedChannel(){return"stable"}function downloadBase(env=process.env){let explicit=env.VELLOO_DOWNLOAD_BASE;if(explicit)return explicit.replace(/\/+$/,"");let channel=releaseChannel(env);return DEFAULT_DOWNLOAD_BASE[channel==="local"?"stable":channel]}function installerUrl(env=process.env){return env.VELLOO_INSTALLER_URL??`${downloadBase(env)}/install.sh`}function latestVersionUrl(env=process.env){if(env.VELLOO_UPDATE_URL)return env.VELLOO_UPDATE_URL;if(releaseChannel(env)==="stable"&&installMethod(env)==="npm")return NPM_LATEST_URL;return`${downloadBase(env)}/latest.json`}function runningRelease(){return{version:PACKAGE_VERSION,build:TOOL_VERSION,...BUILD_TIME_MS===void 0?{}:{builtAt:BUILD_TIME_MS}}}function compareVersions(left,right){let parse=(value)=>{let match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);if(!match)return null;return{core:[Number(match[1]),Number(match[2]),Number(match[3])],prerelease:match[4]??null}},a=parse(left),b=parse(right);if(!a||!b)return 0;for(let index=0;index<3;index++){let delta=(a.core[index]??0)-(b.core[index]??0);if(delta!==0)return Math.sign(delta)}if(a.prerelease===b.prerelease)return 0;if(a.prerelease===null)return 1;if(b.prerelease===null)return-1;return a.prerelease.localeCompare(b.prerelease,void 0,{numeric:!0})}function isNewerRelease(candidate,running=runningRelease()){let byVersion=compareVersions(candidate.version,running.version);if(byVersion!==0)return byVersion>0;if(typeof candidate.builtAt!=="number")return!1;if(typeof running.builtAt!=="number")return!0;return candidate.builtAt>running.builtAt}function releaseLabel(info){return info.build??info.version}var FETCH_TIMEOUT_MS=2000;async function fetchLatestRelease(env=process.env){if(releaseChannel(env)==="local"&&!env.VELLOO_UPDATE_URL){let local=await readLocalRelease();if(!local)return null;return{version:local.version,build:local.build,builtAt:local.builtAt}}let response=await fetch(latestVersionUrl(env),{headers:{accept:"application/json","user-agent":`velloo/${PACKAGE_VERSION}`},signal:AbortSignal.timeout(FETCH_TIMEOUT_MS)});if(!response.ok)throw Error(`release check returned HTTP ${response.status}`);let body=await response.json();if(typeof body.version!=="string"||!/^\d+\.\d+\.\d+/.test(body.version))throw Error("release check returned an invalid version");return{version:body.version,...typeof body.build==="string"?{build:body.build}:{},...typeof body.builtAt==="number"?{builtAt:body.builtAt}:{}}}function installedVellooBin(env=process.env){let candidates=[],method=installMethod(env);if(method==="direct")candidates.push(join(env.VELLOO_BIN_DIR??join(homedir(),".local","bin"),"velloo"));if(method==="homebrew"){let prefix=env.HOMEBREW_PREFIX??(process.platform==="darwin"?"/opt/homebrew":null);if(prefix)candidates.push(join(prefix,"bin","velloo"));candidates.push("/usr/local/bin/velloo")}if(method==="npm"||method==="local"){let node=env.VELLOO_NODE_EXECUTABLE;if(node)candidates.push(join(dirname(node),"velloo"));let prefix=npmGlobalPrefix(env);if(prefix)candidates.push(process.platform==="win32"?join(prefix,"velloo.cmd"):join(prefix,"bin","velloo"))}return candidates.find((path)=>existsSync(path))??null}function npmGlobalPrefix(env){try{let result=Bun.spawnSync([npmExecutable(env),"prefix","-g"],{env}),out=result.stdout.toString().trim();return result.success&&out?out:null}catch{return null}}function npmExecutable(env=process.env){let node=env.VELLOO_NODE_EXECUTABLE;if(node){let adjacent=join(dirname(node),process.platform==="win32"?"npm.cmd":"npm");if(existsSync(adjacent))return adjacent}return process.platform==="win32"?"npm.cmd":"npm"}var import_picocolors=__toESM(require_picocolors(),1);import{existsSync as existsSync2}from"fs";import{mkdir,mkdtemp,readFile as readFile2,rename,rm,writeFile}from"fs/promises";import{homedir as homedir2,tmpdir}from"os";import{dirname as dirname2,join as join2}from"path";var CHECK_INTERVAL_MS=86400000,LOCAL_CHECK_INTERVAL_MS=1e4;function releaseSource(){return releaseChannel()==="local"&&!process.env.VELLOO_UPDATE_URL?`file:${localReleasePath()}`:latestVersionUrl()}function cachePath(){return process.env.VELLOO_UPDATE_CACHE??join2(homedir2(),".velloo","update-check.json")}async function readCacheFile(){try{let value=JSON.parse(await readFile2(cachePath(),"utf8"));if(typeof value!=="object"||value===null)return{};return Object.fromEntries(Object.entries(value).filter(([,entry])=>typeof entry==="object"&&entry!==null))}catch{return{}}}async function readCache(){let entry=(await readCacheFile())[releaseSource()];if(typeof entry?.checkedAt!=="number"||typeof entry.latest!=="string")return null;return entry}async function writeCache(value){let path=cachePath(),file={...await readCacheFile(),[releaseSource()]:value};await mkdir(dirname2(path),{recursive:!0});let temporary=`${path}.${process.pid}.tmp`;await writeFile(temporary,`${JSON.stringify(file,null,2)}
2
+ import{PACKAGE_VERSION,TOOL_VERSION,BUILD_TIME_MS}from"./chunk-g7pbfad5.js";import{selfCommand}from"./chunk-skk6yvnw.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM}from"./chunk-2smsg2ct.js";var import_picocolors=__toESM(require_picocolors(),1);import{existsSync as existsSync2}from"fs";import{mkdir,mkdtemp,readFile as readFile2,rename,rm,writeFile}from"fs/promises";import{homedir as homedir2,tmpdir}from"os";import{dirname as dirname2,join as join2}from"path";import{existsSync,realpathSync}from"fs";import{readFile}from"fs/promises";import{homedir}from"os";import{dirname,join}from"path";var DEFAULT_DOWNLOAD_BASE={stable:"https://get.velloo.design",dev:"https://get.dev.velloo.design"},NPM_LATEST_URL="https://registry.npmjs.org/velloo/latest";function localReleasePath(){return process.env.VELLOO_LOCAL_RELEASE??join(homedir(),".velloo","local-release.json")}async function readLocalRelease(){try{let raw=JSON.parse(await readFile(localReleasePath(),"utf8"));if(typeof raw!=="object"||raw===null)return null;let value=raw;if(typeof value.builtAt!=="number"||typeof value.tarball!=="string")return null;return{version:typeof value.version==="string"?value.version:PACKAGE_VERSION,build:typeof value.build==="string"?value.build:value.version??PACKAGE_VERSION,builtAt:value.builtAt,tarball:value.tarball,repo:typeof value.repo==="string"?value.repo:dirname(value.tarball)}}catch{return null}}function looksLikeHomebrew(env){let entry=env.VELLOO_LAUNCHER_PATH??Bun.main;if(!entry)return!1;try{return/[/\\]Cellar[/\\]velloo[/\\]/.test(realpathSync(entry))}catch{return/[/\\]Cellar[/\\]velloo[/\\]/.test(entry)}}var EXPLICIT=["npm","direct","homebrew","local","source"];function installMethod(env=process.env){let declared=env.VELLOO_INSTALL_METHOD;if(declared&&EXPLICIT.includes(declared)){if(declared==="npm"&&looksLikeHomebrew(env))return"homebrew";return declared}if(looksLikeHomebrew(env))return"homebrew";return"unknown"}function selfUpgradable(method){return method==="npm"||method==="direct"||method==="homebrew"||method==="local"}function releaseChannel(env=process.env){let declared=env.VELLOO_RELEASE_CHANNEL??bakedChannel();return declared==="dev"||declared==="local"?declared:"stable"}function bakedChannel(){return"stable"}function downloadBase(env=process.env){let explicit=env.VELLOO_DOWNLOAD_BASE;if(explicit)return explicit.replace(/\/+$/,"");let channel=releaseChannel(env);return DEFAULT_DOWNLOAD_BASE[channel==="local"?"stable":channel]}function installerUrl(env=process.env){return env.VELLOO_INSTALLER_URL??`${downloadBase(env)}/install.sh`}function latestVersionUrl(env=process.env){if(env.VELLOO_UPDATE_URL)return env.VELLOO_UPDATE_URL;if(releaseChannel(env)==="stable"&&installMethod(env)==="npm")return NPM_LATEST_URL;return`${downloadBase(env)}/latest.json`}function runningRelease(){return{version:PACKAGE_VERSION,build:TOOL_VERSION,...BUILD_TIME_MS===void 0?{}:{builtAt:BUILD_TIME_MS}}}function compareVersions(left,right){let parse=(value)=>{let match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);if(!match)return null;return{core:[Number(match[1]),Number(match[2]),Number(match[3])],prerelease:match[4]??null}},a=parse(left),b=parse(right);if(!a||!b)return 0;for(let index=0;index<3;index++){let delta=(a.core[index]??0)-(b.core[index]??0);if(delta!==0)return Math.sign(delta)}if(a.prerelease===b.prerelease)return 0;if(a.prerelease===null)return 1;if(b.prerelease===null)return-1;return a.prerelease.localeCompare(b.prerelease,void 0,{numeric:!0})}function isNewerRelease(candidate,running=runningRelease()){let byVersion=compareVersions(candidate.version,running.version);if(byVersion!==0)return byVersion>0;if(typeof candidate.builtAt!=="number")return!1;if(typeof running.builtAt!=="number")return!0;return candidate.builtAt>running.builtAt}function releaseLabel(info){return info.build??info.version}var FETCH_TIMEOUT_MS=2000;async function fetchLatestRelease(env=process.env){if(releaseChannel(env)==="local"&&!env.VELLOO_UPDATE_URL){let local=await readLocalRelease();if(!local)return null;return{version:local.version,build:local.build,builtAt:local.builtAt}}let response=await fetch(latestVersionUrl(env),{headers:{accept:"application/json","user-agent":`velloo/${PACKAGE_VERSION}`},signal:AbortSignal.timeout(FETCH_TIMEOUT_MS)});if(!response.ok)throw Error(`release check returned HTTP ${response.status}`);let body=await response.json();if(typeof body.version!=="string"||!/^\d+\.\d+\.\d+/.test(body.version))throw Error("release check returned an invalid version");return{version:body.version,...typeof body.build==="string"?{build:body.build}:{},...typeof body.builtAt==="number"?{builtAt:body.builtAt}:{}}}function installedVellooBin(env=process.env){let candidates=[],method=installMethod(env);if(method==="direct")candidates.push(join(env.VELLOO_BIN_DIR??join(homedir(),".local","bin"),"velloo"));if(method==="homebrew"){let prefix=env.HOMEBREW_PREFIX??(process.platform==="darwin"?"/opt/homebrew":null);if(prefix)candidates.push(join(prefix,"bin","velloo"));candidates.push("/usr/local/bin/velloo")}if(method==="npm"||method==="local"){let node=env.VELLOO_NODE_EXECUTABLE;if(node)candidates.push(join(dirname(node),"velloo"));let prefix=npmGlobalPrefix(env);if(prefix)candidates.push(process.platform==="win32"?join(prefix,"velloo.cmd"):join(prefix,"bin","velloo"))}return candidates.find((path)=>existsSync(path))??null}function npmGlobalPrefix(env){try{let result=Bun.spawnSync([npmExecutable(env),"prefix","-g"],{env}),out=result.stdout.toString().trim();return result.success&&out?out:null}catch{return null}}function npmExecutable(env=process.env){let node=env.VELLOO_NODE_EXECUTABLE;if(node){let adjacent=join(dirname(node),process.platform==="win32"?"npm.cmd":"npm");if(existsSync(adjacent))return adjacent}return process.platform==="win32"?"npm.cmd":"npm"}var CHECK_INTERVAL_MS=86400000,LOCAL_CHECK_INTERVAL_MS=1e4;function releaseSource(){return releaseChannel()==="local"&&!process.env.VELLOO_UPDATE_URL?`file:${localReleasePath()}`:latestVersionUrl()}function cachePath(){return process.env.VELLOO_UPDATE_CACHE??join2(homedir2(),".velloo","update-check.json")}async function readCacheFile(){try{let value=JSON.parse(await readFile2(cachePath(),"utf8"));if(typeof value!=="object"||value===null)return{};return Object.fromEntries(Object.entries(value).filter(([,entry])=>typeof entry==="object"&&entry!==null))}catch{return{}}}async function readCache(){let entry=(await readCacheFile())[releaseSource()];if(typeof entry?.checkedAt!=="number"||typeof entry.latest!=="string")return null;return entry}async function writeCache(value){let path=cachePath(),file={...await readCacheFile(),[releaseSource()]:value};await mkdir(dirname2(path),{recursive:!0});let temporary=`${path}.${process.pid}.tmp`;await writeFile(temporary,`${JSON.stringify(file,null,2)}
3
3
  `);try{await rename(temporary,path)}catch(error){if(process.platform!=="win32")throw error;await rm(path,{force:!0}),await rename(temporary,path)}}function cachedRelease(cache){return{version:cache.latest,...cache.latestBuild===void 0?{}:{build:cache.latestBuild},...cache.latestBuiltAt===void 0?{}:{builtAt:cache.latestBuiltAt}}}async function refreshUpdateCache(){try{let previous=await readCache(),latest=await fetchLatestRelease();if(!latest)return;await writeCache({checkedAt:Date.now(),latest:latest.version,...latest.build===void 0?{}:{latestBuild:latest.build},...latest.builtAt===void 0?{}:{latestBuiltAt:latest.builtAt},...previous?.notifiedAt?{notifiedAt:previous.notifiedAt}:{},...previous?.notifiedVersion?{notifiedVersion:previous.notifiedVersion}:{}})}catch{}}function spawnUpdateCheck(){try{Bun.spawn(selfCommand(["__update_check"]),{stdin:"ignore",stdout:"ignore",stderr:"ignore",env:{...process.env,VELLOO_UPDATE_CHECK_CHILD:"1"}}).unref()}catch{}}function checkIntervalMs(){return releaseChannel()==="local"?LOCAL_CHECK_INTERVAL_MS:CHECK_INTERVAL_MS}function checkSuppressed(){return process.env.VELLOO_DISABLE_UPDATE_CHECK==="1"||process.env.VELLOO_UPDATE_CHECK_CHILD==="1"||!selfUpgradable(installMethod())}async function maybeNotifyAboutUpdate(options={}){if(checkSuppressed()||!(options.isTTY??process.stderr.isTTY))return;let now=options.now??Date.now(),cached=await readCache(),latest=(releaseChannel()==="local"?await fetchLatestRelease().catch(()=>null):null)??(cached?cachedRelease(cached):null);if(latest&&isNewerRelease(latest)){if(!(cached?.notifiedVersion===releaseLabel(latest)&&typeof cached?.notifiedAt==="number"&&now-cached.notifiedAt<checkIntervalMs())){let running=runningRelease();console.error(""),console.error(`${import_picocolors.default.yellow("Update available:")} ${releaseLabel(running)} \u2192 ${releaseLabel(latest)}. Run ${import_picocolors.default.cyan("velloo upgrade")}.`),await writeCache({checkedAt:cached?.checkedAt??now,latest:latest.version,...latest.build===void 0?{}:{latestBuild:latest.build},...latest.builtAt===void 0?{}:{latestBuiltAt:latest.builtAt},notifiedAt:now,notifiedVersion:releaseLabel(latest)}).catch(()=>{})}}if(!cached||now-cached.checkedAt>=checkIntervalMs())spawnUpdateCheck()}async function runInherited(command,env=process.env){let code=await Bun.spawn(command,{stdin:"inherit",stdout:"inherit",stderr:"inherit",env}).exited;if(code!==0)throw Error(`\`${command.join(" ")}\` exited with status ${code}`)}async function updateStatus(opts={}){let method=installMethod(),channel=releaseChannel(),running=runningRelease(),base={method,channel,current:releaseLabel(running),latest:null,available:!1,upgradable:selfUpgradable(method),...selfUpgradable(method)?{}:{reason:unsupportedReason(method)}};if(!selfUpgradable(method))return base;let release=null;if(opts.refresh)try{if(release=await fetchLatestRelease(),release)await writeCache({checkedAt:Date.now(),latest:release.version,...release.build===void 0?{}:{latestBuild:release.build},...release.builtAt===void 0?{}:{latestBuiltAt:release.builtAt}}).catch(()=>{})}catch(error){return{...base,reason:error instanceof Error?error.message:String(error)}}else{let cached=await readCache();if(cached)release=cachedRelease(cached);if(!cached||Date.now()-cached.checkedAt>=checkIntervalMs())spawnUpdateCheck()}if(!release)return base;return{...base,latest:releaseLabel(release),available:isNewerRelease(release,running)}}function unsupportedReason(method){return method==="source"?"this Velloo is running from source; update the checkout with your Git workflow":"could not identify this installation. Reinstall with `npm install -g velloo@latest` or the installer at https://get.velloo.design/install.sh"}async function upgradeInstalledVelloo(options={}){let method=installMethod(),channel=releaseChannel(),running=runningRelease();if(!selfUpgradable(method))throw Error(unsupportedReason(method));let latest=await fetchLatestRelease();if(!latest)throw Error(channel==="local"?`no local build found at ${localReleasePath()} \u2014 run \`bun run cli:build\` in the velloo checkout first`:"this channel has no published release");let done=(upgraded)=>({upgraded,from:releaseLabel(running),to:releaseLabel(latest),method,channel});if(!isNewerRelease(latest,running))return console.log(`velloo: ${releaseLabel(running)} is already the latest ${channel} build.`),done(!1);if(options.checkOnly)return console.log(`velloo: ${releaseLabel(running)} \u2192 ${releaseLabel(latest)} is available (${method} installation, ${channel} channel).`),done(!1);if(method==="homebrew")await runInherited(["brew","upgrade",process.env.VELLOO_BREW_FORMULA??"velloo"]);else if(channel==="local")await upgradeFromLocalBuild();else if(method==="npm"){let packageName=process.env.VELLOO_NPM_PACKAGE??"velloo",spec=channel==="dev"?`${downloadBase()}/velloo.tgz`:`${packageName}@${latest.version}`;await runInherited([npmExecutable(),"install","-g",spec])}else{let response=await fetch(installerUrl(),{signal:AbortSignal.timeout(1e4)});if(!response.ok)throw Error(`installer download returned HTTP ${response.status}`);let directory=await mkdtemp(join2(tmpdir(),"velloo-upgrade-")),installer=join2(directory,"install.sh");try{await writeFile(installer,await response.text(),{mode:448}),await runInherited(["bash",installer],{...process.env,VELLOO_VERSION:latest.version,VELLOO_DOWNLOAD_BASE:downloadBase()})}finally{await rm(directory,{recursive:!0,force:!0})}}return console.log(`velloo: upgraded ${releaseLabel(running)} \u2192 ${releaseLabel(latest)} via ${method}.`),done(!0)}async function upgradeFromLocalBuild(){let local=await readLocalRelease();if(!local)throw Error("no local build recorded \u2014 run `bun run cli:build` first");if(!existsSync2(local.tarball))throw Error(`the recorded local build is gone (${local.tarball}) \u2014 run \`bun run cli:build\` in ${local.repo} again`);await runInherited([npmExecutable(),"install","-g","--include=optional",local.tarball])}
4
4
  export{installedVellooBin,refreshUpdateCache,maybeNotifyAboutUpdate,updateStatus,upgradeInstalledVelloo};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{isCancel,confirm,select}from"./chunk-80fjfyha.js";import{designNameIssue}from"./chunk-5x4nw0zz.js";import{managedDesignId,localDesignOf,removeLocalDesign,recordedDesignName,designRenameConflict,followDesignRename,loadDesignFolder}from"./chunk-brnkprt5.js";import"./chunk-ex36z3b3.js";import"./chunk-bys7pqdb.js";import{agentRootCandidates}from"./chunk-4y2xy1kf.js";import"./chunk-3m28pvzr.js";import{daemonRoot,readLock,isLive,stopDaemon}from"./chunk-xzf52wvn.js";import{fail}from"./chunk-s23gtejf.js";import"./chunk-eh2xp4v6.js";import"./chunk-acsv6m3n.js";import"./chunk-p93x4pn2.js";import{displayPath}from"./chunk-mdgwdqnq.js";import"./chunk-b8vcbbrg.js";import"./chunk-s84vxcdv.js";import{recordedAppRoot,planAppRootChange,applyAppRootChange,promptAppRootChoice,planRelocation,relocateDesign,runInit2}from"./chunk-g8xrwjvq.js";import"./chunk-kkvf0xk2.js";import{repinAgentConfigs,migrateFolder2}from"./chunk-0vaff40g.js";import"./chunk-pjtg3wnm.js";import"./chunk-5yhnnhkd.js";import"./chunk-vs8j3x6t.js";import"./chunk-82x2jkfq.js";import{findManifest,manifestListing,findDesigns,unregisterDesign,isWithin,writeDesignName,registerLocalDesign,DESIGN_ARG_DESCRIPTION,hasDesignConfig,DESIGN_ID_ARG,resolveDesign,checkoutDesigns}from"./chunk-64pex3mw.js";import"./chunk-abr2hgay.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID}from"crypto";import{existsSync,realpathSync}from"fs";import{readdir,rm}from"fs/promises";import{join,relative,resolve}from"path";var import_picocolors=__toESM(require_picocolors(),1);async function countJson(dir,skipSuffix){try{return(await readdir(dir)).filter((f)=>f.endsWith(".json")&&!(skipSuffix&&f.endsWith(skipSuffix))).length}catch{return 0}}async function daemonState(folder){let rec=await readLock(daemonRoot(folder));if(!rec)return"stopped";return await isLive(rec)?`running :${rec.canvasPort}`:"stopped"}var list=defineCommand({meta:{name:"list",description:"List the repo's designs"},async run(){let cwd=resolve("."),found=await findDesigns(cwd).catch((err)=>{fail("design list",err.message)});if(!found||found.designs.length===0){let only=await resolveDesign(void 0,"design list",{requireConfig:!0,cwd,takesDesign:!1,onFail:(message)=>fail("design list",message)}),name=recordedDesignName(only)??"(unnamed)",owner=await manifestListing(only).catch(()=>null);if(owner&&owner.dir!==cwd){console.log(`velloo design: "${name}" (${relative(cwd,only)||only}) is listed in \`${relative(cwd,owner.path)}\`. Run \`velloo design list\` from \`${relative(cwd,owner.dir)||"."}\` to see that project's designs.`);return}console.log("velloo design: 1 design (no velloo.json yet)"),console.log(` ${import_picocolors.default.dim("1")} ${import_picocolors.default.bold(name)} ${relative(cwd,only)||only} ${await daemonState(only)}`),console.log(import_picocolors.default.dim(" `velloo design add` creates a second design and lists both."));return}let base=found.repo?.dir??found.local[0]?.root??cwd,rows=[];for(let design of found.designs){let live=design.exists&&!design.outsideRepo;rows.push({name:found.conflicts.has(design.name)?`${design.name} (duplicate)`:design.name,path:design.local?`${displayPath(design.root)} (local)`:relative(base,design.root)||".",boards:live?await countJson(join(design.root,"boards"),".notes.json"):0,state:design.outsideRepo?import_picocolors.default.yellow("outside the repo"):live?await daemonState(design.root):import_picocolors.default.yellow("missing"),live})}let pad=(text,width)=>text+" ".repeat(Math.max(0,width-Bun.stringWidth(text))),width=Math.max(...rows.map((r)=>Bun.stringWidth(r.name))),pathWidth=Math.max(...rows.map((r)=>Bun.stringWidth(r.path))),idWidth=String(rows.length).length;console.log(`velloo design: ${rows.length} in ${base}`);for(let[index,row]of rows.entries()){let marker=row.name===found.defaultDesign?import_picocolors.default.cyan("\u25CF"):" ",id=import_picocolors.default.dim(String(index+1).padStart(idWidth)),boards=row.live?`${row.boards} board${row.boards===1?"":"s"}`:"\u2014";console.log(` ${id} ${marker} ${import_picocolors.default.bold(pad(row.name,width))} ${import_picocolors.default.dim(pad(row.path,pathWidth))} ${boards.padEnd(9)} ${row.state}`)}console.log(import_picocolors.default.dim(" Pass a number as --id to the other `velloo design` commands."));for(let[name,designs]of found.conflicts)console.log(import_picocolors.default.yellow(` ! ${designs.length} designs are named "${name}" \u2014 rename one with \`velloo design rename <path> <new-name>\`.`))}}),add=defineCommand({meta:{name:"add",description:"Create another design in this repo (the init wizard, folder pre-chosen)"},args:{path:{type:"positional",required:!1,description:"Where the new design folder goes, relative to the repo (asked when omitted)"},external:{type:"boolean",description:"Create in Velloo-managed external storage"},nonInteractive:{type:"boolean",description:"Use defaults without prompting"},connect:{type:"boolean",default:!0,description:"Wire project agents"},library:{type:"string",description:"Component library (default: the sibling folder's)"},name:{type:"string",description:"The design's name (default: derived from its folder)"}},async run({args}){await runInit2({addFolder:!0,external:args.external,nonInteractive:args.nonInteractive,connect:args.connect,...args.path?{designFolder:args.path}:{},...args.library?{library:args.library}:{},...args.name?{name:args.name}:{}})}}),remove=defineCommand({meta:{name:"remove",description:"Remove a design \u2014 one outside the repo can keep its files (asked, or --delete-content)"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,deleteContent:{type:"boolean",description:"Also delete the files of a design outside the repo (with --yes, or to skip the question)"},yes:{type:"boolean",description:"Skip the confirmation (required without an interactive terminal)"}},async run({args}){let cwd=resolve("."),folder=await resolveDesign(args.design,"design remove",{id:args.id,interactive:!0,requireConfig:!0,cwd}),local=localDesignOf(folder);if(!Boolean(process.stdin.isTTY)&&args.yes!==!0)fail("design remove","refusing to delete a design folder without --yes");let boards=await countJson(join(folder,"boards"),".notes.json"),screens=await countJson(join(folder,"screens"),".annotations.json"),name=recordedDesignName(folder)??folder;console.log(`velloo design: ${name} (${folder})`),console.log(import_picocolors.default.dim(` ${boards} board${boards===1?"":"s"}, ${screens} screen${screens===1?"":"s"}`));let keepContent=local!==null&&args.deleteContent!==!0;if(args.yes!==!0)if(local&&args.deleteContent!==!0){let managed=managedDesignId(folder)!==null,choice=await select({message:"Remove this design?",initialValue:managed?"delete":"keep",options:[{value:"delete",label:"Delete it and its files",hint:managed?"in Velloo's storage":folder},{value:"keep",label:"Forget it, keep the files",hint:folder},{value:"cancel",label:"Cancel"}]});if(isCancel(choice)||choice==="cancel"){console.log(import_picocolors.default.dim(" Nothing changed."));return}keepContent=choice==="keep"}else{let approved=await confirm({message:"Delete this design folder and everything in it?",initialValue:!1});if(isCancel(approved)||!approved){console.log(import_picocolors.default.dim(" Nothing changed."));return}}if(await stopDaemon(daemonRoot(folder)))console.log(import_picocolors.default.dim(" Stopped its canvas."));let dropped=local?null:await unregisterDesign(folder,cwd).catch((err)=>fail("design remove",err.message));if(local)await removeLocalDesign(local.id);if(!keepContent)await rm(folder,{recursive:!0,force:!0});if(console.log(`velloo design: ${keepContent?"retained content at":"removed"} ${folder}`),local)console.log(import_picocolors.default.dim(` Forgot the local design "${name}".`));if(dropped?.name)console.log(import_picocolors.default.dim(dropped.removedManifest?` Dropped "${dropped.name}" \u2014 velloo.json listed no other designs, so it's gone too.`:` Dropped "${dropped.name}" from velloo.json.`))}}),move=defineCommand({meta:{name:"move",description:"Preview moving a design between the repository, managed storage and another directory; --yes applies"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,external:{type:"boolean",description:"Move to managed storage (a local design)"},to:{type:"string",description:"New directory \u2014 inside the repository it goes in velloo.json, outside it is local"},yes:{type:"boolean",description:"Apply the displayed move"}},async run({args}){let source=await resolveDesign(args.design,"design move",{id:args.id,interactive:!0,requireConfig:!0}),plan=await planRelocation(source,resolve("."),args.to,args.external===!0).catch((err)=>fail("design move",err.message)),local="a local design on this machine",recorded=plan.sourceManifestPath?plan.destinationLocal?`velloo.json path "${plan.sourceManifestPath}" \u2192 ${"a local design on this machine"}`:`velloo.json path "${plan.sourceManifestPath}" \u2192 "${plan.destinationManifestPath}"`:plan.destinationLocal?"stays a local design on this machine":`${"a local design on this machine"} \u2192 velloo.json path "${plan.destinationManifestPath}"`;if(console.log(`Source: ${plan.source}
2
+ import{isCancel,confirm,select}from"./chunk-80fjfyha.js";import{designNameIssue}from"./chunk-w40pqmyz.js";import{managedDesignId,localDesignOf,removeLocalDesign,recordedDesignName,designRenameConflict,followDesignRename,loadDesignFolder}from"./chunk-yp39f93e.js";import"./chunk-n70zfv0m.js";import"./chunk-zexzy6zv.js";import{agentRootCandidates}from"./chunk-2x5rw009.js";import"./chunk-g7pbfad5.js";import{daemonRoot,readLock,isLive,stopDaemon}from"./chunk-f8jchnwe.js";import{fail}from"./chunk-s23gtejf.js";import"./chunk-pasvz8xw.js";import"./chunk-seaar5na.js";import"./chunk-p93x4pn2.js";import{displayPath}from"./chunk-02gpgqj4.js";import"./chunk-gv209d5j.js";import"./chunk-fejhav0p.js";import{recordedAppRoot,planAppRootChange,applyAppRootChange,promptAppRootChoice,planRelocation,relocateDesign,runInit2}from"./chunk-8a2agf28.js";import"./chunk-c11n1tve.js";import{repinAgentConfigs,migrateFolder2}from"./chunk-5tatkt2j.js";import"./chunk-pjtg3wnm.js";import"./chunk-ysf9388d.js";import"./chunk-v6w8akph.js";import"./chunk-8kn6tp4b.js";import{findManifest,manifestListing,findDesigns,unregisterDesign,isWithin,writeDesignName,registerLocalDesign,DESIGN_ARG_DESCRIPTION,hasDesignConfig,DESIGN_ID_ARG,resolveDesign,checkoutDesigns}from"./chunk-z3r8px3d.js";import"./chunk-3f2p94wa.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID}from"crypto";import{existsSync,realpathSync}from"fs";import{readdir,rm}from"fs/promises";import{join,relative,resolve}from"path";var import_picocolors=__toESM(require_picocolors(),1);async function countJson(dir,skipSuffix){try{return(await readdir(dir)).filter((f)=>f.endsWith(".json")&&!(skipSuffix&&f.endsWith(skipSuffix))).length}catch{return 0}}async function daemonState(folder){let rec=await readLock(daemonRoot(folder));if(!rec)return"stopped";return await isLive(rec)?`running :${rec.canvasPort}`:"stopped"}var list=defineCommand({meta:{name:"list",description:"List the repo's designs"},async run(){let cwd=resolve("."),found=await findDesigns(cwd).catch((err)=>{fail("design list",err.message)});if(!found||found.designs.length===0){let only=await resolveDesign(void 0,"design list",{requireConfig:!0,cwd,takesDesign:!1,onFail:(message)=>fail("design list",message)}),name=recordedDesignName(only)??"(unnamed)",owner=await manifestListing(only).catch(()=>null);if(owner&&owner.dir!==cwd){console.log(`velloo design: "${name}" (${relative(cwd,only)||only}) is listed in \`${relative(cwd,owner.path)}\`. Run \`velloo design list\` from \`${relative(cwd,owner.dir)||"."}\` to see that project's designs.`);return}console.log("velloo design: 1 design (no velloo.json yet)"),console.log(` ${import_picocolors.default.dim("1")} ${import_picocolors.default.bold(name)} ${relative(cwd,only)||only} ${await daemonState(only)}`),console.log(import_picocolors.default.dim(" `velloo design add` creates a second design and lists both."));return}let base=found.repo?.dir??found.local[0]?.root??cwd,rows=[];for(let design of found.designs){let live=design.exists&&!design.outsideRepo;rows.push({name:found.conflicts.has(design.name)?`${design.name} (duplicate)`:design.name,path:design.local?`${displayPath(design.root)} (local)`:relative(base,design.root)||".",boards:live?await countJson(join(design.root,"boards"),".notes.json"):0,state:design.outsideRepo?import_picocolors.default.yellow("outside the repo"):live?await daemonState(design.root):import_picocolors.default.yellow("missing"),live})}let pad=(text,width)=>text+" ".repeat(Math.max(0,width-Bun.stringWidth(text))),width=Math.max(...rows.map((r)=>Bun.stringWidth(r.name))),pathWidth=Math.max(...rows.map((r)=>Bun.stringWidth(r.path))),idWidth=String(rows.length).length;console.log(`velloo design: ${rows.length} in ${base}`);for(let[index,row]of rows.entries()){let marker=row.name===found.defaultDesign?import_picocolors.default.cyan("\u25CF"):" ",id=import_picocolors.default.dim(String(index+1).padStart(idWidth)),boards=row.live?`${row.boards} board${row.boards===1?"":"s"}`:"\u2014";console.log(` ${id} ${marker} ${import_picocolors.default.bold(pad(row.name,width))} ${import_picocolors.default.dim(pad(row.path,pathWidth))} ${boards.padEnd(9)} ${row.state}`)}console.log(import_picocolors.default.dim(" Pass a number as --id to the other `velloo design` commands."));for(let[name,designs]of found.conflicts)console.log(import_picocolors.default.yellow(` ! ${designs.length} designs are named "${name}" \u2014 rename one with \`velloo design rename <path> <new-name>\`.`))}}),add=defineCommand({meta:{name:"add",description:"Create another design in this repo (the init wizard, folder pre-chosen)"},args:{path:{type:"positional",required:!1,description:"Where the new design folder goes, relative to the repo (asked when omitted)"},external:{type:"boolean",description:"Create in Velloo-managed external storage"},nonInteractive:{type:"boolean",description:"Use defaults without prompting"},connect:{type:"boolean",default:!0,description:"Wire project agents"},library:{type:"string",description:"Component library (default: the sibling folder's)"},name:{type:"string",description:"The design's name (default: derived from its folder)"}},async run({args}){await runInit2({addFolder:!0,external:args.external,nonInteractive:args.nonInteractive,connect:args.connect,...args.path?{designFolder:args.path}:{},...args.library?{library:args.library}:{},...args.name?{name:args.name}:{}})}}),remove=defineCommand({meta:{name:"remove",description:"Remove a design \u2014 one outside the repo can keep its files (asked, or --delete-content)"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,deleteContent:{type:"boolean",description:"Also delete the files of a design outside the repo (with --yes, or to skip the question)"},yes:{type:"boolean",description:"Skip the confirmation (required without an interactive terminal)"}},async run({args}){let cwd=resolve("."),folder=await resolveDesign(args.design,"design remove",{id:args.id,interactive:!0,requireConfig:!0,cwd}),local=localDesignOf(folder);if(!Boolean(process.stdin.isTTY)&&args.yes!==!0)fail("design remove","refusing to delete a design folder without --yes");let boards=await countJson(join(folder,"boards"),".notes.json"),screens=await countJson(join(folder,"screens"),".annotations.json"),name=recordedDesignName(folder)??folder;console.log(`velloo design: ${name} (${folder})`),console.log(import_picocolors.default.dim(` ${boards} board${boards===1?"":"s"}, ${screens} screen${screens===1?"":"s"}`));let keepContent=local!==null&&args.deleteContent!==!0;if(args.yes!==!0)if(local&&args.deleteContent!==!0){let managed=managedDesignId(folder)!==null,choice=await select({message:"Remove this design?",initialValue:managed?"delete":"keep",options:[{value:"delete",label:"Delete it and its files",hint:managed?"in Velloo's storage":folder},{value:"keep",label:"Forget it, keep the files",hint:folder},{value:"cancel",label:"Cancel"}]});if(isCancel(choice)||choice==="cancel"){console.log(import_picocolors.default.dim(" Nothing changed."));return}keepContent=choice==="keep"}else{let approved=await confirm({message:"Delete this design folder and everything in it?",initialValue:!1});if(isCancel(approved)||!approved){console.log(import_picocolors.default.dim(" Nothing changed."));return}}if(await stopDaemon(daemonRoot(folder)))console.log(import_picocolors.default.dim(" Stopped its canvas."));let dropped=local?null:await unregisterDesign(folder,cwd).catch((err)=>fail("design remove",err.message));if(local)await removeLocalDesign(local.id);if(!keepContent)await rm(folder,{recursive:!0,force:!0});if(console.log(`velloo design: ${keepContent?"retained content at":"removed"} ${folder}`),local)console.log(import_picocolors.default.dim(` Forgot the local design "${name}".`));if(dropped?.name)console.log(import_picocolors.default.dim(dropped.removedManifest?` Dropped "${dropped.name}" \u2014 velloo.json listed no other designs, so it's gone too.`:` Dropped "${dropped.name}" from velloo.json.`))}}),move=defineCommand({meta:{name:"move",description:"Preview moving a design between the repository, managed storage and another directory; --yes applies"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,external:{type:"boolean",description:"Move to managed storage (a local design)"},to:{type:"string",description:"New directory \u2014 inside the repository it goes in velloo.json, outside it is local"},yes:{type:"boolean",description:"Apply the displayed move"}},async run({args}){let source=await resolveDesign(args.design,"design move",{id:args.id,interactive:!0,requireConfig:!0}),plan=await planRelocation(source,resolve("."),args.to,args.external===!0).catch((err)=>fail("design move",err.message)),local="a local design on this machine",recorded=plan.sourceManifestPath?plan.destinationLocal?`velloo.json path "${plan.sourceManifestPath}" \u2192 ${"a local design on this machine"}`:`velloo.json path "${plan.sourceManifestPath}" \u2192 "${plan.destinationManifestPath}"`:plan.destinationLocal?"stays a local design on this machine":`${"a local design on this machine"} \u2192 velloo.json path "${plan.destinationManifestPath}"`;if(console.log(`Source: ${plan.source}
3
3
  Destination: ${plan.destination}
4
4
  Design "${plan.designName}": ${recorded}`),!args.yes){console.log("Preview only. Add --yes to apply the move.");return}await relocateDesign(plan),console.log(`Moved ${plan.designName} to ${plan.destination}. Run velloo connect to refresh existing agent guidance.`)}});async function renameThroughCanvas(folder,name){let rec=await readLock(daemonRoot(folder));if(!rec||!await isLive(rec))return!1;let res;try{res=await fetch(`http://127.0.0.1:${rec.canvasPort}/api/mutate/update_design_name`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name}),signal:AbortSignal.timeout(1e4)})}catch(err){fail("design rename",`the running canvas didn't answer (${err.message}).`)}if(res.ok)return!0;let body=await res.json().catch(()=>null);fail("design rename",body?.error?.message??`the running canvas refused (HTTP ${res.status}).`)}var rename=defineCommand({meta:{name:"rename",description:"Rename a design"},args:{design:{type:"positional",required:!1,description:"The design to rename \u2014 its current name or its folder path (or use --id)"},name:{type:"positional",required:!1,description:"The new name"},id:DESIGN_ID_ARG},async run({args}){let cwd=resolve("."),newName=args.id!==void 0&&args.name===void 0?args.design:args.name,target=args.id!==void 0&&args.name===void 0?void 0:args.design;if(newName===void 0)fail("design rename","pass the design and its new name: `velloo design rename <design> <new name>`.");let folder=await resolveDesign(target,"design rename",{id:args.id,requireConfig:!0,cwd}),previous=recordedDesignName(folder);if(previous===newName){console.log(import_picocolors.default.dim(` Already named "${newName}" \u2014 nothing changed.`));return}let conflict=await designRenameConflict(folder,newName).catch((err)=>fail("design rename",err.message));if(conflict)fail("design rename",conflict);if(!await renameThroughCanvas(folder,newName)){if(await writeDesignName(folder,newName),previous&&await followDesignRename(folder,previous,newName))console.log(import_picocolors.default.dim(` velloo.json defaultDesign \u2192 "${newName}"`))}console.log(`velloo design: renamed ${previous?`"${previous}" `:""}to "${newName}".`);let repinned=previous?await repinAgentConfigs({projectRoots:await agentRootCandidates(folder),designFolder:folder,names:[previous]}).catch(()=>[]):[];for(let path of repinned)console.log(import_picocolors.default.dim(` ${relative(cwd,path)||path}: no longer names the design`))}}),upgrade=defineCommand({meta:{name:"upgrade",description:"Migrate a design to this Velloo's format, without upgrading Velloo itself"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,all:{type:"boolean",description:"Migrate every design in this checkout"},"dry-run":{type:"boolean",default:!1,description:"Show what would change without writing"},skills:{type:"boolean",default:!0,description:"Also refresh the installed agent skills / plugin / rules to this velloo's versions"}},async run({args}){if(args.all&&(args.design||args.id!==void 0))fail("design upgrade","pass a design or --all, not both.");let folders=args.all?await checkoutDesigns(resolve(".")):[await resolveDesign(args.design,"design upgrade",{id:args.id,interactive:!0,requireConfig:!0})];if(folders.length===0)fail("design upgrade","no designs here \u2014 `velloo init` creates one.");for(let folder of folders)await migrateFolder2(folder,{dryRun:args["dry-run"],skills:args.skills})}}),bind=defineCommand({meta:{name:"bind",description:"Attach a design folder outside this checkout to it, on this machine only"},args:{design:{type:"positional",required:!0,description:"The design folder (in managed storage or anywhere outside the checkout)"},name:{type:"string",description:"Rename the design as it is bound"},yes:{type:"boolean",description:"Confirm the local binding"}},async run({args}){let cwd=resolve("."),folder=resolve(args.design);if(!await hasDesignConfig(folder))fail("design bind",`${folder} is not a velloo design folder (no .design/config.json).`);let root=cwd;if(isWithin(root,folder))fail("design bind",`${folder} is inside this checkout (${root}); a design here belongs in velloo.json \u2014 see \`velloo design add\`.`);let name=args.name??recordedDesignName(folder);if(!name)fail("design bind",`${folder} has no design name \u2014 run \`velloo upgrade ${folder}\` first.`);let issue=designNameIssue(name);if(issue)fail("design bind",`invalid --name ${JSON.stringify(name)} \u2014 ${issue}`);let clash=(await findDesigns(root).catch((err)=>fail("design bind",err.message)))?.designs.find((d)=>d.name===name&&!(existsSync(d.root)&&realpathSync(d.root)===realpathSync(folder)));if(clash)fail("design bind",`a design named "${name}" already exists at ${clash.root} \u2014 pass --name to bind this one under another.`);let existing=localDesignOf(folder),appRoot=existing?join(root,relative(existing.root,existing.appRoot)):root,listed=(await findManifest(cwd).catch(()=>null))?.folders.some((path)=>existsSync(path)&&realpathSync(path)===realpathSync(folder));if(console.log(`Design: ${name} (${folder})
5
5
  Checkout: ${root}
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- var package_default={name:"@velloo/cli",version:"0.7.0",license:"Apache-2.0",private:!0,type:"module",description:"Velloo CLI",bin:{velloo:"./src/cli.ts"},exports:{".":"./src/cli.ts"},engines:{bun:">=1.4.0"},scripts:{build:"bun run build.ts"},dependencies:{"@clack/prompts":"1.7.0","@velloo/canvas":"workspace:*","@velloo/codegen":"workspace:*","@velloo/protocol":"workspace:*","@velloo/provider":"workspace:*","@velloo/provider-antd":"workspace:*","@velloo/provider-chakra":"workspace:*","@velloo/provider-html":"workspace:*","@velloo/provider-mui":"workspace:*","@velloo/provider-none":"workspace:*","@velloo/renderer":"workspace:*","@velloo/result":"workspace:*","@velloo/schema":"workspace:*","@velloo/server":"workspace:*","@velloo/shadcn-snapshot":"workspace:*",citty:"0.2.2",picocolors:"1.1.1",zod:"4.5.4"},devDependencies:{"@types/pngjs":"6.0.5",pngjs:"7.0.0",react:"19.2.8","react-dom":"19.2.8"}};var PACKAGE_VERSION=package_default.version,TOOL_VERSION="0.7.0",BUILD_TIME_MS=1791408265521;
2
+ var package_default={name:"@velloo/cli",version:"0.9.0",license:"Apache-2.0",private:!0,type:"module",description:"Velloo CLI",bin:{velloo:"./src/cli.ts"},exports:{".":"./src/cli.ts"},engines:{bun:">=1.4.0"},scripts:{build:"bun run build.ts"},dependencies:{"@clack/prompts":"1.7.0","@velloo/canvas":"workspace:*","@velloo/codegen":"workspace:*","@velloo/protocol":"workspace:*","@velloo/provider":"workspace:*","@velloo/provider-antd":"workspace:*","@velloo/provider-chakra":"workspace:*","@velloo/provider-html":"workspace:*","@velloo/provider-mui":"workspace:*","@velloo/provider-none":"workspace:*","@velloo/renderer":"workspace:*","@velloo/result":"workspace:*","@velloo/schema":"workspace:*","@velloo/server":"workspace:*","@velloo/shadcn-snapshot":"workspace:*",citty:"0.2.2",picocolors:"1.1.1",zod:"4.5.4"},devDependencies:{"@types/pngjs":"6.0.5",pngjs:"7.0.0",react:"19.2.8","react-dom":"19.2.8"}};var PACKAGE_VERSION=package_default.version,TOOL_VERSION="0.9.0",BUILD_TIME_MS=1792412871631;
3
3
  export{PACKAGE_VERSION,TOOL_VERSION,BUILD_TIME_MS};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{detectTailwindMajor,tsconfigAliases,findDesignSystemIn}from"./chunk-brnkprt5.js";import{isServerRenderedApp,hasHtmxMarkup}from"./chunk-nbbnb5e9.js";import{existsSync,readFileSync}from"fs";import{join,posix}from"path";function readJson(path){try{return JSON.parse(readFileSync(path,"utf8"))}catch{return null}}function depRange(deps,name){let v=deps[name];return typeof v==="string"?v:void 0}var REACT_DEPS=["react","react-dom","next","preact","@remix-run/react","react-router"],SCRIPT_UI_DEPS=[...REACT_DEPS,"vue","nuxt","svelte","@sveltejs/kit","@angular/core","solid-js","astro","lit","@builder.io/qwik"];function isStaticSite(appRoot,deps){return existsSync(join(appRoot,"index.html"))&&!SCRIPT_UI_DEPS.some((name)=>depRange(deps,name))}function detectHost(appRoot,repoRoot=appRoot){let pkg=readJson(join(appRoot,"package.json"))??{},deps={...pkg.dependencies??{},...pkg.devDependencies??{}},tailwindMajor=detectTailwindMajor(appRoot),componentsJson=readJson(join(appRoot,"components.json")),shadcn=componentsJson!==null,shadcnStyle=typeof componentsJson?.style==="string"?componentsJson.style:void 0,react=REACT_DEPS.some((name)=>depRange(deps,name)),uiLibrary=depRange(deps,"@mui/material")?"mui":depRange(deps,"antd")?"antd":depRange(deps,"@chakra-ui/react")?"chakra":shadcn?"shadcn":!react&&(depRange(deps,"htmx.org")||depRange(deps,"htmx")||isServerRenderedApp(appRoot)||hasHtmxMarkup(appRoot)||isStaticSite(appRoot,deps))?"html":void 0,unsupportedUi=uiLibrary?void 0:detectUnsupportedUi(deps),designMdPath=findDesignSystemIn([appRoot,repoRoot])??void 0;return{shadcn,shadcnStyle,tailwindMajor,globalsCssPath:findGlobalsCss(appRoot,componentsJson),...designMdPath?{designMdPath}:{},...uiLibrary?{uiLibrary}:{},...unsupportedUi?{unsupportedUi}:{}}}var COMPONENT_DIR_CANDIDATES=["src/components/ui","components/ui","app/components/ui","src/app/components/ui","src/components","components"];function findComponentsDir(appRoot){return componentsJsonUiDir(appRoot)??COMPONENT_DIR_CANDIDATES.find((rel)=>existsSync(join(appRoot,rel)))}function componentsJsonUiDir(appRoot){let aliases=readJson(join(appRoot,"components.json"))?.aliases,ui=typeof aliases?.ui==="string"?aliases.ui:typeof aliases?.components==="string"?`${aliases.components}/ui`:void 0;if(ui===void 0)return;for(let{from,to}of tsconfigAliases(appRoot)){if(!from||!ui.startsWith(from))continue;let rel=posix.normalize(`${to}${ui.slice(from.length)}`).replace(/\/$/,"");if(existsSync(join(appRoot,rel)))return rel}return}function detectUnsupportedUi(deps){let known=[["@mantine/core","Mantine"],["@nextui-org/react","NextUI"],["@heroui/react","HeroUI"],["react-bootstrap","React Bootstrap"],["@fluentui/react-components","Fluent UI"],["@untitledui/icons","Untitled UI"],["@untitledui/file-icons","Untitled UI"],["react-aria-components","React Aria"]];for(let[pkg,label]of known)if(depRange(deps,pkg))return label;return}function findGlobalsCss(appRoot,componentsJson){let candidates=[componentsJson&&typeof componentsJson.tailwind?.css==="string"?componentsJson.tailwind.css:void 0,"src/app/globals.css","app/globals.css","src/styles/globals.css","styles/globals.css","src/index.css","src/globals.css","src/app.css"].filter((c)=>Boolean(c));for(let rel of candidates){let abs=join(appRoot,rel);if(existsSync(abs))return abs}return}
2
+ import{detectTailwindMajor,tsconfigAliases,findDesignSystemIn}from"./chunk-yp39f93e.js";import{isServerRenderedApp,hasHtmxMarkup}from"./chunk-nbbnb5e9.js";import{existsSync,readFileSync}from"fs";import{join,posix}from"path";function readJson(path){try{return JSON.parse(readFileSync(path,"utf8"))}catch{return null}}function depRange(deps,name){let v=deps[name];return typeof v==="string"?v:void 0}var REACT_DEPS=["react","react-dom","next","preact","@remix-run/react","react-router"],SCRIPT_UI_DEPS=[...REACT_DEPS,"vue","nuxt","svelte","@sveltejs/kit","@angular/core","solid-js","astro","lit","@builder.io/qwik"];function isStaticSite(appRoot,deps){return existsSync(join(appRoot,"index.html"))&&!SCRIPT_UI_DEPS.some((name)=>depRange(deps,name))}function detectHost(appRoot,repoRoot=appRoot){let pkg=readJson(join(appRoot,"package.json"))??{},deps={...pkg.dependencies??{},...pkg.devDependencies??{}},tailwindMajor=detectTailwindMajor(appRoot),componentsJson=readJson(join(appRoot,"components.json")),shadcn=componentsJson!==null,shadcnStyle=typeof componentsJson?.style==="string"?componentsJson.style:void 0,react=REACT_DEPS.some((name)=>depRange(deps,name)),uiLibrary=depRange(deps,"@mui/material")?"mui":depRange(deps,"antd")?"antd":depRange(deps,"@chakra-ui/react")?"chakra":shadcn?"shadcn":!react&&(depRange(deps,"htmx.org")||depRange(deps,"htmx")||isServerRenderedApp(appRoot)||hasHtmxMarkup(appRoot)||isStaticSite(appRoot,deps))?"html":void 0,unsupportedUi=uiLibrary?void 0:detectUnsupportedUi(deps),designMdPath=findDesignSystemIn([appRoot,repoRoot])??void 0;return{shadcn,shadcnStyle,tailwindMajor,globalsCssPath:findGlobalsCss(appRoot,componentsJson),...designMdPath?{designMdPath}:{},...uiLibrary?{uiLibrary}:{},...unsupportedUi?{unsupportedUi}:{}}}var COMPONENT_DIR_CANDIDATES=["src/components/ui","components/ui","app/components/ui","src/app/components/ui","src/components","components"];function findComponentsDir(appRoot){return componentsJsonUiDir(appRoot)??COMPONENT_DIR_CANDIDATES.find((rel)=>existsSync(join(appRoot,rel)))}function componentsJsonUiDir(appRoot){let aliases=readJson(join(appRoot,"components.json"))?.aliases,ui=typeof aliases?.ui==="string"?aliases.ui:typeof aliases?.components==="string"?`${aliases.components}/ui`:void 0;if(ui===void 0)return;for(let{from,to}of tsconfigAliases(appRoot)){if(!from||!ui.startsWith(from))continue;let rel=posix.normalize(`${to}${ui.slice(from.length)}`).replace(/\/$/,"");if(existsSync(join(appRoot,rel)))return rel}return}function detectUnsupportedUi(deps){let known=[["@mantine/core","Mantine"],["@nextui-org/react","NextUI"],["@heroui/react","HeroUI"],["react-bootstrap","React Bootstrap"],["@fluentui/react-components","Fluent UI"],["@untitledui/icons","Untitled UI"],["@untitledui/file-icons","Untitled UI"],["react-aria-components","React Aria"]];for(let[pkg,label]of known)if(depRange(deps,pkg))return label;return}function findGlobalsCss(appRoot,componentsJson){let candidates=[componentsJson&&typeof componentsJson.tailwind?.css==="string"?componentsJson.tailwind.css:void 0,"src/app/globals.css","app/globals.css","src/styles/globals.css","styles/globals.css","src/index.css","src/globals.css","src/app.css"].filter((c)=>Boolean(c));for(let rel of candidates){let abs=join(appRoot,rel);if(existsSync(abs))return abs}return}
3
3
  export{detectHost,findComponentsDir};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{upgrade_default,migrateFolder2}from"./chunk-5tatkt2j.js";import"./chunk-2x5rw009.js";import"./chunk-pjtg3wnm.js";import"./chunk-ysf9388d.js";import"./chunk-f8jchnwe.js";import"./chunk-z3r8px3d.js";import"./chunk-80fjfyha.js";import"./chunk-yp39f93e.js";import"./chunk-n70zfv0m.js";import"./chunk-3f2p94wa.js";import"./chunk-zexzy6zv.js";import"./chunk-fejhav0p.js";import"./chunk-g7pbfad5.js";import"./chunk-s23gtejf.js";import"./chunk-w40pqmyz.js";export{upgrade_default as default,migrateFolder2 as migrateFolder};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{isCancel,select}from"./chunk-80fjfyha.js";import"./chunk-5x4nw0zz.js";import"./chunk-brnkprt5.js";import"./chunk-ex36z3b3.js";import"./chunk-bys7pqdb.js";import"./chunk-3m28pvzr.js";import{daemonRoot,listDaemons,stopDaemon}from"./chunk-xzf52wvn.js";import{fail}from"./chunk-s23gtejf.js";import"./chunk-eh2xp4v6.js";import"./chunk-acsv6m3n.js";import"./chunk-p93x4pn2.js";import"./chunk-mdgwdqnq.js";import{designWithFolder}from"./chunk-fmcm06xb.js";import"./chunk-vs8j3x6t.js";import"./chunk-82x2jkfq.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign}from"./chunk-64pex3mw.js";import"./chunk-abr2hgay.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{resolve}from"path";var import_picocolors=__toESM(require_picocolors(),1);var ALL="__all__",canvasLine=(d)=>`${designWithFolder(d.root,void 0,{absolute:!0})} \u2014 ${d.canvasUrl}`;async function stopEach(daemons){for(let d of daemons)await stopDaemon(d.root),console.log(`velloo: stopped ${canvasLine(d)}`)}async function designHere(){try{return await resolveDesign(void 0,"stop",{onFail:()=>{throw Error("no design here")}})}catch{return}}function runningFor(daemons,folder){if(!folder)return;let root=resolve(daemonRoot(folder));return daemons.find((d)=>resolve(d.root)===root)?.root}var stop_default=defineCommand({meta:{name:"stop",description:"Stop a running canvas (picks from the running ones when none is named)"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},all:{type:"boolean",default:!1,description:"Stop every running velloo canvas on this machine"}},async run({args}){if(args.all){let daemons=await listDaemons();if(daemons.length===0)console.log("velloo: no canvas daemons running.");else await stopEach(daemons);return}if(!args.design&&process.stdin.isTTY){let daemons=await listDaemons();if(daemons.length===0){console.log("velloo: no canvas daemons running.");return}if(daemons.length===1){await stopEach(daemons);return}let here=runningFor(daemons,await designHere()),ordered=[...daemons].sort((a,b)=>a.root===here?-1:b.root===here?1:0),chosen=await select({message:"Stop which canvas?",options:[...ordered.map((d)=>({value:d.root,label:designWithFolder(d.root,void 0,{absolute:!0}),hint:d.root===here?`${d.canvasUrl} \xB7 this directory`:d.canvasUrl})),{value:ALL,label:`All ${daemons.length} canvases`}],...here?{initialValue:here}:{}});if(isCancel(chosen))fail("stop","cancelled.");await stopEach(chosen===ALL?daemons:daemons.filter((d)=>d.root===chosen));return}if(!args.design&&!await designHere()){let daemons=await listDaemons();if(daemons.length===0){console.log("velloo: no canvas daemons running.");return}fail("stop",[`there is no design in this directory. ${daemons.length} canvas${daemons.length===1?" is":"es are"} running \u2014 stop one by its design folder, or all of them with \`velloo stop --all\`:`,...daemons.map((d)=>` ${canvasLine(d)}`)].join(`
2
+ import{isCancel,select}from"./chunk-80fjfyha.js";import"./chunk-w40pqmyz.js";import"./chunk-yp39f93e.js";import"./chunk-n70zfv0m.js";import"./chunk-zexzy6zv.js";import"./chunk-g7pbfad5.js";import{daemonRoot,listDaemons,stopDaemon}from"./chunk-f8jchnwe.js";import{fail}from"./chunk-s23gtejf.js";import"./chunk-pasvz8xw.js";import"./chunk-seaar5na.js";import"./chunk-p93x4pn2.js";import"./chunk-02gpgqj4.js";import{designWithFolder}from"./chunk-9b4akcfp.js";import"./chunk-v6w8akph.js";import"./chunk-8kn6tp4b.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign}from"./chunk-z3r8px3d.js";import"./chunk-3f2p94wa.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{resolve}from"path";var import_picocolors=__toESM(require_picocolors(),1);var ALL="__all__",canvasLine=(d)=>`${designWithFolder(d.root,void 0,{absolute:!0})} \u2014 ${d.canvasUrl}`;async function stopEach(daemons){for(let d of daemons)await stopDaemon(d.root),console.log(`velloo: stopped ${canvasLine(d)}`)}async function designHere(){try{return await resolveDesign(void 0,"stop",{onFail:()=>{throw Error("no design here")}})}catch{return}}function runningFor(daemons,folder){if(!folder)return;let root=resolve(daemonRoot(folder));return daemons.find((d)=>resolve(d.root)===root)?.root}var stop_default=defineCommand({meta:{name:"stop",description:"Stop a running canvas (picks from the running ones when none is named)"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},all:{type:"boolean",default:!1,description:"Stop every running velloo canvas on this machine"}},async run({args}){if(args.all){let daemons=await listDaemons();if(daemons.length===0)console.log("velloo: no canvas daemons running.");else await stopEach(daemons);return}if(!args.design&&process.stdin.isTTY){let daemons=await listDaemons();if(daemons.length===0){console.log("velloo: no canvas daemons running.");return}if(daemons.length===1){await stopEach(daemons);return}let here=runningFor(daemons,await designHere()),ordered=[...daemons].sort((a,b)=>a.root===here?-1:b.root===here?1:0),chosen=await select({message:"Stop which canvas?",options:[...ordered.map((d)=>({value:d.root,label:designWithFolder(d.root,void 0,{absolute:!0}),hint:d.root===here?`${d.canvasUrl} \xB7 this directory`:d.canvasUrl})),{value:ALL,label:`All ${daemons.length} canvases`}],...here?{initialValue:here}:{}});if(isCancel(chosen))fail("stop","cancelled.");await stopEach(chosen===ALL?daemons:daemons.filter((d)=>d.root===chosen));return}if(!args.design&&!await designHere()){let daemons=await listDaemons();if(daemons.length===0){console.log("velloo: no canvas daemons running.");return}fail("stop",[`there is no design in this directory. ${daemons.length} canvas${daemons.length===1?" is":"es are"} running \u2014 stop one by its design folder, or all of them with \`velloo stop --all\`:`,...daemons.map((d)=>` ${canvasLine(d)}`)].join(`
3
3
  `))}let folder=await resolveDesign(args.design,"stop"),root=daemonRoot(folder);if(await stopDaemon(root)){console.log(`velloo: stopped ${designWithFolder(folder)}`);return}console.log(`velloo: no canvas running for ${designWithFolder(folder)}`);let others=await listDaemons();if(others.length===0)return;console.log(import_picocolors.default.dim(` ${others.length} canvas${others.length===1?"":"es"} running elsewhere \u2014 run \`velloo stop\` to pick, or \`velloo stop --all\`:`));for(let d of others)console.log(import_picocolors.default.dim(` ${canvasLine(d)}`))}});export{stop_default as default};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{CHROMIUM_INSTALL_ARGV,CHROMIUM_FULL_INSTALL_ARGV,CHROMIUM_DEPS_INSTALL_ARGV}from"./chunk-ex36z3b3.js";import"./chunk-5x4nw0zz.js";import{fail}from"./chunk-s23gtejf.js";import{defineCommand}from"./chunk-2smsg2ct.js";async function run(argv){let code=await Bun.spawn([...argv],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited;if(code!==0)fail("browser install",`browser setup exited with status ${code}`)}var install=defineCommand({meta:{name:"install",description:"Install the browser used for screenshots"},args:{full:{type:"boolean",default:!1,description:"Install full Chromium instead of the smaller headless shell"},"with-deps":{type:"boolean",default:!1,description:"Also install required Linux system packages (may use sudo)"}},async run({args}){if(args["with-deps"]&&process.platform==="linux")await run(CHROMIUM_DEPS_INSTALL_ARGV);await run(args.full?CHROMIUM_FULL_INSTALL_ARGV:CHROMIUM_INSTALL_ARGV)}}),browser_default=defineCommand({meta:{name:"browser",description:"Manage the optional browser used for screenshots"},subCommands:{install}});export{browser_default as default};
2
+ import{CHROMIUM_INSTALL_ARGV,CHROMIUM_FULL_INSTALL_ARGV,CHROMIUM_DEPS_INSTALL_ARGV}from"./chunk-n70zfv0m.js";import"./chunk-w40pqmyz.js";import{fail}from"./chunk-s23gtejf.js";import{defineCommand}from"./chunk-2smsg2ct.js";async function run(argv){let code=await Bun.spawn([...argv],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited;if(code!==0)fail("browser install",`browser setup exited with status ${code}`)}var install=defineCommand({meta:{name:"install",description:"Install the browser used for screenshots"},args:{full:{type:"boolean",default:!1,description:"Install full Chromium instead of the smaller headless shell"},"with-deps":{type:"boolean",default:!1,description:"Also install required Linux system packages (may use sudo)"}},async run({args}){if(args["with-deps"]&&process.platform==="linux")await run(CHROMIUM_DEPS_INSTALL_ARGV);await run(args.full?CHROMIUM_FULL_INSTALL_ARGV:CHROMIUM_INSTALL_ARGV)}}),browser_default=defineCommand({meta:{name:"browser",description:"Manage the optional browser used for screenshots"},subCommands:{install}});export{browser_default as default};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import"./chunk-bys7pqdb.js";import"./chunk-5x4nw0zz.js";import{loadCredential,verifyCredential}from"./chunk-kkvf0xk2.js";import"./chunk-brnkprt5.js";import"./chunk-ex36z3b3.js";import"./chunk-3m28pvzr.js";import{listDaemons}from"./chunk-xzf52wvn.js";import"./chunk-80fjfyha.js";import"./chunk-s23gtejf.js";import"./chunk-eh2xp4v6.js";import"./chunk-acsv6m3n.js";import"./chunk-p93x4pn2.js";import"./chunk-mdgwdqnq.js";import{designWithFolder}from"./chunk-fmcm06xb.js";import"./chunk-vs8j3x6t.js";import"./chunk-82x2jkfq.js";import{defaultCloudUrl}from"./chunk-abr2hgay.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";var import_picocolors=__toESM(require_picocolors(),1);var status_default=defineCommand({meta:{name:"status",description:"List the velloo canvas daemons running on this machine + cloud sign-in"},args:{},async run(){let daemons=await listDaemons();if(daemons.length===0)console.log("velloo: no canvas daemons running.");else for(let d of daemons)console.log(`${d.canvasUrl} ${designWithFolder(d.root,void 0,{absolute:!0})} (pid ${d.pid}, since ${d.startedAt})`);let cloudUrl=defaultCloudUrl(),cred=await loadCredential(cloudUrl);if(!cred){console.log(import_picocolors.default.dim(`cloud: not signed in (${cloudUrl}) \u2014 run \`velloo login\` to share.`));return}let email=await verifyCredential(cloudUrl,cred.token,3000);console.log(email?`cloud: signed in as ${email} (${cloudUrl})`:import_picocolors.default.dim(`cloud: stored credential for ${cred.email} (${cloudUrl}) \u2014 expired or cloud unreachable; \`velloo login\` to refresh.`))}});export{status_default as default};
2
+ import"./chunk-zexzy6zv.js";import"./chunk-w40pqmyz.js";import{loadCredential,verifyCredential}from"./chunk-c11n1tve.js";import"./chunk-yp39f93e.js";import"./chunk-n70zfv0m.js";import"./chunk-g7pbfad5.js";import{listDaemons}from"./chunk-f8jchnwe.js";import"./chunk-80fjfyha.js";import"./chunk-s23gtejf.js";import"./chunk-pasvz8xw.js";import"./chunk-seaar5na.js";import"./chunk-p93x4pn2.js";import"./chunk-02gpgqj4.js";import{designWithFolder}from"./chunk-9b4akcfp.js";import"./chunk-v6w8akph.js";import"./chunk-8kn6tp4b.js";import{defaultCloudUrl}from"./chunk-3f2p94wa.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";var import_picocolors=__toESM(require_picocolors(),1);var status_default=defineCommand({meta:{name:"status",description:"List the velloo canvas daemons running on this machine + cloud sign-in"},args:{},async run(){let daemons=await listDaemons();if(daemons.length===0)console.log("velloo: no canvas daemons running.");else for(let d of daemons)console.log(`${d.canvasUrl} ${designWithFolder(d.root,void 0,{absolute:!0})} (pid ${d.pid}, since ${d.startedAt})`);let cloudUrl=defaultCloudUrl(),cred=await loadCredential(cloudUrl);if(!cred){console.log(import_picocolors.default.dim(`cloud: not signed in (${cloudUrl}) \u2014 run \`velloo login\` to share.`));return}let email=await verifyCredential(cloudUrl,cred.token,3000);console.log(email?`cloud: signed in as ${email} (${cloudUrl})`:import_picocolors.default.dim(`cloud: stored credential for ${cred.email} (${cloudUrl}) \u2014 expired or cloud unreachable; \`velloo login\` to refresh.`))}});export{status_default as default};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{require_react_development}from"./chunk-ex36z3b3.js";import{__toESM,__commonJS}from"./chunk-2smsg2ct.js";var require_react_jsx_runtime_development=__commonJS(function(exports){var React=__toESM(require_react_development());(function(){function getComponentNameFromType(type){if(type==null)return null;if(typeof type==="function")return type.$$typeof===REACT_CLIENT_REFERENCE?null:type.displayName||type.name||null;if(typeof type==="string")return type;switch(type){case REACT_FRAGMENT_TYPE:return"Fragment";case REACT_PROFILER_TYPE:return"Profiler";case REACT_STRICT_MODE_TYPE:return"StrictMode";case REACT_SUSPENSE_TYPE:return"Suspense";case REACT_SUSPENSE_LIST_TYPE:return"SuspenseList";case REACT_ACTIVITY_TYPE:return"Activity"}if(typeof type==="object")switch(typeof type.tag==="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),type.$$typeof){case REACT_PORTAL_TYPE:return"Portal";case REACT_CONTEXT_TYPE:return type.displayName||"Context";case REACT_CONSUMER_TYPE:return(type._context.displayName||"Context")+".Consumer";case REACT_FORWARD_REF_TYPE:var innerType=type.render;return type=type.displayName,type||(type=innerType.displayName||innerType.name||"",type=type!==""?"ForwardRef("+type+")":"ForwardRef"),type;case REACT_MEMO_TYPE:return innerType=type.displayName||null,innerType!==null?innerType:getComponentNameFromType(type.type)||"Memo";case REACT_LAZY_TYPE:innerType=type._payload,type=type._init;try{return getComponentNameFromType(type(innerType))}catch(x){}}return null}function testStringCoercion(value){return""+value}function checkKeyStringCoercion(value){try{testStringCoercion(value);var JSCompiler_inline_result=!1}catch(e){JSCompiler_inline_result=!0}if(JSCompiler_inline_result){JSCompiler_inline_result=console;var JSCompiler_temp_const=JSCompiler_inline_result.error,JSCompiler_inline_result$jscomp$0=typeof Symbol==="function"&&Symbol.toStringTag&&value[Symbol.toStringTag]||value.constructor.name||"Object";return JSCompiler_temp_const.call(JSCompiler_inline_result,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",JSCompiler_inline_result$jscomp$0),testStringCoercion(value)}}function getTaskName(type){if(type===REACT_FRAGMENT_TYPE)return"<>";if(typeof type==="object"&&type!==null&&type.$$typeof===REACT_LAZY_TYPE)return"<...>";try{var name=getComponentNameFromType(type);return name?"<"+name+">":"<...>"}catch(x){return"<...>"}}function getOwner(){var dispatcher=ReactSharedInternals.A;return dispatcher===null?null:dispatcher.getOwner()}function UnknownOwner(){return Error("react-stack-top-frame")}function hasValidKey(config){if(hasOwnProperty.call(config,"key")){var getter=Object.getOwnPropertyDescriptor(config,"key").get;if(getter&&getter.isReactWarning)return!1}return config.key!==void 0}function defineKeyPropWarningGetter(props,displayName){function warnAboutAccessingKey(){specialPropKeyWarningShown||(specialPropKeyWarningShown=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",displayName))}warnAboutAccessingKey.isReactWarning=!0,Object.defineProperty(props,"key",{get:warnAboutAccessingKey,configurable:!0})}function elementRefGetterWithDeprecationWarning(){var componentName=getComponentNameFromType(this.type);return didWarnAboutElementRef[componentName]||(didWarnAboutElementRef[componentName]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),componentName=this.props.ref,componentName!==void 0?componentName:null}function ReactElement(type,key,props,owner,debugStack,debugTask){var refProp=props.ref;return type={$$typeof:REACT_ELEMENT_TYPE,type,key,props,_owner:owner},(refProp!==void 0?refProp:null)!==null?Object.defineProperty(type,"ref",{enumerable:!1,get:elementRefGetterWithDeprecationWarning}):Object.defineProperty(type,"ref",{enumerable:!1,value:null}),type._store={},Object.defineProperty(type._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(type,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(type,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:debugStack}),Object.defineProperty(type,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:debugTask}),Object.freeze&&(Object.freeze(type.props),Object.freeze(type)),type}function jsxDEVImpl(type,config,maybeKey,isStaticChildren,debugStack,debugTask){var children=config.children;if(children!==void 0)if(isStaticChildren)if(isArrayImpl(children)){for(isStaticChildren=0;isStaticChildren<children.length;isStaticChildren++)validateChildKeys(children[isStaticChildren]);Object.freeze&&Object.freeze(children)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else validateChildKeys(children);if(hasOwnProperty.call(config,"key")){children=getComponentNameFromType(type);var keys=Object.keys(config).filter(function(k){return k!=="key"});isStaticChildren=0<keys.length?"{key: someKey, "+keys.join(": ..., ")+": ...}":"{key: someKey}",didWarnAboutKeySpread[children+isStaticChildren]||(keys=0<keys.length?"{"+keys.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
2
+ import{require_react_development}from"./chunk-n70zfv0m.js";import{__toESM,__commonJS}from"./chunk-2smsg2ct.js";var require_react_jsx_runtime_development=__commonJS(function(exports){var React=__toESM(require_react_development());(function(){function getComponentNameFromType(type){if(type==null)return null;if(typeof type==="function")return type.$$typeof===REACT_CLIENT_REFERENCE?null:type.displayName||type.name||null;if(typeof type==="string")return type;switch(type){case REACT_FRAGMENT_TYPE:return"Fragment";case REACT_PROFILER_TYPE:return"Profiler";case REACT_STRICT_MODE_TYPE:return"StrictMode";case REACT_SUSPENSE_TYPE:return"Suspense";case REACT_SUSPENSE_LIST_TYPE:return"SuspenseList";case REACT_ACTIVITY_TYPE:return"Activity"}if(typeof type==="object")switch(typeof type.tag==="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),type.$$typeof){case REACT_PORTAL_TYPE:return"Portal";case REACT_CONTEXT_TYPE:return type.displayName||"Context";case REACT_CONSUMER_TYPE:return(type._context.displayName||"Context")+".Consumer";case REACT_FORWARD_REF_TYPE:var innerType=type.render;return type=type.displayName,type||(type=innerType.displayName||innerType.name||"",type=type!==""?"ForwardRef("+type+")":"ForwardRef"),type;case REACT_MEMO_TYPE:return innerType=type.displayName||null,innerType!==null?innerType:getComponentNameFromType(type.type)||"Memo";case REACT_LAZY_TYPE:innerType=type._payload,type=type._init;try{return getComponentNameFromType(type(innerType))}catch(x){}}return null}function testStringCoercion(value){return""+value}function checkKeyStringCoercion(value){try{testStringCoercion(value);var JSCompiler_inline_result=!1}catch(e){JSCompiler_inline_result=!0}if(JSCompiler_inline_result){JSCompiler_inline_result=console;var JSCompiler_temp_const=JSCompiler_inline_result.error,JSCompiler_inline_result$jscomp$0=typeof Symbol==="function"&&Symbol.toStringTag&&value[Symbol.toStringTag]||value.constructor.name||"Object";return JSCompiler_temp_const.call(JSCompiler_inline_result,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",JSCompiler_inline_result$jscomp$0),testStringCoercion(value)}}function getTaskName(type){if(type===REACT_FRAGMENT_TYPE)return"<>";if(typeof type==="object"&&type!==null&&type.$$typeof===REACT_LAZY_TYPE)return"<...>";try{var name=getComponentNameFromType(type);return name?"<"+name+">":"<...>"}catch(x){return"<...>"}}function getOwner(){var dispatcher=ReactSharedInternals.A;return dispatcher===null?null:dispatcher.getOwner()}function UnknownOwner(){return Error("react-stack-top-frame")}function hasValidKey(config){if(hasOwnProperty.call(config,"key")){var getter=Object.getOwnPropertyDescriptor(config,"key").get;if(getter&&getter.isReactWarning)return!1}return config.key!==void 0}function defineKeyPropWarningGetter(props,displayName){function warnAboutAccessingKey(){specialPropKeyWarningShown||(specialPropKeyWarningShown=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",displayName))}warnAboutAccessingKey.isReactWarning=!0,Object.defineProperty(props,"key",{get:warnAboutAccessingKey,configurable:!0})}function elementRefGetterWithDeprecationWarning(){var componentName=getComponentNameFromType(this.type);return didWarnAboutElementRef[componentName]||(didWarnAboutElementRef[componentName]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),componentName=this.props.ref,componentName!==void 0?componentName:null}function ReactElement(type,key,props,owner,debugStack,debugTask){var refProp=props.ref;return type={$$typeof:REACT_ELEMENT_TYPE,type,key,props,_owner:owner},(refProp!==void 0?refProp:null)!==null?Object.defineProperty(type,"ref",{enumerable:!1,get:elementRefGetterWithDeprecationWarning}):Object.defineProperty(type,"ref",{enumerable:!1,value:null}),type._store={},Object.defineProperty(type._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(type,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(type,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:debugStack}),Object.defineProperty(type,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:debugTask}),Object.freeze&&(Object.freeze(type.props),Object.freeze(type)),type}function jsxDEVImpl(type,config,maybeKey,isStaticChildren,debugStack,debugTask){var children=config.children;if(children!==void 0)if(isStaticChildren)if(isArrayImpl(children)){for(isStaticChildren=0;isStaticChildren<children.length;isStaticChildren++)validateChildKeys(children[isStaticChildren]);Object.freeze&&Object.freeze(children)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else validateChildKeys(children);if(hasOwnProperty.call(config,"key")){children=getComponentNameFromType(type);var keys=Object.keys(config).filter(function(k){return k!=="key"});isStaticChildren=0<keys.length?"{key: someKey, "+keys.join(": ..., ")+": ...}":"{key: someKey}",didWarnAboutKeySpread[children+isStaticChildren]||(keys=0<keys.length?"{"+keys.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
3
3
  let props = %s;
4
4
  <%s {...props} />
5
5
  React keys must be passed directly to JSX without using spread: