tracwell 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/npm/cli.js +14 -12
  2. package/package.json +1 -1
package/dist/npm/cli.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import{randomUUID as me}from"node:crypto";var S="0.6.1";function q(e){let t=[],r={};for(let i=0;i<e.length;i+=1){let a=e[i];if(a==="--"){t.push(...e.slice(i+1));break}if(a==="-h"){r.help=!0;continue}if(!a.startsWith("--")){t.push(a);continue}let c=a.indexOf("=");if(c!==-1){r[a.slice(2,c)]=a.slice(c+1);continue}let l=e[i+1];l!==void 0&&!l.startsWith("--")?(r[a.slice(2)]=l,i+=1):r[a.slice(2)]=!0}let[n,...s]=t;return{command:n,positional:s,flags:r}}function W(e){return e.replace(/-([a-z0-9])/gu,(t,r)=>r.toUpperCase())}import{chmod as be,mkdir as ve,readFile as Se,rm as xe,writeFile as Te}from"node:fs/promises";import{homedir as _e}from"node:os";import{join as I}from"node:path";var x="https://tracwell.app";function B(e=process.env){return e.TRACWELL_CONFIG_DIR?e.TRACWELL_CONFIG_DIR:process.platform==="win32"&&e.APPDATA?I(e.APPDATA,"tracwell"):I(e.XDG_CONFIG_HOME||I(_e(),".config"),"tracwell")}function P(){return I(B(),"config.json")}async function m(){try{return JSON.parse(await Se(P(),"utf8"))}catch(e){if(e.code==="ENOENT")return{};throw new Error(`Could not read ${P()}. Delete it and log in again.`,{cause:e})}}async function y(e){await ve(B(),{recursive:!0,mode:448});let t=P();await Te(t,`${JSON.stringify(e,null,2)}
3
- `,{mode:384}),await be(t,384)}async function K(){await xe(P(),{force:!0})}var H=e=>e.replace(/\/+$/u,"");function G(e,t){return H(e.TRACWELL_URL||t.login?.baseUrl||x)}function L(e){return H(e.TRACWELL_URL||x)}var o=class extends Error{constructor(r,n=1){super(r);this.exitCode=n}exitCode};function Y(e,t){try{return JSON.parse(e).error?.message??t}catch{return t}}async function z(e,t,r,n){let s;try{s=await fetch(`${e}/mcp`,{method:"POST",headers:{accept:"application/json, text/event-stream",authorization:`Bearer ${t}`,"content-type":"application/json","user-agent":`tracwell-cli/${S}`},body:JSON.stringify({jsonrpc:"2.0",id:1,method:r,params:n})})}catch{throw new o(e===x?`Could not reach ${e}. Check your connection and try again.`:`Could not reach ${e}, the address of your saved login. Start that server, or run \`tracwell login\` to sign in to ${x}.`)}let i=await s.text();if(s.status===401)throw new o(`${Y(i,"Not signed in.")} Run \`tracwell login\`.`,3);if(!s.ok)throw new o(Y(i,`Tracwell returned HTTP ${s.status}.`));let a=i.split(`
4
- `).find(l=>l.startsWith("data: "))?.slice(6)??i,c=JSON.parse(a);if(c.error)throw new o(c.error.message??"The request failed.");return c.result}var Ie=e=>`${e.name.replaceAll("_"," ")} ${e.title??""} ${e.description??""}`.toLowerCase();function Z(e,t){let r=t.toLowerCase().split(/\s+/u).filter(Boolean);if(!r.length)return[...e];let n=s=>r.filter(i=>s.name.toLowerCase().includes(i)).length;return e.filter(s=>r.every(i=>Ie(s).includes(i))).toSorted((s,i)=>n(i)-n(s))}async function k(e,t){return(await z(e,t,"tools/list",{})).tools??[]}async function V(e,t,r,n){return await z(e,t,"tools/call",{name:r,arguments:n})}function Pe(e){let t=e.trim();if(!t)throw new o("A step is empty. Use a page like /pricing or an event like event:signup_completed.",2);if(t.startsWith("/"))return{type:"pageview",path:t};let r=t.startsWith("event:")?t.slice(6).trim():t;if(!r)throw new o("`event:` needs an event name, like event:signup_completed.",2);return{type:"event",name:r}}function X(e){let t=typeof e.page=="string"?e.page:void 0,r=typeof e.event=="string"?e.event:void 0;if(t&&r)throw new o("Pass --page or --event, not both.",2);if(t){if(!t.startsWith("/"))throw new o("--page must start with /, like /pricing.",2);return{type:"pageview",path:t}}if(r)return{type:"event",name:r};throw new o("Usage: tracwell goals add <name> --page /pricing (or --event signup_completed)",2)}function Q(e){if(typeof e!="string"||!e.trim())throw new o("Usage: tracwell funnels add <name> --steps /,/pricing,event:checkout_started",2);if(e.trim().startsWith("["))try{return JSON.parse(e)}catch{throw new o("--steps must be a comma list or a JSON array.",2)}let t=e.split(",").map(Pe);if(t.length<2)throw new o("A funnel needs at least 2 steps.",2);return t}function j(e,t){return Object.fromEntries(Object.entries(e).filter(([r])=>!t.includes(r)))}import{access as $e,mkdir as ee,readFile as U,writeFile as te}from"node:fs/promises";import{dirname as ne,join as $,relative as Re}from"node:path";var Ae=new URL("../../skills/tracwell/SKILL.md",import.meta.url);function N(){return U(Ae,"utf8")}var Le=e=>$e(e).then(()=>!0,()=>!1);async function je(e,t,r){let n;try{n=await U(e,"utf8")}catch{}return n===t?"unchanged":n!==void 0&&!r?"skipped":(await ee(ne(e),{recursive:!0}),await te(e,t),n===void 0?"created":"updated")}async function E(e,t){let r=await N(),n=[$(e,".agents/skills/tracwell/SKILL.md")];return await Le($(e,".claude"))&&n.push($(e,".claude/skills/tracwell/SKILL.md")),Promise.all(n.map(async s=>({path:Re(e,s),status:await je(s,r,t)})))}var re="<!-- tracwell:start -->",C="<!-- tracwell:end -->",O=`${re}
2
+ import{randomUUID as he}from"node:crypto";var x="0.6.2";function q(e){let t=[],n={};for(let i=0;i<e.length;i+=1){let a=e[i];if(a==="--"){t.push(...e.slice(i+1));break}if(a==="-h"){n.help=!0;continue}if(!a.startsWith("--")){t.push(a);continue}let c=a.indexOf("=");if(c!==-1){n[a.slice(2,c)]=a.slice(c+1);continue}let l=e[i+1];l!==void 0&&!l.startsWith("--")?(n[a.slice(2)]=l,i+=1):n[a.slice(2)]=!0}let[r,...s]=t;return{command:r,positional:s,flags:n}}function M(e){return e.replace(/-([a-z0-9])/gu,(t,n)=>n.toUpperCase())}import{chmod as ve,mkdir as Se,readFile as xe,rm as Te,writeFile as _e}from"node:fs/promises";import{homedir as Ie}from"node:os";import{join as $}from"node:path";var T="https://tracwell.app";function B(e=process.env){return e.TRACWELL_CONFIG_DIR?e.TRACWELL_CONFIG_DIR:process.platform==="win32"&&e.APPDATA?$(e.APPDATA,"tracwell"):$(e.XDG_CONFIG_HOME||$(Ie(),".config"),"tracwell")}function P(){return $(B(),"config.json")}async function g(){try{return JSON.parse(await xe(P(),"utf8"))}catch(e){if(e.code==="ENOENT")return{};throw new Error(`Could not read ${P()}. Delete it and log in again.`,{cause:e})}}async function h(e){await Se(B(),{recursive:!0,mode:448});let t=P();await _e(t,`${JSON.stringify(e,null,2)}
3
+ `,{mode:384}),await ve(t,384)}async function K(){await Te(P(),{force:!0})}var H=e=>e.replace(/\/+$/u,"");function G(e,t){return H(e.TRACWELL_URL||t.login?.baseUrl||T)}function A(e){return H(e.TRACWELL_URL||T)}var o=class extends Error{constructor(n,r=1){super(n);this.exitCode=r}exitCode};function Y(e,t){try{return JSON.parse(e).error?.message??t}catch{return t}}async function z(e,t,n,r){let s;try{s=await fetch(`${e}/mcp`,{method:"POST",headers:{accept:"application/json, text/event-stream",authorization:`Bearer ${t}`,"content-type":"application/json","user-agent":`tracwell-cli/${x}`},body:JSON.stringify({jsonrpc:"2.0",id:1,method:n,params:r})})}catch{throw new o(e===T?`Could not reach ${e}. Check your connection and try again.`:`Could not reach ${e}, the address of your saved login. Start that server, or run \`tracwell login\` to sign in to ${T}.`)}let i=await s.text();if(s.status===401)throw new o(`${Y(i,"Not signed in.")} Run \`tracwell login\`.`,3);if(!s.ok)throw new o(Y(i,`Tracwell returned HTTP ${s.status}.`));let a=i.split(`
4
+ `).find(l=>l.startsWith("data: "))?.slice(6)??i,c=JSON.parse(a);if(c.error)throw new o(c.error.message??"The request failed.");return c.result}var $e=e=>`${e.name.replaceAll("_"," ")} ${e.title??""} ${e.description??""}`.toLowerCase();function Z(e,t){let n=t.toLowerCase().split(/\s+/u).filter(Boolean);if(!n.length)return[...e];let r=s=>n.filter(i=>s.name.toLowerCase().includes(i)).length;return e.filter(s=>n.every(i=>$e(s).includes(i))).toSorted((s,i)=>r(i)-r(s))}async function k(e,t){return(await z(e,t,"tools/list",{})).tools??[]}async function V(e,t,n,r){return await z(e,t,"tools/call",{name:n,arguments:r})}function Pe(e){let t=e.trim();if(!t)throw new o("A step is empty. Use a page like /pricing or an event like event:signup_completed.",2);if(t.startsWith("/"))return{type:"pageview",path:t};let n=t.startsWith("event:")?t.slice(6).trim():t;if(!n)throw new o("`event:` needs an event name, like event:signup_completed.",2);return{type:"event",name:n}}function X(e){let t=typeof e.page=="string"?e.page:void 0,n=typeof e.event=="string"?e.event:void 0;if(t&&n)throw new o("Pass --page or --event, not both.",2);if(t){if(!t.startsWith("/"))throw new o("--page must start with /, like /pricing.",2);return{type:"pageview",path:t}}if(n)return{type:"event",name:n};throw new o("Usage: tracwell goals add <name> --page /pricing (or --event signup_completed)",2)}function Q(e){if(typeof e!="string"||!e.trim())throw new o("Usage: tracwell funnels add <name> --steps /,/pricing,event:checkout_started",2);if(e.trim().startsWith("["))try{return JSON.parse(e)}catch{throw new o("--steps must be a comma list or a JSON array.",2)}let t=e.split(",").map(Pe);if(t.length<2)throw new o("A funnel needs at least 2 steps.",2);return t}function L(e,t){return Object.fromEntries(Object.entries(e).filter(([n])=>!t.includes(n)))}import{access as je,mkdir as ee,readFile as U,writeFile as te}from"node:fs/promises";import{dirname as ne,join as j,relative as Re}from"node:path";var Ae=new URL("../../skills/tracwell/SKILL.md",import.meta.url);function N(){return U(Ae,"utf8")}var Le=e=>je(e).then(()=>!0,()=>!1);async function Oe(e,t,n){let r;try{r=await U(e,"utf8")}catch{}return r===t?"unchanged":r!==void 0&&!n?"skipped":(await ee(ne(e),{recursive:!0}),await te(e,t),r===void 0?"created":"updated")}async function E(e,t){let n=await N(),r=[j(e,".agents/skills/tracwell/SKILL.md")];return await Le(j(e,".claude"))&&r.push(j(e,".claude/skills/tracwell/SKILL.md")),Promise.all(r.map(async s=>({path:Re(e,s),status:await Oe(s,n,t)})))}var re="<!-- tracwell:start -->",C="<!-- tracwell:end -->",O=`${re}
5
5
  ## Analytics (Tracwell)
6
6
 
7
7
  This project uses Tracwell for analytics. Use the \`tracwell\` CLI (\`npx tracwell\`)
8
8
  to check the install and read reports: \`npx tracwell verify\`, \`npx tracwell overview\`,
9
9
  and \`npx tracwell help\` for everything else. Before analytics work, read
10
10
  \`.agents/skills/tracwell/SKILL.md\` (or run \`npx tracwell skill\`).
11
- ${C}`;function Oe(e){let t=e.indexOf(re),r=e.indexOf(C,t);return t!==-1&&r!==-1?`${e.slice(0,t)}${O}${e.slice(r+C.length)}`:e.trim()?`${e.replace(/\s*$/u,"")}
11
+ ${C}`;function Ce(e){let t=e.indexOf(re),n=e.indexOf(C,t);return t!==-1&&n!==-1?`${e.slice(0,t)}${O}${e.slice(n+C.length)}`:e.trim()?`${e.replace(/\s*$/u,"")}
12
12
 
13
13
  ${O}
14
14
  `:`${O}
15
- `}async function F(e,t="AGENTS.md"){let r=$(e,t),n="";try{n=await U(r,"utf8")}catch{}let s=Oe(n);return s===n?{path:t,status:"unchanged"}:(await ee(ne(r),{recursive:!0}),await te(r,s),{path:t,status:n?"updated":"created"})}import{spawn as Ce}from"node:child_process";import{createHash as Ue,randomBytes as se}from"node:crypto";import{createServer as Ne}from"node:http";var ie="analytics:read definitions:write websites:write offline_access",Ee=300*1e3,Fe=60*1e3,D=e=>e.toString("base64url");async function oe(e,t){let r=await fetch(e,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams(t).toString()}),n=await r.text();if(!r.ok){let s=`Sign-in failed (HTTP ${r.status}).`;try{let i=JSON.parse(n);s=i.error_description??i.error??s}catch{}throw new o(`${s} Run \`tracwell login\` again.`,3)}return JSON.parse(n)}function ae(e,t,r,n){let s=r.refresh_token??n;return{type:"oauth",baseUrl:e,clientId:t,accessToken:r.access_token,...s?{refreshToken:s}:{},expiresAt:Date.now()+(r.expires_in??3600)*1e3}}function De(e){let[t,r]=process.platform==="darwin"?["open",[e]]:process.platform==="win32"?["rundll32",["url.dll,FileProtocolHandler",e]]:["xdg-open",[e]];try{let n=Ce(t,r,{detached:!0,stdio:"ignore"});n.on("error",()=>{}),n.unref()}catch{}}function Je(){return new Promise((e,t)=>{let r=Ne();r.once("error",t),r.listen(0,"127.0.0.1",()=>{let n=r.address();n&&typeof n=="object"?e({server:r,port:n.port}):t(new o("Could not start the local sign-in callback."))})})}var Me='<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>',qe='<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="M12 7.5v5.5M12 16.5v.01"/></svg>',We=(e,t,r=!1)=>`<!doctype html>
15
+ `}async function F(e,t="AGENTS.md"){let n=j(e,t),r="";try{r=await U(n,"utf8")}catch{}let s=Ce(r);return s===r?{path:t,status:"unchanged"}:(await ee(ne(n),{recursive:!0}),await te(n,s),{path:t,status:r?"updated":"created"})}import{spawn as Ue}from"node:child_process";import{createHash as Ne,randomBytes as se}from"node:crypto";import{createServer as Ee}from"node:http";var ie="analytics:read definitions:write websites:write offline_access",Fe=300*1e3,De=60*1e3,D=e=>e.toString("base64url");async function oe(e,t){let n=await fetch(e,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams(t).toString()}),r=await n.text();if(!n.ok){let s=`Sign-in failed (HTTP ${n.status}).`;try{let i=JSON.parse(r);s=i.error_description??i.error??s}catch{}throw new o(`${s} Run \`tracwell login\` again.`,3)}return JSON.parse(r)}function ae(e,t,n,r){let s=n.refresh_token??r;return{type:"oauth",baseUrl:e,clientId:t,accessToken:n.access_token,...s?{refreshToken:s}:{},expiresAt:Date.now()+(n.expires_in??3600)*1e3}}function We(e){let[t,n]=process.platform==="darwin"?["open",[e]]:process.platform==="win32"?["rundll32",["url.dll,FileProtocolHandler",e]]:["xdg-open",[e]];try{let r=Ue(t,n,{detached:!0,stdio:"ignore"});r.on("error",()=>{}),r.unref()}catch{}}function Je(){return new Promise((e,t)=>{let n=Ee();n.once("error",t),n.listen(0,"127.0.0.1",()=>{let r=n.address();r&&typeof r=="object"?e({server:n,port:r.port}):t(new o("Could not start the local sign-in callback."))})})}var qe='<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>',Me='<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="M12 7.5v5.5M12 16.5v.01"/></svg>',Be=(e,t,n=!1)=>`<!doctype html>
16
16
  <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${e} \xB7 Tracwell</title>
17
17
  <style>
18
18
  :root{--bg:#fff;--fg:#111;--muted:#737373;--shell:#f5f5f5;--card:#fff;--line:#e5e5e5;--chip:#f0f0f0;--blue:#357dff}
@@ -29,8 +29,10 @@ p{margin:6px 0 0;color:var(--muted)}
29
29
  code{font:12px ui-monospace,SFMono-Regular,Menlo,monospace;background:var(--chip);padding:2px 6px;border-radius:6px;color:var(--fg);white-space:nowrap}
30
30
  @media (prefers-reduced-motion:no-preference){.card{animation:in .3s cubic-bezier(.16,1,.3,1) both}@keyframes in{from{opacity:0;transform:translateY(4px)}}}
31
31
  </style></head>
32
- <body><main class="shell${r?" ok":""}"><div class="card"><div class="icon">${r?Me:qe}</div><h1>${e}</h1><p>${t}</p></div>
33
- <p class="foot">Tracwell CLI</p></main></body></html>`;function Be(e,t){return new Promise((r,n)=>{let s=setTimeout(()=>n(new o("Sign-in timed out. Run `tracwell login` again.",3)),Ee);e.on("request",(i,a)=>{let c=new URL(i.url??"/","http://127.0.0.1");if(c.pathname!=="/callback"){a.writeHead(404).end();return}let l=c.searchParams.get("error"),d=c.searchParams.get("code"),p=(_,v,h,w=!1)=>a.writeHead(_,{"content-type":"text/html; charset=utf-8"}).end(We(v,h,w));if(c.searchParams.get("state")!==t){p(400,"This sign-in link is out of date","Go back to your terminal and run <code>tracwell login</code> again.");return}if(clearTimeout(s),l||!d){p(400,"Sign-in was cancelled","Nothing was connected. Run <code>tracwell login</code> to try again."),n(new o(c.searchParams.get("error_description")??"Sign-in was cancelled.",3));return}p(200,"You're signed in","The Tracwell CLI is connected. You can close this tab and go back to your terminal.",!0),r(d)})})}async function ce(e,t){let{server:r,port:n}=await Je();try{let s=`http://127.0.0.1:${n}/callback`,i=await fetch(`${e}/api/auth/oauth2/register`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_name:"Tracwell CLI",redirect_uris:[s],token_endpoint_auth_method:"none",grant_types:["authorization_code","refresh_token"],response_types:["code"],scope:ie})}).catch(()=>{throw new o(`Could not reach ${e}. Check your connection and try again.`)});if(!i.ok)throw new o(`Could not start sign-in (HTTP ${i.status}).`,3);let{client_id:a}=await i.json(),c=D(se(32)),l=D(se(16)),d=new URL("/api/auth/oauth2/authorize",e);for(let[v,h]of Object.entries({client_id:a,redirect_uri:s,response_type:"code",scope:ie,state:l,code_challenge:D(Ue("sha256").update(c).digest()),code_challenge_method:"S256",resource:`${e}/mcp`}))d.searchParams.set(v,h);t.print("Sign in and pick your websites in the browser:"),t.print(d.href),t.open&&De(d.href);let p=await Be(r,l),_=await oe(`${e}/api/auth/oauth2/token`,{grant_type:"authorization_code",code:p,client_id:a,redirect_uri:s,code_verifier:c,resource:`${e}/mcp`});return ae(e,a,_)}finally{r.close()}}function le(e,t=Date.now()){return e.expiresAt-Fe<=t}async function de(e){if(!e.refreshToken)throw new o("Your sign-in expired. Run `tracwell login`.",3);let t=await oe(`${e.baseUrl}/api/auth/oauth2/token`,{grant_type:"refresh_token",refresh_token:e.refreshToken,client_id:e.clientId,resource:`${e.baseUrl}/mcp`});return ae(e.baseUrl,e.clientId,t,e.refreshToken)}async function ue(e){let t=e.refreshToken??e.accessToken;await fetch(`${e.baseUrl}/api/auth/oauth2/revoke`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:t,client_id:e.clientId}).toString()}).catch(()=>{})}var Ke=new Set(["json","site","days","compact","key","help","no-browser","dry-run","force"]),R=7,pe=e=>e.toISOString().slice(0,10);function He(e,t){let r=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()-(e-1)));return{from:pe(r),to:pe(t)}}function Ge(e,t,r,n,s){let i="from"in e&&"to"in e,a=!i&&"date"in e;if(!i&&!a)return{};if(r.from!==void 0||r.to!==void 0||r.date!==void 0)return{};if(!(t.has("from")||t.has("date")||n.days!==void 0))return{};let c=n.days===void 0?R:Number(n.days);if(!Number.isInteger(c)||c<1)throw new o("--days must be a whole number of 1 or more.",2);let{from:l,to:d}=He(c,s);if(a)return{date:l,..."to"in e?{to:d}:{}};if(e.from?.format==="date-time"){let p=new Date(`${d}T00:00:00.000Z`);return p.setUTCDate(p.getUTCDate()+1),{from:`${l}T00:00:00.000Z`,to:p.toISOString()}}return{from:l,to:d}}function Ye(e){return e?.type?Array.isArray(e.type)?e.type:[e.type]:[]}function ze(e,t,r){let n=Ye(r);if(t===!0){if(n.length&&!n.includes("boolean"))throw new o(`--${e} needs a value.`,2);return!0}if(n.includes("number")||n.includes("integer")){let s=Number(t);if(Number.isNaN(s))throw new o(`--${e} must be a number.`,2);return s}if(n.includes("boolean"))return t==="true";if(!n.length&&/^\s*[[{]/u.test(t))try{return JSON.parse(t)}catch{throw new o(`--${e} must be JSON.`,2)}if(n.includes("object")||n.includes("array"))try{return JSON.parse(t)}catch{throw new o(`--${e} must be JSON.`,2)}return t}function ge(e,t,r){let n=e.inputSchema?.properties??{},s=new Set(e.inputSchema?.required??[]),i={};if(typeof t.json=="string")try{i=JSON.parse(t.json)}catch{throw new o("--json must be a JSON object.",2)}for(let[c,l]of Object.entries(t)){if(Ke.has(c))continue;let d=W(c);i[d]=ze(c,l,n[d])}let a=typeof t.site=="string"?t.site:r.site;return"projectId"in n&&i.projectId===void 0&&a&&(i.projectId=a),Object.assign(i,Ge(n,s,i,t,r.today)),"requestId"in n&&i.requestId===void 0&&(i.requestId=r.requestId()),i}var M={sites:{tool:"analytics_projects",summary:"List the websites you can read"},snippet:{tool:"install_snippet",summary:"Get the install snippet for a website"},verify:{tool:"install_verify",summary:"Check whether a website is receiving events"},overview:{tool:"analytics_overview",summary:"Traffic overview"},acquisition:{tool:"analytics_acquisition",summary:"Sources, referrers and campaigns"},revenue:{tool:"analytics_revenue",summary:"Revenue and where it came from"},events:{tool:"analytics_events",summary:"Event names and counts"},journeys:{tool:"analytics_journeys",summary:"Recorded sessions"},goals:{tool:"analytics_goals",summary:"Saved goals"},goal:{tool:"analytics_goal",summary:"One goal's report (--goal-id)"},funnels:{tool:"analytics_funnels",summary:"Saved funnels"},funnel:{tool:"analytics_funnel",summary:"One funnel's report (--funnel-id)"},notes:{tool:"notes_list",summary:"Chart notes"}},fe=`tracwell ${S} - Tracwell analytics from your terminal or coding agent
32
+ <body><main class="shell${n?" ok":""}"><div class="card"><div class="icon">${n?qe:Me}</div><h1>${e}</h1><p>${t}</p></div>
33
+ <p class="foot">Tracwell CLI</p></main></body></html>`;function Ke(e,t){return new Promise((n,r)=>{let s=setTimeout(()=>r(new o("Sign-in timed out. Run `tracwell login` again.",3)),Fe);e.on("request",(i,a)=>{let c=new URL(i.url??"/","http://127.0.0.1");if(c.pathname!=="/callback"){a.writeHead(404).end();return}let l=c.searchParams.get("error"),d=c.searchParams.get("code"),p=(I,S,y,m=!1)=>a.writeHead(I,{"content-type":"text/html; charset=utf-8"}).end(Be(S,y,m));if(c.searchParams.get("state")!==t){p(400,"This sign-in link is out of date","Go back to your terminal and run <code>tracwell login</code> again.");return}if(clearTimeout(s),l||!d){p(400,"Sign-in was cancelled","Nothing was connected. Run <code>tracwell login</code> to try again."),r(new o(c.searchParams.get("error_description")??"Sign-in was cancelled.",3));return}p(200,"You're signed in","The Tracwell CLI is connected. You can close this tab and go back to your terminal.",!0),n(d)})})}async function ce(e,t){let{server:n,port:r}=await Je();try{let s=`http://127.0.0.1:${r}/callback`,i=await fetch(`${e}/api/auth/oauth2/register`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_name:"Tracwell CLI",redirect_uris:[s],token_endpoint_auth_method:"none",grant_types:["authorization_code","refresh_token"],response_types:["code"],scope:ie})}).catch(()=>{throw new o(`Could not reach ${e}. Check your connection and try again.`)});if(!i.ok)throw new o(`Could not start sign-in (HTTP ${i.status}).`,3);let{client_id:a}=await i.json(),c=D(se(32)),l=D(se(16)),d=new URL("/api/auth/oauth2/authorize",e);for(let[S,y]of Object.entries({client_id:a,redirect_uri:s,response_type:"code",scope:ie,state:l,code_challenge:D(Ne("sha256").update(c).digest()),code_challenge_method:"S256",resource:`${e}/mcp`}))d.searchParams.set(S,y);t.print("Sign in and pick your websites in the browser:"),t.print(d.href),t.open&&We(d.href);let p=await Ke(n,l),I=await oe(`${e}/api/auth/oauth2/token`,{grant_type:"authorization_code",code:p,client_id:a,redirect_uri:s,code_verifier:c,resource:`${e}/mcp`});return ae(e,a,I)}finally{n.close()}}function le(e,t=Date.now()){return e.expiresAt-De<=t}async function de(e){if(!e.refreshToken)throw new o("Your sign-in expired. Run `tracwell login`.",3);let t=await oe(`${e.baseUrl}/api/auth/oauth2/token`,{grant_type:"refresh_token",refresh_token:e.refreshToken,client_id:e.clientId,resource:`${e.baseUrl}/mcp`});return ae(e.baseUrl,e.clientId,t,e.refreshToken)}async function ue(e){let t=e.refreshToken??e.accessToken;await fetch(`${e.baseUrl}/api/auth/oauth2/revoke`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:t,client_id:e.clientId}).toString()}).catch(()=>{})}function pe(e){let[t]=e;if(e.length===1&&t)return t;if(!e.length)throw new o("This login has no websites. Add one with `tracwell sites add <name> <url>`, or run `tracwell init --url <url>`.",2);let n=e.map(r=>` ${r.name} ${r.id}`).join(`
34
+ `);throw new o(`Pick a website with \`tracwell use <id>\` or pass --site <id>:
35
+ ${n}`,2)}var He=new Set(["json","site","days","compact","key","help","no-browser","dry-run","force"]),R=7,ge=e=>e.toISOString().slice(0,10);function Ge(e,t){let n=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()-(e-1)));return{from:ge(n),to:ge(t)}}function Ye(e,t,n,r,s){let i="from"in e&&"to"in e,a=!i&&"date"in e;if(!i&&!a)return{};if(n.from!==void 0||n.to!==void 0||n.date!==void 0)return{};if(!(t.has("from")||t.has("date")||r.days!==void 0))return{};let c=r.days===void 0?R:Number(r.days);if(!Number.isInteger(c)||c<1)throw new o("--days must be a whole number of 1 or more.",2);let{from:l,to:d}=Ge(c,s);if(a)return{date:l,..."to"in e?{to:d}:{}};if(e.from?.format==="date-time"){let p=new Date(`${d}T00:00:00.000Z`);return p.setUTCDate(p.getUTCDate()+1),{from:`${l}T00:00:00.000Z`,to:p.toISOString()}}return{from:l,to:d}}function ze(e){return e?.type?Array.isArray(e.type)?e.type:[e.type]:[]}function Ze(e,t,n){let r=ze(n);if(t===!0){if(r.length&&!r.includes("boolean"))throw new o(`--${e} needs a value.`,2);return!0}if(r.includes("number")||r.includes("integer")){let s=Number(t);if(Number.isNaN(s))throw new o(`--${e} must be a number.`,2);return s}if(r.includes("boolean"))return t==="true";if(!r.length&&/^\s*[[{]/u.test(t))try{return JSON.parse(t)}catch{throw new o(`--${e} must be JSON.`,2)}if(r.includes("object")||r.includes("array"))try{return JSON.parse(t)}catch{throw new o(`--${e} must be JSON.`,2)}return t}function fe(e,t,n){let r=e.inputSchema?.properties??{},s=new Set(e.inputSchema?.required??[]),i={};if(typeof t.json=="string")try{i=JSON.parse(t.json)}catch{throw new o("--json must be a JSON object.",2)}for(let[c,l]of Object.entries(t)){if(He.has(c))continue;let d=M(c);i[d]=Ze(c,l,r[d])}let a=typeof t.site=="string"?t.site:n.site;return"projectId"in r&&i.projectId===void 0&&a&&(i.projectId=a),Object.assign(i,Ye(r,s,i,t,n.today)),"requestId"in r&&i.requestId===void 0&&(i.requestId=n.requestId()),i}var J={sites:{tool:"analytics_projects",summary:"List the websites you can read"},snippet:{tool:"install_snippet",summary:"Get the install snippet for a website"},verify:{tool:"install_verify",summary:"Check whether a website is receiving events"},overview:{tool:"analytics_overview",summary:"Traffic overview"},acquisition:{tool:"analytics_acquisition",summary:"Sources, referrers and campaigns"},revenue:{tool:"analytics_revenue",summary:"Revenue and where it came from"},events:{tool:"analytics_events",summary:"Event names and counts"},journeys:{tool:"analytics_journeys",summary:"Recorded sessions"},goals:{tool:"analytics_goals",summary:"Saved goals"},goal:{tool:"analytics_goal",summary:"One goal's report (--goal-id)"},funnels:{tool:"analytics_funnels",summary:"Saved funnels"},funnel:{tool:"analytics_funnel",summary:"One funnel's report (--funnel-id)"},notes:{tool:"notes_list",summary:"Chart notes"}},we=`tracwell ${x} - Tracwell analytics from your terminal or coding agent
34
36
 
35
37
  Start here
36
38
  tracwell init [--url <url>] [--name <name>]
@@ -48,7 +50,7 @@ Setup
48
50
  tracwell logout
49
51
 
50
52
  Reports (default: last ${R} days, UTC)
51
- ${Object.entries(M).filter(([e])=>!["sites","snippet","verify"].includes(e)).map(([e,{summary:t}])=>` tracwell ${e.padEnd(22)} ${t}`).join(`
53
+ ${Object.entries(J).filter(([e])=>!["sites","snippet","verify"].includes(e)).map(([e,{summary:t}])=>` tracwell ${e.padEnd(22)} ${t}`).join(`
52
54
  `)}
53
55
 
54
56
  Goals and funnels (need "Manage goals and funnels")
@@ -76,9 +78,9 @@ Common flags
76
78
  --key <key> API key for this command (or set TRACWELL_API_KEY)
77
79
 
78
80
  Output is JSON. Exit codes: 0 ok, 1 request failed, 2 bad input, 3 sign in needed.`,b=e=>{process.stdout.write(`${e}
79
- `)},A=e=>{process.stderr.write(`${e}
80
- `)};function f(e,t){b(typeof e=="string"?e:JSON.stringify(e,null,t.compact?void 0:2))}function Ze(e){if(e.structuredContent!==void 0)return e.structuredContent;let t=(e.content??[]).filter(r=>r.type==="text").map(r=>r.text??"").join(`
81
- `);try{return JSON.parse(t)}catch{return t}}async function u(e){let t=await m(),r=G(process.env,t),n=typeof e.key=="string"?e.key:process.env.TRACWELL_API_KEY;if(n)return{baseUrl:r,token:n,settings:t,viaKey:!0};let s=t.login;if(!s||s.baseUrl!==r)throw new o("Not signed in. Run `tracwell login`, or set TRACWELL_API_KEY.",3);if(s.type==="key")return{baseUrl:r,token:s.key,settings:t,viaKey:!0};if(!le(s))return{baseUrl:r,token:s.accessToken,settings:t,viaKey:!1};let i=await de(s),a={...t,login:i};return await y(a),{baseUrl:r,token:i.accessToken,settings:a,viaKey:!1}}async function we(e){let t=await m(),r=await ce(L(process.env),{open:!e["no-browser"],print:A});await y({...t,login:r})}async function he(e,t){let r=(await k(e.baseUrl,e.token)).find(n=>n.name===t);if(!r)throw new o(`This login cannot use \`${t}\`. Run \`tracwell tools\` to see what it can use.`,2);return r}async function T(e,t,r){let n=await V(e.baseUrl,e.token,t,r),s=Ze(n);if(n.isError)throw new o(typeof s=="string"?s:JSON.stringify(s));return s}async function g(e,t,r,n={}){let s=await he(e,t),i={...ge(s,r,{...e.settings.site?{site:e.settings.site}:{},today:new Date,requestId:me}),...n};if(r["dry-run"])return f({dryRun:!0,tool:t,writes:s.annotations?.readOnlyHint===!1,arguments:i},r);f(await T(e,t,i),r)}function Ve(e,t){let r=e.inputSchema?.properties??{},n=new Set(e.inputSchema?.required??[]),s=Object.entries(r).map(([i,a])=>{let c=Array.isArray(a.type)?a.type.join("|"):a.type??"value",l=i==="projectId"?" (or --site, or `tracwell use`)":i==="requestId"?" (filled in for you)":i==="from"||i==="to"||i==="date"?` (or --days; default last ${R} days)`:"";return` --${i.padEnd(18)} ${c}${n.has(i)?", required":""}${l}${a.description?`
81
+ `)},_=e=>{process.stderr.write(`${e}
82
+ `)};function w(e,t){b(typeof e=="string"?e:JSON.stringify(e,null,t.compact?void 0:2))}function Ve(e){if(e.structuredContent!==void 0)return e.structuredContent;let t=(e.content??[]).filter(n=>n.type==="text").map(n=>n.text??"").join(`
83
+ `);try{return JSON.parse(t)}catch{return t}}async function u(e){let t=await g(),n=G(process.env,t),r=typeof e.key=="string"?e.key:process.env.TRACWELL_API_KEY;if(r)return{baseUrl:n,token:r,settings:t,viaKey:!0};let s=t.login;if(!s||s.baseUrl!==n)throw new o("Not signed in. Run `tracwell login`, or set TRACWELL_API_KEY.",3);if(s.type==="key")return{baseUrl:n,token:s.key,settings:t,viaKey:!0};if(!le(s))return{baseUrl:n,token:s.accessToken,settings:t,viaKey:!1};let i=await de(s),a={...t,login:i};return await h(a),{baseUrl:n,token:i.accessToken,settings:a,viaKey:!1}}async function me(e){let t=await g(),n=await ce(A(process.env),{open:!e["no-browser"],print:_});await h({...t,login:n})}async function ye(e,t){let n=(await k(e.baseUrl,e.token)).find(r=>r.name===t);if(!n)throw new o(`This login cannot use \`${t}\`. Run \`tracwell tools\` to see what it can use.`,2);return n}async function v(e,t,n){let r=await V(e.baseUrl,e.token,t,n),s=Ve(r);if(r.isError)throw new o(typeof s=="string"?s:JSON.stringify(s));return s}async function Xe(e){let{projects:t}=await v(e,"analytics_projects",{limit:100}),n=pe(t);return await h({...await g(),site:n.id}),_(`Using ${n.name} (${n.id}), now your default website.`),n.id}async function f(e,t,n,r={}){let s=await ye(e,t),i=e.settings.site;(s.inputSchema?.required??[]).includes("projectId")&&typeof n.site!="string"&&!i&&r.projectId===void 0&&!(typeof n.json=="string"&&n.json.includes("projectId"))&&n.projectId===void 0&&n["project-id"]===void 0&&(i=await Xe(e));let c={...fe(s,n,{...i?{site:i}:{},today:new Date,requestId:he}),...r};if(n["dry-run"])return w({dryRun:!0,tool:t,writes:s.annotations?.readOnlyHint===!1,arguments:c},n);w(await v(e,t,c),n)}function Qe(e,t){let n=e.inputSchema?.properties??{},r=new Set(e.inputSchema?.required??[]),s=Object.entries(n).map(([i,a])=>{let c=Array.isArray(a.type)?a.type.join("|"):a.type??"value",l=i==="projectId"?" (or --site, or `tracwell use`)":i==="requestId"?" (filled in for you)":i==="from"||i==="to"||i==="date"?` (or --days; default last ${R} days)`:"";return` --${i.padEnd(18)} ${c}${r.has(i)?", required":""}${l}${a.description?`
82
84
  ${a.description}`:""}`});return[`tracwell ${t} (${e.name})`,"",e.description??"","","Arguments",...s].join(`
83
- `)}async function Xe(e,t){let r=async l=>await T(e,"install_snippet",{projectId:l});if(typeof t.site=="string")return{installation:await r(t.site),created:!1};let{projects:n}=await T(e,"analytics_projects",{limit:100}),s=typeof t.url=="string"?t.url:void 0;if(s){let l;try{l=new URL(s).origin}catch{throw new o("--url must be a full address, like https://acme.com.",2)}let p=(await Promise.all(n.map(w=>r(w.id)))).find(w=>w.origins.some(ye=>new URL(ye).origin===l));if(p)return{installation:p,created:!1};if(!(await k(e.baseUrl,e.token)).some(w=>w.name==="website_create"))throw new o(e.viaKey?'API keys cannot add websites. Run `tracwell login` and tick "Add websites", or add it in the dashboard and pass --site.':'This login cannot add websites. Run `tracwell login` again and tick "Add websites".',3);let v=typeof t.name=="string"?t.name:new URL(s).hostname.replace(/^www\./u,""),h=await T(e,"website_create",{requestId:me(),name:v,websiteUrl:s});return{installation:h.website,created:h.created}}let i=n.find(l=>l.id===e.settings.site),a=n.length===1?n[0]:void 0,c=i??a;if(c)return{installation:await r(c.id),created:!1};throw n.length?new o(`Several websites: ${n.map(l=>`${l.name} (${l.id})`).join(", ")}. Pass --site <id> or --url <url>.`,2):new o("No websites yet. Run `tracwell init --url https://your-site.com` to add one.",2)}function J(e){return e.map(t=>`${t.path}: ${t.status==="skipped"?"exists and differs; rerun with --force to replace":t.status}`)}async function Qe(e){let{command:t,positional:r,flags:n}=q(e);if(n.version||t==="version")return b(S);if(!t||t==="help"&&!r[0])return b(fe);if(t==="help"||n.help&&!["skill","agents-md","init","search"].includes(t)){let s=t==="help"?r:[t,...r],i=s[0],a={goals:"goal_create",funnels:"funnel_create",sites:"website_create"},c=(s[1]==="add"?a[i]:void 0)??M[i]?.tool??(i==="call"?s[1]:i);if(!c)return b(fe);let l=await u(n),d=s[1]==="add"&&a[i]?`${i} add`:i;return b(Ve(await he(l,c),d))}switch(t){case"login":{if(typeof n.key=="string"){let s=await m(),i=L(process.env);await k(i,n.key),await y({...s,login:{type:"key",baseUrl:i,key:n.key}})}else await we(n);A("Signed in. Run `tracwell sites` to see your websites.");return}case"logout":{let s=await m();s.login?.type==="oauth"&&await ue(s.login),await K(),A("Signed out.");return}case"use":{let s=r[0];if(!s)throw new o("Usage: tracwell use <website-id>. Run `tracwell sites` for IDs.",2);let i=await m();await y({...i,site:s}),A(`Default website set to ${s}.`);return}case"whoami":{let s=await u(n);return f({baseUrl:s.baseUrl,login:s.viaKey?"api key":"browser",defaultSite:s.settings.site??null,websites:await T(s,"analytics_projects",{})},n)}case"tools":{let s=await u(n),i=await k(s.baseUrl,s.token);return f(i.map(a=>({name:a.name,description:a.description,writes:a.annotations?.readOnlyHint===!1})),n)}case"search":{let s=r.join(" ");if(!s)throw new o("Usage: tracwell search <words>",2);let i=await u(n),a=Z(await k(i.baseUrl,i.token),s);return f(a.map(c=>({name:c.name,description:c.description,writes:c.annotations?.readOnlyHint===!1})),n)}case"call":{let s=r[0];if(!s)throw new o("Usage: tracwell call <tool> [--arg value ...]. Run `tracwell tools` for names.",2);return g(await u(n),s,n)}case"goals":{if(r[0]!=="add")return g(await u(n),"analytics_goals",n);let s=r[1];if(!s)throw new o("Usage: tracwell goals add <name> --page /pricing (or --event signup_completed)",2);let i=X(n);return g(await u(n),"goal_create",j(n,["page","event"]),{name:s,match:i,...n.category===void 0?{category:"other"}:{}})}case"funnels":{if(r[0]!=="add")return g(await u(n),"analytics_funnels",n);let s=r[1];if(!s)throw new o("Usage: tracwell funnels add <name> --steps /,/pricing,event:checkout_started",2);let i=Q(n.steps),a=n["window-hours"]!==void 0||n.windowHours!==void 0;return g(await u(n),"funnel_create",j(n,["steps"]),{name:s,steps:i,...a?{}:{windowHours:24}})}case"sites":{if(r[0]==="add"){let[,s,i]=r;if(!s||!i)throw new o("Usage: tracwell sites add <name> <url>",2);return g(await u(n),"website_create",n,{name:s,websiteUrl:i})}return g(await u(n),"analytics_projects",n)}case"skill":{if(!r[0])return b(await N());if(r[0]!=="install")throw new o("Usage: tracwell skill [install [--force]]",2);return f({installed:J(await E(process.cwd(),!!n.force))},n)}case"agents-md":{if(r[0]!=="install")throw new o("Usage: tracwell agents-md install [--file AGENTS.md]",2);let s=typeof n.file=="string"?n.file:"AGENTS.md";return f({installed:J([await F(process.cwd(),s)])},n)}case"init":{let s;try{s=await u(n)}catch(l){if(!(l instanceof o)||l.exitCode!==3)throw l;await we(n),s=await u(n)}let{installation:i,created:a}=await Xe(s,n);await y({...await m(),site:i.projectId});let c=[...await E(process.cwd(),!!n.force),await F(process.cwd())];return f({website:{id:i.projectId,name:i.name,origins:i.origins,created:a},snippet:{script:i.script,npm:i.npm,placement:i.placement},installed:J(c),next:"Add the script tag (or the npm setup) to the site, load a page, then run `tracwell verify`."},n)}default:{let s=M[t];if(!s)throw new o(`Unknown command \`${t}\`. Run \`tracwell help\`.`,2);return g(await u(n),s.tool,n)}}}Qe(process.argv.slice(2)).catch(e=>{let t=e instanceof o;process.stderr.write(`tracwell: ${t||e instanceof Error?e.message:String(e)}
85
+ `)}async function et(e,t){let n=async l=>await v(e,"install_snippet",{projectId:l});if(typeof t.site=="string")return{installation:await n(t.site),created:!1};let{projects:r}=await v(e,"analytics_projects",{limit:100}),s=typeof t.url=="string"?t.url:void 0;if(s){let l;try{l=new URL(s).origin}catch{throw new o("--url must be a full address, like https://acme.com.",2)}let p=(await Promise.all(r.map(m=>n(m.id)))).find(m=>m.origins.some(ke=>new URL(ke).origin===l));if(p)return{installation:p,created:!1};if(!(await k(e.baseUrl,e.token)).some(m=>m.name==="website_create"))throw new o(e.viaKey?'API keys cannot add websites. Run `tracwell login` and tick "Add websites", or add it in the dashboard and pass --site.':'This login cannot add websites. Run `tracwell login` again and tick "Add websites".',3);let S=typeof t.name=="string"?t.name:new URL(s).hostname.replace(/^www\./u,""),y=await v(e,"website_create",{requestId:he(),name:S,websiteUrl:s});return{installation:y.website,created:y.created}}let i=r.find(l=>l.id===e.settings.site),a=r.length===1?r[0]:void 0,c=i??a;if(c)return{installation:await n(c.id),created:!1};throw r.length?new o(`Several websites: ${r.map(l=>`${l.name} (${l.id})`).join(", ")}. Pass --site <id> or --url <url>.`,2):new o("No websites yet. Run `tracwell init --url https://your-site.com` to add one.",2)}function W(e){return e.map(t=>`${t.path}: ${t.status==="skipped"?"exists and differs; rerun with --force to replace":t.status}`)}async function tt(e){let{command:t,positional:n,flags:r}=q(e);if(r.version||t==="version")return b(x);if(!t||t==="help"&&!n[0])return b(we);if(t==="help"||r.help&&!["skill","agents-md","init","search"].includes(t)){let s=t==="help"?n:[t,...n],i=s[0],a={goals:"goal_create",funnels:"funnel_create",sites:"website_create"},c=(s[1]==="add"?a[i]:void 0)??J[i]?.tool??(i==="call"?s[1]:i);if(!c)return b(we);let l=await u(r),d=s[1]==="add"&&a[i]?`${i} add`:i;return b(Qe(await ye(l,c),d))}switch(t){case"login":{if(typeof r.key=="string"){let s=await g(),i=A(process.env);await k(i,r.key),await h({...s,login:{type:"key",baseUrl:i,key:r.key}})}else await me(r);_("Signed in. Run `tracwell sites` to see your websites.");return}case"logout":{let s=await g();s.login?.type==="oauth"&&await ue(s.login),await K(),_("Signed out.");return}case"use":{let s=n[0];if(!s)throw new o("Usage: tracwell use <website-id>. Run `tracwell sites` for IDs.",2);let i=await g();await h({...i,site:s}),_(`Default website set to ${s}.`);return}case"whoami":{let s=await u(r);return w({baseUrl:s.baseUrl,login:s.viaKey?"api key":"browser",defaultSite:s.settings.site??null,websites:await v(s,"analytics_projects",{})},r)}case"tools":{let s=await u(r),i=await k(s.baseUrl,s.token);return w(i.map(a=>({name:a.name,description:a.description,writes:a.annotations?.readOnlyHint===!1})),r)}case"search":{let s=n.join(" ");if(!s)throw new o("Usage: tracwell search <words>",2);let i=await u(r),a=Z(await k(i.baseUrl,i.token),s);return w(a.map(c=>({name:c.name,description:c.description,writes:c.annotations?.readOnlyHint===!1})),r)}case"call":{let s=n[0];if(!s)throw new o("Usage: tracwell call <tool> [--arg value ...]. Run `tracwell tools` for names.",2);return f(await u(r),s,r)}case"goals":{if(n[0]!=="add")return f(await u(r),"analytics_goals",r);let s=n[1];if(!s)throw new o("Usage: tracwell goals add <name> --page /pricing (or --event signup_completed)",2);let i=X(r);return f(await u(r),"goal_create",L(r,["page","event"]),{name:s,match:i,...r.category===void 0?{category:"other"}:{}})}case"funnels":{if(n[0]!=="add")return f(await u(r),"analytics_funnels",r);let s=n[1];if(!s)throw new o("Usage: tracwell funnels add <name> --steps /,/pricing,event:checkout_started",2);let i=Q(r.steps),a=r["window-hours"]!==void 0||r.windowHours!==void 0;return f(await u(r),"funnel_create",L(r,["steps"]),{name:s,steps:i,...a?{}:{windowHours:24}})}case"sites":{if(n[0]==="add"){let[,s,i]=n;if(!s||!i)throw new o("Usage: tracwell sites add <name> <url>",2);return f(await u(r),"website_create",r,{name:s,websiteUrl:i})}return f(await u(r),"analytics_projects",r)}case"skill":{if(!n[0])return b(await N());if(n[0]!=="install")throw new o("Usage: tracwell skill [install [--force]]",2);return w({installed:W(await E(process.cwd(),!!r.force))},r)}case"agents-md":{if(n[0]!=="install")throw new o("Usage: tracwell agents-md install [--file AGENTS.md]",2);let s=typeof r.file=="string"?r.file:"AGENTS.md";return w({installed:W([await F(process.cwd(),s)])},r)}case"init":{let s;try{s=await u(r)}catch(l){if(!(l instanceof o)||l.exitCode!==3)throw l;await me(r),s=await u(r)}let{installation:i,created:a}=await et(s,r);await h({...await g(),site:i.projectId});let c=[...await E(process.cwd(),!!r.force),await F(process.cwd())];return w({website:{id:i.projectId,name:i.name,origins:i.origins,created:a},snippet:{script:i.script,npm:i.npm,placement:i.placement},installed:W(c),next:"Add the script tag (or the npm setup) to the site, load a page, then run `tracwell verify`."},r)}default:{let s=J[t];if(!s)throw new o(`Unknown command \`${t}\`. Run \`tracwell help\`.`,2);return f(await u(r),s.tool,r)}}}tt(process.argv.slice(2)).catch(e=>{let t=e instanceof o;process.stderr.write(`tracwell: ${t||e instanceof Error?e.message:String(e)}
84
86
  `),process.exitCode=t?e.exitCode:1});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracwell",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Privacy-aware browser analytics, optional server-side AI crawler tracking, and the tracwell CLI for coding agents",
5
5
  "license": "MIT",
6
6
  "type": "module",