@zenovay/cli 1.0.5 → 1.0.6

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.
@@ -0,0 +1,317 @@
1
+ import{readConfig as e,updateConfig as t}from"./config-JVhBplSX.js";import{ApiClient as n,dashboardUrlForSite as r}from"./api-CdcB84ie.js";import{readToken as i}from"./token-store-BvSCse1t.js";import{emit$1 as a,isHeadless as o}from"./emit-D4YlMlzk.js";import{openInBrowser as s}from"./open-url-BtI5aLgL.js";import{scrubSecrets as c}from"./secrets-ApB7yh2A.js";import{parseSseStream as l}from"./client-ntsEXSVC.js";import{loginCommand as u,maybeInstallCompletions as d}from"./login-DU1fY88U.js";import{Banner as f,Panel as p,useTheme as m}from"./panel-CTtQ2ue6.js";import{DiffView as h,ProgressRow as g}from"./progress-row-Afnt5Ng8.js";import{Select as _}from"./prompt-BXvg2B4R.js";import{Keybar as v}from"./keybar-DO5OoZy9.js";import{spark as y}from"./sparkline-CpoBjjlc.js";import b from"node:path";import{promises as x}from"node:fs";import{z as S}from"zod";import C from"node:os";import w from"node:crypto";import{ulid as T}from"ulid";import{Box as E,Text as D,render as ee,useApp as te,useInput as O}from"ink";import k from"node:fs/promises";import A,{useEffect as j,useRef as ne,useState as M}from"react";import{globby as N}from"globby";import P from"ink-spinner";import{applyPatch as F}from"diff";const re=[`@zenovay/tracker`,`@zenovay/next`,`@zenovay/nuxt`,`@zenovay/svelte`,`@zenovay/astro`,`@zenovay/remix`,`@zenovay/node`],I=[`zenovay.config.ts`,`zenovay.config.js`,`zenovay.config.mjs`,`zenovay.config.json`],ie=/<[Ss]cript\b[^>]*\bsrc\s*=\s*["'`]https:\/\/(?:api|cdn)\.zenovay\.com\/[^"'`]+["'`][^>]*>/gi,L=/["'`]https:\/\/(?:api|cdn)\.zenovay\.com\/[^"'`]*z\.js[^"'`]*["'`]/gi,ae=/["']?data-?[Ii]d["']?\s*[=:]\s*["'`](ZV_[A-Za-z0-9]+)["'`]/i,oe=/data-zenovay-site\s*[=:]\s*["'`]([^"'`]+)["'`]/i,se=/\binit\s*\(\s*['"`](ZV_[A-Za-z0-9]+)['"`]/g,ce=/<[Ss]cript\b[^>]*\bsrc\s*=\s*["'`][^"'`]*\/_z\/script\.js[^"'`]*["'`][^>]*>/gi,R=/\/_z\/script\.js[^"'`]*[?&]id=(ZV_[A-Za-z0-9]+)/i,z=[{name:`PostHog`,patterns:[/posthog\.com\b/i,/posthog-js/,/window\.posthog/]},{name:`Plausible`,patterns:[/plausible\.io\/js/i,/window\.plausible/]},{name:`Google Analytics`,patterns:[/googletagmanager\.com\/gtag\/js/i,/www\.google-analytics\.com\/analytics\.js/i,/window\.gtag\b/]},{name:`Mixpanel`,patterns:[/cdn\.mxpnl\.com/i,/mixpanel-browser/]},{name:`Segment`,patterns:[/cdn\.segment\.com\/analytics\.js/i,/window\.analytics\b/]},{name:`Hotjar`,patterns:[/static\.hotjar\.com/i,/\(h,o,t,j,a,r\)/i]},{name:`Amplitude`,patterns:[/cdn\.amplitude\.com/i,/amplitude\.getInstance/]},{name:`Heap`,patterns:[/cdn\.heapanalytics\.com/i]},{name:`FullStory`,patterns:[/fullstory\.com\/s\/fs\.js/i,/window\.FS\.identify/]}];async function B(e,t){let n={zenovayInstalled:!1,correctTrackingCode:!1,foundTrackingCodes:[],zenovayFiles:[],competitors:[],summary:``,recommendation:`fresh-install`};try{let t=await x.readFile(b.join(e,`package.json`),`utf8`),r=JSON.parse(t),i={...r.dependencies,...r.devDependencies};for(let e of re)if(e in i){n.zenovayInstalled=!0,n.packageMarker=e;break}}catch{}for(let t of I)try{await x.access(b.join(e,t)),n.zenovayInstalled=!0,n.configFile=t;break}catch{}let r=await N([`**/*.{html,htm,tsx,jsx,vue,svelte,astro,ts,js,mjs}`,`!node_modules/**`,`!.git/**`,`!dist/**`,`!.next/**`,`!.nuxt/**`,`!build/**`,`!.output/**`],{cwd:e,gitignore:!0,deep:4,absolute:!0}).catch(()=>[]),i=new Map,a=new Set;for(let t of r.slice(0,300)){let r;try{r=await x.readFile(t,`utf8`)}catch{continue}let o=b.relative(e,t);ie.lastIndex=0,L.lastIndex=0,ce.lastIndex=0;let s=r.match(ie),c=r.match(L),l=r.match(ce),u=(s?.length??0)+(c?.length??0)+(l?.length??0);if(u>0){n.zenovayInstalled=!0;let e=(s?.[0]??c?.[0]??l?.[0]??``).slice(0,200),t=r.match(ae)??r.match(oe)??r.match(R),i=t?.[1]??null;i&&a.add(i),n.zenovayFiles.push({file:o,currentTrackingCode:i,scriptTagCount:u,matchSample:e})}if(u===0&&/\.[jt]sx?$/.test(t)){se.lastIndex=0;let e;for(;(e=se.exec(r))!==null;){let t=e[1];a.add(t),n.zenovayInstalled=!0,n.zenovayFiles.push({file:o,currentTrackingCode:t,scriptTagCount:0,matchSample:e[0].slice(0,80)})}}for(let e of z)if(e.patterns.some(e=>e.test(r))){let t=i.get(e.name)??new Set;t.add(o),i.set(e.name,t)}}if(n.foundTrackingCodes=Array.from(a),n.competitors=Array.from(i.entries()).map(([e,t])=>({name:e,files:Array.from(t).slice(0,5)})),n.zenovayInstalled)if(n.foundTrackingCodes.length>0){let e=n.foundTrackingCodes.every(e=>e===t);n.correctTrackingCode=e,n.recommendation=e?`no-op`:`update-tracking-code`}else n.correctTrackingCode=!1,n.recommendation=`no-op`;else n.competitors.length>0?n.recommendation=`fresh-install-alongside-competitor`:n.recommendation=`fresh-install`;return n.summary=V(n,t),n}function V(e,t){let n=[];return e.zenovayInstalled?e.foundTrackingCodes.length===0?n.push(`Zenovay package or config detected but no <script> tag found.`):e.correctTrackingCode?n.push(`Zenovay tracker present in ${e.zenovayFiles.length} file(s) with correct ID ${t}.`):n.push(`Zenovay tracker present in ${e.zenovayFiles.length} file(s), but with mismatching ID(s): ${e.foundTrackingCodes.join(`, `)} (target: ${t}).`):n.push(`No Zenovay tracker found.`),e.competitors.length>0&&n.push(`Competitor tracker(s) present (DO NOT modify): ${e.competitors.map(e=>e.name).join(`, `)}.`),n.join(` `)}async function le(e){let t=await B(e,``);return{installed:t.zenovayInstalled,reasons:t.summary?[t.summary]:[],packageMarker:t.packageMarker,configFile:t.configFile,scriptFile:t.zenovayFiles[0]?.file}}const ue=[/^package\.json$/,/^package-lock\.json$/,/^pnpm-lock\.yaml$/,/^yarn\.lock$/,/^bun\.lock$/,/^tsconfig.*\.json$/,/^(next|nuxt|vite|astro|remix|svelte|tailwind|postcss)\.config\.(ts|mts|cts|js|mjs|cjs)$/,/^app\/.*\.(tsx|ts|jsx|js|svelte|astro|vue)$/,/^pages\/.*\.(tsx|ts|jsx|js)$/,/^src\/app\/.*\.(tsx|ts|jsx|js)$/,/^src\/pages\/.*\.(tsx|ts|jsx|js)$/,/^src\/components\/.*\.(astro|svelte|vue|tsx|ts|jsx|js)$/,/^src\/(root|app|entry-server|entry-client)\.(tsx|ts|jsx|js)$/,/^gatsby-(ssr|browser|config)\.(js|ts|mjs)$/,/\.html?$/,/^src\/main\.(tsx|ts|jsx|js)$/,/^src\/App\.(tsx|ts|jsx|js|svelte|vue)$/,/^src\/routes\/\+layout\.(svelte|ts|js)$/,/^src\/routes\/root\..*$/,/^src\/layouts\/.*\.(astro|svelte|vue)$/,/^\.env\.example$/,/^README\.md$/,/^\.gitignore$/,/^nuxt\.config\.(ts|js|mjs)$/,/^plugins\/.*\.(ts|js)$/],de=[/(^|\/)\.env($|\.(?!example$)[^/]+$)/,/(^|\/)\.git(\/|$)/,/(^|\/)node_modules\//,/(^|\/)\.ssh\//,/(^|\/)\.aws\//,/\.pem$/,/\.key$/,/(^|\/)id_rsa($|\.)/,/(^|\/)\.npmrc$/,/(^|\/)credentials(\.|$)/],H=256*1024,fe=20,pe=20,me=20;var U=class extends Error{constructor(e,t){super(t),this.code=e,this.name=`SandboxError`}};const W=/<script[^>]*src\s*=\s*["'](?:https:\/\/(?:api|cdn)\.zenovay\.com\/[^"']+|[^"']*\/_z\/script\.js[^"']*)["'][^>]*>\s*<\/script>/i,he=[{name:`PostHog`,re:/posthog\.com\b|posthog-js|window\.posthog/i},{name:`Plausible`,re:/plausible\.io\/js|window\.plausible/i},{name:`Google Analytics`,re:/googletagmanager\.com\/gtag\/js|google-analytics\.com\/analytics\.js|window\.gtag\b/i},{name:`Mixpanel`,re:/cdn\.mxpnl\.com|mixpanel-browser/i},{name:`Segment`,re:/cdn\.segment\.com\/analytics\.js|window\.analytics\b/i},{name:`Hotjar`,re:/static\.hotjar\.com|\(h,o,t,j,a,r\)/i},{name:`Amplitude`,re:/cdn\.amplitude\.com|amplitude\.getInstance/i},{name:`Heap`,re:/cdn\.heapanalytics\.com/i},{name:`FullStory`,re:/fullstory\.com\/s\/fs\.js|window\.FS\.identify/i}];var ge=class{sessionId;cwd;cwdReal;undoRoot;readCount=0;writeCount=0;roundCount=0;writtenPathsSet=new Set;constructor(e){this.sessionId=e.sessionId??T(),this.cwd=e.cwd,this.cwdReal=x.realpath(e.cwd),this.undoRoot=e.undoRoot??b.join(C.homedir(),`.zenovay`,`undo`)}incrementRound(){if(this.roundCount++,this.roundCount>me)throw new U(`round-cap`,`Tool-call recursion cap (${me}) exceeded.`)}stats(){return{sessionId:this.sessionId,reads:this.readCount,writes:this.writeCount,rounds:this.roundCount,writtenPaths:[...this.writtenPathsSet]}}async resolveForRead(e){return this.resolveSafe(e,`read`)}async resolveForWrite(e){return this.resolveSafe(e,`write`)}async resolveSafe(e,t){if(typeof e!=`string`||e.length===0)throw new U(`escape`,`Path must be a non-empty string.`);if(e.includes(`\0`))throw new U(`escape`,`Null byte in path.`);if(/^\\\\[?.]\\/.test(e)||/^\\\\[A-Za-z0-9]/.test(e))throw new U(`escape`,`Windows special path prefix: ${e}`);if(/%/.test(e))try{let t=decodeURIComponent(e);if(/%[0-9A-Fa-f]{2}/.test(t))throw new U(`escape`,`Double-encoded path: ${e}`);e=t}catch(t){throw t instanceof U?t:new U(`escape`,`Malformed percent-encoding: ${e}`)}if(e.includes(`\0`))throw new U(`escape`,`Null byte in decoded path.`);if(e.includes(`\\`))throw new U(`escape`,`Backslash in path (Windows-style separator not accepted): ${e}`);let n=await this.cwdReal,r=b.isAbsolute(e)?e:b.join(n,e),i=b.normalize(r),a=i;try{a=await x.realpath(i)}catch(e){if(e.code!==`ENOENT`)throw new U(`escape`,`realpath failed: ${e.message}`);let t=b.dirname(i);try{let e=await x.realpath(t);a=b.join(e,b.basename(i))}catch{}}if(!this.isWithin(a,n))throw new U(`escape`,`Path escapes cwd: ${e}`);let o=b.relative(n,a),s=o.toLowerCase();for(let e of de)if(e.test(o)||e.test(s)){if(t===`read`&&/(^|\/)node_modules\//i.test(o))continue;throw new U(`blocklisted`,`Path is on the hard blocklist: ${o}`)}if(t===`write`){let e=ue.some(e=>e.test(o));if(!e&&!/^\.env(\.local)?$/.test(o)&&!/^\.claude\//.test(o)&&!/^\.cursor\//.test(o))throw new U(`not-allowlisted`,`Write path not on allowlist: ${o}`)}return a}isWithin(e,t){let n=b.relative(t,e);return!n.startsWith(`..`)&&!b.isAbsolute(n)}async safeRead(e){if(this.readCount>=fe)throw new U(`read-cap`,`Read cap (${fe}) reached.`);let t=await this.resolveForRead(e),n=await x.stat(t);if(n.size>H)throw new U(`too-large`,`File exceeds ${H}B: ${e} (${n.size}B)`);let r=await x.readFile(t),i=r.subarray(0,Math.min(r.length,8192));if(i.includes(0))throw new U(`binary-rejected`,`Binary content in ${e}.`);return this.readCount++,r.toString(`utf8`)}async safeWrite(e,t){if(this.writeCount>=pe)throw new U(`write-cap`,`Write cap (${pe}) reached.`);if(t.length>H)throw new U(`too-large`,`Write exceeds ${H}B: ${e}`);let n=await this.resolveForWrite(e),r=await x.readFile(n,`utf8`).catch(e=>{if(e.code===`ENOENT`)return null;throw e});if(r!==null){if(W.test(r)&&!W.test(t))throw new U(`destructive-tracker-removal`,`Refusing to write ${e}: existing Zenovay tracker tag would be removed. If you want to change the data-id, use patch_file to swap the attribute value instead of overwriting the file.`);for(let{name:n,re:i}of he)if(i.test(r)&&!i.test(t))throw new U(`destructive-competitor-removal`,`Refusing to write ${e}: existing ${n} tracker would be removed. Zenovay installs alongside competitors — never replace them.`)}let i=await this.journalBackup(n);await x.mkdir(b.dirname(n),{recursive:!0}),await x.writeFile(n,t,`utf8`),this.writeCount++;let a=await this.cwdReal;return this.writtenPathsSet.add(b.relative(a,n)),{backupPath:i}}async journalBackup(e){let t=await x.readFile(e).catch(e=>{if(e.code===`ENOENT`)return null;throw e}),n=b.join(this.undoRoot,this.sessionId);await x.mkdir(n,{recursive:!0,mode:448});let r=w.createHash(`sha256`).update(e).digest(`hex`).slice(0,16),i=b.join(n,`${r}.bak`),a=b.join(n,`manifest.jsonl`);t&&await x.writeFile(i,t,{mode:384});let o=JSON.stringify({abs:e,backupPath:t?i:null,ts:Date.now()});return await x.appendFile(a,o+`
2
+ `,{mode:384}),t?i:null}static wrapForModel(e,t){let n=w.createHash(`sha256`).update(t).digest(`hex`).slice(0,16),r=t.replaceAll(`&`,`&amp;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`);return`<FILE path="${e}" sha256="${n}">\n${r}\n</FILE>`}static scrubSecrets(e){return c(e)}};async function _e(e){let t=await e.readFile(`package.json`),n=!1;if(t)try{let e=JSON.parse(t);n=!!(e.dependencies?.[`@angular/core`]??e.devDependencies?.[`@angular/core`])}catch{}let r=await e.exists(`angular.json`)||await e.exists(`.angular-cli.json`);if(n||r){let t=await e.exists(`src/index.html`)?`src/index.html`:`index.html`;return{matched:!0,confidence:.9,reason:`Detected Angular via ${n?"`@angular/core`":"`angular.json`"} (shell: ${t}).`}}return{matched:!1,confidence:0,reason:`No Angular dependency or angular.json found.`}}function ve(e){let t=e.trackingCode??e.siteId??``,n=e.targetPath||`src/index.html`,r=`<script defer\n data-id="${t}"\n data-allow-localhost="true"\n src="https://api.zenovay.com/z.js">\n</script>`;return[{targetPath:n,anchorKind:`head-tag`,code:r,description:`Inject the Zenovay tracker inside <head> of the Angular index.html shell.`}]}const ye={id:`angular`,displayName:`Angular`,detect:_e,files:[{path:`src/index.html`,kind:`mutate`,required:!0}],envVarName:`NG_ZENOVAY_SITE`,buildSnippets:ve},be=[`astro.config.ts`,`astro.config.mjs`,`astro.config.js`];async function xe(e){for(let t of be)if(await e.exists(t))return{matched:!0,confidence:.95,reason:`Detected Astro via \`${t}\`.`};let t=await e.readFile(`package.json`);if(t)try{let e=JSON.parse(t);if(e.dependencies?.astro??e.devDependencies?.astro)return{matched:!0,confidence:.8,reason:"Detected Astro via `astro` dependency."}}catch{}return{matched:!1,confidence:0,reason:"No astro.config.* or `astro` dependency found."}}function Se(e){let t=e.trackingCode??e.siteId??``,n=`<script defer data-id="${t}" data-allow-localhost="true" src="https://api.zenovay.com/z.js" is:inline></script>`;return[{targetPath:`src/layouts/Layout.astro`,anchorKind:`head-tag`,code:n,description:`Inject the Zenovay tracker inside <head> of the primary layout.`},{targetPath:`src/layouts/BaseHead.astro`,anchorKind:`head-tag`,code:n,description:"Fallback target when the project uses a dedicated `BaseHead.astro`."}]}const Ce={id:`astro`,displayName:`Astro`,detect:xe,files:[{path:`src/layouts/Layout.astro`,kind:`mutate`,required:!1},{path:`src/layouts/BaseHead.astro`,kind:`mutate`,required:!1},{path:`.env`,kind:`create`,required:!0}],envVarName:`PUBLIC_ZENOVAY_SITE`,buildSnippets:Se};async function we(e){let t=await e.readFile(`package.json`);if(t)try{let e=JSON.parse(t);if(e.dependencies?.gatsby??e.devDependencies?.gatsby)return!0}catch{}return await e.exists(`gatsby-config.js`)||await e.exists(`gatsby-config.ts`)}async function Te(e){return await we(e)?{matched:!0,confidence:.95,reason:"Detected Gatsby via `gatsby` dependency / `gatsby-config`."}:{matched:!1,confidence:0,reason:`No Gatsby dependency or config found.`}}function Ee(e){let t=e.trackingCode??e.siteId??``,n=`const React = require('react');
3
+
4
+ exports.onRenderBody = ({ setHeadComponents }) => {
5
+ setHeadComponents([
6
+ React.createElement('script', {
7
+ key: 'zenovay-tracker',
8
+ defer: true,
9
+ 'data-id': '${t}',
10
+ 'data-allow-localhost': 'true',
11
+ src: 'https://api.zenovay.com/z.js',
12
+ }),
13
+ ]);
14
+ };
15
+ `;return[{targetPath:`gatsby-ssr.js`,anchorKind:`replace-file`,code:n,description:`Add the Zenovay tracker via Gatsby SSR onRenderBody → setHeadComponents.`}]}const De={id:`gatsby`,displayName:`Gatsby`,detect:Te,files:[{path:`gatsby-ssr.js`,kind:`create`,required:!0}],envVarName:`GATSBY_ZENOVAY_SITE`,buildSnippets:Ee},Oe=[`app/layout.tsx`,`app/layout.ts`,`app/layout.jsx`,`app/layout.js`,`src/app/layout.tsx`,`src/app/layout.ts`,`src/app/layout.jsx`,`src/app/layout.js`];async function ke(e){for(let t of Oe)if(await e.exists(t))return t;return null}async function Ae(e){let t=await e.readFile(`package.json`);if(!t)return!1;try{let e=JSON.parse(t);return!!(e.dependencies?.next??e.devDependencies?.next)}catch{return!1}}async function je(e){let t=await Ae(e),n=await e.exists(`app`)||await e.exists(`src/app`),r=await ke(e);return t?n?r?{matched:!0,confidence:.95,reason:`Detected Next.js App Router via \`next\` dep + \`${r}\`.`}:{matched:!1,confidence:.4,reason:"Found `app/` but no root layout file (layout.tsx/ts/jsx/js)."}:{matched:!1,confidence:0,reason:"No `app/` directory found — not an App Router project."}:{matched:!1,confidence:0,reason:"package.json does not list `next` as a dependency."}}function Me(e,t=`script`){let n=e.targetPath||`app/layout.tsx`,r=e.trackingCode??e.siteId??``;return t===`npm`?Pe(n,r):Ne(n,r)}function Ne(e,t){return[{targetPath:e,anchorKind:`after-imports`,code:`import Script from 'next/script';`,description:`Import next/script to inject the Zenovay tracker.`},{targetPath:e,anchorKind:`wrap-children`,code:`<Script
16
+ defer
17
+ data-id="${t}"
18
+ data-allow-localhost="true"
19
+ src="https://api.zenovay.com/z.js"
20
+ strategy="afterInteractive"
21
+ />
22
+ {children}`,description:`Add the Zenovay tracker <Script> next to children in the root layout.`}]}function Pe(e,t){return[{targetPath:`components/zenovay-bootstrap.tsx`,anchorKind:`replace-file`,code:`'use client';
23
+
24
+ import { useEffect } from 'react';
25
+ import { init } from '@zenovay/tracker';
26
+
27
+ /**
28
+ * Mounts the Zenovay tracker exactly once on the client. Equivalent to the
29
+ * canonical <script src="https://api.zenovay.com/z.js"> tag — init() injects
30
+ * the same loader internally.
31
+ */
32
+ export function ZenovayBootstrap() {
33
+ useEffect(() => {
34
+ init('${t}', { allowLocalhost: true });
35
+ }, []);
36
+ return null;
37
+ }
38
+ `,description:`Create a client-component bootstrap that calls init() from @zenovay/tracker.`},{targetPath:e,anchorKind:`after-imports`,code:`import { ZenovayBootstrap } from '@/components/zenovay-bootstrap';`,description:`Import the Zenovay tracker bootstrap into the root layout.`},{targetPath:e,anchorKind:`wrap-children`,code:`<ZenovayBootstrap />
39
+ {children}`,description:`Render the Zenovay tracker bootstrap alongside children in the root layout.`}]}const Fe={id:`next-app-router`,displayName:`Next.js (App Router)`,detect:je,files:[{path:`app/layout.tsx`,kind:`mutate`,required:!0}],envVarName:`NEXT_PUBLIC_ZENOVAY_SITE`,packageDeps:[`@zenovay/tracker`],supportsNpmMode:!0,buildSnippets:Me},Ie=[`pages/_app.tsx`,`pages/_app.ts`,`pages/_app.jsx`,`pages/_app.js`,`src/pages/_app.tsx`,`src/pages/_app.ts`,`src/pages/_app.jsx`,`src/pages/_app.js`];async function Le(e){for(let t of Ie)if(await e.exists(t))return t;return null}async function Re(e){let t=await e.readFile(`package.json`);if(!t)return!1;try{let e=JSON.parse(t);return!!(e.dependencies?.next??e.devDependencies?.next)}catch{return!1}}async function ze(e){let t=await Re(e);if(!t)return{matched:!1,confidence:0,reason:"package.json does not list `next` as a dependency."};let n=await Le(e);if(n)return{matched:!0,confidence:.9,reason:`Detected Next.js Pages Router via \`${n}\`.`};let r=await e.exists(`app`),i=await e.glob(`pages/**/*.{tsx,ts,jsx,js}`);return i.length>0&&!r?{matched:!0,confidence:.7,reason:"Found `pages/` directory with route files but no `_app` — will create one."}:{matched:!1,confidence:.2,reason:`Next.js detected, but no pages-router entry point found.`}}function Be(e){let t=`pages/_app.tsx`,n=e.trackingCode??e.siteId??``,r=`import type { AppProps } from 'next/app';
40
+ import Script from 'next/script';
41
+
42
+ export default function App({ Component, pageProps }: AppProps) {
43
+ return (
44
+ <>
45
+ <Script
46
+ defer
47
+ data-id="${n}"
48
+ data-allow-localhost="true"
49
+ src="https://api.zenovay.com/z.js"
50
+ strategy="afterInteractive"
51
+ />
52
+ <Component {...pageProps} />
53
+ </>
54
+ );
55
+ }
56
+ `;return[{targetPath:t,anchorKind:`after-imports`,code:`import Script from 'next/script';`,description:"Import next/script in `_app`."},{targetPath:t,anchorKind:`wrap-children`,code:`<><Script defer data-id="${n}" data-allow-localhost="true" src="https://api.zenovay.com/z.js" strategy="afterInteractive" /><Component {...pageProps} /></>`,description:`Render the Zenovay tracker alongside the page component.`},{targetPath:t,anchorKind:`replace-file`,code:r,description:"Full replacement when `_app` does not exist yet."}]}const Ve={id:`next-pages-router`,displayName:`Next.js (Pages Router)`,detect:ze,files:[{path:`pages/_app.tsx`,kind:`mutate`,required:!0}],envVarName:`NEXT_PUBLIC_ZENOVAY_SITE`,buildSnippets:Be},He=S.object({modules:S.array(S.string()).optional(),runtimeConfig:S.object({public:S.record(S.string(),S.unknown()).optional()}).optional()}).passthrough();async function Ue(e){let t=await e.readFile(`package.json`);if(!t)return{matched:!1,confidence:0,reason:`No package.json found.`};try{let e=JSON.parse(t),n=!!(e.dependencies?.nuxt??e.devDependencies?.nuxt??e.dependencies?.nuxt3??e.devDependencies?.nuxt3);if(n)return{matched:!0,confidence:.95,reason:"Detected Nuxt via `nuxt` / `nuxt3` dependency."}}catch{}let n=await e.exists(`nuxt.config.ts`)||await e.exists(`nuxt.config.js`)||await e.exists(`nuxt.config.mjs`);return n?{matched:!0,confidence:.6,reason:"Found `nuxt.config.*` but no explicit dependency listing."}:{matched:!1,confidence:0,reason:`No Nuxt dependency or config detected.`}}function We(e){let t=e.trackingCode??e.siteId??``,n=` app: {
57
+ head: {
58
+ script: [
59
+ {
60
+ defer: true,
61
+ src: 'https://api.zenovay.com/z.js',
62
+ 'data-id': '${t}',
63
+ 'data-allow-localhost': 'true',
64
+ },
65
+ ],
66
+ },
67
+ },`;return[{targetPath:`nuxt.config.ts`,anchorKind:`head-tag`,code:n,description:"Add the Zenovay tracker to Nuxt's `app.head.script` so every page loads it."}]}const Ge={id:`nuxt`,displayName:`Nuxt`,detect:Ue,files:[{path:`nuxt.config.ts`,kind:`mutate`,required:!0}],envVarName:`NUXT_PUBLIC_ZENOVAY_SITE`,buildSnippets:We},Ke=[`index.html`,`public/index.html`,`dist/index.html`];async function qe(e){for(let t of Ke)if(await e.exists(t))return{matched:!0,confidence:.5,reason:`Detected plain HTML project via \`${t}\`.`};let t=await e.glob(`**/*.html`);return t.length>0?{matched:!0,confidence:.3,reason:`Found ${t.length} HTML file(s); plain-HTML adapter is usable as fallback.`}:{matched:!1,confidence:0,reason:`No HTML files found in the project.`}}function Je(e){let t=e.siteId??``,n=`<script defer\n data-id="${t}"\n data-allow-localhost="true"\n src="https://api.zenovay.com/z.js">\n</script>`;return[{targetPath:`index.html`,anchorKind:`head-tag`,code:n,description:`Inject the Zenovay tracker inside <head>. Repeat for every HTML file that contains a <head>.`}]}const Ye={id:`plain-html`,displayName:`Plain HTML`,detect:qe,files:[{path:`index.html`,kind:`mutate`,required:!0}],envVarName:`PUBLIC_ZENOVAY_SITE`,buildSnippets:Je};async function Xe(e){let t=await e.readFile(`package.json`);if(!t)return{matched:!1,confidence:0,reason:`No package.json.`};try{let e=JSON.parse(t),n={...e.dependencies??{},...e.devDependencies??{}};if(n[`@builder.io/qwik-city`]??n[`@builder.io/qwik`])return{matched:!0,confidence:.92,reason:"Detected Qwik via `@builder.io/qwik(-city)`."}}catch{}return{matched:!1,confidence:0,reason:`No Qwik dependency found.`}}function Ze(e){let t=e.trackingCode??e.siteId??``,n=e.targetPath||`src/root.tsx`,r=`<script
68
+ defer
69
+ data-id="${t}"
70
+ data-allow-localhost="true"
71
+ src="https://api.zenovay.com/z.js"
72
+ />`;return[{targetPath:n,anchorKind:`head-tag`,code:r,description:`Add the Zenovay tracker into the <head> of the Qwik root document.`}]}const Qe={id:`qwik`,displayName:`Qwik`,detect:Xe,files:[{path:`src/root.tsx`,kind:`mutate`,required:!0}],envVarName:`PUBLIC_ZENOVAY_SITE`,buildSnippets:Ze};async function $e(e){let t=await e.readFile(`package.json`);if(t)try{let e=JSON.parse(t),n={...e.dependencies??{},...e.devDependencies??{}},r=Object.keys(n).some(e=>e.startsWith(`@remix-run/`));if(r)return{matched:!0,confidence:.95,reason:"Detected Remix via `@remix-run/*` dependencies."}}catch{}return await e.exists(`remix.config.js`)||await e.exists(`remix.config.ts`)?{matched:!0,confidence:.8,reason:"Detected Remix via `remix.config.*`."}:{matched:!1,confidence:0,reason:`No Remix dependency or config detected.`}}function et(e){let t=e.trackingCode??e.siteId??``;return[{targetPath:`app/root.tsx`,anchorKind:`head-tag`,code:`<script defer data-id="${t}" data-allow-localhost="true" src="https://api.zenovay.com/z.js"></script>`,description:`Inline the Zenovay tracker <script> inside the document <head>.`}]}const tt={id:`remix`,displayName:`Remix`,detect:$e,files:[{path:`app/root.tsx`,kind:`mutate`,required:!0}],envVarName:`PUBLIC_ZENOVAY_SITE`,buildSnippets:et};async function nt(e){let t=await e.readFile(`package.json`);if(!t)return{matched:!1,confidence:0,reason:`No package.json.`};try{let e=JSON.parse(t),n={...e.dependencies??{},...e.devDependencies??{}};if(n[`@solidjs/start`]??n[`solid-start`])return{matched:!0,confidence:.92,reason:"Detected SolidStart via `@solidjs/start` / `solid-start`."}}catch{}return{matched:!1,confidence:0,reason:`No SolidStart dependency found.`}}function rt(e){let t=e.trackingCode??e.siteId??``,n=e.targetPath||`src/entry-server.tsx`,r=`<script
73
+ defer
74
+ data-id="${t}"
75
+ data-allow-localhost="true"
76
+ src="https://api.zenovay.com/z.js"
77
+ />`;return[{targetPath:n,anchorKind:`head-tag`,code:r,description:`Add the Zenovay tracker into the document <head> rendered by SolidStart entry-server.`}]}const it={id:`solid-start`,displayName:`SolidStart`,detect:nt,files:[{path:`src/entry-server.tsx`,kind:`mutate`,required:!0}],envVarName:`VITE_ZENOVAY_SITE`,buildSnippets:rt};async function at(e){let t=await e.readFile(`package.json`);if(!t)return{matched:!1,confidence:0,reason:`No package.json found.`};try{let e=JSON.parse(t),n=!!(e.dependencies?.[`@sveltejs/kit`]??e.devDependencies?.[`@sveltejs/kit`]);return n?{matched:!0,confidence:.95,reason:"Detected SvelteKit via `@sveltejs/kit` dependency."}:{matched:!1,confidence:0,reason:"`@sveltejs/kit` not in dependencies."}}catch{return{matched:!1,confidence:0,reason:`package.json is not valid JSON.`}}}function ot(e){let t=e.trackingCode??e.siteId??``,n=`<svelte:head>
78
+ <script defer data-id="${t}" data-allow-localhost="true" src="https://api.zenovay.com/z.js"></script>
79
+ </svelte:head>
80
+
81
+ <slot />
82
+ `;return[{targetPath:`src/routes/+layout.svelte`,anchorKind:`replace-file`,code:n,description:`Root layout that loads the Zenovay tracker in every route via <svelte:head>.`}]}const st={id:`sveltekit`,displayName:`SvelteKit`,detect:at,files:[{path:`src/routes/+layout.svelte`,kind:`mutate`,required:!0}],envVarName:`PUBLIC_ZENOVAY_SITE`,buildSnippets:ot},ct=[`next`,`nuxt`,`nuxt3`,`@sveltejs/kit`,`astro`,`@remix-run/react`,`@builder.io/qwik-city`,`@builder.io/qwik`,`@solidjs/start`,`solid-start`,`@angular/core`,`gatsby`],lt=[`index.html`,`public/index.html`];async function ut(e){let t=await e.readFile(`package.json`);if(!t)return{};try{let e=JSON.parse(t);return{...e.dependencies??{},...e.devDependencies??{}}}catch{return{}}}async function dt(e){for(let t of lt)if(await e.exists(t))return t;return null}async function ft(e){let t=await ut(e);if(ct.some(e=>t[e]))return{matched:!1,confidence:0,reason:`A Vite-based meta-framework (Next/Nuxt/SvelteKit/Astro/Remix/Qwik/Solid/Angular) owns this project.`};let n=!!t.vite,r=await dt(e);return n&&r?{matched:!0,confidence:.85,reason:`Detected a Vite app via \`vite\` dependency + \`${r}\`.`}:n?{matched:!0,confidence:.55,reason:"Detected `vite` but no index.html — the agent will locate the SPA entry."}:{matched:!1,confidence:0,reason:"No `vite` dependency found."}}function pt(e){let t=e.trackingCode??e.siteId??``,n=e.targetPath||`index.html`,r=`<script defer\n data-id="${t}"\n data-allow-localhost="true"\n src="https://api.zenovay.com/z.js">\n</script>`;return[{targetPath:n,anchorKind:`head-tag`,code:r,description:`Inject the Zenovay tracker inside <head> of the Vite entry HTML.`}]}const mt={id:`vite`,displayName:`Vite`,detect:ft,files:[{path:`index.html`,kind:`mutate`,required:!0}],envVarName:`VITE_ZENOVAY_SITE`,buildSnippets:pt},ht=[Fe,Ve,Ge,st,Ce,tt,De,ye,it,Qe,mt,Ye];async function gt(e){let t=[];for(let n of ht){let r=await n.detect(e);t.push({adapter:n,result:r})}let n=[...t].sort((e,t)=>t.result.confidence-e.result.confidence),r=n[0],i=r&&r.result.matched?r.adapter:null;return{adapter:i,ranked:n}}function _t(e){let t=t=>b.resolve(e,t);return{cwd:e,async readFile(e){try{return await x.readFile(t(e),`utf8`)}catch(e){let t=e.code;if(t===`ENOENT`||t===`EISDIR`)return null;throw e}},async exists(e){try{return await x.access(t(e)),!0}catch{return!1}},async glob(t){return N(t,{cwd:e,dot:!1,gitignore:!0,onlyFiles:!0,followSymbolicLinks:!1})}}}async function vt(e,t){let n=_t(e),{adapter:r,ranked:i}=await gt(n),a=i[0];return r&&a&&a.result.confidence>=.7?r:t?t.pickInteractively(i):r}function yt(e,t){let n=/[\s"'#=]/.test(t)||t===``,r=n?`"${t.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`)}"`:t;return`${e}=${r}`}function bt(e){let t=new Set;for(let n of e.split(/\r?\n/)){let e=n.trim();if(!e||e.startsWith(`#`))continue;let r=e.indexOf(`=`);if(r<=0)continue;let i=e.slice(0,r).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&t.add(i)}return t}async function G(e,t,n={}){let r=n.filename??`.env.local`,i=b.resolve(e,r),a=``,o=!0;try{a=await x.readFile(i,`utf8`)}catch(e){if(e.code===`ENOENT`)o=!1;else throw e}let s=o?bt(a):new Set,c=[],l=[],u=[];for(let[e,n]of Object.entries(t)){if(s.has(e)){l.push(e);continue}u.push(yt(e,n)),c.push(e)}if(c.length===0)return{created:!1,existing:o,addedKeys:c,skippedKeys:l};let d=`# Added by Zenovay CLI`,f;if(o){let e=a.length>0&&!a.endsWith(`
83
+ `);f=a+(e?`
84
+ `:``)+`
85
+ `+d+`
86
+ `+u.join(`
87
+ `)+`
88
+ `}else f=d+`
89
+ `+u.join(`
90
+ `)+`
91
+ `;if(o)await x.writeFile(i,f,`utf8`);else{let e=await x.open(i,`w`,384);try{await e.writeFile(f,`utf8`)}finally{await e.close()}}return{created:!o,existing:o,addedKeys:c,skippedKeys:l}}const xt=`https://api.zenovay.com/mcp`,St=S.object({url:S.string().url(),headers:S.record(S.string(),S.string()).optional()}),Ct=S.object({mcpServers:S.record(S.string(),St).optional()}).passthrough();function wt(e){return e.HOME??e.USERPROFILE??C.homedir()}function Tt(e){return e.APPDATA??null}function Et(e,t){let n=wt(t),r=Tt(t),i=[];return i.push({id:`cursor`,displayName:`Cursor`,configPath:b.join(n,`.cursor`,`mcp.json`)}),e===`win32`&&r&&i.push({id:`cursor`,displayName:`Cursor (AppData)`,configPath:b.join(r,`Cursor`,`User`,`globalStorage`,`cursor.mcp`,`mcp.json`)}),e===`darwin`?i.push({id:`claude-desktop`,displayName:`Claude Desktop`,configPath:b.join(n,`Library`,`Application Support`,`Claude`,`claude_desktop_config.json`)}):e===`win32`&&r?i.push({id:`claude-desktop`,displayName:`Claude Desktop`,configPath:b.join(r,`Claude`,`claude_desktop_config.json`)}):i.push({id:`claude-desktop`,displayName:`Claude Desktop`,configPath:b.join(n,`.config`,`Claude`,`claude_desktop_config.json`)}),i.push({id:`vscode`,displayName:`VS Code`,configPath:b.join(n,`.vscode`,`mcp.json`)}),i}async function Dt(e){try{return await x.access(e),!0}catch{return!1}}async function Ot(e){try{let t=await x.readFile(e,`utf8`);if(t.trim().length===0)return{};let n=JSON.parse(t),r=Ct.safeParse(n);return r.success?r.data:n&&typeof n==`object`?n:{}}catch{return{}}}function kt(){return{url:xt}}async function K(e){let t=e.env??process.env,n=e.platform??process.platform,r=new Set(e.skip??[]),i=[],a=[],o=[],s=new Set,c=kt();for(let e of Et(n,t)){if(r.has(e.id)){a.push(e.displayName);continue}if(s.has(e.configPath))continue;s.add(e.configPath);try{let t=await Dt(e.configPath);if(!t){a.push(e.displayName);continue}let n=await Ot(e.configPath),r=`${e.configPath}.bak`;await x.copyFile(e.configPath,r);let o={...n,mcpServers:{...n.mcpServers??{},zenovay:c}};await x.mkdir(b.dirname(e.configPath),{recursive:!0}),await x.writeFile(e.configPath,JSON.stringify(o,null,2)+`
92
+ `,`utf8`),i.push(e.displayName)}catch(t){o.push({client:e.displayName,message:t instanceof Error?t.message:String(t)})}}return{installed:i,skipped:a,errors:o}}async function At(e,t,n){let r=n.timeoutMs??18e4,i=n.pollIntervalMs??2e3,a=n.now??(()=>Date.now()),o=n.sleep??(e=>new Promise(t=>setTimeout(t,e))),s=a(),c=s+r;for(n.onTick?.(0);;){let l=await n.api.getFirstEvent({siteId:e,since:t});if(l.received)return{received:!0,event:l.event};let u=a()-s;if(u>=r)return{received:!1};let d=c-a(),f=Math.min(i,Math.max(0,d));if(f<=0)return{received:!1};await o(f),n.onTick?.(a()-s)}}const jt=S.object({path:S.string().min(1)}),Mt=S.object({path:S.string().min(1),content:S.string(),reason:S.string().min(1)}),Nt=S.object({path:S.string().min(1),patch:S.string().min(1),reason:S.string().min(1)}),Pt=S.object({}),Ft=S.object({question:S.string().min(1),choices:S.array(S.string()).optional()});async function It(e,t){let n=jt.safeParse(t);if(!n.success)return{ok:!1,content:null,error:`invalid_args`};try{let t=await e.safeRead(n.data.path),r=ge.scrubSecrets(t),i=24e3,a=r.length>i?`${r.slice(0,i)}\n\n[... truncated: file is ${r.length} chars; showing the first ${i}]`:r;return{ok:!0,content:ge.wrapForModel(n.data.path,a)}}catch(e){return{ok:!1,content:null,error:e.message}}}async function Lt(e,t,n){let r=Mt.safeParse(t);if(!r.success)return{ok:!1,content:null,error:`invalid_args`};let{path:i,content:a,reason:o}=r.data,s=``;try{s=await e.safeRead(i)}catch{s=``}let c=await n.approve({path:i,oldContent:s,newContent:a,reason:o});if(c===`n`)return{ok:!1,content:null,error:`skipped_by_user`};if(c===`e`)return{ok:!1,content:null,error:`deferred_to_user_edit`};try{let{backupPath:t}=await e.safeWrite(i,a);return{ok:!0,content:{path:i,backupPath:t}}}catch(e){return{ok:!1,content:null,error:e.message}}}async function Rt(e,t,n){let r=Nt.safeParse(t);if(!r.success)return{ok:!1,content:null,error:`invalid_args`};let{path:i,patch:a,reason:o}=r.data;if(/^\*\*\*\s+Begin Patch/m.test(a)||/^\*\*\*\s+Update File:/m.test(a))return{ok:!1,content:null,error:'unsupported_patch_format: This tool only accepts STANDARD unified diff with `--- a/...`, `+++ b/...`, and `@@ -N,M +N,M @@` headers. The "*** Begin Patch / *** Update File:" V4A format is NOT supported. Please call write_file with the full new file content instead.'};let s;try{s=await e.safeRead(i)}catch(e){return{ok:!1,content:null,error:e.message}}let c;try{c=F(s,a)}catch(e){return{ok:!1,content:null,error:`patch_failed: ${e.message}. Please call write_file with the full new file content instead — write_file is more reliable than patch_file for this use case.`}}return c===!1||typeof c!=`string`?{ok:!1,content:null,error:`patch_did_not_apply_cleanly. The patch context may not match the file. Please call write_file with the full new file content instead.`}:Lt(e,{path:i,content:c,reason:o},n)}async function zt(e){try{let t=_t(e),{adapter:n,ranked:r}=await gt(t);return{ok:!0,content:{matched:n!==null,adapter:n?.id,displayName:n?.displayName,ranked:r.map(e=>({id:e.adapter.id,confidence:e.result.confidence,reason:e.result.reason}))}}}catch(e){return{ok:!1,content:null,error:e.message}}}async function Bt(e,t){let n=Ft.safeParse(t);if(!n.success)return{ok:!1,content:null,error:`invalid_args`};try{let t=await e.ask(n.data);return{ok:!0,content:{answer:t}}}catch(e){return{ok:!1,content:null,error:e.message}}}async function Vt(e){let{api:t,sandbox:n,approval:r,prompter:i,handlers:a}=e,o=[...e.initialMessages],s=``,c=0;for(;c<me;){n.incrementRound(),c++;let u=await t.openAiStream(`install`,{messages:o,cwd_fingerprint:n.sessionId,diagnosis:e.diagnosis,targetTrackingCode:e.targetTrackingCode}),d=[],f=``;for await(let e of l(u))switch(e.type){case`delta`:f+=e.content,a?.onDelta?.(e.content);break;case`tool_start`:a?.onToolStart?.({id:e.id,name:e.name});break;case`tool_call`:{let t=await Ht(e.name,e.arguments,{sandbox:n,cwd:n.cwd,approval:r,prompter:i});d.push({id:e.id,name:e.name,arguments:e.arguments,result:t}),a?.onToolResult?.({id:e.id,name:e.name,result:t});break}case`usage`:a?.onUsage?.(e);break;case`error`:throw a?.onError?.(e),Error(`[${e.code}] ${e.message}`);case`done`:break}if(f&&(s+=f),d.length===0){f&&o.push({role:`assistant`,content:f});break}o.push({role:`assistant`,content:f,toolCalls:d.map(e=>({id:e.id,type:`function`,function:{name:e.name,arguments:JSON.stringify(e.arguments)}}))});for(let e of d)o.push({role:`tool`,toolCallId:e.id,toolName:e.name,content:JSON.stringify(e.result)})}return{finalText:s,rounds:c}}async function Ht(e,t,n){try{switch(e){case`read_file`:return await It(n.sandbox,t);case`write_file`:return await Lt(n.sandbox,t,n.approval);case`patch_file`:return await Rt(n.sandbox,t,n.approval);case`detect_framework`:return await zt(n.cwd);case`ask_user`:return await Bt(n.prompter,t);default:return{ok:!1,content:null,error:`unknown_tool:${e}`}}}catch(e){return{ok:!1,content:null,error:`tool_threw: ${e.message}`}}}const Ut=e=>`<script defer\n data-id="${e}"\n data-allow-localhost="true"\n src="https://api.zenovay.com/z.js">\n</script>`;async function Wt(e){let t={installed:[],skipped:[],errors:[]},n=await N([`**/*.html`,`!node_modules/**`,`!.git/**`,`!dist/**`,`!.next/**`,`!.nuxt/**`,`!build/**`,`!.output/**`],{cwd:e.cwd,gitignore:!0,absolute:!1});if(n.length===0)return t;let r=Ut(e.trackingCode),i=RegExp(`data-id\\s*=\\s*["']${Gt(e.trackingCode)}["']`);for(let a of n){let n=b.join(e.cwd,a),o;try{o=await x.readFile(n,`utf8`)}catch(e){t.errors.push({file:a,error:e.message});continue}if(W.test(o)&&i.test(o)){t.skipped.push({file:a,reason:`already installed with correct tracking code`});continue}let s;if(W.test(o))s=o.replace(/(<script[^>]*src\s*=\s*["']https:\/\/(?:api|cdn)\.zenovay\.com\/[^"']+["'][^>]*>)/i,t=>t.replace(/data-id\s*=\s*["']ZV_\w+["']/i,`data-id="${e.trackingCode}"`)),s===o&&(s=o.replace(/<script(\s)/i,`<script$1data-id="${e.trackingCode}"$1`));else if(e.updateOnly){t.skipped.push({file:a,reason:`updateOnly=true and no Zenovay tag present`});continue}else if(/<\/head>/i.test(o)){if(s=o.replace(/(\n[\t ]*)?<\/head>/i,(e,t)=>{let n=t??`
93
+ `,i=r.replace(/\n/g,`${n}`);return`${n}${i}${n}</head>`}),s===o){t.skipped.push({file:a,reason:`</head> regex did not match unexpectedly`});continue}}else{t.skipped.push({file:a,reason:`no <head>...</head> in file (fragment or non-page)`});continue}let c=null;for(let{name:e,re:t}of he)if(t.test(o)&&!t.test(s)){c=e;break}if(c){t.errors.push({file:a,error:`would remove ${c} tracker — refusing to write`});continue}try{await x.writeFile(n,s,`utf8`),t.installed.push(a)}catch(e){t.errors.push({file:a,error:e.message})}}return t}function Gt(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Kt=`https://api.zenovay.com/z.js`;function qt(e,t){return e.includes(Kt)&&e.includes(t)}function Jt(e){if(/from\s+['"]next\/script['"]/.test(e))return e;let t=e.split(`
94
+ `),n=-1;for(let e=0;e<t.length;e++)/^\s*import\s/.test(t[e])&&(n=e);let r=`import Script from 'next/script';`;if(n>=0)t.splice(n+1,0,r);else{let e=/^\s*['"]use client['"];?\s*$/.test(t[0]??``);t.splice(e?1:0,0,r)}return t.join(`
95
+ `)}function Yt(e,t){let n=/([ \t]*)<\/body>/,r=e.match(n);if(!r)return null;let i=r[1]??``,a=`${i} <Script defer data-id="${t}" data-allow-localhost="true" src="${Kt}" strategy="afterInteractive" />`;return e.replace(n,`${a}\n${i}</body>`)}async function Xt(e){let{cwd:t,trackingCode:n}=e,r={installed:[],skipped:[],errors:[],fellThrough:!1},i;try{i=await N([`app/**/layout.{tsx,ts,jsx,js}`,`src/app/**/layout.{tsx,ts,jsx,js}`],{cwd:t,gitignore:!1,ignore:[`**/node_modules/**`]})}catch(e){return r.errors.push({file:`app/**/layout`,error:e.message}),r.fellThrough=!0,r}if(i.length===0)return r.fellThrough=!0,r;let a=[];for(let e of i)try{let n=await k.readFile(b.join(t,e),`utf8`);/<body[\s/>]/.test(n)&&a.push({rel:e,src:n,depth:e.split(`/`).length})}catch{}if(a.length===0)return r.fellThrough=!0,r;a.sort((e,t)=>e.depth-t.depth);let o=a[0];if(qt(o.src,n))return r.skipped.push({file:o.rel,reason:`already installed with correct tracking code`}),r;let s=Jt(o.src),c=Yt(s,n);if(!c)return r.fellThrough=!0,r;try{await k.writeFile(b.join(t,o.rel),c),r.installed.push(o.rel)}catch(e){r.errors.push({file:o.rel,error:e.message}),r.fellThrough=!0}return r}const q=`api.zenovay.com/z.js`;async function J(e){try{return await x.readFile(e,`utf8`)}catch{return null}}function Zt(e,t){let n=e.toLowerCase().lastIndexOf(`</head>`);if(n===-1)return null;let r=e.lastIndexOf(`
96
+ `,n-1)+1,i=(e.slice(r,n).match(/^[ \t]*/)??[``])[0],a=`${i}${t}\n`;return e.slice(0,r)+a+e.slice(r)}async function Qt(e,t,n){let r=b.dirname(e),i=/import\s+[^;]*?from\s*['"`](\.[^'"`]+\.(?:astro|tsx|jsx|vue|svelte))['"`]/g,a;for(;(a=i.exec(t))!==null;){let e=b.resolve(r,a[1]),t=await J(e);if(t===null)continue;if(t.includes(q))return e;let i=Zt(t,n);if(i!==null)return await x.writeFile(e,i,`utf8`),e}return null}async function $t(e){if(e.adapter.id===`nuxt`)return tn(e);if(e.adapter.id===`sveltekit`)return nn(e);let{cwd:t,trackingCode:n,siteId:r,adapter:i}=e,a={installed:[],skipped:[],errors:[],fellThrough:!1},o;try{o=i.buildSnippets({trackingCode:n,siteId:r??``},`script`)}catch{return a.fellThrough=!0,a}if(!o||o.length===0)return a.fellThrough=!0,a;let s=new Set(o.map(e=>e.anchorKind));if(s.size===1&&s.has(`head-tag`)){let e=!1;for(let n of o){let r=b.join(t,n.targetPath),i=await J(r);if(i===null){a.skipped.push({file:n.targetPath,reason:`target file not present`});continue}if(i.includes(q)){a.skipped.push({file:n.targetPath,reason:`tracker already present`}),e=!0;break}let o=Zt(i,n.code);if(o===null){let o=await Qt(r,i,n.code);if(o){a.installed.push(b.relative(t,o)),e=!0;break}a.skipped.push({file:n.targetPath,reason:`no </head> anchor (inline or in an imported head component)`});continue}try{await x.writeFile(r,o,`utf8`),a.installed.push(n.targetPath),e=!0;break}catch(e){a.errors.push({file:n.targetPath,error:e.message})}}return!e&&a.installed.length===0&&a.errors.length===0&&(a.fellThrough=!0),a}if(s.size===1&&s.has(`replace-file`)&&o.length===1){let e=o[0];if(!e)return a.fellThrough=!0,a;let n=i.files.find(t=>t.path===e.targetPath),r=b.join(t,e.targetPath),s=await J(r);if(s!==null)return s.includes(q)?(a.skipped.push({file:e.targetPath,reason:`tracker already present`}),a):(a.fellThrough=!0,a);if(n?.kind===`create`){try{await x.mkdir(b.dirname(r),{recursive:!0}),await x.writeFile(r,e.code,`utf8`),a.installed.push(e.targetPath)}catch(t){a.errors.push({file:e.targetPath,error:t.message}),a.fellThrough=a.installed.length===0}return a}return a.fellThrough=!0,a}return a.fellThrough=!0,a}function en(){return{installed:[],skipped:[],errors:[],fellThrough:!1}}async function tn(e){let t=en(),n=[`nuxt.config.ts`,`nuxt.config.mjs`,`nuxt.config.js`],r=null,i=null,a=null;for(let t of n){let n=b.join(e.cwd,t),o=await J(n);if(o!==null){r=t,i=n,a=o;break}}if(a===null||i===null||r===null)return t.skipped.push({file:`nuxt.config.ts`,reason:`nuxt.config not found`}),t.fellThrough=!0,t;if(a.includes(q))return t.skipped.push({file:r,reason:`tracker already present`}),t;if(/\bapp\s*:/.test(a))return t.fellThrough=!0,t;let o=a.match(/defineNuxtConfig\s*\(\s*\{/);if(!o||o.index===void 0)return t.fellThrough=!0,t;let s=e.adapter.buildSnippets({trackingCode:e.trackingCode,siteId:e.siteId??``},`script`)[0]?.code;if(!s)return t.fellThrough=!0,t;let c=o.index+o[0].length,l=`${a.slice(0,c)}\n${s}${a.slice(c)}`;try{await x.writeFile(i,l,`utf8`),t.installed.push(r)}catch(e){t.errors.push({file:r,error:e.message}),t.fellThrough=!0}return t}async function nn(e){let t=en(),n=`src/routes/+layout.svelte`,r=b.join(e.cwd,n),i=await J(r),a=`<svelte:head>\n <script defer data-id="${e.trackingCode}" data-allow-localhost="true" src="https://api.zenovay.com/z.js"></script>\n</svelte:head>\n`;if(i!==null){if(i.includes(q))return t.skipped.push({file:n,reason:`tracker already present`}),t;try{await x.writeFile(r,`${a}\n${i}`,`utf8`),t.installed.push(n)}catch(e){t.errors.push({file:n,error:e.message}),t.fellThrough=!0}return t}let o=e.adapter.buildSnippets({trackingCode:e.trackingCode,siteId:e.siteId??``},`script`)[0]?.code;if(!o)return t.fellThrough=!0,t;try{await x.mkdir(b.dirname(r),{recursive:!0}),await x.writeFile(r,o,`utf8`),t.installed.push(n)}catch(e){t.errors.push({file:n,error:e.message}),t.fellThrough=!0}return t}async function Y(e,t){try{return await x.access(b.join(e,t)),!0}catch{return!1}}async function rn(e,t){try{let n=await x.stat(b.join(e,t));return n.isDirectory()}catch{return!1}}async function an(e,t){try{return await x.readFile(b.join(e,t),`utf8`)}catch{return null}}const on=[`next.config.js`,`next.config.mjs`,`next.config.cjs`,`next.config.ts`];async function sn(e){let t=[],n=await an(e,`wrangler.toml`),r=n!==null&&/^\s*pages_build_output_dir\s*=/m.test(n),i=await rn(e,`functions`),a=await rn(e,`.cloudflare`);if(r&&t.push(`wrangler.toml has pages_build_output_dir`),i&&t.push(`functions/ directory present`),a&&t.push(`.cloudflare/ directory present`),r||i||a)return{host:`cloudflare-pages`,signals:t};let o=await Y(e,`vercel.json`),s=await rn(e,`.vercel`);if(o&&t.push(`vercel.json present`),s&&t.push(`.vercel/ directory present`),o||s)return{host:`vercel`,signals:t};let c=await Y(e,`netlify.toml`),l=await Y(e,b.join(`public`,`_redirects`)),u=await Y(e,`_redirects`);if(c&&t.push(`netlify.toml present`),l&&t.push(`public/_redirects present`),u&&t.push(`_redirects present`),c||l||u)return{host:`netlify`,signals:t};let d=null;for(let t of on)if(await Y(e,t)){d=t;break}return d?(t.push(`${d} present (no Vercel/Netlify/CF-Pages signal)`),{host:`nextjs-server`,signals:t}):(t.push(`no deployment-host signals found`),{host:`unknown`,signals:t})}const cn=`https://api.zenovay.com`;function ln(e){return`// Auto-generated by @zenovay/cli — first-party tracking proxy. Do not edit.
97
+ /**
98
+ * First-Party Proxy for Zenovay Tracking.
99
+ *
100
+ * This Cloudflare Pages Function proxies tracking requests from your site to
101
+ * ${e}/fp/* — making tracking requests "first-party" to bypass browser
102
+ * tracking protection (Firefox ETP, Edge Tracking Prevention, Safari ITP, Brave).
103
+ *
104
+ * Routes (this function lives at functions/_z/[[path]].ts → /_z/*):
105
+ * - GET /_z/script.js → ${e}/fp/script.js
106
+ * - GET /_z/load → ${e}/fp/load
107
+ * - POST /_z/e/:trackingCode → ${e}/fp/e/:trackingCode
108
+ * - POST /_z/events → ${e}/fp/events
109
+ * - GET /_z/settings/:trackingCode → ${e}/fp/settings/:trackingCode
110
+ */
111
+
112
+ interface Env {
113
+ NEXT_PUBLIC_API_URL?: string
114
+ }
115
+
116
+ interface EventContext {
117
+ request: Request
118
+ params: { path?: string[] }
119
+ env: Env
120
+ }
121
+
122
+ /**
123
+ * Handle all HTTP methods (GET, POST, OPTIONS).
124
+ */
125
+ export async function onRequest(context: EventContext): Promise<Response> {
126
+ const { request, params, env } = context
127
+
128
+ // Build the path from the catch-all parameter.
129
+ const pathSegments = params.path || []
130
+ const path = pathSegments.join('/')
131
+
132
+ // API base URL — defaults to the value baked in at generation time, but an
133
+ // env var override (NEXT_PUBLIC_API_URL) still wins if present.
134
+ const apiBase = env.NEXT_PUBLIC_API_URL || '${e}'
135
+
136
+ // Build target URL — proxy to /fp/* endpoints.
137
+ const targetUrl = \`\${apiBase}/fp/\${path}\${new URL(request.url).search}\`
138
+
139
+ // Handle OPTIONS (CORS preflight).
140
+ if (request.method === 'OPTIONS') {
141
+ return new Response(null, {
142
+ status: 204,
143
+ headers: {
144
+ 'Access-Control-Allow-Origin': '*',
145
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
146
+ 'Access-Control-Allow-Headers': 'Content-Type, X-Zenovay-Tracking-Code',
147
+ 'Access-Control-Max-Age': '86400',
148
+ },
149
+ })
150
+ }
151
+
152
+ try {
153
+ // Get the real client IP from Cloudflare headers.
154
+ const clientIP =
155
+ request.headers.get('CF-Connecting-IP') ||
156
+ request.headers.get('X-Forwarded-For')?.split(',')[0]?.trim() ||
157
+ request.headers.get('X-Real-IP') ||
158
+ ''
159
+
160
+ // Clone the original headers.
161
+ const proxyHeaders = new Headers(request.headers)
162
+
163
+ // Add/set proxy identification headers.
164
+ proxyHeaders.set('X-Zenovay-Proxy', 'first-party')
165
+ proxyHeaders.set('X-Zenovay-Real-IP', clientIP)
166
+
167
+ // Forward the visitor's real CF geo data as custom headers before deleting
168
+ // the originals. When this Worker proxies to ${e}, Cloudflare
169
+ // overwrites CF-* headers with the Worker datacenter's info — these custom
170
+ // headers preserve the visitor's real geo.
171
+ const cfCountry = request.headers.get('CF-IPCountry')
172
+ const cfCity = request.headers.get('CF-IPCity')
173
+ const cfRegion = request.headers.get('CF-Region')
174
+ const cfLatitude = request.headers.get('CF-IPLatitude')
175
+ const cfLongitude = request.headers.get('CF-IPLongitude')
176
+ const cfTimezone = request.headers.get('CF-Timezone')
177
+ if (cfCountry) proxyHeaders.set('X-Zenovay-CF-Country', cfCountry)
178
+ if (cfCity) proxyHeaders.set('X-Zenovay-CF-City', cfCity)
179
+ if (cfRegion) proxyHeaders.set('X-Zenovay-CF-Region', cfRegion)
180
+ if (cfLatitude) proxyHeaders.set('X-Zenovay-CF-Latitude', cfLatitude)
181
+ if (cfLongitude) proxyHeaders.set('X-Zenovay-CF-Longitude', cfLongitude)
182
+ if (cfTimezone) proxyHeaders.set('X-Zenovay-CF-Timezone', cfTimezone)
183
+
184
+ // Remove headers that shouldn't be forwarded.
185
+ proxyHeaders.delete('Host')
186
+ proxyHeaders.delete('CF-Connecting-IP')
187
+ proxyHeaders.delete('CF-RAY')
188
+ proxyHeaders.delete('CF-Visitor')
189
+ proxyHeaders.delete('CF-IPCountry')
190
+ proxyHeaders.delete('CF-Worker')
191
+
192
+ // Prepare request options.
193
+ const requestInit: RequestInit = {
194
+ method: request.method,
195
+ headers: proxyHeaders,
196
+ }
197
+
198
+ // Forward the body for POST/PUT/PATCH requests.
199
+ if (request.method === 'POST' || request.method === 'PUT' || request.method === 'PATCH') {
200
+ requestInit.body = await request.arrayBuffer()
201
+ }
202
+
203
+ // Forward the request to the API.
204
+ // Use cf options to bypass Cloudflare edge cache for fresh script responses.
205
+ const response = await fetch(targetUrl, {
206
+ ...requestInit,
207
+ cf: {
208
+ // Bypass cache for scripts to ensure fresh versions.
209
+ cacheTtl: path.endsWith('.js') || path.includes('script') ? 0 : 60,
210
+ cacheEverything: false,
211
+ },
212
+ } as RequestInit)
213
+
214
+ // Clone response headers.
215
+ const responseHeaders = new Headers(response.headers)
216
+
217
+ // Add proxy identification.
218
+ responseHeaders.set('X-Zenovay-Proxied', 'true')
219
+
220
+ // For scripts, ensure the proper content type and no caching.
221
+ if (path.endsWith('.js') || path.includes('script') || path.includes('load')) {
222
+ responseHeaders.set('Content-Type', 'application/javascript; charset=utf-8')
223
+
224
+ // Don't cache scripts so the latest API config (apiUrl, trackingCode) is
225
+ // always served.
226
+ responseHeaders.set('Cache-Control', 'no-cache, no-store, must-revalidate')
227
+ }
228
+
229
+ // CORS headers for the proxied response.
230
+ responseHeaders.set('Access-Control-Allow-Origin', '*')
231
+ responseHeaders.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
232
+ responseHeaders.set('Access-Control-Allow-Headers', 'Content-Type, X-Zenovay-Tracking-Code')
233
+
234
+ // Return the proxied response.
235
+ return new Response(response.body, {
236
+ status: response.status,
237
+ statusText: response.statusText,
238
+ headers: responseHeaders,
239
+ })
240
+ } catch (error) {
241
+ console.error('[Zenovay First-Party Proxy] Error:', error)
242
+
243
+ return new Response(
244
+ JSON.stringify({
245
+ error: 'Proxy error',
246
+ message: error instanceof Error ? error.message : 'Unknown error',
247
+ }),
248
+ {
249
+ status: 502,
250
+ headers: {
251
+ 'Content-Type': 'application/json',
252
+ 'Access-Control-Allow-Origin': '*',
253
+ },
254
+ }
255
+ )
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Handle GET requests specifically.
261
+ */
262
+ export async function onRequestGet(context: EventContext): Promise<Response> {
263
+ return onRequest(context)
264
+ }
265
+
266
+ /**
267
+ * Handle POST requests specifically.
268
+ */
269
+ export async function onRequestPost(context: EventContext): Promise<Response> {
270
+ return onRequest(context)
271
+ }
272
+
273
+ /**
274
+ * Handle OPTIONS requests specifically (CORS preflight).
275
+ */
276
+ export async function onRequestOptions(_context: EventContext): Promise<Response> {
277
+ return new Response(null, {
278
+ status: 204,
279
+ headers: {
280
+ 'Access-Control-Allow-Origin': '*',
281
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
282
+ 'Access-Control-Allow-Headers': 'Content-Type, X-Zenovay-Tracking-Code',
283
+ 'Access-Control-Max-Age': '86400',
284
+ },
285
+ })
286
+ }
287
+ `}async function un(e,t){let n=(t.apiBase||cn).replace(/\/+$/,``),r=b.join(e,`functions`,`_z`),i=b.join(r,`[[path]].ts`);try{return await x.access(i),{file:i,action:`exists`}}catch{}return await x.mkdir(r,{recursive:!0}),await x.writeFile(i,ln(n),`utf8`),{file:i,action:`created`}}const dn=`https://api.zenovay.com`,fn=[`next.config.js`,`next.config.mjs`,`next.config.ts`];function pn(e){return` // Zenovay first-party proxy — forwards /_z/* to the tracking API
288
+ {
289
+ source: '/_z/:path*',
290
+ destination: '${e}/fp/:path*',
291
+ },`}function mn(e){return`/** @type {import('next').NextConfig} */
292
+ const nextConfig = {
293
+ async rewrites() {
294
+ return [
295
+ ${pn(e)}
296
+ ];
297
+ },
298
+ };
299
+
300
+ export default nextConfig;
301
+ `}async function hn(e,t={}){let n=t.apiBase??dn,r=null;for(let t of fn){let n=b.join(e,t);try{await k.access(n),r=n;break}catch{}}if(!r){let t=b.join(e,`next.config.mjs`);return await k.writeFile(t,mn(n),`utf8`),{file:t,action:`created`}}let i=await k.readFile(r,`utf8`);if(i.includes(`/_z/:path*`))return{file:r,action:`updated`};if(/\brewrites\s*(?::|=|\()/.test(i)||/\bredirects\s*\(/.test(i))return{file:r,action:`manual`};let a=i.match(/(?:module\.exports\s*=\s*|export\s+default\s+|const\s+\w+\s*=\s*)\{/);if(!a||a.index===void 0)return{file:r,action:`manual`};let o=a.index+a[0].length-1,s=i.slice(0,o+1),c=i.slice(o+1),l=`
302
+ async rewrites() {
303
+ return [
304
+ ${pn(n)}
305
+ ];
306
+ },`,u=`${s}${l}${c}`;return await k.writeFile(r,u,`utf8`),{file:r,action:`updated`}}const gn=`https://api.zenovay.com`;async function _n(e,t={}){let n=t.apiBase??gn,r=b.join(e,`vercel.json`),i={source:`/_z/(.*)`,destination:`${n}/fp/$1`},a=null;try{a=await k.readFile(r,`utf8`)}catch{a=null}if(a===null){let e={rewrites:[i]};return await k.writeFile(r,`${JSON.stringify(e,null,2)}\n`,`utf8`),{file:r,action:`created`}}let o;try{o=JSON.parse(a)}catch{throw Error(`Cannot parse existing vercel.json at ${r}: invalid JSON`)}let s=Array.isArray(o.rewrites)?o.rewrites:[],c=s.some(e=>typeof e?.source==`string`&&e.source.startsWith(`/_z`));return c?{file:r,action:`exists`}:(o.rewrites=[...s,i],await k.writeFile(r,`${JSON.stringify(o,null,2)}\n`,`utf8`),{file:r,action:`updated`})}const vn=`https://api.zenovay.com`;async function yn(e){let t=b.join(e,`_redirects`),n=b.join(e,`public`,`_redirects`);for(let e of[t,n])try{return await k.access(e),e}catch{}try{let t=await k.stat(b.join(e,`public`));if(t.isDirectory())return n}catch{}return t}async function bn(e,t={}){let n=t.apiBase??vn,r=await yn(e),i=`/_z/* ${n}/fp/:splat 200`,a=null;try{a=await k.readFile(r,`utf8`)}catch{a=null}if(a===null)return await k.mkdir(b.dirname(r),{recursive:!0}),await k.writeFile(r,`# Zenovay first-party proxy — forwards /_z/* to the tracking API\n${i}\n`,`utf8`),{file:r,action:`created`};let o=a.split(/\r?\n/).some(e=>{let t=e.trim();return t.length>0&&!t.startsWith(`#`)&&t.startsWith(`/_z/`)});if(o)return{file:r,action:`exists`};let s=a.length>0&&!a.endsWith(`
307
+ `)?`
308
+ `:``,c=`${a}${s}# Zenovay first-party proxy — forwards /_z/* to the tracking API\n${i}\n`;return await k.writeFile(r,c,`utf8`),{file:r,action:`updated`}}function xn(e){return`<script defer src="/_z/script.js?id=${e}"></script>`}const Sn=/<script\b(?=[^>]*\bsrc\s*=\s*["'`]https:\/\/api\.zenovay\.com\/z\.js["'`])[^>]*>(?:\s*<\/script>)?/gi,Cn=/\bdata-id\s*=\s*["'`]([^"'`]+)["'`]/i;function wn(e,t){let n=e.match(Cn),r=n?.[1]??t;return xn(r)}async function Tn(e,t){let n=await N([`**/*.{html,htm,tsx,jsx,vue,svelte,astro,ts,js,mjs}`,`!node_modules/**`,`!.git/**`,`!dist/**`,`!.next/**`,`!.nuxt/**`,`!build/**`,`!.output/**`],{cwd:e,gitignore:!0,deep:4,absolute:!0}).catch(()=>[]),r=[];for(let i of n.slice(0,300)){let n;try{n=await x.readFile(i,`utf8`)}catch{continue}if(Sn.lastIndex=0,!Sn.test(n))continue;Sn.lastIndex=0;let a=n.replace(Sn,e=>wn(e,t.trackingCode));if(a!==n){try{await x.writeFile(i,a,`utf8`)}catch{continue}r.push(b.relative(e,i))}}return{swapped:r.length>0,files:r}}const En=`Add a proxy from /_z/* to https://api.zenovay.com/fp/* on your host so tracking loads same-origin (first-party).`;async function Dn(e,t){let{trackingCode:n,apiBase:r}=t,i=`unknown`,a=null,o=`skipped`,s=null,c=!1;try{let t=await sn(e);switch(i=t.host,i){case`cloudflare-pages`:{let t=await un(e,{apiBase:r});a=t.file,o=t.action;break}case`nextjs-server`:{let t=await hn(e,{apiBase:r});a=t.file,o=t.action,t.action===`manual`&&(s=`Couldn't safely edit ${t.file}. ${En} In next.config: add a rewrite { source: '/_z/:path*', destination: 'https://api.zenovay.com/fp/:path*' }.`);break}case`vercel`:{let t=await _n(e,{apiBase:r});a=t.file,o=t.action;break}case`netlify`:{let t=await bn(e,{apiBase:r});a=t.file,o=t.action;break}case`unknown`:default:o=`skipped`,s=En;break}}catch(e){a=null,o=`skipped`,s=`${En} (auto-setup hit an error: ${e.message.slice(0,120)})`}try{let t=await Tn(e,{trackingCode:n});c=t.swapped}catch{c=!1}return{host:i,proxyFile:a,proxyAction:o,snippetSwapped:c,manual:s}}const On=`\x1B]8;;`,kn=`\x07`;function An(){return process.env.FORCE_HYPERLINK?!0:!(process.env.NO_COLOR||!process.stdout.isTTY||process.env.CI||(process.env.TERM??``)===`dumb`)}function jn({url:e,label:t,color:n,arrow:r=!0}){let{theme:i}=m(),a=n??i.fg,o=(t??e)+(r?` ↗`:``);return An()?A.createElement(D,{color:a,underline:!0},On,e,kn,o,On,kn):t&&t!==e?A.createElement(D,{color:a},A.createElement(D,{underline:!0},t),A.createElement(D,{color:i.dim},` (`,e,`)`)):A.createElement(D,{color:a,underline:!0},e)}function X({label:e,value:t,labelWidth:n=12}){let{theme:r}=m();return A.createElement(E,{flexDirection:`row`},A.createElement(E,{width:n,flexShrink:0},A.createElement(D,{color:r.dim},e.toUpperCase())),typeof t==`string`?A.createElement(D,{color:r.fg},t):t)}function Mn(e){return e.review&&process.stdout.isTTY?`interactive`:`auto`}function Nn(e){let t=null,n=null,r=!1,i=!1,a=0,o=new Set,s=()=>{for(let e of o)e(t)};return{async approve(o){if(i)return`n`;if(e.mode===`auto`||r)return`y`;let c=String(++a);return new Promise(e=>{t={id:c,...o},n=t=>{t===`a`&&(r=!0),e(t)},s()})},subscribe(e){return o.add(e),()=>{o.delete(e)}},current(){return t},resolve(e,r){if(!t||t.id!==e||!n)return;let i=n;t=null,n=null,s(),i(r)},abort(){if(i=!0,n){let e=n;t=null,n=null,s(),e(`n`)}}}}function Pn(){let e=[12,15,18,22,28,35,41,50,58,64,72,78],t=y(e,24);return[` ↑`,` ${t}`,` └────────────────────────`,` May Jun Jul Aug Sep`]}function Fn(){return[` Landing page ████████████████████ 100%`,` ↓ -28%`,` Pricing ██████████████░░░░░░ 72%`,` ↓ -50%`,` Checkout ███████░░░░░░░░░░░░░ 36%`,` ↓ -64%`,` Purchased ███░░░░░░░░░░░░░░░░░ 13%`]}function In(){return[` ▇▇▇▇▇▇▇▇▇▎ 89 Sarah K. /pricing 🇺🇸`,` ▇▇▇▇▇▇░░░░ 61 anon /docs 🇩🇪`,` ▇▇▇░░░░░░░ 34 anon /changelog 🇯🇵`]}function Ln(){return[` LCP ● 2.1s good ████████████░░░░░░`,` INP ● 89ms good ██████████████████`,` CLS ⚠ 0.14 needs ████████████░░░░░░`,` TTFB ● 380ms good ███████████████░░░`]}const Z=[{title:`Welcome`,body:[`The wizard is an agent — it handles the entire`,`Zenovay setup process on your behalf.`,``,`Reading your project, deciding where the tracker`,`belongs, and writing the snippet for you.`]},{title:`Events`,body:[`Events are the foundation of Zenovay analytics.`,``,`Every pageview, click, and custom action is an`,`event. Group them into Goals to track conversions.`]},{title:`Trends`,body:[`Use trends to measure growth.`,``,`See how visitors, signups, and revenue have`,`changed over the last week, month, or quarter.`],visual:Pn},{title:`Funnels`,body:[`Funnels reveal where users drop off.`,``,`Define the steps you care about — landing,`,`pricing, checkout, purchase — and Zenovay shows`,`the per-step conversion rate.`],visual:Fn},{title:`Live visitors`,body:[`Watch every visitor in real time, ranked by an`,`AI score that highlights high-intent traffic.`,``,"Use `zenovay visitors --live` after install."],visual:In},{title:`Core Web Vitals`,body:[`LCP, INP, CLS — Google's page-experience metrics,`,`collected automatically once the tracker is in.`,``,"Use `zenovay vitals` to see them per page + device."],visual:Ln},{title:`MCP integration`,body:[`Your CLI session can install Zenovay's MCP server`,`into Cursor / Claude Desktop / VS Code.`,``,`Then ask your AI: "what were my top pages today?"`,`and it queries Zenovay directly.`]},{title:`AI insights`,body:[`Zenovay watches your data and surfaces anomalies`,`automatically — traffic spikes, error bursts,`,`revenue drops — without you needing to ask.`,``,"Run `zenovay ai` after install to chat with them."]},{title:`Privacy-first`,body:[`No cookies required. No third-party trackers.`,`Cookieless mode is one toggle in your dashboard.`,``,`GDPR, CCPA, and PECR-compliant out of the box.`]}],Rn=11e3,zn=9,Bn=({width:e,height:t})=>{let{theme:n}=m(),[r,i]=M(0);j(()=>{if(Z.length<=1)return;let e=setInterval(()=>{i(e=>(e+1)%Z.length)},Rn);return()=>clearInterval(e)},[]);let a=Z[r],o=a.visual?a.visual():[];return A.createElement(E,{flexDirection:`column`,width:e,height:t,paddingX:2,paddingY:1},A.createElement(E,{marginBottom:1},A.createElement(D,{color:n.typography.label.color,dimColor:n.typography.label.dim},`LEARN`)),A.createElement(E,{flexDirection:`column`,minHeight:zn,key:r},A.createElement(E,{marginBottom:1},A.createElement(D,{bold:!0,color:n.fg,wrap:`truncate`},a.title)),a.body.map((e,t)=>A.createElement(D,{key:t,color:n.muted,wrap:`truncate`},e||` `)),o.length>0&&A.createElement(E,{flexDirection:`column`,marginTop:1},o.map((e,t)=>A.createElement(D,{key:t,color:n.accent},e)))),A.createElement(E,{flexGrow:1}),A.createElement(E,{marginTop:1},A.createElement(D,null,Z.map((e,t)=>A.createElement(D,{key:t,color:t===r?n.muted:n.dim},t===r?`─`:`·`)))))},Vn={pending:`·`,active:`⠋`,done:`✓`,error:`✗`};function Hn(e,t){switch(e){case`pending`:return t.dim;case`active`:return t.accent;case`done`:return t.success;case`error`:return t.error}}const Un=({tasks:e,width:t,height:n,liveLine:r})=>{let{theme:i}=m();return A.createElement(E,{flexDirection:`column`,width:t,height:n,paddingX:2,paddingY:1},A.createElement(E,{marginBottom:1},A.createElement(D,{color:i.typography.label.color,dimColor:i.typography.label.dim},`TASKS`)),e.map(e=>A.createElement(E,{key:e.id,flexDirection:`column`},A.createElement(E,{flexDirection:`row`},e.status===`active`?A.createElement(D,{color:i.accent},A.createElement(P,{type:`dots`})):A.createElement(D,{color:Hn(e.status,i)},Vn[e.status]),A.createElement(D,null,` `),A.createElement(D,{color:e.status===`pending`?i.muted:i.fg,wrap:`truncate`},e.label)),e.detail&&A.createElement(E,{paddingLeft:2},A.createElement(D,{color:i.dim,wrap:`truncate`},e.detail)))),A.createElement(E,{flexGrow:1}),r&&A.createElement(E,{flexDirection:`row`,marginTop:1},A.createElement(D,{color:i.accent},`◆ `),A.createElement(D,{color:i.fg,wrap:`truncate`},r)))};async function Wn(e){if(o(e))return Gn(e);let t=`quit`,n=null,r=await new Promise(r=>{let{unmount:i,waitUntilExit:a}=ee(A.createElement(Kn,{opts:e,onExit:(e,a,o)=>{t=a??`quit`,n=o??null,i(),r(e)}}),{exitOnCtrlC:!0});a().catch(()=>r(1))});if(r===0&&await d().catch(()=>void 0),r!==0||t===`quit`||!n)return r;let i=n.id;if(t===`watch`){let{overviewCommand:t}=await import(`./overview-L8jqiSuU.js`);return t({cliVersion:e.cliVersion,siteId:i})}if(t===`events`){let{eventsTailCommand:t}=await import(`./events-tail-DEdVkmT0.js`);return t({cliVersion:e.cliVersion,siteId:i})}return 0}async function Gn(t){let r=await import(`./telemetry-Bbh6Vtst.js`).catch(()=>null);r?.emit(`install.start`,{properties:{mode:`headless`}});try{let o=await e(),s=await vt(t.cwd);if(!s)return a({type:`error`,code:`no_framework`,message:`Could not detect framework (headless mode has no picker).`}),r?.emit(`install.error`,{error_code:`no_framework`,properties:{mode:`headless`}}),2;a({type:`framework.detected`,framework:s.id,confidence:1}),r?.emit(`install.framework_detected`,{properties:{framework:s.id}});let c=await i({strict:!1});if(!c&&!process.env.ZENOVAY_API_TOKEN)return a({type:`error`,code:`not_logged_in`,message:"Run `zenovay login` first (or set ZENOVAY_API_TOKEN)."}),2;let l=new n({config:o,cliVersion:t.cliVersion,token:c}),[u,d]=await Promise.all([l.getSites(),B(t.cwd,``).catch(()=>null)]),f;if(t.siteId){if(f=u.find(e=>e.id===t.siteId),!f)return a({type:`error`,code:`site_not_found`,message:`Site ${t.siteId} not found.`}),2}else if(d?.foundTrackingCodes.length){for(let e of d.foundTrackingCodes){let t=u.find(t=>t.trackingCode===e);if(t){f=t;break}}f||=u[0]}else f=u[0];if(!f)return a({type:`error`,code:`no_site`,message:`No sites exist; create one at app.zenovay.com or re-run interactively.`}),2;let p=f.trackingCode??f.id;if(d&&(d.recommendation===`no-op`||d.foundTrackingCodes.includes(p)))return a({type:`info`,message:`Zenovay already installed correctly for ${f.name} (${p})`}),a({type:`done`,command:`init`,exitCode:0}),0;a({type:`info`,message:`Using site ${f.id} (${f.name}) trackingCode=${f.trackingCode??f.id}`});let m=f.trackingCode??f.id,h=await G(t.cwd,{[s.envVarName]:m});if(a({type:`info`,message:`env vars added: ${h.addedKeys.join(`,`)}`}),s.id===`plain-html`){let e=await Wt({cwd:t.cwd,trackingCode:m,updateOnly:d?.recommendation===`update-tracking-code`});if(e.installed.length===0&&e.skipped.length===0)return a({type:`error`,code:`install_failed`,message:`No .html files with a <head> block found to inject the tracker into (errors: ${e.errors.length}).`}),r?.emit(`install.error`,{error_code:`install_failed`,properties:{mode:`headless`,framework:s.id}}),1;if(a({type:`info`,message:`tracker injected into ${e.installed.length} file(s); ${e.skipped.length} already current; ${e.errors.length} error(s)`}),t.firstParty!==!1){let e=await Dn(t.cwd,{trackingCode:m});e.proxyFile&&e.proxyAction!==`manual`&&e.proxyAction!==`skipped`?a({type:`info`,message:`first-party proxy added: ${e.proxyFile} (${e.proxyAction}) — same-origin /_z/* → api.zenovay.com/fp/*`}):e.manual&&a({type:`info`,message:`first-party tracking needs a manual step: ${e.manual}`})}}else return a({type:`error`,code:`headless_framework_unsupported`,message:`Headless install can only auto-inject the tracker for plain-HTML projects. For ${s.displayName}, run \`zenovay init\` interactively — the AI wizard places the <Script> in the correct entry file. (The env var ${h.addedKeys.join(`, `)||s.envVarName} was written.)`}),r?.emit(`install.error`,{error_code:`headless_framework_unsupported`,properties:{mode:`headless`,framework:s.id}}),1;return a({type:`done`,command:`init`,exitCode:0}),r?.emit(`install.success`,{properties:{framework:s.id,mode:`headless`,env_keys_added:h.addedKeys.length}}),0}catch(e){return a({type:`error`,code:`init_failed`,message:e.message}),r?.emit(`install.error`,{error_code:`init_failed`,properties:{mode:`headless`,error:(e.message??`unknown`).slice(0,128)}}),1}}const Kn=({opts:t,onExit:r})=>{let{exit:a}=te(),{theme:o}=m(),[s,c]=M(`intro`),[l,d]=M(null),[h,g]=M([]),[_,y]=M(null),[b,x]=M(null),[S,C]=M(null),[w,T]=M([]),[ee,k]=M(void 0),[N,F]=M(null),[re,I]=M(null),[ie,L]=M(null),[ae,oe]=M(0),[se,ce]=M(!1),[R,z]=M(null),V=ne(null),le=ne(!1);return O((e,t)=>{s===`error`&&(e.toLowerCase()===`q`||t.escape)&&(a(),r(1))}),j(()=>(process.stdout.isTTY&&process.stdout.write(`\x1B[?1049h\x1B[H`),()=>{V.current?.abort(),process.stdout.isTTY&&process.stdout.write(`\x1B[?1049l`)}),[]),j(()=>{le.current||(le.current=!0,(async()=>{try{c(`detecting`);let r=await vt(t.cwd);if(!r){C(`Could not detect your framework. Supported: Next.js, Nuxt, SvelteKit, Astro, Remix, plain HTML.`),c(`error`);return}d(r);let a=await i({strict:!1});!process.env.ZENOVAY_API_TOKEN&&!a&&(c(`login`),await u({cliVersion:t.cliVersion,offerCompletions:!1})),c(`loading-sites`);let o=await e(),s=await i({strict:!1}),l=new n({config:o,cliVersion:t.cliVersion,token:s}),[f,p]=await Promise.all([l.getSites(),B(t.cwd,``).catch(()=>null)]),m=await pr(t.cwd);if(g(f),y(m),f.length===0){c(`create-site`);return}if(p?.foundTrackingCodes.length)for(let e of p.foundTrackingCodes){let t=f.find(t=>t.trackingCode===e);if(t){x(t),c(`already-installed`);return}}if(t.siteId){let e=f.find(e=>e.id===t.siteId);if(!e){C(`Site ${t.siteId} not found in your account.`),c(`error`);return}x(e),Yn(e,r,t,{setPhase:c,setErrorMsg:C,setTasks:T,setLiveLine:k,setSandboxStats:F,setVerifyResult:I,setFirstParty:L,setPendingApproval:z,registerBridge:e=>{V.current=e}});return}let h=mr(f,m);if(t.yes&&h){x(h),Yn(h,r,t,{setPhase:c,setErrorMsg:C,setTasks:T,setLiveLine:k,setSandboxStats:F,setVerifyResult:I,setFirstParty:L,setPendingApproval:z,registerBridge:e=>{V.current=e}});return}c(`site-picker`)}catch(e){C(e.message),c(`error`)}})())},[]),j(()=>{s===`error`&&(V.current?.abort(),setTimeout(()=>{a(),r(1)},8e3))},[s]),A.createElement(E,{flexDirection:`column`},A.createElement(f,{version:t.cliVersion,subtitle:`AI install wizard`}),s===`intro`&&A.createElement(Xn,null),s===`detecting`&&A.createElement(p,{title:`Scanning`,state:`busy`},A.createElement(E,null,A.createElement(D,{color:o.accent},A.createElement(P,{type:`dots`})),A.createElement(D,null,` Detecting framework…`))),s===`login`&&A.createElement(p,{title:`Authenticating`,state:`busy`},A.createElement(D,null,`Opening browser for login… (paste ZENOVAY_API_TOKEN env var to skip)`)),s===`loading-sites`&&A.createElement(p,{title:`Loading your sites`,state:`busy`},A.createElement(E,null,A.createElement(D,{color:o.accent},A.createElement(P,{type:`dots`})),A.createElement(D,null,` Fetching your Zenovay sites…`))),s===`already-installed`&&b&&A.createElement(Qn,{site:b,onContinue:()=>{c(`site-picker`)},onExit:()=>{a(),r(0,`quit`,b)}}),s===`site-picker`&&l&&A.createElement($n,{sites:h,preselectHint:_,onPick:e=>{if(e===`create-new`){c(`create-site`);return}x(e),Yn(e,l,t,{setPhase:c,setErrorMsg:C,setTasks:T,setLiveLine:k,setSandboxStats:F,setVerifyResult:I,setFirstParty:L,setPendingApproval:z,registerBridge:e=>{V.current=e}})}}),s===`create-site`&&A.createElement(p,{title:`Create a new site`,state:`warn`},A.createElement(D,{color:o.warn},"No site picked (or none exist yet). Visit https://app.zenovay.com/websites to create one, then re-run `zenovay init`."),A.createElement(D,{color:o.muted},`(Inline site creation from the CLI is scheduled for V1.1 — it requires a plan-limit check.)`)),s===`agent`&&l&&b&&A.createElement(Zn,{adapter:l,site:b,tasks:w,liveLine:ee,pendingApproval:R,onApprove:e=>{R&&V.current?.resolve(R.id,e)}}),s===`install-complete`&&l&&b&&A.createElement(rr,{adapter:l,site:b,stats:N,verify:re,firstParty:ie,onCommit:()=>c(`commit-prompt`),onSkipCommit:()=>c(`deploy-prompt`)}),s===`commit-prompt`&&b&&A.createElement(ar,{cwd:t.cwd,onDone:e=>{e.committed&&k(`Pushed ${e.shortSha} to ${e.branch}`),c(`deploy-prompt`)},onError:e=>{C(e),c(`error`)}}),s===`deploy-prompt`&&b&&A.createElement(or,{site:b,onDeployed:()=>c(`visit-prompt`),onSkip:()=>c(`verify`)}),s===`visit-prompt`&&b&&A.createElement(sr,{site:b,onContinue:()=>c(`verify`)}),s===`verify`&&b&&A.createElement(lr,{site:b,elapsed:ae,onTick:e=>oe(e),onArrived:()=>{ce(!0),c(`success`)},onTimeout:()=>c(`success`)}),s===`success`&&l&&b&&A.createElement(nr,{adapter:l,site:b,stats:N,arrived:se,firstParty:ie,onPick:e=>{a(),r(0,e,b)}}),s===`error`&&A.createElement(p,{title:`Install failed`,state:`err`},A.createElement(D,{color:o.error},S??`Unknown error`)),A.createElement(v,{items:s===`already-installed`?[{key:`q`,label:`quit`},{key:`r`,label:`re-run wizard`},{key:`ctrl-c`,label:`abort`}]:[{key:`q`,label:`quit`},{key:`ctrl-c`,label:`abort`}]}))};async function Q(e,t,n){if(e.firstParty===!1||!n.setFirstParty)return;n.setTasks(e=>e.some(e=>e.id===`first-party`)?e.map(e=>e.id===`first-party`?{...e,status:`active`}:e):[...e,{id:`first-party`,label:`Set up first-party tracking (all browsers)`,status:`active`}]),n.setLiveLine(`Setting up first-party tracking (all browsers)…`);let r=null;try{r=await Dn(e.cwd,{trackingCode:t}),n.setFirstParty(r)}catch{n.setFirstParty(null)}let i=qn(r);n.setTasks(e=>e.map(e=>e.id===`first-party`?{...e,status:`done`,detail:i}:e)),n.setLiveLine(void 0)}function qn(e){return e?e.proxyFile&&e.proxyAction!==`manual`&&e.proxyAction!==`skipped`?`${b.basename(e.proxyFile)} (${e.proxyAction})`:e.manual?`needs a manual proxy — see below`:e.snippetSwapped?`snippet now same-origin`:`no proxy written`:`skipped`}async function Jn(e,t,n){n.setLiveLine(`Verifying the tracker is in your source…`);try{let r=await B(e,t),i=r.recommendation===`no-op`&&r.foundTrackingCodes.includes(t);n.setVerifyResult?.({ok:i,detail:i?`Tracker confirmed in your source files`:`Couldn't auto-confirm the tracker (diagnosis: ${r.recommendation}) — please double-check`})}catch{n.setVerifyResult?.({ok:!1,detail:`Verification check could not run`})}n.setLiveLine(void 0)}async function Yn(r,a,o,s){try{s.setPhase(`agent`);let c=await e(),l=await i({strict:!1}),u=new n({config:c,cliVersion:o.cliVersion,token:l}),d=new ge({cwd:o.cwd}),f=r.trackingCode??r.id;await t({defaultSiteId:r.id,defaultSiteName:r.name}).catch(()=>void 0);let p=await B(o.cwd,f);if(p.recommendation===`no-op`){s.setTasks(e=>e.map(e=>e.id===`detect`||e.id===`read`||e.id===`write`?{...e,status:`done`,detail:`already installed correctly`}:e)),s.setLiveLine(p.summary),s.setSandboxStats?.({reads:0,writes:0,rounds:0}),!o.skipMcp&&l&&await K({accessToken:l.accessToken}).catch(()=>void 0),s.setVerifyResult?.({ok:!0,detail:`Already installed correctly`}),await Q(o,f,s),s.setPhase(`install-complete`);return}if(p.competitors.length>0&&s.setLiveLine(`Detected ${p.competitors.map(e=>e.name).join(`, `)} — will install Zenovay alongside (won't touch their tags).`),a.id===`next-app-router`){s.setTasks([{id:`detect`,label:`Detect framework`,status:`done`,detail:`Next.js (App Router)`},{id:`read`,label:`Find the document shell layout`,status:`active`},{id:`write`,label:`Insert tracker <Script>`,status:`pending`},{id:`env`,label:`Write env vars (.env.local)`,status:`pending`},{id:`mcp`,label:`Install MCP clients (Cursor/Claude/VS Code)`,status:`pending`},{id:`verify`,label:`Verify the tracker in your source`,status:`pending`}]),s.setLiveLine(`Next.js — locating the document shell layout…`);let e=await Xt({cwd:o.cwd,trackingCode:f});if(!e.fellThrough&&e.errors.length===0){let t=e.installed[0]??`already installed`;s.setTasks(e=>e.map(e=>e.id===`read`||e.id===`write`?{...e,status:`done`,detail:t}:e));let n=await G(o.cwd,{[a.envVarName]:f});if(s.setTasks(e=>e.map(e=>e.id===`env`?{...e,status:`done`,detail:`${n.addedKeys.length} key(s) added`}:e)),!o.skipMcp&&l){let e=await K({accessToken:l.accessToken}).catch(()=>null);s.setTasks(t=>t.map(t=>t.id===`mcp`?{...t,status:`done`,detail:e&&e.installed.length>0?e.installed.join(`, `):`none detected`}:t))}else s.setTasks(e=>e.map(e=>e.id===`mcp`?{...e,status:`done`,detail:`skipped`}:e));await Jn(o.cwd,f,s),s.setTasks(e=>e.map(e=>e.id===`verify`?{...e,status:`done`}:e)),s.setSandboxStats?.({reads:1,writes:e.installed.length,rounds:0}),await Q(o,f,s),s.setPhase(`install-complete`);return}s.setLiveLine(`Custom Next.js structure — handing off to the AI agent…`)}let m=a.id===`plain-html`;if(m){s.setTasks([{id:`detect`,label:`Detect framework`,status:`done`,detail:`Plain HTML`},{id:`read`,label:`Scan .html pages`,status:`active`},{id:`write`,label:`Insert tracker <script>`,status:`pending`},{id:`env`,label:`Write env vars`,status:`pending`},{id:`mcp`,label:`Install MCP clients (Cursor/Claude/VS Code)`,status:`pending`},{id:`verify`,label:`Verify the tracker in your source`,status:`pending`}]),s.setLiveLine(`Plain HTML — using fast install path (no AI needed)…`);let e=await Wt({cwd:o.cwd,trackingCode:f,updateOnly:p.recommendation===`update-tracking-code`});if(e.installed.length===0&&e.errors.length===0&&e.skipped.length===0)throw Error(`No .html files with a <head>...</head> block were found to inject the tracker into.`);if(e.installed.length===0&&e.errors.length>0)throw Error(`Programmatic install failed for all ${e.errors.length} file(s). First error: ${e.errors[0].file}: ${e.errors[0].error}`);s.setTasks(t=>t.map(t=>t.id===`read`||t.id===`write`?{...t,status:`done`,detail:`${e.installed.length} file(s)`}:t)),s.setLiveLine(`Tracker added to ${e.installed.length} file(s). Skipped ${e.skipped.length}, errored ${e.errors.length}.`);let t=await G(o.cwd,{[a.envVarName]:f});if(s.setTasks(e=>e.map(e=>e.id===`env`?{...e,status:`done`,detail:`${t.addedKeys.length} key(s) added`}:e)),!o.skipMcp&&l){let e=await K({accessToken:l.accessToken}).catch(()=>null);s.setTasks(t=>t.map(t=>t.id===`mcp`?{...t,status:`done`,detail:e&&e.installed.length>0?e.installed.join(`, `):`none detected`}:t))}else s.setTasks(e=>e.map(e=>e.id===`mcp`?{...e,status:`done`,detail:`skipped`}:e));await Jn(o.cwd,f,s),s.setSandboxStats?.({reads:e.installed.length,writes:e.installed.length,rounds:0}),await Q(o,f,s),s.setPhase(`install-complete`);return}{let e=await $t({cwd:o.cwd,trackingCode:f,siteId:r.id,adapter:a}),t=e.skipped.some(e=>/already present/.test(e.reason));if(!e.fellThrough&&e.errors.length===0&&(e.installed.length>0||t)){s.setTasks([{id:`detect`,label:`Detect framework`,status:`done`,detail:a.displayName},{id:`write`,label:`Insert tracker snippet`,status:`done`,detail:e.installed[0]??`already installed`},{id:`env`,label:`Write env vars (.env.local)`,status:`active`},{id:`mcp`,label:`Install MCP clients (Cursor/Claude/VS Code)`,status:`pending`},{id:`verify`,label:`Verify the tracker in your source`,status:`pending`}]),s.setLiveLine(`${a.displayName} — using fast install path (no AI needed)…`);let t=await G(o.cwd,{[a.envVarName]:f});if(s.setTasks(e=>e.map(e=>e.id===`env`?{...e,status:`done`,detail:`${t.addedKeys.length} key(s) added`}:e)),!o.skipMcp&&l){let e=await K({accessToken:l.accessToken}).catch(()=>null);s.setTasks(t=>t.map(t=>t.id===`mcp`?{...t,status:`done`,detail:e&&e.installed.length>0?e.installed.join(`, `):`none detected`}:t))}else s.setTasks(e=>e.map(e=>e.id===`mcp`?{...e,status:`done`,detail:`skipped`}:e));await Jn(o.cwd,f,s),s.setTasks(e=>e.map(e=>e.id===`verify`?{...e,status:`done`}:e)),s.setSandboxStats?.({reads:1,writes:e.installed.length,rounds:0}),await Q(o,f,s),s.setPhase(`install-complete`);return}s.setLiveLine(`${a.displayName} — handing off to the AI agent…`)}let h=a.buildSnippets({trackingCode:f,siteId:r.id}),g=`Install Zenovay tracking into this ${a.displayName} project.\nSite: ${r.name} (trackingCode: ${f}).\n1. Call detect_framework to confirm the framework.\n2. Read the entry file(s) for this framework:\n`+a.files.map(e=>` - ${e.path}`).join(`
309
+ `)+`
310
+ 3. Insert the tracker using the snippet below.
311
+ 4. Confirm the change with a diff before writing.
312
+
313
+ Snippet reference (anchored by anchorKind — adapt to the actual file):
314
+ `+h.map(e=>` ${e.anchorKind} @ ${e.targetPath}:\n ${e.code.replace(/\n/g,`
315
+ `)}`).join(`
316
+
317
+ `);s.setTasks([{id:`detect`,label:`Detect framework`,status:`active`},{id:`read`,label:`Read project entry files`,status:`pending`},{id:`write`,label:`Insert tracker snippet`,status:`pending`},{id:`env`,label:`Write env vars (.env.local)`,status:`pending`},{id:`mcp`,label:`Install MCP clients (Cursor/Claude/VS Code)`,status:`pending`},{id:`verify`,label:`Wait for first event`,status:`pending`}]);let _=(e,t,n)=>{s.setTasks(r=>r.map(r=>r.id===e?{...r,status:t,detail:n??r.detail}:r))},v=Nn({mode:Mn(o)});s.registerBridge(v);let y=v.subscribe(e=>s.setPendingApproval(e)),b=null,x=``,S=0;try{await Vt({api:u,sandbox:d,approval:v,prompter:{async ask({question:e}){return e}},handlers:{onToolStart:({name:e})=>{e===`detect_framework`?(_(`detect`,`active`),s.setLiveLine(`Inspecting package.json + config files…`)):e===`read_file`?(_(`detect`,`done`),_(`read`,`active`)):(e===`write_file`||e===`patch_file`)&&(_(`read`,`done`),_(`write`,`active`))},onToolResult:({name:e,result:t})=>{let n=t?.ok!==!1,r=t?.content??{};e===`detect_framework`&&n&&(_(`detect`,`done`,a.displayName),s.setLiveLine(`Detected ${a.displayName}`)),e===`read_file`&&n&&r.path&&s.setLiveLine(`Read ${r.path}`),(e===`write_file`||e===`patch_file`)&&n&&r.path&&(_(`read`,`done`),_(`write`,`done`,`wrote ${r.path}`),s.setLiveLine(`Wrote ${r.path}`))},onDelta:e=>{x=(x+e).slice(-600);let t=Date.now();if(t-S<280)return;S=t;let n=x.replace(/\s+/g,` `).trim().slice(-80);n.length>4&&s.setLiveLine(n)}},initialMessages:[{role:`user`,content:g}],diagnosis:p,targetTrackingCode:f})}catch(e){b=e}finally{y(),s.setPendingApproval(null)}let C=d.stats();if(C.writes===0){let e=b?`Agent error: ${b.message}`:`Agent returned without writing any files (${C.reads} reads, ${C.rounds} rounds). The model may have refused or the AI Gateway may be misconfigured. Check api.zenovay.com logs.`;throw Error(e)}let w=a.files.map(e=>e.path),T=C.writtenPaths.some(e=>w.some(t=>e===t||e.endsWith(`/${t}`)));if(!T)throw Error(`Agent wrote to: [${C.writtenPaths.join(`, `)}] but never modified the framework's tracker target file(s): [${w.join(`, `)}]. The tracker is NOT installed. The model likely put the trackingCode into .env.local instead of into the HTML/JSX entry file.`);_(`write`,`done`),_(`env`,`active`),s.setLiveLine(`Adding env var to .env.local`);let E=await G(o.cwd,{[a.envVarName]:f});if(_(`env`,`done`,`${E.addedKeys.length} key(s) added`),!o.skipMcp&&l){_(`mcp`,`active`),s.setLiveLine(`Detecting Cursor / Claude Desktop / VS Code…`);let e=await K({accessToken:l.accessToken}).catch(()=>null);e&&e.installed.length>0?_(`mcp`,`done`,e.installed.join(`, `)):_(`mcp`,`done`,`none detected`)}else _(`mcp`,`done`,`skipped`);s.setTasks(e=>e.map(e=>e.id===`detect`||e.id===`read`||e.id===`write`?{...e,status:e.status===`pending`?`done`:e.status}:e)),await Jn(o.cwd,f,s),s.setSandboxStats?.(C),await Q(o,f,s),s.setPhase(`install-complete`)}catch(e){s.setErrorMsg(e.message),s.setPhase(`error`)}}const Xn=()=>{let{theme:e}=m();return A.createElement(p,{title:`Zenovay wants to set up your project`,state:`idle`},A.createElement(D,null,`We'll use AI to analyze your codebase and add the Zenovay tracker.`),A.createElement(D,{color:e.muted},`.env* contents stay on your machine. Only framework metadata reaches api.zenovay.com.`))},Zn=({adapter:e,site:t,tasks:n,liveLine:r,pendingApproval:i,onApprove:a})=>{let{theme:o}=m(),s=process.stdout.columns??100,c=(process.stdout.rows??30)-8,l=Math.floor(s/2)-1,u=Math.max(14,c);return A.createElement(E,{flexDirection:`column`},A.createElement(E,{flexDirection:`row`,minHeight:u,borderStyle:`round`,borderColor:o.border},A.createElement(E,{width:l,flexDirection:`column`},A.createElement(Bn,{width:l,height:u})),A.createElement(E,{width:1,flexDirection:`column`},A.createElement(D,{color:o.border},`│`)),A.createElement(E,{width:l,flexDirection:`column`},i?A.createElement(E,{flexDirection:`column`,paddingX:2,paddingTop:1},A.createElement(D,{color:o.warn},`The agent wants to write `,i.path),i.reason?A.createElement(D,{color:o.muted},i.reason):null,A.createElement(h,{key:i.id,filePath:i.path,oldContent:i.oldContent,newContent:i.newContent,onApprove:a})):A.createElement(A.Fragment,null,A.createElement(E,{paddingX:2,paddingTop:1},A.createElement(g,{done:n.filter(e=>e.status===`done`).length,total:n.length,label:n.find(e=>e.status===`active`)?.label})),A.createElement(Un,{tasks:n,liveLine:r,width:l,height:u})))),A.createElement(E,{marginTop:1},A.createElement(D,{color:o.muted},`Site: `,t.name,` · code: `,t.trackingCode??t.id,` · Framework: `,e.displayName)))},Qn=({site:e,onContinue:t,onExit:n})=>{let{theme:r}=m();O((e,r)=>{if(e===`q`||r.escape){n();return}if(e===`r`||e===`R`){t();return}});let i=e.url.replace(/^https?:\/\//,``).replace(/\/$/,``);return A.createElement(p,{title:`Already installed`,state:`ok`},A.createElement(E,{flexDirection:`column`,gap:1},A.createElement(E,{flexDirection:`row`,gap:1},A.createElement(D,{color:r.success},`✓`),A.createElement(D,{bold:!0},e.name),A.createElement(D,{color:r.muted},`(`,i,`)`)),A.createElement(D,{color:r.muted},`Zenovay is already tracking this project. No changes needed.`),e.trackingCode?A.createElement(D,{color:r.dim},`tracking code: `,e.trackingCode):null))},$n=({sites:e,preselectHint:t,onPick:n})=>{let r=mr(e,t),i=e.filter(e=>e.id!==r?.id),a=r?[r,...i]:i,o=[...a.map(e=>({label:fr(e,e.id===r?.id),value:e,description:e.trackingCode?`trackingCode: ${e.trackingCode}`:void 0})),{label:`+ Create new site on app.zenovay.com`,value:`create-new`,description:`Open the dashboard to add a new website`}];return A.createElement(p,{title:t?`Pick a site (best match: ${t})`:`Pick a site`,state:`idle`},A.createElement(_,{items:o,onSelect:n}))};function er(){let e=process.argv[1]??``;return e.includes(`/_npx/`)||e.includes(`\\_npx\\`)||process.env.npm_execpath?.includes(`npx`)?`npx`:`global`}const tr=({k:e,primary:t})=>{let{theme:n}=m();return A.createElement(D,null,A.createElement(D,{color:n.muted},`[`),A.createElement(D,{bold:!0,color:t?n.accent:n.fg},e),A.createElement(D,{color:n.muted},`] `))},nr=({adapter:e,site:t,stats:n,arrived:i,firstParty:a,onPick:o})=>{let{theme:c}=m(),l=er(),u=l===`npx`?`npx @zenovay/cli`:`zenovay`,d=r(t),f=n&&n.rounds>0?`${n.writes} file(s) · ${n.rounds} agent round(s)`:n&&n.writes>0?`${n.writes} file(s) · programmatic (no AI calls)`:null;return O((e,t)=>{let n=e.toLowerCase();n===`w`||t.return?o(`watch`):n===`o`?s(d).catch(()=>void 0):n===`e`?o(`events`):(n===`s`||n===`q`||t.escape)&&o(`quit`)},{isActive:!0}),A.createElement(p,{title:`You're live on Zenovay`,state:`ok`},A.createElement(D,{bold:!0,color:c.success},i?`✓ First event received from ${t.name}`:`✓ Installed successfully`),A.createElement(D,{color:c.accent},`─`.repeat(18)),A.createElement(E,{marginTop:1,flexDirection:`column`},A.createElement(X,{label:`Framework`,value:e.displayName}),A.createElement(X,{label:`Site`,value:A.createElement(jn,{url:t.url,label:t.name})}),t.trackingCode?A.createElement(X,{label:`Tracker`,value:A.createElement(D,{color:c.muted},t.trackingCode)}):null,f?A.createElement(X,{label:`Changes`,value:A.createElement(D,{color:c.muted},f)}):null,A.createElement(X,{label:`Dashboard`,value:A.createElement(jn,{url:d})})),a&&a.proxyFile&&a.proxyAction!==`manual`&&a.proxyAction!==`skipped`?A.createElement(E,{marginTop:1},A.createElement(D,{color:c.success},`✓ `),A.createElement(D,{color:c.muted},`First-party proxy added — after you deploy, tracking works in every browser.`)):a?.manual?A.createElement(E,{marginTop:1,flexDirection:`column`},A.createElement(D,{color:c.warn},`⚠ First-party tracking needs one manual step:`),A.createElement(D,{color:c.muted},` `,a.manual)):null,A.createElement(E,{marginTop:1,flexDirection:`column`},A.createElement(D,{color:c.dim},`WHAT'S NEXT`),A.createElement(E,{marginTop:1},A.createElement(D,null,` `),A.createElement(tr,{k:`W`,primary:!0}),A.createElement(D,null,`Watch live in this terminal `),A.createElement(D,{color:c.muted},`— visitors, pages, sources, vitals`)),A.createElement(E,null,A.createElement(D,null,` `),A.createElement(tr,{k:`O`}),A.createElement(D,null,`Open the dashboard in your browser`)),A.createElement(E,null,A.createElement(D,null,` `),A.createElement(tr,{k:`E`}),A.createElement(D,null,`Stream events as they fire `),A.createElement(D,{color:c.muted},`— page views, custom events, live`)),A.createElement(E,null,A.createElement(D,null,` `),A.createElement(tr,{k:`S`}),A.createElement(D,null,`Exit to shell`))),l===`npx`?A.createElement(E,{marginTop:1,flexDirection:`column`,borderStyle:`single`,borderColor:c.warn,paddingX:1},A.createElement(D,{color:c.warn,bold:!0},`Heads up — you ran via npx`),A.createElement(D,{color:c.muted},"The `zenovay` command isn't on your PATH yet. After you exit, run:"),A.createElement(E,{marginTop:1},A.createElement(D,{color:c.fg},` npm i -g @zenovay/cli `),A.createElement(D,{color:c.muted},"→ then `zenovay overview` works directly"))):A.createElement(E,{marginTop:1},A.createElement(D,{color:c.muted},`From your shell: `),A.createElement(D,{color:c.fg},u,` overview`),A.createElement(D,{color:c.muted},` · `),A.createElement(D,{color:c.fg},u,` --help`)))},rr=({adapter:e,site:t,stats:n,verify:r,firstParty:i,onCommit:a,onSkipCommit:o})=>{let{theme:s}=m();return ur(e=>{e===`y`||e===`Y`||e===`\r`?a():(e===`n`||e===`N`||e===`s`||e===`S`)&&o()}),A.createElement(p,{title:`Tracker installed`,state:r&&!r.ok?`warn`:`ok`},r?A.createElement(D,null,A.createElement(D,{color:r.ok?s.success:s.warn},r.ok?`✔ Verified — `:`⚠ `),r.detail):A.createElement(D,null,A.createElement(D,{color:s.success},`✔ `),`The Zenovay tracker is in your project.`),A.createElement(E,{marginTop:1},A.createElement(D,{color:s.muted},`Framework: `),A.createElement(D,null,e.displayName)),A.createElement(E,null,A.createElement(D,{color:s.muted},`Site: `),A.createElement(D,null,t.name,` (`,t.url,`)`)),n&&A.createElement(E,null,A.createElement(D,{color:s.muted},`Modified: `),A.createElement(D,null,n.writes,` file(s)`)),A.createElement(ir,{firstParty:i}),A.createElement(E,{marginTop:1},A.createElement(D,null,`Commit + push these changes to git? `),A.createElement(D,{color:s.accent},`[Y/n]`)))},ir=({firstParty:e})=>{let{theme:t}=m();if(!e)return null;let n=e.proxyFile&&e.proxyAction!==`manual`&&e.proxyAction!==`skipped`;return n?A.createElement(E,{marginTop:1,flexDirection:`column`},A.createElement(E,null,A.createElement(D,{color:t.success},`✔ `),A.createElement(D,null,`First-party proxy added `),A.createElement(D,{color:t.muted},`(`,e.proxyFile,`)`)),A.createElement(D,{color:t.muted},`After you deploy, tracking records in Edge, Safari and Brave too (same-origin /_z/* → api.zenovay.com).`)):e.manual?A.createElement(E,{marginTop:1,flexDirection:`column`},A.createElement(E,null,A.createElement(D,{color:t.warn},`⚠ `),A.createElement(D,null,`First-party tracking needs one manual step:`)),A.createElement(D,{color:t.muted},e.manual)):null},ar=({cwd:e,onDone:t,onError:n})=>{let{theme:r}=m(),[i,a]=M(`committing`),[o,s]=M(`Staging changes…`);return j(()=>{(async()=>{try{let n=await dr(e,e=>s(e),e=>a(e));n.committed?s(`Pushed ${n.shortSha} to origin/${n.branch}`):s(`No changes to commit (already up-to-date).`),setTimeout(()=>t(n),800)}catch(e){n(e.message)}})()},[]),A.createElement(p,{title:`Committing to git`,state:`busy`},A.createElement(E,null,A.createElement(D,{color:r.accent},A.createElement(P,{type:`dots`})),A.createElement(D,null,` `,i===`pushing`?`Pushing to origin`:`Committing`,`…`)),A.createElement(E,{marginTop:1},A.createElement(D,{color:r.muted},o)))},or=({site:e,onDeployed:t,onSkip:n})=>{let{theme:r}=m();return ur(e=>{e===`y`||e===`Y`||e===`\r`||e===`d`||e===`D`?t():(e===`n`||e===`N`||e===`s`||e===`S`)&&n()}),A.createElement(p,{title:`Ready to deploy?`,state:`idle`},A.createElement(D,null,`The tracker is installed locally. To capture events you need it on the live site:`),A.createElement(D,null,` `),A.createElement(E,null,A.createElement(D,{color:r.muted},` → `),A.createElement(jn,{url:e.url})),A.createElement(D,null,` `),A.createElement(D,{color:r.muted},`Push the change through your normal deploy pipeline (Vercel auto-deploy, CI/CD, etc.).`),A.createElement(E,{marginTop:1},A.createElement(D,null,`Once it's deployed, press `),A.createElement(D,{color:r.accent},`[Y]`),A.createElement(D,null,` to verify the tracker is firing. `),A.createElement(D,{color:r.muted},`[s] skip verification`)))},sr=({site:e,onContinue:t})=>{let{theme:n}=m();return ur(()=>t()),A.createElement(p,{title:`Visit your site`,state:`idle`},A.createElement(D,null,`To send a test event, open any page on your site:`),A.createElement(D,null,` `),A.createElement(E,null,A.createElement(D,{color:n.muted},` → `),A.createElement(jn,{url:e.url})),A.createElement(D,null,` `),A.createElement(D,{color:n.muted},`The tracker fires automatically on page load. We'll verify the next event arrives.`),A.createElement(E,{marginTop:1},A.createElement(D,null,`Press `),A.createElement(D,{color:n.accent},`[any key]`),A.createElement(D,null,` when you've visited the page.`)))};async function cr(e,t){try{let n=await fetch(e,{headers:{"User-Agent":`zenovay-cli/preflight`},signal:AbortSignal.timeout(8e3),redirect:`follow`});if(!n.ok)return{state:`tracker-missing`,httpStatus:n.status};let r=await n.text(),i=/<script[^>]*src\s*=\s*["']https:\/\/(?:api|cdn)\.zenovay\.com\/[^"']+["'][^>]*>\s*<\/script>/gi,a=/data-id\s*=\s*["'](ZV_[A-Za-z0-9]+)["']/i,o=r.match(i)??[];if(o.length===0)return{state:`tracker-missing`};let s=Array.from(new Set(o.map(e=>e.match(a)?.[1]).filter(e=>!!e)));return s.includes(t)?{state:`tracker-correct`}:{state:`tracker-wrong-id`,foundCodes:s}}catch(e){return{state:`tracker-missing`,networkError:e.message}}}const lr=({site:t,elapsed:r,onTick:a,onArrived:o,onTimeout:s})=>{let{theme:c}=m(),[l,u]=M(`preflight`),[d,f]=M(``),[h,g]=M([]),_=ne(!1),v=t.trackingCode??t.id,y=async()=>{u(`preflight`),f(`Fetching ${t.url}…`);let e=await cr(t.url,v);if(!_.current){if(e.state===`tracker-correct`){u(`polling`),f(`Tracker detected on live site — waiting for first hit…`),b();return}if(e.state===`tracker-wrong-id`){u(`wrong-id-on-live`),g(e.foundCodes),f(`Live site has Zenovay but with id(s): ${e.foundCodes.join(`, `)}`);return}u(`awaiting-deploy`),f(e.networkError?`Couldn't reach ${t.url}: ${e.networkError}`:e.httpStatus?`${t.url} returned HTTP ${e.httpStatus}`:`No Zenovay tracker found on ${t.url}.`)}},b=async()=>{let r=Date.now()-10*60*1e3;try{let s=await e(),c=await i({strict:!1}),l=new n({config:s,cliVersion:`0`,token:c}),d=await At(t.id,r,{api:l,timeoutMs:6e4,pollIntervalMs:2500,onTick:e=>{_.current||a(e)}});if(_.current)return;d.received?(u(`success`),o()):u(`timeout`)}catch{_.current||u(`timeout`)}};j(()=>(_.current=!1,y(),()=>{_.current=!0}),[]),O(e=>{(l===`awaiting-deploy`||l===`wrong-id-on-live`||l===`timeout`)&&(e===`r`||e===`R`?y():(e===`s`||e===`S`)&&s())},{isActive:!0});let x=Math.floor(r/1e3);return l===`preflight`?A.createElement(p,{title:`Verifying`,state:`busy`},A.createElement(E,null,A.createElement(D,{color:c.accent},A.createElement(P,{type:`dots`})),A.createElement(D,null,` Checking your live site for the tracker…`)),A.createElement(E,{marginTop:1},A.createElement(D,{color:c.muted},d))):l===`awaiting-deploy`?A.createElement(p,{title:`Tracker not yet on your live site`,state:`warn`},A.createElement(D,{color:c.warn},d),A.createElement(E,{marginTop:1,flexDirection:`column`},A.createElement(D,null,`The tracker is in your local repo but hasn't been deployed yet.`),A.createElement(D,{color:c.muted},`Push your changes and wait for your deploy to finish (Vercel / Cloudflare Pages / CI), then retry.`)),A.createElement(E,{marginTop:1},A.createElement(D,null,`Press `),A.createElement(D,{color:c.accent},`[r]`),A.createElement(D,null,` to re-check, `),A.createElement(D,{color:c.accent},`[s]`),A.createElement(D,null,` to skip verification.`))):l===`wrong-id-on-live`?A.createElement(p,{title:`Live site has a different tracking code`,state:`warn`},A.createElement(D,{color:c.warn},`Expected: `,A.createElement(D,{color:c.accent},v),` `,`Found on live: `,A.createElement(D,{color:c.accent},h.join(`, `))),A.createElement(E,{marginTop:1,flexDirection:`column`},A.createElement(D,null,`Your local install uses a different ID than what's on `,t.url,`.`),A.createElement(D,{color:c.muted},`Deploy the new local version, then retry — or skip if the existing ID is intentional.`)),A.createElement(E,{marginTop:1},A.createElement(D,null,`Press `),A.createElement(D,{color:c.accent},`[r]`),A.createElement(D,null,` to re-check, `),A.createElement(D,{color:c.accent},`[s]`),A.createElement(D,null,` to skip.`))):l===`timeout`?A.createElement(p,{title:`No event arrived in 3 minutes`,state:`warn`},A.createElement(D,{color:c.warn},`The tracker is on your live site but no hits reached api.zenovay.com.`),A.createElement(E,{marginTop:1},A.createElement(D,{color:c.muted},`Try opening `,t.url,` in a fresh browser tab (no ad-blocker), then retry.`)),A.createElement(E,{marginTop:1},A.createElement(D,null,`Press `),A.createElement(D,{color:c.accent},`[r]`),A.createElement(D,null,` to re-check, `),A.createElement(D,{color:c.accent},`[s]`),A.createElement(D,null,` to finish without verification.`))):A.createElement(p,{title:`Waiting for first event`,state:`busy`},A.createElement(E,null,A.createElement(D,{color:c.accent},A.createElement(P,{type:`dots`})),A.createElement(D,null,` Polling api.zenovay.com for the first hit on `),A.createElement(D,{color:c.accent},t.url),A.createElement(D,null,` (`,x,`s elapsed)`)),A.createElement(E,{marginTop:1},A.createElement(D,{color:c.muted},`Reload the page if nothing arrives within ~30 seconds.`)))};function ur(e){O((t,n)=>{e(t,{return:n.return,escape:n.escape})},{isActive:!0})}async function dr(e,t,n){let{execFile:r}=await import(`node:child_process`),{promisify:i}=await import(`node:util`),a=i(r),o=async(t,n)=>{let{stdout:r}=await a(t,n,{cwd:e,maxBuffer:4e6});return r.trim()};try{await o(`git`,[`rev-parse`,`--is-inside-work-tree`])}catch{return t(`Not a git repository — skipped.`),{committed:!1}}let s=await o(`git`,[`status`,`--porcelain`]);if(!s)return{committed:!1};t(`Staging tracker changes…`),await o(`git`,[`add`,`-A`]),t(`Creating commit…`),await o(`git`,[`commit`,`-m`,`chore(zenovay): install tracking script via @zenovay/cli`]);let c=await o(`git`,[`rev-parse`,`--short`,`HEAD`]),l=await o(`git`,[`rev-parse`,`--abbrev-ref`,`HEAD`]);n(`pushing`),t(`Pushing to origin/${l}…`);try{await o(`git`,[`push`,`origin`,l])}catch(e){return t(`Commit ${c} created locally; push failed (${e.message.slice(0,80)}). Push manually with: git push origin ${l}`),{committed:!0,shortSha:c,branch:l}}return n(`done`),{committed:!0,shortSha:c,branch:l}}function fr(e,t){let n=`${e.name}`.padEnd(30,` `).slice(0,30),r=e.url.replace(/^https?:\/\//,``).padEnd(28,` `).slice(0,28),i=t?` ← best match`:``;return`${n} ${r}${i}`}async function pr(e){try{let t=await x.readFile(b.join(e,`package.json`),`utf8`),n=JSON.parse(t);if(n.name)return String(n.name).replace(/^@[^/]+\//,``)}catch{}try{let t=await x.readFile(b.join(e,`.git`,`config`),`utf8`),n=t.match(/url\s*=\s*.*[/:]([^/]+?)(?:\.git)?\s*$/m);if(n&&n[1])return n[1]}catch{}return b.basename(e)}function mr(e,t){if(!t||e.length===0)return e[0]??null;let n=$(t),r=e.find(e=>$(e.name)===n);if(r)return r;let i=e.find(e=>{let t=e.url.replace(/^https?:\/\//,``).replace(/\/.*$/,``),[r]=t.split(`.`);return $(r??``)===n});if(i)return i;let a=e.find(e=>{let t=$(e.name),r=$(e.url);return t.includes(n)||r.includes(n)||n.includes(t)||n.includes(r)});return a??e[0]??null}function $(e){return e.toLowerCase().replace(/[^a-z0-9]/g,``)}export{le as detectInstalled,Wn as initCommand};
@@ -0,0 +1 @@
1
+ import{readConfig as e}from"./config-JVhBplSX.js";import{classifyBump as t,fetchManifest as n}from"./check-D9G0N7NB.js";import{applyUpdate as r,detectInstallMethod as i,runNpmGlobalInstall as a}from"./ensure-latest-N53hzdSC.js";import"./formatter-C4YSOKuq.js";import{emit$1 as o,isHeadless as s}from"./emit-D4YlMlzk.js";async function c(c){let l=await e(),u=await n({cliBase:l.cliBase}),d=t(c.cliVersion,u.latest);if(c.check)return s({json:c.json})?o({type:`info`,message:`latest=${u.latest} current=${c.cliVersion} bump=${d}`}):process.stdout.write(`Current: ${c.cliVersion}\nLatest: ${u.latest}\nBump: ${d}\n`),{updated:!1,from:c.cliVersion,to:u.latest};if(d===`none`)return s({json:c.json})||process.stdout.write(`Already on latest (${c.cliVersion}).\n`),{updated:!1,from:c.cliVersion,to:u.latest};if(d===`downgrade`&&!c.forceDowngrade)throw Error(`Refused to downgrade ${c.cliVersion} → ${u.latest}. Pass --force-downgrade if you really want this.`);let f=i();if(f===`npm-global`){s({json:c.json})||process.stdout.write(`Updating via npm to @zenovay/cli@${u.latest}…\n`);let e=await a(u.latest);return{updated:e,from:c.cliVersion,to:u.latest}}if(f===`npm-local`){let e=`Update your project dependency: npm i @zenovay/cli@${u.latest}`;return s({json:c.json})?o({type:`info`,message:e}):process.stdout.write(`${e}\n`),{updated:!1,from:c.cliVersion,to:u.latest}}try{let e=await r({currentVersion:c.cliVersion,cliBase:l.cliBase,binaryPath:c.binaryPath,forceDowngrade:c.forceDowngrade});return s({json:c.json})?e.updated&&o({type:`update.applied`,from:e.from,to:e.to}):e.updated?process.stdout.write(`Updated ${e.from} → ${e.to}\n`):process.stdout.write(`Not updated (${e.reason??`unknown`}).\n`),e}catch(e){if(/No release binary/i.test(e.message)){let e=`No prebuilt binary for your platform on this release yet. Update via npm instead:\n npm i -g @zenovay/cli@${u.latest}`;return s({json:c.json})?o({type:`info`,message:e}):process.stdout.write(`${e}\n`),{updated:!1,from:c.cliVersion,to:u.latest}}throw e}}export{c as updateCommand};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import(`./init-Bi-u-dYz.js`).then(async({initCommand:e})=>{let{readFileSync:t}=await import(`node:fs`),n=await import(`node:path`),{fileURLToPath:r}=await import(`node:url`),i=n.dirname(r(import.meta.url)),a=JSON.parse(t(n.join(i,`..`,`package.json`),`utf8`)),o=process.argv.slice(2),s=o.includes(`--json`),c=o.includes(`--yes`),l=o.includes(`--skip-mcp`),u=o.includes(`--skip-verify`),d=await e({cliVersion:a.version,cwd:process.cwd(),json:s,yes:c,skipMcp:l,skipVerify:u});process.exit(d)});
2
+ import(`./init-BsCCSk5U.js`).then(async({initCommand:e})=>{let{readFileSync:t}=await import(`node:fs`),n=await import(`node:path`),{fileURLToPath:r}=await import(`node:url`),i=n.dirname(r(import.meta.url)),a=JSON.parse(t(n.join(i,`..`,`package.json`),`utf8`)),o=process.argv.slice(2),s=o.includes(`--json`),c=o.includes(`--yes`),l=o.includes(`--skip-mcp`),u=o.includes(`--skip-verify`),d=await e({cliVersion:a.version,cwd:process.cwd(),json:s,yes:c,skipMcp:l,skipVerify:u});process.exit(d)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenovay/cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Zenovay CLI — AI install wizard + full terminal analytics dashboard",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- import{classifyBump as e,fetchManifest as t,platformKey as n}from"./check-D9G0N7NB.js";import r from"node:path";import{promises as i}from"node:fs";import a from"semver";import o from"node:os";import s from"node:crypto";const c=`AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=`;async function l(e){let n=await t({cliBase:e.cliBase,fetchImpl:e.fetchImpl});return u({...e,manifest:n})}async function u(t){let{manifest:r,currentVersion:o,forceDowngrade:s}=t,c=e(o,r.latest);if(c===`none`)return{updated:!1,from:o,to:r.latest,reason:`already-latest`};if(c===`downgrade`&&!s)return{updated:!1,from:o,to:r.latest,reason:`downgrade-blocked`};r.minSupported&&a.lt(o,r.minSupported);let l=n(),u=r.platforms[l];if(!u)throw Error(`No release binary for platform ${l}`);let h=await d(u,t.fetchImpl);try{return await f(h,u.sha256),await p(u,r.latest,l),await m(h,t.binaryPath),{updated:!0,from:o,to:r.latest}}catch(e){throw await i.unlink(h).catch(()=>void 0),e}}async function d(e,t=fetch){let n=await t(e.url);if(!n.ok)throw Error(`Download failed: ${n.status}`);let a=Buffer.from(await n.arrayBuffer()),s=await i.mkdtemp(r.join(o.tmpdir(),`zenovay-update-`)),c=r.join(s,`zenovay.new`);return await i.writeFile(c,a,{mode:493}),c}async function f(e,t){let n=await i.readFile(e),r=s.createHash(`sha256`).update(n).digest(`hex`);if(r.toLowerCase()!==t.toLowerCase())throw Error(`Checksum mismatch: expected ${t}, got ${r}. Update REFUSED.`)}async function p(e,t,n){let r=Buffer.from(c,`base64`);if(r.length!==32)throw Error(`Invalid release public key — update REFUSED.`);let i=Buffer.from(`${e.sha256}|${t}|${n}`),a=Buffer.from(e.sig,`base64`),o=s.createPublicKey({key:Buffer.concat([Buffer.from(`302a300506032b6570032100`,`hex`),r]),format:`der`,type:`spki`}),l=s.verify(null,i,o,a);if(!l)throw Error(`Signature verification FAILED. Update REFUSED.`)}async function m(e,t){let n=`${t}.old-${Date.now()}`;try{await i.rename(t,n)}catch(e){if(e.code!==`ENOENT`)throw e}try{await i.rename(e,t),process.platform!==`win32`&&await i.chmod(t,493),await i.unlink(n).catch(()=>void 0)}catch(e){try{await i.rename(n,t)}catch{}throw e}}export{u as applyManifest,l as applyUpdate};