@closedloop-ai/cli 0.3.4 → 0.3.5

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 (3) hide show
  1. package/README.md +32 -28
  2. package/dist/closedloop.js +133 -127
  3. package/package.json +1 -1
@@ -1,178 +1,184 @@
1
1
  #!/usr/bin/env node
2
- var B=class extends Error{constructor(t,n,o){super(o),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name,this.code=n,this.exitCode=t,this.nestedError=void 0}},je=class extends B{constructor(t){super(1,"commander.invalidArgument",t),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name}};var Tt=class{constructor(t,n){switch(this.description=n||"",this.variadic=!1,this.parseArg=void 0,this.defaultValue=void 0,this.defaultValueDescription=void 0,this.argChoices=void 0,t[0]){case"<":this.required=!0,this._name=t.slice(1,-1);break;case"[":this.required=!1,this._name=t.slice(1,-1);break;default:this.required=!0,this._name=t;break}this._name.endsWith("...")&&(this.variadic=!0,this._name=this._name.slice(0,-3))}name(){return this._name}_collectValue(t,n){return n===this.defaultValue||!Array.isArray(n)?[t]:(n.push(t),n)}default(t,n){return this.defaultValue=t,this.defaultValueDescription=n,this}argParser(t){return this.parseArg=t,this}choices(t){return this.argChoices=t.slice(),this.parseArg=(n,o)=>{if(!this.argChoices.includes(n))throw new je(`Allowed choices are ${this.argChoices.join(", ")}.`);return this.variadic?this._collectValue(n,o):n},this}argRequired(){return this.required=!0,this}argOptional(){return this.required=!1,this}};function en(e){let t=e.name()+(e.variadic===!0?"...":"");return e.required?"<"+t+">":"["+t+"]"}import{EventEmitter as kl}from"node:events";import uo from"node:child_process";import we from"node:path";import nn from"node:fs";import T from"node:process";import{stripVTControlCharacters as Sl}from"node:util";import{stripVTControlCharacters as $l}from"node:util";var Te=class{constructor(){this.helpWidth=void 0,this.minWidthToWrap=40,this.sortSubcommands=!1,this.sortOptions=!1,this.showGlobalOptions=!1}prepareContext(t){this.helpWidth=this.helpWidth??t.helpWidth??80}visibleCommands(t){let n=t.commands.filter(i=>!i._hidden),o=t._getHelpCommand();return o&&!o._hidden&&n.push(o),this.sortSubcommands&&n.sort((i,r)=>i.name().localeCompare(r.name())),n}compareOptions(t,n){let o=i=>i.short?i.short.replace(/^-/,""):i.long.replace(/^--/,"");return o(t).localeCompare(o(n))}visibleOptions(t){let n=t.options.filter(i=>!i.hidden),o=t._getHelpOption();if(o&&!o.hidden){let i=o.short&&t._findOption(o.short),r=o.long&&t._findOption(o.long);!i&&!r?n.push(o):o.long&&!r?n.push(t.createOption(o.long,o.description)):o.short&&!i&&n.push(t.createOption(o.short,o.description))}return this.sortOptions&&n.sort(this.compareOptions),n}visibleGlobalOptions(t){if(!this.showGlobalOptions)return[];let n=[];for(let o=t.parent;o;o=o.parent){let i=o.options.filter(r=>!r.hidden);n.push(...i)}return this.sortOptions&&n.sort(this.compareOptions),n}visibleArguments(t){return t._argsDescription&&t.registeredArguments.forEach(n=>{n.description=n.description||t._argsDescription[n.name()]||""}),t.registeredArguments.find(n=>n.description)?t.registeredArguments:[]}subcommandTerm(t){let n=t.registeredArguments.map(o=>en(o)).join(" ");return t._name+(t._aliases[0]?"|"+t._aliases[0]:"")+(t.options.length?" [options]":"")+(n?" "+n:"")}optionTerm(t){return t.flags}argumentTerm(t){return t.name()}longestSubcommandTermLength(t,n){return n.visibleCommands(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleSubcommandTerm(n.subcommandTerm(i)))),0)}longestOptionTermLength(t,n){return n.visibleOptions(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleOptionTerm(n.optionTerm(i)))),0)}longestGlobalOptionTermLength(t,n){return n.visibleGlobalOptions(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleOptionTerm(n.optionTerm(i)))),0)}longestArgumentTermLength(t,n){return n.visibleArguments(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleArgumentTerm(n.argumentTerm(i)))),0)}commandUsage(t){let n=t._name;t._aliases[0]&&(n=n+"|"+t._aliases[0]);let o="";for(let i=t.parent;i;i=i.parent)o=i.name()+" "+o;return o+n+" "+t.usage()}commandDescription(t){return t.description()}subcommandDescription(t){return t.summary()||t.description()}optionDescription(t){let n=[];if(t.argChoices&&n.push(`choices: ${t.argChoices.map(o=>JSON.stringify(o)).join(", ")}`),t.defaultValue!==void 0&&(t.required||t.optional||t.isBoolean()&&typeof t.defaultValue=="boolean")&&n.push(`default: ${t.defaultValueDescription||JSON.stringify(t.defaultValue)}`),t.presetArg!==void 0&&t.optional&&n.push(`preset: ${JSON.stringify(t.presetArg)}`),t.envVar!==void 0&&n.push(`env: ${t.envVar}`),n.length>0){let o=`(${n.join(", ")})`;return t.description?`${t.description} ${o}`:o}return t.description}argumentDescription(t){let n=[];if(t.argChoices&&n.push(`choices: ${t.argChoices.map(o=>JSON.stringify(o)).join(", ")}`),t.defaultValue!==void 0&&n.push(`default: ${t.defaultValueDescription||JSON.stringify(t.defaultValue)}`),n.length>0){let o=`(${n.join(", ")})`;return t.description?`${t.description} ${o}`:o}return t.description}formatItemList(t,n,o){return n.length===0?[]:[o.styleTitle(t),...n,""]}groupItems(t,n,o){let i=new Map;return t.forEach(r=>{let s=o(r);i.has(s)||i.set(s,[])}),n.forEach(r=>{let s=o(r);i.has(s)||i.set(s,[]),i.get(s).push(r)}),i}formatHelp(t,n){let o=n.padWidth(t,n),i=n.helpWidth??80;function r(d,m){return n.formatItem(d,o,m,n)}let s=[`${n.styleTitle("Usage:")} ${n.styleUsage(n.commandUsage(t))}`,""],a=n.commandDescription(t);a.length>0&&(s=s.concat([n.boxWrap(n.styleCommandDescription(a),i),""]));let l=n.visibleArguments(t).map(d=>r(n.styleArgumentTerm(n.argumentTerm(d)),n.styleArgumentDescription(n.argumentDescription(d))));if(s=s.concat(this.formatItemList("Arguments:",l,n)),this.groupItems(t.options,n.visibleOptions(t),d=>d.helpGroupHeading??"Options:").forEach((d,m)=>{let h=d.map(f=>r(n.styleOptionTerm(n.optionTerm(f)),n.styleOptionDescription(n.optionDescription(f))));s=s.concat(this.formatItemList(m,h,n))}),n.showGlobalOptions){let d=n.visibleGlobalOptions(t).map(m=>r(n.styleOptionTerm(n.optionTerm(m)),n.styleOptionDescription(n.optionDescription(m))));s=s.concat(this.formatItemList("Global Options:",d,n))}return this.groupItems(t.commands,n.visibleCommands(t),d=>d.helpGroup()||"Commands:").forEach((d,m)=>{let h=d.map(f=>r(n.styleSubcommandTerm(n.subcommandTerm(f)),n.styleSubcommandDescription(n.subcommandDescription(f))));s=s.concat(this.formatItemList(m,h,n))}),s.join(`
3
- `)}displayWidth(t){return $l(t).length}styleTitle(t){return t}styleUsage(t){return t.split(" ").map(n=>n==="[options]"?this.styleOptionText(n):n==="[command]"?this.styleSubcommandText(n):n[0]==="["||n[0]==="<"?this.styleArgumentText(n):this.styleCommandText(n)).join(" ")}styleCommandDescription(t){return this.styleDescriptionText(t)}styleOptionDescription(t){return this.styleDescriptionText(t)}styleSubcommandDescription(t){return this.styleDescriptionText(t)}styleArgumentDescription(t){return this.styleDescriptionText(t)}styleDescriptionText(t){return t}styleOptionTerm(t){return this.styleOptionText(t)}styleSubcommandTerm(t){return t.split(" ").map(n=>n==="[options]"?this.styleOptionText(n):n[0]==="["||n[0]==="<"?this.styleArgumentText(n):this.styleSubcommandText(n)).join(" ")}styleArgumentTerm(t){return this.styleArgumentText(t)}styleOptionText(t){return t}styleArgumentText(t){return t}styleSubcommandText(t){return t}styleCommandText(t){return t}padWidth(t,n){return Math.max(n.longestOptionTermLength(t,n),n.longestGlobalOptionTermLength(t,n),n.longestSubcommandTermLength(t,n),n.longestArgumentTermLength(t,n))}preformatted(t){return/\n[^\S\r\n]/.test(t)}formatItem(t,n,o,i){let s=" ".repeat(2);if(!o)return s+t;let a=t.padEnd(n+t.length-i.displayWidth(t)),l=2,c=(this.helpWidth??80)-n-l-2,d;return c<this.minWidthToWrap||i.preformatted(o)?d=o:d=i.boxWrap(o,c).replace(/\n/g,`
4
- `+" ".repeat(n+l)),s+a+" ".repeat(l)+d.replace(/\n/g,`
5
- ${s}`)}boxWrap(t,n){if(n<this.minWidthToWrap)return t;let o=t.split(/\r\n|\n/),i=/[\s]*[^\s]+/g,r=[];return o.forEach(s=>{let a=s.match(i);if(a===null){r.push("");return}let l=[a.shift()],u=this.displayWidth(l[0]);a.forEach(c=>{let d=this.displayWidth(c);if(u+d<=n){l.push(c),u+=d;return}r.push(l.join(""));let m=c.trimStart();l=[m],u=this.displayWidth(m)}),r.push(l.join(""))}),r.join(`
6
- `)}};var ot=class{constructor(t,n){this.flags=t,this.description=n||"",this.required=t.includes("<"),this.optional=t.includes("["),this.variadic=/\w\.\.\.[>\]]$/.test(t),this.mandatory=!1;let o=vl(t);this.short=o.shortFlag,this.long=o.longFlag,this.negate=!1,this.long&&(this.negate=this.long.startsWith("--no-")),this.defaultValue=void 0,this.defaultValueDescription=void 0,this.presetArg=void 0,this.envVar=void 0,this.parseArg=void 0,this.hidden=!1,this.argChoices=void 0,this.conflictsWith=[],this.implied=void 0,this.helpGroupHeading=void 0}default(t,n){return this.defaultValue=t,this.defaultValueDescription=n,this}preset(t){return this.presetArg=t,this}conflicts(t){return this.conflictsWith=this.conflictsWith.concat(t),this}implies(t){let n=t;return typeof t=="string"&&(n={[t]:!0}),this.implied=Object.assign(this.implied||{},n),this}env(t){return this.envVar=t,this}argParser(t){return this.parseArg=t,this}makeOptionMandatory(t=!0){return this.mandatory=!!t,this}hideHelp(t=!0){return this.hidden=!!t,this}_collectValue(t,n){return n===this.defaultValue||!Array.isArray(n)?[t]:(n.push(t),n)}choices(t){return this.argChoices=t.slice(),this.parseArg=(n,o)=>{if(!this.argChoices.includes(n))throw new je(`Allowed choices are ${this.argChoices.join(", ")}.`);return this.variadic?this._collectValue(n,o):n},this}name(){return this.long?this.long.replace(/^--/,""):this.short.replace(/^-/,"")}attributeName(){return this.negate?Ti(this.name().replace(/^no-/,"")):Ti(this.name())}helpGroup(t){return this.helpGroupHeading=t,this}is(t){return this.short===t||this.long===t}isBoolean(){return!this.required&&!this.optional&&!this.negate}},tn=class{constructor(t){this.positiveOptions=new Map,this.negativeOptions=new Map,this.dualOptions=new Set,t.forEach(n=>{n.negate?this.negativeOptions.set(n.attributeName(),n):this.positiveOptions.set(n.attributeName(),n)}),this.negativeOptions.forEach((n,o)=>{this.positiveOptions.has(o)&&this.dualOptions.add(o)})}valueFromOption(t,n){let o=n.attributeName();if(!this.dualOptions.has(o))return!0;let i=this.negativeOptions.get(o).presetArg,r=i!==void 0?i:!1;return n.negate===(r===t)}};function Ti(e){return e.split("-").reduce((t,n)=>t+n[0].toUpperCase()+n.slice(1))}function vl(e){let t,n,o=/^-[^-]$/,i=/^--[^-]/,r=e.split(/[ |,]+/).concat("guard");if(o.test(r[0])&&(t=r.shift()),i.test(r[0])&&(n=r.shift()),!t&&o.test(r[0])&&(t=r.shift()),!t&&i.test(r[0])&&(t=n,n=r.shift()),r[0].startsWith("-")){let s=r[0],a=`option creation failed due to '${s}' in option flags '${e}'`;throw/^-[^-][^-]/.test(s)?new Error(`${a}
2
+ var B=class extends Error{constructor(t,n,o){super(o),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name,this.code=n,this.exitCode=t,this.nestedError=void 0}},We=class extends B{constructor(t){super(1,"commander.invalidArgument",t),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name}};var Rt=class{constructor(t,n){switch(this.description=n||"",this.variadic=!1,this.parseArg=void 0,this.defaultValue=void 0,this.defaultValueDescription=void 0,this.argChoices=void 0,t[0]){case"<":this.required=!0,this._name=t.slice(1,-1);break;case"[":this.required=!1,this._name=t.slice(1,-1);break;default:this.required=!0,this._name=t;break}this._name.endsWith("...")&&(this.variadic=!0,this._name=this._name.slice(0,-3))}name(){return this._name}_collectValue(t,n){return n===this.defaultValue||!Array.isArray(n)?[t]:(n.push(t),n)}default(t,n){return this.defaultValue=t,this.defaultValueDescription=n,this}argParser(t){return this.parseArg=t,this}choices(t){return this.argChoices=t.slice(),this.parseArg=(n,o)=>{if(!this.argChoices.includes(n))throw new We(`Allowed choices are ${this.argChoices.join(", ")}.`);return this.variadic?this._collectValue(n,o):n},this}argRequired(){return this.required=!0,this}argOptional(){return this.required=!1,this}};function en(e){let t=e.name()+(e.variadic===!0?"...":"");return e.required?"<"+t+">":"["+t+"]"}import{EventEmitter as $l}from"node:events";import ro from"node:child_process";import be from"node:path";import nn from"node:fs";import T from"node:process";import{stripVTControlCharacters as vl}from"node:util";import{stripVTControlCharacters as wl}from"node:util";var Ee=class{constructor(){this.helpWidth=void 0,this.minWidthToWrap=40,this.sortSubcommands=!1,this.sortOptions=!1,this.showGlobalOptions=!1}prepareContext(t){this.helpWidth=this.helpWidth??t.helpWidth??80}visibleCommands(t){let n=t.commands.filter(i=>!i._hidden),o=t._getHelpCommand();return o&&!o._hidden&&n.push(o),this.sortSubcommands&&n.sort((i,r)=>i.name().localeCompare(r.name())),n}compareOptions(t,n){let o=i=>i.short?i.short.replace(/^-/,""):i.long.replace(/^--/,"");return o(t).localeCompare(o(n))}visibleOptions(t){let n=t.options.filter(i=>!i.hidden),o=t._getHelpOption();if(o&&!o.hidden){let i=o.short&&t._findOption(o.short),r=o.long&&t._findOption(o.long);!i&&!r?n.push(o):o.long&&!r?n.push(t.createOption(o.long,o.description)):o.short&&!i&&n.push(t.createOption(o.short,o.description))}return this.sortOptions&&n.sort(this.compareOptions),n}visibleGlobalOptions(t){if(!this.showGlobalOptions)return[];let n=[];for(let o=t.parent;o;o=o.parent){let i=o.options.filter(r=>!r.hidden);n.push(...i)}return this.sortOptions&&n.sort(this.compareOptions),n}visibleArguments(t){return t._argsDescription&&t.registeredArguments.forEach(n=>{n.description=n.description||t._argsDescription[n.name()]||""}),t.registeredArguments.find(n=>n.description)?t.registeredArguments:[]}subcommandTerm(t){let n=t.registeredArguments.map(o=>en(o)).join(" ");return t._name+(t._aliases[0]?"|"+t._aliases[0]:"")+(t.options.length?" [options]":"")+(n?" "+n:"")}optionTerm(t){return t.flags}argumentTerm(t){return t.name()}longestSubcommandTermLength(t,n){return n.visibleCommands(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleSubcommandTerm(n.subcommandTerm(i)))),0)}longestOptionTermLength(t,n){return n.visibleOptions(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleOptionTerm(n.optionTerm(i)))),0)}longestGlobalOptionTermLength(t,n){return n.visibleGlobalOptions(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleOptionTerm(n.optionTerm(i)))),0)}longestArgumentTermLength(t,n){return n.visibleArguments(t).reduce((o,i)=>Math.max(o,this.displayWidth(n.styleArgumentTerm(n.argumentTerm(i)))),0)}commandUsage(t){let n=t._name;t._aliases[0]&&(n=n+"|"+t._aliases[0]);let o="";for(let i=t.parent;i;i=i.parent)o=i.name()+" "+o;return o+n+" "+t.usage()}commandDescription(t){return t.description()}subcommandDescription(t){return t.summary()||t.description()}optionDescription(t){let n=[];if(t.argChoices&&n.push(`choices: ${t.argChoices.map(o=>JSON.stringify(o)).join(", ")}`),t.defaultValue!==void 0&&(t.required||t.optional||t.isBoolean()&&typeof t.defaultValue=="boolean")&&n.push(`default: ${t.defaultValueDescription||JSON.stringify(t.defaultValue)}`),t.presetArg!==void 0&&t.optional&&n.push(`preset: ${JSON.stringify(t.presetArg)}`),t.envVar!==void 0&&n.push(`env: ${t.envVar}`),n.length>0){let o=`(${n.join(", ")})`;return t.description?`${t.description} ${o}`:o}return t.description}argumentDescription(t){let n=[];if(t.argChoices&&n.push(`choices: ${t.argChoices.map(o=>JSON.stringify(o)).join(", ")}`),t.defaultValue!==void 0&&n.push(`default: ${t.defaultValueDescription||JSON.stringify(t.defaultValue)}`),n.length>0){let o=`(${n.join(", ")})`;return t.description?`${t.description} ${o}`:o}return t.description}formatItemList(t,n,o){return n.length===0?[]:[o.styleTitle(t),...n,""]}groupItems(t,n,o){let i=new Map;return t.forEach(r=>{let s=o(r);i.has(s)||i.set(s,[])}),n.forEach(r=>{let s=o(r);i.has(s)||i.set(s,[]),i.get(s).push(r)}),i}formatHelp(t,n){let o=n.padWidth(t,n),i=n.helpWidth??80;function r(m,d){return n.formatItem(m,o,d,n)}let s=[`${n.styleTitle("Usage:")} ${n.styleUsage(n.commandUsage(t))}`,""],a=n.commandDescription(t);a.length>0&&(s=s.concat([n.boxWrap(n.styleCommandDescription(a),i),""]));let l=n.visibleArguments(t).map(m=>r(n.styleArgumentTerm(n.argumentTerm(m)),n.styleArgumentDescription(n.argumentDescription(m))));if(s=s.concat(this.formatItemList("Arguments:",l,n)),this.groupItems(t.options,n.visibleOptions(t),m=>m.helpGroupHeading??"Options:").forEach((m,d)=>{let h=m.map(f=>r(n.styleOptionTerm(n.optionTerm(f)),n.styleOptionDescription(n.optionDescription(f))));s=s.concat(this.formatItemList(d,h,n))}),n.showGlobalOptions){let m=n.visibleGlobalOptions(t).map(d=>r(n.styleOptionTerm(n.optionTerm(d)),n.styleOptionDescription(n.optionDescription(d))));s=s.concat(this.formatItemList("Global Options:",m,n))}return this.groupItems(t.commands,n.visibleCommands(t),m=>m.helpGroup()||"Commands:").forEach((m,d)=>{let h=m.map(f=>r(n.styleSubcommandTerm(n.subcommandTerm(f)),n.styleSubcommandDescription(n.subcommandDescription(f))));s=s.concat(this.formatItemList(d,h,n))}),s.join(`
3
+ `)}displayWidth(t){return wl(t).length}styleTitle(t){return t}styleUsage(t){return t.split(" ").map(n=>n==="[options]"?this.styleOptionText(n):n==="[command]"?this.styleSubcommandText(n):n[0]==="["||n[0]==="<"?this.styleArgumentText(n):this.styleCommandText(n)).join(" ")}styleCommandDescription(t){return this.styleDescriptionText(t)}styleOptionDescription(t){return this.styleDescriptionText(t)}styleSubcommandDescription(t){return this.styleDescriptionText(t)}styleArgumentDescription(t){return this.styleDescriptionText(t)}styleDescriptionText(t){return t}styleOptionTerm(t){return this.styleOptionText(t)}styleSubcommandTerm(t){return t.split(" ").map(n=>n==="[options]"?this.styleOptionText(n):n[0]==="["||n[0]==="<"?this.styleArgumentText(n):this.styleSubcommandText(n)).join(" ")}styleArgumentTerm(t){return this.styleArgumentText(t)}styleOptionText(t){return t}styleArgumentText(t){return t}styleSubcommandText(t){return t}styleCommandText(t){return t}padWidth(t,n){return Math.max(n.longestOptionTermLength(t,n),n.longestGlobalOptionTermLength(t,n),n.longestSubcommandTermLength(t,n),n.longestArgumentTermLength(t,n))}preformatted(t){return/\n[^\S\r\n]/.test(t)}formatItem(t,n,o,i){let s=" ".repeat(2);if(!o)return s+t;let a=t.padEnd(n+t.length-i.displayWidth(t)),l=2,c=(this.helpWidth??80)-n-l-2,m;return c<this.minWidthToWrap||i.preformatted(o)?m=o:m=i.boxWrap(o,c).replace(/\n/g,`
4
+ `+" ".repeat(n+l)),s+a+" ".repeat(l)+m.replace(/\n/g,`
5
+ ${s}`)}boxWrap(t,n){if(n<this.minWidthToWrap)return t;let o=t.split(/\r\n|\n/),i=/[\s]*[^\s]+/g,r=[];return o.forEach(s=>{let a=s.match(i);if(a===null){r.push("");return}let l=[a.shift()],u=this.displayWidth(l[0]);a.forEach(c=>{let m=this.displayWidth(c);if(u+m<=n){l.push(c),u+=m;return}r.push(l.join(""));let d=c.trimStart();l=[d],u=this.displayWidth(d)}),r.push(l.join(""))}),r.join(`
6
+ `)}};var Ie=class{constructor(t,n){this.flags=t,this.description=n||"",this.required=t.includes("<"),this.optional=t.includes("["),this.variadic=/\w\.\.\.[>\]]$/.test(t),this.mandatory=!1;let o=_l(t);this.short=o.shortFlag,this.long=o.longFlag,this.negate=!1,this.long&&(this.negate=this.long.startsWith("--no-")),this.defaultValue=void 0,this.defaultValueDescription=void 0,this.presetArg=void 0,this.envVar=void 0,this.parseArg=void 0,this.hidden=!1,this.argChoices=void 0,this.conflictsWith=[],this.implied=void 0,this.helpGroupHeading=void 0}default(t,n){return this.defaultValue=t,this.defaultValueDescription=n,this}preset(t){return this.presetArg=t,this}conflicts(t){return this.conflictsWith=this.conflictsWith.concat(t),this}implies(t){let n=t;return typeof t=="string"&&(n={[t]:!0}),this.implied=Object.assign(this.implied||{},n),this}env(t){return this.envVar=t,this}argParser(t){return this.parseArg=t,this}makeOptionMandatory(t=!0){return this.mandatory=!!t,this}hideHelp(t=!0){return this.hidden=!!t,this}_collectValue(t,n){return n===this.defaultValue||!Array.isArray(n)?[t]:(n.push(t),n)}choices(t){return this.argChoices=t.slice(),this.parseArg=(n,o)=>{if(!this.argChoices.includes(n))throw new We(`Allowed choices are ${this.argChoices.join(", ")}.`);return this.variadic?this._collectValue(n,o):n},this}name(){return this.long?this.long.replace(/^--/,""):this.short.replace(/^-/,"")}attributeName(){return this.negate?Oi(this.name().replace(/^no-/,"")):Oi(this.name())}helpGroup(t){return this.helpGroupHeading=t,this}is(t){return this.short===t||this.long===t}isBoolean(){return!this.required&&!this.optional&&!this.negate}},tn=class{constructor(t){this.positiveOptions=new Map,this.negativeOptions=new Map,this.dualOptions=new Set,t.forEach(n=>{n.negate?this.negativeOptions.set(n.attributeName(),n):this.positiveOptions.set(n.attributeName(),n)}),this.negativeOptions.forEach((n,o)=>{this.positiveOptions.has(o)&&this.dualOptions.add(o)})}valueFromOption(t,n){let o=n.attributeName();if(!this.dualOptions.has(o))return!0;let i=this.negativeOptions.get(o).presetArg,r=i!==void 0?i:!1;return n.negate===(r===t)}};function Oi(e){return e.split("-").reduce((t,n)=>t+n[0].toUpperCase()+n.slice(1))}function _l(e){let t,n,o=/^-[^-]$/,i=/^--[^-]/,r=e.split(/[ |,]+/).concat("guard");if(o.test(r[0])&&(t=r.shift()),i.test(r[0])&&(n=r.shift()),!t&&o.test(r[0])&&(t=r.shift()),!t&&i.test(r[0])&&(t=n,n=r.shift()),r[0].startsWith("-")){let s=r[0],a=`option creation failed due to '${s}' in option flags '${e}'`;throw/^-[^-][^-]/.test(s)?new Error(`${a}
7
7
  - a short flag is a single dash and a single character
8
8
  - either use a single dash and a single character (for a short flag)
9
9
  - or use a double dash for a long option (and can have two, like '--ws, --workspace')`):o.test(s)?new Error(`${a}
10
10
  - too many short flags`):i.test(s)?new Error(`${a}
11
11
  - too many long flags`):new Error(`${a}
12
- - unrecognised flag format`)}if(t===void 0&&n===void 0)throw new Error(`option creation failed due to no flags found in '${e}'.`);return{shortFlag:t,longFlag:n}}function Cl(e,t){if(Math.abs(e.length-t.length)>3)return Math.max(e.length,t.length);let n=[];for(let o=0;o<=e.length;o++)n[o]=[o];for(let o=0;o<=t.length;o++)n[0][o]=o;for(let o=1;o<=t.length;o++)for(let i=1;i<=e.length;i++){let r;e[i-1]===t[o-1]?r=0:r=1,n[i][o]=Math.min(n[i-1][o]+1,n[i][o-1]+1,n[i-1][o-1]+r),i>1&&o>1&&e[i-1]===t[o-2]&&e[i-2]===t[o-1]&&(n[i][o]=Math.min(n[i][o],n[i-2][o-2]+1))}return n[e.length][t.length]}function lo(e,t){if(!t||t.length===0)return"";t=Array.from(new Set(t));let n=e.startsWith("--");n&&(e=e.slice(2),t=t.map(s=>s.slice(2)));let o=[],i=3,r=.4;return t.forEach(s=>{if(s.length<=1)return;let a=Cl(e,s),l=Math.max(e.length,s.length);(l-a)/l>r&&(a<i?(i=a,o=[s]):a===i&&o.push(s))}),o.sort((s,a)=>s.localeCompare(a)),n&&(o=o.map(s=>`--${s}`)),o.length>1?`
12
+ - unrecognised flag format`)}if(t===void 0&&n===void 0)throw new Error(`option creation failed due to no flags found in '${e}'.`);return{shortFlag:t,longFlag:n}}function bl(e,t){if(Math.abs(e.length-t.length)>3)return Math.max(e.length,t.length);let n=[];for(let o=0;o<=e.length;o++)n[o]=[o];for(let o=0;o<=t.length;o++)n[0][o]=o;for(let o=1;o<=t.length;o++)for(let i=1;i<=e.length;i++){let r;e[i-1]===t[o-1]?r=0:r=1,n[i][o]=Math.min(n[i-1][o]+1,n[i][o-1]+1,n[i-1][o-1]+r),i>1&&o>1&&e[i-1]===t[o-2]&&e[i-2]===t[o-1]&&(n[i][o]=Math.min(n[i][o],n[i-2][o-2]+1))}return n[e.length][t.length]}function io(e,t){if(!t||t.length===0)return"";t=Array.from(new Set(t));let n=e.startsWith("--");n&&(e=e.slice(2),t=t.map(s=>s.slice(2)));let o=[],i=3,r=.4;return t.forEach(s=>{if(s.length<=1)return;let a=bl(e,s),l=Math.max(e.length,s.length);(l-a)/l>r&&(a<i?(i=a,o=[s]):a===i&&o.push(s))}),o.sort((s,a)=>s.localeCompare(a)),n&&(o=o.map(s=>`--${s}`)),o.length>1?`
13
13
  (Did you mean one of ${o.join(", ")}?)`:o.length===1?`
14
- (Did you mean ${o[0]}?)`:""}var it=class e extends kl{constructor(t){super(),this.commands=[],this.options=[],this.parent=null,this._allowUnknownOption=!1,this._allowExcessArguments=!1,this.registeredArguments=[],this._args=this.registeredArguments,this.args=[],this.rawArgs=[],this.processedArgs=[],this._scriptPath=null,this._name=t||"",this._optionValues={},this._optionValueSources={},this._storeOptionsAsProperties=!1,this._actionHandler=null,this._executableHandler=!1,this._executableFile=null,this._executableDir=null,this._defaultCommandName=null,this._exitCallback=null,this._aliases=[],this._combineFlagAndOptionalValue=!0,this._description="",this._summary="",this._argsDescription=void 0,this._enablePositionalOptions=!1,this._passThroughOptions=!1,this._lifeCycleHooks={},this._showHelpAfterError=!1,this._showSuggestionAfterError=!0,this._savedState=null,this._outputConfiguration={writeOut:n=>T.stdout.write(n),writeErr:n=>T.stderr.write(n),outputError:(n,o)=>o(n),getOutHelpWidth:()=>T.stdout.isTTY?T.stdout.columns:void 0,getErrHelpWidth:()=>T.stderr.isTTY?T.stderr.columns:void 0,getOutHasColors:()=>Ei()??(T.stdout.isTTY&&T.stdout.hasColors?.()),getErrHasColors:()=>Ei()??(T.stderr.isTTY&&T.stderr.hasColors?.()),stripColor:n=>Sl(n)},this._hidden=!1,this._helpOption=void 0,this._addImplicitHelpCommand=void 0,this._helpCommand=void 0,this._helpConfiguration={},this._helpGroupHeading=void 0,this._defaultCommandGroup=void 0,this._defaultOptionGroup=void 0}copyInheritedSettings(t){return this._outputConfiguration=t._outputConfiguration,this._helpOption=t._helpOption,this._helpCommand=t._helpCommand,this._helpConfiguration=t._helpConfiguration,this._exitCallback=t._exitCallback,this._storeOptionsAsProperties=t._storeOptionsAsProperties,this._combineFlagAndOptionalValue=t._combineFlagAndOptionalValue,this._allowExcessArguments=t._allowExcessArguments,this._enablePositionalOptions=t._enablePositionalOptions,this._showHelpAfterError=t._showHelpAfterError,this._showSuggestionAfterError=t._showSuggestionAfterError,this}_getCommandAndAncestors(){let t=[];for(let n=this;n;n=n.parent)t.push(n);return t}command(t,n,o){let i=n,r=o;typeof i=="object"&&i!==null&&(r=i,i=null),r=r||{};let[,s,a]=t.match(/([^ ]+) *(.*)/),l=this.createCommand(s);return i&&(l.description(i),l._executableHandler=!0),r.isDefault&&(this._defaultCommandName=l._name),l._hidden=!!(r.noHelp||r.hidden),l._executableFile=r.executableFile||null,a&&l.arguments(a),this._registerCommand(l),l.parent=this,l.copyInheritedSettings(this),i?this:l}createCommand(t){return new e(t)}createHelp(){return Object.assign(new Te,this.configureHelp())}configureHelp(t){return t===void 0?this._helpConfiguration:(this._helpConfiguration=t,this)}configureOutput(t){return t===void 0?this._outputConfiguration:(this._outputConfiguration={...this._outputConfiguration,...t},this)}showHelpAfterError(t=!0){return typeof t!="string"&&(t=!!t),this._showHelpAfterError=t,this}showSuggestionAfterError(t=!0){return this._showSuggestionAfterError=!!t,this}addCommand(t,n){if(!t._name)throw new Error(`Command passed to .addCommand() must have a name
15
- - specify the name in Command constructor or using .name()`);return n=n||{},n.isDefault&&(this._defaultCommandName=t._name),(n.noHelp||n.hidden)&&(t._hidden=!0),this._registerCommand(t),t.parent=this,t._checkForBrokenPassThrough(),this}createArgument(t,n){return new Tt(t,n)}argument(t,n,o,i){let r=this.createArgument(t,n);return typeof o=="function"?r.default(i).argParser(o):r.default(o),this.addArgument(r),this}arguments(t){return t.trim().split(/ +/).forEach(n=>{this.argument(n)}),this}addArgument(t){let n=this.registeredArguments.slice(-1)[0];if(n?.variadic)throw new Error(`only the last argument can be variadic '${n.name()}'`);if(t.required&&t.defaultValue!==void 0&&t.parseArg===void 0)throw new Error(`a default value for a required argument is never used: '${t.name()}'`);return this.registeredArguments.push(t),this}helpCommand(t,n){if(typeof t=="boolean")return this._addImplicitHelpCommand=t,t&&this._defaultCommandGroup&&this._initCommandGroup(this._getHelpCommand()),this;let o=t??"help [command]",[,i,r]=o.match(/([^ ]+) *(.*)/),s=n??"display help for command",a=this.createCommand(i);return a.helpOption(!1),r&&a.arguments(r),s&&a.description(s),this._addImplicitHelpCommand=!0,this._helpCommand=a,(t||n)&&this._initCommandGroup(a),this}addHelpCommand(t,n){return typeof t!="object"?(this.helpCommand(t,n),this):(this._addImplicitHelpCommand=!0,this._helpCommand=t,this._initCommandGroup(t),this)}_getHelpCommand(){return this._addImplicitHelpCommand??(this.commands.length&&!this._actionHandler&&!this._findCommand("help"))?(this._helpCommand===void 0&&this.helpCommand(void 0,void 0),this._helpCommand):null}hook(t,n){let o=["preSubcommand","preAction","postAction"];if(!o.includes(t))throw new Error(`Unexpected value for event passed to hook : '${t}'.
16
- Expecting one of '${o.join("', '")}'`);return this._lifeCycleHooks[t]?this._lifeCycleHooks[t].push(n):this._lifeCycleHooks[t]=[n],this}exitOverride(t){return t?this._exitCallback=t:this._exitCallback=n=>{if(n.code!=="commander.executeSubCommandAsync")throw n},this}_exit(t,n,o){this._exitCallback&&this._exitCallback(new B(t,n,o)),T.exit(t)}action(t){let n=o=>{let i=this.registeredArguments.length,r=o.slice(0,i);return this._storeOptionsAsProperties?r[i]=this:r[i]=this.opts(),r.push(this),t.apply(this,r)};return this._actionHandler=n,this}createOption(t,n){return new ot(t,n)}_callParseArg(t,n,o,i){try{return t.parseArg(n,o)}catch(r){if(r.code==="commander.invalidArgument"){let s=`${i} ${r.message}`;this.error(s,{exitCode:r.exitCode,code:r.code})}throw r}}_registerOption(t){let n=t.short&&this._findOption(t.short)||t.long&&this._findOption(t.long);if(n){let o=t.long&&this._findOption(t.long)?t.long:t.short;throw new Error(`Cannot add option '${t.flags}'${this._name&&` to command '${this._name}'`} due to conflicting flag '${o}'
17
- - already used by option '${n.flags}'`)}this._initOptionGroup(t),this.options.push(t)}_registerCommand(t){let n=i=>[i.name()].concat(i.aliases()),o=n(t).find(i=>this._findCommand(i));if(o){let i=n(this._findCommand(o)).join("|"),r=n(t).join("|");throw new Error(`cannot add command '${r}' as already have command '${i}'`)}this._initCommandGroup(t),this.commands.push(t)}addOption(t){this._registerOption(t);let n=t.name(),o=t.attributeName();t.defaultValue!==void 0&&this.setOptionValueWithSource(o,t.defaultValue,"default");let i=(r,s,a)=>{r==null&&t.presetArg!==void 0&&(r=t.presetArg);let l=this.getOptionValue(o);r!==null&&t.parseArg?r=this._callParseArg(t,r,l,s):r!==null&&t.variadic&&(r=t._collectValue(r,l)),r==null&&(t.negate?r=!1:t.isBoolean()||t.optional?r=!0:r=""),this.setOptionValueWithSource(o,r,a)};return this.on("option:"+n,r=>{let s=`error: option '${t.flags}' argument '${r}' is invalid.`;i(r,s,"cli")}),t.envVar&&this.on("optionEnv:"+n,r=>{let s=`error: option '${t.flags}' value '${r}' from env '${t.envVar}' is invalid.`;i(r,s,"env")}),this}_optionEx(t,n,o,i,r){if(typeof n=="object"&&n instanceof ot)throw new Error("To add an Option object use addOption() instead of option() or requiredOption()");let s=this.createOption(n,o);if(s.makeOptionMandatory(!!t.mandatory),typeof i=="function")s.default(r).argParser(i);else if(i instanceof RegExp){let a=i;i=(l,u)=>{let c=a.exec(l);return c?c[0]:u},s.default(r).argParser(i)}else s.default(i);return this.addOption(s)}option(t,n,o,i){return this._optionEx({},t,n,o,i)}requiredOption(t,n,o,i){return this._optionEx({mandatory:!0},t,n,o,i)}combineFlagAndOptionalValue(t=!0){return this._combineFlagAndOptionalValue=!!t,this}allowUnknownOption(t=!0){return this._allowUnknownOption=!!t,this}allowExcessArguments(t=!0){return this._allowExcessArguments=!!t,this}enablePositionalOptions(t=!0){return this._enablePositionalOptions=!!t,this}passThroughOptions(t=!0){return this._passThroughOptions=!!t,this._checkForBrokenPassThrough(),this}_checkForBrokenPassThrough(){if(this.parent&&this._passThroughOptions&&!this.parent._enablePositionalOptions)throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`)}storeOptionsAsProperties(t=!0){if(this.options.length)throw new Error("call .storeOptionsAsProperties() before adding options");if(Object.keys(this._optionValues).length)throw new Error("call .storeOptionsAsProperties() before setting option values");return this._storeOptionsAsProperties=!!t,this}getOptionValue(t){return this._storeOptionsAsProperties?this[t]:this._optionValues[t]}setOptionValue(t,n){return this.setOptionValueWithSource(t,n,void 0)}setOptionValueWithSource(t,n,o){return this._storeOptionsAsProperties?this[t]=n:this._optionValues[t]=n,this._optionValueSources[t]=o,this}getOptionValueSource(t){return this._optionValueSources[t]}getOptionValueSourceWithGlobals(t){let n;return this._getCommandAndAncestors().forEach(o=>{o.getOptionValueSource(t)!==void 0&&(n=o.getOptionValueSource(t))}),n}_prepareUserArgs(t,n){if(t!==void 0&&!Array.isArray(t))throw new Error("first parameter to parse must be array or undefined");if(n=n||{},t===void 0&&n.from===void 0){T.versions?.electron&&(n.from="electron");let i=T.execArgv??[];(i.includes("-e")||i.includes("--eval")||i.includes("-p")||i.includes("--print"))&&(n.from="eval")}t===void 0&&(t=T.argv),this.rawArgs=t.slice();let o;switch(n.from){case void 0:case"node":this._scriptPath=t[1],o=t.slice(2);break;case"electron":T.defaultApp?(this._scriptPath=t[1],o=t.slice(2)):o=t.slice(1);break;case"user":o=t.slice(0);break;case"eval":o=t.slice(1);break;default:throw new Error(`unexpected parse option { from: '${n.from}' }`)}return!this._name&&this._scriptPath&&this.nameFromFilename(this._scriptPath),this._name=this._name||"program",o}parse(t,n){this._prepareForParse();let o=this._prepareUserArgs(t,n);return this._parseCommand([],o),this}async parseAsync(t,n){this._prepareForParse();let o=this._prepareUserArgs(t,n);return await this._parseCommand([],o),this}_prepareForParse(){this._savedState===null?(this.options.filter(t=>t.negate&&t.defaultValue===void 0&&this.getOptionValue(t.attributeName())===void 0).forEach(t=>{let n=t.long.replace(/^--no-/,"--");this._findOption(n)||this.setOptionValueWithSource(t.attributeName(),!0,"default")}),this.saveStateBeforeParse()):this.restoreStateBeforeParse()}saveStateBeforeParse(){this._savedState={_name:this._name,_optionValues:{...this._optionValues},_optionValueSources:{...this._optionValueSources}}}restoreStateBeforeParse(){if(this._storeOptionsAsProperties)throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
14
+ (Did you mean ${o[0]}?)`:""}var ot=class e extends $l{constructor(t){super(),this.commands=[],this.options=[],this.parent=null,this._allowUnknownOption=!1,this._allowExcessArguments=!1,this.registeredArguments=[],this._args=this.registeredArguments,this.args=[],this.rawArgs=[],this.processedArgs=[],this._scriptPath=null,this._name=t||"",this._optionValues={},this._optionValueSources={},this._storeOptionsAsProperties=!1,this._actionHandler=null,this._executableHandler=!1,this._executableFile=null,this._executableDir=null,this._defaultCommandName=null,this._exitCallback=null,this._aliases=[],this._combineFlagAndOptionalValue=!0,this._description="",this._summary="",this._argsDescription=void 0,this._enablePositionalOptions=!1,this._passThroughOptions=!1,this._lifeCycleHooks={},this._showHelpAfterError=!1,this._showSuggestionAfterError=!0,this._savedState=null,this._outputConfiguration={writeOut:n=>T.stdout.write(n),writeErr:n=>T.stderr.write(n),outputError:(n,o)=>o(n),getOutHelpWidth:()=>T.stdout.isTTY?T.stdout.columns:void 0,getErrHelpWidth:()=>T.stderr.isTTY?T.stderr.columns:void 0,getOutHasColors:()=>Ti()??(T.stdout.isTTY&&T.stdout.hasColors?.()),getErrHasColors:()=>Ti()??(T.stderr.isTTY&&T.stderr.hasColors?.()),stripColor:n=>vl(n)},this._hidden=!1,this._helpOption=void 0,this._addImplicitHelpCommand=void 0,this._helpCommand=void 0,this._helpConfiguration={},this._helpGroupHeading=void 0,this._defaultCommandGroup=void 0,this._defaultOptionGroup=void 0}copyInheritedSettings(t){return this._outputConfiguration=t._outputConfiguration,this._helpOption=t._helpOption,this._helpCommand=t._helpCommand,this._helpConfiguration=t._helpConfiguration,this._exitCallback=t._exitCallback,this._storeOptionsAsProperties=t._storeOptionsAsProperties,this._combineFlagAndOptionalValue=t._combineFlagAndOptionalValue,this._allowExcessArguments=t._allowExcessArguments,this._enablePositionalOptions=t._enablePositionalOptions,this._showHelpAfterError=t._showHelpAfterError,this._showSuggestionAfterError=t._showSuggestionAfterError,this}_getCommandAndAncestors(){let t=[];for(let n=this;n;n=n.parent)t.push(n);return t}command(t,n,o){let i=n,r=o;typeof i=="object"&&i!==null&&(r=i,i=null),r=r||{};let[,s,a]=t.match(/([^ ]+) *(.*)/),l=this.createCommand(s);return i&&(l.description(i),l._executableHandler=!0),r.isDefault&&(this._defaultCommandName=l._name),l._hidden=!!(r.noHelp||r.hidden),l._executableFile=r.executableFile||null,a&&l.arguments(a),this._registerCommand(l),l.parent=this,l.copyInheritedSettings(this),i?this:l}createCommand(t){return new e(t)}createHelp(){return Object.assign(new Ee,this.configureHelp())}configureHelp(t){return t===void 0?this._helpConfiguration:(this._helpConfiguration=t,this)}configureOutput(t){return t===void 0?this._outputConfiguration:(this._outputConfiguration={...this._outputConfiguration,...t},this)}showHelpAfterError(t=!0){return typeof t!="string"&&(t=!!t),this._showHelpAfterError=t,this}showSuggestionAfterError(t=!0){return this._showSuggestionAfterError=!!t,this}addCommand(t,n){if(!t._name)throw new Error(`Command passed to .addCommand() must have a name
15
+ - specify the name in Command constructor or using .name()`);return n=n||{},n.isDefault&&(this._defaultCommandName=t._name),(n.noHelp||n.hidden)&&(t._hidden=!0),this._registerCommand(t),t.parent=this,t._checkForBrokenPassThrough(),this}createArgument(t,n){return new Rt(t,n)}argument(t,n,o,i){let r=this.createArgument(t,n);return typeof o=="function"?r.default(i).argParser(o):r.default(o),this.addArgument(r),this}arguments(t){return t.trim().split(/ +/).forEach(n=>{this.argument(n)}),this}addArgument(t){let n=this.registeredArguments.slice(-1)[0];if(n?.variadic)throw new Error(`only the last argument can be variadic '${n.name()}'`);if(t.required&&t.defaultValue!==void 0&&t.parseArg===void 0)throw new Error(`a default value for a required argument is never used: '${t.name()}'`);return this.registeredArguments.push(t),this}helpCommand(t,n){if(typeof t=="boolean")return this._addImplicitHelpCommand=t,t&&this._defaultCommandGroup&&this._initCommandGroup(this._getHelpCommand()),this;let o=t??"help [command]",[,i,r]=o.match(/([^ ]+) *(.*)/),s=n??"display help for command",a=this.createCommand(i);return a.helpOption(!1),r&&a.arguments(r),s&&a.description(s),this._addImplicitHelpCommand=!0,this._helpCommand=a,(t||n)&&this._initCommandGroup(a),this}addHelpCommand(t,n){return typeof t!="object"?(this.helpCommand(t,n),this):(this._addImplicitHelpCommand=!0,this._helpCommand=t,this._initCommandGroup(t),this)}_getHelpCommand(){return this._addImplicitHelpCommand??(this.commands.length&&!this._actionHandler&&!this._findCommand("help"))?(this._helpCommand===void 0&&this.helpCommand(void 0,void 0),this._helpCommand):null}hook(t,n){let o=["preSubcommand","preAction","postAction"];if(!o.includes(t))throw new Error(`Unexpected value for event passed to hook : '${t}'.
16
+ Expecting one of '${o.join("', '")}'`);return this._lifeCycleHooks[t]?this._lifeCycleHooks[t].push(n):this._lifeCycleHooks[t]=[n],this}exitOverride(t){return t?this._exitCallback=t:this._exitCallback=n=>{if(n.code!=="commander.executeSubCommandAsync")throw n},this}_exit(t,n,o){this._exitCallback&&this._exitCallback(new B(t,n,o)),T.exit(t)}action(t){let n=o=>{let i=this.registeredArguments.length,r=o.slice(0,i);return this._storeOptionsAsProperties?r[i]=this:r[i]=this.opts(),r.push(this),t.apply(this,r)};return this._actionHandler=n,this}createOption(t,n){return new Ie(t,n)}_callParseArg(t,n,o,i){try{return t.parseArg(n,o)}catch(r){if(r.code==="commander.invalidArgument"){let s=`${i} ${r.message}`;this.error(s,{exitCode:r.exitCode,code:r.code})}throw r}}_registerOption(t){let n=t.short&&this._findOption(t.short)||t.long&&this._findOption(t.long);if(n){let o=t.long&&this._findOption(t.long)?t.long:t.short;throw new Error(`Cannot add option '${t.flags}'${this._name&&` to command '${this._name}'`} due to conflicting flag '${o}'
17
+ - already used by option '${n.flags}'`)}this._initOptionGroup(t),this.options.push(t)}_registerCommand(t){let n=i=>[i.name()].concat(i.aliases()),o=n(t).find(i=>this._findCommand(i));if(o){let i=n(this._findCommand(o)).join("|"),r=n(t).join("|");throw new Error(`cannot add command '${r}' as already have command '${i}'`)}this._initCommandGroup(t),this.commands.push(t)}addOption(t){this._registerOption(t);let n=t.name(),o=t.attributeName();t.defaultValue!==void 0&&this.setOptionValueWithSource(o,t.defaultValue,"default");let i=(r,s,a)=>{r==null&&t.presetArg!==void 0&&(r=t.presetArg);let l=this.getOptionValue(o);r!==null&&t.parseArg?r=this._callParseArg(t,r,l,s):r!==null&&t.variadic&&(r=t._collectValue(r,l)),r==null&&(t.negate?r=!1:t.isBoolean()||t.optional?r=!0:r=""),this.setOptionValueWithSource(o,r,a)};return this.on("option:"+n,r=>{let s=`error: option '${t.flags}' argument '${r}' is invalid.`;i(r,s,"cli")}),t.envVar&&this.on("optionEnv:"+n,r=>{let s=`error: option '${t.flags}' value '${r}' from env '${t.envVar}' is invalid.`;i(r,s,"env")}),this}_optionEx(t,n,o,i,r){if(typeof n=="object"&&n instanceof Ie)throw new Error("To add an Option object use addOption() instead of option() or requiredOption()");let s=this.createOption(n,o);if(s.makeOptionMandatory(!!t.mandatory),typeof i=="function")s.default(r).argParser(i);else if(i instanceof RegExp){let a=i;i=(l,u)=>{let c=a.exec(l);return c?c[0]:u},s.default(r).argParser(i)}else s.default(i);return this.addOption(s)}option(t,n,o,i){return this._optionEx({},t,n,o,i)}requiredOption(t,n,o,i){return this._optionEx({mandatory:!0},t,n,o,i)}combineFlagAndOptionalValue(t=!0){return this._combineFlagAndOptionalValue=!!t,this}allowUnknownOption(t=!0){return this._allowUnknownOption=!!t,this}allowExcessArguments(t=!0){return this._allowExcessArguments=!!t,this}enablePositionalOptions(t=!0){return this._enablePositionalOptions=!!t,this}passThroughOptions(t=!0){return this._passThroughOptions=!!t,this._checkForBrokenPassThrough(),this}_checkForBrokenPassThrough(){if(this.parent&&this._passThroughOptions&&!this.parent._enablePositionalOptions)throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`)}storeOptionsAsProperties(t=!0){if(this.options.length)throw new Error("call .storeOptionsAsProperties() before adding options");if(Object.keys(this._optionValues).length)throw new Error("call .storeOptionsAsProperties() before setting option values");return this._storeOptionsAsProperties=!!t,this}getOptionValue(t){return this._storeOptionsAsProperties?this[t]:this._optionValues[t]}setOptionValue(t,n){return this.setOptionValueWithSource(t,n,void 0)}setOptionValueWithSource(t,n,o){return this._storeOptionsAsProperties?this[t]=n:this._optionValues[t]=n,this._optionValueSources[t]=o,this}getOptionValueSource(t){return this._optionValueSources[t]}getOptionValueSourceWithGlobals(t){let n;return this._getCommandAndAncestors().forEach(o=>{o.getOptionValueSource(t)!==void 0&&(n=o.getOptionValueSource(t))}),n}_prepareUserArgs(t,n){if(t!==void 0&&!Array.isArray(t))throw new Error("first parameter to parse must be array or undefined");if(n=n||{},t===void 0&&n.from===void 0){T.versions?.electron&&(n.from="electron");let i=T.execArgv??[];(i.includes("-e")||i.includes("--eval")||i.includes("-p")||i.includes("--print"))&&(n.from="eval")}t===void 0&&(t=T.argv),this.rawArgs=t.slice();let o;switch(n.from){case void 0:case"node":this._scriptPath=t[1],o=t.slice(2);break;case"electron":T.defaultApp?(this._scriptPath=t[1],o=t.slice(2)):o=t.slice(1);break;case"user":o=t.slice(0);break;case"eval":o=t.slice(1);break;default:throw new Error(`unexpected parse option { from: '${n.from}' }`)}return!this._name&&this._scriptPath&&this.nameFromFilename(this._scriptPath),this._name=this._name||"program",o}parse(t,n){this._prepareForParse();let o=this._prepareUserArgs(t,n);return this._parseCommand([],o),this}async parseAsync(t,n){this._prepareForParse();let o=this._prepareUserArgs(t,n);return await this._parseCommand([],o),this}_prepareForParse(){this._savedState===null?(this.options.filter(t=>t.negate&&t.defaultValue===void 0&&this.getOptionValue(t.attributeName())===void 0).forEach(t=>{let n=t.long.replace(/^--no-/,"--");this._findOption(n)||this.setOptionValueWithSource(t.attributeName(),!0,"default")}),this.saveStateBeforeParse()):this.restoreStateBeforeParse()}saveStateBeforeParse(){this._savedState={_name:this._name,_optionValues:{...this._optionValues},_optionValueSources:{...this._optionValueSources}}}restoreStateBeforeParse(){if(this._storeOptionsAsProperties)throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
18
18
  - either make a new Command for each call to parse, or stop storing options as properties`);this._name=this._savedState._name,this._scriptPath=null,this.rawArgs=[],this._optionValues={...this._savedState._optionValues},this._optionValueSources={...this._savedState._optionValueSources},this.args=[],this.processedArgs=[]}_checkForMissingExecutable(t,n,o){if(nn.existsSync(t))return;let i=n?`searched for local subcommand relative to directory '${n}'`:"no directory for search for local subcommand, use .executableDir() to supply a custom directory",r=`'${t}' does not exist
19
19
  - if '${o}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
20
20
  - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
21
- - ${i}`;throw new Error(r)}_executeSubCommand(t,n){n=n.slice();let o=[".js",".ts",".tsx",".mjs",".cjs"];function i(c,d){let m=we.resolve(c,d);if(nn.existsSync(m))return m;if(o.includes(we.extname(d)))return;let h=o.find(f=>nn.existsSync(`${m}${f}`));if(h)return`${m}${h}`}this._checkForMissingMandatoryOptions(),this._checkForConflictingOptions();let r=t._executableFile||`${this._name}-${t._name}`,s=this._executableDir||"";if(this._scriptPath){let c;try{c=nn.realpathSync(this._scriptPath)}catch{c=this._scriptPath}s=we.resolve(we.dirname(c),s)}if(s){let c=i(s,r);if(!c&&!t._executableFile&&this._scriptPath){let d=we.basename(this._scriptPath,we.extname(this._scriptPath));d!==this._name&&(c=i(s,`${d}-${t._name}`))}r=c||r}let a=o.includes(we.extname(r)),l;T.platform!=="win32"?a?(n.unshift(r),n=Ri(T.execArgv).concat(n),l=uo.spawn(T.argv[0],n,{stdio:"inherit"})):l=uo.spawn(r,n,{stdio:"inherit"}):(this._checkForMissingExecutable(r,s,t._name),n.unshift(r),n=Ri(T.execArgv).concat(n),l=uo.spawn(T.execPath,n,{stdio:"inherit"})),l.killed||["SIGUSR1","SIGUSR2","SIGTERM","SIGINT","SIGHUP"].forEach(d=>{T.on(d,()=>{l.killed===!1&&l.exitCode===null&&l.kill(d)})});let u=this._exitCallback;l.on("close",c=>{c=c??1,u?u(new B(c,"commander.executeSubCommandAsync","(close)")):T.exit(c)}),l.on("error",c=>{if(c.code==="ENOENT")this._checkForMissingExecutable(r,s,t._name);else if(c.code==="EACCES")throw new Error(`'${r}' not executable`);if(!u)T.exit(1);else{let d=new B(1,"commander.executeSubCommandAsync","(error)");d.nestedError=c,u(d)}}),this.runningCommand=l}_dispatchSubcommand(t,n,o){let i=this._findCommand(t);i||this.help({error:!0}),i._prepareForParse();let r;return r=this._chainOrCallSubCommandHook(r,i,"preSubcommand"),r=this._chainOrCall(r,()=>{if(i._executableHandler)this._executeSubCommand(i,n.concat(o));else return i._parseCommand(n,o)}),r}_dispatchHelpCommand(t){t||this.help();let n=this._findCommand(t);return n&&!n._executableHandler&&n.help(),this._dispatchSubcommand(t,[],[this._getHelpOption()?.long??this._getHelpOption()?.short??"--help"])}_checkNumberOfArguments(){this.registeredArguments.forEach((t,n)=>{t.required&&this.args[n]==null&&this.missingArgument(t.name())}),!(this.registeredArguments.length>0&&this.registeredArguments[this.registeredArguments.length-1].variadic)&&this.args.length>this.registeredArguments.length&&this._excessArguments(this.args)}_processArguments(){let t=(o,i,r)=>{let s=i;if(i!==null&&o.parseArg){let a=`error: command-argument value '${i}' is invalid for argument '${o.name()}'.`;s=this._callParseArg(o,i,r,a)}return s};this._checkNumberOfArguments();let n=[];this.registeredArguments.forEach((o,i)=>{let r=o.defaultValue;o.variadic?i<this.args.length?(r=this.args.slice(i),o.parseArg&&(r=r.reduce((s,a)=>t(o,a,s),o.defaultValue))):r===void 0&&(r=[]):i<this.args.length&&(r=this.args[i],o.parseArg&&(r=t(o,r,o.defaultValue))),n[i]=r}),this.processedArgs=n}_chainOrCall(t,n){return t?.then&&typeof t.then=="function"?t.then(()=>n()):n()}_chainOrCallHooks(t,n){let o=t,i=[];return this._getCommandAndAncestors().reverse().filter(r=>r._lifeCycleHooks[n]!==void 0).forEach(r=>{r._lifeCycleHooks[n].forEach(s=>{i.push({hookedCommand:r,callback:s})})}),n==="postAction"&&i.reverse(),i.forEach(r=>{o=this._chainOrCall(o,()=>r.callback(r.hookedCommand,this))}),o}_chainOrCallSubCommandHook(t,n,o){let i=t;return this._lifeCycleHooks[o]!==void 0&&this._lifeCycleHooks[o].forEach(r=>{i=this._chainOrCall(i,()=>r(this,n))}),i}_parseCommand(t,n){let o=this.parseOptions(n);if(this._parseOptionsEnv(),this._parseOptionsImplied(),t=t.concat(o.operands),n=o.unknown,this.args=t.concat(n),t&&this._findCommand(t[0]))return this._dispatchSubcommand(t[0],t.slice(1),n);if(this._getHelpCommand()&&t[0]===this._getHelpCommand().name())return this._dispatchHelpCommand(t[1]);if(this._defaultCommandName)return this._outputHelpIfRequested(n),this._dispatchSubcommand(this._defaultCommandName,t,n);this.commands.length&&this.args.length===0&&!this._actionHandler&&!this._defaultCommandName&&this.help({error:!0}),this._outputHelpIfRequested(o.unknown),this._checkForMissingMandatoryOptions(),this._checkForConflictingOptions();let i=()=>{o.unknown.length>0&&this.unknownOption(o.unknown[0])},r=`command:${this.name()}`;if(this._actionHandler){i(),this._processArguments();let s;return s=this._chainOrCallHooks(s,"preAction"),s=this._chainOrCall(s,()=>this._actionHandler(this.processedArgs)),this.parent&&(s=this._chainOrCall(s,()=>{this.parent.emit(r,t,n)})),s=this._chainOrCallHooks(s,"postAction"),s}if(this.parent?.listenerCount(r))i(),this._processArguments(),this.parent.emit(r,t,n);else if(t.length){if(this._findCommand("*"))return this._dispatchSubcommand("*",t,n);this.listenerCount("command:*")?this.emit("command:*",t,n):this.commands.length?this.unknownCommand():(i(),this._processArguments())}else this.commands.length?(i(),this.help({error:!0})):(i(),this._processArguments())}_findCommand(t){if(t)return this.commands.find(n=>n._name===t||n._aliases.includes(t))}_findOption(t){return this.options.find(n=>n.is(t))}_checkForMissingMandatoryOptions(){this._getCommandAndAncestors().forEach(t=>{t.options.forEach(n=>{n.mandatory&&t.getOptionValue(n.attributeName())===void 0&&t.missingMandatoryOptionValue(n)})})}_checkForConflictingLocalOptions(){let t=this.options.filter(o=>{let i=o.attributeName();return this.getOptionValue(i)===void 0?!1:this.getOptionValueSource(i)!=="default"});t.filter(o=>o.conflictsWith.length>0).forEach(o=>{let i=t.find(r=>o.conflictsWith.includes(r.attributeName()));i&&this._conflictingOption(o,i)})}_checkForConflictingOptions(){this._getCommandAndAncestors().forEach(t=>{t._checkForConflictingLocalOptions()})}parseOptions(t){let n=[],o=[],i=n;function r(c){return c.length>1&&c[0]==="-"}let s=c=>/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(c)?!this._getCommandAndAncestors().some(d=>d.options.map(m=>m.short).some(m=>/^-\d$/.test(m))):!1,a=null,l=null,u=0;for(;u<t.length||l;){let c=l??t[u++];if(l=null,c==="--"){i===o&&i.push(c),i.push(...t.slice(u));break}if(a&&(!r(c)||s(c))){this.emit(`option:${a.name()}`,c);continue}if(a=null,r(c)){let d=this._findOption(c);if(d){if(d.required){let m=t[u++];m===void 0&&this.optionMissingArgument(d),this.emit(`option:${d.name()}`,m)}else if(d.optional){let m=null;u<t.length&&(!r(t[u])||s(t[u]))&&(m=t[u++]),this.emit(`option:${d.name()}`,m)}else this.emit(`option:${d.name()}`);a=d.variadic?d:null;continue}}if(c.length>2&&c[0]==="-"&&c[1]!=="-"){let d=this._findOption(`-${c[1]}`);if(d){d.required||d.optional&&this._combineFlagAndOptionalValue?this.emit(`option:${d.name()}`,c.slice(2)):(this.emit(`option:${d.name()}`),l=`-${c.slice(2)}`);continue}}if(/^--[^=]+=/.test(c)){let d=c.indexOf("="),m=this._findOption(c.slice(0,d));if(m&&(m.required||m.optional)){this.emit(`option:${m.name()}`,c.slice(d+1));continue}}if(i===n&&r(c)&&!(this.commands.length===0&&s(c))&&(i=o),(this._enablePositionalOptions||this._passThroughOptions)&&n.length===0&&o.length===0){if(this._findCommand(c)){n.push(c),o.push(...t.slice(u));break}else if(this._getHelpCommand()&&c===this._getHelpCommand().name()){n.push(c,...t.slice(u));break}else if(this._defaultCommandName){o.push(c,...t.slice(u));break}}if(this._passThroughOptions){i.push(c,...t.slice(u));break}i.push(c)}return{operands:n,unknown:o}}opts(){if(this._storeOptionsAsProperties){let t={},n=this.options.length;for(let o=0;o<n;o++){let i=this.options[o].attributeName();t[i]=i===this._versionOptionName?this._version:this[i]}return t}return this._optionValues}optsWithGlobals(){return this._getCommandAndAncestors().reduce((t,n)=>Object.assign(t,n.opts()),{})}error(t,n){this._outputConfiguration.outputError(`${t}
21
+ - ${i}`;throw new Error(r)}_executeSubCommand(t,n){n=n.slice();let o=[".js",".ts",".tsx",".mjs",".cjs"];function i(c,m){let d=be.resolve(c,m);if(nn.existsSync(d))return d;if(o.includes(be.extname(m)))return;let h=o.find(f=>nn.existsSync(`${d}${f}`));if(h)return`${d}${h}`}this._checkForMissingMandatoryOptions(),this._checkForConflictingOptions();let r=t._executableFile||`${this._name}-${t._name}`,s=this._executableDir||"";if(this._scriptPath){let c;try{c=nn.realpathSync(this._scriptPath)}catch{c=this._scriptPath}s=be.resolve(be.dirname(c),s)}if(s){let c=i(s,r);if(!c&&!t._executableFile&&this._scriptPath){let m=be.basename(this._scriptPath,be.extname(this._scriptPath));m!==this._name&&(c=i(s,`${m}-${t._name}`))}r=c||r}let a=o.includes(be.extname(r)),l;T.platform!=="win32"?a?(n.unshift(r),n=Ai(T.execArgv).concat(n),l=ro.spawn(T.argv[0],n,{stdio:"inherit"})):l=ro.spawn(r,n,{stdio:"inherit"}):(this._checkForMissingExecutable(r,s,t._name),n.unshift(r),n=Ai(T.execArgv).concat(n),l=ro.spawn(T.execPath,n,{stdio:"inherit"})),l.killed||["SIGUSR1","SIGUSR2","SIGTERM","SIGINT","SIGHUP"].forEach(m=>{T.on(m,()=>{l.killed===!1&&l.exitCode===null&&l.kill(m)})});let u=this._exitCallback;l.on("close",c=>{c=c??1,u?u(new B(c,"commander.executeSubCommandAsync","(close)")):T.exit(c)}),l.on("error",c=>{if(c.code==="ENOENT")this._checkForMissingExecutable(r,s,t._name);else if(c.code==="EACCES")throw new Error(`'${r}' not executable`);if(!u)T.exit(1);else{let m=new B(1,"commander.executeSubCommandAsync","(error)");m.nestedError=c,u(m)}}),this.runningCommand=l}_dispatchSubcommand(t,n,o){let i=this._findCommand(t);i||this.help({error:!0}),i._prepareForParse();let r;return r=this._chainOrCallSubCommandHook(r,i,"preSubcommand"),r=this._chainOrCall(r,()=>{if(i._executableHandler)this._executeSubCommand(i,n.concat(o));else return i._parseCommand(n,o)}),r}_dispatchHelpCommand(t){t||this.help();let n=this._findCommand(t);return n&&!n._executableHandler&&n.help(),this._dispatchSubcommand(t,[],[this._getHelpOption()?.long??this._getHelpOption()?.short??"--help"])}_checkNumberOfArguments(){this.registeredArguments.forEach((t,n)=>{t.required&&this.args[n]==null&&this.missingArgument(t.name())}),!(this.registeredArguments.length>0&&this.registeredArguments[this.registeredArguments.length-1].variadic)&&this.args.length>this.registeredArguments.length&&this._excessArguments(this.args)}_processArguments(){let t=(o,i,r)=>{let s=i;if(i!==null&&o.parseArg){let a=`error: command-argument value '${i}' is invalid for argument '${o.name()}'.`;s=this._callParseArg(o,i,r,a)}return s};this._checkNumberOfArguments();let n=[];this.registeredArguments.forEach((o,i)=>{let r=o.defaultValue;o.variadic?i<this.args.length?(r=this.args.slice(i),o.parseArg&&(r=r.reduce((s,a)=>t(o,a,s),o.defaultValue))):r===void 0&&(r=[]):i<this.args.length&&(r=this.args[i],o.parseArg&&(r=t(o,r,o.defaultValue))),n[i]=r}),this.processedArgs=n}_chainOrCall(t,n){return t?.then&&typeof t.then=="function"?t.then(()=>n()):n()}_chainOrCallHooks(t,n){let o=t,i=[];return this._getCommandAndAncestors().reverse().filter(r=>r._lifeCycleHooks[n]!==void 0).forEach(r=>{r._lifeCycleHooks[n].forEach(s=>{i.push({hookedCommand:r,callback:s})})}),n==="postAction"&&i.reverse(),i.forEach(r=>{o=this._chainOrCall(o,()=>r.callback(r.hookedCommand,this))}),o}_chainOrCallSubCommandHook(t,n,o){let i=t;return this._lifeCycleHooks[o]!==void 0&&this._lifeCycleHooks[o].forEach(r=>{i=this._chainOrCall(i,()=>r(this,n))}),i}_parseCommand(t,n){let o=this.parseOptions(n);if(this._parseOptionsEnv(),this._parseOptionsImplied(),t=t.concat(o.operands),n=o.unknown,this.args=t.concat(n),t&&this._findCommand(t[0]))return this._dispatchSubcommand(t[0],t.slice(1),n);if(this._getHelpCommand()&&t[0]===this._getHelpCommand().name())return this._dispatchHelpCommand(t[1]);if(this._defaultCommandName)return this._outputHelpIfRequested(n),this._dispatchSubcommand(this._defaultCommandName,t,n);this.commands.length&&this.args.length===0&&!this._actionHandler&&!this._defaultCommandName&&this.help({error:!0}),this._outputHelpIfRequested(o.unknown),this._checkForMissingMandatoryOptions(),this._checkForConflictingOptions();let i=()=>{o.unknown.length>0&&this.unknownOption(o.unknown[0])},r=`command:${this.name()}`;if(this._actionHandler){i(),this._processArguments();let s;return s=this._chainOrCallHooks(s,"preAction"),s=this._chainOrCall(s,()=>this._actionHandler(this.processedArgs)),this.parent&&(s=this._chainOrCall(s,()=>{this.parent.emit(r,t,n)})),s=this._chainOrCallHooks(s,"postAction"),s}if(this.parent?.listenerCount(r))i(),this._processArguments(),this.parent.emit(r,t,n);else if(t.length){if(this._findCommand("*"))return this._dispatchSubcommand("*",t,n);this.listenerCount("command:*")?this.emit("command:*",t,n):this.commands.length?this.unknownCommand():(i(),this._processArguments())}else this.commands.length?(i(),this.help({error:!0})):(i(),this._processArguments())}_findCommand(t){if(t)return this.commands.find(n=>n._name===t||n._aliases.includes(t))}_findOption(t){return this.options.find(n=>n.is(t))}_checkForMissingMandatoryOptions(){this._getCommandAndAncestors().forEach(t=>{t.options.forEach(n=>{n.mandatory&&t.getOptionValue(n.attributeName())===void 0&&t.missingMandatoryOptionValue(n)})})}_checkForConflictingLocalOptions(){let t=this.options.filter(o=>{let i=o.attributeName();return this.getOptionValue(i)===void 0?!1:this.getOptionValueSource(i)!=="default"});t.filter(o=>o.conflictsWith.length>0).forEach(o=>{let i=t.find(r=>o.conflictsWith.includes(r.attributeName()));i&&this._conflictingOption(o,i)})}_checkForConflictingOptions(){this._getCommandAndAncestors().forEach(t=>{t._checkForConflictingLocalOptions()})}parseOptions(t){let n=[],o=[],i=n;function r(c){return c.length>1&&c[0]==="-"}let s=c=>/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(c)?!this._getCommandAndAncestors().some(m=>m.options.map(d=>d.short).some(d=>/^-\d$/.test(d))):!1,a=null,l=null,u=0;for(;u<t.length||l;){let c=l??t[u++];if(l=null,c==="--"){i===o&&i.push(c),i.push(...t.slice(u));break}if(a&&(!r(c)||s(c))){this.emit(`option:${a.name()}`,c);continue}if(a=null,r(c)){let m=this._findOption(c);if(m){if(m.required){let d=t[u++];d===void 0&&this.optionMissingArgument(m),this.emit(`option:${m.name()}`,d)}else if(m.optional){let d=null;u<t.length&&(!r(t[u])||s(t[u]))&&(d=t[u++]),this.emit(`option:${m.name()}`,d)}else this.emit(`option:${m.name()}`);a=m.variadic?m:null;continue}}if(c.length>2&&c[0]==="-"&&c[1]!=="-"){let m=this._findOption(`-${c[1]}`);if(m){m.required||m.optional&&this._combineFlagAndOptionalValue?this.emit(`option:${m.name()}`,c.slice(2)):(this.emit(`option:${m.name()}`),l=`-${c.slice(2)}`);continue}}if(/^--[^=]+=/.test(c)){let m=c.indexOf("="),d=this._findOption(c.slice(0,m));if(d&&(d.required||d.optional)){this.emit(`option:${d.name()}`,c.slice(m+1));continue}}if(i===n&&r(c)&&!(this.commands.length===0&&s(c))&&(i=o),(this._enablePositionalOptions||this._passThroughOptions)&&n.length===0&&o.length===0){if(this._findCommand(c)){n.push(c),o.push(...t.slice(u));break}else if(this._getHelpCommand()&&c===this._getHelpCommand().name()){n.push(c,...t.slice(u));break}else if(this._defaultCommandName){o.push(c,...t.slice(u));break}}if(this._passThroughOptions){i.push(c,...t.slice(u));break}i.push(c)}return{operands:n,unknown:o}}opts(){if(this._storeOptionsAsProperties){let t={},n=this.options.length;for(let o=0;o<n;o++){let i=this.options[o].attributeName();t[i]=i===this._versionOptionName?this._version:this[i]}return t}return this._optionValues}optsWithGlobals(){return this._getCommandAndAncestors().reduce((t,n)=>Object.assign(t,n.opts()),{})}error(t,n){this._outputConfiguration.outputError(`${t}
22
22
  `,this._outputConfiguration.writeErr),typeof this._showHelpAfterError=="string"?this._outputConfiguration.writeErr(`${this._showHelpAfterError}
23
23
  `):this._showHelpAfterError&&(this._outputConfiguration.writeErr(`
24
- `),this.outputHelp({error:!0}));let o=n||{},i=o.exitCode||1,r=o.code||"commander.error";this._exit(i,r,t)}_parseOptionsEnv(){this.options.forEach(t=>{if(t.envVar&&t.envVar in T.env){let n=t.attributeName();(this.getOptionValue(n)===void 0||["default","config","env"].includes(this.getOptionValueSource(n)))&&(t.required||t.optional?this.emit(`optionEnv:${t.name()}`,T.env[t.envVar]):this.emit(`optionEnv:${t.name()}`))}})}_parseOptionsImplied(){let t=new tn(this.options),n=o=>this.getOptionValue(o)!==void 0&&!["default","implied"].includes(this.getOptionValueSource(o));this.options.filter(o=>o.implied!==void 0&&n(o.attributeName())&&t.valueFromOption(this.getOptionValue(o.attributeName()),o)).forEach(o=>{Object.keys(o.implied).filter(i=>!n(i)).forEach(i=>{this.setOptionValueWithSource(i,o.implied[i],"implied")})})}missingArgument(t){let n=`error: missing required argument '${t}'`;this.error(n,{code:"commander.missingArgument"})}optionMissingArgument(t){let n=`error: option '${t.flags}' argument missing`;this.error(n,{code:"commander.optionMissingArgument"})}missingMandatoryOptionValue(t){let n=`error: required option '${t.flags}' not specified`;this.error(n,{code:"commander.missingMandatoryOptionValue"})}_conflictingOption(t,n){let o=s=>{let a=s.attributeName(),l=this.getOptionValue(a),u=this.options.find(d=>d.negate&&a===d.attributeName()),c=this.options.find(d=>!d.negate&&a===d.attributeName());return u&&(u.presetArg===void 0&&l===!1||u.presetArg!==void 0&&l===u.presetArg)?u:c||s},i=s=>{let a=o(s),l=a.attributeName();return this.getOptionValueSource(l)==="env"?`environment variable '${a.envVar}'`:`option '${a.flags}'`},r=`error: ${i(t)} cannot be used with ${i(n)}`;this.error(r,{code:"commander.conflictingOption"})}unknownOption(t){if(this._allowUnknownOption)return;let n="";if(t.startsWith("--")&&this._showSuggestionAfterError){let i=[],r=this;do{let s=r.createHelp().visibleOptions(r).filter(a=>a.long).map(a=>a.long);i=i.concat(s),r=r.parent}while(r&&!r._enablePositionalOptions);n=lo(t,i)}let o=`error: unknown option '${t}'${n}`;this.error(o,{code:"commander.unknownOption"})}_excessArguments(t){if(this._allowExcessArguments)return;let n=this.registeredArguments.length,o=n===1?"":"s",i=t.length,r=this.parent?` for '${this.name()}'`:"",s=t.join(", "),a=`error: too many arguments${r}. Expected ${n} argument${o} but got ${i}: ${s}.`;this.error(a,{code:"commander.excessArguments"})}unknownCommand(){let t=this.args[0],n="";if(this._showSuggestionAfterError){let i=[];this.createHelp().visibleCommands(this).forEach(r=>{i.push(r.name()),r.alias()&&i.push(r.alias())}),n=lo(t,i)}let o=`error: unknown command '${t}'${n}`;this.error(o,{code:"commander.unknownCommand"})}version(t,n,o){if(t===void 0)return this._version;this._version=t,n=n||"-V, --version",o=o||"output the version number";let i=this.createOption(n,o);return this._versionOptionName=i.attributeName(),this._registerOption(i),this.on("option:"+i.name(),()=>{this._outputConfiguration.writeOut(`${t}
25
- `),this._exit(0,"commander.version",t)}),this}description(t,n){return t===void 0&&n===void 0?this._description:(this._description=t,n&&(this._argsDescription=n),this)}summary(t){return t===void 0?this._summary:(this._summary=t,this)}alias(t){if(t===void 0)return this._aliases[0];let n=this;if(this.commands.length!==0&&this.commands[this.commands.length-1]._executableHandler&&(n=this.commands[this.commands.length-1]),t===n._name)throw new Error("Command alias can't be the same as its name");let o=this.parent?._findCommand(t);if(o){let i=[o.name()].concat(o.aliases()).join("|");throw new Error(`cannot add alias '${t}' to command '${this.name()}' as already have command '${i}'`)}return n._aliases.push(t),this}aliases(t){return t===void 0?this._aliases:(t.forEach(n=>this.alias(n)),this)}usage(t){if(t===void 0){if(this._usage)return this._usage;let n=this.registeredArguments.map(o=>en(o));return[].concat(this.options.length||this._helpOption!==null?"[options]":[],this.commands.length?"[command]":[],this.registeredArguments.length?n:[]).join(" ")}return this._usage=t,this}name(t){return t===void 0?this._name:(this._name=t,this)}helpGroup(t){return t===void 0?this._helpGroupHeading??"":(this._helpGroupHeading=t,this)}commandsGroup(t){return t===void 0?this._defaultCommandGroup??"":(this._defaultCommandGroup=t,this)}optionsGroup(t){return t===void 0?this._defaultOptionGroup??"":(this._defaultOptionGroup=t,this)}_initOptionGroup(t){this._defaultOptionGroup&&!t.helpGroupHeading&&t.helpGroup(this._defaultOptionGroup)}_initCommandGroup(t){this._defaultCommandGroup&&!t.helpGroup()&&t.helpGroup(this._defaultCommandGroup)}nameFromFilename(t){return this._name=we.basename(t,we.extname(t)),this}executableDir(t){return t===void 0?this._executableDir:(this._executableDir=t,this)}helpInformation(t){let n=this.createHelp(),o=this._getOutputContext(t);n.prepareContext({error:o.error,helpWidth:o.helpWidth,outputHasColors:o.hasColors});let i=n.formatHelp(this,n);return o.hasColors?i:this._outputConfiguration.stripColor(i)}_getOutputContext(t){t=t||{};let n=!!t.error,o,i,r;return n?(o=a=>this._outputConfiguration.writeErr(a),i=this._outputConfiguration.getErrHasColors(),r=this._outputConfiguration.getErrHelpWidth()):(o=a=>this._outputConfiguration.writeOut(a),i=this._outputConfiguration.getOutHasColors(),r=this._outputConfiguration.getOutHelpWidth()),{error:n,write:a=>(i||(a=this._outputConfiguration.stripColor(a)),o(a)),hasColors:i,helpWidth:r}}outputHelp(t){let n;typeof t=="function"&&(n=t,t=void 0);let o=this._getOutputContext(t),i={error:o.error,write:o.write,command:this};this._getCommandAndAncestors().reverse().forEach(s=>s.emit("beforeAllHelp",i)),this.emit("beforeHelp",i);let r=this.helpInformation({error:o.error});if(n&&(r=n(r),typeof r!="string"&&!Buffer.isBuffer(r)))throw new Error("outputHelp callback must return a string or a Buffer");o.write(r),this._getHelpOption()?.long&&this.emit(this._getHelpOption().long),this.emit("afterHelp",i),this._getCommandAndAncestors().forEach(s=>s.emit("afterAllHelp",i))}helpOption(t,n){return typeof t=="boolean"?(t?(this._helpOption===null&&(this._helpOption=void 0),this._defaultOptionGroup&&this._initOptionGroup(this._getHelpOption())):this._helpOption=null,this):(this._helpOption=this.createOption(t??"-h, --help",n??"display help for command"),(t||n)&&this._initOptionGroup(this._helpOption),this)}_getHelpOption(){return this._helpOption===void 0&&this.helpOption(void 0,void 0),this._helpOption}addHelpOption(t){return this._helpOption=t,this._initOptionGroup(t),this}help(t){this.outputHelp(t);let n=Number(T.exitCode??0);n===0&&t&&typeof t!="function"&&t.error&&(n=1),this._exit(n,"commander.help","(outputHelp)")}addHelpText(t,n){let o=["beforeAll","before","after","afterAll"];if(!o.includes(t))throw new Error(`Unexpected value for position to addHelpText.
24
+ `),this.outputHelp({error:!0}));let o=n||{},i=o.exitCode||1,r=o.code||"commander.error";this._exit(i,r,t)}_parseOptionsEnv(){this.options.forEach(t=>{if(t.envVar&&t.envVar in T.env){let n=t.attributeName();(this.getOptionValue(n)===void 0||["default","config","env"].includes(this.getOptionValueSource(n)))&&(t.required||t.optional?this.emit(`optionEnv:${t.name()}`,T.env[t.envVar]):this.emit(`optionEnv:${t.name()}`))}})}_parseOptionsImplied(){let t=new tn(this.options),n=o=>this.getOptionValue(o)!==void 0&&!["default","implied"].includes(this.getOptionValueSource(o));this.options.filter(o=>o.implied!==void 0&&n(o.attributeName())&&t.valueFromOption(this.getOptionValue(o.attributeName()),o)).forEach(o=>{Object.keys(o.implied).filter(i=>!n(i)).forEach(i=>{this.setOptionValueWithSource(i,o.implied[i],"implied")})})}missingArgument(t){let n=`error: missing required argument '${t}'`;this.error(n,{code:"commander.missingArgument"})}optionMissingArgument(t){let n=`error: option '${t.flags}' argument missing`;this.error(n,{code:"commander.optionMissingArgument"})}missingMandatoryOptionValue(t){let n=`error: required option '${t.flags}' not specified`;this.error(n,{code:"commander.missingMandatoryOptionValue"})}_conflictingOption(t,n){let o=s=>{let a=s.attributeName(),l=this.getOptionValue(a),u=this.options.find(m=>m.negate&&a===m.attributeName()),c=this.options.find(m=>!m.negate&&a===m.attributeName());return u&&(u.presetArg===void 0&&l===!1||u.presetArg!==void 0&&l===u.presetArg)?u:c||s},i=s=>{let a=o(s),l=a.attributeName();return this.getOptionValueSource(l)==="env"?`environment variable '${a.envVar}'`:`option '${a.flags}'`},r=`error: ${i(t)} cannot be used with ${i(n)}`;this.error(r,{code:"commander.conflictingOption"})}unknownOption(t){if(this._allowUnknownOption)return;let n="";if(t.startsWith("--")&&this._showSuggestionAfterError){let i=[],r=this;do{let s=r.createHelp().visibleOptions(r).filter(a=>a.long).map(a=>a.long);i=i.concat(s),r=r.parent}while(r&&!r._enablePositionalOptions);n=io(t,i)}let o=`error: unknown option '${t}'${n}`;this.error(o,{code:"commander.unknownOption"})}_excessArguments(t){if(this._allowExcessArguments)return;let n=this.registeredArguments.length,o=n===1?"":"s",i=t.length,r=this.parent?` for '${this.name()}'`:"",s=t.join(", "),a=`error: too many arguments${r}. Expected ${n} argument${o} but got ${i}: ${s}.`;this.error(a,{code:"commander.excessArguments"})}unknownCommand(){let t=this.args[0],n="";if(this._showSuggestionAfterError){let i=[];this.createHelp().visibleCommands(this).forEach(r=>{i.push(r.name()),r.alias()&&i.push(r.alias())}),n=io(t,i)}let o=`error: unknown command '${t}'${n}`;this.error(o,{code:"commander.unknownCommand"})}version(t,n,o){if(t===void 0)return this._version;this._version=t,n=n||"-V, --version",o=o||"output the version number";let i=this.createOption(n,o);return this._versionOptionName=i.attributeName(),this._registerOption(i),this.on("option:"+i.name(),()=>{this._outputConfiguration.writeOut(`${t}
25
+ `),this._exit(0,"commander.version",t)}),this}description(t,n){return t===void 0&&n===void 0?this._description:(this._description=t,n&&(this._argsDescription=n),this)}summary(t){return t===void 0?this._summary:(this._summary=t,this)}alias(t){if(t===void 0)return this._aliases[0];let n=this;if(this.commands.length!==0&&this.commands[this.commands.length-1]._executableHandler&&(n=this.commands[this.commands.length-1]),t===n._name)throw new Error("Command alias can't be the same as its name");let o=this.parent?._findCommand(t);if(o){let i=[o.name()].concat(o.aliases()).join("|");throw new Error(`cannot add alias '${t}' to command '${this.name()}' as already have command '${i}'`)}return n._aliases.push(t),this}aliases(t){return t===void 0?this._aliases:(t.forEach(n=>this.alias(n)),this)}usage(t){if(t===void 0){if(this._usage)return this._usage;let n=this.registeredArguments.map(o=>en(o));return[].concat(this.options.length||this._helpOption!==null?"[options]":[],this.commands.length?"[command]":[],this.registeredArguments.length?n:[]).join(" ")}return this._usage=t,this}name(t){return t===void 0?this._name:(this._name=t,this)}helpGroup(t){return t===void 0?this._helpGroupHeading??"":(this._helpGroupHeading=t,this)}commandsGroup(t){return t===void 0?this._defaultCommandGroup??"":(this._defaultCommandGroup=t,this)}optionsGroup(t){return t===void 0?this._defaultOptionGroup??"":(this._defaultOptionGroup=t,this)}_initOptionGroup(t){this._defaultOptionGroup&&!t.helpGroupHeading&&t.helpGroup(this._defaultOptionGroup)}_initCommandGroup(t){this._defaultCommandGroup&&!t.helpGroup()&&t.helpGroup(this._defaultCommandGroup)}nameFromFilename(t){return this._name=be.basename(t,be.extname(t)),this}executableDir(t){return t===void 0?this._executableDir:(this._executableDir=t,this)}helpInformation(t){let n=this.createHelp(),o=this._getOutputContext(t);n.prepareContext({error:o.error,helpWidth:o.helpWidth,outputHasColors:o.hasColors});let i=n.formatHelp(this,n);return o.hasColors?i:this._outputConfiguration.stripColor(i)}_getOutputContext(t){t=t||{};let n=!!t.error,o,i,r;return n?(o=a=>this._outputConfiguration.writeErr(a),i=this._outputConfiguration.getErrHasColors(),r=this._outputConfiguration.getErrHelpWidth()):(o=a=>this._outputConfiguration.writeOut(a),i=this._outputConfiguration.getOutHasColors(),r=this._outputConfiguration.getOutHelpWidth()),{error:n,write:a=>(i||(a=this._outputConfiguration.stripColor(a)),o(a)),hasColors:i,helpWidth:r}}outputHelp(t){let n;typeof t=="function"&&(n=t,t=void 0);let o=this._getOutputContext(t),i={error:o.error,write:o.write,command:this};this._getCommandAndAncestors().reverse().forEach(s=>s.emit("beforeAllHelp",i)),this.emit("beforeHelp",i);let r=this.helpInformation({error:o.error});if(n&&(r=n(r),typeof r!="string"&&!Buffer.isBuffer(r)))throw new Error("outputHelp callback must return a string or a Buffer");o.write(r),this._getHelpOption()?.long&&this.emit(this._getHelpOption().long),this.emit("afterHelp",i),this._getCommandAndAncestors().forEach(s=>s.emit("afterAllHelp",i))}helpOption(t,n){return typeof t=="boolean"?(t?(this._helpOption===null&&(this._helpOption=void 0),this._defaultOptionGroup&&this._initOptionGroup(this._getHelpOption())):this._helpOption=null,this):(this._helpOption=this.createOption(t??"-h, --help",n??"display help for command"),(t||n)&&this._initOptionGroup(this._helpOption),this)}_getHelpOption(){return this._helpOption===void 0&&this.helpOption(void 0,void 0),this._helpOption}addHelpOption(t){return this._helpOption=t,this._initOptionGroup(t),this}help(t){this.outputHelp(t);let n=Number(T.exitCode??0);n===0&&t&&typeof t!="function"&&t.error&&(n=1),this._exit(n,"commander.help","(outputHelp)")}addHelpText(t,n){let o=["beforeAll","before","after","afterAll"];if(!o.includes(t))throw new Error(`Unexpected value for position to addHelpText.
26
26
  Expecting one of '${o.join("', '")}'`);let i=`${t}Help`;return this.on(i,r=>{let s;typeof n=="function"?s=n({error:r.error,command:r.command}):s=n,s&&r.write(`${s}
27
- `)}),this}_outputHelpIfRequested(t){let n=this._getHelpOption();n&&t.find(i=>n.is(i))&&(this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)"))}};function Ri(e){return e.map(t=>{if(!t.startsWith("--inspect"))return t;let n,o="127.0.0.1",i="9229",r;return(r=t.match(/^(--inspect(-brk)?)$/))!==null?n=r[1]:(r=t.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null?(n=r[1],/^\d+$/.test(r[3])?i=r[3]:o=r[3]):(r=t.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null&&(n=r[1],o=r[3],i=r[4]),n&&i!=="0"?`${n}=${o}:${parseInt(i)+1}`:t})}function Ei(){if(T.env.NO_COLOR||T.env.FORCE_COLOR==="0"||T.env.FORCE_COLOR==="false")return!1;if(T.env.FORCE_COLOR||T.env.CLICOLOR_FORCE!==void 0)return!0}var vg=new it;import{createHash as Wl}from"node:crypto";import{readdirSync as Gi}from"node:fs";import{join as Ml}from"node:path";import{createHash as Ol,randomUUID as Ii}from"node:crypto";import{chmodSync as Tl,closeSync as co,cpSync as Rl,existsSync as Fe,fstatSync as El,fsyncSync as Il,lstatSync as mo,mkdirSync as fo,mkdtempSync as Pl,openSync as Pi,readFileSync as xi,readdirSync as xl,renameSync as Ni,rmSync as rn,writeFileSync as Li}from"node:fs";import{basename as yo,dirname as rt,join as _e,win32 as po}from"node:path";var g={Success:0,Failure:1,Usage:2},Al=/^[a-z][a-z0-9_]*$/,p=class extends Error{code;exitCode;hint;httpStatus;constructor(t,n,o,i,r){if(!Al.test(t))throw new Error(`CliError code must be snake_case: '${t}'`);if(o===g.Success)throw new Error(`CliError '${t}' cannot use the success exit code`);super(n),this.name="CliError",this.code=t,this.exitCode=o,this.hint=i,this.httpStatus=r}};var Nl="https://api.closedloop.sh",Q={us:"https://api.closedloop.sh",eu:"https://eu.api.closedloop.sh"};function Ue(e){return e===Q.us?"us":e===Q.eu?"eu":null}function sn(e){let t=e.env.CLOSEDLOOP_API_URL?.trim(),n=new URL(t||Nl),o=["localhost","127.0.0.1","[::1]"].includes(n.hostname);if(n.protocol!=="https:"&&!(n.protocol==="http:"&&o))throw new Error("CLOSEDLOOP_API_URL must use https (http only for localhost)");if(n.username||n.password)throw new Error("CLOSEDLOOP_API_URL must not contain a user name or password");return n.origin}function Z(e){let t=e.env.CLOSEDLOOP_CONFIG_DIR?.trim();if(t)return t;let n=e.env.XDG_CONFIG_HOME?.trim();if(n)return _e(n,"closedloop");let o=e.platform??process.platform;return o==="darwin"?_e(e.homeDir,"Library","Application Support","closedloop"):o==="win32"?po.join(e.env.LOCALAPPDATA?.trim()||po.join(e.homeDir,"AppData","Local"),"closedloop"):_e(e.homeDir,".config","closedloop")}function on(e,t,n){(e.platform??process.platform)!=="win32"&&Tl(t,n)}function go(e,t){let n=Z(e);fo(t,{recursive:!0,mode:448}),on(e,n,448),t!==n&&on(e,t,448)}function Di(e){return e.env.CLOSEDLOOP_CONFIG_DIR?.trim()||e.env.XDG_CONFIG_HOME?.trim()?null:(e.platform??process.platform)==="win32"?po.join(e.homeDir,".config","closedloop"):_e(e.homeDir,".config","closedloop")}function Ll(e){let t=Z(e),n=Di(e);if(n===null||t===n||Fe(t)||!Fe(n))return;let o;try{fo(rt(t),{recursive:!0}),o=Pl(_e(rt(t),`.${yo(t)}-migration-`)),Rl(n,o,{recursive:!0,filter:i=>{let r=mo(i);if(!r.isDirectory()&&!r.isFile())throw new Error("legacy config contains a non-regular entry");return!0}}),on(e,o,448);for(let i of xl(o,{recursive:!0})){let r=_e(o,String(i));on(e,r,mo(r).isDirectory()?448:384)}Fe(t)||Ni(o,t)}catch{throw new p("config_migration_failed","your saved ClosedLoop AI settings could not be moved to the new location",g.Failure,"Set CLOSEDLOOP_CONFIG_DIR to your existing config folder and repeat the command. Your original files were kept.")}finally{o&&rn(o,{recursive:!0,force:!0})}}function ji(e){let t,n=new Int32Array(new SharedArrayBuffer(4));for(let s=0;s<5;s++)try{t=Pi(e,"wx",384);break}catch(a){if(a.code!=="EEXIST")throw a;s<4&&Atomics.wait(n,0,0,25)}if(t===void 0)throw new p("config_busy","another CLI process is updating the saved state",g.Failure,`Try again after other CLI commands finish. If no CLI process is running, inspect and remove ${e}.`);let o=t,i,r=()=>{try{if(i&&Fe(e)){let s=mo(e);s.dev===i.dev&&s.ino===i.ino&&rn(e)}}finally{co(o)}};try{i=El(o),Li(o,JSON.stringify({pid:process.pid,owner:Ii()}))}catch(s){throw r(),s}return r}function Dl(e,t){let n=ji(e);try{return t()}finally{n()}}function an(e,t){let n=Z(e);try{return fo(rt(n),{recursive:!0}),Dl(_e(rt(n),`.${yo(n)}.lock`),()=>(Ll(e),t()))}catch(o){throw o instanceof p?o:new p("config_write_failed","the saved config folder could not be updated",g.Failure,`Check permissions and free space for ${n}, then repeat the command.`)}}function Rt(e,t){return _e(Z(e),...t.split("/"))}function Fi(e,t){let n=Rt(e,t);try{return JSON.parse(xi(n,"utf8"))}catch(o){if(o.code==="ENOENT")return null;throw o instanceof SyntaxError?new p("config_invalid",`the saved ${t} file is not valid JSON`,g.Failure,`Back up and remove ${n}, then repeat the command${t==="credentials.json"?" or run `closedloop login` to sign in again":""}.`):new p("config_unreadable",`the saved ${t} file could not be read`,g.Failure,`Check the permissions for ${n} and CLOSEDLOOP_CONFIG_DIR, then repeat the command.`)}}function wo(e){let t=Di(e);t!==null&&t!==Z(e)&&!Fe(Z(e))&&Fe(t)&&an(e,()=>{})}function Re(e,t){return wo(e),Fi(e,t)}function _o(e,t){let n=Rt(e,t);try{return Ol("sha256").update(xi(n)).digest("hex")}catch(o){if(o.code==="ENOENT")return null;throw new p("config_unreadable",`the saved ${t} file could not be read`,g.Failure,`Check the permissions for ${n} and CLOSEDLOOP_CONFIG_DIR, then repeat the command.`)}}function Ui(e,t,n){let o=Rt(e,t),i=rt(o),r=_e(i,`.${yo(o)}.${Ii()}.tmp`),s;try{let a=`${JSON.stringify(n,null,2)}
28
- `;go(e,i),s=Pi(r,"wx",384),Li(s,a),Il(s),co(s),s=void 0,Ni(r,o)}catch{throw new p("config_write_failed",`the saved ${t} file could not be updated`,g.Failure,`Check the permissions and free space for ${i}, then repeat the command. The previous file was kept.`)}finally{s!==void 0&&co(s),rn(r,{force:!0})}}function jl(e,t){try{rn(Rt(e,t),{force:!0})}catch{throw new p("config_write_failed",`the saved ${t} file could not be removed`,g.Failure,"Check the config folder permissions or CLOSEDLOOP_CONFIG_DIR, then repeat the command.")}}var Fl=["credentials.json","pending-login.json","pending-browser-login.json"];function ho(e,t,n){go(e,Z(e));let o=[];try{for(let i of t){let r=Rt(e,i);go(e,rt(r)),o.push(ji(r))}return n({read:i=>Fi(e,i),write:(i,r)=>Ui(e,i,r),remove:i=>jl(e,i)})}finally{for(let i of o.reverse())i()}}function Ul(e,t){return an(e,()=>ho(e,[".credentials.json.lock"],t))}function bo(e,t,n,o=!1){if(o||!Fe(Z(e)))return an(e,()=>ho(e,t,n));try{return ho(e,t,n)}catch(i){throw i instanceof p?i:new p("config_write_failed","the saved config folder could not be updated",g.Failure,`Check permissions and free space for ${Z(e)}, then repeat the command.`)}}function Wi(e,t,n){Fl.includes(t)?Ul(e,o=>o.write(t,n)):an(e,()=>Ui(e,t,n))}var at="general.json",N="credentials.json",$e="pending-login.json",ie="pending-browser-login.json",Hl="working-goal.json",Et="accounts",qi=".general.json.lock",Gl=".credentials.json.lock",ql=/^[^\s\p{C}@<>,;:"()\\[\]]+@(?:[^\s\p{C}@<>,;:"()\\[\].]+\.)+[^\s\p{C}@<>,;:"()\\[\].]+$/u;function ut(e){let t=e?.trim().toLowerCase();return t&&t.length<=320&&ql.test(t)?t:null}function be(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return t.length>0&&t.length<=320&&t.includes("@")&&!/[\s\p{C}]/u.test(t)?t:null}function lt(e){return Wl("sha256").update(e.trim().toLowerCase()).digest("hex").slice(0,16)}function ge(e){return`${Et}/${lt(e)}.json`}function Ji(e){return`${Et}/.${lt(e)}.lock`}function st(e){return e.replace(/\.json$/,"").replace(/-/g,"_")}function Jl(e){return[N,$e,ie,Hl].includes(e)||/^last-[a-z0-9-]+\.json$/.test(e)}function We(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Vi(e){return e==="us"||e==="eu"?e:null}function ln(e){return We(e)?e:{}}function Co(e,t){return We(e)&&typeof e.email=="string"&&e.email===t?e:null}function Bi(e){let t;try{t=Gi(Ml(Z(e),Et))}catch(o){if(o.code==="ENOENT")return[];throw new p("config_unreadable","the saved accounts could not be read",g.Failure,"Check the permissions for the config folder and CLOSEDLOOP_CONFIG_DIR, then repeat the command.")}let n=[];for(let o of t.filter(i=>/^[0-9a-f]{16}\.json$/.test(i)).sort()){let i;try{i=Re(e,`${Et}/${o}`)}catch(s){if(s instanceof p&&s.code==="config_invalid")continue;throw s}let r=We(i)?be(i.email):null;r!==null&&r===i?.email&&ge(r)===`${Et}/${o}`&&n.push(r)}return n.sort()}function Mi(e,t){let n=_o(e,t);if(n===null)return null;try{return{value:Re(e,t),fingerprint:n}}catch(o){if(o instanceof p&&o.code==="config_invalid")return null;throw o}}function Hi(e,t,n){let o=We(n.migrated)?n.migrated:{},i={moves:[],signedIn:null,region:null},r=t.includes(N)?Mi(e,N):null;if(r!==null){let a=We(r.value)?be(r.value.email):null;o[N]===r.fingerprint?i.moves.push({file:N,fingerprint:r.fingerprint,to:null}):a!==null&&(i.signedIn=a,i.region=Vi(r.value.region),i.moves.push({file:N,fingerprint:r.fingerprint,to:a,value:r.value}))}let s=i.signedIn??be(n.active_account);for(let a of t.filter(l=>l!==N)){let l=Mi(e,a);if(l===null)continue;if(o[a]===l.fingerprint){i.moves.push({file:a,fingerprint:l.fingerprint,to:null});continue}let u=null;a===$e?u=We(l.value)?be(l.value.email):null:a===ie?u=s??"settings":u=s,u!==null&&i.moves.push({file:a,fingerprint:l.fingerprint,to:u,value:l.value})}return i}function vo(e){try{return Gi(Z(e)).filter(Jl).sort()}catch(t){if(t.code==="ENOENT"||t.code==="ENOTDIR")return[];throw new p("config_unreadable","the saved config folder could not be read",g.Failure,"Check the permissions for the config folder and CLOSEDLOOP_CONFIG_DIR, then repeat the command.")}}function $o(e){return[...new Set(e.moves.map(t=>t.to).filter(t=>t!==null&&t!=="settings"))].sort()}function un(e){wo(e);let t=vo(e);if(t.length===0)return;let n=Hi(e,t,ln(Re(e,at)));if(n.moves.length===0)return;let o=$o(n),i=[Gl,qi,...[...o].sort((r,s)=>lt(r).localeCompare(lt(s))).map(Ji)];bo(e,i,r=>{let s=ln(r.read(at)),a=Hi(e,vo(e),s);if(a.moves.length===0)return;if($o(a).some(c=>!o.includes(c)))throw new p("config_busy","another CLI process is updating the saved state",g.Failure,"Try again after other CLI commands finish.");for(let c of $o(a)){let d=Co(r.read(ge(c)),c)??{email:c};for(let m of a.moves.filter(h=>h.to===c))d[st(m.file)]=m.value;r.write(ge(c),d)}let l={...We(s.migrated)?s.migrated:{}};for(let c of a.moves)l[c.file]=c.fingerprint;let u=a.moves.find(c=>c.to==="settings");r.write(at,{...s,...u&&{pending_browser_login:u.value},...a.signedIn!==null&&{active_account:a.signedIn,last_email:a.signedIn},...a.region!==null&&{last_region:a.region},migrated:l});for(let c of a.moves)r.remove(c.file)},!0)}function Vl(e,t){for(let n of vo(e))t.remove(n)}function Me(e){return un(e),ln(Re(e,at))}function cn(e,t,n){un(e);let i=Co(Re(e,ge(t)),t)?.[st(n)];return i===void 0?null:i}function It(e){return un(e),Bi(e)}function Ee(e){if(e.account)return e.account;let t=e.accountOption??(e.env.CLOSEDLOOP_ACCOUNT?.trim()||void 0),n;if(t!==void 0){let o=be(t);if(o===null)throw new p("invalid_account","the account must be the email address of an account signed in on this computer",g.Usage,"Run `closedloop accounts` to see the accounts signed in on this computer.");n={email:o,explicit:!0}}else n={email:be(Me(e).active_account),explicit:!1};return e.account=n,n}function Y(e,t,n){un(e);let o=[...new Set((t.accounts==="all"?Bi(e):t.accounts).map(r=>r.trim().toLowerCase()))].sort((r,s)=>lt(r).localeCompare(lt(s))),i=[...t.settings?[qi]:[],...o.map(Ji)];return bo(e,i,r=>{let s=l=>{let u=l.trim().toLowerCase();if(!o.includes(u))throw new Error("an account file was changed without its lock");return u},a=l=>Co(r.read(ge(l)),l);return n({settings:()=>ln(r.read(at)),setSettings:l=>{if(!t.settings)throw new Error("general.json was changed without its lock");r.write(at,l)},read:(l,u)=>{let c=a(s(l))?.[st(u)];return c===void 0?null:c},write:(l,u,c)=>{let d=s(l);r.write(ge(d),{...a(d)??{email:d},[st(u)]:c})},remove:(l,u)=>{let c=s(l),d=a(c);if(d===null||!(st(u)in d))return;let{[st(u)]:m,...h}=d;Object.keys(h).length===1?r.remove(ge(c)):r.write(ge(c),h)},forget:l=>r.remove(ge(s(l))),emails:()=>o.filter(l=>_o(e,ge(l))!==null).sort(),removeLeftovers:()=>Vl(e,r)})})}function dn(e,t,n){return t!==null?Y(e,{accounts:[t]},o=>n({read:i=>o.read(t,i),write:(i,r)=>o.write(t,i,r),remove:i=>o.remove(t,i)})):Y(e,{accounts:[],settings:!0},o=>{let i=r=>{if(r!==ie)throw new Error("only a browser sign-in is kept without an account")};return n({read:r=>{i(r);let s=o.settings().pending_browser_login;return s===void 0?null:s},write:(r,s)=>{i(r),o.setSettings({...o.settings(),pending_browser_login:s})},remove:r=>{i(r);let{pending_browser_login:s,...a}=o.settings();o.setSettings(a)}})})}function ko(e,t,n){if(t!==null)return cn(e,t,n);if(n!==ie)return null;let o=Me(e).pending_browser_login;return o===void 0?null:o}function mn(e,t,n){Y(e,{accounts:[],settings:!0},o=>{let i=o.settings();o.setSettings({...i,last_email:t,...n!==null&&{last_region:n}})})}function pn(e,t){let{email:n}=Ee(e);return n===null?null:cn(e,n,t)}function gn(e,t,n){let{email:o}=Ee(e);o!==null&&Y(e,{accounts:[o]},i=>{i.read(o,N)!==null&&i.write(o,t,n)})}function Yi(e,t){let{email:n}=Ee(e);n!==null&&Y(e,{accounts:[n]},o=>o.remove(n,t))}function hn(e){return Vi(e.region)??Ue(e.api_url)}function ee(e,t=process.platform){return t==="win32"?`'${e.replace(/['‘-‛]/g,"$&$&")}'`:`'${e.replace(/'/g,"'\\''")}'`}function So(e,t=process.platform){return/^[a-z0-9@._+-]+$/i.test(e)?e:ee(e,t)}function Ie(e,t=process.platform){if(e===null)return"closedloop login";let n=So(e,t);return n.startsWith("-")?`closedloop login --email ${n}`:`closedloop login ${n}`}function fn(e,t,n=process.platform){let o=JSON.stringify(e);if(n!=="win32")return t(ee(o,n));let i=o.replace(/[^\x20-\x7e]/g,r=>`\\u${r.charCodeAt(0).toString(16).padStart(4,"0")}`);return`${ee(i,n)} | ${t("-")}`}var re="0.3.4";var yn="npm install -g @closedloop-ai/cli@latest",Oo=/^(\d{1,6})\.(\d{1,6})\.(\d{1,6})$/,Ki="update-notice.json",Bl=1440*60*1e3;function Pt(e){return typeof e=="string"&&Oo.test(e)}function zi(e,t){let n=Oo.exec(e),o=Oo.exec(t);if(!n||!o)throw new Error("compareVersions needs two x.y.z versions");for(let i=1;i<=3;i++)if(Number(n[i])!==Number(o[i]))return Number(n[i])-Number(o[i]);return 0}function Xi(e,t){e.release&&Pt(t)&&(e.release.latest===null||zi(t,e.release.latest)>0)&&(e.release.latest=t)}function To(e){let t=e.release?.latest??null;return t===null||!Pt(re)?null:zi(t,re)>0?t:null}function Ao(e){return e!==void 0&&e!==""&&e!=="0"&&e!=="false"}function Qi(e,t){let n=To(e);if(n===null||!t)return null;let{env:o}=e;if(Ao(o.CI)||Ao(o.NO_UPDATE_NOTIFIER)||Ao(o.CLOSEDLOOP_NO_UPDATE_NOTIFIER))return null;let i=e.now?.()??Date.now(),r=null;try{r=Re(e,Ki)}catch{}if(r&&r.version===n&&typeof r.shown_at=="number"&&i>=r.shown_at&&i-r.shown_at<Bl)return null;try{Wi(e,Ki,{version:n,shown_at:i})}catch{}return`ClosedLoop AI CLI ${n} is out (you have ${re}). Update: ${yn}`}var Yl=7e4,Ro=/[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g,Kl=(e,t)=>{if(typeof t=="string")return t.replace(Ro,"");if(t===null||typeof t!="object"||Array.isArray(t))return t;let n=Object.keys(t);return n.some(o=>o!==o.replace(Ro,""))?Object.fromEntries(n.map(o=>[o.replace(Ro,""),t[o]])):t};async function wn(e,t,n,o,i={}){if(!o.startsWith("/"))throw new Error("an API path starts with /");let r,s,a=AbortSignal.timeout(Yl);try{r=await e.fetch(`${t}${o}`,{method:n,headers:{...i.headers,Accept:"application/json","X-ClosedLoop-Client":"cli","X-ClosedLoop-CLI-Version":re,...i.payload!==void 0&&{"Content-Type":"application/json"},...i.token&&{Authorization:`Bearer ${i.token}`}},...i.payload!==void 0&&{body:JSON.stringify(i.payload)},signal:a}),s=await r.text()}catch(u){throw u instanceof Error&&u.name==="TimeoutError"||a.aborted&&a.reason?.name==="TimeoutError"?new p("api_timeout","ClosedLoop AI took too long to answer",g.Failure,"Try again; if you sent data, check its status before resending it."):new p("api_unreachable",`could not reach ClosedLoop AI at ${t}`,g.Failure,"Check your connection, or CLOSEDLOOP_API_URL if you set it.")}let l={};try{let u=JSON.parse(s,Kl);u!==null&&typeof u=="object"&&!Array.isArray(u)&&(l=u)}catch{}if(r.status===426&&l.code==="upgrade_required")throw Xl(l);if(Xi(e,r.headers.get("X-ClosedLoop-CLI-Latest")),r.status===429)throw zl(e,r.headers.get("Retry-After"),l);return{status:r.status,body:l,text:s}}function zl(e,t,n){let o=typeof n.code=="string"?n.code.toLowerCase():"",i=o==="rate_limit_exceeded"?"rate_limited":o,r=t!==null&&/^\d+(?:\.\d+)?$/.test(t.trim())?Number(t):t!==null?(Date.parse(t)-(e.now?.()??Date.now()))/1e3:NaN,s=Math.max(0,Math.ceil(r)),a=Number.isFinite(s)?`Try again in ${s} second${s===1?"":"s"}.`:"Wait a few minutes, then run it again.";return new p(/^[a-z][a-z0-9_]*$/.test(i)?i:"rate_limited",typeof n.error=="string"&&n.error?n.error:"too many requests to ClosedLoop AI",g.Failure,a,429)}function Xl(e){let t=Pt(e.minimum_version)?e.minimum_version:"a newer version";return new p("upgrade_required",`this ClosedLoop AI CLI (${re}) is too old for the API; ${t} or later is needed`,g.Failure,Pt(e.latest_version)?`Update to ${e.latest_version}: ${yn}`:`Update: ${yn}`)}function He(e,t,n,o){return wn(e,t,"POST",n,{payload:o})}var Ql=["password","email_code","google","microsoft","okta","saml"];function _n(e){return Ql.includes(e)?e:null}var Eo="Run `closedloop login` to sign in.",x='Sign in to the app with your email address. An account made in the CLI has no password: choose "Email me a code" and the app emails you a sign-in code.';function ct(){return new p("session_changed","your saved sign-in changed while this command was running",g.Failure,"Repeat the command. If you signed out, run `closedloop login` to sign in again.")}function dt(e,t,n){if(t===null||typeof t!="object"||Array.isArray(t))return null;let o=t,i=r=>typeof r=="string"&&r.trim()!==""&&r.trim()===r;if(!i(o.api_url)||!i(o.email)||!i(o.access_token)||!i(o.refresh_token)||o.email.toLowerCase()!==n)return null;try{if(sn({...e,env:{CLOSEDLOOP_API_URL:o.api_url}})!==o.api_url)return null}catch{return null}return o}function Po(e,t){let n=t.trim().toLowerCase();return dt(e,cn(e,n,N),n)}function Zl(e){let{email:t}=Ee(e);return t===null?null:Po(e,t)}function xo(e){let t=[];for(let n of It(e)){let o=Po(e,n);o!==null&&t.push({email:n,session:o})}return t}function $(e){let t=Zl(e);if(t!==null)return t;let{email:n,explicit:o}=Ee(e);if(o&&n!==null)throw new p("account_not_signed_in",`${n} is not signed in on this computer`,g.Failure,`Run \`${Ie(n,e.platform)}\` to sign it in, or \`closedloop accounts\` to see the accounts signed in here.`);let i=xo(e).map(r=>r.email);throw new p("not_signed_in","you are not signed in on this computer",g.Failure,i.length===0?Eo:`Run \`closedloop use <email>\` to use one of the accounts signed in here (${i.join(", ")}), or \`closedloop login\` to sign in.`)}function er(e,t,n,o,i){if(!t.access_token||!t.refresh_token)throw new Error("a sign-in needs both tokens");let r=t.email.trim().toLowerCase(),s=new Date(e.now?.()??Date.now()).toISOString(),a=i?.account??null,l=hn(t);Y(e,{accounts:a!==null&&a!==r?[r,a]:[r],settings:!0},u=>{if(i){let m=a===null?u.settings().pending_browser_login??null:u.read(a,i.file);if(JSON.stringify(m)!==JSON.stringify(i.value))throw ct()}u.write(r,N,{...t,signed_in_at:s,sign_in_method:n,signed_in_with:o,saved_at:s}),u.remove(r,$e),u.remove(r,ie),a!==null&&a!==r&&i&&u.remove(a,i.file);let{pending_browser_login:c,...d}=u.settings();u.setSettings({...d,...a!==null&&c!==void 0&&{pending_browser_login:c},active_account:r,last_email:r,...l!==null&&{last_region:l}})}),e.account={email:r,explicit:!1}}function Io(e){let t=e.split(".");if(t.length!==3)return null;try{let n=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8")),{iat:o,exp:i}=n??{};return typeof o!="number"||typeof i!="number"||!Number.isFinite(o)||!Number.isFinite(i)||o<0||i<=o||i*1e3>864e13?null:{issuedAt:o,expiresAt:i}}catch{return null}}function tr(e){let t=Io(e.refresh_token)??Io(e.access_token);return t?new Date(t.expiresAt*1e3).toISOString():null}function eu(e,t){let n=Io(e);return n!==null&&t/1e3>=n.issuedAt+(n.expiresAt-n.issuedAt)/2}async function tu(e,t){let n=t.email.toLowerCase(),o=Po(e,n);if(!o||o.api_url!==t.api_url||o.email!==t.email||o.refresh_token!==t.refresh_token)throw ct();let i=JSON.stringify(o),r=await wn(e,t.api_url,"POST","/api/auth/refresh",{payload:{refreshToken:t.refresh_token}}),{accessToken:s,refreshToken:a}=r.body;if(r.status!==200||typeof s!="string"||!s.trim()||typeof a!="string"||!a.trim())return null;let l={access_token:s,refresh_token:a,saved_at:new Date(e.now?.()??Date.now()).toISOString()};return Y(e,{accounts:[n]},u=>{if(JSON.stringify(u.read(n,N))!==i)throw ct();u.write(n,N,{...t,...l})}),l}async function Zi(e,t){let n=e.pendingSessionRefreshes??=new Map,o=JSON.stringify([t.api_url,t.email,t.refresh_token]),i=n.get(o);i||(i=tu(e,t),n.set(o,i));try{let r=await i;return r?(Object.assign(t,r),!0):!1}finally{n.get(o)===i&&n.delete(o)}}async function v(e,t,n,o,i,r){let s=()=>wn(e,t.api_url,n,o,{token:t.access_token,payload:i,headers:r}),a=null;if(eu(t.access_token,e.now?.()??Date.now()))try{a=await Zi(e,t)}catch(c){if(!(c instanceof p))throw c;let d=["api_unreachable","api_timeout"].includes(c.code),m=c.httpStatus===429;if(!d&&!m)throw c;a=!1}let l=await s();if(l.status!==401)return l;if(a!==null||!await Zi(e,t))throw new p("session_expired","your saved sign-in has expired or was signed out",g.Failure,Eo);let u=await s();if(u.status===401)throw new p("session_expired","your saved sign-in has expired or was signed out",g.Failure,Eo);return u}function No(e,t,n){return v(e,t,"GET",n)}function nr(e,t,n){let o=t.team&&typeof t.team.id=="string"&&typeof t.team.name=="string"?{id:t.team.id,name:t.team.name}:null;return{email:e,region:hn(t),workspace:o,api_url:t.api_url,active:e===n}}function nu(e){let t=[e.region?.toUpperCase()??null,e.workspace?.name??null].filter(Boolean).join(", ");return`${e.active?"*":" "} ${e.email}${t?` (${t})`:""}${e.active?" active":""}`}var or=(e,{print:t,runtime:n})=>{e.command("accounts").description("List the accounts signed in on this computer, and which one is active. Run one command as another of them with --as <email> or CLOSEDLOOP_ACCOUNT.").action(()=>{let o=Me(n),i=be(o.active_account),r=xo(n).map(a=>nr(a.email,a.session,i)),s={accounts:r,active_account:r.some(a=>a.active)?i:null};t.result(s,a=>{if(a.accounts.length===0)return"No account is signed in on this computer. Sign in with: closedloop login <email>";let l=a.accounts.map(nu);return a.active_account===null&&l.push("No account is active. Choose one with: closedloop use <email>"),l.join(`
29
- `)})}),e.command("use").description("Make one of the accounts signed in on this computer the active one.").argument("<email>","The email of the account.").action(o=>{let i=be(o);if(i===null)throw new p("invalid_account","the account must be the email address of an account signed in on this computer",g.Usage,"Run `closedloop accounts` to see the accounts signed in on this computer.");let r=Y(n,{accounts:[i],settings:!0},a=>{let l=dt(n,a.read(i,N),i);return l!==null&&a.setSettings({...a.settings(),active_account:i}),l});if(r===null)throw new p("account_not_signed_in",`${i} is not signed in on this computer`,g.Failure,`Run \`${Ie(i,n.platform)}\` to sign it in, or \`closedloop accounts\` to see the accounts signed in here.`);let s={status:"active",...nr(i,r,i)};t.result(s,a=>`Now using ${a.email}${a.region?` (${a.region.toUpperCase()})`:""}.`)})};function R(e){return new Intl.DateTimeFormat(void 0,{dateStyle:"medium",timeStyle:"short"}).format(new Date(e))}function ve(e,t){return t?`\x1B]8;;${e}\x07${e}\x1B]8;;\x07`:e}function P(e){return new Intl.DateTimeFormat(void 0,{dateStyle:"medium"}).format(new Date(e))}function bn(e,t){try{return new Intl.DateTimeFormat(void 0,{dateStyle:"medium",timeZone:t}).format(new Date(e))}catch{return P(e)}}function xt(e){let[t,n]=e.split("-").map(Number);return new Intl.DateTimeFormat(void 0,{month:"long",year:"numeric",timeZone:"UTC"}).format(new Date(Date.UTC(t,n-1,1)))}function k(e){return new Intl.NumberFormat(void 0,{maximumFractionDigits:0}).format(e)}function mt(e){let t=Math.round(Math.max(0,e)/6e4);if(t<1)return"under a minute";if(t<60)return`${t} min`;let n=Math.floor(t/60);if(n<48){let o=t%60;return o>=5?`${n} h ${o} min`:`${n} h`}return`${Math.round(n/24)} days`}function q(e){return new Intl.DateTimeFormat(void 0,{dateStyle:"medium",timeZone:"UTC"}).format(new Date(e))}function C(e,t,n){let o=typeof e.body.code=="string"?e.body.code.toLowerCase():"",i=typeof e.body.error=="string"&&e.body.error?e.body.error:n;return new p(/^[a-z][a-z0-9_]*$/.test(o)?o:t,i,g.Failure)}var ou=["can_manage_integrations","can_manage_billing","can_manage_members","can_manage_team_settings","can_view_analytics"];async function W(e,t){let n=await v(e,t,"GET","/api/team-info"),o=n.body.teamInfo;if(n.status!==200)throw C(n,"api_error","Your workspace could not be loaded. Try again.");if(typeof o?.team_id!="string"||!o.team_id)throw new p("no_workspace","you are not in a workspace yet",g.Failure,`Create or join one in the app. ${x}`);let i=Object.fromEntries(ou.map(r=>[r,o.permissions?.[r]===!0]));return{id:o.team_id,name:typeof o.team_name=="string"?o.team_name:"your workspace",role:typeof o.role=="string"?o.role:null,permissions:i}}async function Ge(e,t){return(await W(e,t)).id}var iu={can_manage_integrations:"manage integrations",can_manage_billing:"manage billing"};async function pt(e,t,n,o){let i=await W(e,t);if(!i.permissions[n])throw new p(n==="can_manage_integrations"?"integration_manager_required":"billing_manager_required",`you cannot ${o}: it needs the right to ${iu[n]} in ${i.name}`,g.Failure,`Ask an owner or admin of ${i.name} to give you that right, or to do it for you.`);return i}var Pe=new Set(["queued","importing","stalled"]),ru=7200,ir=30,su=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,sr=e=>`${e[0].toUpperCase()}${e.slice(1)}`;function ar(e){if(!e.progress)return null;let{unit:t,done:n,total:o,failed:i}=e.progress;return`${k(n)} of ${k(o)} ${t}${e.percent!==null?` (${e.percent}%)`:""}${i?`, ${k(i)} failed`:""}`}function lr(e){return e.from&&e.to?`${q(e.from)} to ${q(e.to)}${e.window?` (${e.window})`:""}`:e.window}function ur(e,t){if(e.state==="stalled")return e.last_activity_at?`no progress since ${R(e.last_activity_at)}`:"no progress";if(e.state==="queued")return e.created_at?`waiting to start since ${R(e.created_at)}`:"waiting to start";if(e.state==="importing")return e.estimated_finish_at?`about ${mt(Date.parse(e.estimated_finish_at)-t)} left (around ${R(e.estimated_finish_at)})`:e.started_at?`started ${R(e.started_at)}; too early to estimate`:"too early to estimate";if(e.finished_at){let n=e.started_at??e.created_at,o=n?Date.parse(e.finished_at)-Date.parse(n):NaN,i=!Number.isFinite(o)||o<0?"":e.started_at?` (took ${mt(o)})`:` (${mt(o)} after it was requested)`;return`${e.state==="done"?"finished":"ended"} ${R(e.finished_at)}${i}`}return null}function au(e,t){let n=sr(e.state);return e.state==="failed"||e.state==="stalled"?t.red(n):e.state==="importing"||e.state==="queued"?t.orange(n):e.state==="done"?t.green(n):t.dim(n)}function $n(e,t,n){let o=n.enabled&&Pe.has(e.state)&&e.percent!==null?`${n.orange(n.bar(e.percent/100,12))} `:"";return[au(e,n),`${o}${[e.source,lr(e),ar(e),ur(e,t)].filter(Boolean).join(n.dim(" \xB7 "))}`].join(" ")}async function cr(e,t,n){let o=await v(e.runtime,t,"GET",`/api/cli/backfills?limit=${n}`);if(o.status!==200||!Array.isArray(o.body.backfills))throw C(o,"api_error","Backfills could not be loaded. Try again.");return o.body.backfills}async function lu(e,t){let n=t===void 0?20:Number(t);if(!Number.isInteger(n)||n<1||n>100)throw new p("invalid_option","--limit must be a whole number from 1 to 100",g.Usage);let o=$(e.runtime),i=await cr(e,o,n),r=Date.now();e.print.result({backfills:i},s=>{if(s.backfills.length===0)return"No backfills in this workspace yet.";let a=s.backfills.map(u=>$n(u,r,e.print.style)),l=s.backfills.some(u=>Pe.has(u.state))?`
30
- Progress covers importing; insights appear as the imported records are analysed.`:"";return`${a.join(`
31
- `)}${l}`})}async function rr(e,t,n){if(su.test(n)){let r=await v(e.runtime,t,"GET",`/api/cli/backfills/${n.toLowerCase()}`);if(r.status===404)throw new p("backfill_not_found","there is no backfill with that id in your workspace",g.Failure);if(r.status!==200||typeof r.body.id!="string")throw C(r,"api_error","The backfill could not be loaded. Try again.");return r.body}let o=n.trim().toLowerCase(),i=(await cr(e,t,100)).find(r=>r.slug===o||r.source.toLowerCase()===o);if(!i)throw new p("backfill_not_found",`there is no backfill of "${n}" in your workspace`,g.Failure,"See them with: closedloop backfills");return i}function uu(e,t){let n=[`${e.source} backfill: ${e.state}`],o=lr(e);o&&n.push(`Covers: ${o}`);let i=ar(e);i&&n.push(`Progress: ${i}`),e.created_at&&n.push(`Requested: ${R(e.created_at)}`),e.started_at&&n.push(`Started: ${R(e.started_at)}`),e.last_activity_at&&Pe.has(e.state)&&n.push(`Last activity: ${R(e.last_activity_at)}`);let r=ur(e,t);return r&&n.push(sr(r)+"."),Pe.has(e.state)&&n.push("This covers importing; insights appear as the imported records are analysed."),n.push(`id ${e.id}`),n.join(`
32
- `)}async function cu(e,t,n){let o=$(e.runtime),i=await rr(e,o,t);if(n&&Pe.has(i.state)){let r="",s=e.print.spinner("Checking every 30 seconds\u2026");try{for(let a=0;a<ru&&Pe.has(i.state);a+=ir){let l=$n(i,Date.now(),e.print.style);l!==r&&(s.stop(),e.print.note(l)),r=l,s.update("Checking every 30 seconds\u2026"),await e.runtime.sleep(ir*1e3),i=await rr(e,o,i.id)}}finally{s.stop()}}e.print.result(i,r=>uu(r,Date.now()))}var dr=(e,t)=>{e.command("backfills").alias("backfill").description("History imports: their state, progress and an estimate of when they finish.").option("--limit <n>","How many, up to 100 (default 20).").action(async o=>lu(t,o.limit)).command("show").argument("<which>","A backfill id, or a source for its latest backfill, e.g. gong.").description("One backfill in detail.").option("--watch","Keep checking every 30 seconds until it finishes (up to 2 hours).").action(async(o,i)=>cu(t,o,i.watch===!0))};import{existsSync as Cr,readFileSync as mu,statSync as kr}from"node:fs";var w=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Lo=e=>Array.isArray(e)?e.filter(w):[];function xe(e){let{success:t,...n}=e;return n}function Nt(e,t){let n=Array.isArray(e.body.problems)?e.body.problems:Array.isArray(e.body.errors)?e.body.errors:null;if(e.status!==400||!n||!n.length)return null;let o=n.map(s=>{if(typeof s=="string")return s;let{path:a,field:l,message:u}=s??{},c=typeof a=="string"?a:typeof l=="string"?l:"",d=typeof u=="string"?u:JSON.stringify(s);return c?`${c}: ${d}`:d});if(n.map(s=>w(s)&&typeof s.path=="string"?s.path:"").every(s=>s==="name"||s==="description"))return new p("invalid_input",`the request was not accepted; fix ${o.length===1?"this":`these ${o.length}`}:
27
+ `)}),this}_outputHelpIfRequested(t){let n=this._getHelpOption();n&&t.find(i=>n.is(i))&&(this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)"))}};function Ai(e){return e.map(t=>{if(!t.startsWith("--inspect"))return t;let n,o="127.0.0.1",i="9229",r;return(r=t.match(/^(--inspect(-brk)?)$/))!==null?n=r[1]:(r=t.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null?(n=r[1],/^\d+$/.test(r[3])?i=r[3]:o=r[3]):(r=t.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null&&(n=r[1],o=r[3],i=r[4]),n&&i!=="0"?`${n}=${o}:${parseInt(i)+1}`:t})}function Ti(){if(T.env.NO_COLOR||T.env.FORCE_COLOR==="0"||T.env.FORCE_COLOR==="false")return!1;if(T.env.FORCE_COLOR||T.env.CLICOLOR_FORCE!==void 0)return!0}var bg=new ot;import{createHash as jl}from"node:crypto";import{readdirSync as Hi}from"node:fs";import{join as Fl}from"node:path";import{createHash as kl,randomUUID as Ri}from"node:crypto";import{chmodSync as Sl,closeSync as so,cpSync as Ol,existsSync as Ue,fstatSync as Al,fsyncSync as Tl,lstatSync as ao,mkdirSync as mo,mkdtempSync as Rl,openSync as Ei,readFileSync as Ii,readdirSync as El,renameSync as xi,rmSync as rn,writeFileSync as Pi}from"node:fs";import{basename as po,dirname as it,join as $e,win32 as lo}from"node:path";var g={Success:0,Failure:1,Usage:2},Cl=/^[a-z][a-z0-9_]*$/,p=class extends Error{code;exitCode;hint;httpStatus;constructor(t,n,o,i,r){if(!Cl.test(t))throw new Error(`CliError code must be snake_case: '${t}'`);if(o===g.Success)throw new Error(`CliError '${t}' cannot use the success exit code`);super(n),this.name="CliError",this.code=t,this.exitCode=o,this.hint=i,this.httpStatus=r}};var Il="https://api.closedloop.sh",X={us:"https://api.closedloop.sh",eu:"https://eu.api.closedloop.sh"};function He(e){return e===X.us?"us":e===X.eu?"eu":null}function sn(e){let t=e.env.CLOSEDLOOP_API_URL?.trim(),n=new URL(t||Il),o=["localhost","127.0.0.1","[::1]"].includes(n.hostname);if(n.protocol!=="https:"&&!(n.protocol==="http:"&&o))throw new Error("CLOSEDLOOP_API_URL must use https (http only for localhost)");if(n.username||n.password)throw new Error("CLOSEDLOOP_API_URL must not contain a user name or password");return n.origin}function Q(e){let t=e.env.CLOSEDLOOP_CONFIG_DIR?.trim();if(t)return t;let n=e.env.XDG_CONFIG_HOME?.trim();if(n)return $e(n,"closedloop");let o=e.platform??process.platform;return o==="darwin"?$e(e.homeDir,"Library","Application Support","closedloop"):o==="win32"?lo.join(e.env.LOCALAPPDATA?.trim()||lo.join(e.homeDir,"AppData","Local"),"closedloop"):$e(e.homeDir,".config","closedloop")}function on(e,t,n){(e.platform??process.platform)!=="win32"&&Sl(t,n)}function uo(e,t){let n=Q(e);mo(t,{recursive:!0,mode:448}),on(e,n,448),t!==n&&on(e,t,448)}function Ni(e){return e.env.CLOSEDLOOP_CONFIG_DIR?.trim()||e.env.XDG_CONFIG_HOME?.trim()?null:(e.platform??process.platform)==="win32"?lo.join(e.homeDir,".config","closedloop"):$e(e.homeDir,".config","closedloop")}function xl(e){let t=Q(e),n=Ni(e);if(n===null||t===n||Ue(t)||!Ue(n))return;let o;try{mo(it(t),{recursive:!0}),o=Rl($e(it(t),`.${po(t)}-migration-`)),Ol(n,o,{recursive:!0,filter:i=>{let r=ao(i);if(!r.isDirectory()&&!r.isFile())throw new Error("legacy config contains a non-regular entry");return!0}}),on(e,o,448);for(let i of El(o,{recursive:!0})){let r=$e(o,String(i));on(e,r,ao(r).isDirectory()?448:384)}Ue(t)||xi(o,t)}catch{throw new p("config_migration_failed","your saved ClosedLoop AI settings could not be moved to the new location",g.Failure,"Set CLOSEDLOOP_CONFIG_DIR to your existing config folder and repeat the command. Your original files were kept.")}finally{o&&rn(o,{recursive:!0,force:!0})}}function Li(e){let t,n=new Int32Array(new SharedArrayBuffer(4));for(let s=0;s<5;s++)try{t=Ei(e,"wx",384);break}catch(a){if(a.code!=="EEXIST")throw a;s<4&&Atomics.wait(n,0,0,25)}if(t===void 0)throw new p("config_busy","another CLI process is updating the saved state",g.Failure,`Try again after other CLI commands finish. If no CLI process is running, inspect and remove ${e}.`);let o=t,i,r=()=>{try{if(i&&Ue(e)){let s=ao(e);s.dev===i.dev&&s.ino===i.ino&&rn(e)}}finally{so(o)}};try{i=Al(o),Pi(o,JSON.stringify({pid:process.pid,owner:Ri()}))}catch(s){throw r(),s}return r}function Pl(e,t){let n=Li(e);try{return t()}finally{n()}}function an(e,t){let n=Q(e);try{return mo(it(n),{recursive:!0}),Pl($e(it(n),`.${po(n)}.lock`),()=>(xl(e),t()))}catch(o){throw o instanceof p?o:new p("config_write_failed","the saved config folder could not be updated",g.Failure,`Check permissions and free space for ${n}, then repeat the command.`)}}function Et(e,t){return $e(Q(e),...t.split("/"))}function Di(e,t){let n=Et(e,t);try{return JSON.parse(Ii(n,"utf8"))}catch(o){if(o.code==="ENOENT")return null;throw o instanceof SyntaxError?new p("config_invalid",`the saved ${t} file is not valid JSON`,g.Failure,`Back up and remove ${n}, then repeat the command${t==="credentials.json"?" or run `closedloop login` to sign in again":""}.`):new p("config_unreadable",`the saved ${t} file could not be read`,g.Failure,`Check the permissions for ${n} and CLOSEDLOOP_CONFIG_DIR, then repeat the command.`)}}function go(e){let t=Ni(e);t!==null&&t!==Q(e)&&!Ue(Q(e))&&Ue(t)&&an(e,()=>{})}function xe(e,t){return go(e),Di(e,t)}function ho(e,t){let n=Et(e,t);try{return kl("sha256").update(Ii(n)).digest("hex")}catch(o){if(o.code==="ENOENT")return null;throw new p("config_unreadable",`the saved ${t} file could not be read`,g.Failure,`Check the permissions for ${n} and CLOSEDLOOP_CONFIG_DIR, then repeat the command.`)}}function ji(e,t,n){let o=Et(e,t),i=it(o),r=$e(i,`.${po(o)}.${Ri()}.tmp`),s;try{let a=`${JSON.stringify(n,null,2)}
28
+ `;uo(e,i),s=Ei(r,"wx",384),Pi(s,a),Tl(s),so(s),s=void 0,xi(r,o)}catch{throw new p("config_write_failed",`the saved ${t} file could not be updated`,g.Failure,`Check the permissions and free space for ${i}, then repeat the command. The previous file was kept.`)}finally{s!==void 0&&so(s),rn(r,{force:!0})}}function Nl(e,t){try{rn(Et(e,t),{force:!0})}catch{throw new p("config_write_failed",`the saved ${t} file could not be removed`,g.Failure,"Check the config folder permissions or CLOSEDLOOP_CONFIG_DIR, then repeat the command.")}}var Ll=["credentials.json","pending-login.json","pending-browser-login.json"];function co(e,t,n){uo(e,Q(e));let o=[];try{for(let i of t){let r=Et(e,i);uo(e,it(r)),o.push(Li(r))}return n({read:i=>Di(e,i),write:(i,r)=>ji(e,i,r),remove:i=>Nl(e,i)})}finally{for(let i of o.reverse())i()}}function Dl(e,t){return an(e,()=>co(e,[".credentials.json.lock"],t))}function fo(e,t,n,o=!1){if(o||!Ue(Q(e)))return an(e,()=>co(e,t,n));try{return co(e,t,n)}catch(i){throw i instanceof p?i:new p("config_write_failed","the saved config folder could not be updated",g.Failure,`Check permissions and free space for ${Q(e)}, then repeat the command.`)}}function Fi(e,t,n){Ll.includes(t)?Dl(e,o=>o.write(t,n)):an(e,()=>ji(e,t,n))}var st="general.json",j="credentials.json",Ce="pending-login.json",re="pending-browser-login.json",Wl="working-goal.json",It="accounts",Mi=".general.json.lock",Ul=".credentials.json.lock",Hl=/^[^\s\p{C}@<>,;:"()\\[\]]+@(?:[^\s\p{C}@<>,;:"()\\[\].]+\.)+[^\s\p{C}@<>,;:"()\\[\].]+$/u;function lt(e){let t=e?.trim().toLowerCase();return t&&t.length<=320&&Hl.test(t)?t:null}function ve(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return t.length>0&&t.length<=320&&t.includes("@")&&!/[\s\p{C}]/u.test(t)?t:null}function at(e){return jl("sha256").update(e.trim().toLowerCase()).digest("hex").slice(0,16)}function ge(e){return`${It}/${at(e)}.json`}function Gi(e){return`${It}/.${at(e)}.lock`}function rt(e){return e.replace(/\.json$/,"").replace(/-/g,"_")}function Ml(e){return[j,Ce,re,Wl].includes(e)||/^last-[a-z0-9-]+\.json$/.test(e)}function Me(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function qi(e){return e==="us"||e==="eu"?e:null}function ln(e){return Me(e)?e:{}}function _o(e,t){return Me(e)&&typeof e.email=="string"&&e.email===t?e:null}function Ji(e){let t;try{t=Hi(Fl(Q(e),It))}catch(o){if(o.code==="ENOENT")return[];throw new p("config_unreadable","the saved accounts could not be read",g.Failure,"Check the permissions for the config folder and CLOSEDLOOP_CONFIG_DIR, then repeat the command.")}let n=[];for(let o of t.filter(i=>/^[0-9a-f]{16}\.json$/.test(i)).sort()){let i;try{i=xe(e,`${It}/${o}`)}catch(s){if(s instanceof p&&s.code==="config_invalid")continue;throw s}let r=Me(i)?ve(i.email):null;r!==null&&r===i?.email&&ge(r)===`${It}/${o}`&&n.push(r)}return n.sort()}function Wi(e,t){let n=ho(e,t);if(n===null)return null;try{return{value:xe(e,t),fingerprint:n}}catch(o){if(o instanceof p&&o.code==="config_invalid")return null;throw o}}function Ui(e,t,n){let o=Me(n.migrated)?n.migrated:{},i={moves:[],signedIn:null,region:null},r=t.includes(j)?Wi(e,j):null;if(r!==null){let a=Me(r.value)?ve(r.value.email):null;o[j]===r.fingerprint?i.moves.push({file:j,fingerprint:r.fingerprint,to:null}):a!==null&&(i.signedIn=a,i.region=qi(r.value.region),i.moves.push({file:j,fingerprint:r.fingerprint,to:a,value:r.value}))}let s=i.signedIn??ve(n.active_account);for(let a of t.filter(l=>l!==j)){let l=Wi(e,a);if(l===null)continue;if(o[a]===l.fingerprint){i.moves.push({file:a,fingerprint:l.fingerprint,to:null});continue}let u=null;a===Ce?u=Me(l.value)?ve(l.value.email):null:a===re?u=s??"settings":u=s,u!==null&&i.moves.push({file:a,fingerprint:l.fingerprint,to:u,value:l.value})}return i}function wo(e){try{return Hi(Q(e)).filter(Ml).sort()}catch(t){if(t.code==="ENOENT"||t.code==="ENOTDIR")return[];throw new p("config_unreadable","the saved config folder could not be read",g.Failure,"Check the permissions for the config folder and CLOSEDLOOP_CONFIG_DIR, then repeat the command.")}}function yo(e){return[...new Set(e.moves.map(t=>t.to).filter(t=>t!==null&&t!=="settings"))].sort()}function un(e){go(e);let t=wo(e);if(t.length===0)return;let n=Ui(e,t,ln(xe(e,st)));if(n.moves.length===0)return;let o=yo(n),i=[Ul,Mi,...[...o].sort((r,s)=>at(r).localeCompare(at(s))).map(Gi)];fo(e,i,r=>{let s=ln(r.read(st)),a=Ui(e,wo(e),s);if(a.moves.length===0)return;if(yo(a).some(c=>!o.includes(c)))throw new p("config_busy","another CLI process is updating the saved state",g.Failure,"Try again after other CLI commands finish.");for(let c of yo(a)){let m=_o(r.read(ge(c)),c)??{email:c};for(let d of a.moves.filter(h=>h.to===c))m[rt(d.file)]=d.value;r.write(ge(c),m)}let l={...Me(s.migrated)?s.migrated:{}};for(let c of a.moves)l[c.file]=c.fingerprint;let u=a.moves.find(c=>c.to==="settings");r.write(st,{...s,...u&&{pending_browser_login:u.value},...a.signedIn!==null&&{active_account:a.signedIn,last_email:a.signedIn},...a.region!==null&&{last_region:a.region},migrated:l});for(let c of a.moves)r.remove(c.file)},!0)}function Gl(e,t){for(let n of wo(e))t.remove(n)}function Ge(e){return un(e),ln(xe(e,st))}function cn(e,t,n){un(e);let i=_o(xe(e,ge(t)),t)?.[rt(n)];return i===void 0?null:i}function xt(e){return un(e),Ji(e)}function Pe(e){if(e.account)return e.account;let t=e.accountOption??(e.env.CLOSEDLOOP_ACCOUNT?.trim()||void 0),n;if(t!==void 0){let o=ve(t);if(o===null)throw new p("invalid_account","the account must be the email address of an account signed in on this computer",g.Usage,"Run `closedloop accounts` to see the accounts signed in on this computer.");n={email:o,explicit:!0}}else n={email:ve(Ge(e).active_account),explicit:!1};return e.account=n,n}function Y(e,t,n){un(e);let o=[...new Set((t.accounts==="all"?Ji(e):t.accounts).map(r=>r.trim().toLowerCase()))].sort((r,s)=>at(r).localeCompare(at(s))),i=[...t.settings?[Mi]:[],...o.map(Gi)];return fo(e,i,r=>{let s=l=>{let u=l.trim().toLowerCase();if(!o.includes(u))throw new Error("an account file was changed without its lock");return u},a=l=>_o(r.read(ge(l)),l);return n({settings:()=>ln(r.read(st)),setSettings:l=>{if(!t.settings)throw new Error("general.json was changed without its lock");r.write(st,l)},read:(l,u)=>{let c=a(s(l))?.[rt(u)];return c===void 0?null:c},write:(l,u,c)=>{let m=s(l);r.write(ge(m),{...a(m)??{email:m},[rt(u)]:c})},remove:(l,u)=>{let c=s(l),m=a(c);if(m===null||!(rt(u)in m))return;let{[rt(u)]:d,...h}=m;Object.keys(h).length===1?r.remove(ge(c)):r.write(ge(c),h)},forget:l=>r.remove(ge(s(l))),emails:()=>o.filter(l=>ho(e,ge(l))!==null).sort(),removeLeftovers:()=>Gl(e,r)})})}function dn(e,t,n){return t!==null?Y(e,{accounts:[t]},o=>n({read:i=>o.read(t,i),write:(i,r)=>o.write(t,i,r),remove:i=>o.remove(t,i)})):Y(e,{accounts:[],settings:!0},o=>{let i=r=>{if(r!==re)throw new Error("only a browser sign-in is kept without an account")};return n({read:r=>{i(r);let s=o.settings().pending_browser_login;return s===void 0?null:s},write:(r,s)=>{i(r),o.setSettings({...o.settings(),pending_browser_login:s})},remove:r=>{i(r);let{pending_browser_login:s,...a}=o.settings();o.setSettings(a)}})})}function bo(e,t,n){if(t!==null)return cn(e,t,n);if(n!==re)return null;let o=Ge(e).pending_browser_login;return o===void 0?null:o}function mn(e,t,n){Y(e,{accounts:[],settings:!0},o=>{let i=o.settings();o.setSettings({...i,last_email:t,...n!==null&&{last_region:n}})})}function Vi(e,t){let{email:n}=Pe(e);return n===null?null:cn(e,n,t)}function Bi(e,t,n){let{email:o}=Pe(e);o!==null&&Y(e,{accounts:[o]},i=>{i.read(o,j)!==null&&i.write(o,t,n)})}function Yi(e,t){let{email:n}=Pe(e);n!==null&&Y(e,{accounts:[n]},o=>o.remove(n,t))}function pn(e){return qi(e.region)??He(e.api_url)}function he(e,t=process.platform){return t==="win32"?`'${e.replace(/['‘-‛]/g,"$&$&")}'`:`'${e.replace(/'/g,"'\\''")}'`}function $o(e,t=process.platform){return/^[a-z0-9@._+-]+$/i.test(e)?e:he(e,t)}function Ne(e,t=process.platform){if(e===null)return"closedloop login";let n=$o(e,t);return n.startsWith("-")?`closedloop login --email ${n}`:`closedloop login ${n}`}function gn(e,t,n=process.platform){let o=JSON.stringify(e);if(n!=="win32")return t(he(o,n));let i=o.replace(/[^\x20-\x7e]/g,r=>`\\u${r.charCodeAt(0).toString(16).padStart(4,"0")}`);return`${he(i,n)} | ${t("-")}`}var se="0.3.5";var hn="npm install -g @closedloop-ai/cli@latest",Co=/^(\d{1,6})\.(\d{1,6})\.(\d{1,6})$/,Ki="update-notice.json",ql=1440*60*1e3;function Pt(e){return typeof e=="string"&&Co.test(e)}function zi(e,t){let n=Co.exec(e),o=Co.exec(t);if(!n||!o)throw new Error("compareVersions needs two x.y.z versions");for(let i=1;i<=3;i++)if(Number(n[i])!==Number(o[i]))return Number(n[i])-Number(o[i]);return 0}function Xi(e,t){e.release&&Pt(t)&&(e.release.latest===null||zi(t,e.release.latest)>0)&&(e.release.latest=t)}function ko(e){let t=e.release?.latest??null;return t===null||!Pt(se)?null:zi(t,se)>0?t:null}function vo(e){return e!==void 0&&e!==""&&e!=="0"&&e!=="false"}function Qi(e,t){let n=ko(e);if(n===null||!t)return null;let{env:o}=e;if(vo(o.CI)||vo(o.NO_UPDATE_NOTIFIER)||vo(o.CLOSEDLOOP_NO_UPDATE_NOTIFIER))return null;let i=e.now?.()??Date.now(),r=null;try{r=xe(e,Ki)}catch{}if(r&&r.version===n&&typeof r.shown_at=="number"&&i>=r.shown_at&&i-r.shown_at<ql)return null;try{Fi(e,Ki,{version:n,shown_at:i})}catch{}return`ClosedLoop AI CLI ${n} is out (you have ${se}). Update: ${hn}`}var Jl=7e4,So=/[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g,Vl=(e,t)=>{if(typeof t=="string")return t.replace(So,"");if(t===null||typeof t!="object"||Array.isArray(t))return t;let n=Object.keys(t);return n.some(o=>o!==o.replace(So,""))?Object.fromEntries(n.map(o=>[o.replace(So,""),t[o]])):t};async function fn(e,t,n,o,i={}){if(!o.startsWith("/"))throw new Error("an API path starts with /");let r,s,a=AbortSignal.timeout(Jl);try{r=await e.fetch(`${t}${o}`,{method:n,headers:{...i.headers,Accept:"application/json","X-ClosedLoop-Client":"cli","X-ClosedLoop-CLI-Version":se,...i.payload!==void 0&&{"Content-Type":"application/json"},...i.token&&{Authorization:`Bearer ${i.token}`}},...i.payload!==void 0&&{body:JSON.stringify(i.payload)},signal:a}),s=await r.text()}catch(u){throw u instanceof Error&&u.name==="TimeoutError"||a.aborted&&a.reason?.name==="TimeoutError"?new p("api_timeout","ClosedLoop AI took too long to answer",g.Failure,"Try again; if you sent data, check its status before resending it."):new p("api_unreachable",`could not reach ClosedLoop AI at ${t}`,g.Failure,"Check your connection, or CLOSEDLOOP_API_URL if you set it.")}let l={};try{let u=JSON.parse(s,Vl);u!==null&&typeof u=="object"&&!Array.isArray(u)&&(l=u)}catch{}if(r.status===426&&l.code==="upgrade_required")throw Yl(l);if(Xi(e,r.headers.get("X-ClosedLoop-CLI-Latest")),r.status===429)throw Bl(e,r.headers.get("Retry-After"),l);return{status:r.status,body:l,text:s}}function Bl(e,t,n){let o=typeof n.code=="string"?n.code.toLowerCase():"",i=o==="rate_limit_exceeded"?"rate_limited":o,r=t!==null&&/^\d+(?:\.\d+)?$/.test(t.trim())?Number(t):t!==null?(Date.parse(t)-(e.now?.()??Date.now()))/1e3:NaN,s=Math.max(0,Math.ceil(r)),a=Number.isFinite(s)?`Try again in ${s} second${s===1?"":"s"}.`:"Wait a few minutes, then run it again.";return new p(/^[a-z][a-z0-9_]*$/.test(i)?i:"rate_limited",typeof n.error=="string"&&n.error?n.error:"too many requests to ClosedLoop AI",g.Failure,a,429)}function Yl(e){let t=Pt(e.minimum_version)?e.minimum_version:"a newer version";return new p("upgrade_required",`this ClosedLoop AI CLI (${se}) is too old for the API; ${t} or later is needed`,g.Failure,Pt(e.latest_version)?`Update to ${e.latest_version}: ${hn}`:`Update: ${hn}`)}function qe(e,t,n,o){return fn(e,t,"POST",n,{payload:o})}var Kl=["password","email_code","google","microsoft","okta","saml"];function yn(e){return Kl.includes(e)?e:null}var Oo="Run `closedloop login` to sign in.",D='Sign in to the app with your email address. An account made in the CLI has no password: choose "Email me a code" and the app emails you a sign-in code.';function ut(){return new p("session_changed","your saved sign-in changed while this command was running",g.Failure,"Repeat the command. If you signed out, run `closedloop login` to sign in again.")}function ct(e,t,n){if(t===null||typeof t!="object"||Array.isArray(t))return null;let o=t,i=r=>typeof r=="string"&&r.trim()!==""&&r.trim()===r;if(!i(o.api_url)||!i(o.email)||!i(o.access_token)||!i(o.refresh_token)||o.email.toLowerCase()!==n)return null;try{if(sn({...e,env:{CLOSEDLOOP_API_URL:o.api_url}})!==o.api_url)return null}catch{return null}return o}function To(e,t){let n=t.trim().toLowerCase();return ct(e,cn(e,n,j),n)}function zl(e){let{email:t}=Pe(e);return t===null?null:To(e,t)}function Ro(e){let t=[];for(let n of xt(e)){let o=To(e,n);o!==null&&t.push({email:n,session:o})}return t}function v(e){let t=zl(e);if(t!==null)return t;let{email:n,explicit:o}=Pe(e);if(o&&n!==null)throw new p("account_not_signed_in",`${n} is not signed in on this computer`,g.Failure,`Run \`${Ne(n,e.platform)}\` to sign it in, or \`closedloop accounts\` to see the accounts signed in here.`);let i=Ro(e).map(r=>r.email);throw new p("not_signed_in","you are not signed in on this computer",g.Failure,i.length===0?Oo:`Run \`closedloop use <email>\` to use one of the accounts signed in here (${i.join(", ")}), or \`closedloop login\` to sign in.`)}function er(e,t,n,o,i){if(!t.access_token||!t.refresh_token)throw new Error("a sign-in needs both tokens");let r=t.email.trim().toLowerCase(),s=new Date(e.now?.()??Date.now()).toISOString(),a=i?.account??null,l=pn(t);Y(e,{accounts:a!==null&&a!==r?[r,a]:[r],settings:!0},u=>{if(i){let d=a===null?u.settings().pending_browser_login??null:u.read(a,i.file);if(JSON.stringify(d)!==JSON.stringify(i.value))throw ut()}u.write(r,j,{...t,signed_in_at:s,sign_in_method:n,signed_in_with:o,saved_at:s}),u.remove(r,Ce),u.remove(r,re),a!==null&&a!==r&&i&&u.remove(a,i.file);let{pending_browser_login:c,...m}=u.settings();u.setSettings({...m,...a!==null&&c!==void 0&&{pending_browser_login:c},active_account:r,last_email:r,...l!==null&&{last_region:l}})}),e.account={email:r,explicit:!1}}function Ao(e){let t=e.split(".");if(t.length!==3)return null;try{let n=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8")),{iat:o,exp:i}=n??{};return typeof o!="number"||typeof i!="number"||!Number.isFinite(o)||!Number.isFinite(i)||o<0||i<=o||i*1e3>864e13?null:{issuedAt:o,expiresAt:i}}catch{return null}}function tr(e){let t=Ao(e.refresh_token)??Ao(e.access_token);return t?new Date(t.expiresAt*1e3).toISOString():null}function Xl(e,t){let n=Ao(e);return n!==null&&t/1e3>=n.issuedAt+(n.expiresAt-n.issuedAt)/2}async function Ql(e,t){let n=t.email.toLowerCase(),o=To(e,n);if(!o||o.api_url!==t.api_url||o.email!==t.email||o.refresh_token!==t.refresh_token)throw ut();let i=JSON.stringify(o),r=await fn(e,t.api_url,"POST","/api/auth/refresh",{payload:{refreshToken:t.refresh_token}}),{accessToken:s,refreshToken:a}=r.body;if(r.status!==200||typeof s!="string"||!s.trim()||typeof a!="string"||!a.trim())return null;let l={access_token:s,refresh_token:a,saved_at:new Date(e.now?.()??Date.now()).toISOString()};return Y(e,{accounts:[n]},u=>{if(JSON.stringify(u.read(n,j))!==i)throw ut();u.write(n,j,{...t,...l})}),l}async function Zi(e,t){let n=e.pendingSessionRefreshes??=new Map,o=JSON.stringify([t.api_url,t.email,t.refresh_token]),i=n.get(o);i||(i=Ql(e,t),n.set(o,i));try{let r=await i;return r?(Object.assign(t,r),!0):!1}finally{n.get(o)===i&&n.delete(o)}}async function $(e,t,n,o,i,r){let s=()=>fn(e,t.api_url,n,o,{token:t.access_token,payload:i,headers:r}),a=null;if(Xl(t.access_token,e.now?.()??Date.now()))try{a=await Zi(e,t)}catch(c){if(!(c instanceof p))throw c;let m=["api_unreachable","api_timeout"].includes(c.code),d=c.httpStatus===429;if(!m&&!d)throw c;a=!1}let l=await s();if(l.status!==401)return l;if(a!==null||!await Zi(e,t))throw new p("session_expired","your saved sign-in has expired or was signed out",g.Failure,Oo);let u=await s();if(u.status===401)throw new p("session_expired","your saved sign-in has expired or was signed out",g.Failure,Oo);return u}function Eo(e,t,n){return $(e,t,"GET",n)}function nr(e,t,n){let o=t.team&&typeof t.team.id=="string"&&typeof t.team.name=="string"?{id:t.team.id,name:t.team.name}:null;return{email:e,region:pn(t),workspace:o,api_url:t.api_url,active:e===n}}function Zl(e){let t=[e.region?.toUpperCase()??null,e.workspace?.name??null].filter(Boolean).join(", ");return`${e.active?"*":" "} ${e.email}${t?` (${t})`:""}${e.active?" active":""}`}var or=(e,{print:t,runtime:n})=>{e.command("accounts").description("List the accounts signed in on this computer, and which one is active. Run one command as another of them with --as <email> or CLOSEDLOOP_ACCOUNT.").action(()=>{let o=Ge(n),i=ve(o.active_account),r=Ro(n).map(a=>nr(a.email,a.session,i)),s={accounts:r,active_account:r.some(a=>a.active)?i:null};t.result(s,a=>{if(a.accounts.length===0)return"No account is signed in on this computer. Sign in with: closedloop login <email>";let l=a.accounts.map(Zl);return a.active_account===null&&l.push("No account is active. Choose one with: closedloop use <email>"),l.join(`
29
+ `)})}),e.command("use").description("Make one of the accounts signed in on this computer the active one.").argument("<email>","The email of the account.").action(o=>{let i=ve(o);if(i===null)throw new p("invalid_account","the account must be the email address of an account signed in on this computer",g.Usage,"Run `closedloop accounts` to see the accounts signed in on this computer.");let r=Y(n,{accounts:[i],settings:!0},a=>{let l=ct(n,a.read(i,j),i);return l!==null&&a.setSettings({...a.settings(),active_account:i}),l});if(r===null)throw new p("account_not_signed_in",`${i} is not signed in on this computer`,g.Failure,`Run \`${Ne(i,n.platform)}\` to sign it in, or \`closedloop accounts\` to see the accounts signed in here.`);let s={status:"active",...nr(i,r,i)};t.result(s,a=>`Now using ${a.email}${a.region?` (${a.region.toUpperCase()})`:""}.`)})};function E(e){return new Intl.DateTimeFormat(void 0,{dateStyle:"medium",timeStyle:"short"}).format(new Date(e))}function ke(e,t){return t?`\x1B]8;;${e}\x07${e}\x1B]8;;\x07`:e}function N(e){return new Intl.DateTimeFormat(void 0,{dateStyle:"medium"}).format(new Date(e))}function wn(e,t){try{return new Intl.DateTimeFormat(void 0,{dateStyle:"medium",timeZone:t}).format(new Date(e))}catch{return N(e)}}function Nt(e){let[t,n]=e.split("-").map(Number);return new Intl.DateTimeFormat(void 0,{month:"long",year:"numeric",timeZone:"UTC"}).format(new Date(Date.UTC(t,n-1,1)))}function k(e){return new Intl.NumberFormat(void 0,{maximumFractionDigits:0}).format(e)}function dt(e){let t=Math.round(Math.max(0,e)/6e4);if(t<1)return"under a minute";if(t<60)return`${t} min`;let n=Math.floor(t/60);if(n<48){let o=t%60;return o>=5?`${n} h ${o} min`:`${n} h`}return`${Math.round(n/24)} days`}function V(e){return new Intl.DateTimeFormat(void 0,{dateStyle:"medium",timeZone:"UTC"}).format(new Date(e))}var Lt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ir=e=>Lt.test(e.trim()),L=e=>`See the ids with: ${e}`,eu=e=>/^[aeiou]/i.test(e)?"an":"a";function I(e,t,n){let o=e.trim();if(Lt.test(o))return o.toLowerCase();throw new p("invalid_id",`'${e}' is not ${eu(t)} ${t} id`,g.Usage,L(n))}var R=(e,t,n)=>`${t}${e.dim(`id ${String(n)}`)}`;function C(e,t,n){let o=typeof e.body.code=="string"?e.body.code.toLowerCase():"",i=typeof e.body.error=="string"&&e.body.error?e.body.error:n;return new p(/^[a-z][a-z0-9_]*$/.test(o)?o:t,i,g.Failure)}var tu=["can_manage_integrations","can_manage_billing","can_manage_members","can_manage_team_settings","can_view_analytics"];async function M(e,t){let n=await $(e,t,"GET","/api/team-info"),o=n.body.teamInfo;if(n.status!==200)throw C(n,"api_error","Your workspace could not be loaded. Try again.");if(typeof o?.team_id!="string"||!o.team_id)throw new p("no_workspace","you are not in a workspace yet",g.Failure,`Create or join one in the app. ${D}`);let i=Object.fromEntries(tu.map(r=>[r,o.permissions?.[r]===!0]));return{id:o.team_id,name:typeof o.team_name=="string"?o.team_name:"your workspace",role:typeof o.role=="string"?o.role:null,permissions:i}}async function Je(e,t){return(await M(e,t)).id}var nu={can_manage_integrations:"manage integrations",can_manage_billing:"manage billing"};async function mt(e,t,n,o){let i=await M(e,t);if(!i.permissions[n])throw new p(n==="can_manage_integrations"?"integration_manager_required":"billing_manager_required",`you cannot ${o}: it needs the right to ${nu[n]} in ${i.name}`,g.Failure,`Ask an owner or admin of ${i.name} to give you that right, or to do it for you.`);return i}var Le=new Set(["queued","importing","stalled"]),ou=7200,rr=30,sr="closedloop backfills",lr=e=>`${e[0].toUpperCase()}${e.slice(1)}`;function ur(e){if(!e.progress)return null;let{unit:t,done:n,total:o,failed:i}=e.progress;return`${k(n)} of ${k(o)} ${t}${e.percent!==null?` (${e.percent}%)`:""}${i?`, ${k(i)} failed`:""}`}function cr(e){return e.from&&e.to?`${V(e.from)} to ${V(e.to)}${e.window?` (${e.window})`:""}`:e.window}function dr(e,t){if(e.state==="stalled")return e.last_activity_at?`no progress since ${E(e.last_activity_at)}`:"no progress";if(e.state==="queued")return e.created_at?`waiting to start since ${E(e.created_at)}`:"waiting to start";if(e.state==="importing")return e.estimated_finish_at?`about ${dt(Date.parse(e.estimated_finish_at)-t)} left (around ${E(e.estimated_finish_at)})`:e.started_at?`started ${E(e.started_at)}; too early to estimate`:"too early to estimate";if(e.finished_at){let n=e.started_at??e.created_at,o=n?Date.parse(e.finished_at)-Date.parse(n):NaN,i=!Number.isFinite(o)||o<0?"":e.started_at?` (took ${dt(o)})`:` (${dt(o)} after it was requested)`;return`${e.state==="done"?"finished":"ended"} ${E(e.finished_at)}${i}`}return null}function iu(e,t){let n=lr(e.state);return e.state==="failed"||e.state==="stalled"?t.red(n):e.state==="importing"||e.state==="queued"?t.orange(n):e.state==="done"?t.green(n):t.dim(n)}function _n(e,t,n){let o=n.enabled&&Le.has(e.state)&&e.percent!==null?`${n.orange(n.bar(e.percent/100,12))} `:"";return[iu(e,n),`${o}${[e.source,cr(e),ur(e),dr(e,t)].filter(Boolean).join(n.dim(" \xB7 "))}`].join(" ")}async function ru(e,t,n){let o=await $(e.runtime,t,"GET",`/api/cli/backfills?limit=${n}`);if(o.status!==200||!Array.isArray(o.body.backfills))throw C(o,"api_error","Backfills could not be loaded. Try again.");return o.body.backfills}async function su(e,t){let n=t===void 0?20:Number(t);if(!Number.isInteger(n)||n<1||n>100)throw new p("invalid_option","--limit must be a whole number from 1 to 100",g.Usage);let o=v(e.runtime),i=await ru(e,o,n),r=Date.now();e.print.result({backfills:i},s=>{if(s.backfills.length===0)return"No backfills in this workspace yet.";let a=e.print.style,l=s.backfills.flatMap(c=>[_n(c,r,a),R(a," ",c.id)]),u=s.backfills.some(c=>Le.has(c.state))?`
30
+ Progress covers importing; insights appear as the imported records are analysed.`:"";return`${l.join(`
31
+ `)}${u}
32
+ ${a.dim("Read one by its id: closedloop backfills show <id>")}`})}async function ar(e,t,n){let o=I(n,"backfill",sr),i=await $(e.runtime,t,"GET",`/api/cli/backfills/${o}`);if(i.status===404)throw new p("backfill_not_found","there is no backfill with that id in your workspace",g.Failure,L(sr));if(i.status!==200||typeof i.body.id!="string")throw C(i,"api_error","The backfill could not be loaded. Try again.");return i.body}function au(e,t){let n=[`${e.source} backfill: ${e.state}`],o=cr(e);o&&n.push(`Covers: ${o}`);let i=ur(e);i&&n.push(`Progress: ${i}`),e.created_at&&n.push(`Requested: ${E(e.created_at)}`),e.started_at&&n.push(`Started: ${E(e.started_at)}`),e.last_activity_at&&Le.has(e.state)&&n.push(`Last activity: ${E(e.last_activity_at)}`);let r=dr(e,t);return r&&n.push(lr(r)+"."),Le.has(e.state)&&n.push("This covers importing; insights appear as the imported records are analysed."),n.push(`id ${e.id}`),n.join(`
33
+ `)}async function lu(e,t,n){let o=v(e.runtime),i=await ar(e,o,t);if(n&&Le.has(i.state)){let r="",s=e.print.spinner("Checking every 30 seconds\u2026");try{for(let a=0;a<ou&&Le.has(i.state);a+=rr){let l=_n(i,Date.now(),e.print.style);l!==r&&(s.stop(),e.print.note(l)),r=l,s.update("Checking every 30 seconds\u2026"),await e.runtime.sleep(rr*1e3),i=await ar(e,o,i.id)}}finally{s.stop()}}e.print.result(i,r=>au(r,Date.now()))}var mr=(e,t)=>{e.command("backfills").alias("backfill").description("History imports, each with its id: their state, progress and an estimate of when they finish.").option("--limit <n>","How many, up to 100 (default 20).").action(async o=>su(t,o.limit)).command("show").argument("<id>","The backfill's id, from closedloop backfills.").description("One backfill in detail.").option("--watch","Keep checking every 30 seconds until it finishes (up to 2 hours).").action(async(o,i)=>lu(t,o,i.watch===!0))};import{existsSync as kr,readFileSync as uu,statSync as Sr}from"node:fs";var w=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Io=e=>Array.isArray(e)?e.filter(w):[];function De(e){let{success:t,...n}=e;return n}function Dt(e,t){let n=Array.isArray(e.body.problems)?e.body.problems:Array.isArray(e.body.errors)?e.body.errors:null;if(e.status!==400||!n||!n.length)return null;let o=n.map(s=>{if(typeof s=="string")return s;let{path:a,field:l,message:u}=s??{},c=typeof a=="string"?a:typeof l=="string"?l:"",m=typeof u=="string"?u:JSON.stringify(s);return c?`${c}: ${m}`:m});if(n.map(s=>w(s)&&typeof s.path=="string"?s.path:"").every(s=>s==="name"||s==="description"))return new p("invalid_input",`the request was not accepted; fix ${o.length===1?"this":`these ${o.length}`}:
33
34
  ${o.join(`
34
35
  `)}`,g.Failure);let r=`the ${t} was not accepted; fix ${o.length===1?"this":`these ${o.length}`}:`;return new p(t==="rule"?"invalid_rule":"invalid_metric",`${r}
35
36
  ${o.join(`
36
- `)}`,g.Failure,"The fields a rule can name: closedloop fields list; one field's values: closedloop fields show <key>")}async function K(e,t,n,o){let i=$(e.runtime),r=await v(e.runtime,i,t,n,o);if(r.status===429&&typeof r.body.code!="string")throw new p("rate_limited",typeof r.body.error=="string"&&r.body.error?r.body.error:"too many requests; try again in a few minutes",g.Failure,"Wait a few minutes, then run it again.");return r}var z=e=>e.status===200||e.status===201;function he(e){return w(e)&&w(e.current_version)?e.current_version:{}}function Ne(e){return w(e)&&typeof e.version=="number"?e.version:null}async function mr(e,t){let n=new URLSearchParams;t.q&&n.set("q",t.q),t.entity&&n.set("entity",t.entity);let o=n.toString(),i=await K(e,"GET",`/api/fields${o?`?${o}`:""}`);if(!z(i)||!Array.isArray(i.body.fields))throw C(i,"api_error","The fields could not be loaded. Try again.");return Lo(i.body.fields)}async function pr(e,t){let n=await K(e,"GET",`/api/fields/${encodeURIComponent(t)}`);if(n.status===404)throw new p("field_not_found",`there is no field ${t} in this workspace`,g.Failure,"Find its key: closedloop fields search <words>");if(!z(n)||!w(n.body.field))throw C(n,"api_error","The field could not be loaded. Try again.");return n.body.field}async function gr(e,t){let n=await K(e,"POST","/api/segments/preview",{rule:t}),o=Nt(n,"rule");if(o)throw o;if(!z(n)||typeof n.body.count!="number")throw C(n,"api_error","The preview could not be made. Try again.");return xe(n.body)}async function hr(e,t,n,o){let i=await K(e,"POST","/api/segments",{name:t,rule:n,...o!==void 0&&{description:o}}),r=Nt(i,"rule");if(r)throw r;if(i.status===409&&i.body.code==="NAME_TAKEN")throw new p("segment_exists",typeof i.body.error=="string"&&i.body.error?i.body.error:`a segment named "${t}" already exists`,g.Failure,"Pick another name, or see yours: closedloop segments list");if(!z(i)||!w(i.body.segment)||typeof i.body.segment.id!="string")throw C(i,"api_error","The segment could not be saved. Try again.");return i.body.segment}async function fr(e,t){try{let n=await K(e,"GET",`/api/segments/${encodeURIComponent(t)}`),o=z(n)?he(n.body.segment).rule:null;return w(o)?o:null}catch(n){if(n instanceof p)return null;throw n}}async function yr(e){let t=await K(e,"GET","/api/segments");if(!z(t)||!Array.isArray(t.body.segments))throw C(t,"api_error","The segments could not be loaded. Try again.");return Lo(t.body.segments)}async function Do(e,t,n){let o=await K(e,"POST","/api/metrics/preview",{segment:t,calc:n}),i=Nt(o,"metric");if(i)throw i;if(!z(o)||!("value"in o.body))throw C(o,"api_error","The preview could not be made. Try again.");return xe(o.body)}async function wr(e,t,n,o,i){let r=await K(e,"POST","/api/metrics",{name:t,segment:n,calc:o,...i!==void 0&&{description:i}}),s=Nt(r,"metric");if(s)throw s;if(r.status===409&&r.body.code==="NAME_TAKEN")throw new p("metric_exists",typeof r.body.error=="string"&&r.body.error?r.body.error:`a metric named "${t}" already exists`,g.Failure,"Pick another name, or see yours: closedloop metrics list");if(!z(r)||!w(r.body.metric)||typeof r.body.metric.id!="string")throw C(r,"api_error","The metric could not be saved. Try again.");return r.body.metric}async function vn(e){let t=await K(e,"GET","/api/metrics");if(!z(t)||!Array.isArray(t.body.metrics))throw C(t,"api_error","The metrics could not be loaded. Try again.");return Lo(t.body.metrics)}async function _r(e,t,n){let o=await K(e,"DELETE",`/api/metrics/${encodeURIComponent(t)}`,n===void 0?{}:{reason:n}),i=typeof o.body.error=="string"&&o.body.error?o.body.error:null;if(o.status===404)throw new p("not_found",i??"There is no such metric.",g.Failure,"See them: closedloop metrics list");if(o.status===409){let s=o.body.code==="ARCHIVED";throw new p(s?"metric_deleted":"metric_in_use",i??(s?"this metric is already deleted":"this metric is in use"),g.Failure)}let r=Nt(o,"metric");if(r)throw r;if(!z(o)||!w(o.body.metric)||!Array.isArray(o.body.removed))throw C(o,"api_error","The metric could not be deleted. Try again.");return xe(o.body)}async function br(e,t,n){try{let o=await K(e,"GET",`/api/metrics/${encodeURIComponent(t)}?runs_limit=1`);if(!z(o)||!Array.isArray(o.body.versions))return null;let i=o.body.versions.find(r=>w(r)&&r.version===n);return w(i)&&w(i.calc)?i.calc:null}catch(o){if(o instanceof p)return null;throw o}}async function $r(e,t){let n=await K(e,"GET",`/api/metrics/${encodeURIComponent(t)}?runs_limit=1000`);if(n.status===404){let o=typeof n.body.error=="string"&&n.body.error?n.body.error:"There is no such metric.";throw new p("not_found",o,g.Failure,"See them: closedloop metrics list")}if(!z(n)||!w(n.body.metric)||!Array.isArray(n.body.versions)||!Array.isArray(n.body.runs))throw C(n,"api_error","The metric could not be loaded. Try again.");return xe(n.body)}async function jo(e,t,n,o){let i=new URLSearchParams({as_of:n});o!==void 0&&i.set("version",String(o));let r=await K(e,"GET",`/api/metrics/${encodeURIComponent(t)}/value?${i.toString()}`);if(r.status===404){let s=typeof r.body.error=="string"&&r.body.error?r.body.error:"There is no such metric.";throw new p("not_found",s,g.Failure,"See them: closedloop metrics list")}if(!z(r))throw C(r,"api_error","The value could not be loaded. Try again.");return xe(r.body)}var du=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function vr(e,t){let n=$(e);return{api_url:new URL(n.api_url).origin,user_id:typeof n.user_id=="string"?n.user_id:null,email:n.email.toLowerCase(),team_id:t??null}}async function Le(e,t,n){let{runtime:o}=e,i=vr(o),r;try{r=pn(o,t)}catch{return null}if(!r||typeof r!="object"||Array.isArray(r))return null;let s=r.owner;if(!s||s.api_url!==i.api_url||s.user_id!==i.user_id||s.email!==i.email)return null;let a=Array.isArray(r.ids)?r.ids[n-1]:null;return typeof a!="string"||!du.test(a)?r:s.team_id!==(await Lt(e)).id?null:r}function Lt(e){return e.workspace??=W(e.runtime,$(e.runtime)),e.workspace}async function Ce(e,t,n){let o=await Lt(e),{runtime:i}=e;gn(i,t,{...n,owner:vr(i,o.id),at:new Date(i.now?.()??Date.now()).toISOString()})}var pu=1024*1024,Sr=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,qe=(e,t,n)=>new p(e,t,g.Usage,n);function Dt(e){let t=e.trim().replace(/^'/,"");return/^[{[]/.test(t)&&!t.includes('"')&&/[{,]\s*[A-Za-z_][\w.]*\s*:/.test(t)}function Cn(e,t=process.platform,n=e){let o=t==="win32"?`On Windows, PowerShell 5.1 and Command Prompt remove the double quotes from JSON typed in a command. Save the ${e} in a file and give its path: --${n} ${e}.json`:`Put the JSON in single quotes, --${n} '{...}', or save it in a file and give its path: --${n} ${e}.json`;return qe(`invalid_${e}`,`the ${e} given is not valid JSON: the shell removed its double quotes`,o)}var gu=[{option:"rule",kind:"rule"},{option:"segment",kind:"segment"},{option:"calc",kind:"calc"},{option:"file",kind:"structure",command:["products","save"]}];function hu(e,t,n){if(n===void 0)return!0;let o=e.slice(0,t).filter(i=>!i.startsWith("-"));return n.every((i,r)=>o[r]===i)}function Ir(e){for(let t=0;t<e.length;t++)for(let{option:n,kind:o,command:i}of gu){let r=e[t]===`--${n}`?e[t+1]:e[t].startsWith(`--${n}=`)?e[t].slice(n.length+3):void 0;if(r!==void 0&&Dt(r)&&hu(e,t,i))return{option:n,kind:o}}return null}async function De(e,t,n,o,i=pu,r=n){let s,a,l=t.trim(),u=l.startsWith("{")||l.startsWith("[");if(l==="-")s=await e.runtime.readStdin(i),a="standard input";else if(u)s=l,a=`the ${n} given`;else{if(!Cr(l)&&Dt(l))throw Cn(n,e.runtime.platform,r);if(!Cr(l)||!kr(l).isFile())throw qe("file_not_found",`there is no file ${l}`,o);if(kr(l).size>i)throw qe("file_too_large",`${l} is larger than ${Math.round(i/(1024*1024))} MB`);s=mu(l,"utf8").replace(/^\uFEFF/,""),a=l}if(!s.trim())throw qe(`invalid_${n}`,`${a} is empty`,o);let c;try{c=JSON.parse(s)}catch{throw u&&Dt(s)?Cn(n,e.runtime.platform,r):qe(`invalid_${n}`,`${a} is not valid JSON; check its syntax`,o)}if(!w(c))throw qe(`invalid_${n}`,`${a} is not a JSON object`,o);return c}function Sn(e,t,n){return Ce(e,t,{ids:n})}async function se(e,t,n,o,i){let r=t.trim();if(Sr.test(r))return r.toLowerCase();if(!/^\d{1,3}$/.test(r))return null;let s=await Le(e,n,Number(r)),a=Array.isArray(s?.ids)?s.ids:[],l=Number(r),u=a[l-1];if(l>=1&&l<=a.length&&typeof u=="string"&&Sr.test(u))return u.toLowerCase();throw qe("unknown_number",a.length?`the last ${o} list had ${a.length}, not ${l}`:`there is no recent ${o} list to pick a number from`,`List them first: ${i}`)}var Ar={customer:["customer","customers"],deal:["deal","deals"],person:["person","people"],insight:["insight","insights"],context:["context record","context records"],conversation:["conversation","conversations"],usage:["usage week","usage weeks"]},An=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);function M(e,t=2){return typeof e!="string"||!An(Ar,e)?t===1?"member":"members":Ar[e][t===1?0:1]}function O(e){return typeof e!="number"||!Number.isFinite(e)?String(e):Number.isInteger(e)?k(e):new Intl.NumberFormat(void 0,{maximumFractionDigits:2}).format(e)}function L(e){return typeof e=="string"?JSON.stringify(e):Array.isArray(e)?e.map(L).join(", "):e==null?"empty":typeof e=="number"?O(e):typeof e=="object"?JSON.stringify(e):String(e)}function Je(e,t){switch(e){case"exists":return"has a value";case"is_empty":return"is empty";case"within_last":return`within the last ${L(t)} days`;case"within_period":return`within this ${typeof t=="string"?t:L(t)} so far`;case"between":return Array.isArray(t)&&t.length===2?`between ${L(t[0])} and ${L(t[1])}`:`between ${L(t)}`;case"not_in":return`not in ${L(t)}`;default:return`${String(e)} ${L(t)}`}}var Or={">=":"at least",">":"more than","<=":"at most","<":"fewer than","=":"exactly"},Tr={count_distinct:"distinct values of",sum:"sum of",avg:"average of",min:"smallest",max:"largest",median:"median of"};function Pr(e){if(typeof e!="string")return String(e);let t=/^p(\d{1,2})$/.exec(e);return t?`${t[1]}th percentile of`:An(Tr,e)?Tr[e]:e}function xr(e){let t=w(e)&&Array.isArray(e.includes)?e.includes.filter(w):[];return new Map(t.filter(n=>typeof n.version_id=="string"&&typeof n.name=="string").map(n=>[n.version_id,n.name]))}function jt(e,t=new Map){if(typeof e=="string")return e;if(!w(e))return"";let n=i=>jt(i,t),o=(i,r)=>{let s=n(i),a=r==="and"?"or":"and";return w(i)&&Array.isArray(i[a])?`(${s})`:s};if(Array.isArray(e.and))return e.and.map(i=>o(i,"and")).join(" and ");if(Array.isArray(e.or))return e.or.map(i=>o(i,"or")).join(" or ");if("not"in e)return`not (${n(e.not)})`;if(typeof e.segment=="string")return`in segment ${JSON.stringify(t.get(e.segment)??e.segment)}`;if(typeof e.field=="string")return`${e.field} ${Je(e.op,e.value)}`;if(typeof e.has=="string"){let i=w(e.count)?e.count:null;return`has ${typeof e.min=="number"&&e.min!==1?`at least ${L(e.min)} ${M(e.has)}`:i&&typeof i.op=="string"&&An(Or,i.op)?`${Or[i.op]} ${L(i.value)} ${M(e.has)}`:`${/^[aeiou]/.test(M(e.has,1))?"an":"a"} ${M(e.has,1)}`}${e.where?` where ${n(e.where)}`:""}`}if(w(e.subtotal)){let i=e.subtotal,r=i.fn==="count"?`count of ${M(i.of)}`:`${Pr(i.fn)} ${String(i.field??M(i.of))}`,s=i.where?` over ${M(i.of)} where ${n(i.where)}`:"";return`${r}${s} ${Je(e.op,e.value)}`}if(typeof e.moved=="string"){let i=e.from!==void 0?` from ${L(e.from)}`:"";return`${e.moved} moved into ${L(e.into)}${i} ${Rr(e)}`}if(w(e.change)){let i=e.change,r=w(i.start)?` (start ${Je(i.start.op,i.start.value)})`:"";return`change of ${String(i.field)} ${Rr(i)} ${Je(e.op,e.value)}${r}`}if(e.usage==="event"){let i=e.within_last!==void 0?` in the last ${L(e.within_last)} days`:"";return`usage event ${L(e.event)} ${String(e.measure??"count")}${i} ${Je(e.op,e.value)}`}return JSON.stringify(e)}function Rr(e){return e.within_period!==void 0?Je("within_period",e.within_period):Je("within_last",e.within_last)}function Nr(e,t=new Map){return w(e)?e.where===void 0?`all ${M(e.of)}`:jt(e.where,t):"no rule"}function kn(e){if(!w(e))return"no calculation";if(e.fn==="ratio")return`share: ${kn(e.numerator)} / ${kn(e.denominator)}`;let t=e.fn==="count"?"count":`${Pr(e.fn)} ${String(e.field)}`;return e.where?`${t} where ${jt(e.where)}`:t}function Ft(e){if(!w(e)||e.fn!=="ratio"||!w(e.numerator)||!w(e.denominator))return!1;let[t,n]=[e.numerator,e.denominator];return t.fn==="count"&&n.fn==="count"||t.fn==="sum"&&n.fn==="sum"&&t.field===n.field}function Ve(e,t,n,o){if(typeof t!="number"||!Number.isFinite(t))return null;if(w(e)&&e.fn==="ratio"){let i=typeof n=="number"&&typeof o=="number"?` (${O(n)} / ${O(o)})`:"";return Ft(e)?`${(t*100).toFixed(1)}%${i}`:`${O(t)}${i}`}return O(t)}var Er={crm:"CRM",insights:"insights",usage:"product usage"},On=e=>typeof e=="string"&&An(Er,e)?Er[e]:String(e);function Ut(e){let t=Array.isArray(e)?e.filter(n=>typeof n=="string"):[];return t.length?`the data is late (${t.map(On).join(", ")}), so this number may be out of date`:null}function Tn(e){if(typeof e=="string")return e;if(!w(e))return JSON.stringify(e);if(typeof e.message=="string")return e.message;let t=JSON.stringify(e),o=(Array.isArray(e.suggestions)?e.suggestions:[]).map(i=>w(i)?`${L(i.value)}${typeof i.count=="number"?` (${k(i.count)})`:""}`:L(i));return o.length?`${t} Closest: ${o.join(", ")}.`:t}function Lr(e){return Array.isArray(e)?e.filter(w).filter(t=>typeof t.field=="string"&&typeof t.instead_of=="string"):[]}var Fo=e=>typeof e=="number"?k(e):"no number";function Rn(e,t){let n=Lr(e);return n.length?`If the request did not say which field it means, ask the user to choose before saving: ${n.map(i=>`${String(i.instead_of)} gives ${Fo(t)}, ${String(i.field)} gives ${Fo(i.value)}${typeof i.reason=="string"&&i.reason?` (${i.reason})`:""}`).join("; ")}.`:null}function En(e,t){return Lr(t).map(n=>e.dim(`With ${String(n.field)} instead of ${String(n.instead_of)}: ${n.value===null||n.value===void 0?`no number${typeof n.reason=="string"&&n.reason?` (${n.reason})`:""}`:Fo(n.value)}. They are often confused; say which one you mean.`))}var Wo="Insights are filed against the products, areas and features you confirm here.",Fr="An area is a part of a product; a feature is a part of the product that exists, never planned work.",ke="closedloop products confirm",Pn="closedloop products save --file <structure.json>",fu=10*1024*1024,In="Start from the current structure: closedloop products --json > structure.json";function yu(e){let t=e.structure;if(!t||!Array.isArray(t.products)||!Array.isArray(t.areas)||!Array.isArray(t.features))throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);let n=Array.isArray(e.suggested_products)?e.suggested_products:[];return{...e,structure:t,suggested_products:n,can_change:e.can_change===!0}}async function gt(e,t){let n=await v(e,t,"GET","/api/product-structure");if(n.status!==200)throw C(n,"api_error","The product structure could not be loaded. Try again.");return yu(n.body)}var Ur=e=>typeof e=="number"?` \xB7 ${k(e)} insight${e===1?"":"s"}`:"",Uo=(e,t)=>(e??[]).map(n=>`${t}Feature: ${n.name}${Ur(n.insight_count)}`),Dr=(e,t)=>(e??[]).flatMap(n=>[`${t}Area: ${n.name}${Ur(n.insight_count)}`,...Uo(n.features,`${t} `)]);function wu(e){let{structure:t,suggested_products:n}=e,o=[Wo];if(t.products.length+t.areas.length+t.features.length===0&&n.length===0)return o.push("",`No products yet. Suggestions appear once your company research is done; see: closedloop company. Or add your own: ${Pn}`),o.join(`
37
- `);for(let r of t.products)o.push("",`Product: ${r.name}`,...Dr(r.areas," "),...Uo(r.features," "));return t.areas.length+t.features.length>0&&o.push("","Under no product",...Dr(t.areas," "),...Uo(t.features," ")),t.products.length===0&&o.push("","No products confirmed yet."),n.length>0&&o.push("",`Suggested by your company research, not confirmed yet: ${n.map(r=>r.name).join(", ")}. Confirm them with: ${ke}`),e.can_change||o.push("","Only an owner or admin of your workspace can change it."),o.join(`
38
- `)}async function _u(e){let t=$(e.runtime);e.print.result(await gt(e.runtime,t),wu)}var bu={product_area:["area","product"],area_feature:["feature","area"],product_feature:["feature","product"]};function $u(e,t){if(e.action==="link"||e.action==="unlink"){let[o,i]=bu[e.level]??["item","item"];return e.action==="link"?`Put ${o} "${e.child}" under ${i} "${e.parent}"`:`Take ${o} "${e.child}" out of ${i} "${e.parent}"`}let n=`${e.level} "${e.name}"`;switch(e.action){case"create":return`Add ${n}`;case"restore":return e.id&&t.has(e.id)?`Confirm the suggested ${n}`:`Restore ${n}`;case"update":return`Change ${n}${e.fields?.length?` (${e.fields.map(o=>o.replace(/_/g," ")).join(", ")})`:""}`;case"archive":return`Archive ${n}`;default:return`${e.action} ${n}`}}function Wr(e){if(e.length===0)return new p("invalid_structure","the structure has problems; nothing was saved",g.Failure,In);let t=e.map(n=>`${n.path}: ${n.message}`).join(`
37
+ `)}`,g.Failure,"The fields a rule can name: closedloop fields list; one field's values: closedloop fields show <key>")}async function K(e,t,n,o){let i=v(e.runtime),r=await $(e.runtime,i,t,n,o);if(r.status===429&&typeof r.body.code!="string")throw new p("rate_limited",typeof r.body.error=="string"&&r.body.error?r.body.error:"too many requests; try again in a few minutes",g.Failure,"Wait a few minutes, then run it again.");return r}var z=e=>e.status===200||e.status===201;function fe(e){return w(e)&&w(e.current_version)?e.current_version:{}}function je(e){return w(e)&&typeof e.version=="number"?e.version:null}async function pr(e,t){let n=new URLSearchParams;t.q&&n.set("q",t.q),t.entity&&n.set("entity",t.entity);let o=n.toString(),i=await K(e,"GET",`/api/fields${o?`?${o}`:""}`);if(!z(i)||!Array.isArray(i.body.fields))throw C(i,"api_error","The fields could not be loaded. Try again.");return Io(i.body.fields)}async function gr(e,t){let n=await K(e,"GET",`/api/fields/${encodeURIComponent(t)}`);if(n.status===404)throw new p("field_not_found",`there is no field ${t} in this workspace`,g.Failure,"Find its key: closedloop fields search <words>");if(!z(n)||!w(n.body.field))throw C(n,"api_error","The field could not be loaded. Try again.");return n.body.field}async function hr(e,t){let n=await K(e,"POST","/api/segments/preview",{rule:t}),o=Dt(n,"rule");if(o)throw o;if(!z(n)||typeof n.body.count!="number")throw C(n,"api_error","The preview could not be made. Try again.");return De(n.body)}async function fr(e,t,n,o){let i=await K(e,"POST","/api/segments",{name:t,rule:n,...o!==void 0&&{description:o}}),r=Dt(i,"rule");if(r)throw r;if(i.status===409&&i.body.code==="NAME_TAKEN")throw new p("segment_exists",typeof i.body.error=="string"&&i.body.error?i.body.error:`a segment named "${t}" already exists`,g.Failure,"Pick another name, or see yours: closedloop segments list");if(!z(i)||!w(i.body.segment)||typeof i.body.segment.id!="string")throw C(i,"api_error","The segment could not be saved. Try again.");return i.body.segment}async function yr(e,t){try{let n=await K(e,"GET",`/api/segments/${encodeURIComponent(t)}`),o=z(n)?fe(n.body.segment).rule:null;return w(o)?o:null}catch(n){if(n instanceof p)return null;throw n}}async function wr(e){let t=await K(e,"GET","/api/segments");if(!z(t)||!Array.isArray(t.body.segments))throw C(t,"api_error","The segments could not be loaded. Try again.");return Io(t.body.segments)}async function xo(e,t,n){let o=await K(e,"POST","/api/metrics/preview",{segment:t,calc:n}),i=Dt(o,"metric");if(i)throw i;if(!z(o)||!("value"in o.body))throw C(o,"api_error","The preview could not be made. Try again.");return De(o.body)}async function _r(e,t,n,o,i){let r=await K(e,"POST","/api/metrics",{name:t,segment:n,calc:o,...i!==void 0&&{description:i}}),s=Dt(r,"metric");if(s)throw s;if(r.status===409&&r.body.code==="NAME_TAKEN")throw new p("metric_exists",typeof r.body.error=="string"&&r.body.error?r.body.error:`a metric named "${t}" already exists`,g.Failure,"Pick another name, or see yours: closedloop metrics list");if(!z(r)||!w(r.body.metric)||typeof r.body.metric.id!="string")throw C(r,"api_error","The metric could not be saved. Try again.");return r.body.metric}async function br(e){let t=await K(e,"GET","/api/metrics");if(!z(t)||!Array.isArray(t.body.metrics))throw C(t,"api_error","The metrics could not be loaded. Try again.");return Io(t.body.metrics)}async function $r(e,t,n){let o=await K(e,"DELETE",`/api/metrics/${encodeURIComponent(t)}`,n===void 0?{}:{reason:n}),i=typeof o.body.error=="string"&&o.body.error?o.body.error:null;if(o.status===404)throw new p("not_found",i??"There is no such metric.",g.Failure,"See them: closedloop metrics list");if(o.status===409){let s=o.body.code==="ARCHIVED";throw new p(s?"metric_deleted":"metric_in_use",i??(s?"this metric is already deleted":"this metric is in use"),g.Failure)}let r=Dt(o,"metric");if(r)throw r;if(!z(o)||!w(o.body.metric)||!Array.isArray(o.body.removed))throw C(o,"api_error","The metric could not be deleted. Try again.");return De(o.body)}async function vr(e,t,n){try{let o=await K(e,"GET",`/api/metrics/${encodeURIComponent(t)}?runs_limit=1`);if(!z(o)||!Array.isArray(o.body.versions))return null;let i=o.body.versions.find(r=>w(r)&&r.version===n);return w(i)&&w(i.calc)?i.calc:null}catch(o){if(o instanceof p)return null;throw o}}async function Cr(e,t){let n=await K(e,"GET",`/api/metrics/${encodeURIComponent(t)}?runs_limit=1000`);if(n.status===404){let o=typeof n.body.error=="string"&&n.body.error?n.body.error:"There is no such metric.";throw new p("not_found",o,g.Failure,"See them: closedloop metrics list")}if(!z(n)||!w(n.body.metric)||!Array.isArray(n.body.versions)||!Array.isArray(n.body.runs))throw C(n,"api_error","The metric could not be loaded. Try again.");return De(n.body)}async function Po(e,t,n,o){let i=new URLSearchParams({as_of:n});o!==void 0&&i.set("version",String(o));let r=await K(e,"GET",`/api/metrics/${encodeURIComponent(t)}/value?${i.toString()}`);if(r.status===404){let s=typeof r.body.error=="string"&&r.body.error?r.body.error:"There is no such metric.";throw new p("not_found",s,g.Failure,"See them: closedloop metrics list")}if(!z(r))throw C(r,"api_error","The value could not be loaded. Try again.");return De(r.body)}var cu=1024*1024,pt=(e,t,n)=>new p(e,t,g.Usage,n);function jt(e){let t=e.trim().replace(/^'/,"");return/^[{[]/.test(t)&&!t.includes('"')&&/[{,]\s*[A-Za-z_][\w.]*\s*:/.test(t)}function bn(e,t=process.platform,n=e){let o=t==="win32"?`On Windows, PowerShell 5.1 and Command Prompt remove the double quotes from JSON typed in a command. Save the ${e} in a file and give its path: --${n} ${e}.json`:`Put the JSON in single quotes, --${n} '{...}', or save it in a file and give its path: --${n} ${e}.json`;return pt(`invalid_${e}`,`the ${e} given is not valid JSON: the shell removed its double quotes`,o)}var du=[{option:"rule",kind:"rule"},{option:"segment",kind:"segment"},{option:"calc",kind:"calc"},{option:"file",kind:"structure",command:["products","save"]}];function mu(e,t,n){if(n===void 0)return!0;let o=e.slice(0,t).filter(i=>!i.startsWith("-"));return n.every((i,r)=>o[r]===i)}function Ir(e){for(let t=0;t<e.length;t++)for(let{option:n,kind:o,command:i}of du){let r=e[t]===`--${n}`?e[t+1]:e[t].startsWith(`--${n}=`)?e[t].slice(n.length+3):void 0;if(r!==void 0&&jt(r)&&mu(e,t,i))return{option:n,kind:o}}return null}async function Fe(e,t,n,o,i=cu,r=n){let s,a,l=t.trim(),u=l.startsWith("{")||l.startsWith("[");if(l==="-")s=await e.runtime.readStdin(i),a="standard input";else if(u)s=l,a=`the ${n} given`;else{if(!kr(l)&&jt(l))throw bn(n,e.runtime.platform,r);if(!kr(l)||!Sr(l).isFile())throw pt("file_not_found",`there is no file ${l}`,o);if(Sr(l).size>i)throw pt("file_too_large",`${l} is larger than ${Math.round(i/(1024*1024))} MB`);s=uu(l,"utf8").replace(/^\uFEFF/,""),a=l}if(!s.trim())throw pt(`invalid_${n}`,`${a} is empty`,o);let c;try{c=JSON.parse(s)}catch{throw u&&jt(s)?bn(n,e.runtime.platform,r):pt(`invalid_${n}`,`${a} is not valid JSON; check its syntax`,o)}if(!w(c))throw pt(`invalid_${n}`,`${a} is not a JSON object`,o);return c}var Or={customer:["customer","customers"],deal:["deal","deals"],person:["person","people"],insight:["insight","insights"],context:["context record","context records"],conversation:["conversation","conversations"],usage:["usage week","usage weeks"]},vn=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);function G(e,t=2){return typeof e!="string"||!vn(Or,e)?t===1?"member":"members":Or[e][t===1?0:1]}function A(e){return typeof e!="number"||!Number.isFinite(e)?String(e):Number.isInteger(e)?k(e):new Intl.NumberFormat(void 0,{maximumFractionDigits:2}).format(e)}function F(e){return typeof e=="string"?JSON.stringify(e):Array.isArray(e)?e.map(F).join(", "):e==null?"empty":typeof e=="number"?A(e):typeof e=="object"?JSON.stringify(e):String(e)}function Ve(e,t){switch(e){case"exists":return"has a value";case"is_empty":return"is empty";case"within_last":return`within the last ${F(t)} days`;case"within_period":return`within this ${typeof t=="string"?t:F(t)} so far`;case"between":return Array.isArray(t)&&t.length===2?`between ${F(t[0])} and ${F(t[1])}`:`between ${F(t)}`;case"not_in":return`not in ${F(t)}`;default:return`${String(e)} ${F(t)}`}}var Ar={">=":"at least",">":"more than","<=":"at most","<":"fewer than","=":"exactly"},Tr={count_distinct:"distinct values of",sum:"sum of",avg:"average of",min:"smallest",max:"largest",median:"median of"};function xr(e){if(typeof e!="string")return String(e);let t=/^p(\d{1,2})$/.exec(e);return t?`${t[1]}th percentile of`:vn(Tr,e)?Tr[e]:e}function Pr(e){let t=w(e)&&Array.isArray(e.includes)?e.includes.filter(w):[];return new Map(t.filter(n=>typeof n.version_id=="string"&&typeof n.name=="string").map(n=>[n.version_id,n.name]))}function Ft(e,t=new Map){if(typeof e=="string")return e;if(!w(e))return"";let n=i=>Ft(i,t),o=(i,r)=>{let s=n(i),a=r==="and"?"or":"and";return w(i)&&Array.isArray(i[a])?`(${s})`:s};if(Array.isArray(e.and))return e.and.map(i=>o(i,"and")).join(" and ");if(Array.isArray(e.or))return e.or.map(i=>o(i,"or")).join(" or ");if("not"in e)return`not (${n(e.not)})`;if(typeof e.segment=="string")return`in segment ${JSON.stringify(t.get(e.segment)??e.segment)}`;if(typeof e.field=="string")return`${e.field} ${Ve(e.op,e.value)}`;if(typeof e.has=="string"){let i=w(e.count)?e.count:null;return`has ${typeof e.min=="number"&&e.min!==1?`at least ${F(e.min)} ${G(e.has)}`:i&&typeof i.op=="string"&&vn(Ar,i.op)?`${Ar[i.op]} ${F(i.value)} ${G(e.has)}`:`${/^[aeiou]/.test(G(e.has,1))?"an":"a"} ${G(e.has,1)}`}${e.where?` where ${n(e.where)}`:""}`}if(w(e.subtotal)){let i=e.subtotal,r=i.fn==="count"?`count of ${G(i.of)}`:`${xr(i.fn)} ${String(i.field??G(i.of))}`,s=i.where?` over ${G(i.of)} where ${n(i.where)}`:"";return`${r}${s} ${Ve(e.op,e.value)}`}if(typeof e.moved=="string"){let i=e.from!==void 0?` from ${F(e.from)}`:"";return`${e.moved} moved into ${F(e.into)}${i} ${Rr(e)}`}if(w(e.change)){let i=e.change,r=w(i.start)?` (start ${Ve(i.start.op,i.start.value)})`:"";return`change of ${String(i.field)} ${Rr(i)} ${Ve(e.op,e.value)}${r}`}if(e.usage==="event"){let i=e.within_last!==void 0?` in the last ${F(e.within_last)} days`:"";return`usage event ${F(e.event)} ${String(e.measure??"count")}${i} ${Ve(e.op,e.value)}`}return JSON.stringify(e)}function Rr(e){return e.within_period!==void 0?Ve("within_period",e.within_period):Ve("within_last",e.within_last)}function Nr(e,t=new Map){return w(e)?e.where===void 0?`all ${G(e.of)}`:Ft(e.where,t):"no rule"}function $n(e){if(!w(e))return"no calculation";if(e.fn==="ratio")return`share: ${$n(e.numerator)} / ${$n(e.denominator)}`;let t=e.fn==="count"?"count":`${xr(e.fn)} ${String(e.field)}`;return e.where?`${t} where ${Ft(e.where)}`:t}function Wt(e){if(!w(e)||e.fn!=="ratio"||!w(e.numerator)||!w(e.denominator))return!1;let[t,n]=[e.numerator,e.denominator];return t.fn==="count"&&n.fn==="count"||t.fn==="sum"&&n.fn==="sum"&&t.field===n.field}function Be(e,t,n,o){if(typeof t!="number"||!Number.isFinite(t))return null;if(w(e)&&e.fn==="ratio"){let i=typeof n=="number"&&typeof o=="number"?` (${A(n)} / ${A(o)})`:"";return Wt(e)?`${(t*100).toFixed(1)}%${i}`:`${A(t)}${i}`}return A(t)}var Er={crm:"CRM",insights:"insights",usage:"product usage"},Cn=e=>typeof e=="string"&&vn(Er,e)?Er[e]:String(e);function Ut(e){let t=Array.isArray(e)?e.filter(n=>typeof n=="string"):[];return t.length?`the data is late (${t.map(Cn).join(", ")}), so this number may be out of date`:null}function kn(e){if(typeof e=="string")return e;if(!w(e))return JSON.stringify(e);if(typeof e.message=="string")return e.message;let t=JSON.stringify(e),o=(Array.isArray(e.suggestions)?e.suggestions:[]).map(i=>w(i)?`${F(i.value)}${typeof i.count=="number"?` (${k(i.count)})`:""}`:F(i));return o.length?`${t} Closest: ${o.join(", ")}.`:t}function Lr(e){return Array.isArray(e)?e.filter(w).filter(t=>typeof t.field=="string"&&typeof t.instead_of=="string"):[]}var No=e=>typeof e=="number"?k(e):"no number";function Sn(e,t){let n=Lr(e);return n.length?`If the request did not say which field it means, ask the user to choose before saving: ${n.map(i=>`${String(i.instead_of)} gives ${No(t)}, ${String(i.field)} gives ${No(i.value)}${typeof i.reason=="string"&&i.reason?` (${i.reason})`:""}`).join("; ")}.`:null}function On(e,t){return Lr(t).map(n=>e.dim(`With ${String(n.field)} instead of ${String(n.instead_of)}: ${n.value===null||n.value===void 0?`no number${typeof n.reason=="string"&&n.reason?` (${n.reason})`:""}`:No(n.value)}. They are often confused; say which one you mean.`))}var Do="Insights are filed against the products, areas and features you confirm here.",Fr="An area is a part of a product; a feature is a part of the product that exists, never planned work.",Se="closedloop products confirm",Dr="closedloop products",Rn="closedloop products save --file <structure.json>",pu=10*1024*1024,An="Start from the current structure: closedloop products --json > structure.json";function gu(e){let t=e.structure;if(!t||!Array.isArray(t.products)||!Array.isArray(t.areas)||!Array.isArray(t.features))throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);let n=Array.isArray(e.suggested_products)?e.suggested_products:[];return{...e,structure:t,suggested_products:n,can_change:e.can_change===!0}}async function gt(e,t){let n=await $(e,t,"GET","/api/product-structure");if(n.status!==200)throw C(n,"api_error","The product structure could not be loaded. Try again.");return gu(n.body)}var Wr=e=>typeof e=="number"?` \xB7 ${k(e)} insight${e===1?"":"s"}`:"",Tn=(e,t,n,o)=>typeof n=="string"&&n?[t,R(e,`${o} `,n)]:[t],Lo=(e,t,n)=>(t??[]).flatMap(o=>Tn(e,`${n}Feature: ${o.name}${Wr(o.insight_count)}`,o.id,n)),jr=(e,t,n)=>(t??[]).flatMap(o=>[...Tn(e,`${n}Area: ${o.name}${Wr(o.insight_count)}`,o.id,n),...Lo(e,o.features,`${n} `)]);function hu(e,t){let{structure:n,suggested_products:o}=e,i=[Do];if(n.products.length+n.areas.length+n.features.length===0&&o.length===0)return i.push("",`No products yet. Suggestions appear once your company research is done; see: closedloop company. Or add your own: ${Rn}`),i.join(`
38
+ `);for(let s of n.products)i.push("",...Tn(t,`Product: ${s.name}`,s.id,""),...jr(t,s.areas," "),...Lo(t,s.features," "));return n.areas.length+n.features.length>0&&i.push("","Under no product",...jr(t,n.areas," "),...Lo(t,n.features," ")),n.products.length===0&&i.push("","No products confirmed yet."),o.length>0&&i.push("","Suggested by your company research, not confirmed yet:",...o.flatMap(s=>Tn(t,` ${s.name}`,s.id," ")),`Confirm them all with: ${Se}; or only some, by their ids: ${Se} <id>...`),e.can_change||i.push("","Only an owner or admin of your workspace can change it."),i.join(`
39
+ `)}async function fu(e){let t=v(e.runtime);e.print.result(await gt(e.runtime,t),n=>hu(n,e.print.style))}var yu={product_area:["area","product"],area_feature:["feature","area"],product_feature:["feature","product"]};function wu(e,t){if(e.action==="link"||e.action==="unlink"){let[o,i]=yu[e.level]??["item","item"];return e.action==="link"?`Put ${o} "${e.child}" under ${i} "${e.parent}"`:`Take ${o} "${e.child}" out of ${i} "${e.parent}"`}let n=`${e.level} "${e.name}"`;switch(e.action){case"create":return`Add ${n}`;case"restore":return e.id&&t.has(e.id)?`Confirm the suggested ${n}`:`Restore ${n}`;case"update":return`Change ${n}${e.fields?.length?` (${e.fields.map(o=>o.replace(/_/g," ")).join(", ")})`:""}`;case"archive":return`Archive ${n}`;default:return`${e.action} ${n}`}}function Ur(e){if(e.length===0)return new p("invalid_structure","the structure has problems; nothing was saved",g.Failure,An);let t=e.map(n=>`${n.path}: ${n.message}`).join(`
39
40
  `);return new p("invalid_structure",`nothing was saved; fix ${e.length===1?"this":`these ${e.length}`}:
40
- ${t}`,g.Failure,In)}async function vu(e,t,n){let o=await v(e,t,"POST","/api/product-structure/check",{structure:n});if(o.status!==200||typeof o.body.valid!="boolean")throw C(o,"api_error","The product structure could not be checked. Try again.");let i=o.body;if(!i.valid||typeof i.check_token!="string")throw Wr(Array.isArray(i.problems)?i.problems:[]);return{...i,changes:Array.isArray(i.changes)?i.changes:[],warnings:Array.isArray(i.warnings)?i.warnings:[]}}var Cu={structure_changed:"Run the same command again: it checks the structure as it is now.",recalculation_running:"Run the same command again in a few minutes.",owner_or_admin_required:"Ask an owner or admin of your workspace to save it."};async function ku(e,t,n,o){let i=await v(e,t,"POST","/api/product-structure/save",{structure:n,check_token:o});if(i.status===400&&Array.isArray(i.body.problems))throw Wr(i.body.problems);if(i.status!==200||typeof i.body.status!="string"){let s=C(i,"api_error","The product structure could not be saved. Try again."),a=Cu[s.code];throw a?new p(s.code,s.message,s.exitCode,a):s}let r=i.body;return{status:r.status,changes:Array.isArray(r.changes)?r.changes:[],warnings:Array.isArray(r.warnings)?r.warnings:[],recalculation:typeof r.recalculation=="string"?r.recalculation:null}}var Su=e=>/^(y|yes)$/i.test(e.trim());async function Mr(e,t,n,o){let{runtime:i,interactive:r,print:s}=e,a=await vu(i,t,n),l=typeof a.estimated_credits=="number"?a.estimated_credits:null,u=(y,_)=>[...y.map(S=>` ${$u(S,o.suggestedIds)}`),..._.map(S=>` Note: ${S.message}`)],c=[...u(a.changes,a.warnings),...l?[`After a save, the next nightly recalculation re-places needs and insights, estimated at ${k(l)} credits.`]:[]].join(`
41
- `),d={...o.extra,changes:a.changes,warnings:a.warnings,estimated_credits:l};if(a.changes.length===0){s.result({...d,status:"unchanged",recalculation:null,instructions:null},()=>"Nothing to change: the product structure is already like this. Nothing was saved.");return}if(o.checkOnly){s.result({...d,status:"checked",recalculation:null,instructions:null},()=>`Checked, nothing saved. Saving it would:
42
- ${c}`);return}let m=o.yes,h=!1;if(!m&&r&&(s.note(`Saving it would:
43
- ${c}`),h=!0,m=Su(await i.prompt('Save these changes? Type "yes" to save: '))),!m){let y=r?"Nothing was saved.":`Nothing was saved: only the person who uses this workspace can agree to its product structure. Has your user agreed to the changes listed here? Insights are filed against the products, areas and features confirmed here. If they have not, show them the changes and ask first. Once they say yes, run: ${o.again} --yes`;s.result({...d,status:"not_saved",recalculation:null,instructions:y},_=>h?_.instructions:`Saving it would:
41
+ ${t}`,g.Failure,An)}async function _u(e,t,n){let o=await $(e,t,"POST","/api/product-structure/check",{structure:n});if(o.status!==200||typeof o.body.valid!="boolean")throw C(o,"api_error","The product structure could not be checked. Try again.");let i=o.body;if(!i.valid||typeof i.check_token!="string")throw Ur(Array.isArray(i.problems)?i.problems:[]);return{...i,changes:Array.isArray(i.changes)?i.changes:[],warnings:Array.isArray(i.warnings)?i.warnings:[]}}var bu={structure_changed:"Run the same command again: it checks the structure as it is now.",recalculation_running:"Run the same command again in a few minutes.",owner_or_admin_required:"Ask an owner or admin of your workspace to save it."};async function $u(e,t,n,o){let i=await $(e,t,"POST","/api/product-structure/save",{structure:n,check_token:o});if(i.status===400&&Array.isArray(i.body.problems))throw Ur(i.body.problems);if(i.status!==200||typeof i.body.status!="string"){let s=C(i,"api_error","The product structure could not be saved. Try again."),a=bu[s.code];throw a?new p(s.code,s.message,s.exitCode,a):s}let r=i.body;return{status:r.status,changes:Array.isArray(r.changes)?r.changes:[],warnings:Array.isArray(r.warnings)?r.warnings:[],recalculation:typeof r.recalculation=="string"?r.recalculation:null}}var vu=e=>/^(y|yes)$/i.test(e.trim());async function Hr(e,t,n,o){let{runtime:i,interactive:r,print:s}=e,a=await _u(i,t,n),l=typeof a.estimated_credits=="number"?a.estimated_credits:null,u=(y,_)=>[...y.map(S=>` ${wu(S,o.suggestedIds)}`),..._.map(S=>` Note: ${S.message}`)],c=[...u(a.changes,a.warnings),...l?[`After a save, the next nightly recalculation re-places needs and insights, estimated at ${k(l)} credits.`]:[]].join(`
42
+ `),m={...o.extra,changes:a.changes,warnings:a.warnings,estimated_credits:l};if(a.changes.length===0){s.result({...m,status:"unchanged",recalculation:null,instructions:null},()=>"Nothing to change: the product structure is already like this. Nothing was saved.");return}if(o.checkOnly){s.result({...m,status:"checked",recalculation:null,instructions:null},()=>`Checked, nothing saved. Saving it would:
43
+ ${c}`);return}let d=o.yes,h=!1;if(!d&&r&&(s.note(`Saving it would:
44
+ ${c}`),h=!0,d=vu(await i.prompt('Save these changes? Type "yes" to save: '))),!d){let y=r?"Nothing was saved.":`Nothing was saved: only the person who uses this workspace can agree to its product structure. Has your user agreed to the changes listed here? Insights are filed against the products, areas and features confirmed here. If they have not, show them the changes and ask first. Once they say yes, run: ${o.again} --yes`;s.result({...m,status:"not_saved",recalculation:null,instructions:y},_=>h?_.instructions:`Saving it would:
44
45
  ${c}
45
- ${_.instructions}`);return}let f=await ku(i,t,n,a.check_token);s.result({...d,status:f.status,changes:f.changes,warnings:f.warnings,recalculation:f.recalculation,instructions:null},y=>{if(y.status!=="saved")return"Nothing changed, so nothing was saved.";let _=h?["Saved the product structure."]:["Saved the product structure:",...u(y.changes,y.warnings)];return y.recalculation&&_.push(y.recalculation),_.join(`
46
- `)})}var Hr=e=>/^[\w.@-]+$/.test(e)?e:`"${e.replace(/(["\\$`])/g,"\\$1")}"`,jr=e=>e.trim().toLowerCase();async function Au(e,t,n){let o=$(e.runtime),i=await gt(e.runtime,o),r=i.suggested_products;if(r.length===0){let u=i.structure.products.length===0?`Nothing is suggested yet. Suggestions appear once your company research is done; see: closedloop company. Or add your own: ${Pn}`:"Nothing is suggested right now. See your products with: closedloop products";e.print.result({status:"nothing_suggested",confirmed:[],changes:[],instructions:null},()=>u);return}let s=[];for(let u of t){let c=r.find(d=>jr(d.name)===jr(u)||d.id===u.trim());if(!c)throw new p("unknown_product",`"${u}" is not a suggested product; the suggested ones are: ${r.map(d=>d.name).join(", ")}`,g.Usage,`Confirm them all with: ${ke}`);s.includes(c)||s.push(c)}let a=t.length===0?r:s,l={...i.structure,products:[...i.structure.products,...a.map(({id:u,name:c,description:d})=>({id:u,name:c,description:d}))]};await Mr(e,o,l,{yes:n,checkOnly:!1,again:t.length===0?ke:`${ke} ${t.map(Hr).join(" ")}`,suggestedIds:new Set(a.map(u=>u.id)),extra:{confirmed:a.map(u=>u.name)}})}async function Ou(e,t){if(t.file===void 0||!t.file.trim())throw new p("missing_file","give the structure with --file <path>, or --file - to read standard input",g.Usage,In);let n=await De(e,t.file,"structure",In,fu,"file"),o=w(n.structure)?n.structure:n,i=Array.isArray(n.suggested_products)?n.suggested_products.filter(w):[],r=$(e.runtime);await Mr(e,r,o,{yes:t.yes===!0,checkOnly:t.check===!0,again:`closedloop products save --file ${Hr(t.file.trim())}`,suggestedIds:new Set(i.map(s=>String(s.id))),extra:{}})}var Gr=(e,t)=>{let n=e.command("products").description("Your product structure: your products, their areas (the parts of a product) and features (parts of the product that exist, never planned work). Insights are filed against them. Shows it; confirm the products your company research suggested, or save your own. See also: closedloop glossary").action(async()=>_u(t));n.command("confirm").argument("[name...]","The suggested products to confirm, by name; all of them when left out.").description("Confirm the products your company research suggested, so insights are filed against them. Shows the changes and asks before saving (an owner or admin).").option("--yes","Save without asking. An agent adds it only after its user agreed to the changes.").action(async(o,i)=>Au(t,o,i.yes===!0)),n.command("save").description("Save a product structure from a JSON file: the output of `closedloop products --json`, edited, or only its structure: { products: [{ id?, name, description?, areas: [{ id?, name, description?, owner_email?, features: [{ id?, name, description? }] }], features: [...] }], areas: [...], features: [...] }. Products, areas and features left out are archived, never deleted; a field or list left out of an item keeps what is saved, and an empty list removes its items. Shows the changes and asks before saving (an owner or admin).").option("--file <path>","The JSON file, or - to read standard input.").option("--check","Only check it: show the changes or problems and save nothing.").option("--yes","Save without asking. An agent adds it only after its user agreed to the changes.").action(async o=>Ou(t,o))};var te="closedloop company --website <url>",Wt="ClosedLoop AI reads your website to learn your company, its products and its market. That is the basis for the suggested product structure and for analysing your insights, and it usually takes a few minutes.",qr="It reads your website to learn your company and usually takes a few minutes.",Tu="closedloop company --research";var Br="closedloop company --no-domains --confirmed-by-user",Ho="Without them ClosedLoop AI cannot tell colleagues from customers, so everything a colleague says on a call or in an email would be analysed as customer feedback and the results would be wrong.",Ru=3600,Jr=30,Eu=new Set(["waiting","researching","retrying"]);function Go(e){let t=e.research;if(!t||typeof t.state!="string")throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);return e}async function Be(e,t,n=!1){let o=await v(e,t,"GET",n?"/api/cli/company?include=research":"/api/cli/company");if(o.status!==200)throw C(o,"api_error","The company profile could not be loaded. Try again.");return Go(o.body)}async function Ye(e,t){try{return await Be(e,t)}catch(n){if(n instanceof p)return null;throw n}}function xn(e){return!!(e?.email_domains&&!e.email_domains.confirmed)}function Mt(e){return!e||!e.analysis_waiting?null:e.research.state==="no_website"?`Nothing is analysed until your company website is set: ${te}`:e.research.state!=="ready"?"It is analysed once your company research is done; see: closedloop company":xn(e)?"Nothing is analysed until your company's email domains are confirmed; see: closedloop company":"It is analysed soon; see: closedloop company"}var ht=e=>e.join(", ");function Ht(e){if(!xn(e))return null;let t=e.email_domains.suggested,n=t.length>0?`Ask your user whether their colleagues' work email addresses end in ${ht(t.map(i=>`@${i}`))}, and whether there are others.`:"Ask your user which domains their colleagues' work email addresses end in (the part after the @).",o=t.length>0?t.join(","):"<domain,...>";return[`ClosedLoop AI needs your user to confirm their company's email domains before it analyses anything. ${Ho}`,n,`When they have answered, run: closedloop company --domains ${o} --confirmed-by-user (with the domains they gave).`,`If their company has no email domain of its own (everyone uses a personal address such as Gmail), explain to them that every colleague will then count as a customer and the results will be wrong, and ask them to confirm they understand. Only after they say yes, run: ${Br}`,"Do not run either command, and do not add --confirmed-by-user, without your user's answer."].join(" ")}function qo(e){let t=e.email_domains;return t?t.confirmed?t.domains.length>0?`Email domains: ${ht(t.domains)}.`:"Email domains: none (confirmed); colleagues count as customers.":`Email domains: not confirmed yet, so nothing is analysed.${t.suggested.length>0?` Suggested: ${ht(t.suggested)}.`:""} Confirm with: closedloop company --domains ${t.suggested.length>0?t.suggested.join(","):"<domain,...>"}`:null}async function Mo(e,t,n){let i=await v(e,t,"PUT","/api/cli/company/domains",n===null?{no_domains:!0,confirmed_by_user:!0}:{domains:n,confirmed_by_user:!0});if(i.status!==200)throw C(i,"api_error","The email domains could not be saved. Try again.");return Go(i.body)}function Yr(e){return e.split(/[\s,]+/).map(t=>t.trim().replace(/^@/,"").toLowerCase()).filter(Boolean)}var Kr=e=>/^(y|yes)$/i.test(e.trim());async function zr(e,t,n){let{runtime:o,print:i}=e,r=n.email_domains?.suggested??[];i.note(`Your colleagues' email domains tell ClosedLoop AI who is a colleague and who is a customer. ${Ho}`);let s=r.length>0?`Your company's email domains, separated by commas [${ht(r)}]: `:`Your company's email domains, separated by commas (e.g. acme.example), or "none" if it has none: `,a=(await o.prompt(s)).trim();if(/^none$/i.test(a)||!a&&r.length===0){if(!a)return null;let u=await o.prompt('Without email domains every colleague counts as a customer and the results will be wrong. Type "yes" to confirm your company has none: ');return Kr(u)?Mo(o,t,null):null}let l=a?Yr(a):r;return Mo(o,t,l)}async function Jo(e,t,n){let o=await v(e,t,"PUT","/api/cli/company/website",{website:n.trim()});if(o.status!==200)throw C(o,"api_error","The website could not be saved. Try again.");return Go(o.body)}function ft(e,t){let n=e.research;switch(n.state){case"no_website":return`No company website yet. Your data is imported, but nothing is analysed until ClosedLoop AI has researched your company. Set it with: ${te}`;case"waiting":return`Company research: waiting to start. ${qr} Your data is analysed once it is done.`;case"researching":return`Company research: in progress${n.started_at?`, started ${mt(t-Date.parse(n.started_at))} ago`:""}. ${qr} Your data is analysed once it is done.`;case"retrying":return`Company research: the last try failed${n.next_try_at?`; it tries again around ${R(n.next_try_at)}`:""}. Your data is analysed once it is done.`;case"failed":return`Company research keeps failing${n.failed_at?` (last at ${R(n.failed_at)})`:""}; it is tried again about half an hour after each failure. If the address is wrong, set the right one: ${te}. Your data is analysed once research is done.`;case"ready":return`Company research: done${n.researched_at?` (${R(n.researched_at)})`:""}.${e.analysis_waiting?"":" Your data is being analysed."}`;default:return`Company research: ${String(n.state)}. ${e.analysis_waiting?"Your data is analysed once it is done.":"Your data is being analysed."}`}}function Iu(e){let t=e.research.state==="ready"?e.research.summary:null;if(!t)return[];let n=[];return t.description&&n.push(`What it learned: ${t.description}`),t.covers.length>0&&n.push(`It also covers: ${t.covers.join(", ")}.`),n.push(`See all of it: ${Tu}`),n}function Pu(e){return e===null||e<=0?null:e===1?`Next: confirm the product it suggested: ${ke}`:`Next: confirm the ${e} products it suggested: ${ke}`}function Vo(e,t,n=null){let o=[];e.website&&o.push(`Website: ${e.website}`),o.push(ft(e,t));let i=qo(e);i&&o.push(i),e.research.state==="ready"&&e.research.researched_url&&e.website&&e.research.researched_url!==e.website&&o.push(`The profile was researched from ${e.research.researched_url}.`),o.push(...Iu(e));let r=e.research.state==="ready"?Pu(n):null;return r&&o.push(r),o.join(`
47
- `)}async function xu(e,t){try{return(await gt(e,t)).suggested_products.length}catch(n){if(n instanceof p)return null;throw n}}function Nu(e,t){let n=e.research;if(n.state!=="ready")return`${ft(e,t)}
46
+ ${_.instructions}`);return}let f=await $u(i,t,n,a.check_token);s.result({...m,status:f.status,changes:f.changes,warnings:f.warnings,recalculation:f.recalculation,instructions:null},y=>{if(y.status!=="saved")return"Nothing changed, so nothing was saved.";let _=h?["Saved the product structure."]:["Saved the product structure:",...u(y.changes,y.warnings)];return y.recalculation&&_.push(y.recalculation),_.join(`
47
+ `)})}var Cu=e=>/^[\w.@-]+$/.test(e)?e:`"${e.replace(/(["\\$`])/g,"\\$1")}"`;async function ku(e,t,n){let o=t.map(c=>I(c,"product",Dr)),i=v(e.runtime),r=await gt(e.runtime,i),s=r.suggested_products;if(s.length===0){let c=r.structure.products.length===0?`Nothing is suggested yet. Suggestions appear once your company research is done; see: closedloop company. Or add your own: ${Rn}`:"Nothing is suggested right now. See your products with: closedloop products";e.print.result({status:"nothing_suggested",confirmed:[],confirmed_ids:[],changes:[],instructions:null},()=>c);return}let a=[];for(let c of o){let m=s.find(d=>d.id.toLowerCase()===c);if(!m)throw new p("unknown_product",`${c} is not a suggested product`,g.Usage,`See the suggested products and their ids with: ${Dr}`);a.includes(m)||a.push(m)}let l=o.length===0?s:a,u={...r.structure,products:[...r.structure.products,...l.map(({id:c,name:m,description:d})=>({id:c,name:m,description:d}))]};await Hr(e,i,u,{yes:n,checkOnly:!1,again:o.length===0?Se:`${Se} ${l.map(c=>c.id).join(" ")}`,suggestedIds:new Set(l.map(c=>c.id)),extra:{confirmed:l.map(c=>c.name),confirmed_ids:l.map(c=>c.id)}})}async function Su(e,t){if(t.file===void 0||!t.file.trim())throw new p("missing_file","give the structure with --file <path>, or --file - to read standard input",g.Usage,An);let n=await Fe(e,t.file,"structure",An,pu,"file"),o=w(n.structure)?n.structure:n,i=Array.isArray(n.suggested_products)?n.suggested_products.filter(w):[],r=v(e.runtime);await Hr(e,r,o,{yes:t.yes===!0,checkOnly:t.check===!0,again:`closedloop products save --file ${Cu(t.file.trim())}`,suggestedIds:new Set(i.map(s=>String(s.id))),extra:{}})}var Mr=(e,t)=>{let n=e.command("products").description("Your product structure: your products, their areas (the parts of a product) and features (parts of the product that exist, never planned work). Insights are filed against them. Shows it, each with its id; confirm the products your company research suggested, or save your own. See also: closedloop glossary").action(async()=>fu(t));n.command("confirm").argument("[id...]","The ids of the suggested products to confirm, from closedloop products; all of them when left out.").description("Confirm the products your company research suggested, so insights are filed against them. Shows the changes and asks before saving (an owner or admin).").option("--yes","Save without asking. An agent adds it only after its user agreed to the changes.").action(async(o,i)=>ku(t,o,i.yes===!0)),n.command("save").description("Save a product structure from a JSON file: the output of `closedloop products --json`, edited, or only its structure: { products: [{ id?, name, description?, areas: [{ id?, name, description?, owner_email?, features: [{ id?, name, description? }] }], features: [...] }], areas: [...], features: [...] }. Keep each id from that output: the id says which product, area or feature an entry is, and a new one has none. Products, areas and features left out are archived, never deleted; a field or list left out of an item keeps what is saved, and an empty list removes its items. Shows the changes and asks before saving (an owner or admin).").option("--file <path>","The JSON file, or - to read standard input.").option("--check","Only check it: show the changes or problems and save nothing.").option("--yes","Save without asking. An agent adds it only after its user agreed to the changes.").action(async o=>Su(t,o))};var Z="closedloop company --website <url>",Ht="ClosedLoop AI reads your website to learn your company, its products and its market. That is the basis for the suggested product structure and for analysing your insights, and it usually takes a few minutes.",Gr="It reads your website to learn your company and usually takes a few minutes.",Ou="closedloop company --research";var Vr="closedloop company --no-domains --confirmed-by-user",Fo="Without them ClosedLoop AI cannot tell colleagues from customers, so everything a colleague says on a call or in an email would be analysed as customer feedback and the results would be wrong.",Au=3600,qr=30,Tu=new Set(["waiting","researching","retrying"]);function Wo(e){let t=e.research;if(!t||typeof t.state!="string")throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);return e}async function Ye(e,t,n=!1){let o=await $(e,t,"GET",n?"/api/cli/company?include=research":"/api/cli/company");if(o.status!==200)throw C(o,"api_error","The company profile could not be loaded. Try again.");return Wo(o.body)}async function Ke(e,t){try{return await Ye(e,t)}catch(n){if(n instanceof p)return null;throw n}}function En(e){return!!(e?.email_domains&&!e.email_domains.confirmed)}function Mt(e){return!e||!e.analysis_waiting?null:e.research.state==="no_website"?`Nothing is analysed until your company website is set: ${Z}`:e.research.state!=="ready"?"It is analysed once your company research is done; see: closedloop company":En(e)?"Nothing is analysed until your company's email domains are confirmed; see: closedloop company":"It is analysed soon; see: closedloop company"}var ht=e=>e.join(", ");function Gt(e){if(!En(e))return null;let t=e.email_domains.suggested,n=t.length>0?`Ask your user whether their colleagues' work email addresses end in ${ht(t.map(i=>`@${i}`))}, and whether there are others.`:"Ask your user which domains their colleagues' work email addresses end in (the part after the @).",o=t.length>0?t.join(","):"<domain,...>";return[`ClosedLoop AI needs your user to confirm their company's email domains before it analyses anything. ${Fo}`,n,`When they have answered, run: closedloop company --domains ${o} --confirmed-by-user (with the domains they gave).`,`If their company has no email domain of its own (everyone uses a personal address such as Gmail), explain to them that every colleague will then count as a customer and the results will be wrong, and ask them to confirm they understand. Only after they say yes, run: ${Vr}`,"Do not run either command, and do not add --confirmed-by-user, without your user's answer."].join(" ")}function Uo(e){let t=e.email_domains;return t?t.confirmed?t.domains.length>0?`Email domains: ${ht(t.domains)}.`:"Email domains: none (confirmed); colleagues count as customers.":`Email domains: not confirmed yet, so nothing is analysed.${t.suggested.length>0?` Suggested: ${ht(t.suggested)}.`:""} Confirm with: closedloop company --domains ${t.suggested.length>0?t.suggested.join(","):"<domain,...>"}`:null}async function jo(e,t,n){let i=await $(e,t,"PUT","/api/cli/company/domains",n===null?{no_domains:!0,confirmed_by_user:!0}:{domains:n,confirmed_by_user:!0});if(i.status!==200)throw C(i,"api_error","The email domains could not be saved. Try again.");return Wo(i.body)}function Br(e){return e.split(/[\s,]+/).map(t=>t.trim().replace(/^@/,"").toLowerCase()).filter(Boolean)}var Yr=e=>/^(y|yes)$/i.test(e.trim());async function Kr(e,t,n){let{runtime:o,print:i}=e,r=n.email_domains?.suggested??[];i.note(`Your colleagues' email domains tell ClosedLoop AI who is a colleague and who is a customer. ${Fo}`);let s=r.length>0?`Your company's email domains, separated by commas [${ht(r)}]: `:`Your company's email domains, separated by commas (e.g. acme.example), or "none" if it has none: `,a=(await o.prompt(s)).trim();if(/^none$/i.test(a)||!a&&r.length===0){if(!a)return null;let u=await o.prompt('Without email domains every colleague counts as a customer and the results will be wrong. Type "yes" to confirm your company has none: ');return Yr(u)?jo(o,t,null):null}let l=a?Br(a):r;return jo(o,t,l)}async function Ho(e,t,n){let o=await $(e,t,"PUT","/api/cli/company/website",{website:n.trim()});if(o.status!==200)throw C(o,"api_error","The website could not be saved. Try again.");return Wo(o.body)}function ft(e,t){let n=e.research;switch(n.state){case"no_website":return`No company website yet. Your data is imported, but nothing is analysed until ClosedLoop AI has researched your company. Set it with: ${Z}`;case"waiting":return`Company research: waiting to start. ${Gr} Your data is analysed once it is done.`;case"researching":return`Company research: in progress${n.started_at?`, started ${dt(t-Date.parse(n.started_at))} ago`:""}. ${Gr} Your data is analysed once it is done.`;case"retrying":return`Company research: the last try failed${n.next_try_at?`; it tries again around ${E(n.next_try_at)}`:""}. Your data is analysed once it is done.`;case"failed":return`Company research keeps failing${n.failed_at?` (last at ${E(n.failed_at)})`:""}; it is tried again about half an hour after each failure. If the address is wrong, set the right one: ${Z}. Your data is analysed once research is done.`;case"ready":return`Company research: done${n.researched_at?` (${E(n.researched_at)})`:""}.${e.analysis_waiting?"":" Your data is being analysed."}`;default:return`Company research: ${String(n.state)}. ${e.analysis_waiting?"Your data is analysed once it is done.":"Your data is being analysed."}`}}function Ru(e){let t=e.research.state==="ready"?e.research.summary:null;if(!t)return[];let n=[];return t.description&&n.push(`What it learned: ${t.description}`),t.covers.length>0&&n.push(`It also covers: ${t.covers.join(", ")}.`),n.push(`See all of it: ${Ou}`),n}function Eu(e){return e===null||e<=0?null:e===1?`Next: confirm the product it suggested: ${Se}`:`Next: confirm the ${e} products it suggested: ${Se}`}function Mo(e,t,n=null){let o=[];e.website&&o.push(`Website: ${e.website}`),o.push(ft(e,t));let i=Uo(e);i&&o.push(i),e.research.state==="ready"&&e.research.researched_url&&e.website&&e.research.researched_url!==e.website&&o.push(`The profile was researched from ${e.research.researched_url}.`),o.push(...Ru(e));let r=e.research.state==="ready"?Eu(n):null;return r&&o.push(r),o.join(`
48
+ `)}async function Iu(e,t){try{return(await gt(e,t)).suggested_products.length}catch(n){if(n instanceof p)return null;throw n}}function xu(e,t){let n=e.research;if(n.state!=="ready")return`${ft(e,t)}
48
49
  The research text appears here once it is done.`;if(typeof n.text!="string"||!n.text.trim())return`${ft(e,t)}
49
- ClosedLoop AI did not send the research text. Try again later.`;let o=n.researched_url??e.website,i=n.researched_at?`, ${R(n.researched_at)}`:"";return`What the company research found${o?` (from ${o}${i})`:""}:
50
+ ClosedLoop AI did not send the research text. Try again later.`;let o=n.researched_url??e.website,i=n.researched_at?`, ${E(n.researched_at)}`:"";return`What the company research found${o?` (from ${o}${i})`:""}:
50
51
 
51
- ${n.text.trim()}`}async function Vr(e,t,n,o){let i=n,r=e.print.spinner("Checking every 30 seconds\u2026");try{for(let s=0;s<Ru&&Eu.has(i.research.state);s+=Jr)r.update(`${ft(i,Date.now())} Checking every 30 seconds\u2026`),await e.runtime.sleep(Jr*1e3),i=await Be(e.runtime,t,o)}finally{r.stop()}return i}async function Lu(e,t,n){let{runtime:o,interactive:i}=e,r=n.domains===!1?null:Yr(String(n.domains));if(r!==null&&r.length===0)throw new p("invalid_domains","--domains needs the company's email domains, e.g. --domains acme.example",g.Usage);let s=n.confirmedByUser===!0;if(!s&&i){let a=r===null?'Without email domains every colleague counts as a customer and the results will be wrong. Type "yes" to confirm your company has none: ':`Are these your colleagues' email domains: ${ht(r)}? Type "yes" to save them: `;s=Kr(await o.prompt(a))}if(!s){let a=await Be(o,t),l=r===null?"that the company has no email domain of its own":`that their colleagues' email domains are ${ht(r)}`,u=r===null?Br:`closedloop company --domains ${r.join(",")} --confirmed-by-user`,c=i?"Nothing was saved.":`Nothing was saved: only the person who uses this workspace can confirm the email domains. Has your user confirmed ${l}? ${Ho} If they have not, ask them first. Once they say yes, run: ${u}`;return{profile:a,saved:!1,instructions:c}}return{profile:await Mo(o,t,r),saved:!0,instructions:null}}async function Du(e,t){let n=$(e.runtime);if(t.website!==void 0&&!t.website.trim())throw new p("invalid_website","--website needs the company website, e.g. https://acme.example",g.Usage);if(t.confirmedByUser&&t.domains===void 0)throw new p("invalid_options","--confirmed-by-user goes with --domains or --no-domains",g.Usage);if(t.research&&(t.website!==void 0||t.domains!==void 0))throw new p("invalid_options","--research only shows the research; set the website or the email domains without it",g.Usage);if(t.research){let l=await Be(e.runtime,n,!0);t.watch&&(l=await Vr(e,n,l,!0)),e.print.result(l,u=>Nu(u,Date.now()));return}let o=t.website!==void 0?await Jo(e.runtime,n,t.website):await Be(e.runtime,n),i=!1,r=null;if(t.domains!==void 0){let l=await Lu(e,n,t);({profile:o}=l),i=l.saved,r=l.instructions}t.watch&&(o=await Vr(e,n,o,!1)),!r&&!e.interactive&&(r=Ht(o));let s=o.research.state==="ready"?await xu(e.runtime,n):null,a={...o,email_domains_saved:i,instructions:r,products_suggested:s};e.print.result(a,l=>{let u=[];return t.website!==void 0&&u.push(`Website saved. ${Wt}`),l.email_domains_saved?u.push("Email domains saved."):t.domains!==void 0&&l.instructions&&u.push(l.instructions),u.push(Vo(l,Date.now(),l.products_suggested)),u.join(`
52
- `)})}var Xr=(e,t)=>{e.command("company").description("Your company website and the research of it, which comes before any analysis.").option("--website <url>","Set the company website, e.g. https://acme.example (an owner or admin).").option("--domains <list>","Confirm your colleagues' email domains, e.g. acme.example,acme.io (an owner or admin).").option("--no-domains","Confirm your company has no email domain of its own; colleagues then count as customers.").option("--confirmed-by-user","The person using this workspace confirmed the domains. An agent adds it only after its user said yes.").option("--watch","Keep checking every 30 seconds until the research is done (up to an hour).").option("--research","Show everything the company research found: the whole research text.").action(async n=>Du(t,n))};var ju="Insights that name one of these competitors count as competitor mentions, shown on opportunities, features, customer profiles and the weekly brief.",Qr="closedloop competitors add <name> [--website <url>]",Fu="Changing competitors needs the right to change the workspace's settings (an owner or admin).",Uu="It is then marked as not a competitor; the customer mentions behind it are kept, and adding it again restores it.",Wu={researched:"AI research",researched_and_mentioned:"AI research, mentioned by customers",mentioned:"mentioned by customers",edited:"manual"},yt=e=>e.trim().toLowerCase(),Mu=e=>/^(y|yes)$/i.test(e.trim()),Zr=e=>/^[\w.@-]+$/.test(e)?e:`"${e.replace(/(["\\$`])/g,"\\$1")}"`;function Nn(e,t,n){let o=e.status===403,i=C(e,o?"access_denied":"api_error",t);return o&&n?new p(i.code,i.message,g.Failure,Fu):i}var Ln=(e,t)=>`/api/competitors/${encodeURIComponent(e)}${t?`/${encodeURIComponent(t)}`:""}`;function Bo(e){let t=e;if(!t||typeof t.id!="string"||typeof t.competitor_company!="string")throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);return t}async function Yo(e,t,n){let o=await v(e,t,"GET",Ln(n));if(o.status!==200)throw Nn(o,"The competitors could not be loaded. Try again.",!1);if(!Array.isArray(o.body.competitors))throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);return o.body.competitors.map(Bo)}function es(e,t){let n=t.trim(),o=e.find(a=>a.id.toLowerCase()===n.toLowerCase());if(o)return o;let i=e.filter(a=>yt(a.competitor_company)===yt(n));if(i.length===1)return i[0];if(i.length>1)throw new p("ambiguous_competitor",`"${n}" matches more than one competitor: ${i.map(a=>`${a.competitor_company} (${a.id})`).join(", ")}`,g.Usage,"Name it by its id instead.");let r=n?e.filter(a=>yt(a.competitor_company).includes(yt(n))):[],s=r.length>0?`; competitors with that in their name: ${r.map(a=>a.competitor_company).join(", ")}`:"";throw new p("unknown_competitor",`"${n}" is not on your competitor list${s}`,g.Usage,"See the list with: closedloop competitors")}function ts(e){let t=[e.competitor_product&&yt(e.competitor_product)!==yt(e.competitor_company)?`${e.competitor_company} (${e.competitor_product})`:e.competitor_company];e.product_website&&t.push(e.product_website),e.is_direct_competitor===!0&&t.push("direct"),e.is_direct_competitor===!1&&t.push("indirect"),typeof e.mention_count=="number"&&e.mention_count>0&&t.push(`${k(e.mention_count)} mention${e.mention_count===1?"":"s"}`);let n=e.provenance?Wu[e.provenance]:void 0;return n&&t.push(n),t.join(" \xB7 ")}function Hu(e){let t=[ju,""];return e.competitors.length===0?t.push(`No competitors yet. Your company research adds the ones it finds; add your own with: ${Qr}`):(t.push(...e.competitors.map(n=>` ${ts(n)}`),""),t.push(`${k(e.competitors.length)} competitor${e.competitors.length===1?"":"s"}. Add one: ${Qr}. Change one: closedloop competitors edit <name> or remove <name>.`)),e.can_change||t.push("Only someone who can change the workspace's settings (an owner or admin) can change this list."),t.join(`
53
- `)}async function Gu(e){let t=$(e.runtime),n=await W(e.runtime,t),o=await Yo(e.runtime,t,n.id);e.print.result({competitors:o,can_change:n.permissions.can_manage_team_settings},Hu)}async function qu(e,t,n){let o=t.trim();if(!o)throw new p("missing_name",`give the competitor's name, e.g. closedloop competitors add "Acme Rival"`,g.Usage);let i=$(e.runtime),{id:r}=await W(e.runtime,i),s={competitor_company:o,is_direct_competitor:!0};n.website?.trim()&&(s.product_website=n.website.trim()),n.description?.trim()&&(s.description=n.description.trim());let a=await v(e.runtime,i,"POST",Ln(r),s);if(a.status!==200)throw Nn(a,"The competitor could not be added. Try again.",!0);let l=Bo(a.body.competitor);e.print.result({competitor:l},u=>`Added ${u.competitor.competitor_company} to your competitors.`)}async function Ju(e,t,n){if(n.name===void 0&&n.website===void 0&&n.description===void 0)throw new p("nothing_to_change","say what to change: --name, --website or --description",g.Usage);if(n.name!==void 0&&!n.name.trim())throw new p("missing_name","--name needs the new name; a competitor always has one",g.Usage);let o=$(e.runtime),{id:i}=await W(e.runtime,o),r=es(await Yo(e.runtime,o,i),t),s={};n.name!==void 0&&(s.competitor_company=n.name.trim()),n.website!==void 0&&(s.product_website=n.website.trim()||null),n.description!==void 0&&(s.description=n.description.trim()||null);let a=await v(e.runtime,o,"PUT",Ln(i,r.id),s);if(a.status!==200)throw Nn(a,"The competitor could not be saved. Try again.",!0);let l=Bo(a.body.competitor);e.print.result({competitor:l},u=>`Saved: ${ts(u.competitor)}`)}async function Vu(e,t,n){let{runtime:o,interactive:i,print:r}=e,s=$(o),{id:a}=await W(o,s),l=es(await Yo(o,s,a),t),u=l.competitor_company,c=n;if(!c&&i&&(r.note(`${u} will be marked as not a competitor and moved out of this list. The customer mentions behind it are kept, and adding it again restores it.`),c=Mu(await o.prompt(`Type "yes" to remove ${u}: `))),!c){let m=i?"Nothing was removed.":`Nothing was removed: only the person who uses this workspace can remove a competitor. Has your user agreed to remove ${u} from the competitor list? ${Uu} If they have not, ask them first. Once they say yes, run: closedloop competitors remove ${Zr(t.trim())} --yes`;r.result({status:"not_removed",competitor:l,instructions:m},h=>h.instructions);return}let d=await v(o,s,"DELETE",Ln(a,l.id));if(d.status!==200)throw Nn(d,"The competitor could not be removed. Try again.",!0);r.result({status:"removed",competitor:l,instructions:null},m=>`Removed ${m.competitor.competitor_company} from your competitors. The customer mentions behind it are kept; to restore it, run: closedloop competitors add ${Zr(m.competitor.competitor_company)}`)}var ns=(e,t)=>{let n=e.command("competitors").description("The competitors ClosedLoop AI watches for in your insights: the ones AI research found, the ones customers mention and the ones added by hand. Shows them; add, edit or remove one (someone who can change the workspace's settings).").action(async()=>Gu(t));n.command("add").argument("<name>","The competitor's name.").description("Add a competitor, as a direct competitor. Adding one that was removed restores it.").option("--website <url>","Its website, e.g. https://rival.example.").option("--description <text>","What it does, in a sentence.").action(async(o,i)=>qu(t,o,i)),n.command("edit").argument("<name-or-id>","The competitor, by name or id.").description("Change a competitor's name, website or description. Only what you give changes; an empty --website or --description clears it.").option("--name <name>","Its new name.").option("--website <url>","Its website.").option("--description <text>","What it does, in a sentence.").action(async(o,i)=>Ju(t,o,i)),n.command("remove").argument("<name-or-id>","The competitor, by name or id.").description("Remove a competitor from the list: it is marked as not a competitor, its customer mentions are kept, and adding it again restores it. Asks before removing.").option("--yes","Remove without asking. An agent adds it only after its user agreed.").action(async(o,i)=>Vu(t,o,i.yes===!0))};var os={event:"dated records, so a past value can be computed",customer_history:"from the customer history, so a past value can be computed",crm_history:"from the CRM's change history, so a past value can be computed",none:"none: a past value cannot be computed"},Bu=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),ne=e=>typeof e=="string"?e:"",Ko=e=>typeof e!="string"?String(e):/^\d{4}-\d{2}-\d{2}$/.test(e)?q(`${e}T00:00:00Z`):P(e);function Yu(e){if(typeof e.empty_share=="number")return`${Math.round(e.empty_share*100)}%`;let t=e.record_count,n=e.empty_count;return typeof t!="number"||typeof n!="number"||t<=0?null:`${Math.round(n/t*100)}%`}function Ku(e,t){return typeof e.distinct_count=="number"?Math.max(0,e.distinct_count-t):0}var ae=e=>e!=null,zu=e=>e.hidden!==!0&&(e.kind==="number"&&![e.min,e.max,e.p10,e.p90].some(ae)||e.kind==="date"&&![e.first,e.last,e.p10,e.p90].some(ae)),is=e=>String(e).replace(/\s+/g," ").trim(),zo=["customer","deal","person","insight","context","conversation","usage"],Xo="When two fields fit a request, ask the user which one they mean before writing the rule; never pick one silently.";function Xu(e){let t=o=>typeof o=="number"?O(o):Ko(o),n=typeof e.zero_share=="number"&&e.zero_share>0?[`${Math.round(e.zero_share*100)}% are 0`]:[];return ae(e.median)?[...n,`${n.length?"other values: ":""}10th percentile ${t(e.p10)}`,`median ${t(e.median)}`,`90th percentile ${t(e.p90)}`]:n}function ss(e,t){let n=ne(t.label)&&t.label!==t.key?` \xB7 ${t.label}`:"";return`${e.bold(ne(t.key))} ${e.dim(`${ne(t.type)}${n}`)}`}function Qu(e){return w(e.guide)&&w(e.guide.preview)&&e.guide.preview.kind==="empty"}function Zu(e,t,n,o){if(!t.length)return n?`No fields match "${n}".`:o?"No fields hold values yet.":"No fields yet.";let i=new Map;for(let s of t){let a=ne(s.entity)||"other";i.set(a,[...i.get(a)??[],s])}return`${[...i.entries()].map(([s,a])=>[e.bold(s),...a.map(l=>` ${ss(e,l)}`)].join(`
52
+ ${n.text.trim()}`}async function Jr(e,t,n,o){let i=n,r=e.print.spinner("Checking every 30 seconds\u2026");try{for(let s=0;s<Au&&Tu.has(i.research.state);s+=qr)r.update(`${ft(i,Date.now())} Checking every 30 seconds\u2026`),await e.runtime.sleep(qr*1e3),i=await Ye(e.runtime,t,o)}finally{r.stop()}return i}async function Pu(e,t,n){let{runtime:o,interactive:i}=e,r=n.domains===!1?null:Br(String(n.domains));if(r!==null&&r.length===0)throw new p("invalid_domains","--domains needs the company's email domains, e.g. --domains acme.example",g.Usage);let s=n.confirmedByUser===!0;if(!s&&i){let a=r===null?'Without email domains every colleague counts as a customer and the results will be wrong. Type "yes" to confirm your company has none: ':`Are these your colleagues' email domains: ${ht(r)}? Type "yes" to save them: `;s=Yr(await o.prompt(a))}if(!s){let a=await Ye(o,t),l=r===null?"that the company has no email domain of its own":`that their colleagues' email domains are ${ht(r)}`,u=r===null?Vr:`closedloop company --domains ${r.join(",")} --confirmed-by-user`,c=i?"Nothing was saved.":`Nothing was saved: only the person who uses this workspace can confirm the email domains. Has your user confirmed ${l}? ${Fo} If they have not, ask them first. Once they say yes, run: ${u}`;return{profile:a,saved:!1,instructions:c}}return{profile:await jo(o,t,r),saved:!0,instructions:null}}async function Nu(e,t){let n=v(e.runtime);if(t.website!==void 0&&!t.website.trim())throw new p("invalid_website","--website needs the company website, e.g. https://acme.example",g.Usage);if(t.confirmedByUser&&t.domains===void 0)throw new p("invalid_options","--confirmed-by-user goes with --domains or --no-domains",g.Usage);if(t.research&&(t.website!==void 0||t.domains!==void 0))throw new p("invalid_options","--research only shows the research; set the website or the email domains without it",g.Usage);if(t.research){let l=await Ye(e.runtime,n,!0);t.watch&&(l=await Jr(e,n,l,!0)),e.print.result(l,u=>xu(u,Date.now()));return}let o=t.website!==void 0?await Ho(e.runtime,n,t.website):await Ye(e.runtime,n),i=!1,r=null;if(t.domains!==void 0){let l=await Pu(e,n,t);({profile:o}=l),i=l.saved,r=l.instructions}t.watch&&(o=await Jr(e,n,o,!1)),!r&&!e.interactive&&(r=Gt(o));let s=o.research.state==="ready"?await Iu(e.runtime,n):null,a={...o,email_domains_saved:i,instructions:r,products_suggested:s};e.print.result(a,l=>{let u=[];return t.website!==void 0&&u.push(`Website saved. ${Ht}`),l.email_domains_saved?u.push("Email domains saved."):t.domains!==void 0&&l.instructions&&u.push(l.instructions),u.push(Mo(l,Date.now(),l.products_suggested)),u.join(`
53
+ `)})}var zr=(e,t)=>{e.command("company").description("Your company website and the research of it, which comes before any analysis.").option("--website <url>","Set the company website, e.g. https://acme.example (an owner or admin).").option("--domains <list>","Confirm your colleagues' email domains, e.g. acme.example,acme.io (an owner or admin).").option("--no-domains","Confirm your company has no email domain of its own; colleagues then count as customers.").option("--confirmed-by-user","The person using this workspace confirmed the domains. An agent adds it only after its user said yes.").option("--watch","Keep checking every 30 seconds until the research is done (up to an hour).").option("--research","Show everything the company research found: the whole research text.").action(async n=>Nu(t,n))};var Lu="Insights that name one of these competitors count as competitor mentions, shown on opportunities, features, customer profiles and the weekly brief.",Xr="closedloop competitors add <name> [--website <url>]",Go="closedloop competitors",Du="Changing competitors needs the right to change the workspace's settings (an owner or admin).",ju="It is then marked as not a competitor; the customer mentions behind it are kept, and adding it again restores it.",Fu={researched:"AI research",researched_and_mentioned:"AI research, mentioned by customers",mentioned:"mentioned by customers",edited:"manual"},Qr=e=>e.trim().toLowerCase(),Wu=e=>/^(y|yes)$/i.test(e.trim()),Uu=e=>/^[\w.@-]+$/.test(e)?e:`"${e.replace(/(["\\$`])/g,"\\$1")}"`;function In(e,t,n){let o=e.status===403,i=C(e,o?"access_denied":"api_error",t);return o&&n?new p(i.code,i.message,g.Failure,Du):i}var xn=(e,t)=>`/api/competitors/${encodeURIComponent(e)}${t?`/${encodeURIComponent(t)}`:""}`;function qo(e){let t=e;if(!t||typeof t.id!="string"||typeof t.competitor_company!="string")throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);return t}async function Jo(e,t,n){let o=await $(e,t,"GET",xn(n));if(o.status!==200)throw In(o,"The competitors could not be loaded. Try again.",!1);if(!Array.isArray(o.body.competitors))throw new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure);return o.body.competitors.map(qo)}function Zr(e,t){let n=e.find(o=>o.id.toLowerCase()===t.toLowerCase());if(n)return n;throw new p("unknown_competitor",`there is no competitor with the id ${t} on your list`,g.Usage,L(Go))}function es(e){let t=[e.competitor_product&&Qr(e.competitor_product)!==Qr(e.competitor_company)?`${e.competitor_company} (${e.competitor_product})`:e.competitor_company];e.product_website&&t.push(e.product_website),e.is_direct_competitor===!0&&t.push("direct"),e.is_direct_competitor===!1&&t.push("indirect"),typeof e.mention_count=="number"&&e.mention_count>0&&t.push(`${k(e.mention_count)} mention${e.mention_count===1?"":"s"}`);let n=e.provenance?Fu[e.provenance]:void 0;return n&&t.push(n),t.join(" \xB7 ")}function Hu(e,t){let n=[Lu,""];return e.competitors.length===0?n.push(`No competitors yet. Your company research adds the ones it finds; add your own with: ${Xr}`):(n.push(...e.competitors.flatMap(o=>[` ${es(o)}`,R(t," ",o.id)]),""),n.push(`${k(e.competitors.length)} competitor${e.competitors.length===1?"":"s"}. Add one: ${Xr}. Change one by its id: closedloop competitors edit <id> or remove <id>.`)),e.can_change||n.push("Only someone who can change the workspace's settings (an owner or admin) can change this list."),n.join(`
54
+ `)}async function Mu(e){let t=v(e.runtime),n=await M(e.runtime,t),o=await Jo(e.runtime,t,n.id);e.print.result({competitors:o,can_change:n.permissions.can_manage_team_settings},i=>Hu(i,e.print.style))}async function Gu(e,t,n){let o=t.trim();if(!o)throw new p("missing_name",`give the competitor's name, e.g. closedloop competitors add "Acme Rival"`,g.Usage);let i=v(e.runtime),{id:r}=await M(e.runtime,i),s={competitor_company:o,is_direct_competitor:!0};n.website?.trim()&&(s.product_website=n.website.trim()),n.description?.trim()&&(s.description=n.description.trim());let a=await $(e.runtime,i,"POST",xn(r),s);if(a.status!==200)throw In(a,"The competitor could not be added. Try again.",!0);let l=qo(a.body.competitor);e.print.result({competitor:l},u=>`Added ${u.competitor.competitor_company} to your competitors.`)}async function qu(e,t,n){let o=I(t,"competitor",Go);if(n.name===void 0&&n.website===void 0&&n.description===void 0)throw new p("nothing_to_change","say what to change: --name, --website or --description",g.Usage);if(n.name!==void 0&&!n.name.trim())throw new p("missing_name","--name needs the new name; a competitor always has one",g.Usage);let i=v(e.runtime),{id:r}=await M(e.runtime,i),s=Zr(await Jo(e.runtime,i,r),o),a={};n.name!==void 0&&(a.competitor_company=n.name.trim()),n.website!==void 0&&(a.product_website=n.website.trim()||null),n.description!==void 0&&(a.description=n.description.trim()||null);let l=await $(e.runtime,i,"PUT",xn(r,s.id),a);if(l.status!==200)throw In(l,"The competitor could not be saved. Try again.",!0);let u=qo(l.body.competitor);e.print.result({competitor:u},c=>`Saved: ${es(c.competitor)}`)}async function Ju(e,t,n){let{runtime:o,interactive:i,print:r}=e,s=I(t,"competitor",Go),a=v(o),{id:l}=await M(o,a),u=Zr(await Jo(o,a,l),s),c=u.competitor_company,m=n;if(!m&&i&&(r.note(`${c} will be marked as not a competitor and moved out of this list. The customer mentions behind it are kept, and adding it again restores it.`),m=Wu(await o.prompt(`Type "yes" to remove ${c}: `))),!m){let h=i?"Nothing was removed.":`Nothing was removed: only the person who uses this workspace can remove a competitor. Has your user agreed to remove ${c} from the competitor list? ${ju} If they have not, ask them first. Once they say yes, run: closedloop competitors remove ${u.id} --yes`;r.result({status:"not_removed",competitor:u,instructions:h},f=>f.instructions);return}let d=await $(o,a,"DELETE",xn(l,u.id));if(d.status!==200)throw In(d,"The competitor could not be removed. Try again.",!0);r.result({status:"removed",competitor:u,instructions:null},h=>`Removed ${h.competitor.competitor_company} from your competitors. The customer mentions behind it are kept; to restore it, run: closedloop competitors add ${Uu(h.competitor.competitor_company)}`)}var ts=(e,t)=>{let n=e.command("competitors").description("The competitors ClosedLoop AI watches for in your insights: the ones AI research found, the ones customers mention and the ones added by hand. Shows them, each with its id; add one, or edit or remove one by its id (someone who can change the workspace's settings).").action(async()=>Mu(t));n.command("add").argument("<name>","The competitor's name.").description("Add a competitor, as a direct competitor. Adding one that was removed restores it.").option("--website <url>","Its website, e.g. https://rival.example.").option("--description <text>","What it does, in a sentence.").action(async(o,i)=>Gu(t,o,i)),n.command("edit").argument("<id>","The competitor's id, from closedloop competitors. A name is not used: two competitors can share one.").description("Change a competitor's name, website or description. Only what you give changes; an empty --website or --description clears it.").option("--name <name>","Its new name.").option("--website <url>","Its website.").option("--description <text>","What it does, in a sentence.").action(async(o,i)=>qu(t,o,i)),n.command("remove").argument("<id>","The competitor's id, from closedloop competitors.").description("Remove a competitor from the list: it is marked as not a competitor, its customer mentions are kept, and adding it again restores it. Asks before removing.").option("--yes","Remove without asking. An agent adds it only after its user agreed.").action(async(o,i)=>Ju(t,o,i.yes===!0))};var ns={event:"dated records, so a past value can be computed",customer_history:"from the customer history, so a past value can be computed",crm_history:"from the CRM's change history, so a past value can be computed",none:"none: a past value cannot be computed"},Vu=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),ee=e=>typeof e=="string"?e:"",Vo=e=>typeof e!="string"?String(e):/^\d{4}-\d{2}-\d{2}$/.test(e)?V(`${e}T00:00:00Z`):N(e);function Bu(e){if(typeof e.empty_share=="number")return`${Math.round(e.empty_share*100)}%`;let t=e.record_count,n=e.empty_count;return typeof t!="number"||typeof n!="number"||t<=0?null:`${Math.round(n/t*100)}%`}function Yu(e,t){return typeof e.distinct_count=="number"?Math.max(0,e.distinct_count-t):0}var ae=e=>e!=null,Ku=e=>e.hidden!==!0&&(e.kind==="number"&&![e.min,e.max,e.p10,e.p90].some(ae)||e.kind==="date"&&![e.first,e.last,e.p10,e.p90].some(ae)),os=e=>String(e).replace(/\s+/g," ").trim(),Bo=["customer","deal","person","insight","context","conversation","usage"],Yo="When two fields fit a request, ask the user which one they mean before writing the rule; never pick one silently.";function zu(e){let t=o=>typeof o=="number"?A(o):Vo(o),n=typeof e.zero_share=="number"&&e.zero_share>0?[`${Math.round(e.zero_share*100)}% are 0`]:[];return ae(e.median)?[...n,`${n.length?"other values: ":""}10th percentile ${t(e.p10)}`,`median ${t(e.median)}`,`90th percentile ${t(e.p90)}`]:n}function rs(e,t){let n=ee(t.label)&&t.label!==t.key?` \xB7 ${t.label}`:"";return`${e.bold(ee(t.key))} ${e.dim(`${ee(t.type)}${n}`)}`}function Xu(e){return w(e.guide)&&w(e.guide.preview)&&e.guide.preview.kind==="empty"}function Qu(e,t,n,o){if(!t.length)return n?`No fields match "${n}".`:o?"No fields hold values yet.":"No fields yet.";let i=new Map;for(let a of t){let l=ee(a.entity)||"other";i.set(l,[...i.get(l)??[],a])}let r=a=>[` ${rs(e,a)}`,...ee(a.id)&&a.id!==a.key?[R(e," ",a.id)]:[]];return`${[...i.entries()].map(([a,l])=>[e.bold(a),...l.flatMap(r)].join(`
54
55
  `)).join(`
55
56
 
56
57
  `)}
57
58
 
58
- ${e.dim(Xo)}
59
- ${e.dim("Read one in full: closedloop fields show <key>")}`}function ec(e,t,n){let o=[`${ss(e,t)}${e.dim(` \xB7 ${ne(t.entity)}`)}`];ne(t.id)&&t.id!==t.key&&o.push(e.dim(`id ${ne(t.id)}`)),ne(t.description)&&o.push(ne(t.description)),t.example!==void 0&&t.example!==null&&o.push(e.dim(`e.g. ${JSON.stringify(t.example)}`)),typeof t.history=="string"&&o.push(e.dim(`history: ${Bu(os,t.history)?os[t.history]:t.history}`));let i=Array.isArray(t.confused_with)?t.confused_with.filter(l=>typeof l=="string"):[];i.length&&o.push(`Often confused with ${i.join(", ")}. When a request does not say which one it means, ask which, showing the number with each (a preview gives both).`),(t.pii==="email"||t.pii==="phone"||t.pii===!0)&&o.push(e.dim(`It holds ${t.pii==="phone"?"phone numbers":t.pii==="email"?"email addresses":"email addresses or phone numbers"}, so only their pattern is shown.`));let r=w(t.profile)?t.profile:null;if(r){let l=Array.isArray(r.values)?r.values.filter(w):[];if(typeof r.pattern=="string")o.push("",`Values look like ${r.pattern}.`);else if(r.kind==="text")o.push("",`Free text${typeof r.distinct_count=="number"?` with ${O(r.distinct_count)} different values`:""}; no values are kept.`);else if(l.length){let c=Math.max(...l.map(m=>is(m.value).length)),d=Math.max(...l.map(m=>O(m.count).length));if(o.push("","Values:",...l.map(m=>` ${is(m.value).padEnd(c)} ${O(m.count).padStart(d)}`)),typeof r.hidden_values!="number"){let m=Ku(r,l.length);m>0&&o.push(e.dim(` and ${O(m)} more`))}}else if(r.hidden===!0)o.push("",`Not shown: fewer than ${O(r.hidden_below)} customers hold the values at one end of the range.`);else if(ae(r.p10)||ae(r.p90)||typeof r.zero_share=="number"&&r.zero_share>0)o.push("",Xu(r).join(" \xB7 "));else if(zu(r))o.push("","no values yet: no record has one.");else if(ae(r.min)||ae(r.max)){let c=[`smallest ${O(r.min)}`,...ae(r.median)?[`median ${O(r.median)}`]:[],...ae(r.p90)?[`90th percentile ${O(r.p90)}`]:[],`largest ${O(r.max)}`];o.push("",c.join(" \xB7 "))}else(ae(r.first)||ae(r.last))&&o.push("",`first ${Ko(r.first)} \xB7 last ${Ko(r.last)}`);typeof r.hidden_values=="number"&&r.hidden_values>0&&o.push(e.dim(`${O(r.hidden_values)} values held by fewer than ${O(r.hidden_below)} customers are not shown.`));let u=Yu(r);u!==null&&o.push(`${k(r.empty_count)} of ${k(r.record_count)} records have no value (${u}).`)}let a={of:!ne(t.entity)||t.entity==="usage"?"customer":ne(t.entity),where:{field:ne(t.key),op:"exists"}};return o.push("",e.dim(`Use it in a rule: ${fn(a,l=>`closedloop segments preview --rule ${l}`,n)}`)),o.join(`
60
- `)}async function rs(e,t,n){if(t!==void 0&&!zo.includes(t))throw new p("invalid_entity",`--entity must be one of ${zo.join(", ")}`,g.Usage);let o=await mr(e,{entity:t,q:n}),i=n===void 0?o.filter(s=>!Qu(s)):o,r={filter:{entity:t??null,q:n??null},fields:i,ask:Xo,next:"closedloop fields show <key>"};e.print.result(r,s=>Zu(e.print.style,s.fields,n,o.length-i.length))}async function tc(e,t){let o={field:await pr(e,t),next:"closedloop segments preview --rule <file|json|->"};e.print.result(o,i=>ec(e.print.style,i.field,e.runtime.platform))}var as=(e,t)=>{let n=e.command("fields").alias("field").description(`The named fields a segment or a metric is written in: what each means, a preview of this workspace's values and how to filter on it. ${Xo}`),o=`Only one kind of record: ${zo.join(", ")}.`;n.command("list").description("Every field that holds values, one line each: its key, type and label, by the kind of record it describes.").option("--entity <entity>",o).action(async i=>rs(t,i.entity,void 0)),n.command("search").argument("<words...>","Words in the field's key, label or description, e.g. property type.").description("The fields whose key, label or description matches the words.").option("--entity <entity>",o).action(async(i,r)=>rs(t,r.entity,i.join(" ").trim())),n.command("show").argument("<key>","A field key, e.g. customer.country or customer.crm.Property_Type__c.").description("One field in full: what it means, every value with how many records have it, and how many have none.").action(async i=>tc(t,i))};var nc=[{term:"Product structure",definition:"The products, areas and features a team confirms (closedloop products). Insights are filed against them."},{term:"Product",definition:"One of the canonical Products a team owns. Products, the Areas under them and the Features under those are how the product is organised."},{term:"Area",definition:"A part of a Product. Areas and Features are the parts an Insight is filed against."},{term:"Feature",definition:"A part of the product that exists (Checkout, Reporting), never planned work. It sits under an Area or a Product, and Insights are filed against it."},{term:"Insight",definition:"One source-backed product problem, request, bug or improvement extracted from customer evidence. One conversation can produce several."},{term:"Opportunity",definition:"A customer problem, clustered from related Insights and split into Needs; can span products. Shows customer impact and whether delivery work covers it. The top of the discovery layer."},{term:"Need",definition:"One specific need inside an Opportunity, discovered by sub-clustering and backed by Insights. Has one place in the product layer: the Feature holding most of its Insights, else that Area, else that Product."},{term:"Customer",definition:"The person or account that gave feedback."},{term:"Competitor",definition:"A third party customers mention that competes with your product (closedloop competitors). Insights that name one count as competitor mentions."}],ls=(e,t)=>{e.command("glossary").description("What the words mean: product structure, product, area, feature, insight, opportunity, need, customer and competitor.").action(()=>t.print.result({terms:nc},n=>n.terms.map(o=>`${t.print.style.bold(o.term)}: ${o.definition}`).join(`
61
- `)))};import{existsSync as zc,readFileSync as Xc,statSync as Qc}from"node:fs";var oc=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Gt='A formula combines saved metrics, e.g. "[Accounts churned this quarter] / [Paying accounts]", "#2 * 12" or "([MRR] - [MRR]@90d) / [MRR]@90d" (@90d: its value 90 days earlier); name a metric in brackets, by its number in the last metrics list (#2) or by its id.',le=e=>new p("invalid_formula",e,g.Usage,Gt),cs={"+":"add","-":"subtract","*":"multiply","/":"divide"},us={add:"+",subtract:"-",multiply:"*",divide:"/"};function ic(e){let t=[],n=0,o=()=>!t.length||t[t.length-1].kind==="op"||t[t.length-1].kind==="open";for(;n<e.length;){let i=e[n];if(/\s/.test(i)){n+=1;continue}let r=null;if(i==="["){let a=e.indexOf("]",n+1);if(a<0)throw le(`a metric name opened with [ at ${n+1} is not closed with ]`);let l=e.slice(n+1,a).trim();if(!l)throw le(`an empty metric name [] at ${n+1}`);r={kind:"name",text:l},n=a+1}else if(i==="#"){let a=/^#(\d{1,3})/.exec(e.slice(n));if(!a)throw le(`# at ${n+1} must be followed by a number from the last metrics list, e.g. #2`);r={kind:"number",text:a[1]},n+=a[0].length}else oc.test(e.slice(n,n+36))&&(r={kind:"id",text:e.slice(n,n+36).toLowerCase()},n+=36);if(r){let a=/^\s*@\s*(\d+)\s*d\b/i.exec(e.slice(n));if(!a&&/^\s*@/.test(e.slice(n)))throw le(`an earlier value is written @<days>d, e.g. @90d (at ${n+1})`);let l=a?Number(a[1]):void 0;if(l===0)throw le("@0d is the value on the same day: leave it out");t.push({kind:"metric",name:r,...l!==void 0&&{days:l}}),a&&(n+=a[0].length);continue}if(/^\d+(?:\.\d+)?|^\.\d+/.exec(e.slice(n))||i==="-"&&o()&&/^-\s*(\d|\.\d)/.test(e.slice(n))){let a=/^-?\s*(\d+(?:\.\d+)?|\.\d+)/.exec(e.slice(n));t.push({kind:"num",value:Number(a[0].replace(/\s+/g,""))}),n+=a[0].length;continue}if(i in cs){t.push({kind:"op",value:i}),n+=1;continue}if(i==="("){t.push({kind:"open"}),n+=1;continue}if(i===")"){t.push({kind:"close"}),n+=1;continue}throw le(`'${i}' at ${n+1} is not part of a formula`)}return t}function rc(e){let t=0,n=()=>{let a=e[t];if(!a)throw le("the formula ends where a metric or a number is expected");if(t+=1,a.kind==="num")return a.value;if(a.kind==="metric")return{metric:a.name,...a.days!==void 0&&{days_ago:a.days}};if(a.kind==="open"){let l=r();if(e[t]?.kind!=="close")throw le("a ( is not closed with )");return t+=1,l}throw le(`a metric or a number is expected where ${a.kind==="op"?a.value:")"} is`)},o=(a,l)=>{let u=a();for(;e[t]?.kind==="op"&&l.includes(e[t].value);){let c=cs[e[t].value];t+=1,u={fn:"formula",op:c,args:[u,a()]}}return u},i=()=>o(n,"*/"),r=()=>o(i,"+-"),s=r();if(t<e.length)throw le("the formula goes on after it is complete; join the parts with + - * or /");return s}function ds(e){let t=rc(ic(e));if(typeof t=="number"||!("fn"in t))throw le("a formula combines at least two things with + - * or /");return t}function ms(e){let t=[],n=[e];for(;n.length;){let o=n.pop();typeof o!="number"&&("metric"in o?t.push(o.metric):n.push(o.args[1],o.args[0]))}return t}function Qo(e,t){let n=o=>typeof o=="number"?o:"metric"in o?{metric:t(o.metric),...o.days_ago!==void 0&&{days_ago:o.days_ago}}:Qo(o,t);return{fn:"formula",op:e.op,args:[n(e.args[0]),n(e.args[1])]}}var fe=e=>w(e)&&e.fn==="formula";function Dn(e,t=new Map){if(typeof e=="number")return String(e);if(!w(e))return"?";if(typeof e.metric=="string"){let r=t.get(e.metric)??e.metric.slice(0,8),s=typeof e.days_ago=="number"?`@${e.days_ago}d`:"";return`[${r}]${s}`}let n=Array.isArray(e.args)?e.args:[],o=r=>fe(r)?`(${Dn(r,t)})`:Dn(r,t),i=typeof e.op=="string"&&e.op in us?us[e.op]:String(e.op);return`${o(n[0])} ${i} ${o(n[1])}`}function Se(e,t,n,o){if(typeof t!="number"||!Number.isFinite(t))return null;let i=t!==0&&Math.abs(t)<1?String(Number(t.toPrecision(3))):Number.isInteger(t)?k(t):new Intl.NumberFormat(void 0,{maximumFractionDigits:2}).format(t),r=a=>Number.isInteger(a)?k(a):String(Number(a.toPrecision(6))),s=w(e)&&e.op==="divide"&&typeof n=="number"&&typeof o=="number"?` (${r(n)} / ${r(o)})`:"";return`${i}${s}`}import{existsSync as cc}from"node:fs";var Zo="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";function qt(e,t,n){return o=>n&&o?`\x1B[${e}m${o}\x1B[${t}m`:o}function ei(e){let t=e.color===!0;return{enabled:t,width:Math.max(60,Math.min(100,e.columns??100)),bold:qt(1,22,t),dim:qt(2,22,t),red:qt(31,39,t),orange:qt(33,39,t),green:qt(32,39,t),bar:(n,o)=>{let i=Number.isFinite(n)?Math.min(1,Math.max(0,n)):0,r=Math.round(i*o);return`${"\u2588".repeat(r)}${"\u2591".repeat(o-r)}`},sparkline:n=>{let o=Math.max(0,...n);return n.map(i=>Zo[o>0?Math.min(Zo.length-1,Math.round(i/o*(Zo.length-1))):0]).join("")}}}function jn(e,t){return t<=1?e.slice(0,Math.max(0,t)):e.length<=t?e:`${e.slice(0,t-1).trimEnd()}\u2026`}function Fn(e,t,n){let o=e.split(/\s+/).filter(Boolean),i=[],r="";for(let s of o){let a=t-(i.length===0?0:n.length);r&&r.length+1+s.length>a?(i.push(r),r=s):r=r?`${r} ${s}`:s}return r&&i.push(r),i.map((s,a)=>a===0?s:`${n}${s}`).join(`
62
- `)}var ni="last-segments.json",Jt='A rule is JSON: {"of":"customer","where":{"field":"customer.country","op":"in","value":["United States"]}}; a saved segment is included by its name, {"segment":"Groups"}; see closedloop fields list',ps="<file|json|->";async function gs(e,t){if(t===void 0)throw new p("missing_rule","give the rule with --rule <file|json|->",g.Usage,Jt);return De(e,t,"rule",Jt)}function sc(e,t,n,o){let i=typeof t.entity=="string"?t.entity:n.of,r=typeof t.count=="number"?t.count:0,s=[`${k(r)} ${M(i,r)} ${r===1?"matches":"match"}.`],a=Array.isArray(t.steps)?t.steps.filter(w):[],l=a.length&&typeof t.total=="number"?[{condition:null,count:t.total},...a]:a;if(l.length){let f=l.map(b=>b.condition===null||b.condition===void 0?`all ${M(i)}`:jt(b.condition)),y=l.map(b=>typeof b.count=="number"?k(b.count):"?"),_=Math.min(Math.max(...f.map(b=>b.length)),e.width-16),S=Math.max(...y.map(b=>b.length));s.push("",...f.map((b,F)=>` ${jn(b,_).padEnd(_)} ${y[F].padStart(S)}`))}let c=(Array.isArray(t.examples)?t.examples.filter(w):[]).map(f=>[f.name,f.label,f.title,f.customer_id,f.id].find(y=>typeof y=="string")).filter(Boolean);(c.length||typeof t.insights_in_scope=="number")&&s.push(""),c.length&&s.push(`Examples: ${c.join(", ")}`),typeof t.insights_in_scope=="number"&&s.push(`${k(t.insights_in_scope)} insight${t.insights_in_scope===1?"":"s"} in scope.`);let d=Array.isArray(t.warnings)?t.warnings:[],m=Ut(t.stale);(d.length||m)&&s.push("",...m?[e.orange(`Warning: ${m}.`)]:[],...d.map(f=>e.orange(`Warning: ${Tn(f)}`)));let h=En(e,t.alternatives);return h.length&&s.push("",...h),s.push("",e.dim(o)),s.join(`
63
- `)}async function ac(e,t){let n=await gs(e,t),o=await gr(e,n),i=Rn(o.alternatives,o.count),r={...o,rule:n,...i?{ask:i}:{},next:`closedloop segments define <name> --rule ${ps}`};e.print.result(r,s=>sc(e.print.style,s,n,"Save it: closedloop segments define <name> --rule <the same rule>"))}function ti(e){let t=w(e)&&Array.isArray(e.includes)?e.includes.filter(w):[];return t.length?t.map(n=>{let o=n.newer_version===!0&&typeof n.current_version=="number"?`; version ${n.current_version} is newer`:"",i=n.archived===!0?"; archived":"";return`${String(n.name)} (version ${String(n.version)}${o}${i})`}).join(", "):null}async function lc(e,t,n){let o=await gs(e,n.rule),i=await hr(e,t,o,n.description),r=String(i.id),s=he(i),a={segment:i,rule:w(s.rule)?s.rule:o,next:`closedloop metrics preview --segment ${r} --calc count`};e.print.result(a,l=>{let u=typeof i.entity=="string"?i.entity:l.rule.of,c=Ne(s),d=c===null?"":`version ${c}, `,m=ti(s);return`Saved the segment "${String(i.name??t)}" (${d}${M(u)}).${m?`
64
- It includes ${m}.`:""}
65
- ${e.print.style.dim(`Measure it: ${l.next}`)}`})}async function uc(e){let t=await yr(e);await Sn(e,ni,t.map(o=>o.id));let n={segments:t,next:"closedloop metrics define <name> --segment <number|id|name> --calc <calc>"};e.print.result(n,o=>{let i=e.print.style;if(!o.segments.length)return`No segments yet. Define one: closedloop segments define <name> --rule ${ps}`;let r=String(o.segments.length).length,s=" ".repeat(r+2),a=new Map;for(let d of o.segments){let m=he(d).includes;for(let h of Array.isArray(m)?m.filter(w):[]){let f=String(h.segment_id);a.set(f,[...a.get(f)??[],String(d.name)])}}let l=o.segments.map((d,m)=>{let h=he(d),f=Ne(h),y=f===null?"":` \xB7 version ${f}`,_=ti(h),S=a.get(String(d.id))??[];return[`${String(m+1).padStart(r)} ${i.bold(String(d.name))}${i.dim(` \xB7 ${M(d.entity)}${y}`)}`,`${s}${Nr(h.rule,xr(h))}`,..._?[`${s}includes ${_}`]:[],...S.length?[`${s}included in ${S.join(", ")}`]:[],`${s}${i.dim(`id ${String(d.id)}`)}`].join(`
66
- `)}),c=o.segments.some(d=>/is newer/.test(ti(he(d))??""))?`
67
- ${i.dim("A segment keeps the version it included; save its rule again to use the newer one.")}`:"";return`${l.join(`
68
-
69
- `)}${c}
70
-
71
- ${i.dim("Measure one: closedloop metrics preview --segment <number> --calc count")}`})}var hs=(e,t)=>{let n=e.command("segments").alias("segment").description("Who is in scope: customers, deals, people, insights, context or conversations, described as a rule over named fields."),o=`The rule: a JSON file, inline JSON, or - for standard input. ${Jt}.`;n.command("define").argument("<name>",`The segment's name, e.g. "paying US hotels". Other rules include it by this name.`).description("Save a rule as a named segment (version 1). A segment in use is never changed in place: a change makes a new version.").option("--rule <rule>",o).option("--description <text>","What the segment is for.").action(async(i,r)=>lc(t,i,r)),n.command("preview").description("Count a rule without saving it: how many match, how many are left after each condition, examples, insights in scope and warnings.").option("--rule <rule>",o).action(async i=>ac(t,i.rule)),n.command("list").description("Your workspace's segments, numbered, each with its rule in words, the segments it includes and the ones that include it.").action(async()=>uc(t))};var Ke="last-metrics.json",Vt='A calculation is count, or JSON such as {"fn":"sum","field":"customer.open_pipeline"} or {"fn":"ratio","numerator":{"fn":"count","where":{...}},"denominator":{"fn":"count"}}',H=(e,t,n)=>new p(e,t,g.Usage,n),oi=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,dc=e=>{let t=e.trim();return t!==""&&t!=="-"&&!/^[{[]/.test(t)&&!Dt(t)&&!/^\d{1,3}$/.test(t)&&!oi.test(t)&&!cc(t)};async function mc(e,t){let n=ds(t),o=ms(n),i=new Map;if(o.some(a=>a.kind==="name"))for(let a of await vn(e))typeof a.name=="string"&&typeof a.id=="string"&&i.set(a.name.toLowerCase(),a.id);let r=new Map;for(let a of o)a.kind==="number"&&r.set(a.text,await se(e,a.text,Ke,"metrics","closedloop metrics list"));return Qo(n,a=>{if(a.kind==="id")return a.text;if(a.kind==="number")return r.get(a.text);let l=i.get(a.text.toLowerCase());if(!l)throw H("unknown_metric",`no metric in this workspace is named "${a.text}"`,"See their names: closedloop metrics list");return l})}async function ws(e,t){if(t.formula!==void 0){if(t.calc!==void 0)throw H("formula_and_calc","give --formula or --calc, not both",Gt);if(t.segment!==void 0)throw H("formula_has_no_segment","a formula has no segment: it combines saved metrics, each over its own",Gt);return{segment:void 0,calc:await mc(e,t.formula)}}if(t.segment===void 0&&t.calc!==void 0&&!/^[a-z_0-9]+$/i.test(t.calc.trim())){let s=await De(e,t.calc,"calc",Vt);if(fe(s))return{segment:void 0,calc:s}}if(t.segment===void 0)throw H("missing_segment","give the segment with --segment <number|id|name|file|json|->","Your segments: closedloop segments list");if(t.calc===void 0)throw H("missing_calc","give the calculation with --calc <calc>",Vt);if(t.segment.trim()==="-"&&t.calc.trim()==="-")throw H("stdin_twice","only one of --segment and --calc can come from standard input");let o=(dc(t.segment)?t.segment.trim():await se(e,t.segment,ni,"segments","closedloop segments list"))??await De(e,t.segment,"segment",Jt),i=t.calc.trim(),r;if(i==="count")r={fn:"count"};else{if(/^[a-z_0-9]+$/i.test(i))throw H("invalid_calc",`'${i}' is not a calculation`,Vt);r=await De(e,i,"calc",Vt)}return{segment:o,calc:r}}var _s=async(e,t)=>t===void 0?null:typeof t!="string"?t:oi.test(t)?fr(e,t):null,Un=e=>String(e).replace(/\s*\(?\b(failed|not_measured):\s*[a-z_]+\)?/g,"").trim();function bs(e){let t={measured:"measured",rebuilt:"rebuilt from the history",not_available:"no value"};return(Array.isArray(e)?e.filter(w):[]).map(n=>{let o=typeof n.name=="string"?n.name:String(n.metric),i=typeof n.days_ago=="number"&&n.days_ago>0?` ${n.days_ago} days earlier (${Ae(n.as_of)})`:"",r=typeof n.basis=="string"&&Object.prototype.hasOwnProperty.call(t,n.basis)?t[n.basis]:String(n.basis),s=typeof n.value=="number"?O(n.value):`no value${typeof n.reason=="string"&&Un(n.reason)?` (${Un(n.reason)})`:""}`;return` ${o}${i}: ${s}${typeof n.value=="number"?`, ${r}`:""}`})}async function pc(e,t,n){let o=await Do(e,void 0,t),i=n.formula!==void 0?`--formula ${ee(n.formula,e.runtime.platform)}`:"--calc <the same formula>",r={...o,calc:w(o.calc)?o.calc:t,next:`closedloop metrics define <name> ${i}`};e.print.result(r,s=>{let a=e.print.style,l=Se(s.calc,s.value,s.numerator,s.denominator),u=[l!==null?`${l} on ${Ae(s.as_of)}.`:`No value: ${s.reason===void 0||s.reason===null?"an input has no value":Un(s.reason)}.`,...bs(s.inputs)],c=Ut(s.stale);return c&&u.push(a.orange(`Warning: ${c}.`)),u.push("",a.dim(`Save it: ${s.next}`)),u.join(`
72
- `)})}async function gc(e,t){let{segment:n,calc:o}=await ws(e,t);if(n===void 0)return pc(e,o,t);let i=await Do(e,n,o),r=await _s(e,n),s=typeof n=="string"?oi.test(n)?n:ee(n,e.runtime.platform):"<the same segment>",a=Rn(i.alternatives,i.value),l={...i,segment:n,rule:r,calc:o,...a?{ask:a}:{},next:`closedloop metrics define <name> --segment ${s} --calc <calc>`};e.print.result(l,u=>{let c=e.print.style,d=typeof u.member_count=="number"?u.member_count:null,m=M(typeof u.entity=="string"?u.entity:u.rule?.of,d??2),h=d===null?"":u.calc.fn==="count"&&u.value===d?` ${m}`:` over ${k(d)} ${m}`,f=Ve(u.calc,u.value,u.numerator,u.denominator),y=[f!==null?`${f}${h}.`:u.calc.fn==="ratio"?"No value: there is nothing to divide by (the denominator is 0).":`No value${h}: none of them has one.`],_=typeof u.missing_count=="number"?u.missing_count:0;_>0&&d!==null&&typeof u.calc.field=="string"&&y.push(`${k(_)} of ${k(d)} have no value for ${u.calc.field}, so they are left out.`);let S=Array.isArray(u.warnings)?u.warnings:[],b=Ut(u.stale);return b&&y.push(c.orange(`Warning: ${b}.`)),y.push(...S.map(F=>c.orange(`Warning: ${Tn(F)}`))),y.push(...En(c,u.alternatives)),y.push("",c.dim(`Save it: closedloop metrics define <name> --segment ${s} --calc <the same calc>`)),y.join(`
73
- `)})}async function hc(e,t,n){let{segment:o,calc:i}=await ws(e,n),r=await wr(e,t,o,i,n.description),s=String(r.id),a=he(r),l={metric:r,rule:await _s(e,o),calc:w(a.calc)?a.calc:i,next:`closedloop goals metrics add <goal> --metric ${s} --period month`};e.print.result(l,u=>{let c=w(r.segment)&&typeof r.segment.name=="string"?r.segment.name:null,d=o===void 0?", each metric it uses pinned at its current version":typeof o=="string"?"":c?` with its own segment "${c}"`:" with its own segment, named after it",m=Ne(a),h=m===null?"":` (version ${m})`;return`Saved the metric "${String(r.name??t)}"${h}${d}.
74
- `+e.print.style.dim(`It is measured every night. Measure a goal on it: ${u.next}`)})}function fc(e){let t=w(e.sources_fresh_as_of)?e.sources_fresh_as_of:{};return(typeof e.reason=="string"?e.reason.replace(/^stale:\s*/,"").split(/,\s*/).filter(Boolean):[]).map(i=>typeof t[i]=="string"&&!Number.isNaN(Date.parse(t[i]))?`${On(i)} last updated ${P(t[i])}`:`${On(i)} late`).join(", ")}function yc(e,t,n,o={}){let i=e.print.style;if(!w(n))return"not measured yet";let r=typeof n.as_of=="string"?/^\d{4}-\d{2}-\d{2}$/.test(n.as_of)?q(`${n.as_of}T00:00:00Z`):P(n.as_of):"an unknown day",s=typeof n.reason=="string"?n.reason.replace(/^(stale|failed|not_measured):\s*/,""):"",a=s?`: ${s}`:"";if(n.status==="not_measured"||n.status==="failed")return`no value on ${r}`;let l=typeof n.version=="number"?n.version:null,u=Ne(o),c=l!==null&&u!==null&&l!==u,m=`${(c?typeof n.value=="number"?O(n.value):null:fe(t)?Se(t,n.value,n.numerator,n.denominator):Ve(t,n.value,n.numerator,n.denominator))??"no value"}${c?` (version ${l})`:""}`,h=typeof n.basis=="string"?` \xB7 ${n.basis}`:"";if(n.status!=="stale")return`${m} on ${r}${h}`;let f=fc(n);return`${m} on ${r}${h} \xB7 ${i.orange(`stale${f?`: ${f}`:a}`)}`}async function wc(e){let t=await vn(e);await Sn(e,Ke,t.map(o=>o.id));let n={metrics:t,next:"closedloop goals metrics add <goal> --metric <number|id|name> --period month"};e.print.result(n,o=>{let i=e.print.style;if(!o.metrics.length)return"No metrics defined yet. Define one: closedloop metrics define <name> --segment <segment> --calc <calc>";let r=String(o.metrics.length).length,s=" ".repeat(r+2),a=new Map(o.metrics.map(u=>[String(u.id),String(u.name)]));return`${o.metrics.map((u,c)=>{let d=he(u),m=Ne(d),h=m===null?"":` \xB7 version ${m}`,f=typeof u.goals_using=="number"&&u.goals_using>0?u.goals_using:0,y=f?` \xB7 measured by ${f} goal metric${f===1?"":"s"}`:"";return[`${String(c+1).padStart(r)} ${i.bold(String(u.name))}${i.dim(`${h}${y}`)}`,`${s}${fe(d.calc)?`formula: ${Dn(d.calc,a)}`:kn(d.calc)}`,`${s}${yc(e,d.calc,u.latest_run,d)}`,`${s}${i.dim(`id ${String(u.id)}`)}`].join(`
75
- `)}).join(`
76
-
77
- `)}
78
-
79
- ${i.dim("Measure a goal on one: closedloop goals metrics add <goal> --metric <number> --period month")}
80
- `+i.dim("Delete one: closedloop metrics delete <number>")})}var Wn=/^\d{4}-\d{2}-\d{2}$/,Ae=e=>typeof e=="string"&&Wn.test(e)?q(`${e}T00:00:00Z`):String(e),fs={measured:"measured that night",rebuilt:"computed now from the history"};async function _c(e,t,n,o){let i=await se(e,t,Ke,"metrics","closedloop metrics list");if(i===null)throw H("invalid_id",`'${t}' is not a metric id or a number from the last metrics list`,"List them: closedloop metrics list");if(n===void 0||!Wn.test(n))throw H("invalid_day","--as-of must be a day before today, YYYY-MM-DD");if(o!==void 0&&!/^[1-9][0-9]*$/.test(o))throw H("invalid_version","--metric-version must be a version number");let r=await jo(e,i,n,o===void 0?void 0:Number(o)),a=typeof r.member_count=="number"&&r.value===r.member_count&&r.numerator===null&&!Array.isArray(r.inputs)&&typeof r.version=="number"?await br(e,i,r.version):null,l={...r,...a?{calc:a}:{},next:"closedloop metrics list"};e.print.result(l,u=>{let c=e.print.style,d=Ae(u.as_of);if(u.basis==="not_available")return`No value on ${d}: ${String(u.reason??"there is no history to read it from")}.`;let m=Array.isArray(u.inputs),h=(m?Se({op:u.denominator===null?null:"divide"},u.value,u.numerator,u.denominator):null)??(typeof u.value=="number"?O(u.value):"no value"),f=w(u.calc)&&u.calc.fn==="count"&&u.member_count===u.value,y=typeof u.member_count=="number"&&!f?` over ${k(u.member_count)}`:"",_=m&&u.basis==="rebuilt"?"worked out now from the values of the metrics it uses":typeof u.basis=="string"&&Object.prototype.hasOwnProperty.call(fs,u.basis)?fs[u.basis]:String(u.basis),S=[`${h}${y} on ${d} (version ${String(u.version)}), ${_}.`,...m?bs(u.inputs):[]];return u.basis==="rebuilt"&&!m&&S.push(c.dim("Read from the CRM history and the dated records; it is not stored.")),S.join(`
81
- `)})}var bc=(e,t)=>new Date(Date.parse(`${e}T00:00:00Z`)-t*864e5).toISOString().slice(0,10),wt=e=>typeof e=="number"&&Number.isFinite(e)?e:null,ys={rate_limited:"too many requests for now",preview_busy:"the workspace is busy with other previews",preview_timeout:"working out past values takes too long on this workspace",internal_error:"the API did not answer",api_error:"the API did not answer",api_unreachable:"the API did not answer"};async function $c(e,t,n){let o=n.days??"30";if(!/^[1-9][0-9]*$/.test(o))throw H("invalid_days","--days must be a whole number of days, 1 or more");let i=n.every??"day";if(i!=="day"&&i!=="week")throw H("invalid_step","--every must be day or week");if(n.metricVersion!==void 0&&!/^[1-9][0-9]*$/.test(n.metricVersion))throw H("invalid_version","--metric-version must be a version number");let r=await se(e,t,Ke,"metrics","closedloop metrics list")??await $s(e,t),s=await $r(e,r),a=s.metric,l=s.versions.filter(w),u=n.metricVersion===void 0?Ne(he(a)):Number(n.metricVersion),c=l.find(A=>A.version===u);if(!c||typeof c.id!="string")throw H("unknown_version",`this metric has no version ${String(u)}`,"See its versions: closedloop metrics list");let d=typeof s.today=="string"&&Wn.test(s.today)?s.today:null;if(!d)throw new p("api_error","The API did not say which day it is. Try again.",g.Failure);let m=i==="week"?7:1,h=new Map,f=null;for(let A of s.runs.filter(w))A.version_id!==c.id||A.basis!=="measured"||typeof A.as_of!="string"||(A.status==="ok"||A.status==="stale"?h.set(A.as_of,A):A.as_of===d&&(f=A));let y=[],_=null,S=null,b=0;for(let A=0;A<Number(o);A+=m){let J=bc(d,A),V=h.get(J);if(V){y.push({day:J,value:wt(V.value),numerator:wt(V.numerator),denominator:wt(V.denominator),basis:"measured",status:String(V.status),reason:typeof V.reason=="string"?V.reason:null});continue}if(J===d){y.push(f?{day:J,value:null,numerator:null,denominator:null,basis:"measured",status:String(f.status),reason:typeof f.reason=="string"?f.reason:null}:{day:J,value:null,numerator:null,denominator:null,basis:"not_measured",status:null,reason:"the next nightly run measures it"});continue}if(_)continue;if(S){b+=1;continue}let U;try{U=await jo(e,r,J,u??void 0)}catch(tt){if(!(tt instanceof p)||!Object.prototype.hasOwnProperty.call(ys,tt.code))throw tt;S={day:J,reason:ys[tt.code]},b+=1;continue}y.push({day:J,value:wt(U.value),numerator:wt(U.numerator),denominator:wt(U.denominator),basis:String(U.basis),status:typeof U.status=="string"?U.status:null,reason:typeof U.reason=="string"?U.reason:null}),U.basis==="not_available"&&typeof U.history_from=="string"&&Wn.test(U.history_from)&&(_=U.history_from)}y.reverse();let F=y.filter(A=>A.value!==null),oe=F[0],G=F[F.length-1],At=oe&&G&&oe!==G?{from:{day:oe.day,value:oe.value},to:{day:G.day,value:G.value},delta:G.value-oe.value}:null,et=c.calc,ao={metric_id:r,name:a.name,version:u,today:d,time_zone:s.time_zone??null,every:i,days:y,change:At,history_from:_,complete:S===null,...S?{stopped:S,not_fetched:b}:{}};e.print.result(ao,A=>{let J=e.print.style,V=(E,ye,nt)=>(fe(et)?Se(et,E,ye,nt):Ve(et,E,ye,nt))??"no value",U=Math.max(...A.days.map(E=>Ae(E.day).length),0),tt=E=>E.basis==="measured"?E.status==="stale"?"measured, stale":"measured":"worked out from the history",Ot=[`${String(A.name)} (version ${String(A.version)}), ${A.every==="week"?"weekly":"daily"} over the last ${o} days:`];for(let E of A.days){let ye=E.reason?Un(E.reason):"",nt=E.basis==="not_measured"?`not measured yet (${E.reason})`:E.value===null?`no value${ye?`: ${ye}`:""}`:`${V(E.value,E.numerator,E.denominator)} (${tt(E)})`;Ot.push(`${Ae(E.day).padEnd(U)} ${nt}`)}if(A.change){let E=A.change.delta,ye=E>0?"+":E<0?"-":"",nt=Ft(et)?`${ye}${Math.abs(E*100).toFixed(1)} points`:`${ye}${O(Math.abs(E))}${A.change.from.value?` (${ye}${Math.abs(E/A.change.from.value*100).toFixed(1)}%)`:""}`;Ot.push("",`From ${V(A.change.from.value,null,null)} on ${Ae(A.change.from.day)} to ${V(A.change.to.value,null,null)} on ${Ae(A.change.to.day)}: ${nt}.`)}return A.history_from&&Ot.push(J.dim(`No values can be worked out before ${Ae(A.history_from)}, where the history starts.`)),S&&Ot.push(J.orange(`Stopped at ${Ae(S.day)}: ${S.reason}; ${b} earlier day${b===1?"":"s"} without a measured value ${b===1?"was":"were"} not fetched.`)),Ot.join(`
82
- `)})}async function $s(e,t){let n=t.trim().toLowerCase(),o=(await vn(e)).find(i=>typeof i.name=="string"&&i.name.trim().toLowerCase()===n);if(!o||typeof o.id!="string")throw H("unknown_metric",`no metric in this workspace is named "${t.trim()}"`,"See their names: closedloop metrics list");return o.id}async function vc(e,t,n){let o=await se(e,t,Ke,"metrics","closedloop metrics list")??await $s(e,t);if(!n.yes){if(!e.interactive)throw new p("confirmation_required","deleting a metric needs confirmation",g.Usage,`Run it again with --yes: closedloop metrics delete ${ee(t,e.runtime.platform)} --yes`);if((await e.runtime.prompt("Delete this metric? It comes off each of your goals that measures it (a key result takes its indicators); its history stays. Type yes to confirm: ")).trim().toLowerCase()!=="yes"){e.print.result({status:"cancelled",id:o},()=>"Nothing changed.");return}}let i=await _r(e,o,n.reason),r=i.removed.filter(w),s={status:"deleted",id:o,archived_at:w(i.metric)?i.metric.archived_at:null,removed:r};e.print.result(s,a=>{let l=a.removed.map(c=>{let d=Array.isArray(c.kpi_ids)?c.kpi_ids.length:0;return`${String(c.title)} (${d} metric${d===1?"":"s"})`});return`Deleted the metric.${l.length?` It came off ${l.length} goal${l.length===1?"":"s"}: ${l.join(", ")}.`:""} Its history stays.`})}function vs(e,t){let n="The segment: its name, a number from the last segments list, a segment id, or its rule as a JSON file, inline JSON or - for standard input.",o=`The calculation: count, or JSON (inline, a file, or -). ${Vt}.`,i=`A formula over saved metrics instead of a segment and a calculation. ${Gt}`;e.command("define").argument("<name>",`The metric's name, e.g. "share that turned on payments".`).description("Save a calculation over a segment, or a formula over saved metrics, as a named metric (version 1). It is measured every night; a change makes a new version.").option("--segment <segment>",n).option("--calc <calc>",o).option("--formula <formula>",i).option("--description <text>","What the metric shows.").action(async(r,s)=>hc(t,r,s)),e.command("preview").description("Measure a calculation over a segment, or a formula over saved metrics, now without saving it: the value, a share's totals, and how many members have no value.").option("--segment <segment>",n).option("--calc <calc>",o).option("--formula <formula>",i).action(async r=>gc(t,r)),e.command("list").description("Your workspace's metrics, numbered, each with its calculation, segment and latest measured value.").action(async()=>wc(t)),e.command("value").argument("<metric>","A number from the last metrics list, or a metric id.").description("The metric's value on a past day: the number measured that night, else computed from the history; says when it cannot be.").option("--as-of <day>","The day, YYYY-MM-DD, before today.").option("--metric-version <number>","Which version of the metric; the current one by default.").action(async(r,s)=>_c(t,r,s.asOf,s.metricVersion)),e.command("history").argument("<metric>","A number from the last metrics list, a metric id, or its name.").description("The metric day by day, oldest first: the value measured that night, else worked out from the history, else why there is none; and the change from the first value to the last.").option("--days <n>","How many days back, today included (default 30).").option("--every <step>","day (the default) or week: one value a week, on the same weekday as today.").option("--metric-version <number>","Which version of the metric; the current one by default.").action(async(r,s)=>$c(t,r,s)),e.command("delete").argument("<metric>","A number from the last metrics list, a metric id, or its name.").description("Delete a metric: it comes off each of your goals that measures it (a key result takes its indicators), and its history stays. Refused when a teammate's goal measures it, when it is an active goal's only key result, or when a formula uses it.").option("--reason <text>","Why, kept in each goal's history.").option("--yes","Skip the confirmation.").action(async(r,s)=>vc(t,r,s))}var Cs="last-metrics.json",Mn=(e,t,n)=>new p(e,t,g.Usage,n),Cc=/^\d{4}-\d{2}-\d{2}$/,ue=e=>typeof e=="string"&&Cc.test(e)?q(`${e}T00:00:00Z`):String(e),I=e=>typeof e=="number"&&Number.isFinite(e)?e:null;function As(e){return e===7?"week":e===1?"day":`${e} days`}function _t(e){return e===7?"weekly":e===1?"daily":`${e}-day`}function kc(e,t){if(t===7)return`about ${e} week${e===1?"":"s"}`;let n=e*t;return`about ${n} day${n===1?"":"s"}`}var ks=e=>`${e>0?"+":""}${O(e)}`;function bt(e,t){let n=I(e.every_days)??7,o=As(n),i=I(e.lag_points),r=I(e.correlation),s=I(e.moves)??0,a=I(e.followed)??0,l=i!==null?kc(i,n):"",u=e.direction==="opposite";if(e.verdict==="leads")return`Leads: it moved ${l} before the key result${u?", in the other direction (when it rose, the key result fell)":""}, ${a} of ${s} times (correlation of ${_t(n)} changes ${r===null?"?":r.toFixed(2)}).`;if(e.verdict==="not_enough_history"){let d=I(e.points)??0,m=e.history_from?` from ${ue(e.history_from)}`:"",h=w(e.history_ends)?e.history_ends:null,f=h?.series==="indicator"?"the indicator":"the key result",y=h?h.basis==="not_reached"?` The ${o} of ${ue(h.day)} was not read in time; try again.`:h.basis==="failed"?` ${f[0].toUpperCase()}${f.slice(1)} could not be read for ${ue(h.day)}${typeof h.reason=="string"?` (${h.reason})`:""}.`:typeof h.reason=="string"?` For ${f}, ${h.reason.replace(/^not read: /,"")}.`:"":"";return`Not enough history to tell: ${d} ${_t(n)} point${d===1?"":"s"}${m}; at least ${String(e.needed_points)} are needed.${y}`}let c=I(t.min_correlation);switch(e.reason){case"moves_together":return`No lead: it changes in the same ${o} as the key result, not before it (correlation ${r===null?"?":r.toFixed(2)}).`;case"weak":return`No lead: its changes match the key result's later changes only weakly (best: correlation ${r===null?"none":r.toFixed(2)}${l?` ${l} later`:""}; a lead needs ${c??.5} or more either way).`;case"too_few_moves":return`No lead shown: it made only ${s} clear move${s===1?"":"s"} in this time; at least ${String(t.min_moves??3)} are needed.`;case"not_followed_often":return`No lead: the key result followed only ${a} of its ${s} moves ${l} later; a lead needs two of every three.`;case"indicator_flat":return"No lead: the indicator did not change in this time.";case"key_result_flat":return"No lead: the key result did not change in this time, so nothing can be shown to lead it.";default:return"No lead found."}}function Sc(e){let t=Array.isArray(e.learnings)?e.learnings.filter(w):[];if(!t.length)return[];let n=As(I(e.every_days)??7);return["When the key result did not follow:",...t.map(o=>{let i=I(o.indicator_from)??0,r=I(o.indicator_to)??0,s=(I(o.key_result_to)??0)-(I(o.key_result_from)??0),a=I(o.key_result_usual_change)??0;return` ${ue(o.from_day)} to ${ue(o.day)}: the indicator ${r>=i?"rose":"fell"} from ${O(i)} to ${O(r)}; from ${ue(o.key_result_from_day)} to ${ue(o.key_result_day)} the key result changed by ${ks(s)} (usually ${ks(a)} a ${n}).`})]}function Ac(e){let t=Array.isArray(e)?e.filter(w):[],n=t.filter(i=>i.basis==="measured"&&I(i.value)!==null).length,o=t.filter(i=>i.basis==="rebuilt"&&I(i.value)!==null).length;return[n?`${n} measured`:"",o?`${o} rebuilt from the history`:""].filter(Boolean).join(", ")}function Oc(e){let t=Array.isArray(e)?e.filter(w):[];if(!t.length||!t.every(o=>o.basis==="not_read"))return null;let n=typeof t[0].reason=="string"?t[0].reason.replace(/^not read: /,""):"";return n?`${n[0].toUpperCase()}${n.slice(1)}, so the indicator was not read.`:"The indicator was not read."}function qn(e){switch(e){case"cut_short":return"it took longer than ClosedLoop AI's background runs allow";case"interrupted":return"the background run stopped while computing it";case"rule_invalid":return"a rule it reads can no longer be used as written";case"point_timeout":return"a point of its history took too long to read";case"point_failed":return"a point of its history could not be read";default:return`a read failed${typeof e=="string"?` (${e})`:""}`}}function Tc(e){return e.status==="failed"?`Could not be finished: ${qn(e.error_code)}. Ask again tomorrow.`:e.status==="running"?"Being computed now.":"Queued: ClosedLoop AI computes it in the background, usually within minutes."}var ii=e=>Array.isArray(e.checks)?e.checks.filter(w):[],Hn=e=>w(e.counts)?(I(e.counts.queued)??0)+(I(e.counts.running)??0):0,Gn=e=>w(e.indicator)&&typeof e.indicator.name=="string"?e.indicator.name:"the indicator";function Rc(e,t,n){let o=e.print.style,i=w(t.rules)?t.rules:{},r=w(t.key_result)&&typeof t.key_result.name=="string"?t.key_result.name:"the metric",s=ii(t).find(y=>w(y.indicator)&&y.indicator.id===n)??ii(t)[0];if(!s)return`No lead check of this pair yet. Ask for it: closedloop metrics leads ${String(w(t.key_result)?t.key_result.id:"")} --indicator ${n}`;let a=[o.bold(`Does "${Gn(s)}" lead "${r}"?`)],l=w(s.check)?s.check:null;if(s.status!=="done"||!l)return[...a,Tc(s)].join(`
83
- `);a.push(bt(l,i));let u=w(s.series)?s.series:{},c=Oc(u.indicator);c&&a.push(c);let d=y=>Array.isArray(y)?y.filter(w).map(_=>I(_.value)).filter(_=>_!==null):[],m=I(l.points)??0;if(m&&l.history_from){let y=Ac(u.key_result);a.push(o.dim(`${m} ${_t(I(l.every_days)??7)} points from ${ue(l.history_from)}${y?` (key result: ${y})`:""}; checked for ${ue(s.as_of)}.`))}let h=d(u.indicator),f=d(u.key_result);return h.length>1&&f.length>1&&a.push(o.dim(`indicator ${o.sparkline(h)}`),o.dim(`key result ${o.sparkline(f)}`)),a.push(...Sc(l)),a.join(`
84
- `)}function Ec(e,t){let n=t.map(i=>w(i.check)?i.check:{});if(!n.length)return null;if(n.every(i=>i.verdict==="not_enough_history"&&w(i.history_ends)&&i.history_ends.series==="key_result")){let i=n[0],r=w(i.history_ends)?i.history_ends:{},s=typeof r.reason=="string"?` (${r.reason})`:"";return`Not enough history of "${e}" to find what leads it: ${String(i.points)} ${_t(I(i.every_days)??7)} points${i.history_from?` from ${ue(i.history_from)}`:""}${s}; at least ${String(i.needed_points)} are needed.`}return n.every(i=>i.reason==="key_result_flat")?`"${e}" did not change over the last ${String(n[0].points)} ${_t(I(n[0].every_days)??7)} points, so nothing can be shown to lead it.`:null}function Ic(e,t,n){let o=e.print.style;if(typeof t.not_supported=="string")return t.not_supported;let i=w(t.rules)?t.rules:{},r=w(t.key_result)&&typeof t.key_result.name=="string"?t.key_result.name:"this metric",s=ii(t).filter(f=>w(f.indicator)&&f.indicator.kind==="candidate"),a=s.filter(f=>f.status==="done"&&w(f.check)),l=Ec(r,a),u=[];if(l)u.push(l);else{let f=Array.isArray(t.suggestions)?t.suggestions.filter(w):[],y=a.find(b=>w(b.check)&&b.check.history_from),_=y&&w(y.check)?o.dim(` (${String(y.check.points)} ${_t(I(y.check.every_days)??7)} points from ${ue(y.check.history_from)})`):"";u.push(o.bold(`Indicators that led "${r}" on this workspace's history`)+_),f.length||u.push(a.length?"None of the candidates checked so far led it.":"No candidate has been checked yet."),f.forEach((b,F)=>{let oe=w(b.check)?b.check:{},G=w(b.indicator)?b.indicator:{};u.push(`${F+1} ${o.bold(String(G.name))}`,` ${bt(oe,i)}`,o.dim(` Save it: ${fn(G.rule,At=>`closedloop metrics define ${ee(`${r}: ${String(G.name)}`,e.runtime.platform)} --segment ${At} --calc count`,e.runtime.platform)}`))});let S=a.filter(b=>w(b.check)&&b.check.verdict!=="leads");if(S.length){u.push("","Checked, no lead found:");for(let b of S)u.push(` ${Gn(b)}: ${bt(b.check,i)}`)}}let c=s.filter(f=>f.status==="failed");if(c.length){u.push("",o.orange("Could not be finished:"));for(let f of c)u.push(o.orange(` ${Gn(f)}: ${qn(f.error_code)}`))}let d=s.filter(f=>f.status==="queued"||f.status==="running");d.length&&u.push("",`Still being checked (${d.length}): ${d.map(Gn).join("; ")}.`,o.dim(`ClosedLoop AI checks them in the background. See them: closedloop metrics leads ${n} --wait`));let m=Array.isArray(t.skipped)?t.skipped.filter(w):[];if(m.length){u.push("",o.dim("Not checked, no data for them:"));for(let f of m)u.push(o.dim(` ${String(f.name)}: ${String(f.reason)}`))}let h=Array.isArray(t.unknown)?t.unknown.filter(f=>typeof f=="string"):[];return h.length&&u.push("",o.orange(`Unknown candidates: ${h.join(", ")}.`)),u.join(`
85
- `)}var Ss=15e3,ri=30*6e4;async function Pc(e,t,n,o){let i=o,r=e.print.spinner(`Waiting for ClosedLoop AI to finish ${Hn(i)} lead check${Hn(i)===1?"":"s"} (up to ${ri/6e4} minutes)`);try{for(let s=0;s<ri/Ss&&Hn(i)>0;s+=1){await e.runtime.sleep(Ss);let a=await v(e.runtime,t,"GET",n);if(a.status!==200)throw C(a,"api_error","The lead checks could not be read. Try again.");i={...xe(a.body),...xc(o,["queued","skipped","unknown","not_supported"])}}}finally{r.stop()}return i}var xc=(e,t)=>Object.fromEntries(t.filter(n=>e[n]!==void 0).map(n=>[n,e[n]]));async function Nc(e,t,n){let o=await se(e,t,Cs,"metrics","closedloop metrics list");if(o===null)throw Mn("invalid_id",`'${t}' is not a metric id or a number from the last metrics list`,"List them: closedloop metrics list");if(n.indicator!==void 0&&n.candidates!==void 0)throw Mn("indicator_and_candidates","give --indicator or --candidates, not both");let i=n.indicator===void 0?void 0:await se(e,n.indicator,Cs,"metrics","closedloop metrics list");if(i===null)throw Mn("invalid_id",`'${n.indicator}' is not a metric id or a number from the last metrics list`,"List them: closedloop metrics list");let r=n.metricVersion;if(r!==void 0&&!/^[1-9][0-9]*$/.test(r))throw Mn("invalid_version","--metric-version must be a version number");let s={...i&&{indicator:i},...n.candidates&&{candidates:n.candidates},...r&&{version:Number(r)}},a=$(e.runtime),l=await v(e.runtime,a,"POST",`/api/metrics/${o}/leads`,s);if(l.status===404){let h=typeof l.body.error=="string"&&l.body.error?l.body.error:"There is no such metric.";throw new p("not_found",h,g.Failure,"See them: closedloop metrics list")}if(l.status!==200)throw C(l,"api_error","The lead check could not be asked for. Try again.");let u=xe(l.body),c=new URLSearchParams({...i&&{indicator:i},...r&&{version:r}}).toString();n.wait&&(u=await Pc(e,a,`/api/metrics/${o}/leads${c?`?${c}`:""}`,u));let d=`${i?` --indicator ${n.indicator}`:""}${r?` --metric-version ${r}`:""}`,m=Hn(u)>0?`closedloop metrics leads ${t}${d} --wait`:i?"closedloop metrics list":`closedloop metrics leads ${t} --indicator <metric>`;e.print.result({...u,next:m},h=>i?Rc(e,h,i):Ic(e,h,t))}function Os(e,t){e.command("leads").argument("<metric>","The key result: a number from the last metrics list, or a metric id.").description("Whether an indicator's history moves before this metric's, and when it moved and the metric did not follow; without --indicator, the indicators that led it on this workspace's history. ClosedLoop AI computes the checks in the background; this asks for them and shows what is known.").option("--indicator <metric>","The indicator to check: a number from the last metrics list, or a metric id.").option("--candidates <key...>","Only these candidates (keys from an earlier answer).").option("--metric-version <number>","Which version of the key result (a goal's key result stays on the version the goal pins); the current one if not given.").option("--wait",`Wait until the checks are done (up to ${ri/6e4} minutes).`).action(async(n,o)=>Nc(t,n,o))}var Es="last-kpis.json",Ts=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Lc=/^\d{4}-\d{2}-\d{2}$/,Jn="A target is one line: >=1400, <=0.3, between 40 and 60, +500, +10% or, for a share, +5pts.",Bt=(e,t,n)=>new p(e,t,g.Usage,n),Dc=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Xe=(e,t,n=t)=>Dc(e,t)?e[t]:n,jc={week:"weekly",month:"monthly",quarter:"quarterly"},Fc={cli:"through the CLI",app:"in the app",mcp:"through an AI assistant",api:"through the API",job:"automatically"},Is={key_result:"key result",indicator:"indicator"},ai={key_result:"as a key result",indicator:"as an indicator"},ce=e=>typeof e=="string"&&Lc.test(e)?q(`${e}T00:00:00Z`):String(e??"an unknown day"),Ps=e=>new Date(Date.parse(`${e}T00:00:00Z`)-864e5).toISOString().slice(0,10),Yt=e=>e.period==="days"?`every ${e.period_days} days`:Xe(jc,e.period),ze=(e,t)=>t===null?"no value":Ft(e)?`${(t*100).toFixed(1)}%`:fe(e)?Se({},t,null,null):O(t);function li(e){let t=e.written,n=t.value<0?"-":"+",o=Math.abs(t.value)>0&&Math.abs(t.value)<1?String(Number(Math.abs(t.value).toPrecision(3))):O(Math.abs(t.value));return t.as==="percent"?`${n}${o}%`:t.as==="points"?`${n}${o} points`:`${n}${o}`}function xs(e,t){return t.condition==="between"?t.low===null||t.high===null?null:`between ${ze(e,t.low)} and ${ze(e,t.high)}`:t.condition==="at_least"?t.low===null?null:`at least ${ze(e,t.low)}`:t.high===null?null:`at most ${ze(e,t.high)}`}function ui(e){let t=e.target;if(!t)return"no target yet";let n=e.data_series.calc,o=xs(n,t);if(t.written.as==="number")return o??"a target without a bound";let i=e.baseline&&e.baseline.value!==null?`from ${ze(n,e.baseline.value)} on ${ce(e.baseline.as_of)}`:"from the baseline";return o?`${o} (${li(t)} ${i})`:`${li(t)} from the baseline, once it is measured`}function Ns(e,t){let n=t.target,o=n?xs(t.calc,n)??`${li(n)} from the baseline, once it is measured`:"no target yet",i=t.current,r=i?(fe(t.calc)?Se(t.calc,i.value,i.numerator,i.denominator):Ve(t.calc,i.value,i.numerator,i.denominator))??"no value":null,s=i?`now ${r} on ${ce(i.as_of)}${i.status==="stale"?` ${e.orange("(stale)")}`:""}`:"not measured yet";return`${t.name}: ${o}, ${Yt(t)} \xB7 ${s}`}function ci(e,t=!1){return!e.baseline&&t?"baseline measured when the goal is activated":e.baseline?`baseline ${ze(e.data_series.calc,e.baseline.value)} on ${ce(e.baseline.as_of)}`:"baseline not measured yet (it is measured tonight)"}function Ls(e,t,n=!1){if(!t.current)return"not measured yet";let o=t.current;return`now ${(fe(t.data_series.calc)?Se(t.data_series.calc,o.value,o.numerator,o.denominator):Ve(t.data_series.calc,o.value,o.numerator,o.denominator))??"no value"} on ${ce(o.as_of)}${o.status==="stale"?` ${e.orange("(stale: a source it reads is late)")}`:""}`+(n?e.dim(" (a preview: periods start when the goal is activated)"):"")}function Uc(e,t,n){let o=`${ce(n.start)} to ${ce(Ps(n.end))}`;if(n.value===null||n.value===void 0)return`${o}: ${n.status==="failed"||n.status==="not_measured"?"no value":`not measured yet (the run of ${ce(n.end)} settles it)`}`;let i=n.result==="met"?e.green("met"):n.result==="missed"?e.red("missed"):"not judged yet";return`${o}: ${ze(t.data_series.calc,n.value)}, ${i}${n.status==="stale"?e.dim(" (stale)"):""}`}function Wc(e,t,n){return t?t.status==="done"&&t.check?`lead check of ${ce(t.as_of)}: ${bt(t.check,n)}`:t.status==="failed"?e.orange(`lead check of ${ce(t.as_of)} could not be finished: ${qn(t.error_code)}`):e.dim(`lead check ${t.status==="running"?"being computed now":"queued"}: ClosedLoop AI computes it in the background; list the goal again later`):e.dim("lead check: none (an archived metric is not checked)")}function Mc(e,t,n,o,i={},r=!1){let s=" ".repeat(n.length+2),a=t.role==="indicator"?`indicator of ${t.key_result_id&&o.has(t.key_result_id)?o.get(t.key_result_id):"a key result"}`:"key result",l=t.data_series,u=`version ${l.version}${l.current_version!==null&&l.current_version!==l.version?` (the metric is at version ${l.current_version})`:""}`,d=[`${n} ${e.bold(l.name)}${e.dim(` \xB7 ${a} \xB7 ${Yt(t)} \xB7 ${u}`)}`+(t.archived_at?e.dim(` \xB7 archived ${q(t.archived_at)}`):""),`${s}target ${ui(t)} \xB7 ${ci(t,r)}`,`${s}${Ls(e,t,r)}`];for(let m of t.periods)d.push(`${s}${Uc(e,t,m)}`);if(t.current_period&&d.push(`${s}${e.dim(`this period: ${ce(t.current_period.start)} to ${ce(Ps(t.current_period.end))}`)}`),t.role==="indicator"&&t.lead_check!==void 0&&d.push(`${s}${Wc(e,t.lead_check,i)}`),t.role==="key_result"&&t.suggested_indicators?.length){d.push(`${s}suggested indicators (they led it on this workspace's history):`);for(let m of t.suggested_indicators)d.push(`${s} ${e.bold(typeof m.indicator.name=="string"?m.indicator.name:"an indicator")}: ${m.check?bt(m.check,i):""}`);d.push(`${s}${e.dim(`save one as a metric: closedloop metrics leads ${l.id}${l.current_version!==null&&l.current_version!==l.version?` --metric-version ${l.version}`:""}`)}`)}return d.push(`${s}${e.dim(`id ${t.id}`)}`),d}function di(e,t,n={},o=!1){let i=String(t.length).length,r=new Map(t.map((s,a)=>[s.id,String(a+1)]));return t.map((s,a)=>Mc(e,s,String(a+1).padStart(i),r,n,o).join(`
59
+ ${e.dim(Yo)}
60
+ ${e.dim("Read one in full: closedloop fields show <key>")}`}function Zu(e,t,n){let o=[`${rs(e,t)}${e.dim(` \xB7 ${ee(t.entity)}`)}`];ee(t.id)&&t.id!==t.key&&o.push(R(e,"",t.id)),ee(t.description)&&o.push(ee(t.description)),t.example!==void 0&&t.example!==null&&o.push(e.dim(`e.g. ${JSON.stringify(t.example)}`)),typeof t.history=="string"&&o.push(e.dim(`history: ${Vu(ns,t.history)?ns[t.history]:t.history}`));let i=Array.isArray(t.confused_with)?t.confused_with.filter(l=>typeof l=="string"):[];i.length&&o.push(`Often confused with ${i.join(", ")}. When a request does not say which one it means, ask which, showing the number with each (a preview gives both).`),(t.pii==="email"||t.pii==="phone"||t.pii===!0)&&o.push(e.dim(`It holds ${t.pii==="phone"?"phone numbers":t.pii==="email"?"email addresses":"email addresses or phone numbers"}, so only their pattern is shown.`));let r=w(t.profile)?t.profile:null;if(r){let l=Array.isArray(r.values)?r.values.filter(w):[];if(typeof r.pattern=="string")o.push("",`Values look like ${r.pattern}.`);else if(r.kind==="text")o.push("",`Free text${typeof r.distinct_count=="number"?` with ${A(r.distinct_count)} different values`:""}; no values are kept.`);else if(l.length){let c=Math.max(...l.map(d=>os(d.value).length)),m=Math.max(...l.map(d=>A(d.count).length));if(o.push("","Values:",...l.map(d=>` ${os(d.value).padEnd(c)} ${A(d.count).padStart(m)}`)),typeof r.hidden_values!="number"){let d=Yu(r,l.length);d>0&&o.push(e.dim(` and ${A(d)} more`))}}else if(r.hidden===!0)o.push("",`Not shown: fewer than ${A(r.hidden_below)} customers hold the values at one end of the range.`);else if(ae(r.p10)||ae(r.p90)||typeof r.zero_share=="number"&&r.zero_share>0)o.push("",zu(r).join(" \xB7 "));else if(Ku(r))o.push("","no values yet: no record has one.");else if(ae(r.min)||ae(r.max)){let c=[`smallest ${A(r.min)}`,...ae(r.median)?[`median ${A(r.median)}`]:[],...ae(r.p90)?[`90th percentile ${A(r.p90)}`]:[],`largest ${A(r.max)}`];o.push("",c.join(" \xB7 "))}else(ae(r.first)||ae(r.last))&&o.push("",`first ${Vo(r.first)} \xB7 last ${Vo(r.last)}`);typeof r.hidden_values=="number"&&r.hidden_values>0&&o.push(e.dim(`${A(r.hidden_values)} values held by fewer than ${A(r.hidden_below)} customers are not shown.`));let u=Bu(r);u!==null&&o.push(`${k(r.empty_count)} of ${k(r.record_count)} records have no value (${u}).`)}let a={of:!ee(t.entity)||t.entity==="usage"?"customer":ee(t.entity),where:{field:ee(t.key),op:"exists"}};return o.push("",e.dim(`Use it in a rule: ${gn(a,l=>`closedloop segments preview --rule ${l}`,n)}`)),o.join(`
61
+ `)}async function is(e,t,n){if(t!==void 0&&!Bo.includes(t))throw new p("invalid_entity",`--entity must be one of ${Bo.join(", ")}`,g.Usage);let o=await pr(e,{entity:t,q:n}),i=n===void 0?o.filter(s=>!Xu(s)):o,r={filter:{entity:t??null,q:n??null},fields:i,ask:Yo,next:"closedloop fields show <key>"};e.print.result(r,s=>Qu(e.print.style,s.fields,n,o.length-i.length))}async function ec(e,t){let o={field:await gr(e,t),next:"closedloop segments preview --rule <file|json|->"};e.print.result(o,i=>Zu(e.print.style,i.field,e.runtime.platform))}var ss=(e,t)=>{let n=e.command("fields").alias("field").description(`The named fields a segment or a metric is written in: what each means, a preview of this workspace's values and how to filter on it. ${Yo}`),o=`Only one kind of record: ${Bo.join(", ")}.`;n.command("list").description("Every field that holds values, one line each: its key, type and label, by the kind of record it describes.").option("--entity <entity>",o).action(async i=>is(t,i.entity,void 0)),n.command("search").argument("<words...>","Words in the field's key, label or description, e.g. property type.").description("The fields whose key, label or description matches the words.").option("--entity <entity>",o).action(async(i,r)=>is(t,r.entity,i.join(" ").trim())),n.command("show").argument("<key>","A field key, e.g. customer.country or customer.crm.Property_Type__c, or the field's id when the list shows one.").description("One field in full: what it means, every value with how many records have it, and how many have none.").action(async i=>ec(t,i))};var tc=[{term:"Product structure",definition:"The products, areas and features a team confirms (closedloop products). Insights are filed against them."},{term:"Product",definition:"One of the canonical Products a team owns. Products, the Areas under them and the Features under those are how the product is organised."},{term:"Area",definition:"A part of a Product. Areas and Features are the parts an Insight is filed against."},{term:"Feature",definition:"A part of the product that exists (Checkout, Reporting), never planned work. It sits under an Area or a Product, and Insights are filed against it."},{term:"Insight",definition:"One source-backed product problem, request, bug or improvement extracted from customer evidence. One conversation can produce several."},{term:"Opportunity",definition:"A customer problem, clustered from related Insights and split into Needs; can span products. Shows customer impact and whether delivery work covers it. The top of the discovery layer."},{term:"Need",definition:"One specific need inside an Opportunity, discovered by sub-clustering and backed by Insights. Has one place in the product layer: the Feature holding most of its Insights, else that Area, else that Product."},{term:"Customer",definition:"The person or account that gave feedback."},{term:"Competitor",definition:"A third party customers mention that competes with your product (closedloop competitors). Insights that name one count as competitor mentions."}],as=(e,t)=>{e.command("glossary").description("What the words mean: product structure, product, area, feature, insight, opportunity, need, customer and competitor.").action(()=>t.print.result({terms:tc},n=>n.terms.map(o=>`${t.print.style.bold(o.term)}: ${o.definition}`).join(`
62
+ `)))};import{existsSync as Ic,readFileSync as xc,statSync as Pc}from"node:fs";var cs="closedloop metrics list",qt='A formula combines saved metrics by their ids, e.g. "<id> / <id>", "<id> * 12" or "(<id> - <id>@90d) / <id>@90d" (@90d: its value 90 days earlier); write each id alone or in brackets, [<id>], as closedloop metrics list shows it.',Oe=e=>new p("invalid_formula",e,g.Usage,qt),ls=e=>new p("invalid_formula",`${e} is not a metric id: a formula names each metric by its id, alone or in brackets`,g.Usage,L(cs)),ds={"+":"add","-":"subtract","*":"multiply","/":"divide"},us={add:"+",subtract:"-",multiply:"*",divide:"/"};function nc(e){let t=[],n=0,o=()=>!t.length||t[t.length-1].kind==="op"||t[t.length-1].kind==="open";for(;n<e.length;){let i=e[n];if(/\s/.test(i)){n+=1;continue}let r=null;if(i==="["){let a=e.indexOf("]",n+1);if(a<0)throw Oe(`a metric opened with [ at ${n+1} is not closed with ]`);let l=e.slice(n+1,a).trim();if(!l)throw new p("invalid_formula",`an empty [] at ${n+1}: put a metric's id in it`,g.Usage,L(cs));if(!Lt.test(l))throw ls(`[${l}]`);r={kind:"id",text:l.toLowerCase()},n=a+1}else{if(i==="#")throw ls(/^#\d*/.exec(e.slice(n))?.[0]||"#");Lt.test(e.slice(n,n+36))&&(r={kind:"id",text:e.slice(n,n+36).toLowerCase()},n+=36)}if(r){let a=/^\s*@\s*(\d+)\s*d\b/i.exec(e.slice(n));if(!a&&/^\s*@/.test(e.slice(n)))throw Oe(`an earlier value is written @<days>d, e.g. @90d (at ${n+1})`);let l=a?Number(a[1]):void 0;if(l===0)throw Oe("@0d is the value on the same day: leave it out");t.push({kind:"metric",name:r,...l!==void 0&&{days:l}}),a&&(n+=a[0].length);continue}if(/^\d+(?:\.\d+)?|^\.\d+/.exec(e.slice(n))||i==="-"&&o()&&/^-\s*(\d|\.\d)/.test(e.slice(n))){let a=/^-?\s*(\d+(?:\.\d+)?|\.\d+)/.exec(e.slice(n));t.push({kind:"num",value:Number(a[0].replace(/\s+/g,""))}),n+=a[0].length;continue}if(i in ds){t.push({kind:"op",value:i}),n+=1;continue}if(i==="("){t.push({kind:"open"}),n+=1;continue}if(i===")"){t.push({kind:"close"}),n+=1;continue}throw Oe(`'${i}' at ${n+1} is not part of a formula`)}return t}function oc(e){let t=0,n=()=>{let a=e[t];if(!a)throw Oe("the formula ends where a metric or a number is expected");if(t+=1,a.kind==="num")return a.value;if(a.kind==="metric")return{metric:a.name,...a.days!==void 0&&{days_ago:a.days}};if(a.kind==="open"){let l=r();if(e[t]?.kind!=="close")throw Oe("a ( is not closed with )");return t+=1,l}throw Oe(`a metric or a number is expected where ${a.kind==="op"?a.value:")"} is`)},o=(a,l)=>{let u=a();for(;e[t]?.kind==="op"&&l.includes(e[t].value);){let c=ds[e[t].value];t+=1,u={fn:"formula",op:c,args:[u,a()]}}return u},i=()=>o(n,"*/"),r=()=>o(i,"+-"),s=r();if(t<e.length)throw Oe("the formula goes on after it is complete; join the parts with + - * or /");return s}function ms(e){let t=oc(nc(e));if(typeof t=="number"||!("fn"in t))throw Oe("a formula combines at least two things with + - * or /");return t}function Ko(e,t){let n=o=>typeof o=="number"?o:"metric"in o?{metric:t(o.metric),...o.days_ago!==void 0&&{days_ago:o.days_ago}}:Ko(o,t);return{fn:"formula",op:e.op,args:[n(e.args[0]),n(e.args[1])]}}var ye=e=>w(e)&&e.fn==="formula";function Pn(e,t=new Map){if(typeof e=="number")return String(e);if(!w(e))return"?";if(typeof e.metric=="string"){let r=t.get(e.metric)??e.metric,s=typeof e.days_ago=="number"?`@${e.days_ago}d`:"";return`[${r}]${s}`}let n=Array.isArray(e.args)?e.args:[],o=r=>ye(r)?`(${Pn(r,t)})`:Pn(r,t),i=typeof e.op=="string"&&e.op in us?us[e.op]:String(e.op);return`${o(n[0])} ${i} ${o(n[1])}`}function Ae(e,t,n,o){if(typeof t!="number"||!Number.isFinite(t))return null;let i=t!==0&&Math.abs(t)<1?String(Number(t.toPrecision(3))):Number.isInteger(t)?k(t):new Intl.NumberFormat(void 0,{maximumFractionDigits:2}).format(t),r=a=>Number.isInteger(a)?k(a):String(Number(a.toPrecision(6))),s=w(e)&&e.op==="divide"&&typeof n=="number"&&typeof o=="number"?` (${r(n)} / ${r(o)})`:"";return`${i}${s}`}var zo="closedloop metrics list",ps=(e,t,n)=>new p(e,t,g.Usage,n),ic=/^\d{4}-\d{2}-\d{2}$/,le=e=>typeof e=="string"&&ic.test(e)?V(`${e}T00:00:00Z`):String(e),P=e=>typeof e=="number"&&Number.isFinite(e)?e:null;function fs(e){return e===7?"week":e===1?"day":`${e} days`}function yt(e){return e===7?"weekly":e===1?"daily":`${e}-day`}function rc(e,t){if(t===7)return`about ${e} week${e===1?"":"s"}`;let n=e*t;return`about ${n} day${n===1?"":"s"}`}var gs=e=>`${e>0?"+":""}${A(e)}`;function wt(e,t){let n=P(e.every_days)??7,o=fs(n),i=P(e.lag_points),r=P(e.correlation),s=P(e.moves)??0,a=P(e.followed)??0,l=i!==null?rc(i,n):"",u=e.direction==="opposite";if(e.verdict==="leads")return`Leads: it moved ${l} before the key result${u?", in the other direction (when it rose, the key result fell)":""}, ${a} of ${s} times (correlation of ${yt(n)} changes ${r===null?"?":r.toFixed(2)}).`;if(e.verdict==="not_enough_history"){let m=P(e.points)??0,d=e.history_from?` from ${le(e.history_from)}`:"",h=w(e.history_ends)?e.history_ends:null,f=h?.series==="indicator"?"the indicator":"the key result",y=h?h.basis==="not_reached"?` The ${o} of ${le(h.day)} was not read in time; try again.`:h.basis==="failed"?` ${f[0].toUpperCase()}${f.slice(1)} could not be read for ${le(h.day)}${typeof h.reason=="string"?` (${h.reason})`:""}.`:typeof h.reason=="string"?` For ${f}, ${h.reason.replace(/^not read: /,"")}.`:"":"";return`Not enough history to tell: ${m} ${yt(n)} point${m===1?"":"s"}${d}; at least ${String(e.needed_points)} are needed.${y}`}let c=P(t.min_correlation);switch(e.reason){case"moves_together":return`No lead: it changes in the same ${o} as the key result, not before it (correlation ${r===null?"?":r.toFixed(2)}).`;case"weak":return`No lead: its changes match the key result's later changes only weakly (best: correlation ${r===null?"none":r.toFixed(2)}${l?` ${l} later`:""}; a lead needs ${c??.5} or more either way).`;case"too_few_moves":return`No lead shown: it made only ${s} clear move${s===1?"":"s"} in this time; at least ${String(t.min_moves??3)} are needed.`;case"not_followed_often":return`No lead: the key result followed only ${a} of its ${s} moves ${l} later; a lead needs two of every three.`;case"indicator_flat":return"No lead: the indicator did not change in this time.";case"key_result_flat":return"No lead: the key result did not change in this time, so nothing can be shown to lead it.";default:return"No lead found."}}function sc(e){let t=Array.isArray(e.learnings)?e.learnings.filter(w):[];if(!t.length)return[];let n=fs(P(e.every_days)??7);return["When the key result did not follow:",...t.map(o=>{let i=P(o.indicator_from)??0,r=P(o.indicator_to)??0,s=(P(o.key_result_to)??0)-(P(o.key_result_from)??0),a=P(o.key_result_usual_change)??0;return` ${le(o.from_day)} to ${le(o.day)}: the indicator ${r>=i?"rose":"fell"} from ${A(i)} to ${A(r)}; from ${le(o.key_result_from_day)} to ${le(o.key_result_day)} the key result changed by ${gs(s)} (usually ${gs(a)} a ${n}).`})]}function ac(e){let t=Array.isArray(e)?e.filter(w):[],n=t.filter(i=>i.basis==="measured"&&P(i.value)!==null).length,o=t.filter(i=>i.basis==="rebuilt"&&P(i.value)!==null).length;return[n?`${n} measured`:"",o?`${o} rebuilt from the history`:""].filter(Boolean).join(", ")}function lc(e){let t=Array.isArray(e)?e.filter(w):[];if(!t.length||!t.every(o=>o.basis==="not_read"))return null;let n=typeof t[0].reason=="string"?t[0].reason.replace(/^not read: /,""):"";return n?`${n[0].toUpperCase()}${n.slice(1)}, so the indicator was not read.`:"The indicator was not read."}function Dn(e){switch(e){case"cut_short":return"it took longer than ClosedLoop AI's background runs allow";case"interrupted":return"the background run stopped while computing it";case"rule_invalid":return"a rule it reads can no longer be used as written";case"point_timeout":return"a point of its history took too long to read";case"point_failed":return"a point of its history could not be read";default:return`a read failed${typeof e=="string"?` (${e})`:""}`}}function uc(e){return e.status==="failed"?`Could not be finished: ${Dn(e.error_code)}. Ask again tomorrow.`:e.status==="running"?"Being computed now.":"Queued: ClosedLoop AI computes it in the background, usually within minutes."}var Xo=e=>Array.isArray(e.checks)?e.checks.filter(w):[],Nn=e=>w(e.counts)?(P(e.counts.queued)??0)+(P(e.counts.running)??0):0,Ln=e=>w(e.indicator)&&typeof e.indicator.name=="string"?e.indicator.name:"the indicator";function cc(e,t,n){let o=e.print.style,i=w(t.rules)?t.rules:{},r=w(t.key_result)&&typeof t.key_result.name=="string"?t.key_result.name:"the metric",s=Xo(t).find(y=>w(y.indicator)&&y.indicator.id===n)??Xo(t)[0];if(!s)return`No lead check of this pair yet. Ask for it: closedloop metrics leads ${String(w(t.key_result)?t.key_result.id:"")} --indicator ${n}`;let a=[o.bold(`Does "${Ln(s)}" lead "${r}"?`)],l=w(s.check)?s.check:null;if(s.status!=="done"||!l)return[...a,uc(s)].join(`
63
+ `);a.push(wt(l,i));let u=w(s.series)?s.series:{},c=lc(u.indicator);c&&a.push(c);let m=y=>Array.isArray(y)?y.filter(w).map(_=>P(_.value)).filter(_=>_!==null):[],d=P(l.points)??0;if(d&&l.history_from){let y=ac(u.key_result);a.push(o.dim(`${d} ${yt(P(l.every_days)??7)} points from ${le(l.history_from)}${y?` (key result: ${y})`:""}; checked for ${le(s.as_of)}.`))}let h=m(u.indicator),f=m(u.key_result);return h.length>1&&f.length>1&&a.push(o.dim(`indicator ${o.sparkline(h)}`),o.dim(`key result ${o.sparkline(f)}`)),a.push(...sc(l)),a.join(`
64
+ `)}function dc(e,t){let n=t.map(i=>w(i.check)?i.check:{});if(!n.length)return null;if(n.every(i=>i.verdict==="not_enough_history"&&w(i.history_ends)&&i.history_ends.series==="key_result")){let i=n[0],r=w(i.history_ends)?i.history_ends:{},s=typeof r.reason=="string"?` (${r.reason})`:"";return`Not enough history of "${e}" to find what leads it: ${String(i.points)} ${yt(P(i.every_days)??7)} points${i.history_from?` from ${le(i.history_from)}`:""}${s}; at least ${String(i.needed_points)} are needed.`}return n.every(i=>i.reason==="key_result_flat")?`"${e}" did not change over the last ${String(n[0].points)} ${yt(P(n[0].every_days)??7)} points, so nothing can be shown to lead it.`:null}function mc(e,t,n){let o=e.print.style;if(typeof t.not_supported=="string")return t.not_supported;let i=w(t.rules)?t.rules:{},r=w(t.key_result)&&typeof t.key_result.name=="string"?t.key_result.name:"this metric",s=Xo(t).filter(f=>w(f.indicator)&&f.indicator.kind==="candidate"),a=s.filter(f=>f.status==="done"&&w(f.check)),l=dc(r,a),u=[];if(l)u.push(l);else{let f=Array.isArray(t.suggestions)?t.suggestions.filter(w):[],y=a.find(b=>w(b.check)&&b.check.history_from),_=y&&w(y.check)?o.dim(` (${String(y.check.points)} ${yt(P(y.check.every_days)??7)} points from ${le(y.check.history_from)})`):"";u.push(o.bold(`Indicators that led "${r}" on this workspace's history`)+_),f.length||u.push(a.length?"None of the candidates checked so far led it.":"No candidate has been checked yet."),f.forEach((b,U)=>{let ne=w(b.check)?b.check:{},J=w(b.indicator)?b.indicator:{};u.push(`${U+1} ${o.bold(String(J.name))}`,` ${wt(ne,i)}`,o.dim(` Save it: ${gn(J.rule,At=>`closedloop metrics define ${he(`${r}: ${String(J.name)}`,e.runtime.platform)} --segment ${At} --calc count`,e.runtime.platform)}`))});let S=a.filter(b=>w(b.check)&&b.check.verdict!=="leads");if(S.length){u.push("","Checked, no lead found:");for(let b of S)u.push(` ${Ln(b)}: ${wt(b.check,i)}`)}}let c=s.filter(f=>f.status==="failed");if(c.length){u.push("",o.orange("Could not be finished:"));for(let f of c)u.push(o.orange(` ${Ln(f)}: ${Dn(f.error_code)}`))}let m=s.filter(f=>f.status==="queued"||f.status==="running");m.length&&u.push("",`Still being checked (${m.length}): ${m.map(Ln).join("; ")}.`,o.dim(`ClosedLoop AI checks them in the background. See them: closedloop metrics leads ${n} --wait`));let d=Array.isArray(t.skipped)?t.skipped.filter(w):[];if(d.length){u.push("",o.dim("Not checked, no data for them:"));for(let f of d)u.push(o.dim(` ${String(f.name)}: ${String(f.reason)}`))}let h=Array.isArray(t.unknown)?t.unknown.filter(f=>typeof f=="string"):[];return h.length&&u.push("",o.orange(`Unknown candidates: ${h.join(", ")}.`)),u.join(`
65
+ `)}var hs=15e3,Qo=30*6e4;async function pc(e,t,n,o){let i=o,r=e.print.spinner(`Waiting for ClosedLoop AI to finish ${Nn(i)} lead check${Nn(i)===1?"":"s"} (up to ${Qo/6e4} minutes)`);try{for(let s=0;s<Qo/hs&&Nn(i)>0;s+=1){await e.runtime.sleep(hs);let a=await $(e.runtime,t,"GET",n);if(a.status!==200)throw C(a,"api_error","The lead checks could not be read. Try again.");i={...De(a.body),...gc(o,["queued","skipped","unknown","not_supported"])}}}finally{r.stop()}return i}var gc=(e,t)=>Object.fromEntries(t.filter(n=>e[n]!==void 0).map(n=>[n,e[n]]));async function hc(e,t,n){let o=I(t,"metric",zo);if(n.indicator!==void 0&&n.candidates!==void 0)throw ps("indicator_and_candidates","give --indicator or --candidates, not both");let i=n.indicator===void 0?void 0:I(n.indicator,"metric",zo),r=n.metricVersion;if(r!==void 0&&!/^[1-9][0-9]*$/.test(r))throw ps("invalid_version","--metric-version must be a version number");let s={...i&&{indicator:i},...n.candidates&&{candidates:n.candidates},...r&&{version:Number(r)}},a=v(e.runtime),l=await $(e.runtime,a,"POST",`/api/metrics/${o}/leads`,s);if(l.status===404){let h=typeof l.body.error=="string"&&l.body.error?l.body.error:"There is no such metric.";throw new p("not_found",h,g.Failure,"See them: closedloop metrics list")}if(l.status!==200)throw C(l,"api_error","The lead check could not be asked for. Try again.");let u=De(l.body),c=new URLSearchParams({...i&&{indicator:i},...r&&{version:r}}).toString();n.wait&&(u=await pc(e,a,`/api/metrics/${o}/leads${c?`?${c}`:""}`,u));let m=`${i?` --indicator ${i}`:""}${r?` --metric-version ${r}`:""}`,d=Nn(u)>0?`closedloop metrics leads ${o}${m} --wait`:i?zo:`closedloop metrics leads ${o} --indicator <id>`;e.print.result({...u,next:d},h=>i?cc(e,h,i):mc(e,h,o))}function ys(e,t){e.command("leads").argument("<metric>","The key result: its metric id, from closedloop metrics list.").description("Whether an indicator's history moves before this metric's, and when it moved and the metric did not follow; without --indicator, the indicators that led it on this workspace's history. ClosedLoop AI computes the checks in the background; this asks for them and shows what is known.").option("--indicator <id>","The indicator to check: its metric id, from closedloop metrics list.").option("--candidates <key...>","Only these candidates (keys from an earlier answer).").option("--metric-version <number>","Which version of the key result (a goal's key result stays on the version the goal pins); the current one if not given.").option("--wait",`Wait until the checks are done (up to ${Qo/6e4} minutes).`).action(async(n,o)=>hc(t,n,o))}var fc=/^\d{4}-\d{2}-\d{2}$/,jn="A target is one line: >=1400, <=0.3, between 40 and 60, +500, +10% or, for a share, +5pts.",ei=(e,t,n)=>new p(e,t,g.Usage,n),yc=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Xe=(e,t,n=t)=>yc(e,t)?e[t]:n,wc={week:"weekly",month:"monthly",quarter:"quarterly"},_c={cli:"through the CLI",app:"in the app",mcp:"through an AI assistant",api:"through the API",job:"automatically"},_s={key_result:"key result",indicator:"indicator"},ti={key_result:"as a key result",indicator:"as an indicator"},ue=e=>typeof e=="string"&&fc.test(e)?V(`${e}T00:00:00Z`):String(e??"an unknown day"),bs=e=>new Date(Date.parse(`${e}T00:00:00Z`)-864e5).toISOString().slice(0,10),Jt=e=>e.period==="days"?`every ${e.period_days} days`:Xe(wc,e.period),ze=(e,t)=>t===null?"no value":Wt(e)?`${(t*100).toFixed(1)}%`:ye(e)?Ae({},t,null,null):A(t);function ni(e){let t=e.written,n=t.value<0?"-":"+",o=Math.abs(t.value)>0&&Math.abs(t.value)<1?String(Number(Math.abs(t.value).toPrecision(3))):A(Math.abs(t.value));return t.as==="percent"?`${n}${o}%`:t.as==="points"?`${n}${o} points`:`${n}${o}`}function $s(e,t){return t.condition==="between"?t.low===null||t.high===null?null:`between ${ze(e,t.low)} and ${ze(e,t.high)}`:t.condition==="at_least"?t.low===null?null:`at least ${ze(e,t.low)}`:t.high===null?null:`at most ${ze(e,t.high)}`}function oi(e){let t=e.target;if(!t)return"no target yet";let n=e.data_series.calc,o=$s(n,t);if(t.written.as==="number")return o??"a target without a bound";let i=e.baseline&&e.baseline.value!==null?`from ${ze(n,e.baseline.value)} on ${ue(e.baseline.as_of)}`:"from the baseline";return o?`${o} (${ni(t)} ${i})`:`${ni(t)} from the baseline, once it is measured`}function vs(e,t){let n=t.target,o=n?$s(t.calc,n)??`${ni(n)} from the baseline, once it is measured`:"no target yet",i=t.current,r=i?(ye(t.calc)?Ae(t.calc,i.value,i.numerator,i.denominator):Be(t.calc,i.value,i.numerator,i.denominator))??"no value":null,s=i?`now ${r} on ${ue(i.as_of)}${i.status==="stale"?` ${e.orange("(stale)")}`:""}`:"not measured yet";return`${t.name}: ${o}, ${Jt(t)} \xB7 ${s}`}function ii(e,t=!1){return!e.baseline&&t?"baseline measured when the goal is activated":e.baseline?`baseline ${ze(e.data_series.calc,e.baseline.value)} on ${ue(e.baseline.as_of)}`:"baseline not measured yet (it is measured tonight)"}function Cs(e,t,n=!1){if(!t.current)return"not measured yet";let o=t.current;return`now ${(ye(t.data_series.calc)?Ae(t.data_series.calc,o.value,o.numerator,o.denominator):Be(t.data_series.calc,o.value,o.numerator,o.denominator))??"no value"} on ${ue(o.as_of)}${o.status==="stale"?` ${e.orange("(stale: a source it reads is late)")}`:""}`+(n?e.dim(" (a preview: periods start when the goal is activated)"):"")}function bc(e,t,n){let o=`${ue(n.start)} to ${ue(bs(n.end))}`;if(n.value===null||n.value===void 0)return`${o}: ${n.status==="failed"||n.status==="not_measured"?"no value":`not measured yet (the run of ${ue(n.end)} settles it)`}`;let i=n.result==="met"?e.green("met"):n.result==="missed"?e.red("missed"):"not judged yet";return`${o}: ${ze(t.data_series.calc,n.value)}, ${i}${n.status==="stale"?e.dim(" (stale)"):""}`}function $c(e,t,n){return t?t.status==="done"&&t.check?`lead check of ${ue(t.as_of)}: ${wt(t.check,n)}`:t.status==="failed"?e.orange(`lead check of ${ue(t.as_of)} could not be finished: ${Dn(t.error_code)}`):e.dim(`lead check ${t.status==="running"?"being computed now":"queued"}: ClosedLoop AI computes it in the background; list the goal again later`):e.dim("lead check: none (an archived metric is not checked)")}function vc(e,t,n={},o=!1){let r=t.role==="indicator"?`indicator of ${t.key_result_id?`key result ${t.key_result_id}`:"a key result"}`:"key result",s=t.data_series,a=`version ${s.version}${s.current_version!==null&&s.current_version!==s.version?` (the metric is at version ${s.current_version})`:""}`,u=[`${e.bold(s.name)}${e.dim(` \xB7 ${r} \xB7 ${Jt(t)} \xB7 ${a}`)}`+(t.archived_at?e.dim(` \xB7 archived ${V(t.archived_at)}`):""),` target ${oi(t)} \xB7 ${ii(t,o)}`,` ${Cs(e,t,o)}`];for(let c of t.periods)u.push(` ${bc(e,t,c)}`);if(t.current_period&&u.push(` ${e.dim(`this period: ${ue(t.current_period.start)} to ${ue(bs(t.current_period.end))}`)}`),t.role==="indicator"&&t.lead_check!==void 0&&u.push(` ${$c(e,t.lead_check,n)}`),t.role==="key_result"&&t.suggested_indicators?.length){u.push(" suggested indicators (they led it on this workspace's history):");for(let c of t.suggested_indicators)u.push(` ${e.bold(typeof c.indicator.name=="string"?c.indicator.name:"an indicator")}: ${c.check?wt(c.check,n):""}`);u.push(` ${e.dim(`save one as a metric: closedloop metrics leads ${s.id}${s.current_version!==null&&s.current_version!==s.version?` --metric-version ${s.version}`:""}`)}`)}return u.push(R(e," ",t.id)),u}function ri(e,t,n={},o=!1){return t.map(i=>vc(e,i,n,o).join(`
86
66
  `)).join(`
87
67
 
88
- `)}async function mi(e,t,n){let o=n.trim();if(Ts.test(o))return o.toLowerCase();if(/^\d{1,3}$/.test(o)){let i=await Le(e,Es,Number(o)),r=i?.goal_id===t&&Array.isArray(i.ids)?i.ids:[],s=Number(o),a=r[s-1];if(s>=1&&s<=r.length&&typeof a=="string"&&Ts.test(a))return a.toLowerCase();throw Bt("unknown_number",r.length?`the last metric list of this goal had ${r.length}, not ${s}`:"there is no recent metric list of this goal to pick a number from","List them first: closedloop goals metrics list <goal>")}throw Bt("invalid_id",`'${n}' is not a goal metric id or a number from the last goals metrics list`)}function $t(e,t){return e.status===404?new p("goal_not_found","there is no goal (or goal metric) with that id you can see",g.Failure):e.status===403?new p("not_your_goal","only the goal's owner changes or measures its metrics",g.Failure):C(e,"api_error",t)}function Ds(e,t){if(e.status!==400||!Array.isArray(e.body.problems))return null;let n=e.body.problems,o=n.map(i=>`${i.field}: ${i.message}${i.allowed&&i.allowed.length?` (allowed: ${i.allowed.join(", ")})`:""}`);return new p("invalid_kpi",`${t}; fix ${n.length===1?"this":`these ${n.length}`}:
68
+ `)}function si(e,t){return I(t,"goal metric",`closedloop goals metrics list ${e}`)}function _t(e,t){return e.status===404?new p("goal_not_found","there is no goal (or goal metric) with that id you can see",g.Failure,L("closedloop goals list")):e.status===403?new p("not_your_goal","only the goal's owner changes or measures its metrics",g.Failure):C(e,"api_error",t)}function ks(e,t){if(e.status!==400||!Array.isArray(e.body.problems))return null;let n=e.body.problems,o=n.map(i=>`${i.field}: ${i.message}${i.allowed&&i.allowed.length?` (allowed: ${i.allowed.join(", ")})`:""}`);return new p("invalid_kpi",`${t}; fix ${n.length===1?"this":`these ${n.length}`}:
89
69
  ${o.join(`
90
- `)}`,g.Failure,`Your saved metrics: closedloop metrics list. ${Jn}`)}async function pi(e,t,n){let o=$(e.runtime),i=await v(e.runtime,o,"GET",`/api/user-goals/${t}/kpis${n?"?include=ended":""}`);if(i.status!==200||!Array.isArray(i.body.kpis))throw $t(i,"The goal's metrics could not be loaded. Try again.");let r=i.body.kpis;await Ce(e,Es,{goal_id:t,ids:r.map(a=>a.id)});let s=i.body.goal;return{kpis:r,time_zone:typeof i.body.time_zone=="string"?i.body.time_zone:null,today:typeof i.body.today=="string"?i.body.today:null,lead_rules:i.body.lead_rules&&typeof i.body.lead_rules=="object"?i.body.lead_rules:{},state:typeof s?.state=="string"?s.state:null}}async function Hc(e,t,n,o){let i=await t(e,n),r={goal_id:i,...await pi(e,i,o)};e.print.result(r,s=>{let a=e.print.style;if(!s.kpis.length)return`This goal has no metrics. Add a key result: closedloop goals metrics add ${s.goal_id} --metric <metric> --period month --target '>=100'`;let l=s.time_zone?`
70
+ `)}`,g.Failure,`Your saved metrics: closedloop metrics list. ${jn}`)}async function ai(e,t,n){let o=v(e.runtime),i=await $(e.runtime,o,"GET",`/api/user-goals/${t}/kpis${n?"?include=ended":""}`);if(i.status!==200||!Array.isArray(i.body.kpis))throw _t(i,"The goal's metrics could not be loaded. Try again.");let r=i.body.kpis,s=i.body.goal;return{kpis:r,time_zone:typeof i.body.time_zone=="string"?i.body.time_zone:null,today:typeof i.body.today=="string"?i.body.today:null,lead_rules:i.body.lead_rules&&typeof i.body.lead_rules=="object"?i.body.lead_rules:{},state:typeof s?.state=="string"?s.state:null}}async function Cc(e,t,n,o){let i=await t(e,n),r={goal_id:i,...await ai(e,i,o)};e.print.result(r,s=>{let a=e.print.style;if(!s.kpis.length)return`This goal has no metrics. Add a key result: closedloop goals metrics add ${s.goal_id} --metric <metric> --period month --target '>=100'`;let l=s.time_zone?`
91
71
 
92
- ${a.dim(`Days and periods are counted in ${s.time_zone}; a period is settled by the value measured the night after it ends.`)}`:"";return`${di(a,s.kpis,s.lead_rules,s.state==="draft")}${l}`})}var js=e=>/^\d{1,10}$/.test(e)?Number(e):e,Gc=e=>e.trim().replace(/-/g,"_");async function qc(e,t,n,o){let i=await t(e,n);if(o.metric===void 0)throw Bt("missing_metric","give the saved metric with --metric <number|id|name>","Your saved metrics: closedloop metrics list");let r={metric:await se(e,o.metric,Ke,"metrics","closedloop metrics list")??o.metric};o.role!==void 0&&(r.role=Gc(o.role)),o.keyResult!==void 0&&(r.key_result=await mi(e,i,o.keyResult)),o.period!==void 0&&(r.period=o.period),o.days!==void 0&&(r.days=js(o.days)),o.target!==void 0&&(r.target=o.target),o.reason!==void 0&&(r.reason=o.reason),o.dryRun&&(r.dry_run=!0);let s=$(e.runtime),a=await v(e.runtime,s,"POST",`/api/user-goals/${i}/kpis`,r),l=Ds(a,"the metric was not added");if(l)throw l;if(a.status===409&&typeof a.body.kpi_id=="string")throw new p("kpi_exists","this goal already measures that metric this way",g.Failure,`See it: closedloop goals metrics list ${i}`);if(a.status===409)throw new p("goal_archived",String(a.body.error??"this goal is archived; its metrics can no longer change"),g.Failure);let u=a.body.goal_relevance_added??[],c=u.length?`The goal is now about insights bearing on ${u.map(f=>f.replace(/_/g," ")).join(" or ")}.`:null;if(o.dryRun&&a.status===200&&a.body.kpi){let f=a.body.kpi;e.print.result({dry_run:!0,kpi:f,goal_relevance_added:u},y=>[`Checked, nothing saved: ${y.kpi.metric.name} (version ${y.kpi.metric.version}) ${Xe(ai,y.kpi.role)}, ${Yt(y.kpi)}.`,...c?[`Saving it would make the goal about insights bearing on ${u.map(_=>_.replace(/_/g," ")).join(" or ")}.`]:[]].join(`
93
- `));return}let d=a.body.kpi;if(a.status!==201||!d||typeof d.id!="string")throw $t(a,"The metric could not be added. Try again.");let m=a.body.measured===!0,h=a.body.goal_state==="draft";e.print.result({measured:m,kpi:d,goal_relevance_added:u},f=>{let y=[`Added ${f.kpi.data_series.name} (version ${f.kpi.data_series.version}) ${Xe(ai,f.kpi.role)}, ${Yt(f.kpi)}: target ${ui(f.kpi)}.`,f.measured?`Its ${ci(f.kpi)}.`:h?"The goal is a draft: its baseline is measured when you activate it.":"Its baseline could not be measured now; the nightly run measures it."];return c&&y.push(c),y.join(`
94
- `)})}async function Jc(e,t,n,o,i){let r=await t(e,n),s=await mi(e,r,o);if(i.target===void 0)throw Bt("missing_target","give the new target with --target",Jn);if(i.reason===void 0)throw Bt("missing_reason","say why the target changes with --reason; it is kept in the goal's history");let a={target:i.target,reason:i.reason};i.period!==void 0&&(a.period=i.period),i.days!==void 0&&(a.days=js(i.days));let l=$(e.runtime),u=await v(e.runtime,l,"POST",`/api/user-goals/${r}/kpis/${s}/target`,a),c=Ds(u,"the target was not set");if(c)throw c;if(u.status===409)throw new p("kpi_archived",String(u.body.error??"this metric or its goal is archived"),g.Failure);let d=u.body.kpi;if(u.status!==200||!d||typeof d.id!="string")throw $t(u,"The target could not be set. Try again.");let m=u.body.measured===!0,h=u.body.goal_state==="draft";e.print.result({measured:m,kpi:d},f=>[`New target for ${f.kpi.data_series.name}, ${Yt(f.kpi)}: ${ui(f.kpi)}.`,f.measured?`Its new ${ci(f.kpi)}.`:h?"The goal is a draft: its baseline is measured when you activate it.":"Its new baseline could not be measured now; the nightly run measures it."].join(`
95
- `))}async function Vc(e,t,n,o,i){let r=await t(e,n),s=await mi(e,r,o),a=$(e.runtime),l=await v(e.runtime,a,"POST",`/api/user-goals/${r}/kpis/${s}/archive`,i===void 0?{}:{reason:i});if(l.status===404)throw new p("kpi_not_found","this goal has no metric with that id you can see",g.Failure);if(l.status===409)throw new p("kpi_archived",String(l.body.error??"this metric is already archived"),g.Failure);let u=l.body.archived;if(l.status!==200||!u)throw $t(l,"The metric could not be archived. Try again.");let c=Array.isArray(l.body.also_archived)?l.body.also_archived:[];e.print.result({status:"archived",id:u.id,archived_at:u.archived_at,also_archived:c},d=>`Archived the metric${d.also_archived.length?` and its ${d.also_archived.length} indicator${d.also_archived.length===1?"":"s"}`:""}. Its history stays; see it with closedloop goals metrics list ${r} --ended.`)}async function Bc(e,t,n){let o=await t(e,n),i=$(e.runtime),r=await v(e.runtime,i,"POST",`/api/user-goals/${o}/measure`);if(r.status===409)throw new p("measure_refused",String(r.body.error??"the goal cannot be measured now"),g.Failure);if(r.status!==200||!Array.isArray(r.body.measured)||!Array.isArray(r.body.kpis))throw $t(r,"The goal could not be measured. Try again.");let s={measured:r.body.measured,kpis:r.body.kpis};e.print.result(s,a=>{if(!a.kpis.length)return"This goal has no metrics to measure.";let l=e.print.style;return a.kpis.map(u=>{let c=Ls(l,u);return`${l.bold(u.data_series.name)}${l.dim(` \xB7 ${Xe(Is,u.role)}`)}: ${c}`}).join(`
96
- `)})}var Rs=e=>typeof e=="number"?e:typeof e=="string"&&e.trim()!==""&&Number.isFinite(Number(e))?Number(e):null;function si(e){let t=Rs(e.target_low),n=Rs(e.target_high);return e.target_condition==="between"?`between ${t===null?"?":O(t)} and ${n===null?"?":O(n)}`:e.target_condition==="at_least"?t===null?"at least a change from the baseline":`at least ${O(t)}`:e.target_condition==="at_most"?n===null?"at most a change from the baseline":`at most ${O(n)}`:"no target"}function Yc(e,t){let n=e.before??{},o=e.after;if(e.entity==="value"){let r=typeof o.measured_at=="string"?t?bn(o.measured_at,t):q(o.measured_at):"an unknown day";return e.action==="create"?`set the number to ${o.value} for ${r}`:`replaced ${n.value} with ${o.value} for ${r}`}if(e.entity==="kpi"){let r=e.metric?`${e.metric.name}${e.metric.version===null?"":` (version ${e.metric.version})`}`:"a metric no longer listed",s=typeof o.role=="string"?Xe(Is,o.role):"metric";return e.action==="create"?`added ${r} ${Xe(ai,String(o.role),"as a metric")}${o.target_condition?`, target ${si(o)}`:""}`:e.action==="update"?`set a new target for ${r}: ${si(o)} (was ${si(n)})`:`archived the ${s} ${r}`}if(e.action==="create")return"created the goal";if(e.action==="archive")return"deleted the goal (archived)";if(n.state!==o.state&&o.state==="active")return"activated the goal";if(n.state!==o.state&&o.state==="closed")return"closed the goal";let i=Object.keys(o).filter(r=>r!=="updated_at"&&JSON.stringify(o[r])!==JSON.stringify(n[r])).sort();return`changed the goal${i.length?` (${i.join(", ")})`:""}`}async function Kc(e,t,n,o){let i=await t(e,n),r=[o.limit!==void 0?`limit=${encodeURIComponent(o.limit)}`:"",o.page!==void 0?`page=${encodeURIComponent(o.page)}`:""].filter(Boolean).join("&"),s=$(e.runtime),a=await v(e.runtime,s,"GET",`/api/user-goals/${i}/history${r?`?${r}`:""}`);if(a.status!==200||!Array.isArray(a.body.changes))throw $t(a,"The goal's history could not be loaded. Try again.");let l=a.body.changes.map(d=>({...d,actor:{kind:d.actor?.kind,user_id:d.actor?.user_id??null,is_you:d.actor?.is_you===!0}})),u={page:Number(a.body.page),limit:Number(a.body.limit),total:Number(a.body.total),changes:l},c=typeof a.body.time_zone=="string"?a.body.time_zone:null;e.print.result(u,d=>{if(!d.changes.length)return d.total?`No changes on page ${d.page}; there are ${d.total}.`:"No changes yet.";let m=e.print.style,h=d.changes.map(_=>{let S=_.actor.kind==="system"?"ClosedLoop AI":_.actor.is_you?"you":"a teammate",b=_.reason?` \xB7 "${_.reason}"`:"";return`${m.dim(R(_.at))} ${S} ${Yc(_,c)} ${m.dim(Xe(Fc,_.channel,""))}${b}`}),f=(d.page-1)*d.limit+d.changes.length,y=f<d.total?`
72
+ ${a.dim(`Days and periods are counted in ${s.time_zone}; a period is settled by the value measured the night after it ends.`)}`:"";return`${ri(a,s.kpis,s.lead_rules,s.state==="draft")}${l}`})}var Ss=e=>/^\d{1,10}$/.test(e)?Number(e):e,kc=e=>e.trim().replace(/-/g,"_");async function Sc(e,t,n,o){let i=await t(e,n);if(o.metric===void 0)throw ei("missing_metric","give the saved metric with --metric <id>",L("closedloop metrics list"));let r={metric:I(o.metric,"metric","closedloop metrics list")};o.role!==void 0&&(r.role=kc(o.role)),o.keyResult!==void 0&&(r.key_result=si(i,o.keyResult)),o.period!==void 0&&(r.period=o.period),o.days!==void 0&&(r.days=Ss(o.days)),o.target!==void 0&&(r.target=o.target),o.reason!==void 0&&(r.reason=o.reason),o.dryRun&&(r.dry_run=!0);let s=v(e.runtime),a=await $(e.runtime,s,"POST",`/api/user-goals/${i}/kpis`,r),l=ks(a,"the metric was not added");if(l)throw l;if(a.status===409&&typeof a.body.kpi_id=="string")throw new p("kpi_exists","this goal already measures that metric this way",g.Failure,`See it: closedloop goals metrics list ${i}`);if(a.status===409)throw new p("goal_archived",String(a.body.error??"this goal is archived; its metrics can no longer change"),g.Failure);let u=a.body.goal_relevance_added??[],c=u.length?`The goal is now about insights bearing on ${u.map(f=>f.replace(/_/g," ")).join(" or ")}.`:null;if(o.dryRun&&a.status===200&&a.body.kpi){let f=a.body.kpi;e.print.result({dry_run:!0,kpi:f,goal_relevance_added:u},y=>[`Checked, nothing saved: ${y.kpi.metric.name} (version ${y.kpi.metric.version}) ${Xe(ti,y.kpi.role)}, ${Jt(y.kpi)}.`,...c?[`Saving it would make the goal about insights bearing on ${u.map(_=>_.replace(/_/g," ")).join(" or ")}.`]:[]].join(`
73
+ `));return}let m=a.body.kpi;if(a.status!==201||!m||typeof m.id!="string")throw _t(a,"The metric could not be added. Try again.");let d=a.body.measured===!0,h=a.body.goal_state==="draft";e.print.result({measured:d,kpi:m,goal_relevance_added:u},f=>{let y=[`Added ${f.kpi.data_series.name} (version ${f.kpi.data_series.version}) ${Xe(ti,f.kpi.role)}, ${Jt(f.kpi)}: target ${oi(f.kpi)}.`,f.measured?`Its ${ii(f.kpi)}.`:h?"The goal is a draft: its baseline is measured when you activate it.":"Its baseline could not be measured now; the nightly run measures it."];return c&&y.push(c),y.join(`
74
+ `)})}async function Oc(e,t,n,o,i){let r=await t(e,n),s=si(r,o);if(i.target===void 0)throw ei("missing_target","give the new target with --target",jn);if(i.reason===void 0)throw ei("missing_reason","say why the target changes with --reason; it is kept in the goal's history");let a={target:i.target,reason:i.reason};i.period!==void 0&&(a.period=i.period),i.days!==void 0&&(a.days=Ss(i.days));let l=v(e.runtime),u=await $(e.runtime,l,"POST",`/api/user-goals/${r}/kpis/${s}/target`,a),c=ks(u,"the target was not set");if(c)throw c;if(u.status===409)throw new p("kpi_archived",String(u.body.error??"this metric or its goal is archived"),g.Failure);let m=u.body.kpi;if(u.status!==200||!m||typeof m.id!="string")throw _t(u,"The target could not be set. Try again.");let d=u.body.measured===!0,h=u.body.goal_state==="draft";e.print.result({measured:d,kpi:m},f=>[`New target for ${f.kpi.data_series.name}, ${Jt(f.kpi)}: ${oi(f.kpi)}.`,f.measured?`Its new ${ii(f.kpi)}.`:h?"The goal is a draft: its baseline is measured when you activate it.":"Its new baseline could not be measured now; the nightly run measures it."].join(`
75
+ `))}async function Ac(e,t,n,o,i){let r=await t(e,n),s=si(r,o),a=v(e.runtime),l=await $(e.runtime,a,"POST",`/api/user-goals/${r}/kpis/${s}/archive`,i===void 0?{}:{reason:i});if(l.status===404)throw new p("kpi_not_found","this goal has no metric with that id you can see",g.Failure,L(`closedloop goals metrics list ${r}`));if(l.status===409)throw new p("kpi_archived",String(l.body.error??"this metric is already archived"),g.Failure);let u=l.body.archived;if(l.status!==200||!u)throw _t(l,"The metric could not be archived. Try again.");let c=Array.isArray(l.body.also_archived)?l.body.also_archived:[];e.print.result({status:"archived",id:u.id,archived_at:u.archived_at,also_archived:c},m=>`Archived the metric${m.also_archived.length?` and its ${m.also_archived.length} indicator${m.also_archived.length===1?"":"s"}`:""}. Its history stays; see it with closedloop goals metrics list ${r} --ended.`)}async function Tc(e,t,n){let o=await t(e,n),i=v(e.runtime),r=await $(e.runtime,i,"POST",`/api/user-goals/${o}/measure`);if(r.status===409)throw new p("measure_refused",String(r.body.error??"the goal cannot be measured now"),g.Failure);if(r.status!==200||!Array.isArray(r.body.measured)||!Array.isArray(r.body.kpis))throw _t(r,"The goal could not be measured. Try again.");let s={measured:r.body.measured,kpis:r.body.kpis};e.print.result(s,a=>{if(!a.kpis.length)return"This goal has no metrics to measure.";let l=e.print.style;return a.kpis.map(u=>{let c=Cs(l,u);return`${l.bold(u.data_series.name)}${l.dim(` \xB7 ${Xe(_s,u.role)}`)}: ${c}`}).join(`
76
+ `)})}var ws=e=>typeof e=="number"?e:typeof e=="string"&&e.trim()!==""&&Number.isFinite(Number(e))?Number(e):null;function Zo(e){let t=ws(e.target_low),n=ws(e.target_high);return e.target_condition==="between"?`between ${t===null?"?":A(t)} and ${n===null?"?":A(n)}`:e.target_condition==="at_least"?t===null?"at least a change from the baseline":`at least ${A(t)}`:e.target_condition==="at_most"?n===null?"at most a change from the baseline":`at most ${A(n)}`:"no target"}function Rc(e,t){let n=e.before??{},o=e.after;if(e.entity==="value"){let r=typeof o.measured_at=="string"?t?wn(o.measured_at,t):V(o.measured_at):"an unknown day";return e.action==="create"?`set the number to ${o.value} for ${r}`:`replaced ${n.value} with ${o.value} for ${r}`}if(e.entity==="kpi"){let r=e.metric?`${e.metric.name}${e.metric.version===null?"":` (version ${e.metric.version})`}`:"a metric no longer listed",s=typeof o.role=="string"?Xe(_s,o.role):"metric";return e.action==="create"?`added ${r} ${Xe(ti,String(o.role),"as a metric")}${o.target_condition?`, target ${Zo(o)}`:""}`:e.action==="update"?`set a new target for ${r}: ${Zo(o)} (was ${Zo(n)})`:`archived the ${s} ${r}`}if(e.action==="create")return"created the goal";if(e.action==="archive")return"deleted the goal (archived)";if(n.state!==o.state&&o.state==="active")return"activated the goal";if(n.state!==o.state&&o.state==="closed")return"closed the goal";let i=Object.keys(o).filter(r=>r!=="updated_at"&&JSON.stringify(o[r])!==JSON.stringify(n[r])).sort();return`changed the goal${i.length?` (${i.join(", ")})`:""}`}async function Ec(e,t,n,o){let i=await t(e,n),r=[o.limit!==void 0?`limit=${encodeURIComponent(o.limit)}`:"",o.page!==void 0?`page=${encodeURIComponent(o.page)}`:""].filter(Boolean).join("&"),s=v(e.runtime),a=await $(e.runtime,s,"GET",`/api/user-goals/${i}/history${r?`?${r}`:""}`);if(a.status!==200||!Array.isArray(a.body.changes))throw _t(a,"The goal's history could not be loaded. Try again.");let l=a.body.changes.map(m=>({...m,actor:{kind:m.actor?.kind,user_id:m.actor?.user_id??null,is_you:m.actor?.is_you===!0}})),u={page:Number(a.body.page),limit:Number(a.body.limit),total:Number(a.body.total),changes:l},c=typeof a.body.time_zone=="string"?a.body.time_zone:null;e.print.result(u,m=>{if(!m.changes.length)return m.total?`No changes on page ${m.page}; there are ${m.total}.`:"No changes yet.";let d=e.print.style,h=m.changes.map(_=>{let S=_.actor.kind==="system"?"ClosedLoop AI":_.actor.is_you?"you":"a teammate",b=_.reason?` \xB7 "${_.reason}"`:"";return`${d.dim(E(_.at))} ${S} ${Rc(_,c)} ${d.dim(Xe(_c,_.channel,""))}${b}`}),f=(m.page-1)*m.limit+m.changes.length,y=f<m.total?`
97
77
 
98
- ${m.dim(`${f} of ${d.total}. More: --page ${d.page+1}`)}`:"";return`${h.join(`
99
- `)}${y}`})}function Fs(e,t,n){let o=e.command("metrics").alias("metric").alias("kpis").alias("kpi").description("A goal's metrics: each a saved metric (closedloop metrics list) at the version it was added with, as a key result or an indicator of one, with a period and a target.");o.command("list").argument("<goal>","A number from the last goals list, or a goal id.").description("A goal's metrics, each with its target, baseline, newest value, and every finished period with its value and whether it met the target; each indicator with whether its history shows it leads its key result, and each key result with the indicators found to lead it.").option("--ended","Also the metrics that were archived.").action(async(i,r)=>Hc(t,n,i,r.ended===!0)),o.command("add").argument("<goal>","A number from the last goals list, or a goal id.").description("Measure one of your goals on a saved metric. It keeps the metric's current version; its baseline is measured now (a draft's when you activate it). Nothing is saved unless every field is right.").option("--metric <metric>","A number from the last metrics list, a metric id, or its name.").option("--role <role>","key_result (what the goal wants; the default) or indicator (moves sooner; belongs to one key result).").option("--key-result <kpi>","For an indicator: its key result, a number from the last goals metrics list or an id.").option("--period <period>","The span each target is checked over: week, month, quarter or days.").option("--days <n>","The number of days in each period, with --period days.").option("--target <target>",Jn).option("--reason <text>","Why, kept in the goal's history.").option("--dry-run","Check the metric without saving it.").action(async(i,r)=>qc(t,n,i,r)),o.command("target").argument("<goal>","A number from the last goals list, or a goal id.").argument("<kpi>","A number from the last goals metrics list of this goal, or its id.").description("Set a new target (and period) for one of a goal's metrics. Its baseline is measured again; the old target stays in the history.").option("--target <target>",Jn).option("--period <period>","A new period: week, month, quarter or days.").option("--days <n>","The number of days in each period, with --period days.").option("--reason <text>","Why the target changes (required), kept in the goal's history.").action(async(i,r,s)=>Jc(t,n,i,r,s)),o.command("archive").argument("<goal>","A number from the last goals list, or a goal id.").argument("<kpi>","A number from the last goals metrics list of this goal, or its id.").description("Stop measuring one of a goal's metrics; a key result takes its indicators with it. Its history stays.").option("--reason <text>","Why, kept in the goal's history.").action(async(i,r,s)=>Vc(t,n,i,r,s.reason)),e.command("measure").argument("<which>","A number from the last list, or a goal id.").description("Measure one of your goals' metrics now instead of waiting for the night.").action(async i=>Bc(t,n,i)),e.command("history").argument("<which>","A number from the last list, or a goal id.").description("Every change to a goal, its metrics and its values: when, by whom, through what and why, newest first.").option("--limit <n>","Changes per page (default 50, at most 200).").option("--page <n>","The page to show.").action(async(i,r)=>Kc(t,n,i,r))}var gi="working-goal.json",Us=e=>e.team&&typeof e.team.id=="string"?e.team.id:null;function Ws(e,t){let n=pn(e,gi);return!n||typeof n.id!="string"||typeof n.title!="string"||n.api_url!==t.api_url||n.email!==t.email||n.user_id!==(typeof t.user_id=="string"?t.user_id:null)||n.team_id!==null&&n.team_id!==void 0&&typeof n.team_id!="string"?null:{id:n.id,title:n.title,team_id:n.team_id??null}}function Ms(e,t){return Ws(e,t)!==null}function Hs(e,t){let n=Ws(e,t);return n&&n.team_id===Us(t)?{id:n.id,title:n.title}:null}function Gs(e,t,n){let o={id:n.id,title:n.title,api_url:t.api_url,email:t.email,user_id:typeof t.user_id=="string"?t.user_id:null,team_id:Us(t),at:new Date(e.now?.()??Date.now()).toISOString()};gn(e,gi,o)}function Vn(e){Yi(e,gi)}var hi="last-goals.json",Bs=1024*1024,qs=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Zc={goals:[{key:"eu-onboarding",title:"Raise onboarding completion in the EU",description:"Why it matters and what is in or out.",metric:{name:"onboarding completion",unit:"%",direction:"increase",baseline:42,target:60,due:"2026-12-31"},scope:{country:["DE","AT","FR"],productAreaId:"Onboarding"},visibility:"private"}]},X=(e,t,n)=>new p(e,t,g.Usage,n),Ys=(e,t)=>t?bn(e,t):P(e);async function de(e,t){let n=t.trim();if(qs.test(n))return n.toLowerCase();if(/^\d{1,3}$/.test(n)){let o=await Le(e,hi,Number(n)),i=Array.isArray(o?.ids)?o.ids:[],r=Number(n);if(r>=1&&r<=i.length){if(typeof i[r-1]=="string"&&qs.test(i[r-1]))return i[r-1].toLowerCase();throw X("unknown_number","the last list could not be read","List again: closedloop goals list")}throw X("unknown_number",i.length?`the last list had ${i.length} goals, not ${r}`:"there is no recent list to pick a number from","List first: closedloop goals list")}throw X("invalid_id",`'${t}' is not a goal id or a number from the last list`,"Use a number from `closedloop goals list`, or an id.")}var vt={productId:"product",productAreaId:"area",featureAreaId:"feature"},Ct=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Bn=e=>e.map(t=>t.replace(/_/g," ")).join(" or ");function Ks(e){let t=e.relevance;return t?t.from==="goal"?`relevance: ${Bn(t.outcomes)}`:t.from==="kpis"?`relevance from its metrics: ${Bn(t.outcomes)}`:t.unknown&&t.unknown.length?`no relevance: ClosedLoop AI does not know what the metric ${t.unknown.join(", ")} bears on; name outcomeCategory in the scope`:"no relevance yet: add a key result whose metric counts customers by stage or paying state, or name outcomeCategory in the scope":Ct(e.scope,"outcomeCategory")?`relevance: ${Bn(e.scope.outcomeCategory)}`:null}function ed(e){let t=e.scope_names??{},n=new Set((e.scope_missing??[]).map(a=>`${a.field}|${a.value}`)),o=[],i=[];for(let[a,l]of Object.entries(e.scope))if(Ct(vt,a))for(let u of l)o.push(Ct(t,u)?`${t[u]} (${vt[a]})`:n.has(`${a}|${u}`)?`a removed ${vt[a]}`:`${vt[a]} ${u}`);else a!=="outcomeCategory"&&i.push(`${a}: ${l.join(", ")}`);let r=Ks(e),s=[...o.length?[o.join(" or ")]:[],...i];return[s.length?s.join("; "):"all insights",...r?[r]:[]].join("; ")}function td(e){let t=e.scope_missing??[];if(!t.length)return null;let n=Ct(vt,t[0].field)?vt[t[0].field]:"item";return`${t.length===1?`${n==="area"?"An":"A"} ${n} in this goal's scope no longer exists`:`${t.length} products, areas or features in this goal's scope no longer exist`}, so its insights cannot be read: save the goal with a new scope.`}function Yn(e,t){if(e===null)return"?";let n=Number.isInteger(e)?k(e):String(e);return t?t==="%"?`${n}%`:`${n} ${t}`:n}var nd=e=>e.split(`
78
+ ${d.dim(`${f} of ${m.total}. More: --page ${m.page+1}`)}`:"";return`${h.join(`
79
+ `)}${y}`})}function Os(e,t,n){let o=e.command("metrics").alias("metric").alias("kpis").alias("kpi").description("A goal's metrics: each a saved metric (closedloop metrics list) at the version it was added with, as a key result or an indicator of one, with a period and a target.");o.command("list").argument("<goal>","The goal's id, from closedloop goals list.").description("A goal's metrics, each with its id, target, baseline, newest value, and every finished period with its value and whether it met the target; each indicator with whether its history shows it leads its key result, and each key result with the indicators found to lead it.").option("--ended","Also the metrics that were archived.").action(async(i,r)=>Cc(t,n,i,r.ended===!0)),o.command("add").argument("<goal>","The goal's id, from closedloop goals list.").description("Measure one of your goals on a saved metric. It keeps the metric's current version; its baseline is measured now (a draft's when you activate it). Nothing is saved unless every field is right.").option("--metric <id>","The saved metric's id, from closedloop metrics list.").option("--role <role>","key_result (what the goal wants; the default) or indicator (moves sooner; belongs to one key result).").option("--key-result <kpi>","For an indicator: its key result's id, from closedloop goals metrics list <goal>.").option("--period <period>","The span each target is checked over: week, month, quarter or days.").option("--days <n>","The number of days in each period, with --period days.").option("--target <target>",jn).option("--reason <text>","Why, kept in the goal's history.").option("--dry-run","Check the metric without saving it.").action(async(i,r)=>Sc(t,n,i,r)),o.command("target").argument("<goal>","The goal's id, from closedloop goals list.").argument("<kpi>","The goal metric's id, from closedloop goals metrics list <goal>.").description("Set a new target (and period) for one of a goal's metrics. Its baseline is measured again; the old target stays in the history.").option("--target <target>",jn).option("--period <period>","A new period: week, month, quarter or days.").option("--days <n>","The number of days in each period, with --period days.").option("--reason <text>","Why the target changes (required), kept in the goal's history.").action(async(i,r,s)=>Oc(t,n,i,r,s)),o.command("archive").argument("<goal>","The goal's id, from closedloop goals list.").argument("<kpi>","The goal metric's id, from closedloop goals metrics list <goal>.").description("Stop measuring one of a goal's metrics; a key result takes its indicators with it. Its history stays.").option("--reason <text>","Why, kept in the goal's history.").action(async(i,r,s)=>Ac(t,n,i,r,s.reason)),e.command("measure").argument("<goal>","The goal's id, from closedloop goals list.").description("Measure one of your goals' metrics now instead of waiting for the night.").action(async i=>Tc(t,n,i)),e.command("history").argument("<goal>","The goal's id, from closedloop goals list.").description("Every change to a goal, its metrics and its values: when, by whom, through what and why, newest first.").option("--limit <n>","Changes per page (default 50, at most 200).").option("--page <n>","The page to show.").action(async(i,r)=>Ec(t,n,i,r))}var li="working-goal.json",As=e=>e.team&&typeof e.team.id=="string"?e.team.id:null;function Ts(e,t){let n=Vi(e,li);return!n||typeof n.id!="string"||typeof n.title!="string"||n.api_url!==t.api_url||n.email!==t.email||n.user_id!==(typeof t.user_id=="string"?t.user_id:null)||n.team_id!==null&&n.team_id!==void 0&&typeof n.team_id!="string"?null:{id:n.id,title:n.title,team_id:n.team_id??null}}function Rs(e,t){return Ts(e,t)!==null}function Es(e,t){let n=Ts(e,t);return n&&n.team_id===As(t)?{id:n.id,title:n.title}:null}function Is(e,t,n){let o={id:n.id,title:n.title,api_url:t.api_url,email:t.email,user_id:typeof t.user_id=="string"?t.user_id:null,team_id:As(t),at:new Date(e.now?.()??Date.now()).toISOString()};Bi(e,li,o)}function Fn(e){Yi(e,li)}function ui(e){return e.workspace??=M(e.runtime,v(e.runtime)),e.workspace}var Ns="closedloop goals list",Ls=1024*1024,bt="The goal's id, from closedloop goals list.",Nc={goals:[{key:"eu-onboarding",title:"Raise onboarding completion in the EU",description:"Why it matters and what is in or out.",metric:{name:"onboarding completion",unit:"%",direction:"increase",baseline:42,target:60,due:"2026-12-31"},scope:{country:["DE","AT","FR"],productAreaId:"Onboarding"},visibility:"private"}]},we=(e,t,n)=>new p(e,t,g.Usage,n),Ds=(e,t)=>t?wn(e,t):N(e);async function ce(e,t){return I(t,"goal",Ns)}var Ct=()=>new p("goal_not_found","there is no goal with that id you can see",g.Failure,L(Ns)),$t={productId:"product",productAreaId:"area",featureAreaId:"feature"},vt=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Wn=e=>e.map(t=>t.replace(/_/g," ")).join(" or ");function js(e){let t=e.relevance;return t?t.from==="goal"?`relevance: ${Wn(t.outcomes)}`:t.from==="kpis"?`relevance from its metrics: ${Wn(t.outcomes)}`:t.unknown&&t.unknown.length?`no relevance: ClosedLoop AI does not know what the metric ${t.unknown.join(", ")} bears on; name outcomeCategory in the scope`:"no relevance yet: add a key result whose metric counts customers by stage or paying state, or name outcomeCategory in the scope":vt(e.scope,"outcomeCategory")?`relevance: ${Wn(e.scope.outcomeCategory)}`:null}function Lc(e){let t=e.scope_names??{},n=new Set((e.scope_missing??[]).map(a=>`${a.field}|${a.value}`)),o=[],i=[];for(let[a,l]of Object.entries(e.scope))if(vt($t,a))for(let u of l)o.push(vt(t,u)?`${t[u]} (${$t[a]})`:n.has(`${a}|${u}`)?`a removed ${$t[a]}`:`${$t[a]} ${u}`);else a!=="outcomeCategory"&&i.push(`${a}: ${l.join(", ")}`);let r=js(e),s=[...o.length?[o.join(" or ")]:[],...i];return[s.length?s.join("; "):"all insights",...r?[r]:[]].join("; ")}function Dc(e){let t=e.scope_missing??[];if(!t.length)return null;let n=vt($t,t[0].field)?$t[t[0].field]:"item";return`${t.length===1?`${n==="area"?"An":"A"} ${n} in this goal's scope no longer exists`:`${t.length} products, areas or features in this goal's scope no longer exist`}, so its insights cannot be read: save the goal with a new scope.`}function Un(e,t){if(e===null)return"?";let n=Number.isInteger(e)?k(e):String(e);return t?t==="%"?`${n}%`:`${n} ${t}`:n}var jc=e=>e.split(`
100
80
  `).map(t=>t&&` ${t}`).join(`
101
- `);function od(e){return e.archived_at?`archived ${P(e.archived_at)}`:e.state==="closed"?e.closed_at?`closed ${P(e.closed_at)}`:"closed":e.state??null}function zs(e,t,n,o=0){let i=" ".repeat(o),r=i+(n===null?"":" ".repeat(n.length+2)),s=od(t),a=`${i}${n===null?"":`${n} `}${e.bold(t.title)}${s?e.dim(` \xB7 ${s}`):""}${t.visibility==="team"?e.dim(" \xB7 shared"):""}${t.owner.is_you?"":e.dim(" \xB7 a teammate's")}`,l=t.metric.direction==="increase"?"\u2191":"\u2193",u=`${t.metric.name} ${l} ${Yn(t.metric.target,t.metric.unit)}${t.metric.due?` by ${P(`${t.metric.due}T12:00:00Z`)}`:""}`,c=t.current?`now ${Yn(t.current.value,t.metric.unit)}${t.progress===null?"":` (${Math.round(t.progress*100)}% of the way)`}`:`no value yet${t.metric.baseline===null?"":`, from ${Yn(t.metric.baseline,t.metric.unit)}`}`,d=Array.isArray(t.key_results)?t.key_results:[],m=d.length>1?` \xB7 and ${d.length-1} more key result${d.length===2?"":"s"}`:"",h=d.length?`${Ns(e,d[0])}${m}`:`${u} \xB7 ${c}`,f=t.matching_insights===null?"insights unknown":`${k(t.matching_insights)} insight${t.matching_insights===1?"":"s"}`,y=td(t);return[a,`${r}${h}`,`${r}${e.dim(`${ed(t)} \xB7 ${f}${t.key?` \xB7 key ${t.key}`:""}`)}`,...y?[`${r}${y}`]:[]]}function id(e){let t=new Map(e.map(r=>[r.id,r])),n=new Set,o=[],i=e.filter(r=>!r.parent_id||!t.has(r.parent_id));for(let r of[...i,...e]){let s=[{goal:r,depth:0}];for(;s.length;){let a=s.pop();if(n.has(a.goal.id))continue;n.add(a.goal.id),o.push(a);let l=(a.goal.children??[]).map(u=>t.get(u)).filter(u=>u!==void 0&&u.parent_id===a.goal.id);for(let u of l.reverse())s.push({goal:u,depth:a.depth+1})}}return o}async function rd(e,t,n,o){let i=$(e.runtime),r=[t?"include=team":"",n?"archived=true":"",o!==void 0?`state=${encodeURIComponent(o)}`:""].filter(Boolean).join("&"),s=await v(e.runtime,i,"GET",`/api/user-goals${r?`?${r}`:""}`);if(s.status===400&&Array.isArray(s.body.allowed))throw X("invalid_state",String(s.body.error??"that is not a goal state"),`The states: ${s.body.allowed.join(", ")}`);if(s.status!==200||!Array.isArray(s.body.goals))throw C(s,"api_error","Your goals could not be loaded. Try again.");let a=id(s.body.goals),l=a.map(c=>c.goal),u=s.body.truncated===!0;await Ce(e,hi,{ids:l.map(c=>c.id)}),e.print.result({goals:l,truncated:u},c=>{if(c.goals.length===0)return t?"No goals yet, yours or shared.":"No goals yet. Create some: closedloop goals create --file goals.json";let d=e.print.style,m=String(c.goals.length).length,h=a.map((y,_)=>zs(d,y.goal,String(_+1).padStart(m),y.depth).join(`
102
- `)),f=c.truncated?`
81
+ `);function Fc(e){return e.archived_at?`archived ${N(e.archived_at)}`:e.state==="closed"?e.closed_at?`closed ${N(e.closed_at)}`:"closed":e.state??null}function Fs(e,t,n,o=0){let i=" ".repeat(o),r=n?`${i} `:i,s=Fc(t),a=`${i}${e.bold(t.title)}${s?e.dim(` \xB7 ${s}`):""}${t.visibility==="team"?e.dim(" \xB7 shared"):""}${t.owner.is_you?"":e.dim(" \xB7 a teammate's")}`,l=t.metric.direction==="increase"?"\u2191":"\u2193",u=`${t.metric.name} ${l} ${Un(t.metric.target,t.metric.unit)}${t.metric.due?` by ${N(`${t.metric.due}T12:00:00Z`)}`:""}`,c=t.current?`now ${Un(t.current.value,t.metric.unit)}${t.progress===null?"":` (${Math.round(t.progress*100)}% of the way)`}`:`no value yet${t.metric.baseline===null?"":`, from ${Un(t.metric.baseline,t.metric.unit)}`}`,m=Array.isArray(t.key_results)?t.key_results:[],d=m.length>1?` \xB7 and ${m.length-1} more key result${m.length===2?"":"s"}`:"",h=m.length?`${vs(e,m[0])}${d}`:`${u} \xB7 ${c}`,f=t.matching_insights===null?"insights unknown":`${k(t.matching_insights)} insight${t.matching_insights===1?"":"s"}`,y=Dc(t);return[a,`${r}${h}`,`${r}${e.dim(`${Lc(t)} \xB7 ${f}${t.key?` \xB7 key ${t.key}`:""}`)}`,...y?[`${r}${y}`]:[],...n?[R(e,r,t.id)]:[]]}function Wc(e){let t=new Map(e.map(r=>[r.id,r])),n=new Set,o=[],i=e.filter(r=>!r.parent_id||!t.has(r.parent_id));for(let r of[...i,...e]){let s=[{goal:r,depth:0}];for(;s.length;){let a=s.pop();if(n.has(a.goal.id))continue;n.add(a.goal.id),o.push(a);let l=(a.goal.children??[]).map(u=>t.get(u)).filter(u=>u!==void 0&&u.parent_id===a.goal.id);for(let u of l.reverse())s.push({goal:u,depth:a.depth+1})}}return o}async function Uc(e,t,n,o){let i=v(e.runtime),r=[t?"include=team":"",n?"archived=true":"",o!==void 0?`state=${encodeURIComponent(o)}`:""].filter(Boolean).join("&"),s=await $(e.runtime,i,"GET",`/api/user-goals${r?`?${r}`:""}`);if(s.status===400&&Array.isArray(s.body.allowed))throw we("invalid_state",String(s.body.error??"that is not a goal state"),`The states: ${s.body.allowed.join(", ")}`);if(s.status!==200||!Array.isArray(s.body.goals))throw C(s,"api_error","Your goals could not be loaded. Try again.");let a=Wc(s.body.goals),l=a.map(c=>c.goal),u=s.body.truncated===!0;e.print.result({goals:l,truncated:u},c=>{if(c.goals.length===0)return t?"No goals yet, yours or shared.":"No goals yet. Create some: closedloop goals create --file goals.json";let m=e.print.style,d=a.map(f=>Fs(m,f.goal,!0,f.depth).join(`
82
+ `)),h=c.truncated?`
103
83
 
104
- ${d.dim(`Showing the first ${c.goals.length}.`)}`:"";return`${h.join(`
84
+ ${m.dim(`Showing the first ${c.goals.length}.`)}`:"";return`${d.join(`
105
85
 
106
- `)}${f}
86
+ `)}${h}
107
87
 
108
- ${d.dim("Read one: closedloop goals show <number>")}`})}async function sd(e,t){let n=await de(e,t),o=$(e.runtime),i=await v(e.runtime,o,"GET",`/api/user-goals/${n}`);if(i.status===404)throw new p("goal_not_found","there is no goal with that id you can see",g.Failure);let r=i.body.goal;if(i.status!==200||!r||typeof r.id!="string")throw C(i,"api_error","The goal could not be loaded. Try again.");let s=typeof i.body.time_zone=="string"?i.body.time_zone:null,{kpis:a,lead_rules:l}=await pi(e,r.id,!1),u=r.parent_id?await ad(e,r.parent_id):null;e.print.result({...r,kpis:a},c=>{let d=e.print.style,m=zs(d,c,null);c.parent_id&&m.push(`Sub-goal of ${u??"a goal you cannot see"}`),c.children&&c.children.length&&m.push(d.dim(`${c.children.length} sub-goal${c.children.length===1?"":"s"}: closedloop goals list --team`)),c.description&&m.push("",c.description),m.push("",c.kpis.length?`Metrics:
109
- ${nd(di(d,c.kpis,l,c.state==="draft"))}`:d.dim(`No metrics yet. Add a key result: closedloop goals metrics add ${c.id} --metric <metric> --period month --target '>=100'`));let h=c.missing_to_activate;return c.state==="draft"&&Array.isArray(h)&&m.push("",h.length?`A draft. To activate it, it still needs:
88
+ ${m.dim("Read one by its id: closedloop goals show <id>")}`})}async function Hc(e,t){let n=await ce(e,t),o=v(e.runtime),i=await $(e.runtime,o,"GET",`/api/user-goals/${n}`);if(i.status===404)throw Ct();let r=i.body.goal;if(i.status!==200||!r||typeof r.id!="string")throw C(i,"api_error","The goal could not be loaded. Try again.");let s=typeof i.body.time_zone=="string"?i.body.time_zone:null,{kpis:a,lead_rules:l}=await ai(e,r.id,!1),u=r.parent_id?await Mc(e,r.parent_id):null;e.print.result({...r,kpis:a},c=>{let m=e.print.style,d=Fs(m,c,!1);c.parent_id&&d.push(`Sub-goal of ${u??"a goal you cannot see"}`),c.children&&c.children.length&&d.push(m.dim(`${c.children.length} sub-goal${c.children.length===1?"":"s"}: closedloop goals list --team`)),c.description&&d.push("",c.description),d.push("",c.kpis.length?`Metrics:
89
+ ${jc(ri(m,c.kpis,l,c.state==="draft"))}`:m.dim(`No metrics yet. Add a key result: closedloop goals metrics add ${c.id} --metric <metric> --period month --target '>=100'`));let h=c.missing_to_activate;return c.state==="draft"&&Array.isArray(h)&&d.push("",h.length?`A draft. To activate it, it still needs:
110
90
  ${h.map(f=>` - ${f.message}`).join(`
111
- `)}`:`A draft, ready to activate: closedloop goals activate ${c.id}`),c.values&&c.values.length&&(m.push("","Values:",...c.values.slice(0,10).map(f=>` ${Ys(f.measured_at,s)} ${Yn(f.value,c.metric.unit)}`)),c.values.length>10&&m.push(d.dim(` and ${c.values.length-10} earlier`))),m.push("",d.dim(`id ${c.id}`)),m.join(`
112
- `)})}async function ad(e,t){let n=await v(e.runtime,$(e.runtime),"GET",`/api/user-goals/${t}`),o=n.body.goal;return n.status===200&&typeof o?.title=="string"?o.title:null}var Js="insights search reads only its insights; add --all for every insight.";async function kt(e,t){if(!Ms(e.runtime,t))return null;let n=await Lt(e);return Hs(e.runtime,{...t,team:{id:n.id,name:n.name}})}async function ld(e,t,n){if(n&&t!==void 0)throw X("invalid_option","give a goal or --clear, not both");let o=$(e.runtime);if(n){Vn(e.runtime),e.print.result({working_goal:null},()=>"No goal picked: insights search reads every insight.");return}if(t===void 0){let u=await kt(e,o);e.print.result({working_goal:u},c=>c.working_goal?`Working on ${c.working_goal.title}: ${Js}`:"No goal picked: insights search reads every insight. Pick one: closedloop goals use <goal>");return}let i=await de(e,t),r=await Lt(e),s=await v(e.runtime,o,"GET",`/api/user-goals/${i}`);if(s.status===404)throw new p("goal_not_found","there is no goal with that id you can see",g.Failure);let a=s.body.goal;if(s.status!==200||!a||typeof a.id!="string")throw C(s,"api_error","The goal could not be loaded. Try again.");Gs(e.runtime,{...o,team:{id:r.id,name:r.name}},{id:a.id,title:a.title});let l=a.relevance&&a.relevance.outcomes.length===0?` It has ${Ks(a)}.`:"";e.print.result({working_goal:{id:a.id,title:a.title}},u=>`Working on ${u.working_goal.title}: ${Js}${l}`)}var Vs={churn_reason:"reason for leaving",general_dislike:"dislike"};async function ud(e,t){let n=$(e.runtime),o=t!==void 0?await de(e,t):(await kt(e,n))?.id;if(!o)throw X("no_goal","name a goal, or pick the one you work on first","closedloop goals use <goal>");let i=await v(e.runtime,n,"GET",`/api/user-goals/${o}/context`);if(i.status===404&&i.body.code==="goal_not_found")throw new p("goal_not_found","there is no goal with that id you can see",g.Failure);if(i.status===404)throw new p("goal_context_unsupported","this ClosedLoop AI API cannot show a goal's context yet",g.Failure);let r=i.body;if(i.status!==200||!r.goal||typeof r.goal.id!="string"||!Array.isArray(r.goal.relevance?.outcomes))throw C(i,"api_error","The goal's context could not be loaded. Try again.");let s={goal:r.goal,deal_blockers:r.deal_blockers??null,churn_risks:r.churn_risks??null};e.print.result(s,a=>{let l=e.print.style,u=[l.bold(a.goal.title)];if(!a.deal_blockers&&!a.churn_risks)return u.push(`It is about ${Bn(a.goal.relevance.outcomes)}: deal blockers go with new sales and churn risks with retention, so it has neither.`),u.join(`
113
- `);let c=(m,h,f,y,_)=>{f&&(u.push("",`${m}, bearing on ${h}: ${f.total===0?"none in its scope":y(f.total)}`),u.push(...f.items.map(S=>` ${_(S)}`)),f.total>f.items.length&&u.push(l.dim(` and ${k(f.total-f.items.length)} earlier`)))},d=a.deal_blockers?.customers;return c("Deal blockers","new sales",a.deal_blockers,m=>`${k(m)} insight${m===1?"":"s"}${typeof d=="number"?` from ${k(d)} customer${d===1?"":"s"}`:""}`,m=>`${P(m.occurred_at)} ${m.customer_name??"an unknown customer"} \xB7 ${m.title??"an untitled insight"}`),c("Churn risks","retention",a.churn_risks,m=>k(m),m=>`${P(m.occurred_at)} ${m.customer_name??"an unknown customer"} \xB7 ${m.summary??m.quote??"no summary"}`+l.dim([Object.prototype.hasOwnProperty.call(Vs,m.kind)?Vs[m.kind]:null,m.urgency?m.urgency.replace(/_/g," "):null].filter(Boolean).map(h=>` \xB7 ${h}`).join(""))),u.join(`
114
- `)})}async function cd(e,t){let n=$(e.runtime),o=await v(e.runtime,n,"GET",`/api/user-goals/fields${t?`?field=${encodeURIComponent(t)}`:""}`);if(o.status===400){let r=Array.isArray(o.body.allowed)?o.body.allowed.join(", "):"";throw X("unknown_field","that goal field is not supported",r?`The fields: ${r}`:void 0)}if(o.status!==200||!Array.isArray(o.body.fields))throw C(o,"api_error","The fields could not be loaded. Try again.");let i={fields:o.body.fields};e.print.result(i,r=>{let s=e.print.style;return`${r.fields.map(l=>{let u=`${s.bold(l.field)} ${l.label}${s.dim(` \xB7 ${l.description}`)}`,c=l.values.map(m=>` ${m.label===m.value?m.value:`${m.label} ${s.dim(m.value)}`} ${s.dim(`${k(m.insights)} insight${m.insights===1?"":"s"}`)}`),d=l.more_values>0?[s.dim(` and ${l.more_values} more: closedloop goals fields --field ${l.field}`)]:[];return[u,...c.length?c:[s.dim(" no values in this workspace yet")],...d].join(`
91
+ `)}`:`A draft, ready to activate: closedloop goals activate ${c.id}`),c.values&&c.values.length&&(d.push("","Values:",...c.values.slice(0,10).map(f=>` ${Ds(f.measured_at,s)} ${Un(f.value,c.metric.unit)}`)),c.values.length>10&&d.push(m.dim(` and ${c.values.length-10} earlier`))),d.push("",m.dim(`id ${c.id}`)),d.join(`
92
+ `)})}async function Mc(e,t){let n=await $(e.runtime,v(e.runtime),"GET",`/api/user-goals/${t}`),o=n.body.goal;return n.status===200&&typeof o?.title=="string"?o.title:null}var xs="insights search reads only its insights; add --all for every insight.";async function kt(e,t){if(!Rs(e.runtime,t))return null;let n=await ui(e);return Es(e.runtime,{...t,team:{id:n.id,name:n.name}})}async function Gc(e,t,n){if(n&&t!==void 0)throw we("invalid_option","give a goal or --clear, not both");let o=v(e.runtime);if(n){Fn(e.runtime),e.print.result({working_goal:null},()=>"No goal picked: insights search reads every insight.");return}if(t===void 0){let u=await kt(e,o);e.print.result({working_goal:u},c=>c.working_goal?`Working on ${c.working_goal.title}: ${xs}`:"No goal picked: insights search reads every insight. Pick one: closedloop goals use <goal>");return}let i=await ce(e,t),r=await ui(e),s=await $(e.runtime,o,"GET",`/api/user-goals/${i}`);if(s.status===404)throw Ct();let a=s.body.goal;if(s.status!==200||!a||typeof a.id!="string")throw C(s,"api_error","The goal could not be loaded. Try again.");Is(e.runtime,{...o,team:{id:r.id,name:r.name}},{id:a.id,title:a.title});let l=a.relevance&&a.relevance.outcomes.length===0?` It has ${js(a)}.`:"";e.print.result({working_goal:{id:a.id,title:a.title}},u=>`Working on ${u.working_goal.title}: ${xs}${l}`)}var Ps={churn_reason:"reason for leaving",general_dislike:"dislike"};async function qc(e,t){let n=v(e.runtime),o=t!==void 0?await ce(e,t):(await kt(e,n))?.id;if(!o)throw we("no_goal","name a goal, or pick the one you work on first","closedloop goals use <goal>");let i=await $(e.runtime,n,"GET",`/api/user-goals/${o}/context`);if(i.status===404&&i.body.code==="goal_not_found")throw Ct();if(i.status===404)throw new p("goal_context_unsupported","this ClosedLoop AI API cannot show a goal's context yet",g.Failure);let r=i.body;if(i.status!==200||!r.goal||typeof r.goal.id!="string"||!Array.isArray(r.goal.relevance?.outcomes))throw C(i,"api_error","The goal's context could not be loaded. Try again.");let s={goal:r.goal,deal_blockers:r.deal_blockers??null,churn_risks:r.churn_risks??null};e.print.result(s,a=>{let l=e.print.style,u=[l.bold(a.goal.title)];if(!a.deal_blockers&&!a.churn_risks)return u.push(`It is about ${Wn(a.goal.relevance.outcomes)}: deal blockers go with new sales and churn risks with retention, so it has neither.`),u.join(`
93
+ `);let c=(d,h,f,y,_,S)=>{f&&(u.push("",`${d}, bearing on ${h}: ${f.total===0?"none in its scope":y(f.total)}`),u.push(...f.items.flatMap(b=>[` ${_(b)}`,R(l," ",S(b))])),f.total>f.items.length&&u.push(l.dim(` and ${k(f.total-f.items.length)} earlier`)))},m=a.deal_blockers?.customers;return c("Deal blockers","new sales",a.deal_blockers,d=>`${k(d)} insight${d===1?"":"s"}${typeof m=="number"?` from ${k(m)} customer${m===1?"":"s"}`:""}`,d=>`${N(d.occurred_at)} ${d.customer_name??"an unknown customer"} \xB7 ${d.title??"an untitled insight"}`,d=>d.insight_id),a.deal_blockers?.items.length&&u.push(l.dim("Read a deal blocker by its id: closedloop insights show <id>")),c("Churn risks","retention",a.churn_risks,d=>k(d),d=>`${N(d.occurred_at)} ${d.customer_name??"an unknown customer"} \xB7 ${d.summary??d.quote??"no summary"}`+l.dim([Object.prototype.hasOwnProperty.call(Ps,d.kind)?Ps[d.kind]:null,d.urgency?d.urgency.replace(/_/g," "):null].filter(Boolean).map(h=>` \xB7 ${h}`).join("")),d=>d.context_id),u.join(`
94
+ `)})}async function Jc(e,t){let n=v(e.runtime),o=await $(e.runtime,n,"GET",`/api/user-goals/fields${t?`?field=${encodeURIComponent(t)}`:""}`);if(o.status===400){let r=Array.isArray(o.body.allowed)?o.body.allowed.join(", "):"";throw we("unknown_field","that goal field is not supported",r?`The fields: ${r}`:void 0)}if(o.status!==200||!Array.isArray(o.body.fields))throw C(o,"api_error","The fields could not be loaded. Try again.");let i={fields:o.body.fields};e.print.result(i,r=>{let s=e.print.style;return`${r.fields.map(l=>{let u=`${s.bold(l.field)} ${l.label}${s.dim(` \xB7 ${l.description}`)}`,c=l.values.map(d=>` ${d.label===d.value?d.value:`${d.label} ${s.dim(d.value)}`} ${s.dim(`${k(d.insights)} insight${d.insights===1?"":"s"}`)}`),m=l.more_values>0?[s.dim(` and ${l.more_values} more: closedloop goals fields --field ${l.field}`)]:[];return[u,...c.length?c:[s.dim(" no values in this workspace yet")],...m].join(`
115
95
  `)}).join(`
116
96
 
117
97
  `)}
118
98
 
119
- ${s.dim("Use these in a goal's scope; see closedloop goals example")}`})}function dd(e,t){let n,o;if(e!==void 0){if(!zc(e))throw X("file_not_found",`there is no file ${e}`);if(Qc(e).size>Bs)throw X("file_too_large",`${e} is larger than 1 MB`);n=Xc(e,"utf8"),o=e}else n=t??"",o="standard input";if(!n.trim())throw X("nothing_to_send",`${o} is empty`);try{return JSON.parse(n)}catch{throw X("invalid_file",`${o} is not valid JSON; check its syntax`,"See the format: closedloop goals example")}}function md(e){return e.map(t=>{let n=t.allowed&&t.allowed.length?` (allowed: ${t.allowed.slice(0,12).join(", ")}${t.allowed.length>12?", \u2026":""})`:"";return`goal ${t.goal+1}${t.key?` (${t.key})`:""}, ${t.field}: ${t.message}${n}`}).join(`
120
- `)}function pd(e,t){let n=o=>o!==null&&typeof o=="object"&&!Array.isArray(o)&&!Ct(o,"parent")?{...o,parent:t}:o;return Array.isArray(e)?e.map(n):e!==null&&typeof e=="object"&&Ct(e,"goals")&&Array.isArray(e.goals)?{...e,goals:e.goals.map(n)}:n(e)}async function gd(e,t){if(t.file===void 0==(t.stdin!==!0))throw X("nothing_to_send","give the goals with --file <path> or --stdin","See the format: closedloop goals example");let n=dd(t.file,t.stdin?await e.runtime.readStdin(Bs):void 0),o=t.parent===void 0?n:pd(n,await de(e,t.parent)),i=$(e.runtime),r=await v(e.runtime,i,"POST",`/api/user-goals${t.dryRun?"?dry_run=true":""}`,o);if(r.status===400&&Array.isArray(r.body.problems)){let l=r.body.problems;throw new p("invalid_goals",`nothing was saved; fix ${l.length===1?"this":`these ${l.length}`}:
121
- ${md(l)}`,g.Failure,"The values a field takes: closedloop goals fields --field <name>")}if(r.status!==200||!Array.isArray(r.body.goals))throw C(r,"api_error","The goals could not be saved. Try again.");let s={dry_run:r.body.dry_run===!0,goals:r.body.goals},a=[...s.goals].sort((l,u)=>l.index-u.index).map(l=>l.id);!s.dry_run&&a.length&&a.every(l=>typeof l=="string")&&await Ce(e,hi,{ids:a}),e.print.result(s,l=>{let u={created:"created",updated:"updated",would_create:"would be created",would_update:"would update the goal with this key"},c=l.goals.map(h=>`goal ${h.index+1}${h.key?` (${h.key})`:""}: ${Object.prototype.hasOwnProperty.call(u,h.result)?u[h.result]:h.result}${h.state&&h.result!=="would_create"?`, ${h.state}`:""} \xB7 ${h.matching_insights===null?"insights unknown":`${k(h.matching_insights)} matching insight${h.matching_insights===1?"":"s"}`}`),m=!l.dry_run&&l.goals.some(h=>h.state==="draft")?`
122
- ${e.print.style.dim("A draft is activated once it has a key result with a target: closedloop goals metrics add <goal> ..., then closedloop goals activate <goal>")}`:"";return`${l.dry_run?`Checked, nothing saved:
123
- `:""}${c.join(`
124
- `)}${m}`})}async function hd(e,t,n,o){let i=Number(n);if(n.trim()===""||!Number.isFinite(i))throw X("invalid_value",`'${n}' is not a number`);let r=await de(e,t),s=$(e.runtime),a=await v(e.runtime,s,"POST",`/api/user-goals/${r}/values`,o===void 0?{value:i}:{value:i,measured_at:o});if(a.status===404)throw new p("goal_not_found","there is no goal with that id you can see",g.Failure);if(a.status===403)throw new p("not_your_goal","only the goal's owner records its values",g.Failure);if(a.status===409)throw new p("goal_archived",String(a.body.error??"this goal is archived; its values can no longer change"),g.Failure);let l=a.body.value;if(a.status!==200||!l)throw C(a,"api_error","The value could not be recorded. Try again.");let u=typeof a.body.time_zone=="string"?a.body.time_zone:null;e.print.result(l,c=>`${c.result==="replaced"?"Replaced":"Recorded"} ${c.value} for ${Ys(c.measured_at,u)}.`)}async function fd(e,t,n){let o=await de(e,t),i=$(e.runtime);if(!n){if(!e.interactive)throw new p("confirmation_required","deleting a goal needs confirmation",g.Usage,`Run it again with --yes: closedloop goals delete ${t} --yes`);if((await e.runtime.prompt("Delete this goal? It is archived: its values and history stay, and it leaves your list. Type yes to confirm: ")).trim().toLowerCase()!=="yes"){e.print.result({status:"cancelled",id:o},()=>"Nothing changed.");return}}let r=await kt(e,i),s=await v(e.runtime,i,"DELETE",`/api/user-goals/${o}`);if(s.status===404)throw new p("goal_not_found","there is no goal with that id you can see",g.Failure);if(s.status===403)throw new p("not_your_goal","only the goal's owner deletes it",g.Failure);if(s.status===409){let u=s.body.state==="closed";throw new p(u?"goal_closed":"goal_archived",String(s.body.error??(u?"this goal is closed; it stays as it is":"this goal is already archived")),g.Failure)}if(s.status!==200)throw C(s,"api_error","The goal could not be deleted. Try again.");r?.id===o&&Vn(e.runtime);let a=s.body.archived,l=typeof a?.archived_at=="string"?a.archived_at:null;e.print.result({status:"archived",id:o,archived_at:l},()=>"Archived the goal. Its values and history stay; see it with closedloop goals list --archived.")}function Xs(e,t){if(e.status===404)return new p("goal_not_found","there is no goal with that id you can see",g.Failure);if(e.status===403)return new p("not_your_goal",String(e.body.error??"only the goal's owner can do that"),g.Failure);if(e.status===409&&e.body.code==="goal_not_ready"&&Array.isArray(e.body.missing)){let n=e.body.missing;return new p("goal_not_ready",`the goal is not activated; it still needs:
99
+ ${s.dim("Use these in a goal's scope; see closedloop goals example")}`})}function Vc(e,t){let n,o;if(e!==void 0){if(!Ic(e))throw we("file_not_found",`there is no file ${e}`);if(Pc(e).size>Ls)throw we("file_too_large",`${e} is larger than 1 MB`);n=xc(e,"utf8"),o=e}else n=t??"",o="standard input";if(!n.trim())throw we("nothing_to_send",`${o} is empty`);try{return JSON.parse(n)}catch{throw we("invalid_file",`${o} is not valid JSON; check its syntax`,"See the format: closedloop goals example")}}function Bc(e){return e.map(t=>{let n=t.allowed&&t.allowed.length?` (allowed: ${t.allowed.slice(0,12).join(", ")}${t.allowed.length>12?", \u2026":""})`:"";return`goal ${t.goal+1}${t.key?` (${t.key})`:""}, ${t.field}: ${t.message}${n}`}).join(`
100
+ `)}function Yc(e,t){let n=o=>o!==null&&typeof o=="object"&&!Array.isArray(o)&&!vt(o,"parent")?{...o,parent:t}:o;return Array.isArray(e)?e.map(n):e!==null&&typeof e=="object"&&vt(e,"goals")&&Array.isArray(e.goals)?{...e,goals:e.goals.map(n)}:n(e)}async function Kc(e,t){if(t.file===void 0==(t.stdin!==!0))throw we("nothing_to_send","give the goals with --file <path> or --stdin","See the format: closedloop goals example");let n=Vc(t.file,t.stdin?await e.runtime.readStdin(Ls):void 0),o=t.parent===void 0?n:Yc(n,await ce(e,t.parent)),i=v(e.runtime),r=await $(e.runtime,i,"POST",`/api/user-goals${t.dryRun?"?dry_run=true":""}`,o);if(r.status===400&&Array.isArray(r.body.problems)){let a=r.body.problems;throw new p("invalid_goals",`nothing was saved; fix ${a.length===1?"this":`these ${a.length}`}:
101
+ ${Bc(a)}`,g.Failure,"The values a field takes: closedloop goals fields --field <name>")}if(r.status!==200||!Array.isArray(r.body.goals))throw C(r,"api_error","The goals could not be saved. Try again.");let s={dry_run:r.body.dry_run===!0,goals:r.body.goals};e.print.result(s,a=>{let l={created:"created",updated:"updated",would_create:"would be created",would_update:"would update the goal with this key"},u=a.goals.map(d=>`goal ${d.index+1}${d.key?` (${d.key})`:""}: ${Object.prototype.hasOwnProperty.call(l,d.result)?l[d.result]:d.result}${d.state&&d.result!=="would_create"?`, ${d.state}`:""} \xB7 ${d.matching_insights===null?"insights unknown":`${k(d.matching_insights)} matching insight${d.matching_insights===1?"":"s"}`}`+(typeof d.id=="string"&&d.id?`
102
+ ${R(e.print.style," ",d.id)}`:"")),m=!a.dry_run&&a.goals.some(d=>d.state==="draft")?`
103
+ ${e.print.style.dim("A draft is activated once it has a key result with a target: closedloop goals metrics add <goal> ..., then closedloop goals activate <goal>")}`:"";return`${a.dry_run?`Checked, nothing saved:
104
+ `:""}${u.join(`
105
+ `)}${m}`})}async function zc(e,t,n,o){let i=Number(n);if(n.trim()===""||!Number.isFinite(i))throw we("invalid_value",`'${n}' is not a number`);let r=await ce(e,t),s=v(e.runtime),a=await $(e.runtime,s,"POST",`/api/user-goals/${r}/values`,o===void 0?{value:i}:{value:i,measured_at:o});if(a.status===404)throw Ct();if(a.status===403)throw new p("not_your_goal","only the goal's owner records its values",g.Failure);if(a.status===409)throw new p("goal_archived",String(a.body.error??"this goal is archived; its values can no longer change"),g.Failure);let l=a.body.value;if(a.status!==200||!l)throw C(a,"api_error","The value could not be recorded. Try again.");let u=typeof a.body.time_zone=="string"?a.body.time_zone:null;e.print.result(l,c=>`${c.result==="replaced"?"Replaced":"Recorded"} ${c.value} for ${Ds(c.measured_at,u)}.`)}async function Xc(e,t,n){let o=await ce(e,t),i=v(e.runtime);if(!n){if(!e.interactive)throw new p("confirmation_required","deleting a goal needs confirmation",g.Usage,`Run it again with --yes: closedloop goals delete ${o} --yes`);if((await e.runtime.prompt("Delete this goal? It is archived: its values and history stay, and it leaves your list. Type yes to confirm: ")).trim().toLowerCase()!=="yes"){e.print.result({status:"cancelled",id:o},()=>"Nothing changed.");return}}let r=await kt(e,i),s=await $(e.runtime,i,"DELETE",`/api/user-goals/${o}`);if(s.status===404)throw Ct();if(s.status===403)throw new p("not_your_goal","only the goal's owner deletes it",g.Failure);if(s.status===409){let u=s.body.state==="closed";throw new p(u?"goal_closed":"goal_archived",String(s.body.error??(u?"this goal is closed; it stays as it is":"this goal is already archived")),g.Failure)}if(s.status!==200)throw C(s,"api_error","The goal could not be deleted. Try again.");r?.id===o&&Fn(e.runtime);let a=s.body.archived,l=typeof a?.archived_at=="string"?a.archived_at:null;e.print.result({status:"archived",id:o,archived_at:l},()=>"Archived the goal. Its values and history stay; see it with closedloop goals list --archived.")}function Ws(e,t){if(e.status===404)return Ct();if(e.status===403)return new p("not_your_goal",String(e.body.error??"only the goal's owner can do that"),g.Failure);if(e.status===409&&e.body.code==="goal_not_ready"&&Array.isArray(e.body.missing)){let n=e.body.missing;return new p("goal_not_ready",`the goal is not activated; it still needs:
125
106
  ${n.map(o=>` - ${o.message}`).join(`
126
- `)}`,g.Failure,"Add a key result with a target: closedloop goals metrics add <goal> --metric <metric> --period month --target '>=100'")}return e.status===409?new p(`goal_${String(e.body.state??"state")}`,String(e.body.error??t),g.Failure):C(e,"api_error",t)}async function yd(e,t,n){let o=await de(e,t),i=$(e.runtime),r=await v(e.runtime,i,"POST",`/api/user-goals/${o}/activate`,n===void 0?{}:{reason:n}),s=r.body.goal;if(r.status!==200||!s||typeof s.id!="string")throw Xs(r,"The goal could not be activated. Try again.");let a=Number(r.body.metrics??0),l=Number(r.body.baselines_measured??0);e.print.result({status:"active",id:s.id,metrics:a,baselines_measured:l},u=>[`Activated ${s.title}. Its statement, scope and parent now stay as they are; it can gain metrics and new targets.`,`The first period of its ${u.metrics} metric${u.metrics===1?"":"s"} starts today; ${u.baselines_measured===u.metrics?"every baseline was measured now":`${u.baselines_measured} baseline${u.baselines_measured===1?" was":"s were"} measured now, the rest tonight`}.`].join(`
127
- `))}async function wd(e,t,n,o){let i=await de(e,t),r=$(e.runtime);if(!o){if(!e.interactive)throw new p("confirmation_required","closing a goal needs confirmation",g.Usage,`Run it again with --yes: closedloop goals close ${t} --yes`);if((await e.runtime.prompt("Close this goal? It is finished for good: its results and history stay, and it cannot be opened again. Type yes to confirm: ")).trim().toLowerCase()!=="yes"){e.print.result({status:"cancelled",id:i},()=>"Nothing changed.");return}}let s=await kt(e,r),a=await v(e.runtime,r,"POST",`/api/user-goals/${i}/close`,n===void 0?{}:{reason:n}),l=a.body.closed;if(a.status!==200||!l)throw Xs(a,"The goal could not be closed. Try again.");s?.id===i&&Vn(e.runtime);let u=typeof l.closed_at=="string"?l.closed_at:null;e.print.result({status:"closed",id:i,closed_at:u},()=>"Closed the goal. Its results and history stay; see it with closedloop goals list --state closed. Its sub-goals keep their own state.")}var Qs=(e,t)=>{let n=e.command("goals").alias("goal").description("Your goals: a number to move by a date, scoped to the insights it is about.");n.command("list").option("--team","Also the goals teammates share with the team.").option("--archived","Also the goals you deleted, which are kept archived.").option("--state <states>","Only goals in these states, e.g. draft or active,closed (draft, active, closed, archived).").description("Your goals as a tree (each sub-goal under its parent), with their state, current value and how many insights each is about.").action(async o=>rd(t,o.team===!0,o.archived===!0,o.state)),n.command("show").argument("<which>","A number from the last list, or a goal id.").description("One goal in full, with its state, parent goal, metrics (targets, baselines, values and finished periods) and recorded values; for a draft, what it still needs to be activated.").action(async o=>sd(t,o)),n.command("use").argument("[which]","A number from the last list, or a goal id.").description("Pick the goal you work on: insights search then reads only its insights (its scope, bearing on its relevance) until you pick another. With no goal, says which one you work on.").option("--clear","Work on no goal: insights search reads every insight.").action(async(o,i)=>ld(t,o,i.clear===!0)),n.command("context").argument("[which]","A number from the last list, or a goal id; the goal you work on when left out.").description("The deal blockers and churn risks in a goal's scope: deal blockers when it bears on new sales, churn risks when it bears on retention.").action(async o=>ud(t,o)),n.command("fields").description("The fields a goal can be scoped by in this workspace, with the values in use and how many insights each matches.").option("--field <name>","Every value of one field, e.g. country.").action(async o=>cd(t,o.field)),n.command("example").description('An example goals file to copy: { "goals": [ { key, title, description, metric: { name, unit, direction, baseline, target, due }, scope, visibility } ] }.').action(()=>t.print.result(Zc,o=>JSON.stringify(o,null,2))),n.command("create").description('Create goals from a JSON file: one goal, a list, or { "goals": [...] } (see `goals example`). Every goal is checked against your workspace and nothing is saved unless all pass; a goal with a key you used before replaces that goal (a field left out takes its default, so a shared goal sent without visibility becomes private again), as far as its state allows: an active goal keeps its title, scope, parent and metric name, unit and direction, and a new target needs a reason. A new goal is a draft. title, metric.name, metric.direction (increase or decrease) and metric.target are required; scope maps fields from `goals fields` to a value or a list (a product, area or feature by name or id). Products, areas and features together match any of them, at any level; outcomeCategory names the business outcomes the insights must bear on. A goal measures the metrics you add with `goals metrics add`; nothing is added by itself.').option("--file <path>","The JSON file with the goals.").option("--stdin","Read the JSON from standard input.").option("--dry-run","Check the goals and say how many insights each matches, without saving.").option("--parent <goal>","Make every goal in the file a sub-goal of this one (a number from the last list, or an id), unless it names its own parent.").action(async o=>gd(t,o)),n.command("activate").argument("<which>","A number from the last list, or a goal id.").description("Activate one of your draft goals. It needs a key result with a target (and every metric its target); its first period starts today and its baselines are measured now. Then its statement, scope and parent stay as they are.").option("--reason <text>","Why, kept in the goal's history.").action(async(o,i)=>yd(t,o,i.reason)),n.command("close").argument("<which>","A number from the last list, or a goal id.").description("Close one of your active goals: it is finished, its metrics stop and keep their results, and it cannot be opened again. Its sub-goals keep their own state.").option("--reason <text>","Why, kept in the goal's history.").option("--yes","Skip the confirmation.").action(async(o,i)=>wd(t,o,i.reason,i.yes===!0));for(let[o,i]of[["set",!1],["push",!0]])n.command(o,{hidden:i}).argument("<which>","A number from the last list, or a goal id.").argument("<value>","The goal's number as measured, e.g. 47.5 or -3.").description("Set one of your goals' numbers as measured now (or at --at). Earlier values stay as the goal's history; a second value for the same moment replaces the first.").option("--at <date>","When it was measured, e.g. 2026-10-20 (default: now).").action(async(r,s,a)=>hd(t,r,s,a.at));n.command("delete").argument("<which>","A number from the last list, or a goal id.").description("Delete one of your goals. It is archived: its values, metrics and history stay, and it leaves your list.").option("--yes","Skip the confirmation.").action(async(o,i)=>fd(t,o,i.yes===!0)),Fs(n,t,de)};import{existsSync as bd,readFileSync as $d,statSync as vd}from"node:fs";import{extname as Cd}from"node:path";function _d(e){let t=[],n=[],o="",i=!1,r=e.charCodeAt(0)===65279?e.slice(1):e;for(let s=0;s<r.length;s++){let a=r[s];i?a==='"'&&r[s+1]==='"'?(o+='"',s++):a==='"'?i=!1:o+=a:a==='"'&&o===""?i=!0:a===","?(n.push(o),o=""):a===`
107
+ `)}`,g.Failure,"Add a key result with a target: closedloop goals metrics add <goal> --metric <metric> --period month --target '>=100'")}return e.status===409?new p(`goal_${String(e.body.state??"state")}`,String(e.body.error??t),g.Failure):C(e,"api_error",t)}async function Qc(e,t,n){let o=await ce(e,t),i=v(e.runtime),r=await $(e.runtime,i,"POST",`/api/user-goals/${o}/activate`,n===void 0?{}:{reason:n}),s=r.body.goal;if(r.status!==200||!s||typeof s.id!="string")throw Ws(r,"The goal could not be activated. Try again.");let a=Number(r.body.metrics??0),l=Number(r.body.baselines_measured??0);e.print.result({status:"active",id:s.id,metrics:a,baselines_measured:l},u=>[`Activated ${s.title}. Its statement, scope and parent now stay as they are; it can gain metrics and new targets.`,`The first period of its ${u.metrics} metric${u.metrics===1?"":"s"} starts today; ${u.baselines_measured===u.metrics?"every baseline was measured now":`${u.baselines_measured} baseline${u.baselines_measured===1?" was":"s were"} measured now, the rest tonight`}.`].join(`
108
+ `))}async function Zc(e,t,n,o){let i=await ce(e,t),r=v(e.runtime);if(!o){if(!e.interactive)throw new p("confirmation_required","closing a goal needs confirmation",g.Usage,`Run it again with --yes: closedloop goals close ${i} --yes`);if((await e.runtime.prompt("Close this goal? It is finished for good: its results and history stay, and it cannot be opened again. Type yes to confirm: ")).trim().toLowerCase()!=="yes"){e.print.result({status:"cancelled",id:i},()=>"Nothing changed.");return}}let s=await kt(e,r),a=await $(e.runtime,r,"POST",`/api/user-goals/${i}/close`,n===void 0?{}:{reason:n}),l=a.body.closed;if(a.status!==200||!l)throw Ws(a,"The goal could not be closed. Try again.");s?.id===i&&Fn(e.runtime);let u=typeof l.closed_at=="string"?l.closed_at:null;e.print.result({status:"closed",id:i,closed_at:u},()=>"Closed the goal. Its results and history stay; see it with closedloop goals list --state closed. Its sub-goals keep their own state.")}var Us=(e,t)=>{let n=e.command("goals").alias("goal").description("Your goals: a number to move by a date, scoped to the insights it is about.");n.command("list").option("--team","Also the goals teammates share with the team.").option("--archived","Also the goals you deleted, which are kept archived.").option("--state <states>","Only goals in these states, e.g. draft or active,closed (draft, active, closed, archived).").description("Your goals as a tree (each sub-goal under its parent), with their state, current value, how many insights each is about, and each goal's id.").action(async o=>Uc(t,o.team===!0,o.archived===!0,o.state)),n.command("show").argument("<goal>",bt).description("One goal in full, with its state, parent goal, metrics (targets, baselines, values and finished periods) and recorded values; for a draft, what it still needs to be activated.").action(async o=>Hc(t,o)),n.command("use").argument("[goal]",bt).description("Pick the goal you work on: insights search then reads only its insights (its scope, bearing on its relevance) until you pick another. With no goal, says which one you work on.").option("--clear","Work on no goal: insights search reads every insight.").action(async(o,i)=>Gc(t,o,i.clear===!0)),n.command("context").argument("[goal]","The goal's id, from closedloop goals list; the goal you work on when left out.").description("The deal blockers and churn risks in a goal's scope: deal blockers when it bears on new sales, churn risks when it bears on retention.").action(async o=>qc(t,o)),n.command("fields").description("The fields a goal can be scoped by in this workspace, with the values in use and how many insights each matches.").option("--field <name>","Every value of one field, e.g. country.").action(async o=>Jc(t,o.field)),n.command("example").description('An example goals file to copy: { "goals": [ { key, title, description, metric: { name, unit, direction, baseline, target, due }, scope, visibility } ] }.').action(()=>t.print.result(Nc,o=>JSON.stringify(o,null,2))),n.command("create").description('Create goals from a JSON file: one goal, a list, or { "goals": [...] } (see `goals example`). Every goal is checked against your workspace and nothing is saved unless all pass; a goal with a key you used before replaces that goal (a field left out takes its default, so a shared goal sent without visibility becomes private again), as far as its state allows: an active goal keeps its title, scope, parent and metric name, unit and direction, and a new target needs a reason. A new goal is a draft. title, metric.name, metric.direction (increase or decrease) and metric.target are required; scope maps fields from `goals fields` to a value or a list (a product, area or feature by name or id). Products, areas and features together match any of them, at any level; outcomeCategory names the business outcomes the insights must bear on. A goal measures the metrics you add with `goals metrics add`; nothing is added by itself.').option("--file <path>","The JSON file with the goals.").option("--stdin","Read the JSON from standard input.").option("--dry-run","Check the goals and say how many insights each matches, without saving.").option("--parent <goal>","Make every goal in the file a sub-goal of the goal with this id (from closedloop goals list), unless it names its own parent.").action(async o=>Kc(t,o)),n.command("activate").argument("<goal>",bt).description("Activate one of your draft goals. It needs a key result with a target (and every metric its target); its first period starts today and its baselines are measured now. Then its statement, scope and parent stay as they are.").option("--reason <text>","Why, kept in the goal's history.").action(async(o,i)=>Qc(t,o,i.reason)),n.command("close").argument("<goal>",bt).description("Close one of your active goals: it is finished, its metrics stop and keep their results, and it cannot be opened again. Its sub-goals keep their own state.").option("--reason <text>","Why, kept in the goal's history.").option("--yes","Skip the confirmation.").action(async(o,i)=>Zc(t,o,i.reason,i.yes===!0));for(let[o,i]of[["set",!1],["push",!0]])n.command(o,{hidden:i}).argument("<goal>",bt).argument("<value>","The goal's number as measured, e.g. 47.5 or -3.").description("Set one of your goals' numbers as measured now (or at --at). Earlier values stay as the goal's history; a second value for the same moment replaces the first.").option("--at <date>","When it was measured, e.g. 2026-10-20 (default: now).").action(async(r,s,a)=>zc(t,r,s,a.at));n.command("delete").argument("<goal>",bt).description("Delete one of your goals. It is archived: its values, metrics and history stay, and it leaves your list.").option("--yes","Skip the confirmation.").action(async(o,i)=>Xc(t,o,i.yes===!0)),Os(n,t,ce)};import{existsSync as td,readFileSync as nd,statSync as od}from"node:fs";import{extname as id}from"node:path";function ed(e){let t=[],n=[],o="",i=!1,r=e.charCodeAt(0)===65279?e.slice(1):e;for(let s=0;s<r.length;s++){let a=r[s];i?a==='"'&&r[s+1]==='"'?(o+='"',s++):a==='"'?i=!1:o+=a:a==='"'&&o===""?i=!0:a===","?(n.push(o),o=""):a===`
128
109
  `||a==="\r"?(a==="\r"&&r[s+1]===`
129
- `&&s++,n.push(o),t.push(n),n=[],o=""):o+=a}if(i)throw new Error("a quoted field is not closed");return(o!==""||n.length>0)&&(n.push(o),t.push(n)),t.filter(s=>s.some(a=>a.trim()!==""))}function Zs(e){let[t,...n]=_d(e);if(!t)return[];let o=t.map(i=>i.trim().toLowerCase());return n.map(i=>Object.fromEntries(o.map((r,s)=>[r,i[s]??""])))}var Kn=["content","title","reporter_name","reporter_email","customer","date","url","id","tags"],kd=100,Sd=4*1024*1024,na=50*1024*1024,ea=900,ta=5,Ad=new Set([".csv",".json",".jsonl",".ndjson"]),Od="They wait until your workspace's company profile is ready: set your company website with `closedloop company --website <url>`, and they are processed once it is researched.",Td="They are analysed once your company's email domains are confirmed; see: closedloop company",D=(e,t,n)=>new p(e,t,g.Usage,n);function Rd(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw D("invalid_record",`record ${t} is not an object with a content field`);let n={};for(let[o,i]of Object.entries(e)){let r=o.trim().toLowerCase();if(!Kn.includes(r))throw D("unknown_field",`record ${t} has a field that ingest does not take`,`The fields: ${Kn.join(", ")}.`);if(!(i==null||i===""))if(r==="tags"){let s=Array.isArray(i)?i:typeof i=="string"?i.split(";"):null;if(!s||s.some(a=>typeof a!="string"))throw D("invalid_record",`record ${t}'s tags must be words (in a CSV, separated by ;)`);n.tags=s.map(a=>a.trim()).filter(Boolean)}else if(typeof i=="string"||typeof i=="number")n[r]=String(i).trim();else throw D("invalid_record",`record ${t}'s ${r} must be text`)}if(typeof n.content!="string"||!n.content)throw D("invalid_record",`record ${t} has no content`);return n}function Ed(e){if(!bd(e))throw D("file_not_found",`there is no file ${e}`);if(vd(e).size>na)throw D("file_too_large",`${e} is larger than 50 MB`,"Split it into smaller files.");return{text:$d(e,"utf8"),ext:Cd(e).toLowerCase()}}function Id(e,t,n){let o;try{if(t===".csv")o=Zs(e);else if(t===".json"){let i=JSON.parse(e);o=Array.isArray(i)?i:[i]}else o=e.split(/\r?\n/).filter(i=>i.trim()).map(i=>JSON.parse(i))}catch{throw D("invalid_file",`${n} could not be read as ${t.slice(1).toUpperCase()}; check its syntax`,`Each record needs content, and may include ${Kn.slice(1).join(", ")}.`)}if(t===".csv"&&o.length>0&&!Object.keys(o[0]).includes("content"))throw D("invalid_file",`${n} has no content column`,`The first row names the columns: content, and any of ${Kn.slice(1).join(", ")}.`);return o.map((i,r)=>Rd(i,r+1))}async function Pd(e,t,n){let o=[t.length>0,n.stdin===!0,n.file!==void 0].filter(Boolean).length;if(o===0)throw D("nothing_to_send","nothing to send","Give some text, --file <path> or --stdin.");if(o>1)throw D("conflicting_options","send text, --file or --stdin, one at a time");let i={title:n.title,reporter_name:n.from,reporter_email:n.email,customer:n.customer,date:n.date,url:n.url,id:n.id},r=Object.values(i).some(l=>l!==void 0)||(n.tag?.length??0)>0,s;if(n.file!==void 0){let{text:l,ext:u}=Ed(n.file);if(Ad.has(u)){if(r)throw D("conflicting_options",`--title, --from and the like describe one record; ${n.file} carries its own fields`);return Id(l,u,n.file)}s=l}else n.stdin?s=await e.runtime.readStdin(na):s=t.join(" ");if(!s.trim())throw D("nothing_to_send","the text to send is empty");let a={content:s.trim()};for(let[l,u]of Object.entries(i))u!==void 0&&u.trim()&&(a[l]=u.trim());return n.tag?.length&&(a.tags=n.tag.map(l=>l.trim()).filter(Boolean)),[a]}function xd(e){let t=[],n=[],o=0,i=0;for(let r of e){let s=Buffer.byteLength(JSON.stringify(r));n.length>0&&(n.length===kd||o+s>Sd)&&(t.push({offset:i,items:n}),i+=n.length,n=[],o=0),n.push(r),o+=s}return n.length>0&&t.push({offset:i,items:n}),t}async function Nd(e,t,n){let o=[],i=[],r=null,s=n.every(a=>typeof a.id=="string"&&a.id)?"records with an id are not sent twice, so sending the file again is safe":"sending the whole file again would send them twice: send only the rest, or give every record an id";for(let a of xd(n)){let l=o.length?`The first ${o.length} record${o.length===1?" was":"s were"} sent; ${s}.`:"",u;try{u=await v(e.runtime,t,"POST","/api/cli/ingest",{items:a.items})}catch(c){if(!(c instanceof p))throw c;let d=c.code==="api_unreachable"||c.code==="api_timeout"?"The last batch may also have been accepted; check with `closedloop ingest list` before resending it.":"";throw new p(c.code,c.message,c.exitCode,[c.hint,l,d].filter(Boolean).join(" ")||void 0)}if(u.status!==201){if(u.body.code==="INVALID_ITEM"&&typeof u.body.index=="number"){let d=String(u.body.error).replace(/^Record \d+ /,"");throw new p("invalid_record",`record ${a.offset+u.body.index+1} ${d.replace(/\.$/,"")}`,g.Failure,l||void 0)}let c=C(u,"ingest_refused","The records could not be sent.");throw new p(c.code,c.message,g.Failure,l||c.hint)}o.push(...u.body.accepted),i.push(...u.body.duplicates),r=typeof u.body.waiting_for=="string"?u.body.waiting_for:null}return{accepted:o,duplicates:i,waitingFor:r}}async function oa(e,t,n){let o=await v(e.runtime,t,"GET",`/api/cli/ingest?limit=${n}`);if(o.status!==200||!Array.isArray(o.body.records))throw C(o,"api_error","The records could not be loaded. Try again.");return o.body.records}async function Ld(e,t,n){let o=new Set(n.map(i=>i.id));for(let i=0;i<=ea;i+=ta){let r=(await oa(e,t,100)).filter(s=>o.has(s.id));if(r.length===o.size&&r.every(s=>s.state==="done"||s.state==="failed"))return r;i<ea&&await e.runtime.sleep(ta*1e3)}throw new p("still_processing","the records are not processed yet",g.Failure,"Check later with: closedloop ingest list")}function ia(e,t){let n=e.state==="done"?`done, ${e.insights} insight${e.insights===1?"":"s"}`:e.state==="waiting"&&e.waiting_for==="company_profile"?"waiting for the company profile":e.state==="waiting"&&e.waiting_for==="email_domains"?"waiting for the email domains":e.state;return t?e.state==="done"?t.green(n):e.state==="failed"?t.red(n):t.orange(n):n}async function Dd(e,t,n){let o=await Pd(e,t,n);if(n.wait&&o.length>100)throw D("wait_too_many","--wait follows up to 100 records","Send without --wait and check with `closedloop ingest list`.");let i=$(e.runtime);await pt(e.runtime,i,"can_manage_integrations","send data in");let{accepted:r,duplicates:s,waitingFor:a}=await Nd(e,i,o),l=s.length?` ${s.length} had an id already sent and ${s.length===1?"was":"were"} skipped.`:"";if(!n.wait||r.length===0||a!==null){e.print.result({status:"sent",sent:r.length,duplicates:s,waiting_for:a,records:r},h=>`Sent ${h.sent} record${h.sent===1?"":"s"}.${l} ${h.waiting_for==="company_profile"?Od:h.waiting_for==="email_domains"?Td:"They are processed in the next few minutes"}; follow them with: closedloop ingest list`);return}let u=e.print.spinner(`Sent ${r.length} record${r.length===1?"":"s"}; waiting for them to be processed\u2026`),c;try{c=await Ld(e,i,r)}finally{u.stop()}let d=c.reduce((h,f)=>h+f.insights,0),m=c.filter(h=>h.state==="failed").length;if(e.print.result({status:"processed",sent:r.length,duplicates:s,insights:d,failed:m,records:c},h=>`Processed ${h.sent} record${h.sent===1?"":"s"}: ${h.insights} insight${h.insights===1?"":"s"}${h.failed?`, ${h.failed} failed`:""}.${l} See them: closedloop insights search --source cli`),m>0)throw new p("ingest_failed",`${m} record${m===1?"":"s"} failed during processing`,g.Failure,"Check the failed records with `closedloop ingest list` or `closedloop ingest status <id>`.")}async function jd(e,t){let n=t===void 0?20:Number(t);if(!Number.isInteger(n)||n<1||n>100)throw D("invalid_option","--limit must be a whole number from 1 to 100");let o=$(e.runtime),i=await oa(e,o,n);e.print.result({records:i},r=>r.records.length===0?"Nothing sent with closedloop ingest yet.":r.records.map(s=>`${e.print.style.dim(P(s.created_at))} ${ia(s,e.print.style)} ${s.title??s.external_id??"(untitled)"} ${e.print.style.dim(`id ${s.id}`)}`).join(`
130
- `))}var Fd=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;async function Ud(e,t){if(!Fd.test(t))throw D("invalid_id",`'${t}' is not a record id`,"Copy it from `closedloop ingest list`.");let n=$(e.runtime),o=await v(e.runtime,n,"GET",`/api/cli/ingest/${t.toLowerCase()}`);if(o.status===404)throw new p("record_not_found","there is no record with that id in your workspace",g.Failure);if(o.status!==200||typeof o.body.state!="string")throw C(o,"api_error","The record could not be loaded. Try again.");let i=o.body;e.print.result(i,r=>`${r.title??r.external_id??"The record"}: ${ia(r)} (sent ${P(r.created_at)}).`)}var Wd=(e,t)=>[...t??[],e],ra=(e,t)=>{let n=e.command("ingest").argument("[text...]","The text to send as one record.").description("Send your own data in: text, a file (CSV, JSON, JSON Lines, text) or standard input.").option("--file <path>","A file: CSV or JSON (Lines) with a content field per record, or a text file as one record.").option("--stdin","Read one record's text from standard input.").option("--title <title>","A title for the record.").option("--from <name>","Who said it.").option("--email <address>","Their email; the insight is tied to their company when it matches a contact in your CRM.").option("--customer <name>","Their company, kept with the record (the insight's company comes from --email).").option("--date <date>","When it was said, e.g. 2026-10-22.").option("--url <link>","Where it came from.").option("--id <id>","Your own id for it; a record with an id already sent is skipped.").option("--tag <word>","A tag (repeatable).",Wd).option("--wait","Wait until processed, and say how many insights it made.").action(async(o,i)=>Dd(t,o,i));n.command("list").description("The latest records sent with ingest, and their state.").option("--limit <n>","How many, up to 100 (default 20).").action(async o=>jd(t,o.limit)),n.command("status").argument("<id>","The record id, from list or from sending.").description("One record's state, and the insights it made.").action(async o=>Ud(t,o))};var Md=["title","quote","reporter","account"],Hd=["critical","high","medium","low"],sa={week:"1week",month:"1month","3months":"3months"},fi={newest:["source_date","desc"],oldest:["source_date","asc"],severity:["severity","asc"],relevance:["relevance_score","desc"]},aa=240;function zn(e,t,n){if(t===void 0)return;let o=t.trim().toLowerCase();if(!n.includes(o))throw new p("invalid_option",`--${e} must be ${n.join(", ")}`,g.Usage);return o}function la(e,t,n,o){if(t===void 0)return n;let i=Number(t);if(!Number.isInteger(i)||i<1||i>o)throw new p("invalid_option",`--${e} must be a whole number from 1 to ${o}`,g.Usage);return i}async function Gd(e,t,n){let o=await v(e.runtime,t,"GET","/api/cli/integrations");if(o.status!==200||!Array.isArray(o.body.integrations))throw C(o,"api_error","Sources could not be loaded. Try again.");let r=o.body.integrations.find(s=>s.slug===n.toLowerCase()||s.name.toLowerCase()===n.toLowerCase());if(!r)throw new p("unknown_source",`there is no source called "${n}"`,g.Usage,"See the names with `closedloop integrations list`.");return r.name}function qd(e){if(!e)return null;let t=e.replace(/\s+/g," ").trim();return t.length>aa?`${t.slice(0,aa-1)}\u2026`:t}function da(e){let t=e.reporter_name||e.customer_name;return[e.account_name,t&&t!==e.account_name?t:null].filter(n=>!!n)}function Jd(e){return e&&`${e[0].toUpperCase()}${e.slice(1)}`}var Vd=e=>e.map(t=>t.replace(/_/g," ")).join(" or ");async function Bd(e,t,n){if(n.all&&n.goal!==void 0)throw new p("invalid_option","give --goal or --all, not both",g.Usage);if(n.all)return null;if(n.goal!==void 0)return{id:await de(e,n.goal),title:"",picked:!1};let o=await kt(e,t);return o?{...o,picked:!0}:null}async function Yd(e,t,n){let o=zn("in",n.in,Md),i=zn("severity",n.severity,Hd),r=zn("last",n.last,Object.keys(sa)),s=zn("sort",n.sort,Object.keys(fi))??"newest",a=la("limit",n.limit,20,100),l=la("page",n.page,1,1e4);if(o&&!t)throw new p("invalid_option","--in needs search words",g.Usage);let u=$(e.runtime),c=new URLSearchParams({page:String(l),limit:String(a),sortBy:fi[s][0],sortOrder:fi[s][1],filter_options:"false"}),d=t?.trim();d&&(o?c.set("searchIn",o):c.set("searchFragments","true"),c.set("search",d)),n.source&&c.set("source",await Gd(e,u,n.source)),n.category&&c.set("category",n.category.trim().toLowerCase()),i&&c.set("severity",i),r&&c.set("timeRange",sa[r]),n.customer&&c.set("customerEmail",n.customer.trim()),n.account&&c.set("companyDomain",n.account.trim().toLowerCase());let m=await Bd(e,u,n);m&&c.set("goalId",m.id);let h=await v(e.runtime,u,"GET",`/api/feedback?${c}`);if(m&&h.status===404)throw new p("goal_not_found",m.picked?`the goal you work on (${m.title}) is gone or you can no longer see it`:"there is no goal with that id you can see",g.Failure,"Pick another: closedloop goals use <goal>, or search every insight with --all");if(m&&h.status===409&&typeof h.body.error=="string")throw new p(typeof h.body.code=="string"?h.body.code:"goal_unsearchable",h.body.error,g.Failure,"Search every insight with --all");if(h.status!==200||!Array.isArray(h.body.data))throw C(h,"api_error","Insights could not be searched. Try again.");let f=h.body.goal,y=m&&f&&typeof f.id=="string"&&typeof f.title=="string"&&f.relevance&&Array.isArray(f.relevance.outcomes)?f:null;if(m&&(!y||y.id.toLowerCase()!==m.id.toLowerCase()))throw new p("goal_search_unsupported","this ClosedLoop AI API cannot search within a goal yet",g.Failure,"Search every insight with --all");let _=h.body.data,S={query:d||null,goal:y,total:Number(h.body.total)||0,page:l,pages:Number(h.body.totalPages)||0,insights:_.map(b=>({id:b.id,title:b.signal_title,quote:b.content,category:b.category,severity:b.severity,account:b.account_name,customer:b.customer_name,reporter:b.reporter_name,source:b.source_name,date:b.source_date??b.created_at,feature_area:b.feature_area,deal_blocker:b.is_deal_blocker===!0}))};await Ce(e,ma,{ids:S.insights.map(b=>b.id)}),e.print.result(S,b=>{let F=e.print.style,oe=b.goal?` in ${b.goal.title}`:"",G=b.goal?F.dim(`Only ${b.goal.title}'s insights, bearing on ${Vd(b.goal.relevance.outcomes)}; every insight: --all`):null;if(b.insights.length===0)return[b.query?`No insights${oe} match "${b.query}".`:`No insights${oe} match.`,...G?[G]:[]].join(`
131
- `);let At=`${F.bold(`${b.total} insight${b.total===1?"":"s"}`)}${b.query?` for "${b.query}"`:""}${oe}${F.dim(` \xB7 ${Kd[s]}${b.pages>1?` \xB7 page ${b.page} of ${b.pages}`:""}`)}`,et=String(b.insights.length).length,ao=b.insights.map((J,V)=>zd(F,J,String(V+1).padStart(et),n.ids===!0)),A=[b.page<b.pages?`more: --page ${b.page+1}`:null,`read one: closedloop insights show ${b.insights.length>1?"<number>":"1"}`].filter(Boolean).join(" \xB7 ");return`${At}${G?`
132
- ${G}`:""}
110
+ `&&s++,n.push(o),t.push(n),n=[],o=""):o+=a}if(i)throw new Error("a quoted field is not closed");return(o!==""||n.length>0)&&(n.push(o),t.push(n)),t.filter(s=>s.some(a=>a.trim()!==""))}function Hs(e){let[t,...n]=ed(e);if(!t)return[];let o=t.map(i=>i.trim().toLowerCase());return n.map(i=>Object.fromEntries(o.map((r,s)=>[r,i[s]??""])))}var Hn=["content","title","reporter_name","reporter_email","customer","date","url","id","tags"],rd=100,sd=4*1024*1024,qs=50*1024*1024,Ms=900,Gs=5,ad=new Set([".csv",".json",".jsonl",".ndjson"]),ld="They wait until your workspace's company profile is ready: set your company website with `closedloop company --website <url>`, and they are processed once it is researched.",ud="They are analysed once your company's email domains are confirmed; see: closedloop company",q=(e,t,n)=>new p(e,t,g.Usage,n);function cd(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw q("invalid_record",`record ${t} is not an object with a content field`);let n={};for(let[o,i]of Object.entries(e)){let r=o.trim().toLowerCase();if(!Hn.includes(r))throw q("unknown_field",`record ${t} has a field that ingest does not take`,`The fields: ${Hn.join(", ")}.`);if(!(i==null||i===""))if(r==="tags"){let s=Array.isArray(i)?i:typeof i=="string"?i.split(";"):null;if(!s||s.some(a=>typeof a!="string"))throw q("invalid_record",`record ${t}'s tags must be words (in a CSV, separated by ;)`);n.tags=s.map(a=>a.trim()).filter(Boolean)}else if(typeof i=="string"||typeof i=="number")n[r]=String(i).trim();else throw q("invalid_record",`record ${t}'s ${r} must be text`)}if(typeof n.content!="string"||!n.content)throw q("invalid_record",`record ${t} has no content`);return n}function dd(e){if(!td(e))throw q("file_not_found",`there is no file ${e}`);if(od(e).size>qs)throw q("file_too_large",`${e} is larger than 50 MB`,"Split it into smaller files.");return{text:nd(e,"utf8"),ext:id(e).toLowerCase()}}function md(e,t,n){let o;try{if(t===".csv")o=Hs(e);else if(t===".json"){let i=JSON.parse(e);o=Array.isArray(i)?i:[i]}else o=e.split(/\r?\n/).filter(i=>i.trim()).map(i=>JSON.parse(i))}catch{throw q("invalid_file",`${n} could not be read as ${t.slice(1).toUpperCase()}; check its syntax`,`Each record needs content, and may include ${Hn.slice(1).join(", ")}.`)}if(t===".csv"&&o.length>0&&!Object.keys(o[0]).includes("content"))throw q("invalid_file",`${n} has no content column`,`The first row names the columns: content, and any of ${Hn.slice(1).join(", ")}.`);return o.map((i,r)=>cd(i,r+1))}async function pd(e,t,n){let o=[t.length>0,n.stdin===!0,n.file!==void 0].filter(Boolean).length;if(o===0)throw q("nothing_to_send","nothing to send","Give some text, --file <path> or --stdin.");if(o>1)throw q("conflicting_options","send text, --file or --stdin, one at a time");let i={title:n.title,reporter_name:n.from,reporter_email:n.email,customer:n.customer,date:n.date,url:n.url,id:n.id},r=Object.values(i).some(l=>l!==void 0)||(n.tag?.length??0)>0,s;if(n.file!==void 0){let{text:l,ext:u}=dd(n.file);if(ad.has(u)){if(r)throw q("conflicting_options",`--title, --from and the like describe one record; ${n.file} carries its own fields`);return md(l,u,n.file)}s=l}else n.stdin?s=await e.runtime.readStdin(qs):s=t.join(" ");if(!s.trim())throw q("nothing_to_send","the text to send is empty");let a={content:s.trim()};for(let[l,u]of Object.entries(i))u!==void 0&&u.trim()&&(a[l]=u.trim());return n.tag?.length&&(a.tags=n.tag.map(l=>l.trim()).filter(Boolean)),[a]}function gd(e){let t=[],n=[],o=0,i=0;for(let r of e){let s=Buffer.byteLength(JSON.stringify(r));n.length>0&&(n.length===rd||o+s>sd)&&(t.push({offset:i,items:n}),i+=n.length,n=[],o=0),n.push(r),o+=s}return n.length>0&&t.push({offset:i,items:n}),t}async function hd(e,t,n){let o=[],i=[],r=null,s=n.every(a=>typeof a.id=="string"&&a.id)?"records with an id are not sent twice, so sending the file again is safe":"sending the whole file again would send them twice: send only the rest, or give every record an id";for(let a of gd(n)){let l=o.length?`The first ${o.length} record${o.length===1?" was":"s were"} sent; ${s}.`:"",u;try{u=await $(e.runtime,t,"POST","/api/cli/ingest",{items:a.items})}catch(c){if(!(c instanceof p))throw c;let m=c.code==="api_unreachable"||c.code==="api_timeout"?"The last batch may also have been accepted; check with `closedloop ingest list` before resending it.":"";throw new p(c.code,c.message,c.exitCode,[c.hint,l,m].filter(Boolean).join(" ")||void 0)}if(u.status!==201){if(u.body.code==="INVALID_ITEM"&&typeof u.body.index=="number"){let m=String(u.body.error).replace(/^Record \d+ /,"");throw new p("invalid_record",`record ${a.offset+u.body.index+1} ${m.replace(/\.$/,"")}`,g.Failure,l||void 0)}let c=C(u,"ingest_refused","The records could not be sent.");throw new p(c.code,c.message,g.Failure,l||c.hint)}o.push(...u.body.accepted),i.push(...u.body.duplicates),r=typeof u.body.waiting_for=="string"?u.body.waiting_for:null}return{accepted:o,duplicates:i,waitingFor:r}}async function Js(e,t,n){let o=await $(e.runtime,t,"GET",`/api/cli/ingest?limit=${n}`);if(o.status!==200||!Array.isArray(o.body.records))throw C(o,"api_error","The records could not be loaded. Try again.");return o.body.records}async function fd(e,t,n){let o=new Set(n.map(i=>i.id));for(let i=0;i<=Ms;i+=Gs){let r=(await Js(e,t,100)).filter(s=>o.has(s.id));if(r.length===o.size&&r.every(s=>s.state==="done"||s.state==="failed"))return r;i<Ms&&await e.runtime.sleep(Gs*1e3)}throw new p("still_processing","the records are not processed yet",g.Failure,"Check later with: closedloop ingest list")}function Vs(e,t){let n=e.state==="done"?`done, ${e.insights} insight${e.insights===1?"":"s"}`:e.state==="waiting"&&e.waiting_for==="company_profile"?"waiting for the company profile":e.state==="waiting"&&e.waiting_for==="email_domains"?"waiting for the email domains":e.state;return t?e.state==="done"?t.green(n):e.state==="failed"?t.red(n):t.orange(n):n}async function yd(e,t,n){let o=await pd(e,t,n);if(n.wait&&o.length>100)throw q("wait_too_many","--wait follows up to 100 records","Send without --wait and check with `closedloop ingest list`.");let i=v(e.runtime);await mt(e.runtime,i,"can_manage_integrations","send data in");let{accepted:r,duplicates:s,waitingFor:a}=await hd(e,i,o),l=s.length?` ${s.length} had an id already sent and ${s.length===1?"was":"were"} skipped.`:"";if(!n.wait||r.length===0||a!==null){e.print.result({status:"sent",sent:r.length,duplicates:s,waiting_for:a,records:r},h=>`Sent ${h.sent} record${h.sent===1?"":"s"}.${l} ${h.waiting_for==="company_profile"?ld:h.waiting_for==="email_domains"?ud:"They are processed in the next few minutes"}; follow them with: closedloop ingest list`);return}let u=e.print.spinner(`Sent ${r.length} record${r.length===1?"":"s"}; waiting for them to be processed\u2026`),c;try{c=await fd(e,i,r)}finally{u.stop()}let m=c.reduce((h,f)=>h+f.insights,0),d=c.filter(h=>h.state==="failed").length;if(e.print.result({status:"processed",sent:r.length,duplicates:s,insights:m,failed:d,records:c},h=>`Processed ${h.sent} record${h.sent===1?"":"s"}: ${h.insights} insight${h.insights===1?"":"s"}${h.failed?`, ${h.failed} failed`:""}.${l} See them: closedloop insights search --source cli`),d>0)throw new p("ingest_failed",`${d} record${d===1?"":"s"} failed during processing`,g.Failure,"Check the failed records with `closedloop ingest list` or `closedloop ingest status <id>`.")}async function wd(e,t){let n=t===void 0?20:Number(t);if(!Number.isInteger(n)||n<1||n>100)throw q("invalid_option","--limit must be a whole number from 1 to 100");let o=v(e.runtime),i=await Js(e,o,n),r=e.print.style;e.print.result({records:i},s=>s.records.length===0?"Nothing sent with closedloop ingest yet.":s.records.map(a=>`${r.dim(N(a.created_at))} ${Vs(a,r)} ${a.title??a.external_id??"(untitled)"}
111
+ ${R(r," ",a.id)}`).join(`
112
+ `))}async function _d(e,t){let n=I(t,"record","closedloop ingest list"),o=v(e.runtime),i=await $(e.runtime,o,"GET",`/api/cli/ingest/${n}`);if(i.status===404)throw new p("record_not_found","there is no record with that id in your workspace",g.Failure);if(i.status!==200||typeof i.body.state!="string")throw C(i,"api_error","The record could not be loaded. Try again.");let r=i.body;e.print.result(r,s=>`${s.title??s.external_id??"The record"}: ${Vs(s)} (sent ${N(s.created_at)}).`)}var bd=(e,t)=>[...t??[],e],Bs=(e,t)=>{let n=e.command("ingest").argument("[text...]","The text to send as one record.").description("Send your own data in: text, a file (CSV, JSON, JSON Lines, text) or standard input.").option("--file <path>","A file: CSV or JSON (Lines) with a content field per record, or a text file as one record.").option("--stdin","Read one record's text from standard input.").option("--title <title>","A title for the record.").option("--from <name>","Who said it.").option("--email <address>","Their email; the insight is tied to their company when it matches a contact in your CRM.").option("--customer <name>","Their company, kept with the record (the insight's company comes from --email).").option("--date <date>","When it was said, e.g. 2026-10-22.").option("--url <link>","Where it came from.").option("--id <id>","Your own id for it; a record with an id already sent is skipped.").option("--tag <word>","A tag (repeatable).",bd).option("--wait","Wait until processed, and say how many insights it made.").action(async(o,i)=>yd(t,o,i));n.command("list").description("The latest records sent with ingest, and their state.").option("--limit <n>","How many, up to 100 (default 20).").action(async o=>wd(t,o.limit)),n.command("status").argument("<id>","The record id, from list or from sending.").description("One record's state, and the insights it made.").action(async o=>_d(t,o))};var ci="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";function Vt(e,t,n){return o=>n&&o?`\x1B[${e}m${o}\x1B[${t}m`:o}function di(e){let t=e.color===!0;return{enabled:t,width:Math.max(60,Math.min(100,e.columns??100)),bold:Vt(1,22,t),dim:Vt(2,22,t),red:Vt(31,39,t),orange:Vt(33,39,t),green:Vt(32,39,t),bar:(n,o)=>{let i=Number.isFinite(n)?Math.min(1,Math.max(0,n)):0,r=Math.round(i*o);return`${"\u2588".repeat(r)}${"\u2591".repeat(o-r)}`},sparkline:n=>{let o=Math.max(0,...n);return n.map(i=>ci[o>0?Math.min(ci.length-1,Math.round(i/o*(ci.length-1))):0]).join("")}}}function Mn(e,t){return t<=1?e.slice(0,Math.max(0,t)):e.length<=t?e:`${e.slice(0,t-1).trimEnd()}\u2026`}function Gn(e,t,n){let o=e.split(/\s+/).filter(Boolean),i=[],r="";for(let s of o){let a=t-(i.length===0?0:n.length);r&&r.length+1+s.length>a?(i.push(r),r=s):r=r?`${r} ${s}`:s}return r&&i.push(r),i.map((s,a)=>a===0?s:`${n}${s}`).join(`
113
+ `)}var $d=["title","quote","reporter","account"],vd=["critical","high","medium","low"],Ys={week:"1week",month:"1month","3months":"3months"},mi={newest:["source_date","desc"],oldest:["source_date","asc"],severity:["severity","asc"],relevance:["relevance_score","desc"]},Ks=240;function qn(e,t,n){if(t===void 0)return;let o=t.trim().toLowerCase();if(!n.includes(o))throw new p("invalid_option",`--${e} must be ${n.join(", ")}`,g.Usage);return o}function zs(e,t,n,o){if(t===void 0)return n;let i=Number(t);if(!Number.isInteger(i)||i<1||i>o)throw new p("invalid_option",`--${e} must be a whole number from 1 to ${o}`,g.Usage);return i}async function Cd(e,t,n){let o=await $(e.runtime,t,"GET","/api/cli/integrations");if(o.status!==200||!Array.isArray(o.body.integrations))throw C(o,"api_error","Sources could not be loaded. Try again.");let r=o.body.integrations.find(s=>s.slug===n.toLowerCase()||s.name.toLowerCase()===n.toLowerCase());if(!r)throw new p("unknown_source",`there is no source called "${n}"`,g.Usage,"See the names with `closedloop integrations list`.");return r.name}function kd(e){if(!e)return null;let t=e.replace(/\s+/g," ").trim();return t.length>Ks?`${t.slice(0,Ks-1)}\u2026`:t}function Zs(e){let t=e.reporter_name||e.customer_name;return[e.account_name,t&&t!==e.account_name?t:null].filter(n=>!!n)}function Sd(e){return e&&`${e[0].toUpperCase()}${e.slice(1)}`}var Od=e=>e.map(t=>t.replace(/_/g," ")).join(" or ");async function Ad(e,t,n){if(n.all&&n.goal!==void 0)throw new p("invalid_option","give --goal or --all, not both",g.Usage);if(n.all)return null;if(n.goal!==void 0)return{id:await ce(e,n.goal),title:"",picked:!1};let o=await kt(e,t);return o?{...o,picked:!0}:null}async function Td(e,t,n){let o=qn("in",n.in,$d),i=qn("severity",n.severity,vd),r=qn("last",n.last,Object.keys(Ys)),s=qn("sort",n.sort,Object.keys(mi))??"newest",a=zs("limit",n.limit,20,100),l=zs("page",n.page,1,1e4);if(o&&!t)throw new p("invalid_option","--in needs search words",g.Usage);let u=v(e.runtime),c=new URLSearchParams({page:String(l),limit:String(a),sortBy:mi[s][0],sortOrder:mi[s][1],filter_options:"false"}),m=t?.trim();m&&(o?c.set("searchIn",o):c.set("searchFragments","true"),c.set("search",m)),n.source&&c.set("source",await Cd(e,u,n.source)),n.category&&c.set("category",n.category.trim().toLowerCase()),i&&c.set("severity",i),r&&c.set("timeRange",Ys[r]),n.customer&&c.set("customerEmail",n.customer.trim()),n.account&&c.set("companyDomain",n.account.trim().toLowerCase());let d=await Ad(e,u,n);d&&c.set("goalId",d.id);let h=await $(e.runtime,u,"GET",`/api/feedback?${c}`);if(d&&h.status===404)throw new p("goal_not_found",d.picked?`the goal you work on (${d.title}) is gone or you can no longer see it`:"there is no goal with that id you can see",g.Failure,"Pick another: closedloop goals use <goal>, or search every insight with --all");if(d&&h.status===409&&typeof h.body.error=="string")throw new p(typeof h.body.code=="string"?h.body.code:"goal_unsearchable",h.body.error,g.Failure,"Search every insight with --all");if(h.status!==200||!Array.isArray(h.body.data))throw C(h,"api_error","Insights could not be searched. Try again.");let f=h.body.goal,y=d&&f&&typeof f.id=="string"&&typeof f.title=="string"&&f.relevance&&Array.isArray(f.relevance.outcomes)?f:null;if(d&&(!y||y.id.toLowerCase()!==d.id.toLowerCase()))throw new p("goal_search_unsupported","this ClosedLoop AI API cannot search within a goal yet",g.Failure,"Search every insight with --all");let _=h.body.data,S={query:m||null,goal:y,total:Number(h.body.total)||0,page:l,pages:Number(h.body.totalPages)||0,insights:_.map(b=>({id:b.id,title:b.signal_title,quote:b.content,category:b.category,severity:b.severity,account:b.account_name,customer:b.customer_name,reporter:b.reporter_name,source:b.source_name,date:b.source_date??b.created_at,feature_area:b.feature_area,deal_blocker:b.is_deal_blocker===!0}))};e.print.result(S,b=>{let U=e.print.style,ne=b.goal?` in ${b.goal.title}`:"",J=b.goal?U.dim(`Only ${b.goal.title}'s insights, bearing on ${Od(b.goal.relevance.outcomes)}; every insight: --all`):null;if(b.insights.length===0)return[b.query?`No insights${ne} match "${b.query}".`:`No insights${ne} match.`,...J?[J]:[]].join(`
114
+ `);let At=`${U.bold(`${b.total} insight${b.total===1?"":"s"}`)}${b.query?` for "${b.query}"`:""}${ne}${U.dim(` \xB7 ${Rd[s]}${b.pages>1?` \xB7 page ${b.page} of ${b.pages}`:""}`)}`,et=b.insights.map(O=>Ed(U,O)),oo=[b.page<b.pages?`more: --page ${b.page+1}`:null,"read one: closedloop insights show <id>"].filter(Boolean).join(" \xB7 ");return`${At}${J?`
115
+ ${J}`:""}
133
116
 
134
- ${ao.join(`
117
+ ${et.join(`
135
118
 
136
119
  `)}
137
120
 
138
- ${F.dim(Jd(A))}`})}var ma="last-insights.json",Kd={newest:"newest first",oldest:"oldest first",severity:"most severe first",relevance:"most relevant first"},ua={critical:"CRIT",high:"HIGH",medium:"MED",low:"LOW"};function pa(e,t){let n=(t??"").toLowerCase(),o=(Object.prototype.hasOwnProperty.call(ua,n)?ua[n]:"").padEnd(4);return n==="critical"||n==="high"?e.red(o):n==="medium"?e.orange(o):e.dim(o)}function zd(e,t,n,o){let i=" ".repeat(n.length+8),r=e.width-i.length,[s,...a]=Fn(t.title??"(untitled)",r,"").split(`
139
- `),l=[`${n} ${pa(e,t.severity)} ${e.bold(s)}`,...a.map(m=>`${i}${e.bold(m)}`)],u=[...da({account_name:t.account,customer_name:t.customer,reporter_name:t.reporter}).slice(0,1),t.source,t.date?P(t.date):null].filter(Boolean).join(" \xB7 "),c=t.deal_blocker?e.red(`${u?" \xB7 ":""}deal blocker`):"";(u||c)&&l.push(`${i}${e.dim(u)}${c}`);let d=qd(t.quote);return d&&l.push(`${i}${e.dim(`"${jn(d,r-2)}"`)}`),o&&l.push(`${i}${e.dim(`id ${t.id}`)}`),l.join(`
140
- `)}function Xd(e){let t=e&&typeof e=="object"?e.company_name:null;return typeof t=="string"&&t?t:null}var ca=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;async function Qd(e,t){let n=t.trim();if(ca.test(n))return n;if(/^\d{1,3}$/.test(n)){let o=await Le(e,ma,Number(n)),i=Array.isArray(o?.ids)?o.ids:[],r=Number(n);if(r>=1&&r<=i.length){if(typeof i[r-1]=="string"&&ca.test(i[r-1]))return i[r-1];throw new p("unknown_number","the last search could not be read",g.Usage,"Search again: closedloop insights search <words>")}throw new p("unknown_number",i.length?`the last search listed ${i.length} insights, not ${r}`:"there is no recent search to pick a number from",g.Usage,"Search first: closedloop insights search <words>")}throw new p("invalid_id",`'${t}' is not an insight id or a number from the last search`,g.Usage,"Use a number from `closedloop insights search`, or an id.")}async function Zd(e,t,n){let o=await Qd(e,t),i=$(e.runtime),r=await v(e.runtime,i,"GET",`/api/feedback/${o.trim().toLowerCase()}`);if(r.status===404)throw new p("insight_not_found","there is no insight with that id in your workspace",g.Failure);let s=r.body.data;if(r.status!==200||!s||typeof s.id!="string")throw C(r,"api_error","The insight could not be loaded. Try again.");let a=c=>typeof s[c]=="string"&&s[c]?s[c]:null,l=a("original_content"),u={id:s.id,title:a("signal_title"),summary:a("summary"),quote:a("content"),original_quote:l&&l!==a("content")?l:null,category:a("category"),severity:a("severity"),deal_blocker:s.is_deal_blocker===!0,account:a("account_name")??Xd(s.crm_account),customer:a("customer_name"),reporter:a("reporter_name"),reporter_role:a("reporter_role"),source:a("source_name"),source_url:a("source_url"),date:a("source_date")??a("created_at"),feature_area:a("feature_area_name")??a("feature_area"),product_area:a("product_area_name"),pain_point:a("pain_point"),workaround:a("workaround"),use_case:a("use_case"),competitor_gap:a("competitor_gap"),tags:Array.isArray(s.tags)?s.tags:[],...n?{transcript:s.transcript??null}:{}};e.print.result(u,c=>{let d=e.print.style,m=[`${c.severity?`${pa(d,c.severity).trimEnd()} `:""}${d.bold(c.title??"(untitled)")}${c.deal_blocker?d.red(" \xB7 deal blocker"):""}`],h=[...da({account_name:c.account,customer_name:c.customer,reporter_name:c.reporter}),c.reporter_role,c.source,c.date?P(c.date):null,c.category].filter(Boolean).join(" \xB7 ");h&&m.push(d.dim(h)),c.summary&&m.push("",c.summary),c.quote&&m.push("",`"${c.quote.trim()}"`),c.original_quote&&m.push(`Original: "${c.original_quote.trim()}"`);let y=[["Feature area",c.feature_area],["Product area",c.product_area],["Pain point",c.pain_point],["Workaround",c.workaround],["Use case",c.use_case],["Competitor gap",c.competitor_gap],["Source link",c.source_url]].filter(([,_])=>_);return y.length&&m.push("",...y.map(([_,S])=>`${d.dim(`${_}:`)} ${S}`)),n&&typeof c.transcript=="string"&&m.push("","Transcript:",c.transcript),m.join(`
141
- `)})}var ga=(e,t)=>{let n=e.command("insights").alias("insight").description("Search your product insights and read one in full.");n.command("search").argument("[words...]","What to look for; leave out to see the newest.").description("Search insights, as the Insights page does: only the goal you work on (closedloop goals use) when you picked one, else every insight.").option("--in <field>","Search only one field: title, quote, reporter or account.").option("--source <name>","Only from one source, e.g. gong.").option("--category <category>",'Only one category, e.g. "feature request" or bug.').option("--severity <level>","Only one severity: critical, high, medium or low.").option("--last <period>","Only the last week, month or 3months.").option("--customer <email>","Only from one customer email.").option("--account <domain>","Only from one account domain, e.g. acme.com.").option("--sort <order>","newest (default), oldest, severity or relevance.").option("--limit <n>","How many per page, up to 100 (default 20).").option("--page <n>","Which page.").option("--ids","Show each insight's id too.").option("--goal <which>","Only one goal's insights (a number from the last goals list, or a goal id), not the goal you work on.").option("--all","Every insight, not only the goal you work on.").action(async(o,i)=>Yd(t,o.join(" ")||void 0,i)),n.command("show").argument("<which>","A number from the last search, or an insight id.").description("Show one insight in full.").option("--transcript","Include the conversation transcript.").action(async(o,i)=>Zd(t,o,i.transcript===!0))};var ba={1:30,3:90,6:180},em="3";function tm(e){if(e.history===!1)return!1;let t=e.history??em;if(!Object.prototype.hasOwnProperty.call(ba,t))throw new p("invalid_history",`--history takes 1, 3 or 6 months, not '${t}'`,g.Usage,"Or leave the history out with --no-history.");return t}async function nm(e,t,n,o,i){if(n.imports_history!==!0||i===!1||typeof o?.id!="string")return null;let r=ba[i],s=o.config&&typeof o.config=="object"?o.config:{};if((await v(e.runtime,t,"PUT",`/api/user-integrations/${encodeURIComponent(o.id)}`,{config:{...s,backfillPeriod:i,backfill_days:r}})).status!==200)return{months:i,started:!1};let l=await v(e.runtime,t,"POST","/api/trigger-processing",{team_id:await Ge(e.runtime,t),user_integration_id:o.id,integration_id:n.integration_id,backfill_days:r,reason:"manual_trigger"});return{months:i,started:l.status>=200&&l.status<300}}function $a(e,t){if(e.connected){if(!e.connection_id)throw new p("manage_in_app",`${e.name} is set up in its own settings, so it cannot be ${t} here`,g.Failure,`Change it in the app: Integrations, then ${e.name}. ${x}`);if((e.connections??1)>1)throw new p("manage_in_app",`${e.name} has ${e.connections} connections, so it cannot be ${t} as one here`,g.Failure,`Choose the connection in the app: Integrations, then ${e.name}. ${x}`)}}var om=600,im=900,ha=3,rm=3,fa={api_key:"connect with an API key",browser:"connect in the browser",app:"set up in the app",ingest:"send data with closedloop ingest"},sm={browser:"is connected in the browser",app:"is set up in the app",ingest:"receives what you send with closedloop ingest"};async function va(e,t){let n=await v(e.runtime,t,"GET","/api/cli/integrations");if(n.status!==200||!Array.isArray(n.body.integrations))throw C(n,"api_error","Integrations could not be loaded. Try again.");return n.body.integrations}async function zt(e,t,n){let o=n.trim().toLowerCase();if(!o)throw new p("missing_integration","name the integration",g.Usage,"See the names with `closedloop integrations list --available`.");let i=s=>v(e.runtime,t,"GET",`/api/cli/integrations/${encodeURIComponent(s)}`),r=await i(o);if(r.status===404){let s=(await va(e,t)).find(a=>a.name.toLowerCase()===o);s&&(r=await i(s.slug))}if(r.status===404)throw new p("integration_not_found",`there is no integration called "${n.trim()}"`,g.Failure,"See the names with `closedloop integrations list --available`.");if(r.status!==200||typeof r.body.slug!="string")throw C(r,"api_error","The integration could not be loaded. Try again.");return r.body}function ya(e){return e.needs_attention?`needs attention: ${e.problem}`:e.connected?e.status==="paused"?"paused":e.status==="connecting"?"connecting":e.last_sync_at?`connected, last sync ${R(e.last_sync_at)}`:"connected, not synced yet":"not connected"}function am(e){return e.available_on_your_plan?fa[e.connect_method]:`${fa[e.connect_method]}, needs the ${e.plan_label} plan`}function Xn(e){return e.connected&&e.needs_attention?"needs_attention":e.connected?"connected":"available"}var Qn=["needs_attention","connected","available"],lm={needs_attention:"Needs attention",connected:"Connected",available:"Available to connect"};function um(e,t){return t==="needs_attention"?`${e.name} (${e.slug}): ${e.problem}`:t==="available"?`${e.name} (${e.slug}): ${am(e)}`:e.status==="paused"||e.status==="connecting"?`${e.name} (${e.slug}): ${e.status}`:`${e.name} (${e.slug}): ${e.last_sync_at?`last sync ${R(e.last_sync_at)}`:"not synced yet"}`}function cm(e,t,n){let o=e.print.style;return o.enabled?n==="needs_attention"?`${o.red("!")} `:n==="available"?`${o.dim("+")} `:t.status==="paused"||t.status==="connecting"?`${o.orange("\u2016")} `:`${o.green("\u2713")} `:""}async function dm(e,t){if(t.connected&&t.available)throw new p("conflicting_options","use --connected or --available, not both",g.Usage,"Without either, the list shows everything.");let n=$(e.runtime),o=await W(e.runtime,n),i=o.permissions.can_manage_integrations,r=new Set(t.connected?["needs_attention","connected"]:t.available?["available"]:Qn),s=(await va(e,n)).filter(l=>r.has(Xn(l))).sort((l,u)=>Qn.indexOf(Xn(l))-Qn.indexOf(Xn(u))),a=`You cannot connect or change integrations in ${o.name}: ask an owner or admin for the right to manage integrations.`;e.print.result({can_manage_integrations:i,integrations:s},l=>{if(l.integrations.length===0)return t.available?"Every integration is already connected.":"No integrations are connected yet. See what you can connect: closedloop integrations list";let u=Qn.map(c=>({group:c,items:l.integrations.filter(d=>Xn(d)===c)})).filter(({items:c})=>c.length>0).map(({group:c,items:d})=>[e.print.style.bold(lm[c]),...d.map(m=>{let h=` ${cm(e,m,c)}`;return`${h}${Fn(um(m,c),e.print.style.width-h.length," ".repeat(h.length+2))}`})].join(`
121
+ ${U.dim(Sd(oo))}`})}var Xs="closedloop insights search",Rd={newest:"newest first",oldest:"oldest first",severity:"most severe first",relevance:"most relevant first"},Qs={critical:"CRIT",high:"HIGH",medium:"MED",low:"LOW"};function ea(e,t){let n=(t??"").toLowerCase(),o=(Object.prototype.hasOwnProperty.call(Qs,n)?Qs[n]:"").padEnd(4);return n==="critical"||n==="high"?e.red(o):n==="medium"?e.orange(o):e.dim(o)}function Ed(e,t){let n=" ".repeat(6),o=e.width-n.length,[i,...r]=Gn(t.title??"(untitled)",o,"").split(`
122
+ `),s=[`${ea(e,t.severity)} ${e.bold(i)}`,...r.map(c=>`${n}${e.bold(c)}`)],a=[...Zs({account_name:t.account,customer_name:t.customer,reporter_name:t.reporter}).slice(0,1),t.source,t.date?N(t.date):null].filter(Boolean).join(" \xB7 "),l=t.deal_blocker?e.red(`${a?" \xB7 ":""}deal blocker`):"";(a||l)&&s.push(`${n}${e.dim(a)}${l}`);let u=kd(t.quote);return u&&s.push(`${n}${e.dim(`"${Mn(u,o-2)}"`)}`),s.push(R(e,n,t.id)),s.join(`
123
+ `)}function Id(e){let t=e&&typeof e=="object"?e.company_name:null;return typeof t=="string"&&t?t:null}async function xd(e,t,n){let o=I(t,"insight",Xs),i=v(e.runtime),r=await $(e.runtime,i,"GET",`/api/feedback/${o}`);if(r.status===404)throw new p("insight_not_found","there is no insight with that id in your workspace",g.Failure,L(Xs));let s=r.body.data;if(r.status!==200||!s||typeof s.id!="string")throw C(r,"api_error","The insight could not be loaded. Try again.");let a=c=>typeof s[c]=="string"&&s[c]?s[c]:null,l=a("original_content"),u={id:s.id,title:a("signal_title"),summary:a("summary"),quote:a("content"),original_quote:l&&l!==a("content")?l:null,category:a("category"),severity:a("severity"),deal_blocker:s.is_deal_blocker===!0,account:a("account_name")??Id(s.crm_account),customer:a("customer_name"),reporter:a("reporter_name"),reporter_role:a("reporter_role"),source:a("source_name"),source_url:a("source_url"),date:a("source_date")??a("created_at"),feature_area:a("feature_area_name")??a("feature_area"),product_area:a("product_area_name"),pain_point:a("pain_point"),workaround:a("workaround"),use_case:a("use_case"),competitor_gap:a("competitor_gap"),tags:Array.isArray(s.tags)?s.tags:[],...n?{transcript:s.transcript??null}:{}};e.print.result(u,c=>{let m=e.print.style,d=[`${c.severity?`${ea(m,c.severity).trimEnd()} `:""}${m.bold(c.title??"(untitled)")}${c.deal_blocker?m.red(" \xB7 deal blocker"):""}`],h=[...Zs({account_name:c.account,customer_name:c.customer,reporter_name:c.reporter}),c.reporter_role,c.source,c.date?N(c.date):null,c.category].filter(Boolean).join(" \xB7 ");h&&d.push(m.dim(h)),c.summary&&d.push("",c.summary),c.quote&&d.push("",`"${c.quote.trim()}"`),c.original_quote&&d.push(`Original: "${c.original_quote.trim()}"`);let y=[["Feature area",c.feature_area],["Product area",c.product_area],["Pain point",c.pain_point],["Workaround",c.workaround],["Use case",c.use_case],["Competitor gap",c.competitor_gap],["Source link",c.source_url]].filter(([,_])=>_);return y.length&&d.push("",...y.map(([_,S])=>`${m.dim(`${_}:`)} ${S}`)),n&&typeof c.transcript=="string"&&d.push("","Transcript:",c.transcript),d.join(`
124
+ `)})}var ta=(e,t)=>{let n=e.command("insights").alias("insight").description("Search your product insights and read one in full.");n.command("search").argument("[words...]","What to look for; leave out to see the newest.").description("Search insights, as the Insights page does: only the goal you work on (closedloop goals use) when you picked one, else every insight. Each insight shows its id.").option("--in <field>","Search only one field: title, quote, reporter or account.").option("--source <name>","Only from one source, e.g. gong.").option("--category <category>",'Only one category, e.g. "feature request" or bug.').option("--severity <level>","Only one severity: critical, high, medium or low.").option("--last <period>","Only the last week, month or 3months.").option("--customer <email>","Only from one customer email.").option("--account <domain>","Only from one account domain, e.g. acme.com.").option("--sort <order>","newest (default), oldest, severity or relevance.").option("--limit <n>","How many per page, up to 100 (default 20).").option("--page <n>","Which page.").addOption(new Ie("--ids").hideHelp()).option("--goal <goal>","Only one goal's insights (the goal's id, from closedloop goals list), not the goal you work on.").option("--all","Every insight, not only the goal you work on.").action(async(o,i)=>Td(t,o.join(" ")||void 0,i)),n.command("show").argument("<id>","The insight's id, from closedloop insights search.").description("Show one insight in full.").option("--transcript","Include the conversation transcript.").action(async(o,i)=>xd(t,o,i.transcript===!0))};var la={1:30,3:90,6:180},Pd="3";function Nd(e){if(e.history===!1)return!1;let t=e.history??Pd;if(!Object.prototype.hasOwnProperty.call(la,t))throw new p("invalid_history",`--history takes 1, 3 or 6 months, not '${t}'`,g.Usage,"Or leave the history out with --no-history.");return t}async function Ld(e,t,n,o,i){if(n.imports_history!==!0||i===!1||typeof o?.id!="string")return null;let r=la[i],s=o.config&&typeof o.config=="object"?o.config:{};if((await $(e.runtime,t,"PUT",`/api/user-integrations/${encodeURIComponent(o.id)}`,{config:{...s,backfillPeriod:i,backfill_days:r}})).status!==200)return{months:i,started:!1};let l=await $(e.runtime,t,"POST","/api/trigger-processing",{team_id:await Je(e.runtime,t),user_integration_id:o.id,integration_id:n.integration_id,backfill_days:r,reason:"manual_trigger"}),u=l.status>=200&&l.status<300,c=u&&typeof l.body.backfill_operation_id=="string"?l.body.backfill_operation_id:null;return{months:i,started:u,...u?{backfill_id:c}:{}}}function ua(e,t){if(e.connected){if(!e.connection_id)throw new p("manage_in_app",`${e.name} is set up in its own settings, so it cannot be ${t} here`,g.Failure,`Change it in the app: Integrations, then ${e.name}. ${D}`);if((e.connections??1)>1)throw new p("manage_in_app",`${e.name} has ${e.connections} connections, so it cannot be ${t} as one here`,g.Failure,`Choose the connection in the app: Integrations, then ${e.name}. ${D}`)}}var na="closedloop integrations list",Dd=600,jd=900,oa=3,Fd=3,ia={api_key:"connect with an API key",browser:"connect in the browser",app:"set up in the app",ingest:"send data with closedloop ingest"},Wd={browser:"is connected in the browser",app:"is set up in the app",ingest:"receives what you send with closedloop ingest"};async function Ud(e,t){let n=await $(e.runtime,t,"GET","/api/cli/integrations");if(n.status!==200||!Array.isArray(n.body.integrations))throw C(n,"api_error","Integrations could not be loaded. Try again.");return n.body.integrations}async function Yt(e,t,n){let o=I(n,"integration",na),i=await $(e.runtime,t,"GET",`/api/cli/integrations/${encodeURIComponent(o)}`);if(i.status===404)throw new p("integration_not_found",`there is no integration with the id ${o}`,g.Failure,L(na));if(i.status!==200||typeof i.body.slug!="string")throw C(i,"api_error","The integration could not be loaded. Try again.");return i.body}function ra(e){return e.needs_attention?`needs attention: ${e.problem}`:e.connected?e.status==="paused"?"paused":e.status==="connecting"?"connecting":e.last_sync_at?`connected, last sync ${E(e.last_sync_at)}`:"connected, not synced yet":"not connected"}function Hd(e){return e.available_on_your_plan?ia[e.connect_method]:`${ia[e.connect_method]}, needs the ${e.plan_label} plan`}function Jn(e){return e.connected&&e.needs_attention?"needs_attention":e.connected?"connected":"available"}var Vn=["needs_attention","connected","available"],Md={needs_attention:"Needs attention",connected:"Connected",available:"Available to connect"};function Gd(e,t){return t==="needs_attention"?`${e.name}: ${e.problem}`:t==="available"?`${e.name}: ${Hd(e)}`:e.status==="paused"||e.status==="connecting"?`${e.name}: ${e.status}`:`${e.name}: ${e.last_sync_at?`last sync ${E(e.last_sync_at)}`:"not synced yet"}`}function qd(e,t,n){let o=e.print.style;return o.enabled?n==="needs_attention"?`${o.red("!")} `:n==="available"?`${o.dim("+")} `:t.status==="paused"||t.status==="connecting"?`${o.orange("\u2016")} `:`${o.green("\u2713")} `:""}async function Jd(e,t){if(t.connected&&t.available)throw new p("conflicting_options","use --connected or --available, not both",g.Usage,"Without either, the list shows everything.");let n=v(e.runtime),o=await M(e.runtime,n),i=o.permissions.can_manage_integrations,r=new Set(t.connected?["needs_attention","connected"]:t.available?["available"]:Vn),s=(await Ud(e,n)).filter(l=>r.has(Jn(l))).sort((l,u)=>Vn.indexOf(Jn(l))-Vn.indexOf(Jn(u))),a=`You cannot connect or change integrations in ${o.name}: ask an owner or admin for the right to manage integrations.`;e.print.result({can_manage_integrations:i,integrations:s},l=>{if(l.integrations.length===0)return t.available?"Every integration is already connected.":"No integrations are connected yet. See what you can connect: closedloop integrations list";let c=`${Vn.map(m=>({group:m,items:l.integrations.filter(d=>Jn(d)===m)})).filter(({items:m})=>m.length>0).map(({group:m,items:d})=>[e.print.style.bold(Md[m]),...d.map(h=>{let f=` ${qd(e,h,m)}`,y=" ".repeat(f.length+2),_=`${f}${Gn(Gd(h,m),e.print.style.width-f.length,y)}`;return typeof h.integration_id=="string"?`${_}
125
+ ${R(e.print.style,y,h.integration_id)}`:_})].join(`
142
126
  `)).join(`
143
- `);return i?u:`${u}
144
-
145
- ${a}`})}function wa(e){let t=e.choices?` (${e.choices.map(o=>o.value).join(" or ")})`:"",n=e.required===!0?"":e.required===!1?", optional":`, needed when ${e.required.when} is ${e.required.equals}`;return`${e.label}${t}${n}`}async function mm(e,t){let n=$(e.runtime),o=await zt(e,n,t);e.print.result(o,i=>{let r=[`${i.name} (${i.slug})`];if(i.description&&r.push(i.description),r.push(`Status: ${i.connected&&!i.needs_attention&&i.connected_at?`connected since ${R(i.connected_at)}; ${ya(i)}`:ya(i)}`),i.available_on_your_plan||r.push(`Plan: available on the ${i.plan_label} plan, not on yours.`),i.connect.method==="api_key")r.push(`Connect: closedloop integrations connect ${i.slug}`),r.push(` It asks for: ${i.connect.fields.map(wa).join("; ")}.`);else if(i.connect.method==="browser"){r.push(`Connect: closedloop integrations connect ${i.slug} (opens your browser)`);let s=wi(i)?.fields??[];s.length>0&&r.push(` It asks for: ${s.map(wa).join("; ")}.`)}else i.connect.method==="ingest"?r.push('Send data: closedloop ingest "some text", or closedloop ingest --file data.csv'):i.connect.app_url&&(r.push(`Set it up in the app: ${ve(i.connect.app_url,e.runtime.supportsHyperlinks)}`),r.push(` ${x}`));return r.join(`
146
- `)})}function eo(e,t){return typeof e.required=="boolean"?e.required:t[e.required.when]===e.required.equals}function to(e,t){let n={};for(let o of e.field??[]){let i=o.indexOf("="),r=i>0?o.slice(0,i).trim():"",s=t.find(a=>a.key===r);if(!s){let a=t.filter(l=>!l.secret).map(l=>l.key);throw new p("unknown_field","this integration does not accept that setting; use --field name=value",g.Usage,a.length?`Its fields: ${a.join(", ")}.`:"It takes only the API key, on standard input with --stdin.")}if(s.secret)throw new p("secret_in_argument",`${s.label} cannot be passed as an argument`,g.Usage,"Pipe it in instead, so it stays out of your shell history: --stdin.");n[r]=o.slice(i+1).trim()}return n}function Ca(e,t){return!e.choices||t===""||e.choices.some(n=>n.value===t)?null:`${e.label} must be ${e.choices.map(n=>n.value).join(" or ")}`}async function pm(e,t,n){let o=t.choices?` [${t.choices.map(a=>`${a.value} = ${a.label}`).join(", ")}]`:"",i=t.help?` (${t.help})`:"",r=t.default?` (Enter for ${t.default})`:eo(t,n)?"":" (optional, Enter to skip)",s="";for(let a=1;a<=rm;a++){let l=await e.runtime.prompt(`${s}${t.label}${i}${o}${r}: `)||t.default||"",u=Ca(t,l)??(l===""&&eo(t,n)?`${t.label} is needed`:null);if(!u)return l;s=`${u}. `}throw new p("invalid_field",`no valid ${t.label} was given`,g.Usage)}async function gm(e,t,n,o){let i={};if(t.stdin){let r=(await e.runtime.readStdin()).split(/\r?\n/).map(s=>s.trim()).filter(s=>s!=="");if(r.length!==n.length)throw new p("stdin_mismatch",`expected ${n.length} line${n.length===1?"":"s"} on standard input, got ${r.length}`,g.Usage,`Send ${n.map(s=>s.label).join(", then ")}, one per line.`);return n.forEach((s,a)=>{i[s.key]=r[a]}),i}if(!e.interactive)throw new p("secret_required",`${n.map(r=>r.label).join(" and ")} must be piped in`,g.Usage,`For example: printf '%s\\n' "$KEY" | closedloop integrations connect ${o} --stdin`);for(let r of n){let s=await e.runtime.promptSecret(`${r.label}${r.help?` (${r.help})`:""}: `);if(!s)throw new p("secret_required",`${r.label} is needed`,g.Usage);i[r.key]=s}return i}async function ka(e,t,n,o){let i=n.filter(s=>!s.secret);for(let s of i){o[s.key]===void 0&&s.default&&(o[s.key]=s.default);let a=o[s.key]!==void 0?Ca(s,o[s.key]):null;if(a)throw new p("invalid_field",a,g.Usage)}for(let s of i)o[s.key]!==void 0&&o[s.key]!==""||e.interactive&&(s.required===!1||eo(s,o))&&(o[s.key]=await pm(e,s,o));let r=i.filter(s=>eo(s,o)&&!o[s.key]);if(r.length>0)throw new p("missing_field",`${t} also needs ${r.map(s=>s.label).join(", ")}`,g.Usage,`Add ${r.map(s=>`--field ${s.key}=<${s.label}>`).join(" ")}.`)}async function hm(e,t,n,o){let i=tm(o),r=n.connect.fields,s=to(o,r);await ka(e,n.name,r,s),Object.assign(s,await gm(e,o,r.filter(y=>y.secret),n.slug));let a={};for(let y of r)y.target==="config"&&s[y.key]&&(a[y.key]=s[y.key]);let l=y=>r.find(_=>_.target===y&&s[_.key])?.key,u=l("api_key"),c=l("api_secret"),d=await Ye(e.runtime,t),m=await v(e.runtime,t,"POST","/api/user-integrations",{integration_id:n.integration_id,api_key:u?s[u]:void 0,...c?{api_secret:s[c]}:{},config:a});if(m.status!==200&&m.status!==201)throw C(m,"connect_refused",`${n.name} could not be connected.`);let h=m.body.integration&&typeof m.body.integration=="object"?m.body.integration:void 0,f=await nm(e,t,n,h,i);e.print.result({status:"connected",slug:n.slug,name:n.name,reconnected:n.connected,history:f,...Sa(d)},y=>{let _=`${y.reconnected?"Reconnected":"Connected"} ${y.name}.`;return y.history?.started?Zn(`${_} Importing the last ${y.history.months} month${y.history.months==="1"?"":"s"} of history; follow it with: closedloop backfills show ${y.slug}`,d):y.history?Zn(`${_} The history import did not start: start it in the app (Integrations, ${y.name}). ${x}`,d):Zn(`${_} Check on it with: closedloop integrations show ${y.slug}`,d)})}function Sa(e){return{waiting_for:e?.analysis_waiting?"company_profile":null}}function Zn(e,t){let n=Mt(t);return n?`${e}
147
- ${n}`:e}function fm(e,t){return!t.connected||t.needs_attention?!1:e.connected?t.last_connection_at!==e.last_connection_at||t.connection_id!==e.connection_id:!0}function wi(e){let t=e.connect.browser;return!t||typeof t.start!="string"||!t.start.startsWith("/")||!Array.isArray(t.fields)?null:t}var ym="/api/cli/oauth-handoff",wm="/api/cli/oauth-handoff/go",_m="/closedloop/oauth",Aa=/^[A-Za-z0-9_-]{43}$/;function Oa(e,t,n,o){if(typeof e!="string")return null;let i,r;try{i=new URL(e),r=new URL(t)}catch{return null}let s=i.searchParams.getAll(o);return i.origin!==r.origin||i.pathname!==n||s.length!==1||!Aa.test(s[0])?null:{url:i.href,value:s[0]}}async function Ta(e,t){try{return await e.openUrl(t)===!0}catch{return!1}}var bm={remote_browser:"Your browser is on another computer",no_local_listener:"The CLI could not wait for your browser on this computer",browser_not_opened:"The browser could not be opened"},$m=["SSH_CONNECTION","SSH_TTY","SSH_CLIENT","CODESPACES","REMOTE_CONTAINERS","GITPOD_WORKSPACE_ID","CLOUD_SHELL","container","KUBERNETES_SERVICE_HOST"],vm=["/.dockerenv","/run/.containerenv"];function Cm(e){return $m.some(t=>!!e.env[t])||e.env.AWS_EXECUTION_ENV==="CloudShell"?!0:vm.some(t=>e.fileExists?.(t)===!0)}async function Ra(e,t,n,o){let{print:i,runtime:r}=e,s=o.openOnEnter,a=s?r.onEnter(()=>{Ta(r,s)}):()=>{},l=i.spinner(`Waiting for ${n.name} to connect\u2026`);try{for(let u=0;u<o.seconds;u+=ha){await r.sleep(ha*1e3);let c=o.stopped();if(c)throw c;let d=await zt(e,t,n.slug);if(fm(n,d)){let m=await Ye(r,t);i.result({status:"connected",slug:d.slug,name:d.name,reconnected:n.connected,...Sa(m)},h=>Zn(`${h.reconnected?"Reconnected":"Connected"} ${h.name}.`,m));return}}}finally{l.stop(),a()}throw new p("connect_pending",`${n.name} is not connected yet`,g.Failure,`Finish in the browser, then run \`closedloop integrations connect ${n.slug}\` again.`)}async function Kt(e,t,n,o,i){let{print:r,runtime:s,interactive:a}=e,l=n.connect.url;if(!l)throw new p("no_connect_link",`${n.name} has no link to connect it here`,g.Failure,`Connect it from the Integrations page in the app. ${x}`);let u=i?" (the settings you gave were not used)":"",c=o?`${bm[o]}, so connect ${n.name} in the app${u}:`:null,d={status:"browser_needed",slug:n.slug,name:n.name,url:l,...o?{reason:o}:{},next:`Open the link and connect ${n.name}${u}; then run \`closedloop integrations connect ${n.slug}\` again to check.`,app_sign_in:x};if(!a){r.result(d,m=>c?`${c} ${m.url}
148
- ${m.app_sign_in}
149
- Then run: closedloop integrations connect ${m.slug}`:`Open ${m.url} and connect ${m.name}${u}. ${m.app_sign_in} Then run: closedloop integrations connect ${m.slug}`);return}r.result(d,m=>[c??`Connect ${m.name} in your browser${u}:`,` ${ve(m.url,s.supportsHyperlinks)}`,m.app_sign_in,"Press Enter to open it. Waiting for the connection\u2026"].join(`
150
- `)),await Ra(e,t,n,{seconds:om,openOnEnter:l,stopped:()=>null})}var yi="ClosedLoop AI could not finish connecting here. Go back to your terminal.",Ea={status:404,body:"Not found."};function km(e,t,n,o,i){if(i.method!=="GET"||i.path!==_m||o.token===null)return Promise.resolve(Ea);let r=i.query.getAll("n");if(r.length!==1||!Aa.test(r[0]))return Promise.resolve({status:400,body:"This link is not complete. Go back to your terminal."});let s=o.queue.then(()=>Sm(e,t,n,o,r[0]));return o.queue=s.catch(()=>{}),s}async function Sm(e,t,n,o,i){if(o.success)return o.success.n===i?o.success.reply:Ea;try{let r=await v(e.runtime,t,"POST","/api/cli/oauth-handoff/confirm",{t:o.token,n:i});if(r.status===409)return{status:409,body:"Press Continue on the page the link opened, in this browser."};if(r.status!==200)return o.failure=C(r,"connect_refused",`${n.name} could not be connected.`),{status:400,body:yi,last:!0};let s=Oa(r.body.go_url,t.api_url,wm,"g");if(!s)return o.failure=new p("api_error",`${n.name} could not be connected from here`,g.Failure,`Try again, or connect it from the Integrations page in the app. ${x}`),{status:502,body:yi,last:!0};let a={status:302,location:s.url,last:!0};return o.success={n:i,reply:a},a}catch(r){return o.failure=r instanceof p?r:new p("api_error",`${n.name} could not be connected from here`,g.Failure),{status:500,body:yi,last:!0}}}async function Am(e,t,n,o){let i=e.runtime.listenLoopback;if(!i)return null;try{return await i(r=>km(e,t,n,o,r))}catch{return null}}async function Om(e,t,n,o,i,r,s){let a=n.connected&&typeof n.connection_id=="string"&&(n.connections??1)<=1?{existing_connection_id:n.connection_id,is_reconnection:!0}:{},l=await v(e.runtime,t,"POST",o.start,{team_id:r,...i,...a},{"X-ClosedLoop-Browser-Handoff":"1","X-ClosedLoop-Loopback-Port":String(s)});if(l.status<200||l.status>=300)throw C(l,"connect_refused",`${n.name} could not be connected.`);let u=Oa(l.body.handoff_url,t.api_url,ym,"t");if(!u)throw new p("api_error",`${n.name} could not be started from here`,g.Failure,`Try again, or connect it from the Integrations page in the app. ${x}`);return u}async function Tm(e,t,n,o,i){let r=wi(n),s=(o.field??[]).length>0;if(!r)return Kt(e,t,n,null,!1);if(o.inApp)return Kt(e,t,n,null,s);let{print:a,runtime:l,interactive:u}=e;if(Cm(l))return Kt(e,t,n,"remote_browser",s);let c=to(o,r.fields);await ka(e,n.name,r.fields,c);let d=Object.fromEntries(r.fields.filter(y=>c[y.key]).map(y=>[y.key,c[y.key]])),m=Object.keys(d).length>0,h={token:null,failure:null,success:null,queue:Promise.resolve()},f=await Am(e,t,n,h);if(!f)return Kt(e,t,n,"no_local_listener",m);try{let y=await Om(e,t,n,r,d,i,f.port);if(h.token=y.value,u)a.result({status:"browser_needed",slug:n.slug,name:n.name,url:y.url},_=>[`Connect ${_.name} in your browser:`,` ${ve(_.url,l.supportsHyperlinks)}`,"Press Enter to open it. Waiting for the connection\u2026"].join(`
151
- `));else if(!await Ta(l,y.url))return f.close(),Kt(e,t,n,"browser_not_opened",m);await Ra(e,t,n,{seconds:im,openOnEnter:u?y.url:null,stopped:()=>h.failure})}finally{f.close()}}async function Rm(e,t,n){let o=$(e.runtime),i=await zt(e,o,t);if(n.inApp&&i.connect.method!=="browser")throw new p("not_a_browser_integration",`${i.name} does not connect in the browser, so --in-app does not apply`,g.Usage,`Run it without --in-app: closedloop integrations connect ${i.slug}`);let r=i.connect.method==="browser"?wi(i)?.fields??[]:[];if(i.connect.method==="api_key")to(n,i.connect.fields);else if(r.length>0&&!n.stdin)to(n,r);else if((n.field??[]).length>0||n.stdin)throw new p("not_an_api_key_integration",`${i.name} ${sm[i.connect.method]}, not with a key`,g.Usage,`Run it without --field and --stdin: closedloop integrations connect ${i.slug}`);if(i.connected&&!i.needs_attention&&!n.reconnect){e.print.result({status:"already_connected",slug:i.slug,name:i.name},l=>`${l.name} is already connected. To connect it again (for example with a new key), add --reconnect.`);return}let s=i.connect.method!=="ingest"?await pt(e.runtime,o,"can_manage_integrations",`connect ${i.name}`):null;if(!i.available_on_your_plan)throw new p("plan_required",`${i.name} is available on the ${i.plan_label} plan`,g.Failure,"Change your plan with: closedloop plan upgrade");if(i.connect.method==="api_key")return hm(e,o,i,n);if(i.connect.method==="browser"&&s)return Tm(e,o,i,n,s.id);if(i.connect.method==="ingest"){e.print.result({status:"use_ingest",slug:i.slug,name:i.name},()=>"Send your own data with closedloop ingest: some text, or --file with a CSV, JSON or text file. The CLI source is created on the first send.");return}let a=i.connect.app_url;e.print.result({status:"set_up_in_app",slug:i.slug,name:i.name,url:a,app_sign_in:x},l=>`${l.name} is set up in the app${l.url?`: ${ve(l.url,e.runtime.supportsHyperlinks)}`:"."}
152
- ${l.app_sign_in}`)}async function Em(e,t,n){let o=$(e.runtime),i=await zt(e,o,t);if($a(i,"disconnected"),!i.connected||!i.connection_id){e.print.result({status:"not_connected",slug:i.slug,name:i.name},s=>`${s.name} is not connected.`);return}if(await pt(e.runtime,o,"can_manage_integrations",`disconnect ${i.name}`),!n){if(!e.interactive)throw new p("confirmation_required",`disconnecting ${i.name} needs confirmation`,g.Usage,`Run it again with --yes: closedloop integrations disconnect ${i.slug} --yes`);if((await e.runtime.prompt(`Disconnect ${i.name}? Syncing stops; what was already imported stays. Type yes to confirm: `)).toLowerCase()!=="yes"){e.print.result({status:"cancelled",slug:i.slug,name:i.name},()=>"Nothing changed.");return}}let r=await v(e.runtime,o,"DELETE",`/api/user-integrations/${encodeURIComponent(i.connection_id)}`);if(r.status!==200&&r.status!==204)throw C(r,"disconnect_refused",`${i.name} could not be disconnected.`);e.print.result({status:"disconnected",slug:i.slug,name:i.name},s=>`Disconnected ${s.name}. What was already imported stays.`)}async function _a(e,t,n){let o=$(e.runtime),i=await zt(e,o,t),r={slug:i.slug,name:i.name};if($a(i,n?"paused":"resumed"),!i.connected||!i.connection_id){e.print.result({status:"not_connected",...r},a=>`${a.name} is not connected.`);return}if(n===(i.status==="paused")){e.print.result({status:n?"already_paused":"not_paused",...r},a=>n?`${a.name} is already paused.`:`${a.name} is not paused.`);return}await pt(e.runtime,o,"can_manage_integrations",`${n?"pause":"resume"} ${i.name}`);let s=await v(e.runtime,o,"PUT",`/api/user-integrations/${encodeURIComponent(i.connection_id)}`,{status:n?"inactive":"active"});if(s.status!==200)throw C(s,n?"pause_refused":"resume_refused",`${i.name} could not be ${n?"paused":"resumed"}.`);e.print.result({status:n?"paused":"resumed",...r},a=>n?`Paused ${a.name}. Nothing new syncs until you resume it: closedloop integrations resume ${a.slug}`:`Resumed ${a.name}. It syncs again on its usual schedule.`)}var Im=(e,t)=>[...t??[],e],Ia=(e,t)=>{let n=e.command("integrations").alias("integration").description("See, connect and disconnect your workspace's integrations.");n.command("list").description("List integrations: those needing attention, those connected, and those you can connect.").option("--connected","Only the connected ones.").option("--available","Only the ones you can connect.").action(async o=>dm(t,{connected:o.connected===!0,available:o.available===!0})),n.command("show").argument("<name>","The integration, e.g. hubspot.").description("Show one integration: its status and how to connect it.").action(async o=>mm(t,o)),n.command("connect").argument("<name>","The integration, e.g. hubspot.").description("Connect an integration: with an API key, in the browser, or with a link to the app.").option("--field <key=value>","A setting the integration needs, e.g. --field subdomain=acme (repeatable).",Im).option("--stdin","Read the API key (and secret, on a second line) from standard input.").option("--reconnect","Connect again even if it is already connected, e.g. with a new key.").option("--history <months>","With an API key: months of history to import, 1, 3 or 6 (3 if left out).").option("--no-history","With an API key: import no older history, only what comes from now on.").option("--in-app","In the browser: connect on the app's connect page instead (for example when the browser cannot reach this computer).").action(async(o,i)=>Rm(t,o,i)),n.command("pause").argument("<name>","The integration, e.g. hubspot.").description("Stop syncing an integration without disconnecting it.").action(async o=>_a(t,o,!0)),n.command("resume").argument("<name>","The integration, e.g. hubspot.").description("Start syncing a paused integration again.").action(async o=>_a(t,o,!1)),n.command("disconnect").argument("<name>","The integration, e.g. hubspot.").description("Disconnect an integration. Syncing stops; imported data stays.").option("--yes","Skip the confirmation.").action(async(o,i)=>Em(t,o,i.yes===!0))};import{randomUUID as Pm}from"node:crypto";var j=$e,me=ie,xm=60,no="Choose --region us|eu: --region us stores new account data in the United States; --region eu stores it in the European Union.",Nm="Choose your data region: United States (US) or European Union (EU) [US]: ",Xt="Run `closedloop login --email <email>` to get a new code.",Lm="Run `closedloop login --browser` to sign in through your browser.",Dm="Run `closedloop login --browser` to sign in with your password in your browser.",jm="Your company website, e.g. https://acme.example: ",Fm=3,Um=`Next, set your company website: ${te}. ${Wt} Nothing is analysed until it is done.`,Wm=["sso_required","password_sign_in"],Pa={INVALID_EMAIL:{code:"invalid_email"},DISPOSABLE_EMAIL:{code:"disposable_email"},EMAIL_SEND_FAILED:{code:"email_send_failed",hint:"Run the same command again in a minute."},RATE_LIMIT_EXCEEDED:{code:"rate_limited",hint:"Wait a while, then try again.",keepPending:!0},INVALID_CODE:{code:"invalid_code",hint:"Use the code from the newest ClosedLoop AI email and run the command again.",keepPending:!0},CODE_EXPIRED:{code:"code_expired",hint:Xt},CODE_ALREADY_USED:{code:"code_already_used",hint:Xt},INVALID_CHALLENGE:{code:"code_request_invalid",hint:Xt},TWO_FACTOR_REQUIRED:{code:"two_factor_required",hint:"Run it again with --totp <code from your authenticator app> or --backup-code <code>.",keepPending:!0},INVALID_2FA_CODE:{code:"invalid_two_factor_code",hint:`${Xt} Then use the current code from your authenticator app.`},TWO_FACTOR_SETUP_REQUIRED:{code:"two_factor_setup_required"},SSO_REQUIRED:{code:"sso_required",hint:Lm},PASSWORD_SIGN_IN:{code:"password_sign_in",hint:Dm},ACCOUNT_LOCKED:{code:"account_locked"},SIGNUP_LIMIT:{code:"signup_limit"},ACCOUNT_IN_OTHER_REGION:{code:"account_in_other_region",hint:"Set CLOSEDLOOP_API_URL to the API of your account's region and run `closedloop login` again."}},xa={"api.closedloop.sh":"eu.api.closedloop.sh","eu.api.closedloop.sh":"api.closedloop.sh"};function io(e,t){let n=typeof e.body.code=="string"?e.body.code:"",o=Object.prototype.hasOwnProperty.call(Pa,n)?Pa[n]:{code:"api_error"},i=typeof e.body.error=="string"&&e.body.error?e.body.error:`ClosedLoop AI could not complete the request (HTTP ${e.status})`;return Object.assign(new p(o.code,i,g.Failure,t??o.hint),{keepPending:o.keepPending===!0})}function bi(){return new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure)}function $i(e,t,n){return dn(e.runtime,t,o=>{let i=o.read(n),r={request_id:Pm()};return o.write(n,r),{intent:r,previous:i}})}function pe(e,t,n,o,i,r=!0){dn(e.runtime,t,s=>{if(JSON.stringify(s.read(n))!==JSON.stringify(o)){if(r)throw ct();return}i===null?s.remove(n):s.write(n,i)})}function Oe(e,t,n,o){dn(e.runtime,t,i=>{if(JSON.stringify(i.read(n))!==JSON.stringify(o))throw ct()})}function Da(e,t){return So(t,e.runtime.platform)}function ja(e,t,n){return`${Ie(t,e.runtime.platform)} --browser${n!==void 0?` --region ${n}`:""}`}function _i(){return new p("missing_email","a valid email address is required",g.Usage,"Run `closedloop login you@example.com` with your email address.")}function Mm(e){try{return sn(e.runtime)}catch(t){throw new p("invalid_api_url",t.message,g.Usage)}}function Hm(e,t){if(t!==void 0){if(t!=="us"&&t!=="eu")throw new p("invalid_region","the region must be us or eu",g.Usage,no);if(e.runtime.env.CLOSEDLOOP_API_URL?.trim())throw new p("conflicting_options","--region cannot be combined with CLOSEDLOOP_API_URL",g.Usage,"Unset CLOSEDLOOP_API_URL to choose a production region, or omit --region to use the configured API.");return t}}var Qe={company:null,website_saved:!1,website_error:null,next:null,note:null},Gm=`Your workspace has no company website yet, so nothing is analysed; an owner or admin sets it with: ${te}`;async function Na(e,t){try{return{...Qe,company:await Jo(e.runtime,$(e.runtime),t),website_saved:!0}}catch(n){if(!(n instanceof p))throw n;return n.message}}async function qm(e,t,n){let{runtime:o,print:i,interactive:r}=e;if(t!==void 0){let u=await Na(e,t);return typeof u=="string"?{...Qe,website_error:u,next:te}:u}if(!n)return Qe;let s=$(o),a=await Ye(o,s);if(a?.research.state!=="no_website")return Qe;let l=!1;try{l=(await W(o,s)).permissions.can_manage_team_settings}catch(u){if(!(u instanceof p))throw u}if(!l)return{...Qe,company:a,note:Gm};if(!r)return{...Qe,company:a,next:te};i.note(Wt);for(let u=0;u<Fm;u++){let c=(await o.prompt(jm)).trim();if(!c)break;let d=await Na(e,c);if(typeof d!="string")return d;i.note(`That website could not be saved: ${d}`)}return{...Qe,company:a,next:te}}async function Jm(e,t,n){let o={domains_saved:!1,domains_instructions:null,company:t.company};if(!n||t.note)return o;let i=$(e.runtime),r=t.company;if(!r||!xn(r))return{...o,company:r};if(!e.interactive)return{domains_saved:!1,domains_instructions:Ht(r),company:r};try{let s=await zr(e,i,r);if(s)return{domains_saved:!0,domains_instructions:null,company:s}}catch(s){if(!(s instanceof p))throw s;e.print.note(`The email domains were not saved: ${s.message}`)}return{...o,company:r}}async function Fa(e,t,n,o,i,r){let s=n.body;if(typeof s.accessToken!="string"||typeof s.refreshToken!="string"||typeof s.user?.email!="string")throw bi();let a=s.team&&typeof s.team.name=="string"?{id:String(s.team.id),name:s.team.name}:null,l=r.account,u=l!==null&&s.user.email.trim().toLowerCase()!==l;er(e.runtime,{api_url:t,email:s.user.email,user_id:s.user.id,team:a,region:s.region,access_token:s.accessToken,refresh_token:s.refreshToken},o,_n(s.sign_in_method),r);let c=s.new_account===!0,d=o==="email_code"&&c,m=await qm(e,i.website,d),h=await Jm(e,m,d),f={...m,company:h.company},y={status:"signed_in",new_account:c,email:s.user.email,team:a,region:s.region??null,requested_email:l,different_account:u,...f,domains_saved:h.domains_saved,domains_instructions:h.domains_instructions};e.print.result(y,_=>{let S=[_.different_account?`You asked to sign in as ${_.requested_email} but approved as ${_.email}; signed in as ${_.email}${_.team?` (${_.team.name})`:""}.`:_.new_account?`Account created for ${_.email} on the Free plan. You're signed in.`:`Signed in as ${_.email}${_.team?` (${_.team.name})`:""}.`];return _.website_error&&S.push(`The website was not saved: ${_.website_error}`),_.website_saved&&S.push(i.website===void 0?"Website saved.":`Website saved. ${Wt}`),_.domains_saved&&S.push("Email domains saved."),(_.website_saved||_.domains_saved)&&_.company?S.push(Vo(_.company,Date.now())):_.note?S.push(_.note):_.next&&S.push(Um),S.join(`
153
- `)})}function Ua(e,t){if(e!==Q.us&&e!==Q.eu||typeof t!="string")return null;try{let n=new URL(e).hostname,o=Object.prototype.hasOwnProperty.call(xa,n)?xa[n]:null,i=new URL(t);return i.protocol!=="https:"||i.hostname!==o||i.port!==""||i.username!==""||i.password!==""?null:i.origin}catch{return null}}function Wa(e,t){return e.status!==409||e.body.code!=="ACCOUNT_IN_OTHER_REGION"?null:Ua(t,e.body.api_url)}async function Vm(e,t,n){let o=Me(e.runtime),i=o.last_email===t&&o.last_region==="eu"?["eu","us"]:["us","eu"],r=null;for(let s of i){let a=await He(e.runtime,Q[s],"/api/check-email",{email:t});if(n(),a.status!==200||a.body.success!==!0||typeof a.body.exists!="boolean")throw new p("region_check_failed","ClosedLoop AI could not check where this account belongs",g.Failure,`Try again. ${no}`);let l=a.body.ssoRequired===!0;if(a.body.exists){let u=Array.isArray(a.body.authMethods)?a.body.authMethods:[];return{kind:"account",region:s,browser:l||u.includes("password")}}l&&r===null&&(r=s)}return r!==null?{kind:"company_sign_in",region:r}:{kind:"none"}}async function Bm(e,t){if(!e.interactive)throw new p("region_required","choose where the new account's data should live before signing up",g.Usage,no);let n=(await e.runtime.prompt(Nm)).trim().toLowerCase();t();let o=n===""?"us":n;if(o!=="us"&&o!=="eu")throw new p("invalid_region","the region must be us or eu",g.Usage,no);return o}async function Ym(e,t,n,o,i){let r=i??$i(e,n,j);try{Oe(e,n,j,r.intent);let s=t,a=await He(e.runtime,s,"/api/cli/auth/code",{email:n});Oe(e,n,j,r.intent);let l=Wa(a,t);if(l!==null){let m=typeof a.body.region=="string"?a.body.region.toUpperCase():"other";e.print.note(`Your ClosedLoop AI account is in the ${m} region; signing in there.`),s=l,mn(e.runtime,n,Ue(s)),a=await He(e.runtime,s,"/api/cli/auth/code",{email:n}),Oe(e,n,j,r.intent)}if(a.status!==200)throw Object.assign(io(a),{api:s});let{challenge:u,expires_in:c}=a.body;if(typeof u!="string"||typeof c!="number")throw bi();let d={api_url:s,configured_api_url:t,email:n,challenge:u,...o!==void 0&&{requested_region:o},expires_at:new Date((e.runtime.now?.()??Date.now())+c*1e3).toISOString()};return pe(e,n,j,r.intent,d),d}catch(s){throw pe(e,n,j,r.intent,r.previous,!1),s}}function Km(e,t,n){let o=ko(e.runtime,t,j);if(o===null||typeof o!="object"||o.email!==t||!n.includes(o.configured_api_url??o.api_url)||typeof o.challenge!="string"||!o.challenge.trim())throw new p("no_code_requested",`no code was requested for ${t} on this computer`,g.Failure,`Run \`closedloop login --email ${Da(e,t)}\` first, then use the code from the email.`);let i=typeof o.expires_at=="string"?Date.parse(o.expires_at):NaN;if(!Number.isFinite(i)||i<=(e.runtime.now?.()??Date.now()))throw pe(e,t,j,o,null,!1),new p("code_expired","the saved sign-in code request has expired",g.Failure,Xt);return o}async function La(e,t,n,o){return He(e.runtime,t.api_url,"/api/cli/auth/verify",{challenge:t.challenge,code:n.trim(),...o.totp&&{totpCode:o.totp.trim()},...o.backupCode&&{backupCode:o.backupCode.trim()}})}async function Ma(e,t,n,o){let{runtime:i,interactive:r}=e,s=await La(e,t,n,o);if(Oe(e,t.email,j,t),r&&s.status===401&&s.body.code==="TWO_FACTOR_REQUIRED"){let a=await i.promptSecret("Code from your authenticator app: ");s=await La(e,t,n,{...o,totp:a}),Oe(e,t.email,j,t)}if(s.status!==200){let a=Object.assign(io(s),{api:t.api_url});throw a.keepPending||pe(e,t.email,j,t,null,!1),a}await Fa(e,t.api_url,s,"email_code",o,{account:t.email,file:j,value:t})}async function Ha(e,t,n,o,i){try{await i()}catch(r){if(!(r instanceof p)||!Wm.includes(r.code))throw r;let s=r.code==="sso_required";if(!e.interactive)throw new p(r.code,r.message,r.exitCode,`Run \`${ja(e,t,n.region)}\` ${s?"to sign in through your browser":"to sign in with your password in your browser"}.`);e.print.note(`${s?"Your workspace signs in with SSO":"This account signs in with its password"}, so you sign in through your browser.`),await oo(e,r.api??o,n,t)}}async function Ga(e,t,n,o,i,r){let{runtime:s,print:a,interactive:l}=e;await Ha(e,n,o,t,async()=>{let u=await Ym(e,t,n,i,r),c=o.website!==void 0?` --website ${ee(o.website.trim(),s.platform)}`:"",d=u.requested_region!==void 0?` --region ${u.requested_region}`:"",m={status:"code_sent",email:n,region:Ue(u.api_url),expires_at:u.expires_at,next:`closedloop login --email ${Da(e,n)} --code <code>${d}${c}`};if(!l){a.result(m,h=>`We sent a sign-in code to ${h.email}. Finish with: ${h.next}`);return}a.result(m,h=>`We sent a sign-in code to ${h.email}.`),await Ma(e,u,await s.prompt("Code from the email: "),o)})}function zm(e,t,n){let o=ko(e.runtime,t,me);if(o===null||typeof o!="object"||(o.configured_api_url??o.api_url)!==n||typeof o.challenge!="string"||!o.challenge.trim()||typeof o.device_secret!="string"||!o.device_secret.trim()||typeof o.user_code!="string"||!o.user_code.trim()||typeof o.url!="string"||typeof o.interval!="number"||!Number.isFinite(o.interval)||o.interval<1)return null;let i=o.regional_hops??0;if(i!==0&&i!==1||i===0&&o.api_url!==n||i===1&&(o.api_url===n||Ua(n,o.api_url)!==o.api_url))return null;let r=typeof o.expires_at=="string"?Date.parse(o.expires_at):NaN;return!Number.isFinite(r)||r<=(e.runtime.now?.()??Date.now())?null:o}async function Xm(e,t,n){let o=$i(e,t,me);try{let i=await He(e.runtime,n,"/api/cli/auth/device/start",t!==null?{email:t}:{});if(Oe(e,t,me,o.intent),i.status!==200)throw io(i);let r=i.body;if(typeof r.challenge!="string"||typeof r.device_secret!="string"||typeof r.user_code!="string"||typeof r.verification_url!="string"||typeof r.expires_in!="number")throw bi();let s={api_url:n,configured_api_url:n,regional_hops:0,challenge:r.challenge,device_secret:r.device_secret,user_code:r.user_code,url:r.verification_url,interval:typeof r.interval=="number"&&r.interval>=1?r.interval:3,expires_at:new Date((e.runtime.now?.()??Date.now())+r.expires_in*1e3).toISOString()};return pe(e,t,me,o.intent,s),s}catch(i){throw pe(e,t,me,o.intent,o.previous,!1),i}}function qa(e,t,n,o){throw pe(e,t,me,n,null,!1),new p("invalid_region_handoff","ClosedLoop AI returned an invalid regional sign-in response",g.Failure,o)}function Qm(e,t,n,o,i){let r=Wa(o,n.api_url),s=o.body,a=e.runtime.now?.()??Date.now(),l=Date.parse(n.expires_at);(r===null||(n.regional_hops??0)!==0||s.region!==(r===Q.eu?"eu":"us")||typeof s.challenge!="string"||!s.challenge.trim()||s.challenge===n.challenge||typeof s.expires_in!="number"||!Number.isFinite(s.expires_in)||s.expires_in<=0||typeof s.interval!="number"||!Number.isFinite(s.interval)||s.interval<1||!Number.isFinite(l)||l<=a)&&qa(e,t,n,i);let u={...n,configured_api_url:n.configured_api_url??n.api_url,api_url:r,challenge:s.challenge,regional_hops:1,interval:s.interval,expires_at:new Date(Math.min(l,a+s.expires_in*1e3)).toISOString()};return pe(e,t,me,n,u),t!==null&&mn(e.runtime,t,Ue(r)),u}async function Zm(e,t,n,o,i){let r=Math.max(1,Math.ceil(o/n.interval)),s=n,a=()=>He(e.runtime,s.api_url,"/api/cli/auth/device/token",{challenge:s.challenge,deviceSecret:s.device_secret});for(let l=0;l<r;l++){let u=await a();if(Oe(e,t,me,s),u.status===409&&u.body.code==="ACCOUNT_IN_OTHER_REGION"&&(s=Qm(e,t,s,u,i),u=await a(),Oe(e,t,me,s),u.status===409&&u.body.code==="ACCOUNT_IN_OTHER_REGION"&&qa(e,t,s,i)),u.status===200&&u.body.status==="signed_in")return{reply:u,pending:s};if(u.status!==200||u.body.status!=="pending"){let c=io(u,i);throw c.keepPending||pe(e,t,me,s,null,!1),c}if(l<r-1){let c=Math.max(0,Date.parse(s.expires_at)-(e.runtime.now?.()??Date.now()));await e.runtime.sleep(Math.min(s.interval*1e3,c))}}return{reply:null,pending:s}}async function oo(e,t,n,o){let{print:i,interactive:r}=e,s=ja(e,o,n.region),a=`${s}${n.website!==void 0?` --website ${ee(n.website.trim(),e.runtime.platform)}`:""}`,l=r?null:zm(e,o,t),u=l??await Xm(e,o,t),c={status:"approval_needed",email:o,region:Ue(u.api_url),url:u.url,user_code:u.user_code,expires_at:u.expires_at,next:`Open the link, enter the code, approve; then run \`${a}\` again.`};if(!r&&l===null){i.result(c,y=>`Open ${y.url} , enter the code ${y.user_code}, and approve. Then run: ${a}`);return}let d=()=>{};if(r){let{runtime:y}=e,_=await y.copyToClipboard(u.user_code);i.result(c,S=>["Open this link, sign in, and enter this code:",` ${ve(S.url,y.supportsHyperlinks)}`,` ${S.user_code}${_?" (copied to your clipboard)":""}`,"Press Enter to open the link in your browser. Waiting for your approval\u2026"].join(`
154
- `)),d=y.onEnter(()=>{y.openUrl(u.url)})}let m=Math.max(0,(Date.parse(u.expires_at)-(e.runtime.now?.()??Date.now()))/1e3),h,f=r?e.print.spinner("Waiting for your approval in the browser\u2026"):null;try{let y=await Zm(e,o,u,r?m:Math.min(m,xm),`Run \`${s}\` to start again.`);h=y.reply,u=y.pending}finally{f?.stop(),d()}if(h===null)throw new p("approval_pending","the sign-in has not been approved in the browser yet",g.Failure,`Open ${u.url} , enter the code ${u.user_code} and approve, then run \`${s}\` again.`);await Fa(e,u.api_url,h,"browser",n,{account:o,file:me,value:u})}async function ep(e,t,n,o){if(t!==void 0&&n.email!==void 0&&ut(t)!==ut(n.email))throw new p("conflicting_options","give the email once: as the argument or with --email",g.Usage,"Run `closedloop login you@example.com`.");let i=t??n.email;if(i!==void 0){let a=ut(i);if(a===null)throw _i();return a}let r=ut(Me(e.runtime).last_email);if(r!==null)return r;if(o)return null;if(!e.interactive)throw _i();let s=ut(await e.runtime.prompt("Email: "));if(s===null)throw _i();return s}async function tp(e,t,n,o,i){let r=$i(e,t,j),s=()=>Oe(e,t,j,r.intent),a,l;try{a=await Vm(e,t,s),a.kind==="none"&&!o&&(l=await Bm(e,s))}catch(d){throw pe(e,t,j,r.intent,r.previous,!1),d}a.kind==="account"&&(mn(e.runtime,t,a.region),e.print.note(`Signing in as ${t} (${a.region.toUpperCase()})`));let u=a.kind!=="none"?Q[a.region]:l!==void 0?Q[l]:i;if(!(o||a.kind==="company_sign_in"||a.kind==="account"&&a.browser)){await Ga(e,u,t,n,l,r);return}pe(e,t,j,r.intent,r.previous),await oo(e,u,n,t)}async function np(e,t,n){if(n.website!==void 0&&!n.website.trim())throw new p("invalid_website","--website needs the company website, e.g. https://acme.example",g.Usage);let o=Hm(e,n.region),i=n.browser===!0||n.sso===!0;if(i&&n.code!==void 0)throw new p("conflicting_options","--browser signs in through the browser and takes no --code",g.Usage,"Run `closedloop login <email> --browser` without --code.");let r=o!==void 0?Q[o]:Mm(e),s=o!==void 0||!!e.runtime.env.CLOSEDLOOP_API_URL?.trim(),a=await ep(e,t,n,i);if(a===null){await oo(e,r,n,null);return}if(n.code!==void 0){let l=n.code,u=s?[r]:[Q.us,Q.eu];await Ha(e,a,n,r,()=>Ma(e,Km(e,a,u),l,n));return}if(!s){await tp(e,a,n,i,r);return}i?await oo(e,r,n,a):await Ga(e,r,a,n,o)}var Ja=(e,t)=>{e.command("login").alias("signup").description("Sign in, or create an account, with your email. The CLI finds your account and picks how you sign in.").argument("[email]","Your email address. Left out, the CLI uses the one it signed in with last.").option("--email <email>","Your email address, instead of the argument.").option("--code <code>","The code from the email, like BCDFG-HJKLM (finishes a sign-in started without it).").option("--totp <code>","The code from your authenticator app, if your account uses two-factor authentication.").option("--backup-code <code>","A two-factor backup code, instead of --totp.").option("--browser","Sign in through your browser (company sign-in or password).").option("--sso","The same as --browser.").option("--region <region>","New account region, or where a browser sign-in starts: us or eu. Existing accounts sign in in their home region.").option("--website <url>","Your company website, set right after you sign in (ClosedLoop AI researches it before analysing anything).").action(async(n,o)=>{await np(t,n,o)})};function ro(e){try{return e()}catch(t){if(!(t instanceof p)||t.code!=="config_invalid")throw t;return null}}function Va(e,t){let n=({active_account:o,...i})=>JSON.stringify({...i,active:o??null});n(t)!==n(e.settings())&&e.setSettings(t)}function op(e){return Y(e,{accounts:It(e),settings:!0},t=>{let n=[];for(let r of t.emails())dt(e,ro(()=>t.read(r,N)),r)!==null&&n.push(r),t.forget(r);let{pending_browser_login:o,...i}=t.settings();return Va(t,{...i,active_account:null}),t.removeLeftovers(),n})}function ip(e,t){let n=[...new Set([...It(e),...t!==null?[t]:[]])];return Y(e,{accounts:n,settings:!0},o=>{let i=null,r=[];for(let l of o.emails()){let u=dt(e,ro(()=>o.read(l,N)),l);l===t?(i=u,o.forget(l)):u!==null?r.push(l):(ro(()=>o.read(l,$e))!==null||ro(()=>o.read(l,ie))!==null)&&(o.remove(l,$e),o.remove(l,ie))}let{pending_browser_login:s,...a}=o.settings();return Va(o,{...a,...a.active_account===t&&{active_account:null}}),o.removeLeftovers(),{session:i,others:r}})}var Ba=(e,{print:t,runtime:n})=>{e.command("logout").description("Sign out the active account on this computer. Your other sessions stay signed in.").option("--all","Sign out every account on this computer.").action(o=>{if(o.all){let s=op(n);t.result({status:s.length>0?"signed_out":"not_signed_in",emails:s},a=>a.emails.length>0?`Signed out ${a.emails.join(", ")} on this computer.`:"You were not signed in on this computer.");return}let{session:i,others:r}=ip(n,Ee(n).email);if(i===null){t.result({status:"not_signed_in"},()=>"You were not signed in on this computer.");return}t.result({status:"signed_out",email:i.email,other_accounts:r},s=>[`Signed out ${s.email} on this computer.`,...s.other_accounts.length>0?[`Still signed in here: ${s.other_accounts.join(", ")}. Switch with: closedloop use <email>`]:[]].join(`
155
- `))})};var Ya=(e,t)=>{let n=e.command("metrics").alias("metric").description("Metrics: one calculation over a segment, measured every night; a goal measures a saved one with goals metrics add.");vs(n,t),Os(n,t)};var rp="closedloop company --domains <domain,...>",sp=7200,Ka=30,ap=new Set(["waiting","researching","retrying"]);async function Ze(e){try{return await e()}catch(t){if(t instanceof p&&!["not_signed_in","session_expired","upgrade_required"].includes(t.code))return null;throw t}}async function Qt(e,t,n,o){let i=await v(e,t,"GET",n),r=i.body[o];if(i.status!==200||!Array.isArray(r))throw new p("api_error",`${n} could not be read`,g.Failure);return r}async function za(e,t){let[n,o,i,r,s,a,l]=await Promise.all([Ze(()=>Be(e,t)),Ze(()=>Qt(e,t,"/api/cli/backfills?limit=100","backfills")),Ze(()=>Qt(e,t,"/api/cli/integrations","integrations")),Ze(()=>Qt(e,t,"/api/cli/ingest?limit=100","records")),Ze(()=>gt(e,t)),Ze(()=>Qt(e,t,"/api/user-goals?include=team","goals")),Ze(()=>Qt(e,t,"/api/team-members/all","members"))]),u={company:n,backfills:o,integrations:i,records:r,structure:s,goals:a,members:l};if(Object.values(u).every(c=>c===null))throw new p("status_unavailable","your workspace status could not be checked",g.Failure,"Check your connection and try `closedloop status` again.");return u}var Zt=(e,t)=>`${k(e)} ${t}${e===1?"":"s"}`;function lp(e,t){let n=`${Wo} ${Fr}`,o=e.suggested_products.length;return o>0?`${ke} (${k(o)} suggested by your company research). ${n}`:t&&t.research.state!=="ready"?`Suggestions appear once your company research is done: closedloop company --watch. ${n}`:`${Pn}. ${n}`}function Xa(e,t){let{company:n,backfills:o,integrations:i,records:r,structure:s,goals:a,members:l}=e,u=s?s.structure.products:null,c=(o??[]).filter(b=>Pe.has(b.state)),d=r?{waiting:r.filter(b=>b.state==="waiting").length,processing:r.filter(b=>b.state==="processing").length}:null,m=i?i.filter(b=>b.connected):null,h=r?r.length>0:null,f=n?.research.state??null,y=n?.email_domains,_=m===null&&h===null?null:(m?.length??0)>0||h===!0,S=[{step:"website",title:"Company website",optional:!1,done:n?n.website!==null:null,detail:n?.website??null,next:n&&n.website===null?te:null},{step:"research",title:"Company research",optional:!1,done:n?f==="ready":null,detail:n?ft(n,t):null,next:n&&f!=="ready"?f==="no_website"?te:"closedloop company --watch":null},{step:"email_domains",title:"Email domains",optional:!1,done:n?y?y.confirmed:!0:null,detail:n?qo(n):null,next:y&&!y.confirmed?rp:null},{step:"product_structure",title:"Product structure",optional:!1,done:u?u.length>0:null,detail:u?`${Zt(u.length,"product")} confirmed`:null,next:s&&u&&u.length===0?lp(s,n):null},{step:"source",title:"A source of customer conversations",optional:!1,done:_,detail:m&&m.length>0?m.map(b=>b.name??b.slug).join(", "):h?"records sent with closedloop ingest":null,next:_===!1?"closedloop integrations list --available, or send records with: closedloop ingest":null},{step:"history",title:"History imported",optional:!1,done:o===null||_===null?null:_===!0&&c.length===0,detail:o===null?null:c.length>0?`${Zt(c.length,"import")} still running`:null,next:c.length>0?"closedloop backfills":_===!1?"Connect a source first; its history is imported when you connect it":null},{step:"goal",title:"A goal",optional:!0,done:a?a.length>0:null,detail:a?Zt(a.length,"goal"):null,next:a&&a.length===0?"closedloop goals create --file goals.json":null},{step:"team",title:"Your team invited",optional:!0,done:l?l.length>1:null,detail:l?Zt(l.length,"member"):null,next:l&&l.length<=1?`In the app: Settings, then Team. ${x}`:null}];return{running:{research:f&&ap.has(f)?f:null,backfills:c,own_data:d},onboarding:S,instructions:n?Ht(n):null}}function up(e){let t=e.running.own_data;return e.running.research!==null||e.running.backfills.length>0||!!(t&&t.processing>0)}function cp(e,t,n){let o=t.print.style,i=[],r=e.running.own_data,s=[...e.running.backfills.map(c=>` ${$n(c,n,o)}`),...r&&r.waiting+r.processing>0?[` Your own data: ${[r.processing?`${k(r.processing)} processing`:"",r.waiting?`${k(r.waiting)} waiting`:""].filter(Boolean).join(", ")}`]:[]],a=e.onboarding.find(c=>c.step==="research");e.running.research&&a?.detail&&s.unshift(` ${a.detail}`),i.push("Running now"),i.push(...s.length>0?s:[" Nothing."]),i.push("","Onboarding");for(let c of e.onboarding){let d=c.done===!0?o.green("\u2713"):c.done===!1?c.optional?o.dim("\u25CB"):o.orange("\u25CB"):o.dim("?"),m=c.optional?`${c.title} (optional)`:c.title,h=c.done===!0&&c.detail?o.dim(` \xB7 ${c.detail}`):"";i.push(` ${d} ${m}${h}`),c.done!==!0&&c.next&&i.push(` ${o.dim("Next:")} ${c.next}`)}let l=e.onboarding.filter(c=>!c.optional&&c.done===!1).length,u=e.onboarding.filter(c=>!c.optional&&c.done===null).length;return i.push("",l===0&&u===0?"Onboarding is complete.":[l>0?`${Zt(l,"step")} left before your data is fully analysed.`:"",u>0?`${u} required step${u===1?"":"s"} could not be checked; try status again.`:""].filter(Boolean).join(" ")),e.instructions&&!t.interactive&&i.push("",e.instructions),i.join(`
156
- `)}async function dp(e,t){let n=$(e.runtime),o=Xa(await za(e.runtime,n),Date.now());if(t.watch){let i=e.print.spinner("Checking every 30 seconds\u2026");try{for(let r=0;r<sp&&up(o);r+=Ka)await e.runtime.sleep(Ka*1e3),o=Xa(await za(e.runtime,n),Date.now())}finally{i.stop()}}e.print.result(o,i=>cp(i,e,Date.now()))}var Qa=(e,t)=>{e.command("status").description("What is running now (research, history imports, your own data) and what onboarding still needs.").option("--watch","Keep checking every 30 seconds until nothing is running (up to 2 hours).").action(async n=>dp(t,n))};import{existsSync as mp,writeFileSync as pp}from"node:fs";import{resolve as gp}from"node:path";var hp=/^\d{4}-(0[1-9]|1[0-2])$/;async function vi(e,t){let n=await Ge(e.runtime,t),o=await v(e.runtime,t,"GET",`/api/teams-billing/${n}/billing`);if(o.status!==200||typeof o.body.credits_used!="number")throw C(o,"api_error","Your usage could not be loaded. Try again.");return o.body}function fp(){return new Date().toISOString().slice(0,7)}function Za(e){let t=e??fp();if(!hp.test(t))throw new p("invalid_month",`'${t}' is not a month`,g.Usage,"Write it like 2026-10.");return t}function yp(e,t){let n=k(e.credits_used),o=e.current_period_end?R(e.current_period_end):null;if(e.credits_limit===null){let c=e.current_period_cost?` Cost so far: ${`${e.current_period_cost} ${e.currency??""}`.trim()}.`:"";return`${n} credits used this period (${e.plan_label} plan).${c}${o?` The period ends ${o}.`:""}`}let i=k(e.credits_limit),r=e.credits_used/e.credits_limit,s=Math.floor(r*100),a=e.credits_used>0&&s===0?"<1":String(s),u=[`${(e.limit_reached||r>=1?t.red:r>=.8?t.orange:t.green)(t.bar(r,30))} ${t.bold(`${a}%`)}`,`${n} of ${i} credits used this period (${a}%, ${e.plan_label} plan).`];return e.limit_reached?u.push(`The limit is reached: new conversations wait${o?` until ${o}`:" until the next period"}, or until you upgrade (closedloop plan upgrade).`):e.limit_enforced?(o&&u.push(`It resets ${o}.`),e.limit_reached_on&&u.push(`At this pace you reach the limit around ${P(e.limit_reached_on)}; after that, new conversations wait until it resets.`)):(r>=1&&u.push("That is more than the credits your plan includes this period."),o&&u.push(`The period ends ${o}.`)),u.join(`
157
- `)}async function wp(e){let t=$(e.runtime),n=await vi(e,t),o={plan:n.subscription_tier,plan_label:n.plan_label,credits_used:n.credits_used,credits_limit:n.credits_limit,limit_enforced:n.limit_enforced,limit_reached:n.limit_reached,limit_reached_on:n.limit_reached_on,period_start:n.current_period_start,period_end:n.current_period_end,cost_so_far:n.current_period_cost??null,currency:n.currency??null};e.print.result(o,()=>yp(n,e.print.style))}async function _p(e,t){let n=t===void 0?12:Number(t);if(!Number.isInteger(n)||n<1||n>12)throw new p("invalid_months","--months must be a whole number from 1 to 12",g.Usage);let o=$(e.runtime),i=await Ge(e.runtime,o),r=await v(e.runtime,o,"GET",`/api/teams-billing/${i}/usage-history`);if(r.status!==200||!Array.isArray(r.body.months))throw C(r,"api_error","Your usage history could not be loaded. Try again.");let s=r.body.months.map(a=>({month:a.month,credits:a.billableCredits})).sort((a,l)=>a.month<l.month?1:-1).slice(0,n);e.print.result({months:s},a=>{if(a.months.length===0)return"No credits used in the last 12 months.";let l=e.print.style,u=Math.max(...a.months.map(y=>y.credits)),c=a.months.map(y=>xt(y.month)),d=a.months.map(y=>k(y.credits)),m=Math.max(...c.map(y=>y.length)),h=Math.max(...d.map(y=>y.length)),f=a.months.length>1?`
158
- ${l.dim(`Trend, oldest to newest: ${l.sparkline([...a.months].reverse().map(y=>y.credits))}`)}`:"";return a.months.map((y,_)=>`${c[_].padEnd(m)} ${d[_].padStart(h)} credits ${l.green(l.bar(u>0?y.credits/u:0,20))}`).join(`
159
- `)+f})}async function bp(e,t){let n=Za(t),o=$(e.runtime),i=await Ge(e.runtime,o),r=await v(e.runtime,o,"GET",`/api/teams-billing/${i}/usage-by-source/${n}`);if(r.status!==200||!Array.isArray(r.body.sources))throw C(r,"api_error","Your usage could not be loaded. Try again.");let s={month:n,total:Number(r.body.total)||0,sources:r.body.sources};e.print.result(s,a=>a.sources.length===0?`No credits used in ${xt(a.month)}.`:[`${xt(a.month)}: ${k(a.total)} credits`,...a.sources.map(l=>` ${l.name}: ${k(l.credits)}`)].join(`
160
- `))}async function $p(e,t){let n=Za(t.month),o=gp(t.output??`closedloop-ai-usage-${n}.csv`);if(mp(o)&&!t.force)throw new p("file_exists",`${o} already exists`,g.Usage,"Add --force to replace it, or pick another file with --output.");let i=$(e.runtime),r=await Ge(e.runtime,i),s=await v(e.runtime,i,"GET",`/api/teams-billing/${r}/usage-history/${n}/export`);if(s.status!==200||!s.text.startsWith("date,integration,credits"))throw C(s,"api_error","Your usage could not be exported. Try again.");pp(o,s.text,{mode:384});let a=s.text.trimEnd().split(`
161
- `).length-1;e.print.result({status:"saved",month:n,path:o,rows:a},l=>`Saved ${xt(l.month)}'s usage to ${l.path} (${l.rows} rows: date, integration, credits).`)}async function vp(e){let t=$(e.runtime),n=await vi(e,t),o={plan:n.subscription_tier,plan_label:n.plan_label,credits_included:n.credits_limit,limit_enforced:n.limit_enforced,period_start:n.current_period_start,period_end:n.current_period_end,plan_url:n.plan_url};e.print.result(o,i=>{let r=i.credits_included===null?"no monthly limit; you pay for the credits you use":`${k(i.credits_included)} credits a month included${i.limit_enforced?"; new conversations wait once they are used":""}`;return[`${i.plan_label} plan: ${r}.`,"See usage: closedloop usage. Change plan: closedloop plan upgrade"].join(`
162
- `)})}async function Cp(e){let t=$(e.runtime),n=await W(e.runtime,t),o=await vi(e,t),i=o.plan_url;if(!i)throw new p("no_plan_link","the plan page link is not available",g.Failure,`Open Settings, Plan & billing in the app. ${x}`);let r=n.permissions.can_manage_billing,s=r&&e.interactive?await e.runtime.openUrl(i):!1;e.print.result({status:s?"opened":"link",url:i,plan_label:o.plan_label,can_change_plan:r},a=>{let l=ve(a.url,e.runtime.supportsHyperlinks);return a.can_change_plan?`${s?"Opened":"Change your plan at"} ${l}${s?" in your browser.":"."} You are on the ${a.plan_label} plan.
163
- ${x}`:`You are on the ${a.plan_label} plan. Only someone who can manage billing in ${n.name} can change it: ask an owner or admin. The plan page: ${l}
164
- ${x}`})}var el=(e,t)=>{let n=e.command("usage").description("Credits used this period; history, by source and export below.").action(async()=>wp(t));n.command("history").description("Credits used per month, newest first.").option("--months <n>","How many months, up to 12.").action(async o=>_p(t,o.months)),n.command("by-source").description("Credits used in a month per integration or feature, largest first.").option("--month <yyyy-mm>","The month; this month if left out.").action(async o=>bp(t,o.month)),n.command("export").description("Save a month's usage per day and integration as a CSV file.").option("--month <yyyy-mm>","The month; this month if left out.").option("--output <file>","Where to save it; closedloop-ai-usage-<month>.csv if left out.").option("--force","Replace the file if it exists.").action(async o=>$p(t,o))},tl=(e,t)=>{e.command("plan").description("Your plan and what it includes.").action(async()=>vp(t)).command("upgrade").description("Open Plan & billing to change your plan (payment happens in the browser).").action(async()=>Cp(t))};function St(e){return typeof e=="string"&&e!==""?e:null}function nl(e){let t=e;return!t||typeof t!="object"||!St(t.team_id)||!St(t.team_name)?null:{id:t.team_id,name:t.team_name,role:St(t.role),plan:St(t.plan)}}var sl=[["can_manage_integrations","manage integrations"],["can_manage_billing","manage billing"],["can_manage_members","manage members"],["can_manage_team_settings","manage settings"],["can_view_analytics","view analytics"]];function kp(e){let t=e?.permissions??{};return Object.fromEntries(sl.map(([n])=>[n,t[n]===!0]))}var ol={password:"a password",email_code:"an email code",google:"Google SSO",microsoft:"Microsoft SSO",okta:"Okta SSO",saml:"SAML SSO"};function il(e,t){return e==="email_code"?ol.email_code:e!=="browser"?null:t?`${ol[t]} in the browser`:"the browser"}function Sp(e){return typeof e=="string"&&!Number.isNaN(Date.parse(e))?e:null}function Ap(e){let t=[e.role,e.plan&&`${e.plan[0].toUpperCase()}${e.plan.slice(1).replace(/_/g," ")} plan`].filter(Boolean).join(", ");return t?`${e.name} (${t})`:e.name}function rl(e,t,n){if(e.status===403){let o=C(e,"forbidden","you cannot access this account or workspace"),i=o.code==="sso_required"?`Run \`${n}\` to sign in with your company account.`:["two_fa_verification_required","two_fa_setup_session_expired"].includes(o.code)?"Run `closedloop login` to sign in again and complete authentication.":"Ask a workspace owner or admin to check your access.";return new p(o.code,o.message,g.Failure,i)}if(e.status===404&&e.body.code==="USER_NOT_FOUND"){let o=C(e,"user_not_found","your saved account could not be found");return new p(o.code,o.message,g.Failure,"Run `closedloop login` to sign in again.")}return t==="workspace"&&e.status===404&&e.body.error==="No team found for user"?new p("no_workspace","you are not in a workspace yet",g.Failure,`Create or join one in the app. ${x}`):new p("api_error","ClosedLoop AI could not load your account",g.Failure,"Try again in a moment.")}var al=(e,{print:t,runtime:n})=>{e.command("whoami").description("Show who you are signed in as (the active account), and your workspace.").action(async()=>{let o=$(n),i=`${Ie(o.email,n.platform)} --browser`,r=await No(n,o,"/api/auth/profile");if(r.status!==200)throw rl(r,"profile",i);let s=await No(n,o,"/api/team-info");if(s.status!==200)throw rl(s,"workspace",i);let a=r.body.profile??{},l=nl(s.body.teamInfo),u=Array.isArray(s.body.allTeams)?s.body.allTeams.map(nl).filter(m=>m!==null):[],c=l?await Ye(n,o):null,d={status:"signed_in",email:St(a.email)??o.email,name:St(a.full_name),two_factor:a.totp_enabled===!0,workspace:l,other_workspaces:u.filter(m=>m.id!==l?.id),permissions:kp(s.body.teamInfo),company_research:c?c.research.state:null,analysis_waiting:c?c.analysis_waiting:null,region:typeof o.region=="string"?o.region:null,api_url:o.api_url,signed_in_at:Sp(o.signed_in_at),sign_in_method:o.sign_in_method==="email_code"||o.sign_in_method==="browser"?o.sign_in_method:null,signed_in_with:_n(o.signed_in_with),session_expires_at:tr(o),cli:{version:re,latest_version:n.release?.latest??null,update_available:To(n)!==null}};t.result(d,m=>[`Signed in as ${t.style.bold(m.name?`${m.name} <${m.email}>`:m.email)}${m.two_factor?" (two-factor on)":""}`,`Workspace: ${m.workspace?Ap(m.workspace):"none"}`,...m.other_workspaces.length?[`Also in: ${m.other_workspaces.map(h=>h.name).join(", ")}`]:[],(()=>{let h=sl.filter(([f])=>m.permissions[f]).map(([,f])=>f);return h.length?`You can: ${h.join(", ")}.`:"You can read this workspace; to change integrations, billing or members, ask an owner or admin."})(),...c&&Mt(c)?[`Your data: ${Mt(c)}`]:[],`Region: ${(m.region??"unknown").toUpperCase()} \xB7 API: ${m.api_url}`,m.signed_in_at?`Signed in here: ${R(m.signed_in_at)}${il(m.sign_in_method,m.signed_in_with)?` with ${il(m.sign_in_method,m.signed_in_with)}`:""}`:"Signed in here: time not recorded",...m.session_expires_at?[`Stays signed in while you use the CLI; ends ${R(m.session_expires_at)} if unused.`]:[]].join(`
165
- `))})};var ll=[Ja,al,Ba,or,Qa,Xr,Gr,ns,ra,ga,Qs,as,hs,Ya,Ia,dr,el,tl,ls];function ki(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}var dl={update:()=>{},stop:()=>{}},ul="\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F";function Op(e,t){let n=t,o=0,i=()=>e.stderr(`\r\x1B[2K\x1B[2m${ul[o++%ul.length]} ${n}\x1B[22m`);i();let r=setInterval(i,80);return r.unref?.(),{update:s=>{n=s},stop:()=>{clearInterval(r),e.stderr("\r\x1B[2K")}}}function ml(e,t){return ki(e)||!t.stdoutIsTTY?Lp(t):Dp(t)}function Tp(e){return e.hint===void 0?{code:e.code,message:e.message}:{code:e.code,message:e.message,hint:e.hint}}function Ci(e){let t=JSON.stringify(e);if(t===void 0)throw new Error("a result must be JSON data, not undefined or a function");return`${t}
166
- `}function pl(e){return e.options.filter(t=>!t.hidden&&t.long!=="--help")}function gl(e){let t=e.aliases()[0],n=e.registeredArguments.map(i=>{let r=`${i.name()}${i.variadic?"...":""}`;return i.required?`<${r}>`:`[${r}]`}),o=pl(e).map(i=>`[${i.long&&i.short?i.flags.replace(`${i.short}, `,""):i.flags}]`);return[`${e.name()}${t?`|${t}`:""}`,...n,...o].join(" ")}var Rp=`Add --help after a command for details.
167
- `,Ep=30,cl=6;function Ip(e,t){return[...t.visibleOptions(e).map(n=>t.optionTerm(n)),...t.visibleGlobalOptions(e).map(n=>t.optionTerm(n)),...t.visibleArguments(e).map(n=>t.argumentTerm(n)),...t.visibleCommands(e).map(n=>t.subcommandTerm(n))]}function Pp(e,t){return Math.max(0,...Ip(e,t).map(n=>t.displayWidth(n)).filter(n=>n<=Ep))}function xp(e,t,n,o){if(o.displayWidth(e)<=t)return Te.prototype.formatItem.call(o,e,t,n,o);if(!n)return` ${e}`;let i=(o.helpWidth??80)-cl,r=" ".repeat(cl);return` ${e}
127
+ `)}
128
+
129
+ ${e.print.style.dim("Show or connect one by its id: closedloop integrations show <id>")}`;return i?c:`${c}
130
+
131
+ ${a}`})}function sa(e){let t=e.choices?` (${e.choices.map(o=>o.value).join(" or ")})`:"",n=e.required===!0?"":e.required===!1?", optional":`, needed when ${e.required.when} is ${e.required.equals}`;return`${e.label}${t}${n}`}async function Vd(e,t){let n=v(e.runtime),o=await Yt(e,n,t);e.print.result(o,i=>{let r=[i.name];if(i.description&&r.push(i.description),r.push(`Status: ${i.connected&&!i.needs_attention&&i.connected_at?`connected since ${E(i.connected_at)}; ${ra(i)}`:ra(i)}`),i.available_on_your_plan||r.push(`Plan: available on the ${i.plan_label} plan, not on yours.`),i.connect.method==="api_key")r.push(`Connect: closedloop integrations connect ${i.integration_id}`),r.push(` It asks for: ${i.connect.fields.map(sa).join("; ")}.`);else if(i.connect.method==="browser"){r.push(`Connect: closedloop integrations connect ${i.integration_id} (opens your browser)`);let s=gi(i)?.fields??[];s.length>0&&r.push(` It asks for: ${s.map(sa).join("; ")}.`)}else i.connect.method==="ingest"?r.push('Send data: closedloop ingest "some text", or closedloop ingest --file data.csv'):i.connect.app_url&&(r.push(`Set it up in the app: ${ke(i.connect.app_url,e.runtime.supportsHyperlinks)}`),r.push(` ${D}`));return r.push(R(e.print.style,"",i.integration_id)),r.join(`
132
+ `)})}function Yn(e,t){return typeof e.required=="boolean"?e.required:t[e.required.when]===e.required.equals}function Kn(e,t){let n={};for(let o of e.field??[]){let i=o.indexOf("="),r=i>0?o.slice(0,i).trim():"",s=t.find(a=>a.key===r);if(!s){let a=t.filter(l=>!l.secret).map(l=>l.key);throw new p("unknown_field","this integration does not accept that setting; use --field name=value",g.Usage,a.length?`Its fields: ${a.join(", ")}.`:"It takes only the API key, on standard input with --stdin.")}if(s.secret)throw new p("secret_in_argument",`${s.label} cannot be passed as an argument`,g.Usage,"Pipe it in instead, so it stays out of your shell history: --stdin.");n[r]=o.slice(i+1).trim()}return n}function ca(e,t){return!e.choices||t===""||e.choices.some(n=>n.value===t)?null:`${e.label} must be ${e.choices.map(n=>n.value).join(" or ")}`}async function Bd(e,t,n){let o=t.choices?` [${t.choices.map(a=>`${a.value} = ${a.label}`).join(", ")}]`:"",i=t.help?` (${t.help})`:"",r=t.default?` (Enter for ${t.default})`:Yn(t,n)?"":" (optional, Enter to skip)",s="";for(let a=1;a<=Fd;a++){let l=await e.runtime.prompt(`${s}${t.label}${i}${o}${r}: `)||t.default||"",u=ca(t,l)??(l===""&&Yn(t,n)?`${t.label} is needed`:null);if(!u)return l;s=`${u}. `}throw new p("invalid_field",`no valid ${t.label} was given`,g.Usage)}async function Yd(e,t,n,o){let i={};if(t.stdin){let r=(await e.runtime.readStdin()).split(/\r?\n/).map(s=>s.trim()).filter(s=>s!=="");if(r.length!==n.length)throw new p("stdin_mismatch",`expected ${n.length} line${n.length===1?"":"s"} on standard input, got ${r.length}`,g.Usage,`Send ${n.map(s=>s.label).join(", then ")}, one per line.`);return n.forEach((s,a)=>{i[s.key]=r[a]}),i}if(!e.interactive)throw new p("secret_required",`${n.map(r=>r.label).join(" and ")} must be piped in`,g.Usage,`For example: printf '%s\\n' "$KEY" | closedloop integrations connect ${o} --stdin`);for(let r of n){let s=await e.runtime.promptSecret(`${r.label}${r.help?` (${r.help})`:""}: `);if(!s)throw new p("secret_required",`${r.label} is needed`,g.Usage);i[r.key]=s}return i}async function da(e,t,n,o){let i=n.filter(s=>!s.secret);for(let s of i){o[s.key]===void 0&&s.default&&(o[s.key]=s.default);let a=o[s.key]!==void 0?ca(s,o[s.key]):null;if(a)throw new p("invalid_field",a,g.Usage)}for(let s of i)o[s.key]!==void 0&&o[s.key]!==""||e.interactive&&(s.required===!1||Yn(s,o))&&(o[s.key]=await Bd(e,s,o));let r=i.filter(s=>Yn(s,o)&&!o[s.key]);if(r.length>0)throw new p("missing_field",`${t} also needs ${r.map(s=>s.label).join(", ")}`,g.Usage,`Add ${r.map(s=>`--field ${s.key}=<${s.label}>`).join(" ")}.`)}async function Kd(e,t,n,o){let i=Nd(o),r=n.connect.fields,s=Kn(o,r);await da(e,n.name,r,s),Object.assign(s,await Yd(e,o,r.filter(y=>y.secret),n.integration_id));let a={};for(let y of r)y.target==="config"&&s[y.key]&&(a[y.key]=s[y.key]);let l=y=>r.find(_=>_.target===y&&s[_.key])?.key,u=l("api_key"),c=l("api_secret"),m=await Ke(e.runtime,t),d=await $(e.runtime,t,"POST","/api/user-integrations",{integration_id:n.integration_id,api_key:u?s[u]:void 0,...c?{api_secret:s[c]}:{},config:a});if(d.status!==200&&d.status!==201)throw C(d,"connect_refused",`${n.name} could not be connected.`);let h=d.body.integration&&typeof d.body.integration=="object"?d.body.integration:void 0,f=await Ld(e,t,n,h,i);e.print.result({status:"connected",...de(n),reconnected:n.connected,history:f,...ma(m)},y=>{let _=`${y.reconnected?"Reconnected":"Connected"} ${y.name}.`;if(y.history?.started){let S=y.history.backfill_id?`closedloop backfills show ${y.history.backfill_id}`:"closedloop backfills";return Bn(`${_} Importing the last ${y.history.months} month${y.history.months==="1"?"":"s"} of history; follow it with: ${S}`,m)}return y.history?Bn(`${_} The history import did not start: start it in the app (Integrations, ${y.name}). ${D}`,m):Bn(`${_} Check on it with: closedloop integrations show ${y.integration_id}`,m)})}var de=e=>({integration_id:e.integration_id,slug:e.slug,name:e.name});function ma(e){return{waiting_for:e?.analysis_waiting?"company_profile":null}}function Bn(e,t){let n=Mt(t);return n?`${e}
133
+ ${n}`:e}function zd(e,t){return!t.connected||t.needs_attention?!1:e.connected?t.last_connection_at!==e.last_connection_at||t.connection_id!==e.connection_id:!0}function gi(e){let t=e.connect.browser;return!t||typeof t.start!="string"||!t.start.startsWith("/")||!Array.isArray(t.fields)?null:t}var Xd="/api/cli/oauth-handoff",Qd="/api/cli/oauth-handoff/go",Zd="/closedloop/oauth",pa=/^[A-Za-z0-9_-]{43}$/;function ga(e,t,n,o){if(typeof e!="string")return null;let i,r;try{i=new URL(e),r=new URL(t)}catch{return null}let s=i.searchParams.getAll(o);return i.origin!==r.origin||i.pathname!==n||s.length!==1||!pa.test(s[0])?null:{url:i.href,value:s[0]}}async function ha(e,t){try{return await e.openUrl(t)===!0}catch{return!1}}var em={remote_browser:"Your browser is on another computer",no_local_listener:"The CLI could not wait for your browser on this computer",browser_not_opened:"The browser could not be opened"},tm=["SSH_CONNECTION","SSH_TTY","SSH_CLIENT","CODESPACES","REMOTE_CONTAINERS","GITPOD_WORKSPACE_ID","CLOUD_SHELL","container","KUBERNETES_SERVICE_HOST"],nm=["/.dockerenv","/run/.containerenv"];function om(e){return tm.some(t=>!!e.env[t])||e.env.AWS_EXECUTION_ENV==="CloudShell"?!0:nm.some(t=>e.fileExists?.(t)===!0)}async function fa(e,t,n,o){let{print:i,runtime:r}=e,s=o.openOnEnter,a=s?r.onEnter(()=>{ha(r,s)}):()=>{},l=i.spinner(`Waiting for ${n.name} to connect\u2026`);try{for(let u=0;u<o.seconds;u+=oa){await r.sleep(oa*1e3);let c=o.stopped();if(c)throw c;let m=await Yt(e,t,n.integration_id);if(zd(n,m)){let d=await Ke(r,t);i.result({status:"connected",...de(m),reconnected:n.connected,...ma(d)},h=>Bn(`${h.reconnected?"Reconnected":"Connected"} ${h.name}.`,d));return}}}finally{l.stop(),a()}throw new p("connect_pending",`${n.name} is not connected yet`,g.Failure,`Finish in the browser, then run \`closedloop integrations connect ${n.integration_id}\` again.`)}async function Bt(e,t,n,o,i){let{print:r,runtime:s,interactive:a}=e,l=n.connect.url;if(!l)throw new p("no_connect_link",`${n.name} has no link to connect it here`,g.Failure,`Connect it from the Integrations page in the app. ${D}`);let u=i?" (the settings you gave were not used)":"",c=o?`${em[o]}, so connect ${n.name} in the app${u}:`:null,m={status:"browser_needed",...de(n),url:l,...o?{reason:o}:{},next:`Open the link and connect ${n.name}${u}; then run \`closedloop integrations connect ${n.integration_id}\` again to check.`,app_sign_in:D};if(!a){r.result(m,d=>c?`${c} ${d.url}
134
+ ${d.app_sign_in}
135
+ Then run: closedloop integrations connect ${d.integration_id}`:`Open ${d.url} and connect ${d.name}${u}. ${d.app_sign_in} Then run: closedloop integrations connect ${d.integration_id}`);return}r.result(m,d=>[c??`Connect ${d.name} in your browser${u}:`,` ${ke(d.url,s.supportsHyperlinks)}`,d.app_sign_in,"Press Enter to open it. Waiting for the connection\u2026"].join(`
136
+ `)),await fa(e,t,n,{seconds:Dd,openOnEnter:l,stopped:()=>null})}var pi="ClosedLoop AI could not finish connecting here. Go back to your terminal.",ya={status:404,body:"Not found."};function im(e,t,n,o,i){if(i.method!=="GET"||i.path!==Zd||o.token===null)return Promise.resolve(ya);let r=i.query.getAll("n");if(r.length!==1||!pa.test(r[0]))return Promise.resolve({status:400,body:"This link is not complete. Go back to your terminal."});let s=o.queue.then(()=>rm(e,t,n,o,r[0]));return o.queue=s.catch(()=>{}),s}async function rm(e,t,n,o,i){if(o.success)return o.success.n===i?o.success.reply:ya;try{let r=await $(e.runtime,t,"POST","/api/cli/oauth-handoff/confirm",{t:o.token,n:i});if(r.status===409)return{status:409,body:"Press Continue on the page the link opened, in this browser."};if(r.status!==200)return o.failure=C(r,"connect_refused",`${n.name} could not be connected.`),{status:400,body:pi,last:!0};let s=ga(r.body.go_url,t.api_url,Qd,"g");if(!s)return o.failure=new p("api_error",`${n.name} could not be connected from here`,g.Failure,`Try again, or connect it from the Integrations page in the app. ${D}`),{status:502,body:pi,last:!0};let a={status:302,location:s.url,last:!0};return o.success={n:i,reply:a},a}catch(r){return o.failure=r instanceof p?r:new p("api_error",`${n.name} could not be connected from here`,g.Failure),{status:500,body:pi,last:!0}}}async function sm(e,t,n,o){let i=e.runtime.listenLoopback;if(!i)return null;try{return await i(r=>im(e,t,n,o,r))}catch{return null}}async function am(e,t,n,o,i,r,s){let a=n.connected&&typeof n.connection_id=="string"&&(n.connections??1)<=1?{existing_connection_id:n.connection_id,is_reconnection:!0}:{},l=await $(e.runtime,t,"POST",o.start,{team_id:r,...i,...a},{"X-ClosedLoop-Browser-Handoff":"1","X-ClosedLoop-Loopback-Port":String(s)});if(l.status<200||l.status>=300)throw C(l,"connect_refused",`${n.name} could not be connected.`);let u=ga(l.body.handoff_url,t.api_url,Xd,"t");if(!u)throw new p("api_error",`${n.name} could not be started from here`,g.Failure,`Try again, or connect it from the Integrations page in the app. ${D}`);return u}async function lm(e,t,n,o,i){let r=gi(n),s=(o.field??[]).length>0;if(!r)return Bt(e,t,n,null,!1);if(o.inApp)return Bt(e,t,n,null,s);let{print:a,runtime:l,interactive:u}=e;if(om(l))return Bt(e,t,n,"remote_browser",s);let c=Kn(o,r.fields);await da(e,n.name,r.fields,c);let m=Object.fromEntries(r.fields.filter(y=>c[y.key]).map(y=>[y.key,c[y.key]])),d=Object.keys(m).length>0,h={token:null,failure:null,success:null,queue:Promise.resolve()},f=await sm(e,t,n,h);if(!f)return Bt(e,t,n,"no_local_listener",d);try{let y=await am(e,t,n,r,m,i,f.port);if(h.token=y.value,u)a.result({status:"browser_needed",...de(n),url:y.url},_=>[`Connect ${_.name} in your browser:`,` ${ke(_.url,l.supportsHyperlinks)}`,"Press Enter to open it. Waiting for the connection\u2026"].join(`
137
+ `));else if(!await ha(l,y.url))return f.close(),Bt(e,t,n,"browser_not_opened",d);await fa(e,t,n,{seconds:jd,openOnEnter:u?y.url:null,stopped:()=>h.failure})}finally{f.close()}}async function um(e,t,n){let o=v(e.runtime),i=await Yt(e,o,t);if(n.inApp&&i.connect.method!=="browser")throw new p("not_a_browser_integration",`${i.name} does not connect in the browser, so --in-app does not apply`,g.Usage,`Run it without --in-app: closedloop integrations connect ${i.integration_id}`);let r=i.connect.method==="browser"?gi(i)?.fields??[]:[];if(i.connect.method==="api_key")Kn(n,i.connect.fields);else if(r.length>0&&!n.stdin)Kn(n,r);else if((n.field??[]).length>0||n.stdin)throw new p("not_an_api_key_integration",`${i.name} ${Wd[i.connect.method]}, not with a key`,g.Usage,`Run it without --field and --stdin: closedloop integrations connect ${i.integration_id}`);if(i.connected&&!i.needs_attention&&!n.reconnect){e.print.result({status:"already_connected",...de(i)},l=>`${l.name} is already connected. To connect it again (for example with a new key), add --reconnect.`);return}let s=i.connect.method!=="ingest"?await mt(e.runtime,o,"can_manage_integrations",`connect ${i.name}`):null;if(!i.available_on_your_plan)throw new p("plan_required",`${i.name} is available on the ${i.plan_label} plan`,g.Failure,"Change your plan with: closedloop plan upgrade");if(i.connect.method==="api_key")return Kd(e,o,i,n);if(i.connect.method==="browser"&&s)return lm(e,o,i,n,s.id);if(i.connect.method==="ingest"){e.print.result({status:"use_ingest",...de(i)},()=>"Send your own data with closedloop ingest: some text, or --file with a CSV, JSON or text file. The CLI source is created on the first send.");return}let a=i.connect.app_url;e.print.result({status:"set_up_in_app",...de(i),url:a,app_sign_in:D},l=>`${l.name} is set up in the app${l.url?`: ${ke(l.url,e.runtime.supportsHyperlinks)}`:"."}
138
+ ${l.app_sign_in}`)}async function cm(e,t,n){let o=v(e.runtime),i=await Yt(e,o,t);if(ua(i,"disconnected"),!i.connected||!i.connection_id){e.print.result({status:"not_connected",...de(i)},s=>`${s.name} is not connected.`);return}if(await mt(e.runtime,o,"can_manage_integrations",`disconnect ${i.name}`),!n){if(!e.interactive)throw new p("confirmation_required",`disconnecting ${i.name} needs confirmation`,g.Usage,`Run it again with --yes: closedloop integrations disconnect ${i.integration_id} --yes`);if((await e.runtime.prompt(`Disconnect ${i.name}? Syncing stops; what was already imported stays. Type yes to confirm: `)).toLowerCase()!=="yes"){e.print.result({status:"cancelled",...de(i)},()=>"Nothing changed.");return}}let r=await $(e.runtime,o,"DELETE",`/api/user-integrations/${encodeURIComponent(i.connection_id)}`);if(r.status!==200&&r.status!==204)throw C(r,"disconnect_refused",`${i.name} could not be disconnected.`);e.print.result({status:"disconnected",...de(i)},s=>`Disconnected ${s.name}. What was already imported stays.`)}async function aa(e,t,n){let o=v(e.runtime),i=await Yt(e,o,t),r=de(i);if(ua(i,n?"paused":"resumed"),!i.connected||!i.connection_id){e.print.result({status:"not_connected",...r},a=>`${a.name} is not connected.`);return}if(n===(i.status==="paused")){e.print.result({status:n?"already_paused":"not_paused",...r},a=>n?`${a.name} is already paused.`:`${a.name} is not paused.`);return}await mt(e.runtime,o,"can_manage_integrations",`${n?"pause":"resume"} ${i.name}`);let s=await $(e.runtime,o,"PUT",`/api/user-integrations/${encodeURIComponent(i.connection_id)}`,{status:n?"inactive":"active"});if(s.status!==200)throw C(s,n?"pause_refused":"resume_refused",`${i.name} could not be ${n?"paused":"resumed"}.`);e.print.result({status:n?"paused":"resumed",...r},a=>n?`Paused ${a.name}. Nothing new syncs until you resume it: closedloop integrations resume ${a.integration_id}`:`Resumed ${a.name}. It syncs again on its usual schedule.`)}var dm=(e,t)=>[...t??[],e],wa=(e,t)=>{let n=e.command("integrations").alias("integration").description("See, connect and disconnect your workspace's integrations, each by its id from the list.");n.command("list").description("List integrations, each with its id: those needing attention, those connected, and those you can connect.").option("--connected","Only the connected ones.").option("--available","Only the ones you can connect.").action(async o=>Jd(t,{connected:o.connected===!0,available:o.available===!0})),n.command("show").argument("<id>","The integration's id, from closedloop integrations list.").description("Show one integration: its status and how to connect it.").action(async o=>Vd(t,o)),n.command("connect").argument("<id>","The integration's id, from closedloop integrations list.").description("Connect an integration: with an API key, in the browser, or with a link to the app.").option("--field <key=value>","A setting the integration needs, e.g. --field subdomain=acme (repeatable).",dm).option("--stdin","Read the API key (and secret, on a second line) from standard input.").option("--reconnect","Connect again even if it is already connected, e.g. with a new key.").option("--history <months>","With an API key: months of history to import, 1, 3 or 6 (3 if left out).").option("--no-history","With an API key: import no older history, only what comes from now on.").option("--in-app","In the browser: connect on the app's connect page instead (for example when the browser cannot reach this computer).").action(async(o,i)=>um(t,o,i)),n.command("pause").argument("<id>","The integration's id, from closedloop integrations list.").description("Stop syncing an integration without disconnecting it.").action(async o=>aa(t,o,!0)),n.command("resume").argument("<id>","The integration's id, from closedloop integrations list.").description("Start syncing a paused integration again.").action(async o=>aa(t,o,!1)),n.command("disconnect").argument("<id>","The integration's id, from closedloop integrations list.").description("Disconnect an integration. Syncing stops; imported data stays.").option("--yes","Skip the confirmation.").action(async(o,i)=>cm(t,o,i.yes===!0))};import{randomUUID as mm}from"node:crypto";var W=Ce,me=re,pm=60,zn="Choose --region us|eu: --region us stores new account data in the United States; --region eu stores it in the European Union.",gm="Choose your data region: United States (US) or European Union (EU) [US]: ",Kt="Run `closedloop login --email <email>` to get a new code.",hm="Run `closedloop login --browser` to sign in through your browser.",fm="Run `closedloop login --browser` to sign in with your password in your browser.",ym="Your company website, e.g. https://acme.example: ",wm=3,_m=`Next, set your company website: ${Z}. ${Ht} Nothing is analysed until it is done.`,bm=["sso_required","password_sign_in"],_a={INVALID_EMAIL:{code:"invalid_email"},DISPOSABLE_EMAIL:{code:"disposable_email"},EMAIL_SEND_FAILED:{code:"email_send_failed",hint:"Run the same command again in a minute."},RATE_LIMIT_EXCEEDED:{code:"rate_limited",hint:"Wait a while, then try again.",keepPending:!0},INVALID_CODE:{code:"invalid_code",hint:"Use the code from the newest ClosedLoop AI email and run the command again.",keepPending:!0},CODE_EXPIRED:{code:"code_expired",hint:Kt},CODE_ALREADY_USED:{code:"code_already_used",hint:Kt},INVALID_CHALLENGE:{code:"code_request_invalid",hint:Kt},TWO_FACTOR_REQUIRED:{code:"two_factor_required",hint:"Run it again with --totp <code from your authenticator app> or --backup-code <code>.",keepPending:!0},INVALID_2FA_CODE:{code:"invalid_two_factor_code",hint:`${Kt} Then use the current code from your authenticator app.`},TWO_FACTOR_SETUP_REQUIRED:{code:"two_factor_setup_required"},SSO_REQUIRED:{code:"sso_required",hint:hm},PASSWORD_SIGN_IN:{code:"password_sign_in",hint:fm},ACCOUNT_LOCKED:{code:"account_locked"},SIGNUP_LIMIT:{code:"signup_limit"},ACCOUNT_IN_OTHER_REGION:{code:"account_in_other_region",hint:"Set CLOSEDLOOP_API_URL to the API of your account's region and run `closedloop login` again."}},ba={"api.closedloop.sh":"eu.api.closedloop.sh","eu.api.closedloop.sh":"api.closedloop.sh"};function Qn(e,t){let n=typeof e.body.code=="string"?e.body.code:"",o=Object.prototype.hasOwnProperty.call(_a,n)?_a[n]:{code:"api_error"},i=typeof e.body.error=="string"&&e.body.error?e.body.error:`ClosedLoop AI could not complete the request (HTTP ${e.status})`;return Object.assign(new p(o.code,i,g.Failure,t??o.hint),{keepPending:o.keepPending===!0})}function fi(){return new p("api_error","ClosedLoop AI sent an unexpected reply",g.Failure)}function yi(e,t,n){return dn(e.runtime,t,o=>{let i=o.read(n),r={request_id:mm()};return o.write(n,r),{intent:r,previous:i}})}function pe(e,t,n,o,i,r=!0){dn(e.runtime,t,s=>{if(JSON.stringify(s.read(n))!==JSON.stringify(o)){if(r)throw ut();return}i===null?s.remove(n):s.write(n,i)})}function Te(e,t,n,o){dn(e.runtime,t,i=>{if(JSON.stringify(i.read(n))!==JSON.stringify(o))throw ut()})}function Ca(e,t){return $o(t,e.runtime.platform)}function ka(e,t,n){return`${Ne(t,e.runtime.platform)} --browser${n!==void 0?` --region ${n}`:""}`}function hi(){return new p("missing_email","a valid email address is required",g.Usage,"Run `closedloop login you@example.com` with your email address.")}function $m(e){try{return sn(e.runtime)}catch(t){throw new p("invalid_api_url",t.message,g.Usage)}}function vm(e,t){if(t!==void 0){if(t!=="us"&&t!=="eu")throw new p("invalid_region","the region must be us or eu",g.Usage,zn);if(e.runtime.env.CLOSEDLOOP_API_URL?.trim())throw new p("conflicting_options","--region cannot be combined with CLOSEDLOOP_API_URL",g.Usage,"Unset CLOSEDLOOP_API_URL to choose a production region, or omit --region to use the configured API.");return t}}var Qe={company:null,website_saved:!1,website_error:null,next:null,note:null},Cm=`Your workspace has no company website yet, so nothing is analysed; an owner or admin sets it with: ${Z}`;async function $a(e,t){try{return{...Qe,company:await Ho(e.runtime,v(e.runtime),t),website_saved:!0}}catch(n){if(!(n instanceof p))throw n;return n.message}}async function km(e,t,n){let{runtime:o,print:i,interactive:r}=e;if(t!==void 0){let u=await $a(e,t);return typeof u=="string"?{...Qe,website_error:u,next:Z}:u}if(!n)return Qe;let s=v(o),a=await Ke(o,s);if(a?.research.state!=="no_website")return Qe;let l=!1;try{l=(await M(o,s)).permissions.can_manage_team_settings}catch(u){if(!(u instanceof p))throw u}if(!l)return{...Qe,company:a,note:Cm};if(!r)return{...Qe,company:a,next:Z};i.note(Ht);for(let u=0;u<wm;u++){let c=(await o.prompt(ym)).trim();if(!c)break;let m=await $a(e,c);if(typeof m!="string")return m;i.note(`That website could not be saved: ${m}`)}return{...Qe,company:a,next:Z}}async function Sm(e,t,n){let o={domains_saved:!1,domains_instructions:null,company:t.company};if(!n||t.note)return o;let i=v(e.runtime),r=t.company;if(!r||!En(r))return{...o,company:r};if(!e.interactive)return{domains_saved:!1,domains_instructions:Gt(r),company:r};try{let s=await Kr(e,i,r);if(s)return{domains_saved:!0,domains_instructions:null,company:s}}catch(s){if(!(s instanceof p))throw s;e.print.note(`The email domains were not saved: ${s.message}`)}return{...o,company:r}}async function Sa(e,t,n,o,i,r){let s=n.body;if(typeof s.accessToken!="string"||typeof s.refreshToken!="string"||typeof s.user?.email!="string")throw fi();let a=s.team&&typeof s.team.name=="string"?{id:String(s.team.id),name:s.team.name}:null,l=r.account,u=l!==null&&s.user.email.trim().toLowerCase()!==l;er(e.runtime,{api_url:t,email:s.user.email,user_id:s.user.id,team:a,region:s.region,access_token:s.accessToken,refresh_token:s.refreshToken},o,yn(s.sign_in_method),r);let c=s.new_account===!0,m=o==="email_code"&&c,d=await km(e,i.website,m),h=await Sm(e,d,m),f={...d,company:h.company},y={status:"signed_in",new_account:c,email:s.user.email,team:a,region:s.region??null,requested_email:l,different_account:u,...f,domains_saved:h.domains_saved,domains_instructions:h.domains_instructions};e.print.result(y,_=>{let S=[_.different_account?`You asked to sign in as ${_.requested_email} but approved as ${_.email}; signed in as ${_.email}${_.team?` (${_.team.name})`:""}.`:_.new_account?`Account created for ${_.email} on the Free plan. You're signed in.`:`Signed in as ${_.email}${_.team?` (${_.team.name})`:""}.`];return _.website_error&&S.push(`The website was not saved: ${_.website_error}`),_.website_saved&&S.push(i.website===void 0?"Website saved.":`Website saved. ${Ht}`),_.domains_saved&&S.push("Email domains saved."),(_.website_saved||_.domains_saved)&&_.company?S.push(Mo(_.company,Date.now())):_.note?S.push(_.note):_.next&&S.push(_m),S.join(`
139
+ `)})}function Oa(e,t){if(e!==X.us&&e!==X.eu||typeof t!="string")return null;try{let n=new URL(e).hostname,o=Object.prototype.hasOwnProperty.call(ba,n)?ba[n]:null,i=new URL(t);return i.protocol!=="https:"||i.hostname!==o||i.port!==""||i.username!==""||i.password!==""?null:i.origin}catch{return null}}function Aa(e,t){return e.status!==409||e.body.code!=="ACCOUNT_IN_OTHER_REGION"?null:Oa(t,e.body.api_url)}async function Om(e,t,n){let o=Ge(e.runtime),i=o.last_email===t&&o.last_region==="eu"?["eu","us"]:["us","eu"],r=null;for(let s of i){let a=await qe(e.runtime,X[s],"/api/check-email",{email:t});if(n(),a.status!==200||a.body.success!==!0||typeof a.body.exists!="boolean")throw new p("region_check_failed","ClosedLoop AI could not check where this account belongs",g.Failure,`Try again. ${zn}`);let l=a.body.ssoRequired===!0;if(a.body.exists){let u=Array.isArray(a.body.authMethods)?a.body.authMethods:[];return{kind:"account",region:s,browser:l||u.includes("password")}}l&&r===null&&(r=s)}return r!==null?{kind:"company_sign_in",region:r}:{kind:"none"}}async function Am(e,t){if(!e.interactive)throw new p("region_required","choose where the new account's data should live before signing up",g.Usage,zn);let n=(await e.runtime.prompt(gm)).trim().toLowerCase();t();let o=n===""?"us":n;if(o!=="us"&&o!=="eu")throw new p("invalid_region","the region must be us or eu",g.Usage,zn);return o}async function Tm(e,t,n,o,i){let r=i??yi(e,n,W);try{Te(e,n,W,r.intent);let s=t,a=await qe(e.runtime,s,"/api/cli/auth/code",{email:n});Te(e,n,W,r.intent);let l=Aa(a,t);if(l!==null){let d=typeof a.body.region=="string"?a.body.region.toUpperCase():"other";e.print.note(`Your ClosedLoop AI account is in the ${d} region; signing in there.`),s=l,mn(e.runtime,n,He(s)),a=await qe(e.runtime,s,"/api/cli/auth/code",{email:n}),Te(e,n,W,r.intent)}if(a.status!==200)throw Object.assign(Qn(a),{api:s});let{challenge:u,expires_in:c}=a.body;if(typeof u!="string"||typeof c!="number")throw fi();let m={api_url:s,configured_api_url:t,email:n,challenge:u,...o!==void 0&&{requested_region:o},expires_at:new Date((e.runtime.now?.()??Date.now())+c*1e3).toISOString()};return pe(e,n,W,r.intent,m),m}catch(s){throw pe(e,n,W,r.intent,r.previous,!1),s}}function Rm(e,t,n){let o=bo(e.runtime,t,W);if(o===null||typeof o!="object"||o.email!==t||!n.includes(o.configured_api_url??o.api_url)||typeof o.challenge!="string"||!o.challenge.trim())throw new p("no_code_requested",`no code was requested for ${t} on this computer`,g.Failure,`Run \`closedloop login --email ${Ca(e,t)}\` first, then use the code from the email.`);let i=typeof o.expires_at=="string"?Date.parse(o.expires_at):NaN;if(!Number.isFinite(i)||i<=(e.runtime.now?.()??Date.now()))throw pe(e,t,W,o,null,!1),new p("code_expired","the saved sign-in code request has expired",g.Failure,Kt);return o}async function va(e,t,n,o){return qe(e.runtime,t.api_url,"/api/cli/auth/verify",{challenge:t.challenge,code:n.trim(),...o.totp&&{totpCode:o.totp.trim()},...o.backupCode&&{backupCode:o.backupCode.trim()}})}async function Ta(e,t,n,o){let{runtime:i,interactive:r}=e,s=await va(e,t,n,o);if(Te(e,t.email,W,t),r&&s.status===401&&s.body.code==="TWO_FACTOR_REQUIRED"){let a=await i.promptSecret("Code from your authenticator app: ");s=await va(e,t,n,{...o,totp:a}),Te(e,t.email,W,t)}if(s.status!==200){let a=Object.assign(Qn(s),{api:t.api_url});throw a.keepPending||pe(e,t.email,W,t,null,!1),a}await Sa(e,t.api_url,s,"email_code",o,{account:t.email,file:W,value:t})}async function Ra(e,t,n,o,i){try{await i()}catch(r){if(!(r instanceof p)||!bm.includes(r.code))throw r;let s=r.code==="sso_required";if(!e.interactive)throw new p(r.code,r.message,r.exitCode,`Run \`${ka(e,t,n.region)}\` ${s?"to sign in through your browser":"to sign in with your password in your browser"}.`);e.print.note(`${s?"Your workspace signs in with SSO":"This account signs in with its password"}, so you sign in through your browser.`),await Xn(e,r.api??o,n,t)}}async function Ea(e,t,n,o,i,r){let{runtime:s,print:a,interactive:l}=e;await Ra(e,n,o,t,async()=>{let u=await Tm(e,t,n,i,r),c=o.website!==void 0?` --website ${he(o.website.trim(),s.platform)}`:"",m=u.requested_region!==void 0?` --region ${u.requested_region}`:"",d={status:"code_sent",email:n,region:He(u.api_url),expires_at:u.expires_at,next:`closedloop login --email ${Ca(e,n)} --code <code>${m}${c}`};if(!l){a.result(d,h=>`We sent a sign-in code to ${h.email}. Finish with: ${h.next}`);return}a.result(d,h=>`We sent a sign-in code to ${h.email}.`),await Ta(e,u,await s.prompt("Code from the email: "),o)})}function Em(e,t,n){let o=bo(e.runtime,t,me);if(o===null||typeof o!="object"||(o.configured_api_url??o.api_url)!==n||typeof o.challenge!="string"||!o.challenge.trim()||typeof o.device_secret!="string"||!o.device_secret.trim()||typeof o.user_code!="string"||!o.user_code.trim()||typeof o.url!="string"||typeof o.interval!="number"||!Number.isFinite(o.interval)||o.interval<1)return null;let i=o.regional_hops??0;if(i!==0&&i!==1||i===0&&o.api_url!==n||i===1&&(o.api_url===n||Oa(n,o.api_url)!==o.api_url))return null;let r=typeof o.expires_at=="string"?Date.parse(o.expires_at):NaN;return!Number.isFinite(r)||r<=(e.runtime.now?.()??Date.now())?null:o}async function Im(e,t,n){let o=yi(e,t,me);try{let i=await qe(e.runtime,n,"/api/cli/auth/device/start",t!==null?{email:t}:{});if(Te(e,t,me,o.intent),i.status!==200)throw Qn(i);let r=i.body;if(typeof r.challenge!="string"||typeof r.device_secret!="string"||typeof r.user_code!="string"||typeof r.verification_url!="string"||typeof r.expires_in!="number")throw fi();let s={api_url:n,configured_api_url:n,regional_hops:0,challenge:r.challenge,device_secret:r.device_secret,user_code:r.user_code,url:r.verification_url,interval:typeof r.interval=="number"&&r.interval>=1?r.interval:3,expires_at:new Date((e.runtime.now?.()??Date.now())+r.expires_in*1e3).toISOString()};return pe(e,t,me,o.intent,s),s}catch(i){throw pe(e,t,me,o.intent,o.previous,!1),i}}function Ia(e,t,n,o){throw pe(e,t,me,n,null,!1),new p("invalid_region_handoff","ClosedLoop AI returned an invalid regional sign-in response",g.Failure,o)}function xm(e,t,n,o,i){let r=Aa(o,n.api_url),s=o.body,a=e.runtime.now?.()??Date.now(),l=Date.parse(n.expires_at);(r===null||(n.regional_hops??0)!==0||s.region!==(r===X.eu?"eu":"us")||typeof s.challenge!="string"||!s.challenge.trim()||s.challenge===n.challenge||typeof s.expires_in!="number"||!Number.isFinite(s.expires_in)||s.expires_in<=0||typeof s.interval!="number"||!Number.isFinite(s.interval)||s.interval<1||!Number.isFinite(l)||l<=a)&&Ia(e,t,n,i);let u={...n,configured_api_url:n.configured_api_url??n.api_url,api_url:r,challenge:s.challenge,regional_hops:1,interval:s.interval,expires_at:new Date(Math.min(l,a+s.expires_in*1e3)).toISOString()};return pe(e,t,me,n,u),t!==null&&mn(e.runtime,t,He(r)),u}async function Pm(e,t,n,o,i){let r=Math.max(1,Math.ceil(o/n.interval)),s=n,a=()=>qe(e.runtime,s.api_url,"/api/cli/auth/device/token",{challenge:s.challenge,deviceSecret:s.device_secret});for(let l=0;l<r;l++){let u=await a();if(Te(e,t,me,s),u.status===409&&u.body.code==="ACCOUNT_IN_OTHER_REGION"&&(s=xm(e,t,s,u,i),u=await a(),Te(e,t,me,s),u.status===409&&u.body.code==="ACCOUNT_IN_OTHER_REGION"&&Ia(e,t,s,i)),u.status===200&&u.body.status==="signed_in")return{reply:u,pending:s};if(u.status!==200||u.body.status!=="pending"){let c=Qn(u,i);throw c.keepPending||pe(e,t,me,s,null,!1),c}if(l<r-1){let c=Math.max(0,Date.parse(s.expires_at)-(e.runtime.now?.()??Date.now()));await e.runtime.sleep(Math.min(s.interval*1e3,c))}}return{reply:null,pending:s}}async function Xn(e,t,n,o){let{print:i,interactive:r}=e,s=ka(e,o,n.region),a=`${s}${n.website!==void 0?` --website ${he(n.website.trim(),e.runtime.platform)}`:""}`,l=r?null:Em(e,o,t),u=l??await Im(e,o,t),c={status:"approval_needed",email:o,region:He(u.api_url),url:u.url,user_code:u.user_code,expires_at:u.expires_at,next:`Open the link, enter the code, approve; then run \`${a}\` again.`};if(!r&&l===null){i.result(c,y=>`Open ${y.url} , enter the code ${y.user_code}, and approve. Then run: ${a}`);return}let m=()=>{};if(r){let{runtime:y}=e,_=await y.copyToClipboard(u.user_code);i.result(c,S=>["Open this link, sign in, and enter this code:",` ${ke(S.url,y.supportsHyperlinks)}`,` ${S.user_code}${_?" (copied to your clipboard)":""}`,"Press Enter to open the link in your browser. Waiting for your approval\u2026"].join(`
140
+ `)),m=y.onEnter(()=>{y.openUrl(u.url)})}let d=Math.max(0,(Date.parse(u.expires_at)-(e.runtime.now?.()??Date.now()))/1e3),h,f=r?e.print.spinner("Waiting for your approval in the browser\u2026"):null;try{let y=await Pm(e,o,u,r?d:Math.min(d,pm),`Run \`${s}\` to start again.`);h=y.reply,u=y.pending}finally{f?.stop(),m()}if(h===null)throw new p("approval_pending","the sign-in has not been approved in the browser yet",g.Failure,`Open ${u.url} , enter the code ${u.user_code} and approve, then run \`${s}\` again.`);await Sa(e,u.api_url,h,"browser",n,{account:o,file:me,value:u})}async function Nm(e,t,n,o){if(t!==void 0&&n.email!==void 0&&lt(t)!==lt(n.email))throw new p("conflicting_options","give the email once: as the argument or with --email",g.Usage,"Run `closedloop login you@example.com`.");let i=t??n.email;if(i!==void 0){let a=lt(i);if(a===null)throw hi();return a}let r=lt(Ge(e.runtime).last_email);if(r!==null)return r;if(o)return null;if(!e.interactive)throw hi();let s=lt(await e.runtime.prompt("Email: "));if(s===null)throw hi();return s}async function Lm(e,t,n,o,i){let r=yi(e,t,W),s=()=>Te(e,t,W,r.intent),a,l;try{a=await Om(e,t,s),a.kind==="none"&&!o&&(l=await Am(e,s))}catch(m){throw pe(e,t,W,r.intent,r.previous,!1),m}a.kind==="account"&&(mn(e.runtime,t,a.region),e.print.note(`Signing in as ${t} (${a.region.toUpperCase()})`));let u=a.kind!=="none"?X[a.region]:l!==void 0?X[l]:i;if(!(o||a.kind==="company_sign_in"||a.kind==="account"&&a.browser)){await Ea(e,u,t,n,l,r);return}pe(e,t,W,r.intent,r.previous),await Xn(e,u,n,t)}async function Dm(e,t,n){if(n.website!==void 0&&!n.website.trim())throw new p("invalid_website","--website needs the company website, e.g. https://acme.example",g.Usage);let o=vm(e,n.region),i=n.browser===!0||n.sso===!0;if(i&&n.code!==void 0)throw new p("conflicting_options","--browser signs in through the browser and takes no --code",g.Usage,"Run `closedloop login <email> --browser` without --code.");let r=o!==void 0?X[o]:$m(e),s=o!==void 0||!!e.runtime.env.CLOSEDLOOP_API_URL?.trim(),a=await Nm(e,t,n,i);if(a===null){await Xn(e,r,n,null);return}if(n.code!==void 0){let l=n.code,u=s?[r]:[X.us,X.eu];await Ra(e,a,n,r,()=>Ta(e,Rm(e,a,u),l,n));return}if(!s){await Lm(e,a,n,i,r);return}i?await Xn(e,r,n,a):await Ea(e,r,a,n,o)}var xa=(e,t)=>{e.command("login").alias("signup").description("Sign in, or create an account, with your email. The CLI finds your account and picks how you sign in.").argument("[email]","Your email address. Left out, the CLI uses the one it signed in with last.").option("--email <email>","Your email address, instead of the argument.").option("--code <code>","The code from the email, like BCDFG-HJKLM (finishes a sign-in started without it).").option("--totp <code>","The code from your authenticator app, if your account uses two-factor authentication.").option("--backup-code <code>","A two-factor backup code, instead of --totp.").option("--browser","Sign in through your browser (company sign-in or password).").option("--sso","The same as --browser.").option("--region <region>","New account region, or where a browser sign-in starts: us or eu. Existing accounts sign in in their home region.").option("--website <url>","Your company website, set right after you sign in (ClosedLoop AI researches it before analysing anything).").action(async(n,o)=>{await Dm(t,n,o)})};function Zn(e){try{return e()}catch(t){if(!(t instanceof p)||t.code!=="config_invalid")throw t;return null}}function Pa(e,t){let n=({active_account:o,...i})=>JSON.stringify({...i,active:o??null});n(t)!==n(e.settings())&&e.setSettings(t)}function jm(e){return Y(e,{accounts:xt(e),settings:!0},t=>{let n=[];for(let r of t.emails())ct(e,Zn(()=>t.read(r,j)),r)!==null&&n.push(r),t.forget(r);let{pending_browser_login:o,...i}=t.settings();return Pa(t,{...i,active_account:null}),t.removeLeftovers(),n})}function Fm(e,t){let n=[...new Set([...xt(e),...t!==null?[t]:[]])];return Y(e,{accounts:n,settings:!0},o=>{let i=null,r=[];for(let l of o.emails()){let u=ct(e,Zn(()=>o.read(l,j)),l);l===t?(i=u,o.forget(l)):u!==null?r.push(l):(Zn(()=>o.read(l,Ce))!==null||Zn(()=>o.read(l,re))!==null)&&(o.remove(l,Ce),o.remove(l,re))}let{pending_browser_login:s,...a}=o.settings();return Pa(o,{...a,...a.active_account===t&&{active_account:null}}),o.removeLeftovers(),{session:i,others:r}})}var Na=(e,{print:t,runtime:n})=>{e.command("logout").description("Sign out the active account on this computer. Your other sessions stay signed in.").option("--all","Sign out every account on this computer.").action(o=>{if(o.all){let s=jm(n);t.result({status:s.length>0?"signed_out":"not_signed_in",emails:s},a=>a.emails.length>0?`Signed out ${a.emails.join(", ")} on this computer.`:"You were not signed in on this computer.");return}let{session:i,others:r}=Fm(n,Pe(n).email);if(i===null){t.result({status:"not_signed_in"},()=>"You were not signed in on this computer.");return}t.result({status:"signed_out",email:i.email,other_accounts:r},s=>[`Signed out ${s.email} on this computer.`,...s.other_accounts.length>0?[`Still signed in here: ${s.other_accounts.join(", ")}. Switch with: closedloop use <email>`]:[]].join(`
141
+ `))})};import{existsSync as Gm}from"node:fs";var zt='A rule is JSON: {"of":"customer","where":{"field":"customer.country","op":"in","value":["United States"]}}; a saved segment is included by its name, {"segment":"Groups"}; see closedloop fields list',La="<file|json|->";async function Da(e,t){if(t===void 0)throw new p("missing_rule","give the rule with --rule <file|json|->",g.Usage,zt);return Fe(e,t,"rule",zt)}function Wm(e,t,n,o){let i=typeof t.entity=="string"?t.entity:n.of,r=typeof t.count=="number"?t.count:0,s=[`${k(r)} ${G(i,r)} ${r===1?"matches":"match"}.`],a=Array.isArray(t.steps)?t.steps.filter(w):[],l=a.length&&typeof t.total=="number"?[{condition:null,count:t.total},...a]:a;if(l.length){let f=l.map(b=>b.condition===null||b.condition===void 0?`all ${G(i)}`:Ft(b.condition)),y=l.map(b=>typeof b.count=="number"?k(b.count):"?"),_=Math.min(Math.max(...f.map(b=>b.length)),e.width-16),S=Math.max(...y.map(b=>b.length));s.push("",...f.map((b,U)=>` ${Mn(b,_).padEnd(_)} ${y[U].padStart(S)}`))}let c=(Array.isArray(t.examples)?t.examples.filter(w):[]).map(f=>[f.name,f.label,f.title,f.customer_id,f.id].find(y=>typeof y=="string")).filter(Boolean);(c.length||typeof t.insights_in_scope=="number")&&s.push(""),c.length&&s.push(`Examples: ${c.join(", ")}`),typeof t.insights_in_scope=="number"&&s.push(`${k(t.insights_in_scope)} insight${t.insights_in_scope===1?"":"s"} in scope.`);let m=Array.isArray(t.warnings)?t.warnings:[],d=Ut(t.stale);(m.length||d)&&s.push("",...d?[e.orange(`Warning: ${d}.`)]:[],...m.map(f=>e.orange(`Warning: ${kn(f)}`)));let h=On(e,t.alternatives);return h.length&&s.push("",...h),s.push("",e.dim(o)),s.join(`
142
+ `)}async function Um(e,t){let n=await Da(e,t),o=await hr(e,n),i=Sn(o.alternatives,o.count),r={...o,rule:n,...i?{ask:i}:{},next:`closedloop segments define <name> --rule ${La}`};e.print.result(r,s=>Wm(e.print.style,s,n,"Save it: closedloop segments define <name> --rule <the same rule>"))}function wi(e){let t=w(e)&&Array.isArray(e.includes)?e.includes.filter(w):[];return t.length?t.map(n=>{let o=n.newer_version===!0&&typeof n.current_version=="number"?`; version ${n.current_version} is newer`:"",i=n.archived===!0?"; archived":"";return`${String(n.name)} (version ${String(n.version)}${o}${i})`}).join(", "):null}async function Hm(e,t,n){let o=await Da(e,n.rule),i=await fr(e,t,o,n.description),r=String(i.id),s=fe(i),a={segment:i,rule:w(s.rule)?s.rule:o,next:`closedloop metrics preview --segment ${r} --calc count`};e.print.result(a,l=>{let u=typeof i.entity=="string"?i.entity:l.rule.of,c=je(s),m=c===null?"":`version ${c}, `,d=wi(s);return`Saved the segment "${String(i.name??t)}" (${m}${G(u)}).${d?`
143
+ It includes ${d}.`:""}
144
+ ${e.print.style.dim(`Measure it: ${l.next}`)}`})}async function Mm(e){let n={segments:await wr(e),next:"closedloop metrics define <name> --segment <id> --calc <calc>"};e.print.result(n,o=>{let i=e.print.style;if(!o.segments.length)return`No segments yet. Define one: closedloop segments define <name> --rule ${La}`;let r=" ",s=new Map;for(let c of o.segments){let m=fe(c).includes;for(let d of Array.isArray(m)?m.filter(w):[]){let h=String(d.segment_id);s.set(h,[...s.get(h)??[],String(c.name)])}}let a=o.segments.map(c=>{let m=fe(c),d=je(m),h=d===null?"":` \xB7 version ${d}`,f=wi(m),y=s.get(String(c.id))??[];return[`${i.bold(String(c.name))}${i.dim(` \xB7 ${G(c.entity)}${h}`)}`,`${r}${Nr(m.rule,Pr(m))}`,...f?[`${r}includes ${f}`]:[],...y.length?[`${r}included in ${y.join(", ")}`]:[],R(i,r,c.id)].join(`
145
+ `)}),u=o.segments.some(c=>/is newer/.test(wi(fe(c))??""))?`
146
+ ${i.dim("A segment keeps the version it included; save its rule again to use the newer one.")}`:"";return`${a.join(`
147
+
148
+ `)}${u}
149
+
150
+ ${i.dim("Measure one: closedloop metrics preview --segment <id> --calc count")}`})}var ja=(e,t)=>{let n=e.command("segments").alias("segment").description("Who is in scope: customers, deals, people, insights, context or conversations, described as a rule over named fields."),o=`The rule: a JSON file, inline JSON, or - for standard input. ${zt}.`;n.command("define").argument("<name>",`The segment's name, e.g. "paying US hotels". Other rules include it by this name.`).description("Save a rule as a named segment (version 1). A segment in use is never changed in place: a change makes a new version.").option("--rule <rule>",o).option("--description <text>","What the segment is for.").action(async(i,r)=>Hm(t,i,r)),n.command("preview").description("Count a rule without saving it: how many match, how many are left after each condition, examples, insights in scope and warnings.").option("--rule <rule>",o).action(async i=>Um(t,i.rule)),n.command("list").description("Your workspace's segments, each with its rule in words, the segments it includes, the ones that include it, and its id.").action(async()=>Mm(t))};var _i="closedloop metrics list",Ua="closedloop segments list",Xt='A calculation is count, or JSON such as {"fn":"sum","field":"customer.open_pipeline"} or {"fn":"ratio","numerator":{"fn":"count","where":{...}},"denominator":{"fn":"count"}}',te=(e,t,n)=>new p(e,t,g.Usage,n),qm=e=>/[\\/]/.test(e)||/\.[a-z0-9]{1,8}$/i.test(e),Jm=e=>Ko(ms(e),t=>t.text);async function Vm(e,t){let n=t.trim();if(ir(n))return n.toLowerCase();if(n==="-"||/^[{[]/.test(n)||jt(n)||Gm(n)||qm(n))return Fe(e,t,"segment",zt);throw new p("invalid_id",`'${t}' is not a segment id, and not a rule (a JSON file, inline JSON or -)`,g.Usage,L(Ua))}async function Ha(e,t){if(t.formula!==void 0){if(t.calc!==void 0)throw te("formula_and_calc","give --formula or --calc, not both",qt);if(t.segment!==void 0)throw te("formula_has_no_segment","a formula has no segment: it combines saved metrics, each over its own",qt);return{segment:void 0,calc:Jm(t.formula)}}if(t.segment===void 0&&t.calc!==void 0&&!/^[a-z_0-9]+$/i.test(t.calc.trim())){let r=await Fe(e,t.calc,"calc",Xt);if(ye(r))return{segment:void 0,calc:r}}if(t.segment===void 0)throw te("missing_segment","give the segment with --segment <id|file|json|->",L(Ua));if(t.calc===void 0)throw te("missing_calc","give the calculation with --calc <calc>",Xt);if(t.segment.trim()==="-"&&t.calc.trim()==="-")throw te("stdin_twice","only one of --segment and --calc can come from standard input");let n=await Vm(e,t.segment),o=t.calc.trim(),i;if(o==="count")i={fn:"count"};else{if(/^[a-z_0-9]+$/i.test(o))throw te("invalid_calc",`'${o}' is not a calculation`,Xt);i=await Fe(e,o,"calc",Xt)}return{segment:n,calc:i}}var Ma=async(e,t)=>t===void 0?null:typeof t!="string"?t:yr(e,t),eo=e=>String(e).replace(/\s*\(?\b(failed|not_measured):\s*[a-z_]+\)?/g,"").trim();function Ga(e){let t={measured:"measured",rebuilt:"rebuilt from the history",not_available:"no value"};return(Array.isArray(e)?e.filter(w):[]).map(n=>{let o=typeof n.name=="string"?n.name:String(n.metric),i=typeof n.days_ago=="number"&&n.days_ago>0?` ${n.days_ago} days earlier (${Re(n.as_of)})`:"",r=typeof n.basis=="string"&&Object.prototype.hasOwnProperty.call(t,n.basis)?t[n.basis]:String(n.basis),s=typeof n.value=="number"?A(n.value):`no value${typeof n.reason=="string"&&eo(n.reason)?` (${eo(n.reason)})`:""}`;return` ${o}${i}: ${s}${typeof n.value=="number"?`, ${r}`:""}`})}async function Bm(e,t,n){let o=await xo(e,void 0,t),i=n.formula!==void 0?`--formula ${he(n.formula,e.runtime.platform)}`:"--calc <the same formula>",r={...o,calc:w(o.calc)?o.calc:t,next:`closedloop metrics define <name> ${i}`};e.print.result(r,s=>{let a=e.print.style,l=Ae(s.calc,s.value,s.numerator,s.denominator),u=[l!==null?`${l} on ${Re(s.as_of)}.`:`No value: ${s.reason===void 0||s.reason===null?"an input has no value":eo(s.reason)}.`,...Ga(s.inputs)],c=Ut(s.stale);return c&&u.push(a.orange(`Warning: ${c}.`)),u.push("",a.dim(`Save it: ${s.next}`)),u.join(`
151
+ `)})}async function Ym(e,t){let{segment:n,calc:o}=await Ha(e,t);if(n===void 0)return Bm(e,o,t);let i=await xo(e,n,o),r=await Ma(e,n),s=typeof n=="string"?n:"<the same segment>",a=Sn(i.alternatives,i.value),l={...i,segment:n,rule:r,calc:o,...a?{ask:a}:{},next:`closedloop metrics define <name> --segment ${s} --calc <calc>`};e.print.result(l,u=>{let c=e.print.style,m=typeof u.member_count=="number"?u.member_count:null,d=G(typeof u.entity=="string"?u.entity:u.rule?.of,m??2),h=m===null?"":u.calc.fn==="count"&&u.value===m?` ${d}`:` over ${k(m)} ${d}`,f=Be(u.calc,u.value,u.numerator,u.denominator),y=[f!==null?`${f}${h}.`:u.calc.fn==="ratio"?"No value: there is nothing to divide by (the denominator is 0).":`No value${h}: none of them has one.`],_=typeof u.missing_count=="number"?u.missing_count:0;_>0&&m!==null&&typeof u.calc.field=="string"&&y.push(`${k(_)} of ${k(m)} have no value for ${u.calc.field}, so they are left out.`);let S=Array.isArray(u.warnings)?u.warnings:[],b=Ut(u.stale);return b&&y.push(c.orange(`Warning: ${b}.`)),y.push(...S.map(U=>c.orange(`Warning: ${kn(U)}`))),y.push(...On(c,u.alternatives)),y.push("",c.dim(`Save it: closedloop metrics define <name> --segment ${s} --calc <the same calc>`)),y.join(`
152
+ `)})}async function Km(e,t,n){let{segment:o,calc:i}=await Ha(e,n),r=await _r(e,t,o,i,n.description),s=String(r.id),a=fe(r),l={metric:r,rule:await Ma(e,o),calc:w(a.calc)?a.calc:i,next:`closedloop goals metrics add <goal> --metric ${s} --period month`};e.print.result(l,u=>{let c=w(r.segment)&&typeof r.segment.name=="string"?r.segment.name:null,m=o===void 0?", each metric it uses pinned at its current version":typeof o=="string"?"":c?` with its own segment "${c}"`:" with its own segment, named after it",d=je(a),h=d===null?"":` (version ${d})`;return`Saved the metric "${String(r.name??t)}"${h}${m}.
153
+ `+e.print.style.dim(`It is measured every night. Measure a goal on it: ${u.next}`)})}function zm(e){let t=w(e.sources_fresh_as_of)?e.sources_fresh_as_of:{};return(typeof e.reason=="string"?e.reason.replace(/^stale:\s*/,"").split(/,\s*/).filter(Boolean):[]).map(i=>typeof t[i]=="string"&&!Number.isNaN(Date.parse(t[i]))?`${Cn(i)} last updated ${N(t[i])}`:`${Cn(i)} late`).join(", ")}function Xm(e,t,n,o={}){let i=e.print.style;if(!w(n))return"not measured yet";let r=typeof n.as_of=="string"?/^\d{4}-\d{2}-\d{2}$/.test(n.as_of)?V(`${n.as_of}T00:00:00Z`):N(n.as_of):"an unknown day",s=typeof n.reason=="string"?n.reason.replace(/^(stale|failed|not_measured):\s*/,""):"",a=s?`: ${s}`:"";if(n.status==="not_measured"||n.status==="failed")return`no value on ${r}`;let l=typeof n.version=="number"?n.version:null,u=je(o),c=l!==null&&u!==null&&l!==u,d=`${(c?typeof n.value=="number"?A(n.value):null:ye(t)?Ae(t,n.value,n.numerator,n.denominator):Be(t,n.value,n.numerator,n.denominator))??"no value"}${c?` (version ${l})`:""}`,h=typeof n.basis=="string"?` \xB7 ${n.basis}`:"";if(n.status!=="stale")return`${d} on ${r}${h}`;let f=zm(n);return`${d} on ${r}${h} \xB7 ${i.orange(`stale${f?`: ${f}`:a}`)}`}async function Qm(e){let n={metrics:await br(e),next:"closedloop goals metrics add <goal> --metric <id> --period month"};e.print.result(n,o=>{let i=e.print.style;if(!o.metrics.length)return"No metrics defined yet. Define one: closedloop metrics define <name> --segment <segment> --calc <calc>";let r=" ",s=new Map(o.metrics.map(l=>[String(l.id),String(l.name)]));return`${o.metrics.map(l=>{let u=fe(l),c=je(u),m=c===null?"":` \xB7 version ${c}`,d=typeof l.goals_using=="number"&&l.goals_using>0?l.goals_using:0,h=d?` \xB7 measured by ${d} goal metric${d===1?"":"s"}`:"";return[`${i.bold(String(l.name))}${i.dim(`${m}${h}`)}`,`${r}${ye(u.calc)?`formula: ${Pn(u.calc,s)}`:$n(u.calc)}`,`${r}${Xm(e,u.calc,l.latest_run,u)}`,R(i,r,l.id)].join(`
154
+ `)}).join(`
155
+
156
+ `)}
157
+
158
+ ${i.dim("Measure a goal on one: closedloop goals metrics add <goal> --metric <id> --period month")}
159
+ `+i.dim("Delete one: closedloop metrics delete <id>")})}var to=/^\d{4}-\d{2}-\d{2}$/,Re=e=>typeof e=="string"&&to.test(e)?V(`${e}T00:00:00Z`):String(e),Fa={measured:"measured that night",rebuilt:"computed now from the history"};async function Zm(e,t,n,o){let i=I(t,"metric",_i);if(n===void 0||!to.test(n))throw te("invalid_day","--as-of must be a day before today, YYYY-MM-DD");if(o!==void 0&&!/^[1-9][0-9]*$/.test(o))throw te("invalid_version","--metric-version must be a version number");let r=await Po(e,i,n,o===void 0?void 0:Number(o)),a=typeof r.member_count=="number"&&r.value===r.member_count&&r.numerator===null&&!Array.isArray(r.inputs)&&typeof r.version=="number"?await vr(e,i,r.version):null,l={...r,...a?{calc:a}:{},next:"closedloop metrics list"};e.print.result(l,u=>{let c=e.print.style,m=Re(u.as_of);if(u.basis==="not_available")return`No value on ${m}: ${String(u.reason??"there is no history to read it from")}.`;let d=Array.isArray(u.inputs),h=(d?Ae({op:u.denominator===null?null:"divide"},u.value,u.numerator,u.denominator):null)??(typeof u.value=="number"?A(u.value):"no value"),f=w(u.calc)&&u.calc.fn==="count"&&u.member_count===u.value,y=typeof u.member_count=="number"&&!f?` over ${k(u.member_count)}`:"",_=d&&u.basis==="rebuilt"?"worked out now from the values of the metrics it uses":typeof u.basis=="string"&&Object.prototype.hasOwnProperty.call(Fa,u.basis)?Fa[u.basis]:String(u.basis),S=[`${h}${y} on ${m} (version ${String(u.version)}), ${_}.`,...d?Ga(u.inputs):[]];return u.basis==="rebuilt"&&!d&&S.push(c.dim("Read from the CRM history and the dated records; it is not stored.")),S.join(`
160
+ `)})}var ep=(e,t)=>new Date(Date.parse(`${e}T00:00:00Z`)-t*864e5).toISOString().slice(0,10),St=e=>typeof e=="number"&&Number.isFinite(e)?e:null,Wa={rate_limited:"too many requests for now",preview_busy:"the workspace is busy with other previews",preview_timeout:"working out past values takes too long on this workspace",internal_error:"the API did not answer",api_error:"the API did not answer",api_unreachable:"the API did not answer"};async function tp(e,t,n){let o=n.days??"30";if(!/^[1-9][0-9]*$/.test(o))throw te("invalid_days","--days must be a whole number of days, 1 or more");let i=n.every??"day";if(i!=="day"&&i!=="week")throw te("invalid_step","--every must be day or week");if(n.metricVersion!==void 0&&!/^[1-9][0-9]*$/.test(n.metricVersion))throw te("invalid_version","--metric-version must be a version number");let r=I(t,"metric",_i),s=await Cr(e,r),a=s.metric,l=s.versions.filter(w),u=n.metricVersion===void 0?je(fe(a)):Number(n.metricVersion),c=l.find(O=>O.version===u);if(!c||typeof c.id!="string")throw te("unknown_version",`this metric has no version ${String(u)}`,"See its versions: closedloop metrics list");let m=typeof s.today=="string"&&to.test(s.today)?s.today:null;if(!m)throw new p("api_error","The API did not say which day it is. Try again.",g.Failure);let d=i==="week"?7:1,h=new Map,f=null;for(let O of s.runs.filter(w))O.version_id!==c.id||O.basis!=="measured"||typeof O.as_of!="string"||(O.status==="ok"||O.status==="stale"?h.set(O.as_of,O):O.as_of===m&&(f=O));let y=[],_=null,S=null,b=0;for(let O=0;O<Number(o);O+=d){let oe=ep(m,O),ie=h.get(oe);if(ie){y.push({day:oe,value:St(ie.value),numerator:St(ie.numerator),denominator:St(ie.denominator),basis:"measured",status:String(ie.status),reason:typeof ie.reason=="string"?ie.reason:null});continue}if(oe===m){y.push(f?{day:oe,value:null,numerator:null,denominator:null,basis:"measured",status:String(f.status),reason:typeof f.reason=="string"?f.reason:null}:{day:oe,value:null,numerator:null,denominator:null,basis:"not_measured",status:null,reason:"the next nightly run measures it"});continue}if(_)continue;if(S){b+=1;continue}let H;try{H=await Po(e,r,oe,u??void 0)}catch(tt){if(!(tt instanceof p)||!Object.prototype.hasOwnProperty.call(Wa,tt.code))throw tt;S={day:oe,reason:Wa[tt.code]},b+=1;continue}y.push({day:oe,value:St(H.value),numerator:St(H.numerator),denominator:St(H.denominator),basis:String(H.basis),status:typeof H.status=="string"?H.status:null,reason:typeof H.reason=="string"?H.reason:null}),H.basis==="not_available"&&typeof H.history_from=="string"&&to.test(H.history_from)&&(_=H.history_from)}y.reverse();let U=y.filter(O=>O.value!==null),ne=U[0],J=U[U.length-1],At=ne&&J&&ne!==J?{from:{day:ne.day,value:ne.value},to:{day:J.day,value:J.value},delta:J.value-ne.value}:null,et=c.calc,oo={metric_id:r,name:a.name,version:u,today:m,time_zone:s.time_zone??null,every:i,days:y,change:At,history_from:_,complete:S===null,...S?{stopped:S,not_fetched:b}:{}};e.print.result(oo,O=>{let oe=e.print.style,ie=(x,_e,nt)=>(ye(et)?Ae(et,x,_e,nt):Be(et,x,_e,nt))??"no value",H=Math.max(...O.days.map(x=>Re(x.day).length),0),tt=x=>x.basis==="measured"?x.status==="stale"?"measured, stale":"measured":"worked out from the history",Tt=[`${String(O.name)} (version ${String(O.version)}), ${O.every==="week"?"weekly":"daily"} over the last ${o} days:`];for(let x of O.days){let _e=x.reason?eo(x.reason):"",nt=x.basis==="not_measured"?`not measured yet (${x.reason})`:x.value===null?`no value${_e?`: ${_e}`:""}`:`${ie(x.value,x.numerator,x.denominator)} (${tt(x)})`;Tt.push(`${Re(x.day).padEnd(H)} ${nt}`)}if(O.change){let x=O.change.delta,_e=x>0?"+":x<0?"-":"",nt=Wt(et)?`${_e}${Math.abs(x*100).toFixed(1)} points`:`${_e}${A(Math.abs(x))}${O.change.from.value?` (${_e}${Math.abs(x/O.change.from.value*100).toFixed(1)}%)`:""}`;Tt.push("",`From ${ie(O.change.from.value,null,null)} on ${Re(O.change.from.day)} to ${ie(O.change.to.value,null,null)} on ${Re(O.change.to.day)}: ${nt}.`)}return O.history_from&&Tt.push(oe.dim(`No values can be worked out before ${Re(O.history_from)}, where the history starts.`)),S&&Tt.push(oe.orange(`Stopped at ${Re(S.day)}: ${S.reason}; ${b} earlier day${b===1?"":"s"} without a measured value ${b===1?"was":"were"} not fetched.`)),Tt.join(`
161
+ `)})}async function np(e,t,n){let o=I(t,"metric",_i);if(!n.yes){if(!e.interactive)throw new p("confirmation_required","deleting a metric needs confirmation",g.Usage,`Run it again with --yes: closedloop metrics delete ${o} --yes`);if((await e.runtime.prompt("Delete this metric? It comes off each of your goals that measures it (a key result takes its indicators); its history stays. Type yes to confirm: ")).trim().toLowerCase()!=="yes"){e.print.result({status:"cancelled",id:o},()=>"Nothing changed.");return}}let i=await $r(e,o,n.reason),r=i.removed.filter(w),s={status:"deleted",id:o,archived_at:w(i.metric)?i.metric.archived_at:null,removed:r};e.print.result(s,a=>{let l=a.removed.map(c=>{let m=Array.isArray(c.kpi_ids)?c.kpi_ids.length:0;return`${String(c.title)} (${m} metric${m===1?"":"s"})`});return`Deleted the metric.${l.length?` It came off ${l.length} goal${l.length===1?"":"s"}: ${l.join(", ")}.`:""} Its history stays.`})}function qa(e,t){let n="The segment: its id (from closedloop segments list), or its rule as a JSON file, inline JSON or - for standard input.",o=`The calculation: count, or JSON (inline, a file, or -). ${Xt}.`,i=`A formula over saved metrics instead of a segment and a calculation. ${qt}`;e.command("define").argument("<name>",`The metric's name, e.g. "share that turned on payments".`).description("Save a calculation over a segment, or a formula over saved metrics, as a named metric (version 1). It is measured every night; a change makes a new version.").option("--segment <segment>",n).option("--calc <calc>",o).option("--formula <formula>",i).option("--description <text>","What the metric shows.").action(async(r,s)=>Km(t,r,s)),e.command("preview").description("Measure a calculation over a segment, or a formula over saved metrics, now without saving it: the value, a share's totals, and how many members have no value.").option("--segment <segment>",n).option("--calc <calc>",o).option("--formula <formula>",i).action(async r=>Ym(t,r)),e.command("list").description("Your workspace's metrics, each with its calculation, segment, latest measured value and id.").action(async()=>Qm(t)),e.command("value").argument("<metric>","The metric's id, from closedloop metrics list.").description("The metric's value on a past day: the number measured that night, else computed from the history; says when it cannot be.").option("--as-of <day>","The day, YYYY-MM-DD, before today.").option("--metric-version <number>","Which version of the metric; the current one by default.").action(async(r,s)=>Zm(t,r,s.asOf,s.metricVersion)),e.command("history").argument("<metric>","The metric's id, from closedloop metrics list.").description("The metric day by day, oldest first: the value measured that night, else worked out from the history, else why there is none; and the change from the first value to the last.").option("--days <n>","How many days back, today included (default 30).").option("--every <step>","day (the default) or week: one value a week, on the same weekday as today.").option("--metric-version <number>","Which version of the metric; the current one by default.").action(async(r,s)=>tp(t,r,s)),e.command("delete").argument("<metric>","The metric's id, from closedloop metrics list.").description("Delete a metric: it comes off each of your goals that measures it (a key result takes its indicators), and its history stays. Refused when a teammate's goal measures it, when it is an active goal's only key result, or when a formula uses it.").option("--reason <text>","Why, kept in each goal's history.").option("--yes","Skip the confirmation.").action(async(r,s)=>np(t,r,s))}var Ja=(e,t)=>{let n=e.command("metrics").alias("metric").description("Metrics: one calculation over a segment, measured every night; a goal measures a saved one with goals metrics add.");qa(n,t),ys(n,t)};var op="closedloop company --domains <domain,...>",ip=7200,Va=30,rp=new Set(["waiting","researching","retrying"]);async function Ze(e){try{return await e()}catch(t){if(t instanceof p&&!["not_signed_in","session_expired","upgrade_required"].includes(t.code))return null;throw t}}async function Qt(e,t,n,o){let i=await $(e,t,"GET",n),r=i.body[o];if(i.status!==200||!Array.isArray(r))throw new p("api_error",`${n} could not be read`,g.Failure);return r}async function Ba(e,t){let[n,o,i,r,s,a,l]=await Promise.all([Ze(()=>Ye(e,t)),Ze(()=>Qt(e,t,"/api/cli/backfills?limit=100","backfills")),Ze(()=>Qt(e,t,"/api/cli/integrations","integrations")),Ze(()=>Qt(e,t,"/api/cli/ingest?limit=100","records")),Ze(()=>gt(e,t)),Ze(()=>Qt(e,t,"/api/user-goals?include=team","goals")),Ze(()=>Qt(e,t,"/api/team-members/all","members"))]),u={company:n,backfills:o,integrations:i,records:r,structure:s,goals:a,members:l};if(Object.values(u).every(c=>c===null))throw new p("status_unavailable","your workspace status could not be checked",g.Failure,"Check your connection and try `closedloop status` again.");return u}var Zt=(e,t)=>`${k(e)} ${t}${e===1?"":"s"}`;function sp(e,t){let n=`${Do} ${Fr}`,o=e.suggested_products.length;return o>0?`${Se} (${k(o)} suggested by your company research). ${n}`:t&&t.research.state!=="ready"?`Suggestions appear once your company research is done: closedloop company --watch. ${n}`:`${Rn}. ${n}`}function Ya(e,t){let{company:n,backfills:o,integrations:i,records:r,structure:s,goals:a,members:l}=e,u=s?s.structure.products:null,c=(o??[]).filter(b=>Le.has(b.state)),m=r?{waiting:r.filter(b=>b.state==="waiting").length,processing:r.filter(b=>b.state==="processing").length}:null,d=i?i.filter(b=>b.connected):null,h=r?r.length>0:null,f=n?.research.state??null,y=n?.email_domains,_=d===null&&h===null?null:(d?.length??0)>0||h===!0,S=[{step:"website",title:"Company website",optional:!1,done:n?n.website!==null:null,detail:n?.website??null,next:n&&n.website===null?Z:null},{step:"research",title:"Company research",optional:!1,done:n?f==="ready":null,detail:n?ft(n,t):null,next:n&&f!=="ready"?f==="no_website"?Z:"closedloop company --watch":null},{step:"email_domains",title:"Email domains",optional:!1,done:n?y?y.confirmed:!0:null,detail:n?Uo(n):null,next:y&&!y.confirmed?op:null},{step:"product_structure",title:"Product structure",optional:!1,done:u?u.length>0:null,detail:u?`${Zt(u.length,"product")} confirmed`:null,next:s&&u&&u.length===0?sp(s,n):null},{step:"source",title:"A source of customer conversations",optional:!1,done:_,detail:d&&d.length>0?d.map(b=>b.name??b.slug).join(", "):h?"records sent with closedloop ingest":null,next:_===!1?"closedloop integrations list --available, or send records with: closedloop ingest":null},{step:"history",title:"History imported",optional:!1,done:o===null||_===null?null:_===!0&&c.length===0,detail:o===null?null:c.length>0?`${Zt(c.length,"import")} still running`:null,next:c.length>0?"closedloop backfills":_===!1?"Connect a source first; its history is imported when you connect it":null},{step:"goal",title:"A goal",optional:!0,done:a?a.length>0:null,detail:a?Zt(a.length,"goal"):null,next:a&&a.length===0?"closedloop goals create --file goals.json":null},{step:"team",title:"Your team invited",optional:!0,done:l?l.length>1:null,detail:l?Zt(l.length,"member"):null,next:l&&l.length<=1?`In the app: Settings, then Team. ${D}`:null}];return{running:{research:f&&rp.has(f)?f:null,backfills:c,own_data:m},onboarding:S,instructions:n?Gt(n):null}}function ap(e){let t=e.running.own_data;return e.running.research!==null||e.running.backfills.length>0||!!(t&&t.processing>0)}function lp(e,t,n){let o=t.print.style,i=[],r=e.running.own_data,s=[...e.running.backfills.map(c=>` ${_n(c,n,o)}`),...r&&r.waiting+r.processing>0?[` Your own data: ${[r.processing?`${k(r.processing)} processing`:"",r.waiting?`${k(r.waiting)} waiting`:""].filter(Boolean).join(", ")}`]:[]],a=e.onboarding.find(c=>c.step==="research");e.running.research&&a?.detail&&s.unshift(` ${a.detail}`),i.push("Running now"),i.push(...s.length>0?s:[" Nothing."]),i.push("","Onboarding");for(let c of e.onboarding){let m=c.done===!0?o.green("\u2713"):c.done===!1?c.optional?o.dim("\u25CB"):o.orange("\u25CB"):o.dim("?"),d=c.optional?`${c.title} (optional)`:c.title,h=c.done===!0&&c.detail?o.dim(` \xB7 ${c.detail}`):"";i.push(` ${m} ${d}${h}`),c.done!==!0&&c.next&&i.push(` ${o.dim("Next:")} ${c.next}`)}let l=e.onboarding.filter(c=>!c.optional&&c.done===!1).length,u=e.onboarding.filter(c=>!c.optional&&c.done===null).length;return i.push("",l===0&&u===0?"Onboarding is complete.":[l>0?`${Zt(l,"step")} left before your data is fully analysed.`:"",u>0?`${u} required step${u===1?"":"s"} could not be checked; try status again.`:""].filter(Boolean).join(" ")),e.instructions&&!t.interactive&&i.push("",e.instructions),i.join(`
162
+ `)}async function up(e,t){let n=v(e.runtime),o=Ya(await Ba(e.runtime,n),Date.now());if(t.watch){let i=e.print.spinner("Checking every 30 seconds\u2026");try{for(let r=0;r<ip&&ap(o);r+=Va)await e.runtime.sleep(Va*1e3),o=Ya(await Ba(e.runtime,n),Date.now())}finally{i.stop()}}e.print.result(o,i=>lp(i,e,Date.now()))}var Ka=(e,t)=>{e.command("status").description("What is running now (research, history imports, your own data) and what onboarding still needs.").option("--watch","Keep checking every 30 seconds until nothing is running (up to 2 hours).").action(async n=>up(t,n))};import{existsSync as cp,writeFileSync as dp}from"node:fs";import{resolve as mp}from"node:path";var pp=/^\d{4}-(0[1-9]|1[0-2])$/;async function bi(e,t){let n=await Je(e.runtime,t),o=await $(e.runtime,t,"GET",`/api/teams-billing/${n}/billing`);if(o.status!==200||typeof o.body.credits_used!="number")throw C(o,"api_error","Your usage could not be loaded. Try again.");return o.body}function gp(){return new Date().toISOString().slice(0,7)}function za(e){let t=e??gp();if(!pp.test(t))throw new p("invalid_month",`'${t}' is not a month`,g.Usage,"Write it like 2026-10.");return t}function hp(e,t){let n=k(e.credits_used),o=e.current_period_end?E(e.current_period_end):null;if(e.credits_limit===null){let c=e.current_period_cost?` Cost so far: ${`${e.current_period_cost} ${e.currency??""}`.trim()}.`:"";return`${n} credits used this period (${e.plan_label} plan).${c}${o?` The period ends ${o}.`:""}`}let i=k(e.credits_limit),r=e.credits_used/e.credits_limit,s=Math.floor(r*100),a=e.credits_used>0&&s===0?"<1":String(s),u=[`${(e.limit_reached||r>=1?t.red:r>=.8?t.orange:t.green)(t.bar(r,30))} ${t.bold(`${a}%`)}`,`${n} of ${i} credits used this period (${a}%, ${e.plan_label} plan).`];return e.limit_reached?u.push(`The limit is reached: new conversations wait${o?` until ${o}`:" until the next period"}, or until you upgrade (closedloop plan upgrade).`):e.limit_enforced?(o&&u.push(`It resets ${o}.`),e.limit_reached_on&&u.push(`At this pace you reach the limit around ${N(e.limit_reached_on)}; after that, new conversations wait until it resets.`)):(r>=1&&u.push("That is more than the credits your plan includes this period."),o&&u.push(`The period ends ${o}.`)),u.join(`
163
+ `)}async function fp(e){let t=v(e.runtime),n=await bi(e,t),o={plan:n.subscription_tier,plan_label:n.plan_label,credits_used:n.credits_used,credits_limit:n.credits_limit,limit_enforced:n.limit_enforced,limit_reached:n.limit_reached,limit_reached_on:n.limit_reached_on,period_start:n.current_period_start,period_end:n.current_period_end,cost_so_far:n.current_period_cost??null,currency:n.currency??null};e.print.result(o,()=>hp(n,e.print.style))}async function yp(e,t){let n=t===void 0?12:Number(t);if(!Number.isInteger(n)||n<1||n>12)throw new p("invalid_months","--months must be a whole number from 1 to 12",g.Usage);let o=v(e.runtime),i=await Je(e.runtime,o),r=await $(e.runtime,o,"GET",`/api/teams-billing/${i}/usage-history`);if(r.status!==200||!Array.isArray(r.body.months))throw C(r,"api_error","Your usage history could not be loaded. Try again.");let s=r.body.months.map(a=>({month:a.month,credits:a.billableCredits})).sort((a,l)=>a.month<l.month?1:-1).slice(0,n);e.print.result({months:s},a=>{if(a.months.length===0)return"No credits used in the last 12 months.";let l=e.print.style,u=Math.max(...a.months.map(y=>y.credits)),c=a.months.map(y=>Nt(y.month)),m=a.months.map(y=>k(y.credits)),d=Math.max(...c.map(y=>y.length)),h=Math.max(...m.map(y=>y.length)),f=a.months.length>1?`
164
+ ${l.dim(`Trend, oldest to newest: ${l.sparkline([...a.months].reverse().map(y=>y.credits))}`)}`:"";return a.months.map((y,_)=>`${c[_].padEnd(d)} ${m[_].padStart(h)} credits ${l.green(l.bar(u>0?y.credits/u:0,20))}`).join(`
165
+ `)+f})}async function wp(e,t){let n=za(t),o=v(e.runtime),i=await Je(e.runtime,o),r=await $(e.runtime,o,"GET",`/api/teams-billing/${i}/usage-by-source/${n}`);if(r.status!==200||!Array.isArray(r.body.sources))throw C(r,"api_error","Your usage could not be loaded. Try again.");let s={month:n,total:Number(r.body.total)||0,sources:r.body.sources};e.print.result(s,a=>a.sources.length===0?`No credits used in ${Nt(a.month)}.`:[`${Nt(a.month)}: ${k(a.total)} credits`,...a.sources.map(l=>` ${l.name}: ${k(l.credits)}`)].join(`
166
+ `))}async function _p(e,t){let n=za(t.month),o=mp(t.output??`closedloop-ai-usage-${n}.csv`);if(cp(o)&&!t.force)throw new p("file_exists",`${o} already exists`,g.Usage,"Add --force to replace it, or pick another file with --output.");let i=v(e.runtime),r=await Je(e.runtime,i),s=await $(e.runtime,i,"GET",`/api/teams-billing/${r}/usage-history/${n}/export`);if(s.status!==200||!s.text.startsWith("date,integration,credits"))throw C(s,"api_error","Your usage could not be exported. Try again.");dp(o,s.text,{mode:384});let a=s.text.trimEnd().split(`
167
+ `).length-1;e.print.result({status:"saved",month:n,path:o,rows:a},l=>`Saved ${Nt(l.month)}'s usage to ${l.path} (${l.rows} rows: date, integration, credits).`)}async function bp(e){let t=v(e.runtime),n=await bi(e,t),o={plan:n.subscription_tier,plan_label:n.plan_label,credits_included:n.credits_limit,limit_enforced:n.limit_enforced,period_start:n.current_period_start,period_end:n.current_period_end,plan_url:n.plan_url};e.print.result(o,i=>{let r=i.credits_included===null?"no monthly limit; you pay for the credits you use":`${k(i.credits_included)} credits a month included${i.limit_enforced?"; new conversations wait once they are used":""}`;return[`${i.plan_label} plan: ${r}.`,"See usage: closedloop usage. Change plan: closedloop plan upgrade"].join(`
168
+ `)})}async function $p(e){let t=v(e.runtime),n=await M(e.runtime,t),o=await bi(e,t),i=o.plan_url;if(!i)throw new p("no_plan_link","the plan page link is not available",g.Failure,`Open Settings, Plan & billing in the app. ${D}`);let r=n.permissions.can_manage_billing,s=r&&e.interactive?await e.runtime.openUrl(i):!1;e.print.result({status:s?"opened":"link",url:i,plan_label:o.plan_label,can_change_plan:r},a=>{let l=ke(a.url,e.runtime.supportsHyperlinks);return a.can_change_plan?`${s?"Opened":"Change your plan at"} ${l}${s?" in your browser.":"."} You are on the ${a.plan_label} plan.
169
+ ${D}`:`You are on the ${a.plan_label} plan. Only someone who can manage billing in ${n.name} can change it: ask an owner or admin. The plan page: ${l}
170
+ ${D}`})}var Xa=(e,t)=>{let n=e.command("usage").description("Credits used this period; history, by source and export below.").action(async()=>fp(t));n.command("history").description("Credits used per month, newest first.").option("--months <n>","How many months, up to 12.").action(async o=>yp(t,o.months)),n.command("by-source").description("Credits used in a month per integration or feature, largest first.").option("--month <yyyy-mm>","The month; this month if left out.").action(async o=>wp(t,o.month)),n.command("export").description("Save a month's usage per day and integration as a CSV file.").option("--month <yyyy-mm>","The month; this month if left out.").option("--output <file>","Where to save it; closedloop-ai-usage-<month>.csv if left out.").option("--force","Replace the file if it exists.").action(async o=>_p(t,o))},Qa=(e,t)=>{e.command("plan").description("Your plan and what it includes.").action(async()=>bp(t)).command("upgrade").description("Open Plan & billing to change your plan (payment happens in the browser).").action(async()=>$p(t))};function Ot(e){return typeof e=="string"&&e!==""?e:null}function Za(e){let t=e;return!t||typeof t!="object"||!Ot(t.team_id)||!Ot(t.team_name)?null:{id:t.team_id,name:t.team_name,role:Ot(t.role),plan:Ot(t.plan)}}var ol=[["can_manage_integrations","manage integrations"],["can_manage_billing","manage billing"],["can_manage_members","manage members"],["can_manage_team_settings","manage settings"],["can_view_analytics","view analytics"]];function vp(e){let t=e?.permissions??{};return Object.fromEntries(ol.map(([n])=>[n,t[n]===!0]))}var el={password:"a password",email_code:"an email code",google:"Google SSO",microsoft:"Microsoft SSO",okta:"Okta SSO",saml:"SAML SSO"};function tl(e,t){return e==="email_code"?el.email_code:e!=="browser"?null:t?`${el[t]} in the browser`:"the browser"}function Cp(e){return typeof e=="string"&&!Number.isNaN(Date.parse(e))?e:null}function kp(e){let t=[e.role,e.plan&&`${e.plan[0].toUpperCase()}${e.plan.slice(1).replace(/_/g," ")} plan`].filter(Boolean).join(", ");return t?`${e.name} (${t})`:e.name}function nl(e,t,n){if(e.status===403){let o=C(e,"forbidden","you cannot access this account or workspace"),i=o.code==="sso_required"?`Run \`${n}\` to sign in with your company account.`:["two_fa_verification_required","two_fa_setup_session_expired"].includes(o.code)?"Run `closedloop login` to sign in again and complete authentication.":"Ask a workspace owner or admin to check your access.";return new p(o.code,o.message,g.Failure,i)}if(e.status===404&&e.body.code==="USER_NOT_FOUND"){let o=C(e,"user_not_found","your saved account could not be found");return new p(o.code,o.message,g.Failure,"Run `closedloop login` to sign in again.")}return t==="workspace"&&e.status===404&&e.body.error==="No team found for user"?new p("no_workspace","you are not in a workspace yet",g.Failure,`Create or join one in the app. ${D}`):new p("api_error","ClosedLoop AI could not load your account",g.Failure,"Try again in a moment.")}var il=(e,{print:t,runtime:n})=>{e.command("whoami").description("Show who you are signed in as (the active account), and your workspace.").action(async()=>{let o=v(n),i=`${Ne(o.email,n.platform)} --browser`,r=await Eo(n,o,"/api/auth/profile");if(r.status!==200)throw nl(r,"profile",i);let s=await Eo(n,o,"/api/team-info");if(s.status!==200)throw nl(s,"workspace",i);let a=r.body.profile??{},l=Za(s.body.teamInfo),u=Array.isArray(s.body.allTeams)?s.body.allTeams.map(Za).filter(d=>d!==null):[],c=l?await Ke(n,o):null,m={status:"signed_in",email:Ot(a.email)??o.email,name:Ot(a.full_name),two_factor:a.totp_enabled===!0,workspace:l,other_workspaces:u.filter(d=>d.id!==l?.id),permissions:vp(s.body.teamInfo),company_research:c?c.research.state:null,analysis_waiting:c?c.analysis_waiting:null,region:typeof o.region=="string"?o.region:null,api_url:o.api_url,signed_in_at:Cp(o.signed_in_at),sign_in_method:o.sign_in_method==="email_code"||o.sign_in_method==="browser"?o.sign_in_method:null,signed_in_with:yn(o.signed_in_with),session_expires_at:tr(o),cli:{version:se,latest_version:n.release?.latest??null,update_available:ko(n)!==null}};t.result(m,d=>[`Signed in as ${t.style.bold(d.name?`${d.name} <${d.email}>`:d.email)}${d.two_factor?" (two-factor on)":""}`,`Workspace: ${d.workspace?kp(d.workspace):"none"}`,...d.other_workspaces.length?[`Also in: ${d.other_workspaces.map(h=>h.name).join(", ")}`]:[],(()=>{let h=ol.filter(([f])=>d.permissions[f]).map(([,f])=>f);return h.length?`You can: ${h.join(", ")}.`:"You can read this workspace; to change integrations, billing or members, ask an owner or admin."})(),...c&&Mt(c)?[`Your data: ${Mt(c)}`]:[],`Region: ${(d.region??"unknown").toUpperCase()} \xB7 API: ${d.api_url}`,d.signed_in_at?`Signed in here: ${E(d.signed_in_at)}${tl(d.sign_in_method,d.signed_in_with)?` with ${tl(d.sign_in_method,d.signed_in_with)}`:""}`:"Signed in here: time not recorded",...d.session_expires_at?[`Stays signed in while you use the CLI; ends ${E(d.session_expires_at)} if unused.`]:[]].join(`
171
+ `))})};var rl=[xa,il,Na,or,Ka,zr,Mr,ts,Bs,ta,Us,ss,ja,Ja,wa,mr,Xa,Qa,as];function vi(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}var ll={update:()=>{},stop:()=>{}},sl="\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F";function Sp(e,t){let n=t,o=0,i=()=>e.stderr(`\r\x1B[2K\x1B[2m${sl[o++%sl.length]} ${n}\x1B[22m`);i();let r=setInterval(i,80);return r.unref?.(),{update:s=>{n=s},stop:()=>{clearInterval(r),e.stderr("\r\x1B[2K")}}}function ul(e,t){return vi(e)||!t.stdoutIsTTY?Pp(t):Np(t)}function Op(e){return e.hint===void 0?{code:e.code,message:e.message}:{code:e.code,message:e.message,hint:e.hint}}function $i(e){let t=JSON.stringify(e);if(t===void 0)throw new Error("a result must be JSON data, not undefined or a function");return`${t}
172
+ `}function cl(e){return e.options.filter(t=>!t.hidden&&t.long!=="--help")}function dl(e){let t=e.aliases()[0],n=e.registeredArguments.map(i=>{let r=`${i.name()}${i.variadic?"...":""}`;return i.required?`<${r}>`:`[${r}]`}),o=cl(e).map(i=>`[${i.long&&i.short?i.flags.replace(`${i.short}, `,""):i.flags}]`);return[`${e.name()}${t?`|${t}`:""}`,...n,...o].join(" ")}var Ap=`Add --help after a command for details.
173
+ `,Tp=30,al=6;function Rp(e,t){return[...t.visibleOptions(e).map(n=>t.optionTerm(n)),...t.visibleGlobalOptions(e).map(n=>t.optionTerm(n)),...t.visibleArguments(e).map(n=>t.argumentTerm(n)),...t.visibleCommands(e).map(n=>t.subcommandTerm(n))]}function Ep(e,t){return Math.max(0,...Rp(e,t).map(n=>t.displayWidth(n)).filter(n=>n<=Tp))}function Ip(e,t,n,o){if(o.displayWidth(e)<=t)return Ee.prototype.formatItem.call(o,e,t,n,o);if(!n)return` ${e}`;let i=(o.helpWidth??80)-al,r=" ".repeat(al);return` ${e}
168
174
  ${r}${o.boxWrap(n,i).replace(/\n/g,`
169
- ${r}`)}`}function Np(e,t){let n=[];for(let i=e;i!==null;i=i.parent)n.unshift(i.name());let o=i=>({flags:i.flags,description:t.optionDescription(i)});return{command:n.join(" "),description:t.commandDescription(e),usage:t.commandUsage(e),arguments:t.visibleArguments(e).map(i=>({name:i.name(),required:i.required,variadic:i.variadic,description:t.argumentDescription(i)})),options:t.visibleOptions(e).map(o),globalOptions:t.visibleGlobalOptions(e).map(o),commands:t.visibleCommands(e).map(i=>({name:i.name(),usage:t.subcommandTerm(i),description:t.subcommandDescription(i),options:pl(i).map(o)}))}}function Lp(e){return{result:t=>e.stdout(Ci(t)),error:t=>e.stderr(Ci({error:Tp(t)})),note:()=>{},style:ei({}),spinner:()=>dl,help:{showGlobalOptions:!0,subcommandTerm:gl,formatHelp:(t,n)=>Ci(Np(t,n))},writeHelp:t=>e.stdout(t),writeHelpOnError:()=>{}}}function Dp(e){let t=ei({color:e.color,columns:e.columns});return{style:t,spinner:n=>e.animate?Op(e,n):dl,result:(n,o)=>{let i=o(n);e.stdout(i.endsWith(`
175
+ ${r}`)}`}function xp(e,t){let n=[];for(let i=e;i!==null;i=i.parent)n.unshift(i.name());let o=i=>({flags:i.flags,description:t.optionDescription(i)});return{command:n.join(" "),description:t.commandDescription(e),usage:t.commandUsage(e),arguments:t.visibleArguments(e).map(i=>({name:i.name(),required:i.required,variadic:i.variadic,description:t.argumentDescription(i)})),options:t.visibleOptions(e).map(o),globalOptions:t.visibleGlobalOptions(e).map(o),commands:t.visibleCommands(e).map(i=>({name:i.name(),usage:t.subcommandTerm(i),description:t.subcommandDescription(i),options:cl(i).map(o)}))}}function Pp(e){return{result:t=>e.stdout($i(t)),error:t=>e.stderr($i({error:Op(t)})),note:()=>{},style:di({}),spinner:()=>ll,help:{showGlobalOptions:!0,subcommandTerm:dl,formatHelp:(t,n)=>$i(xp(t,n))},writeHelp:t=>e.stdout(t),writeHelpOnError:()=>{}}}function Np(e){let t=di({color:e.color,columns:e.columns});return{style:t,spinner:n=>e.animate?Sp(e,n):ll,result:(n,o)=>{let i=o(n);e.stdout(i.endsWith(`
170
176
  `)?i:`${i}
171
177
  `)},error:n=>e.stderr(`${t.red(t.bold("error:"))} ${n.message}
172
178
  ${n.hint===void 0?"":`${t.dim(n.hint)}
173
179
  `}`),note:n=>e.stderr(`${n}
174
- `),help:{showGlobalOptions:!0,subcommandTerm:gl,padWidth:Pp,formatItem:xp,formatHelp:(n,o)=>{let i=Te.prototype.formatHelp.call(o,n,o);return o.visibleCommands(n).length>0?`${i}
175
- ${Rp}`:i}},writeHelp:n=>e.stdout(n),writeHelpOnError:n=>e.stderr(n)}}var Si="Run `closedloop --help` to see the commands and options.";function hl(e,t){let{print:n}=e,o=re,i=new it("closedloop").description("ClosedLoop AI on the command line, for people and AI coding agents.").usage("<command> [options]").option("--json","Print JSON. This is the default when the output is not a terminal.").option("--as <email>","Run the command as one of the accounts signed in on this computer (or set CLOSEDLOOP_ACCOUNT).").option("-V, --version","Print the version.").helpOption("-h, --help","Show help.").helpCommand("help [command]","Show help for a command.").configureHelp(n.help).exitOverride().configureOutput({writeOut:r=>n.writeHelp(r),writeErr:r=>n.writeHelpOnError(r),outputError:()=>{}}).argument("[command...]").action(r=>{if(r.length===0){i.outputHelp();return}throw new p("unknown_command",`unknown command '${r[0]}'`,g.Usage,Si)});i.on("option:version",()=>{throw n.result({version:o},r=>r.version),new B(0,"commander.version",o)}),i.hook("preAction",()=>{let r=i.opts().as;typeof r=="string"&&(e.runtime.accountOption=r)});for(let r of t)r(i,e);return i}import{spawn as yl}from"node:child_process";import{existsSync as jp}from"node:fs";import{createServer as Fp}from"node:http";import{homedir as Up}from"node:os";import{createInterface as Ai}from"node:readline/promises";import{Writable as Wp}from"node:stream";function Mp(e){if(e.FORCE_HYPERLINK==="1")return!0;if(e.FORCE_HYPERLINK==="0")return!1;let t=e.TERM_PROGRAM??"";return["iTerm.app","WezTerm","vscode","ghostty","Hyper"].includes(t)||e.WT_SESSION||e.KITTY_WINDOW_ID||e.KONSOLE_VERSION?!0:Number(e.VTE_VERSION)>=5e3}function so(e,t,n){return new Promise(o=>{try{let i=yl(e,t,{stdio:["pipe","ignore","ignore"],windowsHide:!0}),r=!1,s=u=>{r||(r=!0,clearTimeout(l),o(u))},a=()=>{if(!r){s(!1);try{i.kill("SIGKILL")}catch{}}},l=setTimeout(a,5e3);i.on("error",()=>s(!1)),i.on("close",u=>s(u===0)),i.stdin.on("error",a),i.stdin.end(n)}catch{o(!1)}})}function Oi(e,t){return new Promise(n=>{try{let o=yl(e,t,{stdio:"ignore",detached:!0});o.on("error",()=>n(!1)),o.on("spawn",()=>{o.unref(),n(!0)})}catch{n(!1)}})}async function Hp(e){return process.platform==="darwin"?so("pbcopy",[],e):process.platform==="win32"?so("powershell.exe",["-NoProfile","-NonInteractive","-Command","[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false); Set-Clipboard -Value ([Console]::In.ReadToEnd())"],e):process.env.WAYLAND_DISPLAY&&await so("wl-copy",[],e)?!0:so("xclip",["-selection","clipboard"],e)}async function Gp(e){let t;try{t=new URL(e)}catch{return!1}return t.protocol!=="https:"&&t.protocol!=="http:"?!1:process.platform==="darwin"?Oi("open",[t.href]):process.platform==="win32"?Oi("rundll32",["url.dll,FileProtocolHandler",t.href]):Oi("xdg-open",[t.href])}var qp=10*1024*1024,fl={"Content-Type":"text/plain; charset=utf-8","Cache-Control":"no-store","Referrer-Policy":"no-referrer"};function Jp(e){return new Promise((t,n)=>{let o=0,i=!1,r=Fp((a,l)=>{let u=d=>{l.writeHead(d.status,d.location?{...fl,Location:d.location}:fl),l.end(d.body??"",()=>{d.last&&s()})},c;try{c=new URL(a.url??"/",`http://127.0.0.1:${o}`)}catch{u({status:400,body:"Bad request."});return}if(a.headers.host!==`127.0.0.1:${o}`){u({status:400,body:"Bad request."});return}e({method:a.method??"GET",path:c.pathname,query:c.searchParams}).then(u,()=>u({status:500,body:"Something went wrong. Go back to your terminal."}))}),s=()=>{i||(i=!0,r.close(),r.closeIdleConnections())};r.once("error",n),r.listen(0,"127.0.0.1",()=>{let a=r.address();if(a===null||typeof a=="string"){s(),n(new Error("the loopback server has no port"));return}o=a.port,r.unref(),t({port:o,close:s})})})}function wl(){return{env:process.env,fetch:globalThis.fetch,homeDir:Up(),platform:process.platform,stdinIsTTY:process.stdin.isTTY===!0,supportsHyperlinks:Mp(process.env),sleep:e=>new Promise(t=>setTimeout(t,e)),prompt:async e=>{let t=Ai({input:process.stdin,output:process.stderr});try{return(await t.question(e)).trim()}finally{t.close()}},promptSecret:async e=>{process.stderr.write(e);let t=new Wp({write:(o,i,r)=>r()}),n=Ai({input:process.stdin,output:t,terminal:!0});try{return(await n.question("")).trim()}finally{n.close(),process.stderr.write(`
176
- `)}},readStdin:async(e=qp)=>{let t=[],n=0;for await(let o of process.stdin){if(n+=o.length,n>e)throw new p("stdin_too_large",`standard input exceeds this command's ${e}-byte input limit`,g.Usage,"Split the input into smaller records; --file uses this command's same input limit.");t.push(o)}return Buffer.concat(t).toString("utf8")},copyToClipboard:Hp,openUrl:Gp,now:()=>Date.now(),release:{latest:null},onEnter:e=>{let t=Ai({input:process.stdin});return t.once("line",()=>e()),()=>t.close()},listenLoopback:Jp,fileExists:e=>jp(e)}}var Vp="Run it again; if it keeps failing, report it with the output of `closedloop --version`.",Bp="Add --help to the same command to see its commands.";function Yp(e,t,n){if(e instanceof p)return e;if(e instanceof B){let o=e.code==="commander.excessArguments"?Ir(t):null;if(o!==null)return Cn(o.kind,n,o.option);if(e.code==="commander.help")return new p("missing_command","this command needs one of its commands",g.Usage,Bp);let i=e.code==="commander.unknownOption"?"unknown_option":"usage_error";return new p(i,e.message.replace(/^error: /,""),g.Usage,Si)}return new p("internal_error","something went wrong inside the CLI",g.Failure,Vp)}async function _l(e,t,n=ll,o=wl()){let i=ml(e,t),r=t.stdoutIsTTY&&o.stdinIsTTY&&!ki(e),s={...o,account:void 0,accountOption:void 0},a=hl({print:i,runtime:s,interactive:r},n),l=await Kp(a,e,i,o.platform),u=Qi(s,r);return u!==null&&t.stderr(`
180
+ `),help:{showGlobalOptions:!0,subcommandTerm:dl,padWidth:Ep,formatItem:Ip,formatHelp:(n,o)=>{let i=Ee.prototype.formatHelp.call(o,n,o);return o.visibleCommands(n).length>0?`${i}
181
+ ${Ap}`:i}},writeHelp:n=>e.stdout(n),writeHelpOnError:n=>e.stderr(n)}}var Ci="Run `closedloop --help` to see the commands and options.";function ml(e,t){let{print:n}=e,o=se,i=new ot("closedloop").description("ClosedLoop AI on the command line, for people and AI coding agents.").usage("<command> [options]").option("--json","Print JSON. This is the default when the output is not a terminal.").option("--as <email>","Run the command as one of the accounts signed in on this computer (or set CLOSEDLOOP_ACCOUNT).").option("-V, --version","Print the version.").helpOption("-h, --help","Show help.").helpCommand("help [command]","Show help for a command.").configureHelp(n.help).exitOverride().configureOutput({writeOut:r=>n.writeHelp(r),writeErr:r=>n.writeHelpOnError(r),outputError:()=>{}}).argument("[command...]").action(r=>{if(r.length===0){i.outputHelp();return}throw new p("unknown_command",`unknown command '${r[0]}'`,g.Usage,Ci)});i.on("option:version",()=>{throw n.result({version:o},r=>r.version),new B(0,"commander.version",o)}),i.hook("preAction",()=>{let r=i.opts().as;typeof r=="string"&&(e.runtime.accountOption=r)});for(let r of t)r(i,e);return i}import{spawn as gl}from"node:child_process";import{existsSync as Lp}from"node:fs";import{createServer as Dp}from"node:http";import{homedir as jp}from"node:os";import{createInterface as ki}from"node:readline/promises";import{Writable as Fp}from"node:stream";function Wp(e){if(e.FORCE_HYPERLINK==="1")return!0;if(e.FORCE_HYPERLINK==="0")return!1;let t=e.TERM_PROGRAM??"";return["iTerm.app","WezTerm","vscode","ghostty","Hyper"].includes(t)||e.WT_SESSION||e.KITTY_WINDOW_ID||e.KONSOLE_VERSION?!0:Number(e.VTE_VERSION)>=5e3}function no(e,t,n){return new Promise(o=>{try{let i=gl(e,t,{stdio:["pipe","ignore","ignore"],windowsHide:!0}),r=!1,s=u=>{r||(r=!0,clearTimeout(l),o(u))},a=()=>{if(!r){s(!1);try{i.kill("SIGKILL")}catch{}}},l=setTimeout(a,5e3);i.on("error",()=>s(!1)),i.on("close",u=>s(u===0)),i.stdin.on("error",a),i.stdin.end(n)}catch{o(!1)}})}function Si(e,t){return new Promise(n=>{try{let o=gl(e,t,{stdio:"ignore",detached:!0});o.on("error",()=>n(!1)),o.on("spawn",()=>{o.unref(),n(!0)})}catch{n(!1)}})}async function Up(e){return process.platform==="darwin"?no("pbcopy",[],e):process.platform==="win32"?no("powershell.exe",["-NoProfile","-NonInteractive","-Command","[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false); Set-Clipboard -Value ([Console]::In.ReadToEnd())"],e):process.env.WAYLAND_DISPLAY&&await no("wl-copy",[],e)?!0:no("xclip",["-selection","clipboard"],e)}async function Hp(e){let t;try{t=new URL(e)}catch{return!1}return t.protocol!=="https:"&&t.protocol!=="http:"?!1:process.platform==="darwin"?Si("open",[t.href]):process.platform==="win32"?Si("rundll32",["url.dll,FileProtocolHandler",t.href]):Si("xdg-open",[t.href])}var Mp=10*1024*1024,pl={"Content-Type":"text/plain; charset=utf-8","Cache-Control":"no-store","Referrer-Policy":"no-referrer"};function Gp(e){return new Promise((t,n)=>{let o=0,i=!1,r=Dp((a,l)=>{let u=m=>{l.writeHead(m.status,m.location?{...pl,Location:m.location}:pl),l.end(m.body??"",()=>{m.last&&s()})},c;try{c=new URL(a.url??"/",`http://127.0.0.1:${o}`)}catch{u({status:400,body:"Bad request."});return}if(a.headers.host!==`127.0.0.1:${o}`){u({status:400,body:"Bad request."});return}e({method:a.method??"GET",path:c.pathname,query:c.searchParams}).then(u,()=>u({status:500,body:"Something went wrong. Go back to your terminal."}))}),s=()=>{i||(i=!0,r.close(),r.closeIdleConnections())};r.once("error",n),r.listen(0,"127.0.0.1",()=>{let a=r.address();if(a===null||typeof a=="string"){s(),n(new Error("the loopback server has no port"));return}o=a.port,r.unref(),t({port:o,close:s})})})}function hl(){return{env:process.env,fetch:globalThis.fetch,homeDir:jp(),platform:process.platform,stdinIsTTY:process.stdin.isTTY===!0,supportsHyperlinks:Wp(process.env),sleep:e=>new Promise(t=>setTimeout(t,e)),prompt:async e=>{let t=ki({input:process.stdin,output:process.stderr});try{return(await t.question(e)).trim()}finally{t.close()}},promptSecret:async e=>{process.stderr.write(e);let t=new Fp({write:(o,i,r)=>r()}),n=ki({input:process.stdin,output:t,terminal:!0});try{return(await n.question("")).trim()}finally{n.close(),process.stderr.write(`
182
+ `)}},readStdin:async(e=Mp)=>{let t=[],n=0;for await(let o of process.stdin){if(n+=o.length,n>e)throw new p("stdin_too_large",`standard input exceeds this command's ${e}-byte input limit`,g.Usage,"Split the input into smaller records; --file uses this command's same input limit.");t.push(o)}return Buffer.concat(t).toString("utf8")},copyToClipboard:Up,openUrl:Hp,now:()=>Date.now(),release:{latest:null},onEnter:e=>{let t=ki({input:process.stdin});return t.once("line",()=>e()),()=>t.close()},listenLoopback:Gp,fileExists:e=>Lp(e)}}var qp="Run it again; if it keeps failing, report it with the output of `closedloop --version`.",Jp="Add --help to the same command to see its commands.";function Vp(e,t,n){if(e instanceof p)return e;if(e instanceof B){let o=e.code==="commander.excessArguments"?Ir(t):null;if(o!==null)return bn(o.kind,n,o.option);if(e.code==="commander.help")return new p("missing_command","this command needs one of its commands",g.Usage,Jp);let i=e.code==="commander.unknownOption"?"unknown_option":"usage_error";return new p(i,e.message.replace(/^error: /,""),g.Usage,Ci)}return new p("internal_error","something went wrong inside the CLI",g.Failure,qp)}async function fl(e,t,n=rl,o=hl()){let i=ul(e,t),r=t.stdoutIsTTY&&o.stdinIsTTY&&!vi(e),s={...o,account:void 0,accountOption:void 0},a=ml({print:i,runtime:s,interactive:r},n),l=await Bp(a,e,i,o.platform),u=Qi(s,r);return u!==null&&t.stderr(`
177
183
  ${u}
178
- `),l}async function Kp(e,t,n,o){try{return await e.parseAsync([...t],{from:"user"}),g.Success}catch(i){if(i instanceof B&&i.exitCode===0)return g.Success;let r=Yp(i,t,o);return n.error(r),r.exitCode}}function bl(e=process.env){let n=e.FORCE_COLOR==="1"||process.stdout.isTTY===!0&&!e.NO_COLOR&&e.TERM!=="dumb";return{stdout:o=>{process.stdout.write(o)},stderr:o=>{process.stderr.write(o)},stdoutIsTTY:process.stdout.isTTY===!0,color:n,animate:n&&process.stderr.isTTY===!0&&!e.CI,columns:process.stdout.columns||void 0}}process.exitCode=await _l(process.argv.slice(2),bl());
184
+ `),l}async function Bp(e,t,n,o){try{return await e.parseAsync([...t],{from:"user"}),g.Success}catch(i){if(i instanceof B&&i.exitCode===0)return g.Success;let r=Vp(i,t,o);return n.error(r),r.exitCode}}function yl(e=process.env){let n=e.FORCE_COLOR==="1"||process.stdout.isTTY===!0&&!e.NO_COLOR&&e.TERM!=="dumb";return{stdout:o=>{process.stdout.write(o)},stderr:o=>{process.stderr.write(o)},stdoutIsTTY:process.stdout.isTTY===!0,color:n,animate:n&&process.stderr.isTTY===!0&&!e.CI,columns:process.stdout.columns||void 0}}process.exitCode=await fl(process.argv.slice(2),yl());