document360-capture 0.2.18 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +11 -11
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{Command as po}from"commander";import{createRequire as uo}from"node:module";import{input as ae,confirm as nt}from"@inquirer/prompts";import{existsSync as it,readFileSync as ce}from"node:fs";import{basename as le,join as pe}from"node:path";import{readFileSync as Q,writeFileSync as Xt,existsSync as V,mkdirSync as Qt}from"node:fs";import{basename as Vt,dirname as Zt,join as L}from"node:path";var te=L(".d360-writer","capture.json"),k={captureDir:".d360-writer/user-docs/_capture",outputDir:".d360-writer/user-docs/_screenshots",viewport:{width:1440,height:900},loginTimeoutMs:0,annotation:{enabled:!0}},D=class extends Error{};function m(t=process.cwd()){return L(t,te)}function h(t){if(!V(t))throw new Error(`No project config at ${t}. Run \`d360-capture init\` to create one.`);let e=JSON.parse(Q(t,"utf8").replace(/^/,""));return oe(e,t)}function O(t,e){Qt(Zt(t),{recursive:!0}),Xt(t,JSON.stringify(e,null,2)+`
3
- `,"utf8")}function ee(t){try{return"^"+new URL(t).origin.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}catch{return"."}}function oe(t,e,o=process.cwd()){re(t,e);let r=Object.keys(t.profiles),n=t.defaultProfile??(r.length===1?r[0]:r.includes("staging")?"staging":r[0]);return{projectId:t.projectId||ne(o)||Vt(o),captureDir:t.captureDir??k.captureDir,outputDir:t.outputDir??k.outputDir,viewport:t.viewport??{...k.viewport},loginTimeoutMs:t.loginTimeoutMs??k.loginTimeoutMs,annotation:t.annotation??{...k.annotation},profiles:t.profiles,defaultProfile:n}}function C(t,e){let o=e??t.defaultProfile,r=t.profiles[o];if(!r){let i=Object.keys(t.profiles).join(", ");throw new D(`Unknown profile "${o}". Available: ${i}`)}let n=r.connection.authBoundaryUrlPattern;return{name:o,connection:{startUrl:r.connection.startUrl,authBoundaryUrlPattern:n||ee(r.connection.startUrl)},captureDir:r.captureDir??t.captureDir,outputDir:r.outputDir??t.outputDir,viewport:r.viewport??t.viewport,production:r.production===!0,scope:r.scope??{},authBoundaryDefaulted:!n}}function re(t,e){if(t.environments&&!t.profiles)throw new D(`Config at ${e} uses the old "environments" shape, which is no longer supported. It is now "profiles" (each with a "connection" { startUrl }) + "defaultProfile". Re-run \`d360-capture init\`, then \`d360-capture auth --profile <name>\`.`);if(!t.profiles||Object.keys(t.profiles).length===0)throw new D(`Invalid config at ${e}: needs at least one profile under "profiles".`);for(let[o,r]of Object.entries(t.profiles))if(!r?.connection?.startUrl)throw new D(`Invalid config at ${e}: profile "${o}" is missing connection.startUrl.`);if(t.defaultProfile&&!t.profiles[t.defaultProfile])throw new D(`Invalid config at ${e}: defaultProfile "${t.defaultProfile}" is not in profiles.`)}function ne(t){let e=L(t,"package.json");if(!V(e))return null;try{return JSON.parse(Q(e,"utf8")).name??null}catch{return null}}import{existsSync as Z,readFileSync as ie}from"node:fs";import{join as tt}from"node:path";var se=tt(".d360-writer","config.json");function et(t=process.cwd()){return tt(t,se)}function ot(t=process.cwd()){return Z(et(t))}function rt(t=process.cwd()){let e=et(t);if(!Z(e))return{};try{let o=JSON.parse(ie(e,"utf8").replace(/^/,"")),r=o.docsDir?.replace(/\/+$/,"");return{projectId:o.projectId,captureDir:o.captureDir??(r?`${r}/_capture`:void 0),outputDir:o.outputDir??(r?`${r}/_screenshots`:void 0),appUrl:typeof o.appUrl=="string"&&o.appUrl.startsWith("http")?o.appUrl:void 0}}catch{return{}}}async function st(t={}){let e=process.cwd(),o=m(e),r=t.yes===!0;if(it(o)&&!r&&!await nt({message:`${o} already exists. Overwrite?`,default:!1})){console.log("Init cancelled.");return}let n=rt(e),i=n.projectId||ue(e)||le(e),s=[n.projectId&&"project name",n.captureDir&&"directories"].filter(Boolean);s.length>0?(console.log(""),console.log(`Found .d360-writer/config.json \u2014 inheriting ${s.join(" + ")}.`)):ot(e)&&(console.log(""),console.log("Found .d360-writer/config.json (could not read defaults from it \u2014 using built-in defaults)."));let a=r?n.appUrl??"https://example.com/":await ae({message:"Live app URL (where captures start):",default:n.appUrl??"https://example.com/",validate:c=>c.startsWith("http")?!0:"Must be a full URL"}),p=r?!1:await nt({message:"Is this URL your production environment?",default:!1}),u=p?"production":"staging",f={projectId:i,...n.captureDir?{captureDir:n.captureDir}:{},...n.outputDir?{outputDir:n.outputDir}:{},profiles:{[u]:{connection:{startUrl:a},...p?{production:!0}:{}}},defaultProfile:u};O(o,f),console.log(""),console.log(`\u2713 Wrote ${o}`),r&&a==="https://example.com/"&&console.log(` \u26A0 Set the ${u} profile's connection.startUrl to your real app URL.`),console.log(""),console.log("Next steps:"),console.log(" 1. d360-capture auth # log in once, no timeout"),console.log(" 2. d360-capture capture # run all specs in the capture dir"),console.log(""),console.log("Add more profiles or tune the auth boundary later by editing .d360-writer/capture.json."),console.log("")}function ue(t){let e=pe(t,"package.json");if(!it(e))return null;try{return JSON.parse(ce(e,"utf8")).name??null}catch{return null}}import{chromium as ve}from"playwright";import{existsSync as at,mkdirSync as fe,statSync as de}from"node:fs";import{homedir as ge}from"node:os";import{join as M,dirname as Ao}from"node:path";function ct(){return M(ge(),".document360-capture","auth-states")}function A(t,e){return M(ct(),t,`${e}.json`)}function lt(t){let e=M(ct(),t);at(e)||fe(e,{recursive:!0})}function j(t,e){let o=A(t,e);if(!at(o))return{path:o,exists:!1};let r=de(o);return{path:o,exists:!0,ageMs:Date.now()-r.mtimeMs}}import{existsSync as me}from"node:fs";import{spawn as he}from"node:child_process";import{createRequire as we}from"node:module";import{chromium as ye}from"playwright";var Pe=we(import.meta.url);function _(){try{return ye.executablePath()}catch{return null}}function T(){let t=_();return t!=null&&me(t)}async function N(){if(T())return;console.log(""),console.log("\u25B6 Chromium is not downloaded on this machine yet \u2014 fetching it now (one-time)\u2026");let t=Pe.resolve("playwright/cli.js"),e=await new Promise((o,r)=>{let n=he(process.execPath,[t,"install","chromium"],{stdio:"inherit"});n.on("error",r),n.on("exit",i=>o(i??1))});if(e!==0||!T())throw new Error(`Chromium download failed (exit code ${e}). Check your network/proxy and retry, or run:
4
- node "${t}" install chromium`);console.log("\u2713 Chromium ready.")}async function pt(t){let e=h(m()),o=C(e,t.profile);lt(e.projectId);let r=A(e.projectId,o.name);await N(),console.log(""),o.production&&console.log(`\u26A0 Profile "${o.name}" is PRODUCTION.`),console.log(`Opening Chromium against ${o.connection.startUrl}`),console.log(`Auth-state will save to: ${r}`),console.log("");let n=await ve.launch({headless:!1,args:["--start-maximized"]}),i=await n.newContext({viewport:null}),s=await i.newPage();await s.goto(o.connection.startUrl,{waitUntil:"domcontentloaded"}),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),t.auto?(console.log(" Log in fully (MFA included) at your own pace."),console.log(" The session saves automatically when you reach the app \u2014"),console.log(" no keypress needed.")):(console.log(" Log in fully (MFA included) at your own pace."),console.log(" When you reach the post-login app, come back to this"),console.log(" terminal and press ENTER to save the session.")),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log("");let a=new RegExp(o.connection.authBoundaryUrlPattern),p=setInterval(()=>{a.test(s.url())&&i.storageState({path:r}).catch(()=>{})},3e4);try{if(t.auto){let u=Date.now()+6e5;for(;;){if(s.isClosed())throw new Error("Browser window was closed before login completed. Run d360-capture auth again.");if(a.test(s.url()))break;if(Date.now()>u)throw new Error("Timed out waiting for login (10 minutes). Run d360-capture auth again.");await s.waitForTimeout(1e3)}await s.waitForTimeout(2e3)}else for(;;){await $e();let u=s.url();if(a.test(u))break;console.log(""),console.log("\u2717 Login doesn't look complete yet \u2014 the browser is still at:"),console.log(` ${u}`),console.log(" Finish logging in until you can see the app, then press ENTER again."),console.log(" (Ctrl+C aborts without saving.)")}await i.storageState({path:r}),console.log(""),console.log(`\u2713 Session saved to ${r}`),console.log(" This file contains session cookies \u2014 never commit it to git.")}finally{clearInterval(p),await n.close().catch(()=>{})}}function $e(){return new Promise(t=>{let e=()=>{process.stdin.off("data",e),process.stdin.pause(),t()};process.stdin.resume(),process.stdin.once("data",e)})}import{existsSync as Fe,readdirSync as Je,readFileSync as He}from"node:fs";import{isAbsolute as Ut,join as We,resolve as It}from"node:path";import{writeFileSync as ut,mkdirSync as xe,existsSync as ft}from"node:fs";import{createRequire as be}from"node:module";import{join as U,isAbsolute as dt,resolve as gt}from"node:path";var B=be(import.meta.url);function mt(t){let{cwd:e,profileName:o,captureDir:r,viewport:n,storageStatePath:i}=t,s;try{t.startUrl&&(s=new URL(t.startUrl).origin)}catch{}let a=U(e,".d360-writer","cache");ft(a)||xe(a,{recursive:!0});let p=U(a,`playwright.${o}.config.cjs`),u=dt(r)?r:gt(e,r),f=dt(i)?i:gt(e,i),c=B.resolve("@playwright/test"),d=`// Generated by document360-capture \u2014 do not edit by hand.
2
+ import{Command as uo}from"commander";import{createRequire as fo}from"node:module";import{input as ce,confirm as it}from"@inquirer/prompts";import{existsSync as st,readFileSync as le}from"node:fs";import{basename as pe,join as ue}from"node:path";import{readFileSync as V,writeFileSync as Qt,existsSync as Z,mkdirSync as Vt}from"node:fs";import{basename as Zt,dirname as te,join as L}from"node:path";var ee=L(".d360-writer","capture.json"),k={captureDir:".d360-writer/user-docs/_capture",outputDir:".d360-writer/user-docs/_screenshots",viewport:{width:1440,height:900},loginTimeoutMs:0,annotation:{enabled:!0}},D=class extends Error{};function m(t=process.cwd()){return L(t,ee)}function h(t){if(!Z(t))throw new Error(`No project config at ${t}. Run \`d360-capture init\` to create one.`);let e=JSON.parse(V(t,"utf8").replace(/^/,""));return re(e,t)}function O(t,e){Vt(te(t),{recursive:!0}),Qt(t,JSON.stringify(e,null,2)+`
3
+ `,"utf8")}function oe(t){try{return"^"+new URL(t).origin.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}catch{return"."}}function re(t,e,o=process.cwd()){ne(t,e);let r=Object.keys(t.profiles),n=t.defaultProfile??(r.length===1?r[0]:r.includes("staging")?"staging":r[0]);return{projectId:t.projectId||ie(o)||Zt(o),captureDir:t.captureDir??k.captureDir,outputDir:t.outputDir??k.outputDir,viewport:t.viewport??{...k.viewport},loginTimeoutMs:t.loginTimeoutMs??k.loginTimeoutMs,annotation:t.annotation??{...k.annotation},profiles:t.profiles,defaultProfile:n}}function C(t,e){let o=e??t.defaultProfile,r=t.profiles[o];if(!r){let i=Object.keys(t.profiles).join(", ");throw new D(`Unknown profile "${o}". Available: ${i}`)}let n=r.connection.authBoundaryUrlPattern;return{name:o,connection:{startUrl:r.connection.startUrl,authBoundaryUrlPattern:n||oe(r.connection.startUrl)},captureDir:r.captureDir??t.captureDir,outputDir:r.outputDir??t.outputDir,viewport:r.viewport??t.viewport,production:r.production===!0,scope:r.scope??{},authBoundaryDefaulted:!n}}function ne(t,e){if(t.environments&&!t.profiles)throw new D(`Config at ${e} uses the old "environments" shape, which is no longer supported. It is now "profiles" (each with a "connection" { startUrl }) + "defaultProfile". Re-run \`d360-capture init\`, then \`d360-capture auth --profile <name>\`.`);if(!t.profiles||Object.keys(t.profiles).length===0)throw new D(`Invalid config at ${e}: needs at least one profile under "profiles".`);for(let[o,r]of Object.entries(t.profiles))if(!r?.connection?.startUrl)throw new D(`Invalid config at ${e}: profile "${o}" is missing connection.startUrl.`);if(t.defaultProfile&&!t.profiles[t.defaultProfile])throw new D(`Invalid config at ${e}: defaultProfile "${t.defaultProfile}" is not in profiles.`)}function ie(t){let e=L(t,"package.json");if(!Z(e))return null;try{return JSON.parse(V(e,"utf8")).name??null}catch{return null}}import{existsSync as tt,readFileSync as se}from"node:fs";import{join as et}from"node:path";var ae=et(".d360-writer","config.json");function ot(t=process.cwd()){return et(t,ae)}function rt(t=process.cwd()){return tt(ot(t))}function nt(t=process.cwd()){let e=ot(t);if(!tt(e))return{};try{let o=JSON.parse(se(e,"utf8").replace(/^/,"")),r=o.docsDir?.replace(/\/+$/,"");return{projectId:o.projectId,captureDir:o.captureDir??(r?`${r}/_capture`:void 0),outputDir:o.outputDir??(r?`${r}/_screenshots`:void 0),appUrl:typeof o.appUrl=="string"&&o.appUrl.startsWith("http")?o.appUrl:void 0}}catch{return{}}}async function at(t={}){let e=process.cwd(),o=m(e),r=t.yes===!0;if(st(o)&&!r&&!await it({message:`${o} already exists. Overwrite?`,default:!1})){console.log("Init cancelled.");return}let n=nt(e),i=n.projectId||fe(e)||pe(e),s=[n.projectId&&"project name",n.captureDir&&"directories"].filter(Boolean);s.length>0?(console.log(""),console.log(`Found .d360-writer/config.json \u2014 inheriting ${s.join(" + ")}.`)):rt(e)&&(console.log(""),console.log("Found .d360-writer/config.json (could not read defaults from it \u2014 using built-in defaults)."));let a=r?n.appUrl??"https://example.com/":await ce({message:"Live app URL (where captures start):",default:n.appUrl??"https://example.com/",validate:c=>c.startsWith("http")?!0:"Must be a full URL"}),p=r?!1:await it({message:"Is this URL your production environment?",default:!1}),u=p?"production":"staging",f={projectId:i,...n.captureDir?{captureDir:n.captureDir}:{},...n.outputDir?{outputDir:n.outputDir}:{},profiles:{[u]:{connection:{startUrl:a},...p?{production:!0}:{}}},defaultProfile:u};O(o,f),console.log(""),console.log(`\u2713 Wrote ${o}`),r&&a==="https://example.com/"&&console.log(` \u26A0 Set the ${u} profile's connection.startUrl to your real app URL.`),console.log(""),console.log("Next steps:"),console.log(" 1. d360-capture auth # log in once, no timeout"),console.log(" 2. d360-capture capture # run all specs in the capture dir"),console.log(""),console.log("Add more profiles or tune the auth boundary later by editing .d360-writer/capture.json."),console.log("")}function fe(t){let e=ue(t,"package.json");if(!st(e))return null;try{return JSON.parse(le(e,"utf8")).name??null}catch{return null}}import{chromium as $e}from"playwright";import{existsSync as ct,mkdirSync as de,statSync as ge}from"node:fs";import{homedir as me}from"node:os";import{join as M,dirname as Uo}from"node:path";function lt(){return M(me(),".document360-capture","auth-states")}function A(t,e){return M(lt(),t,`${e}.json`)}function pt(t){let e=M(lt(),t);ct(e)||de(e,{recursive:!0})}function j(t,e){let o=A(t,e);if(!ct(o))return{path:o,exists:!1};let r=ge(o);return{path:o,exists:!0,ageMs:Date.now()-r.mtimeMs}}import{existsSync as he}from"node:fs";import{spawn as we}from"node:child_process";import{createRequire as ye}from"node:module";import{chromium as Pe}from"playwright";var ve=ye(import.meta.url);function _(){try{return Pe.executablePath()}catch{return null}}function T(){let t=_();return t!=null&&he(t)}async function N(){if(T())return;console.log(""),console.log("\u25B6 Chromium is not downloaded on this machine yet \u2014 fetching it now (one-time)\u2026");let t=ve.resolve("playwright/cli.js"),e=await new Promise((o,r)=>{let n=we(process.execPath,[t,"install","chromium"],{stdio:"inherit"});n.on("error",r),n.on("exit",i=>o(i??1))});if(e!==0||!T())throw new Error(`Chromium download failed (exit code ${e}). Check your network/proxy and retry, or run:
4
+ node "${t}" install chromium`);console.log("\u2713 Chromium ready.")}async function ut(t){let e=h(m()),o=C(e,t.profile);pt(e.projectId);let r=A(e.projectId,o.name);await N(),console.log(""),o.production&&console.log(`\u26A0 Profile "${o.name}" is PRODUCTION.`),console.log(`Opening Chromium against ${o.connection.startUrl}`),console.log(`Auth-state will save to: ${r}`),console.log("");let n=await $e.launch({headless:!1,args:["--start-maximized"]}),i=await n.newContext({viewport:null}),s=await i.newPage();await s.goto(o.connection.startUrl,{waitUntil:"domcontentloaded"}),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),t.auto?(console.log(" Log in fully (MFA included) at your own pace."),console.log(" The session saves automatically when you reach the app \u2014"),console.log(" no keypress needed.")):(console.log(" Log in fully (MFA included) at your own pace."),console.log(" When you reach the post-login app, come back to this"),console.log(" terminal and press ENTER to save the session.")),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log("");let a=new RegExp(o.connection.authBoundaryUrlPattern),p=setInterval(()=>{a.test(s.url())&&i.storageState({path:r}).catch(()=>{})},3e4);try{if(t.auto){let u=Date.now()+6e5;for(;;){if(s.isClosed())throw new Error("Browser window was closed before login completed. Run d360-capture auth again.");if(a.test(s.url()))break;if(Date.now()>u)throw new Error("Timed out waiting for login (10 minutes). Run d360-capture auth again.");await s.waitForTimeout(1e3)}await s.waitForTimeout(2e3)}else for(;;){await xe();let u=s.url();if(a.test(u))break;console.log(""),console.log("\u2717 Login doesn't look complete yet \u2014 the browser is still at:"),console.log(` ${u}`),console.log(" Finish logging in until you can see the app, then press ENTER again."),console.log(" (Ctrl+C aborts without saving.)")}await i.storageState({path:r}),console.log(""),console.log(`\u2713 Session saved to ${r}`),console.log(" This file contains session cookies \u2014 never commit it to git.")}finally{clearInterval(p),await n.close().catch(()=>{})}}function xe(){return new Promise(t=>{let e=()=>{process.stdin.off("data",e),process.stdin.pause(),t()};process.stdin.resume(),process.stdin.once("data",e)})}import{existsSync as Je,readdirSync as He,readFileSync as We}from"node:fs";import{isAbsolute as It,join as qe,resolve as Ot}from"node:path";import{writeFileSync as ft,mkdirSync as be,existsSync as dt}from"node:fs";import{createRequire as Se}from"node:module";import{join as U,isAbsolute as gt,resolve as mt}from"node:path";var B=Se(import.meta.url);function ht(t){let{cwd:e,profileName:o,captureDir:r,viewport:n,storageStatePath:i}=t,s;try{t.startUrl&&(s=new URL(t.startUrl).origin)}catch{}let a=U(e,".d360-writer","cache");dt(a)||be(a,{recursive:!0});let p=U(a,`playwright.${o}.config.cjs`),u=gt(r)?r:mt(e,r),f=gt(i)?i:mt(e,i),c=B.resolve("@playwright/test"),d=`// Generated by document360-capture \u2014 do not edit by hand.
5
5
  const { defineConfig } = require(${JSON.stringify(c)});
6
6
  module.exports = defineConfig({
7
7
  testDir: ${JSON.stringify(u)},
@@ -24,11 +24,11 @@ module.exports = defineConfig({
24
24
  contextOptions: { reducedMotion: 'reduce' },
25
25
  },
26
26
  });
27
- `;ut(p,d,"utf8");let w=U(a,`tsconfig.${o}.json`),$=I=>I.replace(/\\/g,"/"),P=B.resolve("document360-capture/helpers"),v=U(B.resolve("@playwright/test/package.json"),".."),y=U(v,"index.mjs"),x=ft(y)?y:c,S={compilerOptions:{baseUrl:".",paths:{"document360-capture/helpers":[$(P)],"@playwright/test":[$(x)]}}};return ut(w,JSON.stringify(S,null,2),"utf8"),{configPath:p,tsconfigPath:w}}import{spawn as Se}from"node:child_process";import{existsSync as ht,readFileSync as Ce,unlinkSync as je}from"node:fs";import{createRequire as De}from"node:module";import{fileURLToPath as Re}from"node:url";import{delimiter as ke,dirname as E,join as F}from"node:path";function J(t){if(Array.isArray(t)){for(let e of t){let o=J(e);if(o)return o}return null}if(t&&typeof t=="object"){let e=t;if((e.type==="skip"||e.type==="fixme")&&typeof e.description=="string"&&e.description)return e.description;for(let o of Object.values(e)){let r=J(o);if(r)return r}}return null}function Ae(t,e){return t!==0?"failed":/\b\d+\s+skipped\b/.test(e)&&!/\b\d+\s+passed\b/.test(e)?"skipped":"passed"}var Ue=De(import.meta.url);async function wt(t){let{cwd:e,configPath:o,tsconfigPath:r,startUrl:n,authBoundaryUrlPattern:i,specId:s,captureDirAbs:a}=t,p=Te();if(!p)return{exitCode:127,stdout:"",stderr:"Cannot locate @playwright/test CLI. Try: npm install -g document360-capture (reinstall)",outcome:"failed"};let u=F(E(o),`pw-report-${s??"all"}-${process.pid}.json`),f=[p,"test","--config",o,"--tsconfig",r,"--reporter=line,json"];return s&&f.push(Ie(`${s}.spec.ts`)),new Promise(c=>{let d="",w="",$=Se(process.execPath,f,{cwd:e,env:{...process.env,CAPTURE_START_URL:n,CAPTURE_AUTH_BOUNDARY:i,CAPTURE_SCOPE:JSON.stringify(t.scope??{}),PLAYWRIGHT_JSON_OUTPUT_NAME:u,NODE_PATH:Oe()},shell:!1}),P=()=>{try{$.kill()}catch{}};t.signal&&(t.signal.aborted?P():t.signal.addEventListener("abort",P,{once:!0})),$.stdout.on("data",v=>{let y=v.toString();d+=y,process.stdout.write(y)}),$.stderr.on("data",v=>{let y=v.toString();w+=y,process.stderr.write(y)}),$.on("error",v=>{t.signal?.removeEventListener("abort",P),w+=`
28
- [spawn error] ${v.message}`,c({exitCode:127,stdout:d,stderr:w,outcome:"failed"})}),$.on("close",v=>{t.signal?.removeEventListener("abort",P);let y=v??1,x=Ae(y,d),S;try{ht(u)&&(x==="skipped"&&(S=J(JSON.parse(Ce(u,"utf8")))??void 0),je(u))}catch{}c({exitCode:y,stdout:d,stderr:w,outcome:x,skipReason:S})})})}function Ie(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Oe(){let t=E(Re(import.meta.url));for(let o=0;o<4&&!ht(F(t,"package.json"));o++)t=E(t);let e=[F(t,"node_modules"),E(t)];return process.env.NODE_PATH&&e.push(process.env.NODE_PATH),e.join(ke)}function Te(){let t=["@playwright/test/cli","@playwright/test/cli.js","playwright/cli","playwright/cli.js"];for(let e of t)try{return Ue.resolve(e)}catch{}return null}var Ne=[/login\.microsoftonline\.com/i,/login\.live\.com/i,/accounts\.google\.com/i,/\/oauth(2)?\/authorize/i,/\/sign[- ]?in/i,/\bauthn\b/i],Ee=[/waitForURL.*timed?[- ]?out/i,/page\.goto.*redirected/i,/storageState.*invalid/i];function yt(t){return Ne.some(e=>e.test(t))?!0:Ee.some(e=>e.test(t))}import{readdirSync as jt,readFileSync as Me,existsSync as Dt,copyFileSync as _e}from"node:fs";import{join as Rt}from"node:path";import q from"sharp";function H(t){return t.replace(/[<>&"']/g,e=>({"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&apos;"})[e])}function vt(t){let o=Math.max(0,t.x-4),r=Math.max(0,t.y-4),n=t.width+8,i=t.height+8;return`<rect x="${o}" y="${r}" width="${n}" height="${i}" rx="6" ry="6" fill="rgba(255,213,0,0.18)" stroke="#f5a623" stroke-width="2.5"/>`}function $t(t){return`<rect x="${t.x}" y="${t.y}" width="${t.width}" height="${t.height}" fill="#1f2328"/>`}var W="-apple-system,Segoe UI,sans-serif",Pt=(t,e,o)=>Math.max(e,Math.min(o,t)),Le=13;function xt(t,e,o,r){let n=Le,i=Pt(e.x,n,Math.max(n,o-n)),s=Pt(e.y,n,Math.max(n,r-n));return`<circle cx="${i}" cy="${s}" r="${n}" fill="#d1242f" stroke="#fff" stroke-width="2"/><text x="${i}" y="${s+4}" font-family="${W}" font-size="13" font-weight="700" fill="#fff" text-anchor="middle">${H(t)}</text>`}function bt(t,e){return t===0?0:e.pad*2+t*e.lineHeight}function St(t,e){let{top:o,width:r,pad:n,lineHeight:i}=e,s=11,a=20+s,p=[`<line x1="0" y1="${o}" x2="${r}" y2="${o}" stroke="#d0d7de" stroke-width="1"/>`];return t.forEach((u,f)=>{let c=o+n+f*i+i/2;p.push(`<circle cx="${a}" cy="${c}" r="${s}" fill="#d1242f" stroke="#fff" stroke-width="1.5"/>`,`<text x="${a}" y="${c+4}" font-family="${W}" font-size="12" font-weight="700" fill="#fff" text-anchor="middle">${H(u.label)}</text>`,`<text x="${a+s+12}" y="${c+5}" font-family="${W}" font-size="14" fill="#1f2328">${H(u.text)}</text>`)}),p.join("")}async function Be(t){let e=JSON.parse(Me(t,"utf8"));if(!Dt(e.rawPath))return{id:e.id,ok:!1,reason:`raw missing: ${e.rawPath}`};let o=await q(e.rawPath).metadata(),r=o.width??1440,n=o.height??900,i={pad:16,lineHeight:30},s=bt(e.annotations.length,i),a=n+s,p=[];for(let c of e.highlight)p.push(vt(c.bbox));for(let c of e.redact)p.push($t(c.bbox));for(let c of e.annotations)p.push(xt(c.label,c.bbox,r,n));if(s>0&&p.push(`<rect x="0" y="${n}" width="${r}" height="${s}" fill="#ffffff"/>`,St(e.annotations.map(c=>({label:c.label,text:c.text})),{top:n,width:r,...i})),p.length===0)return await q(e.rawPath).toFile(e.finalPath),{id:e.id,ok:!0,finalPath:e.finalPath};let u=`<svg xmlns="http://www.w3.org/2000/svg" width="${r}" height="${a}">${p.join("")}</svg>`,f=q(e.rawPath);return s>0&&(f=f.extend({bottom:s,background:"#ffffff"})),await f.composite([{input:Buffer.from(u),top:0,left:0}]).toFile(e.finalPath),{id:e.id,ok:!0,finalPath:e.finalPath}}async function Ct(t){let e=jt(t).filter(r=>r.endsWith(".raw.png")),o=[];for(let r of e){let n=Rt(t,r),i=n.replace(/\.raw\.png$/,".png");try{_e(n,i),o.push({id:r.replace(/\.raw\.png$/,""),ok:!0,finalPath:i})}catch(s){o.push({id:r,ok:!1,reason:String(s)})}}return o}async function kt(t,e){if(!Dt(t))return[];if(!e)return Ct(t);let o=jt(t).filter(n=>n.endsWith(".annotations.json"));if(o.length===0)return Ct(t);let r=[];for(let n of o){let i=Rt(t,n);try{r.push(await Be(i))}catch(s){r.push({id:n,ok:!1,reason:String(s)})}}return r}function At(t){let e=/^\s*\/\/\s*CAPTURE-SAFETY:\s*(readonly|mutates)\b[\s—:–-]*(.*)$/im.exec(t);if(e)return{mutates:e[1].toLowerCase()==="mutates",declared:!0,what:e[2]?.trim()||void 0};let o=/setInputFiles/.test(t),r=/\{\s*name:\s*['"][^'"]*\b(create|import|save|apply|confirm|delete|submit|send|mint)\b[^'"]*['"][^}]*\}\s*\)\s*\.click\(/i.test(t),n=/\.fill\(/.test(t);return{mutates:o||n&&r,declared:!1}}async function Ot(t,e){let o=process.cwd(),r=h(m(o)),n=C(r,e.profile),i=n.name,s=j(r.projectId,i);if(!s.exists)return console.error(""),console.error(`\u2717 No saved session for profile "${i}".`),console.error(` Expected at: ${s.path}`),console.error(` Run: d360-capture auth --profile ${i}`),2;let a=Ut(n.captureDir)?n.captureDir:It(o,n.captureDir);if(!Fe(a))return console.error(`\u2717 Capture directory missing: ${a}`),console.error(" Generate at least one spec there before running capture."),2;await N();let{configPath:p,tsconfigPath:u}=mt({cwd:o,profileName:i,captureDir:n.captureDir,viewport:n.viewport,storageStatePath:A(r.projectId,i),startUrl:n.connection.startUrl}),f=t?[t]:qe(a);if(f.length===0)return console.error(`\u2717 No .spec.ts files in ${n.captureDir}.`),2;console.log("");let c=l=>{try{return At(He(We(a,`${l}.spec.ts`),"utf8"))}catch{return{mutates:!1}}};if(!t&&!e.allowMutations){let l=f.filter(g=>c(g).mutates);if(l.length>0){f=f.filter(g=>!c(g).mutates),console.log(`\u26A0 ${l.length} spec${l.length===1?"":"s"} perform${l.length===1?"s":""} a REAL write in your app \u2014 excluded from this bulk run:`);for(let g of l){let X=c(g);console.log(` \u26A0 ${g}${X.what?` \u2014 ${X.what}`:""} (run alone: d360-capture capture ${g})`)}if(f.length===0)return 0}}else if(t&&c(t).mutates){let l=c(t);console.log(`\u26A0 This capture performs a REAL write in your app${l.what?`: ${l.what}`:""}. Running because you named it explicitly.`)}n.production&&console.log(`\u26A0 Profile "${i}" is PRODUCTION (read-only capture).`),console.log(`\u25B6 Running ${f.length} spec${f.length===1?"":"s"} against ${i} (${n.connection.startUrl})`),t||console.log(" Press Esc (or Ctrl+C) in this terminal to stop after the current spec; press again to stop now.");let d=!1,w=null,$=Ge(()=>{d?w?.abort():(d=!0,console.log(`
29
- \u23F8 Stopping after the current spec\u2026 (press again to stop now)`))}),P=[],v=!1;try{for(let l of f){if(d)break;w=new AbortController,console.log(`
30
- \u25B6 ${l}`);let g=await wt({cwd:o,configPath:p,tsconfigPath:u,startUrl:n.connection.startUrl,authBoundaryUrlPattern:n.connection.authBoundaryUrlPattern,specId:l,captureDirAbs:a,signal:w.signal,scope:n.scope});if(P.push({id:l,outcome:g.outcome,skipReason:g.skipReason}),g.outcome==="skipped"&&g.skipReason&&console.log(` \u2298 skipped \u2014 ${g.skipReason}`),g.exitCode!==0&&!v&&yt(`${g.stdout}
31
- ${g.stderr}`)&&(v=!0,console.error(""),console.error(" Output mentions a login page \u2014 if specs that used to pass now fail this way, your"),console.error(` saved session may have expired. Re-auth: d360-capture auth --profile ${i}`),console.error(" (Selector/route timeouts are spec bugs, not auth bugs \u2014 inspect the failure above.)")),w.signal.aborted)break}}finally{$(),w=null}let y=P.filter(l=>l.outcome==="passed").length,x=P.filter(l=>l.outcome==="failed"),S=P.filter(l=>l.outcome==="skipped"),I=f.length-P.length,K=!e.noAnnotate&&r.annotation.enabled,Kt=Ut(n.outputDir)?n.outputDir:It(o,n.outputDir);console.log(""),console.log(K?"\u25B6 Annotating screenshots\u2026":"\u25B6 Skipping annotation (--no-annotate or config off)");let R=(await kt(Kt,K)).filter(l=>!l.ok);console.log(""),console.log(`\u2713 ${y} captured`+(S.length?` \xB7 \u2298 ${S.length} skipped`:"")+(x.length?` \xB7 \u2717 ${x.length} failed`:"")+(I?` \xB7 ${I} not run (stopped)`:"")+` \u2192 ${n.outputDir}`);for(let l of S)console.log(` \u2298 ${l.id}${l.skipReason?` \u2014 ${l.skipReason}`:""}`);for(let l of x)console.log(` \u2717 ${l.id}`);if(R.length>0){console.log(`\u2717 ${R.length} annotation failure${R.length===1?"":"s"}:`);for(let l of R)console.log(` - ${l.id}: ${l.reason}`)}return x.length===0&&R.length===0?0:1}function qe(t){return Je(t).filter(e=>e.endsWith(".spec.ts")).map(e=>e.slice(0,-8)).sort()}function Ge(t){let e=process.stdin;if(e.isTTY){let r=n=>{let i=n[0];(i===27||i===3||i===113||i===81)&&t()};return e.setRawMode(!0),e.resume(),e.on("data",r),()=>{e.off("data",r);try{e.setRawMode(!1)}catch{}e.pause()}}let o=()=>t();return process.on("SIGINT",o),()=>{process.off("SIGINT",o)}}import{existsSync as Tt,readdirSync as ze,statSync as Ye}from"node:fs";import{isAbsolute as Nt,join as Ke,resolve as Et}from"node:path";async function Lt(){let t=process.cwd(),e=h(m(t)),o=Nt(e.captureDir)?e.captureDir:Et(t,e.captureDir),r=Nt(e.outputDir)?e.outputDir:Et(t,e.outputDir);if(!Tt(o)){console.log(`Capture dir does not exist: ${o}`);return}let n=ze(o).filter(i=>i.endsWith(".spec.ts")).sort();if(n.length===0){console.log(`No specs found in ${e.captureDir}.`);return}console.log(""),console.log(" Spec ID Last capture"),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");for(let i of n){let s=i.replace(/\.spec\.ts$/,""),a=Ke(r,`${s}.png`),p=Tt(a)?new Date(Ye(a).mtimeMs).toISOString().slice(0,19).replace("T"," "):"(never)";console.log(` ${s.padEnd(47)} ${p}`)}console.log("")}import{existsSync as Mt,readFileSync as _t}from"node:fs";import{isAbsolute as Xe,resolve as Qe}from"node:path";import{createRequire as Ve}from"node:module";var Bt=Ve(import.meta.url);async function Ft(t){let e=process.cwd(),o=0,r=c=>console.log(` \u2713 ${c}`),n=c=>{console.log(` \u2717 ${c}`),o++};console.log(""),console.log("Checking document360-capture setup..."),console.log("");let i=m(e);if(!Mt(i))return n(`Project config missing at ${i} \u2014 run \`d360-capture init\``),2;r(`Project config: ${i}`);let s;try{s=h(i),r(`Config parses; projectId="${s.projectId}"`)}catch(c){return n(`Config invalid: ${c.message}`),2}let a;try{a=C(s,t.profile),r(`Profile "${a.name}" resolved (startUrl=${a.connection.startUrl})${a.production?" \u26A0 PRODUCTION":""}`)}catch(c){return n(`Profile resolve failed: ${c.message}`),1}a.authBoundaryDefaulted&&r(`Auth boundary: ${a.connection.authBoundaryUrlPattern} (auto-derived from start URL; set connection.authBoundaryUrlPattern to override)`);let p=Xe(a.captureDir)?a.captureDir:Qe(e,a.captureDir);Mt(p)?r(`Capture dir exists: ${a.captureDir}`):r(`Capture dir not created yet: ${a.captureDir} (created with your first spec \u2014 author specs in d360-writer via /screenshot)`);let u=j(s.projectId,a.name);if(u.exists){let c=u.ageMs!=null?Math.floor(u.ageMs/864e5):null;r(`Auth-state present (${c}d old): ${u.path}`);try{let d=JSON.parse(_t(u.path,"utf8"));d&&typeof d=="object"?r("Auth-state parses as JSON"):n("Auth-state is not a valid object")}catch(d){n(`Auth-state unreadable: ${d.message}`)}}else n(`Auth-state missing for "${a.name}" \u2014 run \`d360-capture auth --profile ${a.name}\``);try{let c=Bt.resolve("playwright/package.json"),d=JSON.parse(_t(c,"utf8"));r(`playwright@${d.version} installed`)}catch{n("playwright not resolvable \u2014 reinstall document360-capture")}return T()?r(`Chromium browser downloaded: ${_()}`):n("Chromium browser not downloaded \u2014 it auto-downloads on the next `d360-capture auth` or `capture` run"),["@playwright/test/cli","@playwright/test/cli.js"].some(c=>{try{return Bt.resolve(c),!0}catch{return!1}})?r("@playwright/test CLI resolvable"):n("@playwright/test CLI not resolvable \u2014 reinstall document360-capture"),console.log(""),console.log(o===0?"All checks passed.":`${o} check${o===1?"":"s"} failed.`),console.log(""),o===0?0:1}async function Jt(){let t=h(m());console.log(""),console.log(`projectId: ${t.projectId}`),console.log(`captureDir: ${t.captureDir} (default)`),console.log(`outputDir: ${t.outputDir} (default)`),console.log(`viewport: ${t.viewport.width}x${t.viewport.height} (default)`),console.log(`annotation enabled: ${t.annotation.enabled}`),console.log(`defaultProfile: ${t.defaultProfile}`),console.log(""),console.log("Profiles:");for(let[e,o]of Object.entries(t.profiles)){let r=j(t.projectId,e),n=r.exists?`auth saved ${r.ageMs!=null?Math.floor(r.ageMs/864e5)+"d ago":""}`:"no auth-state",i=`${e===t.defaultProfile?" \u25CF":""}${o.production?" \u26A0 PRODUCTION":""}`;console.log(` ${e.padEnd(10)} ${o.connection.startUrl}${i}`),o.captureDir&&console.log(` captureDir: ${o.captureDir}`),console.log(` ${n}`)}console.log("")}function Ht(){let t=h(m());console.log("");for(let[e,o]of Object.entries(t.profiles)){let r=e===t.defaultProfile?"\u25CF ":" ",n=o.production?" \u26A0 PRODUCTION":"",s=j(t.projectId,e).exists?"auth saved":"no auth-state";console.log(`${r}${e} \u2192 ${o.connection.startUrl}${n} [${s}]`)}console.log(""),console.log("\u25CF = default. Switch with: d360-capture profile use <name>"),console.log("")}function Wt(t){let e=m(),o=h(e);if(!o.profiles[t])return console.error(`\u2717 Unknown profile "${t}". Available: ${Object.keys(o.profiles).join(", ")}`),1;o.defaultProfile=t,O(e,o);let r=o.profiles[t].production?" \u26A0 PRODUCTION":"";return console.log(`\u2713 Default profile is now "${t}"${r}`),0}function qt(t){let e=h(m()),o=C(e,t);console.log(""),console.log(`Profile ${o.name}${o.production?" \u26A0 PRODUCTION":""}`),console.log(` startUrl: ${o.connection.startUrl}`),console.log(` authBoundary:${o.connection.authBoundaryUrlPattern}`),console.log(` captureDir: ${o.captureDir}`),console.log(` outputDir: ${o.outputDir}`),console.log(` viewport: ${o.viewport.width}x${o.viewport.height}`);let r=Object.keys(o.scope);return r.length>0&&console.log(` scope: ${r.map(n=>`${n}=${o.scope[n]}`).join(", ")}`),console.log(""),0}import{homedir as Ze}from"node:os";import{join as to,dirname as eo}from"node:path";import{mkdirSync as oo,readFileSync as ro,writeFileSync as no}from"node:fs";var Gt="document360-capture",G=to(Ze(),".document360-capture","update-cache.json"),io=10800*1e3;function so(t,e){let o=i=>{let[s="",a=""]=i.trim().replace(/^v/,"").split("-",2),p=s.split(".").map(u=>parseInt(u,10)||0);return{n:[p[0]??0,p[1]??0,p[2]??0],pre:a}},r=o(t),n=o(e);for(let i=0;i<3;i++)if(r.n[i]!==n.n[i])return r.n[i]<n.n[i]?-1:1;return r.pre===n.pre?0:r.pre?n.pre?r.pre<n.pre?-1:1:-1:1}function ao(){try{return JSON.parse(ro(G,"utf8"))}catch{return null}}function co(){let t=(process.argv[1]??"").replace(/\\/g,"/");return/\/_npx\//.test(t)||/\/packages\/capture\//.test(t)?!1:/\/node_modules\/document360-capture\//.test(t)||!/\/packages\//.test(t)}async function lo(){try{let t=new AbortController,e=setTimeout(()=>t.abort(),2500),o=await fetch(`https://registry.npmjs.org/${Gt}/latest`,{signal:t.signal,headers:{accept:"application/json"}});if(clearTimeout(e),!o.ok)return;let r=await o.json(),n=typeof r.version=="string"?r.version:null;if(!n)return;oo(eo(G),{recursive:!0}),no(G,`${JSON.stringify({checkedAt:new Date().toISOString(),latest:n})}
32
- `)}catch{}}function zt(t){try{if(!co())return;let e=ao();e?.latest&&so(t,e.latest)<0&&console.error(`
33
- \u2B06 d360-capture ${t} \u2192 ${e.latest} available \u2014 run: npm i -g ${Gt}@latest
34
- `),e?.checkedAt&&Date.now()-new Date(e.checkedAt).getTime()<io||lo()}catch{}}var fo=uo(import.meta.url),Yt=fo("../package.json");zt(Yt.version);var b=new po;function z(t){t.env&&(console.error("\u2717 --env was replaced by --profile (connection profiles). Use: --profile <name>"),process.exit(2))}b.name("d360-capture").description("Capture product screenshots from Playwright specs. Login once, capture many.").version(Yt.version);b.command("init").description("Setup. Inherits from .d360-writer/config.json when present; asks only the app URL. Writes .d360-writer/capture.json.").option("-y, --yes","Non-interactive: scaffold with defaults + a placeholder URL, no prompts").action(async t=>{await st(t)});b.command("auth").description("Open Chromium for interactive login. Press ENTER when done to save the session (or --auto to save on login).").option("-p, --profile <name>","Profile name (default: defaultProfile from config)").option("--auto","Save automatically once login completes \u2014 no ENTER (used by d360-writer)").option("-e, --env <name>",!1).action(async t=>{z(t),await pt(t)});b.command("capture [spec-id]").description("Run one spec by id, or all specs if omitted.").option("-p, --profile <name>","Profile name (default: defaultProfile from config)").option("-e, --env <name>",!1).option("--no-annotate","Skip annotation overlay; emit raw screenshots only").option("--allow-mutations","Let a BULK run execute specs that perform real writes (default: they are excluded; run them singly)").action(async(t,e)=>{z(e);let o=await Ot(t,e);o!==0&&process.exit(o)});b.command("list").description("List discovered specs and their last-capture times.").action(async()=>{await Lt()});b.command("doctor").description("Validate config, auth-state, and browser install.").option("-p, --profile <name>","Profile to check").option("-e, --env <name>",!1).action(async t=>{z(t);let e=await Ft(t);e!==0&&process.exit(e)});b.command("status").description("Print resolved config and auth-state freshness.").action(async()=>{await Jt()});var Y=b.command("profile").description("Manage capture profiles for the current repo");Y.command("list",{isDefault:!0}).description("List profiles (\u25CF = default)").action(()=>Ht());Y.command("use <name>").description("Set the default profile for this repo").action(t=>process.exit(Wt(t)));Y.command("show [name]").description("Print the resolved profile").action(t=>process.exit(qt(t)));b.parseAsync(process.argv).catch(t=>{console.error(""),console.error(`\u2717 ${t.message}`),process.exit(1)});
27
+ `;ft(p,d,"utf8");let w=U(a,`tsconfig.${o}.json`),$=I=>I.replace(/\\/g,"/"),y=B.resolve("document360-capture/helpers"),v=U(B.resolve("@playwright/test/package.json"),".."),P=U(v,"index.mjs"),x=dt(P)?P:c,S={compilerOptions:{baseUrl:".",paths:{"document360-capture/helpers":[$(y)],"@playwright/test":[$(x)]}}};return ft(w,JSON.stringify(S,null,2),"utf8"),{configPath:p,tsconfigPath:w}}import{spawn as Ce}from"node:child_process";import{existsSync as wt,readFileSync as je,unlinkSync as De}from"node:fs";import{createRequire as Re}from"node:module";import{fileURLToPath as ke}from"node:url";import{delimiter as Ae,dirname as E,join as F}from"node:path";function J(t){if(Array.isArray(t)){for(let e of t){let o=J(e);if(o)return o}return null}if(t&&typeof t=="object"){let e=t;if((e.type==="skip"||e.type==="fixme")&&typeof e.description=="string"&&e.description)return e.description;for(let o of Object.values(e)){let r=J(o);if(r)return r}}return null}function Ue(t,e){return t!==0?"failed":/\b\d+\s+skipped\b/.test(e)&&!/\b\d+\s+passed\b/.test(e)?"skipped":"passed"}var Ie=Re(import.meta.url);async function yt(t){let{cwd:e,configPath:o,tsconfigPath:r,startUrl:n,authBoundaryUrlPattern:i,specId:s,captureDirAbs:a}=t,p=Ne();if(!p)return{exitCode:127,stdout:"",stderr:"Cannot locate @playwright/test CLI. Try: npm install -g document360-capture (reinstall)",outcome:"failed"};let u=F(E(o),`pw-report-${s??"all"}-${process.pid}.json`),f=[p,"test","--config",o,"--tsconfig",r,"--reporter=line,json"];return s&&f.push(Oe(`${s}.spec.ts`)),new Promise(c=>{let d="",w="",$=Ce(process.execPath,f,{cwd:e,env:{...process.env,CAPTURE_START_URL:n,CAPTURE_AUTH_BOUNDARY:i,CAPTURE_SCOPE:JSON.stringify(t.scope??{}),PLAYWRIGHT_JSON_OUTPUT_NAME:u,NODE_PATH:Te()},shell:!1}),y=()=>{try{$.kill()}catch{}};t.signal&&(t.signal.aborted?y():t.signal.addEventListener("abort",y,{once:!0})),$.stdout.on("data",v=>{let P=v.toString();d+=P,process.stdout.write(P)}),$.stderr.on("data",v=>{let P=v.toString();w+=P,process.stderr.write(P)}),$.on("error",v=>{t.signal?.removeEventListener("abort",y),w+=`
28
+ [spawn error] ${v.message}`,c({exitCode:127,stdout:d,stderr:w,outcome:"failed"})}),$.on("close",v=>{t.signal?.removeEventListener("abort",y);let P=v??1,x=Ue(P,d),S;try{wt(u)&&(x==="skipped"&&(S=J(JSON.parse(je(u,"utf8")))??void 0),De(u))}catch{}c({exitCode:P,stdout:d,stderr:w,outcome:x,skipReason:S})})})}function Oe(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Te(){let t=E(ke(import.meta.url));for(let o=0;o<4&&!wt(F(t,"package.json"));o++)t=E(t);let e=[F(t,"node_modules"),E(t)];return process.env.NODE_PATH&&e.push(process.env.NODE_PATH),e.join(Ae)}function Ne(){let t=["@playwright/test/cli","@playwright/test/cli.js","playwright/cli","playwright/cli.js"];for(let e of t)try{return Ie.resolve(e)}catch{}return null}var Ee=[/login\.microsoftonline\.com/i,/login\.live\.com/i,/accounts\.google\.com/i,/\/oauth(2)?\/authorize/i,/\/sign[- ]?in/i,/\bauthn\b/i],Le=[/waitForURL.*timed?[- ]?out/i,/page\.goto.*redirected/i,/storageState.*invalid/i];function Pt(t){return Ee.some(e=>e.test(t))?!0:Le.some(e=>e.test(t))}import{readdirSync as Dt,readFileSync as _e,existsSync as Rt,copyFileSync as Be}from"node:fs";import{join as kt}from"node:path";import q from"sharp";function H(t){return t.replace(/[<>&"']/g,e=>({"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&apos;"})[e])}function $t(t){let o=Math.max(0,t.x-4),r=Math.max(0,t.y-4),n=t.width+8,i=t.height+8;return`<rect x="${o}" y="${r}" width="${n}" height="${i}" rx="6" ry="6" fill="rgba(255,213,0,0.18)" stroke="#f5a623" stroke-width="2.5"/>`}function xt(t){return`<rect x="${t.x}" y="${t.y}" width="${t.width}" height="${t.height}" fill="#1f2328"/>`}var W="-apple-system,Segoe UI,sans-serif",vt=(t,e,o)=>Math.max(e,Math.min(o,t)),Me=13;function bt(t,e,o,r){let n=Me,i=vt(e.x,n,Math.max(n,o-n)),s=vt(e.y,n,Math.max(n,r-n));return`<circle cx="${i}" cy="${s}" r="${n}" fill="#d1242f" stroke="#fff" stroke-width="2"/><text x="${i}" y="${s+4}" font-family="${W}" font-size="13" font-weight="700" fill="#fff" text-anchor="middle">${H(t)}</text>`}function St(t,e){return t===0?0:e.pad*2+t*e.lineHeight}function Ct(t,e){let{top:o,width:r,pad:n,lineHeight:i}=e,s=11,a=20+s,p=[`<line x1="0" y1="${o}" x2="${r}" y2="${o}" stroke="#d0d7de" stroke-width="1"/>`];return t.forEach((u,f)=>{let c=o+n+f*i+i/2;p.push(`<circle cx="${a}" cy="${c}" r="${s}" fill="#d1242f" stroke="#fff" stroke-width="1.5"/>`,`<text x="${a}" y="${c+4}" font-family="${W}" font-size="12" font-weight="700" fill="#fff" text-anchor="middle">${H(u.label)}</text>`,`<text x="${a+s+12}" y="${c+5}" font-family="${W}" font-size="14" fill="#1f2328">${H(u.text)}</text>`)}),p.join("")}async function Fe(t){let e=JSON.parse(_e(t,"utf8"));if(!Rt(e.rawPath))return{id:e.id,ok:!1,reason:`raw missing: ${e.rawPath}`};let o=await q(e.rawPath).metadata(),r=o.width??1440,n=o.height??900,i={pad:16,lineHeight:30},s=St(e.annotations.length,i),a=n+s,p=[];for(let c of e.highlight)p.push($t(c.bbox));for(let c of e.redact)p.push(xt(c.bbox));for(let c of e.annotations)p.push(bt(c.label,c.bbox,r,n));if(s>0&&p.push(`<rect x="0" y="${n}" width="${r}" height="${s}" fill="#ffffff"/>`,Ct(e.annotations.map(c=>({label:c.label,text:c.text})),{top:n,width:r,...i})),p.length===0)return await q(e.rawPath).toFile(e.finalPath),{id:e.id,ok:!0,finalPath:e.finalPath};let u=`<svg xmlns="http://www.w3.org/2000/svg" width="${r}" height="${a}">${p.join("")}</svg>`,f=q(e.rawPath);return s>0&&(f=f.extend({bottom:s,background:"#ffffff"})),await f.composite([{input:Buffer.from(u),top:0,left:0}]).toFile(e.finalPath),{id:e.id,ok:!0,finalPath:e.finalPath}}async function jt(t,e){let o=Dt(t).filter(n=>n.endsWith(".raw.png")).filter(n=>!e||e.has(n.slice(0,-8))),r=[];for(let n of o){let i=kt(t,n),s=i.replace(/\.raw\.png$/,".png");try{Be(i,s),r.push({id:n.replace(/\.raw\.png$/,""),ok:!0,finalPath:s})}catch(a){r.push({id:n,ok:!1,reason:String(a)})}}return r}async function At(t,e,o){if(!Rt(t))return[];if(o?.size===0)return[];if(!e)return jt(t,o);let r=Dt(t).filter(i=>i.endsWith(".annotations.json")).filter(i=>!o||o.has(i.slice(0,-17)));if(r.length===0)return jt(t,o);let n=[];for(let i of r){let s=kt(t,i);try{n.push(await Fe(s))}catch(a){n.push({id:i,ok:!1,reason:String(a)})}}return n}function Ut(t){let e=/^\s*\/\/\s*CAPTURE-SAFETY:\s*(readonly|mutates)\b[\s—:–-]*(.*)$/im.exec(t);if(e)return{mutates:e[1].toLowerCase()==="mutates",declared:!0,what:e[2]?.trim()||void 0};let o=/setInputFiles/.test(t),r=/\{\s*name:\s*['"][^'"]*\b(create|import|save|apply|confirm|delete|submit|send|mint)\b[^'"]*['"][^}]*\}\s*\)\s*\.click\(/i.test(t),n=/\.fill\(/.test(t);return{mutates:o||n&&r,declared:!1}}async function Tt(t,e){let o=process.cwd(),r=h(m(o)),n=C(r,e.profile),i=n.name,s=j(r.projectId,i);if(!s.exists)return console.error(""),console.error(`\u2717 No saved session for profile "${i}".`),console.error(` Expected at: ${s.path}`),console.error(` Run: d360-capture auth --profile ${i}`),2;let a=It(n.captureDir)?n.captureDir:Ot(o,n.captureDir);if(!Je(a))return console.error(`\u2717 Capture directory missing: ${a}`),console.error(" Generate at least one spec there before running capture."),2;await N();let{configPath:p,tsconfigPath:u}=ht({cwd:o,profileName:i,captureDir:n.captureDir,viewport:n.viewport,storageStatePath:A(r.projectId,i),startUrl:n.connection.startUrl}),f=t?[t]:Ge(a);if(f.length===0)return console.error(`\u2717 No .spec.ts files in ${n.captureDir}.`),2;console.log("");let c=l=>{try{return Ut(We(qe(a,`${l}.spec.ts`),"utf8"))}catch{return{mutates:!1}}};if(!t&&!e.allowMutations){let l=f.filter(g=>c(g).mutates);if(l.length>0){f=f.filter(g=>!c(g).mutates),console.log(`\u26A0 ${l.length} spec${l.length===1?"":"s"} perform${l.length===1?"s":""} a REAL write in your app \u2014 excluded from this bulk run:`);for(let g of l){let Q=c(g);console.log(` \u26A0 ${g}${Q.what?` \u2014 ${Q.what}`:""} (run alone: d360-capture capture ${g})`)}if(f.length===0)return 0}}else if(t&&c(t).mutates){let l=c(t);console.log(`\u26A0 This capture performs a REAL write in your app${l.what?`: ${l.what}`:""}. Running because you named it explicitly.`)}n.production&&console.log(`\u26A0 Profile "${i}" is PRODUCTION (read-only capture).`),console.log(`\u25B6 Running ${f.length} spec${f.length===1?"":"s"} against ${i} (${n.connection.startUrl})`),t||console.log(" Press Esc (or Ctrl+C) in this terminal to stop after the current spec; press again to stop now.");let d=!1,w=null,$=ze(()=>{d?w?.abort():(d=!0,console.log(`
29
+ \u23F8 Stopping after the current spec\u2026 (press again to stop now)`))}),y=[],v=!1;try{for(let l of f){if(d)break;w=new AbortController,console.log(`
30
+ \u25B6 ${l}`);let g=await yt({cwd:o,configPath:p,tsconfigPath:u,startUrl:n.connection.startUrl,authBoundaryUrlPattern:n.connection.authBoundaryUrlPattern,specId:l,captureDirAbs:a,signal:w.signal,scope:n.scope});if(y.push({id:l,outcome:g.outcome,skipReason:g.skipReason}),g.outcome==="skipped"&&g.skipReason&&console.log(` \u2298 skipped \u2014 ${g.skipReason}`),g.exitCode!==0&&!v&&Pt(`${g.stdout}
31
+ ${g.stderr}`)&&(v=!0,console.error(""),console.error(" Output mentions a login page \u2014 if specs that used to pass now fail this way, your"),console.error(` saved session may have expired. Re-auth: d360-capture auth --profile ${i}`),console.error(" (Selector/route timeouts are spec bugs, not auth bugs \u2014 inspect the failure above.)")),w.signal.aborted)break}}finally{$(),w=null}let P=y.filter(l=>l.outcome==="passed").length,x=y.filter(l=>l.outcome==="failed"),S=y.filter(l=>l.outcome==="skipped"),I=f.length-y.length,K=!e.noAnnotate&&r.annotation.enabled,Xt=It(n.outputDir)?n.outputDir:Ot(o,n.outputDir),X=new Set(y.filter(l=>l.outcome==="passed").map(l=>l.id));X.size>0&&(console.log(""),console.log(K?"\u25B6 Annotating screenshots\u2026":"\u25B6 Skipping annotation (--no-annotate or config off)"));let R=(await At(Xt,K,X)).filter(l=>!l.ok);console.log(""),console.log(`\u2713 ${P} captured`+(S.length?` \xB7 \u2298 ${S.length} skipped`:"")+(x.length?` \xB7 \u2717 ${x.length} failed`:"")+(I?` \xB7 ${I} not run (stopped)`:"")+` \u2192 ${n.outputDir}`);for(let l of S)console.log(` \u2298 ${l.id}${l.skipReason?` \u2014 ${l.skipReason}`:""}`);for(let l of x)console.log(` \u2717 ${l.id}`);if(R.length>0){console.log(`\u2717 ${R.length} annotation failure${R.length===1?"":"s"}:`);for(let l of R)console.log(` - ${l.id}: ${l.reason}`)}return x.length===0&&R.length===0?0:1}function Ge(t){return He(t).filter(e=>e.endsWith(".spec.ts")).map(e=>e.slice(0,-8)).sort()}function ze(t){let e=process.stdin;if(e.isTTY){let r=n=>{let i=n[0];(i===27||i===3||i===113||i===81)&&t()};return e.setRawMode(!0),e.resume(),e.on("data",r),()=>{e.off("data",r);try{e.setRawMode(!1)}catch{}e.pause()}}let o=()=>t();return process.on("SIGINT",o),()=>{process.off("SIGINT",o)}}import{existsSync as Nt,readdirSync as Ye,statSync as Ke}from"node:fs";import{isAbsolute as Et,join as Xe,resolve as Lt}from"node:path";async function Mt(){let t=process.cwd(),e=h(m(t)),o=Et(e.captureDir)?e.captureDir:Lt(t,e.captureDir),r=Et(e.outputDir)?e.outputDir:Lt(t,e.outputDir);if(!Nt(o)){console.log(`Capture dir does not exist: ${o}`);return}let n=Ye(o).filter(i=>i.endsWith(".spec.ts")).sort();if(n.length===0){console.log(`No specs found in ${e.captureDir}.`);return}console.log(""),console.log(" Spec ID Last capture"),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");for(let i of n){let s=i.replace(/\.spec\.ts$/,""),a=Xe(r,`${s}.png`),p=Nt(a)?new Date(Ke(a).mtimeMs).toISOString().slice(0,19).replace("T"," "):"(never)";console.log(` ${s.padEnd(47)} ${p}`)}console.log("")}import{existsSync as _t,readFileSync as Bt}from"node:fs";import{isAbsolute as Qe,resolve as Ve}from"node:path";import{createRequire as Ze}from"node:module";var Ft=Ze(import.meta.url);async function Jt(t){let e=process.cwd(),o=0,r=c=>console.log(` \u2713 ${c}`),n=c=>{console.log(` \u2717 ${c}`),o++};console.log(""),console.log("Checking document360-capture setup..."),console.log("");let i=m(e);if(!_t(i))return n(`Project config missing at ${i} \u2014 run \`d360-capture init\``),2;r(`Project config: ${i}`);let s;try{s=h(i),r(`Config parses; projectId="${s.projectId}"`)}catch(c){return n(`Config invalid: ${c.message}`),2}let a;try{a=C(s,t.profile),r(`Profile "${a.name}" resolved (startUrl=${a.connection.startUrl})${a.production?" \u26A0 PRODUCTION":""}`)}catch(c){return n(`Profile resolve failed: ${c.message}`),1}a.authBoundaryDefaulted&&r(`Auth boundary: ${a.connection.authBoundaryUrlPattern} (auto-derived from start URL; set connection.authBoundaryUrlPattern to override)`);let p=Qe(a.captureDir)?a.captureDir:Ve(e,a.captureDir);_t(p)?r(`Capture dir exists: ${a.captureDir}`):r(`Capture dir not created yet: ${a.captureDir} (created with your first spec \u2014 author specs in d360-writer via /screenshot)`);let u=j(s.projectId,a.name);if(u.exists){let c=u.ageMs!=null?Math.floor(u.ageMs/864e5):null;r(`Auth-state present (${c}d old): ${u.path}`);try{let d=JSON.parse(Bt(u.path,"utf8"));d&&typeof d=="object"?r("Auth-state parses as JSON"):n("Auth-state is not a valid object")}catch(d){n(`Auth-state unreadable: ${d.message}`)}}else n(`Auth-state missing for "${a.name}" \u2014 run \`d360-capture auth --profile ${a.name}\``);try{let c=Ft.resolve("playwright/package.json"),d=JSON.parse(Bt(c,"utf8"));r(`playwright@${d.version} installed`)}catch{n("playwright not resolvable \u2014 reinstall document360-capture")}return T()?r(`Chromium browser downloaded: ${_()}`):n("Chromium browser not downloaded \u2014 it auto-downloads on the next `d360-capture auth` or `capture` run"),["@playwright/test/cli","@playwright/test/cli.js"].some(c=>{try{return Ft.resolve(c),!0}catch{return!1}})?r("@playwright/test CLI resolvable"):n("@playwright/test CLI not resolvable \u2014 reinstall document360-capture"),console.log(""),console.log(o===0?"All checks passed.":`${o} check${o===1?"":"s"} failed.`),console.log(""),o===0?0:1}async function Ht(){let t=h(m());console.log(""),console.log(`projectId: ${t.projectId}`),console.log(`captureDir: ${t.captureDir} (default)`),console.log(`outputDir: ${t.outputDir} (default)`),console.log(`viewport: ${t.viewport.width}x${t.viewport.height} (default)`),console.log(`annotation enabled: ${t.annotation.enabled}`),console.log(`defaultProfile: ${t.defaultProfile}`),console.log(""),console.log("Profiles:");for(let[e,o]of Object.entries(t.profiles)){let r=j(t.projectId,e),n=r.exists?`auth saved ${r.ageMs!=null?Math.floor(r.ageMs/864e5)+"d ago":""}`:"no auth-state",i=`${e===t.defaultProfile?" \u25CF":""}${o.production?" \u26A0 PRODUCTION":""}`;console.log(` ${e.padEnd(10)} ${o.connection.startUrl}${i}`),o.captureDir&&console.log(` captureDir: ${o.captureDir}`),console.log(` ${n}`)}console.log("")}function Wt(){let t=h(m());console.log("");for(let[e,o]of Object.entries(t.profiles)){let r=e===t.defaultProfile?"\u25CF ":" ",n=o.production?" \u26A0 PRODUCTION":"",s=j(t.projectId,e).exists?"auth saved":"no auth-state";console.log(`${r}${e} \u2192 ${o.connection.startUrl}${n} [${s}]`)}console.log(""),console.log("\u25CF = default. Switch with: d360-capture profile use <name>"),console.log("")}function qt(t){let e=m(),o=h(e);if(!o.profiles[t])return console.error(`\u2717 Unknown profile "${t}". Available: ${Object.keys(o.profiles).join(", ")}`),1;o.defaultProfile=t,O(e,o);let r=o.profiles[t].production?" \u26A0 PRODUCTION":"";return console.log(`\u2713 Default profile is now "${t}"${r}`),0}function Gt(t){let e=h(m()),o=C(e,t);console.log(""),console.log(`Profile ${o.name}${o.production?" \u26A0 PRODUCTION":""}`),console.log(` startUrl: ${o.connection.startUrl}`),console.log(` authBoundary:${o.connection.authBoundaryUrlPattern}`),console.log(` captureDir: ${o.captureDir}`),console.log(` outputDir: ${o.outputDir}`),console.log(` viewport: ${o.viewport.width}x${o.viewport.height}`);let r=Object.keys(o.scope);return r.length>0&&console.log(` scope: ${r.map(n=>`${n}=${o.scope[n]}`).join(", ")}`),console.log(""),0}import{homedir as to}from"node:os";import{join as eo,dirname as oo}from"node:path";import{mkdirSync as ro,readFileSync as no,writeFileSync as io}from"node:fs";var zt="document360-capture",G=eo(to(),".document360-capture","update-cache.json"),so=10800*1e3;function ao(t,e){let o=i=>{let[s="",a=""]=i.trim().replace(/^v/,"").split("-",2),p=s.split(".").map(u=>parseInt(u,10)||0);return{n:[p[0]??0,p[1]??0,p[2]??0],pre:a}},r=o(t),n=o(e);for(let i=0;i<3;i++)if(r.n[i]!==n.n[i])return r.n[i]<n.n[i]?-1:1;return r.pre===n.pre?0:r.pre?n.pre?r.pre<n.pre?-1:1:-1:1}function co(){try{return JSON.parse(no(G,"utf8"))}catch{return null}}function lo(){let t=(process.argv[1]??"").replace(/\\/g,"/");return/\/_npx\//.test(t)||/\/packages\/capture\//.test(t)?!1:/\/node_modules\/document360-capture\//.test(t)||!/\/packages\//.test(t)}async function po(){try{let t=new AbortController,e=setTimeout(()=>t.abort(),2500),o=await fetch(`https://registry.npmjs.org/${zt}/latest`,{signal:t.signal,headers:{accept:"application/json"}});if(clearTimeout(e),!o.ok)return;let r=await o.json(),n=typeof r.version=="string"?r.version:null;if(!n)return;ro(oo(G),{recursive:!0}),io(G,`${JSON.stringify({checkedAt:new Date().toISOString(),latest:n})}
32
+ `)}catch{}}function Yt(t){try{if(!lo())return;let e=co();e?.latest&&ao(t,e.latest)<0&&console.error(`
33
+ \u2B06 d360-capture ${t} \u2192 ${e.latest} available \u2014 run: npm i -g ${zt}@latest
34
+ `),e?.checkedAt&&Date.now()-new Date(e.checkedAt).getTime()<so||po()}catch{}}var go=fo(import.meta.url),Kt=go("../package.json");Yt(Kt.version);var b=new uo;function z(t){t.env&&(console.error("\u2717 --env was replaced by --profile (connection profiles). Use: --profile <name>"),process.exit(2))}b.name("d360-capture").description("Capture product screenshots from Playwright specs. Login once, capture many.").version(Kt.version);b.command("init").description("Setup. Inherits from .d360-writer/config.json when present; asks only the app URL. Writes .d360-writer/capture.json.").option("-y, --yes","Non-interactive: scaffold with defaults + a placeholder URL, no prompts").action(async t=>{await at(t)});b.command("auth").description("Open Chromium for interactive login. Press ENTER when done to save the session (or --auto to save on login).").option("-p, --profile <name>","Profile name (default: defaultProfile from config)").option("--auto","Save automatically once login completes \u2014 no ENTER (used by d360-writer)").option("-e, --env <name>",!1).action(async t=>{z(t),await ut(t)});b.command("capture [spec-id]").description("Run one spec by id, or all specs if omitted.").option("-p, --profile <name>","Profile name (default: defaultProfile from config)").option("-e, --env <name>",!1).option("--no-annotate","Skip annotation overlay; emit raw screenshots only").option("--allow-mutations","Let a BULK run execute specs that perform real writes (default: they are excluded; run them singly)").action(async(t,e)=>{z(e);let o=await Tt(t,e);o!==0&&process.exit(o)});b.command("list").description("List discovered specs and their last-capture times.").action(async()=>{await Mt()});b.command("doctor").description("Validate config, auth-state, and browser install.").option("-p, --profile <name>","Profile to check").option("-e, --env <name>",!1).action(async t=>{z(t);let e=await Jt(t);e!==0&&process.exit(e)});b.command("status").description("Print resolved config and auth-state freshness.").action(async()=>{await Ht()});var Y=b.command("profile").description("Manage capture profiles for the current repo");Y.command("list",{isDefault:!0}).description("List profiles (\u25CF = default)").action(()=>Wt());Y.command("use <name>").description("Set the default profile for this repo").action(t=>process.exit(qt(t)));Y.command("show [name]").description("Print the resolved profile").action(t=>process.exit(Gt(t)));b.parseAsync(process.argv).catch(t=>{console.error(""),console.error(`\u2717 ${t.message}`),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document360-capture",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "description": "CLI that captures product screenshots from Playwright specs. Login once, capture many. Companion to document360-writer.",
5
5
  "type": "module",
6
6
  "bin": {