@kahunalabs/cli 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import{createRequire as y40}from"node:module";var x40=Object.create;var{getProto
3
3
  attempted loading from: `+J+" and package: "+B+`
4
4
  `;if(Q)D+="Error finding package.json: "+Q.message+`
5
5
  `;if(W)D+="Error resolving package: "+W.message+`
6
- `;throw Error(D);function _(v){var u=iR(N7.join(v,"prebuilds")).map(P80),x=u.filter(M80(lA,cA)).sort(F80)[0];if(!x)return;return M(N7.join(v,"prebuilds",x.name))}function M(v){var u=iR(v).map(w80),x=u.filter(j80(nR,oR)),F=x.sort(T80(nR))[0];if(F)return N7.join(v,F.file)}};function iR(J){try{return A_0.readdirSync(J)}catch($){return[]}}function z80(J,$){var Q=iR(J).filter($);return Q[0]&&N7.join(J,Q[0])}function A80(J){return/\.node$/.test(J)}function P80(J){var $=J.split("-");if($.length!==2)return;var Q=$[0],Y=$[1].split("+");if(!Q)return;if(!Y.length)return;if(!Y.every(Boolean))return;return{name:J,platform:Q,architectures:Y}}function M80(J,$){return function(Q){if(Q==null)return!1;if(Q.platform!==J)return!1;return Q.architectures.includes($)}}function F80(J,$){return J.architectures.length-$.architectures.length}function w80(J){var $=J.split("."),Q=$.pop(),Y={file:J,specificity:0};if(Q!=="node")return;for(var Z=0;Z<$.length;Z++){var G=$[Z];if(G==="node"||G==="electron"||G==="node-webkit")Y.runtime=G;else if(G==="napi")Y.napi=!0;else if(G.slice(0,3)==="abi")Y.abi=G.slice(3);else if(G.slice(0,2)==="uv")Y.uv=G.slice(2);else if(G.slice(0,4)==="armv")Y.armv=G.slice(4);else if(G==="glibc"||G==="musl")Y.libc=G;else continue;Y.specificity++}return Y}function j80(J,$){return function(Q){if(Q==null)return!1;if(Q.runtime!==J&&!L_0(Q))return!1;if(Q.abi!==$&&!Q.napi)return!1;if(Q.uv&&Q.uv!==L80)return!1;if(Q.armv&&Q.armv!==rR)return!1;if(Q.libc&&Q.libc!==q80)return!1;return!0}}function L_0(J){return J.runtime==="node"&&J.napi}function T80(J){return function($,Q){if($.runtime!==Q.runtime)return $.runtime===J?-1:1;else if($.abi!==Q.abi)return $.abi?-1:1;else if($.specificity!==Q.specificity)return $.specificity>Q.specificity?-1:1;else return 0}}function P_0(){return!!(process.versions&&process.versions.nw)}function M_0(){if(process.versions&&process.versions.electron)return!0;if(process.env.ELECTRON_RUN_AS_NODE)return!0;return typeof window<"u"&&window.process&&window.process.type==="renderer"}function F_0(J){if(J!=="linux")return!1;let{familySync:$,MUSL:Q}=D80();return $()===Q}U1.parseTags=w80;U1.matchTags=j80;U1.compareTags=T80;U1.parseTuple=P80;U1.matchTuple=M80;U1.compareTuples=F80});var I80=bQ((Iu0,tR)=>{var aR=typeof __webpack_require__==="function"?__non_webpack_require__:i9;if(typeof aR.addon==="function")tR.exports=aR.addon.bind(aR);else tR.exports=R80()});var v80=bQ((vu0,E80)=>{var __dirname="/Users/Kalle/dev/projects/kingsmaker/node_modules/.bun/msgpackr-extract@3.0.4/node_modules/msgpackr-extract";E80.exports=I80()(__dirname)});import{realpathSync as GL0}from"node:fs";import{pathToFileURL as XL0}from"node:url";import{parseArgs as HL0}from"node:util";import{chmodSync as d40,mkdirSync as u40,readFileSync as f40,renameSync as p40,writeFileSync as c40}from"node:fs";import{homedir as oI}from"node:os";import nZ from"node:path";var A={ok:0,failed:1,usage:2,unauthenticated:3,forbidden:4,notFound:5,conflict:6};class O extends Error{exitCode;hint;constructor(J,$=A.failed,Q){super(J);this.name="CliError",this.exitCode=$,this.hint=Q}}var g40=(J)=>typeof J==="object"&&J!==null&&(("code"in J)||("status"in J)),h40={UNAUTHENTICATED:"Not signed in.",NOT_A_MEMBER:"Your account is not a member of this organization.",DEACTIVATED:"Your account is deactivated in this organization.",NO_ACTIVE_ORG:"Your session has no active organization.",FORBIDDEN:"You do not have permission to do that.",MANAGED_READONLY:"That concept is managed by an integration and cannot be edited here.",VERSION_CONFLICT:"The record changed while this command was running.",NOT_FOUND:"No such record.",CONFLICT:"That name is already taken.",VALIDATION:"A value was rejected by the field's rules.",ILLEGAL_TRANSITION:"That enum field does not allow moving straight to that value.",RELATION_TARGET_MISMATCH:"That record belongs to a different concept than the relation targets.",FIELD_CONFIG_INVALID:"The field configuration is not valid for that kind.",CONCEPT_IN_USE:"That concept still has records.",FIELD_IN_USE:"That field still holds values, or something references it.",RECORD_VERSION_IN_USE:"Another record still links to that version.",VERSIONING_IN_USE:"Versioning cannot be switched off while drafts or multiple versions exist.",TASK_STATUS_IN_USE:"Tasks still hold that status.",TASK_PRIORITY_IN_USE:"Tasks still hold that priority.",VERSION_FROZEN:"That version is published, so it cannot be edited.",DRAFT_EXISTS:"There is already an open draft for that record.",SINGLE_RECORD_PROTECTED:"A single-record concept always keeps its one record.",SINGLE_RECORD_CONFLICT:"That concept has more than one record, so it cannot become single-record.",RECORD_NOT_PUBLISHED:"That record has no published version yet.",ATTACHMENT_TOO_LARGE:"The file is larger than the server accepts."},m40={UNAUTHENTICATED:"Run `allt auth login` first.",NO_ACTIVE_ORG:"Ask an administrator to add you to the organization.",VERSION_CONFLICT:"Re-run the command; it re-reads before writing.",CONCEPT_IN_USE:"Delete or archive its records first, or archive the concept instead.",FIELD_IN_USE:"Archive the field instead — archiving keeps the data and is reversible.",VERSIONING_IN_USE:"Publish or discard the open drafts first.",VERSION_FROZEN:"Open a new draft: `allt record version create <id>`.",DRAFT_EXISTS:"Publish or discard it: `allt record version publish|discard <id>`.",ILLEGAL_TRANSITION:"`allt concept get <concept>` shows the field's allowed values."},O7=(J)=>{switch(J){case 401:return A.unauthenticated;case 403:return A.forbidden;case 404:return A.notFound;case 409:return A.conflict;default:return A.failed}},__=(J)=>{if(J instanceof O)return{message:J.message,hint:J.hint,exitCode:J.exitCode};if(g40(J)){let $=J.code??"";return{message:h40[$]??($||J.message||"Request failed"),hint:m40[$],exitCode:O7(J.status)}}if(J instanceof Error)return{message:J.message,exitCode:A.failed};return{message:String(J),exitCode:A.failed}};var rI=()=>nZ.join(process.env.XDG_CONFIG_HOME||nZ.join(oI(),".config"),"kahuna"),iI=()=>nZ.join(rI(),"config.json"),l40=()=>["allting","kingsmaker"].map((J)=>nZ.join(process.env.XDG_CONFIG_HOME||nZ.join(oI(),".config"),J,"config.json")),h3=()=>{for(let J of[iI(),...l40()])try{return JSON.parse(f40(J,"utf8"))}catch($){if($?.code!=="ENOENT")throw new O(`${J} is not valid JSON.`,A.failed,"Fix or delete the file, then run `kahuna auth login` again.")}return{}},sB=(J)=>{let $=rI();u40($,{recursive:!0,mode:448});let Q=nZ.join($,`.config.json.${process.pid}`);c40(Q,`${JSON.stringify(J,null,2)}
6
+ `;throw Error(D);function _(v){var u=iR(N7.join(v,"prebuilds")).map(P80),x=u.filter(M80(lA,cA)).sort(F80)[0];if(!x)return;return M(N7.join(v,"prebuilds",x.name))}function M(v){var u=iR(v).map(w80),x=u.filter(j80(nR,oR)),F=x.sort(T80(nR))[0];if(F)return N7.join(v,F.file)}};function iR(J){try{return A_0.readdirSync(J)}catch($){return[]}}function z80(J,$){var Q=iR(J).filter($);return Q[0]&&N7.join(J,Q[0])}function A80(J){return/\.node$/.test(J)}function P80(J){var $=J.split("-");if($.length!==2)return;var Q=$[0],Y=$[1].split("+");if(!Q)return;if(!Y.length)return;if(!Y.every(Boolean))return;return{name:J,platform:Q,architectures:Y}}function M80(J,$){return function(Q){if(Q==null)return!1;if(Q.platform!==J)return!1;return Q.architectures.includes($)}}function F80(J,$){return J.architectures.length-$.architectures.length}function w80(J){var $=J.split("."),Q=$.pop(),Y={file:J,specificity:0};if(Q!=="node")return;for(var Z=0;Z<$.length;Z++){var G=$[Z];if(G==="node"||G==="electron"||G==="node-webkit")Y.runtime=G;else if(G==="napi")Y.napi=!0;else if(G.slice(0,3)==="abi")Y.abi=G.slice(3);else if(G.slice(0,2)==="uv")Y.uv=G.slice(2);else if(G.slice(0,4)==="armv")Y.armv=G.slice(4);else if(G==="glibc"||G==="musl")Y.libc=G;else continue;Y.specificity++}return Y}function j80(J,$){return function(Q){if(Q==null)return!1;if(Q.runtime!==J&&!L_0(Q))return!1;if(Q.abi!==$&&!Q.napi)return!1;if(Q.uv&&Q.uv!==L80)return!1;if(Q.armv&&Q.armv!==rR)return!1;if(Q.libc&&Q.libc!==q80)return!1;return!0}}function L_0(J){return J.runtime==="node"&&J.napi}function T80(J){return function($,Q){if($.runtime!==Q.runtime)return $.runtime===J?-1:1;else if($.abi!==Q.abi)return $.abi?-1:1;else if($.specificity!==Q.specificity)return $.specificity>Q.specificity?-1:1;else return 0}}function P_0(){return!!(process.versions&&process.versions.nw)}function M_0(){if(process.versions&&process.versions.electron)return!0;if(process.env.ELECTRON_RUN_AS_NODE)return!0;return typeof window<"u"&&window.process&&window.process.type==="renderer"}function F_0(J){if(J!=="linux")return!1;let{familySync:$,MUSL:Q}=D80();return $()===Q}U1.parseTags=w80;U1.matchTags=j80;U1.compareTags=T80;U1.parseTuple=P80;U1.matchTuple=M80;U1.compareTuples=F80});var I80=bQ((Iu0,tR)=>{var aR=typeof __webpack_require__==="function"?__non_webpack_require__:i9;if(typeof aR.addon==="function")tR.exports=aR.addon.bind(aR);else tR.exports=R80()});var v80=bQ((vu0,E80)=>{var __dirname="/home/runner/work/kahuna/kahuna/node_modules/.bun/msgpackr-extract@3.0.4/node_modules/msgpackr-extract";E80.exports=I80()(__dirname)});import{realpathSync as GL0}from"node:fs";import{pathToFileURL as XL0}from"node:url";import{parseArgs as HL0}from"node:util";import{chmodSync as d40,mkdirSync as u40,readFileSync as f40,renameSync as p40,writeFileSync as c40}from"node:fs";import{homedir as oI}from"node:os";import nZ from"node:path";var A={ok:0,failed:1,usage:2,unauthenticated:3,forbidden:4,notFound:5,conflict:6};class O extends Error{exitCode;hint;constructor(J,$=A.failed,Q){super(J);this.name="CliError",this.exitCode=$,this.hint=Q}}var g40=(J)=>typeof J==="object"&&J!==null&&(("code"in J)||("status"in J)),h40={UNAUTHENTICATED:"Not signed in.",NOT_A_MEMBER:"Your account is not a member of this organization.",DEACTIVATED:"Your account is deactivated in this organization.",NO_ACTIVE_ORG:"Your session has no active organization.",FORBIDDEN:"You do not have permission to do that.",MANAGED_READONLY:"That concept is managed by an integration and cannot be edited here.",VERSION_CONFLICT:"The record changed while this command was running.",NOT_FOUND:"No such record.",CONFLICT:"That name is already taken.",VALIDATION:"A value was rejected by the field's rules.",ILLEGAL_TRANSITION:"That enum field does not allow moving straight to that value.",RELATION_TARGET_MISMATCH:"That record belongs to a different concept than the relation targets.",FIELD_CONFIG_INVALID:"The field configuration is not valid for that kind.",CONCEPT_IN_USE:"That concept still has records.",FIELD_IN_USE:"That field still holds values, or something references it.",RECORD_VERSION_IN_USE:"Another record still links to that version.",VERSIONING_IN_USE:"Versioning cannot be switched off while drafts or multiple versions exist.",TASK_STATUS_IN_USE:"Tasks still hold that status.",TASK_PRIORITY_IN_USE:"Tasks still hold that priority.",VERSION_FROZEN:"That version is published, so it cannot be edited.",DRAFT_EXISTS:"There is already an open draft for that record.",SINGLE_RECORD_PROTECTED:"A single-record concept always keeps its one record.",SINGLE_RECORD_CONFLICT:"That concept has more than one record, so it cannot become single-record.",RECORD_NOT_PUBLISHED:"That record has no published version yet.",ATTACHMENT_TOO_LARGE:"The file is larger than the server accepts."},m40={UNAUTHENTICATED:"Run `allt auth login` first.",NO_ACTIVE_ORG:"Ask an administrator to add you to the organization.",VERSION_CONFLICT:"Re-run the command; it re-reads before writing.",CONCEPT_IN_USE:"Delete or archive its records first, or archive the concept instead.",FIELD_IN_USE:"Archive the field instead — archiving keeps the data and is reversible.",VERSIONING_IN_USE:"Publish or discard the open drafts first.",VERSION_FROZEN:"Open a new draft: `allt record version create <id>`.",DRAFT_EXISTS:"Publish or discard it: `allt record version publish|discard <id>`.",ILLEGAL_TRANSITION:"`allt concept get <concept>` shows the field's allowed values."},O7=(J)=>{switch(J){case 401:return A.unauthenticated;case 403:return A.forbidden;case 404:return A.notFound;case 409:return A.conflict;default:return A.failed}},__=(J)=>{if(J instanceof O)return{message:J.message,hint:J.hint,exitCode:J.exitCode};if(g40(J)){let $=J.code??"";return{message:h40[$]??($||J.message||"Request failed"),hint:m40[$],exitCode:O7(J.status)}}if(J instanceof Error)return{message:J.message,exitCode:A.failed};return{message:String(J),exitCode:A.failed}};var rI=()=>nZ.join(process.env.XDG_CONFIG_HOME||nZ.join(oI(),".config"),"kahuna"),iI=()=>nZ.join(rI(),"config.json"),l40=()=>["allting","kingsmaker"].map((J)=>nZ.join(process.env.XDG_CONFIG_HOME||nZ.join(oI(),".config"),J,"config.json")),h3=()=>{for(let J of[iI(),...l40()])try{return JSON.parse(f40(J,"utf8"))}catch($){if($?.code!=="ENOENT")throw new O(`${J} is not valid JSON.`,A.failed,"Fix or delete the file, then run `kahuna auth login` again.")}return{}},sB=(J)=>{let $=rI();u40($,{recursive:!0,mode:448});let Q=nZ.join($,`.config.json.${process.pid}`);c40(Q,`${JSON.stringify(J,null,2)}
7
7
  `,{mode:384}),d40(Q,384),p40(Q,iI())},aB=(J)=>J.replace(/\/+$/,""),O_=(J=h3())=>{let $=process.env.KAHUNA_HOST??J.host;if(!$)throw new O("No deployment configured.",A.usage,"Run `kahuna auth login --host <url>`, or set KAHUNA_HOST.");return aB($)},d0=()=>{let J=h3(),$=O_(J);if(!J.cookie)throw new O("Not signed in.",A.unauthenticated,"Run `kahuna auth login` first.");return{host:$,cookie:J.cookie,email:J.email}};var q_=(J)=>{if(J===null||J===void 0)return"";if(J instanceof Date)return J.toISOString();if(typeof J==="object")return JSON.stringify(J);return String(J)},m3=(J)=>{let $=0;for(let Q of J){let Y=Q.codePointAt(0)??0;$+=Y>=4352&&(Y<=4447||Y>=11904&&Y<=42191||Y>=65280&&Y<=65376||Y>=127744&&Y<=129791)?2:1}return $},n40=(J,$)=>{if(J.length===0)return"";let Q=$??[...new Set(J.flatMap((H)=>Object.keys(H)))],Y=J.map((H)=>Q.map((K)=>q_(H[K]))),Z=Q.map((H,K)=>Math.max(m3(H),...Y.map((B)=>m3(B[K]??"")))),G=(H)=>H.map((K,B)=>K+" ".repeat(Math.max(0,(Z[B]??0)-m3(K)))).join(" ").trimEnd();return[G(Q.map((H)=>H.toUpperCase())),...Y.map(G)].join(`
8
8
  `)},L_=(J,$)=>{let Q=$??[...new Set(J.flatMap((Z)=>Object.keys(Z)))],Y=(Z)=>{let G=q_(Z);return/[",\r\n]/.test(G)?`"${G.replaceAll('"','""')}"`:G};return[Q.join(","),...J.map((Z)=>Q.map((G)=>Y(Z[G])).join(","))].join(`
9
9
  `)},o40=(J,$,Q)=>{switch(J){case"json":return JSON.stringify($,null,2);case"csv":return L_($,Q);default:return n40($,Q)}},k0=(J,$,Q)=>{if(J==="table"&&$.length===0)return;let Y=o40(J,$,Q);if(Y)process.stdout.write(`${Y}
@@ -63,7 +63,7 @@ ${Q.map((Y)=>` ${Y}`).join(`
63
63
  `),j(`Wrote ${V.length} rows to ${N}.`)}else process.stdout.write(`${D}
64
64
  `)}if(Q){let K=JSON.stringify(H,null,2),B=J.flags.out;if(B){let{writeFileSync:W}=await import("node:fs");W(B,`${K}
65
65
  `),j(`Wrote ${G.length} concepts to ${B}.`)}else process.stdout.write(`${K}
66
- `)}})}},{path:"record import",summary:"Create or update records from a CSV or JSON file",usage:"record import <concept> <file> [--key <field>] [--dry-run]",options:{key:{type:"string"}},run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <concept> <file>.",A.usage);let Y;try{Y=dq0(Q,"utf8")}catch{throw new O(`Cannot read ${Q}.`,A.notFound)}let Z=Q.endsWith(".json")?JSON.parse(Y):l90(Y);if(!Array.isArray(Z))throw new O("Expected a JSON array of objects.",A.usage);if(Z.length===0){j("Nothing to import — the file has no rows.");return}await n90(async(G)=>{let{concept:H,fields:K}=await n4(G,$),B=Object.keys(Z[0]??{}).filter((F)=>F!=="id"),W=new Map,V=[];for(let F of B){let S=K.find((I)=>I.name.toLowerCase()===F.trim().toLowerCase());if(S)W.set(F,S);else V.push(F)}if(V.length>0)throw new O(`${H.name} has no field named ${V.map((F)=>`"${F}"`).join(", ")}.`,A.notFound,`Columns available: ${K.map((F)=>F.name).join(", ")}`);let N=J.flags.key?L4(K,J.flags.key):void 0,D=N?await G.call((F)=>F.listRecords({conceptId:H.id})):[],_=new Map;for(let F of D){let S=F.state[N?.id??""];if(typeof S==="string")_.set(S.toLowerCase(),F)}let M=0,v=0,u=[];for(let[F,S]of Z.entries()){let I={};for(let[h,f]of W){let r=S[h];if(r===void 0)continue;I[f.id]=r===""?null:fq0(f,String(r))}let o=N?String(S[N.name]??"").toLowerCase():"",a=N&&o?_.get(o):void 0;if(J.flags["dry-run"]){if(a)v++;else M++;continue}try{if(a)await v9(async()=>(await G.call((h)=>h.getRecord({id:a.id}))).recordVersion,(h)=>G.call((f)=>f.updateRecord({id:a.id,expectedVersion:h.version,patch:I}))),v++;else await G.call((h)=>h.createRecord({conceptId:H.id,fields:I})),M++}catch(h){let f=h;u.push({row:F+2,error:f.code??f.message??String(h)})}}let x=J.flags["dry-run"]?"Would import":"Imported";if(j(`${x}: ${M} created, ${v} updated, ${u.length} failed.`),u.length>0)throw k0("table",u),new O(`${u.length} row(s) failed.`,A.failed,"Row numbers count the header as line 1.")})}}],fq0=(J,$)=>{switch(J.kind){case"number":case"money":{let Q=Number($);if(Number.isNaN(Q))throw new O(`"${$}" is not a number.`,A.usage);return Q}case"bool":return["true","yes","1"].includes($.toLowerCase());case"json":try{return JSON.parse($)}catch{throw new O(`"${J.name}" needs valid JSON.`,A.usage)}default:return $}};import{spawn as lq0}from"node:child_process";import{fileURLToPath as nq0}from"node:url";var kQ={name:"@kahunalabs/cli",version:"0.0.13",description:"The Kahuna command line — kahuna.",type:"module",bin:{kahuna:"./dist/index.js"},engines:{node:">=20"},scripts:{typecheck:"tsc -p .",test:"vitest run",start:"bun index.ts",build:"bun build index.ts --target=node --outdir dist --minify",prepublishOnly:"bun run build"},devDependencies:{"@effect/platform":"0.96.1","@effect/rpc":"0.75.1","@kahunalabs/contract":"workspace:*","@types/node":"25.9.2",effect:"3.21.3",vitest:"4.1.11"},files:["dist","README.md","LICENSE"],license:"MIT",repository:{type:"git",url:"https://github.com/debuglebowski/kahuna.git",directory:"packages/cli"},keywords:["kahuna","cli"],publishConfig:{access:"public"}};import{existsSync as cq0}from"node:fs";import W_ from"node:path";var y3="@kahunalabs/cli",r90=()=>(process.env.KAHUNA_REGISTRY||"https://registry.npmjs.org").replace(/\/+$/,""),i90=(J)=>{let $=/^v?(\d+)\.(\d+)\.(\d+)$/.exec(J.trim());if(!$)return null;return[Number($[1]),Number($[2]),Number($[3])]},s90=(J,$)=>{let Q=i90(J),Y=i90($);if(!Q||!Y)return!1;for(let Z=0;Z<3;Z++)if(Q[Z]!==Y[Z])return(Q[Z]??0)>(Y[Z]??0);return!1},a90=async()=>{let J=`${r90()}/${y3.replace("/","%2F")}/latest`,$;try{$=await fetch(J,{headers:{accept:"application/json"}})}catch(Y){throw new O(`Cannot reach the npm registry at ${r90()} (${Y instanceof Error?Y.message:String(Y)}).`,A.failed,"Check your network, or point KAHUNA_REGISTRY at a mirror.")}if(!$.ok)throw new O(`The registry did not answer for ${y3} (HTTP ${$.status}).`,A.failed);let Q=await $.json().catch(()=>null);if(!Q?.version)throw new O(`The registry returned no version for ${y3}.`,A.failed);return Q.version},oB=(J,$)=>J.includes($.replaceAll("/",W_.sep)),t90=(J)=>{let $=`${W_.sep}node_modules${W_.sep}`,Q=J.indexOf($);if(Q===-1)return"source";if(oB(J,"/_npx/"))return"npx";if(oB(J,"/.bun/install/global/"))return"bun";if(oB(J,"/pnpm/global/"))return"pnpm";if(oB(J,"/.config/yarn/global/")||oB(J,"/.yarn/global/"))return"yarn";let Y=J.slice(0,Q);return cq0(W_.join(Y,"package.json"))?"local":"npm"},e90=(J)=>{let $=`${y3}@latest`;switch(J){case"bun":return{command:"bun",args:["add","-g",$]};case"pnpm":return{command:"pnpm",args:["add","-g",$]};case"yarn":return{command:"yarn",args:["global","add",$]};default:return{command:"npm",args:["install","-g",$]}}},U_=({command:J,args:$})=>[J,...$].join(" "),J40=(J)=>{switch(J){case"npx":return{message:"This CLI is running from an npx cache, so there is no install to update.",hint:`Run \`npx ${y3}@latest\` to get the newest version.`};case"local":return{message:"This CLI is a dependency of a project, not a global install.",hint:`Run \`npm install ${y3}@latest\` in that project — updating it from here would rewrite its lockfile.`};default:return{message:"This CLI is running from a source checkout or a linked build.",hint:"Update it with git, then `bun run cli:build`."}}};var oq0=async(J)=>new Promise(($,Q)=>{let Y=lq0(J.command,[...J.args],{stdio:["ignore",2,2],shell:process.platform==="win32"});Y.on("error",(Z)=>{Q(new O(Z.code==="ENOENT"?`\`${J.command}\` is not on your PATH.`:`Could not run \`${J.command}\` (${Z.message}).`,A.failed,`Run \`${U_(J)}\` yourself.`))}),Y.on("close",(Z)=>{if(Z===0)return $();Q(new O(`\`${U_(J)}\` exited ${Z??"abnormally"}.`,A.failed,"If that was a permissions error, re-run it with sudo — or move your global prefix somewhere you own."))})}),$40=[{path:"cli update",summary:"Upgrade this CLI to the newest published version",usage:"cli update [--dry-run] [--json]",run:async(J)=>{let $=kQ.version,Q=await a90();if(!s90(Q,$)){if(F6(J.format,{current:$,latest:Q,updateAvailable:!1}),J.format!=="json")j("Already on the newest published version.");return}let Y=t90(nq0(import.meta.url));if(Y==="local"||Y==="npx"||Y==="source"){let{message:H,hint:K}=J40(Y);throw new O(`${H} ${Q} is published.`,A.failed,K)}let Z=e90(Y),G=U_(Z);if(J.flags["dry-run"]){if(F6(J.format,{current:$,latest:Q,updateAvailable:!0,manager:Y,command:G,updated:!1}),J.format!=="json")j(`Would run: ${G}`);return}if(J.format!=="json")j(`Updating ${$} -> ${Q} with \`${G}\`…`);if(await oq0(Z),F6(J.format,{current:$,latest:Q,updateAvailable:!0,manager:Y,command:G,updated:!0}),J.format!=="json")j(`Updated to ${Q}. Run \`allt --version\` to confirm.`)}}];var A7=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},Q40=["text","number","date","bool","enum","relation","file","computed","user","json","money","richtext"],Y40={cardinality:{type:"string"},"inverse-name":{type:"string"},"inverse-plural-name":{type:"string"},multiple:{type:"boolean"},format:{type:"string"},"option-color":{type:"string",multiple:!0},transition:{type:"string",multiple:!0}},Z40=(J,$)=>{let Q=$.cardinality;if(Q!==void 0){if(Q!=="one"&&Q!=="many")throw new O("--cardinality takes one or many.",A.usage);J.cardinality=Q}if($["inverse-name"]!==void 0)J.inverseName=$["inverse-name"];if($["inverse-plural-name"]!==void 0)J.inversePluralName=$["inverse-plural-name"];if($.multiple)J.multiple=!0;if($.format!==void 0)J.format=$.format;let Y=vQ($["option-color"],"option=hex");if(Y.length>0){let G={...J.optionColors};for(let[H,K]of Y)G[H]=K;J.optionColors=G}let Z=vQ($.transition,"from=to,to2");if(Z.length>0){let G={...J.transitions};for(let[H,K]of Z)G[H]=K.split(",").map((B)=>B.trim()).filter(Boolean);J.transitions=G}},X40=[{path:"concept list",summary:"List the concepts in this organization",usage:"concept list [--archived] [--counts] [--json]",options:{archived:{type:"boolean"},counts:{type:"boolean"}},run:async(J)=>{await A7(async($)=>{let Q=await $.call((Y)=>Y.listConcepts({includeArchived:Boolean(J.flags.archived),withCounts:Boolean(J.flags.counts)}));k0(J.format,Q.map((Y)=>({slug:Y.slug,name:Y.name,records:Y.recordCount??"",versioned:Y.versioningEnabled?"yes":"",single:Y.singleRecord?"yes":"",archived:Y.archivedAt?"yes":"",id:Y.id})),["slug","name","records","versioned","single","archived","id"])})}},{path:"concept get",summary:"Show one concept and its fields",usage:"concept get <concept> [--json]",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await A7(async(Q)=>{let{concept:Y,fields:Z}=await n4(Q,$);if(J.format==="json"){F6(J.format,{...Y,fields:Z});return}F6(J.format,{slug:Y.slug,name:Y.name,plural:Y.pluralName??"",description:Y.description??"",versioning:Y.versioningEnabled?"on":"off",singleRecord:Y.singleRecord?"yes":"no",titleField:Z.find((G)=>G.id===Y.titleFieldId)?.name??"(none)",id:Y.id}),j(""),k0("table",Z.map((G)=>({field:G.name,kind:G.kind,required:G.config.requirement==="required"?"yes":"",unique:G.config.unique?"yes":"",target:G.config.target??"",id:G.id})),["field","kind","required","unique","target","id"])})}},{path:"concept create",summary:"Create a concept",usage:"concept create <name> [--color <hex>] [--description <text>] [--plural <name>]",options:{color:{type:"string"},description:{type:"string"},plural:{type:"string"},icon:{type:"string"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("A name is required.",A.usage);await A7(async(Q)=>{if(J.flags["dry-run"]){j(`Would create concept "${$}".`);return}let Y=await Q.call((K)=>K.createConcept({name:$,color:J.flags.color??null})),Z=J.flags.description,G=J.flags.plural,H=J.flags.icon;if(Z||G||H)await Q.call((K)=>K.updateConcept({id:Y.id,description:Z??null,pluralName:G,icon:H}));j(`Created concept "${Y.name}" (${Y.slug}).`)})}},{path:"concept update",summary:"Rename a concept or change its settings",usage:"concept update <concept> [--name <n>] [--plural <n>] [--description <t>] [--icon <i>] [--color <hex>] [--versioning on|off] [--title-field <field>] [--single-record on|off]",options:{name:{type:"string"},plural:{type:"string"},description:{type:"string"},icon:{type:"string"},color:{type:"string"},versioning:{type:"string"},"title-field":{type:"string"},"single-record":{type:"string"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await A7(async(Q)=>{let{concept:Y,fields:Z}=await n4(Q,$),G=(N,D)=>{if(D===void 0)return;if(D==="on"||D==="true")return!0;if(D==="off"||D==="false")return!1;throw new O(`--${N} takes on or off.`,A.usage)},H=G("versioning",J.flags.versioning),K=G("single-record",J.flags["single-record"]),B=J.flags["title-field"];if(J.flags["dry-run"]){j(`Would update "${Y.name}".`);return}let W={id:Y.id,description:J.flags.description??Y.description,name:J.flags.name,pluralName:J.flags.plural,icon:J.flags.icon,color:J.flags.color,versioningEnabled:H};if(W.name!==void 0||W.pluralName!==void 0||W.icon!==void 0||W.color!==void 0||H!==void 0||J.flags.description!==void 0)await Q.call((N)=>N.updateConcept(W));if(B!==void 0){let N=L4(Z,B);await Q.call((D)=>D.setConceptTitleField({id:Y.id,titleFieldId:N.id}))}if(K!==void 0)await Q.call((N)=>N.setConceptSingleRecord({conceptId:Y.id,singleRecord:K}));j(`Updated "${Y.name}".`)})}},{path:"concept archive",summary:"Archive a concept",usage:"concept archive <concept>",run:async(J)=>G40(J.args[0],J,"archive")},{path:"concept restore",summary:"Restore an archived concept",usage:"concept restore <concept>",run:async(J)=>G40(J.args[0],J,"restore")},{path:"concept delete",summary:"PURGE a concept and everything in it",usage:"concept delete <concept> --yes",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);x9(J.flags,`permanently delete "${$}" and all of its records`),await A7(async(Q)=>{let Y=await Q.call((G)=>G.listConcepts({includeArchived:!0})),Z=q4(Y,$);if(J.flags["dry-run"]){j(`Would PURGE "${Z.name}" and every record in it. This cannot be undone.`);return}await Q.call((G)=>G.deleteConcept({id:Z.id})),j(`Purged "${Z.name}".`)})}},{path:"concept field list",summary:"List a concept's fields",usage:"concept field list <concept> [--archived]",options:{archived:{type:"boolean"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await A7(async(Q)=>{let Y=await Q.call((H)=>H.listConcepts({})),Z=q4(Y,$),G=await Q.call((H)=>H.listFields({conceptId:Z.id,includeArchived:Boolean(J.flags.archived)}));k0(J.format,G.map((H)=>({name:H.name,kind:H.kind,position:H.position,required:H.config.requirement==="required"?"yes":"",unique:H.config.unique?"yes":"",target:H.config.target??"",archived:H.archivedAt?"yes":"",id:H.id})),["name","kind","position","required","unique","target","archived","id"])})}},{path:"concept field add",summary:"Add a field to a concept",usage:"concept field add <concept> --name <n> --kind <kind> [--options a,b,c] [--target <concept>] [--cardinality one|many] [--inverse-name <n>] [--inverse-plural-name <n>] [--multiple] [--format <fmt>] [--option-color <opt>=<hex>]... [--transition <from>=<to,to2>]... [--required] [--unique]",options:{name:{type:"string"},kind:{type:"string"},options:{type:"string"},target:{type:"string"},required:{type:"boolean"},unique:{type:"boolean"},icon:{type:"string"},...Y40},run:async(J)=>{let[$]=J.args,Q=J.flags.name,Y=J.flags.kind;if(!$||!Q||!Y)throw new O("Need <concept> --name <n> --kind <kind>.",A.usage);if(!Q40.includes(Y))throw new O(`Unknown field kind "${Y}".`,A.usage,`Kinds: ${Q40.join(", ")}`);await A7(async(Z)=>{let G=await Z.call((W)=>W.listConcepts({})),H=q4(G,$),K={};if(J.flags.options)K.options=String(J.flags.options).split(",").map((W)=>W.trim()).filter(Boolean);if(J.flags.required)K.requirement="required";if(J.flags.unique)K.unique=!0;if(Z40(K,J.flags),Y==="relation"){let W=J.flags.target;if(!W)throw new O("A relation field needs --target <concept>.",A.usage,"The target is the concept the edges point at.");K.target=q4(G,W).id}if(J.flags["dry-run"]){j(`Would add ${Y} field "${Q}" to "${H.name}".`);return}let B=await Z.call((W)=>W.addField({conceptId:H.id,name:Q,kind:Y,config:K,icon:J.flags.icon??null}));j(`Added "${B.name}" (${B.kind}) to "${H.name}".`)})}},{path:"concept field update",summary:"Rename a field or change its config",usage:"concept field update <concept> <field> [--name <n>] [--options a,b,c] [--cardinality one|many] [--inverse-name <n>] [--inverse-plural-name <n>] [--multiple] [--format <fmt>] [--option-color <opt>=<hex>]... [--transition <from>=<to,to2>]... [--required] [--unique]",options:{name:{type:"string"},options:{type:"string"},required:{type:"boolean"},unique:{type:"boolean"},icon:{type:"string"},...Y40},run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <concept> <field>.",A.usage);await A7(async(Y)=>{let{concept:Z,fields:G}=await n4(Y,$),H=L4(G,Q),K={...H.config};if(J.flags.options)K.options=String(J.flags.options).split(",").map((W)=>W.trim()).filter(Boolean);if(J.flags.required)K.requirement="required";if(J.flags.unique)K.unique=!0;if(Z40(K,J.flags),J.flags["dry-run"]){j(`Would update field "${H.name}" on "${Z.name}".`);return}let B=await Y.call((W)=>W.updateField({id:H.id,name:J.flags.name,config:K,icon:J.flags.icon}));j(`Updated "${B.name}".`)})}},{path:"concept field reorder",summary:"Set the field order for a concept",usage:"concept field reorder <concept> <field> [<field> ...]",run:async(J)=>{let[$,...Q]=J.args;if(!$||Q.length===0)throw new O("Need <concept> and at least one field.",A.usage);await A7(async(Y)=>{let{concept:Z,fields:G}=await n4(Y,$),H=Q.map((W)=>L4(G,W)),K=G.filter((W)=>!H.some((V)=>V.id===W.id)),B=[...H,...K].map((W,V)=>({id:W.id,position:V}));if(J.flags["dry-run"]){j(`Would order: ${[...H,...K].map((W)=>W.name).join(", ")}`);return}await Y.call((W)=>W.reorderFields({conceptId:Z.id,orders:B})),j(`Reordered ${B.length} fields on "${Z.name}".`)})}},{path:"concept field archive",summary:"Archive a field (its data is kept)",usage:"concept field archive <concept> <field>",run:async(J)=>RI(J,"archive")},{path:"concept field restore",summary:"Restore an archived field",usage:"concept field restore <concept> <field>",run:async(J)=>RI(J,"restore")},{path:"concept field delete",summary:"PURGE a field and its values",usage:"concept field delete <concept> <field> --yes",run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <concept> <field>.",A.usage);x9(J.flags,`permanently delete field "${Q}"`),await RI(J,"delete")}}],G40=async(J,$,Q)=>{if(!J)throw new O("Which concept?",A.usage);await A7(async(Y)=>{let Z=await Y.call((H)=>H.listConcepts({includeArchived:!0})),G=q4(Z,J);if($.flags["dry-run"]){j(`Would ${Q} "${G.name}".`);return}await Y.call((H)=>Q==="archive"?H.archiveConcept({id:G.id}):H.restoreConcept({id:G.id})),j(`${Q==="archive"?"Archived":"Restored"} "${G.name}".`)})},RI=async(J,$)=>{let[Q,Y]=J.args;if(!Q||!Y)throw new O("Need <concept> <field>.",A.usage);await A7(async(Z)=>{let G=await Z.call((W)=>W.listConcepts({includeArchived:!0})),H=q4(G,Q),K=await Z.call((W)=>W.listFields({conceptId:H.id,includeArchived:!0})),B=L4(K,Y);if(J.flags["dry-run"]){j(`Would ${$} field "${B.name}" on "${H.name}".`);return}await Z.call((W)=>$==="archive"?W.archiveField({id:B.id}):$==="restore"?W.restoreField({id:B.id}):W.deleteField({id:B.id})),j(`${$==="delete"?"Purged":$==="archive"?"Archived":"Restored"} "${B.name}".`)})};var W40=[{type:"metric",description:"A single hero number — count, sum, or average over a concept's records. Can show a delta versus N days ago."},{type:"list",description:"A filterable, sortable table of a concept's records. Supports a flat, grouped-by-field, or status-row layout."},{type:"breakdown",description:"A chart breaking a concept's records down by a grouping field. Renders as bars, pie, donut, stacked, or a table."},{type:"attention",description:"Surfaces records that need attention, banded by how long they've gone quiet (cooling/cold/heating/steady)."},{type:"trend",description:"A time-series chart of event volume for a concept, bucketed by day or week, with an optional delta header."},{type:"activity",description:"A recent-activity feed of events on a concept's records, with optional inline diffs of what changed."},{type:"analytics",description:"An aggregated time-series pulled from PostHog — active users, event counts, or a custom HogQL query."},{type:"tasks",description:"A task list with assignee, status, and due-date filters, and an optional inline composer."},{type:"members",description:"The organization's member roster, with configurable columns (role/email/joined) and sorting."},{type:"welcome",description:"A greeting tile showing an org activity pulse and a set of curated quick links."},{type:"goal",description:"A metric tracked against a manual target — progress toward a quota, or staying under a budget."},{type:"shortcuts",description:"Hand-picked links to records, dashboards, or URLs. Fully manual — no filters or live data."},{type:"note",description:"Free-form rich text pinned to the canvas, like a sticky note."},{type:"kanban",description:"A drag-and-drop board of records in columns keyed by an enum field."},{type:"calendar",description:"Records from one or more concepts plotted on a calendar by a date field, in month, week, or agenda view."},{type:"gantt",description:"A Gantt timeline — records as bars between a start and end date field, with optional progress bars and swimlanes."},{type:"files",description:"A file browser scoped to a record, a concept, the whole org, or the widget's own private bucket."},{type:"document",description:"An inline rich-text editor bound to one record's richtext field, autosaving as you type."},{type:"record-details",description:"The current record's field values. Only meaningful on a record dashboard."},{type:"record-connections",description:"The current record's relations to other records. Only meaningful on a record dashboard."},{type:"record-graph",description:"A graph visualization of the current record's connections. Only meaningful on a record dashboard."},{type:"record-labels",description:"The current record's labels. Only meaningful on a record dashboard."},{type:"record-versions",description:"The current record's version history. Only meaningful on a record dashboard."},{type:"record-notes",description:"Notes attached to the current record. Only meaningful on a record dashboard."},{type:"record-tasks",description:"Tasks attached to the current record. Only meaningful on a record dashboard."},{type:"record-activity",description:"The current record's activity feed. Only meaningful on a record dashboard."},{type:"record-mentions",description:"Places elsewhere that @-mention the current record. Only meaningful on a record dashboard."}],H40=W40.map((J)=>J.type),R5=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},U40=(J,$)=>{let Q=$.trim().toLowerCase(),Y=J.find((G)=>G.id===$)??J.find((G)=>G.name.toLowerCase()===Q);if(Y)return Y;let Z=J.filter((G)=>G.name.toLowerCase().startsWith(Q));if(Z.length===1&&Z[0])return Z[0];throw new O(Z.length>1?`"${$}" matches ${Z.length} dashboards.`:`No dashboard named "${$}".`,Z.length>1?A.usage:A.notFound,Z.length>1?Z.map((G)=>` ${G.name}`).join(`
66
+ `)}})}},{path:"record import",summary:"Create or update records from a CSV or JSON file",usage:"record import <concept> <file> [--key <field>] [--dry-run]",options:{key:{type:"string"}},run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <concept> <file>.",A.usage);let Y;try{Y=dq0(Q,"utf8")}catch{throw new O(`Cannot read ${Q}.`,A.notFound)}let Z=Q.endsWith(".json")?JSON.parse(Y):l90(Y);if(!Array.isArray(Z))throw new O("Expected a JSON array of objects.",A.usage);if(Z.length===0){j("Nothing to import — the file has no rows.");return}await n90(async(G)=>{let{concept:H,fields:K}=await n4(G,$),B=Object.keys(Z[0]??{}).filter((F)=>F!=="id"),W=new Map,V=[];for(let F of B){let S=K.find((I)=>I.name.toLowerCase()===F.trim().toLowerCase());if(S)W.set(F,S);else V.push(F)}if(V.length>0)throw new O(`${H.name} has no field named ${V.map((F)=>`"${F}"`).join(", ")}.`,A.notFound,`Columns available: ${K.map((F)=>F.name).join(", ")}`);let N=J.flags.key?L4(K,J.flags.key):void 0,D=N?await G.call((F)=>F.listRecords({conceptId:H.id})):[],_=new Map;for(let F of D){let S=F.state[N?.id??""];if(typeof S==="string")_.set(S.toLowerCase(),F)}let M=0,v=0,u=[];for(let[F,S]of Z.entries()){let I={};for(let[h,f]of W){let r=S[h];if(r===void 0)continue;I[f.id]=r===""?null:fq0(f,String(r))}let o=N?String(S[N.name]??"").toLowerCase():"",a=N&&o?_.get(o):void 0;if(J.flags["dry-run"]){if(a)v++;else M++;continue}try{if(a)await v9(async()=>(await G.call((h)=>h.getRecord({id:a.id}))).recordVersion,(h)=>G.call((f)=>f.updateRecord({id:a.id,expectedVersion:h.version,patch:I}))),v++;else await G.call((h)=>h.createRecord({conceptId:H.id,fields:I})),M++}catch(h){let f=h;u.push({row:F+2,error:f.code??f.message??String(h)})}}let x=J.flags["dry-run"]?"Would import":"Imported";if(j(`${x}: ${M} created, ${v} updated, ${u.length} failed.`),u.length>0)throw k0("table",u),new O(`${u.length} row(s) failed.`,A.failed,"Row numbers count the header as line 1.")})}}],fq0=(J,$)=>{switch(J.kind){case"number":case"money":{let Q=Number($);if(Number.isNaN(Q))throw new O(`"${$}" is not a number.`,A.usage);return Q}case"bool":return["true","yes","1"].includes($.toLowerCase());case"json":try{return JSON.parse($)}catch{throw new O(`"${J.name}" needs valid JSON.`,A.usage)}default:return $}};import{spawn as lq0}from"node:child_process";import{fileURLToPath as nq0}from"node:url";var kQ={name:"@kahunalabs/cli",version:"0.0.14",description:"The Kahuna command line — kahuna.",type:"module",bin:{kahuna:"dist/index.js"},engines:{node:">=20"},scripts:{typecheck:"tsc -p .",test:"vitest run",start:"bun index.ts",build:"bun build index.ts --target=node --outdir dist --minify",prepublishOnly:"bun run build"},devDependencies:{"@effect/platform":"0.96.1","@effect/rpc":"0.75.1","@kahunalabs/contract":"workspace:*","@types/node":"25.9.2",effect:"3.21.3",vitest:"4.1.11"},files:["dist","README.md","LICENSE"],license:"MIT",repository:{type:"git",url:"git+https://github.com/debuglebowski/kahuna.git",directory:"packages/cli"},keywords:["kahuna","cli"],publishConfig:{access:"public"}};import{existsSync as cq0}from"node:fs";import W_ from"node:path";var y3="@kahunalabs/cli",r90=()=>(process.env.KAHUNA_REGISTRY||"https://registry.npmjs.org").replace(/\/+$/,""),i90=(J)=>{let $=/^v?(\d+)\.(\d+)\.(\d+)$/.exec(J.trim());if(!$)return null;return[Number($[1]),Number($[2]),Number($[3])]},s90=(J,$)=>{let Q=i90(J),Y=i90($);if(!Q||!Y)return!1;for(let Z=0;Z<3;Z++)if(Q[Z]!==Y[Z])return(Q[Z]??0)>(Y[Z]??0);return!1},a90=async()=>{let J=`${r90()}/${y3.replace("/","%2F")}/latest`,$;try{$=await fetch(J,{headers:{accept:"application/json"}})}catch(Y){throw new O(`Cannot reach the npm registry at ${r90()} (${Y instanceof Error?Y.message:String(Y)}).`,A.failed,"Check your network, or point KAHUNA_REGISTRY at a mirror.")}if(!$.ok)throw new O(`The registry did not answer for ${y3} (HTTP ${$.status}).`,A.failed);let Q=await $.json().catch(()=>null);if(!Q?.version)throw new O(`The registry returned no version for ${y3}.`,A.failed);return Q.version},oB=(J,$)=>J.includes($.replaceAll("/",W_.sep)),t90=(J)=>{let $=`${W_.sep}node_modules${W_.sep}`,Q=J.indexOf($);if(Q===-1)return"source";if(oB(J,"/_npx/"))return"npx";if(oB(J,"/.bun/install/global/"))return"bun";if(oB(J,"/pnpm/global/"))return"pnpm";if(oB(J,"/.config/yarn/global/")||oB(J,"/.yarn/global/"))return"yarn";let Y=J.slice(0,Q);return cq0(W_.join(Y,"package.json"))?"local":"npm"},e90=(J)=>{let $=`${y3}@latest`;switch(J){case"bun":return{command:"bun",args:["add","-g",$]};case"pnpm":return{command:"pnpm",args:["add","-g",$]};case"yarn":return{command:"yarn",args:["global","add",$]};default:return{command:"npm",args:["install","-g",$]}}},U_=({command:J,args:$})=>[J,...$].join(" "),J40=(J)=>{switch(J){case"npx":return{message:"This CLI is running from an npx cache, so there is no install to update.",hint:`Run \`npx ${y3}@latest\` to get the newest version.`};case"local":return{message:"This CLI is a dependency of a project, not a global install.",hint:`Run \`npm install ${y3}@latest\` in that project — updating it from here would rewrite its lockfile.`};default:return{message:"This CLI is running from a source checkout or a linked build.",hint:"Update it with git, then `bun run cli:build`."}}};var oq0=async(J)=>new Promise(($,Q)=>{let Y=lq0(J.command,[...J.args],{stdio:["ignore",2,2],shell:process.platform==="win32"});Y.on("error",(Z)=>{Q(new O(Z.code==="ENOENT"?`\`${J.command}\` is not on your PATH.`:`Could not run \`${J.command}\` (${Z.message}).`,A.failed,`Run \`${U_(J)}\` yourself.`))}),Y.on("close",(Z)=>{if(Z===0)return $();Q(new O(`\`${U_(J)}\` exited ${Z??"abnormally"}.`,A.failed,"If that was a permissions error, re-run it with sudo — or move your global prefix somewhere you own."))})}),$40=[{path:"cli update",summary:"Upgrade this CLI to the newest published version",usage:"cli update [--dry-run] [--json]",run:async(J)=>{let $=kQ.version,Q=await a90();if(!s90(Q,$)){if(F6(J.format,{current:$,latest:Q,updateAvailable:!1}),J.format!=="json")j("Already on the newest published version.");return}let Y=t90(nq0(import.meta.url));if(Y==="local"||Y==="npx"||Y==="source"){let{message:H,hint:K}=J40(Y);throw new O(`${H} ${Q} is published.`,A.failed,K)}let Z=e90(Y),G=U_(Z);if(J.flags["dry-run"]){if(F6(J.format,{current:$,latest:Q,updateAvailable:!0,manager:Y,command:G,updated:!1}),J.format!=="json")j(`Would run: ${G}`);return}if(J.format!=="json")j(`Updating ${$} -> ${Q} with \`${G}\`…`);if(await oq0(Z),F6(J.format,{current:$,latest:Q,updateAvailable:!0,manager:Y,command:G,updated:!0}),J.format!=="json")j(`Updated to ${Q}. Run \`allt --version\` to confirm.`)}}];var A7=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},Q40=["text","number","date","bool","enum","relation","file","computed","user","json","money","richtext"],Y40={cardinality:{type:"string"},"inverse-name":{type:"string"},"inverse-plural-name":{type:"string"},multiple:{type:"boolean"},format:{type:"string"},"option-color":{type:"string",multiple:!0},transition:{type:"string",multiple:!0}},Z40=(J,$)=>{let Q=$.cardinality;if(Q!==void 0){if(Q!=="one"&&Q!=="many")throw new O("--cardinality takes one or many.",A.usage);J.cardinality=Q}if($["inverse-name"]!==void 0)J.inverseName=$["inverse-name"];if($["inverse-plural-name"]!==void 0)J.inversePluralName=$["inverse-plural-name"];if($.multiple)J.multiple=!0;if($.format!==void 0)J.format=$.format;let Y=vQ($["option-color"],"option=hex");if(Y.length>0){let G={...J.optionColors};for(let[H,K]of Y)G[H]=K;J.optionColors=G}let Z=vQ($.transition,"from=to,to2");if(Z.length>0){let G={...J.transitions};for(let[H,K]of Z)G[H]=K.split(",").map((B)=>B.trim()).filter(Boolean);J.transitions=G}},X40=[{path:"concept list",summary:"List the concepts in this organization",usage:"concept list [--archived] [--counts] [--json]",options:{archived:{type:"boolean"},counts:{type:"boolean"}},run:async(J)=>{await A7(async($)=>{let Q=await $.call((Y)=>Y.listConcepts({includeArchived:Boolean(J.flags.archived),withCounts:Boolean(J.flags.counts)}));k0(J.format,Q.map((Y)=>({slug:Y.slug,name:Y.name,records:Y.recordCount??"",versioned:Y.versioningEnabled?"yes":"",single:Y.singleRecord?"yes":"",archived:Y.archivedAt?"yes":"",id:Y.id})),["slug","name","records","versioned","single","archived","id"])})}},{path:"concept get",summary:"Show one concept and its fields",usage:"concept get <concept> [--json]",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await A7(async(Q)=>{let{concept:Y,fields:Z}=await n4(Q,$);if(J.format==="json"){F6(J.format,{...Y,fields:Z});return}F6(J.format,{slug:Y.slug,name:Y.name,plural:Y.pluralName??"",description:Y.description??"",versioning:Y.versioningEnabled?"on":"off",singleRecord:Y.singleRecord?"yes":"no",titleField:Z.find((G)=>G.id===Y.titleFieldId)?.name??"(none)",id:Y.id}),j(""),k0("table",Z.map((G)=>({field:G.name,kind:G.kind,required:G.config.requirement==="required"?"yes":"",unique:G.config.unique?"yes":"",target:G.config.target??"",id:G.id})),["field","kind","required","unique","target","id"])})}},{path:"concept create",summary:"Create a concept",usage:"concept create <name> [--color <hex>] [--description <text>] [--plural <name>]",options:{color:{type:"string"},description:{type:"string"},plural:{type:"string"},icon:{type:"string"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("A name is required.",A.usage);await A7(async(Q)=>{if(J.flags["dry-run"]){j(`Would create concept "${$}".`);return}let Y=await Q.call((K)=>K.createConcept({name:$,color:J.flags.color??null})),Z=J.flags.description,G=J.flags.plural,H=J.flags.icon;if(Z||G||H)await Q.call((K)=>K.updateConcept({id:Y.id,description:Z??null,pluralName:G,icon:H}));j(`Created concept "${Y.name}" (${Y.slug}).`)})}},{path:"concept update",summary:"Rename a concept or change its settings",usage:"concept update <concept> [--name <n>] [--plural <n>] [--description <t>] [--icon <i>] [--color <hex>] [--versioning on|off] [--title-field <field>] [--single-record on|off]",options:{name:{type:"string"},plural:{type:"string"},description:{type:"string"},icon:{type:"string"},color:{type:"string"},versioning:{type:"string"},"title-field":{type:"string"},"single-record":{type:"string"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await A7(async(Q)=>{let{concept:Y,fields:Z}=await n4(Q,$),G=(N,D)=>{if(D===void 0)return;if(D==="on"||D==="true")return!0;if(D==="off"||D==="false")return!1;throw new O(`--${N} takes on or off.`,A.usage)},H=G("versioning",J.flags.versioning),K=G("single-record",J.flags["single-record"]),B=J.flags["title-field"];if(J.flags["dry-run"]){j(`Would update "${Y.name}".`);return}let W={id:Y.id,description:J.flags.description??Y.description,name:J.flags.name,pluralName:J.flags.plural,icon:J.flags.icon,color:J.flags.color,versioningEnabled:H};if(W.name!==void 0||W.pluralName!==void 0||W.icon!==void 0||W.color!==void 0||H!==void 0||J.flags.description!==void 0)await Q.call((N)=>N.updateConcept(W));if(B!==void 0){let N=L4(Z,B);await Q.call((D)=>D.setConceptTitleField({id:Y.id,titleFieldId:N.id}))}if(K!==void 0)await Q.call((N)=>N.setConceptSingleRecord({conceptId:Y.id,singleRecord:K}));j(`Updated "${Y.name}".`)})}},{path:"concept archive",summary:"Archive a concept",usage:"concept archive <concept>",run:async(J)=>G40(J.args[0],J,"archive")},{path:"concept restore",summary:"Restore an archived concept",usage:"concept restore <concept>",run:async(J)=>G40(J.args[0],J,"restore")},{path:"concept delete",summary:"PURGE a concept and everything in it",usage:"concept delete <concept> --yes",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);x9(J.flags,`permanently delete "${$}" and all of its records`),await A7(async(Q)=>{let Y=await Q.call((G)=>G.listConcepts({includeArchived:!0})),Z=q4(Y,$);if(J.flags["dry-run"]){j(`Would PURGE "${Z.name}" and every record in it. This cannot be undone.`);return}await Q.call((G)=>G.deleteConcept({id:Z.id})),j(`Purged "${Z.name}".`)})}},{path:"concept field list",summary:"List a concept's fields",usage:"concept field list <concept> [--archived]",options:{archived:{type:"boolean"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await A7(async(Q)=>{let Y=await Q.call((H)=>H.listConcepts({})),Z=q4(Y,$),G=await Q.call((H)=>H.listFields({conceptId:Z.id,includeArchived:Boolean(J.flags.archived)}));k0(J.format,G.map((H)=>({name:H.name,kind:H.kind,position:H.position,required:H.config.requirement==="required"?"yes":"",unique:H.config.unique?"yes":"",target:H.config.target??"",archived:H.archivedAt?"yes":"",id:H.id})),["name","kind","position","required","unique","target","archived","id"])})}},{path:"concept field add",summary:"Add a field to a concept",usage:"concept field add <concept> --name <n> --kind <kind> [--options a,b,c] [--target <concept>] [--cardinality one|many] [--inverse-name <n>] [--inverse-plural-name <n>] [--multiple] [--format <fmt>] [--option-color <opt>=<hex>]... [--transition <from>=<to,to2>]... [--required] [--unique]",options:{name:{type:"string"},kind:{type:"string"},options:{type:"string"},target:{type:"string"},required:{type:"boolean"},unique:{type:"boolean"},icon:{type:"string"},...Y40},run:async(J)=>{let[$]=J.args,Q=J.flags.name,Y=J.flags.kind;if(!$||!Q||!Y)throw new O("Need <concept> --name <n> --kind <kind>.",A.usage);if(!Q40.includes(Y))throw new O(`Unknown field kind "${Y}".`,A.usage,`Kinds: ${Q40.join(", ")}`);await A7(async(Z)=>{let G=await Z.call((W)=>W.listConcepts({})),H=q4(G,$),K={};if(J.flags.options)K.options=String(J.flags.options).split(",").map((W)=>W.trim()).filter(Boolean);if(J.flags.required)K.requirement="required";if(J.flags.unique)K.unique=!0;if(Z40(K,J.flags),Y==="relation"){let W=J.flags.target;if(!W)throw new O("A relation field needs --target <concept>.",A.usage,"The target is the concept the edges point at.");K.target=q4(G,W).id}if(J.flags["dry-run"]){j(`Would add ${Y} field "${Q}" to "${H.name}".`);return}let B=await Z.call((W)=>W.addField({conceptId:H.id,name:Q,kind:Y,config:K,icon:J.flags.icon??null}));j(`Added "${B.name}" (${B.kind}) to "${H.name}".`)})}},{path:"concept field update",summary:"Rename a field or change its config",usage:"concept field update <concept> <field> [--name <n>] [--options a,b,c] [--cardinality one|many] [--inverse-name <n>] [--inverse-plural-name <n>] [--multiple] [--format <fmt>] [--option-color <opt>=<hex>]... [--transition <from>=<to,to2>]... [--required] [--unique]",options:{name:{type:"string"},options:{type:"string"},required:{type:"boolean"},unique:{type:"boolean"},icon:{type:"string"},...Y40},run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <concept> <field>.",A.usage);await A7(async(Y)=>{let{concept:Z,fields:G}=await n4(Y,$),H=L4(G,Q),K={...H.config};if(J.flags.options)K.options=String(J.flags.options).split(",").map((W)=>W.trim()).filter(Boolean);if(J.flags.required)K.requirement="required";if(J.flags.unique)K.unique=!0;if(Z40(K,J.flags),J.flags["dry-run"]){j(`Would update field "${H.name}" on "${Z.name}".`);return}let B=await Y.call((W)=>W.updateField({id:H.id,name:J.flags.name,config:K,icon:J.flags.icon}));j(`Updated "${B.name}".`)})}},{path:"concept field reorder",summary:"Set the field order for a concept",usage:"concept field reorder <concept> <field> [<field> ...]",run:async(J)=>{let[$,...Q]=J.args;if(!$||Q.length===0)throw new O("Need <concept> and at least one field.",A.usage);await A7(async(Y)=>{let{concept:Z,fields:G}=await n4(Y,$),H=Q.map((W)=>L4(G,W)),K=G.filter((W)=>!H.some((V)=>V.id===W.id)),B=[...H,...K].map((W,V)=>({id:W.id,position:V}));if(J.flags["dry-run"]){j(`Would order: ${[...H,...K].map((W)=>W.name).join(", ")}`);return}await Y.call((W)=>W.reorderFields({conceptId:Z.id,orders:B})),j(`Reordered ${B.length} fields on "${Z.name}".`)})}},{path:"concept field archive",summary:"Archive a field (its data is kept)",usage:"concept field archive <concept> <field>",run:async(J)=>RI(J,"archive")},{path:"concept field restore",summary:"Restore an archived field",usage:"concept field restore <concept> <field>",run:async(J)=>RI(J,"restore")},{path:"concept field delete",summary:"PURGE a field and its values",usage:"concept field delete <concept> <field> --yes",run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <concept> <field>.",A.usage);x9(J.flags,`permanently delete field "${Q}"`),await RI(J,"delete")}}],G40=async(J,$,Q)=>{if(!J)throw new O("Which concept?",A.usage);await A7(async(Y)=>{let Z=await Y.call((H)=>H.listConcepts({includeArchived:!0})),G=q4(Z,J);if($.flags["dry-run"]){j(`Would ${Q} "${G.name}".`);return}await Y.call((H)=>Q==="archive"?H.archiveConcept({id:G.id}):H.restoreConcept({id:G.id})),j(`${Q==="archive"?"Archived":"Restored"} "${G.name}".`)})},RI=async(J,$)=>{let[Q,Y]=J.args;if(!Q||!Y)throw new O("Need <concept> <field>.",A.usage);await A7(async(Z)=>{let G=await Z.call((W)=>W.listConcepts({includeArchived:!0})),H=q4(G,Q),K=await Z.call((W)=>W.listFields({conceptId:H.id,includeArchived:!0})),B=L4(K,Y);if(J.flags["dry-run"]){j(`Would ${$} field "${B.name}" on "${H.name}".`);return}await Z.call((W)=>$==="archive"?W.archiveField({id:B.id}):$==="restore"?W.restoreField({id:B.id}):W.deleteField({id:B.id})),j(`${$==="delete"?"Purged":$==="archive"?"Archived":"Restored"} "${B.name}".`)})};var W40=[{type:"metric",description:"A single hero number — count, sum, or average over a concept's records. Can show a delta versus N days ago."},{type:"list",description:"A filterable, sortable table of a concept's records. Supports a flat, grouped-by-field, or status-row layout."},{type:"breakdown",description:"A chart breaking a concept's records down by a grouping field. Renders as bars, pie, donut, stacked, or a table."},{type:"attention",description:"Surfaces records that need attention, banded by how long they've gone quiet (cooling/cold/heating/steady)."},{type:"trend",description:"A time-series chart of event volume for a concept, bucketed by day or week, with an optional delta header."},{type:"activity",description:"A recent-activity feed of events on a concept's records, with optional inline diffs of what changed."},{type:"analytics",description:"An aggregated time-series pulled from PostHog — active users, event counts, or a custom HogQL query."},{type:"tasks",description:"A task list with assignee, status, and due-date filters, and an optional inline composer."},{type:"members",description:"The organization's member roster, with configurable columns (role/email/joined) and sorting."},{type:"welcome",description:"A greeting tile showing an org activity pulse and a set of curated quick links."},{type:"goal",description:"A metric tracked against a manual target — progress toward a quota, or staying under a budget."},{type:"shortcuts",description:"Hand-picked links to records, dashboards, or URLs. Fully manual — no filters or live data."},{type:"note",description:"Free-form rich text pinned to the canvas, like a sticky note."},{type:"kanban",description:"A drag-and-drop board of records in columns keyed by an enum field."},{type:"calendar",description:"Records from one or more concepts plotted on a calendar by a date field, in month, week, or agenda view."},{type:"gantt",description:"A Gantt timeline — records as bars between a start and end date field, with optional progress bars and swimlanes."},{type:"files",description:"A file browser scoped to a record, a concept, the whole org, or the widget's own private bucket."},{type:"document",description:"An inline rich-text editor bound to one record's richtext field, autosaving as you type."},{type:"record-details",description:"The current record's field values. Only meaningful on a record dashboard."},{type:"record-connections",description:"The current record's relations to other records. Only meaningful on a record dashboard."},{type:"record-graph",description:"A graph visualization of the current record's connections. Only meaningful on a record dashboard."},{type:"record-labels",description:"The current record's labels. Only meaningful on a record dashboard."},{type:"record-versions",description:"The current record's version history. Only meaningful on a record dashboard."},{type:"record-notes",description:"Notes attached to the current record. Only meaningful on a record dashboard."},{type:"record-tasks",description:"Tasks attached to the current record. Only meaningful on a record dashboard."},{type:"record-activity",description:"The current record's activity feed. Only meaningful on a record dashboard."},{type:"record-mentions",description:"Places elsewhere that @-mention the current record. Only meaningful on a record dashboard."}],H40=W40.map((J)=>J.type),R5=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},U40=(J,$)=>{let Q=$.trim().toLowerCase(),Y=J.find((G)=>G.id===$)??J.find((G)=>G.name.toLowerCase()===Q);if(Y)return Y;let Z=J.filter((G)=>G.name.toLowerCase().startsWith(Q));if(Z.length===1&&Z[0])return Z[0];throw new O(Z.length>1?`"${$}" matches ${Z.length} dashboards.`:`No dashboard named "${$}".`,Z.length>1?A.usage:A.notFound,Z.length>1?Z.map((G)=>` ${G.name}`).join(`
67
67
  `):void 0)},z1=async(J,$)=>U40(await J.call((Q)=>Q.listAllDashboards()),$),pZ=(J)=>{if(!J.body.children)throw new O(`"${J.name}" hasn't been opened in the app since before widget editing existed.`,A.usage,"Open it once in Kahuna to migrate its layout, then retry.");return J.body},rq0=(J)=>J.type==="group"?J.label??null:J.title??null,cZ=(J)=>{let $=[],Q=(Y,Z,G)=>{Y.forEach((H,K)=>{let B=[...Z,K];if($.push({id:H.id,type:H.type,label:rq0(H),path:B,depth:G}),H.type==="group")Q(H.children,B,G+1)})};return Q(J.children,[],0),$},rB=(J,$)=>{let Q=$.trim().toLowerCase(),Y=J.find((G)=>G.id===$)??J.find((G)=>(G.label??"").toLowerCase()===Q);if(Y)return Y;let Z=J.filter((G)=>(G.label??"").toLowerCase().startsWith(Q));if(Z.length===1&&Z[0])return Z[0];throw new O(Z.length>1?`"${$}" matches ${Z.length} widgets/groups.`:`No widget or group matching "${$}".`,Z.length>1?A.usage:A.notFound,Z.length>1?Z.map((G)=>` ${G.label??"(untitled)"} [${G.type}] ${G.id}`).join(`
68
68
  `):"Run `kahuna dashboard widget list <dashboard>`.")},vI=(J,$)=>{let[Q,...Y]=$,Z=Q===void 0?void 0:J[Q];if(!Z)throw new O("Stale path — the tree changed underneath this command.",A.conflict);if(Y.length===0)return Z;if(Z.type!=="group")throw new O("Path continues past a leaf widget.",A.usage);return vI(Z.children,Y)},xI=(J,$)=>{let[Q,...Y]=$;if(Y.length===0)return J.filter((Z,G)=>G!==Q);return J.map((Z,G)=>{if(G!==Q)return Z;if(Z.type!=="group")throw new O("Path continues past a leaf widget.",A.usage);return{...Z,children:xI(Z.children,Y)}})},SI=(J,$,Q)=>{let[Y,...Z]=$;return J.map((G,H)=>{if(H!==Y)return G;if(Z.length===0)return Q(G);if(G.type!=="group")throw new O("Path continues past a leaf widget.",A.usage);return{...G,children:SI(G.children,Z,Q)}})},II=(J,$)=>{if(!$||$==="root")return null;let Q=rB(cZ(J),$);if(Q.type!=="group")throw new O(`"${$}" is a widget, not a group.`,A.usage);return Q.id},EI=(J,$,Q,Y)=>{let Z=J.children;if($===null){let H=Q??Z.length;return{...J,children:[...Z.slice(0,H),Y,...Z.slice(H)]}}let G=cZ(J).find((H)=>H.id===$);if(!G)throw new O(`No group with id "${$}" (it may have just been removed).`,A.notFound);return{...J,children:SI(Z,G.path,(H)=>{if(H.type!=="group")throw new O(`"${$}" is a widget, not a group.`,A.usage);let K=Q??H.children.length;return{...H,children:[...H.children.slice(0,K),Y,...H.children.slice(K)]}})}},V40=(J)=>{try{return JSON.parse(J)}catch{return J}},iq0=(J)=>J===void 0?[]:Array.isArray(J)?J:[String(J)],sq0=new Set(["id","type"]),kI=(J)=>{if(sq0.has(J))throw new O(`"${J}" is structural and can't be changed with --set/--unset.`,A.usage,J==="type"?"To change a widget's type, remove it and add a new one.":void 0)},K40={set:{type:"string",multiple:!0},unset:{type:"string",multiple:!0}},B40=async(J,$,Q,Y)=>{let[Z,G]=J;if(!Z||!G)throw new O(`Need <dashboard> <${Y}>.`,A.usage);let H=vQ($.set,"key=value"),K=iq0($.unset);if(H.length===0&&K.length===0)throw new O("Nothing to set — pass --set key=value or --unset key.",A.usage);for(let[B]of H)kI(B);for(let B of K)kI(B);await R5(async(B)=>{let W=await z1(B,Z),V=pZ(W),N=rB(cZ(V),G),D=N.type==="group";if(D!==(Y==="group"))throw new O(`"${G}" is a ${D?"group":"widget"}, not a ${Y}.`,A.usage,`Use \`kahuna dashboard ${D?"group":"widget"} set\` instead.`);let _={...V,children:SI(V.children,N.path,(M)=>{let v={...M};for(let[u,x]of H)v[u]=V40(x);for(let u of K)delete v[u];return v})};if(Q){j(`Would update "${N.label??N.id}".`);return}await B.call((M)=>M.updateDashboard({id:W.id,body:_,expectedUpdatedAt:W.updatedAt})),j(`Updated "${N.label??N.id}" on "${W.name}".`)})},N40=[{path:"dashboard list",summary:"Every dashboard, including record-view templates",usage:"dashboard list [--json]",run:async(J)=>{await R5(async($)=>{let Q=await $.call((Y)=>Y.listAllDashboards());k0(J.format,Q.map((Y)=>({name:Y.name,icon:Y.icon??"",kind:Y.kind,scope:Y.ownerId?"personal":"org",hidden:Y.hidden?"yes":"",concept:Y.conceptId??"",id:Y.id})),["name","icon","kind","scope","hidden","concept","id"])})}},{path:"dashboard create",summary:"Create a dashboard, empty or copied from another",usage:"dashboard create <name> [--from <dashboard>] [--icon <i>] [--scope org|personal] [--kind page|record] [--concept <c>]",options:{from:{type:"string"},icon:{type:"string"},scope:{type:"string"},kind:{type:"string"},concept:{type:"string"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("A name is required.",A.usage);let Q=J.flags.kind;if(Q!==void 0&&Q!=="page"&&Q!=="record")throw new O("--kind takes page or record.",A.usage);let Y=J.flags.concept;if(Q==="record"&&!Y)throw new O("--kind record needs --concept <c>.",A.usage);let Z=J.flags.scope;if(Z!==void 0&&Z!=="org"&&Z!=="personal")throw new O("--scope takes org or personal.",A.usage);let G=J.flags.from;await R5(async(H)=>{let K={children:[]},B=Q,W,V=Z??"org";if(G){let D=await z1(H,G);K=D.body,B=Q??D.kind,W=D.conceptId,V=Z??(D.ownerId?"personal":"org")}if(B==="record"&&Y){let D=await H.call((_)=>_.listConcepts({}));W=q4(D,Y).id}if(J.flags["dry-run"]){j(`Would create dashboard "${$}"${G?` (from "${G}")`:""} (${V}).`);return}let N=await H.call((D)=>D.createDashboard({name:$,icon:J.flags.icon??null,scope:V,body:K,kind:B,conceptId:W??null}));j(`Created dashboard "${N.name}" (${N.id}).`)})}},{path:"dashboard update",summary:"Rename, re-icon, hide/show, or rescope a dashboard",usage:"dashboard update <dashboard> [--name <n>] [--icon <i>] [--hide | --show] [--scope org|personal]",options:{name:{type:"string"},icon:{type:"string"},hide:{type:"boolean"},show:{type:"boolean"},scope:{type:"string"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which dashboard?",A.usage);if(J.flags.hide&&J.flags.show)throw new O("Pass --hide or --show, not both.",A.usage);let Q=J.flags.scope;if(Q!==void 0&&Q!=="org"&&Q!=="personal")throw new O("--scope takes org or personal.",A.usage);let Y={};if(J.flags.name!==void 0)Y.name=J.flags.name;if(J.flags.icon!==void 0)Y.icon=J.flags.icon;if(J.flags.hide)Y.hidden=!0;if(J.flags.show)Y.hidden=!1;if(Q!==void 0)Y.scope=Q;if(Object.keys(Y).length===0)throw new O("Nothing to update.",A.usage,"Pass --name, --icon, --hide/--show, or --scope.");await R5(async(Z)=>{let G=await z1(Z,$);if(J.flags["dry-run"]){j(`Would update "${G.name}".`);return}let H=await Z.call((K)=>K.updateDashboard({id:G.id,...Y,expectedUpdatedAt:G.updatedAt}));j(`Updated "${H.name}".`)})}},{path:"dashboard reorder",summary:"Reorder dashboards",usage:"dashboard reorder <dashboard> [<dashboard> ...]",run:async(J)=>{let $=J.args;if($.length===0)throw new O("Need at least one dashboard.",A.usage);await R5(async(Q)=>{let Y=await Q.call((V)=>V.listAllDashboards()),Z=$.map((V)=>U40(Y,V)),G=(V)=>V.ownerId?"personal":"org",H=new Set(Z.map(G));if(H.size>1)throw new O("Cannot reorder org and personal dashboards together.",A.usage,"Org and personal dashboards are separate lists — reorder each in its own call.");let K=[...H][0],B=Y.filter((V)=>G(V)===K&&!Z.some((N)=>N.id===V.id)),W=[...Z,...B];if(J.flags["dry-run"]){j(`Would order: ${W.map((V)=>V.name).join(", ")}`);return}await Q.call((V)=>V.reorderDashboards({orders:W.map((N,D)=>({id:N.id,position:D}))})),j(`Reordered ${W.length} dashboards.`)})}},{path:"dashboard delete",summary:"Delete a dashboard",usage:"dashboard delete <dashboard> --yes",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which dashboard?",A.usage);x9(J.flags,`delete dashboard "${$}"`),await R5(async(Q)=>{let Y=await z1(Q,$);if(J.flags["dry-run"]){j(`Would delete "${Y.name}".`);return}await Q.call((Z)=>Z.deleteDashboard({id:Y.id})),j(`Deleted "${Y.name}".`)})}},{path:"dashboard widget list",summary:"List every widget and group on a dashboard",usage:"dashboard widget list <dashboard>",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which dashboard?",A.usage);await R5(async(Q)=>{let Y=await z1(Q,$),Z=pZ(Y),G=cZ(Z);if(G.length===0){j("(empty)");return}k0(J.format,G.map((H)=>({path:" ".repeat(H.depth)+(H.label??"(untitled)"),type:H.type,id:H.id})),["path","type","id"])})}},{path:"dashboard widget get",summary:"Show one widget or group's full configuration",usage:"dashboard widget get <dashboard> <widget> [--json]",run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <dashboard> <widget>.",A.usage);await R5(async(Y)=>{let Z=await z1(Y,$),G=pZ(Z),H=rB(cZ(G),Q),K=vI(G.children,H.path);F6(J.format,K)})}},{path:"dashboard widget types",summary:"List the widget kinds --type accepts",usage:"dashboard widget types",run:async(J)=>{k0(J.format,W40,["type","description"])}},{path:"dashboard widget add",summary:"Add a widget to a dashboard",usage:"dashboard widget add <dashboard> --type <kind> [--parent <group>] [--title <t>] [--set key=value ...]",options:{type:{type:"string"},parent:{type:"string"},title:{type:"string"},set:{type:"string",multiple:!0}},run:async(J)=>{let[$]=J.args,Q=J.flags.type;if(!$||!Q)throw new O("Need <dashboard> --type <kind>.",A.usage);if(!H40.includes(Q))throw new O(`Unknown widget type "${Q}".`,A.usage,`Types: ${H40.join(", ")}`);let Y=vQ(J.flags.set,"key=value");for(let[G]of Y)kI(G);let Z=J.flags.title;await R5(async(G)=>{let H=await z1(G,$),K=pZ(H),B=II(K,J.flags.parent),W={id:crypto.randomUUID(),type:Q,title:Z??null};for(let[N,D]of Y)W[N]=V40(D);let V=EI(K,B,void 0,W);if(J.flags["dry-run"]){j(`Would add a "${Q}" widget to "${H.name}".`);return}await G.call((N)=>N.updateDashboard({id:H.id,body:V,expectedUpdatedAt:H.updatedAt})),j(`Added a "${Q}" widget (${W.id}) to "${H.name}".`)})}},{path:"dashboard widget set",summary:"Edit a widget's configuration",usage:"dashboard widget set <dashboard> <widget> [--set key=value ...] [--unset key ...]",options:K40,run:async(J)=>B40(J.args,J.flags,Boolean(J.flags["dry-run"]),"widget")},{path:"dashboard widget remove",summary:"Remove a widget or group (and its contents) from a dashboard",usage:"dashboard widget remove <dashboard> <widget>",run:async(J)=>{let[$,Q]=J.args;if(!$||!Q)throw new O("Need <dashboard> <widget>.",A.usage);await R5(async(Y)=>{let Z=await z1(Y,$),G=pZ(Z),H=rB(cZ(G),Q),K={...G,children:xI(G.children,H.path)};if(J.flags["dry-run"]){j(`Would remove "${H.label??H.id}".`);return}await Y.call((B)=>B.updateDashboard({id:Z.id,body:K,expectedUpdatedAt:Z.updatedAt})),j(`Removed "${H.label??H.id}" from "${Z.name}".`)})}},{path:"dashboard widget move",summary:"Move a widget or group to a different parent",usage:"dashboard widget move <dashboard> <widget> --parent <group|root> [--index <n>]",options:{parent:{type:"string"},index:{type:"string"}},run:async(J)=>{let[$,Q]=J.args,Y=J.flags.parent;if(!$||!Q||!Y)throw new O("Need <dashboard> <widget> --parent <group|root>.",A.usage);let Z=J.flags.index,G=Z===void 0?void 0:Number(Z);if(G!==void 0&&!Number.isInteger(G))throw new O("--index must be a whole number.",A.usage);await R5(async(H)=>{let K=await z1(H,$),B=pZ(K),W=rB(cZ(B),Q),V=vI(B.children,W.path),N={...B,children:xI(B.children,W.path)},D=II(N,Y),_=EI(N,D,G,V);if(J.flags["dry-run"]){j(`Would move "${W.label??W.id}" under ${Y}.`);return}await H.call((M)=>M.updateDashboard({id:K.id,body:_,expectedUpdatedAt:K.updatedAt})),j(`Moved "${W.label??W.id}" on "${K.name}".`)})}},{path:"dashboard group add",summary:"Add a row/col container to a dashboard",usage:"dashboard group add <dashboard> --direction row|col [--parent <group>] [--label <l>] [--tabs]",options:{direction:{type:"string"},parent:{type:"string"},label:{type:"string"},tabs:{type:"boolean"}},run:async(J)=>{let[$]=J.args,Q=J.flags.direction;if(!$||!Q)throw new O("Need <dashboard> --direction row|col.",A.usage);if(Q!=="row"&&Q!=="col")throw new O("--direction takes row or col.",A.usage);await R5(async(Y)=>{let Z=await z1(Y,$),G=pZ(Z),H=II(G,J.flags.parent),K={id:crypto.randomUUID(),type:"group",direction:Q,label:J.flags.label??null,children:[],...J.flags.tabs?{display:"tabs"}:{}},B=EI(G,H,void 0,K);if(J.flags["dry-run"]){j(`Would add a ${Q} group to "${Z.name}".`);return}await Y.call((W)=>W.updateDashboard({id:Z.id,body:B,expectedUpdatedAt:Z.updatedAt})),j(`Added a ${Q} group (${K.id}) to "${Z.name}".`)})}},{path:"dashboard group set",summary:"Edit a group's configuration",usage:"dashboard group set <dashboard> <group> [--set key=value ...] [--unset key ...]",options:K40,run:async(J)=>B40(J.args,J.flags,Boolean(J.flags["dry-run"]),"group")}];var D40=[{path:"record event list",summary:"Recent events, by concept and time window",usage:"record event list [--concept <name>] [--record <id>] [--since <iso|days>] [--limit n]",options:{concept:{type:"string"},record:{type:"string"},since:{type:"string"},limit:{type:"string"}},run:async(J)=>{let $=d0(),Q=H6($);try{let Y=J.flags.concept,Z=Y?(await n4(Q,Y)).concept.id:void 0,G=J.flags.limit?Number(J.flags.limit):void 0;if(G!==void 0&&Number.isNaN(G))throw new O("--limit needs a number.",A.usage);let H=await Q.call((W)=>W.listEvents({conceptId:Z,since:aq0(J.flags.since),limit:G})),K=J.flags.record,B=K?H.filter((W)=>JSON.stringify(W).includes(K)):H;if(K&&H.length>0&&B.length===0)j(`No events for ${K} in the window the server returned (${H.length} events). listEvents has no per-record filter, so widen --since or raise --limit.`);k0(J.format,B)}finally{await Q.dispose()}}}],aq0=(J)=>{if(!J)return;let $=Number(J);if(!Number.isNaN($))return Date.now()-$*24*60*60*1000;let Q=Date.parse(J);if(Number.isNaN(Q))throw new O(`"${J}" is neither a date nor a number of days.`,A.usage,"Try --since 7 (days) or --since 2026-08-01.");return Q};var V_=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},z40=async(J,$)=>{let Q=await J.call((H)=>H.listLabels({includeArchived:!0})),Y=$.trim().toLowerCase(),Z=Q.find((H)=>H.id===$)??Q.find((H)=>H.name.toLowerCase()===Y);if(Z)return Z;let G=Q.filter((H)=>H.name.toLowerCase().startsWith(Y));if(G.length===1&&G[0])return G[0];if(G.length>1)throw new O(`"${$}" matches ${G.length} labels.`,A.usage,G.map((H)=>` ${H.name}`).join(`
69
69
  `));throw new O(`No label named "${$}".`,A.notFound)},A40=[{path:"label list",summary:"List the organization's labels",usage:"label list [--archived] [--json]",options:{archived:{type:"boolean"}},run:async(J)=>{await V_(async($)=>{let Q=await $.call((Y)=>Y.listLabels({includeArchived:Boolean(J.flags.archived)}));k0(J.format,Q.map((Y)=>({name:Y.name,color:Y.color??"",primary:Y.primary?"yes":"",archived:Y.archivedAt?"yes":"",id:Y.id})),["name","color","primary","archived","id"])})}},{path:"label create",summary:"Create a label",usage:"label create <name> [--color <hex>] [--primary]",options:{color:{type:"string"},primary:{type:"boolean"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("A name is required.",A.usage);await V_(async(Q)=>{if(J.flags["dry-run"]){j(`Would create label "${$}".`);return}let Y=await Q.call((Z)=>Z.createLabel({name:$,color:J.flags.color??null,primary:Boolean(J.flags.primary)}));j(`Created label "${Y.name}".`)})}},{path:"label update",summary:"Rename a label or change its colour",usage:"label update <label> [--name <n>] [--color <hex>] [--primary]",options:{name:{type:"string"},color:{type:"string"},primary:{type:"boolean"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which label?",A.usage);await V_(async(Q)=>{let Y=await z40(Q,$);if(J.flags["dry-run"]){j(`Would update label "${Y.name}".`);return}let Z=await Q.call((G)=>G.renameLabel({id:Y.id,name:J.flags.name,color:J.flags.color,primary:J.flags.primary===void 0?void 0:Boolean(J.flags.primary)}));j(`Updated label "${Z.name}".`)})}},{path:"label archive",summary:"Archive a label",usage:"label archive <label>",run:async(J)=>bI(J,"archive")},{path:"label restore",summary:"Restore an archived label",usage:"label restore <label>",run:async(J)=>bI(J,"restore")},{path:"label delete",summary:"PURGE a label",usage:"label delete <label> --yes",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which label?",A.usage);x9(J.flags,`permanently delete label "${$}"`),await bI(J,"delete")}}],bI=async(J,$)=>{let[Q]=J.args;if(!Q)throw new O("Which label?",A.usage);await V_(async(Y)=>{let Z=await z40(Y,Q);if(J.flags["dry-run"]){j(`Would ${$} label "${Z.name}".`);return}await Y.call((G)=>$==="archive"?G.archiveLabel({id:Z.id}):$==="restore"?G.restoreLabel({id:Z.id}):G.deleteLabel({id:Z.id})),j(`${$==="delete"?"Purged":$==="archive"?"Archived":"Restored"} "${Z.name}".`)})};import{readFileSync as tq0}from"node:fs";var N_=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},_40=async(J,$)=>{if(typeof $==="string")try{return tq0($,"utf8")}catch{throw new O(`Cannot read ${$}.`,A.notFound)}if(J.length>0)return J.join(" ");if(process.stdin.isTTY)throw new O("Nothing to write.",A.usage,"Pass the text, --file <path>, or pipe it in.");let Q=[];for await(let Y of process.stdin)Q.push(Y);return Buffer.concat(Q).toString("utf8").trim()},O40=[{path:"note list",summary:"Notes on a record",usage:"note list --record <id> [--archived]",options:{record:{type:"string"},archived:{type:"boolean"}},run:async(J)=>{let $=J.flags.record??J.args[0];if(!$)throw new O("Which record?",A.usage,"listNotes needs a subject — there is no org-wide note listing.");await N_(async(Q)=>{let Y=await Q.call((Z)=>Z.listNotes({subjectId:$,includeArchived:Boolean(J.flags.archived)}));k0(J.format,Y.map((Z)=>({id:Z.id,body:Z.body.length>80?`${Z.body.slice(0,77)}...`:Z.body,created:Z.createdAt,archived:Z.archivedAt?"yes":""})),["id","body","created","archived"])})}},{path:"note create",summary:"Write a note, on a record or standalone",usage:"note create [text] [--record <id>] [--file <path>]",options:{record:{type:"string"},file:{type:"string"}},run:async(J)=>{let $=await _40(J.args,J.flags.file);if(!$)throw new O("The note is empty.",A.usage);await N_(async(Q)=>{if(J.flags["dry-run"]){j(`Would write a ${$.length}-character note.`);return}let Y=await Q.call((Z)=>Z.createNote({subjectId:J.flags.record??null,body:$}));j(`Wrote note ${Y.id}.`)})}},{path:"note update",summary:"Replace a note's text",usage:"note update <id> [text] [--file <path>]",options:{file:{type:"string"},record:{type:"string"}},run:async(J)=>{let[$,...Q]=J.args;if(!$)throw new O("Which note?",A.usage);let Y=await _40(Q,J.flags.file);if(!Y)throw new O("The note is empty.",A.usage);await N_(async(Z)=>{if(J.flags["dry-run"]){j(`Would replace note ${$}.`);return}let G=J.flags.record;if(!G)throw new O("note update needs --record <id>.",A.usage,"Notes are read through their subject, so the CLI cannot find one by id alone.");await v9(async()=>{let K=(await Z.call((B)=>B.listNotes({subjectId:G,includeArchived:!0}))).find((B)=>B.id===$);if(!K)throw new O(`No note ${$} on that record.`,A.notFound);return K},(H)=>Z.call((K)=>K.updateNote({id:$,expectedVersion:H.version,body:Y}))),j(`Updated note ${$}.`)})}},{path:"note delete",summary:"PURGE a note",usage:"note delete <id> --yes",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which note?",A.usage);x9(J.flags,`permanently delete note ${$}`),await N_(async(Q)=>{if(J.flags["dry-run"]){j(`Would PURGE note ${$}.`);return}await Q.call((Y)=>Y.deleteNote({id:$})),j(`Purged note ${$}.`)})}}];var L40=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},eq0=async(J,$,Q="POST")=>{let Y=d0(),Z=await fetch(`${Y.host}${J}`,{method:Q,redirect:"manual",headers:{"content-type":"application/json",cookie:Y.cookie??"",origin:Y.host},body:$===void 0?void 0:JSON.stringify($)}).catch((G)=>{throw new O(`Cannot reach ${Y.host} (${G instanceof Error?G.message:String(G)}).`,A.failed)});if(!Z.ok){let G=await Z.json().catch(()=>null);throw new O(G?.error??`Request failed (HTTP ${Z.status}).`,O7(Z.status))}return Z.json().catch(()=>({}))},P40=[{path:"organization member list",summary:"Everyone in the organization, with their roles",usage:"organization member list [--json]",run:async(J)=>{await L40(async($)=>{let Q=await $.call((H)=>H.listRoles()),Y=await $.call((H)=>H.listDeactivatedMembers()),Z=new Map;for(let H of Q){let{actors:K}=await $.call((B)=>B.roleHolders({roleId:H.id}));for(let B of K){let W=Z.get(B)??[];W.push(H.name),Z.set(B,W)}}let G=new Set(Y.map((H)=>H.userId));for(let H of Y)if(!Z.has(H.userId))Z.set(H.userId,[]);k0(J.format,[...Z].map(([H,K])=>({userId:H,roles:K.join(", "),active:G.has(H)?"no":"yes"})),["userId","roles","active"])})}},{path:"organization member add",summary:"Add an existing account to the organization by email",usage:"organization member add <email>",run:async(J)=>{let[$]=J.args;if(!$)throw new O("An email address is required.",A.usage);if(J.flags["dry-run"]){j(`Would add ${$} to the organization.`);return}try{await eq0("/api/org/members",{email:$})}catch(Q){if(Q.message==="NO_SUCH_USER")throw new O(`No account for ${$}.`,A.notFound,"Sign-up is closed, so the account must be provisioned first: `bun scripts/create-user.ts <email> <password>` on the server.");throw Q}j(`Added ${$}.`)}},{path:"organization member deactivate",summary:"Block a member without deleting anything",usage:"organization member deactivate <user-id>",run:async(J)=>q40(J,"deactivate")},{path:"organization member reactivate",summary:"Let a deactivated member back in",usage:"organization member reactivate <user-id>",run:async(J)=>q40(J,"reactivate")}],q40=async(J,$)=>{let[Q]=J.args;if(!Q)throw new O("Which member?",A.usage);await L40(async(Y)=>{if(J.flags["dry-run"]){j(`Would ${$} ${Q}.`);return}await Y.call((Z)=>$==="deactivate"?Z.deactivateMember({userId:Q}):Z.reactivateMember({userId:Q})),j(`${$==="deactivate"?"Deactivated":"Reactivated"} ${Q}.`)})};var IJ=async(J)=>{let $=H6(d0());try{return await J($)}finally{await $.dispose()}},yI=(J)=>J===void 0?[]:Array.isArray(J)?J:[String(J)],JL0=(J,$)=>["id","record","label",...J.versioningEnabled?["version","status"]:[],...$.map((Q)=>Q.name)],hI=(J,$,Q,Y="table")=>{let Z={id:J.id,record:J.recordId,label:G_(J,$,Q)};if($.versioningEnabled)Z.version=J.versionSeq,Z.status=J.versionStatus;for(let G of Q){let H=J.state[G.id];Z[G.name]=G.kind==="file"&&Y!=="json"?nB(H):H}if(J.archivedAt)Z.archived=!0;return Z},$L0=50000,QL0=(J,$,Q)=>{let Y=J;for(let Z of Q){let G=Z.indexOf("=");if(G===-1)throw new O(`Expected field=value, got "${Z}".`,A.usage);let H=Z.slice(0,G),K=Z.slice(G+1).toLowerCase(),B=$.find((W)=>W.name.toLowerCase()===H.toLowerCase()||W.id===H);if(!B)throw new O(`No field named "${H}".`,A.notFound);Y=Y.filter((W)=>{let V=W.state[B.id];if(B.kind==="file")return K===""?nB(V)==="":nB(V).split(", ").some((N)=>N.toLowerCase()===K);return(V===null||V===void 0?"":String(V)).toLowerCase()===K})}return Y},F40=[{path:"record list",summary:"List a concept's records",usage:"record list <concept> [--where field=value] [--sort field] [--limit n] [--archived]",options:{where:{type:"string",multiple:!0},sort:{type:"string"},limit:{type:"string"},archived:{type:"boolean"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await IJ(async(Q)=>{let{concept:Y,fields:Z}=await n4(Q,$),G=await Q.call((V)=>V.listRecords({conceptId:Y.id,includeArchived:Boolean(J.flags.archived)}));if(G.length>=$L0)j(`Warning: the server returned ${G.length} records, its maximum. Some may be missing —`+" the API has no pagination, so this is a hard ceiling, not a page.");let H=QL0(G,Z,yI(J.flags.where)),K=J.flags.sort;if(K){let V=Z.find((D)=>D.name.toLowerCase()===K.toLowerCase());if(!V)throw new O(`No field named "${K}".`,A.notFound);let N=(D)=>V.kind==="file"?nB(D.state[V.id]):String(D.state[V.id]??"");H=[...H].sort((D,_)=>N(D).localeCompare(N(_)))}let B=J.flags.limit?Number(J.flags.limit):void 0;if(B!==void 0&&Number.isNaN(B))throw new O("--limit needs a number.",A.usage);let W=B?H.slice(0,B):H;k0(J.format,W.map((V)=>hI(V,Y,Z,J.format)),JL0(Y,Z))})}},{path:"record get",summary:"Show one record",usage:"record get <id | concept-slug-for-single-record>",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which record?",A.usage);await IJ(async(Q)=>{let Y=await D_(Q,$),Z=hI(Y.recordVersion,Y.concept,Y.fields,J.format);if(Y.labels.length>0)Z.labels=Y.labels.map((G)=>G.name).join(", ");F6(J.format,Z)})}},{path:"record create",summary:"Create a record",usage:"record create <concept> --field name=value [--field ...] [--file property=path ...]",options:{field:{type:"string",multiple:!0,short:"f"},file:{type:"string",multiple:!0}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which concept?",A.usage);await IJ(async(Q)=>{let{concept:Y,fields:Z}=await n4(Q,$),G=FI(Z,yI(J.flags.field)),H=vQ(J.flags.file,"property=path").map(([V,N])=>{let D=L4(Z,V);if(D.kind!=="file")throw new O(`"${D.name}" is a ${D.kind} field, not a file property.`,A.usage);return{field:D,path:N,bytes:b90(N)}});if(J.flags["dry-run"]){j(`Would create a ${Y.name} with ${Object.keys(G).length} field(s)${H.length?` and ${H.length} file(s)`:""}.`);return}let K={},B=d0();for(let V of H){let{id:N}=await jI(B,"/api/attachments/staged",V.path,V.bytes);K[V.field.id]=[...K[V.field.id]??[],N]}let W=await Q.call((V)=>V.createRecord({conceptId:Y.id,fields:G,...H.length?{files:K}:{}}));gI(J.format,W,Y,Z,"Created")})}},{path:"record update",summary:"Patch fields on a record",usage:"record update <id> --field name=value [--field ...]",options:{field:{type:"string",multiple:!0,short:"f"}},run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which record?",A.usage);await IJ(async(Q)=>{let Y=await D_(Q,$),Z=FI(Y.fields,yI(J.flags.field));if(Object.keys(Z).length===0)throw new O("Nothing to change.",A.usage);if(J.flags["dry-run"]){j(`Would patch ${Object.keys(Z).length} field(s) on ${$}.`);return}let G=await v9(async()=>(await Q.call((H)=>H.getRecord({id:$}))).recordVersion,(H)=>Q.call((K)=>K.updateRecord({id:$,expectedVersion:H.version,patch:Z})));gI(J.format,G,Y.concept,Y.fields,"Updated")})}},{path:"record transition",summary:"Move an enum field along its allowed transitions",usage:"record transition <id> --field <name> --to <value>",options:{field:{type:"string"},to:{type:"string"}},run:async(J)=>{let[$]=J.args,Q=J.flags.field,Y=J.flags.to;if(!$||!Q||!Y)throw new O("Need <id> --field <name> --to <value>.",A.usage);await IJ(async(Z)=>{let G=await D_(Z,$),H=G.fields.find((B)=>B.name.toLowerCase()===Q.toLowerCase()||B.id===Q);if(!H)throw new O(`No field named "${Q}".`,A.notFound);if(J.flags["dry-run"]){j(`Would move "${H.name}" to "${Y}" on ${$}.`);return}let K=await v9(async()=>(await Z.call((B)=>B.getRecord({id:$}))).recordVersion,(B)=>Z.call((W)=>W.transitionRecord({id:$,expectedVersion:B.version,field:H.id,to:Y})));gI(J.format,K,G.concept,G.fields,"Moved")})}},{path:"record archive",summary:"Archive a record, or one version with --version",usage:"record archive <id> [--version]",options:{version:{type:"boolean"}},run:async(J)=>M40(J,"archive")},{path:"record restore",summary:"Restore an archived record, or one version with --version",usage:"record restore <id> [--version]",options:{version:{type:"boolean"}},run:async(J)=>M40(J,"restore")},{path:"record delete",summary:"PURGE a record version permanently",usage:"record delete <id> --yes",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which record?",A.usage);x9(J.flags,`permanently delete ${$}`),await IJ(async(Q)=>{if(J.flags["dry-run"]){j(`Would PURGE record version ${$}. This cannot be undone.`);return}await Q.call((Y)=>Y.deleteRecordVersion({id:$})),j(`Purged ${$}.`)})}},{path:"record search",summary:"Search records inside one concept",usage:"record search <concept> <query> [--limit n]",options:{limit:{type:"string"}},run:async(J)=>{let[$,...Q]=J.args,Y=Q.join(" ");if(!$||!Y)throw new O("Need <concept> <query>.",A.usage);await IJ(async(Z)=>{let{concept:G}=await n4(Z,$),H=await Z.call((K)=>K.searchRecords({conceptId:G.id,query:Y,limit:J.flags.limit?Number(J.flags.limit):void 0}));k0(J.format,H.map((K)=>({id:K.recordVersionId,record:K.recordId,label:K.label,version:K.versionSeq,status:K.versionStatus})))})}},{path:"record changed",summary:"The recent-changes feed",usage:"record changed [--json]",run:async(J)=>{await IJ(async($)=>{let Q=await $.call((Y)=>Y.getChanged());k0(J.format,Q.map((Y)=>({...Y})))})}},{path:"record open",summary:"Print the web URL for a record",usage:"record open <id> [--print]",run:async(J)=>{let[$]=J.args;if(!$)throw new O("Which record?",A.usage);let Q=d0();process.stdout.write(`${Q.host}/records/${$}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@kahunalabs/cli",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "The Kahuna command line \u2014 kahuna.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "kahuna": "./dist/index.js"
7
+ "kahuna": "dist/index.js"
8
8
  },
9
9
  "engines": {
10
10
  "node": ">=20"
@@ -32,7 +32,7 @@
32
32
  "license": "MIT",
33
33
  "repository": {
34
34
  "type": "git",
35
- "url": "https://github.com/debuglebowski/kahuna.git",
35
+ "url": "git+https://github.com/debuglebowski/kahuna.git",
36
36
  "directory": "packages/cli"
37
37
  },
38
38
  "keywords": [