velloo 0.4.0 → 0.5.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.
- package/README.md +29 -7
- package/canvas/assets/index-BDBBHdHH.css +2 -0
- package/canvas/assets/index-QdsLJNY9.js +29 -0
- package/canvas/assets/lucide-all-R8DUyY-B.js +1 -0
- package/canvas/index.html +2 -2
- package/chunk-0rdf6trc.js +2 -0
- package/{chunk-tkvb3wb8.js → chunk-0wafc9aw.js} +1 -1
- package/chunk-0wqxb8r9.js +3 -0
- package/{chunk-hzjzmkp6.js → chunk-10hap8kc.js} +1 -1
- package/chunk-18eqh3s1.js +2 -0
- package/chunk-19xyzep8.js +2 -0
- package/{chunk-sn9vg42q.js → chunk-1h3gqy7w.js} +1 -1
- package/{chunk-3kqtny5t.js → chunk-33t0csxs.js} +2 -2
- package/{chunk-0r80246c.js → chunk-3ffs39e8.js} +1 -1
- package/chunk-42bj6r30.js +3 -0
- package/chunk-49w26e94.js +5 -0
- package/{chunk-nte0t7v3.js → chunk-4pkw6d9n.js} +2 -2
- package/{chunk-vh4144az.js → chunk-4sjvdakg.js} +1 -1
- package/chunk-5f0xv80x.js +5 -0
- package/{chunk-2fgfex9b.js → chunk-5ma8zbty.js} +1 -1
- package/{chunk-8yz604tw.js → chunk-5y9hem1j.js} +1 -1
- package/chunk-66qgm91k.js +708 -0
- package/{chunk-46xykc71.js → chunk-675tr675.js} +1 -1
- package/{chunk-7cktk5qr.js → chunk-6cgvzwec.js} +1 -1
- package/chunk-6k05kqnc.js +3 -0
- package/{chunk-09385g5r.js → chunk-6ytv1v2r.js} +1 -1
- package/{chunk-33r6266k.js → chunk-75sc3adm.js} +1 -1
- package/chunk-7cexzg71.js +2 -0
- package/chunk-98rann8t.js +2 -0
- package/{chunk-e56eb38j.js → chunk-a16mz9zf.js} +1 -1
- package/chunk-afe25dpr.js +2 -0
- package/{chunk-zkn1m8tw.js → chunk-ahm4zd56.js} +1 -1
- package/chunk-asvdqkdp.js +6 -0
- package/{chunk-8sfx5x8k.js → chunk-b3x80jc1.js} +1 -1
- package/{chunk-4rvmpsda.js → chunk-cdqah1hy.js} +1 -1
- package/{chunk-z1r0sc3k.js → chunk-cqt1dpxd.js} +1 -1
- package/{chunk-k10n8na7.js → chunk-d4r0myxr.js} +1 -1
- package/{chunk-cdxy4xzs.js → chunk-d9kx7v12.js} +1 -1
- package/{chunk-t4zss1tk.js → chunk-df557bn4.js} +1 -1
- package/{chunk-25j8vgpa.js → chunk-edgwx026.js} +1 -1
- package/chunk-emv1j50t.js +2 -0
- package/{chunk-3gbmq4jz.js → chunk-ewgdcgrk.js} +1 -1
- package/chunk-f45p6kcb.js +2 -0
- package/{chunk-8krqgzny.js → chunk-gn9wcc1b.js} +1 -1
- package/{chunk-vbe2r4kv.js → chunk-hr3gbak5.js} +2 -2
- package/{chunk-y0rwf1ze.js → chunk-j7tzbt3d.js} +1 -1
- package/chunk-mm8wv3bg.js +3 -0
- package/chunk-nbbnb5e9.js +3 -0
- package/{chunk-c4fym47a.js → chunk-ngbqd7kb.js} +32 -12
- package/chunk-nkqtnwzz.js +2 -0
- package/{chunk-jtsn41cs.js → chunk-q3kf8243.js} +1 -1
- package/{chunk-c6hywdvq.js → chunk-qpv28z8d.js} +1 -1
- package/{chunk-n5acf0s3.js → chunk-rd65xm6r.js} +1 -1
- package/{chunk-ys3s5gaa.js → chunk-ryga3ny1.js} +1 -1
- package/{chunk-cvn689d5.js → chunk-sb5h27w9.js} +2 -2
- package/chunk-tw4kw4ps.js +22 -0
- package/chunk-v6c3h9ft.js +3 -0
- package/chunk-vgx47anc.js +3 -0
- package/chunk-vrvkvb0v.js +244 -0
- package/{chunk-v0syg2hx.js → chunk-vryadew3.js} +9 -7
- package/chunk-w6f40y2x.js +11 -0
- package/{chunk-c5zjen06.js → chunk-we8hvy6e.js} +1 -1
- package/chunk-wkqrb2tz.js +4 -0
- package/{chunk-js6mz7h1.js → chunk-xzkczmgg.js} +1 -1
- package/{chunk-8yadb6xz.js → chunk-ycrddh47.js} +1 -1
- package/{chunk-bhx8k09w.js → chunk-z78c1kf8.js} +1 -1
- package/cli.js +1 -1
- package/package.json +3 -1
- package/pkgs/provider-html/src/index.ts +68 -0
- package/pkgs/provider-html/src/registry.ts +121 -0
- package/pkgs/provider-html/src/version.ts +1 -0
- package/pkgs/provider-none/src/components-inline.tsx +12 -3
- package/pkgs/provider-none/src/components.tsx +11 -3
- package/pkgs/provider-none/src/manifest.ts +1 -0
- package/pkgs/schema/src/comment.ts +12 -0
- package/pkgs/schema/src/config.ts +33 -1
- package/pkgs/schema/src/css-declarations.ts +41 -0
- package/pkgs/schema/src/index.ts +4 -0
- package/pkgs/schema/src/theme.ts +40 -0
- package/plugins/claude/velloo/agents/velloo-design-reviewer.md +56 -13
- package/plugins/claude/velloo/agents/velloo-designer.md +4 -1
- package/skills/velloo-setup/SKILL.md +19 -55
- package/canvas/assets/index-CSSyvzzo.css +0 -2
- package/canvas/assets/index-MXiDRs_Q.js +0 -29
- package/canvas/assets/lucide-all-BSrW0hTo.js +0 -1
- package/chunk-0zen7bzb.js +0 -3
- package/chunk-1tdh4h14.js +0 -6
- package/chunk-38ev3cp3.js +0 -3
- package/chunk-5qd2ykvw.js +0 -26
- package/chunk-6qfyem23.js +0 -2
- package/chunk-6z0kpe65.js +0 -2
- package/chunk-8g5ghy46.js +0 -5
- package/chunk-cgzhcvsj.js +0 -2
- package/chunk-egccxmed.js +0 -688
- package/chunk-fjbzk4sw.js +0 -3
- package/chunk-gsne0ap4.js +0 -2
- package/chunk-nc5k1ez4.js +0 -2
- package/chunk-njhdn9j0.js +0 -4
- package/chunk-q4d60wr3.js +0 -3
- package/chunk-rsy246re.js +0 -2
- package/chunk-syg6k8m0.js +0 -10
- package/chunk-t8xk4thc.js +0 -244
- package/chunk-tqwe5tc3.js +0 -2
- package/chunk-va9v9y5m.js +0 -2
- package/chunk-yjmawrv5.js +0 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{BoardSchema,isArchived,designNameIssue,toDesignName,ConfigSchema,ScreenSchema}from"./chunk-nte0t7v3.js";import{writeJsonAtomic,listLocalDesigns,writeLocalDesign,readRepoManifestAt,findOwningManifest,recordedDesignName,findDesigns2,pickDesign}from"./chunk-egccxmed.js";import{isCancel,multiselect,select}from"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,rm,stat}from"fs/promises";import{homedir}from"os";import{basename,dirname,isAbsolute,join,relative,resolve,sep}from"path";var MANIFEST_FILE="velloo.json",DEFAULT_FOLDER_NAME="velloo";function findManifest(dir){return readRepoManifestAt(dir,{strict:!0})}function manifestListing(folder){return findOwningManifest(folder,{strict:!0})}function findDesigns(cwd){return findDesigns2(cwd,{strict:!0})}function sameFolder(a,b){return a===b||existsSync(a)&&existsSync(b)&&realpathSync(a)===realpathSync(b)}function assertCurrent(found){if(found.legacy)throw Error(`${found.path} is in the pre-designs format (\`projects\`) \u2014 run \`velloo upgrade\` to migrate it first.`)}async function unregisterDesign(folder,projectDir){let abs=resolve(folder),here=projectDir?await findManifest(projectDir):null,found=here?.folders.some((path)=>sameFolder(path,abs))?here:await manifestListing(abs);if(!found)return{name:null,path:null,removedManifest:!1};let index=found.folders.findIndex((path)=>sameFolder(path,abs));if(index===-1)return{name:null,path:found.path,removedManifest:!1};assertCurrent(found);let name=recordedDesignName(abs)??basename(abs),manifest=withoutDesign(found.manifest,index,name);if(manifest===null)return await rm(found.path,{force:!0}),{name,path:found.path,removedManifest:!0};return await writeJsonAtomic(found.path,manifest),{name,path:found.path,removedManifest:!1}}function withoutDesign(manifest,index,name){let designs=manifest.designs.filter((_,i)=>i!==index),{defaultDesign,...rest}=manifest;if(designs.length===0&&!rest.feedback)return null;return{...rest,designs,...defaultDesign&&defaultDesign!==name?{defaultDesign}:{}}}function isWithin(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!isAbsolute(rel)}async function findGitRoot(start){let home=homedir(),dir=resolve(start);while(dir!==home){try{return await stat(join(dir,".git")),dir}catch{}let parent=dirname(dir);if(parent===dir)return null;dir=parent}return null}var SCAN_SKIP=new Set(["node_modules",".git","dist","build",".next",".turbo","coverage",".velloo"]);async function discoverDesignFolders(root,maxDepth=3){let out=[],walk=async(dir,depth)=>{let entries;try{entries=await readdir(dir,{withFileTypes:!0})}catch{return}for(let entry of entries){if(!entry.isDirectory()||entry.name.startsWith(".")||SCAN_SKIP.has(entry.name))continue;let child=join(dir,entry.name);try{await stat(join(child,".design","config.json")),out.push(child);continue}catch{}if(depth<maxDepth)await walk(child,depth+1)}};return await walk(resolve(root),1),out.sort()}function deriveName(folder,projectDir){let base=basename(folder),nested=base===DEFAULT_FOLDER_NAME&&dirname(folder)!==projectDir;return toDesignName(nested?basename(dirname(folder)):base)??"app"}async function chooseDesignName(request){let folder=resolve(request.folder),issue=request.requested===void 0?null:designNameIssue(request.requested);if(issue)throw Error(`invalid --name ${JSON.stringify(request.requested)} \u2014 ${issue}`);let taken=await takenNames(request,folder);if(request.requested){let holder=taken.get(request.requested);if(holder)throw Error(`a design named "${request.requested}" already exists at ${holder} \u2014 pick another --name.`);return request.requested}let base;if(request.storage==="repository")base=deriveName(folder,resolve(request.checkout));else{let chosen=request.storage==="chosen"&&basename(folder)!==DEFAULT_FOLDER_NAME;base=deriveName(chosen?folder:request.appRoot)}let name=base;for(let n=2;taken.has(name);n++)name=`${base}-${n}`;return name}async function takenNames(request,folder){let from=request.checkout,taken=new Map,set=await findDesigns(from).catch(()=>null);for(let design of set?.designs??[])if(!sameFolder(design.root,folder))taken.set(design.name,design.root);if(!set?.repo)for(let sibling of await discoverDesignFolders(request.checkout,1)){let name=recordedDesignName(sibling);if(name&&!sameFolder(sibling,folder))taken.set(name,sibling)}return taken}async function writeDesignName(folder,name){let path=join(folder,".design","config.json"),config=ConfigSchema.parse(JSON.parse(await readFile(path,"utf8")));await writeJsonAtomic(path,{...config,name})}async function registerDesign(folder,projectDir){let abs=resolve(folder),name=recordedDesignName(abs);if(!name)throw Error(`${abs} has no design name \u2014 run \`velloo design upgrade ${abs}\` first.`);let dir=resolve(projectDir),found=await findManifest(dir),path=found?found.path:join(dir,MANIFEST_FILE);if(found?.folders.some((target)=>sameFolder(target,abs)))return{name,path,created:!1};if(found)assertCurrent(found);let clash=(await findDesigns(dir))?.designs.find((d)=>d.name===name&&!sameFolder(d.root,abs));if(clash)throw Error(`a design named "${name}" already exists at ${clash.root} \u2014 rename one with \`velloo design rename\`.`);let designs=[...found?.manifest.designs??[]],rel=(target)=>relative(dir,target).split("\\").join("/")||".",defaultDesign=found?.manifest.defaultDesign;if(!found)for(let sibling of await discoverDesignFolders(dir,1)){if(sibling===abs)continue;if(designs.push(rel(sibling)),sibling===join(dir,DEFAULT_FOLDER_NAME))defaultDesign=recordedDesignName(sibling)??void 0}designs.push(rel(abs));let manifest={...found?.manifest??{},designs,...defaultDesign?{defaultDesign}:{}};return await writeJsonAtomic(path,manifest),{name,path,created:!0}}async function registerLocalDesign(opts){await writeLocalDesign(opts.id,{root:opts.root,appRoot:opts.appRoot,...opts.designPath?{designPath:opts.designPath}:{}})}import{existsSync as existsSync2,readdirSync}from"fs";import{readdir as readdir2,readFile as readFile2}from"fs/promises";import{homedir as homedir2}from"os";import{basename as basename2,dirname as dirname2,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";var DEFAULT_FOLDER="velloo",DESIGN_ARG_DESCRIPTION="A design name from ./velloo.json, a design folder, or a velloo.json file or its directory (default: the design folder you're in, else this directory's velloo.json: its only design or defaultDesign)";async function existingDesignFolder(appRoot){try{let found=await findDesigns(appRoot);for(let design of found?.designs??[])if(design.exists)return design.root}catch{}let conventional=join2(resolve2(appRoot),DEFAULT_FOLDER);if(await hasDesignConfig(conventional))return conventional;let[first]=await discoverDesignFolders(resolve2(appRoot));return first??null}function isDesignFolderSync(dir){return existsSync2(join2(dir,".design","config.json"))}function isEmptyOrMissingSync(dir){try{return readdirSync(dir).length===0}catch(err){if(err.code==="ENOENT")return!0;throw err}}async function hasDesignConfig(dir){try{return await readFile2(join2(dir,".design","config.json"),"utf8"),!0}catch{return!1}}var DESIGN_ID_ARG={type:"string",description:"The design's number in `velloo design list`, instead of its name or path"};async function designFolder(set,design,abort){let repo=set.repo;if(repo&&design.outsideRepo){let entry=relative2(repo.dir,design.root);abort(`design ${JSON.stringify(design.name)} in ${repo.path} points outside the repository (${entry}), which velloo.json cannot authorize. Pass the design path explicitly, or run \`velloo design bind ${design.root}\` here to keep it as a local design on this machine.`)}if(!design.exists)abort(`design ${JSON.stringify(design.name)} ${design.local?"(a local design)":`in ${repo?.path}`} points at ${design.root}, which is not a velloo design folder (no .design/config.json).`);return design.root}function quoteArg(value){return/^[\w@%+=:,./-]+$/.test(value)?value:`'${value.replace(/'/g,"'\\''")}'`}function designsLabel(set){return set.repo?.path??"this checkout's local designs"}function byName(set,name,abort){let shared=set.conflicts.get(name);if(shared)abort(`${shared.length} designs are named ${JSON.stringify(name)}: ${shared.map((d)=>d.root).join(", ")}. Pass a path, and rename one with \`velloo design rename <path> <new-name>\`.`);return set.byName.get(name)}async function resolveDesign(arg,cmd,opts={}){let abort=opts.onFail??((m)=>fail(cmd,m)),cwd=resolve2(opts.cwd??"."),set=null;try{set=await findDesigns(cwd)}catch(err){abort(err.message)}if(opts.id!==void 0){if(arg)abort("pass a design or --id, not both.");let n=Number(opts.id);if(!Number.isInteger(n)||n<1)abort("--id takes a design's number from `velloo design list`.");if(!set||set.designs.length===0){if(n===1)return resolveDesign(void 0,cmd,{...opts,id:void 0});abort(`there is no design #${n} here \u2014 \`velloo design list\` numbers them.`)}let entry=set.designs[n-1];if(!entry)abort(`there is no design #${n} \u2014 ${designsLabel(set)} has ${set.designs.length}. \`velloo design list\` numbers them.`);return designFolder(set,entry,abort)}if(arg){let named=set&&!arg.includes(sep2)?byName(set,arg,abort):void 0;if(set&&named)return designFolder(set,named,abort);let explicit=resolve2(cwd,arg);if(await hasDesignConfig(explicit))return explicit;let project=basename2(explicit)==="velloo.json"&&existsSync2(explicit)?dirname2(explicit):explicit;if((await findDesigns(project).catch((err)=>abort(err.message)))?.designs.length)return resolveDesign(void 0,cmd,{...opts,cwd:project});let nested=join2(explicit,DEFAULT_FOLDER);if(await hasDesignConfig(nested))return nested;if(!set&&!arg.includes(sep2)){let above=await enclosingProject(cwd),match=above?.designs.find((d)=>d.name===arg);if(above&&match)abort(`design ${JSON.stringify(arg)} is listed in \`${shortPath(cwd,above.label)}\`, and commands only read the directory they run in. Run from \`${shortPath(cwd,above.dir)}\`, or pass its folder (\`${quoteArg(shortPath(cwd,match.root))}\`) instead of its name.`)}if(set&&!arg.includes(sep2))abort(`unknown design ${JSON.stringify(arg)} \u2014 ${designsLabel(set)} lists: ${set.designs.map((d)=>d.name).join(", ")}. Pass a design name or a folder path.`);if(opts.requireConfig)abort(`${explicit} is not a velloo design folder (no .design/config.json).`);return explicit}if(set&&set.designs.length>0){if(await hasDesignConfig(cwd))return cwd;let picked=pickDesign(set,cwd);if(picked&&picked.reason!=="arbitrary")return designFolder(set,picked.design,abort);let names=set.designs.map((d)=>d.name);if(opts.interactive&&process.stdin.isTTY){let chosen=await select({message:"Which design?",options:set.designs.map((d)=>({value:d.root,label:d.name,hint:relative2(cwd,d.root)}))});if(isCancel(chosen))abort("cancelled.");let design=set.designs.find((d)=>d.root===chosen);return designFolder(set,design,abort)}let example=cmd.startsWith("design ")?`velloo ${cmd} --id 1\` for ${quoteArg(names[0]??"")}, as numbered by \`velloo design list`:`velloo ${cmd} ${designExample(opts,names[0]??"")}`;abort(`${designsLabel(set)} lists several designs: ${names.join(", ")}. Pass one (e.g. \`${example}\`)${set.repo?' or set "defaultDesign"':""}.`)}if(await hasDesignConfig(cwd))return cwd;let here=join2(cwd,DEFAULT_FOLDER);if(await hasDesignConfig(here))return here;let takesDesign=opts.takesDesign!==!1,above=await enclosingProject(cwd);if(above){let nearest=[...above.designs].sort((a,b)=>relative2(cwd,a.root).length-relative2(cwd,b.root).length)[0],example=takesDesign&&nearest?`, or pass a design folder (e.g. \`velloo ${cmd} ${designExample(opts,relative2(cwd,nearest.root).split(sep2).join("/"))}\`)`:"";abort(`no velloo.json or design folder here. \`${shortPath(cwd,above.label)}\` lists ${above.designs.map((d)=>JSON.stringify(d.name)).join(", ")}, and commands only read the directory they run in: run from \`${shortPath(cwd,above.dir)}\`${example}.`)}let example=takesDesign?`, pass a design folder or a velloo.json path (e.g. \`velloo ${cmd} ${designExample(opts,"apps/web")}\`)`:"";abort(`no velloo.json or design folder in ${cwd}. Run from the directory that has your velloo.json${example}, or \`velloo init\` here.${movedCheckoutHint(cwd)}`)}function shortPath(cwd,path){let rel=relative2(cwd,path);return rel!==""&&rel.length<path.length?rel.split(sep2).join("/"):path}function designExample(opts,design){return opts.designFlag?`${opts.designFlag}=${quoteArg(design)}`:quoteArg(design)}async function enclosingProject(cwd){let here=resolve2(cwd);for(let dir=dirname2(here);dir!==here;){let set=await findDesigns(dir).catch(()=>null),designs=set?.designs.filter((d)=>d.exists&&!d.outsideRepo)??[];if(set&&designs.length>0)return{dir,label:set.repo?.path??`${dir} (local designs)`,designs};let parent=dirname2(dir);if(parent===dir)return null;dir=parent}return null}async function checkoutDesigns(cwd){let set=await findDesigns(cwd).catch(()=>null);if(set&&set.designs.length>0)return set.designs.filter((d)=>d.exists&&!d.outsideRepo).map((d)=>d.root);try{return[await resolveDesign(void 0,"upgrade",{requireConfig:!0,cwd,onFail:()=>{throw Error("no design here")}})]}catch{return[]}}async function resolveDesignForSession(arg,cmd,cwd=resolve2(".")){if(!arg&&!await hasDesignConfig(cwd)){let set=await findDesigns(cwd).catch(()=>null),picked=set?pickDesign(set,cwd):null;if(set&&picked?.reason==="arbitrary")return{folder:await designFolder(set,picked.design,(m)=>fail(cmd,m)),pick:"arbitrary"}}return{folder:await resolveDesign(arg,cmd,{cwd}),pick:void 0}}function movedCheckoutHint(cwd){if(!insideProject(cwd))return"";let orphans=listLocalDesigns().filter((design)=>!existsSync2(design.root)&&existsSync2(design.designRoot));if(orphans.length===0)return"";return`
|
|
2
|
+
import{BoardSchema,isArchived,designNameIssue,toDesignName,ConfigSchema,ScreenSchema}from"./chunk-4pkw6d9n.js";import{writeJsonAtomic,listLocalDesigns,writeLocalDesign,readRepoManifestAt,findOwningManifest,recordedDesignName,findDesigns2,pickDesign}from"./chunk-66qgm91k.js";import{isCancel,multiselect,select}from"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,rm,stat}from"fs/promises";import{homedir}from"os";import{basename,dirname,isAbsolute,join,relative,resolve,sep}from"path";var MANIFEST_FILE="velloo.json",DEFAULT_FOLDER_NAME="velloo";function findManifest(dir){return readRepoManifestAt(dir,{strict:!0})}function manifestListing(folder){return findOwningManifest(folder,{strict:!0})}function findDesigns(cwd){return findDesigns2(cwd,{strict:!0})}function sameFolder(a,b){return a===b||existsSync(a)&&existsSync(b)&&realpathSync(a)===realpathSync(b)}function assertCurrent(found){if(found.legacy)throw Error(`${found.path} is in the pre-designs format (\`projects\`) \u2014 run \`velloo upgrade\` to migrate it first.`)}async function unregisterDesign(folder,projectDir){let abs=resolve(folder),here=projectDir?await findManifest(projectDir):null,found=here?.folders.some((path)=>sameFolder(path,abs))?here:await manifestListing(abs);if(!found)return{name:null,path:null,removedManifest:!1};let index=found.folders.findIndex((path)=>sameFolder(path,abs));if(index===-1)return{name:null,path:found.path,removedManifest:!1};assertCurrent(found);let name=recordedDesignName(abs)??basename(abs),manifest=withoutDesign(found.manifest,index,name);if(manifest===null)return await rm(found.path,{force:!0}),{name,path:found.path,removedManifest:!0};return await writeJsonAtomic(found.path,manifest),{name,path:found.path,removedManifest:!1}}function withoutDesign(manifest,index,name){let designs=manifest.designs.filter((_,i)=>i!==index),{defaultDesign,...rest}=manifest;if(designs.length===0&&!rest.feedback)return null;return{...rest,designs,...defaultDesign&&defaultDesign!==name?{defaultDesign}:{}}}function isWithin(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!isAbsolute(rel)}async function findGitRoot(start){let home=homedir(),dir=resolve(start);while(dir!==home){try{return await stat(join(dir,".git")),dir}catch{}let parent=dirname(dir);if(parent===dir)return null;dir=parent}return null}var SCAN_SKIP=new Set(["node_modules",".git","dist","build",".next",".turbo","coverage",".velloo"]);async function discoverDesignFolders(root,maxDepth=3){let out=[],walk=async(dir,depth)=>{let entries;try{entries=await readdir(dir,{withFileTypes:!0})}catch{return}for(let entry of entries){if(!entry.isDirectory()||entry.name.startsWith(".")||SCAN_SKIP.has(entry.name))continue;let child=join(dir,entry.name);try{await stat(join(child,".design","config.json")),out.push(child);continue}catch{}if(depth<maxDepth)await walk(child,depth+1)}};return await walk(resolve(root),1),out.sort()}function deriveName(folder,projectDir){let base=basename(folder),nested=base===DEFAULT_FOLDER_NAME&&dirname(folder)!==projectDir;return toDesignName(nested?basename(dirname(folder)):base)??"app"}async function chooseDesignName(request){let folder=resolve(request.folder),issue=request.requested===void 0?null:designNameIssue(request.requested);if(issue)throw Error(`invalid --name ${JSON.stringify(request.requested)} \u2014 ${issue}`);let taken=await takenNames(request,folder);if(request.requested){let holder=taken.get(request.requested);if(holder)throw Error(`a design named "${request.requested}" already exists at ${holder} \u2014 pick another --name.`);return request.requested}let base;if(request.storage==="repository")base=deriveName(folder,resolve(request.checkout));else{let chosen=request.storage==="chosen"&&basename(folder)!==DEFAULT_FOLDER_NAME;base=deriveName(chosen?folder:request.appRoot)}let name=base;for(let n=2;taken.has(name);n++)name=`${base}-${n}`;return name}async function takenNames(request,folder){let from=request.checkout,taken=new Map,set=await findDesigns(from).catch(()=>null);for(let design of set?.designs??[])if(!sameFolder(design.root,folder))taken.set(design.name,design.root);if(!set?.repo)for(let sibling of await discoverDesignFolders(request.checkout,1)){let name=recordedDesignName(sibling);if(name&&!sameFolder(sibling,folder))taken.set(name,sibling)}return taken}async function writeDesignName(folder,name){let path=join(folder,".design","config.json"),config=ConfigSchema.parse(JSON.parse(await readFile(path,"utf8")));await writeJsonAtomic(path,{...config,name})}async function registerDesign(folder,projectDir){let abs=resolve(folder),name=recordedDesignName(abs);if(!name)throw Error(`${abs} has no design name \u2014 run \`velloo design upgrade ${abs}\` first.`);let dir=resolve(projectDir),found=await findManifest(dir),path=found?found.path:join(dir,MANIFEST_FILE);if(found?.folders.some((target)=>sameFolder(target,abs)))return{name,path,created:!1};if(found)assertCurrent(found);let clash=(await findDesigns(dir))?.designs.find((d)=>d.name===name&&!sameFolder(d.root,abs));if(clash)throw Error(`a design named "${name}" already exists at ${clash.root} \u2014 rename one with \`velloo design rename\`.`);let designs=[...found?.manifest.designs??[]],rel=(target)=>relative(dir,target).split("\\").join("/")||".",defaultDesign=found?.manifest.defaultDesign;if(!found)for(let sibling of await discoverDesignFolders(dir,1)){if(sibling===abs)continue;if(designs.push(rel(sibling)),sibling===join(dir,DEFAULT_FOLDER_NAME))defaultDesign=recordedDesignName(sibling)??void 0}designs.push(rel(abs));let manifest={...found?.manifest??{},designs,...defaultDesign?{defaultDesign}:{}};return await writeJsonAtomic(path,manifest),{name,path,created:!0}}async function registerLocalDesign(opts){await writeLocalDesign(opts.id,{root:opts.root,appRoot:opts.appRoot,...opts.designPath?{designPath:opts.designPath}:{}})}import{existsSync as existsSync2,readdirSync}from"fs";import{readdir as readdir2,readFile as readFile2}from"fs/promises";import{homedir as homedir2}from"os";import{basename as basename2,dirname as dirname2,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";var DEFAULT_FOLDER="velloo",DESIGN_ARG_DESCRIPTION="A design name from ./velloo.json, a design folder, or a velloo.json file or its directory (default: the design folder you're in, else this directory's velloo.json: its only design or defaultDesign)";async function existingDesignFolder(appRoot){try{let found=await findDesigns(appRoot);for(let design of found?.designs??[])if(design.exists)return design.root}catch{}let conventional=join2(resolve2(appRoot),DEFAULT_FOLDER);if(await hasDesignConfig(conventional))return conventional;let[first]=await discoverDesignFolders(resolve2(appRoot));return first??null}function isDesignFolderSync(dir){return existsSync2(join2(dir,".design","config.json"))}function isEmptyOrMissingSync(dir){try{return readdirSync(dir).length===0}catch(err){if(err.code==="ENOENT")return!0;throw err}}async function hasDesignConfig(dir){try{return await readFile2(join2(dir,".design","config.json"),"utf8"),!0}catch{return!1}}var DESIGN_ID_ARG={type:"string",description:"The design's number in `velloo design list`, instead of its name or path"};async function designFolder(set,design,abort){let repo=set.repo;if(repo&&design.outsideRepo){let entry=relative2(repo.dir,design.root);abort(`design ${JSON.stringify(design.name)} in ${repo.path} points outside the repository (${entry}), which velloo.json cannot authorize. Pass the design path explicitly, or run \`velloo design bind ${design.root}\` here to keep it as a local design on this machine.`)}if(!design.exists)abort(`design ${JSON.stringify(design.name)} ${design.local?"(a local design)":`in ${repo?.path}`} points at ${design.root}, which is not a velloo design folder (no .design/config.json).`);return design.root}function quoteArg(value){return/^[\w@%+=:,./-]+$/.test(value)?value:`'${value.replace(/'/g,"'\\''")}'`}function designsLabel(set){return set.repo?.path??"this checkout's local designs"}function byName(set,name,abort){let shared=set.conflicts.get(name);if(shared)abort(`${shared.length} designs are named ${JSON.stringify(name)}: ${shared.map((d)=>d.root).join(", ")}. Pass a path, and rename one with \`velloo design rename <path> <new-name>\`.`);return set.byName.get(name)}async function resolveDesign(arg,cmd,opts={}){let abort=opts.onFail??((m)=>fail(cmd,m)),cwd=resolve2(opts.cwd??"."),set=null;try{set=await findDesigns(cwd)}catch(err){abort(err.message)}if(opts.id!==void 0){if(arg)abort("pass a design or --id, not both.");let n=Number(opts.id);if(!Number.isInteger(n)||n<1)abort("--id takes a design's number from `velloo design list`.");if(!set||set.designs.length===0){if(n===1)return resolveDesign(void 0,cmd,{...opts,id:void 0});abort(`there is no design #${n} here \u2014 \`velloo design list\` numbers them.`)}let entry=set.designs[n-1];if(!entry)abort(`there is no design #${n} \u2014 ${designsLabel(set)} has ${set.designs.length}. \`velloo design list\` numbers them.`);return designFolder(set,entry,abort)}if(arg){let named=set&&!arg.includes(sep2)?byName(set,arg,abort):void 0;if(set&&named)return designFolder(set,named,abort);let explicit=resolve2(cwd,arg);if(await hasDesignConfig(explicit))return explicit;let project=basename2(explicit)==="velloo.json"&&existsSync2(explicit)?dirname2(explicit):explicit;if((await findDesigns(project).catch((err)=>abort(err.message)))?.designs.length)return resolveDesign(void 0,cmd,{...opts,cwd:project});let nested=join2(explicit,DEFAULT_FOLDER);if(await hasDesignConfig(nested))return nested;if(!set&&!arg.includes(sep2)){let above=await enclosingProject(cwd),match=above?.designs.find((d)=>d.name===arg);if(above&&match)abort(`design ${JSON.stringify(arg)} is listed in \`${shortPath(cwd,above.label)}\`, and commands only read the directory they run in. Run from \`${shortPath(cwd,above.dir)}\`, or pass its folder (\`${quoteArg(shortPath(cwd,match.root))}\`) instead of its name.`)}if(set&&!arg.includes(sep2))abort(`unknown design ${JSON.stringify(arg)} \u2014 ${designsLabel(set)} lists: ${set.designs.map((d)=>d.name).join(", ")}. Pass a design name or a folder path.`);if(opts.requireConfig)abort(`${explicit} is not a velloo design folder (no .design/config.json).`);return explicit}if(set&&set.designs.length>0){if(await hasDesignConfig(cwd))return cwd;let picked=pickDesign(set,cwd);if(picked&&picked.reason!=="arbitrary")return designFolder(set,picked.design,abort);let names=set.designs.map((d)=>d.name);if(opts.interactive&&process.stdin.isTTY){let chosen=await select({message:"Which design?",options:set.designs.map((d)=>({value:d.root,label:d.name,hint:relative2(cwd,d.root)}))});if(isCancel(chosen))abort("cancelled.");let design=set.designs.find((d)=>d.root===chosen);return designFolder(set,design,abort)}let example=cmd.startsWith("design ")?`velloo ${cmd} --id 1\` for ${quoteArg(names[0]??"")}, as numbered by \`velloo design list`:`velloo ${cmd} ${designExample(opts,names[0]??"")}`;abort(`${designsLabel(set)} lists several designs: ${names.join(", ")}. Pass one (e.g. \`${example}\`)${set.repo?' or set "defaultDesign"':""}.`)}if(await hasDesignConfig(cwd))return cwd;let here=join2(cwd,DEFAULT_FOLDER);if(await hasDesignConfig(here))return here;let takesDesign=opts.takesDesign!==!1,above=await enclosingProject(cwd);if(above){let nearest=[...above.designs].sort((a,b)=>relative2(cwd,a.root).length-relative2(cwd,b.root).length)[0],example=takesDesign&&nearest?`, or pass a design folder (e.g. \`velloo ${cmd} ${designExample(opts,relative2(cwd,nearest.root).split(sep2).join("/"))}\`)`:"";abort(`no velloo.json or design folder here. \`${shortPath(cwd,above.label)}\` lists ${above.designs.map((d)=>JSON.stringify(d.name)).join(", ")}, and commands only read the directory they run in: run from \`${shortPath(cwd,above.dir)}\`${example}.`)}let example=takesDesign?`, pass a design folder or a velloo.json path (e.g. \`velloo ${cmd} ${designExample(opts,"apps/web")}\`)`:"";abort(`no velloo.json or design folder in ${cwd}. Run from the directory that has your velloo.json${example}, or \`velloo init\` here.${movedCheckoutHint(cwd)}`)}function shortPath(cwd,path){let rel=relative2(cwd,path);return rel!==""&&rel.length<path.length?rel.split(sep2).join("/"):path}function designExample(opts,design){return opts.designFlag?`${opts.designFlag}=${quoteArg(design)}`:quoteArg(design)}async function enclosingProject(cwd){let here=resolve2(cwd);for(let dir=dirname2(here);dir!==here;){let set=await findDesigns(dir).catch(()=>null),designs=set?.designs.filter((d)=>d.exists&&!d.outsideRepo)??[];if(set&&designs.length>0)return{dir,label:set.repo?.path??`${dir} (local designs)`,designs};let parent=dirname2(dir);if(parent===dir)return null;dir=parent}return null}async function checkoutDesigns(cwd){let set=await findDesigns(cwd).catch(()=>null);if(set&&set.designs.length>0)return set.designs.filter((d)=>d.exists&&!d.outsideRepo).map((d)=>d.root);try{return[await resolveDesign(void 0,"upgrade",{requireConfig:!0,cwd,onFail:()=>{throw Error("no design here")}})]}catch{return[]}}async function resolveDesignForSession(arg,cmd,cwd=resolve2(".")){if(!arg&&!await hasDesignConfig(cwd)){let set=await findDesigns(cwd).catch(()=>null),picked=set?pickDesign(set,cwd):null;if(set&&picked?.reason==="arbitrary")return{folder:await designFolder(set,picked.design,(m)=>fail(cmd,m)),pick:"arbitrary"}}return{folder:await resolveDesign(arg,cmd,{cwd}),pick:void 0}}function movedCheckoutHint(cwd){if(!insideProject(cwd))return"";let orphans=listLocalDesigns().filter((design)=>!existsSync2(design.root)&&existsSync2(design.designRoot));if(orphans.length===0)return"";return`
|
|
3
3
|
This machine has designs for checkouts that no longer exist at their old paths:${orphans.map((design)=>`
|
|
4
4
|
"${recordedDesignName(design.designRoot)??design.legacyName??design.id}" at ${design.designRoot}, made for ${design.root}`).join("")}
|
|
5
5
|
If this is one of those checkouts, moved, attach its design with \`velloo design bind <design folder>\`. Otherwise forget it with \`velloo design remove <design folder>\`.`}function insideProject(cwd){let home=homedir2();for(let dir=cwd;dir!==home&&dirname2(dir)!==dir;dir=dirname2(dir))if(existsSync2(join2(dir,".git"))||existsSync2(join2(dir,"package.json")))return!0;return!1}async function listScreens(folder){let files;try{files=(await readdir2(join2(folder,"screens"))).filter((f)=>f.endsWith(".json"))}catch{return[]}let out=[];for(let f of files.sort()){let path=join2(folder,"screens",f);try{let s=ScreenSchema.parse(JSON.parse(await readFile2(path,"utf8")));out.push({id:s.id,name:s.name||s.id,path})}catch{}}return out}async function listBoards(folder){let files;try{files=(await readdir2(join2(folder,"boards"))).filter((f)=>f.endsWith(".json"))}catch{return[]}let out=[];for(let f of files.sort())try{let b=BoardSchema.parse(JSON.parse(await readFile2(join2(folder,"boards",f),"utf8")));out.push({id:b.id,name:b.name||b.id,screens:b.frames.map((fr)=>fr.screen),archived:isArchived(b)})}catch{}return out}function boardsByScreen(boards){let map=new Map;for(let b of boards)for(let sid of b.screens){let names=map.get(sid)??[];names.push(b.name),map.set(sid,names)}return map}async function pickBoards(folder,arg,interactive,cmd){let all=await listBoards(folder);if(all.length===0)return[];if(arg){let ids=new Set(arg.split(",").map((s)=>s.trim()).filter(Boolean)),chosen=all.filter((b)=>ids.has(b.id));if(chosen.length===0)fail(cmd,`no boards matched "${arg}". Available: ${all.map((b)=>b.id).join(", ")}`);return chosen}let boards=all.filter((b)=>!b.archived);if(boards.length===0)return[];if(!interactive)return boards;let chosen=await multiselect(boardSelectionPrompt(boards));if(isCancel(chosen))fail(cmd,"cancelled.");return resolveBoardSelection(boards,chosen)}function boardSelectionPrompt(boards){return{message:"Which boards to publish? (a: select all / clear all)",options:boards.map((board)=>({value:board.id,label:board.name,hint:`${board.screens.length} screen${board.screens.length===1?"":"s"}`})),initialValues:[],required:!1}}function resolveBoardSelection(boards,selectedIds){let selected=new Set(selectedIds),chosen=boards.filter((board)=>selected.has(board.id));return chosen.length>0?chosen:null}async function pickScreen(folder,arg,interactive,cmd){let screens=await listScreens(folder);if(arg){let byId=screens.find((s)=>s.id===arg);if(byId)return byId;return{id:arg,name:arg,path:resolve2(arg)}}if(screens.length===0)fail(cmd,`no screens found in ${folder}/screens.`);if(screens.length===1)return screens[0];if(!interactive)fail(cmd,`pick a screen: ${screens.map((s)=>s.id).join(", ")}`);let onBoard=boardsByScreen(await listBoards(folder)),chosen=await select({message:"Which screen?",options:screens.map((s)=>({value:s.id,label:s.name,hint:onBoard.get(s.id)?.join(", ")??`${s.id}.json`}))});if(isCancel(chosen))fail(cmd,"cancelled.");return screens.find((s)=>s.id===chosen)}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{loadDesignFolder,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-
|
|
2
|
+
import{loadDesignFolder,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-66qgm91k.js";import{join}from"path";async function loadPipeline(folderPath){let design=await loadDesignFolder(folderPath),config=design.config,{providers,defaultProvider}=await resolveProviders(config,folderPath),jit=new TailwindJit(Object.values(providers),join(folderPath,"screens"),void 0,()=>extraThemeBlock(design),void 0,()=>findHostTailwindConfig(folderPath,config.hostApp),config.styling?.framework),snapshotCss=await jit.build();return{folderPath,design,config,providers,defaultProvider,jit,snapshotCss}}
|
|
3
3
|
export{loadPipeline};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{recordedDesignName}from"./chunk-
|
|
2
|
+
import{recordedDesignName}from"./chunk-66qgm91k.js";import{displayPath}from"./chunk-vryadew3.js";import{basename,relative,resolve}from"path";function designWithFolder(folder,name,opts={}){let shown=name??recordedDesignName(folder)??basename(folder),rel=relative(resolve("."),folder),where=rel!==""&&!rel.startsWith("..")&&!opts.absolute?`./${rel}`:displayPath(folder);return`"${shown}" (${where})`}
|
|
3
3
|
export{designWithFolder};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"./chunk-
|
|
2
|
+
import"./chunk-4pkw6d9n.js";import{recordedDesignName}from"./chunk-66qgm91k.js";import"./chunk-ngbqd7kb.js";import"./chunk-afe25dpr.js";import"./chunk-42bj6r30.js";import{assertLoopbackHost,daemonRoot,isLive,ensureDaemon,stopDaemon}from"./chunk-asvdqkdp.js";import"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import"./chunk-5ma8zbty.js";import"./chunk-cqt1dpxd.js";import"./chunk-sb5h27w9.js";import"./chunk-vryadew3.js";import{openUrl}from"./chunk-hggap8fq.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{designWithFolder}from"./chunk-6ytv1v2r.js";import"./chunk-4sjvdakg.js";import{findDesigns,DESIGN_ARG_DESCRIPTION,resolveDesign}from"./chunk-675tr675.js";import"./chunk-edgwx026.js";import{traceEnabled}from"./chunk-6tx68d90.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{basename,join}from"path";var import_picocolors=__toESM(require_picocolors(),1);function actionForRunKey(key,targetCount=1){if(key==="b")return{kind:"background"};if(key==="q"||key==="\x03")return{kind:"stop"};if(targetCount<=1)return key==="o"?{kind:"open"}:null;if(key==="a"||key==="o")return{kind:"open"};if(/^[1-9]$/.test(key)){let index=Number(key);return index<=targetCount?{kind:"open",index}:null}return null}function shouldStayForeground(opts){return!opts.background&&opts.stdinIsTTY}function bindRunKeys(onKey){let stdin=process.stdin;if(!stdin.isTTY)return()=>{};stdin.setRawMode(!0),stdin.resume(),stdin.setEncoding("utf8");let onData=(chunk)=>{onKey(chunk)};return stdin.on("data",onData),()=>{if(stdin.off("data",onData),stdin.isTTY)stdin.setRawMode(!1);stdin.pause()}}var LIVE_POLL_MS=2000;async function waitInForeground(opts){return await new Promise((resolve)=>{let settled=!1,unbind=()=>{},timer,finish=(outcome)=>{if(settled)return;if(settled=!0,unbind(),timer!==void 0)clearInterval(timer);process.off("SIGINT",onStop),process.off("SIGTERM",onStop),resolve(outcome)},onStop=()=>finish("stop");unbind=bindRunKeys((key)=>{let action=actionForRunKey(key,opts.targetCount??1);if(!action)return;if(action.kind==="open")opts.onOpen(action.index);else finish(action.kind)}),process.on("SIGINT",onStop),process.on("SIGTERM",onStop),timer=setInterval(()=>{opts.isLive().then((live)=>{if(!live)finish("died")})},LIVE_POLL_MS)})}import{resolve}from"path";async function resolveRunTargets(arg,opts={}){let cwd=resolve(opts.cwd??"."),single=async()=>{let folder=await resolveDesign(arg,"run",{interactive:!0,requireConfig:!0,cwd});return[{name:recordedDesignName(folder)??void 0,folder}]};if(arg)return single();let found;try{found=await findDesigns(cwd)}catch{return single()}if(!found||found.designs.length<2)return single();let targets=[];for(let design of found.designs)if(design.exists&&!design.outsideRepo)targets.push({name:design.name,folder:design.root});if(targets.length===0)return single();if(targets.length===1)return targets;let defaultDesign=found.defaultDesign;if(defaultDesign)targets.sort((a,b)=>a.name===defaultDesign?-1:b.name===defaultDesign?1:0);return targets}function label(target){return recordedDesignName(target.folder)??target.name??basename(target.folder)}function printKeys(attached){if(console.log(""),attached.length>1)attached.forEach(({target},i)=>{console.log(` ${import_picocolors.default.cyan(String(i+1))} open ${label(target)}`)}),console.log(` ${import_picocolors.default.cyan("a")} open all`);else console.log(` ${import_picocolors.default.cyan("o")} open the browser`);console.log(` ${import_picocolors.default.cyan("b")} run in the background`),console.log(` ${import_picocolors.default.cyan("q")} stop`),console.log("")}function printBackgroundStay(designArg){console.log(`velloo: it keeps running in the background \u2014 awake while the canvas is open or an agent is connected. Stop it anytime with \`velloo stop${designArg}\`.`)}var run_default=defineCommand({meta:{name:"run",description:"Open the canvas for a design folder and stay in the foreground (starts a persistent canvas if none is running)"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},port:{type:"string",description:"Preferred canvas port when starting fresh (default: the port this folder ran on last, else 7300)"},host:{type:"string",description:"Loopback bind hostname: 127.0.0.1 (default), localhost or ::1"},open:{type:"boolean",default:!1,description:"Open the canvas in your browser"},background:{type:"boolean",default:!1,description:"Leave the canvas running and return to the shell (the default when stdin is not a TTY)"}},async run({args}){assertLoopbackHost(args.host??"127.0.0.1");let targets=await resolveRunTargets(args.design,{}),preferredPort=args.port?Number(args.port):void 0;if(preferredPort!==void 0&&(!Number.isFinite(preferredPort)||preferredPort<0))fail("run",`invalid --port ${JSON.stringify(args.port)}`);let progress=createProgress();progress.start(targets.length>1?"starting canvases":"starting canvas");let spawned=0,running=[];try{for(let[index,target]of targets.entries()){if(targets.length>1)progress.step(`starting ${label(target)}`);let rec=await ensureDaemon(target.folder,{...preferredPort!==void 0&&index===0?{preferredPort}:{},host:args.host,onSpawn:()=>{spawned++,progress.step(`waiting for ${label(target)}`)}});running.push({target,rec})}progress.succeed(spawned===0?targets.length>1?"connected to running canvases":"connected to running canvas":targets.length>1?`started ${targets.length} canvases`:"started canvas")}catch(error){throw progress.fail("could not start canvas"),error}let designArg=args.design?` ${args.design}`:"",first=running[0];if(running.length===1)console.log(`velloo: design ${designWithFolder(first.target.folder,first.target.name)}`),console.log(`velloo: canvas at ${first.rec.canvasUrl}`);else{console.log("velloo: canvases");let rows=running.map(({target,rec})=>({design:designWithFolder(target.folder,target.name),url:rec.canvasUrl})),width=Math.max(...rows.map((row)=>row.design.length));for(let row of rows)console.log(` ${import_picocolors.default.bold(row.design.padEnd(width))} ${row.url}`)}if(traceEnabled())if(spawned>0)console.log(`velloo: \u29BF trace recording ON \u2014 agent MCP calls tape to ${join(first.target.folder,".velloo","trace")} (view with \`velloo trace\`)`);else console.log(`velloo: VELLOO_TRACE is set, but a canvas was already running \u2014 recording is NOT active on it. Run \`velloo stop${designArg}\`, then re-run to record. The agent must also connect through this same (trace-enabled) velloo.`);let foreground=shouldStayForeground({background:Boolean(args.background),stdinIsTTY:Boolean(process.stdin.isTTY)});if(foreground)printKeys(running);else printBackgroundStay(designArg);let attached=running,openTarget=(index)=>{let picked=index===void 0?attached:[attached[index-1]];for(let entry of picked)if(entry)openUrl(entry.rec.canvasUrl)};if(args.open)openTarget(running.length>1?void 0:1);if(!foreground)return;for(;;){let outcome=await waitInForeground({isLive:async()=>{for(let{rec}of attached)if(!await isLive(rec))return!1;return!0},onOpen:openTarget,targetCount:attached.length});if(outcome==="background"){printBackgroundStay(designArg);return}if(outcome==="died"){let alive=[];for(let entry of attached)if(await isLive(entry.rec))alive.push(entry);else console.log(`velloo: the canvas for "${label(entry.target)}" stopped.`);if(attached=alive,attached.length===0){console.log(`velloo: no canvases left. Start again with \`velloo run${designArg}\`.`);return}console.log(import_picocolors.default.dim(` Still running: ${attached.map(({target})=>label(target)).join(", ")}.`)),printKeys(attached);continue}for(let{target}of attached){let stopped=await stopDaemon(daemonRoot(target.folder));console.log(stopped?`velloo: stopped ${designWithFolder(target.folder)}`:`velloo: no canvas running for ${designWithFolder(target.folder)}`)}return}}});export{run_default as default};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{STYLE_PROP}from"./chunk-66qgm91k.js";import{styleObjectFromCss}from"./chunk-4pkw6d9n.js";import{NONE_MANIFEST2,inlineRegistry,entryCssPath2,componentsDir2}from"./chunk-mm8wv3bg.js";import{require_react_development}from"./chunk-ngbqd7kb.js";import{__toESM}from"./chunk-2smsg2ct.js";import{HTML_VERSION}from"./chunk-6k05kqnc.js";var import_react=__toESM(require_react_development(),1),SAFE_TAG=/^(?:a|abbr|address|article|aside|b|bdi|bdo|blockquote|br|button|caption|circle|cite|clipPath|code|col|colgroup|data|dd|defs|del|details|dfn|div|dl|dt|ellipse|em|fieldset|figcaption|figure|footer|form|g|h[1-6]|header|hr|i|img|input|ins|kbd|label|legend|li|line|linearGradient|main|mark|mask|meter|nav|ol|optgroup|option|output|p|path|picture|polygon|polyline|pre|progress|q|radialGradient|rect|s|samp|section|select|small|source|span|stop|strong|sub|summary|sup|svg|table|tbody|td|text|textarea|tfoot|th|thead|time|tr|tspan|u|ul|var|wbr)$/,URL_ATTRIBUTES=new Set(["href","src","action","formaction","poster","cite","xlinkhref","xlink:href"]),SCRIPT_URL=/^(?:javascript|vbscript):|^data:text\/html/i,withoutControls=(value)=>[...value].filter((char)=>char.charCodeAt(0)>32).join(""),REACT_PROP={class:"className",for:"htmlFor",autocomplete:"autoComplete",autofocus:"autoFocus",tabindex:"tabIndex",readonly:"readOnly",maxlength:"maxLength",minlength:"minLength",colspan:"colSpan",rowspan:"rowSpan",enctype:"encType",novalidate:"noValidate",datetime:"dateTime",srcset:"srcSet",crossorigin:"crossOrigin",accesskey:"accessKey",contenteditable:"contentEditable",spellcheck:"spellCheck",formaction:"formAction",formmethod:"formMethod",inputmode:"inputMode",referrerpolicy:"referrerPolicy"};function safeAttributeValue(name,value){let key=name.toLowerCase();if(key.startsWith("on")||key==="dangerouslysetinnerhtml")return!1;if(URL_ATTRIBUTES.has(key)&&typeof value==="string")return!SCRIPT_URL.test(withoutControls(value));return!0}function safeTag(component,as){if(!SAFE_TAG.test(as))throw Error(`${component}: unsupported HTML tag ${JSON.stringify(as)}`);return as}function nativeAttributes(props){return Object.fromEntries(Object.entries(props).map(([name,value])=>[REACT_PROP[name]??name,value]).filter(([name,value])=>safeAttributeValue(name,value)).flatMap(([name,value])=>{if(name!=="style"||typeof value!=="string")return[[name,value]];let style=styleObjectFromCss(value);return style?[[name,style]]:[]}))}function Html({as="div",children,...props}){return import_react.createElement(safeTag("Html",as),nativeAttributes(props),children)}var registry={...inlineRegistry,Html};var htmlDescriptors=[{id:"Html",category:"ui",source:"velloo",group:"layout",family:"Html",allowUnknownProps:!0,designModeNotes:"Use for a native HTML element. Set as to a semantic tag and pass ordinary HTML or hx-* attributes as props.",props:[{name:"as",type:"string",optional:!0,control:"string",defaultValue:"div"},{name:"className",type:"string",optional:!0,control:"string"},{name:"style",type:"CSSProperties",optional:!0,control:"string"},{name:"children",type:"ReactNode",optional:!0,control:"string"}]}],HTML_MANIFEST=[...NONE_MANIFEST2,...htmlDescriptors],HTML_INTRO=["**This is a server-rendered HTML/htmx app.** Compose semantic HTML with `Html` nodes (`as=\"form\"`, \u2026), styled by the app's own CSS classes or inline `style` \u2014 there is no Tailwind; new classes need CSS authored in the app. Give forms and controls the real `hx-*` attributes: the canvas never fires them, and `emit_code` keeps them in the native HTML it returns for the app's templates.","","A design never contacts the running app: it is styled by copies of the app's stylesheets, which `store_host_files` refreshes from source or from a capture (including built CSS a `screenshot` reports missing). Reference the app's files by their real paths (`src=\"/static/logo.png\"`), not as Velloo assets. Theme tokens are CSS variables (`var(--color-primary)`); `emit_theme` writes the stylesheet that defines them.","","To reproduce a page, capture it from the running app with `start_capture_session` (the user signs in if needed), rebuild it from `get_capture` with the content written into the nodes, and verify with `compare_to_url { captureId }`.","","On a form, a custom `hx-trigger` replaces htmx's default submit trigger \u2014 include `submit` when a submit button must swap a response instead of navigating.",""];function createProvider(){return{id:"html",version:HTML_VERSION,label:"HTML + htmx",componentsDir:componentsDir2,styleEntryPath:entryCssPath2,registry,loadManifest:async()=>HTML_MANIFEST,styleChannel:STYLE_PROP,styleChannels:["style"],registryForChannel:()=>registry,mcpIntro:()=>HTML_INTRO,hostStylesheets:!0,codegenFormat:"html",elementComponent:"Html"}}export{createProvider};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{upgrade_default,migrateFolder2}from"./chunk-a16mz9zf.js";import"./chunk-33t0csxs.js";import"./chunk-pjtg3wnm.js";import"./chunk-b3x80jc1.js";import"./chunk-asvdqkdp.js";import"./chunk-675tr675.js";import"./chunk-80fjfyha.js";import"./chunk-66qgm91k.js";import"./chunk-ngbqd7kb.js";import"./chunk-edgwx026.js";import"./chunk-afe25dpr.js";import"./chunk-0wafc9aw.js";import"./chunk-42bj6r30.js";import"./chunk-s23gtejf.js";import"./chunk-4pkw6d9n.js";export{upgrade_default as default,migrateFolder2 as migrateFolder};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{CURRENT_SCHEMA_VERSION}from"./chunk-
|
|
2
|
+
import{CURRENT_SCHEMA_VERSION}from"./chunk-4pkw6d9n.js";import{localDesignOf,recordedDesignName}from"./chunk-66qgm91k.js";import{AGENTS,designArgumentFor,agentRootCandidates,writeAgentConfig,refreshAgentArtifacts}from"./chunk-33t0csxs.js";import{daemonRoot,readLock,isLive,stopDaemon}from"./chunk-asvdqkdp.js";import{fail}from"./chunk-s23gtejf.js";import{installedVellooBin,upgradeInstalledVelloo}from"./chunk-0wafc9aw.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{upgradeFolder}from"./chunk-b3x80jc1.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign,checkoutDesigns}from"./chunk-675tr675.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);import{readFile}from"fs/promises";import{homedir}from"os";var isStrings=(value)=>Array.isArray(value)&&value.every((item)=>typeof item==="string");function asEntry(value){if(value===null||typeof value!=="object")return null;let{command,args}=value;return typeof command==="string"&&isStrings(args)?{command,args}:null}function readStdioEntry(content,format){try{switch(format){case"json":return asEntry(JSON.parse(content).mcpServers?.velloo);case"vscode-json":return asEntry(JSON.parse(content).servers?.velloo);case"opencode-json":{let command=JSON.parse(content).mcp?.velloo?.command;return isStrings(command)&&command[0]?{command:command[0],args:command.slice(1)}:null}case"codex-toml":return asEntry(Bun.TOML.parse(content).mcp_servers?.velloo);case"continue-block":return asEntry(Bun.YAML.parse(content).mcpServers?.[0]);case"continue-config":return null}}catch{return null}}async function repinAgentConfigs(opts){if(localDesignOf(opts.designFolder))return[];let homeDir=opts.homeDir??homedir(),names=new Set(opts.names),rewritten=[];for(let projectRoot of opts.projectRoots){let designArg=await designArgumentFor(projectRoot,opts.designFolder),desired=designArg?["mcp",designArg]:["mcp"];for(let agent of Object.values(AGENTS)){if(agent.scope!=="project"||agent.gui)continue;let path=agent.path(projectRoot,homeDir),content;try{content=await readFile(path,"utf8")}catch{continue}let entry=readStdioEntry(content,agent.format),pinned=entry?.args.length===2&&entry.args[0]==="mcp"?entry.args[1]:void 0;if(!entry||pinned===void 0||!names.has(pinned)||pinned===desired[1])continue;await writeAgentConfig(projectRoot,agent,{transport:"stdio",command:entry.command,args:desired},homeDir),rewritten.push(path)}}return rewritten}async function targetFolders(arg){if(arg)return[await resolveDesign(arg,"upgrade",{requireConfig:!0})];return checkoutDesigns(resolve("."))}var upgrade_default=defineCommand({meta:{name:"upgrade",description:"Upgrade Velloo and migrate designs to the current format \u2014 every design in this checkout, or the one named"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},"dry-run":{type:"boolean",default:!1,description:"Show what would change without writing"},check:{type:"boolean",default:!1,description:"Check for a Velloo update without installing it"},"binary-only":{type:"boolean",default:!1,description:"Upgrade the Velloo installation and leave the designs alone"},"design-only":{type:"boolean",default:!1,description:"Migrate the designs with this Velloo, without self-upgrading first (same as `velloo design upgrade`)"},skills:{type:"boolean",default:!0,description:"Also refresh the installed agent skills / plugin / rules to this velloo's versions"}},async run({args}){let dryRun=args["dry-run"]||args.check,folders=args["binary-only"]?[]:await targetFolders(args.design),outcome=null;if(!(args["design-only"]||args["folder-only"]===!0))try{outcome=await upgradeInstalledVelloo({checkOnly:dryRun})}catch(error){let message=error instanceof Error?error.message:String(error);if(folders.length===0)fail("upgrade",message);console.log(import_picocolors.default.dim(`velloo: skipping the self-upgrade \u2014 ${message}`))}if(folders.length===0)return;if(outcome?.upgraded){let bin=installedVellooBin();if(bin){for(let folder of folders){let argv=[bin,"upgrade",folder,"--design-only",...args.skills?[]:["--no-skills"]];if(await Bun.spawn(argv,{stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,VELLOO_DISABLE_UPDATE_CHECK:"1"}}).exited!==0)fail("upgrade",`the upgraded velloo could not migrate ${folder}`)}return}console.log(import_picocolors.default.dim(`velloo: could not locate the upgraded velloo \u2014 run \`velloo upgrade\` again to migrate ${folders.length===1?"the design":"the designs"}.`));return}for(let folder of folders)await migrateFolder2(folder,{dryRun,skills:args.skills})}});async function migrateFolder2(folder,opts){let{dryRun}=opts,progress=createProgress();progress.start(dryRun?"checking upgrade":"preparing upgrade");let stopped=!1,result;try{if(!dryRun){let root=daemonRoot(folder),running=await readLock(root);if(running&&await isLive(running))progress.step("stopping running canvas");stopped=await stopDaemon(root)}progress.step(dryRun?"checking design folder":"upgrading design folder"),result=await upgradeFolder(folder,{dryRun})}catch(err){progress.fail("upgrade failed"),fail("upgrade",err instanceof Error?err.message:String(err))}let refreshed=[],repinned=[];try{let name=recordedDesignName(folder);if(!dryRun&&name)repinned=await repinAgentConfigs({projectRoots:await agentRootCandidates(folder),designFolder:folder,names:[name]});if(opts.skills&&!dryRun){progress.step("refreshing agent guidance");let projectRoots=await agentRootCandidates(folder);({refreshed}=await refreshAgentArtifacts({projectRoots,designFolder:folder}))}progress.succeed(dryRun?"upgrade check complete":"upgrade complete")}catch(error){throw progress.fail("upgrade failed"),error}if(stopped)console.log("velloo: stopped the running canvas for this folder.");if(result.applied.length===0){console.log(`velloo: ${folder} is already at schema version ${CURRENT_SCHEMA_VERSION}.`);for(let file of result.changedFiles)console.log(` ${dryRun?"~":"\u2713"} ${file} (designs list format)`)}else{console.log(`velloo: ${dryRun?"would migrate":"migrated"} ${folder} from schema version ${result.from} to ${result.to}:`);for(let step of result.applied)console.log(` - ${step}`);for(let file of result.changedFiles)console.log(` ${dryRun?"~":"\u2713"} ${file}`);if(!dryRun)console.log("velloo: folder validated against the current schemas.")}if(repinned.length>0){console.log(""),console.log(import_picocolors.default.green("\u2713 Agent MCP configs now find the design without naming it:"));for(let path of repinned)console.log(` ${import_picocolors.default.cyan(path)}`)}if(refreshed.length>0){console.log(""),console.log(import_picocolors.default.green("\u2713 Refreshed agent guidance to this velloo:"));for(let line of refreshed)console.log(` ${import_picocolors.default.cyan(line)}`)}}
|
|
3
3
|
export{repinAgentConfigs,upgrade_default,migrateFolder2};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{NEVER,string,number,boolean,_null,unknown,array,object,strictObject,union,record,_enum,literal,preprocess,FrameSchemeSchema,MAX_BOARD_NAME_LENGTH,NodeIdSchema,RepoComponentRefSchema,NodeSchema,SnippetParamSchema}from"./chunk-4pkw6d9n.js";var Visibility=_enum(["public","private"]),AuthConfigResponseSchema=object({appUrl:string().optional(),issuer:string().optional(),clientId:string().optional()}),HealthResponseSchema=object({ok:boolean().optional(),db:object({ok:boolean().optional()}).optional(),blob:object({ok:boolean().optional()}).optional()}),AccountResponseSchema=object({email:string(),name:string().optional(),tier:string().optional(),creditMicros:number().nullable().optional(),accountId:string().optional()});function protectedSharesAllowed(tier){return tier!=="free"}var PROTECTED_SHARES_UNAVAILABLE="private and password-protected links need a paid plan \u2014 free accounts publish public links only";function teamOnlyAllowed(tier){return tier===void 0||tier==="business"||tier==="enterprise"}var GUESTS_UNAVAILABLE="sharing a board with guests needs a paid plan \u2014 upgrade your plan to invite guests",PublishSlotSchema=object({slug:string(),url:string(),title:string(),teamId:string().nullable(),visibility:Visibility,passwordProtected:boolean(),latestVersionId:string().nullable(),lastPublishedAt:string().nullable(),context:object({boardIds:array(string()),selectionFingerprint:string().nullable(),contextKnown:boolean(),repo:string().nullable(),branch:string().nullable()})}),PublishDestinationsResponseSchema=object({effectiveTeamId:string().nullable(),slots:array(PublishSlotSchema)}),LinkAudienceEntrySchema=object({type:_enum(["organization","team","account"]),id:string(),name:string().optional()}),LinkResponseSchema=object({slug:string(),visibility:Visibility.optional(),passwordProtected:boolean().optional(),audience:array(LinkAudienceEntrySchema).optional(),publicComments:boolean().optional()}),LinkAccessResponseSchema=object({visibility:Visibility,passwordProtected:boolean(),audience:array(LinkAudienceEntrySchema).optional(),publicComments:boolean().optional()}),VersionUploadResponseSchema=object({files:number(),bytes:number(),url:string(),tier:string().optional(),history:object({retained:boolean(),versions:number(),pruned:number()}).optional()}),PublishedDesignSchema=object({slug:string(),title:string().nullable(),url:string(),visibility:Visibility,passwordProtected:boolean(),canManage:boolean(),mine:boolean(),ownerEmail:string().nullable().optional(),ownerName:string().nullable().optional(),teamName:string().nullable().optional(),audience:array(LinkAudienceEntrySchema).optional(),publicComments:boolean().optional(),guestCount:number().optional(),git:object({repo:string().optional(),branch:string().optional()}).nullable().optional(),published:boolean(),lastPublishedAt:string().nullable()}),PublishedDesignsResponseSchema=object({links:array(PublishedDesignSchema).optional()}),TeamSchema=object({id:string(),name:string(),isDefault:boolean().optional(),role:string().optional(),canPublish:boolean().optional(),canManage:boolean().optional()}),TeamsResponseSchema=object({teams:array(TeamSchema)}),GuestSchema=object({id:string(),name:string().nullable(),email:string().optional(),createdAt:string(),lastSeenAt:string().nullable(),linkExpiresAt:string().nullable().optional()}),GuestsResponseSchema=object({guests:array(GuestSchema)}),GuestInviteResponseSchema=object({guest:GuestSchema,delivery:object({sent:boolean(),reason:string().optional()}),guestUrl:string().optional()}),GuestLinkResponseSchema=object({guestUrl:string()}),DeviceCodeResponseSchema=object({device_code:string(),user_code:string(),verification_uri:string(),verification_uri_complete:string(),expires_in:number(),interval:number()}),DeviceTokenResponseSchema=object({access_token:string().optional(),error:string().optional(),error_description:string().optional()}),CliTokenResponseSchema=object({token:string(),email:string()}),IntentPriceSchema=object({intent:string(),summary:string(),priceMicros:number(),output:_enum(["image","svg"]),aspects:array(string()),defaultAspect:string(),reference:_enum(["forbidden","optional","required"])}),IntentCatalogResponseSchema=object({intents:array(IntentPriceSchema),maxCount:number().optional()}),GeneratedAssetPayloadSchema=object({kind:_enum(["image","svg"]),dataUrl:string()}),GenerateResponseSchema=object({id:string(),intent:string().optional(),chargedMicros:number(),balanceMicros:number(),assets:array(GeneratedAssetPayloadSchema).min(1).optional(),kind:_enum(["image","svg"]).optional(),dataUrl:string().optional()}).transform((r,ctx)=>{let assets=r.assets??(r.dataUrl&&r.kind?[{kind:r.kind,dataUrl:r.dataUrl}]:null);if(!assets)return ctx.addIssue({code:"custom",message:"no assets in the generation reply"}),NEVER;return{id:r.id,intent:r.intent??"",assets,chargedMicros:r.chargedMicros,balanceMicros:r.balanceMicros}}),FeedbackIssuerResponseSchema=object({scheme:string(),publicKey:string()}),FeedbackSignaturesResponseSchema=object({signatures:array(string())});var ACTIVITY_EVENT_TYPE="activity",SocketFrameSchema=union([object({type:literal("screen-changed"),screenId:string().min(1)}),object({type:literal("board-changed"),boardId:string().min(1)}),object({type:literal("theme-changed")}),object({type:literal("snippet-changed"),snippetId:string().min(1)}),object({type:literal("annotations-changed"),screenId:string().min(1)}),object({type:literal("notes-changed"),boardId:string().min(1)}),object({type:literal("comments-changed"),boardId:string().min(1),scope:_enum(["local","shared"])}),object({type:literal("config-changed")}),object({type:literal("folder-reloaded")}),object({type:literal("reload-error"),source:string(),message:string()}),object({type:literal(ACTIVITY_EVENT_TYPE)}).loose()]);var CLOUD_ERROR_CODES=["bad_request","unauthorized","sign_in_required","insufficient_credits","forbidden","not_found","conflict","payload_too_large","unsupported_media_type","rate_limited","generation_failed","billing_upstream","unavailable","storage_unavailable","internal"];function isCloudErrorCode(value){return typeof value==="string"&&CLOUD_ERROR_CODES.includes(value)}var unreachable=(error,opts={})=>({kind:"Unreachable",detail:error instanceof Error?error.message:String(error),...opts.url!==void 0?{url:opts.url}:{},...opts.timedOut?{timedOut:!0}:{}}),httpFailure=(operation,status,detail,code)=>({kind:"HttpFailure",operation,status,detail:detail&&detail.length>0?detail:"unknown",...code!==void 0?{code}:{}}),loggedOut=(detail)=>detail===void 0?{kind:"LoggedOut"}:{kind:"LoggedOut",detail},protocolViolation=(detail)=>({kind:"ProtocolViolation",detail}),uploadRaceLost=()=>({kind:"UploadRaceLost"}),insecureCloudUrl=(url)=>({kind:"InsecureCloudUrl",url}),invalidRequest=(detail)=>({kind:"InvalidRequest",detail});async function readFailure(res){let body=await res.json().catch(()=>{return});if(!body||typeof body!=="object")return{detail:"unknown"};let{message,error}=body,detail=typeof message==="string"&&message.length>0?message:"unknown";return isCloudErrorCode(error)?{detail,code:error}:{detail}}function cloudFailure(operation,status,detail,code){if(status!==401&&code!=="unauthorized")return httpFailure(operation,status,detail,code);return loggedOut(detail&&detail.length>0&&detail!=="unknown"?`velloo-cloud rejected the stored credential (${detail})`:"velloo-cloud rejected the stored credential")}async function httpFailureFrom(operation,res){let{detail,code}=await readFailure(res);return cloudFailure(operation,res.status,detail,code)}function boardLimitFrom(error){if(error.kind!=="HttpFailure"||error.status!==403)return null;let match=/\bthe (\w+) plan is limited to (\d+) active cloud boards\b/.exec(error.detail);if(!match?.[1]||!match[2])return null;return{tier:match[1],limit:Number(match[2])}}function describeBoardLimit({tier,limit}){return`the ${tier} plan keeps ${limit} board${limit===1?"":"s"} published at a time \u2014 take one down to publish another`}var serverTrouble=(status)=>status>=500?" \u2014 the cloud is having trouble; check its status or try again later":"";function describeCloudError(error){switch(error.kind){case"Unreachable":{let target=error.url?`cannot reach ${error.url}`:"cannot reach the cloud";return error.timedOut?`${target} \u2014 timed out (${error.detail}); check your connection and try again`:`${target} (${error.detail}) \u2014 check your connection and try again`}case"HttpFailure":return`${error.operation} failed (${error.status}): ${error.detail}${serverTrouble(error.status)}`;case"LoggedOut":return error.detail?`${error.detail} \u2014 run \`velloo login\``:"you are signed out \u2014 run `velloo login`";case"ProtocolViolation":return`${error.detail} \u2014 this is a bug; please report it`;case"UploadRaceLost":return"upload confirmation was lost and the retry found the publish slot changed \u2014 "+"the publish may have succeeded; check your published boards before retrying";case"InsecureCloudUrl":return`refusing to send credentials to ${error.url} over an insecure connection`;case"InvalidRequest":return error.detail;default:{let exhaustive=error;return"the cloud request failed"}}}function ok(value){return{ok:!0,value}}function err(error){return{ok:!1,error}}function map(r,fn){return r.ok?ok(fn(r.value)):r}async function tryCatchAsync(fn,onError){try{return ok(await fn())}catch(thrown){return err(onError(thrown))}}function*$(r){if(!r.ok)throw yield r,Error("unreachable: generator resumed after err");return r.value}async function DoAsync(gen){let iter=gen(),step=await iter.next();while(!step.done){let r=step.value;if(!r.ok)return r;step=await iter.next(r.value)}return ok(step.value)}async function cloudJson(res,schema,operation){let body;try{body=await res.json()}catch(error){return err(protocolViolation(`${operation}: the cloud's response was not JSON (${error instanceof Error?error.message:String(error)})`))}let parsed=schema.safeParse(body);if(!parsed.success)return err(protocolViolation(`${operation}: the cloud's response did not match the protocol`));return ok(parsed.data)}async function cloudFetch(url,schema,opts){let{operation,token,timeoutMs,...init}=opts,res;try{res=await fetch(url,{...init,headers:{...token!==void 0?{authorization:`Bearer ${token}`}:{},...init.headers},...timeoutMs!==void 0?{signal:AbortSignal.timeout(timeoutMs)}:{}})}catch(error){let timedOut=error instanceof Error&&error.name==="TimeoutError";return err(unreachable(error,{url,...timedOut?{timedOut:!0}:{}}))}if(!res.ok)return err(await httpFailureFrom(operation,res));return cloudJson(res,schema,operation)}var PathArraySchema=array(number()),IdLocatorSchema=string().regex(/^@[a-zA-Z][a-zA-Z0-9_-]*$/,{message:"id locator must match /^@[a-zA-Z][a-zA-Z0-9_-]*$/"}),jsonTolerant=(schema)=>preprocess((v)=>{if(typeof v==="string"){let t=v.trim();if(t.startsWith("[")||t.startsWith("{"))try{return JSON.parse(t)}catch{}}return v},schema),singularTolerant=(schema)=>preprocess((v)=>typeof v==="object"&&v!==null&&!Array.isArray(v)?[v]:v,schema),LocatorSchema=jsonTolerant(union([PathArraySchema,IdLocatorSchema])).describe('Node address: an "@id" like "@hero-cta", or an index path from the root ([0,2,1])'),LocatorOrRootSchema=LocatorSchema.default([]),InnerPathSchema=string().describe('Body node: "@id" (preferred \u2014 survives restructures), a dotted index path ("0.2"), or "" for the root'),PatchRecordSchema=record(string(),unknown());var invalid=(message,issues)=>issues===void 0?{ok:!1,message}:{ok:!1,message,issues},ScreenId=string().min(1),BoardId=string().min(1),FrameId=string().min(1),SnippetId=string().min(1),FramePatchSchema=strictObject({x:number().optional(),y:number().optional(),w:number().int().positive().optional(),h:number().int().positive().optional(),label:string().nullable().optional(),group:string().nullable().optional(),scheme:FrameSchemeSchema.nullable().optional()}),EmitAsSchema=object({name:string().min(1),importPath:string().min(1)}).describe("Mark the node a host-component facade: the canvas renders the subtree, emit_code emits <name /> from importPath instead."),AnnotationPositionSchema=union([object({x:number(),y:number()}),literal("auto")]),addNodeShape={screenId:ScreenId,parentPath:LocatorOrRootSchema,componentRef:string().min(1),id:NodeIdSchema.optional(),props:PatchRecordSchema.optional(),children:jsonTolerant(union([array(NodeSchema),string(),number()])).optional(),index:number().int().nonnegative().optional(),emitAs:EmitAsSchema.optional(),repo:RepoComponentRefSchema.optional().describe("Place an app component by identity (importPath + exportName [+ member]) when it isn't in list_components' repo catalog yet")},AddNodeBody=strictObject(addNodeShape);function normalizeAddNode(input){let{children,...rest}=input;if(typeof children==="string"||typeof children==="number")return invalid("add_node: `children` must be an array of nodes.",[{code:"invalid_type",expected:"array",path:["children"]}]);let props=input.props;return{ok:!0,args:{...rest,...props===void 0?{}:{props},...children===void 0?{}:{children}}}}var StylePayloadSchema=jsonTolerant(union([string(),PatchRecordSchema,_null()])).describe("Native style channel: a className string (Tailwind), an object of properties (sx / style), or null to clear"),PropsPatchEntrySchema=object({path:LocatorSchema,propPatch:PatchRecordSchema.optional(),style:StylePayloadSchema.optional()}).refine((e)=>e.propPatch!==void 0||e.style!==void 0,{message:"each patch needs propPatch, style, or both"}),GestureSchema=string().max(64).optional().describe("Opaque id of the drag this write belongs to; merges its undo steps into one"),updatePropsShape={screenId:ScreenId,patches:jsonTolerant(singularTolerant(array(PropsPatchEntrySchema).min(1))).describe("One entry per node; length 1 for a single edit"),gesture:GestureSchema},UpdatePropsBody=strictObject(updatePropsShape),applyClassesShape={screenId:ScreenId,path:LocatorOrRootSchema,classes:string(),gesture:GestureSchema},ApplyClassesBody=strictObject(applyClassesShape),removeNodeShape={screenId:ScreenId,path:LocatorSchema},RemoveNodeBody=strictObject(removeNodeShape),moveNodeShape={screenId:ScreenId,fromPath:LocatorSchema,toParent:LocatorSchema,toIndex:number().int().nonnegative().optional()},MoveNodeBody=strictObject(moveNodeShape),setNodeIdShape={screenId:ScreenId,path:LocatorSchema,id:NodeIdSchema.nullable()},SetNodeIdBody=strictObject(setNodeIdShape),RoutePath=string().trim().min(1).transform((value)=>value.startsWith("/")?value:`/${value}`),addScreenShape={name:string().min(1),id:string().min(1).optional(),fromScreenId:string().min(1).optional(),tree:NodeSchema.optional(),route:RoutePath.optional()},AddScreenBody=strictObject(addScreenShape),removeScreenShape={screenId:ScreenId},RemoveScreenBody=strictObject(removeScreenShape),setScreenTreeShape={screenId:ScreenId,tree:NodeSchema},SetScreenTreeBody=strictObject(setScreenTreeShape),updateScreenShape={screenId:ScreenId,patch:object({name:string().min(1).optional(),route:RoutePath.nullable().optional()})},UpdateScreenBody=strictObject(updateScreenShape),addBoardShape={name:string().min(1).max(MAX_BOARD_NAME_LENGTH),id:string().min(1).optional(),group:string().min(1).optional()},AddBoardBody=strictObject(addBoardShape),updateBoardShape={boardId:BoardId,patch:object({name:string().min(1).max(MAX_BOARD_NAME_LENGTH).optional(),theme:string().min(1).nullable().optional(),archived:boolean().optional(),group:string().min(1).nullable().optional()})},UpdateBoardBody=strictObject(updateBoardShape),removeBoardShape={boardId:BoardId},RemoveBoardBody=strictObject(removeBoardShape),reorderBoardsShape={order:array(string().min(1))},ReorderBoardsBody=strictObject(reorderBoardsShape),addBoardGroupShape={name:string().min(1),color:string().min(1).optional()},AddBoardGroupBody=strictObject(addBoardGroupShape),updateBoardGroupShape={groupId:string().min(1),patch:object({name:string().min(1).optional(),color:string().min(1).nullable().optional()})},UpdateBoardGroupBody=strictObject(updateBoardGroupShape),removeBoardGroupShape={groupId:string().min(1)},RemoveBoardGroupBody=strictObject(removeBoardGroupShape),reorderBoardGroupsShape={order:array(string().min(1))},ReorderBoardGroupsBody=strictObject(reorderBoardGroupsShape),addFrameShape={boardId:BoardId,screenId:ScreenId,x:number().optional(),y:number().optional(),w:number().int().positive(),h:number().int().positive(),label:string().optional(),group:string().optional(),id:string().min(1).optional()},AddFrameBody=strictObject(addFrameShape),FramePatchEntrySchema=preprocess((v)=>{if(typeof v!=="object"||v===null||Array.isArray(v)||"patch"in v)return v;let{frameId,...patch}=v;return Object.keys(patch).length>0?{frameId,patch}:v},object({frameId:FrameId,patch:FramePatchSchema})),updateFrameShape={boardId:BoardId,patches:jsonTolerant(singularTolerant(array(FramePatchEntrySchema).min(1))).describe("One entry per frame; length 1 for a single edit")},UpdateFrameBody=strictObject(updateFrameShape),removeFrameShape={boardId:BoardId,frameId:FrameId},RemoveFrameBody=strictObject(removeFrameShape),moveFrameShape={boardId:BoardId,frameId:FrameId,toBoardId:BoardId,x:number().optional(),y:number().optional()},MoveFrameBody=strictObject(moveFrameShape),addSnippetShape={name:string().min(1),id:string().min(1).optional(),params:array(SnippetParamSchema).default([]),tree:NodeSchema},AddSnippetBody=strictObject(addSnippetShape),updateSnippetShape={snippetId:SnippetId,patch:object({name:string().min(1).optional(),params:array(SnippetParamSchema).optional(),tree:NodeSchema.optional(),innerPatch:object({innerPath:InnerPathSchema,propPatch:PatchRecordSchema}).optional()})},UpdateSnippetBody=strictObject(updateSnippetShape),removeSnippetShape={snippetId:SnippetId},RemoveSnippetBody=strictObject(removeSnippetShape),instantiateSnippetShape={screenId:ScreenId,parentPath:LocatorOrRootSchema,snippetId:SnippetId,id:NodeIdSchema.optional(),args:PatchRecordSchema.optional(),extraClassName:string().optional().describe("One-off classes layered onto the snippet body's root, for this instance"),overrides:record(string(),object({props:PatchRecordSchema})).optional(),index:number().int().nonnegative().optional()},InstantiateSnippetBody=strictObject(instantiateSnippetShape),overrideSnippetPropsShape={screenId:ScreenId,path:LocatorSchema,innerPath:InnerPathSchema,propPatch:PatchRecordSchema},OverrideSnippetPropsBody=strictObject(overrideSnippetPropsShape),updateSnippetArgsShape={screenId:ScreenId,path:LocatorSchema,argPatch:PatchRecordSchema.default({}),extraClassName:string().nullable().optional(),gesture:GestureSchema},UpdateSnippetArgsBody=strictObject(updateSnippetArgsShape),updateSnippetInstanceShape={screenId:ScreenId,path:LocatorSchema,argPatch:PatchRecordSchema.optional(),extraClassName:string().nullable().optional(),innerPath:InnerPathSchema.optional(),propPatch:PatchRecordSchema.optional()},UpdateSnippetInstanceBody=strictObject(updateSnippetInstanceShape);function normalizeUpdateSnippetInstance(input){let wantsArgs=input.argPatch!==void 0||input.extraClassName!==void 0,hasInner=input.innerPath!==void 0,hasPatch=input.propPatch!==void 0;if(hasInner!==hasPatch)return invalid("update_snippet_instance: innerPath and propPatch go together.");if(!wantsArgs&&!hasInner)return invalid("update_snippet_instance: pass argPatch, extraClassName, or innerPath+propPatch.");let plan={};if(wantsArgs)plan.args={screenId:input.screenId,path:input.path,argPatch:input.argPatch??{},...input.extraClassName!==void 0?{extraClassName:input.extraClassName}:{}};if(hasInner&&input.innerPath!==void 0&&input.propPatch!==void 0)plan.override={screenId:input.screenId,path:input.path,innerPath:input.innerPath,propPatch:input.propPatch};return{ok:!0,args:plan}}var addAnnotationShape={screenId:ScreenId,target:object({locator:LocatorSchema}),body:string(),position:AnnotationPositionSchema.optional(),collapsed:boolean().optional()},AddAnnotationBody=strictObject(addAnnotationShape),updateAnnotationShape={screenId:ScreenId,annotationId:string().min(1),patch:object({body:string().optional(),position:AnnotationPositionSchema.optional(),collapsed:boolean().nullable().optional()})},UpdateAnnotationBody=strictObject(updateAnnotationShape),removeAnnotationShape={screenId:ScreenId,annotationId:string().min(1)},RemoveAnnotationBody=strictObject(removeAnnotationShape),NoteAttachmentSchema=object({frameId:FrameId,screenId:ScreenId,locator:LocatorSchema}),addNoteShape={boardId:BoardId,x:number().optional(),y:number().optional(),width:number().positive().optional(),body:string(),attachment:NoteAttachmentSchema.optional()},AddNoteBody=object(addNoteShape).refine((a)=>a.attachment!==void 0||a.x!==void 0&&a.y!==void 0,{message:"A note without an attachment needs x and y",path:["x"]}),updateNoteShape={boardId:BoardId,noteId:string().min(1),patch:object({x:number().optional(),y:number().optional(),width:number().positive().optional(),body:string().optional()})},UpdateNoteBody=strictObject(updateNoteShape),removeNoteShape={boardId:BoardId,noteId:string().min(1)},RemoveNoteBody=strictObject(removeNoteShape),updateViewportPresetsShape={presets:array(object({name:string().min(1),w:number().int().positive(),h:number().int().positive()}))},UpdateViewportPresetsBody=strictObject(updateViewportPresetsShape),updateDefaultsShape={defaultBoard:string().min(1).nullable().optional(),defaultScreen:string().min(1).nullable().optional()},UpdateDefaultsBody=strictObject(updateDefaultsShape),updateCodegenShape={componentsAlias:string().nullable().optional()},UpdateCodegenBody=strictObject(updateCodegenShape),updateDesignNameShape={name:string()},UpdateDesignNameBody=strictObject(updateDesignNameShape),updateFeedbackShape={enabled:boolean().optional(),contactOk:boolean().optional()},UpdateFeedbackBody=strictObject(updateFeedbackShape);export{ok,err,map,tryCatchAsync,$,DoAsync,isCloudErrorCode,AuthConfigResponseSchema,HealthResponseSchema,AccountResponseSchema,protectedSharesAllowed,PROTECTED_SHARES_UNAVAILABLE,teamOnlyAllowed,GUESTS_UNAVAILABLE,PublishDestinationsResponseSchema,LinkResponseSchema,LinkAccessResponseSchema,VersionUploadResponseSchema,PublishedDesignsResponseSchema,TeamsResponseSchema,GuestsResponseSchema,GuestInviteResponseSchema,GuestLinkResponseSchema,DeviceCodeResponseSchema,DeviceTokenResponseSchema,CliTokenResponseSchema,IntentCatalogResponseSchema,GenerateResponseSchema,FeedbackIssuerResponseSchema,FeedbackSignaturesResponseSchema,unreachable,loggedOut,protocolViolation,uploadRaceLost,insecureCloudUrl,invalidRequest,readFailure,cloudFailure,httpFailureFrom,boardLimitFrom,describeBoardLimit,describeCloudError,cloudJson,cloudFetch,LocatorSchema,AddNodeBody,normalizeAddNode,updatePropsShape,UpdatePropsBody,ApplyClassesBody,removeNodeShape,RemoveNodeBody,moveNodeShape,MoveNodeBody,setNodeIdShape,SetNodeIdBody,addScreenShape,AddScreenBody,removeScreenShape,RemoveScreenBody,SetScreenTreeBody,updateScreenShape,addBoardShape,AddBoardBody,updateBoardShape,UpdateBoardBody,removeBoardShape,RemoveBoardBody,reorderBoardsShape,ReorderBoardsBody,AddBoardGroupBody,UpdateBoardGroupBody,RemoveBoardGroupBody,ReorderBoardGroupsBody,addFrameShape,AddFrameBody,updateFrameShape,UpdateFrameBody,removeFrameShape,RemoveFrameBody,MoveFrameBody,addSnippetShape,AddSnippetBody,updateSnippetShape,UpdateSnippetBody,removeSnippetShape,RemoveSnippetBody,InstantiateSnippetBody,UpdateSnippetArgsBody,updateSnippetInstanceShape,UpdateSnippetInstanceBody,normalizeUpdateSnippetInstance,AddAnnotationBody,UpdateAnnotationBody,RemoveAnnotationBody,AddNoteBody,UpdateNoteBody,RemoveNoteBody,updateViewportPresetsShape,UpdateViewportPresetsBody,UpdateDefaultsBody,UpdateCodegenBody,UpdateDesignNameBody,UpdateFeedbackBody};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"./chunk-
|
|
2
|
+
import"./chunk-4pkw6d9n.js";import{withMcpSessionUrl,MCP_SURFACE_MODES,parseMcpSurfaceSelection,withMcpSurfaceUrl,runStdioFormatGate,runStdioMcpProxy}from"./chunk-66qgm91k.js";import"./chunk-ngbqd7kb.js";import"./chunk-afe25dpr.js";import"./chunk-42bj6r30.js";import{assertLoopbackHost,daemonRoot,DesignFolderFormatError,ensureDaemon,stopDaemon}from"./chunk-asvdqkdp.js";import"./chunk-80fjfyha.js";import"./chunk-s23gtejf.js";import{upgradeFolder}from"./chunk-b3x80jc1.js";import{DESIGN_ARG_DESCRIPTION,resolveDesignForSession}from"./chunk-675tr675.js";import"./chunk-edgwx026.js";import{traceEnabled}from"./chunk-6tx68d90.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{join}from"path";async function serveFormatGate(err){console.error(`velloo mcp: ${err.message.slice(8)}`),console.error("velloo mcp: serving the upgrade gate over MCP instead of the design tools.");let closing=!1,shutdown=async(close)=>{if(closing)return;closing=!0,await close?.(),process.exit(0)},gate=await runStdioFormatGate({root:err.root,found:err.found,current:err.current,upgrade:err.found<err.current?async()=>(await stopDaemon(daemonRoot(err.root)),upgradeFolder(err.root)):void 0,onExit:()=>void shutdown()});return process.on("SIGINT",()=>void shutdown(gate.close)),process.on("SIGTERM",()=>void shutdown(gate.close)),process.stdin.on("end",()=>void shutdown(gate.close)),process.stdin.on("close",()=>void shutdown(gate.close)),await new Promise(()=>{})}var mcp_default=defineCommand({meta:{name:"mcp",description:"Connect this agent to the folder's canvas over MCP (stdio by default). Usually spawned by your agent."},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},http:{type:"boolean",default:!1,description:"Print the daemon's HTTP MCP URL instead of bridging over stdio"},port:{type:"string",description:"Preferred canvas port when starting the daemon fresh (default 7300)"},host:{type:"string",description:"Loopback bind hostname: 127.0.0.1 (default), localhost or ::1"},surface:{type:"string",description:`Tool surface: ${MCP_SURFACE_MODES.join(", ")} (default guided; env VELLOO_MCP_SURFACE)`}},async run({args}){assertLoopbackHost(args.host??"127.0.0.1");let{folder,pick}=await resolveDesignForSession(args.design,"mcp"),preferredPort=args.port?Number(args.port):void 0,parsedSurface=parseMcpSurfaceSelection(args.surface??process.env.VELLOO_MCP_SURFACE);if(!parsedSurface.ok)throw Error(`velloo mcp: ${parsedSurface.error}`);let surface=parsedSurface.selection,spawned=!1,rec;try{rec=await ensureDaemon(folder,{preferredPort,host:args.host,onSpawn:()=>{spawned=!0}})}catch(err){if(err instanceof DesignFolderFormatError&&!args.http)await serveFormatGate(err);throw err}if(traceEnabled())console.error(spawned?`velloo: \u29BF trace recording ON \u2014 tapes \u2192 ${join(folder,".velloo","trace")} (view with \`velloo trace\`)`:"velloo: VELLOO_TRACE is set, but attached to an already-running canvas \u2014 recording is only active if that daemon was started trace-enabled (`velloo stop` then reconnect to force a fresh one).");if(args.http){let selectedUrl=withMcpSessionUrl(withMcpSurfaceUrl(rec.mcpUrl,surface),{pick});console.log(`velloo: canvas at ${rec.canvasUrl}`),console.log(`velloo: MCP server at ${selectedUrl} (point your AI agent here)`),console.log("velloo: it keeps running in the background \u2014 `velloo stop` to stop it.");return}console.error(`velloo: bridging MCP to the canvas at ${rec.canvasUrl}`);let closing=!1,shutdown=async(close)=>{if(closing)return;closing=!0,await close?.(),process.exit(0)},current=folder,portFor=(root)=>root===folder?preferredPort:void 0,sessionUrl=(mcpUrl,opened)=>withMcpSessionUrl(withMcpSurfaceUrl(mcpUrl,surface),{switchable:!0,pick:opened}),proxy=await runStdioMcpProxy(sessionUrl(rec.mcpUrl,pick),{onExit:()=>void shutdown(),rediscover:async()=>{try{let fresh=await ensureDaemon(current,{preferredPort:portFor(current),host:args.host});return sessionUrl(fresh.mcpUrl,void 0)}catch{return null}},switchTo:async(root)=>{try{let next=await ensureDaemon(root,{preferredPort:portFor(root),host:args.host});return current=root,{url:sessionUrl(next.mcpUrl,void 0)}}catch(err){let message=err instanceof Error?err.message:String(err);return{error:message.startsWith("velloo: ")?message.slice(8):message}}}});process.on("SIGINT",()=>void shutdown(proxy.close)),process.on("SIGTERM",()=>void shutdown(proxy.close)),process.stdin.on("end",()=>void shutdown(proxy.close)),process.stdin.on("close",()=>void shutdown(proxy.close)),await new Promise(()=>{})}});export{mcp_default as default};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{string,number,object,CURRENT_SCHEMA_VERSION,schemaVersionOf}from"./chunk-4pkw6d9n.js";import{writeJsonAtomic}from"./chunk-66qgm91k.js";import{TOOL_VERSION}from"./chunk-42bj6r30.js";import{defaultCloudUrl}from"./chunk-edgwx026.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));let entry=Bun.main;return existsSync(entry)?[process.execPath,entry,...args]:[process.execPath,...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
|
+
`).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
|
+
${lines.join(`
|
|
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}
|
|
6
|
+
export{assertLoopbackHost,daemonRoot,DesignFolderFormatError,assertFolderFormatCurrent,readLock,writeLock,removeLock,readRememberedPort,rememberPort,portCandidates,isLive,registryUpsert,registryRemove,listDaemons,ensureDaemon,stopDaemon};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{normalizeRepoManifest,CURRENT_SCHEMA_VERSION,schemaVersionOf,planMigration}from"./chunk-
|
|
2
|
+
import{normalizeRepoManifest,CURRENT_SCHEMA_VERSION,schemaVersionOf,planMigration}from"./chunk-4pkw6d9n.js";import{localDesignOf,findOwningManifest,recordedDesignName,loadDesignFolder}from"./chunk-66qgm91k.js";import{TOOL_VERSION}from"./chunk-42bj6r30.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,writeFile}from"fs/promises";import{join,resolve}from"path";function isObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}var isAnnotationSidecar=(file)=>file.endsWith(".annotations.json"),isThemeDocument=(file)=>file.endsWith(".json");async function planRewrites(folder,dir,matches,transform){let files=[];try{files=await readdir(join(folder,dir))}catch{return[]}let out=[];for(let file of files.sort()){if(!matches(file))continue;let rel=join(dir,file),prev=await readFile(join(folder,rel),"utf8"),next=`${JSON.stringify(transform(JSON.parse(prev)),null,2)}
|
|
3
3
|
`;if(next!==prev)out.push({rel,next})}return out}async function upgradeFolder(folder,opts={}){let configPath=join(folder,".design","config.json"),rawConfig=JSON.parse(await readFile(configPath,"utf8")),from=schemaVersionOf(rawConfig),manifest=await findOwningManifest(folder).catch(()=>null),run=planMigration(rawConfig,{name:legacyNameOf(folder,manifest)}),manifestFiles=manifest?.legacy?[manifest.path]:[];if(run.applied.length===0){if(manifest?.legacy&&!opts.dryRun)await migrateManifest(manifest);return{from,to:CURRENT_SCHEMA_VERSION,applied:[],changedFiles:manifestFiles}}let rewrites=[...await planRewrites(folder,"screens",isAnnotationSidecar,(parsed)=>Array.isArray(parsed)?parsed.map((e)=>typeof e==="object"&&e!==null?run.annotation(e):e):parsed),...await planRewrites(folder,"theme",isThemeDocument,(parsed)=>isObject(parsed)?run.theme(parsed):parsed)],nextConfig={...run.config,toolVersion:TOOL_VERSION},changedFiles=[join(".design","config.json"),...rewrites.map((r)=>r.rel),...manifestFiles];if(opts.dryRun)return{from,to:CURRENT_SCHEMA_VERSION,applied:run.applied,changedFiles};await writeFile(configPath,`${JSON.stringify(nextConfig,null,2)}
|
|
4
4
|
`,"utf8");for(let r of rewrites)await writeFile(join(folder,r.rel),r.next,"utf8");if(manifest?.legacy)await migrateManifest(manifest);return await loadDesignFolder(folder),{from,to:CURRENT_SCHEMA_VERSION,applied:run.applied,changedFiles}}function legacyNameOf(folder,manifest){let local=localDesignOf(folder);if(local)return local.legacyName;let target=realOr(folder);for(let[path,name]of manifest?.legacyNames??[])if(realOr(path)===target)return name;return}function realOr(path){return existsSync(path)?realpathSync(path):resolve(path)}async function migrateManifest(manifest){for(let[path,name]of manifest.legacyNames){if(recordedDesignName(path))continue;let configPath=join(path,".design","config.json"),raw;try{raw=JSON.parse(await readFile(configPath,"utf8"))}catch{continue}if(isObject(raw))await writeFile(configPath,`${JSON.stringify({...raw,name},null,2)}
|
|
5
5
|
`)}let raw=JSON.parse(await readFile(manifest.path,"utf8"));await writeFile(manifest.path,`${JSON.stringify(normalizeRepoManifest(raw).manifest,null,2)}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"./chunk-
|
|
2
|
+
import"./chunk-4pkw6d9n.js";import{topUpTokens}from"./chunk-66qgm91k.js";import"./chunk-ngbqd7kb.js";import"./chunk-afe25dpr.js";import{normalizeCloudUrl,loadCredential,saveCredential,verifyCredential,performDeviceLogin}from"./chunk-wkqrb2tz.js";import{fail}from"./chunk-s23gtejf.js";import{defaultCloudUrl}from"./chunk-edgwx026.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{createInterface}from"readline/promises";async function waitForEnter(message){if(!process.stdin.isTTY)return;let rl=createInterface({input:process.stdin,output:process.stdout});try{await rl.question(message)}finally{rl.close()}}var login_default=defineCommand({meta:{name:"login",description:"Log in to velloo-cloud (opens the browser, saves a CLI token)"},args:{url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},force:{type:"boolean",description:"Re-authenticate even if already logged in",default:!1}},async run({args}){let cloudUrl=args.url?normalizeCloudUrl(args.url):defaultCloudUrl();if(!args.force){let existing=await loadCredential(cloudUrl),email=existing&&await verifyCredential(cloudUrl,existing.token);if(email){console.log(`velloo login: already logged in to ${cloudUrl} as ${email}.`),console.log(" Use `velloo login --force` to switch accounts, or `velloo logout` to sign out.");return}}let result;try{result=await performDeviceLogin(cloudUrl,async({verificationUrl,userCode})=>{console.log(`
|
|
3
3
|
velloo login will open your browser to approve this device (code ${userCode}).`),await waitForEnter("Press Enter to open the browser\u2026 "),console.log(`Opening ${verificationUrl}`),console.log(`If the browser doesn't open, visit that URL and enter: ${userCode}`)})}catch(err){fail("login",err instanceof Error?err.message:String(err))}let path=await saveCredential(cloudUrl,result);console.log(`velloo login: logged in to ${cloudUrl} as ${result.email}`),console.log(` credentials saved to ${path}`);try{await topUpTokens({url:cloudUrl,token:result.token},10)}catch{}}});export{login_default as default};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{fail,failWithError}from"./chunk-s23gtejf.js";import{firstPositional}from"./chunk-94vx1yb0.js";import{traceEnabled}from"./chunk-6tx68d90.js";var TOLERATED={upgrade:["folder-only"]},BUILTIN_FLAGS=["help","h","version","v"],camel=(name)=>name.replace(/-([a-z0-9])/g,(_,c)=>c.toUpperCase()),kebab=(name)=>name.replace(/[A-Z]/g,(c)=>`-${c.toLowerCase()}`);async function knownFlags(args){let defs=(typeof args==="function"?await args():await args)??{},known=new Set(BUILTIN_FLAGS);for(let[name,def]of Object.entries(defs)){if(def.type==="positional")continue;let alias="alias"in def?def.alias:void 0;for(let spelling of[name,...alias===void 0?[]:[alias].flat()])known.add(spelling),known.add(camel(spelling)),known.add(kebab(spelling))}return known}function flagsIn(rawArgs){let flags=[];for(let token of rawArgs){if(token==="--")break;if(!token.startsWith("-")||token==="-"||/^-\d/.test(token))continue;if(token.startsWith("--")){let name=token.slice(2).split("=")[0]??"";flags.push(name.startsWith("no-")?name.slice(3):name)}else flags.push(...token.slice(1).split("=")[0]?.split("")??[])}return flags}function strictFlags(name,cmd){let subCommands=cmd.subCommands,table=async()=>typeof subCommands==="function"?await subCommands():await subCommands,checked=subCommands?{...cmd,subCommands:async()=>Object.fromEntries(Object.entries(await table()??{}).map(([child,load])=>[child,async()=>{let def=typeof load==="function"?await load():await load;return strictFlags(`${name} ${child}`,def)}]))}:cmd;if(subCommands&&!cmd.run)return checked;let setup=cmd.setup;return{...checked,async setup(context){if(subCommands){let child=await firstPositional(context.rawArgs,cmd.args);if(child!==void 0&&Object.hasOwn(await table()??{},child)){await setup?.(context);return}}let known=await knownFlags(cmd.args),tolerated=TOLERATED[name]??[],unknown=flagsIn(context.rawArgs).filter((flag)=>flag!==""&&!known.has(flag)&&!tolerated.includes(flag));if(unknown.length>0){let listed=[...known].filter((f)=>f.length>1&&!BUILTIN_FLAGS.includes(f)&&f===kebab(f)),hint=listed.length>0?` (it takes ${listed.map((f)=>`--${f}`).join(", ")})`:"";fail(name,`unknown option ${unknown.map((f)=>f.length===1?`-${f}`:`--${f}`).join(", ")}${hint}`)}await setup?.(context)}}}var LOADERS={init:()=>import("./chunk-
|
|
2
|
+
import{fail,failWithError}from"./chunk-s23gtejf.js";import{firstPositional}from"./chunk-94vx1yb0.js";import{traceEnabled}from"./chunk-6tx68d90.js";var TOLERATED={upgrade:["folder-only"]},BUILTIN_FLAGS=["help","h","version","v"],camel=(name)=>name.replace(/-([a-z0-9])/g,(_,c)=>c.toUpperCase()),kebab=(name)=>name.replace(/[A-Z]/g,(c)=>`-${c.toLowerCase()}`);async function knownFlags(args){let defs=(typeof args==="function"?await args():await args)??{},known=new Set(BUILTIN_FLAGS);for(let[name,def]of Object.entries(defs)){if(def.type==="positional")continue;let alias="alias"in def?def.alias:void 0;for(let spelling of[name,...alias===void 0?[]:[alias].flat()])known.add(spelling),known.add(camel(spelling)),known.add(kebab(spelling))}return known}function flagsIn(rawArgs){let flags=[];for(let token of rawArgs){if(token==="--")break;if(!token.startsWith("-")||token==="-"||/^-\d/.test(token))continue;if(token.startsWith("--")){let name=token.slice(2).split("=")[0]??"";flags.push(name.startsWith("no-")?name.slice(3):name)}else flags.push(...token.slice(1).split("=")[0]?.split("")??[])}return flags}function strictFlags(name,cmd){let subCommands=cmd.subCommands,table=async()=>typeof subCommands==="function"?await subCommands():await subCommands,checked=subCommands?{...cmd,subCommands:async()=>Object.fromEntries(Object.entries(await table()??{}).map(([child,load])=>[child,async()=>{let def=typeof load==="function"?await load():await load;return strictFlags(`${name} ${child}`,def)}]))}:cmd;if(subCommands&&!cmd.run)return checked;let setup=cmd.setup;return{...checked,async setup(context){if(subCommands){let child=await firstPositional(context.rawArgs,cmd.args);if(child!==void 0&&Object.hasOwn(await table()??{},child)){await setup?.(context);return}}let known=await knownFlags(cmd.args),tolerated=TOLERATED[name]??[],unknown=flagsIn(context.rawArgs).filter((flag)=>flag!==""&&!known.has(flag)&&!tolerated.includes(flag));if(unknown.length>0){let listed=[...known].filter((f)=>f.length>1&&!BUILTIN_FLAGS.includes(f)&&f===kebab(f)),hint=listed.length>0?` (it takes ${listed.map((f)=>`--${f}`).join(", ")})`:"";fail(name,`unknown option ${unknown.map((f)=>f.length===1?`-${f}`:`--${f}`).join(", ")}${hint}`)}await setup?.(context)}}}var LOADERS={init:()=>import("./chunk-18eqh3s1.js").then((m)=>m.default),login:()=>import("./chunk-cdqah1hy.js").then((m)=>m.default),logout:()=>import("./chunk-gn9wcc1b.js").then((m)=>m.default),connect:()=>import("./chunk-ryga3ny1.js").then((m)=>m.default),run:()=>import("./chunk-75sc3adm.js").then((m)=>m.default),design:()=>import("./chunk-10hap8kc.js").then((m)=>m.default),mcp:()=>import("./chunk-ahm4zd56.js").then((m)=>m.default),stop:()=>import("./chunk-q3kf8243.js").then((m)=>m.default),status:()=>import("./chunk-ewgdcgrk.js").then((m)=>m.default),__daemon:()=>import("./chunk-f45p6kcb.js").then((m)=>m.default),__update_check:()=>import("./chunk-df557bn4.js").then((m)=>m.default),render:()=>import("./chunk-19xyzep8.js").then((m)=>m.default),capture:()=>import("./chunk-3ffs39e8.js").then((m)=>m.default),browser:()=>import("./chunk-1h3gqy7w.js").then((m)=>m.default),export:()=>import("./chunk-0rdf6trc.js").then((m)=>m.default),upgrade:()=>import("./chunk-98rann8t.js").then((m)=>m.default),publish:()=>import("./chunk-ycrddh47.js").then((m)=>m.default),emit:()=>import("./chunk-w6f40y2x.js").then((m)=>m.default),theme:()=>import("./chunk-5f0xv80x.js").then((m)=>m.default),completions:()=>import("./chunk-rd65xm6r.js").then((m)=>m.default),...traceEnabled()?{trace:()=>import("./chunk-d4r0myxr.js").then((m)=>m.default)}:{}};function guarded(name,load){return async()=>{let cmd=strictFlags(name,await load()),run=cmd.run;if(!run)return cmd;return{...cmd,async run(ctx){try{await run(ctx)}catch(err){failWithError(name,err)}}}}}var COMMANDS=Object.fromEntries(Object.entries(LOADERS).map(([name,load])=>[name,guarded(name,load)]));
|
|
3
3
|
export{COMMANDS};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"./chunk-80fjfyha.js";import{string,number,boolean,unknown,array,object}from"./chunk-
|
|
2
|
+
import"./chunk-80fjfyha.js";import{string,number,boolean,unknown,array,object}from"./chunk-4pkw6d9n.js";import"./chunk-66qgm91k.js";import"./chunk-ngbqd7kb.js";import"./chunk-afe25dpr.js";import{fail}from"./chunk-s23gtejf.js";import{openUrl}from"./chunk-hggap8fq.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign}from"./chunk-675tr675.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{writeFileSync}from"fs";import{basename,join as join3,resolve as resolve2}from"path";import{readFileSync}from"fs";import{join,resolve,sep}from"path";var AssetRefSchema=object({file:string(),mimeType:string(),bytes:number()}),TapeRecordSchema=object({seq:number().optional(),ts:string().optional(),tool:string().optional(),durationMs:number().optional(),ok:boolean().optional(),params:unknown().optional(),text:string().optional(),images:array(AssetRefSchema).optional(),isError:boolean().optional(),error:object({name:string().optional(),message:string().optional(),stack:string().optional()}).optional(),sessionId:string().optional()}),TapeMetaSchema=object({tapeId:string().optional(),startedAt:string().optional(),folder:string().optional(),pid:number().optional()}),PendingCallSchema=object({seq:number().optional(),ts:string().optional(),tool:string().optional(),params:unknown().optional(),sessionId:string().optional()});function loadTape(dir){let meta={};try{let parsed=TapeMetaSchema.safeParse(JSON.parse(readFileSync(join(dir,"meta.json"),"utf8")));if(parsed.success)meta=parsed.data}catch{}let records=[],raw="";try{raw=readFileSync(join(dir,"tape.jsonl"),"utf8")}catch{}for(let line of raw.split(`
|
|
3
3
|
`)){let t=line.trim();if(!t)continue;try{let parsed=TapeRecordSchema.safeParse(JSON.parse(t));if(parsed.success)records.push(parsed.data)}catch{}}let pending=[];try{let raw=JSON.parse(readFileSync(join(dir,"pending.json"),"utf8"));if(Array.isArray(raw))pending=raw.flatMap((entry)=>{let parsed=PendingCallSchema.safeParse(entry);return parsed.success?[parsed.data]:[]})}catch{}let done=new Set(records.map((r)=>r.seq));return pending=pending.filter((p)=>!done.has(p.seq)),{dir,meta,records,pending}}var ESC={"&":"&","<":"<",">":">",'"':""","'":"'"};function esc(s){return s.replace(/[&<>"']/g,(c)=>ESC[c]??c)}function isBad(r){return r.ok===!1||r.isError===!0||Boolean(r.error)}function prettyJsonText(text){try{return JSON.stringify(JSON.parse(text),null,2)}catch{return text}}function fmtDuration(ms){if(ms===void 0)return"";return ms>=1000?`${(ms/1000).toFixed(2)}s`:`${ms}ms`}function fmtBytes(n){if(n>=1048576)return`${(n/1048576).toFixed(1)}MB`;if(n>=1024)return`${(n/1024).toFixed(0)}KB`;return`${n}B`}function timeOnly(iso){if(!iso)return"";let m=/T(\d{2}:\d{2}:\d{2})/.exec(iso);return m?m[1]:iso}function tapeStats(tape){let totalMs=tape.records.reduce((s,r)=>s+(r.durationMs??0),0);return{calls:tape.records.length,errors:tape.records.filter(isBad).length,totalLabel:fmtDuration(totalMs)}}function safeMimeType(mime){return/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i.test(mime)?mime:"application/octet-stream"}function inlineImage(dir,asset){let dataUri="";try{let abs=resolve(dir,asset.file);if(abs!==resolve(dir)&&!abs.startsWith(resolve(dir)+sep))return`<div class="missing">out-of-tree asset: ${esc(asset.file)}</div>`;let buf=readFileSync(abs);dataUri=`data:${safeMimeType(asset.mimeType)};base64,${buf.toString("base64")}`}catch{return`<div class="missing">missing asset: ${esc(asset.file)}</div>`}return`<img class="shot" loading="lazy" src="${dataUri}" alt="${esc(asset.file)}"/>`}function renderResult(dir,rec){if(rec.error){let head=esc([rec.error.name,rec.error.message].filter(Boolean).join(": ")||"error"),stack=rec.error.stack?`<pre class="stack">${esc(rec.error.stack)}</pre>`:"";return`<div class="err-box"><div class="err-head">${head}</div>${stack}</div>`}let parts=[];if(rec.text)parts.push(`<pre class="json">${esc(prettyJsonText(rec.text))}</pre>`);if(rec.images?.length){let imgs=rec.images.map((a)=>`<figure>${inlineImage(dir,a)}<figcaption>${esc(a.file.replace(/^assets\//,""))} \xB7 ${fmtBytes(a.bytes)}</figcaption></figure>`).join("");parts.push(`<div class="shots">${imgs}</div>`)}if(rec.isError)parts.unshift('<div class="flag">isError: true</div>');return parts.join("")||'<div class="muted">no output</div>'}function renderRow(dir,rec){let bad=isBad(rec),tool=esc(rec.tool??"?"),seq=rec.seq??"",params=esc(JSON.stringify(rec.params??null,null,2)),search=esc(`${rec.tool??""} ${rec.text??""} ${JSON.stringify(rec.params??"")}`.toLowerCase().slice(0,4000)),nShots=rec.images?.length?` \xB7 ${rec.images.length}\uD83D\uDCF7`:"";return`<details class="call${bad?" bad":""}" data-seq="${rec.seq??""}" data-error="${bad?1:0}" data-search="${search}">
|
|
4
4
|
<summary>
|
|
5
5
|
<span class="dot"></span>
|