@kizenapps/cli 1.13.0-44aaefe → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -0
- package/dist/index.css +2 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +784 -172
- package/dist/index.js.map +1 -1
- package/dist/viewer/assets/{CodeStepsPage-d5OMJi9J.js → CodeStepsPage-Dd84cmbe.js} +1 -1
- package/dist/viewer/assets/{ConfigurationPage-CI0pRNJ0.js → ConfigurationPage-DUiOAiMK.js} +1 -1
- package/dist/viewer/assets/CredentialsContext-p3i8s7a6.js +1 -0
- package/dist/viewer/assets/{DevSidebar-BFsoIbMt.js → DevSidebar-CkLpkWSy.js} +1 -1
- package/dist/viewer/assets/{SandboxPage-Dyb8i7vf.js → SandboxPage-D3Qdh_0M.js} +1 -1
- package/dist/viewer/assets/{SecretsPage-g2FF7d-B.js → SecretsPage-CQVPedWl.js} +1 -1
- package/dist/viewer/assets/{WhenBadge-DKz8ukwo.js → WhenBadge-CV5t88Iv.js} +1 -1
- package/dist/viewer/assets/api-C4R44YCL.js +11 -0
- package/dist/viewer/assets/{bootstrapQuery-DTAPKOf-.js → bootstrapQuery-WnoB2iya.js} +1 -1
- package/dist/viewer/assets/index-B1r1yKD-.js +5 -0
- package/dist/viewer/assets/{useCompleteSetup-QS3W9RRO.js → useCompleteSetup-BgC9V-FN.js} +4 -4
- package/dist/viewer/assets/{usePluginConfig--4sAyik4.js → usePluginConfig-mg5WevIz.js} +1 -1
- package/dist/viewer/index.html +4 -4
- package/package.json +26 -20
- package/dist/viewer/assets/CredentialsContext-Cwl_xyt_.js +0 -1
- package/dist/viewer/assets/api--pW2v3sH.js +0 -11
- package/dist/viewer/assets/index-BdpUrI71.js +0 -5
package/dist/index.js
CHANGED
|
@@ -1,163 +1,363 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readFileSync as
|
|
3
|
-
`)
|
|
4
|
-
|
|
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+`
|
|
5
8
|
`:`
|
|
6
|
-
`+
|
|
7
|
-
`;
|
|
8
|
-
"name": "Hello Frame",
|
|
9
|
-
"api_name": "hello_frame",
|
|
10
|
-
"title": "Hello Frame",
|
|
11
|
-
"default_position": "bottom-right",
|
|
12
|
-
"minimized_style": "circle",
|
|
13
|
-
"height": 320,
|
|
14
|
-
"width": 280
|
|
15
|
-
}
|
|
16
|
-
`;var Er=`this.outputUI(\`
|
|
17
|
-
<div class="hello-frame">
|
|
18
|
-
<h2 class="hello-frame__title">Hello from a floating frame</h2>
|
|
19
|
-
<p class="hello-frame__body">Edit src/floatingFrames/helloFrame/script.js to change this.</p>
|
|
20
|
-
</div>
|
|
21
|
-
\`);
|
|
22
|
-
`;var kr=`.hello-frame {
|
|
23
|
-
padding: 16px;
|
|
24
|
-
font-family: system-ui, sans-serif;
|
|
25
|
-
}
|
|
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
|
|
26
12
|
|
|
27
|
-
.
|
|
28
|
-
margin: 0 0 8px;
|
|
29
|
-
font-size: 16px;
|
|
30
|
-
}
|
|
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".
|
|
31
14
|
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
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);
|
|
35
47
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"api_name": "hello_block",
|
|
39
|
-
"types": ["dashboards"]
|
|
48
|
+
.kz-block--with-header {
|
|
49
|
+
padding-top: 0;
|
|
40
50
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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;
|
|
52
219
|
}
|
|
53
220
|
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
221
|
+
if (rows.length === 0) {
|
|
222
|
+
render('<div class="kz-state kz-state--empty">No Data</div>');
|
|
223
|
+
return;
|
|
57
224
|
}
|
|
58
225
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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);
|
|
62
277
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"color": "#0ea5e9",
|
|
66
|
-
"tooltip": "Hello adornment",
|
|
67
|
-
"field_type": "phonenumber"
|
|
278
|
+
.kz-block--with-header {
|
|
279
|
+
padding-top: 0;
|
|
68
280
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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;
|
|
73
295
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
padding: 24px;
|
|
83
|
-
font-family: system-ui, sans-serif;
|
|
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);
|
|
84
304
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
font-size: 20px;
|
|
305
|
+
.kz-state--empty {
|
|
306
|
+
font-size: 32px;
|
|
307
|
+
color: var(--color-background-medium);
|
|
89
308
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
color: #555;
|
|
309
|
+
.kz-state--error {
|
|
310
|
+
font-size: 14px;
|
|
311
|
+
color: var(--color-font-secondary);
|
|
94
312
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
color: #94a3b8;
|
|
313
|
+
.kz-chart {
|
|
314
|
+
flex: 1;
|
|
315
|
+
min-height: 0;
|
|
99
316
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
317
|
+
.kz-chart svg {
|
|
318
|
+
display: block;
|
|
319
|
+
width: 100%;
|
|
320
|
+
height: 100%;
|
|
104
321
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
font: inherit;
|
|
109
|
-
color: #0f172a;
|
|
110
|
-
background: #e2e8f0;
|
|
111
|
-
border: 0;
|
|
112
|
-
border-radius: 4px;
|
|
113
|
-
cursor: pointer;
|
|
322
|
+
.grid {
|
|
323
|
+
stroke: var(--color-border-medium-light);
|
|
324
|
+
stroke-width: 1;
|
|
114
325
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
326
|
+
.axis {
|
|
327
|
+
fill: var(--color-background-dark);
|
|
328
|
+
font-size: 10px;
|
|
118
329
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const greeting = tone === 'cheer' ? 'Hello, and good to see you!' : 'Hello from a routable page.';
|
|
122
|
-
|
|
123
|
-
this.outputUI(\`
|
|
124
|
-
<div class="hello-page">
|
|
125
|
-
<h1 class="hello-page__title">\${greeting}</h1>
|
|
126
|
-
<p class="hello-page__body">
|
|
127
|
-
This markup came from eventScripts/greet.js. Either button re-runs it and
|
|
128
|
-
repaints the page with a different greeting.
|
|
129
|
-
</p>
|
|
130
|
-
<div class="hello-page__actions">
|
|
131
|
-
<form data-script="greet">
|
|
132
|
-
<input type="hidden" name="tone" value="plain" />
|
|
133
|
-
<button class="hello-page__button" type="submit">Plain</button>
|
|
134
|
-
</form>
|
|
135
|
-
<form data-script="greet">
|
|
136
|
-
<input type="hidden" name="tone" value="cheer" />
|
|
137
|
-
<button class="hello-page__button" type="submit">Cheerful</button>
|
|
138
|
-
</form>
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
\`);
|
|
142
|
-
`;var jr=`{
|
|
143
|
-
"api_name": "hello_toolbar_item",
|
|
144
|
-
"label": "Hello Toolbar Item",
|
|
145
|
-
"icon": "star",
|
|
146
|
-
"color": "#0ea5e9"
|
|
330
|
+
.bar {
|
|
331
|
+
fill: var(--color-viz-cat-1-primary);
|
|
147
332
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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;
|
|
152
340
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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);
|
|
158
354
|
}
|
|
159
|
-
|
|
160
|
-
|
|
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=`---
|
|
161
361
|
applyTo: "src/**/*.js,kizen.json"
|
|
162
362
|
---
|
|
163
363
|
|
|
@@ -221,7 +421,7 @@ service they belong to.
|
|
|
221
421
|
Encrypt secrets locally with \`appbuilder encrypt\` before committing them. Do
|
|
222
422
|
not commit a plaintext secret value in any form; a comment, a script fixture,
|
|
223
423
|
and a commit you intend to rewrite later all count.
|
|
224
|
-
`;var
|
|
424
|
+
`;var Oo=`---
|
|
225
425
|
applyTo: "kizen.json"
|
|
226
426
|
---
|
|
227
427
|
|
|
@@ -294,15 +494,427 @@ version bump in the same PR.
|
|
|
294
494
|
| \`required_entitlement\` | add gating | MINOR |
|
|
295
495
|
| \`required_entitlement\` | change or remove | PATCH |
|
|
296
496
|
| Manifest metadata (\`name\`, \`description\`, \`entry\`, \`release_branches\`, \`release_environments\`, \`published\`, \`developer_business_id\`) | any change | PATCH |
|
|
297
|
-
`;
|
|
298
|
-
`,"utf-8"),await lt(ce(e.targetDir,"src"),{recursive:!0}),await lt(ce(e.targetDir,"releaseNotes"),{recursive:!0}),await Promise.all([Yr(e.targetDir,Ur(e.artifacts??[])),Yr(e.targetDir,Wr()),Ut(ce(e.targetDir,...es.split("/")),Gr(e.apiName))]),Ne(e.targetDir)}var he=["name","apiName","externalLink","description","developerBusinessId"],ut={name:"Name",apiName:"API name",externalLink:"External link",description:"Description",developerBusinessId:"Business ID"},qt=["name","apiName","description"],Jt=/^[a-z_][a-z0-9_]+$/,Wt="must start with a letter or underscore and contain only lowercase letters, numbers, or underscores (minimum 2 characters)";function ft(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}function Zr(e){return{name:"",apiName:"",externalLink:"",description:"",developerBusinessId:e}}function Qr(e,t){let n=t.trim();if(qt.includes(e)&&!n)return`${ut[e]} is required`;if(e==="apiName"&&!Jt.test(n))return`${ut[e]} ${Wt}`}function en(e,t){return e==="description"?t:t.trim()}function tn(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 E,jsxs as N}from"react/jsx-runtime";var ke=({text:e})=>E(I,{dimColor:!0,children:e}),rn=({parentDir:e,defaultBusinessId:t,defaultEnvironment:n})=>{let o=rs(),[r,i]=Be({type:"target-select",cursor:0}),[d,f]=Be("in-place"),[g,m]=Be(()=>Zr(t)),[b,T]=Be(()=>[...X]),[P,x]=Be(""),[p,y]=Be(e),S=mt((v,c)=>{f(v),x(c.name),i({type:"field-entry",field:0,values:c})},[]),H=mt(async v=>{let c=await dt(v);if(c!=="ok"){i({type:"precheck-error",targetDir:v,reason:c});return}y(v),S("in-place",g)},[g,S]),O=mt(async(v,c)=>{try{let a=await Xr(e,v.apiName);y(a),i({type:"confirm",values:v,artifacts:c})}catch(a){i({type:"error",message:a instanceof Error?a.message:String(a)})}},[e]),w=mt(async(v,c,a)=>{try{await pt({targetDir:c,name:v.name,apiName:v.apiName,externalLink:v.externalLink,description:v.description,developerBusinessId:v.developerBusinessId,developerEnvironment:n,artifacts:a}),i({type:"done",targetDir:c})}catch(s){i({type:"error",message:s instanceof Error?s.message:String(s)})}},[n]);if(ns((v,c)=>{if(c.ctrl&&v==="c"){o.exit();return}if(r.type==="target-select"){c.upArrow?i({type:"target-select",cursor:0}):c.downArrow?i({type:"target-select",cursor:1}):c.return?r.cursor===0?H(e):S("sub-dir",g):c.escape&&o.exit();return}if(r.type==="precheck-error"){(c.return||c.escape)&&o.exit();return}if(r.type==="field-entry"){let{field:a,values:s}=r,l=he[a];if(!l)return;if(c.escape){o.exit();return}if(c.backspace||c.delete){x(u=>u.slice(0,-1));return}if(c.upArrow&&a>0){let u={...s,[l]:P},h=a-1,k=he[h];i({type:"field-entry",field:h,values:u}),m(u),x(k?u[k]:"");return}if(c.return||c.tab||c.downArrow){let u=Qr(l,P);if(u!==void 0){i({type:"field-entry",field:a,values:s,fieldError:u});return}let h=en(l,P),k={...s,[l]:h};m(k);let _=a+1;if(_>=he.length)x(""),i({type:"artifact-select",values:k,cursor:0,selected:b});else{let A=he[_],M=A?k[A]:"",W=A==="apiName"&&!M?ft(k.name):M;i({type:"field-entry",field:_,values:k}),x(W)}return}v&&!c.ctrl&&!c.meta&&x(u=>u+v);return}if(r.type==="artifact-select"){let{cursor:a,selected:s,values:l}=r;if(c.escape){let u=he.length-1,h=he[u];i({type:"field-entry",field:u,values:l}),x(h?l[h]:"");return}if(c.upArrow){i({...r,cursor:Math.max(0,a-1)});return}if(c.downArrow){i({...r,cursor:Math.min(X.length-1,a+1)});return}if(v===" "){let u=X[a];if(u){let h=tn(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}c.return&&(d==="sub-dir"?(i({type:"resolving-target"}),O(l,s)):i({type:"confirm",values:l,artifacts:s}));return}if(r.type==="confirm"){if(c.escape){i({type:"artifact-select",values:r.values,cursor:0,selected:r.artifacts});return}c.return&&(i({type:"writing"}),w(r.values,p,r.artifacts));return}(r.type==="done"||r.type==="error")&&(c.return||c.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 N(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[E(q,{}),N(z,{flexDirection:"column",gap:0,children:[E(I,{bold:!0,children:"Where should the new plugin live?"}),v.map((c,a)=>{let s=r.cursor===a;return N(z,{gap:2,children:[E(I,{...s&&{color:"cyan"},children:s?"\u276F":" "}),E(I,{bold:s,...s&&{color:"cyan"},children:c.label}),E(I,{dimColor:!0,children:c.desc})]},c.label)})]}),E(ke,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(r.type==="precheck-error"){let{targetDir:v,reason:c}=r;return N(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[E(q,{}),E(I,{color:"red",bold:!0,children:"Cannot create plugin"}),N(z,{flexDirection:"column",gap:0,children:[N(I,{children:[v," already contains ",E(I,{bold:!0,children:c==="has-manifest"?"kizen.json":".kizenapp/"}),"."]}),E(I,{dimColor:!0,children:"Remove the conflicting file/directory or choose a different target to avoid overwriting an existing plugin."})]}),E(ke,{text:"Enter or Esc to exit"})]})}if(r.type==="field-entry"){let{field:v,fieldError:c}=r,a=d==="sub-dir"?`Target: sub-directory of ${e} (inferred from API name)`:`Target: ${p}`;return N(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[E(q,{}),N(z,{flexDirection:"column",gap:0,children:[E(I,{bold:!0,children:"Plugin details"}),E(I,{dimColor:!0,children:a})]}),c!==void 0&&N(I,{color:"red",children:["Error: ",c]}),E(z,{flexDirection:"column",gap:0,children:he.map((s,l)=>{let u=v===l,h=u?P:g[s],k=qt.includes(s),_=`${ut[s]}${k?"":" (optional)"}`;return N(z,{gap:2,children:[E(z,{width:24,children:E(I,{bold:u,...u&&{color:"cyan"},children:_})}),E(I,{...u&&{color:"cyan"},children:">"}),E(I,{children:h}),u&&E(I,{color:"cyan",children:"\u2588"})]},s)})}),E(ke,{text:"Enter/Tab/\u2193 next \xB7 \u2191 previous \xB7 Backspace delete \xB7 Esc cancel"})]})}if(r.type==="artifact-select"){let{cursor:v,selected:c}=r;return N(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[E(q,{}),N(z,{flexDirection:"column",gap:0,children:[E(I,{bold:!0,children:"Which artifacts should we scaffold?"}),E(I,{dimColor:!0,children:"Each one gets a directory with a config.json and a script that runs."})]}),E(z,{flexDirection:"column",gap:0,children:X.map((a,s)=>{let l=v===s,u=c.includes(a);return N(z,{gap:1,children:[E(I,{...l&&{color:"cyan"},children:l?"\u276F":" "}),E(I,{...u&&{color:"green"},children:u?"\u25C9":"\u25EF"}),E(I,{bold:l,...l&&{color:"cyan"},children:Lt[a]})]},a)})}),E(ke,{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=Vt({targetDir:p,name:r.values.name,apiName:r.values.apiName,externalLink:r.values.externalLink,description:r.values.description,developerBusinessId:r.values.developerBusinessId,developerEnvironment:n});return N(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[E(q,{}),N(z,{flexDirection:"column",gap:0,children:[E(I,{bold:!0,children:"Ready to create"}),N(I,{dimColor:!0,children:["Target: ",p]})]}),N(z,{flexDirection:"column",gap:0,children:[E(I,{bold:!0,children:"kizen.json preview"}),E(I,{children:JSON.stringify(v,null,2)})]}),N(z,{flexDirection:"column",gap:0,children:[E(I,{bold:!0,children:"Artifacts"}),r.artifacts.length===0?N(I,{dimColor:!0,children:[" ","none (src/ will hold only thumbnail.png)"]}):r.artifacts.map(c=>N(I,{dimColor:!0,children:[" ",Lt[c]]},c))]}),N(z,{flexDirection:"column",gap:0,children:[E(I,{dimColor:!0,children:"Also creates:"}),N(I,{dimColor:!0,children:[" ",gt(p,"src/")]}),N(I,{dimColor:!0,children:[" ",gt(p,"src/thumbnail.png")," (generated placeholder)"]}),N(I,{dimColor:!0,children:[" ",gt(p,"releaseNotes/")]}),N(I,{dimColor:!0,children:[" ",gt(p,".gitignore")," (adds .kizenapp/)"]})]}),E(ke,{text:"Enter to create \xB7 Esc to go back"})]})}return r.type==="resolving-target"?E(z,{paddingY:1,paddingX:2,children:E(I,{dimColor:!0,children:"Finding available sub-directory\u2026"})}):r.type==="writing"?E(z,{paddingY:1,paddingX:2,children:E(I,{dimColor:!0,children:"Creating plugin\u2026"})}):r.type==="done"?N(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[E(q,{}),N(I,{color:"green",bold:!0,children:["Plugin created at ",r.targetDir]}),N(z,{flexDirection:"column",gap:0,children:[E(I,{dimColor:!0,children:"Next steps:"}),N(I,{children:[" ","cd ",r.targetDir]}),N(I,{children:[" ","npx --yes @kizenapps/cli dev"]}),N(I,{dimColor:!0,children:[" ","Replace src/thumbnail.png with real artwork before publishing."]})]}),E(ke,{text:"Enter or Esc to exit"})]}):N(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[E(q,{}),E(I,{color:"red",bold:!0,children:"Failed to create plugin"}),E(I,{children:r.message}),E(ke,{text:"Enter or Esc to exit"})]})};import{mkdir as nn,readFile as os,readdir as is,writeFile as on}from"fs/promises";import{homedir as ss}from"os";import{dirname as as,join as ht}from"path";var Y=["go","fmo","staging","integration","test1"];var Pe=ht(ss(),".kizenappbuilder"),De=ht(Pe,"credentials.json"),ye="credentials";function ls(e){return Y.includes(e)}function cs(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:ls(n)?n:"go"}}async function be(e){let t=await os(e,"utf-8");return cs(JSON.parse(t))}async function Z(){try{return await be(De)}catch{return null}}async function ds(e){await nn(as(De),{recursive:!0,mode:448}),await on(De,JSON.stringify(e,null,2),{encoding:"utf-8",mode:384})}function sn(e){return ht(Pe,`${e}.json`)}async function Ie(){let e=[{name:ye,path:De,isDefault:!0}];try{let t=await is(Pe);for(let n of t){if(!n.endsWith(".json"))continue;let o=n.slice(0,-5);o!==ye&&e.push({name:o,path:ht(Pe,n),isDefault:!1})}}catch{}return e}async function an(e,t){if(e===ye){await ds(t);return}await nn(Pe,{recursive:!0,mode:448}),await on(sn(e),JSON.stringify(t,null,2),{encoding:"utf-8",mode:384})}async function de(e){try{return await be(sn(e))}catch{return null}}var ps="A hello-world Kizen plugin.";function us(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 fs(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()??"")||ft(o);if(!Jt.test(r))throw new Error(`api_name "${r}" is invalid: ${Wt}. Pass --api-name.`);let i=e.description?.trim(),d=e.businessId?.trim()??t.businessId,f=gs(e,t);return{targetDir:n,name:o,apiName:r,externalLink:e.externalLink?.trim()??"",description:i===void 0||i===""?ps:i,developerBusinessId:d,developerEnvironment:f,artifacts:us(e.artifacts)}}function ms(e){return Y.includes(e)}function gs(e,t){let n=e.environment?.trim();if(n!==void 0&&n!==""){if(!ms(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 ln(e,t,n,o){let r;try{r=fs(e,t,n)}catch(f){o.writeStderr(`Error: ${f instanceof Error?f.message:String(f)}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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 = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
|
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=`{
|
|
757
|
+
"name": "Hello Frame",
|
|
758
|
+
"api_name": "hello_frame",
|
|
759
|
+
"title": "Hello Frame",
|
|
760
|
+
"default_position": "bottom-right",
|
|
761
|
+
"minimized_style": "circle",
|
|
762
|
+
"height": 320,
|
|
763
|
+
"width": 280
|
|
764
|
+
}
|
|
765
|
+
`;var wi=`this.outputUI(\`
|
|
766
|
+
<div class="hello-frame">
|
|
767
|
+
<h2 class="hello-frame__title">Hello from a floating frame</h2>
|
|
768
|
+
<p class="hello-frame__body">Edit src/floatingFrames/helloFrame/script.js to change this.</p>
|
|
769
|
+
</div>
|
|
770
|
+
\`);
|
|
771
|
+
`;var xi=`.hello-frame {
|
|
772
|
+
padding: 16px;
|
|
773
|
+
font-family: system-ui, sans-serif;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.hello-frame__title {
|
|
777
|
+
margin: 0 0 8px;
|
|
778
|
+
font-size: 16px;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.hello-frame__body {
|
|
782
|
+
margin: 0;
|
|
783
|
+
color: #555;
|
|
784
|
+
}
|
|
785
|
+
`;var vi=`{
|
|
786
|
+
"name": "Hello Block",
|
|
787
|
+
"api_name": "hello_block",
|
|
788
|
+
"types": ["dashboards"]
|
|
789
|
+
}
|
|
790
|
+
`;var ki=`this.outputUI(\`
|
|
791
|
+
<div class="hello-block">
|
|
792
|
+
<h2 class="hello-block__title">Hello from a block</h2>
|
|
793
|
+
<p class="hello-block__body">Edit src/blocks/helloBlock/script.js to change this.</p>
|
|
794
|
+
</div>
|
|
795
|
+
\`);
|
|
796
|
+
`;var Ci=`.hello-block {
|
|
797
|
+
padding: 16px;
|
|
798
|
+
font-family: system-ui, sans-serif;
|
|
799
|
+
background: #f8fafc;
|
|
800
|
+
border-radius: 6px;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.hello-block__title {
|
|
804
|
+
margin: 0 0 8px;
|
|
805
|
+
font-size: 16px;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.hello-block__body {
|
|
809
|
+
margin: 0;
|
|
810
|
+
color: #555;
|
|
811
|
+
}
|
|
812
|
+
`;var Si=`{
|
|
813
|
+
"icon": "phone",
|
|
814
|
+
"color": "#0ea5e9",
|
|
815
|
+
"tooltip": "Hello adornment",
|
|
816
|
+
"field_type": "phonenumber"
|
|
817
|
+
}
|
|
818
|
+
`;var _i=`this.showToast('Hello from a data adornment.', { variant: 'success' });
|
|
819
|
+
`;var Pi=`{
|
|
820
|
+
"name": "Hello Page",
|
|
821
|
+
"api_name": "hello_page"
|
|
822
|
+
}
|
|
823
|
+
`;var Ei=`this.outputUI(\`
|
|
824
|
+
<div class="hello-page">
|
|
825
|
+
<p class="hello-page__loading">Loading\u2026</p>
|
|
826
|
+
</div>
|
|
827
|
+
\`);
|
|
828
|
+
|
|
829
|
+
this.runEventScript('greet');
|
|
830
|
+
`;var Ti=`.hello-page {
|
|
831
|
+
padding: 24px;
|
|
832
|
+
font-family: system-ui, sans-serif;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.hello-page__title {
|
|
836
|
+
margin: 0 0 8px;
|
|
837
|
+
font-size: 20px;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.hello-page__body {
|
|
841
|
+
margin: 0 0 16px;
|
|
842
|
+
color: #555;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.hello-page__loading {
|
|
846
|
+
margin: 0;
|
|
847
|
+
color: #94a3b8;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.hello-page__actions {
|
|
851
|
+
display: flex;
|
|
852
|
+
gap: 8px;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.hello-page__button {
|
|
856
|
+
padding: 6px 12px;
|
|
857
|
+
font: inherit;
|
|
858
|
+
color: #0f172a;
|
|
859
|
+
background: #e2e8f0;
|
|
860
|
+
border: 0;
|
|
861
|
+
border-radius: 4px;
|
|
862
|
+
cursor: pointer;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.hello-page__button:hover {
|
|
866
|
+
background: #cbd5e1;
|
|
867
|
+
}
|
|
868
|
+
`;var Ii=`const tone = this.args?.formData?.tone?.[0] ?? 'plain';
|
|
869
|
+
|
|
870
|
+
const greeting = tone === 'cheer' ? 'Hello, and good to see you!' : 'Hello from a routable page.';
|
|
871
|
+
|
|
872
|
+
this.outputUI(\`
|
|
873
|
+
<div class="hello-page">
|
|
874
|
+
<h1 class="hello-page__title">\${greeting}</h1>
|
|
875
|
+
<p class="hello-page__body">
|
|
876
|
+
This markup came from eventScripts/greet.js. Either button re-runs it and
|
|
877
|
+
repaints the page with a different greeting.
|
|
878
|
+
</p>
|
|
879
|
+
<div class="hello-page__actions">
|
|
880
|
+
<form data-script="greet">
|
|
881
|
+
<input type="hidden" name="tone" value="plain" />
|
|
882
|
+
<button class="hello-page__button" type="submit">Plain</button>
|
|
883
|
+
</form>
|
|
884
|
+
<form data-script="greet">
|
|
885
|
+
<input type="hidden" name="tone" value="cheer" />
|
|
886
|
+
<button class="hello-page__button" type="submit">Cheerful</button>
|
|
887
|
+
</form>
|
|
888
|
+
</div>
|
|
889
|
+
</div>
|
|
890
|
+
\`);
|
|
891
|
+
`;var Ai=`{
|
|
892
|
+
"api_name": "hello_toolbar_item",
|
|
893
|
+
"label": "Hello Toolbar Item",
|
|
894
|
+
"icon": "star",
|
|
895
|
+
"color": "#0ea5e9"
|
|
896
|
+
}
|
|
897
|
+
`;var Ni=`this.showToast('Hello from a toolbar item.', { variant: 'success' });
|
|
898
|
+
`;var Di=`{
|
|
899
|
+
"api_name": "hello_object_settings_item",
|
|
900
|
+
"label": "Hello Object Settings"
|
|
901
|
+
}
|
|
902
|
+
`;var Ri=`this.showToast('Hello from an object settings item.', { variant: 'success' });
|
|
903
|
+
`;var Bi=`{
|
|
904
|
+
"name": "Hello Action",
|
|
905
|
+
"api_name": "hello_action",
|
|
906
|
+
"hint_object_name": "client_client"
|
|
907
|
+
}
|
|
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}
|
|
303
915
|
`),o.writeStdout(`Thumbnail: src/thumbnail.png (generated placeholder; replace it before publishing)
|
|
304
|
-
`),
|
|
305
|
-
`)}function
|
|
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
|
|
306
918
|
bcrypt
|
|
307
919
|
boto3
|
|
308
920
|
fpdf2
|
|
@@ -320,8 +932,8 @@ pytz
|
|
|
320
932
|
requests
|
|
321
933
|
sqlalchemy>=2.0,<2.1
|
|
322
934
|
tzdata
|
|
323
|
-
`;function
|
|
324
|
-
`).map(t=>t.trim().replace(/\s+#.*$/,"")).filter(t=>t.length>0&&!t.startsWith("#"))}function
|
|
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=`
|
|
325
937
|
import sys, json, os, traceback
|
|
326
938
|
|
|
327
939
|
# --- Inputs proxy (attribute access) ---
|
|
@@ -481,29 +1093,29 @@ sys.stdout.write('\\n__KIZEN_RESULT_START__\\n')
|
|
|
481
1093
|
sys.stdout.write(json.dumps(result, ensure_ascii=False))
|
|
482
1094
|
sys.stdout.write('\\n__KIZEN_RESULT_END__\\n')
|
|
483
1095
|
sys.stdout.flush()
|
|
484
|
-
`.trim(),
|
|
485
|
-
`);for(;
|
|
486
|
-
`)}}),
|
|
487
|
-
`).trim(),b=m.length>0?`: ${m}`:"";r(new Error(`${e} exited with code ${String(g)}${b}`))}})})}var Qt=null;function Ds(e,t){if(qe)return t&&Qt?.add(t),qe;let n=new Set;t&&n.add(t),Qt=n;let o=i=>{for(let d of n)d(i)},r=Je(bt,"bin","python");return qe=(async()=>{try{let i=!1;try{await Ps(r);let d=await Cn(r);i=d!==null&&wn(d)}catch{i=!1}if(!i){await er(bt,{recursive:!0,force:!0}).catch(()=>{});let d=await Bs(e);await bn(d,["-m","venv",bt])}return o({kind:"start"}),await bn(r,["-m","pip","install","--disable-pip-version-check",...vn],(d,f)=>{o({kind:"log",line:d,stream:f})}),o({kind:"complete"}),r}catch(i){let d=i instanceof Error?i.message:String(i);throw o({kind:"error",message:d}),qe=null,await er(bt,{recursive:!0,force:!0}).catch(()=>{}),i}finally{Qt=null}})(),qe}function Fs(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(),d=e.slice(o+t.length,r).trim();try{let f=JSON.parse(d);return{userStdout:i,result:f}}catch{return{userStdout:e,result:null}}}function Os(e){return{success:!1,outputValues:{},logs:[],stdout:"",stderr:"",error:e,exitCode:1,durationMs:0}}async function Sn(e){let t=e.timeout??As;if(!Ns(e.scriptRuntime))return Os(`Unsupported runtime "${e.scriptRuntime}". Supported runtimes: ${xn.join(", ")}. Update the step's config.json "runtime" field (e.g. "python 3.13").`);let n=await Ds(e.scriptRuntime,e.onInstallProgress),o=Date.now(),r;try{r=await ks(Je(Is(),"kizen-step-"));let i=Je(r,"wrapper.py"),d=Je(r,"script.py");await Promise.all([yn(i,Rs,"utf-8"),yn(d,e.script,"utf-8")]);let f={...process.env,__KIZEN_INPUTS__:JSON.stringify(e.inputs),__KIZEN_SECRETS__:JSON.stringify(e.secrets),__KIZEN_SCRIPT_PATH__:d},{exitCode:g,stdout:m,stderr:b}=await new Promise(y=>{let S=[],H=[],O=!1,w=tr(n,[i],{env:f,cwd:r,stdio:["ignore","pipe","pipe"]}),v=setTimeout(()=>{w.kill("SIGKILL")},t);w.stdout.on("data",a=>{S.push(a)}),w.stderr.on("data",a=>{H.push(a)});let c=a=>{O||(O=!0,clearTimeout(v),y({exitCode:a??1,stdout:Buffer.concat(S).toString("utf-8"),stderr:Buffer.concat(H).toString("utf-8")}))};w.on("close",c),w.on("error",a=>{O||(O=!0,clearTimeout(v),y({exitCode:1,stdout:"",stderr:a.message}))})}),T=Date.now()-o,{userStdout:P,result:x}=Fs(m);if(x){let y={success:x.success,outputValues:x.outputValues,logs:x.logs,stdout:P,stderr:b,exitCode:g,durationMs:T};return x.error!==null&&(y.error=x.error),x.httpRequests&&(y.httpRequests=x.httpRequests),y}let p={success:!1,outputValues:{},logs:[],stdout:P,stderr:b,exitCode:g,durationMs:T};return g!==0&&(p.error=`Process exited with code ${String(g)}`),p}finally{r&&er(r,{recursive:!0,force:!0})}}var Tn=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 Pn,CRYPTO_VERSION as In,deserializeEnvelope as qs,encrypt as Js,serializeEnvelope as Ws}from"@kizenapps/packager";function rr(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 Ks=2*1024*1024,Gs=new Set([".DS_Store",".gitignore"]);function Ys(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 Bn=["kizen.com","kizen.dev"];function Xs(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:Bn.some(o=>n===o||n.endsWith("."+o))}function Zs(e){let t;try{t=new URL(e)}catch{return!1}if(t.protocol!=="https:")return!1;let n=t.hostname.toLowerCase();return Bn.some(o=>n===o||n.endsWith("."+o))}function Qs(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=Us(e,n);if(o!==e&&!o.startsWith(e+nr))return null;try{let r=await zs(o);return r!==e&&!r.startsWith(e+nr)?null:r}catch{return null}}function An(e,t){let n=t===e?"":t.slice(e.length+1);return n===""?!1:n.split(nr).some(r=>tt.has(r))}function Dn(){let e=Vs(import.meta.url);return Ke(Hs(e),"viewer")}function Nn(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 or(e){try{return await $s(e),!0}catch{return!1}}function Fn(e,t,n,o,r,i,d,f,g,m,b){let T=pn({onChange:f,enabled:b}),P=js(m);return(x,p)=>{(async()=>{let y=x.url??"/";try{if(!Ys(x.headers.host)){p.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Forbidden"}));return}if((x.method==="POST"||x.method==="PATCH"||x.method==="DELETE")&&!Qs(x.headers.origin)){p.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Forbidden"}));return}if(t(`Received request: ${y}`),y==="/api/credentials"){p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({credentials:o.current??null,activeProfile:r.current??null}));return}if(y==="/api/credential-profiles"&&x.method==="GET"){let a=await Ie();p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({profiles:a,active:r.current}));return}if(y==="/api/credentials/switch"&&x.method==="POST"){let a=[];for await(let h of x)a.push(h);let l=JSON.parse(Buffer.concat(a).toString("utf-8")||"{}").profile,u=l===void 0||l===""?await Z():await de(l);if(u!==null){o.current=u,r.current=l??void 0;let h=r.current,k=await xe(i);await ve(i,{...k,credentialMode:"global",...h!==void 0&&{activeCredentialProfile:h}}),d({type:"credentials-updated",credentials:u,activeProfile:r.current??null})}p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({ok:u!==null}));return}if(y==="/api/bundle"){let a=Ke(process.cwd(),".kizenapp","bundle.json");try{let s=await Ms(a,"utf-8");p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(s)}catch{p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end("{}")}return}if(y==="/api/last-path"&&x.method==="POST"){let a=[];for await(let l of x)a.push(l);let s=JSON.parse(Buffer.concat(a).toString("utf-8")||"{}");if(typeof s.path=="string"){g.current=s.path;let l=await xe(i);await ve(i,{...l,lastPath:s.path})}p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end('{"ok":true}');return}if(y==="/api/encryption-target"&&x.method==="GET"){let s=(await xe(i)).encryptionTarget==="dev"?"dev":"prod";p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({target:s}));return}if(y==="/api/encryption-target"&&x.method==="POST"){let a=[];for await(let h of x)a.push(h);let s=JSON.parse(Buffer.concat(a).toString("utf-8")||"{}"),l=s!==null&&typeof s=="object"?s:{};if(l.target!=="dev"&&l.target!=="prod"){p.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"target must be one of: 'dev', 'prod'"}));return}let u=await xe(i);await ve(i,{...u,encryptionTarget:l.target}),p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end('{"ok":true}');return}if(y==="/api/execute-step"&&x.method==="POST"){let a=[];for await(let h of x)a.push(h);let s=JSON.parse(Buffer.concat(a).toString("utf-8")||"{}");if(typeof s.script!="string"){p.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Missing required field: script"}));return}let l={script:s.script,scriptRuntime:s.scriptRuntime??"python-3-13",inputs:hn(s.inputs),secrets:s.secrets??{},onInstallProgress:h=>{h.kind==="start"?d({type:"venv-install-start"}):h.kind==="log"?d({type:"venv-install-log",line:h.line,stream:h.stream}):h.kind==="complete"?d({type:"venv-install-complete"}):d({type:"venv-install-error",message:h.message})}};typeof s.timeout=="number"&&s.timeout>0&&(l.timeout=Math.min(s.timeout,300*1e3));let u=await Sn(l);p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify(u));return}if(y==="/api/proxy-cache/clear"){T.clear(),n({kind:"info",message:"Proxy cache cleared"}),p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end('{"ok":true}');return}if(y==="/api/local/encrypt"&&x.method==="POST"){let a=[];for await(let l of x)a.push(l);let s=JSON.parse(Buffer.concat(a).toString("utf-8")||"{}");if(typeof s.publicKeyPem!="string"||typeof s.value!="string"){p.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Missing required fields: publicKeyPem, value"}));return}try{let l=Js(s.value,s.publicKeyPem),u=Ws(l);p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify(u))}catch(l){p.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:l.message}))}return}if(y==="/api/local/validate"&&x.method==="POST"){let a=[];for await(let _ of x)a.push(_);let s=JSON.parse(Buffer.concat(a).toString("utf-8")||"{}");if(typeof s.value!="string"){p.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Missing required field: value"}));return}let l,u;try{l=qs(s.value)}catch(_){u=_.message}if(u!==void 0||l===void 0){p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({valid:!1,checks:[],error:u}));return}let h=_=>Buffer.from(_,"base64").length,k=[{label:"Version",expected:String(In),actual:String(l.v),pass:l.v===In},{label:"Algorithm",expected:Pn,actual:l.alg,pass:l.alg===Pn},{label:"Wrapped key",expected:"384 bytes (RSA-3072)",actual:`${String(h(l.k))} bytes`,pass:h(l.k)===384},{label:"IV",expected:"12 bytes",actual:`${String(h(l.iv))} bytes`,pass:h(l.iv)===12},{label:"Auth tag",expected:"16 bytes",actual:`${String(h(l.tag))} bytes`,pass:h(l.tag)===16},{label:"Ciphertext",expected:"\u2265 1 byte",actual:`${String(h(l.ct))} bytes`,pass:h(l.ct)>=1}];p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({valid:k.every(_=>_.pass),checks:k}));return}if(y.startsWith("/api/wizard")){let a=x.headers["x-encryption-target"],l=(Array.isArray(a)?a[0]:a)==="dev"?"dev":"prod",u=Ve(l);if(!Xs(u)){p.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:`Encryption API host is not allowed: ${u}`}));return}let h=y.slice(11)||"/",k=`${u}${h}`,_=x.method??"GET",A=[];for await(let Se of x)A.push(Se);let{host:M,"x-encryption-target":W,...te}=x.headers,oe=A.length>0?Buffer.concat(A):void 0,ie=oe&&oe.length>0?oe:void 0,re;try{re=await fetch(k,{method:_,headers:te,...ie!==void 0&&{body:ie}})}catch(Se){let It=Se.code==="ECONNREFUSED"?`plugin-wizard is not running at ${u}`:`Failed to reach plugin-wizard: ${Se.message}`;p.writeHead(503,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:It}));return}let ee=yt(Object.fromEntries(re.headers)),Oe=await re.arrayBuffer();p.writeHead(re.status,ee),p.end(Buffer.from(Oe));return}if(y.startsWith("/api/proxy")){let a=x.headers["x-proxy-target"];if(typeof a!="string"){p.writeHead(400),p.end("Missing x-proxy-target header");return}if(!Zs(a)){p.writeHead(400),p.end("Invalid proxy target");return}let s=y.slice(10)||"/",l=`${a}${s}`,u=x.method??"GET",h=[];for await(let re of x)h.push(re);let{host:k,"x-proxy-target":_,...A}=x.headers;if(u==="GET"){let re=`GET::${l}`,{response:ee,fromCache:Oe}=await T.get(re,()=>fetch(l,{method:"GET",headers:A}));n({kind:"request",method:"GET",status:ee.status,upstreamStatus:Nn(s,ee.headers,ee.body),fromCache:Oe,url:s}),p.writeHead(ee.status,ee.headers),p.end(ee.body);return}let M=h.length>0?Buffer.concat(h):void 0,W=M&&M.length>0?M:void 0,te=await fetch(l,{method:u,headers:A,...W!==void 0&&{body:W}}),oe=yt(Object.fromEntries(te.headers)),ie=await Gt(te,Kt);n({kind:"request",method:u,status:te.status,upstreamStatus:Nn(s,oe,ie),fromCache:!1,url:s}),p.writeHead(te.status,oe),p.end(ie);return}if(y.startsWith("/api/source/tree")&&x.method==="GET"){let s=new URL(y,"http://localhost").searchParams.get("path")??"",l=await Rn(P,s);if(l===null){p.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Invalid path"}));return}if(An(P,l)){p.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Path is excluded"}));return}try{let u=await Ls(l,{withFileTypes:!0}),h=await Promise.all(u.filter(_=>_.isDirectory()?!tt.has(_.name):_.isFile()?!Gs.has(_.name):!1).map(async _=>{let A=_.isDirectory()?"dir":"file",M={name:_.name,kind:A};if(A==="file")try{let W=await En(Ke(l,_.name));M.size=W.size}catch{}return M}));h.sort((_,A)=>_.kind!==A.kind?_.kind==="dir"?-1:1:_.name.localeCompare(A.name));let k=l===P?"":l.slice(P.length+1);p.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({path:k,entries:h}))}catch(u){let h=u instanceof Error?u.message:String(u);t(`source/tree failed (${s}): ${h}`),p.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Not found"}))}return}if(y.startsWith("/api/source/file")&&x.method==="GET"){let s=new URL(y,"http://localhost").searchParams.get("path")??"";if(s===""){p.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Missing path"}));return}let l=await Rn(P,s);if(l===null){p.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Invalid path"}));return}if(An(P,l)){p.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Path is excluded"}));return}try{let u=await En(l);if(!u.isFile()){p.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Not a file"}));return}if(u.size>Ks){p.writeHead(413,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"File too large",size:u.size}));return}let h=kn(l).toLowerCase(),k=Yt[h],_=Tn.has(h),A=k??(_?"text/plain; charset=utf-8":"application/octet-stream");p.writeHead(200,{"Content-Type":A,"Content-Length":String(u.size)}),_n(l).pipe(p)}catch(u){let h=u instanceof Error?u.message:String(u);t(`source/file failed (${s}): ${h}`),p.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),p.end(JSON.stringify({error:"Not found"}))}return}let O=Ke(e,y==="/"?"/index.html":y),w=await or(O)?O:Ke(e,"index.html"),v=kn(w),c=Yt[v]??"application/octet-stream";p.writeHead(200,{"Content-Type":c}),_n(w).pipe(p)}catch(S){if(y.startsWith("/api/proxy")){let H=y.slice(10)||"/",O=x.method??"GET";S instanceof Ue?n({kind:"info",message:`${O} ${H} \u2192 502 (response exceeded ${String(S.limit)} bytes)`}):n({kind:"request",method:O,status:502,fromCache:!1,url:H})}else n({kind:"info",message:`Error handling ${y}: ${S instanceof Error?S.message:String(S)}`});p.headersSent||(p.writeHead(502),p.end("Bad Gateway"))}})()}}import{launch as ea}from"chrome-launcher";import{createRequire as ta}from"module";import{mkdirSync as ra}from"fs";import{readFile as na}from"fs/promises";import{dirname as oa,join as ia}from"path";import{fileURLToPath as sa}from"url";var On=ta(import.meta.url)("chrome-remote-interface"),aa=new Set(["content-security-policy","content-security-policy-report-only","x-frame-options"]),$n=["kizen.dev","kizen.com"];function jn(e){try{let{hostname:t}=new URL(e);return $n.some(n=>t===n||t.endsWith("."+n))}catch{return!1}}async function la(e){try{let t=ia(oa(sa(import.meta.url)),"viewer","icon.png"),n=await na(t);await e.Browser.setDockTile({image:n.toString("base64")})}catch{}}async function Ln(e){ra(e.userDataDir,{recursive:!0});let t=`http://localhost:${String(e.port)}`,n=await ea({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,d=null,f=null,g=0,m=!1,b=w=>{e.debugLog?.(w)},T=w=>{e.verbose&&e.debugLog?.(w)},P=()=>{i!==null&&(clearInterval(i),i=null)},x=w=>w==="chrome://new-tab-page/"||w==="chrome://newtab/",p=async w=>{P(),d=null,await la(w),await w.Fetch.enable({patterns:$n.flatMap(c=>[{urlPattern:`*://${c}/*`,requestStage:"Response"},{urlPattern:`*://*.${c}/*`,requestStage:"Response"}])}),w.on("Fetch.requestPaused",c=>{(async()=>{let a=c,{requestId:s,request:l,responseHeaders:u,responseStatusCode:h}=a,k=l.url;T(`Fetch ${String(h??"?")} ${k}`);let _=[],A=!1;for(let M of u??[])if(!aa.has(M.name.toLowerCase()))if(M.name.toLowerCase()==="set-cookie"&&jn(k)){let W=M.value.split(`
|
|
488
|
-
`).map(
|
|
489
|
-
`);_.push({name:M.name,value:W}),A=!0}else _.push(M);A&&T(` rewrote Set-Cookie SameSite on ${k}`);try{let{body:M,base64Encoded:W}=await w.Fetch.getResponseBody({requestId:s});await w.Fetch.fulfillRequest({requestId:s,responseCode:h??200,responseHeaders:_,body:M,...W&&{base64Encoded:!0}})}catch(M){T(` fulfillRequest failed (${M.message}), falling through`);try{await w.Fetch.continueRequest({requestId:s})}catch(W){T(` continueRequest also failed (${W.message})`)}}})()}),i=setInterval(()=>{(async()=>{try{let{cookies:c}=await w.Network.getAllCookies(),a=0;for(let s of c){if(!jn(`https://${s.domain??""}`)||s.sameSite==="None")continue;let l=s.domain??"",u=l.startsWith(".")?l.slice(1):l;await w.Network.setCookie({url:`https://${u}`,name:s.name,value:s.value,domain:l,path:s.path??"/",secure:!0,...s.httpOnly!==void 0&&{httpOnly:s.httpOnly},...s.expirationDate!==void 0&&{expires:s.expirationDate},sameSite:"None"}),a++}a>0&&T(`cookie-patch cycle: updated ${String(a)} cookie(s) to SameSite=None`)}catch(c){T(`cookie-patch cycle failed: ${c.message}`)}})()},2e3),await w.Target.setDiscoverTargets({discover:!0});let v=c=>{if(c.type==="page"){if(x(c.url)){b(`closed stray new-tab-page target ${c.targetId}`),d!==null&&w.Target.activateTarget({targetId:d}).catch(()=>{}),w.Target.closeTarget({targetId:c.targetId}).catch(()=>{});return}d===null&&(d=c.targetId,b(`captured main target ${c.targetId} (${c.url})`))}};w.on("Target.targetCreated",c=>{let{targetInfo:a}=c;T(`target +${a.type} ${a.targetId} ${a.url}`),v(a)}),w.on("Target.targetInfoChanged",c=>{let{targetInfo:a}=c;T(`target ~${a.type} ${a.targetId} ${a.url}`),v(a)}),w.on("disconnect",()=>{m||r!==w||(b("CDP disconnected"),r=null,P(),y())})},y=()=>{if(m||f!==null)return;let w=Math.min(1e3*2**g,1e4),v=g+1;g++,b(`CDP reconnect scheduled in ${String(w)}ms (attempt #${String(v)})`),f=setTimeout(()=>{f=null,(async()=>{let c;try{c=await On({port:o})}catch{b(`CDP reconnect attempt #${String(v)} failed`),y();return}if(m){try{await c.close()}catch{}return}r=c;try{await p(c),b(`CDP reconnected after ${String(v)} attempt(s)`),g=0}catch{r===c&&(r=null);try{await c.close()}catch{}b(`CDP reconnect attempt #${String(v)} failed during setup`),y()}})()},w)},S=30,H=300;for(let w=0;w<S;w++){await new Promise(v=>setTimeout(v,H)),b(`CDP connect attempt ${String(w+1)}/${String(S)}`);try{let v=await On({port:o});r=v,e.path&&v.send("Page.navigate",{url:`${t}${e.path}`});try{await p(v),b("CDP connected")}catch{r===v&&(r=null);try{await v.close()}catch{}b("CDP setup failed after connect")}break}catch{if(w===S-1){let v=`CDP unavailable after ${String(S)} attempts \u2014 viewer running without interception (CSP/cookie rewrites disabled)`;return b(v),e.onWarn?.(v),{kill:()=>{n.kill()}}}}}return r?{kill:()=>{if(b("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 ca,useState as da}from"react";function xt(e){let[t,n]=da([]),o=ca(r=>{n(i=>[...i,`${new Date().toLocaleTimeString()}: ${r}`].slice(-e))},[e]);return[t,o]}import{useCallback as vt,useEffect as ir,useState as wt}from"react";import{Box as V,Text as j,useInput as pa}from"ink";import{jsx as R,jsxs as J}from"react/jsx-runtime";var we=["apiKey","userId","businessId"],ua={apiKey:"API Key",userId:"User ID",businessId:"Business ID"},Ge=({text:e})=>R(j,{dimColor:!0,children:e}),Ct=({initialMode:e,showProfileManager:t,onComplete:n,onCancel:o})=>{let[r,i]=wt(e==="global"&&!t?{type:"loading"}:e==="global"&&t?{type:"profile-loading"}:{type:"mode-select",cursor:0}),[d,f]=wt(""),[g,m]=wt(null),[b,T]=wt(void 0),P=vt(c=>{if(c==="local"){n({mode:"local",credentials:null});return}i({type:"profile-loading"})},[n]),x=vt(async c=>{if(c===null){i({type:"profile-loading"});let l=await Ie();i({type:"name-entry",nameBuffer:"",existingProfiles:l});return}T(c.isDefault?void 0:c.name),i({type:"loading"});let a=c.isDefault?await Z():await de(c.name),s=a?Math.max(0,Y.indexOf(a.environment)):0;i({type:"creds-entry",field:0,values:a??{},envCursor:s})},[]),p=vt(async(c,a)=>{let s=Y[a]??"go",l={apiKey:c.apiKey??"",userId:c.userId??"",businessId:c.businessId??"",environment:s};i({type:"saving"});try{await an(b??ye,l),n({mode:"global",credentials:l,...b!==void 0&&{profileName:b}})}catch(u){m(u instanceof Error?u.message:String(u)),i({type:"creds-entry",field:0,values:c,envCursor:a})}},[n,b]);pa((c,a)=>{if(a.ctrl&&c==="c"&&process.exit(0),a.escape){if((r.type==="creds-entry"||r.type==="name-entry")&&e==="global"&&t){i({type:"profile-loading"}),y();return}o?.();return}if(r.type==="mode-select"){if(a.upArrow)i({type:"mode-select",cursor:0});else if(a.downArrow)i({type:"mode-select",cursor:1});else if(a.return){let s=r.cursor===0?"global":"local";P(s)}return}if(r.type==="profile-select"){let{profiles:s,cursor:l}=r,u=s.length,h=s.length+1;if(a.upArrow)i({...r,cursor:(l-1+h)%h});else if(a.downArrow)i({...r,cursor:(l+1)%h});else if(a.return)if(l===u)i({type:"name-entry",nameBuffer:"",existingProfiles:s});else{let k=s[l]??s[0];k&&x(k)}return}if(r.type==="name-entry"){let{nameBuffer:s,existingProfiles:l}=r;if(a.backspace||a.delete)i({type:"name-entry",nameBuffer:s.slice(0,-1),existingProfiles:l});else if(a.return){let u=s.trim();if(!u){i({...r,nameError:"Name cannot be empty"});return}if(u===ye){i({...r,nameError:`"${ye}" is reserved`});return}if(l.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 c&&!a.ctrl&&!a.meta&&i({type:"name-entry",nameBuffer:s+c,existingProfiles:l});return}if(r.type==="creds-entry"){let{field:s,values:l,envCursor:u}=r;if(s===we.length){a.leftArrow?i({...r,envCursor:(u-1+Y.length)%Y.length}):a.rightArrow?i({...r,envCursor:(u+1)%Y.length}):a.upArrow?(i({...r,field:s-1}),f(l[we[s-1]]??"")):a.return&&p(l,u);return}let k=we[s];if(!k)return;if(a.backspace||a.delete)f(_=>_.slice(0,-1));else if(a.upArrow&&s>0){let _={...l,[k]:d},A=s-1;i({...r,field:A,values:_}),f(_[we[A]]??"")}else if(a.return||a.tab||a.downArrow){let _={...l,[k]:d},A=s+1;i({...r,field:A,values:_}),A<we.length?f(_[we[A]]??""):f("")}else c&&!a.ctrl&&!a.meta&&f(_=>_+c)}});let y=vt(async()=>{let c=await Ie();i({type:"profile-select",profiles:c,cursor:0})},[]);if(ir(()=>{r.type==="profile-loading"&&y()},[r.type,y]),ir(()=>{e==="global"&&!t&&(async()=>{let c=await Z(),a=c?Math.max(0,Y.indexOf(c.environment)):0;i({type:"creds-entry",field:0,values:c??{},envCursor:a})})()},[]),ir(()=>{r.type==="creds-entry"&&r.field===0&&f(r.values.apiKey??"")},[r.type]),r.type==="loading"||r.type==="saving"||r.type==="profile-loading"){let c=r.type==="saving"?"Saving credentials\u2026":r.type==="profile-loading"?"Loading profiles\u2026":"Loading credentials\u2026";return R(V,{paddingY:1,paddingX:2,children:R(j,{dimColor:!0,children:c})})}if(r.type==="done")return null;if(r.type==="mode-select")return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[R(q,{}),J(V,{flexDirection:"column",gap:0,children:[R(j,{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((a,s)=>{let l=r.cursor===s;return J(V,{gap:2,children:[R(j,{...l&&{color:"cyan"},children:l?"\u276F":" "}),R(j,{bold:l,...l&&{color:"cyan"},children:a.label}),R(j,{dimColor:!0,children:a.desc})]},a.mode)})]}),R(Ge,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit"})]});if(r.type==="profile-select"){let{profiles:c,cursor:a}=r,s=c.length;return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[R(q,{}),J(V,{flexDirection:"column",gap:0,children:[R(j,{bold:!0,children:"Credential profiles"}),c.map((l,u)=>{let h=a===u;return J(V,{gap:2,children:[R(j,{...h&&{color:"cyan"},children:h?"\u276F":" "}),R(j,{bold:h,...h&&{color:"cyan"},children:l.isDefault?"Default":l.name}),R(j,{dimColor:!0,children:l.path})]},l.name)}),J(V,{gap:2,children:[R(j,{...a===s&&{color:"cyan"},children:a===s?"\u276F":" "}),R(j,{bold:a===s,...a===s&&{color:"cyan"},children:"+ Add new profile"})]})]}),R(Ge,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(r.type==="name-entry"){let{nameBuffer:c,nameError:a}=r;return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[R(q,{}),R(V,{flexDirection:"column",gap:0,children:R(j,{bold:!0,children:"New credential profile"})}),a&&J(j,{color:"red",children:["Error: ",a]}),J(V,{gap:2,children:[R(V,{width:12,children:R(j,{bold:!0,color:"cyan",children:"Profile name"})}),R(j,{color:"cyan",children:">"}),R(j,{children:c}),R(j,{color:"cyan",children:"\u2588"})]}),R(Ge,{text:"Type a name \xB7 Enter to confirm \xB7 Esc to go back"})]})}let{field:S,values:H,envCursor:O}=r,w=S===we.length,v=b??ye;return J(V,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[R(q,{}),J(V,{flexDirection:"column",gap:0,children:[R(j,{bold:!0,children:b!==void 0?`Profile: ${b}`:"Global credentials"}),J(j,{dimColor:!0,children:["Saved to:"," ",b!==void 0?`~/.kizenappbuilder/${b}.json`:De]})]}),g&&J(j,{color:"red",children:["Error: ",g]}),J(V,{flexDirection:"column",gap:0,children:[we.map((c,a)=>{let s=S===a,l=s?d:H[c]??"";return J(V,{gap:2,children:[R(V,{width:12,children:R(j,{bold:s,...s&&{color:"cyan"},children:ua[c]})}),R(j,{...s&&{color:"cyan"},children:">"}),R(j,{children:l}),s&&R(j,{color:"cyan",children:"\u2588"})]},c)}),J(V,{gap:2,children:[R(V,{width:12,children:R(j,{bold:w,...w&&{color:"cyan"},children:"Environment"})}),R(j,{...w&&{color:"cyan"},children:">"}),R(V,{gap:1,children:Y.map((c,a)=>{let s=a===O;return w?R(j,{bold:s,...s&&{color:"cyan"},children:s?`[${c}]`:c},c):R(j,{dimColor:!s,bold:s,children:c},c)})})]})]}),w?R(Ge,{text:"\u2190\u2192 to select \xB7 \u2191\u2193 to move \xB7 Enter to save \xB7 Esc to cancel"}):R(Ge,{text:"\u2191\u2193 to move \xB7 Backspace to delete \xB7 Esc to cancel"}),J(j,{dimColor:!0,children:["Profile: ",v]})]})};import{jsx as D,jsxs as U}from"react/jsx-runtime";var xa=[".kizenapp",".git"],zn=({port:e,pluginDir:t,outputDir:n,credentials:o,credentialMode:r,activeCredentialProfile:i,lastPath:d,debug:f=!1,verbose:g=!1,autoViewer:m=!0,cacheEnabled:b=!0})=>{let T=pe(o),P=pe(i),x=pe(d),[p,y]=Q("main"),[S,H]=Q(r),[O,w]=Q("starting"),[v,c]=Q(null),[a,s]=xt(50),[l,u]=xt(50),[h,k]=xt(50),[_,A]=Q([]),[M,W]=Q("pending"),[te,oe]=Q(null),[ie,re]=Q(null),[ee,Oe]=Q(null),[Se,It]=Q(null),[Rt,At]=Q(0),[cr,ko]=Q(0),[Po,Io]=Q(0),[Nt,Ro]=Q(0),Bt=pe(!1),dr=pe(!1),Xe=pe(null),je=pe(null),ue=pe(new Set),Ze=pe([]),ne=Fe(C=>{let L=JSON.stringify(C);if(ue.current.size===0){Ze.current=[...Ze.current,L].slice(-50);return}let fe=!1;for(let se of ue.current)if(se.readyState===1)try{se.send(L)}catch{ue.current.delete(se),fe=!0}fe&&At(ue.current.size)},[]),Qe=s,Dt=Fe(()=>{Xe.current?.kill(),Xe.current=null,Ln({port:e,userDataDir:Mn(n,".chrome"),broadcast:ne,onWarn:C=>{s(`\u26A0 ${C}`)},...x.current!==void 0&&{path:x.current},...f&&{debugLog:k},...g&&{verbose:!0}}).then(C=>{Xe.current=C})},[e,n,ne,f,g,k,s]);St(()=>{let C=()=>{Xe.current?.kill()};return process.on("exit",C),()=>{process.off("exit",C)}},[]),ma((C,L)=>{(C==="q"||L.ctrl&&C==="c")&&process.exit(0),C==="c"&&y("editing"),C==="v"&&Dt()},{isActive:p==="main"}),St(()=>{O!=="running"||dr.current||!m||(dr.current=!0,Dt())},[O,m,Dt]);let Ao=Fe(C=>{T.current=C.credentials,P.current=C.profileName,y("main"),H(C.mode);let L=C.profileName;ve(n,{credentialMode:C.mode,...L!==void 0&&{activeCredentialProfile:L}}),ne({type:"credentials-updated",credentials:C.credentials})},[n,ne]),pr=Fe(C=>{A(L=>[...L,`${new Date().toLocaleTimeString()}: ${rr(C)}`].slice(-50)),C.kind==="request"&&(C.fromCache?Io(L=>L+1):Ro(L=>L+1)),ne({type:"proxy-log",entry:C})},[ne]),Te=Fe(C=>{u(C),ne({type:"log",message:C})},[u,ne]),Ft=Fe(()=>{Bt.current||(Bt.current=!0,W("building"),oe(null),re(null),Te("Build started"),nt(t,n).then(({bundleSize:C})=>{Te(`Build finished \u2014 bundle: ${$e(C)}`),W("done"),Oe(new Date),It(C),Te("Notifying viewers to reload"),ne({type:"rebuild"})}).catch(C=>{C instanceof ga?(re(C.issues),oe(null)):(oe(C instanceof Error?C.message:String(C)),re(null)),W("error")}).finally(()=>{Bt.current=!1}))},[t,n,Te,ne]);if(St(()=>{Ft();let C=ha(t,{recursive:!0},(L,fe)=>{if(!fe)return;let se=fe.replace(/\\/g,"/");xa.some(et=>se.startsWith(et))||(Te(`File change detected: ${fe}`),je.current!==null&&clearTimeout(je.current),je.current=setTimeout(Ft,150))});return C.on("error",L=>{Te(`File watcher error \u2014 rebuilds have stopped: ${L.message}`)}),()=>{C.close(),je.current!==null&&clearTimeout(je.current)}},[t,Ft,Te]),St(()=>{let C=Dn();or(Mn(C,"index.html")).then(L=>{if(!L){c("Viewer not built. Run 'pnpm build:viewer' first."),w("error");return}let fe=Fn(C,Qe,pr,T,P,n,ne,ko,x,t,b),se=ya(fe),et=new ba({server:se});return et.on("connection",_e=>{for(let Bo of Ze.current)_e.readyState===1&&_e.send(Bo);Ze.current=[],Qe("Viewer connected for live reload"),ue.current.add(_e),At(ue.current.size),_e.on("close",()=>{ue.current.delete(_e),At(ue.current.size)})}),se.listen(e,"127.0.0.1",()=>{w("running"),Qe(`Server started on 127.0.0.1:${String(e)}`)}),se.on("error",_e=>{c(_e.message),w("error")}),()=>{et.close(),se.close()}})},[e,Qe,pr,ne,n,t,b]),p==="editing")return D(Ct,{...S!==void 0&&{initialMode:S},showProfileManager:S==="global",onComplete:Ao,onCancel:()=>{y("main")}});let Ot=ee!==null?Math.round((Date.now()-ee.getTime())/1e3):null,No=Ot===null?"":Ot<5?" (just now)":` (${String(Ot)}s ago)`,ur=ie?.filter(C=>C.severity==="error").length??0;return U(K,{flexDirection:"column",paddingY:1,paddingX:2,children:[D(q,{marginBottom:1}),U(K,{flexDirection:"column",children:[U(K,{gap:2,marginBottom:0,children:[D(B,{bold:!0,dimColor:!0,children:"App Build"}),M==="pending"&&D(B,{dimColor:!0,children:"waiting\u2026"}),M==="building"&&U(K,{gap:1,children:[D(me,{}),D(B,{children:"Building plugin package\u2026"})]}),M==="done"&&U(K,{gap:1,children:[D(B,{color:"green",children:"\u2713 Built"}),ee!==null&&U(B,{dimColor:!0,children:["at ",ee.toLocaleTimeString(),No]}),Se!==null&&U(B,{dimColor:!0,children:["\xB7 bundle: ",$e(Se)]})]}),M==="error"&&(ie!==null?U(B,{color:"red",children:["\u2717 ",ur," validation ",ur===1?"error":"errors"]}):U(B,{color:"red",children:["\u2717 ",te??"unknown error"]}))]}),ie!==null&&D(it,{issues:ie}),D(K,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:l.slice(-8).map((C,L)=>D(B,{dimColor:!0,wrap:"truncate",children:C},L))})]}),U(K,{marginTop:1,flexDirection:"column",children:[U(K,{gap:2,children:[D(B,{bold:!0,dimColor:!0,children:"Web Server"}),O==="starting"&&D(B,{dimColor:!0,children:"starting\u2026"}),O==="running"&&U(K,{gap:2,children:[D(B,{color:"green",children:"\u2713 Running"}),U(B,{dimColor:!0,children:["\u25CF ",Rt," viewer",Rt!==1?"s":""]})]}),O==="error"&&U(B,{color:"red",children:["\u2717 ",v??"Server error"]})]}),D(K,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:a.slice(-8).map((C,L)=>D(B,{dimColor:!0,wrap:"truncate",children:C},L))})]}),U(K,{marginTop:1,flexDirection:"column",children:[U(K,{gap:2,children:[D(B,{bold:!0,dimColor:!0,children:"Network Proxy"}),D(B,{color:"green",children:"\u2713 Active"}),D(B,{dimColor:!0,children:b?`\u25CF ${String(cr)} key${cr!==1?"s":""} in the network cache \xB7 ${String(Po)} hit \xB7 ${String(Nt)} miss`:`\u25CF network cache disabled \xB7 ${String(Nt)} request${Nt!==1?"s":""}`})]}),D(K,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:_.length===0?D(B,{dimColor:!0,children:" No requests yet."}):_.slice(-8).map((C,L)=>D(B,{dimColor:!0,wrap:"truncate",children:C},L))})]}),f&&U(K,{marginTop:1,flexDirection:"column",children:[U(K,{gap:2,children:[D(B,{bold:!0,dimColor:!0,children:"CDP Debug"}),U(B,{dimColor:!0,children:["\u25CF ",h.length," event(s)"]})]}),D(K,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:h.length===0?D(B,{dimColor:!0,children:" No CDP events yet."}):h.slice(-8).map((C,L)=>D(B,{dimColor:!0,wrap:"truncate",children:C},L))})]}),U(K,{marginTop:1,gap:1,children:[D(B,{dimColor:!0,children:"["}),D(B,{children:"q"}),D(B,{dimColor:!0,children:"] quit ["}),D(B,{children:"c"}),D(B,{dimColor:!0,children:"] credentials ["}),D(B,{children:"v"}),U(B,{dimColor:!0,children:["] ",Rt===0?"open viewer":"relaunch viewer"]})]})]})};async function Hn(e){return new Promise(t=>{let n=null,o=i=>{n?.(),t(i)},{unmount:r}=Vn(Un(Ct,{...e!==void 0&&{initialMode:e},onComplete:o}),{exitOnCtrlC:!1});n=r})}function qn(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`;Ne(o);let i=null,d,f,{lastPath:g}=await xe(r);if(t.credentials)i=await be(t.credentials);else{let p=await xe(r);if(p.credentialMode==="local")d="local";else if(p.credentialMode==="global"){if(d="global",f=p.activeCredentialProfile,f?(i=await de(f),i||(i=await Z(),f=void 0)):i=await Z(),!i){let y=await Hn("global");i=y.credentials,f=y.profileName}}else{let y=await Hn();i=y.credentials,d=y.mode,f=y.profileName;let S=y.profileName;await ve(r,{credentialMode:y.mode,...S!==void 0&&{activeCredentialProfile:S}})}}let m=t.verbose===!0,b=m||t.debug===!0,T=t.viewer!==!1,P=t.cache!==!1,{waitUntilExit:x}=Vn(Un(zn,{port:n,pluginDir:o,outputDir:r,credentials:i,...d!==void 0&&{credentialMode:d},...f!==void 0&&{activeCredentialProfile:f},...g!==void 0&&{lastPath:g},debug:b,verbose:m,autoViewer:T,cacheEnabled:P}),{exitOnCtrlC:!1});await x()})}import{createElement as Oa}from"react";import{render as ja}from"ink";import{useCallback as _t,useEffect as Xn,useState as Et}from"react";import{Box as ae,Text as $,useApp as Ra,useInput as Aa}from"ink";import{encrypt as va,serializeEnvelope as wa}from"@kizenapps/packager";function Ca(e){let t=e;return t.code==="ECONNREFUSED"||t.cause?.code==="ECONNREFUSED"}function Jn(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 Wn(e,t,n){try{return await fetch(`${e}${t}`,n)}catch(o){throw new Error(Ca(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 Sa(e,t,n){let o=Ve(e.stage),r=await Wn(o,"/get-public-key",{method:"POST",headers:Jn(t),body:JSON.stringify({api_name:n})}),i=await Kn(r);if(!r.ok||typeof i.public_key!="string"){let d=typeof i.error=="string"?i.error:`HTTP ${String(r.status)}`;throw new Error(`Failed to fetch public key: ${d}`)}return i.public_key}function Ta(e,t){let n=va(t,e),o=wa(n);if(typeof o.value!="string")throw new Error("Unexpected serialized envelope shape from @kizenapps/packager");return o.value}async function _a(e,t,n,o){let r=Ve(e.stage),i=await Wn(r,"/encrypt",{method:"POST",headers:Jn(t),body:JSON.stringify({api_name:n,value:o})}),d=await Kn(i);if(!i.ok||d.encrypted!==!0||typeof d.value!="string"){let f=typeof d.error=="string"?d.error:`HTTP ${String(i.status)}`;throw new Error(`Encryption via plugin-wizard failed: ${f}`)}return d.value}async function Tt(e,t,n,o,r){if(e.isRemote)return r?.("encrypting"),_a(e,t,n,o);r?.("fetching-key");let i=await Sa(e,t,n);return r?.("encrypting"),Ta(i,o)}import{spawn as Ea}from"child_process";import{platform as Gn}from"process";function ka(){return Gn==="darwin"?[{cmd:"pbcopy",args:[]}]:Gn==="win32"?[{cmd:"clip",args:[]}]:[{cmd:"wl-copy",args:[]},{cmd:"xclip",args:["-selection","clipboard"]},{cmd:"xsel",args:["--clipboard","--input"]}]}var Pa=2e3;function Ia(e,{cmd:t,args:n}){return new Promise(o=>{let r;try{r=Ea(t,n,{stdio:["pipe","ignore","ignore"]})}catch{o(!1);return}let i=r,d=!1;function f(m){d||(d=!0,clearTimeout(g),o(m))}let g=setTimeout(()=>{i.kill(),f(!1)},Pa);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 Yn(e){for(let t of ka())if(await Ia(e,t))return!0;return!1}import{Fragment as kt,jsx as F,jsxs as G}from"react/jsx-runtime";var sr=({text:e})=>F($,{dimColor:!0,children:e}),Zn=({ctx:e,stageDefaulted:t,credentialsPath:n,initialApiName:o,defaultApiName:r,initialValue:i,onDone:d})=>{let f=Ra(),[g,m]=Et({type:"loading"}),[b,T]=Et(null),[P,x]=Et(o??""),[p,y]=Et(i??""),S=_t(s=>{process.exitCode=1,m({type:"error",message:s instanceof Error?s.message:String(s)})},[]),H=_t(async(s,l,u)=>{try{let h=await Tt(e,s,l,u,A=>{m(A==="fetching-key"?{type:"fetching-key"}:{type:"encrypting"})}),k=JSON.stringify({encrypted:!0,value:h},null,2);d?.(h);let _=await Yn(k);m({type:"done",envelope:k,copied:_})}catch(h){S(h)}},[e,S,d]),O=_t((s,l,u)=>{if(l.trim()===""){m({type:"api-name-entry",buffer:r??""});return}if(u===""){m({type:"value-entry",buffer:""});return}H(s,l.trim(),u)},[r,H]),w=_t(async s=>{try{m({type:"loading"});let l=s.isDefault?await Z():await de(s.name);if(l===null)throw new Error(`Could not load credential profile: ${s.name}`);T(l),O(l,P,p)}catch(l){S(l)}},[P,p,O,S]);Xn(()=>{(async()=>{try{if(n!==void 0){let u;try{u=await be(n)}catch(h){throw new Error(`Could not load credentials from ${n}: ${h instanceof Error?h.message:String(h)}`)}T(u),O(u,P,p);return}let s=await Ie();if(s.length===0)throw new Error("No credential profiles found. Create ~/.kizenappbuilder/credentials.json first.");let[l]=s;if(s.length===1&&l!==void 0){let u=l.isDefault?await Z():await de(l.name);if(u===null)throw new Error(`Could not load credential profile: ${l.name}`);T(u),O(u,P,p);return}m({type:"profile-select",profiles:s,cursor:0})}catch(s){S(s)}})()},[]),Xn(()=>{(g.type==="done"||g.type==="error")&&f.exit()},[g,f]),Aa((s,l)=>{if(l.ctrl&&s==="c"){process.exitCode=130,f.exit();return}if(g.type==="profile-select"){let{profiles:u,cursor:h}=g;if(l.upArrow)m({...g,cursor:(h-1+u.length)%u.length});else if(l.downArrow)m({...g,cursor:(h+1)%u.length});else if(l.return){let k=u[h];k!==void 0&&w(k)}return}if(g.type==="api-name-entry"){let{buffer:u}=g;if(l.backspace||l.delete)m({type:"api-name-entry",buffer:u.slice(0,-1)});else if(l.return){let h=u.trim();if(h===""){m({type:"api-name-entry",buffer:u,error:"api_name cannot be empty"});return}x(h),b?O(b,h,p):S(new Error("Internal error: credentials were not loaded"))}else s&&!l.ctrl&&!l.meta&&m({type:"api-name-entry",buffer:u+s});return}if(g.type==="value-entry"){let{buffer:u}=g;if(l.backspace||l.delete)m({type:"value-entry",buffer:u.slice(0,-1)});else if(l.return){if(u===""){m({type:"value-entry",buffer:u,error:"secret cannot be empty"});return}y(u),b?O(b,P,u):S(new Error("Internal error: credentials were not loaded"))}else s&&!l.ctrl&&!l.meta&&m({type:"value-entry",buffer:u+s});return}});let v=e.isRemote?"remote Kizen API":"on-machine",c=t===!0?`${e.stage} (default)`:e.stage,a=s=>G(ae,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[F(q,{}),G($,{dimColor:!0,children:["Mode: ",v," Stage: ",c]}),s]});if(g.type==="loading")return a(G(ae,{gap:1,children:[F(me,{}),F($,{dimColor:!0,children:"Loading credentials\u2026"})]}));if(g.type==="profile-select"){let{profiles:s,cursor:l}=g;return a(G(kt,{children:[F($,{bold:!0,children:"Credential profile"}),F(ae,{flexDirection:"column",children:s.map((u,h)=>{let k=l===h;return G(ae,{gap:2,children:[F($,{...k&&{color:"cyan"},children:k?"\u276F":" "}),F($,{bold:k,...k&&{color:"cyan"},children:u.isDefault?"Default":u.name}),F($,{dimColor:!0,children:u.path})]},u.name)})}),F(sr,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit"})]}))}return g.type==="api-name-entry"?a(G(kt,{children:[F($,{bold:!0,children:"Plugin api_name"}),g.error!==void 0&&G($,{color:"red",children:["Error: ",g.error]}),G(ae,{gap:1,children:[F($,{color:"cyan",children:">"}),F($,{children:g.buffer}),F($,{color:"cyan",children:"\u2588"})]}),F(sr,{text:"Type the api_name \xB7 Enter to confirm \xB7 Ctrl+C to quit"})]})):g.type==="value-entry"?a(G(kt,{children:[F($,{bold:!0,children:"Secret value"}),g.error!==void 0&&G($,{color:"red",children:["Error: ",g.error]}),G(ae,{gap:1,children:[F($,{color:"cyan",children:">"}),F($,{children:"\u2022".repeat(g.buffer.length)}),F($,{color:"cyan",children:"\u2588"})]}),F(sr,{text:"Input hidden \xB7 Enter to encrypt \xB7 Ctrl+C to quit"})]})):g.type==="fetching-key"?a(G(ae,{gap:1,children:[F(me,{}),G($,{children:["Fetching ",e.stage," public key for ",F($,{bold:!0,children:P}),"\u2026"]})]})):g.type==="encrypting"?a(G(ae,{gap:1,children:[F(me,{}),F($,{children:"Encrypting\u2026"})]})):g.type==="error"?a(G(ae,{gap:1,children:[F($,{color:"red",children:"\u2717"}),F($,{color:"red",children:g.message})]})):a(G(kt,{children:[G(ae,{gap:1,children:[F($,{color:"green",children:"\u2713"}),F($,{bold:!0,children:"Encrypted secret ready"})]}),g.copied?F($,{color:"green",children:"\u2713 Copied to clipboard \u2014 paste it into the secret value in your kizen.json."}):F($,{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($,{children:g.envelope})]}))};import{readFile as Na,writeFile as Ba}from"fs/promises";import{join as Da}from"path";function Qn(e){return{encrypted:!0,value:e}}function eo(e){return JSON.stringify(Qn(e))}async function ar(e,t){await Ba(e,`${JSON.stringify(Qn(t),null,2)}
|
|
490
|
-
`,"utf-8")}var
|
|
491
|
-
`}async function
|
|
492
|
-
`),
|
|
493
|
-
`),t.out!==void 0)try{await
|
|
494
|
-
`)}catch(
|
|
495
|
-
`),
|
|
496
|
-
`)}catch(
|
|
497
|
-
${
|
|
498
|
-
`)}var
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}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(`
|
|
499
1111
|
`)+`
|
|
500
|
-
`}function
|
|
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(`
|
|
501
1113
|
`);return`<!DOCTYPE html>
|
|
502
1114
|
<html lang="en">
|
|
503
1115
|
<head>
|
|
504
1116
|
<meta charset="UTF-8">
|
|
505
1117
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
506
|
-
<title>${
|
|
1118
|
+
<title>${we(o)} \u2014 Plugin Report</title>
|
|
507
1119
|
<style>
|
|
508
1120
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
509
1121
|
:root{
|
|
@@ -540,17 +1152,17 @@ img{border:1px solid var(--bd);border-radius:4px}
|
|
|
540
1152
|
</head>
|
|
541
1153
|
<body>
|
|
542
1154
|
<header>
|
|
543
|
-
<h1>${
|
|
544
|
-
${i?`<p>${
|
|
1155
|
+
<h1>${we(o)}${n?` <span style="color:var(--mu);font-size:.8em">v${we(n)}</span>`:""}</h1>
|
|
1156
|
+
${i?`<p>${we(i)}</p>`:""}
|
|
545
1157
|
</header>
|
|
546
1158
|
<div class="layout">
|
|
547
1159
|
<nav>
|
|
548
1160
|
<a href="#config" class="cfg" data-id="config">kizen.json (config)</a>
|
|
549
|
-
${
|
|
1161
|
+
${l}
|
|
550
1162
|
</nav>
|
|
551
1163
|
<main>
|
|
552
|
-
<section id="config"><h2 class="fp">kizen.json \u2014 Plugin Configuration</h2><div class="fb"><pre><code>${
|
|
553
|
-
${
|
|
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}
|
|
554
1166
|
</main>
|
|
555
1167
|
</div>
|
|
556
1168
|
<script>
|
|
@@ -563,7 +1175,7 @@ ${g}
|
|
|
563
1175
|
})();
|
|
564
1176
|
</script>
|
|
565
1177
|
</body>
|
|
566
|
-
</html>`}var
|
|
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:
|
|
567
1179
|
${i}
|
|
568
|
-
${
|
|
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();
|
|
569
1181
|
//# sourceMappingURL=index.js.map
|