@inkly-org/cli 0.5.4 → 0.6.0
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.
- package/README.md +1 -1
- package/dist/capture-listener.js +13 -9
- package/dist/cli.js +147 -123
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ Useful flags: `--headed` (watch a login), `--profile <name>` (reuse a signed-in
|
|
|
75
75
|
| `inkly logout` | Remove saved credentials. |
|
|
76
76
|
| `inkly status [--json]` | Show auth and hub status. |
|
|
77
77
|
| `inkly sync [--demo <slug>] [--dry-run]` | Upload local capture assets to the CDN. |
|
|
78
|
-
| `inkly
|
|
78
|
+
| `inkly snapshot <demo> [--yes]` | Publish a standalone `/p/<id>` snapshot. |
|
|
79
79
|
|
|
80
80
|
### Maintenance
|
|
81
81
|
|
package/dist/capture-listener.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import vt from"mri";import{spawn as
|
|
2
|
+
import vt from"mri";import{spawn as Ke}from"child_process";import{createHash as Ft,randomUUID as Ht}from"crypto";import{mkdir as O,mkdtemp as Bt,open as Wt,readFile as F,readdir as zt,rm as Ze,writeFile as U}from"fs/promises";import{existsSync as de}from"fs";import{dirname as pe,isAbsolute as Kt,join as y,resolve as Je,sep as Zt}from"path";import{tmpdir as qt,homedir as qe}from"os";import{fileURLToPath as Ye}from"url";import{unzipSync as Qt,zipSync as en}from"fflate";import Xe from"ws";import rn from"sharp";import on from"subset-font";import{zipSync as At}from"fflate";import{JSDOM as un}from"jsdom";import{randomUUID as xe}from"crypto";import{mkdir as Te,readFile as Ie,writeFile as Ee}from"fs/promises";import{dirname as _e,join as De}from"path";import{homedir as Le}from"os";import{PostHog as Ue}from"posthog-node";var ne=process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN??process.env.POSTHOG_PROJECT_TOKEN??"phc_Dmh4w8YFNosi3YHD4Sv7ShHX7ccS8kmtRcm8XSVmB5jT",Re=process.env.NEXT_PUBLIC_POSTHOG_HOST??process.env.POSTHOG_HOST??"https://us.i.posthog.com",$e="@inkly-org/cli@0.5.1",V=De(Le(),".inkly","analytics.json"),te=null,$=null,re=null;function Me(){return!!ne&&process.env.INKLY_TELEMETRY_DISABLED!=="1"}function Ne(){return Me()?(te??=new Ue(ne,{host:Re,flushAt:1,flushInterval:0}),te):null}async function Oe(){try{let e=await Ie(V,"utf8");return JSON.parse(e)}catch{return{}}}async function Fe(e){await Te(_e(V),{recursive:!0}),await Ee(V,JSON.stringify(e,null,2)+`
|
|
3
3
|
`,{encoding:"utf8",mode:384})}async function He(){return $||($=(async()=>{let e=await Oe();if(typeof e.platformUserId=="string"&&e.platformUserId&&(re=e.platformUserId),typeof e.distinctId=="string"&&e.distinctId)return e.distinctId;let r=`cli_${xe()}`;return await Fe({...e,distinctId:r}),r})(),$)}async function je(){let e=await He();return re??e}function Be(){return{surface:"cli",release:process.env.POSTHOG_RELEASE??$e,node_version:process.versions.node,platform:process.platform,arch:process.arch,ci:!!process.env.CI}}async function ie(e,r={}){let i=Ne();if(i)try{i.capture({distinctId:await je(),event:e,properties:{...Be(),...r}})}catch{}}import{mkdir as We,rename as ze,writeFile as Ve}from"fs/promises";import{dirname as Ge}from"path";async function se(e,r,i){await We(Ge(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await Ve(t,r,i?.mode!=null?{mode:i.mode}:void 0),await ze(t,e)}var me=`Options:
|
|
4
4
|
--url <url> URL to open and arm for recording.
|
|
5
5
|
--name <name> Demo name written into the exported ZIP.
|
|
@@ -26,6 +26,8 @@ Usage:
|
|
|
26
26
|
inkly capture start --url <url> --name <name>
|
|
27
27
|
inkly capture stop --session <id> --out <dir>
|
|
28
28
|
inkly capture cancel --session <id>
|
|
29
|
+
inkly capture status --session <id>
|
|
30
|
+
inkly capture undo --session <id>
|
|
29
31
|
inkly capture profiles
|
|
30
32
|
|
|
31
33
|
${me}
|
|
@@ -43,6 +45,8 @@ Usage:
|
|
|
43
45
|
inkly capture-html start --url <url> --name <name>
|
|
44
46
|
inkly capture-html stop --session <id> --out <dir>
|
|
45
47
|
inkly capture-html cancel --session <id>
|
|
48
|
+
inkly capture-html status --session <id>
|
|
49
|
+
inkly capture-html undo --session <id>
|
|
46
50
|
inkly capture-html profiles
|
|
47
51
|
|
|
48
52
|
${me}
|
|
@@ -57,7 +61,7 @@ ${i}`);return t}function ce(e,r){let i=Je(e);for(;;){let t=y(i,r);if(de(t))retur
|
|
|
57
61
|
innerHeight: window.innerHeight,
|
|
58
62
|
outerWidth: window.outerWidth,
|
|
59
63
|
outerHeight: window.outerHeight
|
|
60
|
-
})`,5e3),t=JSON.parse(String(i)),n={innerWidth:Number(t.innerWidth),innerHeight:Number(t.innerHeight),outerWidth:Number(t.outerWidth),outerHeight:Number(t.outerHeight)};if(!Number.isFinite(n.innerWidth)||!Number.isFinite(n.innerHeight)||!Number.isFinite(n.outerWidth)||!Number.isFinite(n.outerHeight))throw new Error(`could not measure Chrome viewport: ${JSON.stringify(t)}`);return n}async function j(e,r,i,t,n){try{let o=(await e.send("Browser.getWindowForTarget",{targetId:i})).windowId;if(typeof o!="number")throw new Error("Browser.getWindowForTarget returned no windowId");for(let d=0;d<4;d+=1){let a=await at(e,r),g=Math.max(0,a.outerWidth-a.innerWidth),p=Math.max(0,a.outerHeight-a.innerHeight),m=t+g,b=n+p;if(Math.abs(a.innerWidth-t)<=1&&Math.abs(a.innerHeight-n)<=1)return;await e.send("Browser.setWindowBounds",{windowId:o,bounds:{windowState:"normal",width:m,height:b}}),await T(250)}}catch{}await e.send("Emulation.setDeviceMetricsOverride",{width:t,height:n,deviceScaleFactor:1,mobile:!1},r).catch(()=>{}),await T(100)}function J(e){if(e.length<24)return null;let r=[137,80,78,71,13,10,26,10];for(let o=0;o<r.length;o+=1)if(e[o]!==r[o])return null;if(String.fromCharCode(e[12],e[13],e[14],e[15])!=="IHDR")return null;let t=new DataView(e.buffer,e.byteOffset,e.byteLength),n=t.getUint32(16,!1),s=t.getUint32(20,!1);return n>0&&s>0?{width:n,height:s}:null}var
|
|
64
|
+
})`,5e3),t=JSON.parse(String(i)),n={innerWidth:Number(t.innerWidth),innerHeight:Number(t.innerHeight),outerWidth:Number(t.outerWidth),outerHeight:Number(t.outerHeight)};if(!Number.isFinite(n.innerWidth)||!Number.isFinite(n.innerHeight)||!Number.isFinite(n.outerWidth)||!Number.isFinite(n.outerHeight))throw new Error(`could not measure Chrome viewport: ${JSON.stringify(t)}`);return n}async function j(e,r,i,t,n){try{let o=(await e.send("Browser.getWindowForTarget",{targetId:i})).windowId;if(typeof o!="number")throw new Error("Browser.getWindowForTarget returned no windowId");for(let d=0;d<4;d+=1){let a=await at(e,r),g=Math.max(0,a.outerWidth-a.innerWidth),p=Math.max(0,a.outerHeight-a.innerHeight),m=t+g,b=n+p;if(Math.abs(a.innerWidth-t)<=1&&Math.abs(a.innerHeight-n)<=1)return;await e.send("Browser.setWindowBounds",{windowId:o,bounds:{windowState:"normal",width:m,height:b}}),await T(250)}}catch{}await e.send("Emulation.setDeviceMetricsOverride",{width:t,height:n,deviceScaleFactor:1,mobile:!1},r).catch(()=>{}),await T(100)}function J(e){if(e.length<24)return null;let r=[137,80,78,71,13,10,26,10];for(let o=0;o<r.length;o+=1)if(e[o]!==r[o])return null;if(String.fromCharCode(e[12],e[13],e[14],e[15])!=="IHDR")return null;let t=new DataView(e.buffer,e.byteOffset,e.byteLength),n=t.getUint32(16,!1),s=t.getUint32(20,!1);return n>0&&s>0?{width:n,height:s}:null}var K=`(() => {
|
|
61
65
|
if (window.__inklyCliRecorderInstalled) return;
|
|
62
66
|
window.__inklyCliRecorderInstalled = true;
|
|
63
67
|
window.__inklyCliLastClick = null;
|
|
@@ -241,7 +245,7 @@ function selectorFor(el) {
|
|
|
241
245
|
window.addEventListener("scroll", onMotionScroll, true);
|
|
242
246
|
window.addEventListener("wheel", onMotionScroll, true);
|
|
243
247
|
window.addEventListener("touchmove", onMotionScroll, true);
|
|
244
|
-
})();`;async function B(e,r,i=!1){i&&(await e.send("Runtime.addBinding",{name:"__inklyCliCaptureEvent"},r).catch(()=>{}),await e.send("Runtime.addBinding",{name:"__inklyCliCaptureClick"},r).catch(()=>{})),await e.send("Page.addScriptToEvaluateOnNewDocument",{source:
|
|
248
|
+
})();`;async function B(e,r,i=!1){i&&(await e.send("Runtime.addBinding",{name:"__inklyCliCaptureEvent"},r).catch(()=>{}),await e.send("Runtime.addBinding",{name:"__inklyCliCaptureClick"},r).catch(()=>{})),await e.send("Page.addScriptToEvaluateOnNewDocument",{source:K},r),await S(e,r,K,5e3).catch(()=>{})}async function W(e,r){await S(e,r,K,5e3).catch(()=>{});let i=await S(e,r,`JSON.stringify({
|
|
245
249
|
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
246
250
|
sourceUrl: location.href,
|
|
247
251
|
title: document.title || "Captured screen",
|
|
@@ -252,7 +256,7 @@ function selectorFor(el) {
|
|
|
252
256
|
maxX: Math.max(0, document.documentElement.scrollWidth - window.innerWidth),
|
|
253
257
|
maxY: Math.max(0, document.documentElement.scrollHeight - window.innerHeight)
|
|
254
258
|
}
|
|
255
|
-
})`,5e3),t=JSON.parse(String(i));return{viewport:{width:Number(t.viewport?.width)||fe,height:Number(t.viewport?.height)||ge},sourceUrl:typeof t.sourceUrl=="string"?t.sourceUrl:"",title:typeof t.title=="string"?t.title:"Captured screen",click:t.click??null,scroll:{x:Number(t.scroll?.x)||0,y:Number(t.scroll?.y)||0,maxX:Number(t.scroll?.maxX)||0,maxY:Number(t.scroll?.maxY)||0}}}function lt(){let e=y(oe,"singlefile-injected.js");if(de(e))return e;let r=ce(process.cwd(),"packages/capture-extension/dist/singlefile-injected.js"),i=ce(oe,"packages/capture-extension/dist/singlefile-injected.js"),t=r??i;if(!t)throw new Error("SingleFile bundle not found. Run `npm run extension:build` before --html capture.");return t}var ct={removeFrames:!0,removeImports:!0,blockScripts:!0,compressHTML:!1,compressCSS:!1,saveRawPage:!1,loadDeferredImages:!1,loadDeferredImagesMaxIdleTime:1500,loadDeferredImagesKeepZoomLevel:!1,loadDeferredImagesBlockCookies:!1,loadDeferredImagesBlockStorage:!1,blockMixedContent:!1,insertMetaCSP:!0,insertMetaNoIndex:!1,insertSingleFileComment:!1,removeAlternativeFonts:!0,removeAlternativeImages:!0,removeAlternativeMedias:!0,removeUnusedFonts:!0,backgroundSave:!1,removeUnusedStyles:!0,removeHiddenElements:!0,compressContent:!1},
|
|
259
|
+
})`,5e3),t=JSON.parse(String(i));return{viewport:{width:Number(t.viewport?.width)||fe,height:Number(t.viewport?.height)||ge},sourceUrl:typeof t.sourceUrl=="string"?t.sourceUrl:"",title:typeof t.title=="string"?t.title:"Captured screen",click:t.click??null,scroll:{x:Number(t.scroll?.x)||0,y:Number(t.scroll?.y)||0,maxX:Number(t.scroll?.maxX)||0,maxY:Number(t.scroll?.maxY)||0}}}function lt(){let e=y(oe,"singlefile-injected.js");if(de(e))return e;let r=ce(process.cwd(),"packages/capture-extension/dist/singlefile-injected.js"),i=ce(oe,"packages/capture-extension/dist/singlefile-injected.js"),t=r??i;if(!t)throw new Error("SingleFile bundle not found. Run `npm run extension:build` before --html capture.");return t}var ct={removeFrames:!0,removeImports:!0,blockScripts:!0,compressHTML:!1,compressCSS:!1,saveRawPage:!1,loadDeferredImages:!1,loadDeferredImagesMaxIdleTime:1500,loadDeferredImagesKeepZoomLevel:!1,loadDeferredImagesBlockCookies:!1,loadDeferredImagesBlockStorage:!1,blockMixedContent:!1,insertMetaCSP:!0,insertMetaNoIndex:!1,insertSingleFileComment:!1,removeAlternativeFonts:!0,removeAlternativeImages:!0,removeAlternativeMedias:!0,removeUnusedFonts:!0,backgroundSave:!1,removeUnusedStyles:!0,removeHiddenElements:!0,compressContent:!1},Z="__inklySingleFileFetch",ut=`(() => {
|
|
256
260
|
if (globalThis.__inklyFetchInstalled) return;
|
|
257
261
|
globalThis.__inklyFetchInstalled = true;
|
|
258
262
|
const pending = new Map();
|
|
@@ -272,7 +276,7 @@ function selectorFor(el) {
|
|
|
272
276
|
const id = nextId++;
|
|
273
277
|
pending.set(id, { resolve, reject });
|
|
274
278
|
try {
|
|
275
|
-
globalThis.${
|
|
279
|
+
globalThis.${Z}(JSON.stringify({
|
|
276
280
|
id,
|
|
277
281
|
url: String(url),
|
|
278
282
|
options: {
|
|
@@ -295,7 +299,7 @@ function selectorFor(el) {
|
|
|
295
299
|
return viaNode(url, options);
|
|
296
300
|
}
|
|
297
301
|
};
|
|
298
|
-
})();`;function dt(e,r){let i,t,n;try{let s=new URL(e);i=s.hostname,t=s.pathname||"/",n=s.protocol==="https:"}catch{return""}return r.filter(s=>{let o=s.domain.replace(/^\./,""),d=i===o||i.endsWith(`.${o}`),a=t.startsWith(s.path||"/"),g=!s.secure||n;return d&&a&&g}).map(s=>`${s.name}=${s.value}`).join("; ")}async function pt(e,r,i,t){let n;try{let s=JSON.parse(i);n=s.id;let o={...s.options?.headers??{}},d=dt(s.url,t);d&&(o.cookie=d),s.options?.referrer&&(o.referer=s.options.referrer);let a=await fetch(s.url,{headers:o,redirect:"follow"}),g=Buffer.from(await a.arrayBuffer()),p={};a.headers.forEach((b,v)=>{p[v]=b});let m={status:a.status,headers:p,data:g.toString("base64")};await S(e,r,`globalThis.__inklyResolveFetch(${n}, ${JSON.stringify(m)})`,2e4)}catch(s){if(n===void 0)return;await S(e,r,`globalThis.__inklyRejectFetch(${n}, ${JSON.stringify({error:String(s)})})`,2e4).catch(()=>{})}}var G=Math.max(6e4,Number(process.env.INKLY_CAPTURE_TIMEOUT_MS)||24e4);async function mt(e,r,i=!1){let t=await F(lt(),"utf8");await e.send("Network.enable",{},r).catch(()=>{}),await e.send("Runtime.addBinding",{name:
|
|
302
|
+
})();`;function dt(e,r){let i,t,n;try{let s=new URL(e);i=s.hostname,t=s.pathname||"/",n=s.protocol==="https:"}catch{return""}return r.filter(s=>{let o=s.domain.replace(/^\./,""),d=i===o||i.endsWith(`.${o}`),a=t.startsWith(s.path||"/"),g=!s.secure||n;return d&&a&&g}).map(s=>`${s.name}=${s.value}`).join("; ")}async function pt(e,r,i,t){let n;try{let s=JSON.parse(i);n=s.id;let o={...s.options?.headers??{}},d=dt(s.url,t);d&&(o.cookie=d),s.options?.referrer&&(o.referer=s.options.referrer);let a=await fetch(s.url,{headers:o,redirect:"follow"}),g=Buffer.from(await a.arrayBuffer()),p={};a.headers.forEach((b,v)=>{p[v]=b});let m={status:a.status,headers:p,data:g.toString("base64")};await S(e,r,`globalThis.__inklyResolveFetch(${n}, ${JSON.stringify(m)})`,2e4)}catch(s){if(n===void 0)return;await S(e,r,`globalThis.__inklyRejectFetch(${n}, ${JSON.stringify({error:String(s)})})`,2e4).catch(()=>{})}}var G=Math.max(6e4,Number(process.env.INKLY_CAPTURE_TIMEOUT_MS)||24e4);async function mt(e,r,i=!1){let t=await F(lt(),"utf8");await e.send("Network.enable",{},r).catch(()=>{}),await e.send("Runtime.addBinding",{name:Z},r).catch(()=>{});let n=[];try{n=(await e.send("Network.getAllCookies",{},r)).cookies??[]}catch{}let s=e.on("Runtime.bindingCalled",(o,d)=>{if(d!==r)return;let a=o;a.name!==Z||typeof a.payload!="string"||pt(e,r,a.payload,n)});try{await S(e,r,t,3e4),await S(e,r,ut,1e4);let o=await S(e,r,`(async () => {
|
|
299
303
|
const sf = globalThis.__inklySingleFile;
|
|
300
304
|
if (!sf || typeof sf.getPageData !== "function") throw new Error("SingleFile not loaded");
|
|
301
305
|
// initOptions (2nd arg) is forwarded to single-file-core/core/util.js
|
|
@@ -311,10 +315,10 @@ function selectorFor(el) {
|
|
|
311
315
|
]);
|
|
312
316
|
if (typeof data.content === "string") return data.content;
|
|
313
317
|
return new TextDecoder().decode(new Uint8Array(data.content));
|
|
314
|
-
})()`,G+15e3);return String(o)}finally{s()}}async function ft(e,r){if(e.runtime!=="cli")throw new Error("HTML capture is only available for --runtime cli sessions");if(!e.captureDir)throw new Error("CLI capture session has no captureDir");let i=new I(e.browserWsUrl);try{let t=await H(i,e.targetId);await i.send("Page.enable",{},t).catch(()=>{}),await i.send("Runtime.enable",{},t).catch(()=>{}),await B(i,t),await j(i,t,e.targetId,e.width,e.height);let n=await W(i,t),s=await mt(i,t,e.cliBlockVideos===!0),o=(e.cliScreens?.length??0)+1,d=y(e.captureDir,`html-${String(o).padStart(3,"0")}`),a=y(d,"index.html");await O(d,{recursive:!0}),await U(a,s,"utf8");let g={...e,cliScreens:[...e.cliScreens??[],{kind:"html",pngPath:null,htmlPath:a,viewport:n.viewport,naturalSize:n.viewport,sourceUrl:n.sourceUrl,title:n.title,click:r??n.click,scroll:n.scroll,capturedAt:new Date().toISOString()}],tabUrl:n.sourceUrl};return await i.send("Target.detachFromTarget",{sessionId:t}).catch(()=>{}),await
|
|
318
|
+
})()`,G+15e3);return String(o)}finally{s()}}async function ft(e,r){if(e.runtime!=="cli")throw new Error("HTML capture is only available for --runtime cli sessions");if(!e.captureDir)throw new Error("CLI capture session has no captureDir");let i=new I(e.browserWsUrl);try{let t=await H(i,e.targetId);await i.send("Page.enable",{},t).catch(()=>{}),await i.send("Runtime.enable",{},t).catch(()=>{}),await B(i,t),await j(i,t,e.targetId,e.width,e.height);let n=await W(i,t),s=await mt(i,t,e.cliBlockVideos===!0),o=(e.cliScreens?.length??0)+1,d=y(e.captureDir,`html-${String(o).padStart(3,"0")}`),a=y(d,"index.html");await O(d,{recursive:!0}),await U(a,s,"utf8");let g={...e,cliScreens:[...e.cliScreens??[],{kind:"html",pngPath:null,htmlPath:a,viewport:n.viewport,naturalSize:n.viewport,sourceUrl:n.sourceUrl,title:n.title,click:r??n.click,scroll:n.scroll,capturedAt:new Date().toISOString()}],tabUrl:n.sourceUrl};return await i.send("Target.detachFromTarget",{sessionId:t}).catch(()=>{}),await E(g),g}finally{i.close()}}async function gt(e){await new Promise((r,i)=>{let t=Ke("ffmpeg",e,{stdio:["ignore","ignore","pipe"]}),n=[];t.stderr.on("data",s=>n.push(s)),t.on("error",i),t.on("close",s=>{if(s===0){r();return}i(new Error(`ffmpeg exited ${s}: ${Buffer.concat(n).toString("utf8").slice(-2e3)}`))})})}async function ht(e){let r=[...e.frames].sort((h,k)=>h.receivedAt-k.receivedAt),i=[],t=r[0]?.receivedAt;if(t!==void 0){let h=1e3/M,k=t;for(let A of r)A.receivedAt<k&&A!==r[r.length-1]||(i.push(A),k=A.receivedAt+h)}let n=i.slice(-Math.ceil(he/1e3*M)-2),s=n[0]?.receivedAt,o=n[n.length-1]?.receivedAt,d=s!==void 0&&o!==void 0?o-s:0;if(n.length<tt||d<nt||new Set(n.map(h=>h.data)).size<=1)return null;let a=Math.ceil(rt/1e3*M),g=n.length>=a?n:n.concat(Array.from({length:a-n.length},()=>n[n.length-1])),p=y(e.captureDir,`video-${String(e.index).padStart(3,"0")}`);await Ze(p,{recursive:!0,force:!0}).catch(()=>{}),await O(p,{recursive:!0});for(let[h,k]of g.entries())await U(y(p,`frame-${String(h+1).padStart(5,"0")}.png`),Buffer.from(k.data,"base64"));let m=y(p,"poster.png");await U(m,Buffer.from(n[0].data,"base64"));let b=y(e.captureDir,`video-${String(e.index).padStart(3,"0")}.webm`);await gt(["-y","-framerate",String(M),"-i",y(p,"frame-%05d.png"),"-c:v","libvpx-vp9","-pix_fmt","yuv420p","-deadline","realtime","-cpu-used","6",b]);let v=new Uint8Array(await F(m));return{videoPath:b,posterPngPath:m,naturalSize:J(v)??{width:fe,height:ge}}}async function we(e,r,i,t){if(!e.captureDir)throw new Error("CLI capture session has no captureDir");let n=Buffer.from(r,"base64"),s=(e.cliScreens?.length??0)+1,o=y(e.captureDir,`screen-${String(s).padStart(3,"0")}.png`);await O(e.captureDir,{recursive:!0}),await U(o,n);let d=J(n)??i.viewport,a={...e,cliScreens:[...e.cliScreens??[],{kind:"image",pngPath:o,viewport:i.viewport,naturalSize:d,sourceUrl:i.sourceUrl,title:i.title,click:t,scroll:i.scroll,capturedAt:new Date().toISOString()}],tabUrl:i.sourceUrl};return await E(a),a}async function wt(e,r,i,t,n){if(e.runtime!=="cli")throw new Error("video capture is only available for --runtime cli sessions");if(!e.captureDir)throw new Error("CLI capture session has no captureDir");let s=t??null,o=null;try{if(!s){o=new I(e.browserWsUrl);let p=await H(o,e.targetId);await o.send("Page.enable",{},p).catch(()=>{}),await o.send("Runtime.enable",{},p).catch(()=>{}),await B(o,p),await j(o,p,e.targetId,e.width,e.height),s=await W(o,p),await o.send("Target.detachFromTarget",{sessionId:p}).catch(()=>{})}let d=(e.cliScreens?.length??0)+1,a=await ht({frames:r,captureDir:e.captureDir,index:d});if(!a)return n&&t?we(e,n,t,i??null):ye(e,void 0,i,"click");let g={...e,cliScreens:[...e.cliScreens??[],{kind:"video",pngPath:a.posterPngPath,videoPath:a.videoPath,posterPngPath:a.posterPngPath,viewport:s.viewport,naturalSize:a.naturalSize,sourceUrl:s.sourceUrl,title:s.title,click:i??s.click,scroll:s.scroll,capturedAt:new Date().toISOString()}],tabUrl:s.sourceUrl};return await E(g),g}finally{o?.close()}}async function ye(e,r,i,t="click"){if(e.runtime!=="cli")throw new Error("capture is only available for --runtime cli sessions");if(!e.captureDir)throw new Error("CLI capture session has no captureDir");let n=new I(e.browserWsUrl);try{let s=await H(n,e.targetId);await n.send("Page.enable",{},s).catch(()=>{}),await n.send("Runtime.enable",{},s).catch(()=>{}),await B(n,s),await j(n,s,e.targetId,e.width,e.height);let o=await W(n,s),a=(await n.send("Page.captureScreenshot",{format:"png",fromSurface:!0,captureBeyondViewport:!1},s)).data;if(typeof a!="string")throw new Error("Page.captureScreenshot returned no data");let g=Buffer.from(a,"base64"),p=(e.cliScreens?.length??0)+1,m=y(e.captureDir,`screen-${String(p).padStart(3,"0")}.png`);await O(e.captureDir,{recursive:!0}),await U(m,g);let b=J(g)??o.viewport,v=t==="click"?i??o.click:null,h=r&&t==="click"?{...v??{x:.5,y:.5},label:r}:v,k={...e,cliScreens:[...e.cliScreens??[],{kind:"image",pngPath:m,viewport:o.viewport,naturalSize:b,sourceUrl:o.sourceUrl,title:o.title,click:h,scroll:o.scroll,capturedAt:new Date().toISOString()}],tabUrl:o.sourceUrl};return await n.send("Target.detachFromTarget",{sessionId:s}).catch(()=>{}),await E(k),k}finally{n.close()}}function be(){return y(process.env.INKLY_CAPTURE_AGENT_HOME||y(qe(),".inkly"),"capture-agent","sessions")}function ve(e){if(!/^[a-zA-Z0-9._:-]+$/.test(e))throw new Error(`invalid session id: ${e}`);return y(be(),`${e}.json`)}function yt(e){return y(be(),`${e}.listener.json`)}async function bt(e){try{let r=await F(yt(e),"utf8");return JSON.parse(r)}catch{return null}}async function E(e){await se(ve(e.id),`${JSON.stringify(e,null,2)}
|
|
315
319
|
`)}async function x(e){let r=await F(ve(e),"utf8");return JSON.parse(r)}async function ke(e){let r=ot(e,"session",Qe),i=await x(r);if(i.runtime!=="cli")throw new Error("listen subcommand is only available for cli runtime sessions");let t=await bt(r),n=new I(i.browserWsUrl),s=null,o=i.targetId,d=Promise.resolve(),a=Promise.resolve(),g=0,p=new Set,m=[],b=!1,v=null,h=null,k=!1;async function A(u){s&&(await n.send("Page.stopScreencast",{},s).catch(()=>{}),await n.send("Target.detachFromTarget",{sessionId:s}).catch(()=>{})),m=[],b=!1,v=null,h=null,o=u,s=await H(n,u),await n.send("Page.enable",{},s).catch(()=>{}),await n.send("Runtime.enable",{},s).catch(()=>{});let w=await x(r).catch(()=>i);await j(n,s,u,w.width,w.height),await B(n,s,!0),m=[],b=!1,v=null,h=null;let c=await x(r).catch(()=>w);c.cliRecordVideo&&c.cliCaptureKind!=="html"&&await n.send("Page.startScreencast",{format:"png",quality:90,maxWidth:c.width,maxHeight:c.height,everyNthFrame:1},s).catch(f=>{process.stderr.write(`capture video start failed: ${f.message}
|
|
316
|
-
`)});let l=await x(r).catch(()=>i);await
|
|
320
|
+
`)});let l=await x(r).catch(()=>i);await E({...l,targetId:u,cliListenerPid:l.cliListenerPid??t?.cliListenerPid??null,cliListenerLogPath:l.cliListenerLogPath??t?.cliListenerLogPath??null,cliListenerReadyAt:l.cliListenerReadyAt??new Date().toISOString()})}async function R(u){k||(k=!0,s&&(await n.send("Page.stopScreencast",{},s).catch(()=>{}),await n.send("Target.detachFromTarget",{sessionId:s}).catch(()=>{})),n.close(),process.exit(u))}process.once("SIGTERM",()=>{R(0)}),process.once("SIGINT",()=>{R(0)}),await n.send("Target.setDiscoverTargets",{discover:!0}).catch(()=>{});for(let u of await ue(n).catch(()=>[]))p.add(u.targetId);await A(i.targetId);{let u=await x(r).catch(()=>i);await E({...u,targetId:o,cliListenerPid:u.cliListenerPid??t?.cliListenerPid??null,cliListenerLogPath:u.cliListenerLogPath??t?.cliListenerLogPath??null,cliListenerReadyAt:u.cliListenerReadyAt??new Date().toISOString()})}n.on("Target.targetCreated",u=>{let c=u.targetInfo;!c||c.type!=="page"||!(c.openerId===o||Date.now()<g)||(a=a.then(async()=>{await T(250),await A(c.targetId),p.add(c.targetId)}).catch(f=>{process.stderr.write(`capture target follow failed: ${f.message}
|
|
317
321
|
`)}))}),n.on("Page.screencastFrame",(u,w)=>{if(w!==s)return;let c=u;typeof c.sessionId=="number"&&n.send("Page.screencastFrameAck",{sessionId:c.sessionId},w).catch(()=>{}),typeof c.data=="string"&&(m.push({data:c.data,receivedAt:Date.now()}),m.length>ae&&(m=m.slice(-ae)))});async function q(){let u=await ue(n).catch(()=>[]),w=u.find(c=>c.openerId===o)??u.find(c=>c.targetId!==o&&!p.has(c.targetId));for(let c of u)p.add(c.targetId);!w||w.targetId===o||await A(w.targetId)}async function Y(u){!u||!s||await n.send("Page.navigate",{url:u},s).catch(w=>{process.stderr.write(`capture post-click navigation failed: ${w.message}
|
|
318
|
-
`)})}n.on("Runtime.bindingCalled",(u,w)=>{if(w!==s)return;let c=u;if(c.name!=="__inklyCliCaptureEvent"&&c.name!=="__inklyCliCaptureClick"||typeof c.payload!="string")return;let l=null;try{let f=JSON.parse(c.payload);c.name==="__inklyCliCaptureClick"?l={type:"click",click:f,page:null}:l={type:f.type==="input"||f.type==="scroll"?f.type:"click",click:typeof f.click=="object"&&f.click?f.click:null,page:typeof f.page=="object"&&f.page?f.page:null}}catch{l=null}if(l){if(l.type==="click")g=Date.now()+3e3;else{let f=Date.now();v=v??f,h=f,b=!0}d=d.then(async()=>{if(l.type!=="click")return;let f=Date.now(),
|
|
322
|
+
`)})}n.on("Runtime.bindingCalled",(u,w)=>{if(w!==s)return;let c=u;if(c.name!=="__inklyCliCaptureEvent"&&c.name!=="__inklyCliCaptureClick"||typeof c.payload!="string")return;let l=null;try{let f=JSON.parse(c.payload);c.name==="__inklyCliCaptureClick"?l={type:"click",click:f,page:null}:l={type:f.type==="input"||f.type==="scroll"?f.type:"click",click:typeof f.click=="object"&&f.click?f.click:null,page:typeof f.page=="object"&&f.page?f.page:null}}catch{l=null}if(l){if(l.type==="click")g=Date.now()+3e3;else{let f=Date.now();v=v??f,h=f,b=!0}d=d.then(async()=>{if(l.type!=="click")return;let f=Date.now(),_=v,X=h,Se=b&&_!==null&&X!==null&&f-X<=et;b=!1,v=null,h=null;let Ae=Math.max(_?_-250:0,f-he),Pe=_?m.filter(C=>C.receivedAt>=Ae):[...m],z=l.page?.navigationUrl??null,D=m.length?m[m.length-1].data:null,L=l.page?{viewport:l.page.viewport,sourceUrl:l.page.url,title:l.page.title,click:l.click,scroll:l.page.scroll}:null;if(s&&(L||(L=await W(n,s).catch(()=>null)),!D)){let C=await n.send("Page.captureScreenshot",{format:"png",fromSurface:!0,captureBeyondViewport:!1},s).catch(()=>null),ee=C&&C.data;D=typeof ee=="string"?ee:null}m=[];let P=await x(r).catch(()=>null);if(!P||P.runtime!=="cli"){await R(0);return}if(P.cliCaptureKind==="html"){await T(z?500:2e3);let C=await ft(P,l.click);await N({session:C,stepKind:"html",action:l.type,click:l.click}),await Y(z),await T(2e3),await a,await q();return}if(await Y(z),await T(2e3),await a,await q(),P.cliRecordVideo&&Se){let C=await wt(P,Pe,l.click,L??void 0,D);await N({session:C,stepKind:le(C),action:l.type,click:l.click});return}if(D&&L){let C=await we(P,D,L,l.click??null);await N({session:C,stepKind:"image",action:l.type,click:l.click});return}let Q=await ye(P,void 0,l.click,"click");await N({session:Q,stepKind:le(Q),action:l.type,click:l.click})}).catch(f=>{process.stderr.write(`capture listener failed: ${f.message}
|
|
319
323
|
`)})}});let Ce=setInterval(()=>{x(r).catch(()=>{clearInterval(Ce),R(0)})},1e3);return await new Promise(()=>{}),0}async function kt(){let e=vt(process.argv.slice(2));process.exit(await ke(e))}kt().catch(e=>{process.stderr.write(`capture listener failed: ${e.message}
|
|
320
324
|
`),process.exit(1)});
|