@kizenapps/cli 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/README.md +11 -238
  2. package/dist/index.js +164 -806
  3. package/dist/index.js.map +1 -1
  4. package/dist/viewer/assets/{AppDetailPage-CsAJGeXG.js → AppDetailPage-CGrukEIj.js} +1 -1
  5. package/dist/viewer/assets/CodeStepsPage-Cu9UiRy9.js +1 -0
  6. package/dist/viewer/assets/{ConfigurationPage-DUiOAiMK.js → ConfigurationPage-CI0pRNJ0.js} +1 -1
  7. package/dist/viewer/assets/CredentialsContext-Cwl_xyt_.js +1 -0
  8. package/dist/viewer/assets/{DevSidebar-CkLpkWSy.js → DevSidebar-BFsoIbMt.js} +1 -1
  9. package/dist/viewer/assets/{SandboxPage-D3Qdh_0M.js → SandboxPage-Dyb8i7vf.js} +1 -1
  10. package/dist/viewer/assets/{SecretsPage-CQVPedWl.js → SecretsPage-g2FF7d-B.js} +1 -1
  11. package/dist/viewer/assets/{WhenBadge-CV5t88Iv.js → WhenBadge-DKz8ukwo.js} +1 -1
  12. package/dist/viewer/assets/api--pW2v3sH.js +11 -0
  13. package/dist/viewer/assets/{bootstrapQuery-WnoB2iya.js → bootstrapQuery-DTAPKOf-.js} +1 -1
  14. package/dist/viewer/assets/index-GvAPdgKb.js +5 -0
  15. package/dist/viewer/assets/{useCompleteSetup-BgC9V-FN.js → useCompleteSetup-QS3W9RRO.js} +4 -4
  16. package/dist/viewer/assets/{usePluginConfig-mg5WevIz.js → usePluginConfig--4sAyik4.js} +1 -1
  17. package/dist/viewer/index.html +4 -4
  18. package/package.json +20 -26
  19. package/dist/index.css +0 -2
  20. package/dist/index.css.map +0 -1
  21. package/dist/viewer/assets/CodeStepsPage-Dd84cmbe.js +0 -1
  22. package/dist/viewer/assets/CredentialsContext-p3i8s7a6.js +0 -1
  23. package/dist/viewer/assets/api-C4R44YCL.js +0 -11
  24. package/dist/viewer/assets/index-B1r1yKD-.js +0 -5
package/dist/index.js CHANGED
@@ -1,759 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import*as Sp from"http";var Nt="If you're behind a proxy, upgrade Node to 24.14+ or set NODE_USE_ENV_PROXY=1 on a Node version that supports it.",ma=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy"],Ln=e=>e!==void 0&&e!=="",Pe="not-configured";function Hn({env:e,http:t}){return Pe==="applied"||(ma.some(r=>Ln(e[r]))?Ln(e.NODE_USE_ENV_PROXY)?Pe="already-set":typeof t.setGlobalProxyFromEnv!="function"?Pe="unavailable":(t.setGlobalProxyFromEnv(e),Pe="applied"):Pe="not-configured"),Pe}var Dt=()=>Pe;import{readFileSync as bp}from"fs";import{dirname as wp,join as xp}from"path";import{fileURLToPath as vp}from"url";import{Command as kp}from"commander";import{createElement as Uc}from"react";import{render as Vc}from"ink";import{PluginValidationError as Wc}from"@kizenapps/packager";import{spawn as ha}from"child_process";import{platform as Un}from"process";function ga(){return Un==="darwin"?[{cmd:"pbcopy",args:[]}]:Un==="win32"?[{cmd:"clip",args:[]}]:[{cmd:"wl-copy",args:[]},{cmd:"xclip",args:["-selection","clipboard"]},{cmd:"xsel",args:["--clipboard","--input"]}]}var ya=2e3;function ba(e,{cmd:t,args:r}){return new Promise(o=>{let n;try{n=ha(t,r,{stdio:["pipe","ignore","ignore"]})}catch{o(!1);return}let i=n,s=!1;function l(p){s||(s=!0,clearTimeout(a),o(p))}let a=setTimeout(()=>{i.kill(),l(!1)},ya);a.unref(),i.on("error",()=>{l(!1)}),i.on("close",p=>{l(p===0)}),i.stdin.on("error",()=>{l(!1)});try{i.stdin.end(e)}catch{l(!1)}})}async function Rt(e){for(let t of ga())if(await ba(e,t))return!0;return!1}import{BLOCKS_DIRECTORY_NAME as ja,CONFIG_FILE_NAME as Fa,MANIFEST_FILE_NAME as Oa,sanitizeToAPIName as za}from"@kizenapps/packager";import{mkdir as Pa,writeFile as Ea}from"fs/promises";import{join as Ta}from"path";import{PluginValidationError as Ia,packagePlugin as Aa,parseManifestFromFiles as Na,transformDeployablePlugin as Da,transformFiles as Ra,validatePluginApp as Ba}from"@kizenapps/packager";import{readdir as wa,readFile as Vn}from"fs/promises";import{join as Wn,relative as xa}from"path";var va=new Set([".png",".svg"]),ka=new Set([".kzn"]),Ca=new Set([".DS_Store"]),Bt=new Set(["node_modules",".git",".kizenapp",".github",".claude","__pycache__"]),Sa=8e3;function _a(e){return e.subarray(0,Sa).includes(0)}async function Kn(e,t){let r=await wa(e,{withFileTypes:!0}),o=[];for(let n of r)if(n.isDirectory()){if(Bt.has(n.name))continue;o.push(...await Kn(Wn(e,n.name),t))}else n.isFile()&&(Ca.has(n.name)||o.push(Wn(e,n.name)));return o}async function $t(e,{detectBinaryByContent:t=!1}={}){let r=await Kn(e,e);return Promise.all(r.map(async o=>{let n=xa(e,o).split("\\").join("/"),i=n.lastIndexOf("."),s=i>=0?n.slice(i).toLowerCase():"";if(va.has(s)){let a=await Vn(o);return{path:n,content:"",base64Image:a.toString("base64")}}let l=await Vn(o);return ka.has(s)||t&&_a(l)?{path:n,content:"",binaryData:l}:{path:n,content:l.toString("utf-8")}}))}var $a=(e,t)=>({...e,thumbnail:e.thumbnail?Buffer.from(e.thumbnail).toString("base64"):null,kznFile:e.kznFile?Buffer.from(e.kznFile).toString("base64"):null,allReleaseNotes:t});async function Tr(e,t){t?.("reading-files");let r=await $t(e);t?.("validating");let o=Ba(r);if(o.some(a=>a.severity==="error"))throw new Ia(o);t?.("minifying");let n=await Ra(r);t?.("packaging");let i=Na(n),s=Aa(n,i),l=Object.values(s).map(Da);return{files:r,deployable:l,issues:o}}async function jt(e,t,r){await Pa(t,{recursive:!0});let{files:o,deployable:n}=await Tr(e,r);r?.("writing-bundle");let i=n.map(p=>{let h=p.release_notes_directory?.replace(/\/$/,""),w=h?o.filter(x=>x.path.startsWith(h+"/")&&x.path.endsWith(".md")&&x.content).map(x=>({version:x.path.slice(x.path.lastIndexOf("/")+1,-3),notes:x.content})).sort((x,d)=>d.version.localeCompare(x.version,void 0,{numeric:!0})):[];return $a(p,w)}),s=JSON.stringify(i,null,2),l=(p,h)=>p==="allReleaseNotes"?void 0:h,a=Buffer.byteLength(JSON.stringify(i,l,2),"utf-8");return await Ea(Ta(t,"bundle.json"),s,"utf-8"),{bundleSize:a}}var Ar=["min_w","max_w","min_h","max_h"],qn=[["min_w","max_w"],["min_h","max_h"]],Ft={default_w:["min_w","max_w"],default_h:["min_h","max_h"]},Gn=Object.keys(Ft),Ma=[...Ar,...Gn],Yn=[...Ma,"host_chrome"],La=["\0","{__ref:"],qe=class extends Error{reason;available;field;constructor(t,r,o={}){super(r),this.name="BlockExportError",this.reason=t,this.available=o.available??[],this.field=o.field}};function Nr(e){let t=new Set(e.map(r=>r.pluginApiName)).size>1;return e.map(r=>t?`${r.pluginApiName}/${r.apiName}`:r.apiName).join(", ")}function Dr(e,t){let r=e.flatMap(s=>s.artifacts.custom_blocks.map(l=>({block:l,pluginApiName:s.api_name}))),o=r.map(({block:s,pluginApiName:l})=>({pluginApiName:l,apiName:s.api_name}));if(r.length===0)return{ok:!1,reason:"no_blocks",available:o};if(t===void 0){let[s]=r;return r.length===1&&s?{ok:!0,...s}:{ok:!1,reason:"ambiguous",available:o}}let n=r.filter(({block:s})=>s.api_name===t),[i]=n;return i?n.length>1?{ok:!1,reason:"ambiguous",available:n.map(({block:s,pluginApiName:l})=>({pluginApiName:l,apiName:s.api_name}))}:{ok:!0,...i}:{ok:!1,reason:"not_found",available:o}}var Rr=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Jn=e=>typeof e=="number"&&Number.isInteger(e)&&e>0,Ir=e=>La.some(t=>e.includes(t));function Br(e){let t=e,{name:r,script:o,styles:n,event_scripts:i}=t;if(typeof o!="string"||!o.trim())return"script";if(r!==void 0&&typeof r!="string")return"name";if(n!==void 0&&typeof n!="string")return"styles";if(i!==void 0){if(!Rr(i))return"event_scripts";let h=Object.entries(i).find(([w,x])=>typeof x!="string"||Ir(w)||Ir(x));if(h)return`event_scripts.${h[0]}`}let s=[["name",r],["script",o],["styles",n]].find(([,h])=>typeof h=="string"&&Ir(h));if(s)return s[0];let l=Ar.find(h=>t[h]!==void 0&&!Jn(t[h]));if(l)return l;let a=qn.find(([h,w])=>{let x=t[h],d=t[w];return typeof x=="number"&&typeof d=="number"&&x>d});if(a)return a[0];let p=Gn.find(h=>{let w=t[h];if(w===void 0)return!1;if(typeof w!="number"||!Jn(w))return!0;let[x,d]=Ft[h],c=t[x],y=t[d];return typeof c=="number"&&w<c||typeof y=="number"&&w>y});if(p)return p;if(t.host_chrome!==void 0&&typeof t.host_chrome!="boolean")return"host_chrome"}var $r=(e,t)=>{if(t==="script")return`Block "${e}" has no script. Add a non-empty script.js to the block directory.`;if(t==="host_chrome")return`Block "${e}" has an invalid host_chrome: it must be true or false.`;let r=qn.find(([o])=>o===t);if(r)return`Block "${e}" has an invalid ${t}: it must be a positive integer no greater than ${r[1]}.`;if(Ar.includes(t))return`Block "${e}" has an invalid ${t}: it must be a positive integer.`;if(t in Ft){let[o,n]=Ft[t];return`Block "${e}" has an invalid ${t}: it must be a positive integer between ${o} and ${n}.`}return`Block "${e}" has an invalid ${t}: it must be a string without NUL characters or "{__ref:".`},Ha=(e,t)=>{let{reason:r,available:o}=e,n=Nr(o);return r==="no_blocks"?new qe(r,"This plugin has no blocks. Scaffold one with `appbuilder create --artifacts block`.",{available:o}):r==="not_found"?new qe(r,`No block with api_name "${t??""}". Available blocks: ${n}`,{available:o}):new qe(r,t===void 0?`This plugin has several blocks; pass the api_name of the one to export: ${n}`:`More than one plugin has a block with api_name "${t}": ${n}`,{available:o})},Ua=e=>{try{let t=JSON.parse(e);return Rr(t)?t:void 0}catch{return}},Va=e=>{let t=e.find(i=>i.path===Oa),r=t?JSON.parse(t.content):[],o=Array.isArray(r)?r:[r],n=new Map;for(let i of o)Rr(i)&&typeof i.api_name=="string"&&typeof i.entry=="string"&&n.set(i.api_name,i.entry);return n},Wa=(e,t)=>{let r=`${t.replace(/\/+$/,"")}/${ja}/`,o=new Map;for(let n of e){if(!n.path.startsWith(r))continue;let[i,s,...l]=n.path.slice(r.length).split("/");if(!i||s!==Fa||l.length>0)continue;let a=Ua(n.content);if(!a)continue;let p=typeof a.api_name=="string"&&a.api_name!==""?a.api_name:za(i),h={};for(let w of Yn)a[w]!==void 0&&(h[w]=a[w]);o.set(p,h)}return o},Ka=(e,t={})=>({...Object.fromEntries(Object.entries(e).filter(([o])=>!Yn.includes(o))),...t});function Ja(e,t){let r=Va(t);return e.map(o=>{let n=r.get(o.api_name),i=n===void 0?new Map:Wa(t,n);return{...o,artifacts:{...o.artifacts,custom_blocks:o.artifacts.custom_blocks.map(s=>Ka(s,i.get(s.api_name)))}}})}async function jr(e){let{files:t,deployable:r,issues:o}=await Tr(e);return{deployable:Ja(r,t),warnings:o.filter(n=>n.severity==="warning")}}async function Xn(e,t){let{deployable:r,warnings:o}=await jr(e),n=Dr(r,t);if(!n.ok)throw Ha(n,t);let i=Br(n.block);if(i!==void 0)throw new qe("invalid_block",$r(n.block.api_name,i),{field:i});return{block:n.block,pluginApiName:n.pluginApiName,warnings:o}}var Fr=(e,t,r)=>`${String(e)} ${e===1?t:r}`,qa=e=>{let t=new Map;for(let r of e){let o=r.path??r.pluginApiName??"general",n=t.get(o)??[];n.push(r),t.set(o,n)}return t},Ga=e=>{let t=e.filter(n=>n.severity==="error").length,r=e.length-t;if(t===0)return`\u26A0 ${Fr(r,"validation warning","validation warnings")}`;let o=`\u2717 ${Fr(t,"validation error","validation errors")}`;return r===0?o:`${o}, ${Fr(r,"warning","warnings")}`};function me(e){if(e.length===0)return"";let t=[...qa(e)].map(([r,o])=>[r,...o.map(n=>` ${n.severity==="error"?"\u2717":"\u26A0"} ${n.message} (${n.rule})`)].join(`
3
- `));return[Ga(e),...t].join(`
4
-
5
- `)}import{randomUUID as Gl}from"crypto";import{join as Yl}from"path";import{PluginValidationError as Go}from"@kizenapps/packager";var Zn={go:"https://app.go.kizen.com/api",fmo:"https://app.fmo.kizen.com/api",staging:"https://staging.kizen.com/api",integration:"https://integration.kizen.dev/api",test1:"https://test1.kizen.dev/api"},Qn={go:"https://go.kizen.com",fmo:"https://fmo.kizen.com",staging:"https://v2.staging.kizen.com",integration:"https://v2.integration.kizen.dev",test1:"https://test1.kizen.dev"},Ya=["go","fmo"];function Or(e){return Ya.includes(e)}import{mkdir as Xa,readFile as Za,writeFile as Qa}from"fs/promises";import{join as eo}from"path";async function he(e){try{let t=await Za(eo(e,"config.json"),"utf-8");return JSON.parse(t)}catch{return{}}}async function Ee(e,t){await Xa(e,{recursive:!0}),await Qa(eo(e,"config.json"),JSON.stringify(t,null,2),"utf-8")}import{mkdir as to,readFile as ro,readdir as el,writeFile as no}from"fs/promises";import{homedir as tl}from"os";import{dirname as rl,join as Ot}from"path";var X=["go","fmo","staging","integration","test1"];function Ge(e){return typeof e=="string"?e.trim():""}function at(e){return{apiKey:Ge(e.apiKey),userId:Ge(e.userId),businessId:Ge(e.businessId)}}var ze=Ot(tl(),".kizenappbuilder"),xe=Ot(ze,"credentials.json"),Te="credentials";function oo(e){return X.includes(e)}function io(e){return typeof e=="string"?e.trim():e}function so(e){if(typeof e!="object"||e===null)throw new Error("Credentials must be a JSON object");let t=e,r=io(t.environment);return{...at(t),environment:oo(r)?r:"go"}}async function Ie(e){let t=await ro(e,"utf-8");return so(JSON.parse(t))}function nl(e){let t=io(e.environment);return t==null||t===""?"missing":oo(t)?"explicit":"invalid"}async function zr(e){let t=await ro(e,"utf-8"),r=JSON.parse(t);return{credentials:so(r),environmentSource:nl(r)}}async function te(){try{return await Ie(xe)}catch{return null}}async function ol(e){await to(rl(xe),{recursive:!0,mode:448}),await no(xe,JSON.stringify(e,null,2),{encoding:"utf-8",mode:384})}function ao(e){return Ot(ze,`${e}.json`)}async function Ae(){let e=[{name:Te,path:xe,isDefault:!0}];try{let t=await el(ze);for(let r of t){if(!r.endsWith(".json"))continue;let o=r.slice(0,-5);o!==Te&&e.push({name:o,path:Ot(ze,r),isDefault:!1})}}catch{}return e}async function lo(e,t){if(e===Te){await ol(t);return}await to(ze,{recursive:!0,mode:448}),await no(ao(e),JSON.stringify(t,null,2),{encoding:"utf-8",mode:384})}async function ve(e){try{return await Ie(ao(e))}catch{return null}}async function co(){let e=await Ae(),t=await Promise.all(e.map(r=>zr(r.path).then(()=>!0,()=>!1)));return e.filter((r,o)=>t[o]===!0)}import*as Ye from"fs";import*as uo from"path";function Ne(e){let t=uo.join(e,".gitignore"),r=".kizenapp/",o="";if(Ye.existsSync(t)){o=Ye.readFileSync(t,"utf8");let i=o.split(`
6
- `).map(s=>s.trim());if(i.includes(r)||i.includes(".kizenapp"))return}let n=o.endsWith(`
7
- `)?r+`
8
- `:`
9
- `+r+`
10
- `;Ye.writeFileSync(t,o+n)}var il="The API key is invalid for this business, or the user id doesn't match. Check the credentials file's apiKey, userId and businessId.",sl="The API key's user needs the Standard API Keys permission and edit access to this dashboard. Homepages also need the Customize Homepages permission.",al=100,ll=new Set(["detail","non_field_errors"]),re=class extends Error{kind;status;hint;constructor(t,r,o={}){super(r),this.name="KizenApiError",this.kind=t,this.status=o.status,this.hint=o.hint}},Xe=e=>typeof e=="object"&&e!==null&&!Array.isArray(e);function Mr(e,t,r){let o=n=>{r.push(t===""?n:`${t}: ${n}`)};if(typeof e=="string"){e.trim()!==""&&o(e);return}if(typeof e=="number"||typeof e=="boolean"){o(String(e));return}if(Array.isArray(e)){let n=e.filter(i=>typeof i=="string"&&i.trim()!==""||typeof i=="number"||typeof i=="boolean");n.length>0&&o(n.map(String).join(" "));for(let i of e)typeof i=="object"&&i!==null&&Mr(i,t,r);return}if(Xe(e))for(let[n,i]of Object.entries(e)){let s=t===""&&ll.has(n)?"":t===""?n:`${t}.${n}`;Mr(i,s,r)}}function cl(e){if(typeof e!="object"||e===null)return;let t=[];return Mr(e,"",t),t.length>0?t.join("; "):void 0}function po(e){let t=e;return t.cause?.code??t.code??t.cause?.message??t.message??String(e)}async function dl(e){let t=await e.text();if(t==="")return{text:t,json:void 0};try{return{text:t,json:JSON.parse(t)}}catch{return{text:t,json:void 0}}}function ul(e,t,r,o){let n=cl(o),i=`${e} ${t} failed with ${String(r)}${n===void 0?"":`: ${n}`}`;return r===401?new re("auth_failed",i,{status:r,hint:il}):r===403?new re("forbidden",i,{status:r,hint:sl}):r===404?new re("not_found",i,{status:r}):new re("api_error",i,{status:r})}function fo(e,t,r){if(Array.isArray(r))return{results:r,next:null};if(Xe(r)&&Array.isArray(r.results))return{results:r.results,next:r.next};throw new re("api_error",`${e} ${t} returned an unexpected response shape`)}function mo(e){let{credentials:t}=e,r=e.fetch??fetch,o=e.baseUrl??Zn[t.environment],n=async(l,a,p)=>{let h=/^https?:\/\//.test(a)?a:`${o}${a}`,w=h.startsWith(o)?h.slice(o.length):h,x={"X-API-KEY":t.apiKey,"X-USER-ID":t.userId,"X-BUSINESS-ID":t.businessId,Accept:"application/json"},d={method:l,headers:x};p!==void 0&&(x["Content-Type"]="application/json",d.body=JSON.stringify(p));let c;try{c=await r(h,d)}catch(P){throw new re("network_error",`Couldn't reach Kizen at ${o}: ${po(P)}${Dt()==="unavailable"?` ${Nt}`:""}`)}let y,v;try{({text:y,json:v}=await dl(c))}catch(P){throw new re("network_error",`${l} ${w}: couldn't read the response: ${po(P)}. The request may have been applied; check Kizen before retrying.`,{status:c.status})}if(!c.ok)throw ul(l,w,c.status,v);if(v===void 0&&y!=="")throw new re("api_error",`${l} ${w} returned a response that is not JSON`,{status:c.status});return v},i=encodeURIComponent,s=l=>{let a=new URL(o),p=new URL(l,a.origin);if(p.hostname!==a.hostname)throw new re("api_error",`GET /custom-objects pagination pointed at an unexpected host ${p.hostname}`);return new URL(`${p.pathname}${p.search}`,a.origin).toString()};return{async listDashboards(l,a){let p=new URLSearchParams({size:"10000",include_sharing:"true",dashboard_type:l});a!==void 0&&p.set("custom_object_id",a);let h=`/dashboards/mine?${p.toString()}`;return fo("GET",h,await n("GET",h)).results},async listCustomObjects(){let l=[],a=new Set,p="/custom-objects?page_size=200";for(;p!==void 0&&!a.has(p)&&a.size<al;){a.add(p);let h=fo("GET",p,await n("GET",p));l.push(...h.results),p=typeof h.next=="string"&&h.next!==""?s(h.next):void 0}return l},async getCustomObject(l){let a=`/custom-objects/${i(l)}`,p=await n("GET",a);if(!Xe(p)||typeof p.id!="string"||typeof p.fetch_url!="string")throw new re("api_error",`GET ${a} returned an unexpected response shape`);return p},async getClientObjectId(){let l=await n("GET","/auth/bootstrap"),a=Xe(l)?l.business:void 0,p=Xe(a)?a.client_object:void 0,h=Xe(p)?p.id:void 0;return typeof h=="string"&&h!==""?h:null},async getDashboard(l){return await n("GET",`/dashboards/${i(l)}`)},async createDashlet(l,a){return await n("POST",`/dashboards/${i(l)}/dashlet`,a)},async updateDashlet(l,a,p){return await n("PATCH",`/dashboards/${i(l)}/dashlet/${i(a)}`,p)}}}import{createHash as pl}from"crypto";import{mkdir as fl,readFile as ml,writeFile as hl}from"fs/promises";import{dirname as gl,join as yo}from"path";var Lr="appbuilder:",Me=".kizenapp/pushes.json",yl=["min_w","max_w","min_h","max_h","default_w","default_h"],bl=["environment","businessId","pluginApiName","blockApiName","dashboardId","dashletId","contentHash","pushedAt"];function de(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Hr(e){return typeof e=="number"&&Number.isFinite(e)}function zt(e,t,r){return Math.min(Math.max(e,t),r)}function wl(e){let t={...e};return delete t.custom_object,t}function bo(e,t){return`${Lr}${e}/${t}`}function wo(e){if(!e.startsWith(Lr))return null;let t=e.slice(Lr.length).split("/");if(t.length!==2)return null;let[r,o]=t;return!r||!o?null:{pluginApiName:r,blockApiName:o}}function xo(e){let t={};for(let r of yl){let o=e[r];Hr(o)&&(t[r]=o)}return t}function vo(e){return typeof e.host_chrome=="boolean"?{host_chrome:e.host_chrome}:{}}function ko(e){return{kind:"custom_code",version:1,name:e.name,script:e.script,styles:e.styles,event_scripts:Object.entries(e.event_scripts).map(([t,r])=>({name:t,script:r})),...xo(e),...vo(e)}}function xl(e){return{object_id:e.objectId,entity_type:"static_content",report_type:"html",chart_type:"html",content:e.content,fe_extra_info:{}}}function vl(e){let t=0;for(let r of e){let o=r.layout;if(!de(o))continue;let{y:n,h:i}=o;!Hr(n)||!Hr(i)||(t=Math.max(t,n+i))}return t}function kl(e,t,r){let o=zt(t.default_w??t.min_w??6,t.min_w??1,t.max_w??12),n=zt(t.default_h??t.min_h??3,t.min_h??1,t.max_h??99);return{i:r,x:0,y:vl(e),w:zt(o,2,12),h:zt(n,1,99)}}function Co(e){return{name:bo(e.pluginApiName,e.blockApiName),layout:kl(e.dashlets,e.content,e.layoutId),config:xl({content:e.content,objectId:e.objectId})}}function So(e,t){let r=e.config;return{config:{...wl(de(r)?r:{}),content:t}}}function _o(e){let t=e.config;return de(t)?t.content:void 0}function Ze(e){let t=e.config;if(!de(t)||t.chart_type!=="html")return!1;let r=t.content;return de(r)&&r.kind==="custom_code"}function Po(e){return e.dashlets.filter(Ze)}function Mt(e){let t=_o(e);return de(t)&&typeof t.name=="string"?t.name:e.name}function Cl(e){let t=r=>typeof r=="string"?r:"";return Array.isArray(e)?e.filter(de).map(r=>({name:t(r.name),script:t(r.script)})):de(e)?Object.entries(e).map(([r,o])=>({name:r,script:t(o)})):[]}function Ur(e){if(Array.isArray(e))return e.map(Ur);if(de(e)){let t={};for(let r of Object.keys(e).sort())t[r]=Ur(e[r]);return t}return e}function Sl(e){return typeof e=="string"?e.trim():typeof e=="number"||typeof e=="boolean"?String(e).trim():""}function Vr(e){let t=de(e)?e:{},r={name:Sl(t.name),script:typeof t.script=="string"?t.script:"",styles:typeof t.styles=="string"?t.styles:"",event_scripts:Cl(t.event_scripts),...xo(t),...vo(t)};return pl("sha256").update(JSON.stringify(Ur(r))).digest("hex")}function Lt(e){let{entry:t}=e;if(t?.dashboardId===e.dashboardId){let i=e.dashlets.find(s=>s.id===t.dashletId);if(i&&Ze(i))return{kind:"map",dashlet:i,entry:t}}let r=bo(e.pluginApiName,e.blockApiName),o=e.dashlets.filter(i=>i.name===r&&Ze(i)),[n]=o;return o.length===1&&n?{kind:"name",dashlet:n}:o.length>1?{kind:"ambiguous",dashlets:o}:{kind:"none"}}function Eo(e,t){return t?.dashletId!==e.id?!1:Vr(_o(e))!==t.contentHash}function Wr(e,t){return e.environment===t.environment&&e.businessId===t.businessId&&e.pluginApiName===t.pluginApiName&&e.blockApiName===t.blockApiName}function lt(e,t){return e.find(r=>Wr(r,t))}function To(e,t){let r=e.findIndex(o=>Wr(o,t));return r===-1?[...e,t]:e.map((o,n)=>n===r?t:o)}function ct(e,t){return e.filter(r=>!Wr(r,t))}function _l(e){if(!de(e))return null;for(let r of bl)if(typeof e[r]!="string")return null;let t=e;return{environment:t.environment,businessId:Ge(t.businessId),pluginApiName:t.pluginApiName,blockApiName:t.blockApiName,dashboardId:t.dashboardId,dashletId:t.dashletId,contentHash:t.contentHash,pushedAt:t.pushedAt}}var ho=e=>Date.parse(e.pushedAt)||0;function Pl(e){let t=new Map;for(let r of e){let o=[r.environment,r.businessId,r.pluginApiName,r.blockApiName].join("\0"),n=t.get(o);(n===void 0||ho(r)>ho(n))&&t.set(o,r)}return[...t.values()]}var El=e=>e instanceof Error&&e.code==="ENOENT",go=e=>e instanceof Error?e.message:String(e);async function Io(e){let t;try{t=await ml(yo(e,Me),"utf8")}catch(o){if(El(o))return[];throw new Error(`Couldn't read ${Me}: ${go(o)}. Fix its permissions, or delete it to start a fresh push map.`)}let r;try{r=JSON.parse(t)}catch(o){throw new Error(`${Me} is not valid JSON (${go(o)}). Fix it, or delete it to start a fresh push map.`)}if(!Array.isArray(r))throw new Error(`${Me} must contain a JSON array of push entries. Fix it, or delete it to start a fresh push map.`);return Pl(r.map(_l).filter(o=>o!==null))}async function Ao(e,t){let r=yo(e,Me);await fl(gl(r),{recursive:!0}),await hl(r,`${JSON.stringify(t,null,2)}
11
- `,"utf8")}var Tl={generic_dashboard:"dashboard",homepage:"homepage",chart_group:"chart_group"},Il={dashboard:"generic_dashboard",homepage:"homepage",chart_group:"chart_group"};function No(e){return Tl[e]}function Do(e){return Il[e]}function Ro(e){let t=e.appBaseUrl.replace(/\/+$/,""),r=e.dashboardId;return e.surface==="homepage"?`${t}/home/${r}`:e.surface==="chart_group"&&e.customObject?e.customObject.fetchUrl==="client"?`${t}/clients/charts/${r}`:`${t}/custom-objects/${e.customObject.id}/charts/${r}`:`${t}/dashboard/${r}`}import{access as Fl,mkdir as Ol,readFile as zl,writeFile as Ml}from"fs/promises";import{dirname as Ll,join as dt,posix as Hl}from"path";var Bo="---\nname: kizen-custom-block\ndescription: Use this skill when the user asks to build, change or publish a Kizen content block, custom block, chart, or dashboard/homepage visualization in this plugin. It covers the block file layout, the plugin engine runtime rules, and the appbuilder validate, preview and push loop.\n---\n\n# Kizen custom blocks\n\nFollow these rules exactly. The runtime is strict, and a push changes a live Kizen dashlet.\n\n## Keep this skill current\n\nAt the start of any block task, run `appbuilder setup-claude` in the plugin directory.\n\n- If it reports `updated` for `.claude/skills/kizen-custom-block/SKILL.md`, your loaded copy of this skill is stale. Re-read `.claude/skills/kizen-custom-block/SKILL.md` before doing anything else, and follow the new copy.\n- If it reports `created` for any file, or `updated` for `.claude/skills/kizen-custom-block/design.md`, read that file before writing any markup or styles.\n- If it reports `created` or `updated` for `lib/kizenData.js` (under the plugin's entry directory, usually `src/`), re-read its exports before using them.\n- If every file line reports `unchanged`, continue.\n- If the command doesn't exist (an older CLI), skip it and continue.\n- If it fails (for example, you aren't in the plugin directory that holds `kizen.json`), switch to that directory and run it once more. If it still fails, continue without it.\n\n`setup-claude` only rewrites the CLI-managed files: the ones in this skill directory and `src/lib/kizenData.js`. It never touches block code, so it's always safe to run.\n\n## What a block is\n\nA block is a directory under `src/blocks/<dir>/`:\n\n- `config.json` (required): `name`, `api_name` (always set it explicitly), `types` (any of `homepages`, `dashboards`, `charts` for chart groups, and `records`; a block only appears on the surfaces it lists), and the optional sizes `min_w`, `max_w`, `min_h`, `max_h`, `default_w` and `default_h`. Widths are grid columns (1 to 12) and heights are grid rows. Each size you set is a positive integer, each `min_*` is no greater than its `max_*`, and each `default_*` falls within whichever of its `min_*` and `max_*` you set. `default_w` and `default_h` are the starting size when `block push` creates a new dashlet; updates never resize an existing dashlet. Only the sizes you write are exported and pushed.\n - `host_chrome` (optional boolean, default `true`): whether the host paints the dashlet card around the block. Omit it unless you are turning chrome off. Only a value you write is exported and pushed, and any value other than `true` or `false` fails validation.\n- `script.js` (required): the render script.\n- `styles.css` (optional).\n- `eventScripts/<name>.js` (optional): one file per interactive handler.\n\nShared helpers live in `src/lib/*.js`. Import them with relative named imports only, for example `import { formatMoney } from '../../lib/format.js';`. No default imports, no bare package names.\n\n`src/lib/kizenData.js` is managed by appbuilder: `setup-claude` and CLI updates replace it. Never edit it. Put your own helpers in another file under `src/lib/`.\n\n### Host chrome\n\nDecide the mode yourself from the request. Ask the user only if they already raised it and their choice is unclear.\n\n- Keep host chrome (omit `host_chrome`) for standard data dashlets such as charts, KPIs, tables and lists that should match native cards.\n- Set `\"host_chrome\": false` for full-bleed heroes, imagery, custom-shaped cards, or any design whose background is part of the visual.\n\nChrome off, in `config.json`:\n\n```json\n{\n \"name\": \"Welcome hero\",\n \"api_name\": \"welcome_hero\",\n \"types\": [\"homepages\", \"dashboards\"],\n \"host_chrome\": false\n}\n```\n\nFollow \"Chrome off\" in `design.md` for the layout rules. In your summary to the user, state which mode you chose and why in one line. `block push --dry-run --json` shows the value as `body.config.content.host_chrome` (absent when chrome is on by default), so check it there. Changing `host_chrome` on a block you already pushed is an ordinary update: validate, then `block push <api_name> --yes --json`.\n\n## Runtime rules\n\nBlocks run in the plugin engine web worker.\n\n- `script.js` is the body of an async function, and `this` is the worker context. Use `await` at the top level.\n- There is no DOM: no `document` or `window`. There are no npm packages. Don't use `eval` or `Function`.\n- Render with `this.outputUI(htmlString)`. The output is sanitized with DOMPurify, so `<script>` tags and `on*` attributes are stripped. Don't rely on them.\n- Charts are hand-built inline SVG strings. There are no chart libraries.\n- Interactivity: put `data-script=\"<eventScriptName>\"` on a `<button>` whose content is text only. Clicking it runs `eventScripts/<eventScriptName>.js` in a fresh worker, so no state carries over from `script.js`.\n- Args:\n - `this.args.dateFilter`: `{ start, end }`, or `undefined`.\n - `this.args.teamFilter`.\n - `this.args.objectId`: set on chart groups.\n - Homepages have no date or team filter. Handle both being absent.\n- Data: read records with the `kizenData.js` helpers (see \"Reading Kizen data\"). They call `this.getWithErrors` and `this.postWithErrors`, which use relative URLs and run as the viewing user.\n- Toasts: `this.showToast(message, options)`. `message` MUST be a string. Passing an object crashes the host app.\n- Styling:\n - The host applies `* { font-size: 10px }`. Set explicit px font sizes on everything.\n - With host chrome (the default), the host paints the card (background, border, border radius and shadow) from the dashboard's style settings, like native dashlets. The block root is transparent, fills the card, and paints no card of its own.\n - With `\"host_chrome\": false`, the host paints nothing: no background, border, radius or shadow. The block root fills the card edge to edge and owns its whole look (see \"Chrome off\" in `design.md`).\n - Put chart labels inside the SVG as `<text>` so they scale with the `viewBox`.\n- The code is stored in a Kizen dashlet. Never include a NUL character or the literal text `{__ref:` anywhere in any file.\n\n## Reading Kizen data\n\nRead records through `src/lib/kizenData.js`. Always import it from `../../lib/kizenData.js`. Its helpers take the worker context as their first argument, so pass `this`.\n\n- `getFields(this, object)` loads an object's fields. `object` is the object's id or name, and Contacts is `client_client`. Match fields by `name`.\n- `blockFilters(this.args, fields)` returns `{ filters, unapplied }`. Always apply `filters`, so the block reacts to the dashboard's date and team filters the way native dashlets do (date on `created`, team on `owner`). When `unapplied` contains `'teamFilter'`, the team filter couldn't be applied (a roles-only selection, or an object with no owner field). Show \"(team filter not applied)\".\n- `dropdownFilter(field, 'Name')` or `dropdownFilter(field, ['A', 'B'])` filters a dropdown by option names.\n- `searchRecords(this, { object, fieldNames, filters })` returns `{ records, count, truncated }`. When `truncated` is true, show \"Showing first N\" with N = `records.length`.\n- `fieldValue(record, name)`, `fieldLabel(record, name)`, `countBy(records, name, field?)`, `sumBy(records, name, valueOf?)` and `formatNumber(n, prefix)` read and summarize the results. `countBy` groups empty values under `key: ''`. Filter that group out, or give it a label.\n- Every record value or label you put into markup goes through `escapeHtml(value)`, including option names, owner names and text fields. `this.outputUI` sanitizes scripts, but it keeps images, links, forms and styles, and the block renders for every viewer. `escapeHtml` is the only escaping helper; never write your own.\n- Never hand-build filter or search JSON when a helper exists. Every helper throws a readable `Error`, so wrap the fetch in `try` and render the error state from `design.md`.\n\nCount Contacts by a dropdown field named `status` for the current dashboard filters. Use a field name from `getFields`:\n\n```js\nimport {\n blockFilters,\n countBy,\n escapeHtml,\n getFields,\n searchRecords,\n} from '../../lib/kizenData.js';\n\ntry {\n const fields = await getFields(this, 'client_client');\n const { filters, unapplied } = blockFilters(this.args, fields);\n const search = { object: 'client_client', fieldNames: ['status'], filters };\n const { records, count, truncated } = await searchRecords(this, search);\n const rows = countBy(records, 'status').filter((row) => row.key !== '');\n const items = rows.map((row) => `<li>${escapeHtml(row.label)}: ${row.count}</li>`).join('');\n const note = truncated ? `Showing first ${records.length} of ${count}. ` : '';\n const team = unapplied.includes('teamFilter') ? '(team filter not applied)' : '';\n this.outputUI(`<ul>${items}</ul><p>${note}${team}</p>`);\n} catch {\n this.outputUI('<div class=\"kz-state kz-state--error\">Couldn\\'t load data.</div>');\n}\n```\n\n## Design\n\nBefore writing any markup or styles, read `design.md` in this skill directory and follow it exactly.\n\n## The loop\n\nRun these exact commands. The flags and JSON keys are the contract. `<api_name>` is the block's `config.json` `api_name`. `block targets` and `block push` both accept `-c/--credentials <path>` and `--profile <name>` to pick the credential.\n\n1. **Validate.** Run `appbuilder block export <api_name>`. Fix every validation error it prints, then run it again until it passes.\n2. **Choose the target with the user (first publish only).**\n - `appbuilder block targets --json` returns `{ surfaces: { dashboard: [...], homepage: [...] }, customObjects: [{ id, objectName, fetchUrl }] }`. Contacts appears in `customObjects` when available.\n - `appbuilder block targets --object <id> --json` returns `{ surfaces: { chart_group: [...] } }` for that object.\n - Only offer rows where `canEdit !== false`. Ask the user which surface and which specific one. Don't pick for them.\n - Optionally run `appbuilder block push <api_name> --dashboard <id> --json` to get a `needs_choice` result listing the existing custom blocks and \"new\".\n3. **Preview.** Run `appbuilder block push <api_name> --dashboard <id> [--dashlet <id> | --create] --dry-run --json`. Show the user the `environment`, `businessId`, `dashboard.name`, `action` and any `warnings`.\n4. **Apply** once the user agrees: run the same command with `--yes` in place of `--dry-run`. Treat the push as done only when `applied === true`. Give the user the returned `url`, then refresh the browser (below).\n5. **Change requests.** Edit the files, validate again (step 1), then run `appbuilder block push <api_name> --yes --json`. With no target flags it updates the remembered dashlet. Refresh the browser (below) after each push. Repeat for each change.\n\nA headless run without `--yes` is always a dry run.\n\n### Refresh the browser after a push\n\nDo this after every applied push, only when Claude-in-Chrome tools are available. Dashlets don't poll, so the user's open tab keeps the old block until it's refreshed. Don't reload the page.\n\n1. List the open tabs (`tabs_context_mcp`) and find the one whose URL has the same origin and path as the result's `url`.\n2. If there is no such tab, open `url` in a new tab (`tabs_create_mcp`) and go to step 5.\n3. If the result has a `refresh` field, run `refresh.script` exactly as given in that tab with the page JavaScript tool (`javascript_tool`). If it has no `refresh` field, navigate that tab to `url` (`navigate`) and go to step 5.\n4. Check what the script returned:\n - `{ refreshed: true, ... }`: the dashboard refetched in place.\n - `undefined`: the refresh hook isn't available on this page. Navigate the tab to `url` instead. Then run `localStorage.getItem('kizen-flag-custom-code-blocks')` in that tab with `javascript_tool`. If it isn't `'true'`, tell the user once per conversation that setting that localStorage key to `'true'` in the Kizen tab enables in-place refresh. Never set it yourself.\n5. Wait a couple of seconds, take a screenshot (`computer`), and check that the block rendered: not blank, and no visible error. Report what you saw to the user. If the block is blank or shows an error, read the console and fix the block before pushing again.\n\nNever run any other injected code in the user's tabs. `refresh.script` is the only script you may run, with one exception: the read-only `localStorage.getItem('kizen-flag-custom-code-blocks')` check above.\n\n### JSON results\n\n- Dry run: `{ ok: true, applied: false, dryRun: true, action: 'create' | 'update', environment, businessId, dashboard: { id, name, type }, dashletId | null, url, method, path, body, warnings }`.\n- Applied: `{ ok: true, applied: true, dryRun: false, action: 'created' | 'updated', ..., dashletId, url, refresh?: { dashboardId, script }, remembered }`. `refresh` is absent when the dashboard id isn't a uuid.\n- If a `block push` result's `warnings` contains `Claude files managed by appbuilder are out of date; run appbuilder setup-claude`, run `appbuilder setup-claude`, re-read `.claude/skills/kizen-custom-block/SKILL.md`, then continue.\n- Error: `{ ok: false, code, message, choice?, choices? }`. `code` is one of `needs_choice`, `validation_failed`, `invalid_block`, `auth_failed`, `forbidden`, `remembered_target_missing`, `drift_detected`, `network_error`, `credentials_invalid`, `production_requires_flag`, `not_found`, `usage_error`, `api_error` or `local_error`.\n\n### `needs_choice`\n\n`choice` is `'profile'`, `'block'`, `'dashboard'` or `'target'`. `choices` is a list of `{ value, label, args, surface?, canEdit?, pushKey?, isFromThisPlugin? }`, where `args` is a string array. Ask the user using each option's `label`, then re-run the same command with the chosen option's `args` appended exactly. Never construct these flags by hand. The \"new\" option is `{ value: 'new', args: ['--dashboard', <id>, '--create'] }`.\n\n## Guardrails\n\n- Never pass `--allow-production` unless the user explicitly asked, in this conversation, to publish to production (`go` or `fmo`).\n- On `drift_detected`, stop and ask the user. Someone edited the block in Kizen since your last push, and `--force` overwrites their changes. Pass `--force` only when the user says to discard them.\n- On `needs_choice`, ask the user using the returned `choices` as described above. Never guess.\n- On `remembered_target_missing`, the remembered dashboard or dashlet is gone. Ask the user where to publish instead, then push with `--dashboard <id>` plus `--create` or a `--dashlet` choice, or use `--forget` to clear the memory. `--create` alone won't work.\n- On `credentials_invalid`, tell the user to fix the credential file named in `message`, or to pass `--profile` or `-c`.\n- On `production_requires_flag`, the target is `go` or `fmo`. Apply the production rule above.\n- Never push to a new target without first showing the user a dry run.\n- Blocks run with the viewing user's permissions for every viewer. Don't write code that creates, updates or deletes data unless the user asked for it.\n";var $o=`# Designing a Kizen custom block
12
-
13
- With host chrome (the default), a block must look like a native Kizen dashlet. Follow every rule here exactly. Where a rule says "use judgment", pick the option that looks most native. A block whose \`config.json\` sets \`"host_chrome": false\` follows section M for its card and outer box, and skips every rule marked "with host chrome".
14
-
15
- ## A. Card and outer box (with host chrome)
16
-
17
- The host paints the card. Background, border, border radius and drop shadow all come from the dashboard's style settings, the same as native dashlets.
18
-
19
- - Never paint a card. The block root has no background (it stays transparent), no border, no \`border-radius\`, no \`box-shadow\` and no outer margin.
20
- - The block's content box starts at the card's top-left corner (0,0) and fills the whole card. The host body padding is 0. The root is always \`width: 100%; height: 100%; box-sizing: border-box;\`.
21
- - The host draws a 46px header overlay (\`position: absolute\`, padding \`11px 15px 15px\`) over the block's top edge. The dashlet menu appears at its top right on hover. The top-right strip, 60px wide and 46px tall from the card's top edge, must hold no interactive controls and no text. Non-interactive chart marks may pass under it.
22
- - The native grid has a 10px gap between cards and around the grid. Row height is 105px (100px at the mobile, 2-column breakpoint), so a card is \`rows * 105 + (rows - 1) * 10\` px tall. Width depends on the breakpoint's column count.
23
- - Blocks are fluid. Never assume a pixel size. Use percentages, flex or grid, and an SVG \`viewBox\`.
24
- - Set \`default_w\` (columns) and \`default_h\` (rows) in \`config.json\` so the content fits comfortably at its starting size: roughly 3\xD72 for a KPI tile, 6\xD74 for a chart, and 6\xD75 for a table or list. A card \`rows\` tall is \`rows * 105 + (rows - 1) * 10\` px, so 2 rows is 220px, 4 rows is 440px and 5 rows is 565px. Keep each default within any \`min_*\` and \`max_*\` the block sets.
25
-
26
- ## B. Inner spacing
27
-
28
- With host chrome, measure these insets from the card's edge. With chrome off, measure them from the edge of the block-owned card, or follow section M for full bleed.
29
-
30
- - Horizontal inset: 15px left and right, matching the native body padding \`0 15px\`.
31
- - With a header, use the native header band (section C). Content under a header starts at 35px from the top. The header overlay still covers the card down to 46px, so the top-right strip 60px wide from 35px to 46px must hold no interactive controls or text.
32
- - Without a header, use 15px top padding. The same top-right strip, 60px wide down to 46px, stays free of controls and text.
33
- - Bottom padding: 15px.
34
- - Major gaps use 15px: between a KPI value and its comparison row, and between sections.
35
- - Minor gaps use 10px: legend rows, list-row vertical padding, and the padding above a legend.
36
-
37
- \`\`\`css
38
- .kz-block {
39
- box-sizing: border-box;
40
- width: 100%;
41
- height: 100%;
42
- padding: 15px;
43
- display: flex;
44
- flex-direction: column;
45
- font-family: var(--font-family-sans-serif);
46
- color: var(--color-font-primary);
47
- }
48
- .kz-block--with-header {
49
- padding-top: 0;
50
- }
51
- \`\`\`
52
-
53
- ## C. Header (optional; use judgment)
54
-
55
- A block doesn't need a header, and it may use a different kind of header when the content calls for one. When it shows a native-style title, match native exactly:
56
-
57
- - A band 35px tall with padding \`7.5px 60px 7.5px 15px\`. The 60px right padding keeps long titles out from under the hover menu.
58
- - Title text 12px, weight 100 (Proxima Nova Light), \`text-transform: uppercase\`, \`color: var(--color-font-primary)\`, \`line-height: 20px\`, left-aligned at 15px.
59
- - Blocks can't read per-dashboard title-style settings, so always use these defaults.
60
- - Copy \`.kz-header\` from the example below. Its \`margin: 0 -15px\` cancels the root's side padding so the title lands at 15px.
61
-
62
- ## D. Typography
63
-
64
- - Font: Proxima Nova through \`font-family: var(--font-family-sans-serif)\`. The host already loads it.
65
- - The host applies \`* { font-size: 10px; line-height: 1 }\`. Set an explicit px \`font-size\` on every text element.
66
- - Scale (px): hero 32, header 24, subheader 18, body 14, button 13, label 12, small 11, micro 10.
67
- - Weights: 100 light, 400 regular, 600 bold, 900 black.
68
- - Text colors: \`var(--color-font-primary)\` (#4A5660) for primary text, \`var(--color-font-secondary)\` (#7E8C98) for secondary text, \`var(--color-font-placeholder)\` (#A8B3BC) for muted text and captions.
69
- - Header titles and category or axis labels are uppercase. Legend labels use sentence case.
70
-
71
- ## E. Color tokens
72
-
73
- Always use CSS custom properties. Never write raw hex, because themes can change the values. The hex values below are reference only.
74
-
75
- | Use | Token | Default |
76
- | ------------------------ | -------------------------------------------- | ------- |
77
- | Surface | \`--color-background-white\` | #FFFFFF |
78
- | Surface | \`--color-background-light\` | #F5F6F7 |
79
- | Surface | \`--color-background-lighter\` | #F9FAFB |
80
- | Surface | \`--color-background-medium\` | #D8DDE1 |
81
- | Surface | \`--color-background-dark\` | #A8B3BC |
82
- | Surface | \`--color-background-darker\` | #4A5660 |
83
- | Surface | \`--color-background-hover\` | #D8EAFD |
84
- | Border, divider | \`--color-border-medium\` | #D8DDE1 |
85
- | Border, divider | \`--color-border-medium-light\` | #ECEEF0 |
86
- | Border, divider | \`--color-border-light\` | #F5F6F7 |
87
- | Links, secondary actions | \`--color-button-secondary-default\`, \`-hover\` | #4090F7 |
88
- | Primary actions | \`--color-button-primary-default\`, \`-hover\` | #4BC7B4 |
89
- | Status good | \`--color-viz-cat-1-primary\` | #32C055 |
90
- | Status bad | \`--color-viz-cat-7-primary\` | #EB272B |
91
-
92
- Color SVG through CSS classes in \`styles.css\`, for example \`.bar-1 { fill: var(--color-viz-cat-1-primary); }\`. Never use \`fill="var(...)"\` attributes; they aren't reliable.
93
-
94
- ## F. Charts
95
-
96
- Native dashlets use amCharts 5. Blocks hand-build SVG that mimics it.
97
-
98
- - Series palette, in order: \`--color-viz-cat-N-primary\` for N = 1 to 10 (the tokens native charts read). The paired light shade \`--color-viz-cat-N-secondary\` is for backgrounds, tracks and comparison series.
99
- - Defaults (primary / secondary): 1 #32C055 / #ADE6BB, 2 #285DE4 / #6FB2F9, 3 #F3A800 / #FADC99, 4 #4926CC / #B7A6EA, 5 #38B3B9 / #9BD9DB, 6 #F26C56 / #F4A63A, 7 #EB272B / #FF8A8A, 8 #D100A0 / #FF85E2, 9 #AE764A / #D6BAA4, 10 #7A7A7A / #BCBCBC.
100
- - Axis labels: \`fill: var(--color-background-dark)\`, 10 to 11 user units, small and muted.
101
- - Gridlines: \`stroke: var(--color-border-medium-light)\`, 1px.
102
- - Tooltip, if you draw one: background \`var(--color-background-darker)\`, text \`var(--color-background-white)\`.
103
- - Donut: 2px white gaps between segments (\`stroke: var(--color-background-white); stroke-width: 2;\`).
104
- - Dashed forecast or incomplete segments: \`stroke-dasharray: 4 4;\`.
105
- - Legend: 8px circular dots, 10px gap to a 10px label, 20px column gap between items, 10px row gap when wrapping, 10px padding above the legend.
106
- - Put chart labels inside the SVG as \`<text>\` so they scale with the \`viewBox\`.
107
-
108
- ## G. KPI tiles
109
-
110
- The value is hero 32px, weight 400, \`var(--color-font-primary)\`, centered when it stands alone. A comparison or delta row sits 15px below it, \`var(--color-viz-cat-1-primary)\` when good and \`var(--color-viz-cat-7-primary)\` when bad.
111
-
112
- ## H. Number formatting
113
-
114
- Match the native \`collapseNumber\` rules:
115
-
116
- - Not a number: \`0\`.
117
- - |n| < 1: up to 4 decimals, trailing zeros removed.
118
- - |n| < 1,000: up to 5 significant digits, trailing zeros removed.
119
- - 1,000 to 9,999: an integer with a thousands separator.
120
- - 10,000 and up: round to 4 significant digits first, then show K, M or B.
121
- - 1 trillion and up: \`999B+\`, with no sign.
122
- - Currency: prefix the symbol, for example \`$\`.
123
-
124
- Never copy or rewrite this helper. Import it from the CLI-managed data lib:
125
-
126
- \`\`\`js
127
- import { formatNumber } from '../../lib/kizenData.js';
128
- \`\`\`
129
-
130
- ## I. States
131
-
132
- - Loading: a centered, subtle "Loading\u2026" line on a transparent background (with chrome off, on whatever background the block paints). Render it with \`this.outputUI\` first, before any data fetch.
133
- - Empty: centered "No Data", 32px, \`color: var(--color-background-medium)\`.
134
- - Error: a centered 14px message in \`var(--color-font-secondary)\`, for example "Couldn't load data." Never show raw error objects.
135
-
136
- ## J. Icons
137
-
138
- Inline SVG only, with \`fill: currentColor\` or a token applied through a class. Icon fonts and host icon classes aren't available.
139
-
140
- ## K. Scrollbars
141
-
142
- Every scroll container uses the app's expanding scrollbar. Add the \`kds-expanding-scrollbar\` class to the element that scrolls, for example \`<div class="kz-list kds-expanding-scrollbar">\`. The host finds these containers on its own, including ones that render later, and attaches the native thin bar that widens on hover.
143
-
144
- - Put the class on the element that has \`overflow: auto\`, not on its parent. Give that element a bounded height, usually a flex child with \`flex: 1; min-height: 0\`.
145
- - Never style scrollbars yourself: no \`::-webkit-scrollbar\` rules, \`scrollbar-width\` or \`scrollbar-color\`.
146
- - Only scroll when the content really is a list. Otherwise fit the content to the card.
147
-
148
- ## L. Don'ts
149
-
150
- - With host chrome, don't paint an outer card, shadow or border.
151
- - Don't use fixed pixel widths.
152
- - Don't use raw hex colors.
153
- - Don't use \`em\` or \`rem\` units. The base is 10px, so they surprise.
154
- - Don't load custom web fonts.
155
- - Don't put content under the top-right menu area.
156
- - Don't add a scroll container without \`kds-expanding-scrollbar\` (section K).
157
-
158
- ## M. Chrome off (\`"host_chrome": false\`)
159
-
160
- The host paints no background, border, radius or shadow. The block root is still \`width: 100%; height: 100%; box-sizing: border-box;\` and starts at the card's top-left corner (0,0) with no host padding. Pick one of these layouts:
161
-
162
- - Transparent full bleed: the root fills the card edge to edge and the dashboard background shows through wherever the block paints nothing. Use it for heroes and imagery, for example a \`background-image\` with \`background-size: cover\` on the root.
163
- - Block-owned card: the block paints its own card with its own background, \`border-radius\`, border and \`box-shadow\`, using tokens. The host card still clips at its edges (\`overflow: hidden\`), so inset the card from the root by at least its shadow's blur plus offset, usually 8 to 12px (for example \`padding: 10px\` on the root with the card inside). A card with no inset loses its shadow.
164
-
165
- The host adds no backdrop behind its controls, so keep two areas visually quiet, with no dark or busy art directly under them:
166
-
167
- - The top-right menu strip, 60px wide and 46px tall. It still holds no interactive controls and no text.
168
- - The top-left drag-handle area in edit mode, about 30px square. The handle icon sits 5px from the left and 10px from the top and is 15px tall.
169
-
170
- Section A's grid, fluid-size and default-size rules, section C (when you show a native-style header), sections D to K and the rest of section L still apply. Inside a block-owned card, use section B's spacing.
171
-
172
- ## Example: header, bar chart and legend
173
-
174
- The block counts Contacts by their \`status\` dropdown for the current dashboard filters, with the helpers from \`src/lib/kizenData.js\` (see "Reading Kizen data" in \`SKILL.md\`). Swap \`OBJECT\` and \`FIELD\` for the object and field the user asked about. The chart's only text is the y-axis labels at the left edge and the category labels along the bottom, so nothing lands in the top-right strip under the menu.
175
-
176
- \`script.js\`:
177
-
178
- \`\`\`js
179
- import {
180
- blockFilters,
181
- countBy,
182
- escapeHtml,
183
- fieldByName,
184
- formatNumber,
185
- getFields,
186
- searchRecords,
187
- } from '../../lib/kizenData.js';
188
-
189
- const OBJECT = 'client_client';
190
- const FIELD = 'status';
191
-
192
- const render = (body) =>
193
- this.outputUI(
194
- \`<div class="kz-block kz-block--with-header"><div class="kz-header">Contacts by status</div>\${body}</div>\`,
195
- );
196
-
197
- render('<div class="kz-state">Loading\u2026</div>');
198
-
199
- let rows;
200
- let notes;
201
- try {
202
- const fields = await getFields(this, OBJECT);
203
- const field = fieldByName(fields, FIELD);
204
- if (!field) throw new Error(\`\${OBJECT} has no \${FIELD} field\`);
205
- const { filters, unapplied } = blockFilters(this.args, fields);
206
- const { records, count, truncated } = await searchRecords(this, {
207
- object: OBJECT,
208
- fieldNames: [FIELD],
209
- filters,
210
- });
211
- rows = countBy(records, FIELD, field).filter((row) => row.key !== '');
212
- notes = [
213
- truncated ? \`Showing first \${formatNumber(records.length)} of \${formatNumber(count)}\` : '',
214
- unapplied.includes('teamFilter') ? '(team filter not applied)' : '',
215
- ].filter(Boolean);
216
- } catch {
217
- render('<div class="kz-state kz-state--error">Couldn\\'t load data.</div>');
218
- return;
219
- }
220
-
221
- if (rows.length === 0) {
222
- render('<div class="kz-state kz-state--empty">No Data</div>');
223
- return;
224
- }
225
-
226
- const W = 300,
227
- H = 150,
228
- left = 36,
229
- top = 6,
230
- bottom = 18;
231
- const plot = H - top - bottom;
232
- const max = Math.max(...rows.map((r) => r.count)) || 1;
233
- const y = (v) => top + plot - (v / max) * plot;
234
- const step = (W - left) / rows.length;
235
- const barW = Math.min(20, step / 2);
236
-
237
- const grid = [0, 0.5, 1]
238
- .map((f) => f * max)
239
- .map(
240
- (t) =>
241
- \`<line class="grid" x1="\${left}" x2="\${W}" y1="\${y(t)}" y2="\${y(t)}"/>\` +
242
- \`<text class="axis" x="\${left - 6}" y="\${y(t) + 3}" text-anchor="end">\${formatNumber(t)}</text>\`,
243
- )
244
- .join('');
245
-
246
- const bars = rows
247
- .map((r, i) => {
248
- const cx = left + i * step + step / 2;
249
- return (
250
- \`<rect class="bar" x="\${cx - barW / 2}" y="\${y(r.count)}" width="\${barW}" height="\${top + plot - y(r.count)}"/>\` +
251
- \`<text class="axis" x="\${cx}" y="\${H - 4}" text-anchor="middle">\${escapeHtml(r.label)}</text>\`
252
- );
253
- })
254
- .join('');
255
-
256
- render(
257
- \`<div class="kz-chart"><svg viewBox="0 0 \${W} \${H}">\${grid}\${bars}</svg></div>\` +
258
- '<div class="kz-legend">' +
259
- '<span class="kz-legend__item"><span class="kz-dot"></span>Contacts</span>' +
260
- notes.map((note) => \`<span class="kz-note">\${escapeHtml(note)}</span>\`).join('') +
261
- '</div>',
262
- );
263
- \`\`\`
264
-
265
- \`styles.css\`:
266
-
267
- \`\`\`css
268
- .kz-block {
269
- box-sizing: border-box;
270
- width: 100%;
271
- height: 100%;
272
- padding: 15px;
273
- display: flex;
274
- flex-direction: column;
275
- font-family: var(--font-family-sans-serif);
276
- color: var(--color-font-primary);
277
- }
278
- .kz-block--with-header {
279
- padding-top: 0;
280
- }
281
- .kz-header {
282
- flex: none;
283
- box-sizing: border-box;
284
- height: 35px;
285
- margin: 0 -15px;
286
- padding: 7.5px 60px 7.5px 15px;
287
- font-size: 12px;
288
- font-weight: 100;
289
- line-height: 20px;
290
- text-transform: uppercase;
291
- color: var(--color-font-primary);
292
- white-space: nowrap;
293
- overflow: hidden;
294
- text-overflow: ellipsis;
295
- }
296
- .kz-state {
297
- flex: 1;
298
- display: flex;
299
- align-items: center;
300
- justify-content: center;
301
- text-align: center;
302
- font-size: 14px;
303
- color: var(--color-font-placeholder);
304
- }
305
- .kz-state--empty {
306
- font-size: 32px;
307
- color: var(--color-background-medium);
308
- }
309
- .kz-state--error {
310
- font-size: 14px;
311
- color: var(--color-font-secondary);
312
- }
313
- .kz-chart {
314
- flex: 1;
315
- min-height: 0;
316
- }
317
- .kz-chart svg {
318
- display: block;
319
- width: 100%;
320
- height: 100%;
321
- }
322
- .grid {
323
- stroke: var(--color-border-medium-light);
324
- stroke-width: 1;
325
- }
326
- .axis {
327
- fill: var(--color-background-dark);
328
- font-size: 10px;
329
- }
330
- .bar {
331
- fill: var(--color-viz-cat-1-primary);
332
- }
333
- .kz-legend {
334
- flex: none;
335
- display: flex;
336
- flex-wrap: wrap;
337
- column-gap: 20px;
338
- row-gap: 10px;
339
- padding-top: 10px;
340
- }
341
- .kz-legend__item {
342
- display: flex;
343
- align-items: center;
344
- gap: 10px;
345
- font-size: 10px;
346
- color: var(--color-font-primary);
347
- }
348
- .kz-dot {
349
- flex: none;
350
- width: 8px;
351
- height: 8px;
352
- border-radius: 50%;
353
- background: var(--color-viz-cat-1-primary);
354
- }
355
- .kz-note {
356
- font-size: 10px;
357
- color: var(--color-font-placeholder);
358
- }
359
- \`\`\`
360
- `;var jo='# Copilot review instructions for this Kizen plugin\n\nThis repo is a Kizen plugin app: a `kizen.json` manifest plus the artifacts\nthat `@kizenapps/packager` finds by walking the directory tree under the\nmanifest\'s `entry` (default `src/`). No manifest key lists artifacts. The\ndirectory layout decides what gets packaged.\n\n## Component directories\n\nUnder `entry`, the packager treats these directory names as component types:\n\n- `floatingFrames`\n- `blocks`\n- `dataAdornments`\n- `pages`\n- `views`\n- `toolbarItems`\n- `objectSettingsItems`\n- `actions`\n- `routeScripts`\n- `automationSteps`\n- `calendarSources`\n\nEvery directory under a component type needs a `config.json`. `views/` is the\none exception.\n\n## `api_name` rules\n\n- An `api_name` must match `/^[a-z_][a-z0-9_]+$/`: lowercase letters, digits\n and underscores, starting with a letter or underscore, at least 2 characters.\n- Set `api_name` explicitly in each artifact\'s `config.json`. The fallback\n lowercases and strips the directory name (`myFrame` becomes `myframe`), and\n that result can collide with another artifact\'s explicit `api_name`.\n- Data adornments have no `api_name` field. Flag one that adds it as dead\n configuration.\n- Sibling components in the same directory need unique `api_name`s. `pages/`\n and `views/` go further and share one namespace, so a page and a view with\n the same `api_name` collide even though they live in different directories.\n\n## A green build does not mean a correct artifact\n\nThe packager coerces malformed artifact config instead of rejecting it: an\ninvalid `field_type` on a data adornment becomes `phonenumber`, and an invalid\n`minimized_style` on a floating frame becomes `circle`. It also says nothing\nabout a missing required field. Publishing is the real gate. The publish step\ncreates each artifact with no defaulting, so a field the packager never emitted\nis absent and the publish request 400s. Review each\n`config.json` as if the build will not catch mistakes in it, because for these\ncases it does not.\n\nPublishing an app requires these fields per artifact type. An artifact missing\none builds clean and fails to publish:\n\n| Artifact | Required fields |\n| --- | --- |\n| Floating frame | `api_name`, `name`, `title` |\n| Block | `api_name`, `name` |\n| Data adornment | `field_type` (one of `phonenumber`, `date`, `datetime`) |\n| Routable page | `api_name`, `name` |\n| Toolbar item | `api_name`, `label`, and a non-blank `script.js` |\n| Object settings item | `api_name`, `label`, and a non-blank `script.js` |\n| JS action | `api_name`, `name`, `hint_object_name` (non-blank) |\n\nA floating frame whose `default_position` ends in `-fixed` must set\n`minimized_style: "circle"` or omit `minimized_style`. Any other value fails\nthe build with `structure/fixed-frame-minimized-style`.\n\n## Publishing\n\nPublishing an app requires exactly one `thumbnail.png`, and it must sit inside\nthe `entry` directory (`src/thumbnail.png` by default). The packager never sees\na thumbnail at the repo root, and publishing then fails with "Thumbnail is\nrequired for publishing".\n\n## Scope\n\nThis file covers repo structure and general review. Two path-scoped files\ncover the rest: `.github/instructions/security.instructions.md` for\nsecurity-sensitive patterns and\n`.github/instructions/version-discipline.instructions.md` for manifest version\nbumps.\n';var Fo=`---
361
- applyTo: "src/**/*.js,kizen.json"
362
- ---
363
-
364
- # Security review rules for plugin scripts
365
-
366
- These rules mirror the \`security/*\` validation rules in \`@kizenapps/packager\`.
367
- Flag violations the way the packager would, and also flag the patterns listed
368
- here that its static scan cannot detect.
369
-
370
- ## \`__dangerouslySkipProxy\`
371
-
372
- The packager lets \`__dangerouslySkipProxy\` through with the build WARNING
373
- \`security/dangerously-skip-proxy\`. Flag every occurrence for human review
374
- anyway, because the request leaves the browser directly instead of passing
375
- through the Kizen proxy.
376
-
377
- ## Plaintext credentials
378
-
379
- In \`kizen.json\`, a service's \`auth_credentials.token\`,
380
- \`auth_credentials.password\` and \`auth_credentials.client_secret\` must each be
381
- one of:
382
-
383
- - an encrypted envelope (\`{ "encrypted": true, "value": "..." }\`) that
384
- \`appbuilder encrypt\` produced
385
- - a \`{{secret.KEY}}\` reference, with \`KEY\` declared in \`base_config.secrets\`
386
- - a reference through \`auth_credentials.integration_secret_api_name\`
387
-
388
- A raw literal in any of those three fields is the build ERROR
389
- \`security/plaintext-credential\`. Treat a credential that reaches a commit in
390
- plaintext as compromised and rotate it; deleting the line does not undo the
391
- exposure.
392
-
393
- \`client_id\` is excluded from this rule on purpose. Public client ids are not
394
- sensitive, so do not flag them.
395
-
396
- ## Undeclared secret references
397
-
398
- A \`{{secret.KEY}}\` reference whose \`KEY\` is missing from \`base_config.secrets\`
399
- is the build ERROR \`security/undeclared-secret-reference\`, because nothing
400
- would supply the value at install time.
401
-
402
- ## Dynamic code execution
403
-
404
- \`eval(...)\`, \`new Function(...)\` or \`Function(...)\`, and a string argument to
405
- \`setTimeout\` or \`setInterval\` are the build ERROR \`security/dynamic-code\`.
406
-
407
- This rule catches direct, literal usage only. The packager's scan is
408
- parser-based, so a call built through a computed property or a reassigned
409
- reference gets past it. Obfuscated dynamic code stays a human-review concern.
410
-
411
- ## Secret logging and exfiltration
412
-
413
- The packager has no static check for a secret that gets logged, sent to an
414
- unexpected endpoint, or otherwise exfiltrated. Review for this by hand: look
415
- for credentials or decrypted secret values flowing into \`console.log\`, error
416
- messages, analytics calls, or outbound requests to anything other than the
417
- service they belong to.
418
-
419
- ## Handling secrets in this repo
420
-
421
- Encrypt secrets locally with \`appbuilder encrypt\` before committing them. Do
422
- not commit a plaintext secret value in any form; a comment, a script fixture,
423
- and a commit you intend to rewrite later all count.
424
- `;var Oo=`---
425
- applyTo: "kizen.json"
426
- ---
427
-
428
- # Version-bump discipline for \`kizen.json\`
429
-
430
- ## What a wrong bump breaks
431
-
432
- A business that installs this plugin accumulates its own stored state
433
- (automation runs, config values, references to entities) shaped by what was
434
- published at the time. Publishing is additive: it never rewrites a business's
435
- stored rows, and the plugin author has no way to migrate that state for them.
436
- A change is breaking when it invalidates an assumption the business's existing
437
- data already depends on.
438
-
439
- A MINOR or PATCH republish applies at once to a business's running automations
440
- and installed artifacts. Only a new MAJOR version waits for the business to
441
- upgrade explicitly. A change that needed MAJOR but shipped as MINOR or PATCH
442
- therefore breaks the business with no upgrade gate in between.
443
-
444
- Two kinds of change get MAJOR because they fail without an error:
445
-
446
- - Renaming a config field, changing its type, or adding a required key. A
447
- template reference like \`{{config.key}}\` for a key that no longer exists
448
- resolves to a literal \`null\`, and the artifact renders wrong or does
449
- nothing.
450
- - Removing or renaming a JS action. A business's automation and UI
451
- associations key on the action's \`api_name\` as a plain string, with no
452
- foreign key. After a rename or removal those associations stop firing.
453
-
454
- Review every \`kizen.json\` diff against the table below before approving the
455
- version bump in the same PR.
456
-
457
- ## Bump matrix
458
-
459
- | Resource | Change | Required bump |
460
- | --- | --- | --- |
461
- | Plugin | change \`api_name\` | **HARD-BLOCK**: identity can never change |
462
- | Service | add | MINOR |
463
- | Service | remove | MAJOR |
464
- | Service | change auth type or \`auth_level\` | MAJOR |
465
- | Service | change \`auth_credentials\` (client id/secret, scopes, authorize/token URLs) | MAJOR |
466
- | Service | change base URL or other non-auth field | PATCH |
467
- | Service | rename \`api_name\` | MAJOR |
468
- | Secret | add | MINOR |
469
- | Secret | remove | MINOR |
470
- | Secret | rename key | MAJOR |
471
- | Secret | re-encrypt value only | PATCH |
472
- | Automation step | add | MINOR |
473
- | Automation step | remove entire step | MAJOR |
474
- | Automation step | remove an input or output | MAJOR |
475
- | Automation step | change input/output \`data_type\` | MAJOR |
476
- | Automation step | add required input | MAJOR |
477
- | Automation step | add optional input | MINOR |
478
- | Automation step | change python runtime | PATCH |
479
- | Automation step | edit script body only | PATCH |
480
- | Artifact (frame, block, adornment, page, route script, toolbar/object-settings item, calendar source) | add | MINOR |
481
- | Artifact | remove | MINOR |
482
- | Artifact | rename \`api_name\` | MINOR |
483
- | Artifact | edit script or behavior | PATCH |
484
- | JS action | add | MINOR |
485
- | JS action | remove | MAJOR |
486
- | JS action | rename \`api_name\` | MAJOR |
487
- | JS action | change \`hint_object_name\` or script | PATCH |
488
- | Config field | add optional (with default) | MINOR |
489
- | Config field | add required (no default) | MAJOR |
490
- | Config field | remove | MINOR |
491
- | Config field | rename | MAJOR |
492
- | Config field | change type | MAJOR |
493
- | Config field | change default or label | PATCH |
494
- | \`required_entitlement\` | add gating | MINOR |
495
- | \`required_entitlement\` | change or remove | PATCH |
496
- | Manifest metadata (\`name\`, \`description\`, \`entry\`, \`release_branches\`, \`release_environments\`, \`published\`, \`developer_business_id\`) | any change | PATCH |
497
- `;var zo=`const MAX_PAGE_SIZE = 1000;
498
- const DEFAULT_MAX_PAGES = 10;
499
-
500
- const fieldCache = new Map();
501
-
502
- export const formatNumber = (input, prefix = '') => {
503
- const n = Number.isNaN(Number(input)) ? 0 : Number(input);
504
- const abs = Math.abs(n);
505
- const sign = n < 0 ? '-' : '';
506
- if (abs < 1) return \`\${sign}\${prefix}\${Number(abs.toFixed(4))}\`;
507
- if (abs < 1000) return \`\${sign}\${prefix}\${Number(abs.toPrecision(5))}\`;
508
- if (abs < 10000) return \`\${sign}\${prefix}\${Math.round(abs).toLocaleString()}\`;
509
- const rounded = Number(abs.toPrecision(4));
510
- if (rounded >= 1e12) return \`\${prefix}999B+\`;
511
- const [unit, suffix] = rounded >= 1e9 ? [1e9, 'B'] : rounded >= 1e6 ? [1e6, 'M'] : [1e3, 'K'];
512
- return \`\${sign}\${prefix}\${Number((rounded / unit).toPrecision(4))}\${suffix}\`;
513
- };
514
-
515
- const HTML_ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
516
-
517
- export const escapeHtml = (value) =>
518
- value === null || value === undefined
519
- ? ''
520
- : String(value).replace(/[&<>"']/g, (char) => HTML_ESCAPES[char]);
521
-
522
- const describeError = (error) => {
523
- if (typeof error === 'string') return error;
524
- if (error instanceof Error) return error.message;
525
- if (error && typeof error === 'object') {
526
- if (typeof error.message === 'string') return error.message;
527
- try {
528
- return JSON.stringify(error);
529
- } catch {
530
- return String(error);
531
- }
532
- }
533
- return String(error);
534
- };
535
-
536
- const loadFields = async (ctx, object) => {
537
- const [data, error] = await ctx.getWithErrors(
538
- \`/custom-objects/\${encodeURIComponent(object)}/fields\`,
539
- );
540
- if (error) {
541
- throw new Error(\`Couldn't load fields for \${object}: \${describeError(error)}\`);
542
- }
543
- if (!Array.isArray(data)) {
544
- throw new Error(\`Couldn't load fields for \${object}: the response was not a list of fields\`);
545
- }
546
- return data;
547
- };
548
-
549
- export const getFields = (ctx, object) => {
550
- if (!fieldCache.has(object)) {
551
- const pending = loadFields(ctx, object);
552
- fieldCache.set(object, pending);
553
- pending.catch(() => {
554
- if (fieldCache.get(object) === pending) fieldCache.delete(object);
555
- });
556
- }
557
- return fieldCache.get(object);
558
- };
559
-
560
- export const fieldByName = (fields, name) => fields.find((field) => field.name === name);
561
-
562
- const nonCustomFilter = (field, condition, value) => ({
563
- type: 'fields_v2',
564
- subtype: 'non_custom',
565
- field,
566
- condition,
567
- value,
568
- });
569
-
570
- export const blockFilters = (args, fields) => {
571
- const filters = [];
572
- const unapplied = [];
573
- const dateFilter = args?.dateFilter;
574
- const teamFilter = args?.teamFilter;
575
-
576
- if (dateFilter) {
577
- if (dateFilter.start && dateFilter.end) {
578
- filters.push(nonCustomFilter('created', 'between', [dateFilter.start, dateFilter.end]));
579
- } else {
580
- unapplied.push('dateFilter');
581
- }
582
- }
583
-
584
- const teamMembers = teamFilter?.teamMembers ?? [];
585
- const roles = teamFilter?.roles ?? [];
586
-
587
- if (teamMembers.length > 0) {
588
- if (fieldByName(fields, 'owner')) {
589
- filters.push(nonCustomFilter('owner', 'is_any_of', [...teamMembers]));
590
- } else {
591
- unapplied.push('teamFilter');
592
- }
593
- } else if (roles.length > 0) {
594
- unapplied.push('teamFilter');
595
- }
596
-
597
- return { filters, unapplied };
598
- };
599
-
600
- export const dropdownFilter = (field, optionNameOrNames) => {
601
- const names = Array.isArray(optionNameOrNames) ? optionNameOrNames : [optionNameOrNames];
602
- const ids = names.map((name) => {
603
- const option = (field.options ?? []).find((candidate) => candidate.name === name);
604
- if (!option) {
605
- throw new Error(\`\${field.display_name ?? field.name} has no option named "\${name}"\`);
606
- }
607
- return option.id;
608
- });
609
- const multiple = Array.isArray(optionNameOrNames);
610
- return {
611
- type: 'fields_v2',
612
- subtype: 'custom',
613
- field: \`custom::\${field.id}\`,
614
- condition: multiple ? 'is_any_of' : '=',
615
- value: multiple ? ids : ids[0],
616
- };
617
- };
618
-
619
- export const searchRecords = async (
620
- ctx,
621
- {
622
- object,
623
- fieldNames,
624
- filters = [],
625
- pageSize = MAX_PAGE_SIZE,
626
- maxPages = DEFAULT_MAX_PAGES,
627
- ordering,
628
- },
629
- ) => {
630
- const size = Number.isFinite(Number(pageSize))
631
- ? Math.max(1, Math.min(MAX_PAGE_SIZE, Math.floor(Number(pageSize))))
632
- : MAX_PAGE_SIZE;
633
- const pages = Number.isFinite(Number(maxPages))
634
- ? Math.max(1, Math.floor(Number(maxPages)))
635
- : DEFAULT_MAX_PAGES;
636
- const body = {
637
- field_names: fieldNames,
638
- query: Array.isArray(filters) && filters.length > 0 ? [{ and: true, filters }] : [],
639
- };
640
- const orderingParam = ordering ? \`&ordering=\${encodeURIComponent(ordering)}\` : '';
641
- const records = [];
642
- let count = 0;
643
- let hasMore = false;
644
-
645
- for (let page = 1; page <= pages; page += 1) {
646
- const [data, error] = await ctx.postWithErrors(
647
- \`/records/\${encodeURIComponent(object)}/search?page_size=\${size}&page=\${page}\${orderingParam}\`,
648
- body,
649
- );
650
- if (error) {
651
- throw new Error(\`Couldn't search \${object}: \${describeError(error)}\`);
652
- }
653
- const results = Array.isArray(data?.results) ? data.results : [];
654
- records.push(...results);
655
- count = typeof data?.count === 'number' ? data.count : records.length;
656
- hasMore = Boolean(data?.next) && results.length > 0;
657
- if (!hasMore) break;
658
- }
659
-
660
- return { records, count, truncated: hasMore || count > records.length };
661
- };
662
-
663
- const fieldEntry = (record, name) =>
664
- Object.values(record?.fields ?? {}).find((entry) => entry?.name === name);
665
-
666
- export const fieldValue = (record, name) => fieldEntry(record, name)?.value;
667
-
668
- const labelOf = (value) => {
669
- if (value === null || value === undefined || value === '') return '';
670
- if (Array.isArray(value)) return value.map(labelOf).filter(Boolean).join(', ');
671
- if (typeof value === 'object') {
672
- if ('amount' in value) {
673
- if (value.amount === null || value.amount === undefined || value.amount === '') return '';
674
- return formatNumber(Number(value.amount), value.symbol ?? '');
675
- }
676
- if ('first_name' in value || 'last_name' in value) {
677
- const fullName = [value.first_name, value.last_name].filter(Boolean).join(' ');
678
- return fullName || value.email || '';
679
- }
680
- return String(value.name ?? value.display_name ?? '');
681
- }
682
- return String(value);
683
- };
684
-
685
- export const fieldLabel = (record, name) => labelOf(fieldValue(record, name));
686
-
687
- const keyOf = (value) => {
688
- if (value === null || value === undefined) return '';
689
- if (typeof value === 'object') return String(value.id ?? labelOf(value));
690
- return String(value);
691
- };
692
-
693
- const valuesOf = (value) => {
694
- if (Array.isArray(value)) return value.length > 0 ? value : [null];
695
- return [value];
696
- };
697
-
698
- export const countBy = (records, name, field) => {
699
- const groups = new Map();
700
-
701
- for (const record of records) {
702
- for (const value of valuesOf(fieldValue(record, name))) {
703
- const key = keyOf(value);
704
- const group = groups.get(key);
705
- if (group) {
706
- group.count += 1;
707
- } else {
708
- groups.set(key, { key, label: labelOf(value), count: 1 });
709
- }
710
- }
711
- }
712
-
713
- const order = new Map(
714
- (field?.options ?? [])
715
- .filter((option) => typeof option.order === 'number' && Number.isFinite(option.order))
716
- .map((option) => [String(option.id), option.order]),
717
- );
718
- const rank = (key) => (order.has(key) ? order.get(key) : Number.POSITIVE_INFINITY);
719
-
720
- return [...groups.values()].sort((a, b) => {
721
- if (b.count !== a.count) return b.count - a.count;
722
- const ra = rank(a.key);
723
- const rb = rank(b.key);
724
- return ra === rb ? 0 : ra < rb ? -1 : 1;
725
- });
726
- };
727
-
728
- const numberOf = (value) => {
729
- if (value === null || value === undefined || value === '') return 0;
730
- const raw = typeof value === 'object' ? value.amount : value;
731
- if (raw === null || raw === undefined || raw === '') return 0;
732
- const n = Number(raw);
733
- return Number.isFinite(n) ? n : 0;
734
- };
735
-
736
- export const sumBy = (records, name, valueOf) =>
737
- records.reduce(
738
- (total, record) =>
739
- total + (valueOf ? numberOf(valueOf(record)) : numberOf(fieldValue(record, name))),
740
- 0,
741
- );
742
- `;function Lo(){return[{path:".github/copilot-instructions.md",content:jo},{path:".github/instructions/security.instructions.md",content:Fo},{path:".github/instructions/version-discipline.instructions.md",content:Oo}]}var Mo="lib/kizenData.js",jl=(e="src")=>e===""?Mo:`${e}/${Mo}`;function Le(e=["src"]){return[{path:".claude/skills/kizen-custom-block/SKILL.md",content:Bo},{path:".claude/skills/kizen-custom-block/design.md",content:$o},...[...new Set(e)].map(t=>({path:jl(t),content:zo}))]}var Ho=".claude/skills/kizen-custom-block/SKILL.md",Uo="Claude files managed by appbuilder are out of date; run appbuilder setup-claude",Ul=e=>e instanceof Error&&"code"in e&&e.code==="ENOENT",ut={readFile:async e=>{try{return await zl(e,"utf-8")}catch(t){if(Ul(t))return;throw t}},writeFile:async(e,t)=>{await Ol(Ll(e),{recursive:!0}),await Ml(e,t,"utf-8")},exists:async e=>{try{return await Fl(e),!0}catch{return!1}}},Vl=e=>{if(typeof e!="string"||e.trim()==="")return;let t=Hl.normalize(e.trim().replaceAll("\\","/")).replace(/\/+$/,"");if(!(t===".."||t.startsWith("../")||t.startsWith("/")))return t==="."?"":t};function Wl(e){let r=(Array.isArray(e)?e:[e]).map(o=>typeof o=="object"&&o!==null&&"entry"in o?Vl(o.entry):void 0).filter(o=>o!==void 0);return r.length>0?[...new Set(r)]:["src"]}async function Vo(e,t=ut){try{let r=await t.readFile(dt(e,"kizen.json"));return Le(r===void 0?void 0:Wl(JSON.parse(r)))}catch{return Le()}}var Kl=(e,t)=>e===void 0?"created":e===t?"unchanged":"updated";async function Wo(e,t=ut,r=Le()){return Promise.all(r.map(async o=>({path:o.path,status:Kl(await t.readFile(dt(e,o.path)),o.content)})))}async function Jl(e,t={},r=ut,o=Le()){let n=await Wo(e,r,o);return t.dryRun===!0||await Promise.all(o.filter((i,s)=>n[s]?.status!=="unchanged").map(i=>r.writeFile(dt(e,i.path),i.content))),n}async function Ko(e,t=ut,r){try{let o=r??await Vo(e,t);return!o.some(i=>i.path===Ho)||await t.readFile(dt(e,Ho))===void 0?!1:(await Wo(e,t,o)).some(i=>i.status!=="unchanged")}catch{return!1}}var ql={fs:ut,log:e=>{console.log(e)},error:e=>{console.error(e)},setExitCode:e=>{process.exitCode=e}},Kr=(e,t)=>`${String(e.filter(r=>r.status===t).length)} ${t}`;async function Jo(e,t,r=ql,o){if(!await r.fs.exists(dt(e,"kizen.json"))){r.error("Error: kizen.json not found. Run this command from a plugin directory."),r.setExitCode(1);return}let n=t.dryRun===!0,i;try{i=await Jl(e,{dryRun:n},r.fs,o??await Vo(e,r.fs))}catch(a){r.error(`Error: ${a instanceof Error?a.message:String(a)}`),r.setExitCode(1);return}for(let a of i)r.log(`${a.status.padEnd(9)} ${a.path}`);let s=`${Kr(i,"created")}, ${Kr(i,"updated")}, ${Kr(i,"unchanged")}`,l="these files are managed by appbuilder, so local edits to them are replaced";r.log(n?`Dry run: ${s}; nothing written (${l}).`:`Claude files: ${s} (${l}).`)}function Ut(e){return{cwd:e,loadCredentialsDetailed:zr,listLoadableCredentialProfiles:co,loadConfig:he,packageBlocks:jr,createClient:t=>mo({credentials:t}),readPushMap:Io,writePushMap:Ao,ensureGitignore:Ne,isClaudeSkillStale:t=>Ko(t),randomUUID:Gl,now:()=>new Date,writeStdout:t=>{process.stdout.write(t)},writeStderr:t=>{process.stderr.write(t)},setExitCode:t=>{process.exitCode=t}}}var Xl=new Set(["owner","admin","edit"]),Zl="Chart groups are not listed here; find them with `appbuilder block targets --object <id> --json` and pass --dashboard <id>.",Yr=e=>e instanceof Error?e.message:typeof e=="string"?e:"Unknown error";function L(e,t,r={}){return{ok:!1,code:e,message:t,...r}}var ge=e=>({ok:!0,value:e});function He(e){return e instanceof re?L(e.kind,e.message,e.hint===void 0?{}:{hint:e.hint}):e instanceof Go?L("validation_failed",me(e.issues),{issues:e.issues}):L("local_error",Yr(e))}var Ql=()=>X.join(", ");async function Ht(e,t){let r;try{r=await t.loadCredentialsDetailed(e.path)}catch(i){return L("credentials_invalid",`Couldn't read credentials from ${e.path}: ${Yr(i)}`)}if(r.environmentSource!=="explicit"){let i=r.environmentSource==="missing"?'has no "environment"':'has an invalid "environment"';return L("credentials_invalid",`${e.path} ${i}. Set it to one of: ${Ql()}. The CLI won't default to production.`)}let{credentials:o}=r,n=["apiKey","userId","businessId"].filter(i=>o[i].trim()==="");return n.length>0?L("credentials_invalid",`${e.path} is missing ${n.map(i=>`"${i}"`).join(", ")}.`):ge({credentials:o,source:e})}var Jr=e=>({kind:"profile",name:e.name,path:e.path});async function Vt(e,t){if(e.credentials!==void 0)return Ht({kind:"file",path:e.credentials},t);let r=await t.listLoadableCredentialProfiles(),o=r.map(l=>l.name);if(e.profile!==void 0){let l=r.find(a=>a.name===e.profile);return l?Ht(Jr(l),t):L("credentials_invalid",o.length===0?`No loadable credential profile named "${e.profile}". No profiles are loadable; create ${xe} or pass -c <path>.`:`No loadable credential profile named "${e.profile}". Loadable profiles: ${o.join(", ")}`)}let n=await t.loadConfig(Yl(t.cwd,".kizenapp"));if(n.credentialMode==="local")return L("credentials_invalid","This plugin is set to use local browser-only credentials the CLI can't read. Pass -c <path> or --profile <name>.");let i=n.activeCredentialProfile===void 0?void 0:r.find(l=>l.name===n.activeCredentialProfile);if(i)return Ht(Jr(i),t);let[s]=r;return r.length===1&&s?Ht(Jr(s),t):r.length===0?L("credentials_invalid",`No credentials found. Create ${xe} or pass -c <path>.`):L("needs_choice",`Several credential profiles are available; pass --profile <name>: ${o.join(", ")}`,{choice:"profile",choices:r.map(l=>({value:l.name,label:l.isDefault?`${l.name} (default)`:l.name,args:["--profile",l.name]}))})}function ec(e,t){let r=new Set(t.map(o=>o.pluginApiName)).size>1;return t.map(({pluginApiName:o,apiName:n})=>{let i=e.find(l=>l.api_name===o)?.artifacts.custom_blocks.find(l=>l.api_name===n),s=r?`${o}/${n}`:n;return{value:n,label:`${i?.name??n} (${s})`,args:[n]}})}function tc(e,t){let r=Nr(e.available);return e.reason==="no_blocks"?"This plugin has no blocks. Scaffold one with `appbuilder create --artifacts block`.":e.reason==="not_found"?`No block with api_name "${t??""}". Available blocks: ${r}`:t===void 0?`This plugin has several blocks; pass the api_name of the one to push: ${r}`:`More than one plugin has a block with api_name "${t}": ${r}`}async function Xr(e,t){let r;try{r=await t.packageBlocks(t.cwd)}catch(s){if(s instanceof Go)return L("validation_failed",me(s.issues),{issues:s.issues});throw s}let o=Dr(r.deployable,e);if(!o.ok){let s=tc(o,e);return o.reason==="no_blocks"?L("invalid_block",s):L("needs_choice",s,{choice:"block",choices:ec(r.deployable,o.available)})}let n=Br(o.block);if(n!==void 0)return L("invalid_block",$r(o.block.api_name,n),{field:n});let i=ko(o.block);return ge({block:o.block,pluginApiName:o.pluginApiName,content:i,contentHash:Vr(i),warnings:r.warnings})}var Yo=e=>e==null?null:Xl.has(e.toLowerCase());function pt(e,t){return{id:e.id,name:e.name,type:t,dashletsCount:e.dashlets_count??null,employeeAccess:e.employee_access??null,hidden:e.hidden??!1,canEdit:Yo(e.employee_access)}}var Xo=e=>({id:e.id,objectName:e.object_name,fetchUrl:e.fetch_url}),rc="Contacts";async function Zr(e){let[t,r]=await Promise.all([e.listCustomObjects(),e.getClientObjectId().catch(()=>null)]),o=t.map(Xo).sort((n,i)=>n.objectName.localeCompare(i.objectName));return r!==null&&r!==""&&!o.some(n=>n.id===r)&&o.push({id:r,objectName:rc,fetchUrl:"client"}),o}async function nc(e,t,r){let{environment:o,businessId:n}=t;try{if(r.object!==void 0){let a=r.object,[p,h]=await Promise.all([e.getCustomObject(a).catch(()=>null),e.listDashboards("chart_group",a)]);return ge({ok:!0,environment:o,businessId:n,object:p?Xo(p):null,surfaces:{chart_group:h.map(w=>pt(w,"chart_group"))}})}let[i,s,l]=await Promise.all([e.listDashboards("generic_dashboard"),e.listDashboards("homepage"),Zr(e)]);return ge({ok:!0,environment:o,businessId:n,surfaces:{dashboard:i.map(a=>pt(a,"dashboard")),homepage:s.map(a=>pt(a,"homepage"))},customObjects:l})}catch(i){return He(i)}}var Qr=(e,t)=>t.dashboard===e.id;function Wt(e,t){return[...Po(e).filter(o=>Qr(e,o)).map(o=>{let n=wo(o.name);return{value:o.id,label:Mt(o),args:["--dashboard",e.id,"--dashlet",o.id],pushKey:n?o.name:null,isFromThisPlugin:n?.pluginApiName===t.pluginApiName}}),{value:"new",label:"Create a new block",args:["--dashboard",e.id,"--create"]}]}function Qe(e,t,r,o){return ge({dashboard:e,surface:No(e.type),action:t,dashlet:r,resolvedBy:o})}async function oc(e){let[t,r]=await Promise.all([e.client.listDashboards("generic_dashboard"),e.client.listDashboards("homepage")]),o=n=>i=>({value:i.id,label:i.name,args:["--dashboard",i.id],surface:n,canEdit:Yo(i.employee_access)});return L("needs_choice",`Choose where to push; pass --dashboard <id>. ${Zl}`,{choice:"dashboard",choices:[...t.map(o("dashboard")),...r.map(o("homepage"))]})}async function ic(e,t,r){let o=await e.client.getDashboard(t);if(r.create)return Qe(o,"create",void 0,"flag");let n=()=>Wt(o,e.block);if(r.dashlet!==void 0){let s=r.dashlet,l=o.dashlets.find(a=>a.id===s&&Qr(o,a));return l?Ze(l)?Qe(o,"update",l,"flag"):L("usage_error",`Dashlet ${s} is not a custom code block, so it can't be updated with a pushed block. Pass --create to add a new one.`):L("not_found",`Dashlet ${s} is not on dashboard "${o.name}" (${o.id}).`,{choice:"target",choices:n()})}let i=Lt({dashboardId:o.id,dashlets:o.dashlets,pluginApiName:e.block.pluginApiName,blockApiName:e.block.block.api_name,entry:e.entry});return i.kind==="map"?Qe(o,"update",i.dashlet,"push_map"):i.kind==="name"?Qe(o,"update",i.dashlet,"name"):L("needs_choice",i.kind==="ambiguous"?`Dashboard "${o.name}" has ${String(i.dashlets.length)} blocks pushed from this block; pass --dashlet <id> to pick one, or --create.`:`Choose a block to update on "${o.name}", or create a new one; pass --dashlet <id> or --create.`,{choice:"target",choices:n()})}async function sc(e,t,r){let o;try{o=await e.client.getDashboard(t.dashboardId)}catch(i){if(i instanceof re&&i.kind==="not_found")return L("remembered_target_missing",`The remembered dashboard ${t.dashboardId} no longer exists. Pass --dashboard <id> to choose another, or --forget to clear it.`);throw i}if(r.create)return Qe(o,"create",void 0,"remembered");let n=o.dashlets.find(i=>i.id===t.dashletId&&Qr(o,i));return n&&Ze(n)?Qe(o,"update",n,"remembered"):L("remembered_target_missing",`The remembered block (dashlet ${t.dashletId}) is no longer on "${o.name}". Choose a block to update, or create a new one.`,{choice:"target",choices:Wt(o,e.block)})}async function en(e,t){if(t.dashlet!==void 0&&t.create)return L("usage_error","Pass either --dashlet <id> or --create, not both.");if(t.dashlet!==void 0&&t.dashboard===void 0)return L("usage_error","--dashlet <id> needs --dashboard <id>.");try{return t.dashboard!==void 0?await ic(e,t.dashboard,t):e.entry?await sc(e,e.entry,t):await oc(e)}catch(r){return He(r)}}async function ac(e,t,r){let o=t.custom_object;if(o==null||o==="")return null;if(typeof o!="string"){if(typeof o!="object"||typeof o.id!="string")return null;let{id:n,fetch_url:i}=o;return typeof i=="string"?{id:n,fetchUrl:i}:{id:n}}if(r!=="chart_group")return{id:o};try{let n=await e.getCustomObject(o);return{id:o,fetchUrl:n.fetch_url}}catch{return{id:o}}}var qr=encodeURIComponent;async function tn(e,t,r,o){let{credentials:n,block:i}=e,{dashboard:s,surface:l,dashlet:a}=t,p=[],h,w,x;if(t.action==="update"){if(!a)return L("usage_error","An update needs a dashlet to update.");if(Eo(a,e.entry)){let y=`"${Mt(a)}" (dashlet ${a.id}) was changed in Kizen since the last push.`;if(!r.force)return L("drift_detected",`${y} Pushing would overwrite those changes; pass --force to overwrite them.`);p.push(`${y} Overwriting it because of --force.`)}h=So(a,i.content),w=`/dashboards/${qr(s.id)}/dashlet/${qr(a.id)}`,x=`Update "${Mt(a)}" (dashlet ${a.id})`}else h=Co({pluginApiName:i.pluginApiName,blockApiName:i.block.api_name,content:i.content,dashlets:s.dashlets,objectId:o.randomUUID(),layoutId:o.randomUUID()}),w=`/dashboards/${qr(s.id)}/dashlet`,x=`Create a new block on "${s.name}"`;let d=t.action==="update"&&a?a.id:null,c=Ro({appBaseUrl:Qn[n.environment],surface:l,dashboardId:s.id,customObject:await ac(e.client,s,l)});return ge({action:t.action,method:t.action==="update"?"PATCH":"POST",path:w,body:h,dashboard:{id:s.id,name:s.name,type:l},dashletId:d,url:c,resolvedBy:t.resolvedBy,warnings:p,summary:{environment:n.environment,businessId:n.businessId,isProduction:Or(n.environment),surface:l,dashboardId:s.id,dashboardName:s.name,action:t.action,actionLine:x,dashletId:d,blockName:i.block.name,blockApiName:i.block.api_name,pluginApiName:i.pluginApiName,scriptBytes:Buffer.byteLength(i.content.script,"utf8"),stylesBytes:Buffer.byteLength(i.content.styles,"utf8")}})}var et=e=>({environment:e.credentials.environment,businessId:e.credentials.businessId,pluginApiName:e.block.pluginApiName,blockApiName:e.block.block.api_name});async function rn(e,t,r){let o;try{if(t.action==="create"){let a=(await e.client.createDashlet(t.dashboard.id,t.body)).id;if(typeof a!="string"||a==="")return L("api_error",`POST ${t.path} succeeded but the response had no dashlet id; check "${t.dashboard.name}" before pushing again.`);o=a}else{if(t.dashletId===null)return L("usage_error","An update needs a dashlet to update.");await e.client.updateDashlet(t.dashboard.id,t.dashletId,t.body),o=t.dashletId}}catch(l){return He(l)}let n=[...t.warnings],i={...et(e),dashboardId:t.dashboard.id,dashletId:o,contentHash:e.block.contentHash,pushedAt:r.now().toISOString()},s=!1;try{r.ensureGitignore(r.cwd),await r.writePushMap(r.cwd,To(e.pushMap,i)),s=!0}catch(l){n.push(`The push succeeded, but ${Me} couldn't be written, so this target isn't remembered: ${Yr(l)}`)}return ge({dashletId:o,remembered:s,warnings:n})}var lc=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function nn(e){return lc.test(e)?{dashboardId:e,script:`await window.__kizenCustomBlocks?.refresh('${e}')`}:null}async function on(e,t){let r=et(e);return lt(e.pushMap,r)?(await t.writePushMap(t.cwd,ct(e.pushMap,r)),!0):!1}function sn(e,t){return e.dryRun?ge({kind:"dry-run",reason:"flag"}):e.yes?Or(t)&&!e.allowProduction?L("production_requires_flag",`${t} is a production environment. Pass --allow-production with --yes to write to it.`):ge({kind:"apply"}):ge({kind:"dry-run",reason:"no_yes"})}var cc="Dry run only: pass --yes to write.";function ft(e){return[`Environment: ${e.environment}${e.isProduction?" (production)":""}`,`Business: ${e.businessId}`,`Surface: ${e.surface}`,`Dashboard: "${e.dashboardName}" (${e.dashboardId})`,`Action: ${e.actionLine}`,`Block: ${e.blockName} (${e.pluginApiName}/${e.blockApiName})`,`Size: script ${String(e.scriptBytes)} bytes, styles ${String(e.stylesBytes)} bytes`]}var dc=e=>`${e.path===void 0?"":`${e.path}: `}${e.message} (${e.rule})`,uc=e=>/^[\w./:@=-]+$/.test(e)?e:JSON.stringify(e);function pc(e,t,r){let o=[`Error: ${e.message}`];if(e.hint!==void 0&&o.push(`Hint: ${e.hint}`),e.choices&&e.choices.length>0){o.push("Choices:");for(let n of e.choices){let i=r(n,e.choice).map(uc).join(" ");o.push(` ${n.label} \u2192 ${t} ${i}`)}}return`${o.join(`
743
- `)}
744
- `}function Zo(e,t,r,o){e.json?e.deps.writeStdout(`${JSON.stringify(t,null,2)}
745
- `):e.deps.writeStderr(pc(t,r,o)),e.deps.setExitCode(1)}var qo=e=>e.credentials!==void 0?["-c",e.credentials]:e.profile===void 0?[]:["--profile",e.profile];async function fc(e){try{return await e.isClaudeSkillStale(e.cwd)?[Uo]:[]}catch{return[]}}async function Qo(e,t,r){let o={json:t.json===!0,deps:r},n="appbuilder block push",i=e===void 0?[]:[e],s=(a,p)=>p==="profile"?[...i,...a.args]:p==="block"?[...a.args,...qo(t)]:[...i,...qo(t),...a.args],l=a=>{Zo(o,a,n,s)};try{let a=await Vt(t,r);if(!a.ok){l(a);return}let{credentials:p}=a.value,h=r.createClient(p),w=await Xr(e,r);if(!w.ok){l(w);return}let x=w.value;i=[x.block.api_name],x.warnings.length>0&&r.writeStderr(`${me(x.warnings)}
746
- `);let d={client:h,credentials:p,block:x,pushMap:[],entry:void 0};d.pushMap=await r.readPushMap(r.cwd);let c=!1;t.forget&&(c=await on(d,r),c&&(d.pushMap=ct(d.pushMap,et(d)),r.writeStderr(`Forgot the remembered target for ${x.block.api_name}.
747
- `))),d.entry=lt(d.pushMap,et(d));let y=await en(d,t);if(!y.ok){l(y);return}let v=sn(t,p.environment);if(!v.ok){l(v);return}let P=await tn(d,y.value,t,r);if(!P.ok){l(P);return}let C=P.value,_={pluginApiName:x.pluginApiName,apiName:x.block.api_name,name:x.block.name},S={environment:p.environment,businessId:p.businessId,dashboard:C.dashboard},g=[...x.warnings.map(dc),...await fc(r)],f=v.value;if(f.kind==="dry-run"){let R=[...g,...C.warnings],M={ok:!0,applied:!1,dryRun:!0,reason:f.reason,action:C.action,...S,dashletId:C.dashletId,url:C.url,resolvedBy:C.resolvedBy,block:_,method:C.method,path:C.path,body:C.body,warnings:R,...c?{forgotten:!0}:{}};o.json?(r.writeStdout(`${JSON.stringify(M,null,2)}
748
- `),r.writeStderr([...ft(C.summary),...R.map(W=>`Warning: ${W}`),""].join(`
749
- `))):r.writeStdout([...ft(C.summary),`URL: ${C.url}`,...R.map(W=>`Warning: ${W}`),"",`${C.method} ${C.path}`,JSON.stringify(C.body,null,2),""].join(`
750
- `)),f.reason==="no_yes"&&r.writeStderr(`${cc}
751
- `);return}let u=await rn(d,C,r);if(!u.ok){l(u);return}let m=u.value,b=[...g,...m.warnings],k=nn(C.dashboard.id),I={ok:!0,applied:!0,dryRun:!1,action:C.action==="create"?"created":"updated",...S,dashletId:m.dashletId,url:C.url,...k?{refresh:k}:{},resolvedBy:C.resolvedBy,block:_,remembered:m.remembered,warnings:b,...c?{forgotten:!0}:{}},E=[`\u2713 ${I.action==="created"?"Created":"Updated"} ${x.block.name} on "${C.dashboard.name}" (dashlet ${m.dashletId})`,...ft(C.summary),`URL: ${C.url}`,...k?[`Refresh an open tab: ${k.script}`]:[],...b.map(R=>`Warning: ${R}`),""].join(`
752
- `);o.json?(r.writeStdout(`${JSON.stringify(I,null,2)}
753
- `),r.writeStderr(E)):r.writeStdout(E)}catch(a){l(He(a))}}var mc=(e,t)=>e.padEnd(t);function Gr(e,t){if(t.length===0)return[e," (none)"];let r=["NAME","ID","DASHLETS","ACCESS"],o=t.map(s=>[`${s.name}${s.hidden?" (hidden)":""}`,s.id,s.dashletsCount===null?"-":String(s.dashletsCount),`${s.employeeAccess??"-"}${s.canEdit===!1?" (no edit access)":""}`]),n=r.map((s,l)=>Math.max(s.length,...o.map(a=>(a[l]??"").length))),i=s=>` ${s.map((l,a)=>mc(l,n[a]??0)).join(" ")}`.trimEnd();return[e,i(r),...o.map(i)]}function hc(e){let t=[`Environment: ${e.environment}`,`Business: ${e.businessId}`,""];return"object"in e?t.push(e.object===null?"Custom object: not found in /custom-objects":`Custom object: ${e.object.objectName} (${e.object.id})`,"",...Gr("Chart groups",e.surfaces.chart_group)):t.push(...Gr("Dashboards",e.surfaces.dashboard),"",...Gr("Homepages",e.surfaces.homepage),"","Custom objects",...e.customObjects.length===0?[" (none)"]:e.customObjects.map(r=>` ${r.objectName} ${r.id}`),"","List an object\u2019s chart groups with `appbuilder block targets --object <id>`."),`${t.join(`
754
- `)}
755
- `}async function ei(e,t){let r={json:e.json===!0,deps:t},o="appbuilder block targets",n=e.object===void 0?[]:["--object",e.object],i=l=>[...l.args,...n],s=l=>{Zo(r,l,o,i)};try{let l=await Vt(e,t);if(!l.ok){s(l);return}let{credentials:a}=l.value,p=await nc(t.createClient(a),a,e.object===void 0?{}:{object:e.object});if(!p.ok){s(p);return}t.writeStdout(r.json?`${JSON.stringify(p.value,null,2)}
756
- `:hc(p.value))}catch(l){s(He(l))}}import{useEffect as ii,useState as qt}from"react";import{Box as oe,Text as q,useApp as Rc,useInput as ci}from"ink";import"@kizenapps/packager";import{Box as wc,Text as xc}from"ink";import{Box as gc,Text as yc}from"ink";import{jsx as ti}from"react/jsx-runtime";var bc=[" :::::::: "," :::::: ::::::::::: "," :::::: :::::::::: "," :::::: :::::: ::::::: "," ::::: :::::: ::::::::::::::::: "," ::::: ::::: ::::::::::::::::::: "," ::::: ::::: :::::: ::::: ",":::: ::::: ::: ::::: :: ","::::: :::::: :::::::::: ","::::: :::::: ::::::::::: "," ::::: ::: ::::::: "," ::::::: ::: ::::: "," ::::::::::: :::::: :::::"," :::::::::: :::::: :::::"," :: ::::: ::: ::::: ::::"," ::::: :::::: ::::: ::::: ::: "," ::::::::::::::::::: ::::: ::::: "," ::::::::::::::::: ::::: ::::: "," ::::::::: :::::: :::::: "," :::::::::: :::::: "," ::::::::::: :::::: "," :::::::: "],ri=()=>ti(gc,{flexDirection:"column",children:bc.map((e,t)=>ti(yc,{color:"cyan",children:e},t))});import{Fragment as vc,jsx as an,jsxs as kc}from"react/jsx-runtime";var K=({marginBottom:e=0})=>kc(vc,{children:[an(ri,{}),an(wc,{flexDirection:"column",marginTop:1,marginBottom:e,children:an(xc,{bold:!0,color:"cyan",children:"Kizen App Development Toolkit"})})]});import{useMemo as Cc,useState as Sc}from"react";import{Box as ln,Text as Ue,useInput as _c}from"ink";var Kt=(e,t,r)=>Math.min(Math.max(e,t),r);function Jt(e,t,r,o={}){if(t<=0)return 0;let n=Kt(Math.trunc(e)||0,0,t-1),i=a=>o.disabled?.[a]!==!0;if(r===0)return n;let s=r>0?1:-1;if(Math.abs(r)===1){for(let a=1;a<=t;a++){let p=((n+s*a)%t+t)%t;if(i(p))return p}return n}let l=Kt(n+Math.trunc(r),0,t-1);for(let a=l;a>=0&&a<t;a+=s)if(i(a))return a;for(let a=l-s;a>=0&&a<t;a-=s)if(i(a))return a;return n}function ni(e,t,r=10){if(t<=0)return{start:0,end:0};let o=Math.max(1,Math.floor(r));if(t<=o)return{start:0,end:t};let n=Kt(Math.trunc(e)||0,0,t-1),i=Kt(n-Math.floor(o/2),0,t-o);return{start:i,end:i+o}}import{jsx as Ve,jsxs as cn}from"react/jsx-runtime";var Pc="\u2191/\u2193 to move, Enter to select, Esc to cancel",Ec=10,Tc=(e,t)=>{let r=Jt(t,e.length,0);if(e[r]?.disabled!==!0)return r;let o=e.findIndex(n=>n.disabled!==!0);return o===-1?r:o},We=({title:e,items:t,onSelect:r,onCancel:o,hint:n=Pc,initialIndex:i=0,windowSize:s=Ec,isActive:l=!0})=>{let[a,p]=Sc(()=>Tc(t,i)),h=Cc(()=>t.map(P=>P.disabled===!0),[t]),w=Jt(a,t.length,0),x=Math.max(1,Math.floor(s));_c((P,C)=>{let _=S=>{p(Jt(w,t.length,S,{disabled:h}))};if(C.upArrow||P==="k")_(-1);else if(C.downArrow||P==="j")_(1);else if(C.pageUp)_(-x);else if(C.pageDown)_(x);else if(C.return){let S=t[w];S!==void 0&&S.disabled!==!0&&r(S,w)}else C.escape&&o?.()},{isActive:l});let{start:d,end:c}=ni(w,t.length,x),y=d,v=t.length-c;return cn(ln,{flexDirection:"column",gap:1,children:[Ve(Ue,{bold:!0,children:e}),cn(ln,{flexDirection:"column",children:[y>0&&Ve(Ue,{dimColor:!0,children:` \u2191 ${String(y)} more`}),t.slice(d,c).map((P,C)=>{let S=d+C===w,g=P.disabled===!0,f=S&&!g;return cn(ln,{gap:2,children:[Ve(Ue,{...f&&{color:"cyan"},dimColor:g,children:S?"\u276F":" "}),Ve(Ue,{bold:f,dimColor:g,...f&&{color:"cyan"},children:P.label}),P.description!==void 0&&Ve(Ue,{dimColor:!0,children:P.description})]},P.key)}),v>0&&Ve(Ue,{dimColor:!0,children:` \u2193 ${String(v)} more`})]}),Ve(Ue,{dimColor:!0,children:n})]})};import{useEffect as Ic,useState as Ac}from"react";import{Text as Nc}from"ink";import{jsx as Dc}from"react/jsx-runtime";var oi=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],ye=()=>{let[e,t]=Ac(0);return Ic(()=>{let r=setInterval(()=>{t(o=>(o+1)%oi.length)},80);return()=>{clearInterval(r)}},[]),Dc(Nc,{color:"cyan",children:oi[e]??"\u280B"})};import{Fragment as Hc,jsx as B,jsxs as J}from"react/jsx-runtime";var si=[{key:"dashboard",label:"Dashboard"},{key:"homepage",label:"Homepage"},{key:"chart_group",label:"Chart group"}],Bc={dashboard:"dashboard",homepage:"homepage",chart_group:"chart group"},$c="The block was edited in Kizen since your last push. Overwriting discards those edits.",Gt=e=>e.args.includes("--create");function ai(e,t){let r=e.filter(i=>i.value===t),o=e.filter(Gt),n=e.filter(i=>i.value!==t&&!Gt(i));return r.length>0?[...r,...n,...o]:[...o,...n]}function jc(e,t){if(Gt(e))return{key:e.value,label:e.label};if(e.value===t)return{key:e.value,label:`Update "${e.label}" (last pushed here)`};let r=e.isFromThisPlugin?"pushed from this plugin":e.pushKey;return{key:e.value,label:`Update "${e.label}"`,...r?{description:r}:{}}}function Fc(e){let t=e.dashletsCount===null?e.name:`${e.name} (${String(e.dashletsCount)} dashlets)`;if(e.canEdit===!1)return{key:e.id,label:t,description:"no edit access",disabled:!0};let r=[e.employeeAccess,e.hidden?"hidden":null].filter(o=>o!==null&&o!=="").join(" \xB7 ");return{key:e.id,label:t,...r===""?{}:{description:r}}}function Oc(e,t,r){let o={...e,dashboard:t};return delete o.dashlet,delete o.create,Gt(r)?{...o,create:!0}:{...o,dashlet:r.value}}function zc(e,t,r,o){let n=d=>{t.setExitCode(1),r({type:"error",view:d})},i=d=>{d().catch(c=>{n(He(c))})},s=async(d,c)=>{r({type:"applying",plan:c});let y=await rn(d,c,t);if(!y.ok){n(y);return}r({type:"done",plan:c,applied:y.value})},l=async(d,c,y)=>{r({type:"loading",label:"Preparing the push\u2026"});let v=await tn(d,c,y,t);if(!v.ok){if(v.code==="drift_detected"){r({type:"confirm",kind:"drift",message:v.message,target:c,options:y,context:d});return}n(v);return}if(y.dryRun){r({type:"done",plan:v.value,applied:null});return}if(y.yes){let P=sn(y,d.credentials.environment);if(P.ok&&P.value.kind==="apply"){await s(d,v.value);return}}r({type:"confirm",kind:"push",plan:v.value,options:y,context:d})},a=async(d,c,y)=>{r({type:"loading",label:"Resolving the target\u2026"});let v=await en(d,c);if(v.ok){await l(d,v.value,c);return}let P=c.dashboard??d.entry?.dashboardId;if((v.code==="needs_choice"&&v.choice==="target"||v.code==="remembered_target_missing")&&v.choices&&v.choices.length>0&&P!==void 0){r({type:"target-select",dashboardId:P,title:"Choose a block to update, or create a new one",message:v.message,choices:ai(v.choices,null),matchedId:null,back:y,options:c,context:d});return}n(v)},p=async(d,c,y)=>{let v=et({credentials:c,block:y}),P=await t.readPushMap(t.cwd);d.forget&&await on({credentials:c,block:y,pushMap:P},t)&&(P=ct(P,v),o(_=>({..._,notices:[..._.notices,`Forgot the remembered target for ${y.block.api_name}.`]})));let C={client:t.createClient(c),credentials:c,block:y,pushMap:P,entry:lt(P,v)};if(d.dashboard!==void 0||C.entry){await a(C,d,null);return}r({type:"surface-select",options:d,context:C})},h=async(d,c,y)=>{r({type:"packaging"});let v=await Xr(y,t);if(!v.ok){if(v.choice==="block"&&v.choices){r({type:"block-select",options:d,credentials:c,choices:v.choices});return}n(v);return}o(P=>({...P,warnings:v.value.warnings})),await p(d,c,v.value)},w=async d=>{r({type:"loading",label:"Loading credentials\u2026"});let c=await Vt(d,t);if(!c.ok){if(c.choice==="profile"&&c.choices){r({type:"profile-select",options:d,choices:c.choices});return}n(c);return}let{credentials:y}=c.value;o(v=>({...v,credentials:y})),await h(d,y,e)},x=async({options:d,context:c},y,v,P)=>{let C=Bc[y];r({type:"loading",label:`Loading ${C}s\u2026`});let S=(await c.client.listDashboards(Do(y),v?.id)).map(u=>pt(u,y)),{environment:g,businessId:f}=c.credentials;if(S.length===0){n({message:`No ${C}s found${v?` on ${v.objectName}`:""} for business ${f} (${g}).`});return}r({type:"dashboard-select",surface:y,objectId:v?.id??null,title:v?`Choose a chart group on ${v.objectName}`:`Choose a ${C}`,rows:S,back:P,options:d,context:c})};return{start:d=>{i(()=>w(d))},pickProfile:(d,c)=>{i(()=>w({...d.options,profile:c.value}))},pickBlock:(d,c)=>{i(()=>h(d.options,d.credentials,c.value))},pickSurface:(d,c)=>{i(async()=>{if(c!=="chart_group"){await x(d,c,null,d);return}r({type:"loading",label:"Loading custom objects\u2026"});let y=await Zr(d.context.client);if(y.length===0){n({message:"No custom objects found, so there are no chart groups to push to."});return}r({type:"object-select",objects:y,back:d,options:d.options,context:d.context})})},pickObject:(d,c)=>{i(()=>x(d,"chart_group",c,d))},pickDashboard:(d,c)=>{i(async()=>{let{context:y}=d,v={...d.options,dashboard:c.id};if(v.dashlet!==void 0||v.create){await a(y,v,d);return}r({type:"loading",label:`Loading "${c.name}"\u2026`});let P=await y.client.getDashboard(c.id),C=Lt({dashboardId:P.id,dashlets:P.dashlets,pluginApiName:y.block.pluginApiName,blockApiName:y.block.block.api_name,entry:y.entry}),_=C.kind==="map"||C.kind==="name"?C.dashlet.id:null;r({type:"target-select",dashboardId:P.id,title:`Choose what to push to on "${P.name}"`,message:null,choices:ai(Wt(P,y.block),_),matchedId:_,back:d,options:v,context:y})})},pickTarget:(d,c)=>{i(()=>a(d.context,Oc(d.options,d.dashboardId,c),d))},acceptDrift:d=>{i(()=>l(d.context,d.target,{...d.options,force:!0}))},acceptPush:d=>{i(()=>s(d.context,d.plan))},cancel:()=>{n({message:"Cancelled."})}}}var li=({prompt:e,strict:t,onAccept:r,onCancel:o})=>(ci((n,i)=>{if(i.ctrl)return;let s=n.toLowerCase();s==="y"||!t&&i.return?r():(t||s==="n"||i.escape)&&o()}),B(q,{bold:!0,children:e})),tt=({lines:e,color:t})=>B(oe,{flexDirection:"column",children:e.map((r,o)=>B(q,{...t&&{color:t},children:r},`${String(o)}-${r}`))}),di=({plan:e})=>J(oe,{flexDirection:"column",children:[B(tt,{lines:ft(e.summary)}),J(q,{children:["URL: ",e.url]})]}),dn=({label:e})=>J(oe,{gap:1,children:[B(ye,{}),B(q,{dimColor:!0,children:e})]}),Mc=({view:e})=>J(oe,{flexDirection:"column",gap:1,children:[J(oe,{gap:1,children:[B(q,{color:"red",children:"\u2717"}),B(q,{color:"red",children:e.issues&&e.issues.length>0?me(e.issues):e.message})]}),e.hint!==void 0&&J(q,{dimColor:!0,children:["Hint: ",e.hint]}),e.choices&&e.choices.length>0&&J(oe,{flexDirection:"column",children:[B(q,{children:"Choices:"}),B(tt,{lines:e.choices.map(t=>` ${t.label}`)})]})]}),Lc=({plan:e,applied:t})=>{let r=nn(e.dashboard.id);return t===null?J(oe,{flexDirection:"column",gap:1,children:[B(q,{color:"yellow",children:"Dry run: nothing was written."}),B(di,{plan:e}),e.warnings.length>0&&B(tt,{lines:e.warnings.map(o=>`Warning: ${o}`),color:"yellow"}),J(oe,{flexDirection:"column",children:[J(q,{bold:!0,children:[e.method," ",e.path]}),B(q,{children:JSON.stringify(e.body,null,2)})]})]}):J(oe,{flexDirection:"column",gap:1,children:[J(oe,{gap:1,children:[B(q,{color:"green",children:"\u2713"}),J(q,{bold:!0,children:[e.action==="create"?"Created":"Updated"," ",e.summary.blockName,' on "',e.dashboard.name,'"']})]}),J(oe,{flexDirection:"column",children:[J(q,{children:["URL: ",e.url]}),r&&J(q,{dimColor:!0,children:["Refresh an open tab: ",r.script]})]}),t.warnings.length>0&&B(tt,{lines:t.warnings.map(o=>`Warning: ${o}`),color:"yellow"})]})},ui=({apiName:e,options:t,deps:r})=>{let o=Rc(),[n,i]=qt({type:"loading",label:"Loading credentials\u2026"}),[s,l]=qt({credentials:null,warnings:[],notices:[]}),[a]=qt(()=>zc(e,r,i,l)),[p]=qt(t);ii(()=>{a.start(p)},[a,p]),ii(()=>{(n.type==="done"||n.type==="error")&&o.exit()},[n,o]),ci((w,x)=>{x.ctrl&&w==="c"&&n.type!=="done"&&n.type!=="error"&&(r.setExitCode(130),o.exit())});let h=w=>J(oe,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[B(K,{}),s.credentials&&J(q,{dimColor:!0,children:["Environment: ",s.credentials.environment," \xB7 Business:"," ",s.credentials.businessId]}),s.warnings.length>0&&B(q,{dimColor:!0,children:me(s.warnings)}),s.notices.length>0&&B(tt,{lines:s.notices}),w]});switch(n.type){case"loading":return h(B(dn,{label:n.label}));case"packaging":return h(B(dn,{label:"Packaging blocks\u2026"}));case"applying":return h(B(dn,{label:`Pushing ${n.plan.summary.blockName} to "${n.plan.dashboard.name}"\u2026`}));case"profile-select":return h(B(We,{title:"Credential profile",items:n.choices.map(w=>({key:w.value,label:w.label})),onSelect:(w,x)=>{let d=n.choices[x];d&&a.pickProfile(n,d)},onCancel:a.cancel},"profile"));case"block-select":return h(B(We,{title:"Block to push",items:n.choices.map(w=>({key:w.value,label:w.label})),onSelect:(w,x)=>{let d=n.choices[x];d&&a.pickBlock(n,d)},onCancel:a.cancel},"block"));case"surface-select":return h(B(We,{title:"Where should the block go?",items:si,onSelect:(w,x)=>{let d=si[x];d&&a.pickSurface(n,d.key)},onCancel:a.cancel},"surface"));case"object-select":return h(B(We,{title:"Custom object",items:n.objects.map(w=>({key:w.id,label:w.objectName})),onSelect:(w,x)=>{let d=n.objects[x];d&&a.pickObject(n,d)},onCancel:()=>{i(n.back)},hint:"\u2191/\u2193 to move, Enter to select, Esc to go back"},"object"));case"dashboard-select":return h(B(We,{title:n.title,items:n.rows.map(Fc),onSelect:(w,x)=>{let d=n.rows[x];d&&a.pickDashboard(n,d)},onCancel:()=>{i(n.back)},hint:"\u2191/\u2193 to move, Enter to select, Esc to go back"},`dashboards-${n.surface}-${n.objectId??""}`));case"target-select":{let{back:w}=n;return h(J(Hc,{children:[n.message!==null&&B(q,{color:"yellow",children:n.message}),B(We,{title:n.title,items:n.choices.map(x=>jc(x,n.matchedId)),onSelect:(x,d)=>{let c=n.choices[d];c&&a.pickTarget(n,c)},onCancel:w?()=>{i(w)}:a.cancel,...w&&{hint:"\u2191/\u2193 to move, Enter to select, Esc to go back"}},`targets-${n.dashboardId}`)]}))}case"confirm":return n.kind==="drift"?h(J(oe,{flexDirection:"column",gap:1,children:[B(q,{color:"yellow",children:$c}),B(q,{dimColor:!0,children:n.message}),B(li,{prompt:"Type y to overwrite (y/N)",strict:!0,onAccept:()=>{a.acceptDrift(n)},onCancel:a.cancel},"drift")]})):h(J(oe,{flexDirection:"column",gap:1,children:[B(di,{plan:n.plan}),n.plan.warnings.length>0&&B(tt,{lines:n.plan.warnings.map(w=>`Warning: ${w}`),color:"yellow"}),n.plan.summary.isProduction&&B(q,{color:"red",bold:!0,children:"PRODUCTION"}),B(li,{prompt:n.plan.summary.isProduction?"Type y to push to production (y/N)":"Push? (Y/n)",strict:n.plan.summary.isProduction,onAccept:()=>{a.acceptPush(n)},onCancel:a.cancel},"push")]}));case"done":return h(B(Lc,{plan:n.plan,applied:n.applied}));case"error":return h(B(Mc,{view:n.view}))}};var Kc=e=>e instanceof Wc?me(e.issues):`Error: ${e instanceof Error?e.message:String(e)}`;function pi(e){let t=e.command("block").description("Work with plugin content blocks");t.command("export").description("Print one packaged block as JSON for the Custom Block (AI Coded) paste editor").argument("[api_name]","api_name of the block to export (optional when there is only one)").option("--copy","also copy the JSON to the clipboard").action(async(r,o)=>{try{let{block:n,warnings:i}=await Xn(process.cwd(),r);i.length>0&&console.error(me(i));let s=JSON.stringify(n,null,2);console.log(s),o.copy&&console.error(await Rt(s)?`Copied block "${n.api_name}" to the clipboard.`:"Couldn't copy to the clipboard; use the JSON printed to stdout instead.")}catch(n){console.error(Kc(n)),process.exitCode=1}}),t.command("push").description("Push a block to a Kizen dashboard, homepage or chart group dashlet").argument("[api_name]","api_name of the block to push (optional when there is only one)").option("-c, --credentials <path>","path to a credentials JSON file").option("--profile <name>","stored credential profile to use").option("--dashboard <id>","dashboard, homepage or chart group to push to").option("--dashlet <id>","existing custom code dashlet to update (needs --dashboard)").option("--create","create a new dashlet instead of updating one").option("--dry-run","show the request without writing anything").option("--yes","write without asking (headless runs are dry runs without it)").option("--allow-production","allow headless writes to go/fmo").option("--force","overwrite even if the block was edited in Kizen since the last push").option("--forget","clear the remembered target for this block first").option("--json","print a JSON result to stdout (always headless)").action(async(r,o)=>{try{if(!process.stdin.isTTY||!process.stdout.isTTY||o.json===!0){await Qo(r,o,Ut(process.cwd()));return}let{waitUntilExit:n}=Vc(Uc(ui,{...r!==void 0&&{apiName:r},options:o,deps:Ut(process.cwd())}),{exitOnCtrlC:!1});await n()}catch(n){console.error(`Error: ${n instanceof Error?n.message:String(n)}`),process.exitCode=1}}),t.command("targets").description("List the dashboards, homepages and chart groups a block can be pushed to").option("-c, --credentials <path>","path to a credentials JSON file").option("--profile <name>","stored credential profile to use").option("--object <id>","list the chart groups of this custom object").option("--json","print a JSON result to stdout").action(async r=>{try{await ei(r,Ut(process.cwd()))}catch(o){console.error(`Error: ${o instanceof Error?o.message:String(o)}`),process.exitCode=1}})}import{createElement as Xc}from"react";import{render as Zc}from"ink";import{useEffect as mi,useState as gt}from"react";import{Box as yt,Text as rt,useApp as qc}from"ink";import{PluginValidationError as Gc}from"@kizenapps/packager";function mt(e){return e<1024?`${String(e)} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(2)} MB`}import{Box as un,Text as Yt}from"ink";import{jsx as fi,jsxs as ht}from"react/jsx-runtime";var Jc=e=>{let t=new Map;for(let r of e){let o=r.path??r.pluginApiName??"general",n=t.get(o)??[];n.push(r),t.set(o,n)}return t},Xt=({issues:e})=>{let t=e.filter(r=>r.severity==="error").length;return ht(un,{flexDirection:"column",marginTop:1,children:[ht(Yt,{color:"red",children:["\u2717 ",t," validation ",t===1?"error":"errors"]}),[...Jc(e)].map(([r,o])=>ht(un,{flexDirection:"column",marginTop:1,children:[fi(Yt,{bold:!0,children:r}),o.map((n,i)=>fi(un,{paddingLeft:2,children:ht(Yt,{color:n.severity==="error"?"red":"yellow",children:[n.severity==="error"?"\u2717":"\u26A0"," ",n.message," ",ht(Yt,{dimColor:!0,children:["(",n.rule,")"]})]})},`${n.rule}-${String(i)}`))]},r))]})};import{Fragment as Yc,jsx as De,jsxs as Ke}from"react/jsx-runtime";var hi=["creating-dir","reading-files","validating","minifying","packaging","writing-bundle"],Zt={"creating-dir":"Creating .kizenapp directory","reading-files":"Reading plugin files",validating:"Validating plugin app",minifying:"Minifying scripts",packaging:"Packaging plugin","writing-bundle":"Writing bundle.json"},gi=({outputDir:e,pluginDir:t})=>{let r=qc(),[o,n]=gt("creating-dir"),[i,s]=gt(!1),[l,a]=gt(null),[p,h]=gt(null),[w,x]=gt(null);mi(()=>{jt(t,e,n).then(({bundleSize:y})=>{x(y),n("done")}).catch(y=>{y instanceof Gc?a(y.issues):h(y instanceof Error?y.message:String(y)),s(!0),process.exitCode=1})},[e,t]),mi(()=>{(o==="done"||i)&&r.exit()},[o,i,r]);let d=hi.indexOf(o),c=o==="done";return Ke(yt,{flexDirection:"column",paddingY:1,paddingX:2,children:[De(K,{marginBottom:1}),Ke(yt,{flexDirection:"column",gap:0,children:[hi.map((y,v)=>{let P=o===y,C=c||d>v;return De(yt,{gap:1,children:i&&v===d?Ke(rt,{color:"red",children:["\u2717 ",Zt[y]]}):C?Ke(rt,{color:"green",children:["\u2713 ",Zt[y]]}):P?Ke(Yc,{children:[De(ye,{}),De(rt,{children:Zt[y]})]}):Ke(rt,{dimColor:!0,children:[" ",Zt[y]]})},y)}),c&&w!==null&&De(yt,{marginTop:1,gap:1,children:Ke(rt,{color:"green",children:["\u2713 bundle \u2014 ",mt(w)]})}),l!==null&&De(Xt,{issues:l}),p!==null&&De(yt,{marginTop:1,borderStyle:"single",borderColor:"red",paddingX:1,children:De(rt,{color:"red",children:p})})]})]})};function yi(e){e.command("build").description("Bundle the plugin app into .kizenapp directory").action(async()=>{let t=process.cwd(),r=`${t}/.kizenapp`;Ne(t);let{waitUntilExit:o}=Zc(Xc(gi,{outputDir:r,pluginDir:t}));await o()})}import{createElement as Wd}from"react";import{render as Kd}from"ink";import{useCallback as sr,useState as nt}from"react";import{Box as V,Text as N,useApp as Od,useInput as zd}from"ink";import{join as ar}from"path";import{access as Bd,mkdir as er,writeFile as yn}from"fs/promises";import{dirname as $d,join as ke}from"path";var bi=`{
2
+ import{readFileSync as ul}from"fs";import{dirname as fl,join as ml}from"path";import{fileURLToPath as gl}from"url";import{Command as hl}from"commander";import{createElement as hi}from"react";import{render as yi}from"ink";import{useEffect as wr,useState as Me}from"react";import{Box as He,Text as Be,useApp as ui}from"ink";import{PluginValidationError as fi}from"@kizenapps/packager";import{mkdir as Vo,writeFile as qo}from"fs/promises";import{join as Jo}from"path";import{PluginValidationError as Wo,packagePlugin as Go,parseManifestFromFiles as Ko,transformDeployablePlugin as Yo,transformFiles as Xo,validatePluginApp as Zo}from"@kizenapps/packager";import{readdir as jo,readFile as mr}from"fs/promises";import{join as gr,relative as Lo}from"path";var Oo=new Set([".png",".svg"]),zo=new Set([".kzn"]),Mo=new Set([".DS_Store"]),rt=new Set(["node_modules",".git",".kizenapp",".copilot-docs",".github",".claude","__pycache__"]),Ho=8e3;function Uo(e){return e.subarray(0,Ho).includes(0)}async function hr(e,t){let n=await jo(e,{withFileTypes:!0}),o=[];for(let r of n)if(r.isDirectory()){if(rt.has(r.name))continue;o.push(...await hr(gr(e,r.name),t))}else r.isFile()&&(Mo.has(r.name)||o.push(gr(e,r.name)));return o}async function nt(e,{detectBinaryByContent:t=!1}={}){let n=await hr(e,e);return Promise.all(n.map(async o=>{let r=Lo(e,o).split("\\").join("/"),i=r.lastIndexOf("."),p=i>=0?r.slice(i).toLowerCase():"";if(Oo.has(p)){let g=await mr(o);return{path:r,content:"",base64Image:g.toString("base64")}}let f=await mr(o);return zo.has(p)||t&&Uo(f)?{path:r,content:"",binaryData:f}:{path:r,content:f.toString("utf-8")}}))}var Qo=(e,t)=>({...e,thumbnail:e.thumbnail?Buffer.from(e.thumbnail).toString("base64"):null,kznFile:e.kznFile?Buffer.from(e.kznFile).toString("base64"):null,allReleaseNotes:t});async function ot(e,t,n){await Vo(t,{recursive:!0}),n?.("reading-files");let o=await nt(e);n?.("validating");let r=Zo(o);if(r.some(x=>x.severity==="error"))throw new Wo(r);n?.("minifying");let i=await Xo(o);n?.("packaging");let p=Ko(i),f=Go(i,p),g=Object.values(f).map(Yo);n?.("writing-bundle");let m=g.map(x=>{let c=x.release_notes_directory?.replace(/\/$/,""),b=c?o.filter(w=>w.path.startsWith(c+"/")&&w.path.endsWith(".md")&&w.content).map(w=>({version:w.path.slice(w.path.lastIndexOf("/")+1,-3),notes:w.content})).sort((w,H)=>H.version.localeCompare(w.version,void 0,{numeric:!0})):[];return Qo(x,b)}),y=JSON.stringify(m,null,2),T=(x,c)=>x==="allReleaseNotes"?void 0:c,P=Buffer.byteLength(JSON.stringify(m,T,2),"utf-8");return await qo(Jo(t,"bundle.json"),y,"utf-8"),{bundleSize:P}}function Oe(e){return e<1024?`${String(e)} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(2)} MB`}import{Box as ni,Text as oi}from"ink";import{Box as ei,Text as ti}from"ink";import{jsx as yr}from"react/jsx-runtime";var ri=[" :::::::: "," :::::: ::::::::::: "," :::::: :::::::::: "," :::::: :::::: ::::::: "," ::::: :::::: ::::::::::::::::: "," ::::: ::::: ::::::::::::::::::: "," ::::: ::::: :::::: ::::: ",":::: ::::: ::: ::::: :: ","::::: :::::: :::::::::: ","::::: :::::: ::::::::::: "," ::::: ::: ::::::: "," ::::::: ::: ::::: "," ::::::::::: :::::: :::::"," :::::::::: :::::: :::::"," :: ::::: ::: ::::: ::::"," ::::: :::::: ::::: ::::: ::: "," ::::::::::::::::::: ::::: ::::: "," ::::::::::::::::: ::::: ::::: "," ::::::::: :::::: :::::: "," :::::::::: :::::: "," ::::::::::: :::::: "," :::::::: "],br=()=>yr(ei,{flexDirection:"column",children:ri.map((e,t)=>yr(ti,{color:"cyan",children:e},t))});import{Fragment as ii,jsx as Lt,jsxs as si}from"react/jsx-runtime";var q=({marginBottom:e=0})=>si(ii,{children:[Lt(br,{}),Lt(ni,{flexDirection:"column",marginTop:1,marginBottom:e,children:Lt(oi,{bold:!0,color:"cyan",children:"Kizen App Development Toolkit"})})]});import{useEffect as ai,useState as li}from"react";import{Text as ci}from"ink";import{jsx as di}from"react/jsx-runtime";var xr=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],me=()=>{let[e,t]=li(0);return ai(()=>{let n=setInterval(()=>{t(o=>(o+1)%xr.length)},80);return()=>{clearInterval(n)}},[]),di(ci,{color:"cyan",children:xr[e]??"\u280B"})};import{Box as Ot,Text as it}from"ink";import{jsx as vr,jsxs as ze}from"react/jsx-runtime";var pi=e=>{let t=new Map;for(let n of e){let o=n.path??n.pluginApiName??"general",r=t.get(o)??[];r.push(n),t.set(o,r)}return t},st=({issues:e})=>{let t=e.filter(n=>n.severity==="error").length;return ze(Ot,{flexDirection:"column",marginTop:1,children:[ze(it,{color:"red",children:["\u2717 ",t," validation ",t===1?"error":"errors"]}),[...pi(e)].map(([n,o])=>ze(Ot,{flexDirection:"column",marginTop:1,children:[vr(it,{bold:!0,children:n}),o.map((r,i)=>vr(Ot,{paddingLeft:2,children:ze(it,{color:r.severity==="error"?"red":"yellow",children:[r.severity==="error"?"\u2717":"\u26A0"," ",r.message," ",ze(it,{dimColor:!0,children:["(",r.rule,")"]})]})},`${r.rule}-${String(i)}`))]},n))]})};import{Fragment as mi,jsx as ge,jsxs as Pe}from"react/jsx-runtime";var Cr=["creating-dir","reading-files","validating","minifying","packaging","writing-bundle"],at={"creating-dir":"Creating .kizenapp directory","reading-files":"Reading plugin files",validating:"Validating plugin app",minifying:"Minifying scripts",packaging:"Packaging plugin","writing-bundle":"Writing bundle.json"},Sr=({outputDir:e,pluginDir:t})=>{let n=ui(),[o,r]=Me("creating-dir"),[i,p]=Me(!1),[f,g]=Me(null),[m,y]=Me(null),[T,P]=Me(null);wr(()=>{ot(t,e,r).then(({bundleSize:b})=>{P(b),r("done")}).catch(b=>{b instanceof fi?g(b.issues):y(b instanceof Error?b.message:String(b)),p(!0),process.exitCode=1})},[e,t]),wr(()=>{(o==="done"||i)&&n.exit()},[o,i,n]);let x=Cr.indexOf(o),c=o==="done";return Pe(He,{flexDirection:"column",paddingY:1,paddingX:2,children:[ge(q,{marginBottom:1}),Pe(He,{flexDirection:"column",gap:0,children:[Cr.map((b,w)=>{let H=o===b,j=c||x>w;return ge(He,{gap:1,children:i&&w===x?Pe(Be,{color:"red",children:["\u2717 ",at[b]]}):j?Pe(Be,{color:"green",children:["\u2713 ",at[b]]}):H?Pe(mi,{children:[ge(me,{}),ge(Be,{children:at[b]})]}):Pe(Be,{dimColor:!0,children:[" ",at[b]]})},b)}),c&&T!==null&&ge(He,{marginTop:1,gap:1,children:Pe(Be,{color:"green",children:["\u2713 bundle \u2014 ",Oe(T)]})}),f!==null&&ge(st,{issues:f}),m!==null&&ge(He,{marginTop:1,borderStyle:"single",borderColor:"red",paddingX:1,children:ge(Be,{color:"red",children:m})})]})]})};import*as Ae from"fs";import*as Tr from"path";var gi=[".kizenapp/",".copilot-docs/"];function he(e){let t=Tr.join(e,".gitignore"),n="";Ae.existsSync(t)&&(n=Ae.readFileSync(t,"utf8"));let o=new Set(n.split(`
3
+ `).map(f=>f.trim())),r=gi.filter(f=>!o.has(f)&&!o.has(f.replace(/\/$/,"")));if(r.length===0)return;let i=r.join(`
4
+ `)+`
5
+ `,p=n===""||n.endsWith(`
6
+ `)?i:`
7
+ `+i;Ae.writeFileSync(t,n+p)}function Er(e){e.command("build").description("Bundle the plugin app into .kizenapp directory").action(async()=>{let t=process.cwd(),n=`${t}/.kizenapp`;he(t);let{waitUntilExit:o}=yi(hi(Sr,{outputDir:n,pluginDir:t}));await o()})}import{createElement as ws}from"react";import{render as Cs}from"ink";import{useCallback as gt,useState as De}from"react";import{Box as M,Text as I,useApp as as,useInput as ls}from"ink";import{join as ht}from"path";import{access as ns,mkdir as dt,writeFile as Vt}from"fs/promises";import{dirname as os,join as ce}from"path";var _r=`{
757
8
  "name": "Hello Frame",
758
9
  "api_name": "hello_frame",
759
10
  "title": "Hello Frame",
@@ -762,13 +13,13 @@ export const sumBy = (records, name, valueOf) =>
762
13
  "height": 320,
763
14
  "width": 280
764
15
  }
765
- `;var wi=`this.outputUI(\`
16
+ `;var kr=`this.outputUI(\`
766
17
  <div class="hello-frame">
767
18
  <h2 class="hello-frame__title">Hello from a floating frame</h2>
768
19
  <p class="hello-frame__body">Edit src/floatingFrames/helloFrame/script.js to change this.</p>
769
20
  </div>
770
21
  \`);
771
- `;var xi=`.hello-frame {
22
+ `;var Pr=`.hello-frame {
772
23
  padding: 16px;
773
24
  font-family: system-ui, sans-serif;
774
25
  }
@@ -782,18 +33,18 @@ export const sumBy = (records, name, valueOf) =>
782
33
  margin: 0;
783
34
  color: #555;
784
35
  }
785
- `;var vi=`{
36
+ `;var Ir=`{
786
37
  "name": "Hello Block",
787
38
  "api_name": "hello_block",
788
39
  "types": ["dashboards"]
789
40
  }
790
- `;var ki=`this.outputUI(\`
41
+ `;var Nr=`this.outputUI(\`
791
42
  <div class="hello-block">
792
43
  <h2 class="hello-block__title">Hello from a block</h2>
793
44
  <p class="hello-block__body">Edit src/blocks/helloBlock/script.js to change this.</p>
794
45
  </div>
795
46
  \`);
796
- `;var Ci=`.hello-block {
47
+ `;var Rr=`.hello-block {
797
48
  padding: 16px;
798
49
  font-family: system-ui, sans-serif;
799
50
  background: #f8fafc;
@@ -809,25 +60,25 @@ export const sumBy = (records, name, valueOf) =>
809
60
  margin: 0;
810
61
  color: #555;
811
62
  }
812
- `;var Si=`{
63
+ `;var Br=`{
813
64
  "icon": "phone",
814
65
  "color": "#0ea5e9",
815
66
  "tooltip": "Hello adornment",
816
67
  "field_type": "phonenumber"
817
68
  }
818
- `;var _i=`this.showToast('Hello from a data adornment.', { variant: 'success' });
819
- `;var Pi=`{
69
+ `;var Ar=`this.showToast('Hello from a data adornment.', { variant: 'success' });
70
+ `;var Dr=`{
820
71
  "name": "Hello Page",
821
72
  "api_name": "hello_page"
822
73
  }
823
- `;var Ei=`this.outputUI(\`
74
+ `;var Fr=`this.outputUI(\`
824
75
  <div class="hello-page">
825
76
  <p class="hello-page__loading">Loading\u2026</p>
826
77
  </div>
827
78
  \`);
828
79
 
829
80
  this.runEventScript('greet');
830
- `;var Ti=`.hello-page {
81
+ `;var $r=`.hello-page {
831
82
  padding: 24px;
832
83
  font-family: system-ui, sans-serif;
833
84
  }
@@ -865,7 +116,7 @@ this.runEventScript('greet');
865
116
  .hello-page__button:hover {
866
117
  background: #cbd5e1;
867
118
  }
868
- `;var Ii=`const tone = this.args?.formData?.tone?.[0] ?? 'plain';
119
+ `;var jr=`const tone = this.args?.formData?.tone?.[0] ?? 'plain';
869
120
 
870
121
  const greeting = tone === 'cheer' ? 'Hello, and good to see you!' : 'Hello from a routable page.';
871
122
 
@@ -888,33 +139,138 @@ this.outputUI(\`
888
139
  </div>
889
140
  </div>
890
141
  \`);
891
- `;var Ai=`{
142
+ `;var Lr=`{
892
143
  "api_name": "hello_toolbar_item",
893
144
  "label": "Hello Toolbar Item",
894
145
  "icon": "star",
895
146
  "color": "#0ea5e9"
896
147
  }
897
- `;var Ni=`this.showToast('Hello from a toolbar item.', { variant: 'success' });
898
- `;var Di=`{
148
+ `;var Or=`this.showToast('Hello from a toolbar item.', { variant: 'success' });
149
+ `;var zr=`{
899
150
  "api_name": "hello_object_settings_item",
900
151
  "label": "Hello Object Settings"
901
152
  }
902
- `;var Ri=`this.showToast('Hello from an object settings item.', { variant: 'success' });
903
- `;var Bi=`{
153
+ `;var Mr=`this.showToast('Hello from an object settings item.', { variant: 'success' });
154
+ `;var Hr=`{
904
155
  "name": "Hello Action",
905
156
  "api_name": "hello_action",
906
157
  "hint_object_name": "client_client"
907
158
  }
908
- `;var $i=`this.showToast('Hello from a JS action.', { variant: 'success' });
909
- `;var ne=["floatingFrame","block","dataAdornment","page","toolbarItem","objectSettingsItem","jsAction"],pn={floatingFrame:"Floating frame",block:"Block",dataAdornment:"Data adornment",page:"Routable page",toolbarItem:"Toolbar item",objectSettingsItem:"Object settings item",jsAction:"JS action"},yd={floatingFrame:{directory:"floatingFrames",component:"helloFrame",files:{"config.json":bi,"script.js":wi,"styles.css":xi}},block:{directory:"blocks",component:"helloBlock",files:{"config.json":vi,"script.js":ki,"styles.css":Ci}},dataAdornment:{directory:"dataAdornments",component:"helloAdornment",files:{"config.json":Si,"script.js":_i}},page:{directory:"pages",component:"helloPage",files:{"config.json":Pi,"script.js":Ei,"styles.css":Ti,"eventScripts/greet.js":Ii}},toolbarItem:{directory:"toolbarItems",component:"helloToolbarItem",files:{"config.json":Ai,"script.js":Ni}},objectSettingsItem:{directory:"objectSettingsItems",component:"helloObjectSettingsItem",files:{"config.json":Di,"script.js":Ri}},jsAction:{directory:"actions",component:"helloAction",files:{"config.json":Bi,"script.js":$i}}};function bd(e,t="src"){let{directory:r,component:o,files:n}=yd[e];return Object.entries(n).map(([i,s])=>({path:`${t}/${r}/${o}/${i}`,content:s}))}function ji(e,t="src"){return[...new Set(e)].flatMap(r=>bd(r,t))}import{deflateSync as wd}from"zlib";var mn=512,xd=150,vd=26,hn=46,Fi=.6,kd=hn*.55,Cd=.55,Sd=.47;function _d(e){let t=2166136261;for(let r=0;r<e.length;r+=1)t^=e.charCodeAt(r),t=t+((t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24))>>>0;return t}function Pd(e){let t=_d(e);return{hue:t%360,quarterTurns:(t>>>16)%4}}function Ed(e,t,r){let o=(1-Math.abs(2*r-1))*t,n=e/60,i=o*(1-Math.abs(n%2-1)),s=r-o/2,[l,a,p]=n<1?[o,i,0]:n<2?[i,o,0]:n<3?[0,o,i]:n<4?[0,i,o]:n<5?[i,0,o]:[o,0,i];return[Math.round((l+s)*255),Math.round((a+s)*255),Math.round((p+s)*255)]}function bt(e,t,r,o,n){let i=Math.abs(e)-r+n,s=Math.abs(t)-o+n;return Math.hypot(Math.max(i,0),Math.max(s,0))+Math.min(Math.max(i,s),0)-n}function Qt(e,t,r,o,n){return Math.hypot(e-r,t-o)-n}function Td(e,t){let r=xd,o=hn,n=o*Fi,i=kd,s=bt(e,t,r,r,vd),l=Math.min(Qt(e,t,0,-r-n,o),bt(e,t+r+n/2,i,n/2+1,0)),a=Math.min(Qt(e,t,r+n,0,o),bt(e-r-n/2,t,n/2+1,i,0));s=Math.min(s,l,a);let p=Math.min(Qt(e,t,0,r-n,o),bt(e,t-r+n/2,i,n/2+1,0)),h=Math.min(Qt(e,t,-r+n,0,o),bt(e+r-n/2,t,n/2+1,i,0));return Math.max(s,-p,-h)}function Id(e){let t=mn,r=new Uint8Array(t*t*4),[o,n,i]=Ed(e.hue,Cd,Sd),s=hn*(1+Fi),l=(t-1)/2,a=s/2;for(let p=0;p<t;p+=1)for(let h=0;h<t;h+=1){let w=h-l,x=p-l;for(let v=0;v<e.quarterTurns;v+=1)[w,x]=[-x,w];let d=Td(w+a,x-a),c=Math.min(1,Math.max(0,.5-d));if(c<=0)continue;let y=(p*t+h)*4;r[y]=o,r[y+1]=n,r[y+2]=i,r[y+3]=Math.round(c*255)}return r}var Ad=new Uint32Array(256).map((e,t)=>{let r=t;for(let o=0;o<8;o+=1)r=r&1?3988292384^r>>>1:r>>>1;return r>>>0});function Nd(e){let t=4294967295;for(let r of e)t=(Ad[(t^r)&255]??0)^t>>>8;return(t^4294967295)>>>0}function fn(e,t){let r=Buffer.from(e,"latin1"),o=Buffer.alloc(4);o.writeUInt32BE(t.length);let n=Buffer.alloc(4);return n.writeUInt32BE(Nd(Buffer.concat([r,t]))),Buffer.concat([o,r,t,n])}var Dd=Buffer.from([137,80,78,71,13,10,26,10]);function Rd(e,t,r){if(r.length!==e*t*4)throw new Error(`encodePng: expected ${String(e*t*4)} bytes of RGBA for ${String(e)}x${String(t)}, got ${String(r.length)}`);let o=Buffer.alloc(13);o.writeUInt32BE(e,0),o.writeUInt32BE(t,4),o[8]=8,o[9]=6;let n=e*4,i=Buffer.alloc((n+1)*t);for(let s=0;s<t;s+=1)i.set(r.subarray(s*n,(s+1)*n),s*(n+1)+1);return Buffer.concat([Dd,fn("IHDR",o),fn("IDAT",wd(i,{level:9})),fn("IEND",new Uint8Array(0))])}function Oi(e){return Rd(mn,mn,Id(Pd(e)))}var jd="src/thumbnail.png",Fd=["go","fmo"];async function tr(e){try{return await Bd(e),!0}catch{return!1}}async function rr(e){return await tr(ke(e,"kizen.json"))?"has-manifest":await tr(ke(e,".kizenapp"))?"has-kizenapp":"ok"}async function zi(e,t){let r=ke(e,t);if(!await tr(r))return r;for(let o=1;o<1e3;o++){let n=ke(e,`${t}-${String(o)}`);if(!await tr(n))return n}throw new Error(`Could not find an available directory name for "${t}"`)}function bn(e){let t=e.developerBusinessId.trim(),r=t!==""&&!Fd.includes(e.developerEnvironment)?[e.developerEnvironment]:["prod"];return{name:e.name,version:"1.0.0",published:!0,api_name:e.apiName,external_link:e.externalLink,description:e.description,entry:"src/",engine:"1.0.0",release_notes_directory:"releaseNotes/",release_environments:r,config_template:{},base_config:{},...t?{developer_business_id:{[e.developerEnvironment]:t}}:{}}}async function gn(e,t){let r=t.map(n=>({fullPath:ke(e,...n.path.split("/")),content:n.content})),o=new Set(r.map(n=>$d(n.fullPath)));await Promise.all([...o].map(n=>er(n,{recursive:!0}))),await Promise.all(r.map(n=>yn(n.fullPath,n.content,"utf-8")))}async function nr(e){await er(e.targetDir,{recursive:!0});let t=bn(e);await yn(ke(e.targetDir,"kizen.json"),JSON.stringify(t,null,2)+`
910
- `,"utf-8"),await er(ke(e.targetDir,"src"),{recursive:!0}),await er(ke(e.targetDir,"releaseNotes"),{recursive:!0}),await Promise.all([gn(e.targetDir,ji(e.artifacts??[])),gn(e.targetDir,Lo()),gn(e.targetDir,Le()),yn(ke(e.targetDir,...jd.split("/")),Oi(e.apiName))]),Ne(e.targetDir)}var Re=["name","apiName","externalLink","description","developerBusinessId"],or={name:"Name",apiName:"API name",externalLink:"External link",description:"Description",developerBusinessId:"Business ID"},wn=["name","apiName","description"],xn=/^[a-z_][a-z0-9_]+$/,vn="must start with a letter or underscore and contain only lowercase letters, numbers, or underscores (minimum 2 characters)";function ir(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}function Mi(e){return{name:"",apiName:"",externalLink:"",description:"",developerBusinessId:e}}function Li(e,t){let r=t.trim();if(wn.includes(e)&&!r)return`${or[e]} is required`;if(e==="apiName"&&!xn.test(r))return`${or[e]} ${vn}`}function Hi(e,t){return e==="description"?t:t.trim()}function Ui(e,t){let r=new Set(e);return r.has(t)?r.delete(t):r.add(t),ne.filter(o=>r.has(o))}import{jsx as A,jsxs as $}from"react/jsx-runtime";var Je=({text:e})=>A(N,{dimColor:!0,children:e}),Vi=({parentDir:e,defaultBusinessId:t,defaultEnvironment:r})=>{let o=Od(),[n,i]=nt({type:"target-select",cursor:0}),[s,l]=nt("in-place"),[a,p]=nt(()=>Mi(t)),[h,w]=nt(()=>[...ne]),[x,d]=nt(""),[c,y]=nt(e),v=sr((S,g)=>{l(S),d(g.name),i({type:"field-entry",field:0,values:g})},[]),P=sr(async S=>{let g=await rr(S);if(g!=="ok"){i({type:"precheck-error",targetDir:S,reason:g});return}y(S),v("in-place",a)},[a,v]),C=sr(async(S,g)=>{try{let f=await zi(e,S.apiName);y(f),i({type:"confirm",values:S,artifacts:g})}catch(f){i({type:"error",message:f instanceof Error?f.message:String(f)})}},[e]),_=sr(async(S,g,f)=>{try{await nr({targetDir:g,name:S.name,apiName:S.apiName,externalLink:S.externalLink,description:S.description,developerBusinessId:S.developerBusinessId,developerEnvironment:r,artifacts:f}),i({type:"done",targetDir:g})}catch(u){i({type:"error",message:u instanceof Error?u.message:String(u)})}},[r]);if(zd((S,g)=>{if(g.ctrl&&S==="c"){o.exit();return}if(n.type==="target-select"){g.upArrow?i({type:"target-select",cursor:0}):g.downArrow?i({type:"target-select",cursor:1}):g.return?n.cursor===0?P(e):v("sub-dir",a):g.escape&&o.exit();return}if(n.type==="precheck-error"){(g.return||g.escape)&&o.exit();return}if(n.type==="field-entry"){let{field:f,values:u}=n,m=Re[f];if(!m)return;if(g.escape){o.exit();return}if(g.backspace||g.delete){d(b=>b.slice(0,-1));return}if(g.upArrow&&f>0){let b={...u,[m]:x},k=f-1,I=Re[k];i({type:"field-entry",field:k,values:b}),p(b),d(I?b[I]:"");return}if(g.return||g.tab||g.downArrow){let b=Li(m,x);if(b!==void 0){i({type:"field-entry",field:f,values:u,fieldError:b});return}let k=Hi(m,x),I={...u,[m]:k};p(I);let E=f+1;if(E>=Re.length)d(""),i({type:"artifact-select",values:I,cursor:0,selected:h});else{let R=Re[E],M=R?I[R]:"",W=R==="apiName"&&!M?ir(I.name):M;i({type:"field-entry",field:E,values:I}),d(W)}return}S&&!g.ctrl&&!g.meta&&d(b=>b+S);return}if(n.type==="artifact-select"){let{cursor:f,selected:u,values:m}=n;if(g.escape){let b=Re.length-1,k=Re[b];i({type:"field-entry",field:b,values:m}),d(k?m[k]:"");return}if(g.upArrow){i({...n,cursor:Math.max(0,f-1)});return}if(g.downArrow){i({...n,cursor:Math.min(ne.length-1,f+1)});return}if(S===" "){let b=ne[f];if(b){let k=Ui(u,b);w(k),i({...n,selected:k})}return}if(S==="a"){w([...ne]),i({...n,selected:[...ne]});return}if(S==="n"){w([]),i({...n,selected:[]});return}g.return&&(s==="sub-dir"?(i({type:"resolving-target"}),C(m,u)):i({type:"confirm",values:m,artifacts:u}));return}if(n.type==="confirm"){if(g.escape){i({type:"artifact-select",values:n.values,cursor:0,selected:n.artifacts});return}g.return&&(i({type:"writing"}),_(n.values,c,n.artifacts));return}(n.type==="done"||n.type==="error")&&(g.return||g.escape)&&o.exit()}),n.type==="target-select"){let S=[{label:"Use current directory",desc:e},{label:"Create new sub-directory",desc:`${e}/<inferred from API name>`}];return $(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[A(K,{}),$(V,{flexDirection:"column",gap:0,children:[A(N,{bold:!0,children:"Where should the new plugin live?"}),S.map((g,f)=>{let u=n.cursor===f;return $(V,{gap:2,children:[A(N,{...u&&{color:"cyan"},children:u?"\u276F":" "}),A(N,{bold:u,...u&&{color:"cyan"},children:g.label}),A(N,{dimColor:!0,children:g.desc})]},g.label)})]}),A(Je,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(n.type==="precheck-error"){let{targetDir:S,reason:g}=n;return $(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[A(K,{}),A(N,{color:"red",bold:!0,children:"Cannot create plugin"}),$(V,{flexDirection:"column",gap:0,children:[$(N,{children:[S," already contains ",A(N,{bold:!0,children:g==="has-manifest"?"kizen.json":".kizenapp/"}),"."]}),A(N,{dimColor:!0,children:"Remove the conflicting file/directory or choose a different target to avoid overwriting an existing plugin."})]}),A(Je,{text:"Enter or Esc to exit"})]})}if(n.type==="field-entry"){let{field:S,fieldError:g}=n,f=s==="sub-dir"?`Target: sub-directory of ${e} (inferred from API name)`:`Target: ${c}`;return $(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[A(K,{}),$(V,{flexDirection:"column",gap:0,children:[A(N,{bold:!0,children:"Plugin details"}),A(N,{dimColor:!0,children:f})]}),g!==void 0&&$(N,{color:"red",children:["Error: ",g]}),A(V,{flexDirection:"column",gap:0,children:Re.map((u,m)=>{let b=S===m,k=b?x:a[u],I=wn.includes(u),E=`${or[u]}${I?"":" (optional)"}`;return $(V,{gap:2,children:[A(V,{width:24,children:A(N,{bold:b,...b&&{color:"cyan"},children:E})}),A(N,{...b&&{color:"cyan"},children:">"}),A(N,{children:k}),b&&A(N,{color:"cyan",children:"\u2588"})]},u)})}),A(Je,{text:"Enter/Tab/\u2193 next \xB7 \u2191 previous \xB7 Backspace delete \xB7 Esc cancel"})]})}if(n.type==="artifact-select"){let{cursor:S,selected:g}=n;return $(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[A(K,{}),$(V,{flexDirection:"column",gap:0,children:[A(N,{bold:!0,children:"Which artifacts should we scaffold?"}),A(N,{dimColor:!0,children:"Each one gets a directory with a config.json and a script that runs."})]}),A(V,{flexDirection:"column",gap:0,children:ne.map((f,u)=>{let m=S===u,b=g.includes(f);return $(V,{gap:1,children:[A(N,{...m&&{color:"cyan"},children:m?"\u276F":" "}),A(N,{...b&&{color:"green"},children:b?"\u25C9":"\u25EF"}),A(N,{bold:m,...m&&{color:"cyan"},children:pn[f]})]},f)})}),A(Je,{text:"\u2191\u2193 move \xB7 Space toggle \xB7 a all \xB7 n none \xB7 Enter continue \xB7 Esc back"})]})}if(n.type==="confirm"){let S=bn({targetDir:c,name:n.values.name,apiName:n.values.apiName,externalLink:n.values.externalLink,description:n.values.description,developerBusinessId:n.values.developerBusinessId,developerEnvironment:r});return $(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[A(K,{}),$(V,{flexDirection:"column",gap:0,children:[A(N,{bold:!0,children:"Ready to create"}),$(N,{dimColor:!0,children:["Target: ",c]})]}),$(V,{flexDirection:"column",gap:0,children:[A(N,{bold:!0,children:"kizen.json preview"}),A(N,{children:JSON.stringify(S,null,2)})]}),$(V,{flexDirection:"column",gap:0,children:[A(N,{bold:!0,children:"Artifacts"}),n.artifacts.length===0?$(N,{dimColor:!0,children:[" ","none (src/ will hold only thumbnail.png)"]}):n.artifacts.map(g=>$(N,{dimColor:!0,children:[" ",pn[g]]},g))]}),$(V,{flexDirection:"column",gap:0,children:[A(N,{dimColor:!0,children:"Also creates:"}),$(N,{dimColor:!0,children:[" ",ar(c,"src/")]}),$(N,{dimColor:!0,children:[" ",ar(c,"src/thumbnail.png")," (generated placeholder)"]}),$(N,{dimColor:!0,children:[" ",ar(c,"releaseNotes/")]}),$(N,{dimColor:!0,children:[" ",ar(c,".gitignore")," (adds .kizenapp/)"]})]}),A(Je,{text:"Enter to create \xB7 Esc to go back"})]})}return n.type==="resolving-target"?A(V,{paddingY:1,paddingX:2,children:A(N,{dimColor:!0,children:"Finding available sub-directory\u2026"})}):n.type==="writing"?A(V,{paddingY:1,paddingX:2,children:A(N,{dimColor:!0,children:"Creating plugin\u2026"})}):n.type==="done"?$(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[A(K,{}),$(N,{color:"green",bold:!0,children:["Plugin created at ",n.targetDir]}),$(V,{flexDirection:"column",gap:0,children:[A(N,{dimColor:!0,children:"Next steps:"}),$(N,{children:[" ","cd ",n.targetDir]}),$(N,{children:[" ","npx --yes @kizenapps/cli dev"]}),$(N,{dimColor:!0,children:[" ","Replace src/thumbnail.png with real artwork before publishing."]})]}),A(Je,{text:"Enter or Esc to exit"})]}):$(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[A(K,{}),A(N,{color:"red",bold:!0,children:"Failed to create plugin"}),A(N,{children:n.message}),A(Je,{text:"Enter or Esc to exit"})]})};var Md="A hello-world Kizen plugin.";function Ld(e){if(e===void 0||e.trim()===""||e.trim()==="all")return[...ne];if(e.trim()==="none")return[];let t=e.split(",").map(o=>o.trim()).filter(o=>o!==""),r=t.filter(o=>!ne.includes(o));if(r.length>0)throw new Error(`Unknown artifact type(s): ${r.join(", ")}. Valid types: ${ne.join(", ")}, or "all" / "none".`);return[...new Set(t)]}function Hd(e,t,r){let o=e.name?.trim()??"";if(o==="")throw new Error("--name is required for a non-interactive run.");let n=(e.apiName?.trim()??"")||ir(o);if(!xn.test(n))throw new Error(`api_name "${n}" is invalid: ${vn}. Pass --api-name.`);let i=e.description?.trim(),s=e.businessId?.trim()??t.businessId,l=Vd(e,t);return{targetDir:r,name:o,apiName:n,externalLink:e.externalLink?.trim()??"",description:i===void 0||i===""?Md:i,developerBusinessId:s,developerEnvironment:l,artifacts:Ld(e.artifacts)}}function Ud(e){return X.includes(e)}function Vd(e,t){let r=e.environment?.trim();if(r!==void 0&&r!==""){if(!Ud(r))throw new Error(`Unknown environment "${r}". Valid environments: ${X.join(", ")}.`);return r}if(e.businessId!==void 0&&t.businessId==="")throw new Error("--environment is required alongside --business-id when no credentials are saved, so the id is keyed to the environment that issued it.");return t.environment}async function Wi(e,t,r,o){let n;try{n=Hd(e,t,r)}catch(l){o.writeStderr(`Error: ${l instanceof Error?l.message:String(l)}
911
- `),o.setExitCode(1);return}let i=await o.precheckTargetDir(r);if(i!=="ok"){let l=i==="has-manifest"?"kizen.json":".kizenapp/";o.writeStderr(`Error: ${r} already contains ${l}. Refusing to overwrite an existing plugin.
912
- `),o.setExitCode(1);return}try{await o.createPlugin(n)}catch(l){o.writeStderr(`Error: ${l instanceof Error?l.message:String(l)}
913
- `),o.setExitCode(1);return}let s=n.artifacts&&n.artifacts.length>0?n.artifacts.join(", "):"none";o.writeStdout(`Created plugin ${n.apiName} at ${r}
914
- `),o.writeStdout(`Artifacts: ${s}
159
+ `;var Ur=`this.showToast('Hello from a JS action.', { variant: 'success' });
160
+ `;var X=["floatingFrame","block","dataAdornment","page","toolbarItem","objectSettingsItem","jsAction"],zt={floatingFrame:"Floating frame",block:"Block",dataAdornment:"Data adornment",page:"Routable page",toolbarItem:"Toolbar item",objectSettingsItem:"Object settings item",jsAction:"JS action"},$i={floatingFrame:{directory:"floatingFrames",component:"helloFrame",files:{"config.json":_r,"script.js":kr,"styles.css":Pr}},block:{directory:"blocks",component:"helloBlock",files:{"config.json":Ir,"script.js":Nr,"styles.css":Rr}},dataAdornment:{directory:"dataAdornments",component:"helloAdornment",files:{"config.json":Br,"script.js":Ar}},page:{directory:"pages",component:"helloPage",files:{"config.json":Dr,"script.js":Fr,"styles.css":$r,"eventScripts/greet.js":jr}},toolbarItem:{directory:"toolbarItems",component:"helloToolbarItem",files:{"config.json":Lr,"script.js":Or}},objectSettingsItem:{directory:"objectSettingsItems",component:"helloObjectSettingsItem",files:{"config.json":zr,"script.js":Mr}},jsAction:{directory:"actions",component:"helloAction",files:{"config.json":Hr,"script.js":Ur}}};function ji(e,t="src"){let{directory:n,component:o,files:r}=$i[e];return Object.entries(r).map(([i,p])=>({path:`${t}/${n}/${o}/${i}`,content:p}))}function Vr(e,t="src"){return[...new Set(e)].flatMap(n=>ji(n,t))}var qr=`# Copilot review instructions for this Kizen plugin
161
+
162
+ This repo is a Kizen plugin app: a \`kizen.json\` manifest plus the artifacts
163
+ that \`@kizenapps/packager\` finds by walking the directory tree under the
164
+ manifest's \`entry\` (default \`src/\`). No manifest key lists artifacts. The
165
+ directory layout decides what gets packaged.
166
+
167
+ ## The full Kizen plugin documentation is available at \`.copilot-docs/\`
168
+
169
+ Before this review started, the complete Kizen plugin developer documentation
170
+ was fetched into \`.copilot-docs/\` by \`.github/workflows/copilot-code-review.yml\`.
171
+ It is not committed to this repo \u2014 it is cloned from \`kizen/app-engine\` at review
172
+ time, so it is always current. It is the authoritative reference for every plugin
173
+ API, manifest field and runtime contract. Deliberately, no plugin rules are
174
+ copied into this repo: read them from \`.copilot-docs/\` instead.
175
+
176
+ Use it. Start from:
177
+
178
+ - \`.copilot-docs/17-gotchas.md\` \u2014 the consolidated list of traps and silent
179
+ failure modes, grouped by topic. Scan the sections relevant to the changed
180
+ files.
181
+ - \`.copilot-docs/method-index.md\` \u2014 A\u2013Z lookup from any method, manifest field,
182
+ endpoint or identifier to the doc that owns it.
183
+ - \`.copilot-docs/03-manifest-reference.md\` \u2014 every \`kizen.json\` and \`config.json\`
184
+ field, the component directory names, \`api_name\` rules, and the build/publish
185
+ validation rules.
186
+ - The other numbered docs (\`.copilot-docs/04-worker-runtime-api.md\`,
187
+ \`.copilot-docs/07-automation-steps.md\`, \`.copilot-docs/08-actions.md\`, and so
188
+ on) for complete contracts.
189
+
190
+ **Many Kizen plugin APIs fail silently when misused** \u2014 the call succeeds,
191
+ nothing throws, and the behavior is quietly wrong. These defects are not
192
+ detectable from the calling code alone, and they are the single most valuable
193
+ thing to catch in review. When a changed file calls a \`this.*\` worker method,
194
+ look that method up in \`method-index.md\` and read its contract before concluding
195
+ the call is correct.
196
+
197
+ The same applies to artifact config: check each \`config.json\` against
198
+ \`03-manifest-reference.md\`, and each automation step against
199
+ \`07-automation-steps.md\`.
200
+
201
+ When a review comment is based on the documentation, name the file it came from.
202
+
203
+ If \`.copilot-docs/\` is not present, the setup workflow did not run \u2014 it only
204
+ takes effect once it is on the repository's default branch \u2014 so say that in
205
+ the review instead of guessing at the rules from memory.
206
+
207
+ ## Scope
208
+
209
+ This file covers repo structure and general review. Two path-scoped files cover
210
+ the rest: \`.github/instructions/security.instructions.md\` for security-sensitive
211
+ patterns and \`.github/instructions/version-discipline.instructions.md\` for
212
+ manifest version bumps.
213
+ `;var Jr='---\napplyTo: "src/**/*.js,kizen.json"\n---\n\n# Security review rules for plugin scripts\n\nThe rules themselves live in the docs corpus that\n`.github/workflows/copilot-code-review.yml` fetches into `.copilot-docs/`, so\nthey cannot drift out of date here. Read them there rather than reviewing from\nmemory.\n\nWhen a change touches a service, `auth_credentials`, a `{{secret.KEY}}`\nreference, `base_config.secrets`, or dynamic code (`eval`, `new Function`, or a\nstring argument to `setTimeout` or `setInterval`), read\n`.copilot-docs/06-auth-secrets-services.md` together with the "Services, auth &\nsecrets" and "Release & publish" sections of `.copilot-docs/17-gotchas.md`, and\nreview the change against what they say.\n\nWhen a change uses `__dangerouslySkipProxy`, also read\n`.copilot-docs/11-output-ui-iframes-frames.md`.\n';var Wr=`---
214
+ applyTo: "kizen.json"
215
+ ---
216
+
217
+ # Version-bump discipline for \`kizen.json\`
218
+
219
+ The bump matrix lives in the docs corpus that
220
+ \`.github/workflows/copilot-code-review.yml\` fetches into \`.copilot-docs/\`, so it
221
+ cannot drift out of date here.
222
+
223
+ Check every \`kizen.json\` diff against the "How big a bump?" matrix in
224
+ \`.copilot-docs/16-release-and-publish.md\` and the "Release & publish" section of
225
+ \`.copilot-docs/17-gotchas.md\`, and name the change that sets the required level.
226
+ `;var Gr=`# Copilot only reads this file from the default branch, and only if the job is named copilot-setup-steps.
227
+ name: Copilot code review setup
228
+
229
+ on:
230
+ workflow_dispatch:
231
+
232
+ jobs:
233
+ copilot-setup-steps:
234
+ runs-on: ubuntu-latest
235
+ timeout-minutes: 10
236
+ permissions:
237
+ contents: read
238
+ steps:
239
+ - name: Check out this plugin repo
240
+ uses: actions/checkout@v4
241
+
242
+ - name: Fetch the Kizen plugin docs corpus into .copilot-docs/
243
+ run: |
244
+ set -euo pipefail
245
+ git clone --depth 1 --filter=blob:none --sparse \\
246
+ https://github.com/kizen/app-engine.git .docs-src
247
+ git -C .docs-src sparse-checkout set --no-cone '/docs/**/*.md' '!/docs/site/**'
248
+ mv .docs-src/docs .copilot-docs
249
+ rm -rf .docs-src
250
+
251
+ - name: Verify the corpus landed
252
+ run: |
253
+ set -euo pipefail
254
+ test -f .copilot-docs/17-gotchas.md
255
+ test -f .copilot-docs/method-index.md
256
+
257
+ numbered=$(find .copilot-docs -maxdepth 1 -name '[0-9][0-9]-*.md' | wc -l | tr -d ' ')
258
+ test "$numbered" -ge 18
259
+ test -f .copilot-docs/examples/kitchen_sink.md
260
+
261
+ count=$(find .copilot-docs -name '*.md' | wc -l | tr -d ' ')
262
+ echo "Fetched $count markdown files into .copilot-docs/ ($numbered numbered docs)"
263
+ test "$count" -ge 24
264
+ du -sh .copilot-docs
265
+ `;function lt(){return[{path:".github/copilot-instructions.md",content:qr},{path:".github/instructions/security.instructions.md",content:Jr},{path:".github/instructions/version-discipline.instructions.md",content:Wr},{path:".github/workflows/copilot-code-review.yml",content:Gr}]}import{deflateSync as Hi}from"zlib";var Ht=512,Ui=150,Vi=26,Ut=46,Kr=.6,qi=Ut*.55,Ji=.55,Wi=.47;function Gi(e){let t=2166136261;for(let n=0;n<e.length;n+=1)t^=e.charCodeAt(n),t=t+((t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24))>>>0;return t}function Ki(e){let t=Gi(e);return{hue:t%360,quarterTurns:(t>>>16)%4}}function Yi(e,t,n){let o=(1-Math.abs(2*n-1))*t,r=e/60,i=o*(1-Math.abs(r%2-1)),p=n-o/2,[f,g,m]=r<1?[o,i,0]:r<2?[i,o,0]:r<3?[0,o,i]:r<4?[0,i,o]:r<5?[i,0,o]:[o,0,i];return[Math.round((f+p)*255),Math.round((g+p)*255),Math.round((m+p)*255)]}function Ue(e,t,n,o,r){let i=Math.abs(e)-n+r,p=Math.abs(t)-o+r;return Math.hypot(Math.max(i,0),Math.max(p,0))+Math.min(Math.max(i,p),0)-r}function ct(e,t,n,o,r){return Math.hypot(e-n,t-o)-r}function Xi(e,t){let n=Ui,o=Ut,r=o*Kr,i=qi,p=Ue(e,t,n,n,Vi),f=Math.min(ct(e,t,0,-n-r,o),Ue(e,t+n+r/2,i,r/2+1,0)),g=Math.min(ct(e,t,n+r,0,o),Ue(e-n-r/2,t,r/2+1,i,0));p=Math.min(p,f,g);let m=Math.min(ct(e,t,0,n-r,o),Ue(e,t-n+r/2,i,r/2+1,0)),y=Math.min(ct(e,t,-n+r,0,o),Ue(e+n-r/2,t,r/2+1,i,0));return Math.max(p,-m,-y)}function Zi(e){let t=Ht,n=new Uint8Array(t*t*4),[o,r,i]=Yi(e.hue,Ji,Wi),p=Ut*(1+Kr),f=(t-1)/2,g=p/2;for(let m=0;m<t;m+=1)for(let y=0;y<t;y+=1){let T=y-f,P=m-f;for(let w=0;w<e.quarterTurns;w+=1)[T,P]=[-P,T];let x=Xi(T+g,P-g),c=Math.min(1,Math.max(0,.5-x));if(c<=0)continue;let b=(m*t+y)*4;n[b]=o,n[b+1]=r,n[b+2]=i,n[b+3]=Math.round(c*255)}return n}var Qi=new Uint32Array(256).map((e,t)=>{let n=t;for(let o=0;o<8;o+=1)n=n&1?3988292384^n>>>1:n>>>1;return n>>>0});function es(e){let t=4294967295;for(let n of e)t=(Qi[(t^n)&255]??0)^t>>>8;return(t^4294967295)>>>0}function Mt(e,t){let n=Buffer.from(e,"latin1"),o=Buffer.alloc(4);o.writeUInt32BE(t.length);let r=Buffer.alloc(4);return r.writeUInt32BE(es(Buffer.concat([n,t]))),Buffer.concat([o,n,t,r])}var ts=Buffer.from([137,80,78,71,13,10,26,10]);function rs(e,t,n){if(n.length!==e*t*4)throw new Error(`encodePng: expected ${String(e*t*4)} bytes of RGBA for ${String(e)}x${String(t)}, got ${String(n.length)}`);let o=Buffer.alloc(13);o.writeUInt32BE(e,0),o.writeUInt32BE(t,4),o[8]=8,o[9]=6;let r=e*4,i=Buffer.alloc((r+1)*t);for(let p=0;p<t;p+=1)i.set(n.subarray(p*r,(p+1)*r),p*(r+1)+1);return Buffer.concat([ts,Mt("IHDR",o),Mt("IDAT",Hi(i,{level:9})),Mt("IEND",new Uint8Array(0))])}function Yr(e){return rs(Ht,Ht,Zi(Ki(e)))}var is="src/thumbnail.png",ss=["go","fmo"];async function pt(e){try{return await ns(e),!0}catch{return!1}}async function ye(e){return await pt(ce(e,"kizen.json"))?"has-manifest":await pt(ce(e,".kizenapp"))?"has-kizenapp":"ok"}async function Zr(e,t){let n=ce(e,t);if(!await pt(n))return n;for(let o=1;o<1e3;o++){let r=ce(e,`${t}-${String(o)}`);if(!await pt(r))return r}throw new Error(`Could not find an available directory name for "${t}"`)}function qt(e){let t=e.developerBusinessId.trim(),n=t!==""&&!ss.includes(e.developerEnvironment)?[e.developerEnvironment]:["prod"];return{name:e.name,version:"1.0.0",published:!0,api_name:e.apiName,external_link:e.externalLink,description:e.description,entry:"src/",engine:"1.0.0",release_notes_directory:"releaseNotes/",release_environments:n,config_template:{},base_config:{},...t?{developer_business_id:{[e.developerEnvironment]:t}}:{}}}async function Xr(e,t){let n=t.map(r=>({fullPath:ce(e,...r.path.split("/")),content:r.content})),o=new Set(n.map(r=>os(r.fullPath)));await Promise.all([...o].map(r=>dt(r,{recursive:!0}))),await Promise.all(n.map(r=>Vt(r.fullPath,r.content,"utf-8")))}async function ut(e){await dt(e.targetDir,{recursive:!0});let t=qt(e);await Vt(ce(e.targetDir,"kizen.json"),JSON.stringify(t,null,2)+`
266
+ `,"utf-8"),await dt(ce(e.targetDir,"src"),{recursive:!0}),await dt(ce(e.targetDir,"releaseNotes"),{recursive:!0}),await Promise.all([Xr(e.targetDir,Vr(e.artifacts??[])),Xr(e.targetDir,lt()),Vt(ce(e.targetDir,...is.split("/")),Yr(e.apiName))]),he(e.targetDir)}var be=["name","apiName","externalLink","description","developerBusinessId"],ft={name:"Name",apiName:"API name",externalLink:"External link",description:"Description",developerBusinessId:"Business ID"},Jt=["name","apiName","description"],Wt=/^[a-z_][a-z0-9_]+$/,Gt="must start with a letter or underscore and contain only lowercase letters, numbers, or underscores (minimum 2 characters)";function mt(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}function Qr(e){return{name:"",apiName:"",externalLink:"",description:"",developerBusinessId:e}}function en(e,t){let n=t.trim();if(Jt.includes(e)&&!n)return`${ft[e]} is required`;if(e==="apiName"&&!Wt.test(n))return`${ft[e]} ${Gt}`}function tn(e,t){return e==="description"?t:t.trim()}function rn(e,t){let n=new Set(e);return n.has(t)?n.delete(t):n.add(t),X.filter(o=>n.has(o))}import{jsx as _,jsxs as B}from"react/jsx-runtime";var Ie=({text:e})=>_(I,{dimColor:!0,children:e}),nn=({parentDir:e,defaultBusinessId:t,defaultEnvironment:n})=>{let o=as(),[r,i]=De({type:"target-select",cursor:0}),[p,f]=De("in-place"),[g,m]=De(()=>Qr(t)),[y,T]=De(()=>[...X]),[P,x]=De(""),[c,b]=De(e),w=gt((v,d)=>{f(v),x(d.name),i({type:"field-entry",field:0,values:d})},[]),H=gt(async v=>{let d=await ye(v);if(d!=="ok"){i({type:"precheck-error",targetDir:v,reason:d});return}b(v),w("in-place",g)},[g,w]),j=gt(async(v,d)=>{try{let l=await Zr(e,v.apiName);b(l),i({type:"confirm",values:v,artifacts:d})}catch(l){i({type:"error",message:l instanceof Error?l.message:String(l)})}},[e]),C=gt(async(v,d,l)=>{try{await ut({targetDir:d,name:v.name,apiName:v.apiName,externalLink:v.externalLink,description:v.description,developerBusinessId:v.developerBusinessId,developerEnvironment:n,artifacts:l}),i({type:"done",targetDir:d})}catch(s){i({type:"error",message:s instanceof Error?s.message:String(s)})}},[n]);if(ls((v,d)=>{if(d.ctrl&&v==="c"){o.exit();return}if(r.type==="target-select"){d.upArrow?i({type:"target-select",cursor:0}):d.downArrow?i({type:"target-select",cursor:1}):d.return?r.cursor===0?H(e):w("sub-dir",g):d.escape&&o.exit();return}if(r.type==="precheck-error"){(d.return||d.escape)&&o.exit();return}if(r.type==="field-entry"){let{field:l,values:s}=r,a=be[l];if(!a)return;if(d.escape){o.exit();return}if(d.backspace||d.delete){x(u=>u.slice(0,-1));return}if(d.upArrow&&l>0){let u={...s,[a]:P},h=l-1,k=be[h];i({type:"field-entry",field:h,values:u}),m(u),x(k?u[k]:"");return}if(d.return||d.tab||d.downArrow){let u=en(a,P);if(u!==void 0){i({type:"field-entry",field:l,values:s,fieldError:u});return}let h=tn(a,P),k={...s,[a]:h};m(k);let E=l+1;if(E>=be.length)x(""),i({type:"artifact-select",values:k,cursor:0,selected:y});else{let R=be[E],z=R?k[R]:"",W=R==="apiName"&&!z?mt(k.name):z;i({type:"field-entry",field:E,values:k}),x(W)}return}v&&!d.ctrl&&!d.meta&&x(u=>u+v);return}if(r.type==="artifact-select"){let{cursor:l,selected:s,values:a}=r;if(d.escape){let u=be.length-1,h=be[u];i({type:"field-entry",field:u,values:a}),x(h?a[h]:"");return}if(d.upArrow){i({...r,cursor:Math.max(0,l-1)});return}if(d.downArrow){i({...r,cursor:Math.min(X.length-1,l+1)});return}if(v===" "){let u=X[l];if(u){let h=rn(s,u);T(h),i({...r,selected:h})}return}if(v==="a"){T([...X]),i({...r,selected:[...X]});return}if(v==="n"){T([]),i({...r,selected:[]});return}d.return&&(p==="sub-dir"?(i({type:"resolving-target"}),j(a,s)):i({type:"confirm",values:a,artifacts:s}));return}if(r.type==="confirm"){if(d.escape){i({type:"artifact-select",values:r.values,cursor:0,selected:r.artifacts});return}d.return&&(i({type:"writing"}),C(r.values,c,r.artifacts));return}(r.type==="done"||r.type==="error")&&(d.return||d.escape)&&o.exit()}),r.type==="target-select"){let v=[{label:"Use current directory",desc:e},{label:"Create new sub-directory",desc:`${e}/<inferred from API name>`}];return B(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[_(q,{}),B(M,{flexDirection:"column",gap:0,children:[_(I,{bold:!0,children:"Where should the new plugin live?"}),v.map((d,l)=>{let s=r.cursor===l;return B(M,{gap:2,children:[_(I,{...s&&{color:"cyan"},children:s?"\u276F":" "}),_(I,{bold:s,...s&&{color:"cyan"},children:d.label}),_(I,{dimColor:!0,children:d.desc})]},d.label)})]}),_(Ie,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(r.type==="precheck-error"){let{targetDir:v,reason:d}=r;return B(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[_(q,{}),_(I,{color:"red",bold:!0,children:"Cannot create plugin"}),B(M,{flexDirection:"column",gap:0,children:[B(I,{children:[v," already contains ",_(I,{bold:!0,children:d==="has-manifest"?"kizen.json":".kizenapp/"}),"."]}),_(I,{dimColor:!0,children:"Remove the conflicting file/directory or choose a different target to avoid overwriting an existing plugin."})]}),_(Ie,{text:"Enter or Esc to exit"})]})}if(r.type==="field-entry"){let{field:v,fieldError:d}=r,l=p==="sub-dir"?`Target: sub-directory of ${e} (inferred from API name)`:`Target: ${c}`;return B(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[_(q,{}),B(M,{flexDirection:"column",gap:0,children:[_(I,{bold:!0,children:"Plugin details"}),_(I,{dimColor:!0,children:l})]}),d!==void 0&&B(I,{color:"red",children:["Error: ",d]}),_(M,{flexDirection:"column",gap:0,children:be.map((s,a)=>{let u=v===a,h=u?P:g[s],k=Jt.includes(s),E=`${ft[s]}${k?"":" (optional)"}`;return B(M,{gap:2,children:[_(M,{width:24,children:_(I,{bold:u,...u&&{color:"cyan"},children:E})}),_(I,{...u&&{color:"cyan"},children:">"}),_(I,{children:h}),u&&_(I,{color:"cyan",children:"\u2588"})]},s)})}),_(Ie,{text:"Enter/Tab/\u2193 next \xB7 \u2191 previous \xB7 Backspace delete \xB7 Esc cancel"})]})}if(r.type==="artifact-select"){let{cursor:v,selected:d}=r;return B(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[_(q,{}),B(M,{flexDirection:"column",gap:0,children:[_(I,{bold:!0,children:"Which artifacts should we scaffold?"}),_(I,{dimColor:!0,children:"Each one gets a directory with a config.json and a script that runs."})]}),_(M,{flexDirection:"column",gap:0,children:X.map((l,s)=>{let a=v===s,u=d.includes(l);return B(M,{gap:1,children:[_(I,{...a&&{color:"cyan"},children:a?"\u276F":" "}),_(I,{...u&&{color:"green"},children:u?"\u25C9":"\u25EF"}),_(I,{bold:a,...a&&{color:"cyan"},children:zt[l]})]},l)})}),_(Ie,{text:"\u2191\u2193 move \xB7 Space toggle \xB7 a all \xB7 n none \xB7 Enter continue \xB7 Esc back"})]})}if(r.type==="confirm"){let v=qt({targetDir:c,name:r.values.name,apiName:r.values.apiName,externalLink:r.values.externalLink,description:r.values.description,developerBusinessId:r.values.developerBusinessId,developerEnvironment:n});return B(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[_(q,{}),B(M,{flexDirection:"column",gap:0,children:[_(I,{bold:!0,children:"Ready to create"}),B(I,{dimColor:!0,children:["Target: ",c]})]}),B(M,{flexDirection:"column",gap:0,children:[_(I,{bold:!0,children:"kizen.json preview"}),_(I,{children:JSON.stringify(v,null,2)})]}),B(M,{flexDirection:"column",gap:0,children:[_(I,{bold:!0,children:"Artifacts"}),r.artifacts.length===0?B(I,{dimColor:!0,children:[" ","none (src/ will hold only thumbnail.png)"]}):r.artifacts.map(d=>B(I,{dimColor:!0,children:[" ",zt[d]]},d))]}),B(M,{flexDirection:"column",gap:0,children:[_(I,{dimColor:!0,children:"Also creates:"}),B(I,{dimColor:!0,children:[" ",ht(c,"src/")]}),B(I,{dimColor:!0,children:[" ",ht(c,"src/thumbnail.png")," (generated placeholder)"]}),B(I,{dimColor:!0,children:[" ",ht(c,"releaseNotes/")]}),B(I,{dimColor:!0,children:[" ",ht(c,".gitignore")," (adds .kizenapp/)"]})]}),_(Ie,{text:"Enter to create \xB7 Esc to go back"})]})}return r.type==="resolving-target"?_(M,{paddingY:1,paddingX:2,children:_(I,{dimColor:!0,children:"Finding available sub-directory\u2026"})}):r.type==="writing"?_(M,{paddingY:1,paddingX:2,children:_(I,{dimColor:!0,children:"Creating plugin\u2026"})}):r.type==="done"?B(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[_(q,{}),B(I,{color:"green",bold:!0,children:["Plugin created at ",r.targetDir]}),B(M,{flexDirection:"column",gap:0,children:[_(I,{dimColor:!0,children:"Next steps:"}),B(I,{children:[" ","cd ",r.targetDir]}),B(I,{children:[" ","npx --yes @kizenapps/cli dev"]}),B(I,{dimColor:!0,children:[" ","Replace src/thumbnail.png with real artwork before publishing."]})]}),_(Ie,{text:"Enter or Esc to exit"})]}):B(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[_(q,{}),_(I,{color:"red",bold:!0,children:"Failed to create plugin"}),_(I,{children:r.message}),_(Ie,{text:"Enter or Esc to exit"})]})};import{mkdir as on,readFile as cs,readdir as ds,writeFile as sn}from"fs/promises";import{homedir as ps}from"os";import{dirname as us,join as yt}from"path";var Y=["go","fmo","staging","integration","test1"];var Ne=yt(ps(),".kizenappbuilder"),Fe=yt(Ne,"credentials.json"),xe="credentials";function fs(e){return Y.includes(e)}function ms(e){if(typeof e!="object"||e===null)throw new Error("Credentials must be a JSON object");let t=e,n=t.environment;return{apiKey:typeof t.apiKey=="string"?t.apiKey:"",userId:typeof t.userId=="string"?t.userId:"",businessId:typeof t.businessId=="string"?t.businessId:"",environment:fs(n)?n:"go"}}async function ve(e){let t=await cs(e,"utf-8");return ms(JSON.parse(t))}async function Z(){try{return await ve(Fe)}catch{return null}}async function gs(e){await on(us(Fe),{recursive:!0,mode:448}),await sn(Fe,JSON.stringify(e,null,2),{encoding:"utf-8",mode:384})}function an(e){return yt(Ne,`${e}.json`)}async function Re(){let e=[{name:xe,path:Fe,isDefault:!0}];try{let t=await ds(Ne);for(let n of t){if(!n.endsWith(".json"))continue;let o=n.slice(0,-5);o!==xe&&e.push({name:o,path:yt(Ne,n),isDefault:!1})}}catch{}return e}async function ln(e,t){if(e===xe){await gs(t);return}await on(Ne,{recursive:!0,mode:448}),await sn(an(e),JSON.stringify(t,null,2),{encoding:"utf-8",mode:384})}async function de(e){try{return await ve(an(e))}catch{return null}}var hs="A hello-world Kizen plugin.";function ys(e){if(e===void 0||e.trim()===""||e.trim()==="all")return[...X];if(e.trim()==="none")return[];let t=e.split(",").map(o=>o.trim()).filter(o=>o!==""),n=t.filter(o=>!X.includes(o));if(n.length>0)throw new Error(`Unknown artifact type(s): ${n.join(", ")}. Valid types: ${X.join(", ")}, or "all" / "none".`);return[...new Set(t)]}function bs(e,t,n){let o=e.name?.trim()??"";if(o==="")throw new Error("--name is required for a non-interactive run.");let r=(e.apiName?.trim()??"")||mt(o);if(!Wt.test(r))throw new Error(`api_name "${r}" is invalid: ${Gt}. Pass --api-name.`);let i=e.description?.trim(),p=e.businessId?.trim()??t.businessId,f=vs(e,t);return{targetDir:n,name:o,apiName:r,externalLink:e.externalLink?.trim()??"",description:i===void 0||i===""?hs:i,developerBusinessId:p,developerEnvironment:f,artifacts:ys(e.artifacts)}}function xs(e){return Y.includes(e)}function vs(e,t){let n=e.environment?.trim();if(n!==void 0&&n!==""){if(!xs(n))throw new Error(`Unknown environment "${n}". Valid environments: ${Y.join(", ")}.`);return n}if(e.businessId!==void 0&&t.businessId==="")throw new Error("--environment is required alongside --business-id when no credentials are saved, so the id is keyed to the environment that issued it.");return t.environment}async function cn(e,t,n,o){let r;try{r=bs(e,t,n)}catch(f){o.writeStderr(`Error: ${f instanceof Error?f.message:String(f)}
267
+ `),o.setExitCode(1);return}let i=await o.precheckTargetDir(n);if(i!=="ok"){let f=i==="has-manifest"?"kizen.json":".kizenapp/";o.writeStderr(`Error: ${n} already contains ${f}. Refusing to overwrite an existing plugin.
268
+ `),o.setExitCode(1);return}try{await o.createPlugin(r)}catch(f){o.writeStderr(`Error: ${f instanceof Error?f.message:String(f)}
269
+ `),o.setExitCode(1);return}let p=r.artifacts&&r.artifacts.length>0?r.artifacts.join(", "):"none";o.writeStdout(`Created plugin ${r.apiName} at ${n}
270
+ `),o.writeStdout(`Artifacts: ${p}
915
271
  `),o.writeStdout(`Thumbnail: src/thumbnail.png (generated placeholder; replace it before publishing)
916
- `),n.developerBusinessId.trim()===""&&o.writeStderr(`Warning: no developer_business_id configured, so this plugin cannot be preview-published yet.
917
- `)}function Ki(e){e.command("create").description("Scaffold a new Kizen plugin project").option("-n, --name <name>","plugin display name (implies a non-interactive run)").option("-a, --api-name <name>","plugin api_name (inferred from --name when omitted)").option("-d, --description <text>","plugin description").option("-l, --external-link <url>","external link shown on the plugin listing").option("-b, --business-id <id>","developer business id (defaults to saved credentials)").option("-e, --environment <env>",`environment the business id belongs to: ${X.join(", ")} (defaults to saved credentials)`).option("--artifacts <list>",`comma-separated artifact types, or "all" / "none" (default: all). Types: ${ne.join(", ")}`).action(async t=>{let r=process.cwd(),o=await te(),n=o?.businessId??"",i=o?.environment??"go",s=Object.values(t).some(p=>p!==void 0);if(!process.stdin.isTTY||!process.stdout.isTTY||s){await Wi(t,{businessId:n,environment:i},r,{precheckTargetDir:rr,createPlugin:nr,writeStdout:p=>{process.stdout.write(p)},writeStderr:p=>{process.stderr.write(p)},setExitCode:p=>{process.exitCode=p}});return}let{waitUntilExit:a}=Kd(Wd(Vi,{parentDir:r,defaultBusinessId:n,defaultEnvironment:i}),{exitOnCtrlC:!1});await a()})}import{createElement as _s}from"react";import{render as Ps}from"ink";import{useCallback as ot,useEffect as mr,useRef as Ce,useState as ie}from"react";import{Box as Q,Text as j,useInput as Mu}from"ink";import{PluginValidationError as Lu}from"@kizenapps/packager";import{watch as Hu}from"fs";import{createServer as Uu}from"http";import{join as ks}from"path";import{WebSocketServer as Vu}from"ws";import{createReadStream as as,realpathSync as cu}from"fs";import{access as du,readdir as uu,readFile as pu,realpath as fu,stat as ls}from"fs/promises";import{dirname as mu,extname as cs,join as St,resolve as hu,sep as Nn}from"path";import{fileURLToPath as gu}from"url";var Jd=new Set(["set-cookie","strict-transport-security","content-security-policy","content-security-policy-report-only","x-frame-options","content-encoding","content-length"]);function lr(e){let t={};for(let[r,o]of Object.entries(e)){let n=r.toLowerCase();Jd.has(n)||n.startsWith("access-control-")||(t[r]=o)}return t}var kn=10*1024*1024,qd=6e4,Gd=200,wt=class extends Error{bytesRead;limit;constructor(t,r){super(`Upstream response exceeded ${String(r)} bytes (read ${String(t)}).`),this.name="ProxyResponseTooLargeError",this.bytesRead=t,this.limit=r}};async function Cn(e,t){let r=e.body;if(r===null)return Buffer.alloc(0);let o=[],n=0;for await(let i of r){if(n+=i.length,n>t)throw new wt(n,t);o.push(i)}return Buffer.concat(o)}async function Ji(e){let t=lr(Object.fromEntries(e.headers)),r=await Cn(e,kn);return{status:e.status,headers:t,body:r}}function qi(e={}){let{onChange:t,enabled:r=!0}=e;if(!r)return{async get(s,l){return{response:await Ji(await l()),fromCache:!1}},clear(){}};let o=new Map,n=()=>{t?.(o.size)},i=s=>{o.delete(s)&&n()};return{get(s,l){let a=o.get(s);if(a!==void 0){if(Date.now()-a.insertedAt<qd)return a.promise.then(h=>({response:h,fromCache:!0}));i(s)}if(o.size>=Gd){let h=o.keys().next().value;h!==void 0&&i(h)}let p=l().then(Ji).catch(h=>{throw i(s),h});return o.set(s,{insertedAt:Date.now(),promise:p}),n(),p.then(h=>((h.status<200||h.status>=300)&&i(s),{response:h,fromCache:!1}))},clear(){o.clear(),n()}}}var Sn={".html":"text/html; charset=utf-8",".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".json":"application/json; charset=utf-8",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".map":"application/json"};var _n={prod:"https://plugin-wizard.kizen.com",dev:"https://plugin-wizard.kizen.dev",localDev:"http://localhost:9823"};function xt(e){let t=process.env.PLUGIN_WIZARD_URL;return t!==void 0&&t!==""?t:e==="prod"?process.env.PLUGIN_WIZARD_URL_PROD||_n.prod:process.env.PLUGIN_WIZARD_URL_DEV!==void 0&&process.env.PLUGIN_WIZARD_URL_DEV!==""?process.env.PLUGIN_WIZARD_URL_DEV:process.env.APPBUILDER_LOCAL_DEV!==void 0&&process.env.APPBUILDER_LOCAL_DEV!==""?_n.localDev:_n.dev}import{spawn as In}from"child_process";import{writeFile as Qi,rm as Tn,mkdtemp as Qd,access as eu}from"fs/promises";import{join as kt}from"path";import{tmpdir as tu}from"os";var Gi=`awslambdaric
272
+ `),r.developerBusinessId.trim()===""&&o.writeStderr(`Warning: no developer_business_id configured, so this plugin cannot be preview-published yet.
273
+ `)}function dn(e){e.command("create").description("Scaffold a new Kizen plugin project").option("-n, --name <name>","plugin display name (implies a non-interactive run)").option("-a, --api-name <name>","plugin api_name (inferred from --name when omitted)").option("-d, --description <text>","plugin description").option("-l, --external-link <url>","external link shown on the plugin listing").option("-b, --business-id <id>","developer business id (defaults to saved credentials)").option("-e, --environment <env>",`environment the business id belongs to: ${Y.join(", ")} (defaults to saved credentials)`).option("--artifacts <list>",`comma-separated artifact types, or "all" / "none" (default: all). Types: ${X.join(", ")}`).action(async t=>{let n=process.cwd(),o=await Z(),r=o?.businessId??"",i=o?.environment??"go",p=Object.values(t).some(m=>m!==void 0);if(!process.stdin.isTTY||!process.stdout.isTTY||p){await cn(t,{businessId:r,environment:i},n,{precheckTargetDir:ye,createPlugin:ut,writeStdout:m=>{process.stdout.write(m)},writeStderr:m=>{process.stderr.write(m)},setExitCode:m=>{process.exitCode=m}});return}let{waitUntilExit:g}=Cs(ws(nn,{parentDir:n,defaultBusinessId:r,defaultEnvironment:i}),{exitOnCtrlC:!1});await g()})}import{createElement as Vn}from"react";import{render as qn}from"ink";import{useCallback as $e,useEffect as Tt,useRef as pe,useState as Q}from"react";import{Box as G,Text as A,useInput as Sa}from"ink";import{PluginValidationError as Ta}from"@kizenapps/packager";import{watch as Ea}from"fs";import{createServer as _a}from"http";import{join as Mn}from"path";import{WebSocketServer as ka}from"ws";import{createReadStream as _n,realpathSync as Vs}from"fs";import{access as qs,readdir as Js,readFile as Ws,realpath as Gs,stat as kn}from"fs/promises";import{dirname as Ks,extname as Pn,join as Ke,resolve as Ys,sep as or}from"path";import{fileURLToPath as Xs}from"url";var Ss=new Set(["set-cookie","strict-transport-security","content-security-policy","content-security-policy-report-only","x-frame-options","content-encoding","content-length"]);function bt(e){let t={};for(let[n,o]of Object.entries(e)){let r=n.toLowerCase();Ss.has(r)||r.startsWith("access-control-")||(t[n]=o)}return t}var Kt=10*1024*1024,Ts=6e4,Es=200,Ve=class extends Error{bytesRead;limit;constructor(t,n){super(`Upstream response exceeded ${String(n)} bytes (read ${String(t)}).`),this.name="ProxyResponseTooLargeError",this.bytesRead=t,this.limit=n}};async function Yt(e,t){let n=e.body;if(n===null)return Buffer.alloc(0);let o=[],r=0;for await(let i of n){if(r+=i.length,r>t)throw new Ve(r,t);o.push(i)}return Buffer.concat(o)}async function pn(e){let t=bt(Object.fromEntries(e.headers)),n=await Yt(e,Kt);return{status:e.status,headers:t,body:n}}function un(e={}){let{onChange:t,enabled:n=!0}=e;if(!n)return{async get(p,f){return{response:await pn(await f()),fromCache:!1}},clear(){}};let o=new Map,r=()=>{t?.(o.size)},i=p=>{o.delete(p)&&r()};return{get(p,f){let g=o.get(p);if(g!==void 0){if(Date.now()-g.insertedAt<Ts)return g.promise.then(y=>({response:y,fromCache:!0}));i(p)}if(o.size>=Es){let y=o.keys().next().value;y!==void 0&&i(y)}let m=f().then(pn).catch(y=>{throw i(p),y});return o.set(p,{insertedAt:Date.now(),promise:m}),r(),m.then(y=>((y.status<200||y.status>=300)&&i(p),{response:y,fromCache:!1}))},clear(){o.clear(),r()}}}var Xt={".html":"text/html; charset=utf-8",".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".json":"application/json; charset=utf-8",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".map":"application/json"};import{mkdir as _s,readFile as ks,writeFile as Ps}from"fs/promises";import{join as fn}from"path";async function we(e){try{let t=await ks(fn(e,"config.json"),"utf-8");return JSON.parse(t)}catch{return{}}}async function Ce(e,t){await _s(e,{recursive:!0}),await Ps(fn(e,"config.json"),JSON.stringify(t,null,2),"utf-8")}var Zt={prod:"https://plugin-wizard.kizen.com",dev:"https://plugin-wizard.kizen.dev",localDev:"http://localhost:9823"};function qe(e){let t=process.env.PLUGIN_WIZARD_URL;return t!==void 0&&t!==""?t:e==="prod"?process.env.PLUGIN_WIZARD_URL_PROD||Zt.prod:process.env.PLUGIN_WIZARD_URL_DEV!==void 0&&process.env.PLUGIN_WIZARD_URL_DEV!==""?process.env.PLUGIN_WIZARD_URL_DEV:process.env.APPBUILDER_LOCAL_DEV!==void 0&&process.env.APPBUILDER_LOCAL_DEV!==""?Zt.localDev:Zt.dev}import{spawn as rr}from"child_process";import{writeFile as bn,rm as tr,mkdtemp as Bs,access as As}from"fs/promises";import{join as We}from"path";import{tmpdir as Ds}from"os";var mn=`awslambdaric
918
274
  bcrypt
919
275
  boto3
920
276
  fpdf2
@@ -932,8 +288,8 @@ pytz
932
288
  requests
933
289
  sqlalchemy>=2.0,<2.1
934
290
  tzdata
935
- `;function Yi(e){return e.split(`
936
- `).map(t=>t.trim().replace(/\s+#.*$/,"")).filter(t=>t.length>0&&!t.startsWith("#"))}function Xi(e){let t=/^python-(\d+)[.-](\d+)$/.exec(e),r=t?.[1],o=t?.[2];return r!==void 0&&o!==void 0?`python${r}.${o}`:"python3"}function Xd(e){return e==null||e.trim()===""?null:e}function Zd(e){return e==null?null:typeof e=="string"?Xd(e):typeof e=="number"||typeof e=="boolean"||typeof e=="bigint"?String(e):typeof e=="function"||typeof e=="symbol"?null:JSON.stringify(e)}function Zi(e){return e?Object.fromEntries(Object.entries(e).map(([t,r])=>[t,Zd(r)])):{}}var ru=`
291
+ `;import{AUTOMATION_SCRIPT_RUNTIMES as Fs,isSupportedScriptRuntime as $s,scriptRuntimeToApiName as vn}from"@kizenapps/packager";function gn(e){return e.split(`
292
+ `).map(t=>t.trim().replace(/\s+#.*$/,"")).filter(t=>t.length>0&&!t.startsWith("#"))}function hn(e){let t=/^python-(\d+)[.-](\d+)$/.exec(e),n=t?.[1],o=t?.[2];return n!==void 0&&o!==void 0?`python${n}.${o}`:"python3"}function Ns(e){return e==null||e.trim()===""?null:e}function Rs(e){return e==null?null:typeof e=="string"?Ns(e):typeof e=="number"||typeof e=="boolean"||typeof e=="bigint"?String(e):typeof e=="function"||typeof e=="symbol"?null:JSON.stringify(e)}function yn(e){return e?Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Rs(n)])):{}}var js=`
937
293
  import sys, json, os, traceback
938
294
 
939
295
  # --- Inputs proxy (attribute access) ---
@@ -1093,29 +449,29 @@ sys.stdout.write('\\n__KIZEN_RESULT_START__\\n')
1093
449
  sys.stdout.write(json.dumps(result, ensure_ascii=False))
1094
450
  sys.stdout.write('\\n__KIZEN_RESULT_END__\\n')
1095
451
  sys.stdout.flush()
1096
- `.trim(),nu=3e4,cr=kt(process.cwd(),".kizenapp","venv"),ts=["python-3-12","python-3-13"];function ou(e){return ts.includes(e)}var rs=Yi(Gi);if(rs.length===0)throw new Error("python-requirements.txt bundled no packages \u2014 check the build output");var vt=null,Ct=[3,10];function ns([e,t]){return e>Ct[0]||e===Ct[0]&&t>=Ct[1]}function Pn(e){return`${String(e[0])}.${String(e[1])}`}function os(e){return new Promise(t=>{let r=In(e,["-c","import sys; print('%d.%d' % sys.version_info[:2])"],{stdio:["ignore","pipe","ignore"]}),o="";r.stdout.on("data",n=>{o+=n.toString("utf-8")}),r.on("error",()=>{t(null)}),r.on("close",n=>{let i=/(\d+)\.(\d+)/.exec(o);if(n!==0||!i){t(null);return}t([Number(i[1]),Number(i[2])])})})}async function iu(e){let t=Xi(e),r=t==="python3"?["python3"]:[t,"python3"],o=[];for(let n of r){let i=await os(n);if(!i){o.push(`${n}: not found`);continue}if(ns(i))return n;o.push(`${n}: ${Pn(i)} (too old)`)}throw new Error(`No Python >= ${Pn(Ct)} found for runtime "${e}". The local runner needs Python ${Pn(Ct)}+ to install the bundled packages (e.g. psycopg 3.3), matching the remote code-runner (3.12 / 3.13). Tried ${o.join(", ")}. Install Python 3.12 or 3.13 and retry.`)}function es(e,t,r){return new Promise((o,n)=>{let i=In(e,t,{stdio:["ignore","pipe","pipe"]}),s=(a,p)=>{let h=[],w="";return a.on("data",x=>{w+=x.toString("utf-8");let d=w.indexOf(`
1097
- `);for(;d>=0;){let c=w.slice(0,d).replace(/\r$/,"");w=w.slice(d+1),h.push(c),r?.(c,p),d=w.indexOf(`
1098
- `)}}),a.on("end",()=>{if(w.length>0){let x=w.replace(/\r$/,"");h.push(x),r?.(x,p)}}),h},l=s(i.stderr,"stderr");s(i.stdout,"stdout"),i.on("error",n),i.on("close",a=>{if(a===0)o();else{let p=l.slice(-10).join(`
1099
- `).trim(),h=p.length>0?`: ${p}`:"";n(new Error(`${e} exited with code ${String(a)}${h}`))}})})}var En=null;function su(e,t){if(vt)return t&&En?.add(t),vt;let r=new Set;t&&r.add(t),En=r;let o=i=>{for(let s of r)s(i)},n=kt(cr,"bin","python");return vt=(async()=>{try{let i=!1;try{await eu(n);let s=await os(n);i=s!==null&&ns(s)}catch{i=!1}if(!i){await Tn(cr,{recursive:!0,force:!0}).catch(()=>{});let s=await iu(e);await es(s,["-m","venv",cr])}return o({kind:"start"}),await es(n,["-m","pip","install","--disable-pip-version-check",...rs],(s,l)=>{o({kind:"log",line:s,stream:l})}),o({kind:"complete"}),n}catch(i){let s=i instanceof Error?i.message:String(i);throw o({kind:"error",message:s}),vt=null,await Tn(cr,{recursive:!0,force:!0}).catch(()=>{}),i}finally{En=null}})(),vt}function au(e){let t="__KIZEN_RESULT_START__",r="__KIZEN_RESULT_END__",o=e.indexOf(t),n=e.indexOf(r);if(o===-1||n===-1)return{userStdout:e,result:null};let i=e.slice(0,o).trimEnd(),s=e.slice(o+t.length,n).trim();try{let l=JSON.parse(s);return{userStdout:i,result:l}}catch{return{userStdout:e,result:null}}}function lu(e){return{success:!1,outputValues:{},logs:[],stdout:"",stderr:"",error:e,exitCode:1,durationMs:0}}async function is(e){let t=e.timeout??nu;if(!ou(e.scriptRuntime))return lu(`Unsupported runtime "${e.scriptRuntime}". Supported runtimes: ${ts.join(", ")}. Update the step's config.json "runtime" field (e.g. "python 3.13").`);let r=await su(e.scriptRuntime,e.onInstallProgress),o=Date.now(),n;try{n=await Qd(kt(tu(),"kizen-step-"));let i=kt(n,"wrapper.py"),s=kt(n,"script.py");await Promise.all([Qi(i,ru,"utf-8"),Qi(s,e.script,"utf-8")]);let l={...process.env,__KIZEN_INPUTS__:JSON.stringify(e.inputs),__KIZEN_SECRETS__:JSON.stringify(e.secrets),__KIZEN_SCRIPT_PATH__:s},{exitCode:a,stdout:p,stderr:h}=await new Promise(y=>{let v=[],P=[],C=!1,_=In(r,[i],{env:l,cwd:n,stdio:["ignore","pipe","pipe"]}),S=setTimeout(()=>{_.kill("SIGKILL")},t);_.stdout.on("data",f=>{v.push(f)}),_.stderr.on("data",f=>{P.push(f)});let g=f=>{C||(C=!0,clearTimeout(S),y({exitCode:f??1,stdout:Buffer.concat(v).toString("utf-8"),stderr:Buffer.concat(P).toString("utf-8")}))};_.on("close",g),_.on("error",f=>{C||(C=!0,clearTimeout(S),y({exitCode:1,stdout:"",stderr:f.message}))})}),w=Date.now()-o,{userStdout:x,result:d}=au(p);if(d){let y={success:d.success,outputValues:d.outputValues,logs:d.logs,stdout:x,stderr:h,exitCode:a,durationMs:w};return d.error!==null&&(y.error=d.error),d.httpRequests&&(y.httpRequests=d.httpRequests),y}let c={success:!1,outputValues:{},logs:[],stdout:x,stderr:h,exitCode:a,durationMs:w};return a!==0&&(c.error=`Process exited with code ${String(a)}`),c}finally{n&&Tn(n,{recursive:!0,force:!0})}}var ss=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".md",".mdx",".yml",".yaml",".toml",".css",".scss",".html",".htm",".xml",".svg",".txt",".sh",".py",".env",".gitignore",".prettierrc",".eslintrc"]);import{CRYPTO_ALG as ds,CRYPTO_VERSION as us,deserializeEnvelope as yu,encrypt as bu,serializeEnvelope as wu}from"@kizenapps/packager";function An(e){if(e.kind==="info")return e.message;let t=e.fromCache?" [cache]":"",r=e.upstreamStatus!==void 0?` (upstream ${String(e.upstreamStatus)})`:"";return`${e.method} ${e.url} \u2192 ${String(e.status)}${r}${t}`}var xu=2*1024*1024,vu=new Set([".DS_Store",".gitignore"]);function ku(e){if(e===void 0||e==="")return!1;let t=e.startsWith("[")?e.slice(0,e.indexOf("]")+1):e.split(":")[0]??"";return t==="localhost"||t==="127.0.0.1"||t==="[::1]"}var hs=["kizen.com","kizen.dev"];function Cu(e){let t;try{t=new URL(e)}catch{return!1}let r=t.hostname.toLowerCase();return r==="localhost"||r==="127.0.0.1"||r==="[::1]"?!0:t.protocol!=="https:"?!1:hs.some(o=>r===o||r.endsWith("."+o))}function Su(e){let t;try{t=new URL(e)}catch{return!1}if(t.protocol!=="https:")return!1;let r=t.hostname.toLowerCase();return hs.some(o=>r===o||r.endsWith("."+o))}function _u(e){if(e===void 0||e==="")return!1;try{let{hostname:t}=new URL(e);return t==="localhost"||t==="127.0.0.1"||t==="::1"}catch{return!1}}async function ps(e,t){if(t.includes("\0"))return null;let r=t.replace(/^\/+/,""),o=hu(e,r);if(o!==e&&!o.startsWith(e+Nn))return null;try{let n=await fu(o);return n!==e&&!n.startsWith(e+Nn)?null:n}catch{return null}}function fs(e,t){let r=t===e?"":t.slice(e.length+1);return r===""?!1:r.split(Nn).some(n=>Bt.has(n))}function gs(){let e=gu(import.meta.url);return St(mu(e),"viewer")}function ms(e,t,r){if(e.includes("/external-integrations/proxy")&&t["content-type"]?.includes("application/json"))try{let o=JSON.parse(r.toString("utf8"));return typeof o.status_code=="number"?o.status_code:void 0}catch{return}}async function Dn(e){try{return await du(e),!0}catch{return!1}}function ys(e,t,r,o,n,i,s,l,a,p,h){let w=qi({onChange:l,enabled:h}),x=cu(p);return(d,c)=>{(async()=>{let y=d.url??"/";try{if(!ku(d.headers.host)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Forbidden"}));return}if((d.method==="POST"||d.method==="PATCH"||d.method==="DELETE")&&!_u(d.headers.origin)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Forbidden"}));return}if(t(`Received request: ${y}`),y==="/api/credentials"){c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({credentials:o.current??null,activeProfile:n.current??null}));return}if(y==="/api/credential-profiles"&&d.method==="GET"){let f=await Ae();c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({profiles:f,active:n.current}));return}if(y==="/api/credentials/switch"&&d.method==="POST"){let f=[];for await(let k of d)f.push(k);let m=JSON.parse(Buffer.concat(f).toString("utf-8")||"{}").profile,b=m===void 0||m===""?await te():await ve(m);if(b!==null){o.current=b,n.current=m??void 0;let k=n.current,I=await he(i);await Ee(i,{...I,credentialMode:"global",...k!==void 0&&{activeCredentialProfile:k}}),s({type:"credentials-updated",credentials:b,activeProfile:n.current??null})}c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:b!==null}));return}if(y==="/api/bundle"){let f=St(process.cwd(),".kizenapp","bundle.json");try{let u=await pu(f,"utf-8");c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(u)}catch{c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end("{}")}return}if(y==="/api/last-path"&&d.method==="POST"){let f=[];for await(let m of d)f.push(m);let u=JSON.parse(Buffer.concat(f).toString("utf-8")||"{}");if(typeof u.path=="string"){a.current=u.path;let m=await he(i);await Ee(i,{...m,lastPath:u.path})}c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end('{"ok":true}');return}if(y==="/api/encryption-target"&&d.method==="GET"){let u=(await he(i)).encryptionTarget==="dev"?"dev":"prod";c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({target:u}));return}if(y==="/api/encryption-target"&&d.method==="POST"){let f=[];for await(let k of d)f.push(k);let u=JSON.parse(Buffer.concat(f).toString("utf-8")||"{}"),m=u!==null&&typeof u=="object"?u:{};if(m.target!=="dev"&&m.target!=="prod"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"target must be one of: 'dev', 'prod'"}));return}let b=await he(i);await Ee(i,{...b,encryptionTarget:m.target}),c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end('{"ok":true}');return}if(y==="/api/execute-step"&&d.method==="POST"){let f=[];for await(let k of d)f.push(k);let u=JSON.parse(Buffer.concat(f).toString("utf-8")||"{}");if(typeof u.script!="string"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing required field: script"}));return}let m={script:u.script,scriptRuntime:u.scriptRuntime??"python-3-13",inputs:Zi(u.inputs),secrets:u.secrets??{},onInstallProgress:k=>{k.kind==="start"?s({type:"venv-install-start"}):k.kind==="log"?s({type:"venv-install-log",line:k.line,stream:k.stream}):k.kind==="complete"?s({type:"venv-install-complete"}):s({type:"venv-install-error",message:k.message})}};typeof u.timeout=="number"&&u.timeout>0&&(m.timeout=Math.min(u.timeout,300*1e3));let b=await is(m);c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify(b));return}if(y==="/api/proxy-cache/clear"){w.clear(),r({kind:"info",message:"Proxy cache cleared"}),c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end('{"ok":true}');return}if(y==="/api/local/encrypt"&&d.method==="POST"){let f=[];for await(let m of d)f.push(m);let u=JSON.parse(Buffer.concat(f).toString("utf-8")||"{}");if(typeof u.publicKeyPem!="string"||typeof u.value!="string"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing required fields: publicKeyPem, value"}));return}try{let m=bu(u.value,u.publicKeyPem),b=wu(m);c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify(b))}catch(m){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:m.message}))}return}if(y==="/api/local/validate"&&d.method==="POST"){let f=[];for await(let E of d)f.push(E);let u=JSON.parse(Buffer.concat(f).toString("utf-8")||"{}");if(typeof u.value!="string"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing required field: value"}));return}let m,b;try{m=yu(u.value)}catch(E){b=E.message}if(b!==void 0||m===void 0){c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({valid:!1,checks:[],error:b}));return}let k=E=>Buffer.from(E,"base64").length,I=[{label:"Version",expected:String(us),actual:String(m.v),pass:m.v===us},{label:"Algorithm",expected:ds,actual:m.alg,pass:m.alg===ds},{label:"Wrapped key",expected:"384 bytes (RSA-3072)",actual:`${String(k(m.k))} bytes`,pass:k(m.k)===384},{label:"IV",expected:"12 bytes",actual:`${String(k(m.iv))} bytes`,pass:k(m.iv)===12},{label:"Auth tag",expected:"16 bytes",actual:`${String(k(m.tag))} bytes`,pass:k(m.tag)===16},{label:"Ciphertext",expected:"\u2265 1 byte",actual:`${String(k(m.ct))} bytes`,pass:k(m.ct)>=1}];c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({valid:I.every(E=>E.pass),checks:I}));return}if(y.startsWith("/api/wizard")){let f=d.headers["x-encryption-target"],m=(Array.isArray(f)?f[0]:f)==="dev"?"dev":"prod",b=xt(m);if(!Cu(b)){c.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:`Encryption API host is not allowed: ${b}`}));return}let k=y.slice(11)||"/",I=`${b}${k}`,E=d.method??"GET",R=[];for await(let je of d)R.push(je);let{host:M,"x-encryption-target":W,...ae}=d.headers,ue=R.length>0?Buffer.concat(R):void 0,pe=ue&&ue.length>0?ue:void 0,le;try{le=await fetch(I,{method:E,headers:ae,...pe!==void 0&&{body:pe}})}catch(je){let xr=je.code==="ECONNREFUSED"?`plugin-wizard is not running at ${b}`:`Failed to reach plugin-wizard: ${je.message}`;c.writeHead(503,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:xr}));return}let se=lr(Object.fromEntries(le.headers)),it=await le.arrayBuffer();c.writeHead(le.status,se),c.end(Buffer.from(it));return}if(y.startsWith("/api/proxy")){let f=d.headers["x-proxy-target"];if(typeof f!="string"){c.writeHead(400),c.end("Missing x-proxy-target header");return}if(!Su(f)){c.writeHead(400),c.end("Invalid proxy target");return}let u=y.slice(10)||"/",m=`${f}${u}`,b=d.method??"GET",k=[];for await(let le of d)k.push(le);let{host:I,"x-proxy-target":E,...R}=d.headers;if(b==="GET"){let le=`GET::${m}`,{response:se,fromCache:it}=await w.get(le,()=>fetch(m,{method:"GET",headers:R}));r({kind:"request",method:"GET",status:se.status,upstreamStatus:ms(u,se.headers,se.body),fromCache:it,url:u}),c.writeHead(se.status,se.headers),c.end(se.body);return}let M=k.length>0?Buffer.concat(k):void 0,W=M&&M.length>0?M:void 0,ae=await fetch(m,{method:b,headers:R,...W!==void 0&&{body:W}}),ue=lr(Object.fromEntries(ae.headers)),pe=await Cn(ae,kn);r({kind:"request",method:b,status:ae.status,upstreamStatus:ms(u,ue,pe),fromCache:!1,url:u}),c.writeHead(ae.status,ue),c.end(pe);return}if(y.startsWith("/api/source/tree")&&d.method==="GET"){let u=new URL(y,"http://localhost").searchParams.get("path")??"",m=await ps(x,u);if(m===null){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Invalid path"}));return}if(fs(x,m)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Path is excluded"}));return}try{let b=await uu(m,{withFileTypes:!0}),k=await Promise.all(b.filter(E=>E.isDirectory()?!Bt.has(E.name):E.isFile()?!vu.has(E.name):!1).map(async E=>{let R=E.isDirectory()?"dir":"file",M={name:E.name,kind:R};if(R==="file")try{let W=await ls(St(m,E.name));M.size=W.size}catch{}return M}));k.sort((E,R)=>E.kind!==R.kind?E.kind==="dir"?-1:1:E.name.localeCompare(R.name));let I=m===x?"":m.slice(x.length+1);c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({path:I,entries:k}))}catch(b){let k=b instanceof Error?b.message:String(b);t(`source/tree failed (${u}): ${k}`),c.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Not found"}))}return}if(y.startsWith("/api/source/file")&&d.method==="GET"){let u=new URL(y,"http://localhost").searchParams.get("path")??"";if(u===""){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing path"}));return}let m=await ps(x,u);if(m===null){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Invalid path"}));return}if(fs(x,m)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Path is excluded"}));return}try{let b=await ls(m);if(!b.isFile()){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Not a file"}));return}if(b.size>xu){c.writeHead(413,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"File too large",size:b.size}));return}let k=cs(m).toLowerCase(),I=Sn[k],E=ss.has(k),R=I??(E?"text/plain; charset=utf-8":"application/octet-stream");c.writeHead(200,{"Content-Type":R,"Content-Length":String(b.size)}),as(m).pipe(c)}catch(b){let k=b instanceof Error?b.message:String(b);t(`source/file failed (${u}): ${k}`),c.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Not found"}))}return}let C=St(e,y==="/"?"/index.html":y),_=await Dn(C)?C:St(e,"index.html"),S=cs(_),g=Sn[S]??"application/octet-stream";c.writeHead(200,{"Content-Type":g}),as(_).pipe(c)}catch(v){if(y.startsWith("/api/proxy")){let P=y.slice(10)||"/",C=d.method??"GET";v instanceof wt?r({kind:"info",message:`${C} ${P} \u2192 502 (response exceeded ${String(v.limit)} bytes)`}):r({kind:"request",method:C,status:502,fromCache:!1,url:P})}else r({kind:"info",message:`Error handling ${y}: ${v instanceof Error?v.message:String(v)}`});c.headersSent||(c.writeHead(502),c.end("Bad Gateway"))}})()}}import{launch as Pu}from"chrome-launcher";import{createRequire as Eu}from"module";import{mkdirSync as Tu}from"fs";import{readFile as Iu}from"fs/promises";import{dirname as Au,join as Nu}from"path";import{fileURLToPath as Du}from"url";var bs=Eu(import.meta.url)("chrome-remote-interface"),Ru=new Set(["content-security-policy","content-security-policy-report-only","x-frame-options"]),xs=["kizen.dev","kizen.com"];function ws(e){try{let{hostname:t}=new URL(e);return xs.some(r=>t===r||t.endsWith("."+r))}catch{return!1}}async function Bu(e){try{let t=Nu(Au(Du(import.meta.url)),"viewer","icon.png"),r=await Iu(t);await e.Browser.setDockTile({image:r.toString("base64")})}catch{}}async function vs(e){Tu(e.userDataDir,{recursive:!0});let t=`http://localhost:${String(e.port)}`,r=await Pu({startingUrl:t,chromeFlags:[`--app=${t}`,"--no-first-run","--no-default-browser-check","--disable-extensions","--disable-features=DeviceBoundSessions"],userDataDir:e.userDataDir,handleSIGINT:!1,logLevel:"silent"}),o=r.port,n=null,i=null,s=null,l=null,a=0,p=!1,h=_=>{e.debugLog?.(_)},w=_=>{e.verbose&&e.debugLog?.(_)},x=()=>{i!==null&&(clearInterval(i),i=null)},d=_=>_==="chrome://new-tab-page/"||_==="chrome://newtab/",c=async _=>{x(),s=null,await Bu(_),await _.Fetch.enable({patterns:xs.flatMap(g=>[{urlPattern:`*://${g}/*`,requestStage:"Response"},{urlPattern:`*://*.${g}/*`,requestStage:"Response"}])}),_.on("Fetch.requestPaused",g=>{(async()=>{let f=g,{requestId:u,request:m,responseHeaders:b,responseStatusCode:k}=f,I=m.url;w(`Fetch ${String(k??"?")} ${I}`);let E=[],R=!1;for(let M of b??[])if(!Ru.has(M.name.toLowerCase()))if(M.name.toLowerCase()==="set-cookie"&&ws(I)){let W=M.value.split(`
1100
- `).map(ae=>/SameSite=/i.test(ae)?ae.replace(/SameSite=\w+/i,"SameSite=None"):ae+"; SameSite=None; Secure").join(`
1101
- `);E.push({name:M.name,value:W}),R=!0}else E.push(M);R&&w(` rewrote Set-Cookie SameSite on ${I}`);try{let{body:M,base64Encoded:W}=await _.Fetch.getResponseBody({requestId:u});await _.Fetch.fulfillRequest({requestId:u,responseCode:k??200,responseHeaders:E,body:M,...W&&{base64Encoded:!0}})}catch(M){w(` fulfillRequest failed (${M.message}), falling through`);try{await _.Fetch.continueRequest({requestId:u})}catch(W){w(` continueRequest also failed (${W.message})`)}}})()}),i=setInterval(()=>{(async()=>{try{let{cookies:g}=await _.Network.getAllCookies(),f=0;for(let u of g){if(!ws(`https://${u.domain??""}`)||u.sameSite==="None")continue;let m=u.domain??"",b=m.startsWith(".")?m.slice(1):m;await _.Network.setCookie({url:`https://${b}`,name:u.name,value:u.value,domain:m,path:u.path??"/",secure:!0,...u.httpOnly!==void 0&&{httpOnly:u.httpOnly},...u.expirationDate!==void 0&&{expires:u.expirationDate},sameSite:"None"}),f++}f>0&&w(`cookie-patch cycle: updated ${String(f)} cookie(s) to SameSite=None`)}catch(g){w(`cookie-patch cycle failed: ${g.message}`)}})()},2e3),await _.Target.setDiscoverTargets({discover:!0});let S=g=>{if(g.type==="page"){if(d(g.url)){h(`closed stray new-tab-page target ${g.targetId}`),s!==null&&_.Target.activateTarget({targetId:s}).catch(()=>{}),_.Target.closeTarget({targetId:g.targetId}).catch(()=>{});return}s===null&&(s=g.targetId,h(`captured main target ${g.targetId} (${g.url})`))}};_.on("Target.targetCreated",g=>{let{targetInfo:f}=g;w(`target +${f.type} ${f.targetId} ${f.url}`),S(f)}),_.on("Target.targetInfoChanged",g=>{let{targetInfo:f}=g;w(`target ~${f.type} ${f.targetId} ${f.url}`),S(f)}),_.on("disconnect",()=>{p||n!==_||(h("CDP disconnected"),n=null,x(),y())})},y=()=>{if(p||l!==null)return;let _=Math.min(1e3*2**a,1e4),S=a+1;a++,h(`CDP reconnect scheduled in ${String(_)}ms (attempt #${String(S)})`),l=setTimeout(()=>{l=null,(async()=>{let g;try{g=await bs({port:o})}catch{h(`CDP reconnect attempt #${String(S)} failed`),y();return}if(p){try{await g.close()}catch{}return}n=g;try{await c(g),h(`CDP reconnected after ${String(S)} attempt(s)`),a=0}catch{n===g&&(n=null);try{await g.close()}catch{}h(`CDP reconnect attempt #${String(S)} failed during setup`),y()}})()},_)},v=30,P=300;for(let _=0;_<v;_++){await new Promise(S=>setTimeout(S,P)),h(`CDP connect attempt ${String(_+1)}/${String(v)}`);try{let S=await bs({port:o});n=S,e.path&&S.send("Page.navigate",{url:`${t}${e.path}`});try{await c(S),h("CDP connected")}catch{n===S&&(n=null);try{await S.close()}catch{}h("CDP setup failed after connect")}break}catch{if(_===v-1){let S=`CDP unavailable after ${String(v)} attempts \u2014 viewer running without interception (CSP/cookie rewrites disabled)`;return h(S),e.onWarn?.(S),{kill:()=>{r.kill()}}}}}return n?{kill:()=>{if(h("CDP kill requested \u2014 tearing down"),p=!0,l!==null&&(clearTimeout(l),l=null),x(),n!==null){try{n.close()}catch{}n=null}r.kill()}}:{kill:()=>{r.kill()}}}import{useCallback as $u,useState as ju}from"react";function dr(e){let[t,r]=ju([]),o=$u(n=>{r(i=>[...i,`${new Date().toLocaleTimeString()}: ${n}`].slice(-e))},[e]);return[t,o]}import{useCallback as ur,useEffect as Rn,useState as pr}from"react";import{Box as Y,Text as z,useInput as Fu}from"ink";import{jsx as D,jsxs as Z}from"react/jsx-runtime";var Be=["apiKey","userId","businessId"],Ou={apiKey:"API Key",userId:"User ID",businessId:"Business ID"},_t=({text:e})=>D(z,{dimColor:!0,children:e}),fr=({initialMode:e,showProfileManager:t,onComplete:r,onCancel:o})=>{let[n,i]=pr(e==="global"&&!t?{type:"loading"}:e==="global"&&t?{type:"profile-loading"}:{type:"mode-select",cursor:0}),[s,l]=pr(""),[a,p]=pr(null),[h,w]=pr(void 0),x=ur(g=>{if(g==="local"){r({mode:"local",credentials:null});return}i({type:"profile-loading"})},[r]),d=ur(async g=>{if(g===null){i({type:"profile-loading"});let m=await Ae();i({type:"name-entry",nameBuffer:"",existingProfiles:m});return}w(g.isDefault?void 0:g.name),i({type:"loading"});let f=g.isDefault?await te():await ve(g.name),u=f?Math.max(0,X.indexOf(f.environment)):0;i({type:"creds-entry",field:0,values:f??{},envCursor:u})},[]),c=ur(async(g,f)=>{let u=X[f]??"go",m={...at(g),environment:u};i({type:"saving"});try{await lo(h??Te,m),r({mode:"global",credentials:m,...h!==void 0&&{profileName:h}})}catch(b){p(b instanceof Error?b.message:String(b)),i({type:"creds-entry",field:0,values:g,envCursor:f})}},[r,h]);Fu((g,f)=>{if(f.ctrl&&g==="c"&&process.exit(0),f.escape){if((n.type==="creds-entry"||n.type==="name-entry")&&e==="global"&&t){i({type:"profile-loading"}),y();return}o?.();return}if(n.type==="mode-select"){if(f.upArrow)i({type:"mode-select",cursor:0});else if(f.downArrow)i({type:"mode-select",cursor:1});else if(f.return){let u=n.cursor===0?"global":"local";x(u)}return}if(n.type==="profile-select"){let{profiles:u,cursor:m}=n,b=u.length,k=u.length+1;if(f.upArrow)i({...n,cursor:(m-1+k)%k});else if(f.downArrow)i({...n,cursor:(m+1)%k});else if(f.return)if(m===b)i({type:"name-entry",nameBuffer:"",existingProfiles:u});else{let I=u[m]??u[0];I&&d(I)}return}if(n.type==="name-entry"){let{nameBuffer:u,existingProfiles:m}=n;if(f.backspace||f.delete)i({type:"name-entry",nameBuffer:u.slice(0,-1),existingProfiles:m});else if(f.return){let b=u.trim();if(!b){i({...n,nameError:"Name cannot be empty"});return}if(b===Te){i({...n,nameError:`"${Te}" is reserved`});return}if(m.some(k=>k.name===b)){i({...n,nameError:`Profile "${b}" already exists`});return}w(b),i({type:"creds-entry",field:0,values:{},envCursor:0}),l("")}else g&&!f.ctrl&&!f.meta&&i({type:"name-entry",nameBuffer:u+g,existingProfiles:m});return}if(n.type==="creds-entry"){let{field:u,values:m,envCursor:b}=n;if(u===Be.length){f.leftArrow?i({...n,envCursor:(b-1+X.length)%X.length}):f.rightArrow?i({...n,envCursor:(b+1)%X.length}):f.upArrow?(i({...n,field:u-1}),l(m[Be[u-1]]??"")):f.return&&c(m,b);return}let I=Be[u];if(!I)return;if(f.backspace||f.delete)l(E=>E.slice(0,-1));else if(f.upArrow&&u>0){let E={...m,[I]:s},R=u-1;i({...n,field:R,values:E}),l(E[Be[R]]??"")}else if(f.return||f.tab||f.downArrow){let E={...m,[I]:s},R=u+1;i({...n,field:R,values:E}),R<Be.length?l(E[Be[R]]??""):l("")}else g&&!f.ctrl&&!f.meta&&l(E=>E+g)}});let y=ur(async()=>{let g=await Ae();i({type:"profile-select",profiles:g,cursor:0})},[]);if(Rn(()=>{n.type==="profile-loading"&&y()},[n.type,y]),Rn(()=>{e==="global"&&!t&&(async()=>{let g=await te(),f=g?Math.max(0,X.indexOf(g.environment)):0;i({type:"creds-entry",field:0,values:g??{},envCursor:f})})()},[]),Rn(()=>{n.type==="creds-entry"&&n.field===0&&l(n.values.apiKey??"")},[n.type]),n.type==="loading"||n.type==="saving"||n.type==="profile-loading"){let g=n.type==="saving"?"Saving credentials\u2026":n.type==="profile-loading"?"Loading profiles\u2026":"Loading credentials\u2026";return D(Y,{paddingY:1,paddingX:2,children:D(z,{dimColor:!0,children:g})})}if(n.type==="done")return null;if(n.type==="mode-select")return Z(Y,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[D(K,{}),Z(Y,{flexDirection:"column",gap:0,children:[D(z,{bold:!0,children:"Credential mode"}),[{label:"Global",desc:"~/.kizenappbuilder/credentials.json",mode:"global"},{label:"Local",desc:"Enter credentials in the browser dev tools",mode:"local"}].map((f,u)=>{let m=n.cursor===u;return Z(Y,{gap:2,children:[D(z,{...m&&{color:"cyan"},children:m?"\u276F":" "}),D(z,{bold:m,...m&&{color:"cyan"},children:f.label}),D(z,{dimColor:!0,children:f.desc})]},f.mode)})]}),D(_t,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit"})]});if(n.type==="profile-select"){let{profiles:g,cursor:f}=n,u=g.length;return Z(Y,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[D(K,{}),Z(Y,{flexDirection:"column",gap:0,children:[D(z,{bold:!0,children:"Credential profiles"}),g.map((m,b)=>{let k=f===b;return Z(Y,{gap:2,children:[D(z,{...k&&{color:"cyan"},children:k?"\u276F":" "}),D(z,{bold:k,...k&&{color:"cyan"},children:m.isDefault?"Default":m.name}),D(z,{dimColor:!0,children:m.path})]},m.name)}),Z(Y,{gap:2,children:[D(z,{...f===u&&{color:"cyan"},children:f===u?"\u276F":" "}),D(z,{bold:f===u,...f===u&&{color:"cyan"},children:"+ Add new profile"})]})]}),D(_t,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(n.type==="name-entry"){let{nameBuffer:g,nameError:f}=n;return Z(Y,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[D(K,{}),D(Y,{flexDirection:"column",gap:0,children:D(z,{bold:!0,children:"New credential profile"})}),f&&Z(z,{color:"red",children:["Error: ",f]}),Z(Y,{gap:2,children:[D(Y,{width:12,children:D(z,{bold:!0,color:"cyan",children:"Profile name"})}),D(z,{color:"cyan",children:">"}),D(z,{children:g}),D(z,{color:"cyan",children:"\u2588"})]}),D(_t,{text:"Type a name \xB7 Enter to confirm \xB7 Esc to go back"})]})}let{field:v,values:P,envCursor:C}=n,_=v===Be.length,S=h??Te;return Z(Y,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[D(K,{}),Z(Y,{flexDirection:"column",gap:0,children:[D(z,{bold:!0,children:h!==void 0?`Profile: ${h}`:"Global credentials"}),Z(z,{dimColor:!0,children:["Saved to:"," ",h!==void 0?`~/.kizenappbuilder/${h}.json`:xe]})]}),a&&Z(z,{color:"red",children:["Error: ",a]}),Z(Y,{flexDirection:"column",gap:0,children:[Be.map((g,f)=>{let u=v===f,m=u?s:P[g]??"";return Z(Y,{gap:2,children:[D(Y,{width:12,children:D(z,{bold:u,...u&&{color:"cyan"},children:Ou[g]})}),D(z,{...u&&{color:"cyan"},children:">"}),D(z,{children:m}),u&&D(z,{color:"cyan",children:"\u2588"})]},g)}),Z(Y,{gap:2,children:[D(Y,{width:12,children:D(z,{bold:_,..._&&{color:"cyan"},children:"Environment"})}),D(z,{..._&&{color:"cyan"},children:">"}),D(Y,{gap:1,children:X.map((g,f)=>{let u=f===C;return _?D(z,{bold:u,...u&&{color:"cyan"},children:u?`[${g}]`:g},g):D(z,{dimColor:!u,bold:u,children:g},g)})})]})]}),_?D(_t,{text:"\u2190\u2192 to select \xB7 \u2191\u2193 to move \xB7 Enter to save \xB7 Esc to cancel"}):D(_t,{text:"\u2191\u2193 to move \xB7 Backspace to delete \xB7 Esc to cancel"}),Z(z,{dimColor:!0,children:["Profile: ",S]})]})};import{jsx as F,jsxs as G}from"react/jsx-runtime";var Wu=[".kizenapp",".git"],Cs=({port:e,pluginDir:t,outputDir:r,credentials:o,credentialMode:n,activeCredentialProfile:i,lastPath:s,debug:l=!1,verbose:a=!1,autoViewer:p=!0,cacheEnabled:h=!0})=>{let w=Ce(o),x=Ce(i),d=Ce(s),[c,y]=ie("main"),[v,P]=ie(n),[C,_]=ie("starting"),[S,g]=ie(null),[f,u]=dr(50),[m,b]=dr(50),[k,I]=dr(50),[E,R]=ie([]),[M,W]=ie("pending"),[ae,ue]=ie(null),[pe,le]=ie(null),[se,it]=ie(null),[je,xr]=ie(null),[vr,kr]=ie(0),[Fn,aa]=ie(0),[la,ca]=ie(0),[Cr,da]=ie(0),Sr=Ce(!1),On=Ce(!1),Et=Ce(null),st=Ce(null),Se=Ce(new Set),Tt=Ce([]),ce=ot(T=>{let U=JSON.stringify(T);if(Se.current.size===0){Tt.current=[...Tt.current,U].slice(-50);return}let _e=!1;for(let fe of Se.current)if(fe.readyState===1)try{fe.send(U)}catch{Se.current.delete(fe),_e=!0}_e&&kr(Se.current.size)},[]),It=u,_r=ot(()=>{Et.current?.kill(),Et.current=null,vs({port:e,userDataDir:ks(r,".chrome"),broadcast:ce,onWarn:T=>{u(`\u26A0 ${T}`)},...d.current!==void 0&&{path:d.current},...l&&{debugLog:I},...a&&{verbose:!0}}).then(T=>{Et.current=T})},[e,r,ce,l,a,I,u]);mr(()=>{let T=()=>{Et.current?.kill()};return process.on("exit",T),()=>{process.off("exit",T)}},[]),Mu((T,U)=>{(T==="q"||U.ctrl&&T==="c")&&process.exit(0),T==="c"&&y("editing"),T==="v"&&_r()},{isActive:c==="main"}),mr(()=>{C!=="running"||On.current||!p||(On.current=!0,_r())},[C,p,_r]);let ua=ot(T=>{w.current=T.credentials,x.current=T.profileName,y("main"),P(T.mode);let U=T.profileName;Ee(r,{credentialMode:T.mode,...U!==void 0&&{activeCredentialProfile:U}}),ce({type:"credentials-updated",credentials:T.credentials})},[r,ce]),zn=ot(T=>{R(U=>[...U,`${new Date().toLocaleTimeString()}: ${An(T)}`].slice(-50)),T.kind==="request"&&(T.fromCache?ca(U=>U+1):da(U=>U+1)),ce({type:"proxy-log",entry:T})},[ce]),Fe=ot(T=>{b(T),ce({type:"log",message:T})},[b,ce]),Pr=ot(()=>{Sr.current||(Sr.current=!0,W("building"),ue(null),le(null),Fe("Build started"),jt(t,r).then(({bundleSize:T})=>{Fe(`Build finished \u2014 bundle: ${mt(T)}`),W("done"),it(new Date),xr(T),Fe("Notifying viewers to reload"),ce({type:"rebuild"})}).catch(T=>{T instanceof Lu?(le(T.issues),ue(null)):(ue(T instanceof Error?T.message:String(T)),le(null)),W("error")}).finally(()=>{Sr.current=!1}))},[t,r,Fe,ce]);if(mr(()=>{Pr();let T=Hu(t,{recursive:!0},(U,_e)=>{if(!_e)return;let fe=_e.replace(/\\/g,"/");Wu.some(At=>fe.startsWith(At))||(Fe(`File change detected: ${_e}`),st.current!==null&&clearTimeout(st.current),st.current=setTimeout(Pr,150))});return T.on("error",U=>{Fe(`File watcher error \u2014 rebuilds have stopped: ${U.message}`)}),()=>{T.close(),st.current!==null&&clearTimeout(st.current)}},[t,Pr,Fe]),mr(()=>{let T=gs();Dn(ks(T,"index.html")).then(U=>{if(!U){g("Viewer not built. Run 'pnpm build:viewer' first."),_("error");return}let _e=ys(T,It,zn,w,x,r,ce,aa,d,t,h),fe=Uu(_e),At=new Vu({server:fe});return At.on("connection",Oe=>{for(let fa of Tt.current)Oe.readyState===1&&Oe.send(fa);Tt.current=[],It("Viewer connected for live reload"),Se.current.add(Oe),kr(Se.current.size),Oe.on("close",()=>{Se.current.delete(Oe),kr(Se.current.size)})}),fe.listen(e,"127.0.0.1",()=>{_("running"),It(`Server started on 127.0.0.1:${String(e)}`)}),fe.on("error",Oe=>{g(Oe.message),_("error")}),()=>{At.close(),fe.close()}})},[e,It,zn,ce,r,t,h]),c==="editing")return F(fr,{...v!==void 0&&{initialMode:v},showProfileManager:v==="global",onComplete:ua,onCancel:()=>{y("main")}});let Er=se!==null?Math.round((Date.now()-se.getTime())/1e3):null,pa=Er===null?"":Er<5?" (just now)":` (${String(Er)}s ago)`,Mn=pe?.filter(T=>T.severity==="error").length??0;return G(Q,{flexDirection:"column",paddingY:1,paddingX:2,children:[F(K,{marginBottom:1}),G(Q,{flexDirection:"column",children:[G(Q,{gap:2,marginBottom:0,children:[F(j,{bold:!0,dimColor:!0,children:"App Build"}),M==="pending"&&F(j,{dimColor:!0,children:"waiting\u2026"}),M==="building"&&G(Q,{gap:1,children:[F(ye,{}),F(j,{children:"Building plugin package\u2026"})]}),M==="done"&&G(Q,{gap:1,children:[F(j,{color:"green",children:"\u2713 Built"}),se!==null&&G(j,{dimColor:!0,children:["at ",se.toLocaleTimeString(),pa]}),je!==null&&G(j,{dimColor:!0,children:["\xB7 bundle: ",mt(je)]})]}),M==="error"&&(pe!==null?G(j,{color:"red",children:["\u2717 ",Mn," validation ",Mn===1?"error":"errors"]}):G(j,{color:"red",children:["\u2717 ",ae??"unknown error"]}))]}),pe!==null&&F(Xt,{issues:pe}),F(Q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:m.slice(-8).map((T,U)=>F(j,{dimColor:!0,wrap:"truncate",children:T},U))})]}),G(Q,{marginTop:1,flexDirection:"column",children:[G(Q,{gap:2,children:[F(j,{bold:!0,dimColor:!0,children:"Web Server"}),C==="starting"&&F(j,{dimColor:!0,children:"starting\u2026"}),C==="running"&&G(Q,{gap:2,children:[F(j,{color:"green",children:"\u2713 Running"}),G(j,{dimColor:!0,children:["\u25CF ",vr," viewer",vr!==1?"s":""]})]}),C==="error"&&G(j,{color:"red",children:["\u2717 ",S??"Server error"]})]}),F(Q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:f.slice(-8).map((T,U)=>F(j,{dimColor:!0,wrap:"truncate",children:T},U))})]}),G(Q,{marginTop:1,flexDirection:"column",children:[G(Q,{gap:2,children:[F(j,{bold:!0,dimColor:!0,children:"Network Proxy"}),F(j,{color:"green",children:"\u2713 Active"}),F(j,{dimColor:!0,children:h?`\u25CF ${String(Fn)} key${Fn!==1?"s":""} in the network cache \xB7 ${String(la)} hit \xB7 ${String(Cr)} miss`:`\u25CF network cache disabled \xB7 ${String(Cr)} request${Cr!==1?"s":""}`})]}),F(Q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:E.length===0?F(j,{dimColor:!0,children:" No requests yet."}):E.slice(-8).map((T,U)=>F(j,{dimColor:!0,wrap:"truncate",children:T},U))})]}),l&&G(Q,{marginTop:1,flexDirection:"column",children:[G(Q,{gap:2,children:[F(j,{bold:!0,dimColor:!0,children:"CDP Debug"}),G(j,{dimColor:!0,children:["\u25CF ",k.length," event(s)"]})]}),F(Q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:k.length===0?F(j,{dimColor:!0,children:" No CDP events yet."}):k.slice(-8).map((T,U)=>F(j,{dimColor:!0,wrap:"truncate",children:T},U))})]}),G(Q,{marginTop:1,gap:1,children:[F(j,{dimColor:!0,children:"["}),F(j,{children:"q"}),F(j,{dimColor:!0,children:"] quit ["}),F(j,{children:"c"}),F(j,{dimColor:!0,children:"] credentials ["}),F(j,{children:"v"}),G(j,{dimColor:!0,children:["] ",vr===0?"open viewer":"relaunch viewer"]})]})]})};async function Ss(e){return new Promise(t=>{let r=null,o=i=>{r?.(),t(i)},{unmount:n}=Ps(_s(fr,{...e!==void 0&&{initialMode:e},onComplete:o}),{exitOnCtrlC:!1});r=n})}function Es(e){e.command("dev").description("Start the plugin viewer dev server").option("-p, --port <port>","port to listen on","3121").option("-c, --credentials <path>","path to a credentials JSON file").option("-d, --debug","Enable CDP lifecycle logs in the TUI").option("-v, --verbose","Log every CDP event and handled error (implies --debug)").option("--no-viewer","Don't auto-launch the viewer on startup (press [v] to launch manually)").option("--no-cache","Disable the network proxy cache (always fetch upstream)").action(async t=>{let r=parseInt(t.port,10),o=process.cwd(),n=`${o}/.kizenapp`;Ne(o);let i=null,s,l,{lastPath:a}=await he(n);if(t.credentials)i=await Ie(t.credentials);else{let c=await he(n);if(c.credentialMode==="local")s="local";else if(c.credentialMode==="global"){if(s="global",l=c.activeCredentialProfile,l?(i=await ve(l),i||(i=await te(),l=void 0)):i=await te(),!i){let y=await Ss("global");i=y.credentials,l=y.profileName}}else{let y=await Ss();i=y.credentials,s=y.mode,l=y.profileName;let v=y.profileName;await Ee(n,{credentialMode:y.mode,...v!==void 0&&{activeCredentialProfile:v}})}}let p=t.verbose===!0,h=p||t.debug===!0,w=t.viewer!==!1,x=t.cache!==!1,{waitUntilExit:d}=Ps(_s(Cs,{port:r,pluginDir:o,outputDir:n,credentials:i,...s!==void 0&&{credentialMode:s},...l!==void 0&&{activeCredentialProfile:l},...a!==void 0&&{lastPath:a},debug:h,verbose:p,autoViewer:w,cacheEnabled:x}),{exitOnCtrlC:!1});await d()})}import{createElement as op}from"react";import{render as ip}from"ink";import{useCallback as gr,useEffect as Ns,useState as yr}from"react";import{Box as be,Text as H,useApp as Zu,useInput as Qu}from"ink";import{encrypt as Ku,serializeEnvelope as Ju}from"@kizenapps/packager";function qu(e){let t=e;return t.code==="ECONNREFUSED"||t.cause?.code==="ECONNREFUSED"}function Ts(e){return{"Content-Type":"application/json","x-api-key":e.apiKey,"x-user-id":e.userId,"x-business-id":e.businessId,"x-auth-environment":e.environment}}async function Is(e,t,r){try{return await fetch(`${e}${t}`,r)}catch(o){throw new Error(qu(o)?`Plugin-wizard is not running at ${e}`:`Failed to reach plugin-wizard: ${o.message}${Dt()==="unavailable"?` ${Nt}`:""}`)}}async function As(e){let t=await e.text();if(t==="")return{};try{let r=JSON.parse(t);return typeof r=="object"&&r!==null?r:{}}catch{return{}}}async function Gu(e,t,r){let o=xt(e.stage),n=await Is(o,"/get-public-key",{method:"POST",headers:Ts(t),body:JSON.stringify({api_name:r})}),i=await As(n);if(!n.ok||typeof i.public_key!="string"){let s=typeof i.error=="string"?i.error:`HTTP ${String(n.status)}`;throw new Error(`Failed to fetch public key: ${s}`)}return i.public_key}function Yu(e,t){let r=Ku(t,e),o=Ju(r);if(typeof o.value!="string")throw new Error("Unexpected serialized envelope shape from @kizenapps/packager");return o.value}async function Xu(e,t,r,o){let n=xt(e.stage),i=await Is(n,"/encrypt",{method:"POST",headers:Ts(t),body:JSON.stringify({api_name:r,value:o})}),s=await As(i);if(!i.ok||s.encrypted!==!0||typeof s.value!="string"){let l=typeof s.error=="string"?s.error:`HTTP ${String(i.status)}`;throw new Error(`Encryption via plugin-wizard failed: ${l}`)}return s.value}async function hr(e,t,r,o,n){if(e.isRemote)return n?.("encrypting"),Xu(e,t,r,o);n?.("fetching-key");let i=await Gu(e,t,r);return n?.("encrypting"),Yu(i,o)}import{Fragment as br,jsx as O,jsxs as ee}from"react/jsx-runtime";var Bn=({text:e})=>O(H,{dimColor:!0,children:e}),Ds=({ctx:e,stageDefaulted:t,credentialsPath:r,initialApiName:o,defaultApiName:n,initialValue:i,onDone:s})=>{let l=Zu(),[a,p]=yr({type:"loading"}),[h,w]=yr(null),[x,d]=yr(o??""),[c,y]=yr(i??""),v=gr(u=>{process.exitCode=1,p({type:"error",message:u instanceof Error?u.message:String(u)})},[]),P=gr(async(u,m,b)=>{try{let k=await hr(e,u,m,b,R=>{p(R==="fetching-key"?{type:"fetching-key"}:{type:"encrypting"})}),I=JSON.stringify({encrypted:!0,value:k},null,2);s?.(k);let E=await Rt(I);p({type:"done",envelope:I,copied:E})}catch(k){v(k)}},[e,v,s]),C=gr((u,m,b)=>{if(m.trim()===""){p({type:"api-name-entry",buffer:n??""});return}if(b===""){p({type:"value-entry",buffer:""});return}P(u,m.trim(),b)},[n,P]),_=gr(async u=>{try{p({type:"loading"});let m=u.isDefault?await te():await ve(u.name);if(m===null)throw new Error(`Could not load credential profile: ${u.name}`);w(m),C(m,x,c)}catch(m){v(m)}},[x,c,C,v]);Ns(()=>{(async()=>{try{if(r!==void 0){let b;try{b=await Ie(r)}catch(k){throw new Error(`Could not load credentials from ${r}: ${k instanceof Error?k.message:String(k)}`)}w(b),C(b,x,c);return}let u=await Ae();if(u.length===0)throw new Error("No credential profiles found. Create ~/.kizenappbuilder/credentials.json first.");let[m]=u;if(u.length===1&&m!==void 0){let b=m.isDefault?await te():await ve(m.name);if(b===null)throw new Error(`Could not load credential profile: ${m.name}`);w(b),C(b,x,c);return}p({type:"profile-select",profiles:u,cursor:0})}catch(u){v(u)}})()},[]),Ns(()=>{(a.type==="done"||a.type==="error")&&l.exit()},[a,l]),Qu((u,m)=>{if(m.ctrl&&u==="c"){process.exitCode=130,l.exit();return}if(a.type==="profile-select"){let{profiles:b,cursor:k}=a;if(m.upArrow)p({...a,cursor:(k-1+b.length)%b.length});else if(m.downArrow)p({...a,cursor:(k+1)%b.length});else if(m.return){let I=b[k];I!==void 0&&_(I)}return}if(a.type==="api-name-entry"){let{buffer:b}=a;if(m.backspace||m.delete)p({type:"api-name-entry",buffer:b.slice(0,-1)});else if(m.return){let k=b.trim();if(k===""){p({type:"api-name-entry",buffer:b,error:"api_name cannot be empty"});return}d(k),h?C(h,k,c):v(new Error("Internal error: credentials were not loaded"))}else u&&!m.ctrl&&!m.meta&&p({type:"api-name-entry",buffer:b+u});return}if(a.type==="value-entry"){let{buffer:b}=a;if(m.backspace||m.delete)p({type:"value-entry",buffer:b.slice(0,-1)});else if(m.return){if(b===""){p({type:"value-entry",buffer:b,error:"secret cannot be empty"});return}y(b),h?C(h,x,b):v(new Error("Internal error: credentials were not loaded"))}else u&&!m.ctrl&&!m.meta&&p({type:"value-entry",buffer:b+u});return}});let S=e.isRemote?"remote Kizen API":"on-machine",g=t===!0?`${e.stage} (default)`:e.stage,f=u=>ee(be,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[O(K,{}),ee(H,{dimColor:!0,children:["Mode: ",S," Stage: ",g]}),u]});if(a.type==="loading")return f(ee(be,{gap:1,children:[O(ye,{}),O(H,{dimColor:!0,children:"Loading credentials\u2026"})]}));if(a.type==="profile-select"){let{profiles:u,cursor:m}=a;return f(ee(br,{children:[O(H,{bold:!0,children:"Credential profile"}),O(be,{flexDirection:"column",children:u.map((b,k)=>{let I=m===k;return ee(be,{gap:2,children:[O(H,{...I&&{color:"cyan"},children:I?"\u276F":" "}),O(H,{bold:I,...I&&{color:"cyan"},children:b.isDefault?"Default":b.name}),O(H,{dimColor:!0,children:b.path})]},b.name)})}),O(Bn,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit"})]}))}return a.type==="api-name-entry"?f(ee(br,{children:[O(H,{bold:!0,children:"Plugin api_name"}),a.error!==void 0&&ee(H,{color:"red",children:["Error: ",a.error]}),ee(be,{gap:1,children:[O(H,{color:"cyan",children:">"}),O(H,{children:a.buffer}),O(H,{color:"cyan",children:"\u2588"})]}),O(Bn,{text:"Type the api_name \xB7 Enter to confirm \xB7 Ctrl+C to quit"})]})):a.type==="value-entry"?f(ee(br,{children:[O(H,{bold:!0,children:"Secret value"}),a.error!==void 0&&ee(H,{color:"red",children:["Error: ",a.error]}),ee(be,{gap:1,children:[O(H,{color:"cyan",children:">"}),O(H,{children:"\u2022".repeat(a.buffer.length)}),O(H,{color:"cyan",children:"\u2588"})]}),O(Bn,{text:"Input hidden \xB7 Enter to encrypt \xB7 Ctrl+C to quit"})]})):a.type==="fetching-key"?f(ee(be,{gap:1,children:[O(ye,{}),ee(H,{children:["Fetching ",e.stage," public key for ",O(H,{bold:!0,children:x}),"\u2026"]})]})):a.type==="encrypting"?f(ee(be,{gap:1,children:[O(ye,{}),O(H,{children:"Encrypting\u2026"})]})):a.type==="error"?f(ee(be,{gap:1,children:[O(H,{color:"red",children:"\u2717"}),O(H,{color:"red",children:a.message})]})):f(ee(br,{children:[ee(be,{gap:1,children:[O(H,{color:"green",children:"\u2713"}),O(H,{bold:!0,children:"Encrypted secret ready"})]}),a.copied?O(H,{color:"green",children:"\u2713 Copied to clipboard \u2014 paste it into the secret value in your kizen.json."}):O(H,{dimColor:!0,children:"Paste this into the secret value in your kizen.json (re-run with --out <path> to save it to a file; selecting it here may capture line wraps):"}),O(H,{children:a.envelope})]}))};import{readFile as ep,writeFile as tp}from"fs/promises";import{join as rp}from"path";function Rs(e){return{encrypted:!0,value:e}}function Bs(e){return JSON.stringify(Rs(e))}async function $n(e,t){await tp(e,`${JSON.stringify(Rs(t),null,2)}
1102
- `,"utf-8")}var np="prod";function $s(e){return e==="dev"||e==="prod"}function js(e){return`Error: --stage must be "dev" or "prod" (got: "${e}")`}function Fs(e){return{stage:e==="dev"||e==="prod"?e:np,wasDefaulted:e===void 0}}function Os(e){return`No --stage given; defaulting to ${e} encryption keys (pass --stage dev to override)
1103
- `}async function zs(e=process.cwd()){try{let t=await ep(rp(e,"kizen.json"),"utf-8"),r=JSON.parse(t),o=Array.isArray(r)?r[0]:r,n=typeof o.api_name=="string"&&o.api_name!==""?o.api_name:void 0;return{...n!==void 0&&{apiName:n}}}catch{}return{}}function Ms(e){return e.replace(/[\r\n]+$/,"")}async function Ls(){let e=[];for await(let t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf-8")}async function Hs(e,t,r,o,n){let i=t.credentials!==void 0?await n.loadCredentialsFromFile(t.credentials):await n.loadGlobalCredentials();if(i===null)throw new Error("No credentials found. Pass --credentials or create ~/.kizenappbuilder/credentials.json.");let s=t.apiName??r.apiName;if(s===void 0||s.trim()==="")throw new Error("api_name is required in non-interactive mode. Pass --api-name or run inside a plugin directory.");o&&n.writeStderr(Os(e.stage));let l=t.value;if(l===void 0){if(n.stdinIsTTY())throw new Error('No secret provided and stdin is a terminal. Pass --value, or pipe the secret in (e.g. `printf %s "$SECRET" | npx --yes @kizenapps/cli encrypt \u2026`).');n.writeStderr(`Reading secret from stdin (pass --value to skip)\u2026
1104
- `),l=Ms(await n.readStdin())}if(l==="")throw new Error("A secret value is required. Pass --value or pipe it via stdin.");let a=await n.encryptSecret(e,i,s.trim(),l);if(n.writeStdout(`${Bs(a)}
1105
- `),t.out!==void 0)try{await n.writeEnvelopeFile(t.out,a),n.writeStderr(`Wrote encrypted envelope to ${t.out}
1106
- `)}catch(p){n.writeStderr(`Error writing ${t.out}: ${p instanceof Error?p.message:String(p)}
1107
- `),n.setExitCode(1)}}function Us(e){e.command("encrypt").description("Encrypt a secret for a plugin against its encryption keys").option("--remote","encrypt via the plugin-wizard's remote API instead of on-machine").option("-c, --credentials <path>","path to a credentials JSON file").option("-a, --api-name <name>","plugin api_name the secret belongs to").option("-v, --value <value>","plaintext secret value to encrypt").option("-s, --stage <stage>","which encryption API to use: dev or prod (default: prod)").option("-o, --out <path>","also write the encrypted envelope to a file").action(async t=>{t.stage!==void 0&&!$s(t.stage)&&(console.error(js(t.stage)),process.exit(1));let o=t.apiName===void 0?await zs():{},{stage:n,wasDefaulted:i}=Fs(t.stage),s={isRemote:t.remote===!0,stage:n};if(!process.stdin.isTTY||!process.stdout.isTTY){try{await Hs(s,t,o,i,{loadCredentialsFromFile:Ie,loadGlobalCredentials:te,encryptSecret:hr,readStdin:Ls,stdinIsTTY:()=>process.stdin.isTTY,writeStdout:p=>{process.stdout.write(p)},writeStderr:p=>{process.stderr.write(p)},writeEnvelopeFile:$n,setExitCode:p=>{process.exitCode=p}})}catch(p){console.error(`Error: ${p instanceof Error?p.message:String(p)}`),process.exit(1)}return}let l,{waitUntilExit:a}=ip(op(Ds,{ctx:s,stageDefaulted:i,onDone:p=>{l=p},...t.credentials!==void 0&&{credentialsPath:t.credentials},...t.apiName!==void 0&&{initialApiName:t.apiName},...o.apiName!==void 0&&{defaultApiName:o.apiName},...t.value!==void 0&&{initialValue:t.value}}),{exitOnCtrlC:!1});if(await a(),l!==void 0&&t.out!==void 0)try{await $n(t.out,l),process.stderr.write(`Wrote encrypted envelope to ${t.out}
1108
- `)}catch(p){console.error(`Error writing ${t.out}: ${p instanceof Error?p.message:String(p)}`),process.exitCode=1}})}var sp=new Set(["phone","envelope","pen-nib","calendar-days","paperclip","file","clipboard","pen","tag","book","print","pen-to-square","folder","folder-open","thumbtack","globe","city","briefcase","compass","address-book","business-time","computer-classic","pen-fancy","badge-check","mug-saucer","landmark","chart-simple","envelopes","diagram-venn","calendar","pencil","phone-volume","copy","table","building","network-wired","address-card","tags","registered","signature","phone-office","projector","sitemap","marker","badge","wallet","copyright","bullseye","calculator","certificate","percent","eraser","file-spreadsheet","glasses","industry","vault","user-tie-hair-long","user-tie-hair","user-hair-mullet","trademark","timeline-arrow","timeline","table-tree","table-rows","table-pivot","table-layout","table-columns","stapler","square-poll-vertical","square-poll-horizontal","square-phone-flip","square-phone","square-pen","square-kanban","square-envelope","socks","slot-machine","signature-slash","signature-lock","shredder","scissors","scanner-image","scale-unbalanced-flip","scale-unbalanced","scale-balanced","router","rectangle-pro","print-slash","print-magnifying-glass","presentation-screen","podium","phone-slash","phone-intercom","phone-flip","person-chalkboard","pen-nib-slash","pen-clip","paste","paperclip-vertical","notebook","note-sticky","money-check-pen","money-check-dollar-pen","magnifying-glass-chart","magnifying-glass-arrow-right","list-tree","list-timeline","list-radio","list-dropdown","list-check","laptop-file","lamp-desk","keynote","industry-windows","inboxes","inbox-full","house-laptop","highlighter","folders","folder-xmark","folder-tree","folder-plus","folder-minus","folder-arrow-up","folder-arrow-down","floppy-disk-circle-xmark","floppy-disk-circle-arrow-right","floppy-disk","file-user","file-lines","file-circle-plus","file-circle-info","file-chart-pie","file-chart-column","fax","envelope-open","envelope-dot","envelope-circle-check","coffee-pot","cloud-word","clipboard-question","chart-user","chart-tree-map","chart-simple-horizontal","chart-pyramid","chart-pie-simple","chart-pie","chart-line-up","chart-line-down","chart-line","cake-candles","cabinet-filing","bullhorn","briefcase-blank","briefcase-arrow-right","brain-arrow-curved-right","boxes-packing","box-archive","book-section","bars-staggered","bars-progress","badge-percent","badge-dollar","arrows-to-eye","arrows-to-dot","arrows-spin","sparkles","megaphone","block-question","lightbulb-slash","comments-dollar","comments","bullseye-pointer","lightbulb","user-group-crown","user-crown","signal-stream-slash","signal-stream","rectangle-ad","ranking-star","person-rays","people-group","messages-dollar","message-dollar","magnifying-glass-location","magnifying-glass-dollar","lightbulb-on","lightbulb-exclamation","lightbulb-dollar","lightbulb-cfl-on","lightbulb-cfl","hundred-points","group-arrows-rotate","gift-card","filter-circle-dollar","envelopes-bulk","envelope-open-text","envelope-open-dollar","comment-dollar","chart-mixed","bullseye-arrow","star","heart","camera-retro","truck-fast","bell","cart-shopping","gift","barcode","bookmark","camera","thumbs-up","thumbs-down","key","truck","balloons","handshake","shop","shirt","bag-shopping","moped","cart-plus","credit-card","store","cash-register","booth-curtain","trophy","receipt","truck-container","truck-clock","truck-bolt","trophy-star","store-slash","store-lock","star-exclamation","shop-slash","shop-lock","rectangle-barcode","person-booth","nfc-trash","nfc-symbol","nfc-slash","nfc-signal","nfc-pen","nfc-magnifying-glass","nfc-lock","nfc","money-check-dollar","money-check","mobile-signal-out","mobile-signal","hexagon-vertical-nft-slanted","hexagon-vertical-nft","hexagon-image","hand-holding-box","gifts","gem","credit-card-front","credit-card-blank","cart-xmark","cart-shopping-fast","cart-minus","cart-circle-xmark","cart-circle-plus","cart-circle-exclamation","cart-circle-check","cart-circle-arrow-up","cart-circle-arrow-down","cart-arrow-up","cart-arrow-down","box-open-full","basket-shopping-simple","basket-shopping","barcode-scan","barcode-read","balloon","bags-shopping","bars","browser","chart-bar","cog","database","calendar-alt","play","kizen-k","add","book-alt","form-entity","arrows-to-circle","question-circle","chevron-down","chevron-up","window-restore","forward"]),ap=[...sp].sort(),lp=new Set(["kizen-k","form-entity"]),Vs=[...ap,...[...lp].sort()];function Ws(e){e.command("icons").description("List all valid icon names accepted by toolbar items, pages, and adornments").action(()=>{for(let t of Vs)console.log(t)})}import{mkdir as gp,writeFile as ra,readFile as yp}from"fs/promises";import{join as wr}from"path";var cp=new Set(["developer_business_id"]);function dp(e){return Array.isArray(e)?e.map(t=>{if(typeof t!="object"||t===null)return t;let r=t;if(typeof r.auth_credentials!="object"||r.auth_credentials===null)return r;let o=Object.fromEntries(Object.keys(r.auth_credentials).map(n=>[n,"*****"]));return{...r,auth_credentials:o}}):e}function Js(e){let t={name:"",children:new Map,isFile:!1,filePath:""};for(let r of e){let o=r.path.split("/"),n=t;o.forEach((i,s)=>{n.children.has(i)||n.children.set(i,{name:i,children:new Map,isFile:s===o.length-1,filePath:o.slice(0,s+1).join("/")});let l=n.children.get(i);l&&(n=l)})}return t}function qs(e){return[...e.children.values()].sort((t,r)=>t.isFile!==r.isFile?t.isFile?1:-1:t.name.localeCompare(r.name))}function jn(e){return"file-"+e.replace(/[^a-zA-Z0-9]/g,"-")}function we(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Gs(e){let t=e.lastIndexOf(".");return t>=0?e.slice(t).toLowerCase():""}var up={".ts":"typescript",".tsx":"typescript",".js":"javascript",".jsx":"javascript",".mjs":"javascript",".cjs":"javascript",".json":"json",".css":"css",".scss":"scss",".html":"html",".htm":"html",".md":"markdown",".mdx":"markdown",".py":"python",".sh":"bash",".yml":"yaml",".yaml":"yaml",".xml":"xml",".toml":"toml"};function pp(e){return up[Gs(e)]??""}function Ks(e){let t=[...e.matchAll(/`+/g)].reduce((r,o)=>Math.max(r,o[0].length),0);return"`".repeat(Math.max(3,t+1))}function Ys(e,t=""){let r=qs(e);return r.map((o,n)=>{let i=n===r.length-1,s=i?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",l=i?" ":"\u2502 ";return o.isFile?`${t}${s}${o.name}`:`${t}${s}${o.name}/
1109
- ${Ys(o,t+l)}`}).join(`
1110
- `)}var fp=/(^|\/)licen[cs]e(\.md|\.txt)?$/i;function Xs(e){return e.filter(t=>t.path!=="kizen.json"&&t.path!=="plugin-report.html"&&!fp.test(t.path))}function Zs(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!cp.has(t)).map(([t,r])=>[t,t==="services"?dp(r):r]))}var mp={".png":"image/png",".svg":"image/svg+xml",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp"};function Qs(e){return`<ul>${qs(e).map(o=>o.isFile?`<li class="tf"><a href="#${jn(o.filePath)}" data-id="${jn(o.filePath)}">${we(o.name)}</a></li>`:`<li class="td"><span class="dn">${we(o.name)}/</span>${Qs(o)}</li>`).join("")}</ul>`}function hp(e){let t;return e.base64Image!==void 0?t=`<img src="data:${mp[Gs(e.path)]??"image/png"};base64,${e.base64Image}" alt="${we(e.path)}" style="max-width:100%">`:e.binaryData!==void 0?t=`<p class="bin">[Binary file: ${String(e.binaryData.length)} bytes]</p>`:t=`<pre><code>${we(e.content)}</code></pre>`,`<section id="${jn(e.path)}"><h2 class="fp">${we(e.path)}</h2><div class="fb">${t}</div></section>`}function ea(e,t){let r=Zs(e),o=typeof e.name=="string"?e.name:"Plugin",n=typeof e.version=="string"?e.version:"",i=typeof e.description=="string"?e.description:"",s=Xs(t),l=[];l.push(`# ${o}${n?` v${n}`:""}`),i&&l.push("",i);let a=JSON.stringify(r,null,2),p=Ks(a);l.push("","## Configuration (`kizen.json`)","",`${p}json`,a,p),l.push("","## File Tree","","```",Ys(Js(s)),"```"),l.push("","## Files");for(let h of s)if(l.push("",`### \`${h.path}\``,""),h.base64Image!==void 0)l.push(`[Image file: ${h.path}]`);else if(h.binaryData!==void 0)l.push(`[Binary file: ${String(h.binaryData.length)} bytes]`);else{let w=Ks(h.content);l.push(`${w}${pp(h.path)}`,h.content,w)}return l.join(`
452
+ `.trim(),Ls=3e4,xt=We(process.cwd(),".kizenapp","venv"),Os=Fs.map(vn),wn=gn(mn);if(wn.length===0)throw new Error("python-requirements.txt bundled no packages \u2014 check the build output");var Je=null,Ge=[3,10];function Cn([e,t]){return e>Ge[0]||e===Ge[0]&&t>=Ge[1]}function Qt(e){return`${String(e[0])}.${String(e[1])}`}function Sn(e){return new Promise(t=>{let n=rr(e,["-c","import sys; print('%d.%d' % sys.version_info[:2])"],{stdio:["ignore","pipe","ignore"]}),o="";n.stdout.on("data",r=>{o+=r.toString("utf-8")}),n.on("error",()=>{t(null)}),n.on("close",r=>{let i=/(\d+)\.(\d+)/.exec(o);if(r!==0||!i){t(null);return}t([Number(i[1]),Number(i[2])])})})}async function zs(e){let t=hn(e),n=t==="python3"?["python3"]:[t,"python3"],o=[];for(let r of n){let i=await Sn(r);if(!i){o.push(`${r}: not found`);continue}if(Cn(i))return r;o.push(`${r}: ${Qt(i)} (too old)`)}throw new Error(`No Python >= ${Qt(Ge)} found for runtime "${e}". The local runner needs Python ${Qt(Ge)}+ to install the bundled packages (e.g. psycopg 3.3), matching the remote code-runner (3.12 / 3.13). Tried ${o.join(", ")}. Install Python 3.12 or 3.13 and retry.`)}function xn(e,t,n){return new Promise((o,r)=>{let i=rr(e,t,{stdio:["ignore","pipe","pipe"]}),p=(g,m)=>{let y=[],T="";return g.on("data",P=>{T+=P.toString("utf-8");let x=T.indexOf(`
453
+ `);for(;x>=0;){let c=T.slice(0,x).replace(/\r$/,"");T=T.slice(x+1),y.push(c),n?.(c,m),x=T.indexOf(`
454
+ `)}}),g.on("end",()=>{if(T.length>0){let P=T.replace(/\r$/,"");y.push(P),n?.(P,m)}}),y},f=p(i.stderr,"stderr");p(i.stdout,"stdout"),i.on("error",r),i.on("close",g=>{if(g===0)o();else{let m=f.slice(-10).join(`
455
+ `).trim(),y=m.length>0?`: ${m}`:"";r(new Error(`${e} exited with code ${String(g)}${y}`))}})})}var er=null;function Ms(e,t){if(Je)return t&&er?.add(t),Je;let n=new Set;t&&n.add(t),er=n;let o=i=>{for(let p of n)p(i)},r=We(xt,"bin","python");return Je=(async()=>{try{let i=!1;try{await As(r);let p=await Sn(r);i=p!==null&&Cn(p)}catch{i=!1}if(!i){await tr(xt,{recursive:!0,force:!0}).catch(()=>{});let p=await zs(e);await xn(p,["-m","venv",xt])}return o({kind:"start"}),await xn(r,["-m","pip","install","--disable-pip-version-check",...wn],(p,f)=>{o({kind:"log",line:p,stream:f})}),o({kind:"complete"}),r}catch(i){let p=i instanceof Error?i.message:String(i);throw o({kind:"error",message:p}),Je=null,await tr(xt,{recursive:!0,force:!0}).catch(()=>{}),i}finally{er=null}})(),Je}function Hs(e){let t="__KIZEN_RESULT_START__",n="__KIZEN_RESULT_END__",o=e.indexOf(t),r=e.indexOf(n);if(o===-1||r===-1)return{userStdout:e,result:null};let i=e.slice(0,o).trimEnd(),p=e.slice(o+t.length,r).trim();try{let f=JSON.parse(p);return{userStdout:i,result:f}}catch{return{userStdout:e,result:null}}}function Us(e){return{success:!1,outputValues:{},logs:[],stdout:"",stderr:"",error:e,exitCode:1,durationMs:0}}async function Tn(e){let t=e.timeout??Ls;if(!$s(e.scriptRuntime))return Us(`Unsupported runtime "${e.scriptRuntime}". Supported runtimes: ${Os.join(", ")}. Update the step's config.json "runtime" field (e.g. "python 3.13").`);let o=await Ms(vn(e.scriptRuntime),e.onInstallProgress),r=Date.now(),i;try{i=await Bs(We(Ds(),"kizen-step-"));let p=We(i,"wrapper.py"),f=We(i,"script.py");await Promise.all([bn(p,js,"utf-8"),bn(f,e.script,"utf-8")]);let g={...process.env,__KIZEN_INPUTS__:JSON.stringify(e.inputs),__KIZEN_SECRETS__:JSON.stringify(e.secrets),__KIZEN_SCRIPT_PATH__:f},{exitCode:m,stdout:y,stderr:T}=await new Promise(w=>{let H=[],j=[],C=!1,v=rr(o,[p],{env:g,cwd:i,stdio:["ignore","pipe","pipe"]}),d=setTimeout(()=>{v.kill("SIGKILL")},t);v.stdout.on("data",s=>{H.push(s)}),v.stderr.on("data",s=>{j.push(s)});let l=s=>{C||(C=!0,clearTimeout(d),w({exitCode:s??1,stdout:Buffer.concat(H).toString("utf-8"),stderr:Buffer.concat(j).toString("utf-8")}))};v.on("close",l),v.on("error",s=>{C||(C=!0,clearTimeout(d),w({exitCode:1,stdout:"",stderr:s.message}))})}),P=Date.now()-r,{userStdout:x,result:c}=Hs(y);if(c){let w={success:c.success,outputValues:c.outputValues,logs:c.logs,stdout:x,stderr:T,exitCode:m,durationMs:P};return c.error!==null&&(w.error=c.error),c.httpRequests&&(w.httpRequests=c.httpRequests),w}let b={success:!1,outputValues:{},logs:[],stdout:x,stderr:T,exitCode:m,durationMs:P};return m!==0&&(b.error=`Process exited with code ${String(m)}`),b}finally{i&&tr(i,{recursive:!0,force:!0})}}var En=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".md",".mdx",".yml",".yaml",".toml",".css",".scss",".html",".htm",".xml",".svg",".txt",".sh",".py",".env",".gitignore",".prettierrc",".eslintrc"]);import{CRYPTO_ALG as In,CRYPTO_VERSION as Nn,DEFAULT_AUTOMATION_RUNTIME as Zs,deserializeEnvelope as Qs,encrypt as ea,scriptRuntimeToApiName as ta,serializeEnvelope as ra}from"@kizenapps/packager";function nr(e){if(e.kind==="info")return e.message;let t=e.fromCache?" [cache]":"",n=e.upstreamStatus!==void 0?` (upstream ${String(e.upstreamStatus)})`:"";return`${e.method} ${e.url} \u2192 ${String(e.status)}${n}${t}`}var na=2*1024*1024,oa=new Set([".DS_Store",".gitignore"]);function ia(e){if(e===void 0||e==="")return!1;let t=e.startsWith("[")?e.slice(0,e.indexOf("]")+1):e.split(":")[0]??"";return t==="localhost"||t==="127.0.0.1"||t==="[::1]"}var Dn=["kizen.com","kizen.dev"];function sa(e){let t;try{t=new URL(e)}catch{return!1}let n=t.hostname.toLowerCase();return n==="localhost"||n==="127.0.0.1"||n==="[::1]"?!0:t.protocol!=="https:"?!1:Dn.some(o=>n===o||n.endsWith("."+o))}function aa(e){let t;try{t=new URL(e)}catch{return!1}if(t.protocol!=="https:")return!1;let n=t.hostname.toLowerCase();return Dn.some(o=>n===o||n.endsWith("."+o))}function la(e){if(e===void 0||e==="")return!1;try{let{hostname:t}=new URL(e);return t==="localhost"||t==="127.0.0.1"||t==="::1"}catch{return!1}}async function Rn(e,t){if(t.includes("\0"))return null;let n=t.replace(/^\/+/,""),o=Ys(e,n);if(o!==e&&!o.startsWith(e+or))return null;try{let r=await Gs(o);return r!==e&&!r.startsWith(e+or)?null:r}catch{return null}}function Bn(e,t){let n=t===e?"":t.slice(e.length+1);return n===""?!1:n.split(or).some(r=>rt.has(r))}function Fn(){let e=Xs(import.meta.url);return Ke(Ks(e),"viewer")}function An(e,t,n){if(e.includes("/external-integrations/proxy")&&t["content-type"]?.includes("application/json"))try{let o=JSON.parse(n.toString("utf8"));return typeof o.status_code=="number"?o.status_code:void 0}catch{return}}async function ir(e){try{return await qs(e),!0}catch{return!1}}function $n(e,t,n,o,r,i,p,f,g,m,y){let T=un({onChange:f,enabled:y}),P=Vs(m);return(x,c)=>{(async()=>{let b=x.url??"/";try{if(!ia(x.headers.host)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Forbidden"}));return}if((x.method==="POST"||x.method==="PATCH"||x.method==="DELETE")&&!la(x.headers.origin)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Forbidden"}));return}if(t(`Received request: ${b}`),b==="/api/credentials"){c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({credentials:o.current??null,activeProfile:r.current??null}));return}if(b==="/api/credential-profiles"&&x.method==="GET"){let l=await Re();c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({profiles:l,active:r.current}));return}if(b==="/api/credentials/switch"&&x.method==="POST"){let l=[];for await(let h of x)l.push(h);let a=JSON.parse(Buffer.concat(l).toString("utf-8")||"{}").profile,u=a===void 0||a===""?await Z():await de(a);if(u!==null){o.current=u,r.current=a??void 0;let h=r.current,k=await we(i);await Ce(i,{...k,credentialMode:"global",...h!==void 0&&{activeCredentialProfile:h}}),p({type:"credentials-updated",credentials:u,activeProfile:r.current??null})}c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:u!==null}));return}if(b==="/api/bundle"){let l=Ke(process.cwd(),".kizenapp","bundle.json");try{let s=await Ws(l,"utf-8");c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(s)}catch{c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end("{}")}return}if(b==="/api/last-path"&&x.method==="POST"){let l=[];for await(let a of x)l.push(a);let s=JSON.parse(Buffer.concat(l).toString("utf-8")||"{}");if(typeof s.path=="string"){g.current=s.path;let a=await we(i);await Ce(i,{...a,lastPath:s.path})}c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end('{"ok":true}');return}if(b==="/api/encryption-target"&&x.method==="GET"){let s=(await we(i)).encryptionTarget==="dev"?"dev":"prod";c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({target:s}));return}if(b==="/api/encryption-target"&&x.method==="POST"){let l=[];for await(let h of x)l.push(h);let s=JSON.parse(Buffer.concat(l).toString("utf-8")||"{}"),a=s!==null&&typeof s=="object"?s:{};if(a.target!=="dev"&&a.target!=="prod"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"target must be one of: 'dev', 'prod'"}));return}let u=await we(i);await Ce(i,{...u,encryptionTarget:a.target}),c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end('{"ok":true}');return}if(b==="/api/execute-step"&&x.method==="POST"){let l=[];for await(let h of x)l.push(h);let s=JSON.parse(Buffer.concat(l).toString("utf-8")||"{}");if(typeof s.script!="string"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing required field: script"}));return}let a={script:s.script,scriptRuntime:s.scriptRuntime??ta(Zs),inputs:yn(s.inputs),secrets:s.secrets??{},onInstallProgress:h=>{h.kind==="start"?p({type:"venv-install-start"}):h.kind==="log"?p({type:"venv-install-log",line:h.line,stream:h.stream}):h.kind==="complete"?p({type:"venv-install-complete"}):p({type:"venv-install-error",message:h.message})}};typeof s.timeout=="number"&&s.timeout>0&&(a.timeout=Math.min(s.timeout,300*1e3));let u=await Tn(a);c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify(u));return}if(b==="/api/proxy-cache/clear"){T.clear(),n({kind:"info",message:"Proxy cache cleared"}),c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end('{"ok":true}');return}if(b==="/api/local/encrypt"&&x.method==="POST"){let l=[];for await(let a of x)l.push(a);let s=JSON.parse(Buffer.concat(l).toString("utf-8")||"{}");if(typeof s.publicKeyPem!="string"||typeof s.value!="string"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing required fields: publicKeyPem, value"}));return}try{let a=ea(s.value,s.publicKeyPem),u=ra(a);c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify(u))}catch(a){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:a.message}))}return}if(b==="/api/local/validate"&&x.method==="POST"){let l=[];for await(let E of x)l.push(E);let s=JSON.parse(Buffer.concat(l).toString("utf-8")||"{}");if(typeof s.value!="string"){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing required field: value"}));return}let a,u;try{a=Qs(s.value)}catch(E){u=E.message}if(u!==void 0||a===void 0){c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({valid:!1,checks:[],error:u}));return}let h=E=>Buffer.from(E,"base64").length,k=[{label:"Version",expected:String(Nn),actual:String(a.v),pass:a.v===Nn},{label:"Algorithm",expected:In,actual:a.alg,pass:a.alg===In},{label:"Wrapped key",expected:"384 bytes (RSA-3072)",actual:`${String(h(a.k))} bytes`,pass:h(a.k)===384},{label:"IV",expected:"12 bytes",actual:`${String(h(a.iv))} bytes`,pass:h(a.iv)===12},{label:"Auth tag",expected:"16 bytes",actual:`${String(h(a.tag))} bytes`,pass:h(a.tag)===16},{label:"Ciphertext",expected:"\u2265 1 byte",actual:`${String(h(a.ct))} bytes`,pass:h(a.ct)>=1}];c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({valid:k.every(E=>E.pass),checks:k}));return}if(b.startsWith("/api/wizard")){let l=x.headers["x-encryption-target"],a=(Array.isArray(l)?l[0]:l)==="dev"?"dev":"prod",u=qe(a);if(!sa(u)){c.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:`Encryption API host is not allowed: ${u}`}));return}let h=b.slice(11)||"/",k=`${u}${h}`,E=x.method??"GET",R=[];for await(let Ee of x)R.push(Ee);let{host:z,"x-encryption-target":W,...te}=x.headers,oe=R.length>0?Buffer.concat(R):void 0,ie=oe&&oe.length>0?oe:void 0,re;try{re=await fetch(k,{method:E,headers:te,...ie!==void 0&&{body:ie}})}catch(Ee){let Nt=Ee.code==="ECONNREFUSED"?`plugin-wizard is not running at ${u}`:`Failed to reach plugin-wizard: ${Ee.message}`;c.writeHead(503,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:Nt}));return}let ee=bt(Object.fromEntries(re.headers)),je=await re.arrayBuffer();c.writeHead(re.status,ee),c.end(Buffer.from(je));return}if(b.startsWith("/api/proxy")){let l=x.headers["x-proxy-target"];if(typeof l!="string"){c.writeHead(400),c.end("Missing x-proxy-target header");return}if(!aa(l)){c.writeHead(400),c.end("Invalid proxy target");return}let s=b.slice(10)||"/",a=`${l}${s}`,u=x.method??"GET",h=[];for await(let re of x)h.push(re);let{host:k,"x-proxy-target":E,...R}=x.headers;if(u==="GET"){let re=`GET::${a}`,{response:ee,fromCache:je}=await T.get(re,()=>fetch(a,{method:"GET",headers:R}));n({kind:"request",method:"GET",status:ee.status,upstreamStatus:An(s,ee.headers,ee.body),fromCache:je,url:s}),c.writeHead(ee.status,ee.headers),c.end(ee.body);return}let z=h.length>0?Buffer.concat(h):void 0,W=z&&z.length>0?z:void 0,te=await fetch(a,{method:u,headers:R,...W!==void 0&&{body:W}}),oe=bt(Object.fromEntries(te.headers)),ie=await Yt(te,Kt);n({kind:"request",method:u,status:te.status,upstreamStatus:An(s,oe,ie),fromCache:!1,url:s}),c.writeHead(te.status,oe),c.end(ie);return}if(b.startsWith("/api/source/tree")&&x.method==="GET"){let s=new URL(b,"http://localhost").searchParams.get("path")??"",a=await Rn(P,s);if(a===null){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Invalid path"}));return}if(Bn(P,a)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Path is excluded"}));return}try{let u=await Js(a,{withFileTypes:!0}),h=await Promise.all(u.filter(E=>E.isDirectory()?!rt.has(E.name):E.isFile()?!oa.has(E.name):!1).map(async E=>{let R=E.isDirectory()?"dir":"file",z={name:E.name,kind:R};if(R==="file")try{let W=await kn(Ke(a,E.name));z.size=W.size}catch{}return z}));h.sort((E,R)=>E.kind!==R.kind?E.kind==="dir"?-1:1:E.name.localeCompare(R.name));let k=a===P?"":a.slice(P.length+1);c.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({path:k,entries:h}))}catch(u){let h=u instanceof Error?u.message:String(u);t(`source/tree failed (${s}): ${h}`),c.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Not found"}))}return}if(b.startsWith("/api/source/file")&&x.method==="GET"){let s=new URL(b,"http://localhost").searchParams.get("path")??"";if(s===""){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Missing path"}));return}let a=await Rn(P,s);if(a===null){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Invalid path"}));return}if(Bn(P,a)){c.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Path is excluded"}));return}try{let u=await kn(a);if(!u.isFile()){c.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Not a file"}));return}if(u.size>na){c.writeHead(413,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"File too large",size:u.size}));return}let h=Pn(a).toLowerCase(),k=Xt[h],E=En.has(h),R=k??(E?"text/plain; charset=utf-8":"application/octet-stream");c.writeHead(200,{"Content-Type":R,"Content-Length":String(u.size)}),_n(a).pipe(c)}catch(u){let h=u instanceof Error?u.message:String(u);t(`source/file failed (${s}): ${h}`),c.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:"Not found"}))}return}let j=Ke(e,b==="/"?"/index.html":b),C=await ir(j)?j:Ke(e,"index.html"),v=Pn(C),d=Xt[v]??"application/octet-stream";c.writeHead(200,{"Content-Type":d}),_n(C).pipe(c)}catch(w){if(b.startsWith("/api/proxy")){let H=b.slice(10)||"/",j=x.method??"GET";w instanceof Ve?n({kind:"info",message:`${j} ${H} \u2192 502 (response exceeded ${String(w.limit)} bytes)`}):n({kind:"request",method:j,status:502,fromCache:!1,url:H})}else n({kind:"info",message:`Error handling ${b}: ${w instanceof Error?w.message:String(w)}`});c.headersSent||(c.writeHead(502),c.end("Bad Gateway"))}})()}}import{launch as ca}from"chrome-launcher";import{createRequire as da}from"module";import{mkdirSync as pa}from"fs";import{readFile as ua}from"fs/promises";import{dirname as fa,join as ma}from"path";import{fileURLToPath as ga}from"url";var jn=da(import.meta.url)("chrome-remote-interface"),ha=new Set(["content-security-policy","content-security-policy-report-only","x-frame-options"]),On=["kizen.dev","kizen.com"];function Ln(e){try{let{hostname:t}=new URL(e);return On.some(n=>t===n||t.endsWith("."+n))}catch{return!1}}async function ya(e){try{let t=ma(fa(ga(import.meta.url)),"viewer","icon.png"),n=await ua(t);await e.Browser.setDockTile({image:n.toString("base64")})}catch{}}async function zn(e){pa(e.userDataDir,{recursive:!0});let t=`http://localhost:${String(e.port)}`,n=await ca({startingUrl:t,chromeFlags:[`--app=${t}`,"--no-first-run","--no-default-browser-check","--disable-extensions","--disable-features=DeviceBoundSessions"],userDataDir:e.userDataDir,handleSIGINT:!1,logLevel:"silent"}),o=n.port,r=null,i=null,p=null,f=null,g=0,m=!1,y=C=>{e.debugLog?.(C)},T=C=>{e.verbose&&e.debugLog?.(C)},P=()=>{i!==null&&(clearInterval(i),i=null)},x=C=>C==="chrome://new-tab-page/"||C==="chrome://newtab/",c=async C=>{P(),p=null,await ya(C),await C.Fetch.enable({patterns:On.flatMap(d=>[{urlPattern:`*://${d}/*`,requestStage:"Response"},{urlPattern:`*://*.${d}/*`,requestStage:"Response"}])}),C.on("Fetch.requestPaused",d=>{(async()=>{let l=d,{requestId:s,request:a,responseHeaders:u,responseStatusCode:h}=l,k=a.url;T(`Fetch ${String(h??"?")} ${k}`);let E=[],R=!1;for(let z of u??[])if(!ha.has(z.name.toLowerCase()))if(z.name.toLowerCase()==="set-cookie"&&Ln(k)){let W=z.value.split(`
456
+ `).map(te=>/SameSite=/i.test(te)?te.replace(/SameSite=\w+/i,"SameSite=None"):te+"; SameSite=None; Secure").join(`
457
+ `);E.push({name:z.name,value:W}),R=!0}else E.push(z);R&&T(` rewrote Set-Cookie SameSite on ${k}`);try{let{body:z,base64Encoded:W}=await C.Fetch.getResponseBody({requestId:s});await C.Fetch.fulfillRequest({requestId:s,responseCode:h??200,responseHeaders:E,body:z,...W&&{base64Encoded:!0}})}catch(z){T(` fulfillRequest failed (${z.message}), falling through`);try{await C.Fetch.continueRequest({requestId:s})}catch(W){T(` continueRequest also failed (${W.message})`)}}})()}),i=setInterval(()=>{(async()=>{try{let{cookies:d}=await C.Network.getAllCookies(),l=0;for(let s of d){if(!Ln(`https://${s.domain??""}`)||s.sameSite==="None")continue;let a=s.domain??"",u=a.startsWith(".")?a.slice(1):a;await C.Network.setCookie({url:`https://${u}`,name:s.name,value:s.value,domain:a,path:s.path??"/",secure:!0,...s.httpOnly!==void 0&&{httpOnly:s.httpOnly},...s.expirationDate!==void 0&&{expires:s.expirationDate},sameSite:"None"}),l++}l>0&&T(`cookie-patch cycle: updated ${String(l)} cookie(s) to SameSite=None`)}catch(d){T(`cookie-patch cycle failed: ${d.message}`)}})()},2e3),await C.Target.setDiscoverTargets({discover:!0});let v=d=>{if(d.type==="page"){if(x(d.url)){y(`closed stray new-tab-page target ${d.targetId}`),p!==null&&C.Target.activateTarget({targetId:p}).catch(()=>{}),C.Target.closeTarget({targetId:d.targetId}).catch(()=>{});return}p===null&&(p=d.targetId,y(`captured main target ${d.targetId} (${d.url})`))}};C.on("Target.targetCreated",d=>{let{targetInfo:l}=d;T(`target +${l.type} ${l.targetId} ${l.url}`),v(l)}),C.on("Target.targetInfoChanged",d=>{let{targetInfo:l}=d;T(`target ~${l.type} ${l.targetId} ${l.url}`),v(l)}),C.on("disconnect",()=>{m||r!==C||(y("CDP disconnected"),r=null,P(),b())})},b=()=>{if(m||f!==null)return;let C=Math.min(1e3*2**g,1e4),v=g+1;g++,y(`CDP reconnect scheduled in ${String(C)}ms (attempt #${String(v)})`),f=setTimeout(()=>{f=null,(async()=>{let d;try{d=await jn({port:o})}catch{y(`CDP reconnect attempt #${String(v)} failed`),b();return}if(m){try{await d.close()}catch{}return}r=d;try{await c(d),y(`CDP reconnected after ${String(v)} attempt(s)`),g=0}catch{r===d&&(r=null);try{await d.close()}catch{}y(`CDP reconnect attempt #${String(v)} failed during setup`),b()}})()},C)},w=30,H=300;for(let C=0;C<w;C++){await new Promise(v=>setTimeout(v,H)),y(`CDP connect attempt ${String(C+1)}/${String(w)}`);try{let v=await jn({port:o});r=v,e.path&&v.send("Page.navigate",{url:`${t}${e.path}`});try{await c(v),y("CDP connected")}catch{r===v&&(r=null);try{await v.close()}catch{}y("CDP setup failed after connect")}break}catch{if(C===w-1){let v=`CDP unavailable after ${String(w)} attempts \u2014 viewer running without interception (CSP/cookie rewrites disabled)`;return y(v),e.onWarn?.(v),{kill:()=>{n.kill()}}}}}return r?{kill:()=>{if(y("CDP kill requested \u2014 tearing down"),m=!0,f!==null&&(clearTimeout(f),f=null),P(),r!==null){try{r.close()}catch{}r=null}n.kill()}}:{kill:()=>{n.kill()}}}import{useCallback as ba,useState as xa}from"react";function vt(e){let[t,n]=xa([]),o=ba(r=>{n(i=>[...i,`${new Date().toLocaleTimeString()}: ${r}`].slice(-e))},[e]);return[t,o]}import{useCallback as wt,useEffect as sr,useState as Ct}from"react";import{Box as V,Text as $,useInput as va}from"ink";import{jsx as N,jsxs as J}from"react/jsx-runtime";var Se=["apiKey","userId","businessId"],wa={apiKey:"API Key",userId:"User ID",businessId:"Business ID"},Ye=({text:e})=>N($,{dimColor:!0,children:e}),St=({initialMode:e,showProfileManager:t,onComplete:n,onCancel:o})=>{let[r,i]=Ct(e==="global"&&!t?{type:"loading"}:e==="global"&&t?{type:"profile-loading"}:{type:"mode-select",cursor:0}),[p,f]=Ct(""),[g,m]=Ct(null),[y,T]=Ct(void 0),P=wt(d=>{if(d==="local"){n({mode:"local",credentials:null});return}i({type:"profile-loading"})},[n]),x=wt(async d=>{if(d===null){i({type:"profile-loading"});let a=await Re();i({type:"name-entry",nameBuffer:"",existingProfiles:a});return}T(d.isDefault?void 0:d.name),i({type:"loading"});let l=d.isDefault?await Z():await de(d.name),s=l?Math.max(0,Y.indexOf(l.environment)):0;i({type:"creds-entry",field:0,values:l??{},envCursor:s})},[]),c=wt(async(d,l)=>{let s=Y[l]??"go",a={apiKey:d.apiKey??"",userId:d.userId??"",businessId:d.businessId??"",environment:s};i({type:"saving"});try{await ln(y??xe,a),n({mode:"global",credentials:a,...y!==void 0&&{profileName:y}})}catch(u){m(u instanceof Error?u.message:String(u)),i({type:"creds-entry",field:0,values:d,envCursor:l})}},[n,y]);va((d,l)=>{if(l.ctrl&&d==="c"&&process.exit(0),l.escape){if((r.type==="creds-entry"||r.type==="name-entry")&&e==="global"&&t){i({type:"profile-loading"}),b();return}o?.();return}if(r.type==="mode-select"){if(l.upArrow)i({type:"mode-select",cursor:0});else if(l.downArrow)i({type:"mode-select",cursor:1});else if(l.return){let s=r.cursor===0?"global":"local";P(s)}return}if(r.type==="profile-select"){let{profiles:s,cursor:a}=r,u=s.length,h=s.length+1;if(l.upArrow)i({...r,cursor:(a-1+h)%h});else if(l.downArrow)i({...r,cursor:(a+1)%h});else if(l.return)if(a===u)i({type:"name-entry",nameBuffer:"",existingProfiles:s});else{let k=s[a]??s[0];k&&x(k)}return}if(r.type==="name-entry"){let{nameBuffer:s,existingProfiles:a}=r;if(l.backspace||l.delete)i({type:"name-entry",nameBuffer:s.slice(0,-1),existingProfiles:a});else if(l.return){let u=s.trim();if(!u){i({...r,nameError:"Name cannot be empty"});return}if(u===xe){i({...r,nameError:`"${xe}" is reserved`});return}if(a.some(h=>h.name===u)){i({...r,nameError:`Profile "${u}" already exists`});return}T(u),i({type:"creds-entry",field:0,values:{},envCursor:0}),f("")}else d&&!l.ctrl&&!l.meta&&i({type:"name-entry",nameBuffer:s+d,existingProfiles:a});return}if(r.type==="creds-entry"){let{field:s,values:a,envCursor:u}=r;if(s===Se.length){l.leftArrow?i({...r,envCursor:(u-1+Y.length)%Y.length}):l.rightArrow?i({...r,envCursor:(u+1)%Y.length}):l.upArrow?(i({...r,field:s-1}),f(a[Se[s-1]]??"")):l.return&&c(a,u);return}let k=Se[s];if(!k)return;if(l.backspace||l.delete)f(E=>E.slice(0,-1));else if(l.upArrow&&s>0){let E={...a,[k]:p},R=s-1;i({...r,field:R,values:E}),f(E[Se[R]]??"")}else if(l.return||l.tab||l.downArrow){let E={...a,[k]:p},R=s+1;i({...r,field:R,values:E}),R<Se.length?f(E[Se[R]]??""):f("")}else d&&!l.ctrl&&!l.meta&&f(E=>E+d)}});let b=wt(async()=>{let d=await Re();i({type:"profile-select",profiles:d,cursor:0})},[]);if(sr(()=>{r.type==="profile-loading"&&b()},[r.type,b]),sr(()=>{e==="global"&&!t&&(async()=>{let d=await Z(),l=d?Math.max(0,Y.indexOf(d.environment)):0;i({type:"creds-entry",field:0,values:d??{},envCursor:l})})()},[]),sr(()=>{r.type==="creds-entry"&&r.field===0&&f(r.values.apiKey??"")},[r.type]),r.type==="loading"||r.type==="saving"||r.type==="profile-loading"){let d=r.type==="saving"?"Saving credentials\u2026":r.type==="profile-loading"?"Loading profiles\u2026":"Loading credentials\u2026";return N(V,{paddingY:1,paddingX:2,children:N($,{dimColor:!0,children:d})})}if(r.type==="done")return null;if(r.type==="mode-select")return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[N(q,{}),J(V,{flexDirection:"column",gap:0,children:[N($,{bold:!0,children:"Credential mode"}),[{label:"Global",desc:"~/.kizenappbuilder/credentials.json",mode:"global"},{label:"Local",desc:"Enter credentials in the browser dev tools",mode:"local"}].map((l,s)=>{let a=r.cursor===s;return J(V,{gap:2,children:[N($,{...a&&{color:"cyan"},children:a?"\u276F":" "}),N($,{bold:a,...a&&{color:"cyan"},children:l.label}),N($,{dimColor:!0,children:l.desc})]},l.mode)})]}),N(Ye,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit"})]});if(r.type==="profile-select"){let{profiles:d,cursor:l}=r,s=d.length;return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[N(q,{}),J(V,{flexDirection:"column",gap:0,children:[N($,{bold:!0,children:"Credential profiles"}),d.map((a,u)=>{let h=l===u;return J(V,{gap:2,children:[N($,{...h&&{color:"cyan"},children:h?"\u276F":" "}),N($,{bold:h,...h&&{color:"cyan"},children:a.isDefault?"Default":a.name}),N($,{dimColor:!0,children:a.path})]},a.name)}),J(V,{gap:2,children:[N($,{...l===s&&{color:"cyan"},children:l===s?"\u276F":" "}),N($,{bold:l===s,...l===s&&{color:"cyan"},children:"+ Add new profile"})]})]}),N(Ye,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(r.type==="name-entry"){let{nameBuffer:d,nameError:l}=r;return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[N(q,{}),N(V,{flexDirection:"column",gap:0,children:N($,{bold:!0,children:"New credential profile"})}),l&&J($,{color:"red",children:["Error: ",l]}),J(V,{gap:2,children:[N(V,{width:12,children:N($,{bold:!0,color:"cyan",children:"Profile name"})}),N($,{color:"cyan",children:">"}),N($,{children:d}),N($,{color:"cyan",children:"\u2588"})]}),N(Ye,{text:"Type a name \xB7 Enter to confirm \xB7 Esc to go back"})]})}let{field:w,values:H,envCursor:j}=r,C=w===Se.length,v=y??xe;return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[N(q,{}),J(V,{flexDirection:"column",gap:0,children:[N($,{bold:!0,children:y!==void 0?`Profile: ${y}`:"Global credentials"}),J($,{dimColor:!0,children:["Saved to:"," ",y!==void 0?`~/.kizenappbuilder/${y}.json`:Fe]})]}),g&&J($,{color:"red",children:["Error: ",g]}),J(V,{flexDirection:"column",gap:0,children:[Se.map((d,l)=>{let s=w===l,a=s?p:H[d]??"";return J(V,{gap:2,children:[N(V,{width:12,children:N($,{bold:s,...s&&{color:"cyan"},children:wa[d]})}),N($,{...s&&{color:"cyan"},children:">"}),N($,{children:a}),s&&N($,{color:"cyan",children:"\u2588"})]},d)}),J(V,{gap:2,children:[N(V,{width:12,children:N($,{bold:C,...C&&{color:"cyan"},children:"Environment"})}),N($,{...C&&{color:"cyan"},children:">"}),N(V,{gap:1,children:Y.map((d,l)=>{let s=l===j;return C?N($,{bold:s,...s&&{color:"cyan"},children:s?`[${d}]`:d},d):N($,{dimColor:!s,bold:s,children:d},d)})})]})]}),C?N(Ye,{text:"\u2190\u2192 to select \xB7 \u2191\u2193 to move \xB7 Enter to save \xB7 Esc to cancel"}):N(Ye,{text:"\u2191\u2193 to move \xB7 Backspace to delete \xB7 Esc to cancel"}),J($,{dimColor:!0,children:["Profile: ",v]})]})};import{jsx as D,jsxs as U}from"react/jsx-runtime";var Pa=[".kizenapp",".git"],Hn=({port:e,pluginDir:t,outputDir:n,credentials:o,credentialMode:r,activeCredentialProfile:i,lastPath:p,debug:f=!1,verbose:g=!1,autoViewer:m=!0,cacheEnabled:y=!0})=>{let T=pe(o),P=pe(i),x=pe(p),[c,b]=Q("main"),[w,H]=Q(r),[j,C]=Q("starting"),[v,d]=Q(null),[l,s]=vt(50),[a,u]=vt(50),[h,k]=vt(50),[E,R]=Q([]),[z,W]=Q("pending"),[te,oe]=Q(null),[ie,re]=Q(null),[ee,je]=Q(null),[Ee,Nt]=Q(null),[Rt,Bt]=Q(0),[dr,No]=Q(0),[Ro,Bo]=Q(0),[At,Ao]=Q(0),Dt=pe(!1),pr=pe(!1),Ze=pe(null),Le=pe(null),ue=pe(new Set),Qe=pe([]),ne=$e(S=>{let O=JSON.stringify(S);if(ue.current.size===0){Qe.current=[...Qe.current,O].slice(-50);return}let fe=!1;for(let se of ue.current)if(se.readyState===1)try{se.send(O)}catch{ue.current.delete(se),fe=!0}fe&&Bt(ue.current.size)},[]),et=s,Ft=$e(()=>{Ze.current?.kill(),Ze.current=null,zn({port:e,userDataDir:Mn(n,".chrome"),broadcast:ne,onWarn:S=>{s(`\u26A0 ${S}`)},...x.current!==void 0&&{path:x.current},...f&&{debugLog:k},...g&&{verbose:!0}}).then(S=>{Ze.current=S})},[e,n,ne,f,g,k,s]);Tt(()=>{let S=()=>{Ze.current?.kill()};return process.on("exit",S),()=>{process.off("exit",S)}},[]),Sa((S,O)=>{(S==="q"||O.ctrl&&S==="c")&&process.exit(0),S==="c"&&b("editing"),S==="v"&&Ft()},{isActive:c==="main"}),Tt(()=>{j!=="running"||pr.current||!m||(pr.current=!0,Ft())},[j,m,Ft]);let Do=$e(S=>{T.current=S.credentials,P.current=S.profileName,b("main"),H(S.mode);let O=S.profileName;Ce(n,{credentialMode:S.mode,...O!==void 0&&{activeCredentialProfile:O}}),ne({type:"credentials-updated",credentials:S.credentials})},[n,ne]),ur=$e(S=>{R(O=>[...O,`${new Date().toLocaleTimeString()}: ${nr(S)}`].slice(-50)),S.kind==="request"&&(S.fromCache?Bo(O=>O+1):Ao(O=>O+1)),ne({type:"proxy-log",entry:S})},[ne]),_e=$e(S=>{u(S),ne({type:"log",message:S})},[u,ne]),$t=$e(()=>{Dt.current||(Dt.current=!0,W("building"),oe(null),re(null),_e("Build started"),ot(t,n).then(({bundleSize:S})=>{_e(`Build finished \u2014 bundle: ${Oe(S)}`),W("done"),je(new Date),Nt(S),_e("Notifying viewers to reload"),ne({type:"rebuild"})}).catch(S=>{S instanceof Ta?(re(S.issues),oe(null)):(oe(S instanceof Error?S.message:String(S)),re(null)),W("error")}).finally(()=>{Dt.current=!1}))},[t,n,_e,ne]);if(Tt(()=>{$t();let S=Ea(t,{recursive:!0},(O,fe)=>{if(!fe)return;let se=fe.replace(/\\/g,"/");Pa.some(tt=>se.startsWith(tt))||(_e(`File change detected: ${fe}`),Le.current!==null&&clearTimeout(Le.current),Le.current=setTimeout($t,150))});return S.on("error",O=>{_e(`File watcher error \u2014 rebuilds have stopped: ${O.message}`)}),()=>{S.close(),Le.current!==null&&clearTimeout(Le.current)}},[t,$t,_e]),Tt(()=>{let S=Fn();ir(Mn(S,"index.html")).then(O=>{if(!O){d("Viewer not built. Run 'pnpm build:viewer' first."),C("error");return}let fe=$n(S,et,ur,T,P,n,ne,No,x,t,y),se=_a(fe),tt=new ka({server:se});return tt.on("connection",ke=>{for(let $o of Qe.current)ke.readyState===1&&ke.send($o);Qe.current=[],et("Viewer connected for live reload"),ue.current.add(ke),Bt(ue.current.size),ke.on("close",()=>{ue.current.delete(ke),Bt(ue.current.size)})}),se.listen(e,"127.0.0.1",()=>{C("running"),et(`Server started on 127.0.0.1:${String(e)}`)}),se.on("error",ke=>{d(ke.message),C("error")}),()=>{tt.close(),se.close()}})},[e,et,ur,ne,n,t,y]),c==="editing")return D(St,{...w!==void 0&&{initialMode:w},showProfileManager:w==="global",onComplete:Do,onCancel:()=>{b("main")}});let jt=ee!==null?Math.round((Date.now()-ee.getTime())/1e3):null,Fo=jt===null?"":jt<5?" (just now)":` (${String(jt)}s ago)`,fr=ie?.filter(S=>S.severity==="error").length??0;return U(G,{flexDirection:"column",paddingY:1,paddingX:2,children:[D(q,{marginBottom:1}),U(G,{flexDirection:"column",children:[U(G,{gap:2,marginBottom:0,children:[D(A,{bold:!0,dimColor:!0,children:"App Build"}),z==="pending"&&D(A,{dimColor:!0,children:"waiting\u2026"}),z==="building"&&U(G,{gap:1,children:[D(me,{}),D(A,{children:"Building plugin package\u2026"})]}),z==="done"&&U(G,{gap:1,children:[D(A,{color:"green",children:"\u2713 Built"}),ee!==null&&U(A,{dimColor:!0,children:["at ",ee.toLocaleTimeString(),Fo]}),Ee!==null&&U(A,{dimColor:!0,children:["\xB7 bundle: ",Oe(Ee)]})]}),z==="error"&&(ie!==null?U(A,{color:"red",children:["\u2717 ",fr," validation ",fr===1?"error":"errors"]}):U(A,{color:"red",children:["\u2717 ",te??"unknown error"]}))]}),ie!==null&&D(st,{issues:ie}),D(G,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:a.slice(-8).map((S,O)=>D(A,{dimColor:!0,wrap:"truncate",children:S},O))})]}),U(G,{marginTop:1,flexDirection:"column",children:[U(G,{gap:2,children:[D(A,{bold:!0,dimColor:!0,children:"Web Server"}),j==="starting"&&D(A,{dimColor:!0,children:"starting\u2026"}),j==="running"&&U(G,{gap:2,children:[D(A,{color:"green",children:"\u2713 Running"}),U(A,{dimColor:!0,children:["\u25CF ",Rt," viewer",Rt!==1?"s":""]})]}),j==="error"&&U(A,{color:"red",children:["\u2717 ",v??"Server error"]})]}),D(G,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:l.slice(-8).map((S,O)=>D(A,{dimColor:!0,wrap:"truncate",children:S},O))})]}),U(G,{marginTop:1,flexDirection:"column",children:[U(G,{gap:2,children:[D(A,{bold:!0,dimColor:!0,children:"Network Proxy"}),D(A,{color:"green",children:"\u2713 Active"}),D(A,{dimColor:!0,children:y?`\u25CF ${String(dr)} key${dr!==1?"s":""} in the network cache \xB7 ${String(Ro)} hit \xB7 ${String(At)} miss`:`\u25CF network cache disabled \xB7 ${String(At)} request${At!==1?"s":""}`})]}),D(G,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:E.length===0?D(A,{dimColor:!0,children:" No requests yet."}):E.slice(-8).map((S,O)=>D(A,{dimColor:!0,wrap:"truncate",children:S},O))})]}),f&&U(G,{marginTop:1,flexDirection:"column",children:[U(G,{gap:2,children:[D(A,{bold:!0,dimColor:!0,children:"CDP Debug"}),U(A,{dimColor:!0,children:["\u25CF ",h.length," event(s)"]})]}),D(G,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:h.length===0?D(A,{dimColor:!0,children:" No CDP events yet."}):h.slice(-8).map((S,O)=>D(A,{dimColor:!0,wrap:"truncate",children:S},O))})]}),U(G,{marginTop:1,gap:1,children:[D(A,{dimColor:!0,children:"["}),D(A,{children:"q"}),D(A,{dimColor:!0,children:"] quit ["}),D(A,{children:"c"}),D(A,{dimColor:!0,children:"] credentials ["}),D(A,{children:"v"}),U(A,{dimColor:!0,children:["] ",Rt===0?"open viewer":"relaunch viewer"]})]})]})};async function Un(e){return new Promise(t=>{let n=null,o=i=>{n?.(),t(i)},{unmount:r}=qn(Vn(St,{...e!==void 0&&{initialMode:e},onComplete:o}),{exitOnCtrlC:!1});n=r})}function Jn(e){e.command("dev").description("Start the plugin viewer dev server").option("-p, --port <port>","port to listen on","3121").option("-c, --credentials <path>","path to a credentials JSON file").option("-d, --debug","Enable CDP lifecycle logs in the TUI").option("-v, --verbose","Log every CDP event and handled error (implies --debug)").option("--no-viewer","Don't auto-launch the viewer on startup (press [v] to launch manually)").option("--no-cache","Disable the network proxy cache (always fetch upstream)").action(async t=>{let n=parseInt(t.port,10),o=process.cwd(),r=`${o}/.kizenapp`;he(o);let i=null,p,f,{lastPath:g}=await we(r);if(t.credentials)i=await ve(t.credentials);else{let c=await we(r);if(c.credentialMode==="local")p="local";else if(c.credentialMode==="global"){if(p="global",f=c.activeCredentialProfile,f?(i=await de(f),i||(i=await Z(),f=void 0)):i=await Z(),!i){let b=await Un("global");i=b.credentials,f=b.profileName}}else{let b=await Un();i=b.credentials,p=b.mode,f=b.profileName;let w=b.profileName;await Ce(r,{credentialMode:b.mode,...w!==void 0&&{activeCredentialProfile:w}})}}let m=t.verbose===!0,y=m||t.debug===!0,T=t.viewer!==!1,P=t.cache!==!1,{waitUntilExit:x}=qn(Vn(Hn,{port:n,pluginDir:o,outputDir:r,credentials:i,...p!==void 0&&{credentialMode:p},...f!==void 0&&{activeCredentialProfile:f},...g!==void 0&&{lastPath:g},debug:y,verbose:m,autoViewer:T,cacheEnabled:P}),{exitOnCtrlC:!1});await x()})}import{createElement as qa}from"react";import{render as Ja}from"ink";import{useCallback as _t,useEffect as Zn,useState as kt}from"react";import{Box as ae,Text as L,useApp as Oa,useInput as za}from"ink";import{encrypt as Ia,serializeEnvelope as Na}from"@kizenapps/packager";function Ra(e){let t=e;return t.code==="ECONNREFUSED"||t.cause?.code==="ECONNREFUSED"}function Wn(e){return{"Content-Type":"application/json","x-api-key":e.apiKey,"x-user-id":e.userId,"x-business-id":e.businessId,"x-auth-environment":e.environment}}async function Gn(e,t,n){try{return await fetch(`${e}${t}`,n)}catch(o){throw new Error(Ra(o)?`Plugin-wizard is not running at ${e}`:`Failed to reach plugin-wizard: ${o.message}`)}}async function Kn(e){let t=await e.text();if(t==="")return{};try{let n=JSON.parse(t);return typeof n=="object"&&n!==null?n:{}}catch{return{}}}async function Ba(e,t,n){let o=qe(e.stage),r=await Gn(o,"/get-public-key",{method:"POST",headers:Wn(t),body:JSON.stringify({api_name:n})}),i=await Kn(r);if(!r.ok||typeof i.public_key!="string"){let p=typeof i.error=="string"?i.error:`HTTP ${String(r.status)}`;throw new Error(`Failed to fetch public key: ${p}`)}return i.public_key}function Aa(e,t){let n=Ia(t,e),o=Na(n);if(typeof o.value!="string")throw new Error("Unexpected serialized envelope shape from @kizenapps/packager");return o.value}async function Da(e,t,n,o){let r=qe(e.stage),i=await Gn(r,"/encrypt",{method:"POST",headers:Wn(t),body:JSON.stringify({api_name:n,value:o})}),p=await Kn(i);if(!i.ok||p.encrypted!==!0||typeof p.value!="string"){let f=typeof p.error=="string"?p.error:`HTTP ${String(i.status)}`;throw new Error(`Encryption via plugin-wizard failed: ${f}`)}return p.value}async function Et(e,t,n,o,r){if(e.isRemote)return r?.("encrypting"),Da(e,t,n,o);r?.("fetching-key");let i=await Ba(e,t,n);return r?.("encrypting"),Aa(i,o)}import{spawn as Fa}from"child_process";import{platform as Yn}from"process";function $a(){return Yn==="darwin"?[{cmd:"pbcopy",args:[]}]:Yn==="win32"?[{cmd:"clip",args:[]}]:[{cmd:"wl-copy",args:[]},{cmd:"xclip",args:["-selection","clipboard"]},{cmd:"xsel",args:["--clipboard","--input"]}]}var ja=2e3;function La(e,{cmd:t,args:n}){return new Promise(o=>{let r;try{r=Fa(t,n,{stdio:["pipe","ignore","ignore"]})}catch{o(!1);return}let i=r,p=!1;function f(m){p||(p=!0,clearTimeout(g),o(m))}let g=setTimeout(()=>{i.kill(),f(!1)},ja);g.unref(),i.on("error",()=>{f(!1)}),i.on("close",m=>{f(m===0)}),i.stdin.on("error",()=>{f(!1)});try{i.stdin.end(e)}catch{f(!1)}})}async function Xn(e){for(let t of $a())if(await La(e,t))return!0;return!1}import{Fragment as Pt,jsx as F,jsxs as K}from"react/jsx-runtime";var ar=({text:e})=>F(L,{dimColor:!0,children:e}),Qn=({ctx:e,stageDefaulted:t,credentialsPath:n,initialApiName:o,defaultApiName:r,initialValue:i,onDone:p})=>{let f=Oa(),[g,m]=kt({type:"loading"}),[y,T]=kt(null),[P,x]=kt(o??""),[c,b]=kt(i??""),w=_t(s=>{process.exitCode=1,m({type:"error",message:s instanceof Error?s.message:String(s)})},[]),H=_t(async(s,a,u)=>{try{let h=await Et(e,s,a,u,R=>{m(R==="fetching-key"?{type:"fetching-key"}:{type:"encrypting"})}),k=JSON.stringify({encrypted:!0,value:h},null,2);p?.(h);let E=await Xn(k);m({type:"done",envelope:k,copied:E})}catch(h){w(h)}},[e,w,p]),j=_t((s,a,u)=>{if(a.trim()===""){m({type:"api-name-entry",buffer:r??""});return}if(u===""){m({type:"value-entry",buffer:""});return}H(s,a.trim(),u)},[r,H]),C=_t(async s=>{try{m({type:"loading"});let a=s.isDefault?await Z():await de(s.name);if(a===null)throw new Error(`Could not load credential profile: ${s.name}`);T(a),j(a,P,c)}catch(a){w(a)}},[P,c,j,w]);Zn(()=>{(async()=>{try{if(n!==void 0){let u;try{u=await ve(n)}catch(h){throw new Error(`Could not load credentials from ${n}: ${h instanceof Error?h.message:String(h)}`)}T(u),j(u,P,c);return}let s=await Re();if(s.length===0)throw new Error("No credential profiles found. Create ~/.kizenappbuilder/credentials.json first.");let[a]=s;if(s.length===1&&a!==void 0){let u=a.isDefault?await Z():await de(a.name);if(u===null)throw new Error(`Could not load credential profile: ${a.name}`);T(u),j(u,P,c);return}m({type:"profile-select",profiles:s,cursor:0})}catch(s){w(s)}})()},[]),Zn(()=>{(g.type==="done"||g.type==="error")&&f.exit()},[g,f]),za((s,a)=>{if(a.ctrl&&s==="c"){process.exitCode=130,f.exit();return}if(g.type==="profile-select"){let{profiles:u,cursor:h}=g;if(a.upArrow)m({...g,cursor:(h-1+u.length)%u.length});else if(a.downArrow)m({...g,cursor:(h+1)%u.length});else if(a.return){let k=u[h];k!==void 0&&C(k)}return}if(g.type==="api-name-entry"){let{buffer:u}=g;if(a.backspace||a.delete)m({type:"api-name-entry",buffer:u.slice(0,-1)});else if(a.return){let h=u.trim();if(h===""){m({type:"api-name-entry",buffer:u,error:"api_name cannot be empty"});return}x(h),y?j(y,h,c):w(new Error("Internal error: credentials were not loaded"))}else s&&!a.ctrl&&!a.meta&&m({type:"api-name-entry",buffer:u+s});return}if(g.type==="value-entry"){let{buffer:u}=g;if(a.backspace||a.delete)m({type:"value-entry",buffer:u.slice(0,-1)});else if(a.return){if(u===""){m({type:"value-entry",buffer:u,error:"secret cannot be empty"});return}b(u),y?j(y,P,u):w(new Error("Internal error: credentials were not loaded"))}else s&&!a.ctrl&&!a.meta&&m({type:"value-entry",buffer:u+s});return}});let v=e.isRemote?"remote Kizen API":"on-machine",d=t===!0?`${e.stage} (default)`:e.stage,l=s=>K(ae,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[F(q,{}),K(L,{dimColor:!0,children:["Mode: ",v," Stage: ",d]}),s]});if(g.type==="loading")return l(K(ae,{gap:1,children:[F(me,{}),F(L,{dimColor:!0,children:"Loading credentials\u2026"})]}));if(g.type==="profile-select"){let{profiles:s,cursor:a}=g;return l(K(Pt,{children:[F(L,{bold:!0,children:"Credential profile"}),F(ae,{flexDirection:"column",children:s.map((u,h)=>{let k=a===h;return K(ae,{gap:2,children:[F(L,{...k&&{color:"cyan"},children:k?"\u276F":" "}),F(L,{bold:k,...k&&{color:"cyan"},children:u.isDefault?"Default":u.name}),F(L,{dimColor:!0,children:u.path})]},u.name)})}),F(ar,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit"})]}))}return g.type==="api-name-entry"?l(K(Pt,{children:[F(L,{bold:!0,children:"Plugin api_name"}),g.error!==void 0&&K(L,{color:"red",children:["Error: ",g.error]}),K(ae,{gap:1,children:[F(L,{color:"cyan",children:">"}),F(L,{children:g.buffer}),F(L,{color:"cyan",children:"\u2588"})]}),F(ar,{text:"Type the api_name \xB7 Enter to confirm \xB7 Ctrl+C to quit"})]})):g.type==="value-entry"?l(K(Pt,{children:[F(L,{bold:!0,children:"Secret value"}),g.error!==void 0&&K(L,{color:"red",children:["Error: ",g.error]}),K(ae,{gap:1,children:[F(L,{color:"cyan",children:">"}),F(L,{children:"\u2022".repeat(g.buffer.length)}),F(L,{color:"cyan",children:"\u2588"})]}),F(ar,{text:"Input hidden \xB7 Enter to encrypt \xB7 Ctrl+C to quit"})]})):g.type==="fetching-key"?l(K(ae,{gap:1,children:[F(me,{}),K(L,{children:["Fetching ",e.stage," public key for ",F(L,{bold:!0,children:P}),"\u2026"]})]})):g.type==="encrypting"?l(K(ae,{gap:1,children:[F(me,{}),F(L,{children:"Encrypting\u2026"})]})):g.type==="error"?l(K(ae,{gap:1,children:[F(L,{color:"red",children:"\u2717"}),F(L,{color:"red",children:g.message})]})):l(K(Pt,{children:[K(ae,{gap:1,children:[F(L,{color:"green",children:"\u2713"}),F(L,{bold:!0,children:"Encrypted secret ready"})]}),g.copied?F(L,{color:"green",children:"\u2713 Copied to clipboard \u2014 paste it into the secret value in your kizen.json."}):F(L,{dimColor:!0,children:"Paste this into the secret value in your kizen.json (re-run with --out <path> to save it to a file; selecting it here may capture line wraps):"}),F(L,{children:g.envelope})]}))};import{readFile as Ma,writeFile as Ha}from"fs/promises";import{join as Ua}from"path";function eo(e){return{encrypted:!0,value:e}}function to(e){return JSON.stringify(eo(e))}async function lr(e,t){await Ha(e,`${JSON.stringify(eo(t),null,2)}
458
+ `,"utf-8")}var Va="prod";function ro(e){return e==="dev"||e==="prod"}function no(e){return`Error: --stage must be "dev" or "prod" (got: "${e}")`}function oo(e){return{stage:e==="dev"||e==="prod"?e:Va,wasDefaulted:e===void 0}}function io(e){return`No --stage given; defaulting to ${e} encryption keys (pass --stage dev to override)
459
+ `}async function so(e=process.cwd()){try{let t=await Ma(Ua(e,"kizen.json"),"utf-8"),n=JSON.parse(t),o=Array.isArray(n)?n[0]:n,r=typeof o.api_name=="string"&&o.api_name!==""?o.api_name:void 0;return{...r!==void 0&&{apiName:r}}}catch{}return{}}function ao(e){return e.replace(/[\r\n]+$/,"")}async function lo(){let e=[];for await(let t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf-8")}async function co(e,t,n,o,r){let i=t.credentials!==void 0?await r.loadCredentialsFromFile(t.credentials):await r.loadGlobalCredentials();if(i===null)throw new Error("No credentials found. Pass --credentials or create ~/.kizenappbuilder/credentials.json.");let p=t.apiName??n.apiName;if(p===void 0||p.trim()==="")throw new Error("api_name is required in non-interactive mode. Pass --api-name or run inside a plugin directory.");o&&r.writeStderr(io(e.stage));let f=t.value;if(f===void 0){if(r.stdinIsTTY())throw new Error('No secret provided and stdin is a terminal. Pass --value, or pipe the secret in (e.g. `printf %s "$SECRET" | npx --yes @kizenapps/cli encrypt \u2026`).');r.writeStderr(`Reading secret from stdin (pass --value to skip)\u2026
460
+ `),f=ao(await r.readStdin())}if(f==="")throw new Error("A secret value is required. Pass --value or pipe it via stdin.");let g=await r.encryptSecret(e,i,p.trim(),f);if(r.writeStdout(`${to(g)}
461
+ `),t.out!==void 0)try{await r.writeEnvelopeFile(t.out,g),r.writeStderr(`Wrote encrypted envelope to ${t.out}
462
+ `)}catch(m){r.writeStderr(`Error writing ${t.out}: ${m instanceof Error?m.message:String(m)}
463
+ `),r.setExitCode(1)}}function po(e){e.command("encrypt").description("Encrypt a secret for a plugin against its encryption keys").option("--remote","encrypt via the plugin-wizard's remote API instead of on-machine").option("-c, --credentials <path>","path to a credentials JSON file").option("-a, --api-name <name>","plugin api_name the secret belongs to").option("-v, --value <value>","plaintext secret value to encrypt").option("-s, --stage <stage>","which encryption API to use: dev or prod (default: prod)").option("-o, --out <path>","also write the encrypted envelope to a file").action(async t=>{t.stage!==void 0&&!ro(t.stage)&&(console.error(no(t.stage)),process.exit(1));let o=t.apiName===void 0?await so():{},{stage:r,wasDefaulted:i}=oo(t.stage),p={isRemote:t.remote===!0,stage:r};if(!process.stdin.isTTY||!process.stdout.isTTY){try{await co(p,t,o,i,{loadCredentialsFromFile:ve,loadGlobalCredentials:Z,encryptSecret:Et,readStdin:lo,stdinIsTTY:()=>process.stdin.isTTY,writeStdout:m=>{process.stdout.write(m)},writeStderr:m=>{process.stderr.write(m)},writeEnvelopeFile:lr,setExitCode:m=>{process.exitCode=m}})}catch(m){console.error(`Error: ${m instanceof Error?m.message:String(m)}`),process.exit(1)}return}let f,{waitUntilExit:g}=Ja(qa(Qn,{ctx:p,stageDefaulted:i,onDone:m=>{f=m},...t.credentials!==void 0&&{credentialsPath:t.credentials},...t.apiName!==void 0&&{initialApiName:t.apiName},...o.apiName!==void 0&&{defaultApiName:o.apiName},...t.value!==void 0&&{initialValue:t.value}}),{exitOnCtrlC:!1});if(await g(),f!==void 0&&t.out!==void 0)try{await lr(t.out,f),process.stderr.write(`Wrote encrypted envelope to ${t.out}
464
+ `)}catch(m){console.error(`Error writing ${t.out}: ${m instanceof Error?m.message:String(m)}`),process.exitCode=1}})}var Wa=new Set(["phone","envelope","pen-nib","calendar-days","paperclip","file","clipboard","pen","tag","book","print","pen-to-square","folder","folder-open","thumbtack","globe","city","briefcase","compass","address-book","business-time","computer-classic","pen-fancy","badge-check","mug-saucer","landmark","chart-simple","envelopes","diagram-venn","calendar","pencil","phone-volume","copy","table","building","network-wired","address-card","tags","registered","signature","phone-office","projector","sitemap","marker","badge","wallet","copyright","bullseye","calculator","certificate","percent","eraser","file-spreadsheet","glasses","industry","vault","user-tie-hair-long","user-tie-hair","user-hair-mullet","trademark","timeline-arrow","timeline","table-tree","table-rows","table-pivot","table-layout","table-columns","stapler","square-poll-vertical","square-poll-horizontal","square-phone-flip","square-phone","square-pen","square-kanban","square-envelope","socks","slot-machine","signature-slash","signature-lock","shredder","scissors","scanner-image","scale-unbalanced-flip","scale-unbalanced","scale-balanced","router","rectangle-pro","print-slash","print-magnifying-glass","presentation-screen","podium","phone-slash","phone-intercom","phone-flip","person-chalkboard","pen-nib-slash","pen-clip","paste","paperclip-vertical","notebook","note-sticky","money-check-pen","money-check-dollar-pen","magnifying-glass-chart","magnifying-glass-arrow-right","list-tree","list-timeline","list-radio","list-dropdown","list-check","laptop-file","lamp-desk","keynote","industry-windows","inboxes","inbox-full","house-laptop","highlighter","folders","folder-xmark","folder-tree","folder-plus","folder-minus","folder-arrow-up","folder-arrow-down","floppy-disk-circle-xmark","floppy-disk-circle-arrow-right","floppy-disk","file-user","file-lines","file-circle-plus","file-circle-info","file-chart-pie","file-chart-column","fax","envelope-open","envelope-dot","envelope-circle-check","coffee-pot","cloud-word","clipboard-question","chart-user","chart-tree-map","chart-simple-horizontal","chart-pyramid","chart-pie-simple","chart-pie","chart-line-up","chart-line-down","chart-line","cake-candles","cabinet-filing","bullhorn","briefcase-blank","briefcase-arrow-right","brain-arrow-curved-right","boxes-packing","box-archive","book-section","bars-staggered","bars-progress","badge-percent","badge-dollar","arrows-to-eye","arrows-to-dot","arrows-spin","sparkles","megaphone","block-question","lightbulb-slash","comments-dollar","comments","bullseye-pointer","lightbulb","user-group-crown","user-crown","signal-stream-slash","signal-stream","rectangle-ad","ranking-star","person-rays","people-group","messages-dollar","message-dollar","magnifying-glass-location","magnifying-glass-dollar","lightbulb-on","lightbulb-exclamation","lightbulb-dollar","lightbulb-cfl-on","lightbulb-cfl","hundred-points","group-arrows-rotate","gift-card","filter-circle-dollar","envelopes-bulk","envelope-open-text","envelope-open-dollar","comment-dollar","chart-mixed","bullseye-arrow","star","heart","camera-retro","truck-fast","bell","cart-shopping","gift","barcode","bookmark","camera","thumbs-up","thumbs-down","key","truck","balloons","handshake","shop","shirt","bag-shopping","moped","cart-plus","credit-card","store","cash-register","booth-curtain","trophy","receipt","truck-container","truck-clock","truck-bolt","trophy-star","store-slash","store-lock","star-exclamation","shop-slash","shop-lock","rectangle-barcode","person-booth","nfc-trash","nfc-symbol","nfc-slash","nfc-signal","nfc-pen","nfc-magnifying-glass","nfc-lock","nfc","money-check-dollar","money-check","mobile-signal-out","mobile-signal","hexagon-vertical-nft-slanted","hexagon-vertical-nft","hexagon-image","hand-holding-box","gifts","gem","credit-card-front","credit-card-blank","cart-xmark","cart-shopping-fast","cart-minus","cart-circle-xmark","cart-circle-plus","cart-circle-exclamation","cart-circle-check","cart-circle-arrow-up","cart-circle-arrow-down","cart-arrow-up","cart-arrow-down","box-open-full","basket-shopping-simple","basket-shopping","barcode-scan","barcode-read","balloon","bags-shopping","bars","browser","chart-bar","cog","database","calendar-alt","play","kizen-k","add","book-alt","form-entity","arrows-to-circle","question-circle","chevron-down","chevron-up","window-restore","forward"]),Ga=[...Wa].sort(),Ka=new Set(["kizen-k","form-entity"]),uo=[...Ga,...[...Ka].sort()];function fo(e){e.command("icons").description("List all valid icon names accepted by toolbar items, pages, and adornments").action(()=>{for(let t of uo)console.log(t)})}import{mkdir as nl,writeFile as To,readFile as ol}from"fs/promises";import{join as It}from"path";var Ya=new Set(["developer_business_id"]);function Xa(e){return Array.isArray(e)?e.map(t=>{if(typeof t!="object"||t===null)return t;let n=t;if(typeof n.auth_credentials!="object"||n.auth_credentials===null)return n;let o=Object.fromEntries(Object.keys(n.auth_credentials).map(r=>[r,"*****"]));return{...n,auth_credentials:o}}):e}function go(e){let t={name:"",children:new Map,isFile:!1,filePath:""};for(let n of e){let o=n.path.split("/"),r=t;o.forEach((i,p)=>{r.children.has(i)||r.children.set(i,{name:i,children:new Map,isFile:p===o.length-1,filePath:o.slice(0,p+1).join("/")});let f=r.children.get(i);f&&(r=f)})}return t}function ho(e){return[...e.children.values()].sort((t,n)=>t.isFile!==n.isFile?t.isFile?1:-1:t.name.localeCompare(n.name))}function cr(e){return"file-"+e.replace(/[^a-zA-Z0-9]/g,"-")}function le(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function yo(e){let t=e.lastIndexOf(".");return t>=0?e.slice(t).toLowerCase():""}var Za={".ts":"typescript",".tsx":"typescript",".js":"javascript",".jsx":"javascript",".mjs":"javascript",".cjs":"javascript",".json":"json",".css":"css",".scss":"scss",".html":"html",".htm":"html",".md":"markdown",".mdx":"markdown",".py":"python",".sh":"bash",".yml":"yaml",".yaml":"yaml",".xml":"xml",".toml":"toml"};function Qa(e){return Za[yo(e)]??""}function mo(e){let t=[...e.matchAll(/`+/g)].reduce((n,o)=>Math.max(n,o[0].length),0);return"`".repeat(Math.max(3,t+1))}function bo(e,t=""){let n=ho(e);return n.map((o,r)=>{let i=r===n.length-1,p=i?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",f=i?" ":"\u2502 ";return o.isFile?`${t}${p}${o.name}`:`${t}${p}${o.name}/
465
+ ${bo(o,t+f)}`}).join(`
466
+ `)}var el=/(^|\/)licen[cs]e(\.md|\.txt)?$/i;function xo(e){return e.filter(t=>t.path!=="kizen.json"&&t.path!=="plugin-report.html"&&!el.test(t.path))}function vo(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!Ya.has(t)).map(([t,n])=>[t,t==="services"?Xa(n):n]))}var tl={".png":"image/png",".svg":"image/svg+xml",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp"};function wo(e){return`<ul>${ho(e).map(o=>o.isFile?`<li class="tf"><a href="#${cr(o.filePath)}" data-id="${cr(o.filePath)}">${le(o.name)}</a></li>`:`<li class="td"><span class="dn">${le(o.name)}/</span>${wo(o)}</li>`).join("")}</ul>`}function rl(e){let t;return e.base64Image!==void 0?t=`<img src="data:${tl[yo(e.path)]??"image/png"};base64,${e.base64Image}" alt="${le(e.path)}" style="max-width:100%">`:e.binaryData!==void 0?t=`<p class="bin">[Binary file: ${String(e.binaryData.length)} bytes]</p>`:t=`<pre><code>${le(e.content)}</code></pre>`,`<section id="${cr(e.path)}"><h2 class="fp">${le(e.path)}</h2><div class="fb">${t}</div></section>`}function Co(e,t){let n=vo(e),o=typeof e.name=="string"?e.name:"Plugin",r=typeof e.version=="string"?e.version:"",i=typeof e.description=="string"?e.description:"",p=xo(t),f=[];f.push(`# ${o}${r?` v${r}`:""}`),i&&f.push("",i);let g=JSON.stringify(n,null,2),m=mo(g);f.push("","## Configuration (`kizen.json`)","",`${m}json`,g,m),f.push("","## File Tree","","```",bo(go(p)),"```"),f.push("","## Files");for(let y of p)if(f.push("",`### \`${y.path}\``,""),y.base64Image!==void 0)f.push(`[Image file: ${y.path}]`);else if(y.binaryData!==void 0)f.push(`[Binary file: ${String(y.binaryData.length)} bytes]`);else{let T=mo(y.content);f.push(`${T}${Qa(y.path)}`,y.content,T)}return f.join(`
1111
467
  `)+`
1112
- `}function ta(e,t){let r=Zs(e),o=typeof e.name=="string"?e.name:"Plugin",n=typeof e.version=="string"?e.version:"",i=typeof e.description=="string"?e.description:"",s=Xs(t),l=Qs(Js(s)),a=s.map(hp).join(`
468
+ `}function So(e,t){let n=vo(e),o=typeof e.name=="string"?e.name:"Plugin",r=typeof e.version=="string"?e.version:"",i=typeof e.description=="string"?e.description:"",p=xo(t),f=wo(go(p)),g=p.map(rl).join(`
1113
469
  `);return`<!DOCTYPE html>
1114
470
  <html lang="en">
1115
471
  <head>
1116
472
  <meta charset="UTF-8">
1117
473
  <meta name="viewport" content="width=device-width,initial-scale=1">
1118
- <title>${we(o)} \u2014 Plugin Report</title>
474
+ <title>${le(o)} \u2014 Plugin Report</title>
1119
475
  <style>
1120
476
  *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
1121
477
  :root{
@@ -1152,17 +508,17 @@ img{border:1px solid var(--bd);border-radius:4px}
1152
508
  </head>
1153
509
  <body>
1154
510
  <header>
1155
- <h1>${we(o)}${n?` <span style="color:var(--mu);font-size:.8em">v${we(n)}</span>`:""}</h1>
1156
- ${i?`<p>${we(i)}</p>`:""}
511
+ <h1>${le(o)}${r?` <span style="color:var(--mu);font-size:.8em">v${le(r)}</span>`:""}</h1>
512
+ ${i?`<p>${le(i)}</p>`:""}
1157
513
  </header>
1158
514
  <div class="layout">
1159
515
  <nav>
1160
516
  <a href="#config" class="cfg" data-id="config">kizen.json (config)</a>
1161
- ${l}
517
+ ${f}
1162
518
  </nav>
1163
519
  <main>
1164
- <section id="config"><h2 class="fp">kizen.json \u2014 Plugin Configuration</h2><div class="fb"><pre><code>${we(JSON.stringify(r,null,2))}</code></pre></div></section>
1165
- ${a}
520
+ <section id="config"><h2 class="fp">kizen.json \u2014 Plugin Configuration</h2><div class="fb"><pre><code>${le(JSON.stringify(n,null,2))}</code></pre></div></section>
521
+ ${g}
1166
522
  </main>
1167
523
  </div>
1168
524
  <script>
@@ -1175,7 +531,9 @@ ${a}
1175
531
  })();
1176
532
  </script>
1177
533
  </body>
1178
- </html>`}var na=wr(ze,"examples");function oa(e){e.command("report").description("Generate a self-contained HTML report of the plugin").option("-o, --output <path>",`output file path (default: ${na}/<api_name>.html)`).action(async t=>{let r=process.cwd(),o;try{let a=await yp(wr(r,"kizen.json"),"utf-8"),p=JSON.parse(a);o=Array.isArray(p)?p[0]:p}catch{console.error("Error: kizen.json not found. Run this command from a plugin directory."),process.exit(1)}let n=typeof o.api_name=="string"?o.api_name:"plugin",i=t.output??wr(na,`${n}.html`),s=i.replace(/\.html$/,".md"),l=await $t(r,{detectBinaryByContent:!0});await gp(wr(i,".."),{recursive:!0}),await Promise.all([ra(i,ta(o,l),"utf-8"),ra(s,ea(o,l),"utf-8")]),console.log(`Plugin report written to:
534
+ </html>`}var Eo=It(Ne,"examples");function _o(e){e.command("report").description("Generate a self-contained HTML report of the plugin").option("-o, --output <path>",`output file path (default: ${Eo}/<api_name>.html)`).action(async t=>{let n=process.cwd(),o;try{let g=await ol(It(n,"kizen.json"),"utf-8"),m=JSON.parse(g);o=Array.isArray(m)?m[0]:m}catch{console.error("Error: kizen.json not found. Run this command from a plugin directory."),process.exit(1)}let r=typeof o.api_name=="string"?o.api_name:"plugin",i=t.output??It(Eo,`${r}.html`),p=i.replace(/\.html$/,".md"),f=await nt(n,{detectBinaryByContent:!0});await nl(It(i,".."),{recursive:!0}),await Promise.all([To(i,So(o,f),"utf-8"),To(p,Co(o,f),"utf-8")]),console.log(`Plugin report written to:
1179
535
  ${i}
1180
- ${s}`)})}function ia(e){e.command("setup-claude").description("Install or refresh the Claude Code skill for building custom blocks").option("--dry-run","report what would change without writing files").action(async t=>{await Jo(process.cwd(),t)})}function Cp(){let e=xp(wp(vp(import.meta.url)),"..","package.json");return JSON.parse(bp(e,"utf-8")).version}function sa(){let e=new kp;return e.name("appbuilder").description("Kizen plugin app builder").version(Cp()),Ki(e),ia(e),yi(e),Es(e),Us(e),oa(e),Ws(e),pi(e),e}Hn({env:process.env,http:Sp});sa().parse();
536
+ ${p}`)})}import{mkdir as il,readFile as sl,writeFile as al}from"fs/promises";import{dirname as ll,join as cl}from"path";async function dl(e){try{return await sl(e,"utf-8")}catch(t){if(t.code==="ENOENT")return;throw t}}async function pl(e,t,n){let o=cl(e,...t.path.split("/")),r=await dl(o);if(r===t.content)return{path:t.path,status:"unchanged"};let i=r===void 0?"created":"updated";return n||(await il(ll(o),{recursive:!0}),await al(o,t.content,"utf-8")),{path:t.path,status:i}}async function ko(e,t={}){if(await ye(e)!=="has-manifest")throw new Error(`No kizen.json found in ${e}`);let n=t.dryRun===!0,o=await Promise.all(lt().map(r=>pl(e,r,n)));return n||he(e),{files:o}}function Po(e){e.command("setup-copilot").description("Add or refresh the Copilot code-review setup in an existing plugin repo").option("--dry-run","report what would change without writing anything").action(async t=>{let n=process.cwd();if(await ye(n)!=="has-manifest"){console.error(`No kizen.json found in ${n} \u2014 run "appbuilder setup-copilot" from the root of a plugin repo.`),process.exitCode=1;return}let o=t.dryRun===!0;try{let r=await ko(n,{dryRun:o});for(let i of r.files)console.log(`${i.status.padEnd(9)} ${i.path}`);if(o){console.log(`
537
+ Dry run \u2014 no files and no .gitignore entries were written.`);return}console.log(`
538
+ Ensured .gitignore ignores .kizenapp/ and .copilot-docs/.`),console.log("The code review workflow only takes effect once it is on the repository default branch \u2014 merge it before expecting reviews to cite .copilot-docs/.")}catch(r){console.error(`Error: ${r instanceof Error?r.message:String(r)}`),process.exitCode=1}})}function yl(){let e=ml(fl(gl(import.meta.url)),"..","package.json");return JSON.parse(ul(e,"utf-8")).version}function Io(){let e=new hl;return e.name("appbuilder").description("Kizen plugin app builder").version(yl()),dn(e),Po(e),Er(e),Jn(e),po(e),_o(e),fo(e),e}Io().parse();
1181
539
  //# sourceMappingURL=index.js.map